From 50607b514d8a9ed6c8c5c1c119158754f5f6ff17 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 30 Jun 2023 17:54:58 +0100 Subject: ext2: remove redundant assignment to variable desc and variable best_desc Variable desc is being assigned a value that is never read, the exit via label found immeditely returns with no access to desc. The assignment is redundant and can be removed. Also remove variable best_desc since this is not used. Cleans up clang scan muild warning: fs/ext2/ialloc.c:297:4: warning: Value stored to 'desc' is never read [deadcode.DeadStores] Signed-off-by: Colin Ian King Signed-off-by: Jan Kara Message-Id: <20230630165458.166238-1-colin.i.king@gmail.com> --- fs/ext2/ialloc.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'fs') diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index a4e1d7a9c544..34cd5dc1da23 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -273,7 +273,6 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent) if ((parent == d_inode(sb->s_root)) || (EXT2_I(parent)->i_flags & EXT2_TOPDIR_FL)) { - struct ext2_group_desc *best_desc = NULL; int best_ndir = inodes_per_group; int best_group = -1; @@ -291,10 +290,8 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent) continue; best_group = group; best_ndir = le16_to_cpu(desc->bg_used_dirs_count); - best_desc = desc; } if (best_group >= 0) { - desc = best_desc; group = best_group; goto found; } -- cgit v1.2.3 From 024128477809f8073d870307c8157b8826ebfd08 Mon Sep 17 00:00:00 2001 From: Baokun Li Date: Fri, 30 Jun 2023 19:08:18 +0800 Subject: quota: factor out dquot_write_dquot() Refactor out dquot_write_dquot() to reduce duplicate code. Signed-off-by: Baokun Li Signed-off-by: Jan Kara Message-Id: <20230630110822.3881712-2-libaokun1@huawei.com> --- fs/quota/dquot.c | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) (limited to 'fs') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index e3e4f4047657..108ba9f1e420 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -628,6 +628,18 @@ out: } EXPORT_SYMBOL(dquot_scan_active); +static inline int dquot_write_dquot(struct dquot *dquot) +{ + int ret = dquot->dq_sb->dq_op->write_dquot(dquot); + if (ret < 0) { + quota_error(dquot->dq_sb, "Can't write quota structure " + "(error %d). Quota may get out of sync!", ret); + /* Clear dirty bit anyway to avoid infinite loop. */ + clear_dquot_dirty(dquot); + } + return ret; +} + /* Write all dquot structures to quota files */ int dquot_writeback_dquots(struct super_block *sb, int type) { @@ -658,16 +670,9 @@ int dquot_writeback_dquots(struct super_block *sb, int type) * use count */ dqgrab(dquot); spin_unlock(&dq_list_lock); - err = sb->dq_op->write_dquot(dquot); - if (err) { - /* - * Clear dirty bit anyway to avoid infinite - * loop here. - */ - clear_dquot_dirty(dquot); - if (!ret) - ret = err; - } + err = dquot_write_dquot(dquot); + if (err && !ret) + ret = err; dqput(dquot); spin_lock(&dq_list_lock); } @@ -765,8 +770,6 @@ static struct shrinker dqcache_shrinker = { */ void dqput(struct dquot *dquot) { - int ret; - if (!dquot) return; #ifdef CONFIG_QUOTA_DEBUG @@ -794,17 +797,7 @@ we_slept: if (dquot_dirty(dquot)) { spin_unlock(&dq_list_lock); /* Commit dquot before releasing */ - ret = dquot->dq_sb->dq_op->write_dquot(dquot); - if (ret < 0) { - quota_error(dquot->dq_sb, "Can't write quota structure" - " (error %d). Quota may get out of sync!", - ret); - /* - * We clear dirty bit anyway, so that we avoid - * infinite loop here - */ - clear_dquot_dirty(dquot); - } + dquot_write_dquot(dquot); goto we_slept; } if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) { -- cgit v1.2.3 From 4b9bdfa16535de8f49bf954aeed0f525ee2fc322 Mon Sep 17 00:00:00 2001 From: Baokun Li Date: Fri, 30 Jun 2023 19:08:19 +0800 Subject: quota: rename dquot_active() to inode_quota_active() Now we have a helper function dquot_dirty() to determine if dquot has DQ_MOD_B bit. dquot_active() can easily be misunderstood as a helper function to determine if dquot has DQ_ACTIVE_B bit. So we avoid this by renaming it to inode_quota_active() and later on we will add the helper function dquot_active() to determine if dquot has DQ_ACTIVE_B bit. Signed-off-by: Baokun Li Signed-off-by: Jan Kara Message-Id: <20230630110822.3881712-3-libaokun1@huawei.com> --- fs/quota/dquot.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 108ba9f1e420..a08698d9859a 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1418,7 +1418,7 @@ static int info_bdq_free(struct dquot *dquot, qsize_t space) return QUOTA_NL_NOWARN; } -static int dquot_active(const struct inode *inode) +static int inode_quota_active(const struct inode *inode) { struct super_block *sb = inode->i_sb; @@ -1441,7 +1441,7 @@ static int __dquot_initialize(struct inode *inode, int type) qsize_t rsv; int ret = 0; - if (!dquot_active(inode)) + if (!inode_quota_active(inode)) return 0; dquots = i_dquot(inode); @@ -1549,7 +1549,7 @@ bool dquot_initialize_needed(struct inode *inode) struct dquot **dquots; int i; - if (!dquot_active(inode)) + if (!inode_quota_active(inode)) return false; dquots = i_dquot(inode); @@ -1660,7 +1660,7 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags) int reserve = flags & DQUOT_SPACE_RESERVE; struct dquot **dquots; - if (!dquot_active(inode)) { + if (!inode_quota_active(inode)) { if (reserve) { spin_lock(&inode->i_lock); *inode_reserved_space(inode) += number; @@ -1730,7 +1730,7 @@ int dquot_alloc_inode(struct inode *inode) struct dquot_warn warn[MAXQUOTAS]; struct dquot * const *dquots; - if (!dquot_active(inode)) + if (!inode_quota_active(inode)) return 0; for (cnt = 0; cnt < MAXQUOTAS; cnt++) warn[cnt].w_type = QUOTA_NL_NOWARN; @@ -1773,7 +1773,7 @@ int dquot_claim_space_nodirty(struct inode *inode, qsize_t number) struct dquot **dquots; int cnt, index; - if (!dquot_active(inode)) { + if (!inode_quota_active(inode)) { spin_lock(&inode->i_lock); *inode_reserved_space(inode) -= number; __inode_add_bytes(inode, number); @@ -1815,7 +1815,7 @@ void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number) struct dquot **dquots; int cnt, index; - if (!dquot_active(inode)) { + if (!inode_quota_active(inode)) { spin_lock(&inode->i_lock); *inode_reserved_space(inode) += number; __inode_sub_bytes(inode, number); @@ -1859,7 +1859,7 @@ void __dquot_free_space(struct inode *inode, qsize_t number, int flags) struct dquot **dquots; int reserve = flags & DQUOT_SPACE_RESERVE, index; - if (!dquot_active(inode)) { + if (!inode_quota_active(inode)) { if (reserve) { spin_lock(&inode->i_lock); *inode_reserved_space(inode) -= number; @@ -1914,7 +1914,7 @@ void dquot_free_inode(struct inode *inode) struct dquot * const *dquots; int index; - if (!dquot_active(inode)) + if (!inode_quota_active(inode)) return; dquots = i_dquot(inode); @@ -2086,7 +2086,7 @@ int dquot_transfer(struct mnt_idmap *idmap, struct inode *inode, struct super_block *sb = inode->i_sb; int ret; - if (!dquot_active(inode)) + if (!inode_quota_active(inode)) return 0; if (i_uid_needs_update(idmap, iattr, inode)) { -- cgit v1.2.3 From 33bcfafc48cb186bc4bbcea247feaa396594229e Mon Sep 17 00:00:00 2001 From: Baokun Li Date: Fri, 30 Jun 2023 19:08:20 +0800 Subject: quota: add new helper dquot_active() Add new helper function dquot_active() to make the code more concise. Signed-off-by: Baokun Li Signed-off-by: Jan Kara Message-Id: <20230630110822.3881712-4-libaokun1@huawei.com> --- fs/quota/dquot.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index a08698d9859a..88aa747f4800 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -336,6 +336,11 @@ static void wait_on_dquot(struct dquot *dquot) mutex_unlock(&dquot->dq_lock); } +static inline int dquot_active(struct dquot *dquot) +{ + return test_bit(DQ_ACTIVE_B, &dquot->dq_flags); +} + static inline int dquot_dirty(struct dquot *dquot) { return test_bit(DQ_MOD_B, &dquot->dq_flags); @@ -351,14 +356,14 @@ int dquot_mark_dquot_dirty(struct dquot *dquot) { int ret = 1; - if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) + if (!dquot_active(dquot)) return 0; if (sb_dqopt(dquot->dq_sb)->flags & DQUOT_NOLIST_DIRTY) return test_and_set_bit(DQ_MOD_B, &dquot->dq_flags); /* If quota is dirty already, we don't have to acquire dq_list_lock */ - if (test_bit(DQ_MOD_B, &dquot->dq_flags)) + if (dquot_dirty(dquot)) return 1; spin_lock(&dq_list_lock); @@ -440,7 +445,7 @@ int dquot_acquire(struct dquot *dquot) smp_mb__before_atomic(); set_bit(DQ_READ_B, &dquot->dq_flags); /* Instantiate dquot if needed */ - if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && !dquot->dq_off) { + if (!dquot_active(dquot) && !dquot->dq_off) { ret = dqopt->ops[dquot->dq_id.type]->commit_dqblk(dquot); /* Write the info if needed */ if (info_dirty(&dqopt->info[dquot->dq_id.type])) { @@ -482,7 +487,7 @@ int dquot_commit(struct dquot *dquot) goto out_lock; /* Inactive dquot can be only if there was error during read/init * => we have better not writing it */ - if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) + if (dquot_active(dquot)) ret = dqopt->ops[dquot->dq_id.type]->commit_dqblk(dquot); else ret = -EIO; @@ -597,7 +602,7 @@ int dquot_scan_active(struct super_block *sb, spin_lock(&dq_list_lock); list_for_each_entry(dquot, &inuse_list, dq_inuse) { - if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) + if (!dquot_active(dquot)) continue; if (dquot->dq_sb != sb) continue; @@ -612,7 +617,7 @@ int dquot_scan_active(struct super_block *sb, * outstanding call and recheck the DQ_ACTIVE_B after that. */ wait_on_dquot(dquot); - if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) { + if (dquot_active(dquot)) { ret = fn(dquot, priv); if (ret < 0) goto out; @@ -663,7 +668,7 @@ int dquot_writeback_dquots(struct super_block *sb, int type) dquot = list_first_entry(&dirty, struct dquot, dq_dirty); - WARN_ON(!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)); + WARN_ON(!dquot_active(dquot)); /* Now we have active dquot from which someone is * holding reference so we can safely just increase @@ -800,7 +805,7 @@ we_slept: dquot_write_dquot(dquot); goto we_slept; } - if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) { + if (dquot_active(dquot)) { spin_unlock(&dq_list_lock); dquot->dq_sb->dq_op->release_dquot(dquot); goto we_slept; @@ -901,7 +906,7 @@ we_slept: * already finished or it will be canceled due to dq_count > 1 test */ wait_on_dquot(dquot); /* Read the dquot / allocate space in quota file */ - if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) { + if (!dquot_active(dquot)) { int err; err = sb->dq_op->acquire_dquot(dquot); -- cgit v1.2.3 From dabc8b20756601b9e1cc85a81d47d3f98ed4d13a Mon Sep 17 00:00:00 2001 From: Baokun Li Date: Fri, 30 Jun 2023 19:08:21 +0800 Subject: quota: fix dqput() to follow the guarantees dquot_srcu should provide The dquot_mark_dquot_dirty() using dquot references from the inode should be protected by dquot_srcu. quota_off code takes care to call synchronize_srcu(&dquot_srcu) to not drop dquot references while they are used by other users. But dquot_transfer() breaks this assumption. We call dquot_transfer() to drop the last reference of dquot and add it to free_dquots, but there may still be other users using the dquot at this time, as shown in the function graph below: cpu1 cpu2 _________________|_________________ wb_do_writeback CHOWN(1) ... ext4_da_update_reserve_space dquot_claim_block ... dquot_mark_dquot_dirty // try to dirty old quota test_bit(DQ_ACTIVE_B, &dquot->dq_flags) // still ACTIVE if (test_bit(DQ_MOD_B, &dquot->dq_flags)) // test no dirty, wait dq_list_lock ... dquot_transfer __dquot_transfer dqput_all(transfer_from) // rls old dquot dqput // last dqput dquot_release clear_bit(DQ_ACTIVE_B, &dquot->dq_flags) atomic_dec(&dquot->dq_count) put_dquot_last(dquot) list_add_tail(&dquot->dq_free, &free_dquots) // add the dquot to free_dquots if (!test_and_set_bit(DQ_MOD_B, &dquot->dq_flags)) add dqi_dirty_list // add released dquot to dirty_list This can cause various issues, such as dquot being destroyed by dqcache_shrink_scan() after being added to free_dquots, which can trigger a UAF in dquot_mark_dquot_dirty(); or after dquot is added to free_dquots and then to dirty_list, it is added to free_dquots again after dquot_writeback_dquots() is executed, which causes the free_dquots list to be corrupted and triggers a UAF when dqcache_shrink_scan() is called for freeing dquot twice. As Honza said, we need to fix dquot_transfer() to follow the guarantees dquot_srcu should provide. But calling synchronize_srcu() directly from dquot_transfer() is too expensive (and mostly unnecessary). So we add dquot whose last reference should be dropped to the new global dquot list releasing_dquots, and then queue work item which would call synchronize_srcu() and after that perform the final cleanup of all the dquots on releasing_dquots. Fixes: 4580b30ea887 ("quota: Do not dirty bad dquots") Suggested-by: Jan Kara Signed-off-by: Baokun Li Signed-off-by: Jan Kara Message-Id: <20230630110822.3881712-5-libaokun1@huawei.com> --- fs/quota/dquot.c | 96 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 88aa747f4800..c7afe433d991 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -225,13 +225,22 @@ static void put_quota_format(struct quota_format_type *fmt) /* * Dquot List Management: - * The quota code uses four lists for dquot management: the inuse_list, - * free_dquots, dqi_dirty_list, and dquot_hash[] array. A single dquot - * structure may be on some of those lists, depending on its current state. + * The quota code uses five lists for dquot management: the inuse_list, + * releasing_dquots, free_dquots, dqi_dirty_list, and dquot_hash[] array. + * A single dquot structure may be on some of those lists, depending on + * its current state. * * All dquots are placed to the end of inuse_list when first created, and this * list is used for invalidate operation, which must look at every dquot. * + * When the last reference of a dquot will be dropped, the dquot will be + * added to releasing_dquots. We'd then queue work item which would call + * synchronize_srcu() and after that perform the final cleanup of all the + * dquots on the list. Both releasing_dquots and free_dquots use the + * dq_free list_head in the dquot struct. When a dquot is removed from + * releasing_dquots, a reference count is always subtracted, and if + * dq_count == 0 at that point, the dquot will be added to the free_dquots. + * * Unused dquots (dq_count == 0) are added to the free_dquots list when freed, * and this list is searched whenever we need an available dquot. Dquots are * removed from the list as soon as they are used again, and @@ -250,6 +259,7 @@ static void put_quota_format(struct quota_format_type *fmt) static LIST_HEAD(inuse_list); static LIST_HEAD(free_dquots); +static LIST_HEAD(releasing_dquots); static unsigned int dq_hash_bits, dq_hash_mask; static struct hlist_head *dquot_hash; @@ -260,6 +270,9 @@ static qsize_t inode_get_rsv_space(struct inode *inode); static qsize_t __inode_get_rsv_space(struct inode *inode); static int __dquot_initialize(struct inode *inode, int type); +static void quota_release_workfn(struct work_struct *work); +static DECLARE_DELAYED_WORK(quota_release_work, quota_release_workfn); + static inline unsigned int hashfn(const struct super_block *sb, struct kqid qid) { @@ -305,12 +318,18 @@ static inline void put_dquot_last(struct dquot *dquot) dqstats_inc(DQST_FREE_DQUOTS); } +static inline void put_releasing_dquots(struct dquot *dquot) +{ + list_add_tail(&dquot->dq_free, &releasing_dquots); +} + static inline void remove_free_dquot(struct dquot *dquot) { if (list_empty(&dquot->dq_free)) return; list_del_init(&dquot->dq_free); - dqstats_dec(DQST_FREE_DQUOTS); + if (!atomic_read(&dquot->dq_count)) + dqstats_dec(DQST_FREE_DQUOTS); } static inline void put_inuse(struct dquot *dquot) @@ -552,6 +571,8 @@ static void invalidate_dquots(struct super_block *sb, int type) struct dquot *dquot, *tmp; restart: + flush_delayed_work("a_release_work); + spin_lock(&dq_list_lock); list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) { if (dquot->dq_sb != sb) @@ -560,6 +581,12 @@ restart: continue; /* Wait for dquot users */ if (atomic_read(&dquot->dq_count)) { + /* dquot in releasing_dquots, flush and retry */ + if (!list_empty(&dquot->dq_free)) { + spin_unlock(&dq_list_lock); + goto restart; + } + atomic_inc(&dquot->dq_count); spin_unlock(&dq_list_lock); /* @@ -770,6 +797,49 @@ static struct shrinker dqcache_shrinker = { .seeks = DEFAULT_SEEKS, }; +/* + * Safely release dquot and put reference to dquot. + */ +static void quota_release_workfn(struct work_struct *work) +{ + struct dquot *dquot; + struct list_head rls_head; + + spin_lock(&dq_list_lock); + /* Exchange the list head to avoid livelock. */ + list_replace_init(&releasing_dquots, &rls_head); + spin_unlock(&dq_list_lock); + +restart: + synchronize_srcu(&dquot_srcu); + spin_lock(&dq_list_lock); + while (!list_empty(&rls_head)) { + dquot = list_first_entry(&rls_head, struct dquot, dq_free); + /* Dquot got used again? */ + if (atomic_read(&dquot->dq_count) > 1) { + remove_free_dquot(dquot); + atomic_dec(&dquot->dq_count); + continue; + } + if (dquot_dirty(dquot)) { + spin_unlock(&dq_list_lock); + /* Commit dquot before releasing */ + dquot_write_dquot(dquot); + goto restart; + } + if (dquot_active(dquot)) { + spin_unlock(&dq_list_lock); + dquot->dq_sb->dq_op->release_dquot(dquot); + goto restart; + } + /* Dquot is inactive and clean, now move it to free list */ + remove_free_dquot(dquot); + atomic_dec(&dquot->dq_count); + put_dquot_last(dquot); + } + spin_unlock(&dq_list_lock); +} + /* * Put reference to dquot */ @@ -786,7 +856,7 @@ void dqput(struct dquot *dquot) } #endif dqstats_inc(DQST_DROPS); -we_slept: + spin_lock(&dq_list_lock); if (atomic_read(&dquot->dq_count) > 1) { /* We have more than one user... nothing to do */ @@ -798,25 +868,15 @@ we_slept: spin_unlock(&dq_list_lock); return; } + /* Need to release dquot? */ - if (dquot_dirty(dquot)) { - spin_unlock(&dq_list_lock); - /* Commit dquot before releasing */ - dquot_write_dquot(dquot); - goto we_slept; - } - if (dquot_active(dquot)) { - spin_unlock(&dq_list_lock); - dquot->dq_sb->dq_op->release_dquot(dquot); - goto we_slept; - } - atomic_dec(&dquot->dq_count); #ifdef CONFIG_QUOTA_DEBUG /* sanity check */ BUG_ON(!list_empty(&dquot->dq_free)); #endif - put_dquot_last(dquot); + put_releasing_dquots(dquot); spin_unlock(&dq_list_lock); + queue_delayed_work(system_unbound_wq, "a_release_work, 1); } EXPORT_SYMBOL(dqput); -- cgit v1.2.3 From 7bce48f0fec602b3b6c335963b26d9eefa417788 Mon Sep 17 00:00:00 2001 From: Baokun Li Date: Fri, 30 Jun 2023 19:08:22 +0800 Subject: quota: simplify drop_dquot_ref() As Honza said, remove_inode_dquot_ref() currently does not release the last dquot reference but instead adds the dquot to tofree_head list. This is because dqput() can sleep while dropping of the last dquot reference (writing back the dquot and calling ->release_dquot()) and that must not happen under dq_list_lock. Now that dqput() queues the final dquot cleanup into a workqueue, remove_inode_dquot_ref() can call dqput() unconditionally and we can significantly simplify it. Here we open code the simplified code of remove_inode_dquot_ref() into remove_dquot_ref() and remove the function put_dquot_list() which is no longer used. Signed-off-by: Baokun Li Signed-off-by: Jan Kara Message-Id: <20230630110822.3881712-6-libaokun1@huawei.com> --- fs/quota/dquot.c | 70 ++++++++------------------------------------------------ 1 file changed, 9 insertions(+), 61 deletions(-) (limited to 'fs') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index c7afe433d991..e8232242dd34 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1072,59 +1072,7 @@ out: return err; } -/* - * Remove references to dquots from inode and add dquot to list for freeing - * if we have the last reference to dquot - */ -static void remove_inode_dquot_ref(struct inode *inode, int type, - struct list_head *tofree_head) -{ - struct dquot **dquots = i_dquot(inode); - struct dquot *dquot = dquots[type]; - - if (!dquot) - return; - - dquots[type] = NULL; - if (list_empty(&dquot->dq_free)) { - /* - * The inode still has reference to dquot so it can't be in the - * free list - */ - spin_lock(&dq_list_lock); - list_add(&dquot->dq_free, tofree_head); - spin_unlock(&dq_list_lock); - } else { - /* - * Dquot is already in a list to put so we won't drop the last - * reference here. - */ - dqput(dquot); - } -} - -/* - * Free list of dquots - * Dquots are removed from inodes and no new references can be got so we are - * the only ones holding reference - */ -static void put_dquot_list(struct list_head *tofree_head) -{ - struct list_head *act_head; - struct dquot *dquot; - - act_head = tofree_head->next; - while (act_head != tofree_head) { - dquot = list_entry(act_head, struct dquot, dq_free); - act_head = act_head->next; - /* Remove dquot from the list so we won't have problems... */ - list_del_init(&dquot->dq_free); - dqput(dquot); - } -} - -static void remove_dquot_ref(struct super_block *sb, int type, - struct list_head *tofree_head) +static void remove_dquot_ref(struct super_block *sb, int type) { struct inode *inode; #ifdef CONFIG_QUOTA_DEBUG @@ -1141,11 +1089,16 @@ static void remove_dquot_ref(struct super_block *sb, int type, */ spin_lock(&dq_data_lock); if (!IS_NOQUOTA(inode)) { + struct dquot **dquots = i_dquot(inode); + struct dquot *dquot = dquots[type]; + #ifdef CONFIG_QUOTA_DEBUG if (unlikely(inode_get_rsv_space(inode) > 0)) reserved = 1; #endif - remove_inode_dquot_ref(inode, type, tofree_head); + dquots[type] = NULL; + if (dquot) + dqput(dquot); } spin_unlock(&dq_data_lock); } @@ -1162,13 +1115,8 @@ static void remove_dquot_ref(struct super_block *sb, int type, /* Gather all references from inodes and drop them */ static void drop_dquot_ref(struct super_block *sb, int type) { - LIST_HEAD(tofree_head); - - if (sb->dq_op) { - remove_dquot_ref(sb, type, &tofree_head); - synchronize_srcu(&dquot_srcu); - put_dquot_list(&tofree_head); - } + if (sb->dq_op) + remove_dquot_ref(sb, type); } static inline -- cgit v1.2.3 From ca97f7e541d78e43599388bc70d99609156150a3 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 12 Jul 2023 12:25:45 -0600 Subject: udf: Fix -Wstringop-overflow warnings Use unsigned type in call to macro mint_t(). This avoids confusing the compiler about possible negative values that would cause the value in _len_ to wrap around. Fixes the following -Wstringop-warnings seen when building ARM architecture with allyesconfig (GCC 13): fs/udf/directory.c: In function 'udf_copy_fi': include/linux/fortify-string.h:57:33: warning: '__builtin_memcpy' specified bound between 2147483648 and 4294967295 exceeds maximum object size 2147483647 [-Wstringop-overflow=] 57 | #define __underlying_memcpy __builtin_memcpy | ^ include/linux/fortify-string.h:648:9: note: in expansion of macro '__underlying_memcpy' 648 | __underlying_##op(p, q, __fortify_size); \ | ^~~~~~~~~~~~~ include/linux/fortify-string.h:693:26: note: in expansion of macro '__fortify_memcpy_chk' 693 | #define memcpy(p, q, s) __fortify_memcpy_chk(p, q, s, \ | ^~~~~~~~~~~~~~~~~~~~ fs/udf/directory.c:99:9: note: in expansion of macro 'memcpy' 99 | memcpy(&iter->fi, iter->bh[0]->b_data + off, len); | ^~~~~~ include/linux/fortify-string.h:57:33: warning: '__builtin_memcpy' specified bound between 2147483648 and 4294967295 exceeds maximum object size 2147483647 [-Wstringop-overflow=] 57 | #define __underlying_memcpy __builtin_memcpy | ^ include/linux/fortify-string.h:648:9: note: in expansion of macro '__underlying_memcpy' 648 | __underlying_##op(p, q, __fortify_size); \ | ^~~~~~~~~~~~~ include/linux/fortify-string.h:693:26: note: in expansion of macro '__fortify_memcpy_chk' 693 | #define memcpy(p, q, s) __fortify_memcpy_chk(p, q, s, \ | ^~~~~~~~~~~~~~~~~~~~ fs/udf/directory.c:99:9: note: in expansion of macro 'memcpy' 99 | memcpy(&iter->fi, iter->bh[0]->b_data + off, len); | ^~~~~~ AR fs/udf/built-in.a This helps with the ongoing efforts to globally enable -Wstringop-overflow. Link: https://github.com/KSPP/linux/issues/329 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Jan Kara Message-Id: --- fs/udf/directory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/udf/directory.c b/fs/udf/directory.c index 1c775e072b2f..93153665eb37 100644 --- a/fs/udf/directory.c +++ b/fs/udf/directory.c @@ -95,7 +95,7 @@ static int udf_copy_fi(struct udf_fileident_iter *iter) } off = iter->pos & (blksize - 1); - len = min_t(int, sizeof(struct fileIdentDesc), blksize - off); + len = min_t(u32, sizeof(struct fileIdentDesc), blksize - off); memcpy(&iter->fi, iter->bh[0]->b_data + off, len); if (len < sizeof(struct fileIdentDesc)) memcpy((char *)(&iter->fi) + len, iter->bh[1]->b_data, -- cgit v1.2.3 From 7a64774add85ce673a089810fae193b02003be24 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 2 Aug 2023 13:54:39 +0200 Subject: quota: use lockdep_assert_held_write in dquot_load_quota_sb Use lockdep_assert_held_write to assert and self-document the locking state in dquot_load_quota_sb instead of hand-crafting it with a trylock. Signed-off-by: Christoph Hellwig Signed-off-by: Jan Kara Message-Id: <20230802115439.2145212-2-hch@lst.de> --- fs/quota/dquot.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index e8232242dd34..303632f7948f 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2365,11 +2365,10 @@ int dquot_load_quota_sb(struct super_block *sb, int type, int format_id, struct quota_info *dqopt = sb_dqopt(sb); int error; + lockdep_assert_held_write(&sb->s_umount); + /* Just unsuspend quotas? */ BUG_ON(flags & DQUOT_SUSPENDED); - /* s_umount should be held in exclusive mode */ - if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount))) - up_read(&sb->s_umount); if (!fmt) return -ESRCH; -- cgit v1.2.3 From 5ae6ca2cc1ca508548446fe1325f4690145df153 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 24 Mar 2023 11:37:44 +0100 Subject: udf: Drop pointless aops assignment Since we have merged normal and in-ICB address_space operations, there's no need to assign aops when expanding from in-ICB format. Signed-off-by: Jan Kara --- fs/udf/inode.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs') diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 28cdfc57d946..165fc003afbb 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -352,8 +352,6 @@ int udf_expand_file_adinicb(struct inode *inode) iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT; else iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; - /* from now on we have normal address_space methods */ - inode->i_data.a_ops = &udf_aops; up_write(&iinfo->i_data_sem); mark_inode_dirty(inode); return 0; -- cgit v1.2.3 From e88076348425b7d0491c8c98d8732a7df8de7aa3 Mon Sep 17 00:00:00 2001 From: Georg Ottinger Date: Tue, 15 Aug 2023 12:03:40 +0200 Subject: ext2: fix datatype of block number in ext2_xattr_set2() I run a small server that uses external hard drives for backups. The backup software I use uses ext2 filesystems with 4KiB block size and the server is running SELinux and therefore relies on xattr. I recently upgraded the hard drives from 4TB to 12TB models. I noticed that after transferring some TBs I got a filesystem error "Freeing blocks not in datazone - block = 18446744071529317386, count = 1" and the backup process stopped. Trying to fix the fs with e2fsck resulted in a completely corrupted fs. The error probably came from ext2_free_blocks(), and because of the large number 18e19 this problem immediately looked like some kind of integer overflow. Whereas the 4TB fs was about 1e9 blocks, the new 12TB is about 3e9 blocks. So, searching the ext2 code, I came across the line in fs/ext2/xattr.c:745 where ext2_new_block() is called and the resulting block number is stored in the variable block as an int datatype. If a block with a block number greater than INT32_MAX is returned, this variable overflows and the call to sb_getblk() at line fs/ext2/xattr.c:750 fails, then the call to ext2_free_blocks() produces the error. Signed-off-by: Georg Ottinger Signed-off-by: Jan Kara Message-Id: <20230815100340.22121-1-g.ottinger@gmx.at> --- fs/ext2/xattr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 8906ba479aaf..89517937d36c 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -742,10 +742,10 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, /* We need to allocate a new block */ ext2_fsblk_t goal = ext2_group_first_block_no(sb, EXT2_I(inode)->i_block_group); - int block = ext2_new_block(inode, goal, &error); + ext2_fsblk_t block = ext2_new_block(inode, goal, &error); if (error) goto cleanup; - ea_idebug(inode, "creating block %d", block); + ea_idebug(inode, "creating block %lu", block); new_bh = sb_getblk(sb, block); if (unlikely(!new_bh)) { -- cgit v1.2.3 From 2445a8a1922b46c56189203f7fdc1e1cb2a47cdd Mon Sep 17 00:00:00 2001 From: Ye Bin Date: Tue, 15 Aug 2023 19:26:09 +0800 Subject: ext2: remove ext2_new_block() Now, only xattr allocate block use ext2_new_block(), so just opencode it in the xattr code. Signed-off-by: Ye Bin Message-Id: <20230815112612.221145-2-yebin10@huawei.com> Signed-off-by: Jan Kara --- fs/ext2/balloc.c | 7 ------- fs/ext2/ext2.h | 1 - fs/ext2/xattr.c | 4 +++- 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index eca60b747c6b..ddaa823fc7f5 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -1431,13 +1431,6 @@ out: return 0; } -ext2_fsblk_t ext2_new_block(struct inode *inode, unsigned long goal, int *errp) -{ - unsigned long count = 1; - - return ext2_new_blocks(inode, goal, &count, errp); -} - #ifdef EXT2FS_DEBUG unsigned long ext2_count_free(struct buffer_head *map, unsigned int numchars) diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 35a041c47c38..954fb82ab22c 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -695,7 +695,6 @@ static inline struct ext2_inode_info *EXT2_I(struct inode *inode) /* balloc.c */ extern int ext2_bg_has_super(struct super_block *sb, int group); extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group); -extern ext2_fsblk_t ext2_new_block(struct inode *, unsigned long, int *); extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long, unsigned long *, int *); extern int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk, diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 89517937d36c..06933eabac26 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -742,7 +742,9 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, /* We need to allocate a new block */ ext2_fsblk_t goal = ext2_group_first_block_no(sb, EXT2_I(inode)->i_block_group); - ext2_fsblk_t block = ext2_new_block(inode, goal, &error); + unsigned long count = 1; + ext2_fsblk_t block = ext2_new_blocks(inode, goal, + &count, &error); if (error) goto cleanup; ea_idebug(inode, "creating block %lu", block); -- cgit v1.2.3 From b450159d0903b06ebea121a010ab9c424b67c408 Mon Sep 17 00:00:00 2001 From: Ye Bin Date: Tue, 15 Aug 2023 19:26:10 +0800 Subject: ext2: introduce new flags argument for ext2_new_blocks() This patch introduces a new flags argument for ext2_new_blocks() and also a new EXT2_ALLOC_NORESERVE flag. Signed-off-by: Ye Bin Message-Id: <20230815112612.221145-3-yebin10@huawei.com> Signed-off-by: Jan Kara --- fs/ext2/balloc.c | 3 ++- fs/ext2/ext2.h | 8 +++++++- fs/ext2/inode.c | 2 +- fs/ext2/xattr.c | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index ddaa823fc7f5..a9648c3137db 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -1195,6 +1195,7 @@ int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk, * @goal: given target block(filesystem wide) * @count: target number of blocks to allocate * @errp: error code + * @flags: allocate flags * * ext2_new_blocks uses a goal block to assist allocation. If the goal is * free, or there is a free block within 32 blocks of the goal, that block @@ -1204,7 +1205,7 @@ int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk, * This function also updates quota and i_blocks field. */ ext2_fsblk_t ext2_new_blocks(struct inode *inode, ext2_fsblk_t goal, - unsigned long *count, int *errp) + unsigned long *count, int *errp, unsigned int flags) { struct buffer_head *bitmap_bh = NULL; struct buffer_head *gdp_bh; diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 954fb82ab22c..26dbe579768c 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -398,6 +398,12 @@ struct ext2_inode { #define EXT2_ERRORS_PANIC 3 /* Panic */ #define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE +/* + * Allocation flags + */ +#define EXT2_ALLOC_NORESERVE 0x1 /* Do not use reservation + * window for allocation */ + /* * Structure of the super block */ @@ -696,7 +702,7 @@ static inline struct ext2_inode_info *EXT2_I(struct inode *inode) extern int ext2_bg_has_super(struct super_block *sb, int group); extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group); extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long, - unsigned long *, int *); + unsigned long *, int *, unsigned int); extern int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk, unsigned int count); extern void ext2_free_blocks (struct inode *, unsigned long, diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 75983215c7a1..39c958f7e028 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -415,7 +415,7 @@ static int ext2_alloc_blocks(struct inode *inode, while (1) { count = target; /* allocating blocks for indirect blocks and direct blocks */ - current_block = ext2_new_blocks(inode,goal,&count,err); + current_block = ext2_new_blocks(inode, goal, &count, err, 0); if (*err) goto failed_out; diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 06933eabac26..3b4ff6974ab8 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -744,7 +744,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, EXT2_I(inode)->i_block_group); unsigned long count = 1; ext2_fsblk_t block = ext2_new_blocks(inode, goal, - &count, &error); + &count, &error, 0); if (error) goto cleanup; ea_idebug(inode, "creating block %lu", block); -- cgit v1.2.3 From 83f99de1b7c0dcfa42b211e5e40334b7ad786b36 Mon Sep 17 00:00:00 2001 From: Ye Bin Date: Tue, 15 Aug 2023 19:26:11 +0800 Subject: ext2: fix race between setxattr and write back There's an issue when allocating xattrs as follows: Block Allocation Reservation Windows Map (ext2_try_to_allocate_with_rsv): reservation window 0x000000006f105382 start: 0, end: 0 reservation window 0x000000008fd1a555 start: 1044, end: 1059 Window map complete. kernel BUG at fs/ext2/balloc.c:1158! invalid opcode: 0000 [#1] PREEMPT SMP KASAN RIP: 0010:ext2_try_to_allocate_with_rsv.isra.0+0x15c4/0x1800 Call Trace: ext2_new_blocks+0x935/0x1690 ext2_new_block+0x73/0xa0 ext2_xattr_set2+0x74f/0x1730 ext2_xattr_set+0x12b6/0x2260 ext2_xattr_user_set+0x9c/0x110 __vfs_setxattr+0x139/0x1d0 __vfs_setxattr_noperm+0xfc/0x370 __vfs_setxattr_locked+0x205/0x2c0 vfs_setxattr+0x19d/0x3b0 do_setxattr+0xff/0x220 setxattr+0x123/0x150 path_setxattr+0x193/0x1e0 __x64_sys_setxattr+0xc8/0x170 do_syscall_64+0x35/0x80 entry_SYSCALL_64_after_hwframe+0x63/0xcd Above issue may happens as follows: setxattr write back ext2_xattr_set ext2_xattr_set2 ext2_new_block ext2_new_blocks ext2_try_to_allocate_with_rsv alloc_new_reservation --> group=0 [0, 1023] rsv [1016, 1023] do_writepages mpage_writepages write_cache_pages __mpage_writepage ext2_get_block ext2_get_blocks ext2_alloc_branch ext2_new_blocks ext2_try_to_allocate_with_rsv alloc_new_reservation -->group=1 [1024, 2047] rsv [1044, 1059] if ((my_rsv->rsv_start > group_last_block) || (my_rsv->rsv_end < group_first_block) rsv_window_dump BUG(); Now ext2 mkwrite doesn't allocate new blocks so for these cases we may be allocating blocks during writeback. However, there is no protection between ext2_xattr_set() and do_writepages() so these two functions can conflict on handling the reservation window. To solve about issue don't use the reservation window when allocating block for xattr. Signed-off-by: Ye Bin Message-Id: <20230815112612.221145-4-yebin10@huawei.com> Signed-off-by: Jan Kara --- fs/ext2/balloc.c | 14 +++++++------- fs/ext2/xattr.c | 3 ++- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index a9648c3137db..dd74c77a4ac1 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -1244,15 +1244,15 @@ ext2_fsblk_t ext2_new_blocks(struct inode *inode, ext2_fsblk_t goal, es = EXT2_SB(sb)->s_es; ext2_debug("goal=%lu.\n", goal); /* - * Allocate a block from reservation only when - * filesystem is mounted with reservation(default,-o reservation), and - * it's a regular file, and - * the desired window size is greater than 0 (One could use ioctl - * command EXT2_IOC_SETRSVSZ to set the window size to 0 to turn off - * reservation on that particular file) + * Allocate a block from reservation only when the filesystem is + * mounted with reservation(default,-o reservation), and it's a regular + * file, and the desired window size is greater than 0 (One could use + * ioctl command EXT2_IOC_SETRSVSZ to set the window size to 0 to turn + * off reservation on that particular file). Also do not use the + * reservation window if the caller asked us not to do it. */ block_i = EXT2_I(inode)->i_block_alloc_info; - if (block_i) { + if (!(flags & EXT2_ALLOC_NORESERVE) && block_i) { windowsz = block_i->rsv_window_node.rsv_goal_size; if (windowsz > 0) my_rsv = &block_i->rsv_window_node; diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 3b4ff6974ab8..b7c64fc1c008 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -744,7 +744,8 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, EXT2_I(inode)->i_block_group); unsigned long count = 1; ext2_fsblk_t block = ext2_new_blocks(inode, goal, - &count, &error, 0); + &count, &error, + EXT2_ALLOC_NORESERVE); if (error) goto cleanup; ea_idebug(inode, "creating block %lu", block); -- cgit v1.2.3 From 9bc6fc3304d89f19c028cb4a8d6af94f9e5faeb0 Mon Sep 17 00:00:00 2001 From: Ye Bin Date: Tue, 15 Aug 2023 19:26:12 +0800 Subject: ext2: dump current reservation window info There's report BUG in 'ext2_try_to_allocate_with_rsv()', although there's now dump of all reservation windows information. But there's unknown which window is being processed.So this is not helpful for locating the issue. To better analyze the problem, dump the information about reservation window that is being processed. And just bail with error instead of BUG here. Signed-off-by: Ye Bin Message-Id: <20230815112612.221145-5-yebin10@huawei.com> Signed-off-by: Jan Kara --- fs/ext2/balloc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index dd74c77a4ac1..424b277f31ff 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -1133,8 +1133,13 @@ ext2_try_to_allocate_with_rsv(struct super_block *sb, unsigned int group, if ((my_rsv->rsv_start > group_last_block) || (my_rsv->rsv_end < group_first_block)) { + ext2_error(sb, __func__, + "Reservation out of group %u range goal %d fsb[%lu,%lu] rsv[%lu, %lu]", + group, grp_goal, group_first_block, + group_last_block, my_rsv->rsv_start, + my_rsv->rsv_end); rsv_window_dump(&EXT2_SB(sb)->s_rsv_window_root, 1); - BUG(); + return -1; } ret = ext2_try_to_allocate(sb, group, bitmap_bh, grp_goal, &num, &my_rsv->rsv_window); -- cgit v1.2.3 From 2ebc736c8452f8ccf86f5398e8d8ceec283aa50d Mon Sep 17 00:00:00 2001 From: Georg Ottinger Date: Thu, 17 Aug 2023 21:59:25 +0200 Subject: ext2: improve consistency of ext2_fsblk_t datatype usage The ext2 block allocation/deallocation functions and their respective calls use a mixture of unsigned long and ext2_fsblk_t datatypes to index the desired ext2 block. This commit replaces occurrences of unsigned long with ext2_fsblk_t, covering the functions ext2_new_block(), ext2_new_blocks(), ext2_free_blocks(), ext2_free_data() and ext2_free_branches(). This commit is rather conservative, and only replaces unsigned long with ext2_fsblk_t if the variable is used to index a specific ext2 block. Signed-off-by: Georg Ottinger Signed-off-by: Jan Kara Message-Id: <20230817195925.10268-1-g.ottinger@gmx.at> --- fs/ext2/balloc.c | 4 ++-- fs/ext2/ext2.h | 5 ++--- fs/ext2/inode.c | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 424b277f31ff..2f48d25170f7 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -474,8 +474,8 @@ void ext2_discard_reservation(struct inode *inode) * @block: start physical block to free * @count: number of blocks to free */ -void ext2_free_blocks (struct inode * inode, unsigned long block, - unsigned long count) +void ext2_free_blocks(struct inode * inode, ext2_fsblk_t block, + unsigned long count) { struct buffer_head *bitmap_bh = NULL; struct buffer_head * bh2; diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 26dbe579768c..7fdd685c384d 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -701,12 +701,11 @@ static inline struct ext2_inode_info *EXT2_I(struct inode *inode) /* balloc.c */ extern int ext2_bg_has_super(struct super_block *sb, int group); extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group); -extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long, +extern ext2_fsblk_t ext2_new_blocks(struct inode *, ext2_fsblk_t, unsigned long *, int *, unsigned int); extern int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk, unsigned int count); -extern void ext2_free_blocks (struct inode *, unsigned long, - unsigned long); +extern void ext2_free_blocks(struct inode *, ext2_fsblk_t, unsigned long); extern unsigned long ext2_count_free_blocks (struct super_block *); extern unsigned long ext2_count_dirs (struct super_block *); extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 39c958f7e028..8ba631154243 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1082,8 +1082,8 @@ no_top: */ static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q) { - unsigned long block_to_free = 0, count = 0; - unsigned long nr; + ext2_fsblk_t block_to_free = 0, count = 0; + ext2_fsblk_t nr; for ( ; p < q ; p++) { nr = le32_to_cpu(*p); @@ -1123,7 +1123,7 @@ static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q) static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int depth) { struct buffer_head * bh; - unsigned long nr; + ext2_fsblk_t nr; if (depth--) { int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); -- cgit v1.2.3 From df1ae36a4a0e92340daea12e88d43eeb2eb013b1 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Fri, 18 Aug 2023 21:11:21 +0100 Subject: ext2: Fix kernel-doc warnings Document a few parameters of ext2_alloc_blocks(). Redo the alloc_new_reservation() and find_next_reservable_window() kernel-doc entirely. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Jan Kara Message-Id: <20230818201121.2720451-1-willy@infradead.org> --- fs/ext2/balloc.c | 101 +++++++++++++++++++++++++------------------------------ fs/ext2/inode.c | 16 +++++---- 2 files changed, 56 insertions(+), 61 deletions(-) (limited to 'fs') diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 2f48d25170f7..27377a0faa6f 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -718,36 +718,34 @@ fail_access: } /** - * find_next_reservable_window(): - * find a reservable space within the given range. - * It does not allocate the reservation window for now: - * alloc_new_reservation() will do the work later. + * find_next_reservable_window - Find a reservable space within the given range. + * @search_head: The list to search. + * @my_rsv: The reservation we're currently using. + * @sb: The super block. + * @start_block: The first block we consider to start the real search from + * @last_block: The maximum block number that our goal reservable space + * could start from. * - * @search_head: the head of the searching list; - * This is not necessarily the list head of the whole filesystem + * It does not allocate the reservation window: alloc_new_reservation() + * will do the work later. * - * We have both head and start_block to assist the search - * for the reservable space. The list starts from head, - * but we will shift to the place where start_block is, - * then start from there, when looking for a reservable space. + * We search the given range, rather than the whole reservation double + * linked list, (start_block, last_block) to find a free region that is + * of my size and has not been reserved. * - * @sb: the super block. + * @search_head is not necessarily the list head of the whole filesystem. + * We have both head and @start_block to assist the search for the + * reservable space. The list starts from head, but we will shift to + * the place where start_block is, then start from there, when looking + * for a reservable space. * - * @start_block: the first block we consider to start the real search from - * - * @last_block: - * the maximum block number that our goal reservable space - * could start from. This is normally the last block in this - * group. The search will end when we found the start of next - * possible reservable space is out of this boundary. - * This could handle the cross boundary reservation window - * request. - * - * basically we search from the given range, rather than the whole - * reservation double linked list, (start_block, last_block) - * to find a free region that is of my size and has not - * been reserved. + * @last_block is normally the last block in this group. The search will end + * when we found the start of next possible reservable space is out + * of this boundary. This could handle the cross boundary reservation + * window request. * + * Return: -1 if we could not find a range of sufficient size. If we could, + * return 0 and fill in @my_rsv with the range information. */ static int find_next_reservable_window( struct ext2_reserve_window_node *search_head, @@ -835,41 +833,34 @@ static int find_next_reservable_window( } /** - * alloc_new_reservation()--allocate a new reservation window - * - * To make a new reservation, we search part of the filesystem - * reservation list (the list that inside the group). We try to - * allocate a new reservation window near the allocation goal, - * or the beginning of the group, if there is no goal. - * - * We first find a reservable space after the goal, then from - * there, we check the bitmap for the first free block after - * it. If there is no free block until the end of group, then the - * whole group is full, we failed. Otherwise, check if the free - * block is inside the expected reservable space, if so, we - * succeed. - * If the first free block is outside the reservable space, then - * start from the first free block, we search for next available - * space, and go on. - * - * on succeed, a new reservation will be found and inserted into the list - * It contains at least one free block, and it does not overlap with other - * reservation windows. + * alloc_new_reservation - Allocate a new reservation window. + * @my_rsv: The reservation we're currently using. + * @grp_goal: The goal block relative to the start of the group. + * @sb: The super block. + * @group: The group we are trying to allocate in. + * @bitmap_bh: The block group block bitmap. * - * failed: we failed to find a reservation window in this group + * To make a new reservation, we search part of the filesystem reservation + * list (the list inside the group). We try to allocate a new + * reservation window near @grp_goal, or the beginning of the + * group, if @grp_goal is negative. * - * @my_rsv: the reservation + * We first find a reservable space after the goal, then from there, + * we check the bitmap for the first free block after it. If there is + * no free block until the end of group, then the whole group is full, + * we failed. Otherwise, check if the free block is inside the expected + * reservable space, if so, we succeed. * - * @grp_goal: The goal (group-relative). It is where the search for a - * free reservable space should start from. - * if we have a goal(goal >0 ), then start from there, - * no goal(goal = -1), we start from the first block - * of the group. + * If the first free block is outside the reservable space, then start + * from the first free block, we search for next available space, and + * go on. * - * @sb: the super block - * @group: the group we are trying to allocate in - * @bitmap_bh: the block group block bitmap + * on succeed, a new reservation will be found and inserted into the + * list. It contains at least one free block, and it does not overlap + * with other reservation windows. * + * Return: 0 on success, -1 if we failed to find a reservation window + * in this group */ static int alloc_new_reservation(struct ext2_reserve_window_node *my_rsv, ext2_grpblk_t grp_goal, struct super_block *sb, diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 8ba631154243..c7b04c15dd5b 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -385,12 +385,16 @@ ext2_blks_to_allocate(Indirect * branch, int k, unsigned long blks, } /** - * ext2_alloc_blocks: multiple allocate blocks needed for a branch - * @indirect_blks: the number of blocks need to allocate for indirect - * blocks - * @blks: the number of blocks need to allocate for direct blocks - * @new_blocks: on return it will store the new block numbers for - * the indirect blocks(if needed) and the first direct block, + * ext2_alloc_blocks: Allocate multiple blocks needed for a branch. + * @inode: Owner. + * @goal: Preferred place for allocation. + * @indirect_blks: The number of blocks needed to allocate for indirect blocks. + * @blks: The number of blocks need to allocate for direct blocks. + * @new_blocks: On return it will store the new block numbers for + * the indirect blocks(if needed) and the first direct block. + * @err: Error pointer. + * + * Return: Number of blocks allocated. */ static int ext2_alloc_blocks(struct inode *inode, ext2_fsblk_t goal, int indirect_blks, int blks, -- cgit v1.2.3