Subsystems that want to register CPU hotplug callbacks, as well as perform
initialization for the CPUs that are already online, often do it as shown
below:

        get_online_cpus();

        for_each_online_cpu(cpu)
                init_cpu(cpu);

        register_cpu_notifier(&foobar_cpu_notifier);

        put_online_cpus();

This is wrong, since it is prone to ABBA deadlocks involving the
cpu_add_remove_lock and the cpu_hotplug.lock (when running concurrently
with CPU hotplug operations).

Instead, the correct and race-free way of performing the callback
registration is:

        cpu_notifier_register_begin();

        for_each_online_cpu(cpu)
                init_cpu(cpu);

        /* Note the use of the double underscored version of the API */
        __register_cpu_notifier(&foobar_cpu_notifier);

        cpu_notifier_register_done();


Fix the zsmalloc code by using this latter form of callback registration.

Cc: Minchan Kim <minc...@kernel.org>
Cc: Nitin Gupta <ngu...@vflare.org>
Cc: Ingo Molnar <mi...@kernel.org>
Cc: linux...@kvack.org
Signed-off-by: Srivatsa S. Bhat <srivatsa.b...@linux.vnet.ibm.com>
---

 mm/zsmalloc.c |   17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index c03ca5e..36b4591 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -814,21 +814,32 @@ static void zs_exit(void)
 {
        int cpu;
 
+       cpu_notifier_register_begin();
+
        for_each_online_cpu(cpu)
                zs_cpu_notifier(NULL, CPU_DEAD, (void *)(long)cpu);
-       unregister_cpu_notifier(&zs_cpu_nb);
+       __unregister_cpu_notifier(&zs_cpu_nb);
+
+       cpu_notifier_register_done();
 }
 
 static int zs_init(void)
 {
        int cpu, ret;
 
-       register_cpu_notifier(&zs_cpu_nb);
+       cpu_notifier_register_begin();
+
+       __register_cpu_notifier(&zs_cpu_nb);
        for_each_online_cpu(cpu) {
                ret = zs_cpu_notifier(NULL, CPU_UP_PREPARE, (void *)(long)cpu);
-               if (notifier_to_errno(ret))
+               if (notifier_to_errno(ret)) {
+                       cpu_notifier_register_done();
                        goto fail;
+               }
        }
+
+       cpu_notifier_register_done();
+
        return 0;
 fail:
        zs_exit();

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to