diff options
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/Kconfig | 6 | ||||
-rw-r--r-- | fs/xfs/xfs_mount.c | 42 | ||||
-rw-r--r-- | fs/xfs/xfs_mount.h | 59 |
3 files changed, 107 insertions, 0 deletions
diff --git a/fs/xfs/Kconfig b/fs/xfs/Kconfig index 6077ac04c0c3..db60944ab3c3 100644 --- a/fs/xfs/Kconfig +++ b/fs/xfs/Kconfig @@ -97,11 +97,17 @@ config XFS_DRAIN_INTENTS bool select JUMP_LABEL if HAVE_ARCH_JUMP_LABEL +config XFS_LIVE_HOOKS + bool + select JUMP_LABEL if HAVE_ARCH_JUMP_LABEL + config XFS_ONLINE_SCRUB bool "XFS online metadata check support" default n depends on XFS_FS depends on TMPFS && SHMEM + depends on SRCU + select XFS_LIVE_HOOKS select XFS_DRAIN_INTENTS help If you say Y here you will be able to check metadata on a diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index e427202c3ab2..38deae942ed1 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1490,3 +1490,45 @@ xfs_ag_intents_busy( } #endif /* CONFIG_XFS_DRAIN_INTENTS */ + +#ifdef CONFIG_XFS_LIVE_HOOKS +/* Initialize a notifier chain. */ +void +xfs_hooks_init( + struct xfs_hooks *chain) +{ + srcu_init_notifier_head(&chain->head); +} + +/* Make it so a function gets called whenever we hit a certain hook point. */ +int +xfs_hooks_add( + struct xfs_hooks *chain, + struct xfs_hook *hook) +{ + ASSERT(hook->nb.notifier_call != NULL); + BUILD_BUG_ON(offsetof(struct xfs_hook, nb) != 0); + + return srcu_notifier_chain_register(&chain->head, &hook->nb); +} + +/* Remove a previously installed hook. */ +void +xfs_hooks_del( + struct xfs_hooks *chain, + struct xfs_hook *hook) +{ + srcu_notifier_chain_unregister(&chain->head, &hook->nb); + rcu_barrier(); +} + +/* Call a hook. Returns the NOTIFY_* value returned by the last hook. */ +int +xfs_hooks_call( + struct xfs_hooks *chain, + unsigned long val, + void *priv) +{ + return srcu_notifier_call_chain(&chain->head, val, priv); +} +#endif /* CONFIG_XFS_LIVE_HOOKS */ diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index ba7888c4f650..a9eb5e4cf153 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -56,6 +56,65 @@ struct xfs_error_cfg { long retry_timeout; /* in jiffies, -1 = infinite */ }; +#ifdef CONFIG_XFS_LIVE_HOOKS +struct xfs_hooks { + struct srcu_notifier_head head; +}; +#else +struct xfs_hooks { /* empty */ }; +#endif + +/* + * If hooks and jump labels are enabled, we use jump labels (aka patching of + * the code segment) to avoid the minute overhead of calling an empty notifier + * chain when we know there are no callers. If hooks are enabled without jump + * labels, hardwire the predicate to true because calling an empty srcu + * notifier chain isn't so expensive. + */ +#if defined(CONFIG_JUMP_LABEL) && defined(CONFIG_XFS_LIVE_HOOKS) +# define DEFINE_STATIC_XFS_HOOK_SWITCH(name) \ + static DEFINE_STATIC_KEY_FALSE(name) +# define xfs_hooks_switch_on(name) static_branch_inc(name) +# define xfs_hooks_switch_off(name) static_branch_dec(name) +# define xfs_hooks_switched_on(name) static_branch_unlikely(name) +#elif defined(CONFIG_XFS_LIVE_HOOKS) +# define DEFINE_STATIC_XFS_HOOK_SWITCH(name) +# define xfs_hooks_switch_on(name) ((void)0) +# define xfs_hooks_switch_off(name) ((void)0) +# define xfs_hooks_switched_on(name) (true) +#else +# define DEFINE_STATIC_XFS_HOOK_SWITCH(name) +# define xfs_hooks_switch_on(name) ((void)0) +# define xfs_hooks_switch_off(name) ((void)0) +# define xfs_hooks_switched_on(name) (false) +#endif /* JUMP_LABEL && XFS_LIVE_HOOKS */ + +#ifdef CONFIG_XFS_LIVE_HOOKS +struct xfs_hook { + /* This must come at the start of the structure. */ + struct notifier_block nb; +}; + +typedef int (*xfs_hook_fn_t)(struct xfs_hook *hook, unsigned long action, + void *data); + +void xfs_hooks_init(struct xfs_hooks *chain); +int xfs_hooks_add(struct xfs_hooks *chain, struct xfs_hook *hook); +void xfs_hooks_del(struct xfs_hooks *chain, struct xfs_hook *hook); +int xfs_hooks_call(struct xfs_hooks *chain, unsigned long action, + void *priv); + +static inline void xfs_hook_setup(struct xfs_hook *hook, xfs_hook_fn_t fn) +{ + hook->nb.notifier_call = (notifier_fn_t)fn; + hook->nb.priority = 0; +} + +#else +# define xfs_hooks_init(chain) ((void)0) +# define xfs_hooks_call(chain, val, priv) (NOTIFY_DONE) +#endif + #ifdef CONFIG_XFS_DRAIN_INTENTS /* * Passive drain mechanism. This data structure tracks a count of some items |