Skip to content

Commit

Permalink
WorkStruct: Pass the work_struct pointer instead of context data
Browse files Browse the repository at this point in the history
Pass the work_struct pointer to the work function rather than context data.
The work function can use container_of() to work out the data.

For the cases where the container of the work_struct may go away the moment the
pending bit is cleared, it is made possible to defer the release of the
structure by deferring the clearing of the pending bit.

To make this work, an extra flag is introduced into the management side of the
work_struct.  This governs auto-release of the structure upon execution.

Ordinarily, the work queue executor would release the work_struct for further
scheduling or deallocation by clearing the pending bit prior to jumping to the
work function.  This means that, unless the driver makes some guarantee itself
that the work_struct won't go away, the work function may not access anything
else in the work_struct or its container lest they be deallocated..  This is a
problem if the auxiliary data is taken away (as done by the last patch).

However, if the pending bit is *not* cleared before jumping to the work
function, then the work function *may* access the work_struct and its container
with no problems.  But then the work function must itself release the
work_struct by calling work_release().

In most cases, automatic release is fine, so this is the default.  Special
initiators exist for the non-auto-release case (ending in _NAR).


Signed-Off-By: David Howells <[email protected]>
  • Loading branch information
dhowells committed Nov 22, 2006
1 parent 365970a commit 65f27f3
Show file tree
Hide file tree
Showing 51 changed files with 293 additions and 219 deletions.
6 changes: 3 additions & 3 deletions arch/x86_64/kernel/mce.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,16 +306,16 @@ void mce_log_therm_throt_event(unsigned int cpu, __u64 status)
*/

static int check_interval = 5 * 60; /* 5 minutes */
static void mcheck_timer(void *data);
static DECLARE_DELAYED_WORK(mcheck_work, mcheck_timer, NULL);
static void mcheck_timer(struct work_struct *work);
static DECLARE_DELAYED_WORK(mcheck_work, mcheck_timer);

static void mcheck_check_cpu(void *info)
{
if (mce_available(&current_cpu_data))
do_machine_check(NULL, 0);
}

