public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Oleg Nesterov <oleg@tv-sign.ru>
To: Gautham R Shenoy <ego@in.ibm.com>
Cc: akpm@linux-foundation.org, paulmck@us.ibm.com,
	torvalds@linux-foundation.org, linux-kernel@vger.kernel.org,
	vatsa@in.ibm.com, "Rafael J. Wysocki" <rjw@sisk.pl>,
	mingo@elte.hu, dipankar@in.ibm.com, dino@in.ibm.com,
	masami.hiramatsu.pt@hitachi.com
Subject: Re: [PATCH 7/8] Clean up workqueue.c with respect to the freezer based cpu-hotplug
Date: Tue, 3 Apr 2007 15:47:29 +0400	[thread overview]
Message-ID: <20070403114729.GA776@tv-sign.ru> (raw)
In-Reply-To: <20070402054206.GG12962@in.ibm.com>

> On 04/02, Gautham R Shenoy wrote:
>
> Clean up workqueue.c from the perspective of freezer-based cpu-hotplug.
> This patch

I'll study these patches later, a couple of comments after the quick reading.

>   This means that all non-singlethreaded workqueues *have* to
>   be frozen to avoid any races.

We can't freeze workqueue if some work_struct continuously re-queues itself.
Perhaps this is not a problem (I don't see a good reason for such a behaviour),
but this should be documented.

>  static int worker_thread(void *__cwq)
>  {
>  	struct cpu_workqueue_struct *cwq = __cwq;
> +	int bind_cpu;
>  	DEFINE_WAIT(wait);
>  	struct k_sigaction sa;
>
>  	freezer_exempt(cwq->wq->freeze_exempt_events);
> -
> +	bind_cpu = smp_processor_id();
>  	/*
>  	 * We inherited MPOL_INTERLEAVE from the booting kernel.
>  	 * Set MPOL_DEFAULT to insure node local allocations.
> @@ -308,20 +287,28 @@ static int worker_thread(void *__cwq)
>  	siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD));
>  	do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0);
>
> -	for (;;) {
> +	while (!kthread_should_stop()) {
>  		try_to_freeze();
> -
> +
> +		if (cpu_is_offline(bind_cpu) && !is_single_threaded(cwq->wq))
> +			goto wait_to_die;
> +
>  		prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE);
> -		if (cwq->status == CWQ_RUNNING && list_empty(&cwq->worklist))
> +		if (list_empty(&cwq->worklist))
>  			schedule();
>  		finish_wait(&cwq->more_work, &wait);
>
> -		if (cwq_should_stop(cwq))
> -			break;
> -
>  		run_workqueue(cwq);
>  	}
>
> +wait_to_die:
> +	set_current_state(TASK_INTERRUPTIBLE);
> +	while(!kthread_should_stop()) {
> +		schedule();
> +		set_current_state(TASK_INTERRUPTIBLE);
> +	}
> +	__set_current_state(TASK_RUNNING);
> +
>  	return 0;
>  }

I still think that wait_to_die + bind_cpu is unneeded complication.
Why can't we do the following:

	static int worker_thread(void *__cwq)
	{
		...

		for (;;) {
			try_to_freeze();

			prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE);
			if (!kthread_should_stop() && list_empty(&cwq->worklist))
				schedule();
			finish_wait(&cwq->more_work, &wait);

			if (kthread_should_stop(cwq))
				break;

			run_workqueue(cwq);
		}

		return 0;
	}

?

>  void fastcall flush_workqueue(struct workqueue_struct *wq)
>  {
> -	const cpumask_t *cpu_map = wq_cpu_map(wq);
>  	int cpu;
>
>  	might_sleep();
> -	for_each_cpu_mask(cpu, *cpu_map)
> +	for_each_online_cpu(cpu)
>  		flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu));
>  }

Hm... I can't understand this change. I believe it is wrong.

> @@ -644,13 +630,6 @@ static int create_workqueue_thread(struc
>  		return PTR_ERR(p);
>
>  	cwq->thread = p;
> -	cwq->status = CWQ_RUNNING;
> -	if (!is_single_threaded(wq))
> -		kthread_bind(p, cpu);
> -
> -	if (is_single_threaded(wq) || cpu_online(cpu))
> -		wake_up_process(p);
> -
>  	return 0;
>  }

Well, this is a matter of taste, but I don't like this change. Now we
should add kthread_bind/wake_up_process calls to __create_workqueue()
and workqueue_cpu_callback(). I won't persist though.

> @@ -680,15 +659,21 @@ static struct workqueue_struct *__create
>  	if (singlethread) {
>  		cwq = init_cpu_workqueue(wq, singlethread_cpu);
>  		err = create_workqueue_thread(cwq, singlethread_cpu);
> +		wake_up_process(cwq->thread);
>  	} else {
>  		mutex_lock(&workqueue_mutex);
>  		list_add(&wq->list, &workqueues);
>
> -		for_each_possible_cpu(cpu) {
> +		for_each_online_cpu(cpu) {

This is wrong. CPU_UP_PREPARE doesn't call init_cpu_workqueue().
Easy to fix, but I personally think is is better to initialize
the whole cpu_possible_map.

>  static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu)
>  {
> -	struct wq_barrier barr;
> -	int alive = 0;
>
> -	spin_lock_irq(&cwq->lock);
>  	if (cwq->thread != NULL) {
> -		insert_wq_barrier(cwq, &barr, 1);
> -		cwq->status = CWQ_SHOULD_STOP;
> -		alive = 1;
> -	}
> -	spin_unlock_irq(&cwq->lock);
> -
> -	if (alive) {
>  		thaw_process(cwq->thread);
> -		wait_for_completion(&barr.done);
> -
> -		while (unlikely(cwq->status != CWQ_STOPPED))
> -			cpu_relax();
> -		/*
> -		 * Wait until cwq->thread unlocks cwq->lock,
> -		 * it won't touch *cwq after that.
> -		 */
> -		smp_rmb();
> +		kthread_stop(cwq->thread);
>  		cwq->thread = NULL;
> -		spin_unlock_wait(&cwq->lock);
>  	}
>  }

