Re: [linux-pm] [PATCH] Workqueue freezer support.

From: Nigel Cunningham
Date: Fri Aug 05 2005 - 07:14:08 EST


Hi.

I finally found some time to finish this off. I don't really like the
end result - the macros looked clearer to me - but here goes. If it
looks okay, I'll seek sign offs from each of the affected driver
maintainers and from Ingo. Anyone else?

Regards,

Nigel

drivers/acpi/osl.c | 2
drivers/block/ll_rw_blk.c | 2
drivers/char/hvc_console.c | 2
drivers/char/hvcs.c | 2
drivers/input/serio/serio.c | 2
drivers/md/dm-crypt.c | 2
drivers/scsi/hosts.c | 2
drivers/usb/net/pegasus.c | 2
include/linux/kthread.h | 27 ++++++++----
include/linux/workqueue.h | 9 ++--
kernel/kthread.c | 94 ++++++++++++++++++++++++++++++++++++++++----
kernel/sched.c | 1
kernel/softirq.c | 3 -
kernel/workqueue.c | 36 +++++++++++-----
14 files changed, 144 insertions(+), 42 deletions(-)
diff -ruNp 400-workthreads.patch-old/drivers/acpi/osl.c 400-workthreads.patch-new/drivers/acpi/osl.c
--- 400-workthreads.patch-old/drivers/acpi/osl.c 2005-08-02 22:30:57.000000000 +1000
+++ 400-workthreads.patch-new/drivers/acpi/osl.c 2005-08-02 22:33:49.000000000 +1000
@@ -98,7 +98,7 @@ acpi_os_initialize1(void)
return AE_NULL_ENTRY;
}
#endif
- kacpid_wq = create_singlethread_workqueue("kacpid");
+ kacpid_wq = create_nofreeze_singlethread_workqueue("kacpid");
BUG_ON(!kacpid_wq);

return AE_OK;
diff -ruNp 400-workthreads.patch-old/drivers/block/ll_rw_blk.c 400-workthreads.patch-new/drivers/block/ll_rw_blk.c
--- 400-workthreads.patch-old/drivers/block/ll_rw_blk.c 2005-08-02 22:30:57.000000000 +1000
+++ 400-workthreads.patch-new/drivers/block/ll_rw_blk.c 2005-08-02 22:33:49.000000000 +1000
@@ -3215,7 +3215,7 @@ EXPORT_SYMBOL(kblockd_flush);

