aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2014-01-29 16:51:42 +1100
committerEli Qiao <taget@linux.vnet.ibm.com>2014-01-29 15:34:28 +0800
commit2775d623072188316e8b42cca7ac2e8ba0332edf (patch)
treec645f7fe22d584571274d26a75fc5ac03d443a36
parent76d38fe1553889aee5caba523178edc4d6d0f06e (diff)
downloadpowerkvm-2775d623072188316e8b42cca7ac2e8ba0332edf.tar.gz
md: Avoid deadlock in raid5_alloc_percpu
register_cpu_notifier() can deadlock if called inside a get/put_online_cpus block. To avoid this, move the call to register_cpu_notifier before the get_online_cpus(). [paulus@samba.org - renamed alloc_xxx to alloc_percpu_areas, fixed compile errors, made up patch description] This fixes BZ 103213. Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--drivers/md/raid5.c73
1 files changed, 34 insertions, 39 deletions
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 4bed5454b8dc1..2091f31ba80e9 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -5067,6 +5067,25 @@ static void free_conf(struct r5conf *conf)
kfree(conf);
}
+static int alloc_percpu_areas(struct r5conf *conf, struct raid5_percpu *percpu,
+ int cpu)
+{
+ if (conf->level == 6 && !percpu->spare_page)
+ percpu->spare_page = alloc_page(GFP_KERNEL);
+ if (!percpu->scribble)
+ percpu->scribble = kmalloc(conf->scribble_len, GFP_KERNEL);
+
+ if (!percpu->scribble || (conf->level == 6 && !percpu->spare_page)) {
+ safe_put_page(percpu->spare_page);
+ kfree(percpu->scribble);
+ pr_err("%s: failed memory allocation for cpu%d\n",
+ __func__, cpu);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_HOTPLUG_CPU
static int raid456_cpu_notify(struct notifier_block *nfb, unsigned long action,
void *hcpu)
@@ -5078,19 +5097,8 @@ static int raid456_cpu_notify(struct notifier_block *nfb, unsigned long action,
switch (action) {
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
- if (conf->level == 6 && !percpu->spare_page)
- percpu->spare_page = alloc_page(GFP_KERNEL);
- if (!percpu->scribble)
- percpu->scribble = kmalloc(conf->scribble_len, GFP_KERNEL);
-
- if (!percpu->scribble ||
- (conf->level == 6 && !percpu->spare_page)) {
- safe_put_page(percpu->spare_page);
- kfree(percpu->scribble);
- pr_err("%s: failed memory allocation for cpu%ld\n",
- __func__, cpu);
+ if (alloc_percpu_areas(conf, percpu, cpu))
return notifier_from_errno(-ENOMEM);
- }
break;
case CPU_DEAD:
case CPU_DEAD_FROZEN:
@@ -5109,40 +5117,27 @@ static int raid456_cpu_notify(struct notifier_block *nfb, unsigned long action,
static int raid5_alloc_percpu(struct r5conf *conf)
{
unsigned long cpu;
- struct page *spare_page;
- struct raid5_percpu __percpu *allcpus;
- void *scribble;
- int err;
+ int err = 0;
- allcpus = alloc_percpu(struct raid5_percpu);
- if (!allcpus)
+ conf->percpu = alloc_percpu(struct raid5_percpu);
+ if (!conf->percpu)
return -ENOMEM;
- conf->percpu = allcpus;
- get_online_cpus();
- err = 0;
- for_each_present_cpu(cpu) {
- if (conf->level == 6) {
- spare_page = alloc_page(GFP_KERNEL);
- if (!spare_page) {
- err = -ENOMEM;
- break;
- }
- per_cpu_ptr(conf->percpu, cpu)->spare_page = spare_page;
- }
- scribble = kmalloc(conf->scribble_len, GFP_KERNEL);
- if (!scribble) {
- err = -ENOMEM;
- break;
- }
- per_cpu_ptr(conf->percpu, cpu)->scribble = scribble;
- }
#ifdef CONFIG_HOTPLUG_CPU
conf->cpu_notify.notifier_call = raid456_cpu_notify;
conf->cpu_notify.priority = 0;
- if (err == 0)
- err = register_cpu_notifier(&conf->cpu_notify);
+ err = register_cpu_notifier(&conf->cpu_notify);
+ if (err)
+ return err;
#endif
+
+ get_online_cpus();
+ for_each_present_cpu(cpu) {
+ err = alloc_percpu_areas(conf, per_cpu_ptr(conf->percpu, cpu),
+ cpu);
+ if (err)
+ break;
+ }
put_online_cpus();
return err;