Deadlockable. Suppose that the freezing is in progress, cwq->thread is not
frozen yet. cleanup_workqueue_thread() calls thaw_process(cwq->thread),
then cwq->thread() goes to refrigerator, kthread_stop() blocks forever.

> +static void take_over_work(struct workqueue_struct *wq, unsigned int cpu)
> +{
> +	struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu);
> +	struct list_head list;
> +	struct work_struct *work;
> +
> +	spin_lock_irq(&cwq->lock);

This CPU is dead (or cancelled), we don't need cwq->lock.

>  static int __devinit workqueue_cpu_callback(struct notifier_block *nfb,
>  						unsigned long action,
>  						void *hcpu)
> @@ -782,11 +768,6 @@ static int __devinit workqueue_cpu_callb
>  	struct cpu_workqueue_struct *cwq;
>  	struct workqueue_struct *wq;
>
> -	switch (action) {
> -	case CPU_UP_PREPARE:
> -		cpu_set(cpu, cpu_populated_map);
> -	}
> -
>  	mutex_lock(&workqueue_mutex);
>  	list_for_each_entry(wq, &workqueues, list) {
>  		cwq = per_cpu_ptr(wq->cpu_wq, cpu);
> @@ -799,6 +780,7 @@ static int __devinit workqueue_cpu_callb
>  			return NOTIFY_BAD;
>
>  		case CPU_ONLINE:
> +			kthread_bind(cwq->thread, cpu);
>  			wake_up_process(cwq->thread);
>  			break;
>
> @@ -806,6 +788,7 @@ static int __devinit workqueue_cpu_callb
>  			if (cwq->thread)
>  				wake_up_process(cwq->thread);
>  		case CPU_DEAD:
> +			take_over_work(wq, cpu);
>  			cleanup_workqueue_thread(cwq, cpu);
>  			break;
>  		}

This means that the work_struct on single_threaded wq can't use any of

	__create_workqueue()
	destroy_workqueue()
	flush_workqueue()
	cancel_work_sync()

, they are all racy wrt workqueue_cpu_callback(), and we don't freeze
single_threaded workqueues. This is bad.

Probaly we should:

	- freeze all workqueues, even the single_threaded ones.

	- helper_init() explicitely does __create_workqueue(FE_ALL).
	  this means that we should never use the functions above
	  with this workqueue.

Oleg.


  reply	other threads:[~2007-04-03 12:43 UTC|newest]

