TASKQUEUE(9) Kernel Developer's Manual TASKQUEUE(9)

NAME

taskqueuequeues for running tasks independently at different priorities

SYNOPSIS

#include <sys/task.h>

struct taskqueue *system_taskqueue;

int
taskqueue_get(struct taskqueue **taskqueuep, pri_t pri);

void
taskqueue_put(struct taskqueue *taskqueue);

int
taskqueue_create(struct taskqueue **taskqueuep, const char *name, pri_t pri, int ipl, int flags);

void
taskqueue_destroy(struct taskqueue *taskqueue);

void
taskqueue_schedule(struct taskqueue *taskqueue, struct task *task);

void
taskqueue_schedule_delayed(struct taskqueue *taskqueue, struct delayed_task *dt, nsec_t nsec, jitter_t jitter);

void
taskqueue_drain(struct taskqueue *);

DESCRIPTION

The taskqueue facility allows task(9) and delayed_task(9) tasks to be scheduled to run at different priorities and independently from other task queues.

The system_taskqueue is a shared low-priority task queue for running tasks in a thread at PRI_NONE, and is used by task_schedule(), delayed_task_schedule(), and task_drain(). For every priority level, there is a shared system task queue which you can acquire with taskqueue_get() and release with taskqueue_put(). Task queues at soft interrupt priorities, such as PRI_SOFTCLOCK, run tasks in soft interrupt context. Task queues at priorities below that run tasks in thread context.

Each system shared task queue runs tasks one at a time per CPU at its priority level. Tasks on shared system task queues are not allowed long sleeps. To run tasks concurrently with other tasks on a CPU, and to run tasks that may sleep, for example to allocate memory, you can create your own task queue with taskqueue_create() and destroy it with taskqueue_destroy().

Warning: Do not schedule a task on multiple different task queues without synchronously cancelling it first. The consequences of doing so are undefined.

FUNCTIONS

system_taskqueue
Shared system task queue for running tasks at PRI_NONE.
taskqueue_get(taskqueuep, pri)
Try to get a shared system task queue to run tasks at priority pri. On success, store a struct taskqueue pointer in taskqueuep and return zero. On failure, return an error code. May sleep.

Task queues acquired with taskqueue_get() must be released with taskqueue_put(). Do not pass them to taskqueue_destroy().

taskqueue_put(taskqueue)
Release a task queue acquired with taskqueue_get(). Caller must not have any tasks, delayed or not, still scheduled on taskqueue. If caller holds the last reference, this is an expensive global synchronization. May sleep.
taskqueue_create(taskqueuep, name, pri, ipl, flags)
Try to create a task queue named name for scheduling tasks from interrupt priority level ipl to run at priority pri. The argument flags must be TASKQUEUE_PERCPU. May sleep.

Task queues created with taskqueue_create() must be destroyed with taskqueue_destroy(). Do not pass them to taskqueue_put().

Tasks running on this task queue are independent from tasks running on other task queues, so tasks on one queue will not block tasks on the other. However, tasks running on this queue on the same CPU will block one another.

taskqueue_destroy(taskqueue)
Destroy a task queue created with taskqueue_create(). taskqueue must not have any tasks, delayed or not, still scheduled. This is an expensive global synchronization operation. May sleep.
taskqueue_schedule(taskqueue, task)
Schedule task to run on taskqueue as soon as possible. If task is already scheduled to run, this has no effect. If task has already begun to run but has not yet completed, this schedules it to run again as soon as possible after it completes.

May be called from any interrupt priority level at or below the ipl passed to taskqueue_create(), or IPL_VM in the case of all shared system task queues.

task_schedule() is equivalent to passing system_taskqueue to taskqueue_schedule().

taskqueue_schedule_delayed(taskqueue, dt, nsec, jitter)
Schedule dt to run on taskqueue after nsec nanoseconds with the specified jitter. If dt is already scheduled to run, or is already running and has not called delayed_task_done(), this has no effect.

May be called from any interrupt priority level at or below the ipl passed to taskqueue_create(), or IPL_VM in the case of all shared system task queues.

delayed_task_schedule() is equivalent to passing system_taskqueue to taskqueue_schedule_delayed().

taskqueue_drain(taskqueue)
Wait for all non-delayed tasks that have been scheduled so far on taskqueue to return. Calling task_done() in the action is not enough: every action must return before taskqueue_drain() returns. This is an expensive global synchronization operation. May sleep.

Note: taskqueue_drain() does not wait for any delayed_task(9) tasks to complete.

task_drain() is equivalent to passing system_taskqueue to taskqueue_drain().

EXAMPLES

The following code illustrates a device driver that runs some tasks at high priority in soft interrupt context and some tasks at low priority in thread context.

struct mydev_softc { 
	... 
	kmutex_t		sc_lock; 
	struct taskqueue	*sc_highpri_taskq; 
	struct task		sc_highpri_task; 
	uint32_t		sc_highpri_set; 
	struct taskqueue	*sc_lowpri_taskq; 
	struct task		sc_lowpri_task; 
	uint32_t		sc_lowpri_set; 
}; 
 
static void 
mydev_attach(device_t parent, device_t self, void *aux) 
{ 
	struct mydev_softc *sc = device_private(self); 
	... 
	error = taskqueue_get(&sc->sc_highpri_taskq, PRI_SOFTNET); 
	if (error) 
		goto fail0; 
	task_init(&sc->sc_highpri_task, &mydev_highpri_task); 
	error = taskqueue_create(&sc->sc_lowpri_taskq, "mydev", PRI_NONE, 
	    IPL_NET, TASKQUEUE_PERCPU); 
	if (error) 
		goto fail1; 
	task_init(&sc->sc_lowpri_task, &mydev_lowpri_task); 
	... 
} 
 
static void 
mydev_detach(device_t self, int flags) 
{ 
	... 
	if (sc->sc_lowpri_taskq != NULL) { 
		taskqueue_drain(sc->sc_lowpri_taskq); 
		task_destroy(&sc->sc_lowpri_task); 
		taskqueue_destroy(sc->sc_lowpri_taskq); 
	} 
	if (sc->sc_highpri_taskq != NULL) { 
		taskqueue_drain(sc->sc_highpri_taskq); 
		task_destroy(&sc->sc_highpri_task); 
		taskqueue_put(sc->sc_highpri_taskq); 
	} 
	... 
} 
 
static void 
mydev_intr(void *arg) 
{ 
	struct mydev_softc *sc = arg; 
 
	mutex_enter(&sc->sc_lock); 
	...read/write hardware registers to get/ack interrupts... 
	if (ISSET(intrmask, MYDEV_HIGHPRI_EVENT)) { 
		sc->sc_highpri_set |= 
		    __SHIFTOUT(intrmask, MYDEV_HIGHPRI_CRUD); 
		taskqueue_schedule(sc->sc_highpri_taskq, 
		    &sc->sc_highpri_task); 
	} 
	if (ISSET(intrmask, MYDEV_LOWPRI_EVENT)) { 
		sc->sc_lowpri_set |= 
		    __SHIFTOUT(intrmask, MYDEV_LOWPRI_CRUD); 
		taskqueue_schedule(sc->sc_lowpri_taskq, 
		    &sc->sc_lowpri_task); 
	} 
	mutex_exit(&sc->sc_lock); 
	... 
}

CODE REFERENCES

The taskqueue abstraction is implemented in sys/kern/kern_task.c.

SEE ALSO

callout(9), delayed_task(9), softint(9), task(9), workqueue(9)
March 27, 2014 NetBSD 6.1_RC3