int __init blk_dev_init(void)
{
- kblockd_workqueue = create_workqueue("kblockd");
+ kblockd_workqueue = create_nofreeze_workqueue("kblockd");
if (!kblockd_workqueue)
panic("Failed to create kblockd\n");

diff -ruNp 400-workthreads.patch-old/drivers/char/hvc_console.c 400-workthreads.patch-new/drivers/char/hvc_console.c
--- 400-workthreads.patch-old/drivers/char/hvc_console.c 2005-08-02 22:30:58.000000000 +1000
+++ 400-workthreads.patch-new/drivers/char/hvc_console.c 2005-08-02 22:33:49.000000000 +1000
@@ -844,7 +844,7 @@ int __init hvc_init(void)

/* Always start the kthread because there can be hotplug vty adapters
* added later. */
- hvc_task = kthread_run(khvcd, NULL, "khvcd");
+ hvc_task = kthread_nofreeze_run(khvcd, NULL, "khvcd");
if (IS_ERR(hvc_task)) {
panic("Couldn't create kthread for console.\n");
put_tty_driver(hvc_driver);
diff -ruNp 400-workthreads.patch-old/drivers/char/hvcs.c 400-workthreads.patch-new/drivers/char/hvcs.c
--- 400-workthreads.patch-old/drivers/char/hvcs.c 2005-08-02 22:30:58.000000000 +1000
+++ 400-workthreads.patch-new/drivers/char/hvcs.c 2005-08-02 22:33:49.000000000 +1000
@@ -1403,7 +1403,7 @@ static int __init hvcs_module_init(void)
return -ENOMEM;
}

- hvcs_task = kthread_run(khvcsd, NULL, "khvcsd");
+ hvcs_task = kthread_nofreeze_run(khvcsd, NULL, "khvcsd");
if (IS_ERR(hvcs_task)) {
printk(KERN_ERR "HVCS: khvcsd creation failed. Driver not loaded.\n");
kfree(hvcs_pi_buff);
diff -ruNp 400-workthreads.patch-old/drivers/input/serio/serio.c 400-workthreads.patch-new/drivers/input/serio/serio.c
--- 400-workthreads.patch-old/drivers/input/serio/serio.c 2005-08-04 11:48:01.000000000 +1000
+++ 400-workthreads.patch-new/drivers/input/serio/serio.c 2005-08-02 22:33:49.000000000 +1000
@@ -899,7 +899,7 @@ irqreturn_t serio_interrupt(struct serio

static int __init serio_init(void)
{
- serio_task = kthread_run(serio_thread, NULL, "kseriod");
+ serio_task = kthread_nofreeze_run(serio_thread, NULL, "kseriod");
if (IS_ERR(serio_task)) {
printk(KERN_ERR "serio: Failed to start kseriod\n");
return PTR_ERR(serio_task);
diff -ruNp 400-workthreads.patch-old/drivers/md/dm-crypt.c 400-workthreads.patch-new/drivers/md/dm-crypt.c
--- 400-workthreads.patch-old/drivers/md/dm-crypt.c 2005-08-02 22:31:02.000000000 +1000
+++ 400-workthreads.patch-new/drivers/md/dm-crypt.c 2005-08-02 22:33:49.000000000 +1000
@@ -926,7 +926,7 @@ static int __init dm_crypt_init(void)
if (!_crypt_io_pool)
return -ENOMEM;

- _kcryptd_workqueue = create_workqueue("kcryptd");
+ _kcryptd_workqueue = create_nofreeze_workqueue("kcryptd");
if (!_kcryptd_workqueue) {
r = -ENOMEM;
DMERR(PFX "couldn't create kcryptd");
diff -ruNp 400-workthreads.patch-old/drivers/scsi/hosts.c 400-workthreads.patch-new/drivers/scsi/hosts.c
--- 400-workthreads.patch-old/drivers/scsi/hosts.c 2005-08-02 22:31:10.000000000 +1000
+++ 400-workthreads.patch-new/drivers/scsi/hosts.c 2005-08-02 22:33:49.000000000 +1000
@@ -132,7 +132,7 @@ int scsi_add_host(struct Scsi_Host *shos
if (shost->transportt->create_work_queue) {
snprintf(shost->work_q_name, KOBJ_NAME_LEN, "scsi_wq_%d",
shost->host_no);
- shost->work_q = create_singlethread_workqueue(
+ shost->work_q = create_nofreeze_singlethread_workqueue(
shost->work_q_name);
if (!shost->work_q)
goto out_free_shost_data;
diff -ruNp 400-workthreads.patch-old/drivers/usb/net/pegasus.c 400-workthreads.patch-new/drivers/usb/net/pegasus.c
--- 400-workthreads.patch-old/drivers/usb/net/pegasus.c 2005-08-02 22:31:15.000000000 +1000
+++ 400-workthreads.patch-new/drivers/usb/net/pegasus.c 2005-08-02 22:33:49.000000000 +1000
@@ -1411,7 +1411,7 @@ static struct usb_driver pegasus_driver
static int __init pegasus_init(void)
{
pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION);
- pegasus_workqueue = create_singlethread_workqueue("pegasus");
+ pegasus_workqueue = create_nofreeze_singlethread_workqueue("pegasus");
if (!pegasus_workqueue)
return -ENOMEM;
return usb_register(&pegasus_driver);
diff -ruNp 400-workthreads.patch-old/include/linux/kthread.h 400-workthreads.patch-new/include/linux/kthread.h
--- 400-workthreads.patch-old/include/linux/kthread.h 2004-11-03 21:51:12.000000000 +1100
+++ 400-workthreads.patch-new/include/linux/kthread.h 2005-08-03 11:52:01.000000000 +1000
@@ -23,10 +23,20 @@
*
* Returns a task_struct or ERR_PTR(-ENOMEM).
*/
+struct task_struct *__kthread_create(int (*threadfn)(void *data),
+ void *data,
+ unsigned long freezer_flags,
+ const char namefmt[],
+ va_list * args);
+
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char namefmt[], ...);

+struct task_struct *kthread_nofreeze_create(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...);
+
/**
* kthread_run: create and wake a thread.
* @threadfn: the function to run until signal_pending(current).
@@ -35,14 +45,15 @@ struct task_struct *kthread_create(int (
*
* Description: Convenient wrapper for kthread_create() followed by
* wake_up_process(). Returns the kthread, or ERR_PTR(-ENOMEM). */
-#define kthread_run(threadfn, data, namefmt, ...) \
-({ \
- struct task_struct *__k \
- = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
- if (!IS_ERR(__k)) \
- wake_up_process(__k); \
- __k; \
-})
+
+extern struct task_struct * kthread_run(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...);
+
+extern struct task_struct * kthread_nofreeze_run(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...);
+

/**
* kthread_bind: bind a just-created kthread to a cpu.
diff -ruNp 400-workthreads.patch-old/include/linux/workqueue.h 400-workthreads.patch-new/include/linux/workqueue.h
--- 400-workthreads.patch-old/include/linux/workqueue.h 2005-06-20 11:47:30.000000000 +1000
+++ 400-workthreads.patch-new/include/linux/workqueue.h 2005-08-03 11:49:34.000000000 +1000
@@ -51,9 +51,12 @@ struct work_struct {
} while (0)

extern struct workqueue_struct *__create_workqueue(const char *name,
- int singlethread);
-#define create_workqueue(name) __create_workqueue((name), 0)
-#define create_singlethread_workqueue(name) __create_workqueue((name), 1)
+ int singlethread,
+ unsigned long freezer_flag);
+#define create_workqueue(name) __create_workqueue((name), 0, 0)
+#define create_nofreeze_workqueue(name) __create_workqueue((name), 0, PF_NOFREEZE)
+#define create_singlethread_workqueue(name) __create_workqueue((name), 1, 0)
+#define create_nofreeze_singlethread_workqueue(name) __create_workqueue((name), 1, PF_NOFREEZE)

extern void destroy_workqueue(struct workqueue_struct *wq);

diff -ruNp 400-workthreads.patch-old/kernel/kthread.c 400-workthreads.patch-new/kernel/kthread.c
--- 400-workthreads.patch-old/kernel/kthread.c 2005-06-20 11:47:31.000000000 +1000
+++ 400-workthreads.patch-new/kernel/kthread.c 2005-08-04 09:46:43.000000000 +1000
@@ -25,6 +25,7 @@ struct kthread_create_info
/* Information passed to kthread() from keventd. */
int (*threadfn)(void *data);
void *data;
+ unsigned long freezer_flags;
struct completion started;

/* Result passed back to kthread_create() from keventd. */
@@ -86,6 +87,10 @@ static int kthread(void *_create)
/* By default we can run anywhere, unlike keventd. */
set_cpus_allowed(current, CPU_MASK_ALL);

+ /* Set our freezer flags */
+ current->flags &= ~PF_NOFREEZE;
+ current->flags |= (create->freezer_flags & PF_NOFREEZE);
+
/* OK, tell user we're spawned, wait for stop or wakeup */
__set_current_state(TASK_INTERRUPTIBLE);
complete(&create->started);
@@ -119,16 +124,18 @@ static void keventd_create_kthread(void
complete(&create->done);
}

-struct task_struct *kthread_create(int (*threadfn)(void *data),
+struct task_struct *__kthread_create(int (*threadfn)(void *data),
void *data,
+ unsigned long freezer_flags,
const char namefmt[],
- ...)
+ va_list * args)
{
struct kthread_create_info create;
DECLARE_WORK(work, keventd_create_kthread, &create);

create.threadfn = threadfn;
create.data = data;
+ create.freezer_flags = freezer_flags;
init_completion(&create.started);
init_completion(&create.done);

@@ -141,18 +148,89 @@ struct task_struct *kthread_create(int (
queue_work(helper_wq, &work);
wait_for_completion(&create.done);
}
- if (!IS_ERR(create.result)) {
- va_list args;
- va_start(args, namefmt);
+ if (!IS_ERR(create.result))
vsnprintf(create.result->comm, sizeof(create.result->comm),
- namefmt, args);
- va_end(args);
- }
+ namefmt, *args);

return create.result;
}
+
+struct task_struct *kthread_create(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...)
+{
+ struct task_struct * result;
+
+ va_list args;
+ va_start(args, namefmt);
+ result = __kthread_create(threadfn, data, 0, namefmt, &args);
+ va_end(args);
+ return result;
+}
+
EXPORT_SYMBOL(kthread_create);

+struct task_struct *kthread_nofreeze_create(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...)
+{
+ struct task_struct * result;
+
+ va_list args;
+ va_start(args, namefmt);
+ result = __kthread_create(threadfn, data, PF_NOFREEZE, namefmt, &args);
+ va_end(args);
+ return result;
+}
+
+EXPORT_SYMBOL(kthread_nofreeze_create);
+
+/**
+ * kthread_run: create and wake a thread.
+ * @threadfn: the function to run until signal_pending(current).
+ * @data: data ptr for @threadfn.
+ * @namefmt: printf-style name for the thread.
+ *
+ * Description: Convenient wrapper for kthread_create() followed by
+ * wake_up_process(). Returns the kthread, or ERR_PTR(-ENOMEM).
+ **/
+struct task_struct * kthread_run(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...)
+{
+ struct task_struct *__k;
+ va_list args;
+
+ va_start(args, namefmt);
+ __k = __kthread_create(threadfn, data, 0, namefmt, &args);
+ va_end(args);
+
+ if(!IS_ERR(__k))
+ wake_up_process(__k);
+
+ return __k;
+}
+
+EXPORT_SYMBOL(kthread_run);
+
+struct task_struct * kthread_nofreeze_run(int (*threadfn)(void *data),
+ void *data,
+ const char namefmt[], ...)
+{
+ struct task_struct *__k;
+ va_list args;
+
+ va_start(args, namefmt);
+ __k = __kthread_create(threadfn, data, PF_NOFREEZE, namefmt, &args);
+ va_end(args);
+
+ if(!IS_ERR(__k))
+ wake_up_process(__k);
+
+ return __k;
+}
+EXPORT_SYMBOL(kthread_nofreeze_run);
+
void kthread_bind(struct task_struct *k, unsigned int cpu)
{
BUG_ON(k->state != TASK_INTERRUPTIBLE);
diff -ruNp 400-workthreads.patch-old/kernel/sched.c 400-workthreads.patch-new/kernel/sched.c
--- 400-workthreads.patch-old/kernel/sched.c 2005-08-04 11:48:00.000000000 +1000
+++ 400-workthreads.patch-new/kernel/sched.c 2005-08-04 11:48:19.000000000 +1000
@@ -4585,7 +4585,6 @@ static int migration_call(struct notifie
p = kthread_create(migration_thread, hcpu, "migration/%d",cpu);
if (IS_ERR(p))
return NOTIFY_BAD;
- p->flags |= PF_NOFREEZE;
kthread_bind(p, cpu);
/* Must be high prio: stop_machine expects to yield to it. */
rq = task_rq_lock(p, &flags);
diff -ruNp 400-workthreads.patch-old/kernel/softirq.c 400-workthreads.patch-new/kernel/softirq.c
--- 400-workthreads.patch-old/kernel/softirq.c 2005-06-20 11:47:32.000000000 +1000
+++ 400-workthreads.patch-new/kernel/softirq.c 2005-08-02 22:33:49.000000000 +1000
@@ -350,7 +350,6 @@ void __init softirq_init(void)
static int ksoftirqd(void * __bind_cpu)
{
set_user_nice(current, 19);
- current->flags |= PF_NOFREEZE;

set_current_state(TASK_INTERRUPTIBLE);

@@ -456,7 +455,7 @@ static int __devinit cpu_callback(struct
case CPU_UP_PREPARE:
BUG_ON(per_cpu(tasklet_vec, hotcpu).list);
BUG_ON(per_cpu(tasklet_hi_vec, hotcpu).list);
- p = kthread_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu);
+ p = kthread_nofreeze_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu);
if (IS_ERR(p)) {
printk("ksoftirqd for %i failed\n", hotcpu);
return NOTIFY_BAD;
diff -ruNp 400-workthreads.patch-old/kernel/workqueue.c 400-workthreads.patch-new/kernel/workqueue.c
--- 400-workthreads.patch-old/kernel/workqueue.c 2005-06-20 11:47:32.000000000 +1000
+++ 400-workthreads.patch-new/kernel/workqueue.c 2005-08-03 11:57:01.000000000 +1000
@@ -186,8 +186,6 @@ static int worker_thread(void *__cwq)
struct k_sigaction sa;
sigset_t blocked;

- current->flags |= PF_NOFREEZE;
-
set_user_nice(current, -5);

/* Block and flush all signals */
@@ -208,6 +206,7 @@ static int worker_thread(void *__cwq)
schedule();
else
__set_current_state(TASK_RUNNING);
+ try_to_freeze();
remove_wait_queue(&cwq->more_work, &wait);

if (!list_empty(&cwq->worklist))
@@ -277,7 +276,8 @@ void fastcall flush_workqueue(struct wor
}

static struct task_struct *create_workqueue_thread(struct workqueue_struct *wq,
- int cpu)
+ int cpu,
+ unsigned long freezer_flags)
{
struct cpu_workqueue_struct *cwq = wq->cpu_wq + cpu;
struct task_struct *p;
@@ -291,10 +291,21 @@ static struct task_struct *create_workqu
init_waitqueue_head(&cwq->more_work);
init_waitqueue_head(&cwq->work_done);

- if (is_single_threaded(wq))
- p = kthread_create(worker_thread, cwq, "%s", wq->name);
- else
- p = kthread_create(worker_thread, cwq, "%s/%d", wq->name, cpu);
+ if (is_single_threaded(wq)) {
+ if (freezer_flags)
+ p = kthread_nofreeze_create(worker_thread, cwq,
+ "%s", wq->name);
+ else
+ p = kthread_create(worker_thread, cwq,
+ "%s", wq->name);
+ } else {
+ if (freezer_flags)
+ p = kthread_nofreeze_create(worker_thread, cwq,
+ "%s/%d", wq->name, cpu);
+ else
+ p = kthread_create(worker_thread, cwq,
+ "%s/%d", wq->name, cpu);
+ }
if (IS_ERR(p))
return NULL;
cwq->thread = p;
@@ -302,7 +313,8 @@ static struct task_struct *create_workqu
}

struct workqueue_struct *__create_workqueue(const char *name,
- int singlethread)
+ int singlethread,
+ unsigned long freezer_flags)
{
int cpu, destroy = 0;
struct workqueue_struct *wq;
@@ -320,7 +332,7 @@ struct workqueue_struct *__create_workqu
lock_cpu_hotplug();
if (singlethread) {
INIT_LIST_HEAD(&wq->list);
- p = create_workqueue_thread(wq, 0);
+ p = create_workqueue_thread(wq, 0, freezer_flags);
if (!p)
destroy = 1;
else
@@ -330,7 +342,7 @@ struct workqueue_struct *__create_workqu
list_add(&wq->list, &workqueues);
spin_unlock(&workqueue_lock);
for_each_online_cpu(cpu) {
- p = create_workqueue_thread(wq, cpu);
+ p = create_workqueue_thread(wq, cpu, freezer_flags);
if (p) {
kthread_bind(p, cpu);
wake_up_process(p);
@@ -501,7 +513,7 @@ static int __devinit workqueue_cpu_callb
case CPU_UP_PREPARE:
/* Create a new workqueue thread for it. */
list_for_each_entry(wq, &workqueues, list) {
- if (create_workqueue_thread(wq, hotcpu) < 0) {
+ if (create_workqueue_thread(wq, hotcpu, 0) < 0) {
printk("workqueue for %i failed\n", hotcpu);
return NOTIFY_BAD;
}
@@ -540,7 +552,7 @@ static int __devinit workqueue_cpu_callb
void init_workqueues(void)
{
hotcpu_notifier(workqueue_cpu_callback, 0);
- keventd_wq = create_workqueue("events");
+ keventd_wq = create_nofreeze_workqueue("events");
BUG_ON(!keventd_wq);
}


--
Evolution.
Enumerate the requirements.
Consider the interdependencies.
Calculate the probabilities.

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