bcachefs: Handle bio_iov_iter_get_pages() returning unaligned bio
authorKent Overstreet <kent.overstreet@gmail.com>
Sun, 22 Sep 2019 19:02:05 +0000 (15:02 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:08:27 +0000 (17:08 -0400)
If the user buffer isn't aligned to the filesystem block size, on a
large enough IO - where it won't fit into a single bio -
bio_iov_iter_get_pages() won't necessarily return a bio with the proper
alignment.

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

index 142eb0c3cbbc3dd13bd3d7b73ab19a01607178c5..2d7bab51b320d15f85631d7cbaf7c0e7f5ffaa04 100644 (file)
@@ -2025,12 +2025,14 @@ static void bch2_dio_write_loop_async(struct closure *);
 static long bch2_dio_write_loop(struct dio_write *dio)
 {
        bool kthread = (current->flags & PF_KTHREAD) != 0;
+       struct bch_fs *c = dio->iop.op.c;
        struct kiocb *req = dio->req;
        struct address_space *mapping = req->ki_filp->f_mapping;
        struct bch_inode_info *inode = dio->iop.inode;
        struct bio *bio = &dio->iop.op.wbio.bio;
        struct bvec_iter_all iter;
        struct bio_vec *bv;
+       unsigned unaligned;
        loff_t offset;
        bool sync;
        long ret;
@@ -2066,6 +2068,21 @@ static long bch2_dio_write_loop(struct dio_write *dio)
                if (unlikely(ret < 0))
                        goto err;
 
+               unaligned = bio->bi_iter.bi_size & (block_bytes(c) - 1);
+               bio->bi_iter.bi_size -= unaligned;
+               iov_iter_revert(&dio->iter, unaligned);
+
+               if (!bio->bi_iter.bi_size) {
+                       /*
+                        * bio_iov_iter_get_pages was only able to get <
+                        * blocksize worth of pages:
+                        */
+                       bio_for_each_segment_all(bv, bio, iter)
+                               put_page(bv->bv_page);
+                       ret = -EFAULT;
+                       goto err;
+               }
+
                /* gup might have faulted pages back in: */
                ret = write_invalidate_inode_pages_range(mapping,
                                offset,
@@ -2105,8 +2122,8 @@ loop:
        ret = dio->iop.op.error ?: ((long) dio->iop.op.written << 9);
 err:
        bch2_pagecache_block_put(&inode->ei_pagecache_lock);
-       bch2_disk_reservation_put(dio->iop.op.c, &dio->iop.op.res);
-       bch2_quota_reservation_put(dio->iop.op.c, inode, &dio->quota_res);
+       bch2_disk_reservation_put(c, &dio->iop.op.res);
+       bch2_quota_reservation_put(c, inode, &dio->quota_res);
 
        if (dio->free_iov)
                kfree(dio->iter.__iov);