Changeset View
Changeset View
Standalone View
Standalone View
head/sys/compat/linuxkpi/common/src/linux_work.c
Show First 20 Lines • Show All 208 Lines • ▼ Show 20 Lines | |||||
void | void | ||||
linux_work_fn(void *context, int pending) | linux_work_fn(void *context, int pending) | ||||
{ | { | ||||
static const uint8_t states[WORK_ST_MAX] __aligned(8) = { | static const uint8_t states[WORK_ST_MAX] __aligned(8) = { | ||||
[WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ | [WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ | ||||
[WORK_ST_TIMER] = WORK_ST_EXEC, /* delayed work w/o timeout */ | [WORK_ST_TIMER] = WORK_ST_EXEC, /* delayed work w/o timeout */ | ||||
[WORK_ST_TASK] = WORK_ST_EXEC, /* call callback */ | [WORK_ST_TASK] = WORK_ST_EXEC, /* call callback */ | ||||
[WORK_ST_EXEC] = WORK_ST_IDLE, /* complete callback */ | [WORK_ST_EXEC] = WORK_ST_IDLE, /* complete callback */ | ||||
[WORK_ST_CANCEL] = WORK_ST_IDLE, /* complete cancel */ | [WORK_ST_CANCEL] = WORK_ST_EXEC, /* failed to cancel */ | ||||
}; | }; | ||||
struct work_struct *work; | struct work_struct *work; | ||||
struct workqueue_struct *wq; | struct workqueue_struct *wq; | ||||
struct work_exec exec; | struct work_exec exec; | ||||
linux_set_current(curthread); | linux_set_current(curthread); | ||||
/* setup local variables */ | /* setup local variables */ | ||||
work = context; | work = context; | ||||
wq = work->work_queue; | wq = work->work_queue; | ||||
/* store target pointer */ | /* store target pointer */ | ||||
exec.target = work; | exec.target = work; | ||||
/* insert executor into list */ | /* insert executor into list */ | ||||
WQ_EXEC_LOCK(wq); | WQ_EXEC_LOCK(wq); | ||||
TAILQ_INSERT_TAIL(&wq->exec_head, &exec, entry); | TAILQ_INSERT_TAIL(&wq->exec_head, &exec, entry); | ||||
while (1) { | while (1) { | ||||
switch (linux_update_state(&work->state, states)) { | switch (linux_update_state(&work->state, states)) { | ||||
case WORK_ST_TIMER: | case WORK_ST_TIMER: | ||||
case WORK_ST_TASK: | case WORK_ST_TASK: | ||||
case WORK_ST_CANCEL: | |||||
WQ_EXEC_UNLOCK(wq); | WQ_EXEC_UNLOCK(wq); | ||||
/* call work function */ | /* call work function */ | ||||
work->func(work); | work->func(work); | ||||
WQ_EXEC_LOCK(wq); | WQ_EXEC_LOCK(wq); | ||||
/* check if unblocked */ | /* check if unblocked */ | ||||
if (exec.target != work) { | if (exec.target != work) { | ||||
Show All 14 Lines | |||||
static void | static void | ||||
linux_delayed_work_timer_fn(void *arg) | linux_delayed_work_timer_fn(void *arg) | ||||
{ | { | ||||
static const uint8_t states[WORK_ST_MAX] __aligned(8) = { | static const uint8_t states[WORK_ST_MAX] __aligned(8) = { | ||||
[WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ | [WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ | ||||
[WORK_ST_TIMER] = WORK_ST_TASK, /* start queueing task */ | [WORK_ST_TIMER] = WORK_ST_TASK, /* start queueing task */ | ||||
[WORK_ST_TASK] = WORK_ST_TASK, /* NOP */ | [WORK_ST_TASK] = WORK_ST_TASK, /* NOP */ | ||||
[WORK_ST_EXEC] = WORK_ST_TASK, /* queue task another time */ | [WORK_ST_EXEC] = WORK_ST_EXEC, /* NOP */ | ||||
[WORK_ST_CANCEL] = WORK_ST_IDLE, /* complete cancel */ | [WORK_ST_CANCEL] = WORK_ST_TASK, /* failed to cancel */ | ||||
}; | }; | ||||
struct delayed_work *dwork = arg; | struct delayed_work *dwork = arg; | ||||
switch (linux_update_state(&dwork->work.state, states)) { | switch (linux_update_state(&dwork->work.state, states)) { | ||||
case WORK_ST_TIMER: | case WORK_ST_TIMER: | ||||
case WORK_ST_CANCEL: | |||||
linux_delayed_work_enqueue(dwork); | linux_delayed_work_enqueue(dwork); | ||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* This function cancels the given work structure in a synchronous | * This function cancels the given work structure in a synchronous | ||||
* fashion. It returns non-zero if the work was successfully | * fashion. It returns non-zero if the work was successfully | ||||
* cancelled. Else the work was already cancelled. | * cancelled. Else the work was already cancelled. | ||||
*/ | */ | ||||
bool | bool | ||||
linux_cancel_work_sync(struct work_struct *work) | linux_cancel_work_sync(struct work_struct *work) | ||||
{ | { | ||||
static const uint8_t states[WORK_ST_MAX] __aligned(8) = { | static const uint8_t states[WORK_ST_MAX] __aligned(8) = { | ||||
[WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ | [WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ | ||||
[WORK_ST_TIMER] = WORK_ST_IDLE, /* idle */ | [WORK_ST_TIMER] = WORK_ST_TIMER, /* can't happen */ | ||||
[WORK_ST_TASK] = WORK_ST_IDLE, /* idle */ | [WORK_ST_TASK] = WORK_ST_IDLE, /* cancel and drain */ | ||||
[WORK_ST_EXEC] = WORK_ST_IDLE, /* idle */ | [WORK_ST_EXEC] = WORK_ST_IDLE, /* too late, drain */ | ||||
[WORK_ST_CANCEL] = WORK_ST_IDLE, /* idle */ | [WORK_ST_CANCEL] = WORK_ST_IDLE, /* cancel and drain */ | ||||
}; | }; | ||||
struct taskqueue *tq; | struct taskqueue *tq; | ||||
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, | WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, | ||||
"linux_cancel_work_sync() might sleep"); | "linux_cancel_work_sync() might sleep"); | ||||
switch (linux_update_state(&work->state, states)) { | switch (linux_update_state(&work->state, states)) { | ||||
case WORK_ST_IDLE: | case WORK_ST_IDLE: | ||||
case WORK_ST_TIMER: | |||||
return (0); | return (0); | ||||
case WORK_ST_EXEC: | |||||
tq = work->work_queue->taskqueue; | |||||
if (taskqueue_cancel(tq, &work->work_task, NULL) != 0) | |||||
taskqueue_drain(tq, &work->work_task); | |||||
return (0); | |||||
default: | default: | ||||
tq = work->work_queue->taskqueue; | tq = work->work_queue->taskqueue; | ||||
if (taskqueue_cancel(tq, &work->work_task, NULL) != 0) | if (taskqueue_cancel(tq, &work->work_task, NULL) != 0) | ||||
taskqueue_drain(tq, &work->work_task); | taskqueue_drain(tq, &work->work_task); | ||||
return (1); | return (1); | ||||
} | } | ||||
} | } | ||||
Show All 24 Lines | |||||
* successfully cancelled. Else the work may still be busy or already | * successfully cancelled. Else the work may still be busy or already | ||||
* cancelled. | * cancelled. | ||||
*/ | */ | ||||
bool | bool | ||||
linux_cancel_delayed_work(struct delayed_work *dwork) | linux_cancel_delayed_work(struct delayed_work *dwork) | ||||
{ | { | ||||
static const uint8_t states[WORK_ST_MAX] __aligned(8) = { | static const uint8_t states[WORK_ST_MAX] __aligned(8) = { | ||||
[WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ | [WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ | ||||
[WORK_ST_TIMER] = WORK_ST_CANCEL, /* cancel */ | [WORK_ST_TIMER] = WORK_ST_CANCEL, /* try to cancel */ | ||||
[WORK_ST_TASK] = WORK_ST_CANCEL, /* cancel */ | [WORK_ST_TASK] = WORK_ST_CANCEL, /* try to cancel */ | ||||
[WORK_ST_EXEC] = WORK_ST_CANCEL, /* cancel */ | [WORK_ST_EXEC] = WORK_ST_EXEC, /* NOP */ | ||||
[WORK_ST_CANCEL] = WORK_ST_CANCEL, /* cancel */ | [WORK_ST_CANCEL] = WORK_ST_CANCEL, /* NOP */ | ||||
}; | }; | ||||
struct taskqueue *tq; | struct taskqueue *tq; | ||||
switch (linux_update_state(&dwork->work.state, states)) { | switch (linux_update_state(&dwork->work.state, states)) { | ||||
case WORK_ST_TIMER: | case WORK_ST_TIMER: | ||||
if (linux_cancel_timer(dwork, 0)) | case WORK_ST_CANCEL: | ||||
if (linux_cancel_timer(dwork, 0)) { | |||||
atomic_cmpxchg(&dwork->work.state, | |||||
WORK_ST_CANCEL, WORK_ST_IDLE); | |||||
return (1); | return (1); | ||||
} | |||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case WORK_ST_TASK: | case WORK_ST_TASK: | ||||
case WORK_ST_EXEC: | |||||
tq = dwork->work.work_queue->taskqueue; | tq = dwork->work.work_queue->taskqueue; | ||||
if (taskqueue_cancel(tq, &dwork->work.work_task, NULL) == 0) | if (taskqueue_cancel(tq, &dwork->work.work_task, NULL) == 0) { | ||||
atomic_cmpxchg(&dwork->work.state, | |||||
WORK_ST_CANCEL, WORK_ST_IDLE); | |||||
return (1); | return (1); | ||||
} | |||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
default: | default: | ||||
return (0); | return (0); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* This function cancels the given work structure in a synchronous | * This function cancels the given work structure in a synchronous | ||||
* fashion. It returns non-zero if the work was successfully | * fashion. It returns non-zero if the work was successfully | ||||
* cancelled. Else the work was already cancelled. | * cancelled. Else the work was already cancelled. | ||||
*/ | */ | ||||
bool | bool | ||||
linux_cancel_delayed_work_sync(struct delayed_work *dwork) | linux_cancel_delayed_work_sync(struct delayed_work *dwork) | ||||
{ | { | ||||
static const uint8_t states[WORK_ST_MAX] __aligned(8) = { | static const uint8_t states[WORK_ST_MAX] __aligned(8) = { | ||||
[WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ | [WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */ | ||||
[WORK_ST_TIMER] = WORK_ST_IDLE, /* idle */ | [WORK_ST_TIMER] = WORK_ST_IDLE, /* cancel and drain */ | ||||
[WORK_ST_TASK] = WORK_ST_IDLE, /* idle */ | [WORK_ST_TASK] = WORK_ST_IDLE, /* cancel and drain */ | ||||
[WORK_ST_EXEC] = WORK_ST_IDLE, /* idle */ | [WORK_ST_EXEC] = WORK_ST_IDLE, /* too late, drain */ | ||||
[WORK_ST_CANCEL] = WORK_ST_IDLE, /* idle */ | [WORK_ST_CANCEL] = WORK_ST_IDLE, /* cancel and drain */ | ||||
}; | }; | ||||
struct taskqueue *tq; | struct taskqueue *tq; | ||||
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, | WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, | ||||
"linux_cancel_delayed_work_sync() might sleep"); | "linux_cancel_delayed_work_sync() might sleep"); | ||||
switch (linux_update_state(&dwork->work.state, states)) { | switch (linux_update_state(&dwork->work.state, states)) { | ||||
case WORK_ST_IDLE: | case WORK_ST_IDLE: | ||||
return (0); | return (0); | ||||
case WORK_ST_EXEC: | |||||
tq = dwork->work.work_queue->taskqueue; | |||||
if (taskqueue_cancel(tq, &dwork->work.work_task, NULL) != 0) | |||||
taskqueue_drain(tq, &dwork->work.work_task); | |||||
return (0); | |||||
case WORK_ST_TIMER: | case WORK_ST_TIMER: | ||||
case WORK_ST_CANCEL: | |||||
if (linux_cancel_timer(dwork, 1)) { | if (linux_cancel_timer(dwork, 1)) { | ||||
/* | /* | ||||
* Make sure taskqueue is also drained before | * Make sure taskqueue is also drained before | ||||
* returning: | * returning: | ||||
*/ | */ | ||||
tq = dwork->work.work_queue->taskqueue; | tq = dwork->work.work_queue->taskqueue; | ||||
taskqueue_drain(tq, &dwork->work.work_task); | taskqueue_drain(tq, &dwork->work.work_task); | ||||
return (1); | return (1); | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | |||||
* yet executing: | * yet executing: | ||||
*/ | */ | ||||
bool | bool | ||||
linux_work_pending(struct work_struct *work) | linux_work_pending(struct work_struct *work) | ||||
{ | { | ||||
switch (atomic_read(&work->state)) { | switch (atomic_read(&work->state)) { | ||||
case WORK_ST_TIMER: | case WORK_ST_TIMER: | ||||
case WORK_ST_TASK: | case WORK_ST_TASK: | ||||
case WORK_ST_CANCEL: | |||||
return (1); | return (1); | ||||
default: | default: | ||||
return (0); | return (0); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* This function returns true if the given work is busy. | * This function returns true if the given work is busy. | ||||
*/ | */ | ||||
bool | bool | ||||
linux_work_busy(struct work_struct *work) | linux_work_busy(struct work_struct *work) | ||||
{ | { | ||||
struct taskqueue *tq; | struct taskqueue *tq; | ||||
switch (atomic_read(&work->state)) { | switch (atomic_read(&work->state)) { | ||||
case WORK_ST_IDLE: | case WORK_ST_IDLE: | ||||
return (0); | return (0); | ||||
case WORK_ST_EXEC: | case WORK_ST_EXEC: | ||||
case WORK_ST_CANCEL: | |||||
tq = work->work_queue->taskqueue; | tq = work->work_queue->taskqueue; | ||||
return (taskqueue_poll_is_busy(tq, &work->work_task)); | return (taskqueue_poll_is_busy(tq, &work->work_task)); | ||||
default: | default: | ||||
return (1); | return (1); | ||||
} | } | ||||
} | } | ||||
struct workqueue_struct * | struct workqueue_struct * | ||||
▲ Show 20 Lines • Show All 77 Lines • Show Last 20 Lines |