freebsd11_network: Properly implement CALLOUT_RETURNUNLOCKED.

Fixes a double-lock situation in the Atheros driver. We really should
implement FreeBSD's MTX_SPIN instead of relying on cpu_status here...
This commit is contained in:
Augustin Cavalier 2018-07-11 19:03:27 -04:00
parent 3248de3de4
commit 09ddf9b9f3
2 changed files with 23 additions and 7 deletions

View File

@ -1,6 +1,7 @@
/* /*
* Copyright 2010, Axel Dörfler, axeld@pinc-software.de. * Copyright 2010, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License. * Copyright 2018, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*/ */
@ -66,7 +67,8 @@ callout_thread(void* /*data*/)
c->c_func(c->c_arg); c->c_func(c->c_arg);
if (mutex != NULL) if (mutex != NULL
&& (c->c_flags & CALLOUT_RETURNUNLOCKED) == 0)
mtx_unlock(mutex); mtx_unlock(mutex);
mutex_lock(&sLock); mutex_lock(&sLock);

View File

@ -293,11 +293,10 @@ taskqueue_drain_timeout(struct taskqueue *queue,
} }
int static void
taskqueue_enqueue(struct taskqueue *taskQueue, struct task *task) taskqueue_enqueue_locked(struct taskqueue *taskQueue, struct task *task,
cpu_status status)
{ {
cpu_status status;
tq_lock(taskQueue, &status);
/* we don't really support priorities */ /* we don't really support priorities */
if (task->ta_pending) { if (task->ta_pending) {
task->ta_pending++; task->ta_pending++;
@ -310,6 +309,18 @@ taskqueue_enqueue(struct taskqueue *taskQueue, struct task *task)
taskQueue->tq_flags |= TQ_FLAGS_PENDING; taskQueue->tq_flags |= TQ_FLAGS_PENDING;
} }
tq_unlock(taskQueue, status); tq_unlock(taskQueue, status);
}
int
taskqueue_enqueue(struct taskqueue *taskQueue, struct task *task)
{
cpu_status status;
tq_lock(taskQueue, &status);
taskqueue_enqueue_locked(taskQueue, task, status);
/* The lock is released inside. */
return 0; return 0;
} }
@ -319,13 +330,16 @@ taskqueue_timeout_func(void *arg)
{ {
struct taskqueue *queue; struct taskqueue *queue;
struct timeout_task *timeout_task; struct timeout_task *timeout_task;
cpu_status status;
// dummy, as we should never get here on a spin taskqueue
timeout_task = arg; timeout_task = arg;
queue = timeout_task->q; queue = timeout_task->q;
KASSERT((timeout_task->f & DT_CALLOUT_ARMED) != 0, ("Stray timeout")); KASSERT((timeout_task->f & DT_CALLOUT_ARMED) != 0, ("Stray timeout"));
timeout_task->f &= ~DT_CALLOUT_ARMED; timeout_task->f &= ~DT_CALLOUT_ARMED;
queue->tq_callouts--; queue->tq_callouts--;
taskqueue_enqueue(timeout_task->q, &timeout_task->t); taskqueue_enqueue_locked(timeout_task->q, &timeout_task->t, status);
/* The lock is released inside. */
} }