2013-08-12 15:47:24

by Toshi Kani

[permalink] [raw]
Subject: [PATCH 0/2] Protect cpu map updates from ACPI processor

CPU system maps are protected with reader/writer locks. The reader
lock, put_online_cpus(), assures that the maps are not updated while
holding the lock. The writer lock, cpu_hotplug_begin(), is used to
udpate the cpu maps along with cpu_maps_update_begin().

However, the ACPI processor handler updates the cpu maps without
holding the the writer lock. This patchset fixes this problem.

---
The patchset is based on linux-next of the pm tree.

---
Toshi Kani (2):
CPU hotplug: Export cpu_hotplug_begin() interface
ACPI: Acquire writer lock to update cpu maps

---
drivers/acpi/acpi_processor.c | 21 ++++++++++++++++-----
include/linux/cpu.h | 2 ++
kernel/cpu.c | 4 ++--
3 files changed, 20 insertions(+), 7 deletions(-)


2013-08-12 15:47:35

by Toshi Kani

[permalink] [raw]
Subject: [PATCH 2/2] ACPI: Acquire writer lock to update cpu maps

acpi_map_lsapic() is called from acpi_processor_hotadd_init() to update
cpu_possible_mask and cpu_present_mask. acpi_unmap_lsapic() is called
from acpi_processor_remove() to update cpu_possible_mask. Currently,
they are either unprotected or protected with the reader lock, which is
not correct.

This patch protects them with the writer lock with cpu_hotplug_begin()
along with cpu_maps_update_begin(), which must be held before calling
cpu_hotplug_begin(). It also protects arch_register_cpu() /
arch_unregister_cpu(), which creates / deletes a sysfs cpu device
interface.

Signed-off-by: Toshi Kani <[email protected]>
---
drivers/acpi/acpi_processor.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
index 5a74a9c..f29e06e 100644
--- a/drivers/acpi/acpi_processor.c
+++ b/drivers/acpi/acpi_processor.c
@@ -178,14 +178,17 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr)
if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT))
return -ENODEV;

+ cpu_maps_update_begin();
+ cpu_hotplug_begin();
+
ret = acpi_map_lsapic(pr->handle, &pr->id);
if (ret)
- return ret;
+ goto out;

ret = arch_register_cpu(pr->id);
if (ret) {
acpi_unmap_lsapic(pr->id);
- return ret;
+ goto out;
}

/*
@@ -195,7 +198,11 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr)
*/
pr_info("CPU%d has been hot-added\n", pr->id);
pr->flags.need_hotplug_init = 1;
- return 0;
+
+out:
+ cpu_hotplug_done();
+ cpu_maps_update_done();
+ return ret;
}
#else
static inline int acpi_processor_hotadd_init(struct acpi_processor *pr)
@@ -452,11 +459,15 @@ static void acpi_processor_remove(struct acpi_device *device)
per_cpu(processor_device_array, pr->id) = NULL;
per_cpu(processors, pr->id) = NULL;

+ cpu_maps_update_begin();
+ cpu_hotplug_begin();
+
/* Remove the CPU. */
- get_online_cpus();
arch_unregister_cpu(pr->id);
acpi_unmap_lsapic(pr->id);
- put_online_cpus();
+
+ cpu_hotplug_done();
+ cpu_maps_update_done();

try_offline_node(cpu_to_node(pr->id));

2013-08-12 15:47:31

by Toshi Kani

[permalink] [raw]
Subject: [PATCH 1/2] CPU hotplug: Export cpu_hotplug_begin() interface

cpu_hotplug_begin() and cpu_hotplug_done() are the interfaces to
acquire the writer lock of the cpu_hotplug.lock. The interfaces are
used along with cpu_maps_update_begin() / cpu_maps_update_done() to
serialize the updates to the system cpu maps, such as cpu_online_mask
and cpu_present_mask.

cpu_present_mask is updated by the ACPI processor handler. Therefore,
this patch changes the interfaces to global, and exports them in cpu.h.

