summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mm/page_alloc.c15
1 files changed, 9 insertions, 6 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 610a3db680ae..8af0d4fa683d 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -2341,7 +2341,16 @@ void drain_local_pages(struct zone *zone)
static void drain_local_pages_wq(struct work_struct *work)
{
+ /*
+ * drain_all_pages doesn't use proper cpu hotplug protection so
+ * we can race with cpu offline when the WQ can move this from
+ * a cpu pinned worker to an unbound one. We can operate on a different
+ * cpu which is allright but we also have to make sure to not move to
+ * a different one.
+ */
+ preempt_disable();
drain_local_pages(NULL);
+ preempt_enable();
}
/*
@@ -2366,11 +2375,6 @@ void drain_all_pages(struct zone *zone)
if (current->flags & PF_WQ_WORKER)
return;
- /*
- * As this can be called from reclaim context, do not reenter reclaim.
- * An allocation failure can be handled, it's simply slower
- */
- get_online_cpus();
works = alloc_percpu_gfp(struct work_struct, GFP_ATOMIC);
/*
@@ -2421,7 +2425,6 @@ void drain_all_pages(struct zone *zone)
flush_work(&work);
}
}
- put_online_cpus();
}
#ifdef CONFIG_HIBERNATION