Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions sys/compat/linuxkpi/common/src/linux_work.c
Original file line number Diff line number Diff line change
Expand Up @@ -506,12 +506,16 @@ linux_cancel_delayed_work(struct delayed_work *dwork)
}

/*
* This function cancels the given work structure in a synchronous
* fashion. It returns true if the work was successfully
* cancelled. Else the work was already cancelled.
* This function cancels the given delayed work structure in a
* synchronous fashion. It returns true if pending delayed work was
* cancelled. Else the work was not pending.
*
* If the work restarted itself or was busy while being cancelled,
* retry_needed is set to true so the caller can re-check the state.
*/
static bool
linux_cancel_delayed_work_sync_int(struct delayed_work *dwork)
linux_cancel_delayed_work_sync_int(struct delayed_work *dwork,
bool *retry_needed)
{
static const uint8_t states[WORK_ST_MAX] __aligned(8) = {
[WORK_ST_IDLE] = WORK_ST_IDLE, /* NOP */
Expand All @@ -523,6 +527,9 @@ linux_cancel_delayed_work_sync_int(struct delayed_work *dwork)
struct taskqueue *tq;
int ret, state;
bool cancelled;
u_int pending = 0;

*retry_needed = false;

WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
"linux_cancel_delayed_work_sync() might sleep");
Expand All @@ -538,30 +545,41 @@ linux_cancel_delayed_work_sync_int(struct delayed_work *dwork)
cancelled = (callout_stop(&dwork->timer.callout) == 1);

tq = dwork->work.work_queue->taskqueue;
ret = taskqueue_cancel(tq, &dwork->work.work_task, NULL);
ret = taskqueue_cancel(tq, &dwork->work.work_task, &pending);
mtx_unlock(&dwork->timer.mtx);

callout_drain(&dwork->timer.callout);
taskqueue_drain(tq, &dwork->work.work_task);
return (cancelled || (ret != 0));
if (ret != 0)
*retry_needed = true;
return (cancelled || pending != 0);
default:
tq = dwork->work.work_queue->taskqueue;
ret = taskqueue_cancel(tq, &dwork->work.work_task, NULL);
ret = taskqueue_cancel(tq, &dwork->work.work_task, &pending);
mtx_unlock(&dwork->timer.mtx);
if (ret != 0)
if (ret != 0) {
taskqueue_drain(tq, &dwork->work.work_task);
return (ret != 0);
*retry_needed = true;
}
return (pending != 0);
}
}

bool
linux_cancel_delayed_work_sync(struct delayed_work *dwork)
{
bool res;
bool ret;
bool retry_needed = false;

ret = linux_cancel_delayed_work_sync_int(dwork, &retry_needed);
res = ret;

while (ret || retry_needed) {
ret = linux_cancel_delayed_work_sync_int(dwork, &retry_needed);
res = res || ret;
}

res = false;
while (linux_cancel_delayed_work_sync_int(dwork))
res = true;
return (res);
}

Expand Down
Loading