Signed-off-by: Toshi Kani <[email protected]>
---
include/linux/cpu.h | 4 ++++
kernel/cpu.c | 9 +++------
2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index ab0eade..956c0a1 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -172,6 +172,8 @@ extern struct bus_type cpu_subsys;
#ifdef CONFIG_HOTPLUG_CPU
/* Stop CPUs going up and down. */

+extern void cpu_hotplug_begin(void);
+extern void cpu_hotplug_done(void);
extern void get_online_cpus(void);
extern void put_online_cpus(void);
extern void cpu_hotplug_disable(void);
@@ -197,6 +199,8 @@ static inline void cpu_hotplug_driver_unlock(void)

#else /* CONFIG_HOTPLUG_CPU */

+static inline void cpu_hotplug_begin(void) {}
+static inline void cpu_hotplug_done(void) {}
#define get_online_cpus() do { } while (0)
#define put_online_cpus() do { } while (0)
#define cpu_hotplug_disable() do { } while (0)
diff --git a/kernel/cpu.c b/kernel/cpu.c
index b2b227b..d7f07a2 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -113,7 +113,7 @@ EXPORT_SYMBOL_GPL(put_online_cpus);
* get_online_cpus() not an api which is called all that often.
*
*/
-static void cpu_hotplug_begin(void)
+void cpu_hotplug_begin(void)
{
cpu_hotplug.active_writer = current;

@@ -127,7 +127,7 @@ static void cpu_hotplug_begin(void)
}
}

-static void cpu_hotplug_done(void)
+void cpu_hotplug_done(void)
{
cpu_hotplug.active_writer = NULL;
mutex_unlock(&cpu_hotplug.lock);
@@ -154,10 +154,7 @@ void cpu_hotplug_enable(void)
cpu_maps_update_done();
}

-#else /* #if CONFIG_HOTPLUG_CPU */
-static void cpu_hotplug_begin(void) {}
-static void cpu_hotplug_done(void) {}
-#endif /* #else #if CONFIG_HOTPLUG_CPU */
+#endif /* CONFIG_HOTPLUG_CPU */

/* Need to know about CPUs going up/down? */
int __ref register_cpu_notifier(struct notifier_block *nb)

2013-08-13 00:33:09

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 0/2] Protect cpu map updates from ACPI processor

On Monday, August 12, 2013 09:45:52 AM Toshi Kani wrote:
> CPU system maps are protected with reader/writer locks. The reader
> lock, put_online_cpus(), assures that the maps are not updated while
> holding the lock. The writer lock, cpu_hotplug_begin(), is used to
> udpate the cpu maps along with cpu_maps_update_begin().
>
> However, the ACPI processor handler updates the cpu maps without
> holding the the writer lock. This patchset fixes this problem.
>
> ---
> The patchset is based on linux-next of the pm tree.

Basically looks OK to me, but I'd just merge the patches together,
because the [2/2] is the very reason for the change made by [1/2].

Also it would be good to mention the actual scenario that may lead
to problems with that.

Thanks,
Rafael


> ---
> Toshi Kani (2):
> CPU hotplug: Export cpu_hotplug_begin() interface
> ACPI: Acquire writer lock to update cpu maps
>
> ---
> drivers/acpi/acpi_processor.c | 21 ++++++++++++++++-----
> include/linux/cpu.h | 2 ++
> kernel/cpu.c | 4 ++--
> 3 files changed, 20 insertions(+), 7 deletions(-)
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

2013-08-13 00:56:20

by Toshi Kani

[permalink] [raw]
Subject: Re: [PATCH 0/2] Protect cpu map updates from ACPI processor

On Tue, 2013-08-13 at 02:43 +0200, Rafael J. Wysocki wrote:
> On Monday, August 12, 2013 09:45:52 AM Toshi Kani wrote:
> > CPU system maps are protected with reader/writer locks. The reader
> > lock, put_online_cpus(), assures that the maps are not updated while
> > holding the lock. The writer lock, cpu_hotplug_begin(), is used to
> > udpate the cpu maps along with cpu_maps_update_begin().
> >
> > However, the ACPI processor handler updates the cpu maps without
> > holding the the writer lock. This patchset fixes this problem.
> >
> > ---
> > The patchset is based on linux-next of the pm tree.
>
> Basically looks OK to me, but I'd just merge the patches together,
> because the [2/2] is the very reason for the change made by [1/2].

