Merge tag 'for-6.6-rc6-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[platform/kernel/linux-starfive.git] / fs / buffer.c
index 2379564..12e9a71 100644 (file)
@@ -2011,7 +2011,7 @@ void folio_zero_new_buffers(struct folio *folio, size_t from, size_t to)
 }
 EXPORT_SYMBOL(folio_zero_new_buffers);
 
-static void
+static int
 iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh,
                const struct iomap *iomap)
 {
@@ -2025,7 +2025,8 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh,
         * current block, then do not map the buffer and let the caller
         * handle it.
         */
-       BUG_ON(offset >= iomap->offset + iomap->length);
+       if (offset >= iomap->offset + iomap->length)
+               return -EIO;
 
        switch (iomap->type) {
        case IOMAP_HOLE:
@@ -2037,7 +2038,7 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh,
                if (!buffer_uptodate(bh) ||
                    (offset >= i_size_read(inode)))
                        set_buffer_new(bh);
-               break;
+               return 0;
        case IOMAP_DELALLOC:
                if (!buffer_uptodate(bh) ||
                    (offset >= i_size_read(inode)))
@@ -2045,7 +2046,7 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh,
                set_buffer_uptodate(bh);
                set_buffer_mapped(bh);
                set_buffer_delay(bh);
-               break;
+               return 0;
        case IOMAP_UNWRITTEN:
                /*
                 * For unwritten regions, we always need to ensure that regions
@@ -2057,12 +2058,24 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh,
                fallthrough;
        case IOMAP_MAPPED:
                if ((iomap->flags & IOMAP_F_NEW) ||
-                   offset >= i_size_read(inode))
+                   offset >= i_size_read(inode)) {
+                       /*
+                        * This can happen if truncating the block device races
+                        * with the check in the caller as i_size updates on
+                        * block devices aren't synchronized by i_rwsem for
+                        * block devices.
+                        */
+                       if (S_ISBLK(inode->i_mode))
+                               return -EIO;
                        set_buffer_new(bh);
+               }
                bh->b_blocknr = (iomap->addr + offset - iomap->offset) >>
                                inode->i_blkbits;
                set_buffer_mapped(bh);
-               break;
+               return 0;
+       default:
+               WARN_ON_ONCE(1);
+               return -EIO;
        }
 }
 
@@ -2103,13 +2116,12 @@ int __block_write_begin_int(struct folio *folio, loff_t pos, unsigned len,
                        clear_buffer_new(bh);
                if (!buffer_mapped(bh)) {
                        WARN_ON(bh->b_size != blocksize);
-                       if (get_block) {
+                       if (get_block)
                                err = get_block(inode, block, bh, 1);
-                               if (err)
-                                       break;
-                       } else {
-                               iomap_to_bh(inode, block, bh, iomap);
-                       }
+                       else
+                               err = iomap_to_bh(inode, block, bh, iomap);
+                       if (err)
+                               break;
 
                        if (buffer_new(bh)) {
                                clean_bdev_bh_alias(bh);