summaryrefslogtreecommitdiffstats
path: root/fs/fserror.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 10:09:22 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 10:09:22 -0700
commitf4cdf7ca9a1fdcca413157df19753f388a5a224e (patch)
tree60a31895b2446cd7fa27514e8c2843534a0de0da /fs/fserror.c
downloadlinux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.tar.gz
linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.zip
Merge tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-mediagrafted
Pull media updates from Mauro Carvalho Chehab: - v4l2-core: added ISP statistics support and per-block validation - v4l2-core: Allow unknown HDR10 white point and luminance - New camera sensors: Sony IMX678 and IMX471m, Himax HM1092 IR sensor - New codec: Milos: VPU v2.0 codec support - isp driver: gained support for Dreamchip RPPX1 ISP framework - vsp1 driver: gained support for RZ/T2H and RZ/N2H - Novalake driver: gained CVS support for new NVL hardware - dvb-core: fix feed leak on failed DMX_ADD_PID - several driver fixes, cleanups and minor improvements * tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits) media: ipu-bridge: check all DMI entries when overriding sensor rotation media: v4l2-async: avoid deleting unlinked ASC entry on link error media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement media: intel/ipu6: fix async notifier cleanup leak on parse error media: staging/ipu7: fix async notifier UAF on probe error path media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem() media: rcar-isp: Fix VSPX reference leaks media: rcar-isp: Release ISPCORE resources media: i2c: imx415: Release runtime PM reference on VBLANK error media: i2c: imx415: Return test pattern write errors media: renesas: vsp1: Declare index variables in for loop statement media: renesas: vsp1: Make reset control optional to support platforms without a reset line media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs media: nxp: imx8-isi: Add additional 32-bit RGB format support media: nxp: imx8-isi: Add 16-bit raw Bayer format support media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks media: nxp: imx8-isi: Correct color map between V4L2 and ISI ...
Diffstat (limited to 'fs/fserror.c')
-rw-r--r--fs/fserror.c194
1 files changed, 194 insertions, 0 deletions
diff --git a/fs/fserror.c b/fs/fserror.c
new file mode 100644
index 000000000..1e4d11fd9
--- /dev/null
+++ b/fs/fserror.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include <linux/fs.h>
+#include <linux/fsnotify.h>
+#include <linux/mempool.h>
+#include <linux/fserror.h>
+
+#define FSERROR_DEFAULT_EVENT_POOL_SIZE (32)
+
+static struct mempool fserror_events_pool;
+
+void fserror_mount(struct super_block *sb)
+{
+ /*
+ * The pending error counter is biased by 1 so that we don't wake_var
+ * until we're actually trying to unmount.
+ */
+ refcount_set(&sb->s_pending_errors, 1);
+}
+
+void fserror_unmount(struct super_block *sb)
+{
+ /*
+ * If we don't drop the pending error count to zero, then wait for it
+ * to drop below 1, which means that the pending errors cleared and
+ * hopefully we didn't saturate with 1 billion+ concurrent events.
+ */
+ if (!refcount_dec_and_test(&sb->s_pending_errors))
+ wait_var_event(&sb->s_pending_errors,
+ refcount_read(&sb->s_pending_errors) < 1);
+}
+
+static inline void fserror_pending_dec(struct super_block *sb)
+{
+ if (refcount_dec_and_test(&sb->s_pending_errors))
+ wake_up_var(&sb->s_pending_errors);
+}
+
+static inline void fserror_free_event(struct fserror_event *event)
+{
+ fserror_pending_dec(event->sb);
+ mempool_free(event, &fserror_events_pool);
+}
+
+static void fserror_worker(struct work_struct *work)
+{
+ struct fserror_event *event =
+ container_of(work, struct fserror_event, work);
+ struct super_block *sb = event->sb;
+
+ if (sb->s_flags & SB_ACTIVE) {
+ struct fs_error_report report = {
+ /* send positive error number to userspace */
+ .error = -event->error,
+ .inode = event->inode,
+ .sb = event->sb,
+ };
+
+ if (sb->s_op->report_error)
+ sb->s_op->report_error(event);
+
+ fsnotify(FS_ERROR, &report, FSNOTIFY_EVENT_ERROR, NULL, NULL,
+ NULL, 0);
+ }
+
+ iput(event->inode);
+ fserror_free_event(event);
+}
+
+static inline struct fserror_event *fserror_alloc_event(struct super_block *sb,
+ gfp_t gfp_flags)
+{
+ struct fserror_event *event = NULL;
+
+ /*
+ * If pending_errors already reached zero or is no longer active,
+ * the superblock is being deactivated so there's no point in
+ * continuing.
+ *
+ * The order of the check of s_pending_errors and SB_ACTIVE are
+ * mandated by order of accesses in generic_shutdown_super and
+ * fserror_unmount. Barriers are implicitly provided by the refcount
+ * manipulations in this function and fserror_unmount.
+ */
+ if (!refcount_inc_not_zero(&sb->s_pending_errors))
+ return NULL;
+ if (!(sb->s_flags & SB_ACTIVE))
+ goto out_pending;
+
+ event = mempool_alloc(&fserror_events_pool, gfp_flags);
+ if (!event)
+ goto out_pending;
+
+ /* mempool_alloc doesn't support GFP_ZERO */
+ memset(event, 0, sizeof(*event));
+ event->sb = sb;
+ INIT_WORK(&event->work, fserror_worker);
+
+ return event;
+
+out_pending:
+ fserror_pending_dec(sb);
+ return NULL;
+}
+
+/**
+ * fserror_report - report a filesystem error of some kind
+ *
+ * @sb: superblock of the filesystem
+ * @inode: inode within that filesystem, if applicable
+ * @type: type of error encountered
+ * @pos: start of inode range affected, if applicable
+ * @len: length of inode range affected, if applicable
+ * @error: error number encountered, must be negative
+ * @gfp: memory allocation flags for conveying the event to a worker,
+ * since this function can be called from atomic contexts
+ *
+ * Report details of a filesystem error to the super_operations::report_error
+ * callback if present; and to fsnotify for distribution to userspace. @sb,
+ * @gfp, @type, and @error must all be specified. For file I/O errors, the
+ * @inode, @pos, and @len fields must also be specified. For file metadata
+ * errors, @inode must be specified. If @inode is not NULL, then @inode->i_sb
+ * must point to @sb.
+ *
+ * Reporting work is deferred to a workqueue to ensure that ->report_error is
+ * called from process context without any locks held. An active reference to
+ * the inode is maintained until event handling is complete, and unmount will
+ * wait for queued events to drain.
+ */
+void fserror_report(struct super_block *sb, struct inode *inode,
+ enum fserror_type type, loff_t pos, u64 len, int error,
+ gfp_t gfp)
+{
+ struct fserror_event *event;
+
+ /* sb and inode must be from the same filesystem */
+ WARN_ON_ONCE(inode && inode->i_sb != sb);
+
+ /* error number must be negative */
+ WARN_ON_ONCE(error >= 0);
+
+ event = fserror_alloc_event(sb, gfp);
+ if (!event)
+ goto lost;
+
+ event->type = type;
+ event->pos = pos;
+ event->len = len;
+ event->error = error;
+
+ /*
+ * Can't iput from non-sleeping context, so grabbing another reference
+ * to the inode must be the last thing before submitting the event.
+ */
+ if (inode) {
+ event->inode = igrab(inode);
+ if (!event->inode)
+ goto lost_event;
+ }
+
+ /*
+ * Use schedule_work here even if we're already in process context so
+ * that fsnotify and super_operations::report_error implementations are
+ * guaranteed to run in process context without any locks held. Since
+ * errors are supposed to be rare, the overhead shouldn't kill us any
+ * more than the failing device will.
+ */
+ schedule_work(&event->work);
+ return;
+
+lost_event:
+ fserror_free_event(event);
+lost:
+ if (inode)
+ pr_err_ratelimited(
+ "%s: lost file I/O error report for ino %llu type %u pos 0x%llx len 0x%llx error %d",
+ sb->s_id, inode->i_ino, type, pos, len, error);
+ else
+ pr_err_ratelimited(
+ "%s: lost filesystem error report for type %u error %d",
+ sb->s_id, type, error);
+}
+EXPORT_SYMBOL_GPL(fserror_report);
+
+static int __init fserror_init(void)
+{
+ return mempool_init_kmalloc_pool(&fserror_events_pool,
+ FSERROR_DEFAULT_EVENT_POOL_SIZE,
+ sizeof(struct fserror_event));
+}
+fs_initcall(fserror_init);