freebsd_network: Use MTX_SPIN for fast taskqueues instead of a direct spinlock.

This commit is contained in:
Augustin Cavalier
2022-03-09 17:26:23 -05:00
parent 385fce1cde
commit 1b333e9176
+38 -62
View File
@@ -31,7 +31,6 @@ struct taskqueue {
taskqueue_enqueue_fn tq_enqueue; taskqueue_enqueue_fn tq_enqueue;
void *tq_arg; void *tq_arg;
int tq_fast; int tq_fast;
spinlock tq_spinlock;
sem_id tq_sem; sem_id tq_sem;
thread_id *tq_threads; thread_id *tq_threads;
thread_id tq_thread_storage; thread_id tq_thread_storage;
@@ -56,7 +55,7 @@ _taskqueue_create(const char *name, int mflags, int fast,
tq->tq_fast = fast; tq->tq_fast = fast;
if (fast) { if (fast) {
B_INITIALIZE_SPINLOCK(&tq->tq_spinlock); mtx_init(&tq->tq_mutex, name, NULL, MTX_SPIN);
} else { } else {
mtx_init(&tq->tq_mutex, name, NULL, MTX_DEF); mtx_init(&tq->tq_mutex, name, NULL, MTX_DEF);
} }
@@ -77,11 +76,10 @@ _taskqueue_create(const char *name, int mflags, int fast,
static void static void
tq_lock(struct taskqueue *taskQueue, cpu_status *status) tq_lock(struct taskqueue *taskQueue)
{ {
if (taskQueue->tq_fast) { if (taskQueue->tq_fast) {
*status = disable_interrupts(); mtx_lock_spin(&taskQueue->tq_mutex);
acquire_spinlock(&taskQueue->tq_spinlock);
} else { } else {
mtx_lock(&taskQueue->tq_mutex); mtx_lock(&taskQueue->tq_mutex);
} }
@@ -89,11 +87,10 @@ tq_lock(struct taskqueue *taskQueue, cpu_status *status)
static void static void
tq_unlock(struct taskqueue *taskQueue, cpu_status status) tq_unlock(struct taskqueue *taskQueue)
{ {
if (taskQueue->tq_fast) { if (taskQueue->tq_fast) {
release_spinlock(&taskQueue->tq_spinlock); mtx_unlock_spin(&taskQueue->tq_mutex);
restore_interrupts(status);
} else { } else {
mtx_unlock(&taskQueue->tq_mutex); mtx_unlock(&taskQueue->tq_mutex);
} }
@@ -112,20 +109,19 @@ static int32
tq_handle_thread(void *data) tq_handle_thread(void *data)
{ {
struct taskqueue *tq = data; struct taskqueue *tq = data;
cpu_status cpu_state;
struct task *t; struct task *t;
int pending; int pending;
sem_id sem; sem_id sem;
/* just a synchronization point */ /* just a synchronization point */
tq_lock(tq, &cpu_state); tq_lock(tq);
sem = tq->tq_sem; sem = tq->tq_sem;
tq_unlock(tq, cpu_state); tq_unlock(tq);
while (acquire_sem(sem) == B_NO_ERROR) { while (acquire_sem(sem) == B_NO_ERROR) {
tq_lock(tq, &cpu_state); tq_lock(tq);
t = list_remove_head_item(&tq->tq_list); t = list_remove_head_item(&tq->tq_list);
tq_unlock(tq, cpu_state); tq_unlock(tq);
if (t == NULL) if (t == NULL)
continue; continue;
pending = t->ta_pending; pending = t->ta_pending;
@@ -200,7 +196,6 @@ taskqueue_start_threads(struct taskqueue **taskQueue, int count, int priority,
* the TQ is 'fast', locking the TQ disables interrupts... and then * the TQ is 'fast', locking the TQ disables interrupts... and then
* we can't create semaphores, threads and bananas. */ * we can't create semaphores, threads and bananas. */
/* cpu_status state; */
char name[64]; char name[64];
int result; int result;
va_list vl; va_list vl;
@@ -209,9 +204,9 @@ taskqueue_start_threads(struct taskqueue **taskQueue, int count, int priority,
vsnprintf(name, sizeof(name), format, vl); vsnprintf(name, sizeof(name), format, vl);
va_end(vl); va_end(vl);
/*tq_lock(*tqp, &state);*/ /*tq_lock(*tqp);*/
result = _taskqueue_start_threads(taskQueue, count, priority, name); result = _taskqueue_start_threads(taskQueue, count, priority, name);
/*tq_unlock(*tqp, state);*/ /*tq_unlock(*tqp);*/
return result; return result;
} }
@@ -220,10 +215,8 @@ taskqueue_start_threads(struct taskqueue **taskQueue, int count, int priority,
void void
taskqueue_free(struct taskqueue *taskQueue) taskqueue_free(struct taskqueue *taskQueue)
{ {
if (taskQueue == NULL) { if (taskQueue == NULL)
printf("taskqueue_free called with NULL taskqueue\n");
return; return;
}
/* lock and drain list? */ /* lock and drain list? */
taskQueue->tq_flags &= ~TQ_FLAGS_ACTIVE; taskQueue->tq_flags &= ~TQ_FLAGS_ACTIVE;
@@ -250,20 +243,16 @@ taskqueue_free(struct taskqueue *taskQueue)
void void
taskqueue_drain(struct taskqueue *taskQueue, struct task *task) taskqueue_drain(struct taskqueue *taskQueue, struct task *task)
{ {
cpu_status status; if (taskQueue == NULL)
if (taskQueue == NULL) {
printf("taskqueue_drain called with NULL taskqueue\n");
return; return;
}
tq_lock(taskQueue, &status); tq_lock(taskQueue);
while (task->ta_pending != 0) { while (task->ta_pending != 0) {
tq_unlock(taskQueue, status); tq_unlock(taskQueue);
snooze(0); snooze(0);
tq_lock(taskQueue, &status); tq_lock(taskQueue);
} }
tq_unlock(taskQueue, status); tq_unlock(taskQueue);
} }
@@ -271,15 +260,14 @@ void
taskqueue_drain_timeout(struct taskqueue *queue, taskqueue_drain_timeout(struct taskqueue *queue,
struct timeout_task *timeout_task) struct timeout_task *timeout_task)
{ {
cpu_status status;
/* /*
* Set flag to prevent timer from re-starting during drain: * Set flag to prevent timer from re-starting during drain:
*/ */
tq_lock(queue, &status); tq_lock(queue);
KASSERT((timeout_task->f & DT_DRAIN_IN_PROGRESS) == 0, KASSERT((timeout_task->f & DT_DRAIN_IN_PROGRESS) == 0,
("Drain already in progress")); ("Drain already in progress"));
timeout_task->f |= DT_DRAIN_IN_PROGRESS; timeout_task->f |= DT_DRAIN_IN_PROGRESS;
tq_unlock(queue, status); tq_unlock(queue);
callout_drain(&timeout_task->c); callout_drain(&timeout_task->c);
taskqueue_drain(queue, &timeout_task->t); taskqueue_drain(queue, &timeout_task->t);
@@ -287,9 +275,9 @@ taskqueue_drain_timeout(struct taskqueue *queue,
/* /*
* Clear flag to allow timer to re-start: * Clear flag to allow timer to re-start:
*/ */
tq_lock(queue, &status); tq_lock(queue);
timeout_task->f &= ~DT_DRAIN_IN_PROGRESS; timeout_task->f &= ~DT_DRAIN_IN_PROGRESS;
tq_unlock(queue, status); tq_unlock(queue);
} }
@@ -316,8 +304,7 @@ taskqueue_drain_all(struct taskqueue *taskQueue)
static void static void
taskqueue_enqueue_locked(struct taskqueue *taskQueue, struct task *task, taskqueue_enqueue_locked(struct taskqueue *taskQueue, struct task *task)
cpu_status status)
{ {
/* we don't really support priorities */ /* we don't really support priorities */
if (task->ta_pending) { if (task->ta_pending) {
@@ -330,17 +317,15 @@ taskqueue_enqueue_locked(struct taskqueue *taskQueue, struct task *task,
else else
taskQueue->tq_flags |= TQ_FLAGS_PENDING; taskQueue->tq_flags |= TQ_FLAGS_PENDING;
} }
tq_unlock(taskQueue, status); tq_unlock(taskQueue);
} }
int int
taskqueue_enqueue(struct taskqueue *taskQueue, struct task *task) taskqueue_enqueue(struct taskqueue *taskQueue, struct task *task)
{ {
cpu_status status; tq_lock(taskQueue);
taskqueue_enqueue_locked(taskQueue, task);
tq_lock(taskQueue, &status);
taskqueue_enqueue_locked(taskQueue, task, status);
/* The lock is released inside. */ /* The lock is released inside. */
return 0; return 0;
@@ -352,8 +337,6 @@ 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;
@@ -361,7 +344,7 @@ taskqueue_timeout_func(void *arg)
("stray timeout ('%s')", timeout_task->q->tq_name)); ("stray timeout ('%s')", timeout_task->q->tq_name));
timeout_task->f &= ~DT_CALLOUT_ARMED; timeout_task->f &= ~DT_CALLOUT_ARMED;
queue->tq_callouts--; queue->tq_callouts--;
taskqueue_enqueue_locked(timeout_task->q, &timeout_task->t, status); taskqueue_enqueue_locked(timeout_task->q, &timeout_task->t);
/* The lock is released inside. */ /* The lock is released inside. */
} }
@@ -371,19 +354,18 @@ taskqueue_enqueue_timeout(struct taskqueue *queue,
struct timeout_task *ttask, int _ticks) struct timeout_task *ttask, int _ticks)
{ {
int res; int res;
cpu_status status;
tq_lock(queue, &status); tq_lock(queue);
KASSERT(ttask->q == NULL || ttask->q == queue, KASSERT(ttask->q == NULL || ttask->q == queue,
("Migrated queue")); ("Migrated queue"));
ttask->q = queue; ttask->q = queue;
res = ttask->t.ta_pending; res = ttask->t.ta_pending;
if (ttask->f & DT_DRAIN_IN_PROGRESS) { if (ttask->f & DT_DRAIN_IN_PROGRESS) {
/* Do nothing */ /* Do nothing */
tq_unlock(queue, status); tq_unlock(queue);
res = -1; res = -1;
} else if (_ticks == 0) { } else if (_ticks == 0) {
tq_unlock(queue, status); tq_unlock(queue);
taskqueue_enqueue(queue, &ttask->t); taskqueue_enqueue(queue, &ttask->t);
} else { } else {
if ((ttask->f & DT_CALLOUT_ARMED) != 0) { if ((ttask->f & DT_CALLOUT_ARMED) != 0) {
@@ -394,7 +376,7 @@ taskqueue_enqueue_timeout(struct taskqueue *queue,
if (_ticks < 0) if (_ticks < 0)
_ticks = -_ticks; /* Ignore overflow. */ _ticks = -_ticks; /* Ignore overflow. */
} }
tq_unlock(queue, status); tq_unlock(queue);
if (_ticks > 0) { if (_ticks > 0) {
callout_reset(&ttask->c, _ticks, callout_reset(&ttask->c, _ticks,
taskqueue_timeout_func, ttask); taskqueue_timeout_func, ttask);
@@ -421,11 +403,10 @@ int
taskqueue_cancel(struct taskqueue *queue, struct task *task, u_int *pendp) taskqueue_cancel(struct taskqueue *queue, struct task *task, u_int *pendp)
{ {
int error; int error;
cpu_status status;
tq_lock(queue, &status); tq_lock(queue);
error = taskqueue_cancel_locked(queue, task, pendp); error = taskqueue_cancel_locked(queue, task, pendp);
tq_unlock(queue, status); tq_unlock(queue);
return (error); return (error);
} }
@@ -437,16 +418,15 @@ taskqueue_cancel_timeout(struct taskqueue *queue,
{ {
u_int pending, pending1; u_int pending, pending1;
int error; int error;
cpu_status status;
tq_lock(queue, &status); tq_lock(queue);
pending = !!(callout_stop(&timeout_task->c) > 0); pending = !!(callout_stop(&timeout_task->c) > 0);
error = taskqueue_cancel_locked(queue, &timeout_task->t, &pending1); error = taskqueue_cancel_locked(queue, &timeout_task->t, &pending1);
if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) { if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) {
timeout_task->f &= ~DT_CALLOUT_ARMED; timeout_task->f &= ~DT_CALLOUT_ARMED;
queue->tq_callouts--; queue->tq_callouts--;
} }
tq_unlock(queue, status); tq_unlock(queue);
if (pendp != NULL) if (pendp != NULL)
*pendp = pending + pending1; *pendp = pending + pending1;
@@ -579,24 +559,20 @@ uninit_taskqueues()
void void
taskqueue_block(struct taskqueue *taskQueue) taskqueue_block(struct taskqueue *taskQueue)
{ {
cpu_status status; tq_lock(taskQueue);
tq_lock(taskQueue, &status);
taskQueue->tq_flags |= TQ_FLAGS_BLOCKED; taskQueue->tq_flags |= TQ_FLAGS_BLOCKED;
tq_unlock(taskQueue, status); tq_unlock(taskQueue);
} }
void void
taskqueue_unblock(struct taskqueue *taskQueue) taskqueue_unblock(struct taskqueue *taskQueue)
{ {
cpu_status status; tq_lock(taskQueue);
tq_lock(taskQueue, &status);
taskQueue->tq_flags &= ~TQ_FLAGS_BLOCKED; taskQueue->tq_flags &= ~TQ_FLAGS_BLOCKED;
if (taskQueue->tq_flags & TQ_FLAGS_PENDING) { if (taskQueue->tq_flags & TQ_FLAGS_PENDING) {
taskQueue->tq_flags &= ~TQ_FLAGS_PENDING; taskQueue->tq_flags &= ~TQ_FLAGS_PENDING;
taskQueue->tq_enqueue(taskQueue->tq_arg); taskQueue->tq_enqueue(taskQueue->tq_arg);
} }
tq_unlock(taskQueue, status); tq_unlock(taskQueue);
} }