]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iomap: report file I/O errors to the VFS
authorDarrick J. Wong <djwong@kernel.org>
Tue, 13 Jan 2026 00:31:40 +0000 (16:31 -0800)
committerChristian Brauner <brauner@kernel.org>
Tue, 13 Jan 2026 08:58:01 +0000 (09:58 +0100)
Wire up iomap so that it reports all file read and write errors to the
VFS (and hence fsnotify) via the new fserror mechanism.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Link: https://patch.msgid.link/176826402631.3490369.729008983502742314.stgit@frogsfrogsfrogs
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/iomap/buffered-io.c
fs/iomap/direct-io.c
fs/iomap/ioend.c

index e5c1ca440d93bd7468eb2fbf37760295672622e9..b21e989b9fa5e6e70eeb918aca9e545ca73656fe 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/writeback.h>
 #include <linux/swap.h>
 #include <linux/migrate.h>
+#include <linux/fserror.h>
 #include "internal.h"
 #include "trace.h"
 
@@ -371,8 +372,11 @@ static int iomap_read_inline_data(const struct iomap_iter *iter,
        if (folio_test_uptodate(folio))
                return 0;
 
-       if (WARN_ON_ONCE(size > iomap->length))
+       if (WARN_ON_ONCE(size > iomap->length)) {
+               fserror_report_io(iter->inode, FSERR_BUFFERED_READ,
+                                 iomap->offset, size, -EIO, GFP_NOFS);
                return -EIO;
+       }
        if (offset > 0)
                ifs_alloc(iter->inode, folio, iter->flags);
 
@@ -399,6 +403,11 @@ void iomap_finish_folio_read(struct folio *folio, size_t off, size_t len,
                spin_unlock_irqrestore(&ifs->state_lock, flags);
        }
 
+       if (error)
+               fserror_report_io(folio->mapping->host, FSERR_BUFFERED_READ,
+                                 folio_pos(folio) + off, len, error,
+                                 GFP_ATOMIC);
+
        if (finished)
                folio_end_read(folio, uptodate);
 }
@@ -540,6 +549,10 @@ static int iomap_read_folio_iter(struct iomap_iter *iter,
                        if (!*bytes_submitted)
                                iomap_read_init(folio);
                        ret = ctx->ops->read_folio_range(iter, ctx, plen);
+                       if (ret < 0)
+                               fserror_report_io(iter->inode,
+                                                 FSERR_BUFFERED_READ, pos,
+                                                 plen, ret, GFP_NOFS);
                        if (ret)
                                return ret;
                        *bytes_submitted += plen;
@@ -815,6 +828,10 @@ static int __iomap_write_begin(const struct iomap_iter *iter,
                        else
                                status = iomap_bio_read_folio_range_sync(iter,
                                                folio, block_start, plen);
+                       if (status < 0)
+                               fserror_report_io(iter->inode,
+                                                 FSERR_BUFFERED_READ, pos,
+                                                 len, status, GFP_NOFS);
                        if (status)
                                return status;
                }
@@ -1805,6 +1822,7 @@ int iomap_writeback_folio(struct iomap_writepage_ctx *wpc, struct folio *folio)
        u64 pos = folio_pos(folio);
        u64 end_pos = pos + folio_size(folio);
        u64 end_aligned = 0;
+       loff_t orig_pos = pos;
        size_t bytes_submitted = 0;
        int error = 0;
        u32 rlen;
@@ -1848,6 +1866,9 @@ int iomap_writeback_folio(struct iomap_writepage_ctx *wpc, struct folio *folio)
 
        if (bytes_submitted)
                wpc->nr_folios++;
+       if (error && pos > orig_pos)
+               fserror_report_io(inode, FSERR_BUFFERED_WRITE, orig_pos, 0,
+                                 error, GFP_NOFS);
 
        /*
         * We can have dirty bits set past end of file in page_mkwrite path
index 8e273408453a9c19c34c3be5d384f252e321e5ec..a06c73eaa8901bf0b4b2d988f6fa3d4cb8877284 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/pagemap.h>
 #include <linux/iomap.h>
 #include <linux/task_io_accounting_ops.h>
+#include <linux/fserror.h>
 #include "internal.h"
 #include "trace.h"
 
@@ -78,6 +79,13 @@ static void iomap_dio_submit_bio(const struct iomap_iter *iter,
        }
 }
 
+static inline enum fserror_type iomap_dio_err_type(const struct iomap_dio *dio)
+{
+       if (dio->flags & IOMAP_DIO_WRITE)
+               return FSERR_DIRECTIO_WRITE;
+       return FSERR_DIRECTIO_READ;
+}
+
 ssize_t iomap_dio_complete(struct iomap_dio *dio)
 {
        const struct iomap_dio_ops *dops = dio->dops;
@@ -87,6 +95,10 @@ ssize_t iomap_dio_complete(struct iomap_dio *dio)
 
        if (dops && dops->end_io)
                ret = dops->end_io(iocb, dio->size, ret, dio->flags);
+       if (dio->error)
+               fserror_report_io(file_inode(iocb->ki_filp),
+                                 iomap_dio_err_type(dio), offset, dio->size,
+                                 dio->error, GFP_NOFS);
 
        if (likely(!ret)) {
                ret = dio->size;
index 86f44922ed3b6a52ad1529bd69c9160ff52e8975..5b27ee98896707919860bfcc7234ef690ef41e9a 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/list_sort.h>
 #include <linux/pagemap.h>
 #include <linux/writeback.h>
+#include <linux/fserror.h>
 #include "internal.h"
 #include "trace.h"
 
@@ -55,6 +56,11 @@ static u32 iomap_finish_ioend_buffered(struct iomap_ioend *ioend)
 
        /* walk all folios in bio, ending page IO on them */
        bio_for_each_folio_all(fi, bio) {
+               if (ioend->io_error)
+                       fserror_report_io(inode, FSERR_BUFFERED_WRITE,
+                                         folio_pos(fi.folio) + fi.offset,
+                                         fi.length, ioend->io_error,
+                                         GFP_ATOMIC);
                iomap_finish_folio_write(inode, fi.folio, fi.length);
                folio_count++;
        }