Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'vfs-7.0-rc1.fserror' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs error reporting updates from Christian Brauner:
"This contains the changes to support generic I/O error reporting.

Filesystems currently have no standard mechanism for reporting
metadata corruption and file I/O errors to userspace via fsnotify.
Each filesystem (xfs, ext4, erofs, f2fs, etc.) privately defines
EFSCORRUPTED, and error reporting to fanotify is inconsistent or
absent entirely.

This introduces a generic fserror infrastructure built around struct
super_block that gives filesystems a standard way to queue metadata
and file I/O error reports for delivery to fsnotify.

Errors are queued via mempools and queue_work to avoid holding
filesystem locks in the notification path; unmount waits for pending
events to drain. A new super_operations::report_error callback lets
filesystem drivers respond to file I/O errors themselves (to be used
by an upcoming XFS self-healing patchset).

On the uapi side, EFSCORRUPTED and EUCLEAN are promoted from private
per-filesystem definitions to canonical errno.h values across all
architectures"

* tag 'vfs-7.0-rc1.fserror' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
ext4: convert to new fserror helpers
xfs: translate fsdax media errors into file "data lost" errors when convenient
xfs: report fs metadata errors via fsnotify
iomap: report file I/O errors to the VFS
fs: report filesystem and file I/O errors to fsnotify
uapi: promote EFSCORRUPTED and EUCLEAN to errno.h

