From 09ddf9b9f3f5e310afa87522054f7fe1d2885ef4 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Wed, 11 Jul 2018 19:03:27 -0400 Subject: [PATCH] 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... --- src/libs/compat/freebsd11_network/callout.cpp | 6 +++-- src/libs/compat/freebsd11_network/taskqueue.c | 24 +++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/libs/compat/freebsd11_network/callout.cpp b/src/libs/compat/freebsd11_network/callout.cpp index 00f68d3180..7f2315ca3e 100644 --- a/src/libs/compat/freebsd11_network/callout.cpp +++ b/src/libs/compat/freebsd11_network/callout.cpp @@ -1,6 +1,7 @@ /* * 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); - if (mutex != NULL) + if (mutex != NULL + && (c->c_flags & CALLOUT_RETURNUNLOCKED) == 0) mtx_unlock(mutex); mutex_lock(&sLock); diff --git a/src/libs/compat/freebsd11_network/taskqueue.c b/src/libs/compat/freebsd11_network/taskqueue.c index 67a45d3281..cb8fac155b 100644 --- a/src/libs/compat/freebsd11_network/taskqueue.c +++ b/src/libs/compat/freebsd11_network/taskqueue.c @@ -293,11 +293,10 @@ taskqueue_drain_timeout(struct taskqueue *queue, } -int -taskqueue_enqueue(struct taskqueue *taskQueue, struct task *task) +static void +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 */ if (task->ta_pending) { task->ta_pending++; @@ -310,6 +309,18 @@ taskqueue_enqueue(struct taskqueue *taskQueue, struct task *task) taskQueue->tq_flags |= TQ_FLAGS_PENDING; } 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; } @@ -319,13 +330,16 @@ taskqueue_timeout_func(void *arg) { struct taskqueue *queue; struct timeout_task *timeout_task; + cpu_status status; + // dummy, as we should never get here on a spin taskqueue timeout_task = arg; queue = timeout_task->q; KASSERT((timeout_task->f & DT_CALLOUT_ARMED) != 0, ("Stray timeout")); timeout_task->f &= ~DT_CALLOUT_ARMED; 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. */ }