]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/fs-io.c
New upstream release
[bcachefs-tools-debian] / libbcachefs / fs-io.c
index 1d0871f63e4e71402874373a982262d74b00c24c..706180b97a7711138bfb9af59952c748fad6c7a3 100644 (file)
 #include <trace/events/bcachefs.h>
 #include <trace/events/writeback.h>
 
+static inline bool bio_full(struct bio *bio, unsigned len)
+{
+       if (bio->bi_vcnt >= bio->bi_max_vecs)
+               return true;
+       if (bio->bi_iter.bi_size > UINT_MAX - len)
+               return true;
+       return false;
+}
+
 static inline struct address_space *faults_disabled_mapping(void)
 {
        return (void *) (((unsigned long) current->faults_disabled_mapping) & ~1UL);
@@ -142,7 +151,7 @@ static void bch2_quota_reservation_put(struct bch_fs *c,
 static int bch2_quota_reservation_add(struct bch_fs *c,
                                      struct bch_inode_info *inode,
                                      struct quota_res *res,
-                                     unsigned sectors,
+                                     u64 sectors,
                                      bool check_enospc)
 {
        int ret;
@@ -223,7 +232,10 @@ static void i_sectors_acct(struct bch_fs *c, struct bch_inode_info *inode,
                return;
 
        mutex_lock(&inode->ei_quota_lock);
-       BUG_ON((s64) inode->v.i_blocks + sectors < 0);
+       bch2_fs_inconsistent_on((s64) inode->v.i_blocks + sectors < 0, c,
+                               "inode %lu i_blocks underflow: %llu + %lli < 0 (ondisk %lli)",
+                               inode->v.i_ino, (u64) inode->v.i_blocks, sectors,
+                               inode->ei_inode.bi_sectors);
        inode->v.i_blocks += sectors;
 
 #ifdef CONFIG_BCACHEFS_QUOTA
@@ -397,7 +409,7 @@ retry:
        offset = iter.pos.offset;
        bch2_trans_iter_exit(&trans, &iter);
 err:
-       if (ret == -EINTR)
+       if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
                goto retry;
        bch2_trans_exit(&trans);
 
@@ -422,22 +434,20 @@ static void mark_pagecache_unallocated(struct bch_inode_info *inode,
 {
        pgoff_t index = start >> PAGE_SECTORS_SHIFT;
        pgoff_t end_index = (end - 1) >> PAGE_SECTORS_SHIFT;
-       struct pagevec pvec;
+       struct folio_batch fbatch;
+       unsigned i, j;
 
        if (end <= start)
                return;
 
-       pagevec_init(&pvec);
+       folio_batch_init(&fbatch);
 
-       do {
-               unsigned nr_pages, i, j;
-
-               nr_pages = pagevec_lookup_range(&pvec, inode->v.i_mapping,
-                                               &index, end_index);
-               for (i = 0; i < nr_pages; i++) {
-                       struct page *page = pvec.pages[i];
-                       u64 pg_start = page->index << PAGE_SECTORS_SHIFT;
-                       u64 pg_end = (page->index + 1) << PAGE_SECTORS_SHIFT;
+       while (filemap_get_folios(inode->v.i_mapping,
+                                 &index, end_index, &fbatch)) {
+               for (i = 0; i < folio_batch_count(&fbatch); i++) {
+                       struct folio *folio = fbatch.folios[i];
+                       u64 pg_start = folio->index << PAGE_SECTORS_SHIFT;
+                       u64 pg_end = (folio->index + 1) << PAGE_SECTORS_SHIFT;
                        unsigned pg_offset = max(start, pg_start) - pg_start;
                        unsigned pg_len = min(end, pg_end) - pg_offset - pg_start;
                        struct bch_page_state *s;
@@ -446,8 +456,8 @@ static void mark_pagecache_unallocated(struct bch_inode_info *inode,
                        BUG_ON(pg_offset >= PAGE_SECTORS);
                        BUG_ON(pg_offset + pg_len > PAGE_SECTORS);
 
-                       lock_page(page);
-                       s = bch2_page_state(page);
+                       folio_lock(folio);
+                       s = bch2_page_state(&folio->page);
 
                        if (s) {
                                spin_lock(&s->lock);
@@ -456,10 +466,11 @@ static void mark_pagecache_unallocated(struct bch_inode_info *inode,
                                spin_unlock(&s->lock);
                        }
 
-                       unlock_page(page);
+                       folio_unlock(folio);
                }
-               pagevec_release(&pvec);
-       } while (index <= end_index);
+               folio_batch_release(&fbatch);
+               cond_resched();
+       }
 }
 
 static void mark_pagecache_reserved(struct bch_inode_info *inode,
@@ -468,23 +479,21 @@ static void mark_pagecache_reserved(struct bch_inode_info *inode,
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
        pgoff_t index = start >> PAGE_SECTORS_SHIFT;
        pgoff_t end_index = (end - 1) >> PAGE_SECTORS_SHIFT;
-       struct pagevec pvec;
+       struct folio_batch fbatch;
        s64 i_sectors_delta = 0;
+       unsigned i, j;
 
        if (end <= start)
                return;
 
-       pagevec_init(&pvec);
-
-       do {
-               unsigned nr_pages, i, j;
+       folio_batch_init(&fbatch);
 
-               nr_pages = pagevec_lookup_range(&pvec, inode->v.i_mapping,
-                                               &index, end_index);
-               for (i = 0; i < nr_pages; i++) {
-                       struct page *page = pvec.pages[i];
-                       u64 pg_start = page->index << PAGE_SECTORS_SHIFT;
-                       u64 pg_end = (page->index + 1) << PAGE_SECTORS_SHIFT;
+       while (filemap_get_folios(inode->v.i_mapping,
+                                 &index, end_index, &fbatch)) {
+               for (i = 0; i < folio_batch_count(&fbatch); i++) {
+                       struct folio *folio = fbatch.folios[i];
+                       u64 pg_start = folio->index << PAGE_SECTORS_SHIFT;
+                       u64 pg_end = (folio->index + 1) << PAGE_SECTORS_SHIFT;
                        unsigned pg_offset = max(start, pg_start) - pg_start;
                        unsigned pg_len = min(end, pg_end) - pg_offset - pg_start;
                        struct bch_page_state *s;
@@ -493,8 +502,8 @@ static void mark_pagecache_reserved(struct bch_inode_info *inode,
                        BUG_ON(pg_offset >= PAGE_SECTORS);
                        BUG_ON(pg_offset + pg_len > PAGE_SECTORS);
 
-                       lock_page(page);
-                       s = bch2_page_state(page);
+                       folio_lock(folio);
+                       s = bch2_page_state(&folio->page);
 
                        if (s) {
                                spin_lock(&s->lock);
@@ -513,10 +522,11 @@ static void mark_pagecache_reserved(struct bch_inode_info *inode,
                                spin_unlock(&s->lock);
                        }
 
-                       unlock_page(page);
+                       folio_unlock(folio);
                }
-               pagevec_release(&pvec);
-       } while (index <= end_index);
+               folio_batch_release(&fbatch);
+               cond_resched();
+       }
 
        i_sectors_acct(c, inode, NULL, i_sectors_delta);
 }
@@ -596,7 +606,7 @@ static void bch2_page_reservation_put(struct bch_fs *c,
 static int bch2_page_reservation_get(struct bch_fs *c,
                        struct bch_inode_info *inode, struct page *page,
                        struct bch2_page_reservation *res,
-                       unsigned offset, unsigned len, bool check_enospc)
+                       unsigned offset, unsigned len)
 {
        struct bch_page_state *s = bch2_page_state_create(page, 0);
        unsigned i, disk_sectors = 0, quota_sectors = 0;
@@ -616,19 +626,14 @@ static int bch2_page_reservation_get(struct bch_fs *c,
        }
 
        if (disk_sectors) {
-               ret = bch2_disk_reservation_add(c, &res->disk,
-                                               disk_sectors,
-                                               !check_enospc
-                                               ? BCH_DISK_RESERVATION_NOFAIL
-                                               : 0);
+               ret = bch2_disk_reservation_add(c, &res->disk, disk_sectors, 0);
                if (unlikely(ret))
                        return ret;
        }
 
        if (quota_sectors) {
                ret = bch2_quota_reservation_add(c, inode, &res->quota,
-                                                quota_sectors,
-                                                check_enospc);
+                                                quota_sectors, true);
                if (unlikely(ret)) {
                        struct disk_reservation tmp = {
                                .sectors = disk_sectors
@@ -812,7 +817,7 @@ vm_fault_t bch2_page_mkwrite(struct vm_fault *vmf)
                }
        }
 
-       if (bch2_page_reservation_get(c, inode, page, &res, 0, len, true)) {
+       if (bch2_page_reservation_get(c, inode, page, &res, 0, len)) {
                unlock_page(page);
                ret = VM_FAULT_SIGBUS;
                goto out;
@@ -830,47 +835,22 @@ out:
        return ret;
 }
 
-void bch2_invalidatepage(struct page *page, unsigned int offset,
-                        unsigned int length)
+void bch2_invalidate_folio(struct folio *folio, size_t offset, size_t length)
 {
-       if (offset || length < PAGE_SIZE)
+       if (offset || length < folio_size(folio))
                return;
 
-       bch2_clear_page_bits(page);
+       bch2_clear_page_bits(&folio->page);
 }
 
-int bch2_releasepage(struct page *page, gfp_t gfp_mask)
+bool bch2_release_folio(struct folio *folio, gfp_t gfp_mask)
 {
-       if (PageDirty(page))
-               return 0;
-
-       bch2_clear_page_bits(page);
-       return 1;
-}
-
-#ifdef CONFIG_MIGRATION
-int bch2_migrate_page(struct address_space *mapping, struct page *newpage,
-                     struct page *page, enum migrate_mode mode)
-{
-       int ret;
-
-       EBUG_ON(!PageLocked(page));
-       EBUG_ON(!PageLocked(newpage));
-
-       ret = migrate_page_move_mapping(mapping, newpage, page, 0);
-       if (ret != MIGRATEPAGE_SUCCESS)
-               return ret;
+       if (folio_test_dirty(folio) || folio_test_writeback(folio))
+               return false;
 
-       if (PagePrivate(page))
-               attach_page_private(newpage, detach_page_private(page));
-
-       if (mode != MIGRATE_SYNC_NO_COPY)
-               migrate_page_copy(newpage, page);
-       else
-               migrate_page_states(newpage, page);
-       return MIGRATEPAGE_SUCCESS;
+       bch2_clear_page_bits(&folio->page);
+       return true;
 }
-#endif
 
 /* readpage(s): */
 
@@ -1034,10 +1014,9 @@ retry:
                 * read_extent -> io_time_reset may cause a transaction restart
                 * without returning an error, we need to check for that here:
                 */
-               if (!bch2_trans_relock(trans)) {
-                       ret = -EINTR;
+               ret = bch2_trans_relock(trans);
+               if (ret)
                        break;
-               }
 
                bch2_btree_iter_set_pos(&iter,
                                POS(inum.inum, rbio->bio.bi_iter.bi_sector));
@@ -1090,7 +1069,7 @@ retry:
 err:
        bch2_trans_iter_exit(trans, &iter);
 
-       if (ret == -EINTR)
+       if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
                goto retry;
 
        if (ret) {
@@ -1127,12 +1106,12 @@ void bch2_readahead(struct readahead_control *ractl)
                                   readpages_iter.idx,
                                   BIO_MAX_VECS);
                struct bch_read_bio *rbio =
-                       rbio_init(bio_alloc_bioset(GFP_NOFS, n, &c->bio_read),
+                       rbio_init(bio_alloc_bioset(NULL, n, REQ_OP_READ,
+                                                  GFP_NOFS, &c->bio_read),
                                  opts);
 
                readpages_iter.idx++;
 
-               bio_set_op_attrs(&rbio->bio, REQ_OP_READ, 0);
                rbio->bio.bi_iter.bi_sector = (sector_t) index << PAGE_SECTORS_SHIFT;
                rbio->bio.bi_end_io = bch2_readpages_end_io;
                BUG_ON(!bio_add_page(&rbio->bio, page, PAGE_SIZE, 0));
@@ -1164,20 +1143,6 @@ static void __bchfs_readpage(struct bch_fs *c, struct bch_read_bio *rbio,
        bch2_trans_exit(&trans);
 }
 
-int bch2_readpage(struct file *file, struct page *page)
-{
-       struct bch_inode_info *inode = to_bch_ei(page->mapping->host);
-       struct bch_fs *c = inode->v.i_sb->s_fs_info;
-       struct bch_io_opts opts = io_opts(c, &inode->ei_inode);
-       struct bch_read_bio *rbio;
-
-       rbio = rbio_init(bio_alloc_bioset(GFP_NOFS, 1, &c->bio_read), opts);
-       rbio->bio.bi_end_io = bch2_readpages_end_io;
-
-       __bchfs_readpage(c, rbio, inode_inum(inode), page);
-       return 0;
-}
-
 static void bch2_read_single_page_end_io(struct bio *bio)
 {
        complete(bio->bi_private);
@@ -1192,7 +1157,7 @@ static int bch2_read_single_page(struct page *page,
        int ret;
        DECLARE_COMPLETION_ONSTACK(done);
 
-       rbio = rbio_init(bio_alloc_bioset(GFP_NOFS, 1, &c->bio_read),
+       rbio = rbio_init(bio_alloc_bioset(NULL, 1, REQ_OP_READ, GFP_NOFS, &c->bio_read),
                         io_opts(c, &inode->ei_inode));
        rbio->bio.bi_private = &done;
        rbio->bio.bi_end_io = bch2_read_single_page_end_io;
@@ -1210,6 +1175,16 @@ static int bch2_read_single_page(struct page *page,
        return 0;
 }
 
+int bch2_read_folio(struct file *file, struct folio *folio)
+{
+       struct page *page = &folio->page;
+       int ret;
+
+       ret = bch2_read_single_page(page, page->mapping);
+       folio_unlock(folio);
+       return bch2_err_class(ret);
+}
+
 /* writepages: */
 
 struct bch_writepage_state {
@@ -1243,8 +1218,6 @@ static void bch2_writepage_io_done(struct closure *cl)
        struct bio_vec *bvec;
        unsigned i;
 
-       up(&io->op.c->io_in_flight);
-
        if (io->op.error) {
                set_bit(EI_INODE_ERROR, &io->inode->ei_flags);
 
@@ -1278,7 +1251,7 @@ static void bch2_writepage_io_done(struct closure *cl)
         * racing with fallocate can cause us to add fewer sectors than
         * expected - but we shouldn't add more sectors than expected:
         */
-       WARN_ON(io->op.i_sectors_delta > 0);
+       WARN_ON_ONCE(io->op.i_sectors_delta > 0);
 
        /*
         * (error (due to going RO) halfway through a page can screw that up
@@ -1307,8 +1280,6 @@ static void bch2_writepage_do_io(struct bch_writepage_state *w)
 {
        struct bch_writepage_io *io = w->io;
 
-       down(&io->op.c->io_in_flight);
-
        w->io = NULL;
        closure_call(&io->op.cl, bch2_write, NULL, &io->cl);
        continue_at(&io->cl, bch2_writepage_io_done, NULL);
@@ -1327,7 +1298,9 @@ static void bch2_writepage_io_alloc(struct bch_fs *c,
 {
        struct bch_write_op *op;
 
-       w->io = container_of(bio_alloc_bioset(GFP_NOFS, BIO_MAX_VECS,
+       w->io = container_of(bio_alloc_bioset(NULL, BIO_MAX_VECS,
+                                             REQ_OP_WRITE,
+                                             GFP_NOFS,
                                              &c->writepage_bioset),
                             struct bch_writepage_io, op.wbio.bio);
 
@@ -1464,8 +1437,8 @@ do_io:
                                     sectors << 9, offset << 9));
 
                /* Check for writing past i_size: */
-               WARN_ON((bio_end_sector(&w->io->op.wbio.bio) << 9) >
-                       round_up(i_size, block_bytes(c)));
+               WARN_ON_ONCE((bio_end_sector(&w->io->op.wbio.bio) << 9) >
+                            round_up(i_size, block_bytes(c)));
 
                w->io->op.res.sectors += reserved_sectors;
                w->io->op.i_sectors_delta -= dirty_sectors;
@@ -1493,27 +1466,13 @@ int bch2_writepages(struct address_space *mapping, struct writeback_control *wbc
        if (w.io)
                bch2_writepage_do_io(&w);
        blk_finish_plug(&plug);
-       return ret;
-}
-
-int bch2_writepage(struct page *page, struct writeback_control *wbc)
-{
-       struct bch_fs *c = page->mapping->host->i_sb->s_fs_info;
-       struct bch_writepage_state w =
-               bch_writepage_state_init(c, to_bch_ei(page->mapping->host));
-       int ret;
-
-       ret = __bch2_writepage(page, wbc, &w);
-       if (w.io)
-               bch2_writepage_do_io(&w);
-
-       return ret;
+       return bch2_err_class(ret);
 }
 
 /* buffered writes: */
 
 int bch2_write_begin(struct file *file, struct address_space *mapping,
-                    loff_t pos, unsigned len, unsigned flags,
+                    loff_t pos, unsigned len,
                     struct page **pagep, void **fsdata)
 {
        struct bch_inode_info *inode = to_bch_ei(mapping->host);
@@ -1533,7 +1492,7 @@ int bch2_write_begin(struct file *file, struct address_space *mapping,
 
        bch2_pagecache_add_get(&inode->ei_pagecache_lock);
 
-       page = grab_cache_page_write_begin(mapping, index, flags);
+       page = grab_cache_page_write_begin(mapping, index);
        if (!page)
                goto err_unlock;
 
@@ -1563,11 +1522,10 @@ out:
        if (!bch2_page_state_create(page, __GFP_NOFAIL)->uptodate) {
                ret = bch2_page_state_set(c, inode_inum(inode), &page, 1);
                if (ret)
-                       goto out;
+                       goto err;
        }
 
-       ret = bch2_page_reservation_get(c, inode, page, res,
-                                       offset, len, true);
+       ret = bch2_page_reservation_get(c, inode, page, res, offset, len);
        if (ret) {
                if (!PageUptodate(page)) {
                        /*
@@ -1592,7 +1550,7 @@ err_unlock:
        bch2_pagecache_add_put(&inode->ei_pagecache_lock);
        kfree(res);
        *fsdata = NULL;
-       return ret;
+       return bch2_err_class(ret);
 }
 
 int bch2_write_end(struct file *file, struct address_space *mapping,
@@ -1664,7 +1622,7 @@ static int __bch2_buffered_write(struct bch_inode_info *inode,
        bch2_page_reservation_init(c, inode, &res);
 
        for (i = 0; i < nr_pages; i++) {
-               pages[i] = grab_cache_page_write_begin(mapping, index + i, 0);
+               pages[i] = grab_cache_page_write_begin(mapping, index + i);
                if (!pages[i]) {
                        nr_pages = i;
                        if (!i) {
@@ -1709,7 +1667,7 @@ static int __bch2_buffered_write(struct bch_inode_info *inode,
                }
 
                ret = bch2_page_reservation_get(c, inode, page, &res,
-                                               pg_offset, pg_len, true);
+                                               pg_offset, pg_len);
                if (ret)
                        goto out;
 
@@ -1726,7 +1684,7 @@ static int __bch2_buffered_write(struct bch_inode_info *inode,
                unsigned pg_len = min_t(unsigned, len - copied,
                                        PAGE_SIZE - pg_offset);
                unsigned pg_copied = copy_page_from_iter_atomic(page,
-                                               pg_offset, pg_len,iter);
+                                               pg_offset, pg_len, iter);
 
                if (!pg_copied)
                        break;
@@ -1808,11 +1766,11 @@ again:
                 * to check that the address is actually valid, when atomic
                 * usercopies are used, below.
                 */
-               if (unlikely(iov_iter_fault_in_readable(iter, bytes))) {
+               if (unlikely(fault_in_iov_iter_readable(iter, bytes))) {
                        bytes = min_t(unsigned long, iov_iter_count(iter),
                                      PAGE_SIZE - offset);
 
-                       if (unlikely(iov_iter_fault_in_readable(iter, bytes))) {
+                       if (unlikely(fault_in_iov_iter_readable(iter, bytes))) {
                                ret = -EFAULT;
                                break;
                        }
@@ -1870,7 +1828,7 @@ static void bch2_dio_read_complete(struct closure *cl)
 {
        struct dio_read *dio = container_of(cl, struct dio_read, cl);
 
-       dio->req->ki_complete(dio->req, dio->ret, 0);
+       dio->req->ki_complete(dio->req, dio->ret);
        bio_check_or_release(&dio->rbio.bio, dio->should_dirty);
 }
 
@@ -1918,8 +1876,10 @@ static int bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter)
        shorten = iov_iter_count(iter) - round_up(ret, block_bytes(c));
        iter->count -= shorten;
 
-       bio = bio_alloc_bioset(GFP_KERNEL,
-                              iov_iter_npages(iter, BIO_MAX_VECS),
+       bio = bio_alloc_bioset(NULL,
+                              bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS),
+                              REQ_OP_READ,
+                              GFP_KERNEL,
                               &c->dio_read_bioset);
 
        bio->bi_end_io = bch2_direct_IO_read_endio;
@@ -1953,8 +1913,10 @@ static int bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter)
 
        goto start;
        while (iter->count) {
-               bio = bio_alloc_bioset(GFP_KERNEL,
-                                      iov_iter_npages(iter, BIO_MAX_VECS),
+               bio = bio_alloc_bioset(NULL,
+                                      bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS),
+                                      REQ_OP_READ,
+                                      GFP_KERNEL,
                                       &c->bio_read);
                bio->bi_end_io          = bch2_direct_IO_read_split_endio;
 start:
@@ -2012,7 +1974,7 @@ ssize_t bch2_read_iter(struct kiocb *iocb, struct iov_iter *iter)
                                        iocb->ki_pos,
                                        iocb->ki_pos + count - 1);
                if (ret < 0)
-                       return ret;
+                       goto out;
 
                file_accessed(file);
 
@@ -2027,8 +1989,8 @@ ssize_t bch2_read_iter(struct kiocb *iocb, struct iov_iter *iter)
                ret = generic_file_read_iter(iocb, iter);
                bch2_pagecache_add_put(&inode->ei_pagecache_lock);
        }
-
-       return ret;
+out:
+       return bch2_err_class(ret);
 }
 
 /* O_DIRECT writes */
@@ -2070,7 +2032,7 @@ retry:
        offset = iter.pos.offset;
        bch2_trans_iter_exit(&trans, &iter);
 err:
-       if (err == -EINTR)
+       if (bch2_err_matches(err, BCH_ERR_transaction_restart))
                goto retry;
        bch2_trans_exit(&trans);
 
@@ -2096,12 +2058,10 @@ static long bch2_dio_write_loop(struct dio_write *dio)
        if (dio->loop)
                goto loop;
 
-       down(&c->io_in_flight);
-
        while (1) {
                iter_count = dio->iter.count;
 
-               if (kthread)
+               if (kthread && dio->mm)
                        kthread_use_mm(dio->mm);
                BUG_ON(current->faults_disabled_mapping);
                current->faults_disabled_mapping = mapping;
@@ -2111,7 +2071,7 @@ static long bch2_dio_write_loop(struct dio_write *dio)
                dropped_locks = fdm_dropped_locks();
 
                current->faults_disabled_mapping = NULL;
-               if (kthread)
+               if (kthread && dio->mm)
                        kthread_unuse_mm(dio->mm);
 
                /*
@@ -2177,8 +2137,8 @@ static long bch2_dio_write_loop(struct dio_write *dio)
                        struct iovec *iov = dio->inline_vecs;
 
                        if (dio->iter.nr_segs > ARRAY_SIZE(dio->inline_vecs)) {
-                               iov = kmalloc(dio->iter.nr_segs * sizeof(*iov),
-                                             GFP_KERNEL);
+                               iov = kmalloc_array(dio->iter.nr_segs, sizeof(*iov),
+                                                   GFP_KERNEL);
                                if (unlikely(!iov)) {
                                        dio->sync = sync = true;
                                        goto do_io;
@@ -2222,13 +2182,12 @@ loop:
                if (!dio->iter.count)
                        break;
 
-               bio_reset(bio);
+               bio_reset(bio, NULL, REQ_OP_WRITE);
                reinit_completion(&dio->done);
        }
 
        ret = dio->op.error ?: ((long) dio->written << 9);
 err:
-       up(&c->io_in_flight);
        bch2_pagecache_block_put(&inode->ei_pagecache_lock);
        bch2_quota_reservation_put(c, inode, &dio->quota_res);
 
@@ -2243,8 +2202,11 @@ err:
        /* inode->i_dio_count is our ref on inode and thus bch_fs */
        inode_dio_end(&inode->v);
 
+       if (ret < 0)
+               ret = bch2_err_class(ret);
+
        if (!sync) {
-               req->ki_complete(req, ret, 0);
+               req->ki_complete(req, ret);
                ret = -EIOCBQUEUED;
        }
        return ret;
@@ -2303,10 +2265,10 @@ ssize_t bch2_direct_write(struct kiocb *req, struct iov_iter *iter)
                locked = false;
        }
 
-       bio = bio_alloc_bioset(GFP_KERNEL,
-                              iov_iter_is_bvec(iter)
-                              ? 0
-                              : iov_iter_npages(iter, BIO_MAX_VECS),
+       bio = bio_alloc_bioset(NULL,
+                              bio_iov_vecs_to_alloc(iter, BIO_MAX_VECS),
+                              REQ_OP_WRITE,
+                              GFP_KERNEL,
                               &c->dio_write_bioset);
        dio = container_of(bio, struct dio_write, op.wbio.bio);
        init_completion(&dio->done);
@@ -2349,8 +2311,10 @@ ssize_t bch2_write_iter(struct kiocb *iocb, struct iov_iter *from)
        struct bch_inode_info *inode = file_bch_inode(file);
        ssize_t ret;
 
-       if (iocb->ki_flags & IOCB_DIRECT)
-               return bch2_direct_write(iocb, from);
+       if (iocb->ki_flags & IOCB_DIRECT) {
+               ret = bch2_direct_write(iocb, from);
+               goto out;
+       }
 
        /* We can write back this queue in page reclaim */
        current->backing_dev_info = inode_to_bdi(&inode->v);
@@ -2377,8 +2341,8 @@ unlock:
 
        if (ret > 0)
                ret = generic_write_sync(iocb, ret);
-
-       return ret;
+out:
+       return bch2_err_class(ret);
 }
 
 /* fsync: */
@@ -2412,7 +2376,7 @@ int bch2_fsync(struct file *file, loff_t start, loff_t end, int datasync)
        ret2 = sync_inode_metadata(&inode->v, 1);
        ret3 = bch2_flush_inode(c, inode_inum(inode));
 
-       return ret ?: ret2 ?: ret3;
+       return bch2_err_class(ret ?: ret2 ?: ret3);
 }
 
 /* truncate: */
@@ -2446,7 +2410,7 @@ retry:
        start = iter.pos;
        bch2_trans_iter_exit(&trans, &iter);
 err:
-       if (ret == -EINTR)
+       if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
                goto retry;
 
        bch2_trans_exit(&trans);
@@ -2703,9 +2667,11 @@ int bch2_truncate(struct user_namespace *mnt_userns,
                        U64_MAX, &i_sectors_delta);
        i_sectors_acct(c, inode, NULL, i_sectors_delta);
 
-       WARN_ON(!inode->v.i_size && inode->v.i_blocks &&
-               !bch2_journal_error(&c->journal));
-
+       bch2_fs_inconsistent_on(!inode->v.i_size && inode->v.i_blocks &&
+                               !bch2_journal_error(&c->journal), c,
+                               "inode %lu truncated to 0 but i_blocks %llu (ondisk %lli)",
+                               inode->v.i_ino, (u64) inode->v.i_blocks,
+                               inode->ei_inode.bi_sectors);
        if (unlikely(ret))
                goto err;
 
@@ -2716,7 +2682,7 @@ int bch2_truncate(struct user_namespace *mnt_userns,
        ret = bch2_setattr_nonsize(mnt_userns, inode, iattr);
 err:
        bch2_pagecache_block_put(&inode->ei_pagecache_lock);
-       return ret;
+       return bch2_err_class(ret);
 }
 
 /* fallocate: */
@@ -2747,7 +2713,7 @@ static long bchfs_fpunch(struct bch_inode_info *inode, loff_t offset, loff_t len
 
        truncate_pagecache_range(&inode->v, offset, end - 1);
 
-       if (block_start < block_end ) {
+       if (block_start < block_end) {
                s64 i_sectors_delta = 0;
 
                ret = bch2_fpunch(c, inode_inum(inode),
@@ -2834,7 +2800,8 @@ static long bchfs_fcollapse_finsert(struct bch_inode_info *inode,
        bch2_trans_copy_iter(&dst, &src);
        bch2_trans_copy_iter(&del, &src);
 
-       while (ret == 0 || ret == -EINTR) {
+       while (ret == 0 ||
+              bch2_err_matches(ret, BCH_ERR_transaction_restart)) {
                struct disk_reservation disk_res =
                        bch2_disk_reservation_init(c, 0);
                struct bkey_i delete;
@@ -2902,13 +2869,7 @@ reassemble:
 
                next_pos = insert ? bkey_start_pos(&delete.k) : delete.k.p;
 
-               if (copy.k->k.size == k.k->size) {
-                       /*
-                        * If we're moving the entire extent, we can skip
-                        * running triggers:
-                        */
-                       trigger_flags |= BTREE_TRIGGER_NORUN;
-               } else {
+               if (copy.k->k.size != k.k->size) {
                        /* We might end up splitting compressed extents: */
                        unsigned nr_ptrs =
                                bch2_bkey_nr_ptrs_allocated(bkey_i_to_s_c(copy.k));
@@ -3042,14 +3003,14 @@ static int __bchfs_fallocate(struct bch_inode_info *inode, int mode,
 bkey_err:
                bch2_quota_reservation_put(c, inode, &quota_res);
                bch2_disk_reservation_put(c, &disk_res);
-               if (ret == -EINTR)
+               if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
                        ret = 0;
        }
 
        bch2_trans_unlock(&trans); /* lock ordering, before taking pagecache locks: */
        mark_pagecache_reserved(inode, start_sector, iter.pos.offset);
 
-       if (ret == -ENOSPC && (mode & FALLOC_FL_ZERO_RANGE)) {
+       if (bch2_err_matches(ret, ENOSPC) && (mode & FALLOC_FL_ZERO_RANGE)) {
                struct quota_res quota_res = { 0 };
                s64 i_sectors_delta = 0;
 
@@ -3100,7 +3061,7 @@ static long bchfs_fallocate(struct bch_inode_info *inode, int mode,
         * so that the VFS cache i_size is consistent with the btree i_size:
         */
        if (ret &&
-           !(ret == -ENOSPC && (mode & FALLOC_FL_ZERO_RANGE)))
+           !(bch2_err_matches(ret, ENOSPC) && (mode & FALLOC_FL_ZERO_RANGE)))
                return ret;
 
        if (mode & FALLOC_FL_KEEP_SIZE && end > inode->v.i_size)
@@ -3128,13 +3089,17 @@ long bch2_fallocate_dispatch(struct file *file, int mode,
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
        long ret;
 
-       if (!percpu_ref_tryget(&c->writes))
+       if (!percpu_ref_tryget_live(&c->writes))
                return -EROFS;
 
        inode_lock(&inode->v);
        inode_dio_wait(&inode->v);
        bch2_pagecache_block_get(&inode->ei_pagecache_lock);
 
+       ret = file_modified(file);
+       if (ret)
+               goto err;
+
        if (!(mode & ~(FALLOC_FL_KEEP_SIZE|FALLOC_FL_ZERO_RANGE)))
                ret = bchfs_fallocate(inode, mode, offset, len);
        else if (mode == (FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE))
@@ -3145,13 +3110,61 @@ long bch2_fallocate_dispatch(struct file *file, int mode,
                ret = bchfs_fcollapse_finsert(inode, offset, len, false);
        else
                ret = -EOPNOTSUPP;
-
-
+err:
        bch2_pagecache_block_put(&inode->ei_pagecache_lock);
        inode_unlock(&inode->v);
        percpu_ref_put(&c->writes);
 
-       return ret;
+       return bch2_err_class(ret);
+}
+
+static int quota_reserve_range(struct bch_inode_info *inode,
+                              struct quota_res *res,
+                              u64 start, u64 end)
+{
+       struct bch_fs *c = inode->v.i_sb->s_fs_info;
+       struct btree_trans trans;
+       struct btree_iter iter;
+       struct bkey_s_c k;
+       u32 snapshot;
+       u64 sectors = end - start;
+       u64 pos = start;
+       int ret;
+
+       bch2_trans_init(&trans, c, 0, 0);
+retry:
+       bch2_trans_begin(&trans);
+
+       ret = bch2_subvolume_get_snapshot(&trans, inode->ei_subvol, &snapshot);
+       if (ret)
+               goto err;
+
+       bch2_trans_iter_init(&trans, &iter, BTREE_ID_extents,
+                            SPOS(inode->v.i_ino, pos, snapshot), 0);
+
+       while (!(ret = btree_trans_too_many_iters(&trans)) &&
+              (k = bch2_btree_iter_peek_upto(&iter, POS(inode->v.i_ino, end - 1))).k &&
+              !(ret = bkey_err(k))) {
+               if (bkey_extent_is_allocation(k.k)) {
+                       u64 s = min(end, k.k->p.offset) -
+                               max(start, bkey_start_offset(k.k));
+                       BUG_ON(s > sectors);
+                       sectors -= s;
+               }
+               bch2_btree_iter_advance(&iter);
+       }
+       pos = iter.pos.offset;
+       bch2_trans_iter_exit(&trans, &iter);
+err:
+       if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
+               goto retry;
+
+       bch2_trans_exit(&trans);
+
+       if (ret)
+               return ret;
+
+       return bch2_quota_reservation_add(c, inode, res, sectors, true);
 }
 
 loff_t bch2_remap_file_range(struct file *file_src, loff_t pos_src,
@@ -3161,6 +3174,7 @@ loff_t bch2_remap_file_range(struct file *file_src, loff_t pos_src,
        struct bch_inode_info *src = file_bch_inode(file_src);
        struct bch_inode_info *dst = file_bch_inode(file_dst);
        struct bch_fs *c = src->v.i_sb->s_fs_info;
+       struct quota_res quota_res = { 0 };
        s64 i_sectors_delta = 0;
        u64 aligned_len;
        loff_t ret = 0;
@@ -3181,8 +3195,6 @@ loff_t bch2_remap_file_range(struct file *file_src, loff_t pos_src,
 
        bch2_lock_inodes(INODE_LOCK|INODE_PAGECACHE_BLOCK, src, dst);
 
-       file_update_time(file_dst);
-
        inode_dio_wait(&src->v);
        inode_dio_wait(&dst->v);
 
@@ -3199,6 +3211,13 @@ loff_t bch2_remap_file_range(struct file *file_src, loff_t pos_src,
        if (ret)
                goto err;
 
+       ret = quota_reserve_range(dst, &quota_res, pos_dst >> 9,
+                                 (pos_dst + aligned_len) >> 9);
+       if (ret)
+               goto err;
+
+       file_update_time(file_dst);
+
        mark_pagecache_unallocated(src, pos_src >> 9,
                                   (pos_src + aligned_len) >> 9);
 
@@ -3215,8 +3234,7 @@ loff_t bch2_remap_file_range(struct file *file_src, loff_t pos_src,
         */
        ret = min((u64) ret << 9, (u64) len);
 
-       /* XXX get a quota reservation */
-       i_sectors_acct(c, dst, NULL, i_sectors_delta);
+       i_sectors_acct(c, dst, &quota_res, i_sectors_delta);
 
        spin_lock(&dst->v.i_lock);
        if (pos_dst + ret > dst->v.i_size)
@@ -3227,9 +3245,10 @@ loff_t bch2_remap_file_range(struct file *file_src, loff_t pos_src,
            IS_SYNC(file_inode(file_dst)))
                ret = bch2_flush_inode(c, inode_inum(dst));
 err:
+       bch2_quota_reservation_put(c, dst, &quota_res);
        bch2_unlock_inodes(INODE_LOCK|INODE_PAGECACHE_BLOCK, src, dst);
 
-       return ret;
+       return bch2_err_class(ret);
 }
 
 /* fseek: */
@@ -3251,36 +3270,40 @@ static loff_t bch2_seek_pagecache_data(struct inode *vinode,
                                       loff_t start_offset,
                                       loff_t end_offset)
 {
-       struct address_space *mapping = vinode->i_mapping;
-       struct page *page;
+       struct folio_batch fbatch;
        pgoff_t start_index     = start_offset >> PAGE_SHIFT;
        pgoff_t end_index       = end_offset >> PAGE_SHIFT;
        pgoff_t index           = start_index;
+       unsigned i;
        loff_t ret;
        int offset;
 
-       while (index <= end_index) {
-               if (find_get_pages_range(mapping, &index, end_index, 1, &page)) {
-                       lock_page(page);
+       folio_batch_init(&fbatch);
+
+       while (filemap_get_folios(vinode->i_mapping,
+                                 &index, end_index, &fbatch)) {
+               for (i = 0; i < folio_batch_count(&fbatch); i++) {
+                       struct folio *folio = fbatch.folios[i];
 
-                       offset = page_data_offset(page,
-                                       page->index == start_index
+                       folio_lock(folio);
+
+                       offset = page_data_offset(&folio->page,
+                                       folio->index == start_index
                                        ? start_offset & (PAGE_SIZE - 1)
                                        : 0);
                        if (offset >= 0) {
-                               ret = clamp(((loff_t) page->index << PAGE_SHIFT) +
+                               ret = clamp(((loff_t) folio->index << PAGE_SHIFT) +
                                            offset,
                                            start_offset, end_offset);
-                               unlock_page(page);
-                               put_page(page);
+                               folio_unlock(folio);
+                               folio_batch_release(&fbatch);
                                return ret;
                        }
 
-                       unlock_page(page);
-                       put_page(page);
-               } else {
-                       break;
+                       folio_unlock(folio);
                }
+               folio_batch_release(&fbatch);
+               cond_resched();
        }
 
        return end_offset;
@@ -3322,7 +3345,7 @@ retry:
        }
        bch2_trans_iter_exit(&trans, &iter);
 err:
-       if (ret == -EINTR)
+       if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
                goto retry;
 
        bch2_trans_exit(&trans);
@@ -3437,7 +3460,7 @@ retry:
        }
        bch2_trans_iter_exit(&trans, &iter);
 err:
-       if (ret == -EINTR)
+       if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
                goto retry;
 
        bch2_trans_exit(&trans);
@@ -3452,18 +3475,26 @@ err:
 
 loff_t bch2_llseek(struct file *file, loff_t offset, int whence)
 {
+       loff_t ret;
+
        switch (whence) {
        case SEEK_SET:
        case SEEK_CUR:
        case SEEK_END:
-               return generic_file_llseek(file, offset, whence);
+               ret = generic_file_llseek(file, offset, whence);
+               break;
        case SEEK_DATA:
-               return bch2_seek_data(file, offset);
+               ret = bch2_seek_data(file, offset);
+               break;
        case SEEK_HOLE:
-               return bch2_seek_hole(file, offset);
+               ret = bch2_seek_hole(file, offset);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
        }
 
-       return -EINVAL;
+       return bch2_err_class(ret);
 }
 
 void bch2_fs_fsio_exit(struct bch_fs *c)