From: Tejun Heo <tj@kernel.org>
To: torvalds@linux-foundation.org, awalls@radix.net,
linux-kernel@vger.kernel.org, jeff@garzik.org, mingo@elte.hu,
akpm@linux-foundation.org, jens.axboe@oracle.com,
rusty@rustcorp.com.au, cl@linux-foundation.org,
dhowells@redhat.com, arjan@linux.intel.com, avi@redhat.com,
peterz@infradead.org, johannes@sipsolutions.net,
andi@firstfloor.org
Subject: Re: [RFC PATCHSET] concurrency managed workqueue, take#2
Date: Fri, 18 Dec 2009 22:03:36 +0900 [thread overview]
Message-ID: <4B2B7DA8.2070004@kernel.org> (raw)
In-Reply-To: <1261141088-2014-1-git-send-email-tj@kernel.org>
Here's the test program I've been using to verify features.
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/cpu.h>
#include <linux/kthread.h>
#define MAX_WQ_NAME 64
#define MAX_WQS 64
#define MAX_WORKS 64
struct wq_spec {
int id; /* -1 terminates */
unsigned int max_active;
unsigned int flags;
};
enum action {
ACT_TERM, /* end */
ACT_LOG, /* const char * */
ACT_BURN, /* ulong duration_msecs */
ACT_SLEEP, /* ulong duration_msecs */
ACT_WAKEUP, /* ulong work_id */
ACT_REQUEUE, /* ulong delay_msecs */
ACT_FLUSH, /* ulong work_id */
ACT_FLUSH_WQ, /* ulong workqueue_id */
ACT_CANCEL, /* ulong work_id */
};
struct work_action {
enum action action; /* ACT_TERM terminates */
union {
unsigned long v;
const char *s;
};
};
struct work_spec {
int id; /* -1 terminates */
int wq_id;
int requeue_cnt;
unsigned int cpu;
unsigned long initial_delay; /* msecs */
const struct work_action *actions;
};
struct test_scenario {
const struct wq_spec *wq_spec;
const struct work_spec **work_spec; /* NULL terminated */
};
static const struct wq_spec dfl_wq_spec[] = {
{
.id = 0,
.max_active = 32,
.flags = 0,
},
{
.id = 1,
.max_active = 32,
.flags = 0,
},
{
.id = 2,
.max_active = 32,
.flags = WQ_RESCUER,
},
{
.id = 3,
.max_active = 32,
.flags = WQ_FREEZEABLE,
},
{
.id = 4,
.max_active = 1,
.flags = WQ_SINGLE_CPU | WQ_FREEZEABLE/* | WQ_DBG*/,
},
{ .id = -1 },
};
/*
* Scenario 0. All are on cpu0. work16 and 17 burn cpus for 10 and
* 5msecs respectively and requeue themselves. 18 sleeps 2 secs and
* cancel both.
*/
static const struct work_spec work_spec0[] = {
{
.id = 16,
.requeue_cnt = 1024,
.actions = (const struct work_action[]) {
{ ACT_BURN, { 10 }},
{ ACT_REQUEUE, { 0 }},
{ ACT_TERM },
},
},
{
.id = 17,
.requeue_cnt = 1024,
.actions = (const struct work_action[]) {
{ ACT_BURN, { 5 }},
{ ACT_REQUEUE, { 0 }},
{ ACT_TERM },
},
},
{
.id = 18,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "will sleep 2s and cancel both" }},
{ ACT_SLEEP, { 2000 }},
{ ACT_CANCEL, { 16 }},
{ ACT_CANCEL, { 17 }},
{ ACT_TERM },
},
},
{ .id = -1 },
};
static const struct test_scenario scenario0 = {
.wq_spec = dfl_wq_spec,
.work_spec =
(const struct work_spec *[]) { work_spec0, NULL },
};
/*
* Scenario 1. All are on cpu0. Work 0, 1 and 2 sleep for different
* intervals but all three will terminate at around 30secs. 3 starts
* at @28 and 4 at @33 and both sleep for five secs and then
* terminate. 5 waits for 0, 1, 2 and then flush wq which by the time
* should have 3 on it. After 3 completes @32, 5 terminates too.
* After 4 secs, 4 terminates and all test sequence is done.
*/
static const struct work_spec work_spec1[] = {
{
.id = 0,
.actions = (const struct work_action[]) {
{ ACT_BURN, { 3 }}, /* to cause sched activation */
{ ACT_LOG, { .s = "will sleep 30s" }},
{ ACT_SLEEP, { 30000 }},
{ ACT_TERM },
},
},
{
.id = 1,
.actions = (const struct work_action[]) {
{ ACT_BURN, { 5 }},
{ ACT_LOG, { .s = "will sleep 10s and burn 5msec and repeat 3 times" }},
{ ACT_SLEEP, { 10000 }},
{ ACT_BURN, { 5 }},
{ ACT_LOG, { .s = "@10s" }},
{ ACT_SLEEP, { 10000 }},
{ ACT_BURN, { 5 }},
{ ACT_LOG, { .s = "@20s" }},
{ ACT_SLEEP, { 10000 }},
{ ACT_BURN, { 5 }},
{ ACT_LOG, { .s = "@30s" }},
{ ACT_TERM },
},
},
{
.id = 2,
.actions = (const struct work_action[]) {
{ ACT_BURN, { 1 }},
{ ACT_LOG, { .s = "will sleep 3s and burn 1msec and repeat 10 times" }},
{ ACT_SLEEP, { 3000 }},
{ ACT_BURN, { 1 }},
{ ACT_LOG, { .s = "@3s" }},
{ ACT_SLEEP, { 3000 }},
{ ACT_BURN, { 1 }},
{ ACT_LOG, { .s = "@6s" }},
{ ACT_SLEEP, { 3000 }},
{ ACT_BURN, { 1 }},
{ ACT_LOG, { .s = "@9s" }},
{ ACT_SLEEP, { 3000 }},
{ ACT_BURN, { 1 }},
{ ACT_LOG, { .s = "@12s" }},
{ ACT_SLEEP, { 3000 }},
{ ACT_BURN, { 1 }},
{ ACT_LOG, { .s = "@15s" }},
{ ACT_SLEEP, { 3000 }},
{ ACT_BURN, { 1 }},
{ ACT_LOG, { .s = "@18s" }},
{ ACT_SLEEP, { 3000 }},
{ ACT_BURN, { 1 }},
{ ACT_LOG, { .s = "@21s" }},
{ ACT_SLEEP, { 3000 }},
{ ACT_BURN, { 1 }},
{ ACT_LOG, { .s = "@24s" }},
{ ACT_SLEEP, { 3000 }},
{ ACT_BURN, { 1 }},
{ ACT_LOG, { .s = "@27s" }},
{ ACT_SLEEP, { 3000 }},
{ ACT_BURN, { 1 }},
{ ACT_LOG, { .s = "@30s" }},
{ ACT_TERM },
},
},
{
.id = 3,
.initial_delay = 29000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "started@28s, will sleep for 5s" }},
{ ACT_SLEEP, { 5000 }},
{ ACT_TERM },
}
},
{
.id = 4,
.initial_delay = 33000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "started@33s, will sleep for 5s" }},
{ ACT_SLEEP, { 5000 }},
{ ACT_TERM },
}
},
{
.id = 5,
.wq_id = 1, /* can't flush self */
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "flushing 0, 1 and 2" }},
{ ACT_FLUSH, { 0 }},
{ ACT_FLUSH, { 1 }},
{ ACT_FLUSH, { 2 }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{ .id = -1 },
};
static const struct test_scenario scenario1 = {
.wq_spec = dfl_wq_spec,
.work_spec =
(const struct work_spec *[]) { work_spec1, NULL },
};
/*
* Scenario 2. Combination of scenario 0 and 1.
*/
static const struct test_scenario scenario2 = {
.wq_spec = dfl_wq_spec,
.work_spec =
(const struct work_spec *[]) { work_spec0, work_spec1, NULL },
};
/*
* Scenario 3. More complex flushing.
*
* 2:burn 2s 3:4s
* <----> <---------->
* 0:4s 1:4s
* <----------> <..----------->
* ^ ^
* | |
* | |
* 4:flush(cpu0) flush_wq
* 5:flush(cpu0) flush
* 6:flush(cpu1) flush_wq
* 7:flush(cpu1) flush
*/
static const struct work_spec work_spec2[] = {
{
.id = 0,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 1,
.initial_delay = 6000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 2,
.initial_delay = 5000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "burning 2s" }},
{ ACT_BURN, { 2000 }},
{ ACT_TERM },
},
},
{
.id = 3,
.initial_delay = 9000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 4,
.wq_id = 1,
.initial_delay = 1000,
.actions = (const struct work_action[]) {
{ ACT_FLUSH, { 0 }},
{ ACT_SLEEP, { 2500 }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{
.id = 5,
.wq_id = 1,
.initial_delay = 1000,
.actions = (const struct work_action[]) {
{ ACT_FLUSH, { 0 }},
{ ACT_SLEEP, { 2500 }},
{ ACT_FLUSH, { 1 }},
{ ACT_TERM },
},
},
{
.id = 6,
.wq_id = 1,
.cpu = 1,
.initial_delay = 1000,
.actions = (const struct work_action[]) {
{ ACT_FLUSH, { 0 }},
{ ACT_SLEEP, { 2500 }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{
.id = 7,
.wq_id = 1,
.cpu = 1,
.initial_delay = 1000,
.actions = (const struct work_action[]) {
{ ACT_FLUSH, { 0 }},
{ ACT_SLEEP, { 2500 }},
{ ACT_FLUSH, { 1 }},
{ ACT_TERM },
},
},
{ .id = -1 },
};
static const struct test_scenario scenario3 = {
.wq_spec = dfl_wq_spec,
.work_spec =
(const struct work_spec *[]) { work_spec2, NULL },
};
/*
* Scenario 4. Mayday! To be used with MAX_CPU_WORKERS_ORDER reduced
* to 2.
*/
static const struct work_spec work_spec4[] = {
{
.id = 0,
.requeue_cnt = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 5s" }},
{ ACT_SLEEP, { 5000 }},
{ ACT_REQUEUE, { 5000 }},
{ ACT_TERM },
},
},
{
.id = 1,
.requeue_cnt = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 5s" }},
{ ACT_SLEEP, { 5000 }},
{ ACT_REQUEUE, { 5000 }},
{ ACT_TERM },
},
},
{
.id = 2,
.requeue_cnt = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 5s" }},
{ ACT_SLEEP, { 5000 }},
{ ACT_REQUEUE, { 5000 }},
{ ACT_TERM },
},
},
{
.id = 3,
.requeue_cnt = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 5s" }},
{ ACT_SLEEP, { 5000 }},
{ ACT_REQUEUE, { 5000 }},
{ ACT_TERM },
},
},
{
.id = 4,
.wq_id = 2,
.requeue_cnt = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 1s" }},
{ ACT_SLEEP, { 1000 }},
{ ACT_REQUEUE, { 5000 }},
{ ACT_TERM },
},
},
{
.id = 5,
.wq_id = 2,
.requeue_cnt = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 1s" }},
{ ACT_SLEEP, { 1000 }},
{ ACT_REQUEUE, { 5000 }},
{ ACT_TERM },
},
},
{
.id = 6,
.wq_id = 2,
.requeue_cnt = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 1s" }},
{ ACT_SLEEP, { 1000 }},
{ ACT_REQUEUE, { 5000 }},
{ ACT_TERM },
},
},
{
.id = 7,
.wq_id = 2,
.requeue_cnt = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 1s" }},
{ ACT_SLEEP, { 1000 }},
{ ACT_REQUEUE, { 5000 }},
{ ACT_TERM },
},
},
{ .id = -1 },
};
static const struct test_scenario scenario4 = {
.wq_spec = dfl_wq_spec,
.work_spec =
(const struct work_spec *[]) { work_spec4, NULL },
};
/*
* Scenario 5. To test cpu off/onlining. A bunch of long running
* tasks on cpu1. Gets interesting with various other conditions
* applied together - lowered MAX_CPU_WORKERS_ORDER, induced failure
* or delay during CPU_DOWN/UP_PREPARE and so on.
*/
static const struct work_spec work_spec5[] = {
/* runs for 30 secs */
{
.id = 0,
.cpu = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 30s" }},
{ ACT_SLEEP, { 30000 }},
{ ACT_TERM },
},
},
{
.id = 1,
.cpu = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 30s" }},
{ ACT_SLEEP, { 30000 }},
{ ACT_TERM },
},
},
{
.id = 2,
.cpu = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 30s" }},
{ ACT_SLEEP, { 30000 }},
{ ACT_TERM },
},
},
{
.id = 3,
.cpu = 1,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 30s" }},
{ ACT_SLEEP, { 30000 }},
{ ACT_TERM },
},
},
/* kicks in @15 and runs for 15 from wq0 */
{
.id = 4,
.cpu = 1,
.initial_delay = 15000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 15s" }},
{ ACT_SLEEP, { 15000 }},
{ ACT_TERM },
},
},
{
.id = 5,
.cpu = 1,
.initial_delay = 15000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 15s" }},
{ ACT_SLEEP, { 15000 }},
{ ACT_TERM },
},
},
{
.id = 6,
.cpu = 1,
.initial_delay = 15000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 15s" }},
{ ACT_SLEEP, { 15000 }},
{ ACT_TERM },
},
},
{
.id = 7,
.cpu = 1,
.initial_delay = 15000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 15s" }},
{ ACT_SLEEP, { 15000 }},
{ ACT_TERM },
},
},
/* kicks in @15 and runs for 15 from wq2 */
{
.id = 8,
.wq_id = 2,
.cpu = 1,
.initial_delay = 15000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 15s" }},
{ ACT_SLEEP, { 15000 }},
{ ACT_TERM },
},
},
{
.id = 9,
.wq_id = 2,
.cpu = 1,
.initial_delay = 15000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 15s" }},
{ ACT_SLEEP, { 15000 }},
{ ACT_TERM },
},
},
/* kicks in @30 and runs for 15 */
{
.id = 10,
.cpu = 1,
.initial_delay = 30000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 15s" }},
{ ACT_SLEEP, { 15000 }},
{ ACT_TERM },
},
},
{
.id = 11,
.cpu = 1,
.initial_delay = 30000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 15s" }},
{ ACT_SLEEP, { 15000 }},
{ ACT_TERM },
},
},
{ .id = -1 },
};
static const struct test_scenario scenario5 = {
.wq_spec = dfl_wq_spec,
.work_spec =
(const struct work_spec *[]) { work_spec5, NULL },
};
/*
* Scenario 6. Scenario to test freezeable workqueue. User should
* freeze the machine between 0s and 9s.
*
* 0,1:sleep 10s
* <-------->
* <--freezing--><--frozen--><-thawed
* 2,3: sleeps for 10s
* <.....................-------->
* <.....................-------->
*/
static const struct work_spec work_spec6[] = {
/* two works which get queued @0s and sleeps for 10s */
{
.id = 0,
.wq_id = 3,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 10s" }},
{ ACT_SLEEP, { 10000 }},
{ ACT_TERM },
},
},
{
.id = 1,
.wq_id = 3,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 10s" }},
{ ACT_SLEEP, { 10000 }},
{ ACT_TERM },
},
},
/* two works which get queued @9s and sleeps for 10s */
{
.id = 2,
.wq_id = 3,
.initial_delay = 9000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 10s" }},
{ ACT_SLEEP, { 10000 }},
{ ACT_TERM },
},
},
{
.id = 3,
.wq_id = 3,
.initial_delay = 9000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 10s" }},
{ ACT_SLEEP, { 10000 }},
{ ACT_TERM },
},
},
{ .id = -1 },
};
static const struct test_scenario scenario6 = {
.wq_spec = dfl_wq_spec,
.work_spec =
(const struct work_spec *[]) { work_spec6, NULL },
};
/*
* Scenario 7. Scenario to test multi-colored workqueue.
*
* 0 1 2 3 4 5 6 7 8 9 0 1 2 : time in seconds
* 0: <------> cpu0
* 1: <------> cpu1
* 2: <------> cpu2
* 3: <------> cpu3
* 4: <------> cpu0
* 5: <------> cpu1
* 6: <------> cpu2
* 7: <------> cpu3
* Flush workqueues
* 10:^
* 11: ^
* 12: ^
* 13: ^
* 14: ^
* 15: ^
* 16: ^
* 17: ^
* 18: ^
*/
static const struct work_spec work_spec7[] = {
/* 8 works - each sleeps 4sec and starts staggered */
{
.id = 0,
.wq_id = 0,
.cpu = 0,
.initial_delay = 1000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s @ 1s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 1,
.wq_id = 0,
.cpu = 1,
.initial_delay = 2000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s @ 2s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 2,
.wq_id = 0,
.cpu = 2,
.initial_delay = 3000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s @ 3s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 3,
.wq_id = 0,
.cpu = 3,
.initial_delay = 4000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s @ 4s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 4,
.wq_id = 0,
.cpu = 0,
.initial_delay = 5000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s @ 5s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 5,
.wq_id = 0,
.cpu = 1,
.initial_delay = 6000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s @ 6s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 6,
.wq_id = 0,
.cpu = 2,
.initial_delay = 7000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s @ 7s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 7,
.wq_id = 0,
.cpu = 3,
.initial_delay = 8000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s @ 8s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
/* 9 workqueue flushers */
{
.id = 10,
.wq_id = 1,
.initial_delay = 500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "flush_wq @ 0.5s" }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{
.id = 11,
.wq_id = 1,
.initial_delay = 1500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "flush_wq @ 1.5s" }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{
.id = 12,
.wq_id = 1,
.initial_delay = 1500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "flush_wq @ 1.5s" }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{
.id = 13,
.wq_id = 1,
.initial_delay = 3500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "flush_wq @ 3.5s" }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{
.id = 14,
.wq_id = 1,
.initial_delay = 4500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "flush_wq @ 4.5s" }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{
.id = 15,
.wq_id = 1,
.initial_delay = 4500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "flush_wq @ 4.5s" }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{
.id = 16,
.wq_id = 1,
.initial_delay = 6500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "flush_wq @ 6.5s" }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{
.id = 17,
.wq_id = 1,
.initial_delay = 7500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "flush_wq @ 7.5s" }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{
.id = 18,
.wq_id = 1,
.initial_delay = 7500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "flush_wq @ 7.5s" }},
{ ACT_FLUSH_WQ, { 0 }},
{ ACT_TERM },
},
},
{ .id = -1 },
};
static const struct test_scenario scenario7 = {
.wq_spec = dfl_wq_spec,
.work_spec =
(const struct work_spec *[]) { work_spec7, NULL },
};
/*
* Scenario 8. Scenario to test single thread workqueue. Test with
* freeze/thaw, suspend/resume at various points.
*
* 0@cpu0 <------>
* 1@cpu1 <...--->
* 2@cpu2 <...--->
* 3@cpu3 <------>
* 4@cpu0 <...--->
* 5@cpu1 <------>
*/
static const struct work_spec work_spec8[] = {
{
.id = 0,
.wq_id = 4,
.cpu = 0,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s @0s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 1,
.wq_id = 4,
.cpu = 1,
.initial_delay = 2000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 2s @2:4s" }},
{ ACT_SLEEP, { 2000 }},
{ ACT_TERM },
},
},
{
.id = 2,
.wq_id = 4,
.cpu = 2,
.initial_delay = 4000,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 2s @4:6s" }},
{ ACT_SLEEP, { 2000 }},
{ ACT_TERM },
},
},
{
.id = 3,
.wq_id = 4,
.cpu = 3,
.initial_delay = 8500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s @8.5s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{
.id = 4,
.wq_id = 4,
.cpu = 0,
.initial_delay = 10500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 2s @10.5:12.5s" }},
{ ACT_SLEEP, { 2000 }},
{ ACT_TERM },
},
},
{
.id = 5,
.wq_id = 4,
.cpu = 1,
.initial_delay = 14500,
.actions = (const struct work_action[]) {
{ ACT_LOG, { .s = "sleeping for 4s @14.5s" }},
{ ACT_SLEEP, { 4000 }},
{ ACT_TERM },
},
},
{ .id = -1 },
};
static const struct test_scenario scenario8 = {
.wq_spec = dfl_wq_spec,
.work_spec =
(const struct work_spec *[]) { work_spec8, NULL },
};
static const struct test_scenario *scenarios[] = {
&scenario0, &scenario1, &scenario2, &scenario3, &scenario4, &scenario5,
&scenario6, &scenario7, &scenario8,
};
/*
* Execute
*/
static struct task_struct *sequencer;
static char wq_names[MAX_WQS][MAX_WQ_NAME];
static struct workqueue_struct *wqs[MAX_WQS];
static struct delayed_work dworks[MAX_WORKS];
#ifdef CONFIG_LOCKDEP
static struct lock_class_key wq_lockdep_keys[MAX_WORKS];
static struct lock_class_key dwork_lockdep_keys[MAX_WORKS];
#endif
static const struct work_spec *work_specs[MAX_WORKS];
static bool sleeping[MAX_WORKS];
static wait_queue_head_t wait_heads[MAX_WORKS];
static int requeue_cnt[MAX_WORKS];
static void test_work_fn(struct work_struct *work)
{
int id = to_delayed_work(work) - dworks;
const struct work_spec *spec = work_specs[id];
const struct work_action *act = spec->actions;
int rc;
#define pd(lvl, fmt, args...) \
printk("w%02d/%04d@%d "lvl": "fmt"\n", id, current->pid, raw_smp_processor_id() , ##args);
#define plog(fmt, args...) pd("LOG ", fmt , ##args)
#define pinfo(fmt, args...) pd("INFO", fmt , ##args)
#define perr(fmt, args...) pd("ERR ", fmt , ##args)
repeat:
switch (act->action) {
case ACT_TERM:
pinfo("TERM");
return;
case ACT_LOG:
plog("%s", act->s);
break;
case ACT_BURN:
mdelay(act->v);
break;
case ACT_SLEEP:
sleeping[id] = true;
wait_event_timeout(wait_heads[id], !sleeping[id],
msecs_to_jiffies(act->v));
if (!sleeping[id])
pinfo("somebody woke me up");
sleeping[id] = false;
break;
case ACT_WAKEUP:
if (act->v < MAX_WORKS && sleeping[act->v]) {
pinfo("waking %lu up", act->v);
sleeping[act->v] = false;
wake_up(&wait_heads[act->v]);
} else
perr("trying to wake up non-sleeping work %lu",
act->v);
break;
case ACT_REQUEUE:
if (requeue_cnt[id] > 0 || requeue_cnt[id] < 0) {
int cpu;
get_online_cpus();
if (spec->cpu < nr_cpu_ids && cpu_online(spec->cpu))
cpu = spec->cpu;
else
cpu = raw_smp_processor_id();
if (act->v)
queue_delayed_work_on(cpu, wqs[spec->wq_id],
&dworks[id],
msecs_to_jiffies(act->v));
else
queue_work_on(cpu, wqs[spec->wq_id],
&dworks[id].work);
pinfo("requeued on cpu%d delay=%lumsecs",
cpu, act->v);
if (requeue_cnt[id] > 0)
requeue_cnt[id]--;
put_online_cpus();
} else
pinfo("requeue limit reached");
break;
case ACT_FLUSH:
if (act->v < MAX_WORKS && work_specs[act->v]) {
pinfo("flushing work %lu", act->v);
rc = flush_work(&dworks[act->v].work);
pinfo("flushed work %lu, rc=%d", act->v, rc);
} else
perr("trying to flush non-existent work %lu", act->v);
break;
case ACT_FLUSH_WQ:
if (act->v < MAX_WQS && wqs[act->v]) {
pinfo("flushing workqueue %lu", act->v);
flush_workqueue(wqs[act->v]);
pinfo("flushed workqueue %lu", act->v);
} else
perr("trying to flush non-existent workqueue %lu",
act->v);
break;
case ACT_CANCEL:
if (act->v < MAX_WORKS && work_specs[act->v]) {
pinfo("canceling work %lu", act->v);
rc = cancel_delayed_work_sync(&dworks[act->v]);
pinfo("canceled work %lu, rc=%d", act->v, rc);
} else
perr("trying to cancel non-existent work %lu", act->v);
break;
}
act++;
goto repeat;
}
#define for_each_work_spec(spec, i, scenario) \
for (i = 0, spec = scenario->work_spec[i]; spec; \
spec = (spec + 1)->id >= 0 ? spec + 1 : scenario->work_spec[++i])
static int sequencer_thread(void *__scenario)
{
const struct test_scenario *scenario = __scenario;
const struct wq_spec *wq_spec;
const struct work_spec *work_spec;
int i, id;
for (wq_spec = scenario->wq_spec; wq_spec->id >= 0; wq_spec++) {
if (wq_spec->id >= MAX_WQS) {
printk("ERR : wq id %d too high\n", wq_spec->id);
goto err;
}
if (wqs[wq_spec->id]) {
printk("ERR : wq %d already occupied\n", wq_spec->id);
goto err;
}
snprintf(wq_names[wq_spec->id], MAX_WQ_NAME, "test-wq-%02d",
wq_spec->id);
wqs[wq_spec->id] = __create_workqueue_key(wq_names[wq_spec->id],
wq_spec->flags,
wq_spec->max_active,
&wq_lockdep_keys[wq_spec->id],
wq_names[wq_spec->id]);
if (!wqs[wq_spec->id]) {
printk("ERR : failed create wq %d\n", wq_spec->id);
goto err;
}
}
for_each_work_spec(work_spec, i, scenario) {
struct delayed_work *dwork = &dworks[work_spec->id];
struct workqueue_struct *wq = wqs[work_spec->wq_id];
if (work_spec->id >= MAX_WORKS) {
printk("ERR : work id %d too high\n", work_spec->id);
goto err;
}
if (!wq) {
printk("ERR : work %d references non-existent wq %d\n",
work_spec->id, work_spec->wq_id);
goto err;
}
if (work_specs[work_spec->id]) {
printk("ERR : work %d already initialized\n",
work_spec->id);
goto err;
}
INIT_DELAYED_WORK(dwork, test_work_fn);
#ifdef CONFIG_LOCKDEP
lockdep_init_map(&dwork->work.lockdep_map, "test-dwork",
&dwork_lockdep_keys[work_spec->id], 0);
#endif
work_specs[work_spec->id] = work_spec;
init_waitqueue_head(&wait_heads[work_spec->id]);
requeue_cnt[work_spec->id] = work_spec->requeue_cnt ?: -1;
}
for_each_work_spec(work_spec, i, scenario) {
struct delayed_work *dwork = &dworks[work_spec->id];
struct workqueue_struct *wq = wqs[work_spec->wq_id];
int cpu;
get_online_cpus();
if (work_spec->cpu < nr_cpu_ids && cpu_online(work_spec->cpu))
cpu = work_spec->cpu;
else
cpu = raw_smp_processor_id();
if (work_spec->initial_delay)
queue_delayed_work_on(cpu, wq, dwork,
msecs_to_jiffies(work_spec->initial_delay));
else
queue_work_on(cpu, wq, &dwork->work);
put_online_cpus();
}
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
err:
for (id = 0; id < MAX_WORKS; id++)
if (work_specs[id])
cancel_delayed_work_sync(&dworks[id]);
for (id = 0; id < MAX_WQS; id++)
if (wqs[id])
destroy_workqueue(wqs[id]);
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
return 0;
}
static int scenario_switch = -1;
module_param_named(scenario, scenario_switch, int, 0444);
static int __init test_init(void)
{
if (scenario_switch < 0 || scenario_switch >= ARRAY_SIZE(scenarios)) {
printk("TEST WQ - no such scenario\n");
return -EINVAL;
}
printk("TEST WQ - executing scenario %d\n", scenario_switch);
sequencer = kthread_run(sequencer_thread,
(void *)scenarios[scenario_switch],
"test-wq-sequencer");
if (IS_ERR(sequencer))
return PTR_ERR(sequencer);
return 0;
}
static void __exit test_exit(void)
{
kthread_stop(sequencer);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
next prev parent reply other threads:[~2009-12-18 13:03 UTC|newest]
Thread overview: 104+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-12-18 12:57 Tejun Heo
2009-12-18 12:57 ` [PATCH 01/27] sched: rename preempt_notifiers to sched_notifiers and refactor implementation Tejun Heo
2009-12-18 12:57 ` [PATCH 02/27] sched: refactor try_to_wake_up() Tejun Heo
2009-12-18 12:57 ` [PATCH 03/27] sched: implement __set_cpus_allowed() Tejun Heo
2009-12-18 12:57 ` [PATCH 04/27] sched: make sched_notifiers unconditional Tejun Heo
2009-12-18 12:57 ` [PATCH 05/27] sched: add wakeup/sleep sched_notifiers and allow NULL notifier ops Tejun Heo
2009-12-18 12:57 ` [PATCH 06/27] sched: implement try_to_wake_up_local() Tejun Heo
2009-12-18 12:57 ` [PATCH 07/27] acpi: use queue_work_on() instead of binding workqueue worker to cpu0 Tejun Heo
2009-12-18 12:57 ` [PATCH 08/27] stop_machine: reimplement without using workqueue Tejun Heo
2009-12-18 12:57 ` [PATCH 09/27] workqueue: misc/cosmetic updates Tejun Heo
2009-12-18 12:57 ` [PATCH 10/27] workqueue: merge feature parameters into flags Tejun Heo
2009-12-18 12:57 ` [PATCH 11/27] workqueue: define both bit position and mask for work flags Tejun Heo
2009-12-18 12:57 ` [PATCH 12/27] workqueue: separate out process_one_work() Tejun Heo
2009-12-18 12:57 ` [PATCH 13/27] workqueue: temporarily disable workqueue tracing Tejun Heo
2009-12-18 12:57 ` [PATCH 14/27] workqueue: kill cpu_populated_map Tejun Heo
2009-12-18 12:57 ` [PATCH 15/27] workqueue: update cwq alignement Tejun Heo
2009-12-18 12:57 ` [PATCH 16/27] workqueue: reimplement workqueue flushing using color coded works Tejun Heo
2009-12-18 12:57 ` [PATCH 17/27] workqueue: introduce worker Tejun Heo
2009-12-18 12:57 ` [PATCH 18/27] workqueue: reimplement work flushing using linked works Tejun Heo
2009-12-18 12:58 ` [PATCH 19/27] workqueue: implement per-cwq active work limit Tejun Heo
2009-12-18 12:58 ` [PATCH 20/27] workqueue: reimplement workqueue freeze using max_active Tejun Heo
2009-12-18 12:58 ` [PATCH 21/27] workqueue: introduce global cwq and unify cwq locks Tejun Heo
2009-12-18 12:58 ` [PATCH 22/27] workqueue: implement worker states Tejun Heo
2009-12-18 12:58 ` [PATCH 23/27] workqueue: reimplement CPU hotplugging support using trustee Tejun Heo
2009-12-18 12:58 ` [PATCH 24/27] workqueue: make single thread workqueue shared worker pool friendly Tejun Heo
2009-12-18 12:58 ` [PATCH 25/27] workqueue: use shared worklist and pool all workers per cpu Tejun Heo
2009-12-18 12:58 ` [PATCH 26/27] workqueue: implement concurrency managed dynamic worker pool Tejun Heo
2009-12-18 12:58 ` [PATCH 27/27] workqueue: increase max_active of keventd and kill current_is_keventd() Tejun Heo
2009-12-18 13:00 ` SUBJ: [RFC PATCHSET] concurrency managed workqueue, take#2 Tejun Heo
2009-12-18 13:03 ` Tejun Heo [this message]
2009-12-18 13:45 ` workqueue thing Peter Zijlstra
2009-12-18 13:50 ` Andi Kleen
2009-12-18 15:01 ` Arjan van de Ven
2009-12-21 3:19 ` Tejun Heo
2009-12-21 9:17 ` Jens Axboe
2009-12-21 10:35 ` Peter Zijlstra
2009-12-21 11:09 ` Andi Kleen
2009-12-21 11:17 ` Arjan van de Ven
2009-12-21 11:33 ` Andi Kleen
2009-12-21 13:18 ` Tejun Heo
2009-12-21 11:11 ` Arjan van de Ven
2009-12-21 13:22 ` Tejun Heo
2009-12-21 13:53 ` Arjan van de Ven
2009-12-21 14:19 ` Tejun Heo
2009-12-21 15:19 ` Arjan van de Ven
2009-12-22 0:00 ` Tejun Heo
2009-12-22 11:10 ` Peter Zijlstra
2009-12-22 17:20 ` Linus Torvalds
2009-12-22 17:47 ` Peter Zijlstra
2009-12-22 18:07 ` Andi Kleen
2009-12-22 18:20 ` Peter Zijlstra
2009-12-23 8:17 ` Stijn Devriendt
2009-12-23 8:43 ` Peter Zijlstra
2009-12-23 9:01 ` Stijn Devriendt
2009-12-22 18:28 ` Linus Torvalds
2009-12-23 8:06 ` Johannes Berg
2009-12-23 3:37 ` Tejun Heo
2009-12-23 6:52 ` Herbert Xu
2009-12-23 8:00 ` Steffen Klassert
2009-12-23 8:01 ` [PATCH 0/2] Parallel crypto/IPsec v7 Steffen Klassert
2009-12-23 8:03 ` [PATCH 1/2] padata: generic parallelization/serialization interface Steffen Klassert
2009-12-23 8:04 ` [PATCH 2/2] crypto: pcrypt - Add pcrypt crypto parallelization wrapper Steffen Klassert
2010-01-07 5:39 ` [PATCH 0/2] Parallel crypto/IPsec v7 Herbert Xu
2010-01-16 9:44 ` David Miller
2009-12-18 15:30 ` workqueue thing Linus Torvalds
2009-12-18 15:39 ` Ingo Molnar
2009-12-18 15:39 ` Peter Zijlstra
2009-12-18 15:47 ` Linus Torvalds
2009-12-18 15:53 ` Peter Zijlstra
2009-12-21 3:04 ` Tejun Heo
2009-12-21 9:22 ` Peter Zijlstra
2009-12-21 13:30 ` Tejun Heo
2009-12-21 14:26 ` Peter Zijlstra
2009-12-21 23:50 ` Tejun Heo
2009-12-22 11:00 ` Peter Zijlstra
2009-12-22 11:03 ` Peter Zijlstra
2009-12-23 3:43 ` Tejun Heo
2009-12-22 11:04 ` Peter Zijlstra
2009-12-23 3:48 ` Tejun Heo
2009-12-22 11:06 ` Peter Zijlstra
2009-12-23 4:18 ` Tejun Heo
2009-12-23 4:42 ` Linus Torvalds
2009-12-23 6:02 ` Ingo Molnar
2009-12-23 6:13 ` Jeff Garzik
2009-12-23 7:53 ` Ingo Molnar
2009-12-23 8:41 ` Peter Zijlstra
2009-12-23 10:25 ` Jeff Garzik
2009-12-23 13:33 ` Stefan Richter
2009-12-23 14:20 ` Mark Brown
2009-12-23 7:09 ` Tejun Heo
2009-12-23 8:01 ` Ingo Molnar
2009-12-23 8:12 ` Ingo Molnar
2009-12-23 8:32 ` Tejun Heo
2009-12-23 8:42 ` Ingo Molnar
2009-12-23 8:27 ` Tejun Heo
2009-12-23 8:37 ` Ingo Molnar
2009-12-23 8:49 ` Tejun Heo
2009-12-23 8:49 ` Ingo Molnar
2009-12-23 9:03 ` Tejun Heo
2009-12-23 13:40 ` Stefan Richter
2009-12-23 13:43 ` Stefan Richter
2009-12-23 8:25 ` Arjan van de Ven
2009-12-23 13:00 ` Stefan Richter
2009-12-23 8:31 ` Stijn Devriendt
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=4B2B7DA8.2070004@kernel.org \
--to=tj@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=andi@firstfloor.org \
--cc=arjan@linux.intel.com \
--cc=avi@redhat.com \
--cc=awalls@radix.net \
--cc=cl@linux-foundation.org \
--cc=dhowells@redhat.com \
--cc=jeff@garzik.org \
--cc=jens.axboe@oracle.com \
--cc=johannes@sipsolutions.net \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@elte.hu \
--cc=peterz@infradead.org \
--cc=rusty@rustcorp.com.au \
--cc=torvalds@linux-foundation.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox