@@ -30,6 +30,11 @@ xfs_scrub_should_terminate(
struct xfs_scrub_context *sc,
int *error)
{
+ if (sc->is_oom) {
+ if (*error == 0)
+ *error = -ENOMEM;
+ return true;
+ }
if (fatal_signal_pending(current)) {
if (*error == 0)
*error = -EAGAIN;
@@ -49,6 +49,8 @@
#include "scrub/scrub.h"
#include "scrub/btree.h"
+#include <linux/oom.h>
+
/*
* Online Scrub and Repair
*
@@ -156,6 +158,9 @@ xfs_scrub_teardown(
struct xfs_inode *ip_in,
int error)
{
+ if (sc->oom_notify.notifier_call)
+ unregister_oom_notifier(&sc->oom_notify);
+
xfs_scrub_ag_free(sc, &sc->sa);
if (sc->tp) {
xfs_trans_cancel(sc->tp);
@@ -295,6 +300,21 @@ xfs_scrub_experimental_warning(
"EXPERIMENTAL online scrub feature in use. Use at your own risk!");
}
+/* Uhoh, an OOM kill happened, try to kill any running scrubs. */
+static int
+xfs_scrub_oom_kill(
+ struct notifier_block *notifier,
+ unsigned long action,
+ void *data)
+{
+ struct xfs_scrub_context *sc;
+
+ sc = container_of(notifier, struct xfs_scrub_context, oom_notify);
+ sc->is_oom = true;
+
+ return NOTIFY_DONE;
+}
+
/* Dispatch metadata scrubbing. */
int
xfs_scrub_metadata(
@@ -366,6 +386,13 @@ xfs_scrub_metadata(
sc.ops = ops;
sc.try_harder = try_harder;
sc.sa.agno = NULLAGNUMBER;
+ sc.oom_notify.notifier_call = xfs_scrub_oom_kill;
+ sc.oom_notify.priority = 1; /* call us first */
+ error = register_oom_notifier(&sc.oom_notify);
+ if (error) {
+ sc.oom_notify.notifier_call = NULL;
+ goto out_teardown;
+ }
error = sc.ops->setup(&sc, ip);
if (error)
goto out_teardown;
@@ -63,6 +63,10 @@ struct xfs_scrub_context {
uint ilock_flags;
bool try_harder;
+ /* Kill scrub/repair if we OOM. */
+ struct notifier_block oom_notify;
+ bool is_oom;
+
/* State tracking for single-AG operations. */
struct xfs_scrub_ag sa;
};