diff --git a/src/libs/compat/freebsd_iflib/compat/sys/gtaskqueue.h b/src/libs/compat/freebsd_iflib/compat/sys/gtaskqueue.h index 7ef73d450b..9db06ebf9d 100644 --- a/src/libs/compat/freebsd_iflib/compat/sys/gtaskqueue.h +++ b/src/libs/compat/freebsd_iflib/compat/sys/gtaskqueue.h @@ -85,10 +85,6 @@ void taskqgroup_config_gtask_init(void *ctx, struct grouptask *gtask, gtask_fn_t *fn, const char *name); void taskqgroup_config_gtask_deinit(struct grouptask *gtask); -#define TASK_ENQUEUED 0x1 -#define TASK_SKIP_WAKEUP 0x2 -#define TASK_NOENQUEUE 0x4 - #define GTASK_INIT(gtask, flags, priority, func, context) do { \ (gtask)->ta_flags = flags; \ (gtask)->ta_priority = (priority); \ diff --git a/src/libs/compat/freebsd_iflib/compat/sys/taskqueue.h b/src/libs/compat/freebsd_iflib/compat/sys/taskqueue.h deleted file mode 100644 index 728e88e152..0000000000 --- a/src/libs/compat/freebsd_iflib/compat/sys/taskqueue.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2018, Haiku Inc. All rights reserved. - * Distributed under the terms of the MIT License. - */ -#ifndef _FBSD_IFLIB_SYS_TASKQUEUE_H_ -#define _FBSD_IFLIB_SYS_TASKQUEUE_H_ - -/* include the real sys/taskqueue.h */ -#include_next - -typedef void (*taskqueue_callback_fn)(void *context); - - -enum taskqueue_callback_type { - TASKQUEUE_CALLBACK_TYPE_INIT, - TASKQUEUE_CALLBACK_TYPE_SHUTDOWN, -}; -#define TASKQUEUE_CALLBACK_TYPE_MIN TASKQUEUE_CALLBACK_TYPE_INIT -#define TASKQUEUE_CALLBACK_TYPE_MAX TASKQUEUE_CALLBACK_TYPE_SHUTDOWN -#define TASKQUEUE_NUM_CALLBACKS TASKQUEUE_CALLBACK_TYPE_MAX + 1 - - -#endif /* _FBSD_IFLIB_SYS_TASKQUEUE_H_ */ diff --git a/src/libs/compat/freebsd_network/compat/sys/_task.h b/src/libs/compat/freebsd_network/compat/sys/_task.h index d50ee8a88f..da8e7724d9 100644 --- a/src/libs/compat/freebsd_network/compat/sys/_task.h +++ b/src/libs/compat/freebsd_network/compat/sys/_task.h @@ -1,29 +1,63 @@ -/* - * Copyright 2007 Haiku Inc. All rights reserved. - * Distributed under the terms of the MIT License. +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ */ -#ifndef _FBSD_COMPAT_SYS__TASK_H_ -#define _FBSD_COMPAT_SYS__TASK_H_ +#ifndef _SYS__TASK_H_ +#define _SYS__TASK_H_ -/* Haiku's list management */ -#include +#include - -typedef void (*task_fn_t)(void *context, int pending); +/* + * Each task includes a function which is called from + * taskqueue_run(). The first argument is taken from the 'ta_context' + * field of struct task and the second argument is a count of how many + * times the task was enqueued before the call to taskqueue_run(). + * + * List of locks + * (c) const after init + * (q) taskqueue lock + */ +typedef void task_fn_t(void *context, int pending); struct task { - int ta_pending; - int ta_priority; - int ta_flags; - task_fn_t ta_handler; - void *ta_argument; - - struct list_link ta_link; + STAILQ_ENTRY(task) ta_link; /* (q) link for queue */ + uint16_t ta_pending; /* (q) count times queued */ + uint8_t ta_priority; /* (c) Priority */ + uint8_t ta_flags; /* (c) Flags */ + task_fn_t *ta_func; /* (c) task handler */ + void *ta_context; /* (c) argument for handler */ }; +#define TASK_ENQUEUED (1<<0) +#define TASK_NOENQUEUE (1<<1) +#define TASK_SKIP_WAKEUP (1<<2) -#define TASK_NEEDSGIANT (1 << 0) /* Haiku extension, OpenBSD compatibility */ +#define TASK_NEEDSGIANT (1<<3) /* Haiku extension, OpenBSD compatibility */ - -#endif +#endif /* !_SYS__TASK_H_ */ diff --git a/src/libs/compat/freebsd_network/compat/sys/taskqueue.h b/src/libs/compat/freebsd_network/compat/sys/taskqueue.h index d9adb6ad87..f1009051d0 100644 --- a/src/libs/compat/freebsd_network/compat/sys/taskqueue.h +++ b/src/libs/compat/freebsd_network/compat/sys/taskqueue.h @@ -1,15 +1,45 @@ -/* - * Copyright 2007-2018, Haiku, Inc. All rights reserved. - * Distributed under the terms of the MIT License. +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ */ -#ifndef _FBSD_COMPAT_SYS_TASKQUEUE_H_ -#define _FBSD_COMPAT_SYS_TASKQUEUE_H_ +#ifndef _SYS_TASKQUEUE_H_ +#define _SYS_TASKQUEUE_H_ + +#ifndef _KERNEL +#error "no user-servicable parts inside" +#endif #include #include -#include +#include +struct taskqueue; +struct taskqgroup; +struct thread; struct timeout_task { struct taskqueue *q; @@ -18,48 +48,148 @@ struct timeout_task { int f; }; -#define TASKQUEUE_NAMELEN 64 +enum taskqueue_callback_type { + TASKQUEUE_CALLBACK_TYPE_INIT, + TASKQUEUE_CALLBACK_TYPE_SHUTDOWN, +}; +#define TASKQUEUE_CALLBACK_TYPE_MIN TASKQUEUE_CALLBACK_TYPE_INIT +#define TASKQUEUE_CALLBACK_TYPE_MAX TASKQUEUE_CALLBACK_TYPE_SHUTDOWN +#define TASKQUEUE_NUM_CALLBACKS TASKQUEUE_CALLBACK_TYPE_MAX + 1 +#define TASKQUEUE_NAMELEN 32 +typedef void (*taskqueue_callback_fn)(void *context); + +/* + * A notification callback function which is called from + * taskqueue_enqueue(). The context argument is given in the call to + * taskqueue_create(). This function would normally be used to allow the + * queue to arrange to run itself later (e.g., by scheduling a software + * interrupt or waking a kernel thread). + */ typedef void (*taskqueue_enqueue_fn)(void *context); - -struct taskqueue; struct taskqueue *taskqueue_create(const char *name, int mflags, - taskqueue_enqueue_fn enqueue, void *context); -int taskqueue_start_threads(struct taskqueue **tq, int count, int pri, - const char *name, ...) __printflike(4, 5); -void taskqueue_free(struct taskqueue *tq); -void taskqueue_drain(struct taskqueue *tq, struct task *task); -void taskqueue_drain_timeout(struct taskqueue *queue, - struct timeout_task *timeout_task); -void taskqueue_drain_all(struct taskqueue *tq); -void taskqueue_block(struct taskqueue *queue); -void taskqueue_unblock(struct taskqueue *queue); -int taskqueue_enqueue(struct taskqueue *tq, struct task *task); -int taskqueue_enqueue_timeout(struct taskqueue *queue, - struct timeout_task *ttask, int _ticks); -int taskqueue_cancel(struct taskqueue *queue, struct task *task, - u_int *pendp); -int taskqueue_cancel_timeout(struct taskqueue *queue, - struct timeout_task *timeout_task, u_int *pendp); + taskqueue_enqueue_fn enqueue, + void *context); +int taskqueue_start_threads(struct taskqueue **tqp, int count, int pri, + const char *name, ...) __printflike(4, 5); +int taskqueue_enqueue(struct taskqueue *queue, struct task *task); +int taskqueue_enqueue_timeout(struct taskqueue *queue, + struct timeout_task *timeout_task, int _ticks); +int taskqueue_cancel(struct taskqueue *queue, struct task *task, + u_int *pendp); +int taskqueue_cancel_timeout(struct taskqueue *queue, + struct timeout_task *timeout_task, u_int *pendp); +void taskqueue_drain(struct taskqueue *queue, struct task *task); +void taskqueue_drain_timeout(struct taskqueue *queue, + struct timeout_task *timeout_task); +void taskqueue_drain_all(struct taskqueue *queue); +void taskqueue_free(struct taskqueue *queue); +void taskqueue_run(struct taskqueue *queue); +void taskqueue_block(struct taskqueue *queue); +void taskqueue_unblock(struct taskqueue *queue); +int taskqueue_member(struct taskqueue *queue, struct thread *td); +void taskqueue_set_callback(struct taskqueue *queue, + enum taskqueue_callback_type cb_type, + taskqueue_callback_fn callback, void *context); -void taskqueue_thread_enqueue(void *context); +/* + * Functions for dedicated thread taskqueues + */ +void taskqueue_thread_loop(void *arg); +void taskqueue_thread_enqueue(void *context); -extern struct taskqueue *taskqueue_fast; -extern struct taskqueue *taskqueue_swi; -extern struct taskqueue *taskqueue_thread; - -int taskqueue_enqueue_fast(struct taskqueue *queue, struct task *task); -struct taskqueue *taskqueue_create_fast(const char *name, int mflags, - taskqueue_enqueue_fn enqueue, void *context); - -void task_init(struct task *, int prio, task_fn_t handler, void *arg); -#define TASK_INIT(taskp, prio, hand, arg) task_init(taskp, prio, hand, arg) +/* + * Initialise a task structure. + */ +void _task_init(struct task *, int prio, task_fn_t handler, void *arg); +#define TASK_INIT(taskp, prio, hand, arg) _task_init(taskp, prio, hand, arg) #define NET_TASK_INIT TASK_INIT -void timeout_task_init(struct taskqueue *queue, struct timeout_task *timeout_task, - int priority, task_fn_t func, void *context); +void _timeout_task_init(struct taskqueue *queue, + struct timeout_task *timeout_task, int priority, task_fn_t func, + void *context); #define TIMEOUT_TASK_INIT(queue, timeout_task, priority, func, context) \ - timeout_task_init(queue, timeout_task, priority, func, context); + _timeout_task_init(queue, timeout_task, priority, func, context); -#endif +/* + * Declare a reference to a taskqueue. + */ +#define TASKQUEUE_DECLARE(name) \ +extern struct taskqueue *taskqueue_##name + +/* + * Define and initialise a global taskqueue that uses sleep mutexes. + */ +#define TASKQUEUE_DEFINE(name, enqueue, context, init) \ + \ +struct taskqueue *taskqueue_##name; \ + \ +static void \ +taskqueue_define_##name(void *arg) \ +{ \ + taskqueue_##name = \ + taskqueue_create(#name, M_WAITOK, (enqueue), (context)); \ + init; \ +} \ + \ +SYSINIT(taskqueue_##name, SI_SUB_INIT_IF, SI_ORDER_SECOND, \ + taskqueue_define_##name, NULL); \ + \ +struct __hack +#define TASKQUEUE_DEFINE_THREAD(name) \ +TASKQUEUE_DEFINE(name, taskqueue_thread_enqueue, &taskqueue_##name, \ + taskqueue_start_threads(&taskqueue_##name, 1, PWAIT, \ + "%s taskq", #name)) + +/* + * Define and initialise a global taskqueue that uses spin mutexes. + */ +#define TASKQUEUE_FAST_DEFINE(name, enqueue, context, init) \ + \ +struct taskqueue *taskqueue_##name; \ + \ +static void \ +taskqueue_define_##name(void *arg) \ +{ \ + taskqueue_##name = \ + taskqueue_create_fast(#name, M_WAITOK, (enqueue), \ + (context)); \ + init; \ +} \ + \ +SYSINIT(taskqueue_##name, SI_SUB_INIT_IF, SI_ORDER_SECOND, \ + taskqueue_define_##name, NULL); \ + \ +struct __hack +#define TASKQUEUE_FAST_DEFINE_THREAD(name) \ +TASKQUEUE_FAST_DEFINE(name, taskqueue_thread_enqueue, \ + &taskqueue_##name, taskqueue_start_threads(&taskqueue_##name \ + 1, PWAIT, "%s taskq", #name)) + +/* + * These queues are serviced by software interrupt handlers. To enqueue + * a task, call taskqueue_enqueue(taskqueue_swi, &task) or + * taskqueue_enqueue(taskqueue_swi_giant, &task). + */ +TASKQUEUE_DECLARE(swi_giant); +TASKQUEUE_DECLARE(swi); + +/* + * This queue is serviced by a kernel thread. To enqueue a task, call + * taskqueue_enqueue(taskqueue_thread, &task). + */ +TASKQUEUE_DECLARE(thread); + +/* + * Queue for swi handlers dispatched from fast interrupt handlers. + * These are necessarily different from the above because the queue + * must be locked with spinlocks since sleep mutex's cannot be used + * from a fast interrupt handler context. + */ +TASKQUEUE_DECLARE(fast); +struct taskqueue *taskqueue_create_fast(const char *name, int mflags, + taskqueue_enqueue_fn enqueue, + void *context); + +#endif /* !_SYS_TASKQUEUE_H_ */ diff --git a/src/libs/compat/freebsd_network/fbsd_subr_taskqueue.c b/src/libs/compat/freebsd_network/fbsd_subr_taskqueue.c new file mode 100644 index 0000000000..2f237a3c64 --- /dev/null +++ b/src/libs/compat/freebsd_network/fbsd_subr_taskqueue.c @@ -0,0 +1,519 @@ +/*- + * Copyright (c) 2000 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static MALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues"); +static void *taskqueue_giant_ih; +static void *taskqueue_ih; + +struct taskqueue_busy { + struct task *tb_running; + TAILQ_ENTRY(taskqueue_busy) tb_link; +}; + +struct task * const TB_DRAIN_WAITER = (struct task *)0x1; + +struct taskqueue { + STAILQ_HEAD(, task) tq_queue; + taskqueue_enqueue_fn tq_enqueue; + void *tq_context; + char *tq_name; + TAILQ_HEAD(, taskqueue_busy) tq_active; + struct mtx tq_mutex; +#ifdef __HAIKU__ + sem_id tq_sem; + thread_id *tq_threads; + thread_id tq_thread_storage; + int tq_threadcount; +#else + struct thread **tq_threads; +#endif + int tq_tcount; + int tq_spin; + int tq_flags; + int tq_callouts; + taskqueue_callback_fn tq_callbacks[TASKQUEUE_NUM_CALLBACKS]; + void *tq_cb_contexts[TASKQUEUE_NUM_CALLBACKS]; +}; + +#define TQ_FLAGS_ACTIVE (1 << 0) +#define TQ_FLAGS_BLOCKED (1 << 1) +#define TQ_FLAGS_UNLOCKED_ENQUEUE (1 << 2) + +#define DT_CALLOUT_ARMED (1 << 0) +#define DT_DRAIN_IN_PROGRESS (1 << 1) + +#define TQ_LOCK(tq) \ + do { \ + if ((tq)->tq_spin) \ + mtx_lock_spin(&(tq)->tq_mutex); \ + else \ + mtx_lock(&(tq)->tq_mutex); \ + } while (0) +#define TQ_ASSERT_LOCKED(tq) mtx_assert(&(tq)->tq_mutex, MA_OWNED) + +#define TQ_UNLOCK(tq) \ + do { \ + if ((tq)->tq_spin) \ + mtx_unlock_spin(&(tq)->tq_mutex); \ + else \ + mtx_unlock(&(tq)->tq_mutex); \ + } while (0) +#define TQ_ASSERT_UNLOCKED(tq) mtx_assert(&(tq)->tq_mutex, MA_NOTOWNED) + +void +_timeout_task_init(struct taskqueue *queue, struct timeout_task *timeout_task, + int priority, task_fn_t func, void *context) +{ + + TASK_INIT(&timeout_task->t, priority, func, context); + callout_init_mtx(&timeout_task->c, &queue->tq_mutex, + CALLOUT_RETURNUNLOCKED); + timeout_task->q = queue; + timeout_task->f = 0; +} + +static struct taskqueue * +_taskqueue_create(const char *name, int mflags, + taskqueue_enqueue_fn enqueue, void *context, + int mtxflags, const char *mtxname __unused) +{ + struct taskqueue *queue; + char *tq_name; + + tq_name = malloc(TASKQUEUE_NAMELEN, M_TASKQUEUE, mflags | M_ZERO); + if (tq_name == NULL) + return (NULL); + + queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags | M_ZERO); + if (queue == NULL) { + free(tq_name, M_TASKQUEUE); + return (NULL); + } + + snprintf(tq_name, TASKQUEUE_NAMELEN, "%s", (name) ? name : "taskqueue"); + + STAILQ_INIT(&queue->tq_queue); + TAILQ_INIT(&queue->tq_active); + queue->tq_enqueue = enqueue; + queue->tq_context = context; + queue->tq_name = tq_name; + queue->tq_spin = (mtxflags & MTX_SPIN) != 0; + queue->tq_flags |= TQ_FLAGS_ACTIVE; + if (enqueue == taskqueue_thread_enqueue) + queue->tq_flags |= TQ_FLAGS_UNLOCKED_ENQUEUE; + mtx_init(&queue->tq_mutex, tq_name, NULL, mtxflags); + + return (queue); +} + +struct taskqueue * +taskqueue_create(const char *name, int mflags, + taskqueue_enqueue_fn enqueue, void *context) +{ + + return _taskqueue_create(name, mflags, enqueue, context, + MTX_DEF, name); +} + +void +taskqueue_set_callback(struct taskqueue *queue, + enum taskqueue_callback_type cb_type, taskqueue_callback_fn callback, + void *context) +{ + + KASSERT(((cb_type >= TASKQUEUE_CALLBACK_TYPE_MIN) && + (cb_type <= TASKQUEUE_CALLBACK_TYPE_MAX)), + ("Callback type %d not valid, must be %d-%d", cb_type, + TASKQUEUE_CALLBACK_TYPE_MIN, TASKQUEUE_CALLBACK_TYPE_MAX)); + KASSERT((queue->tq_callbacks[cb_type] == NULL), + ("Re-initialization of taskqueue callback?")); + + queue->tq_callbacks[cb_type] = callback; + queue->tq_cb_contexts[cb_type] = context; +} + +void +taskqueue_free(struct taskqueue *queue) +{ + + TQ_LOCK(queue); + queue->tq_flags &= ~TQ_FLAGS_ACTIVE; + taskqueue_terminate(queue->tq_threads, queue); + KASSERT(TAILQ_EMPTY(&queue->tq_active), ("Tasks still running?")); + KASSERT(queue->tq_callouts == 0, ("Armed timeout tasks")); + mtx_destroy(&queue->tq_mutex); + free(queue->tq_threads, M_TASKQUEUE); + free(queue->tq_name, M_TASKQUEUE); + free(queue, M_TASKQUEUE); +} + +static int +taskqueue_enqueue_locked(struct taskqueue *queue, struct task *task) +{ + struct task *ins; + struct task *prev; + + KASSERT(task->ta_func != NULL, ("enqueueing task with NULL func")); + /* + * Count multiple enqueues. + */ + if (task->ta_pending) { + if (task->ta_pending < USHRT_MAX) + task->ta_pending++; + TQ_UNLOCK(queue); + return (0); + } + + /* + * Optimise the case when all tasks have the same priority. + */ + prev = STAILQ_LAST(&queue->tq_queue, task, ta_link); + if (!prev || prev->ta_priority >= task->ta_priority) { + STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link); + } else { + prev = NULL; + for (ins = STAILQ_FIRST(&queue->tq_queue); ins; + prev = ins, ins = STAILQ_NEXT(ins, ta_link)) + if (ins->ta_priority < task->ta_priority) + break; + + if (prev) + STAILQ_INSERT_AFTER(&queue->tq_queue, prev, task, ta_link); + else + STAILQ_INSERT_HEAD(&queue->tq_queue, task, ta_link); + } + + task->ta_pending = 1; + if ((queue->tq_flags & TQ_FLAGS_UNLOCKED_ENQUEUE) != 0) + TQ_UNLOCK(queue); + if ((queue->tq_flags & TQ_FLAGS_BLOCKED) == 0) + queue->tq_enqueue(queue->tq_context); + if ((queue->tq_flags & TQ_FLAGS_UNLOCKED_ENQUEUE) == 0) + TQ_UNLOCK(queue); + + /* Return with lock released. */ + return (0); +} + +int +taskqueue_enqueue(struct taskqueue *queue, struct task *task) +{ + int res; + + TQ_LOCK(queue); + res = taskqueue_enqueue_locked(queue, task); + /* The lock is released inside. */ + + return (res); +} + +static void +taskqueue_timeout_func(void *arg) +{ + struct taskqueue *queue; + struct timeout_task *timeout_task; + + 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_locked(timeout_task->q, &timeout_task->t); + /* The lock is released inside. */ +} + +int +taskqueue_enqueue_timeout(struct taskqueue *queue, + struct timeout_task *timeout_task, int _ticks) +{ + int res; + + TQ_LOCK(queue); + KASSERT(timeout_task->q == NULL || timeout_task->q == queue, + ("Migrated queue")); + KASSERT(!queue->tq_spin, ("Timeout for spin-queue")); + timeout_task->q = queue; + res = timeout_task->t.ta_pending; + if (timeout_task->f & DT_DRAIN_IN_PROGRESS) { + /* Do nothing */ + TQ_UNLOCK(queue); + res = -1; + } else if (_ticks == 0) { + taskqueue_enqueue_locked(queue, &timeout_task->t); + /* The lock is released inside. */ + } else { + if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) { + res++; + } else { + queue->tq_callouts++; + timeout_task->f |= DT_CALLOUT_ARMED; + if (_ticks < 0) + _ticks = -_ticks; /* Ignore overflow. */ + } + if (_ticks > 0) { + callout_reset(&timeout_task->c, _ticks, + taskqueue_timeout_func, timeout_task); + } + TQ_UNLOCK(queue); + } + return (res); +} + +static void +taskqueue_task_nop_fn(void *context, int pending) +{ +} + +void +taskqueue_block(struct taskqueue *queue) +{ + + TQ_LOCK(queue); + queue->tq_flags |= TQ_FLAGS_BLOCKED; + TQ_UNLOCK(queue); +} + +void +taskqueue_unblock(struct taskqueue *queue) +{ + + TQ_LOCK(queue); + queue->tq_flags &= ~TQ_FLAGS_BLOCKED; + if (!STAILQ_EMPTY(&queue->tq_queue)) + queue->tq_enqueue(queue->tq_context); + TQ_UNLOCK(queue); +} + +static void +taskqueue_run_locked(struct taskqueue *queue) +{ + struct taskqueue_busy tb; + struct taskqueue_busy *tb_first; + struct task *task; + int pending; + + KASSERT(queue != NULL, ("tq is NULL")); + TQ_ASSERT_LOCKED(queue); + tb.tb_running = NULL; + + while (STAILQ_FIRST(&queue->tq_queue)) { + TAILQ_INSERT_TAIL(&queue->tq_active, &tb, tb_link); + + /* + * Carefully remove the first task from the queue and + * zero its pending count. + */ + task = STAILQ_FIRST(&queue->tq_queue); + KASSERT(task != NULL, ("task is NULL")); + STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link); + pending = task->ta_pending; + task->ta_pending = 0; + tb.tb_running = task; + TQ_UNLOCK(queue); + + KASSERT(task->ta_func != NULL, ("task->ta_func is NULL")); +#ifdef __HAIKU__ + if ((task->ta_flags & TASK_NEEDSGIANT) != 0) + mtx_lock(&Giant); +#endif + task->ta_func(task->ta_context, pending); +#ifdef __HAIKU__ + if ((task->ta_flags & TASK_NEEDSGIANT) != 0) + mtx_unlock(&Giant); +#endif + + TQ_LOCK(queue); + tb.tb_running = NULL; + + TAILQ_REMOVE(&queue->tq_active, &tb, tb_link); + tb_first = TAILQ_FIRST(&queue->tq_active); + } +} + +void +taskqueue_run(struct taskqueue *queue) +{ + + TQ_LOCK(queue); + taskqueue_run_locked(queue); + TQ_UNLOCK(queue); +} + +static int +task_is_running(struct taskqueue *queue, struct task *task) +{ + struct taskqueue_busy *tb; + + TQ_ASSERT_LOCKED(queue); + TAILQ_FOREACH(tb, &queue->tq_active, tb_link) { + if (tb->tb_running == task) + return (1); + } + return (0); +} + +static int +taskqueue_cancel_locked(struct taskqueue *queue, struct task *task, + u_int *pendp) +{ + + if (task->ta_pending > 0) + STAILQ_REMOVE(&queue->tq_queue, task, task, ta_link); + if (pendp != NULL) + *pendp = task->ta_pending; + task->ta_pending = 0; + return (task_is_running(queue, task) ? EBUSY : 0); +} + +int +taskqueue_cancel(struct taskqueue *queue, struct task *task, u_int *pendp) +{ + int error; + + TQ_LOCK(queue); + error = taskqueue_cancel_locked(queue, task, pendp); + TQ_UNLOCK(queue); + + return (error); +} + +int +taskqueue_cancel_timeout(struct taskqueue *queue, + struct timeout_task *timeout_task, u_int *pendp) +{ + u_int pending, pending1; + int error; + + TQ_LOCK(queue); + pending = !!(callout_stop(&timeout_task->c) > 0); + error = taskqueue_cancel_locked(queue, &timeout_task->t, &pending1); + if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) { + timeout_task->f &= ~DT_CALLOUT_ARMED; + queue->tq_callouts--; + } + TQ_UNLOCK(queue); + + if (pendp != NULL) + *pendp = pending + pending1; + return (error); +} + +void +taskqueue_drain_timeout(struct taskqueue *queue, + struct timeout_task *timeout_task) +{ + + /* + * Set flag to prevent timer from re-starting during drain: + */ + TQ_LOCK(queue); + KASSERT((timeout_task->f & DT_DRAIN_IN_PROGRESS) == 0, + ("Drain already in progress")); + timeout_task->f |= DT_DRAIN_IN_PROGRESS; + TQ_UNLOCK(queue); + + callout_drain(&timeout_task->c); + taskqueue_drain(queue, &timeout_task->t); + + /* + * Clear flag to allow timer to re-start: + */ + TQ_LOCK(queue); + timeout_task->f &= ~DT_DRAIN_IN_PROGRESS; + TQ_UNLOCK(queue); +} + +int +taskqueue_start_threads(struct taskqueue **tqp, int count, int pri, + const char *format, ...) +{ + char name[64]; + int error; + va_list vl; + + va_start(vl, format); + vsnprintf(name, sizeof(name), format, vl); + va_end(vl); + + error = _taskqueue_start_threads(tqp, count, pri, name); + return (error); +} + +static inline void +taskqueue_run_callback(struct taskqueue *tq, + enum taskqueue_callback_type cb_type) +{ + taskqueue_callback_fn tq_callback; + + TQ_ASSERT_UNLOCKED(tq); + tq_callback = tq->tq_callbacks[cb_type]; + if (tq_callback != NULL) + tq_callback(tq->tq_cb_contexts[cb_type]); +} + +int +taskqueue_member(struct taskqueue *queue, struct thread *td) +{ + int i, j, ret = 0; + + for (i = 0, j = 0; ; i++) { + if (queue->tq_threads[i] == NULL) + continue; + if (queue->tq_threads[i] == td) { + ret = 1; + break; + } + if (++j >= queue->tq_tcount) + break; + } + return (ret); +} + +struct taskqueue * +taskqueue_create_fast(const char *name, int mflags, + taskqueue_enqueue_fn enqueue, void *context) +{ + return _taskqueue_create(name, mflags, enqueue, context, + MTX_SPIN, "fast_taskqueue"); +} diff --git a/src/libs/compat/freebsd_network/taskqueue.c b/src/libs/compat/freebsd_network/taskqueue.c index 7ed80a2b78..50163c576f 100644 --- a/src/libs/compat/freebsd_network/taskqueue.c +++ b/src/libs/compat/freebsd_network/taskqueue.c @@ -1,13 +1,8 @@ /* - * Copyright 2009, Colin Günther, coling@gmx.de - * Copyright 2007, Hugo Santos. All Rights Reserved. + * Copyright 2022, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. - * - * Authors: - * Hugo Santos, hugosantos@gmail.com */ - #include "device.h" #include @@ -17,125 +12,42 @@ #include -#define TQ_FLAGS_ACTIVE (1 << 0) -#define TQ_FLAGS_BLOCKED (1 << 1) -#define TQ_FLAGS_PENDING (1 << 2) +static int _taskqueue_start_threads(struct taskqueue **taskQueue, + int count, int priority, const char *name); +static void taskqueue_terminate(struct thread **pp, struct taskqueue *tq); -#define DT_CALLOUT_ARMED (1 << 0) -#define DT_DRAIN_IN_PROGRESS (1 << 1) -struct taskqueue { - char tq_name[TASKQUEUE_NAMELEN]; - struct mtx tq_mutex; - struct list tq_list; - taskqueue_enqueue_fn tq_enqueue; - void *tq_arg; - int tq_fast; - sem_id tq_sem; - thread_id *tq_threads; - thread_id tq_thread_storage; - int tq_threadcount; - int tq_flags; - int tq_callouts; -}; +#define malloc kernel_malloc +#define free kernel_free +#include "fbsd_subr_taskqueue.c" +#undef malloc +#undef free + struct taskqueue *taskqueue_fast = NULL; struct taskqueue *taskqueue_swi = NULL; struct taskqueue *taskqueue_thread = NULL; -static struct taskqueue * -_taskqueue_create(const char *name, int mflags, int fast, - taskqueue_enqueue_fn enqueueFunction, void *context) -{ - struct taskqueue *tq = malloc(sizeof(struct taskqueue)); - if (tq == NULL) - return NULL; - - tq->tq_fast = fast; - - if (fast) { - mtx_init(&tq->tq_mutex, name, NULL, MTX_SPIN); - } else { - mtx_init(&tq->tq_mutex, name, NULL, MTX_DEF); - } - - strlcpy(tq->tq_name, name, sizeof(tq->tq_name)); - list_init_etc(&tq->tq_list, offsetof(struct task, ta_link)); - tq->tq_enqueue = enqueueFunction; - tq->tq_arg = context; - - tq->tq_sem = -1; - tq->tq_threads = NULL; - tq->tq_threadcount = 0; - tq->tq_flags = TQ_FLAGS_ACTIVE; - tq->tq_callouts = 0; - - return tq; -} - - -static void -tq_lock(struct taskqueue *taskQueue) -{ - if (taskQueue->tq_fast) { - mtx_lock_spin(&taskQueue->tq_mutex); - } else { - mtx_lock(&taskQueue->tq_mutex); - } -} - - -static void -tq_unlock(struct taskqueue *taskQueue) -{ - if (taskQueue->tq_fast) { - mtx_unlock_spin(&taskQueue->tq_mutex); - } else { - mtx_unlock(&taskQueue->tq_mutex); - } -} - - -struct taskqueue * -taskqueue_create(const char *name, int mflags, - taskqueue_enqueue_fn enqueueFunction, void *context) -{ - return _taskqueue_create(name, mflags, 0, enqueueFunction, context); -} - - static int32 tq_handle_thread(void *data) { struct taskqueue *tq = data; - struct task *t; - int pending; - sem_id sem; - /* just a synchronization point */ - tq_lock(tq); - sem = tq->tq_sem; - tq_unlock(tq); + taskqueue_run_callback(tq, TASKQUEUE_CALLBACK_TYPE_INIT); + + TQ_LOCK(tq); + sem_id sem = tq->tq_sem; + TQ_UNLOCK(tq); while (acquire_sem(sem) == B_NO_ERROR) { - tq_lock(tq); - t = list_remove_head_item(&tq->tq_list); - tq_unlock(tq); - if (t == NULL) - continue; - pending = t->ta_pending; - t->ta_pending = 0; - - if ((t->ta_flags & TASK_NEEDSGIANT) != 0) - mtx_lock(&Giant); - - t->ta_handler(t->ta_argument, pending); - - if ((t->ta_flags & TASK_NEEDSGIANT) != 0) - mtx_unlock(&Giant); + TQ_LOCK(tq); + taskqueue_run_locked(tq); + TQ_UNLOCK(tq); } + taskqueue_run_callback(tq, TASKQUEUE_CALLBACK_TYPE_SHUTDOWN); + return 0; } @@ -193,56 +105,21 @@ _taskqueue_start_threads(struct taskqueue **taskQueue, int count, int priority, } -int -taskqueue_start_threads(struct taskqueue **taskQueue, int count, int priority, - const char *format, ...) +static void +taskqueue_terminate(struct thread **pp, struct taskqueue *tq) { - /* we assume that start_threads is called in a sane place, and thus - * don't need to be locked. This is mostly due to the fact that if - * the TQ is 'fast', locking the TQ disables interrupts... and then - * we can't create semaphores, threads and bananas. */ - - char name[64]; - int result; - va_list vl; - - va_start(vl, format); - vsnprintf(name, sizeof(name), format, vl); - va_end(vl); - - /*tq_lock(*tqp);*/ - result = _taskqueue_start_threads(taskQueue, count, priority, name); - /*tq_unlock(*tqp);*/ - - return result; -} - - -void -taskqueue_free(struct taskqueue *taskQueue) -{ - if (taskQueue == NULL) + if (tq->tq_sem == -1) return; - /* lock and drain list? */ - taskQueue->tq_flags &= ~TQ_FLAGS_ACTIVE; - if (!taskQueue->tq_fast) - mtx_destroy(&taskQueue->tq_mutex); - if (taskQueue->tq_sem != -1) { - int i; + delete_sem(tq->tq_sem); - delete_sem(taskQueue->tq_sem); - - for (i = 0; i < taskQueue->tq_threadcount; i++) { - status_t status; - wait_for_thread(taskQueue->tq_threads[i], &status); - } - - if (taskQueue->tq_threadcount > 1) - free(taskQueue->tq_threads); + for (int i = 0; i < tq->tq_threadcount; i++) { + status_t status; + wait_for_thread(tq->tq_threads[i], &status); } - free(taskQueue); + if (tq->tq_threadcount > 1) + free(tq->tq_threads); } @@ -252,44 +129,13 @@ taskqueue_drain(struct taskqueue *taskQueue, struct task *task) if (taskQueue == NULL) return; - tq_lock(taskQueue); - while (task->ta_pending != 0) { - tq_unlock(taskQueue); + TQ_LOCK(taskQueue); + while (task->ta_pending != 0 || task_is_running(taskQueue, task)) { + TQ_UNLOCK(taskQueue); snooze(0); - tq_lock(taskQueue); + TQ_LOCK(taskQueue); } - tq_unlock(taskQueue); -} - - -void -taskqueue_drain_timeout(struct taskqueue *queue, - struct timeout_task *timeout_task) -{ - /* - * Set flag to prevent timer from re-starting during drain: - */ - tq_lock(queue); - KASSERT((timeout_task->f & DT_DRAIN_IN_PROGRESS) == 0, - ("Drain already in progress")); - timeout_task->f |= DT_DRAIN_IN_PROGRESS; - tq_unlock(queue); - - callout_drain(&timeout_task->c); - taskqueue_drain(queue, &timeout_task->t); - - /* - * Clear flag to allow timer to re-start: - */ - tq_lock(queue); - timeout_task->f &= ~DT_DRAIN_IN_PROGRESS; - tq_unlock(queue); -} - - -static void -taskqueue_task_nop_fn(void* context, int pending) -{ + TQ_UNLOCK(taskQueue); } @@ -309,165 +155,6 @@ taskqueue_drain_all(struct taskqueue *taskQueue) } -static void -taskqueue_enqueue_locked(struct taskqueue *taskQueue, struct task *task) -{ - /* we don't really support priorities */ - if (task->ta_pending) { - task->ta_pending++; - } else { - list_add_item(&taskQueue->tq_list, task); - task->ta_pending = 1; - if ((taskQueue->tq_flags & TQ_FLAGS_BLOCKED) == 0) - taskQueue->tq_enqueue(taskQueue->tq_arg); - else - taskQueue->tq_flags |= TQ_FLAGS_PENDING; - } - tq_unlock(taskQueue); -} - - -int -taskqueue_enqueue(struct taskqueue *taskQueue, struct task *task) -{ - tq_lock(taskQueue); - taskqueue_enqueue_locked(taskQueue, task); - /* The lock is released inside. */ - - return 0; -} - - -static void -taskqueue_timeout_func(void *arg) -{ - struct taskqueue *queue; - struct timeout_task *timeout_task; - - timeout_task = arg; - queue = timeout_task->q; - KASSERT((timeout_task->f & DT_CALLOUT_ARMED) != 0, - ("stray timeout ('%s')", timeout_task->q->tq_name)); - timeout_task->f &= ~DT_CALLOUT_ARMED; - queue->tq_callouts--; - taskqueue_enqueue_locked(timeout_task->q, &timeout_task->t); - /* The lock is released inside. */ -} - - -int -taskqueue_enqueue_timeout(struct taskqueue *queue, - struct timeout_task *ttask, int _ticks) -{ - int res; - - tq_lock(queue); - KASSERT(ttask->q == NULL || ttask->q == queue, - ("Migrated queue")); - ttask->q = queue; - res = ttask->t.ta_pending; - if (ttask->f & DT_DRAIN_IN_PROGRESS) { - /* Do nothing */ - tq_unlock(queue); - res = -1; - } else if (_ticks == 0) { - tq_unlock(queue); - taskqueue_enqueue(queue, &ttask->t); - } else { - if ((ttask->f & DT_CALLOUT_ARMED) != 0) { - res++; - } else { - queue->tq_callouts++; - ttask->f |= DT_CALLOUT_ARMED; - if (_ticks < 0) - _ticks = -_ticks; /* Ignore overflow. */ - } - tq_unlock(queue); - if (_ticks > 0) { - callout_reset(&ttask->c, _ticks, - taskqueue_timeout_func, ttask); - } - } - return (res); -} - - -static int -taskqueue_cancel_locked(struct taskqueue *queue, struct task *task, - u_int *pendp) -{ - if (task->ta_pending > 0) - list_remove_item(&queue->tq_list, task); - if (pendp != NULL) - *pendp = task->ta_pending; - task->ta_pending = 0; - return 0; -} - - -int -taskqueue_cancel(struct taskqueue *queue, struct task *task, u_int *pendp) -{ - int error; - - tq_lock(queue); - error = taskqueue_cancel_locked(queue, task, pendp); - tq_unlock(queue); - - return (error); -} - - -int -taskqueue_cancel_timeout(struct taskqueue *queue, - struct timeout_task *timeout_task, u_int *pendp) -{ - u_int pending, pending1; - int error; - - tq_lock(queue); - pending = !!(callout_stop(&timeout_task->c) > 0); - error = taskqueue_cancel_locked(queue, &timeout_task->t, &pending1); - if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) { - timeout_task->f &= ~DT_CALLOUT_ARMED; - queue->tq_callouts--; - } - tq_unlock(queue); - - if (pendp != NULL) - *pendp = pending + pending1; - return (error); -} - - -void -taskqueue_block(struct taskqueue *taskQueue) -{ - if (taskQueue == NULL) - return; - - tq_lock(taskQueue); - taskQueue->tq_flags |= TQ_FLAGS_BLOCKED; - tq_unlock(taskQueue); -} - - -void -taskqueue_unblock(struct taskqueue *taskQueue) -{ - if (taskQueue == NULL) - return; - - tq_lock(taskQueue); - taskQueue->tq_flags &= ~TQ_FLAGS_BLOCKED; - if (taskQueue->tq_flags & TQ_FLAGS_PENDING) { - taskQueue->tq_flags &= ~TQ_FLAGS_PENDING; - taskQueue->tq_enqueue(taskQueue->tq_arg); - } - tq_unlock(taskQueue); -} - - void taskqueue_thread_enqueue(void *context) { @@ -476,44 +163,17 @@ taskqueue_thread_enqueue(void *context) } -int -taskqueue_enqueue_fast(struct taskqueue *taskQueue, struct task *task) -{ - return taskqueue_enqueue(taskQueue, task); -} - - -struct taskqueue * -taskqueue_create_fast(const char *name, int mflags, - taskqueue_enqueue_fn enqueueFunction, void *context) -{ - return _taskqueue_create(name, mflags, 1, enqueueFunction, context); -} - - void -task_init(struct task *task, int prio, task_fn_t handler, void *context) +_task_init(struct task *task, int prio, task_fn_t handler, void *context) { task->ta_priority = prio; task->ta_flags = 0; - task->ta_handler = handler; - task->ta_argument = context; + task->ta_func = handler; + task->ta_context = context; task->ta_pending = 0; } -void -timeout_task_init(struct taskqueue *queue, struct timeout_task *timeout_task, - int priority, task_fn_t func, void *context) -{ - TASK_INIT(&timeout_task->t, priority, func, context); - callout_init_mtx(&timeout_task->c, &queue->tq_mutex, - CALLOUT_RETURNUNLOCKED); - timeout_task->q = queue; - timeout_task->f = 0; -} - - status_t init_taskqueues() { @@ -546,7 +206,7 @@ init_taskqueues() } if (HAIKU_DRIVER_REQUIRES(FBSD_THREAD_TASKQUEUE)) { - taskqueue_thread = taskqueue_create_fast("thread taskq", 0, + taskqueue_thread = taskqueue_create("thread taskq", 0, taskqueue_thread_enqueue, &taskqueue_thread); if (taskqueue_thread == NULL) { status = B_NO_MEMORY; diff --git a/src/libs/compat/openbsd_network/compat/sys/task.h b/src/libs/compat/openbsd_network/compat/sys/task.h index 7f14d1c11a..f4f3eb0df5 100644 --- a/src/libs/compat/openbsd_network/compat/sys/task.h +++ b/src/libs/compat/openbsd_network/compat/sys/task.h @@ -20,9 +20,7 @@ struct taskq { static void task_set(struct task *t, void (*fn)(void *), void *arg) { - t->ta_priority = 0; - t->ta_handler = fn; - t->ta_argument = arg; + TASK_INIT(t, 0, fn, arg); } @@ -41,7 +39,7 @@ task_add(struct taskq* tasq, struct task *w) w->ta_flags |= TASK_NEEDSGIANT; if (task_pending(w)) return 0; - return (taskqueue_enqueue_fast(tq, w) == 0); + return (taskqueue_enqueue(tq, w) == 0); }