+373 -24
+2
arch/alpha/include/uapi/asm/errno.h
··· 55 55 #define ENOSR 82 /* Out of streams resources */ 56 56 #define ETIME 83 /* Timer expired */ 57 57 #define EBADMSG 84 /* Not a data message */ 58 + #define EFSBADCRC EBADMSG /* Bad CRC detected */ 58 59 #define EPROTO 85 /* Protocol error */ 59 60 #define ENODATA 86 /* No data available */ 60 61 #define ENOSTR 87 /* Device not a stream */ ··· 97 96 #define EREMCHG 115 /* Remote address changed */ 98 97 99 98 #define EUCLEAN 117 /* Structure needs cleaning */ 99 + #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 100 100 #define ENOTNAM 118 /* Not a XENIX named type file */ 101 101 #define ENAVAIL 119 /* No XENIX semaphores available */ 102 102 #define EISNAM 120 /* Is a named type file */
+2
arch/mips/include/uapi/asm/errno.h
··· 50 50 #define EDOTDOT 73 /* RFS specific error */ 51 51 #define EMULTIHOP 74 /* Multihop attempted */ 52 52 #define EBADMSG 77 /* Not a data message */ 53 + #define EFSBADCRC EBADMSG /* Bad CRC detected */ 53 54 #define ENAMETOOLONG 78 /* File name too long */ 54 55 #define EOVERFLOW 79 /* Value too large for defined data type */ 55 56 #define ENOTUNIQ 80 /* Name not unique on network */ ··· 89 88 #define EISCONN 133 /* Transport endpoint is already connected */ 90 89 #define ENOTCONN 134 /* Transport endpoint is not connected */ 91 90 #define EUCLEAN 135 /* Structure needs cleaning */ 91 + #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 92 92 #define ENOTNAM 137 /* Not a XENIX named type file */ 93 93 #define ENAVAIL 138 /* No XENIX semaphores available */ 94 94 #define EISNAM 139 /* Is a named type file */
+2
arch/parisc/include/uapi/asm/errno.h
··· 36 36 37 37 #define EDOTDOT 66 /* RFS specific error */ 38 38 #define EBADMSG 67 /* Not a data message */ 39 + #define EFSBADCRC EBADMSG /* Bad CRC detected */ 39 40 #define EUSERS 68 /* Too many users */ 40 41 #define EDQUOT 69 /* Quota exceeded */ 41 42 #define ESTALE 70 /* Stale file handle */ ··· 63 62 #define ERESTART 175 /* Interrupted system call should be restarted */ 64 63 #define ESTRPIPE 176 /* Streams pipe error */ 65 64 #define EUCLEAN 177 /* Structure needs cleaning */ 65 + #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 66 66 #define ENOTNAM 178 /* Not a XENIX named type file */ 67 67 #define ENAVAIL 179 /* No XENIX semaphores available */ 68 68 #define EISNAM 180 /* Is a named type file */
+2
arch/sparc/include/uapi/asm/errno.h
··· 48 48 #define ENOSR 74 /* Out of streams resources */ 49 49 #define ENOMSG 75 /* No message of desired type */ 50 50 #define EBADMSG 76 /* Not a data message */ 51 + #define EFSBADCRC EBADMSG /* Bad CRC detected */ 51 52 #define EIDRM 77 /* Identifier removed */ 52 53 #define EDEADLK 78 /* Resource deadlock would occur */ 53 54 #define ENOLCK 79 /* No record locks available */ ··· 92 91 #define ENOTUNIQ 115 /* Name not unique on network */ 93 92 #define ERESTART 116 /* Interrupted syscall should be restarted */ 94 93 #define EUCLEAN 117 /* Structure needs cleaning */ 94 + #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 95 95 #define ENOTNAM 118 /* Not a XENIX named type file */ 96 96 #define ENAVAIL 119 /* No XENIX semaphores available */ 97 97 #define EISNAM 120 /* Is a named type file */
+1 -1
fs/Makefile
··· 16 16 stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \ 17 17 fs_dirent.o fs_context.o fs_parser.o fsopen.o init.o \ 18 18 kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o \ 19 - file_attr.o 19 + file_attr.o fserror.o 20 20 21 21 obj-$(CONFIG_BUFFER_HEAD) += buffer.o mpage.o 22 22 obj-$(CONFIG_PROC_FS) += proc_namespace.o
-2
fs/erofs/internal.h
··· 541 541 long erofs_compat_ioctl(struct file *filp, unsigned int cmd, 542 542 unsigned long arg); 543 543 544 - #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 545 - 546 544 #endif /* __EROFS_INTERNAL_H */
-1
fs/ext2/ext2.h
··· 357 357 */ 358 358 #define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ 359 359 #define EXT2_ERROR_FS 0x0002 /* Errors detected */ 360 - #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 361 360 362 361 /* 363 362 * Mount flags
-3
fs/ext4/ext4.h
··· 3938 3938 get_block_t *get_block); 3939 3939 #endif /* __KERNEL__ */ 3940 3940 3941 - #define EFSBADCRC EBADMSG /* Bad CRC detected */ 3942 - #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 3943 - 3944 3941 #endif /* _EXT4_H */
+2
fs/ext4/ioctl.c
··· 26 26 #include <linux/fsmap.h> 27 27 #include "fsmap.h" 28 28 #include <trace/events/ext4.h> 29 + #include <linux/fserror.h> 29 30 30 31 typedef void ext4_update_sb_callback(struct ext4_sb_info *sbi, 31 32 struct ext4_super_block *es, ··· 845 844 return -EINVAL; 846 845 } 847 846 clear_opt(sb, DISCARD); 847 + fserror_report_shutdown(sb, GFP_KERNEL); 848 848 return 0; 849 849 } 850 850
+9 -4
fs/ext4/super.c
··· 48 48 #include <linux/fsnotify.h> 49 49 #include <linux/fs_context.h> 50 50 #include <linux/fs_parser.h> 51 + #include <linux/fserror.h> 51 52 52 53 #include "ext4.h" 53 54 #include "ext4_extents.h" /* Needed for trace points definition */ ··· 825 824 sb->s_id, function, line, current->comm, &vaf); 826 825 va_end(args); 827 826 } 828 - fsnotify_sb_error(sb, NULL, error ? error : EFSCORRUPTED); 827 + fserror_report_metadata(sb, error ? -abs(error) : -EFSCORRUPTED, 828 + GFP_ATOMIC); 829 829 830 830 ext4_handle_error(sb, force_ro, error, 0, block, function, line); 831 831 } ··· 858 856 current->comm, &vaf); 859 857 va_end(args); 860 858 } 861 - fsnotify_sb_error(inode->i_sb, inode, error ? error : EFSCORRUPTED); 859 + fserror_report_file_metadata(inode, 860 + error ? -abs(error) : -EFSCORRUPTED, 861 + GFP_ATOMIC); 862 862 863 863 ext4_handle_error(inode->i_sb, false, error, inode->i_ino, block, 864 864 function, line); ··· 900 896 current->comm, path, &vaf); 901 897 va_end(args); 902 898 } 903 - fsnotify_sb_error(inode->i_sb, inode, EFSCORRUPTED); 899 + fserror_report_file_metadata(inode, -EFSCORRUPTED, GFP_ATOMIC); 904 900 905 901 ext4_handle_error(inode->i_sb, false, EFSCORRUPTED, inode->i_ino, block, 906 902 function, line); ··· 969 965 printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n", 970 966 sb->s_id, function, line, errstr); 971 967 } 972 - fsnotify_sb_error(sb, NULL, errno ? errno : EFSCORRUPTED); 968 + fserror_report_metadata(sb, errno ? -abs(errno) : -EFSCORRUPTED, 969 + GFP_ATOMIC); 973 970 974 971 ext4_handle_error(sb, false, -errno, 0, 0, function, line); 975 972 }
-3
fs/f2fs/f2fs.h
··· 5004 5004 f2fs_invalidate_compress_pages_range(sbi, blkaddr, len); 5005 5005 } 5006 5006 5007 - #define EFSBADCRC EBADMSG /* Bad CRC detected */ 5008 - #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 5009 - 5010 5007 #endif /* _LINUX_F2FS_H */
+194
fs/fserror.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Copyright (c) 2025 Oracle. All Rights Reserved. 4 + * Author: Darrick J. Wong <djwong@kernel.org> 5 + */ 6 + #include <linux/fs.h> 7 + #include <linux/fsnotify.h> 8 + #include <linux/mempool.h> 9 + #include <linux/fserror.h> 10 + 11 + #define FSERROR_DEFAULT_EVENT_POOL_SIZE (32) 12 + 13 + static struct mempool fserror_events_pool; 14 + 15 + void fserror_mount(struct super_block *sb) 16 + { 17 + /* 18 + * The pending error counter is biased by 1 so that we don't wake_var 19 + * until we're actually trying to unmount. 20 + */ 21 + refcount_set(&sb->s_pending_errors, 1); 22 + } 23 + 24 + void fserror_unmount(struct super_block *sb) 25 + { 26 + /* 27 + * If we don't drop the pending error count to zero, then wait for it 28 + * to drop below 1, which means that the pending errors cleared and 29 + * hopefully we didn't saturate with 1 billion+ concurrent events. 30 + */ 31 + if (!refcount_dec_and_test(&sb->s_pending_errors)) 32 + wait_var_event(&sb->s_pending_errors, 33 + refcount_read(&sb->s_pending_errors) < 1); 34 + } 35 + 36 + static inline void fserror_pending_dec(struct super_block *sb) 37 + { 38 + if (refcount_dec_and_test(&sb->s_pending_errors)) 39 + wake_up_var(&sb->s_pending_errors); 40 + } 41 + 42 + static inline void fserror_free_event(struct fserror_event *event) 43 + { 44 + fserror_pending_dec(event->sb); 45 + mempool_free(event, &fserror_events_pool); 46 + } 47 + 48 + static void fserror_worker(struct work_struct *work) 49 + { 50 + struct fserror_event *event = 51 + container_of(work, struct fserror_event, work); 52 + struct super_block *sb = event->sb; 53 + 54 + if (sb->s_flags & SB_ACTIVE) { 55 + struct fs_error_report report = { 56 + /* send positive error number to userspace */ 57 + .error = -event->error, 58 + .inode = event->inode, 59 + .sb = event->sb, 60 + }; 61 + 62 + if (sb->s_op->report_error) 63 + sb->s_op->report_error(event); 64 + 65 + fsnotify(FS_ERROR, &report, FSNOTIFY_EVENT_ERROR, NULL, NULL, 66 + NULL, 0); 67 + } 68 + 69 + iput(event->inode); 70 + fserror_free_event(event); 71 + } 72 + 73 + static inline struct fserror_event *fserror_alloc_event(struct super_block *sb, 74 + gfp_t gfp_flags) 75 + { 76 + struct fserror_event *event = NULL; 77 + 78 + /* 79 + * If pending_errors already reached zero or is no longer active, 80 + * the superblock is being deactivated so there's no point in 81 + * continuing. 82 + * 83 + * The order of the check of s_pending_errors and SB_ACTIVE are 84 + * mandated by order of accesses in generic_shutdown_super and 85 + * fserror_unmount. Barriers are implicitly provided by the refcount 86 + * manipulations in this function and fserror_unmount. 87 + */ 88 + if (!refcount_inc_not_zero(&sb->s_pending_errors)) 89 + return NULL; 90 + if (!(sb->s_flags & SB_ACTIVE)) 91 + goto out_pending; 92 + 93 + event = mempool_alloc(&fserror_events_pool, gfp_flags); 94 + if (!event) 95 + goto out_pending; 96 + 97 + /* mempool_alloc doesn't support GFP_ZERO */ 98 + memset(event, 0, sizeof(*event)); 99 + event->sb = sb; 100 + INIT_WORK(&event->work, fserror_worker); 101 + 102 + return event; 103 + 104 + out_pending: 105 + fserror_pending_dec(sb); 106 + return NULL; 107 + } 108 + 109 + /** 110 + * fserror_report - report a filesystem error of some kind 111 + * 112 + * @sb: superblock of the filesystem 113 + * @inode: inode within that filesystem, if applicable 114 + * @type: type of error encountered 115 + * @pos: start of inode range affected, if applicable 116 + * @len: length of inode range affected, if applicable 117 + * @error: error number encountered, must be negative 118 + * @gfp: memory allocation flags for conveying the event to a worker, 119 + * since this function can be called from atomic contexts 120 + * 121 + * Report details of a filesystem error to the super_operations::report_error 122 + * callback if present; and to fsnotify for distribution to userspace. @sb, 123 + * @gfp, @type, and @error must all be specified. For file I/O errors, the 124 + * @inode, @pos, and @len fields must also be specified. For file metadata 125 + * errors, @inode must be specified. If @inode is not NULL, then @inode->i_sb 126 + * must point to @sb. 127 + * 128 + * Reporting work is deferred to a workqueue to ensure that ->report_error is 129 + * called from process context without any locks held. An active reference to 130 + * the inode is maintained until event handling is complete, and unmount will 131 + * wait for queued events to drain. 132 + */ 133 + void fserror_report(struct super_block *sb, struct inode *inode, 134 + enum fserror_type type, loff_t pos, u64 len, int error, 135 + gfp_t gfp) 136 + { 137 + struct fserror_event *event; 138 + 139 + /* sb and inode must be from the same filesystem */ 140 + WARN_ON_ONCE(inode && inode->i_sb != sb); 141 + 142 + /* error number must be negative */ 143 + WARN_ON_ONCE(error >= 0); 144 + 145 + event = fserror_alloc_event(sb, gfp); 146 + if (!event) 147 + goto lost; 148 + 149 + event->type = type; 150 + event->pos = pos; 151 + event->len = len; 152 + event->error = error; 153 + 154 + /* 155 + * Can't iput from non-sleeping context, so grabbing another reference 156 + * to the inode must be the last thing before submitting the event. 157 + */ 158 + if (inode) { 159 + event->inode = igrab(inode); 160 + if (!event->inode) 161 + goto lost_event; 162 + } 163 + 164 + /* 165 + * Use schedule_work here even if we're already in process context so 166 + * that fsnotify and super_operations::report_error implementations are 167 + * guaranteed to run in process context without any locks held. Since 168 + * errors are supposed to be rare, the overhead shouldn't kill us any 169 + * more than the failing device will. 170 + */ 171 + schedule_work(&event->work); 172 + return; 173 + 174 + lost_event: 175 + fserror_free_event(event); 176 + lost: 177 + if (inode) 178 + pr_err_ratelimited( 179 + "%s: lost file I/O error report for ino %lu type %u pos 0x%llx len 0x%llx error %d", 180 + sb->s_id, inode->i_ino, type, pos, len, error); 181 + else 182 + pr_err_ratelimited( 183 + "%s: lost filesystem error report for type %u error %d", 184 + sb->s_id, type, error); 185 + } 186 + EXPORT_SYMBOL_GPL(fserror_report); 187 + 188 + static int __init fserror_init(void) 189 + { 190 + return mempool_init_kmalloc_pool(&fserror_events_pool, 191 + FSERROR_DEFAULT_EVENT_POOL_SIZE, 192 + sizeof(struct fserror_event)); 193 + } 194 + fs_initcall(fserror_init);
+22 -1
fs/iomap/buffered-io.c
··· 8 8 #include <linux/writeback.h> 9 9 #include <linux/swap.h> 10 10 #include <linux/migrate.h> 11 + #include <linux/fserror.h> 11 12 #include "internal.h" 12 13 #include "trace.h" 13 14 ··· 372 371 if (folio_test_uptodate(folio)) 373 372 return 0; 374 373 375 - if (WARN_ON_ONCE(size > iomap->length)) 374 + if (WARN_ON_ONCE(size > iomap->length)) { 375 + fserror_report_io(iter->inode, FSERR_BUFFERED_READ, 376 + iomap->offset, size, -EIO, GFP_NOFS); 376 377 return -EIO; 378 + } 377 379 if (offset > 0) 378 380 ifs_alloc(iter->inode, folio, iter->flags); 379 381 ··· 402 398 finished = !ifs->read_bytes_pending; 403 399 spin_unlock_irqrestore(&ifs->state_lock, flags); 404 400 } 401 + 402 + if (error) 403 + fserror_report_io(folio->mapping->host, FSERR_BUFFERED_READ, 404 + folio_pos(folio) + off, len, error, 405 + GFP_ATOMIC); 405 406 406 407 if (finished) 407 408 folio_end_read(folio, uptodate); ··· 549 540 if (!*bytes_submitted) 550 541 iomap_read_init(folio); 551 542 ret = ctx->ops->read_folio_range(iter, ctx, plen); 543 + if (ret < 0) 544 + fserror_report_io(iter->inode, 545 + FSERR_BUFFERED_READ, pos, 546 + plen, ret, GFP_NOFS); 552 547 if (ret) 553 548 return ret; 554 549 *bytes_submitted += plen; ··· 828 815 else 829 816 status = iomap_bio_read_folio_range_sync(iter, 830 817 folio, block_start, plen); 818 + if (status < 0) 819 + fserror_report_io(iter->inode, 820 + FSERR_BUFFERED_READ, pos, 821 + len, status, GFP_NOFS); 831 822 if (status) 832 823 return status; 833 824 } ··· 1843 1826 u64 pos = folio_pos(folio); 1844 1827 u64 end_pos = pos + folio_size(folio); 1845 1828 u64 end_aligned = 0; 1829 + loff_t orig_pos = pos; 1846 1830 size_t bytes_submitted = 0; 1847 1831 int error = 0; 1848 1832 u32 rlen; ··· 1887 1869 1888 1870 if (bytes_submitted) 1889 1871 wpc->nr_folios++; 1872 + if (error && pos > orig_pos) 1873 + fserror_report_io(inode, FSERR_BUFFERED_WRITE, orig_pos, 0, 1874 + error, GFP_NOFS); 1890 1875 1891 1876 /* 1892 1877 * We can have dirty bits set past end of file in page_mkwrite path
+12
fs/iomap/direct-io.c
··· 7 7 #include <linux/pagemap.h> 8 8 #include <linux/iomap.h> 9 9 #include <linux/task_io_accounting_ops.h> 10 + #include <linux/fserror.h> 10 11 #include "internal.h" 11 12 #include "trace.h" 12 13 ··· 79 78 } 80 79 } 81 80 81 + static inline enum fserror_type iomap_dio_err_type(const struct iomap_dio *dio) 82 + { 83 + if (dio->flags & IOMAP_DIO_WRITE) 84 + return FSERR_DIRECTIO_WRITE; 85 + return FSERR_DIRECTIO_READ; 86 + } 87 + 82 88 ssize_t iomap_dio_complete(struct iomap_dio *dio) 83 89 { 84 90 const struct iomap_dio_ops *dops = dio->dops; ··· 95 87 96 88 if (dops && dops->end_io) 97 89 ret = dops->end_io(iocb, dio->size, ret, dio->flags); 90 + if (dio->error) 91 + fserror_report_io(file_inode(iocb->ki_filp), 92 + iomap_dio_err_type(dio), offset, dio->size, 93 + dio->error, GFP_NOFS); 98 94 99 95 if (likely(!ret)) { 100 96 ret = dio->size;
+6
fs/iomap/ioend.c
··· 6 6 #include <linux/list_sort.h> 7 7 #include <linux/pagemap.h> 8 8 #include <linux/writeback.h> 9 + #include <linux/fserror.h> 9 10 #include "internal.h" 10 11 #include "trace.h" 11 12 ··· 56 55 57 56 /* walk all folios in bio, ending page IO on them */ 58 57 bio_for_each_folio_all(fi, bio) { 58 + if (ioend->io_error) 59 + fserror_report_io(inode, FSERR_BUFFERED_WRITE, 60 + folio_pos(fi.folio) + fi.offset, 61 + fi.length, ioend->io_error, 62 + GFP_ATOMIC); 59 63 iomap_finish_folio_write(inode, fi.folio, fi.length); 60 64 folio_count++; 61 65 }
-2
fs/minix/minix.h
··· 175 175 __minix_error_inode((inode), __func__, __LINE__, \ 176 176 (fmt), ##__VA_ARGS__) 177 177 178 - #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 179 - 180 178 #endif /* FS_MINIX_H */
+3
fs/super.c
··· 36 36 #include <linux/lockdep.h> 37 37 #include <linux/user_namespace.h> 38 38 #include <linux/fs_context.h> 39 + #include <linux/fserror.h> 39 40 #include <uapi/linux/mount.h> 40 41 #include "internal.h" 41 42 ··· 364 363 spin_lock_init(&s->s_inode_list_lock); 365 364 INIT_LIST_HEAD(&s->s_inodes_wb); 366 365 spin_lock_init(&s->s_inode_wblist_lock); 366 + fserror_mount(s); 367 367 368 368 s->s_count = 1; 369 369 atomic_set(&s->s_active, 1); ··· 624 622 sync_filesystem(sb); 625 623 sb->s_flags &= ~SB_ACTIVE; 626 624 625 + fserror_unmount(sb); 627 626 cgroup_writeback_umount(sb); 628 627 629 628 /* Evict all inodes with zero refcount. */
-2
fs/udf/udf_sb.h
··· 55 55 #define MF_DUPLICATE_MD 0x01 56 56 #define MF_MIRROR_FE_LOADED 0x02 57 57 58 - #define EFSCORRUPTED EUCLEAN 59 - 60 58 struct udf_meta_data { 61 59 __u32 s_meta_file_loc; 62 60 __u32 s_mirror_file_loc;
+4
fs/xfs/xfs_fsops.c
··· 26 26 #include "xfs_rtrefcount_btree.h" 27 27 #include "xfs_metafile.h" 28 28 29 + #include <linux/fserror.h> 30 + 29 31 /* 30 32 * Write new AG headers to disk. Non-transactional, but need to be 31 33 * written and completed prior to the growfs transaction being logged. ··· 542 540 "Please unmount the filesystem and rectify the problem(s)"); 543 541 if (xfs_error_level >= XFS_ERRLEVEL_HIGH) 544 542 xfs_stack_trace(); 543 + 544 + fserror_report_shutdown(mp->m_super, GFP_KERNEL); 545 545 } 546 546 547 547 /*
+14
fs/xfs/xfs_health.c
··· 20 20 #include "xfs_quota_defs.h" 21 21 #include "xfs_rtgroup.h" 22 22 23 + #include <linux/fserror.h> 24 + 23 25 static void 24 26 xfs_health_unmount_group( 25 27 struct xfs_group *xg, ··· 113 111 spin_lock(&mp->m_sb_lock); 114 112 mp->m_fs_sick |= mask; 115 113 spin_unlock(&mp->m_sb_lock); 114 + 115 + fserror_report_metadata(mp->m_super, -EFSCORRUPTED, GFP_NOFS); 116 116 } 117 117 118 118 /* Mark per-fs metadata as having been checked and found unhealthy by fsck. */ ··· 130 126 mp->m_fs_sick |= mask; 131 127 mp->m_fs_checked |= mask; 132 128 spin_unlock(&mp->m_sb_lock); 129 + 130 + fserror_report_metadata(mp->m_super, -EFSCORRUPTED, GFP_NOFS); 133 131 } 134 132 135 133 /* Mark a per-fs metadata healed. */ ··· 204 198 spin_lock(&xg->xg_state_lock); 205 199 xg->xg_sick |= mask; 206 200 spin_unlock(&xg->xg_state_lock); 201 + 202 + fserror_report_metadata(xg->xg_mount->m_super, -EFSCORRUPTED, GFP_NOFS); 207 203 } 208 204 209 205 /* ··· 223 215 xg->xg_sick |= mask; 224 216 xg->xg_checked |= mask; 225 217 spin_unlock(&xg->xg_state_lock); 218 + 219 + fserror_report_metadata(xg->xg_mount->m_super, -EFSCORRUPTED, GFP_NOFS); 226 220 } 227 221 228 222 /* ··· 297 287 spin_lock(&VFS_I(ip)->i_lock); 298 288 inode_state_clear(VFS_I(ip), I_DONTCACHE); 299 289 spin_unlock(&VFS_I(ip)->i_lock); 290 + 291 + fserror_report_file_metadata(VFS_I(ip), -EFSCORRUPTED, GFP_NOFS); 300 292 } 301 293 302 294 /* Mark inode metadata as having been checked and found unhealthy by fsck. */ ··· 323 311 spin_lock(&VFS_I(ip)->i_lock); 324 312 inode_state_clear(VFS_I(ip), I_DONTCACHE); 325 313 spin_unlock(&VFS_I(ip)->i_lock); 314 + 315 + fserror_report_file_metadata(VFS_I(ip), -EFSCORRUPTED, GFP_NOFS); 326 316 } 327 317 328 318 /* Mark parts of an inode healed. */
-2
fs/xfs/xfs_linux.h
··· 121 121 122 122 #define ENOATTR ENODATA /* Attribute not found */ 123 123 #define EWRONGFS EINVAL /* Mount with wrong filesystem type */ 124 - #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 125 - #define EFSBADCRC EBADMSG /* Bad CRC detected */ 126 124 127 125 #define __return_address __builtin_return_address(0) 128 126
+4
fs/xfs/xfs_notify_failure.c
··· 26 26 #include <linux/mm.h> 27 27 #include <linux/dax.h> 28 28 #include <linux/fs.h> 29 + #include <linux/fserror.h> 29 30 30 31 struct xfs_failure_info { 31 32 xfs_agblock_t startblock; ··· 116 115 if (notify->mf_flags & MF_MEM_PRE_REMOVE) 117 116 invalidate_inode_pages2_range(mapping, pgoff, 118 117 pgoff + pgcnt - 1); 118 + 119 + fserror_report_data_lost(VFS_I(ip), (u64)pgoff << PAGE_SHIFT, 120 + (u64)pgcnt << PAGE_SHIFT, GFP_NOFS); 119 121 120 122 xfs_irele(ip); 121 123 return error;
+7
include/linux/fs/super_types.h
··· 35 35 struct workqueue_struct; 36 36 struct writeback_control; 37 37 struct xattr_handler; 38 + struct fserror_event; 38 39 39 40 extern struct super_block *blockdev_superblock; 40 41 ··· 125 124 */ 126 125 int (*remove_bdev)(struct super_block *sb, struct block_device *bdev); 127 126 void (*shutdown)(struct super_block *sb); 127 + 128 + /* Report a filesystem error */ 129 + void (*report_error)(const struct fserror_event *event); 128 130 }; 129 131 130 132 struct super_block { ··· 272 268 spinlock_t s_inode_wblist_lock; 273 269 struct list_head s_inodes_wb; /* writeback inodes */ 274 270 long s_min_writeback_pages; 271 + 272 + /* number of fserrors that are being sent to fsnotify/filesystems */ 273 + refcount_t s_pending_errors; 275 274 } __randomize_layout; 276 275 277 276 /*
+75
include/linux/fserror.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + /* 3 + * Copyright (c) 2025 Oracle. All Rights Reserved. 4 + * Author: Darrick J. Wong <djwong@kernel.org> 5 + */ 6 + #ifndef _LINUX_FSERROR_H__ 7 + #define _LINUX_FSERROR_H__ 8 + 9 + void fserror_mount(struct super_block *sb); 10 + void fserror_unmount(struct super_block *sb); 11 + 12 + enum fserror_type { 13 + /* pagecache I/O failed */ 14 + FSERR_BUFFERED_READ, 15 + FSERR_BUFFERED_WRITE, 16 + 17 + /* direct I/O failed */ 18 + FSERR_DIRECTIO_READ, 19 + FSERR_DIRECTIO_WRITE, 20 + 21 + /* out of band media error reported */ 22 + FSERR_DATA_LOST, 23 + 24 + /* filesystem metadata */ 25 + FSERR_METADATA, 26 + }; 27 + 28 + struct fserror_event { 29 + struct work_struct work; 30 + struct super_block *sb; 31 + struct inode *inode; 32 + loff_t pos; 33 + u64 len; 34 + enum fserror_type type; 35 + 36 + /* negative error number */ 37 + int error; 38 + }; 39 + 40 + void fserror_report(struct super_block *sb, struct inode *inode, 41 + enum fserror_type type, loff_t pos, u64 len, int error, 42 + gfp_t gfp); 43 + 44 + static inline void fserror_report_io(struct inode *inode, 45 + enum fserror_type type, loff_t pos, 46 + u64 len, int error, gfp_t gfp) 47 + { 48 + fserror_report(inode->i_sb, inode, type, pos, len, error, gfp); 49 + } 50 + 51 + static inline void fserror_report_data_lost(struct inode *inode, loff_t pos, 52 + u64 len, gfp_t gfp) 53 + { 54 + fserror_report(inode->i_sb, inode, FSERR_DATA_LOST, pos, len, -EIO, 55 + gfp); 56 + } 57 + 58 + static inline void fserror_report_file_metadata(struct inode *inode, int error, 59 + gfp_t gfp) 60 + { 61 + fserror_report(inode->i_sb, inode, FSERR_METADATA, 0, 0, error, gfp); 62 + } 63 + 64 + static inline void fserror_report_metadata(struct super_block *sb, int error, 65 + gfp_t gfp) 66 + { 67 + fserror_report(sb, NULL, FSERR_METADATA, 0, 0, error, gfp); 68 + } 69 + 70 + static inline void fserror_report_shutdown(struct super_block *sb, gfp_t gfp) 71 + { 72 + fserror_report(sb, NULL, FSERR_METADATA, 0, 0, -ESHUTDOWN, gfp); 73 + } 74 + 75 + #endif /* _LINUX_FSERROR_H__ */
-3
include/linux/jbd2.h
··· 1815 1815 1816 1816 #endif /* __KERNEL__ */ 1817 1817 1818 - #define EFSBADCRC EBADMSG /* Bad CRC detected */ 1819 - #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 1820 - 1821 1818 #endif /* _LINUX_JBD2_H */
+2
include/uapi/asm-generic/errno.h
··· 55 55 #define EMULTIHOP 72 /* Multihop attempted */ 56 56 #define EDOTDOT 73 /* RFS specific error */ 57 57 #define EBADMSG 74 /* Not a data message */ 58 + #define EFSBADCRC EBADMSG /* Bad CRC detected */ 58 59 #define EOVERFLOW 75 /* Value too large for defined data type */ 59 60 #define ENOTUNIQ 76 /* Name not unique on network */ 60 61 #define EBADFD 77 /* File descriptor in bad state */ ··· 99 98 #define EINPROGRESS 115 /* Operation now in progress */ 100 99 #define ESTALE 116 /* Stale file handle */ 101 100 #define EUCLEAN 117 /* Structure needs cleaning */ 101 + #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 102 102 #define ENOTNAM 118 /* Not a XENIX named type file */ 103 103 #define ENAVAIL 119 /* No XENIX semaphores available */ 104 104 #define EISNAM 120 /* Is a named type file */
+2
tools/arch/alpha/include/uapi/asm/errno.h
··· 55 55 #define ENOSR 82 /* Out of streams resources */ 56 56 #define ETIME 83 /* Timer expired */ 57 57 #define EBADMSG 84 /* Not a data message */ 58 + #define EFSBADCRC EBADMSG /* Bad CRC detected */ 58 59 #define EPROTO 85 /* Protocol error */ 59 60 #define ENODATA 86 /* No data available */ 60 61 #define ENOSTR 87 /* Device not a stream */ ··· 97 96 #define EREMCHG 115 /* Remote address changed */ 98 97 99 98 #define EUCLEAN 117 /* Structure needs cleaning */ 99 + #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 100 100 #define ENOTNAM 118 /* Not a XENIX named type file */ 101 101 #define ENAVAIL 119 /* No XENIX semaphores available */ 102 102 #define EISNAM 120 /* Is a named type file */
+2
tools/arch/mips/include/uapi/asm/errno.h
··· 50 50 #define EDOTDOT 73 /* RFS specific error */ 51 51 #define EMULTIHOP 74 /* Multihop attempted */ 52 52 #define EBADMSG 77 /* Not a data message */ 53 + #define EFSBADCRC EBADMSG /* Bad CRC detected */ 53 54 #define ENAMETOOLONG 78 /* File name too long */ 54 55 #define EOVERFLOW 79 /* Value too large for defined data type */ 55 56 #define ENOTUNIQ 80 /* Name not unique on network */ ··· 89 88 #define EISCONN 133 /* Transport endpoint is already connected */ 90 89 #define ENOTCONN 134 /* Transport endpoint is not connected */ 91 90 #define EUCLEAN 135 /* Structure needs cleaning */ 91 + #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 92 92 #define ENOTNAM 137 /* Not a XENIX named type file */ 93 93 #define ENAVAIL 138 /* No XENIX semaphores available */ 94 94 #define EISNAM 139 /* Is a named type file */
+2
tools/arch/parisc/include/uapi/asm/errno.h
··· 36 36 37 37 #define EDOTDOT 66 /* RFS specific error */ 38 38 #define EBADMSG 67 /* Not a data message */ 39 + #define EFSBADCRC EBADMSG /* Bad CRC detected */ 39 40 #define EUSERS 68 /* Too many users */ 40 41 #define EDQUOT 69 /* Quota exceeded */ 41 42 #define ESTALE 70 /* Stale file handle */ ··· 63 62 #define ERESTART 175 /* Interrupted system call should be restarted */ 64 63 #define ESTRPIPE 176 /* Streams pipe error */ 65 64 #define EUCLEAN 177 /* Structure needs cleaning */ 65 + #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 66 66 #define ENOTNAM 178 /* Not a XENIX named type file */ 67 67 #define ENAVAIL 179 /* No XENIX semaphores available */ 68 68 #define EISNAM 180 /* Is a named type file */
+2
tools/arch/sparc/include/uapi/asm/errno.h
··· 48 48 #define ENOSR 74 /* Out of streams resources */ 49 49 #define ENOMSG 75 /* No message of desired type */ 50 50 #define EBADMSG 76 /* Not a data message */ 51 + #define EFSBADCRC EBADMSG /* Bad CRC detected */ 51 52 #define EIDRM 77 /* Identifier removed */ 52 53 #define EDEADLK 78 /* Resource deadlock would occur */ 53 54 #define ENOLCK 79 /* No record locks available */ ··· 92 91 #define ENOTUNIQ 115 /* Name not unique on network */ 93 92 #define ERESTART 116 /* Interrupted syscall should be restarted */ 94 93 #define EUCLEAN 117 /* Structure needs cleaning */ 94 + #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 95 95 #define ENOTNAM 118 /* Not a XENIX named type file */ 96 96 #define ENAVAIL 119 /* No XENIX semaphores available */ 97 97 #define EISNAM 120 /* Is a named type file */
+2
tools/include/uapi/asm-generic/errno.h
··· 55 55 #define EMULTIHOP 72 /* Multihop attempted */ 56 56 #define EDOTDOT 73 /* RFS specific error */ 57 57 #define EBADMSG 74 /* Not a data message */ 58 + #define EFSBADCRC EBADMSG /* Bad CRC detected */ 58 59 #define EOVERFLOW 75 /* Value too large for defined data type */ 59 60 #define ENOTUNIQ 76 /* Name not unique on network */ 60 61 #define EBADFD 77 /* File descriptor in bad state */ ··· 99 98 #define EINPROGRESS 115 /* Operation now in progress */ 100 99 #define ESTALE 116 /* Stale file handle */ 101 100 #define EUCLEAN 117 /* Structure needs cleaning */ 101 + #define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ 102 102 #define ENOTNAM 118 /* Not a XENIX named type file */ 103 103 #define ENAVAIL 119 /* No XENIX semaphores available */ 104 104 #define EISNAM 120 /* Is a named type file */