I separated for reviewing purpose, but yes, they can be together. Let
me know if you need me to re-send it together.

> Also it would be good to mention the actual scenario that may lead
> to problems with that.

For example, the get_online_cpus() below is supposed to assure that
cpu_possible_mask is not changed while the code is iterating with
for_each_possible_cpu().

get_online_cpus();
for_each_possible_cpu(cpu) {
:
}
put_online_cpus();

However, this lock has no protection with CPU hotplug since the ACPI
processor handler does not use the writer lock when it updates
cpu_possible_mask. The reader lock does not serialize within the
readers.

Thanks,
-Toshi


2013-08-13 11:37:29

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 0/2] Protect cpu map updates from ACPI processor

On Monday, August 12, 2013 06:55:03 PM Toshi Kani wrote:
> On Tue, 2013-08-13 at 02:43 +0200, Rafael J. Wysocki wrote:
> > On Monday, August 12, 2013 09:45:52 AM Toshi Kani wrote:
> > > CPU system maps are protected with reader/writer locks. The reader
> > > lock, put_online_cpus(), assures that the maps are not updated while
> > > holding the lock. The writer lock, cpu_hotplug_begin(), is used to
> > > udpate the cpu maps along with cpu_maps_update_begin().
> > >
> > > However, the ACPI processor handler updates the cpu maps without
> > > holding the the writer lock. This patchset fixes this problem.
> > >
> > > ---
> > > The patchset is based on linux-next of the pm tree.
> >
> > Basically looks OK to me, but I'd just merge the patches together,
> > because the [2/2] is the very reason for the change made by [1/2].
>
> I separated for reviewing purpose, but yes, they can be together. Let
> me know if you need me to re-send it together.

I combined them and added a changelog built out of the messages you sent in
this thread. Please check:

http://git.kernel.org/cgit/linux/kernel/git/rafael/linux-pm.git/commit/?h=bleeding-edge&id=b9d10be7a8e88fdcb12540387c219cdde87b0795

Thanks,
Rafael

2013-08-13 14:54:10

by Toshi Kani

[permalink] [raw]
Subject: Re: [PATCH 0/2] Protect cpu map updates from ACPI processor

On Tue, 2013-08-13 at 13:47 +0200, Rafael J. Wysocki wrote:
> On Monday, August 12, 2013 06:55:03 PM Toshi Kani wrote:
> > On Tue, 2013-08-13 at 02:43 +0200, Rafael J. Wysocki wrote:
> > > On Monday, August 12, 2013 09:45:52 AM Toshi Kani wrote:
> > > > CPU system maps are protected with reader/writer locks. The reader
> > > > lock, put_online_cpus(), assures that the maps are not updated while
> > > > holding the lock. The writer lock, cpu_hotplug_begin(), is used to
> > > > udpate the cpu maps along with cpu_maps_update_begin().
> > > >
> > > > However, the ACPI processor handler updates the cpu maps without
> > > > holding the the writer lock. This patchset fixes this problem.
> > > >
> > > > ---
> > > > The patchset is based on linux-next of the pm tree.
> > >
> > > Basically looks OK to me, but I'd just merge the patches together,
> > > because the [2/2] is the very reason for the change made by [1/2].
> >
> > I separated for reviewing purpose, but yes, they can be together. Let
> > me know if you need me to re-send it together.
>
> I combined them and added a changelog built out of the messages you sent in
> this thread. Please check:
>
> http://git.kernel.org/cgit/linux/kernel/git/rafael/linux-pm.git/commit/?h=bleeding-edge&id=b9d10be7a8e88fdcb12540387c219cdde87b0795
>

Looks good!
Thanks,
-Toshi