static void mcheck_timer(void *data)
static void mcheck_timer(struct work_struct *work)
{
on_each_cpu(mcheck_check_cpu, NULL, 1, 1);
schedule_delayed_work(&mcheck_work, check_interval * HZ);
Expand Down
12 changes: 7 additions & 5 deletions arch/x86_64/kernel/smpboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -753,14 +753,16 @@ static int __cpuinit wakeup_secondary_via_INIT(int phys_apicid, unsigned int sta
}

struct create_idle {
struct work_struct work;
struct task_struct *idle;
struct completion done;
int cpu;
};

void do_fork_idle(void *_c_idle)
void do_fork_idle(struct work_struct *work)
{
struct create_idle *c_idle = _c_idle;
struct create_idle *c_idle =
container_of(work, struct create_idle, work);

c_idle->idle = fork_idle(c_idle->cpu);
complete(&c_idle->done);
Expand All @@ -775,10 +777,10 @@ static int __cpuinit do_boot_cpu(int cpu, int apicid)
int timeout;
unsigned long start_rip;
struct create_idle c_idle = {
.work = __WORK_INITIALIZER(c_idle.work, do_fork_idle),
.cpu = cpu,
.done = COMPLETION_INITIALIZER_ONSTACK(c_idle.done),
};
DECLARE_WORK(work, do_fork_idle, &c_idle);

/* allocate memory for gdts of secondary cpus. Hotplug is considered */
if (!cpu_gdt_descr[cpu].address &&
Expand Down Expand Up @@ -825,9 +827,9 @@ static int __cpuinit do_boot_cpu(int cpu, int apicid)
* thread.
*/
if (!keventd_up() || current_is_keventd())
work.func(work.data);
c_idle.work.func(&c_idle.work);
else {
schedule_work(&work);
schedule_work(&c_idle.work);
wait_for_completion(&c_idle.done);
}

Expand Down
4 changes: 2 additions & 2 deletions arch/x86_64/kernel/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ static unsigned int cpufreq_delayed_issched = 0;
static unsigned int cpufreq_init = 0;
static struct work_struct cpufreq_delayed_get_work;

static void handle_cpufreq_delayed_get(void *v)
static void handle_cpufreq_delayed_get(struct work_struct *v)
{
unsigned int cpu;
for_each_online_cpu(cpu) {
Expand Down Expand Up @@ -639,7 +639,7 @@ static struct notifier_block time_cpufreq_notifier_block = {

static int __init cpufreq_tsc(void)
{
INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get, NULL);
INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get);
if (!cpufreq_register_notifier(&time_cpufreq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER))
cpufreq_init = 1;
Expand Down
7 changes: 4 additions & 3 deletions block/as-iosched.c
Original file line number Diff line number Diff line change
Expand Up @@ -1274,9 +1274,10 @@ static void as_merged_requests(request_queue_t *q, struct request *req,
*
* FIXME! dispatch queue is not a queue at all!
*/
static void as_work_handler(void *data)
static void as_work_handler(struct work_struct *work)
{
struct request_queue *q = data;
struct as_data *ad = container_of(work, struct as_data, antic_work);
struct request_queue *q = ad->q;
unsigned long flags;

spin_lock_irqsave(q->queue_lock, flags);
Expand Down Expand Up @@ -1332,7 +1333,7 @@ static void *as_init_queue(request_queue_t *q, elevator_t *e)
ad->antic_timer.function = as_antic_timeout;
ad->antic_timer.data = (unsigned long)q;
init_timer(&ad->antic_timer);
INIT_WORK(&ad->antic_work, as_work_handler, q);
INIT_WORK(&ad->antic_work, as_work_handler);

INIT_LIST_HEAD(&ad->fifo_list[REQ_SYNC]);
INIT_LIST_HEAD(&ad->fifo_list[REQ_ASYNC]);
Expand Down
8 changes: 5 additions & 3 deletions block/cfq-iosched.c
Original file line number Diff line number Diff line change
Expand Up @@ -1841,9 +1841,11 @@ cfq_set_request(request_queue_t *q, struct request *rq, gfp_t gfp_mask)
return 1;
}

static void cfq_kick_queue(void *data)
static void cfq_kick_queue(struct work_struct *work)
{
request_queue_t *q = data;
struct cfq_data *cfqd =
container_of(work, struct cfq_data, unplug_work);
request_queue_t *q = cfqd->queue;
unsigned long flags;

spin_lock_irqsave(q->queue_lock, flags);
Expand Down Expand Up @@ -1987,7 +1989,7 @@ static void *cfq_init_queue(request_queue_t *q, elevator_t *e)
cfqd->idle_class_timer.function = cfq_idle_class_timer;
cfqd->idle_class_timer.data = (unsigned long) cfqd;

INIT_WORK(&cfqd->unplug_work, cfq_kick_queue, q);
INIT_WORK(&cfqd->unplug_work, cfq_kick_queue);

cfqd->cfq_quantum = cfq_quantum;
cfqd->cfq_fifo_expire[0] = cfq_fifo_expire[0];
Expand Down
8 changes: 4 additions & 4 deletions block/ll_rw_blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
*/
#include <scsi/scsi_cmnd.h>

static void blk_unplug_work(void *data);
static void blk_unplug_work(struct work_struct *work);
static void blk_unplug_timeout(unsigned long data);
static void drive_stat_acct(struct request *rq, int nr_sectors, int new_io);
static void init_request_from_bio(struct request *req, struct bio *bio);
Expand Down Expand Up @@ -227,7 +227,7 @@ void blk_queue_make_request(request_queue_t * q, make_request_fn * mfn)
if (q->unplug_delay == 0)
q->unplug_delay = 1;

INIT_WORK(&q->unplug_work, blk_unplug_work, q);
INIT_WORK(&q->unplug_work, blk_unplug_work);

q->unplug_timer.function = blk_unplug_timeout;
q->unplug_timer.data = (unsigned long)q;
Expand Down Expand Up @@ -1631,9 +1631,9 @@ static void blk_backing_dev_unplug(struct backing_dev_info *bdi,
}
}

static void blk_unplug_work(void *data)
static void blk_unplug_work(struct work_struct *work)
{
request_queue_t *q = data;
request_queue_t *q = container_of(work, request_queue_t, unplug_work);

blk_add_trace_pdu_int(q, BLK_TA_UNPLUG_IO, NULL,
q->rq.count[READ] + q->rq.count[WRITE]);
Expand Down
7 changes: 4 additions & 3 deletions crypto/cryptomgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ struct cryptomgr_param {
char template[CRYPTO_MAX_ALG_NAME];
};

static void cryptomgr_probe(void *data)
static void cryptomgr_probe(struct work_struct *work)
{
struct cryptomgr_param *param = data;
struct cryptomgr_param *param =
container_of(work, struct cryptomgr_param, work);
struct crypto_template *tmpl;
struct crypto_instance *inst;
int err;
Expand Down Expand Up @@ -112,7 +113,7 @@ static int cryptomgr_schedule_probe(struct crypto_larval *larval)
param->larval.type = larval->alg.cra_flags;
param->larval.mask = larval->mask;

INIT_WORK(&param->work, cryptomgr_probe, param);
INIT_WORK(&param->work, cryptomgr_probe);
schedule_work(&param->work);

return NOTIFY_STOP;
Expand Down
25 changes: 8 additions & 17 deletions drivers/acpi/osl.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ ACPI_MODULE_NAME("osl")
struct acpi_os_dpc {
acpi_osd_exec_callback function;
void *context;
struct work_struct work;
};

#ifdef CONFIG_ACPI_CUSTOM_DSDT
Expand Down Expand Up @@ -564,12 +565,9 @@ void acpi_os_derive_pci_id(acpi_handle rhandle, /* upper bound */
acpi_os_derive_pci_id_2(rhandle, chandle, id, &is_bridge, &bus_number);
}

static void acpi_os_execute_deferred(void *context)
static void acpi_os_execute_deferred(struct work_struct *work)
{
struct acpi_os_dpc *dpc = NULL;


dpc = (struct acpi_os_dpc *)context;
struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work);
if (!dpc) {
printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
return;
Expand Down Expand Up @@ -602,7 +600,6 @@ acpi_status acpi_os_execute(acpi_execute_type type,
{
acpi_status status = AE_OK;
struct acpi_os_dpc *dpc;
struct work_struct *task;

ACPI_FUNCTION_TRACE("os_queue_for_execution");

Expand All @@ -615,28 +612,22 @@ acpi_status acpi_os_execute(acpi_execute_type type,

/*
* Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee. The kernel handles the tq_struct list in a
* freed by the callee. The kernel handles the work_struct list in a
* way that allows us to also free its memory inside the callee.
* Because we may want to schedule several tasks with different
* parameters we can't use the approach some kernel code uses of
* having a static tq_struct.
* We can save time and code by allocating the DPC and tq_structs
* from the same memory.
* having a static work_struct.
*/

dpc =
kmalloc(sizeof(struct acpi_os_dpc) + sizeof(struct work_struct),
GFP_ATOMIC);
dpc = kmalloc(sizeof(struct acpi_os_dpc), GFP_ATOMIC);
if (!dpc)
return_ACPI_STATUS(AE_NO_MEMORY);

dpc->function = function;
dpc->context = context;

task = (void *)(dpc + 1);
INIT_WORK(task, acpi_os_execute_deferred, (void *)dpc);

if (!queue_work(kacpid_wq, task)) {
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
if (!queue_work(kacpid_wq, &dpc->work)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Call to queue_work() failed.\n"));
kfree(dpc);
Expand Down
20 changes: 11 additions & 9 deletions drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,7 @@ static unsigned int ata_id_xfermask(const u16 *id)
* ata_port_queue_task - Queue port_task
* @ap: The ata_port to queue port_task for
* @fn: workqueue function to be scheduled
* @data: data value to pass to workqueue function
* @data: data for @fn to use
* @delay: delay time for workqueue function
*
* Schedule @fn(@data) for execution after @delay jiffies using
Expand All @@ -929,15 +929,16 @@ static unsigned int ata_id_xfermask(const u16 *id)
* LOCKING:
* Inherited from caller.
*/
void ata_port_queue_task(struct ata_port *ap, void (*fn)(void *), void *data,
void ata_port_queue_task(struct ata_port *ap, work_func_t fn, void *data,
unsigned long delay)
{
int rc;

if (ap->pflags & ATA_PFLAG_FLUSH_PORT_TASK)
return;

PREPARE_DELAYED_WORK(&ap->port_task, fn, data);
PREPARE_DELAYED_WORK(&ap->port_task, fn);
ap->port_task_data = data;

rc = queue_delayed_work(ata_wq, &ap->port_task, delay);

Expand Down Expand Up @@ -4292,10 +4293,11 @@ int ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
return poll_next;
}

static void ata_pio_task(void *_data)
static void ata_pio_task(struct work_struct *work)
{
struct ata_queued_cmd *qc = _data;
struct ata_port *ap = qc->ap;
struct ata_port *ap =
container_of(work, struct ata_port, port_task.work);
struct ata_queued_cmd *qc = ap->port_task_data;
u8 status;
int poll_next;

Expand Down Expand Up @@ -5317,9 +5319,9 @@ void ata_port_init(struct ata_port *ap, struct ata_host *host,
ap->msg_enable = ATA_MSG_DRV | ATA_MSG_ERR | ATA_MSG_WARN;
#endif

INIT_DELAYED_WORK(&ap->port_task, NULL, NULL);
INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug, ap);
INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan, ap);
INIT_DELAYED_WORK(&ap->port_task, NULL);
INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug);
INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
INIT_LIST_HEAD(&ap->eh_done_q);
init_waitqueue_head(&ap->eh_wait_q);

Expand Down
14 changes: 8 additions & 6 deletions drivers/ata/libata-scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -3079,7 +3079,7 @@ static void ata_scsi_remove_dev(struct ata_device *dev)

/**
* ata_scsi_hotplug - SCSI part of hotplug
* @data: Pointer to ATA port to perform SCSI hotplug on
* @work: Pointer to ATA port to perform SCSI hotplug on
*
* Perform SCSI part of hotplug. It's executed from a separate
* workqueue after EH completes. This is necessary because SCSI
Expand All @@ -3089,9 +3089,10 @@ static void ata_scsi_remove_dev(struct ata_device *dev)
* LOCKING:
* Kernel thread context (may sleep).
*/
void ata_scsi_hotplug(void *data)
void ata_scsi_hotplug(struct work_struct *work)
{
struct ata_port *ap = data;
struct ata_port *ap =
container_of(work, struct ata_port, hotplug_task.work);
int i;

if (ap->pflags & ATA_PFLAG_UNLOADING) {
Expand Down Expand Up @@ -3190,7 +3191,7 @@ static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,

/**
* ata_scsi_dev_rescan - initiate scsi_rescan_device()
* @data: Pointer to ATA port to perform scsi_rescan_device()
* @work: Pointer to ATA port to perform scsi_rescan_device()
*
* After ATA pass thru (SAT) commands are executed successfully,
* libata need to propagate the changes to SCSI layer. This
Expand All @@ -3200,9 +3201,10 @@ static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
* LOCKING:
* Kernel thread context (may sleep).
*/
void ata_scsi_dev_rescan(void *data)
void ata_scsi_dev_rescan(struct work_struct *work)
{
struct ata_port *ap = data;
struct ata_port *ap =
container_of(work, struct ata_port, scsi_rescan_task);
struct ata_device *dev;
unsigned int i;

Expand Down
4 changes: 2 additions & 2 deletions drivers/ata/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ extern struct scsi_transport_template ata_scsi_transport_template;

extern void ata_scsi_scan_host(struct ata_port *ap);
extern int ata_scsi_offline_dev(struct ata_device *dev);
extern void ata_scsi_hotplug(void *data);
extern void ata_scsi_hotplug(struct work_struct *work);
extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
unsigned int buflen);

Expand Down Expand Up @@ -111,7 +111,7 @@ extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args,
unsigned int (*actor) (struct ata_scsi_args *args,
u8 *rbuf, unsigned int buflen));
extern void ata_schedule_scsi_eh(struct Scsi_Host *shost);
extern void ata_scsi_dev_rescan(void *data);
extern void ata_scsi_dev_rescan(struct work_struct *work);
extern int ata_bus_probe(struct ata_port *ap);

/* libata-eh.c */
Expand Down
6 changes: 3 additions & 3 deletions drivers/block/floppy.c
Original file line number Diff line number Diff line change
Expand Up @@ -992,11 +992,11 @@ static void empty(void)
{
}

static DECLARE_WORK(floppy_work, NULL, NULL);
static DECLARE_WORK(floppy_work, NULL);

static void schedule_bh(void (*handler) (void))
{
PREPARE_WORK(&floppy_work, (work_func_t)handler, NULL);
PREPARE_WORK(&floppy_work, (work_func_t)handler);
schedule_work(&floppy_work);
}

Expand All @@ -1008,7 +1008,7 @@ static void cancel_activity(void)

spin_lock_irqsave(&floppy_lock, flags);
do_floppy = NULL;
PREPARE_WORK(&floppy_work, (work_func_t)empty, NULL);
PREPARE_WORK(&floppy_work, (work_func_t)empty);
del_timer(&fd_timer);
spin_unlock_irqrestore(&floppy_lock, flags);
}
Expand Down
Loading

0 comments on commit 65f27f3

Please sign in to comment.