mirror of
https://github.com/linux-apfs/linux-apfs-rw.git
synced 2026-08-18 02:19:59 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0667dbff17 | ||
|
|
af2ee526c0 | ||
|
|
44f4b4f631 | ||
|
|
b44d2a37bf | ||
|
|
4881d7c707 | ||
|
|
2982c8655f | ||
|
|
707d91b486 | ||
|
|
3e5c902a9a | ||
|
|
695455bfdb | ||
|
|
071cdacc32 | ||
|
|
57eb739f59 | ||
|
|
f088d1a2e3 | ||
|
|
8bcaaebb89 | ||
|
|
781e0024d2 | ||
|
|
5d41bd7e64 | ||
|
|
4e9984c815 | ||
|
|
f1c9ab6c38 |
No files matched your search
@@ -150,9 +150,9 @@ struct apfs_spaceman {
|
||||
struct buffer_head *sm_ip_bmaps[];
|
||||
};
|
||||
|
||||
#define TRANSACTION_MAIN_QUEUE_MAX 4096
|
||||
#define TRANSACTION_BUFFERS_MAX 65536
|
||||
#define TRANSACTION_STARTS_MAX 65536
|
||||
#define APFS_TRANS_MAIN_QUEUE_MAX 10000
|
||||
#define APFS_TRANS_BUFFERS_MAX 65536
|
||||
#define APFS_TRANS_STARTS_MAX 65536
|
||||
|
||||
/* Possible states for the container transaction structure */
|
||||
#define APFS_NX_TRANS_FORCE_COMMIT 1 /* Commit guaranteed */
|
||||
@@ -166,22 +166,15 @@ struct apfs_spaceman {
|
||||
struct apfs_nx_transaction {
|
||||
unsigned int t_state;
|
||||
|
||||
struct delayed_work t_work; /* Work task for transaction commits */
|
||||
struct super_block *t_work_sb; /* sb that last queued a commit */
|
||||
|
||||
struct list_head t_inodes; /* List of inodes in the transaction */
|
||||
struct list_head t_buffers; /* List of buffers in the transaction */
|
||||
size_t t_buffers_count; /* Count of items on the list */
|
||||
int t_starts_count; /* Count of starts for transaction */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure that keeps track of a volume transaction.
|
||||
*/
|
||||
struct apfs_vol_transaction {
|
||||
struct buffer_head *t_old_vsb; /* Volume superblock being replaced */
|
||||
|
||||
struct apfs_node t_old_omap_root; /* Omap root node being replaced */
|
||||
struct apfs_node t_old_cat_root; /* Catalog root node being replaced */
|
||||
};
|
||||
|
||||
/* State bits for buffer heads in a transaction */
|
||||
#define BH_TRANS BH_PrivateStart /* Attached to a transaction */
|
||||
#define BH_CSUM (BH_PrivateStart + 1) /* Requires checksum update */
|
||||
@@ -224,6 +217,11 @@ struct apfs_ephemeral_object_info {
|
||||
/* Mount option flags for a container */
|
||||
#define APFS_CHECK_NODES 1
|
||||
#define APFS_READWRITE 2
|
||||
/*
|
||||
* Mount options for a container are decided on its first mount. This flag lets
|
||||
* future mounts know that has happened already.
|
||||
*/
|
||||
#define APFS_FLAGS_SET 4
|
||||
|
||||
/*
|
||||
* Container superblock data in memory
|
||||
@@ -254,6 +252,7 @@ struct apfs_nxsb_info {
|
||||
|
||||
struct apfs_spaceman *nx_spaceman;
|
||||
struct apfs_nx_transaction nx_transaction;
|
||||
int nx_trans_buffers_max;
|
||||
|
||||
/* For now, a single semaphore for every operation */
|
||||
struct rw_semaphore nx_big_sem;
|
||||
@@ -333,9 +332,6 @@ struct apfs_sb_info {
|
||||
|
||||
struct apfs_crypto_state_val *s_dflt_pfk; /* default per-file key */
|
||||
|
||||
struct apfs_vol_transaction s_transaction;
|
||||
int s_trans_buffers_max;
|
||||
|
||||
struct inode *s_private_dir; /* Inode for the private directory */
|
||||
struct work_struct s_orphan_cleanup_work;
|
||||
};
|
||||
@@ -1081,6 +1077,7 @@ extern int apfs_sync_fs(struct super_block *sb, int wait);
|
||||
|
||||
/* transaction.c */
|
||||
extern int apfs_cpoint_data_free(struct super_block *sb, u64 bno);
|
||||
extern void apfs_transaction_init(struct apfs_nx_transaction *trans);
|
||||
extern int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops);
|
||||
extern int apfs_transaction_commit(struct super_block *sb);
|
||||
extern void apfs_inode_join_transaction(struct super_block *sb, struct inode *inode);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PACKAGE_NAME="linux-apfs-rw"
|
||||
PACKAGE_VERSION="0.3.9"
|
||||
PACKAGE_VERSION="0.3.11"
|
||||
|
||||
BUILT_MODULE_NAME[0]="apfs"
|
||||
DEST_MODULE_LOCATION[0]="/extra"
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include <linux/blk_types.h>
|
||||
#include "apfs.h"
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
|
||||
#define MAX(X, Y) ((X) <= (Y) ? (Y) : (X))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* apfs_ext_is_hole - Does this extent represent a hole in a sparse file?
|
||||
@@ -1651,7 +1653,7 @@ static int apfs_dstream_delete_front(struct super_block *sb, u64 ds_id)
|
||||
int ret;
|
||||
|
||||
fq = &sm_raw->sm_fq[APFS_SFQ_MAIN];
|
||||
if (le64_to_cpu(fq->sfq_count) > TRANSACTION_MAIN_QUEUE_MAX)
|
||||
if (le64_to_cpu(fq->sfq_count) > APFS_TRANS_MAIN_QUEUE_MAX)
|
||||
return -EAGAIN;
|
||||
|
||||
query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */);
|
||||
@@ -1710,7 +1712,7 @@ next_extent:
|
||||
}
|
||||
}
|
||||
|
||||
if (le64_to_cpu(fq->sfq_count) <= TRANSACTION_MAIN_QUEUE_MAX)
|
||||
if (le64_to_cpu(fq->sfq_count) <= APFS_TRANS_MAIN_QUEUE_MAX)
|
||||
goto next_extent;
|
||||
ret = -EAGAIN;
|
||||
out:
|
||||
|
||||
+72
-3
@@ -149,9 +149,9 @@ static int apfs_create_snap_name_rec(struct apfs_node *snap_root, const char *na
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
if (err == 0) {
|
||||
/* TODO: avoid transaction abort here */
|
||||
apfs_info(sb, "a snapshot with that name already exists");
|
||||
err = -EEXIST;
|
||||
/* This should never happen here, the caller has checked */
|
||||
apfs_alert(sb, "snapshot name record already exists");
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
if (err != -ENODATA) {
|
||||
@@ -332,6 +332,56 @@ static int apfs_update_omap_snapshots(struct super_block *sb)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_snapshot_name_check - Check if a snapshot with the given name exists
|
||||
* @sb: filesystem superblock
|
||||
* @name: name to check
|
||||
* @name_len: length of @name
|
||||
* @eexist: on return, whether the name exists or not
|
||||
*
|
||||
* Returns 0 on success, or a negative error code in case of failure.
|
||||
*/
|
||||
static int apfs_snapshot_name_check(struct super_block *sb, const char *name, int name_len, bool *eexist)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_superblock *vsb_raw = sbi->s_vsb_raw;
|
||||
struct apfs_node *snap_root = NULL;
|
||||
struct apfs_query *query = NULL;
|
||||
int err;
|
||||
|
||||
snap_root = apfs_read_node(sb, le64_to_cpu(vsb_raw->apfs_snap_meta_tree_oid), APFS_OBJ_PHYSICAL, false /* write */);
|
||||
if (IS_ERR(snap_root)) {
|
||||
apfs_err(sb, "failed to read snap meta root 0x%llx", le64_to_cpu(vsb_raw->apfs_snap_meta_tree_oid));
|
||||
return PTR_ERR(snap_root);
|
||||
}
|
||||
vsb_raw = NULL;
|
||||
|
||||
query = apfs_alloc_query(snap_root, NULL /* parent */);
|
||||
if (!query) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
apfs_init_snap_name_key(name, &query->key);
|
||||
query->flags |= APFS_QUERY_SNAP_META | APFS_QUERY_EXACT;
|
||||
|
||||
err = apfs_btree_query(sb, &query);
|
||||
if (err == 0) {
|
||||
*eexist = true;
|
||||
} else if (err == -ENODATA) {
|
||||
*eexist = false;
|
||||
err = 0;
|
||||
} else {
|
||||
apfs_err(sb, "query failed (%s)", name);
|
||||
}
|
||||
|
||||
out:
|
||||
apfs_free_query(query);
|
||||
query = NULL;
|
||||
apfs_node_free(snap_root);
|
||||
snap_root = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_do_ioc_takesnapshot - Actual work for apfs_ioc_take_snapshot()
|
||||
* @mntpoint: inode of the mount point to snapshot
|
||||
@@ -348,12 +398,31 @@ static int apfs_do_ioc_take_snapshot(struct inode *mntpoint, const char *name, i
|
||||
/* TODO: remember to update the maxops in the future */
|
||||
struct apfs_max_ops maxops = {0};
|
||||
u64 sblock_oid;
|
||||
bool eexist = false;
|
||||
int err;
|
||||
|
||||
err = apfs_transaction_start(sb, maxops);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* This check can fail in normal operation, so run it before making any
|
||||
* changes and exit the transaction cleanly if necessary. The name
|
||||
* lookup will have to be repeated later, but it's ok because I don't
|
||||
* expect snapshot creation to be a bottleneck for anyone.
|
||||
*/
|
||||
err = apfs_snapshot_name_check(sb, name, name_len, &eexist);
|
||||
if (err) {
|
||||
apfs_err(sb, "snapshot name existence check failed");
|
||||
goto fail;
|
||||
}
|
||||
if (eexist) {
|
||||
err = apfs_transaction_commit(sb);
|
||||
if (err)
|
||||
goto fail;
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush the extent caches to the extenref tree before it gets moved to
|
||||
* the snapshot. It seems safer in general to avoid big unpredictable
|
||||
|
||||
+1
-1
@@ -548,7 +548,7 @@ static int apfs_flush_free_queue(struct super_block *sb, unsigned int qid, bool
|
||||
sfq_count = le64_to_cpu(fq->sfq_count);
|
||||
if (qid == APFS_SFQ_IP && sfq_count * 6 <= le64_to_cpu(sm_raw->sm_ip_block_count))
|
||||
break;
|
||||
if (qid == APFS_SFQ_MAIN && sfq_count <= TRANSACTION_MAIN_QUEUE_MAX - 200)
|
||||
if (qid == APFS_SFQ_MAIN && sfq_count <= APFS_TRANS_MAIN_QUEUE_MAX - 200)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+85
-76
@@ -20,8 +20,12 @@ static int apfs_checkpoint_end(struct super_block *sb)
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_obj_phys *obj = &nxi->nx_raw->nx_o;
|
||||
struct buffer_head *bh = NULL;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0)
|
||||
struct address_space *bdev_map = nxi->nx_bdev->bd_mapping;
|
||||
#else
|
||||
struct inode *bdev_inode = nxi->nx_bdev->bd_inode;
|
||||
struct address_space *bdev_map = bdev_inode->i_mapping;
|
||||
#endif
|
||||
int err;
|
||||
|
||||
ASSERT(!(sb->s_flags & SB_RDONLY));
|
||||
@@ -262,6 +266,53 @@ static int apfs_read_ephemeral_objects(struct super_block *sb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apfs_trans_commit_work(struct work_struct *work)
|
||||
{
|
||||
struct super_block *sb = NULL;
|
||||
struct apfs_nxsb_info *nxi = NULL;
|
||||
struct apfs_nx_transaction *trans = NULL;
|
||||
int err;
|
||||
|
||||
trans = container_of(to_delayed_work(work), struct apfs_nx_transaction, t_work);
|
||||
nxi = container_of(trans, struct apfs_nxsb_info, nx_transaction);
|
||||
sb = trans->t_work_sb;
|
||||
|
||||
/*
|
||||
* If sb is set then the transaction already started, there is no need
|
||||
* for apfs_transaction_start() here. It would be cleaner to call it
|
||||
* anyway (and check in there if sb is set), but maxops is a problem
|
||||
* because we don't need any space. I really need to rethink that stuff
|
||||
* (TODO).
|
||||
*/
|
||||
down_write(&nxi->nx_big_sem);
|
||||
if (!sb || sb->s_flags & SB_RDONLY) {
|
||||
/* The commit already took place, or there was an abort */
|
||||
up_write(&nxi->nx_big_sem);
|
||||
return;
|
||||
}
|
||||
|
||||
trans->t_state |= APFS_NX_TRANS_FORCE_COMMIT;
|
||||
err = apfs_transaction_commit(sb);
|
||||
if (err) {
|
||||
apfs_err(sb, "queued commit failed (err:%d)", err);
|
||||
apfs_transaction_abort(sb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_transaction_init - Initialize the transaction struct for the container
|
||||
* @trans: the transaction structure
|
||||
*/
|
||||
void apfs_transaction_init(struct apfs_nx_transaction *trans)
|
||||
{
|
||||
trans->t_state = 0;
|
||||
INIT_DELAYED_WORK(&trans->t_work, apfs_trans_commit_work);
|
||||
INIT_LIST_HEAD(&trans->t_inodes);
|
||||
INIT_LIST_HEAD(&trans->t_buffers);
|
||||
trans->t_buffers_count = 0;
|
||||
trans->t_starts_count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_transaction_start - Begin a new transaction
|
||||
* @sb: superblock structure
|
||||
@@ -272,18 +323,14 @@ static int apfs_read_ephemeral_objects(struct super_block *sb)
|
||||
*/
|
||||
int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_vol_transaction *vol_trans = &sbi->s_transaction;
|
||||
struct apfs_nx_transaction *nx_trans = &nxi->nx_transaction;
|
||||
int err;
|
||||
|
||||
down_write(&nxi->nx_big_sem);
|
||||
mutex_lock(&nxs_mutex); /* Don't mount during a transaction */
|
||||
|
||||
if (sb->s_flags & SB_RDONLY) {
|
||||
/* A previous transaction has failed; this should be rare */
|
||||
mutex_unlock(&nxs_mutex);
|
||||
up_write(&nxi->nx_big_sem);
|
||||
return -EROFS;
|
||||
}
|
||||
@@ -295,7 +342,6 @@ int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops)
|
||||
if (!nxi->nx_eph_list) {
|
||||
err = apfs_read_ephemeral_objects(sb);
|
||||
if (err) {
|
||||
mutex_unlock(&nxs_mutex);
|
||||
up_write(&nxi->nx_big_sem);
|
||||
apfs_err(sb, "failed to read the ephemeral objects");
|
||||
return err;
|
||||
@@ -306,9 +352,6 @@ int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops)
|
||||
++nxi->nx_xid;
|
||||
nxi->nx_raw->nx_next_xid = cpu_to_le64(nxi->nx_xid + 1);
|
||||
|
||||
INIT_LIST_HEAD(&nx_trans->t_inodes);
|
||||
INIT_LIST_HEAD(&nx_trans->t_buffers);
|
||||
|
||||
err = apfs_read_spaceman(sb);
|
||||
if (err) {
|
||||
apfs_err(sb, "failed to read the spaceman");
|
||||
@@ -328,16 +371,7 @@ int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops)
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (!vol_trans->t_old_vsb) {
|
||||
vol_trans->t_old_vsb = sbi->s_vobject.o_bh;
|
||||
get_bh(vol_trans->t_old_vsb);
|
||||
|
||||
/* Backup the old tree roots; the node struct issues make this ugly */
|
||||
vol_trans->t_old_cat_root = *sbi->s_cat_root;
|
||||
get_bh(vol_trans->t_old_cat_root.object.o_bh);
|
||||
vol_trans->t_old_omap_root = *sbi->s_omap->omap_root;
|
||||
get_bh(vol_trans->t_old_omap_root.object.o_bh);
|
||||
|
||||
if (nx_trans->t_starts_count == 0) {
|
||||
err = apfs_map_volume_super(sb, true /* write */);
|
||||
if (err) {
|
||||
apfs_err(sb, "CoW failed for volume super");
|
||||
@@ -401,14 +435,12 @@ int apfs_transaction_flush_all_inodes(struct super_block *sb)
|
||||
list_del_init(&ai->i_list);
|
||||
|
||||
nx_trans->t_state |= APFS_NX_TRANS_COMMITTING;
|
||||
mutex_unlock(&nxs_mutex);
|
||||
up_write(&nxi->nx_big_sem);
|
||||
|
||||
/* Unlocked, so it may call evict() and wait for writeback */
|
||||
iput(inode);
|
||||
|
||||
down_write(&nxi->nx_big_sem);
|
||||
mutex_lock(&nxs_mutex);
|
||||
nx_trans->t_state = 0;
|
||||
|
||||
/* Transaction aborted during writeback, error code is lost */
|
||||
@@ -596,7 +628,6 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
|
||||
{
|
||||
struct apfs_spaceman *sm = APFS_SM(sb);
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_sb_info *sbi;
|
||||
struct apfs_nx_transaction *nx_trans = &nxi->nx_transaction;
|
||||
struct apfs_bh_info *bhi, *tmp;
|
||||
int err = 0;
|
||||
@@ -644,7 +675,11 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
|
||||
}
|
||||
list_for_each_entry_safe(bhi, tmp, &nx_trans->t_buffers, list) {
|
||||
struct buffer_head *bh = bhi->bh;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
|
||||
struct folio *folio = NULL;
|
||||
#else
|
||||
struct page *page = NULL;
|
||||
#endif
|
||||
bool is_metadata;
|
||||
|
||||
ASSERT(buffer_trans(bh));
|
||||
@@ -664,27 +699,44 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
|
||||
kfree(bhi);
|
||||
bhi = NULL;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
|
||||
folio = page_folio(bh->b_page);
|
||||
folio_get(folio);
|
||||
#else
|
||||
page = bh->b_page;
|
||||
get_page(page);
|
||||
#endif
|
||||
|
||||
is_metadata = buffer_csum(bh);
|
||||
clear_buffer_csum(bh);
|
||||
put_bh(bh);
|
||||
bh = NULL;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
|
||||
folio_lock(folio);
|
||||
folio_mkclean(folio);
|
||||
#else
|
||||
/* Future writes to mmapped areas should fault for CoW */
|
||||
lock_page(page);
|
||||
page_mkclean(page);
|
||||
#endif
|
||||
/* XXX: otherwise, the page cache fills up and crashes the machine */
|
||||
if (!is_metadata) {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
|
||||
try_to_free_buffers(folio);
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
|
||||
try_to_free_buffers(page_folio(page));
|
||||
#else
|
||||
try_to_free_buffers(page);
|
||||
#endif
|
||||
}
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
#else
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
#endif
|
||||
}
|
||||
err = apfs_checkpoint_end(sb);
|
||||
if (err) {
|
||||
@@ -692,24 +744,6 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
|
||||
return err;
|
||||
}
|
||||
|
||||
list_for_each_entry(sbi, &nxi->vol_list, list) {
|
||||
struct apfs_vol_transaction *vol_trans = &sbi->s_transaction;
|
||||
|
||||
if (!vol_trans->t_old_vsb)
|
||||
continue;
|
||||
|
||||
brelse(vol_trans->t_old_vsb);
|
||||
vol_trans->t_old_vsb = NULL;
|
||||
|
||||
/* XXX: forget the buffers for the b-tree roots */
|
||||
vol_trans->t_old_omap_root.object.data = NULL;
|
||||
brelse(vol_trans->t_old_omap_root.object.o_bh);
|
||||
vol_trans->t_old_omap_root.object.o_bh = NULL;
|
||||
vol_trans->t_old_cat_root.object.data = NULL;
|
||||
brelse(vol_trans->t_old_cat_root.object.o_bh);
|
||||
vol_trans->t_old_cat_root.object.o_bh = NULL;
|
||||
}
|
||||
|
||||
for (bmap_idx = 0; bmap_idx < APFS_SM(sb)->sm_ip_bmaps_count; ++bmap_idx) {
|
||||
brelse(APFS_SM(sb)->sm_ip_bmaps[bmap_idx]);
|
||||
APFS_SM(sb)->sm_ip_bmaps[bmap_idx] = NULL;
|
||||
@@ -749,9 +783,9 @@ static bool apfs_transaction_need_commit(struct super_block *sb)
|
||||
struct apfs_spaceman_phys *sm_raw = sm->sm_raw;
|
||||
struct apfs_spaceman_free_queue *fq_ip = &sm_raw->sm_fq[APFS_SFQ_IP];
|
||||
struct apfs_spaceman_free_queue *fq_main = &sm_raw->sm_fq[APFS_SFQ_MAIN];
|
||||
int buffers_max = APFS_SB(sb)->s_trans_buffers_max;
|
||||
int starts_max = TRANSACTION_STARTS_MAX;
|
||||
int mq_max = TRANSACTION_MAIN_QUEUE_MAX;
|
||||
int buffers_max = nxi->nx_trans_buffers_max;
|
||||
int starts_max = APFS_TRANS_STARTS_MAX;
|
||||
int mq_max = APFS_TRANS_MAIN_QUEUE_MAX;
|
||||
|
||||
/*
|
||||
* Try to avoid committing halfway through a data block write,
|
||||
@@ -796,17 +830,24 @@ static bool apfs_transaction_need_commit(struct super_block *sb)
|
||||
int apfs_transaction_commit(struct super_block *sb)
|
||||
{
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_nx_transaction *trans = NULL;
|
||||
int err = 0;
|
||||
|
||||
trans = &nxi->nx_transaction;
|
||||
|
||||
if (apfs_transaction_need_commit(sb)) {
|
||||
err = apfs_transaction_commit_nx(sb);
|
||||
if (err) {
|
||||
apfs_err(sb, "transaction commit failed");
|
||||
return err;
|
||||
}
|
||||
trans->t_work_sb = NULL;
|
||||
cancel_delayed_work(&trans->t_work);
|
||||
} else {
|
||||
trans->t_work_sb = sb;
|
||||
mod_delayed_work(system_wq, &trans->t_work, msecs_to_jiffies(100));
|
||||
}
|
||||
|
||||
mutex_unlock(&nxs_mutex);
|
||||
up_write(&nxi->nx_big_sem);
|
||||
return 0;
|
||||
}
|
||||
@@ -889,7 +930,6 @@ static void apfs_force_readonly(struct apfs_nxsb_info *nxi)
|
||||
*/
|
||||
void apfs_transaction_abort(struct super_block *sb)
|
||||
{
|
||||
struct apfs_sb_info *sbi;
|
||||
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
|
||||
struct apfs_nx_transaction *nx_trans = &nxi->nx_transaction;
|
||||
struct apfs_bh_info *bhi, *tmp;
|
||||
@@ -901,7 +941,6 @@ void apfs_transaction_abort(struct super_block *sb)
|
||||
/* Transaction already aborted, do nothing */
|
||||
ASSERT(list_empty(&nx_trans->t_inodes));
|
||||
ASSERT(list_empty(&nx_trans->t_buffers));
|
||||
mutex_unlock(&nxs_mutex);
|
||||
up_write(&nxi->nx_big_sem);
|
||||
return;
|
||||
}
|
||||
@@ -924,35 +963,6 @@ void apfs_transaction_abort(struct super_block *sb)
|
||||
kfree(bhi);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: get rid of all this stuff, it makes little sense. Maybe do an
|
||||
* actual read-only remount?
|
||||
*/
|
||||
list_for_each_entry(sbi, &nxi->vol_list, list) {
|
||||
struct apfs_vol_transaction *vol_trans = &sbi->s_transaction;
|
||||
|
||||
if (!vol_trans->t_old_vsb)
|
||||
continue;
|
||||
|
||||
/* Restore volume state for all volumes */
|
||||
brelse(sbi->s_vobject.o_bh);
|
||||
sbi->s_vobject.o_bh = vol_trans->t_old_vsb;
|
||||
sbi->s_vobject.data = sbi->s_vobject.o_bh->b_data;
|
||||
sbi->s_vobject.block_nr = vol_trans->t_old_vsb->b_blocknr;
|
||||
sbi->s_vsb_raw = (void *)vol_trans->t_old_vsb->b_data;
|
||||
vol_trans->t_old_vsb = NULL;
|
||||
|
||||
/* XXX: restore the old b-tree root nodes */
|
||||
brelse(sbi->s_omap->omap_root->object.o_bh);
|
||||
*(sbi->s_omap->omap_root) = vol_trans->t_old_omap_root;
|
||||
vol_trans->t_old_omap_root.object.o_bh = NULL;
|
||||
vol_trans->t_old_omap_root.object.data = NULL;
|
||||
brelse(sbi->s_cat_root->object.o_bh);
|
||||
*(sbi->s_cat_root) = vol_trans->t_old_cat_root;
|
||||
vol_trans->t_old_cat_root.object.o_bh = NULL;
|
||||
vol_trans->t_old_cat_root.object.data = NULL;
|
||||
}
|
||||
|
||||
sm = APFS_SM(sb);
|
||||
if (sm) {
|
||||
for (bmap_idx = 0; bmap_idx < sm->sm_ip_bmaps_count; ++bmap_idx) {
|
||||
@@ -968,7 +978,6 @@ void apfs_transaction_abort(struct super_block *sb)
|
||||
*/
|
||||
apfs_force_readonly(nxi);
|
||||
|
||||
mutex_unlock(&nxs_mutex);
|
||||
up_write(&nxi->nx_big_sem);
|
||||
|
||||
list_for_each_entry_safe(ai, ai_tmp, &nx_trans->t_inodes, i_list) {
|
||||
|
||||
@@ -12,9 +12,12 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/version.h>
|
||||
#include "unicode.h"
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
|
||||
#define MIN(X, Y) ((X) <= (Y) ? (X) : (Y))
|
||||
#endif
|
||||
|
||||
/* The arrays of unicode data are defined at the bottom of the file */
|
||||
/* TODO: would a single trie with all the data be more efficient? */
|
||||
|
||||
Reference in new issue
Block a user