summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/Kconfig33
-rw-r--r--fs/xfs/xfs_hooks.c41
-rw-r--r--fs/xfs/xfs_hooks.h6
3 files changed, 78 insertions, 2 deletions
diff --git a/fs/xfs/Kconfig b/fs/xfs/Kconfig
index db60944ab3c3..54806c2b80d4 100644
--- a/fs/xfs/Kconfig
+++ b/fs/xfs/Kconfig
@@ -106,7 +106,6 @@ config XFS_ONLINE_SCRUB
default n
depends on XFS_FS
depends on TMPFS && SHMEM
- depends on SRCU
select XFS_LIVE_HOOKS
select XFS_DRAIN_INTENTS
help
@@ -122,6 +121,38 @@ config XFS_ONLINE_SCRUB
If unsure, say N.
+choice
+ prompt "XFS hook implementation"
+ depends on XFS_FS && XFS_LIVE_HOOKS && XFS_ONLINE_SCRUB
+ default XFS_LIVE_HOOKS_BLOCKING if HAVE_ARCH_JUMP_LABEL
+ default XFS_LIVE_HOOKS_SRCU if !HAVE_ARCH_JUMP_LABEL
+ help
+ Pick one
+
+config XFS_LIVE_HOOKS_SRCU
+ bool "SRCU notifier chains"
+ depends on SRCU
+ help
+ Use SRCU notifier chains for filesystem hooks. These have very low
+ overhead for event initiators (the main filesystem) and higher
+ overhead for chain modifiers (scrub waits for RCU grace). This is
+ the best option when jump labels are not supported or there are many
+ CPUs in the system.
+
+ This may cause problems with CPU hotplug invoking reclaim invoking
+ XFS.
+
+config XFS_LIVE_HOOKS_BLOCKING
+ bool "Blocking notifier chains"
+ help
+ Use blocking notifier chains for filesystem hooks. These have medium
+ overhead for event initiators (the main fs) and chain modifiers
+ (scrub) due to their use of rwsems. This is the best option when
+ jump labels can be used to eliminate overhead for the filesystem when
+ scrub is not running.
+
+endchoice
+
config XFS_ONLINE_REPAIR
bool "XFS online metadata repair support"
default n
diff --git a/fs/xfs/xfs_hooks.c b/fs/xfs/xfs_hooks.c
index 3f958ece0dc0..653fc1f82516 100644
--- a/fs/xfs/xfs_hooks.c
+++ b/fs/xfs/xfs_hooks.c
@@ -12,6 +12,7 @@
#include "xfs_ag.h"
#include "xfs_trace.h"
+#if defined(CONFIG_XFS_LIVE_HOOKS_SRCU)
/* Initialize a notifier chain. */
void
xfs_hooks_init(
@@ -51,3 +52,43 @@ xfs_hooks_call(
{
return srcu_notifier_call_chain(&chain->head, val, priv);
}
+#elif defined(CONFIG_XFS_LIVE_HOOKS_BLOCKING)
+/* Initialize a notifier chain. */
+void
+xfs_hooks_init(
+ struct xfs_hooks *chain)
+{
+ BLOCKING_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 blocking_notifier_chain_register(&chain->head, &hook->nb);
+}
+
+/* Remove a previously installed hook. */
+void
+xfs_hooks_del(
+ struct xfs_hooks *chain,
+ struct xfs_hook *hook)
+{
+ blocking_notifier_chain_unregister(&chain->head, &hook->nb);
+}
+
+/* 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 blocking_notifier_call_chain(&chain->head, val, priv);
+}
+#endif /* CONFIG_XFS_LIVE_HOOKS_BLOCKING */
diff --git a/fs/xfs/xfs_hooks.h b/fs/xfs/xfs_hooks.h
index 9cd3f6e07751..7e5ef53f5829 100644
--- a/fs/xfs/xfs_hooks.h
+++ b/fs/xfs/xfs_hooks.h
@@ -6,10 +6,14 @@
#ifndef XFS_HOOKS_H_
#define XFS_HOOKS_H_
-#ifdef CONFIG_XFS_LIVE_HOOKS
+#if defined(CONFIG_XFS_LIVE_HOOKS_SRCU)
struct xfs_hooks {
struct srcu_notifier_head head;
};
+#elif defined(CONFIG_XFS_LIVE_HOOKS_BLOCKING)
+struct xfs_hooks {
+ struct blocking_notifier_head head;
+};
#else
struct xfs_hooks { /* empty */ };
#endif