Thread overview: 70+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-04-02  5:34 [RFC] Cpu-hotplug: Using the Process Freezer (try2) Gautham R Shenoy
2007-04-02  5:37 ` [PATCH 1/8] Enhance process freezer interface for usage beyond software suspend Gautham R Shenoy
2007-04-02 13:56   ` Pavel Machek
2007-04-02 20:48     ` Rafael J. Wysocki
2007-04-02 20:51       ` Pavel Machek
2007-04-06 14:34         ` Rafael J. Wysocki
2007-04-06 22:20           ` Nigel Cunningham
2007-04-07  9:33             ` Rafael J. Wysocki
2007-04-07  9:47               ` Nigel Cunningham
2007-04-09  3:04         ` Gautham R Shenoy
2007-04-03  7:59       ` Gautham R Shenoy
2007-04-05  9:46   ` Oleg Nesterov
2007-04-05 10:59     ` Gautham R Shenoy
2007-04-05 11:30       ` Oleg Nesterov
2007-04-02  5:37 ` [PATCH 2/8] Make process freezer reentrant Gautham R Shenoy
2007-04-05  9:53   ` Oleg Nesterov
2007-04-05 10:19     ` Gautham R Shenoy
2007-04-02  5:38 ` [PATCH 3/8] Use process freezer for cpu-hotplug Gautham R Shenoy
2007-04-05 10:53   ` Oleg Nesterov
2007-04-05 12:14     ` Gautham R Shenoy
2007-04-05 13:34       ` Oleg Nesterov
2007-04-06 17:27   ` Nathan Lynch
2007-04-06 17:34     ` Ingo Molnar
2007-04-06 17:47       ` Nathan Lynch
2007-04-06 22:22         ` Nigel Cunningham
2007-04-14 18:48       ` Pavel Machek
2007-04-02  5:39 ` [PATCH 4/8] Rip out lock_cpu_hotplug() Gautham R Shenoy
2007-04-02  5:40 ` [PATCH 5/8] __cpu_up: use singlethreaded workqueue Gautham R Shenoy
2007-04-05 12:08   ` Oleg Nesterov
2007-04-02  5:41 ` [PATCH 6/8] Make non-singlethreaded workqueues freezeable by default Gautham R Shenoy
2007-04-05 11:57   ` Oleg Nesterov
2007-04-05 20:06     ` Andrew Morton
2007-04-02  5:42 ` [PATCH 7/8] Clean up workqueue.c with respect to the freezer based cpu-hotplug Gautham R Shenoy
2007-04-03 11:47   ` Oleg Nesterov [this message]
2007-04-03 13:59     ` Srivatsa Vaddagiri
2007-04-03 15:03       ` Oleg Nesterov
2007-04-03 17:18         ` Srivatsa Vaddagiri
2007-04-04 15:28           ` Oleg Nesterov
2007-04-04 17:49             ` Srivatsa Vaddagiri
2007-04-05 12:20               ` Oleg Nesterov
2007-04-12  2:22           ` Srivatsa Vaddagiri
2007-04-12 10:01             ` Gautham R Shenoy
2007-04-12 16:00             ` Oleg Nesterov
2007-04-13  9:46               ` Gautham R Shenoy
2007-04-02  5:42 ` [PATCH 8/8] Make kernel threads freezeable for cpu-hotplug Gautham R Shenoy
2007-04-02  6:16 ` [RFC] Cpu-hotplug: Using the Process Freezer (try2) Ingo Molnar
2007-04-02  9:28   ` Srivatsa Vaddagiri
2007-04-02 11:18     ` Ingo Molnar
2007-04-02 12:42       ` Srivatsa Vaddagiri
2007-04-02 14:16         ` Gautham R Shenoy
2007-04-02 18:56         ` Ingo Molnar
2007-04-03 12:56           ` Srivatsa Vaddagiri
2007-04-03 14:15             ` Gautham R Shenoy
2007-04-03 19:25               ` Rafael J. Wysocki
2007-04-04  3:15               ` Srivatsa Vaddagiri
2007-04-04 10:04                 ` Ingo Molnar
2007-04-04 10:41                   ` Gautham R Shenoy
2007-04-04 11:49                     ` Ingo Molnar
2007-04-04 12:24                       ` Gautham R Shenoy
2007-04-02 11:19   ` Gautham R Shenoy
2007-04-02 11:27     ` Ingo Molnar
2007-04-02 22:12       ` Rafael J. Wysocki
2007-04-02 13:22   ` Pavel Machek
2007-04-03 12:01   ` Gautham R Shenoy
2007-04-03 19:34     ` Rafael J. Wysocki
2007-04-03 20:24       ` Andrew Morton
2007-04-04 10:06         ` utrace merge Ingo Molnar
2007-04-04 10:36           ` Christoph Hellwig
2007-04-04 18:41             ` Andrew Morton
2007-04-03 14:01   ` [RFC] Cpu-hotplug: Using the Process Freezer (try2) Gautham R Shenoy

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=20070403114729.GA776@tv-sign.ru \
    --to=oleg@tv-sign.ru \
    --cc=akpm@linux-foundation.org \
    --cc=dino@in.ibm.com \
    --cc=dipankar@in.ibm.com \
    --cc=ego@in.ibm.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=masami.hiramatsu.pt@hitachi.com \
    --cc=mingo@elte.hu \
    --cc=paulmck@us.ibm.com \
    --cc=rjw@sisk.pl \
    --cc=torvalds@linux-foundation.org \
    --cc=vatsa@in.ibm.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox