All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
To: Thomas Gleixner <tglx@linutronix.de>
Cc: LKML <linux-kernel@vger.kernel.org>,
	Peter Zijlstra <peterz@infradead.org>,
	Ingo Molnar <mingo@kernel.org>,
	"Srivatsa S. Bhat" <srivatsa.bhat@linux.vnet.ibm.com>,
	Rusty Russell <rusty@rustcorp.com.au>, Tejun Heo <tj@kernel.org>
Subject: Re: [RFC patch 2/5] smpboot: Provide infrastructure for percpu hotplug threads
Date: Wed, 13 Jun 2012 11:33:01 -0700	[thread overview]
Message-ID: <20120613183301.GE2427@linux.vnet.ibm.com> (raw)
In-Reply-To: <20120613105815.206105518@linutronix.de>

On Wed, Jun 13, 2012 at 11:00:54AM -0000, Thomas Gleixner wrote:
> Provide a generic interface for setting up and tearing down percpu
> threads.
> 
> On registration the threads for already online cpus are created and
> started. On deregistration (modules) the threads are stoppped.
> 
> During hotplug operations the threads are created, started, parked and
> unparked. The datastructure for registration provides a pointer to
> percpu storage space and optional setup, cleanup, park, unpark
> functions. These functions are called when the thread state changes.
> 
> Thread functions should look like the following:
> 
> int thread_fn(void *cookie)
> {
> 	while (!smpboot_thread_check_parking(cookie)) {
> 		do_stuff();
> 	}
> 	return 0;
> }

Very cool!!!

So I am currently trying to apply this to RCU's per-CPU kthread.
I don't believe that I need to mess with RCU's per-rcu_node kthread
because it can just have its affinity adjusted when the first CPU
onlines and the last CPU offlines for the corresponding rcu_node.

One question below about the order of parking.

Also, I have not yet figured out how this avoids a parked thread waking
up while the CPU is offline, but I am probably still missing something.

							Thanx, Paul

> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> ---
>  include/linux/smpboot.h |   40 +++++++++
>  kernel/cpu.c            |    6 +
>  kernel/smpboot.c        |  205 ++++++++++++++++++++++++++++++++++++++++++++++++
>  kernel/smpboot.h        |    4 
>  4 files changed, 255 insertions(+)
> 
> Index: tip/include/linux/smpboot.h
> ===================================================================
> --- /dev/null
> +++ tip/include/linux/smpboot.h
> @@ -0,0 +1,40 @@
> +#ifndef _LINUX_SMPBOOT_H
> +#define _LINUX_SMPBOOT_H
> +
> +#include <linux/types.h>
> +
> +struct task_struct;
> +/* Cookie handed to the thread_fn*/
> +struct smpboot_thread_data;
> +
> +/**
> + * struct smp_hotplug_thread - CPU hotplug related thread descriptor
> + * @store:		Pointer to per cpu storage for the task pointers
> + * @list:		List head for core management
> + * @thread_fn:		The associated thread function
> + * @setup:		Optional setup function, called when the thread gets
> + *			operational the first time
> + * @cleanup:		Optional cleanup function, called when the thread
> + *			should stop (module exit)
> + * @park:		Optional park function, called when the thread is
> + *			parked (cpu offline)
> + * @unpark:		Optional unpark function, called when the thread is
> + *			unparked (cpu online)
> + * @thread_comm:	The base name of the thread
> + */
> +struct smp_hotplug_thread {
> +	struct task_struct __percpu	**store;
> +	struct list_head		list;
> +	int				(*thread_fn)(void *data);
> +	void				(*setup)(unsigned int cpu);
> +	void				(*cleanup)(unsigned int cpu, bool online);
> +	void				(*park)(unsigned int cpu);
> +	void				(*unpark)(unsigned int cpu);
> +	const char			*thread_comm;
> +};
> +
> +int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread);
> +void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread);
> +int smpboot_thread_check_parking(struct smpboot_thread_data *td);
> +
> +#endif
> Index: tip/kernel/cpu.c
> ===================================================================
> --- tip.orig/kernel/cpu.c
> +++ tip/kernel/cpu.c
> @@ -280,6 +280,7 @@ static int __ref _cpu_down(unsigned int 
>  				__func__, cpu);
>  		goto out_release;
>  	}
> +	smpboot_park_threads(cpu);
> 
>  	err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu));
>  	if (err) {
> @@ -354,6 +355,10 @@ static int __cpuinit _cpu_up(unsigned in
>  		goto out;
>  	}
> 
> +	ret = smpboot_create_threads(cpu);
> +	if (ret)
> +		goto out;
> +
>  	ret = __cpu_notify(CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls);
>  	if (ret) {
>  		nr_calls--;
> @@ -370,6 +375,7 @@ static int __cpuinit _cpu_up(unsigned in
> 
>  	/* Now call notifier in preparation. */
>  	cpu_notify(CPU_ONLINE | mod, hcpu);
> +	smpboot_unpark_threads(cpu);
> 
>  out_notify:
>  	if (ret != 0)
> Index: tip/kernel/smpboot.c
> ===================================================================
> --- tip.orig/kernel/smpboot.c
> +++ tip/kernel/smpboot.c
> @@ -1,11 +1,17 @@
>  /*
>   * Common SMP CPU bringup/teardown functions
>   */
> +#include <linux/cpu.h>
>  #include <linux/err.h>
>  #include <linux/smp.h>
>  #include <linux/init.h>
> +#include <linux/list.h>
> +#include <linux/slab.h>
>  #include <linux/sched.h>
> +#include <linux/export.h>
>  #include <linux/percpu.h>
> +#include <linux/kthread.h>
> +#include <linux/smpboot.h>
> 
>  #include "smpboot.h"
> 
> @@ -65,3 +71,202 @@ void __init idle_threads_init(void)
>  	}
>  }
>  #endif
> +
> +static LIST_HEAD(hotplug_threads);
> +static DEFINE_MUTEX(smpboot_threads_lock);
> +
> +struct smpboot_thread_data {
> +	unsigned int			cpu;
> +	unsigned int			status;
> +	struct smp_hotplug_thread	*ht;
> +};
> +
> +enum {
> +	HP_THREAD_NONE = 0,
> +	HP_THREAD_ACTIVE,
> +	HP_THREAD_PARKED,
> +};
> +
> +/**
> + * smpboot_thread_check_parking - percpu hotplug thread loop function
> + * @td:		thread data pointer
> + *
> + * Checks for thread stop and park conditions. Calls the necessary
> + * setup, cleanup, park and unpark functions for the registered
> + * thread.
> + *
> + * Returns 1 when the thread should exit, 0 otherwise.
> + */
> +int smpboot_thread_check_parking(struct smpboot_thread_data *td)
> +{
> +	struct smp_hotplug_thread *ht = td->ht;
> +
> +again:
> +	if (kthread_should_stop()) {
> +		if (ht->cleanup)
> +			ht->cleanup(td->cpu, cpu_online(td->cpu));
> +		kfree(td);
> +		return 1;
> +	}
> +
> +	BUG_ON(td->cpu != smp_processor_id());
> +
> +	if (kthread_should_park()) {
> +		if (ht->park && td->status == HP_THREAD_ACTIVE) {
> +			ht->park(td->cpu);
> +			td->status = HP_THREAD_PARKED;
> +		}
> +		kthread_parkme();
> +		/* We might have been woken for stop */
> +		goto again;
> +	}
> +
> +	switch (td->status) {
> +	case HP_THREAD_NONE:
> +		if (ht->setup)
> +			ht->setup(td->cpu);
> +		td->status = HP_THREAD_ACTIVE;
> +		break;
> +	case HP_THREAD_PARKED:
> +		if (ht->unpark)
> +			ht->unpark(td->cpu);
> +		td->status = HP_THREAD_ACTIVE;
> +		break;
> +	}
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(smpboot_thread_check_parking);
> +
> +static int
> +__smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
> +{
> +	struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
> +	struct smpboot_thread_data *td;
> +
> +	if (tsk)
> +		return 0;
> +
> +	td = kzalloc_node(sizeof(*td), GFP_KERNEL, cpu_to_node(cpu));
> +	if (!td)
> +		return -ENOMEM;
> +	td->cpu = cpu;
> +	td->ht = ht;
> +
> +	tsk = kthread_create_on_cpu(ht->thread_fn, td, cpu, ht->thread_comm);
> +	if (IS_ERR(tsk))
> +		return PTR_ERR(tsk);
> +
> +	get_task_struct(tsk);
> +	*per_cpu_ptr(ht->store, cpu) = tsk;
> +	return 0;
> +}
> +
> +int smpboot_create_threads(unsigned int cpu)
> +{
> +	struct smp_hotplug_thread *cur;
> +	int ret = 0;
> +
> +	mutex_lock(&smpboot_threads_lock);
> +	list_for_each_entry(cur, &hotplug_threads, list) {
> +		ret = __smpboot_create_thread(cur, cpu);
> +		if (ret)
> +			break;
> +	}
> +	mutex_unlock(&smpboot_threads_lock);
> +	return ret;
> +}
> +
> +static void smpboot_unpark_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
> +{
> +	struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
> +
> +	kthread_unpark(tsk);
> +}
> +
> +void smpboot_unpark_threads(unsigned int cpu)
> +{
> +	struct smp_hotplug_thread *cur;
> +
> +	mutex_lock(&smpboot_threads_lock);
> +	list_for_each_entry(cur, &hotplug_threads, list)
> +		smpboot_unpark_thread(cur, cpu);
> +	mutex_unlock(&smpboot_threads_lock);
> +}
> +
> +static void smpboot_park_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
> +{
> +	struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
> +
> +	if (tsk)
> +		kthread_park(tsk);
> +}
> +
> +void smpboot_park_threads(unsigned int cpu)
> +{
> +	struct smp_hotplug_thread *cur;
> +
> +	mutex_lock(&smpboot_threads_lock);
> +	list_for_each_entry(cur, &hotplug_threads, list)

Shouldn't this be list_for_each_entry_reverse()?  Yes, the notifiers
still run in the same order for both online and offline, but all uses
of smpboot_park_threads() would be new, so should be OK with the
proper ordering, right?

> +		smpboot_park_thread(cur, cpu);
> +	mutex_unlock(&smpboot_threads_lock);
> +}
> +
> +static void smpboot_destroy_threads(struct smp_hotplug_thread *ht)
> +{
> +	unsigned int cpu;
> +
> +	/* We need to destroy also the parked threads of offline cpus */
> +	for_each_possible_cpu(cpu) {
> +		struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
> +
> +		if (tsk) {
> +			kthread_stop(tsk);
> +			put_task_struct(tsk);
> +			*per_cpu_ptr(ht->store, cpu) = NULL;
> +		}
> +	}
> +}
> +
> +/**
> + * smpboot_register_percpu_thread - Register a per_cpu thread related to hotplug
> + * @plug_thread:	Hotplug thread descriptor
> + *
> + * Creates and starts the threads on all online cpus.
> + */
> +int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread)
> +{
> +	unsigned int cpu;
> +	int ret = 0;
> +
> +	mutex_lock(&smpboot_threads_lock);
> +	for_each_online_cpu(cpu) {
> +		ret = __smpboot_create_thread(plug_thread, cpu);
> +		if (ret) {
> +			smpboot_destroy_threads(plug_thread);
> +			goto out;
> +		}
> +		smpboot_unpark_thread(plug_thread, cpu);
> +	}
> +	list_add(&plug_thread->list, &hotplug_threads);
> +out:
> +	mutex_unlock(&smpboot_threads_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(smpboot_register_percpu_thread);
> +
> +/**
> + * smpboot_unregister_percpu_thread - Unregister a per_cpu thread related to hotplug
> + * @plug_thread:	Hotplug thread descriptor
> + *
> + * Stops all threads on all possible cpus.
> + */
> +void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread)
> +{
> +	get_online_cpus();
> +	mutex_lock(&smpboot_threads_lock);
> +	list_del(&plug_thread->list);
> +	smpboot_destroy_threads(plug_thread);
> +	mutex_unlock(&smpboot_threads_lock);
> +	put_online_cpus();
> +}
> +EXPORT_SYMBOL_GPL(smpboot_unregister_percpu_thread);
> Index: tip/kernel/smpboot.h
> ===================================================================
> --- tip.orig/kernel/smpboot.h
> +++ tip/kernel/smpboot.h
> @@ -13,4 +13,8 @@ static inline void idle_thread_set_boot_
>  static inline void idle_threads_init(void) { }
>  #endif
> 
> +int smpboot_create_threads(unsigned int cpu);
> +void smpboot_park_threads(unsigned int cpu);
> +void smpboot_unpark_threads(unsigned int cpu);
> +
>  #endif
> 
> 


  reply	other threads:[~2012-06-13 18:33 UTC|newest]

Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-06-13 11:00 [RFC patch 0/5] Per cpu thread hotplug infrastructure Thomas Gleixner
2012-06-13 11:00 ` [RFC patch 2/5] smpboot: Provide infrastructure for percpu hotplug threads Thomas Gleixner
2012-06-13 18:33   ` Paul E. McKenney [this message]
2012-06-13 18:56     ` Thomas Gleixner
2012-06-14  8:08       ` Peter Zijlstra
2012-06-14  8:17         ` Peter Zijlstra
2012-06-15  1:53           ` Tejun Heo
2012-06-15  9:58             ` Peter Zijlstra
2012-06-14  8:37         ` Thomas Gleixner
2012-06-14  8:38           ` Peter Zijlstra
2012-06-13 18:57   ` Paul E. McKenney
2012-06-13 19:02     ` Thomas Gleixner
2012-06-13 19:17       ` Paul E. McKenney
2012-06-13 20:47         ` Paul E. McKenney
2012-06-14  4:51           ` Paul E. McKenney
2012-06-14 11:20             ` Thomas Gleixner
2012-06-14 12:59               ` Paul E. McKenney
2012-06-14 13:01                 ` Peter Zijlstra
2012-06-14 14:47                   ` Paul E. McKenney
2012-06-14 14:56                     ` Peter Zijlstra
2012-06-14 15:07                       ` Thomas Gleixner
2012-06-14 15:45                       ` Paul E. McKenney
2012-06-14 16:20                         ` Thomas Gleixner
2012-06-14 13:32                 ` Thomas Gleixner
2012-06-14 13:47                   ` Thomas Gleixner
2012-06-14 14:12                     ` Thomas Gleixner
2012-06-14 15:01                       ` Paul E. McKenney
2012-06-14 15:08                         ` Thomas Gleixner
2012-06-14 14:50                   ` Paul E. McKenney
2012-06-14 15:02                     ` Thomas Gleixner
2012-06-14 15:38                     ` Paul E. McKenney
2012-06-14 16:19                       ` Thomas Gleixner
2012-06-14 16:48                         ` Paul E. McKenney
2012-06-14 22:40             ` Paul E. McKenney
2012-06-14  8:31   ` Srivatsa S. Bhat
2012-06-14  8:44     ` Thomas Gleixner
2012-06-18  8:47   ` Cyrill Gorcunov
2012-06-18  8:50     ` Thomas Gleixner
2012-06-13 11:00 ` [RFC patch 1/5] kthread: Implement park/unpark facility Thomas Gleixner
2012-06-14  2:32   ` Namhyung Kim
2012-06-14  8:35     ` Thomas Gleixner
2012-06-14  8:59       ` Thomas Gleixner
2012-06-14  8:36   ` Srivatsa S. Bhat
2012-06-14 20:01   ` Silas Boyd-Wickizer
2012-06-14 20:13     ` Thomas Gleixner
2012-06-14 22:13   ` Silas Boyd-Wickizer
2012-06-15  1:44     ` Tejun Heo
2012-06-13 11:00 ` [RFC patch 3/5] softirq: Use hotplug thread infrastructure Thomas Gleixner
2012-06-13 11:00 ` [RFC patch 4/5] watchdog: " Thomas Gleixner
2012-06-13 11:00 ` [RFC patch 5/5] infiniband: ehca: " Thomas Gleixner
2012-06-18  6:30   ` Rusty Russell
2012-06-18  8:08     ` Thomas Gleixner
2012-06-24 10:29       ` Thomas Gleixner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20120613183301.GE2427@linux.vnet.ibm.com \
    --to=paulmck@linux.vnet.ibm.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@kernel.org \
    --cc=peterz@infradead.org \
    --cc=rusty@rustcorp.com.au \
    --cc=srivatsa.bhat@linux.vnet.ibm.com \
    --cc=tglx@linutronix.de \
    --cc=tj@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.