]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
bcachefs: Fix excess transaction restarts in __bchfs_fallocate()
authorKent Overstreet <kent.overstreet@linux.dev>
Tue, 16 Jan 2024 01:37:23 +0000 (20:37 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 21 Jan 2024 18:27:10 +0000 (13:27 -0500)
drop_locks_do() should not be used in a fastpath without first trying
the do in nonblocking mode - the unlock and relock will cause excessive
transaction restarts and potentially livelocking with other threads that
are contending for the same locks.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/btree_iter.h
fs/bcachefs/fs-io-pagecache.c
fs/bcachefs/fs-io-pagecache.h
fs/bcachefs/fs-io.c

index da2b74fa63fcece86d7d92d18dc340330180c657..24772538e4cc74ada59851bd7847dd5ece5ea122 100644 (file)
@@ -819,6 +819,11 @@ __bch2_btree_iter_peek_and_restart(struct btree_trans *trans,
 #define for_each_btree_key_continue_norestart(_iter, _flags, _k, _ret) \
        for_each_btree_key_upto_continue_norestart(_iter, SPOS_MAX, _flags, _k, _ret)
 
+/*
+ * This should not be used in a fastpath, without first trying _do in
+ * nonblocking mode - it will cause excessive transaction restarts and
+ * potentially livelocking:
+ */
 #define drop_locks_do(_trans, _do)                                     \
 ({                                                                     \
        bch2_trans_unlock(_trans);                                      \
index ff664fd0d8ef80e8b4816d7c430e87d41759b498..d359aa9b33b828342bd466b899713f401d939b30 100644 (file)
@@ -309,39 +309,49 @@ void bch2_mark_pagecache_unallocated(struct bch_inode_info *inode,
        }
 }
 
-void bch2_mark_pagecache_reserved(struct bch_inode_info *inode,
-                                 u64 start, u64 end)
+int bch2_mark_pagecache_reserved(struct bch_inode_info *inode,
+                                u64 *start, u64 end,
+                                bool nonblocking)
 {
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
-       pgoff_t index = start >> PAGE_SECTORS_SHIFT;
+       pgoff_t index = *start >> PAGE_SECTORS_SHIFT;
        pgoff_t end_index = (end - 1) >> PAGE_SECTORS_SHIFT;
        struct folio_batch fbatch;
        s64 i_sectors_delta = 0;
-       unsigned i, j;
+       int ret = 0;
 
-       if (end <= start)
-               return;
+       if (end <= *start)
+               return 0;
 
        folio_batch_init(&fbatch);
 
        while (filemap_get_folios(inode->v.i_mapping,
                                  &index, end_index, &fbatch)) {
-               for (i = 0; i < folio_batch_count(&fbatch); i++) {
+               for (unsigned i = 0; i < folio_batch_count(&fbatch); i++) {
                        struct folio *folio = fbatch.folios[i];
+
+                       if (!nonblocking)
+                               folio_lock(folio);
+                       else if (!folio_trylock(folio)) {
+                               folio_batch_release(&fbatch);
+                               ret = -EAGAIN;
+                               break;
+                       }
+
                        u64 folio_start = folio_sector(folio);
                        u64 folio_end = folio_end_sector(folio);
-                       unsigned folio_offset = max(start, folio_start) - folio_start;
-                       unsigned folio_len = min(end, folio_end) - folio_offset - folio_start;
-                       struct bch_folio *s;
 
                        BUG_ON(end <= folio_start);
 
-                       folio_lock(folio);
-                       s = bch2_folio(folio);
+                       *start = min(end, folio_end);
 
+                       struct bch_folio *s = bch2_folio(folio);
                        if (s) {
+                               unsigned folio_offset = max(*start, folio_start) - folio_start;
+                               unsigned folio_len = min(end, folio_end) - folio_offset - folio_start;
+
                                spin_lock(&s->lock);
-                               for (j = folio_offset; j < folio_offset + folio_len; j++) {
+                               for (unsigned j = folio_offset; j < folio_offset + folio_len; j++) {
                                        i_sectors_delta -= s->s[j].state == SECTOR_dirty;
                                        bch2_folio_sector_set(folio, s, j,
                                                folio_sector_reserve(s->s[j].state));
@@ -356,6 +366,7 @@ void bch2_mark_pagecache_reserved(struct bch_inode_info *inode,
        }
 
        bch2_i_sectors_acct(c, inode, NULL, i_sectors_delta);
+       return ret;
 }
 
 static inline unsigned sectors_to_reserve(struct bch_folio_sector *s,
index 27f712ae37a68209275cc3b2955a542314e80e68..8cbaba6565b4493695d679fe41553c197468c752 100644 (file)
@@ -143,7 +143,7 @@ int bch2_folio_set(struct bch_fs *, subvol_inum, struct folio **, unsigned);
 void bch2_bio_page_state_set(struct bio *, struct bkey_s_c);
 
 void bch2_mark_pagecache_unallocated(struct bch_inode_info *, u64, u64);
-void bch2_mark_pagecache_reserved(struct bch_inode_info *, u64, u64);
+int bch2_mark_pagecache_reserved(struct bch_inode_info *, u64 *, u64, bool);
 
 int bch2_get_folio_disk_reservation(struct bch_fs *,
                                struct bch_inode_info *,
index 98bd5babab193bec842dce20b0783e6c958ac5bf..dc52918d06ef3f91c30484822a5a170b08543f9c 100644 (file)
@@ -675,8 +675,11 @@ static int __bchfs_fallocate(struct bch_inode_info *inode, int mode,
 
                bch2_i_sectors_acct(c, inode, &quota_res, i_sectors_delta);
 
-               drop_locks_do(trans,
-                       (bch2_mark_pagecache_reserved(inode, hole_start, iter.pos.offset), 0));
+               if (bch2_mark_pagecache_reserved(inode, &hole_start,
+                                                iter.pos.offset, true))
+                       drop_locks_do(trans,
+                               bch2_mark_pagecache_reserved(inode, &hole_start,
+                                                            iter.pos.offset, false));
 bkey_err:
                bch2_quota_reservation_put(c, inode, &quota_res);
                if (bch2_err_matches(ret, BCH_ERR_transaction_restart))