Commit 9ea09af3 authored by Heiko Carstens's avatar Heiko Carstens Committed by Rusty Russell

stop_machine: introduce stop_machine_create/destroy.

Introduce stop_machine_create/destroy. With this interface subsystems
that need a non-failing stop_machine environment can create the
stop_machine machine threads before actually calling stop_machine.
When the threads aren't needed anymore they can be killed with
stop_machine_destroy again.

When stop_machine gets called and the threads aren't present they
will be created and destroyed automatically. This restores the old
behaviour of stop_machine.

This patch also converts cpu hotplug to the new interface since it
is special: cpu_down calls __stop_machine instead of stop_machine.
However the kstop threads will only be created when stop_machine
gets called.

Changing the code so that the threads would be created automatically
on __stop_machine is currently not possible: when __stop_machine gets
called we hold cpu_add_remove_lock, which is the same lock that
create_rt_workqueue would take. So the workqueue needs to be created
before the cpu hotplug code locks cpu_add_remove_lock.
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent c298be74
...@@ -35,6 +35,24 @@ int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus); ...@@ -35,6 +35,24 @@ int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus);
* won't come or go while it's being called. Used by hotplug cpu. * won't come or go while it's being called. Used by hotplug cpu.
*/ */
int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus); int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus);
/**
* stop_machine_create: create all stop_machine threads
*
* Description: This causes all stop_machine threads to be created before
* stop_machine actually gets called. This can be used by subsystems that
* need a non failing stop_machine infrastructure.
*/
int stop_machine_create(void);
/**
* stop_machine_destroy: destroy all stop_machine threads
*
* Description: This causes all stop_machine threads which were created with
* stop_machine_create to be destroyed again.
*/
void stop_machine_destroy(void);
#else #else
static inline int stop_machine(int (*fn)(void *), void *data, static inline int stop_machine(int (*fn)(void *), void *data,
...@@ -46,5 +64,9 @@ static inline int stop_machine(int (*fn)(void *), void *data, ...@@ -46,5 +64,9 @@ static inline int stop_machine(int (*fn)(void *), void *data,
local_irq_enable(); local_irq_enable();
return ret; return ret;
} }
static inline int stop_machine_create(void) { return 0; }
static inline void stop_machine_destroy(void) { }
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
#endif /* _LINUX_STOP_MACHINE */ #endif /* _LINUX_STOP_MACHINE */
...@@ -269,8 +269,11 @@ out_release: ...@@ -269,8 +269,11 @@ out_release:
int __ref cpu_down(unsigned int cpu) int __ref cpu_down(unsigned int cpu)
{ {
int err = 0; int err;
err = stop_machine_create();
if (err)
return err;
cpu_maps_update_begin(); cpu_maps_update_begin();
if (cpu_hotplug_disabled) { if (cpu_hotplug_disabled) {
...@@ -297,6 +300,7 @@ int __ref cpu_down(unsigned int cpu) ...@@ -297,6 +300,7 @@ int __ref cpu_down(unsigned int cpu)
out: out:
cpu_maps_update_done(); cpu_maps_update_done();
stop_machine_destroy();
return err; return err;
} }
EXPORT_SYMBOL(cpu_down); EXPORT_SYMBOL(cpu_down);
......
...@@ -38,7 +38,10 @@ struct stop_machine_data { ...@@ -38,7 +38,10 @@ struct stop_machine_data {
static unsigned int num_threads; static unsigned int num_threads;
static atomic_t thread_ack; static atomic_t thread_ack;
static DEFINE_MUTEX(lock); static DEFINE_MUTEX(lock);
/* setup_lock protects refcount, stop_machine_wq and stop_machine_work. */
static DEFINE_MUTEX(setup_lock);
/* Users of stop_machine. */
static int refcount;
static struct workqueue_struct *stop_machine_wq; static struct workqueue_struct *stop_machine_wq;
static struct stop_machine_data active, idle; static struct stop_machine_data active, idle;
static const cpumask_t *active_cpus; static const cpumask_t *active_cpus;
...@@ -109,6 +112,43 @@ static int chill(void *unused) ...@@ -109,6 +112,43 @@ static int chill(void *unused)
return 0; return 0;
} }
int stop_machine_create(void)
{
mutex_lock(&setup_lock);
if (refcount)
goto done;
stop_machine_wq = create_rt_workqueue("kstop");
if (!stop_machine_wq)
goto err_out;
stop_machine_work = alloc_percpu(struct work_struct);
if (!stop_machine_work)
goto err_out;
done:
refcount++;
mutex_unlock(&setup_lock);
return 0;
err_out:
if (stop_machine_wq)
destroy_workqueue(stop_machine_wq);
mutex_unlock(&setup_lock);
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(stop_machine_create);
void stop_machine_destroy(void)
{
mutex_lock(&setup_lock);
refcount--;
if (refcount)
goto done;
destroy_workqueue(stop_machine_wq);
free_percpu(stop_machine_work);
done:
mutex_unlock(&setup_lock);
}
EXPORT_SYMBOL_GPL(stop_machine_destroy);
int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus)
{ {
struct work_struct *sm_work; struct work_struct *sm_work;
...@@ -146,19 +186,14 @@ int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus) ...@@ -146,19 +186,14 @@ int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus)
{ {
int ret; int ret;
ret = stop_machine_create();
if (ret)
return ret;
/* No CPUs can come up or down during this. */ /* No CPUs can come up or down during this. */
get_online_cpus(); get_online_cpus();
ret = __stop_machine(fn, data, cpus); ret = __stop_machine(fn, data, cpus);
put_online_cpus(); put_online_cpus();
stop_machine_destroy();
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(stop_machine); EXPORT_SYMBOL_GPL(stop_machine);
static int __init stop_machine_init(void)
{
stop_machine_wq = create_rt_workqueue("kstop");
stop_machine_work = alloc_percpu(struct work_struct);
return 0;
}
core_initcall(stop_machine_init);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment