Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F149728324
D35051.id106104.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D35051.id106104.diff
View Options
diff --git a/share/man/man9/taskqueue.9 b/share/man/man9/taskqueue.9
--- a/share/man/man9/taskqueue.9
+++ b/share/man/man9/taskqueue.9
@@ -28,7 +28,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd September 1, 2021
+.Dd April 25, 2022
.Dt TASKQUEUE 9
.Os
.Sh NAME
@@ -85,6 +85,8 @@
.Ft int
.Fn taskqueue_enqueue "struct taskqueue *queue" "struct task *task"
.Ft int
+.Fn taskqueue_enqueue_flags "struct taskqueue *queue" "struct task *task" "int flags"
+.Ft int
.Fn taskqueue_enqueue_timeout "struct taskqueue *queue" "struct timeout_task *timeout_task" "int ticks"
.Ft int
.Fn taskqueue_enqueue_timeout_sbt "struct taskqueue *queue" "struct timeout_task *timeout_task" "sbintime_t sbt" "sbintime_t pr" "int flags"
@@ -225,6 +227,28 @@
.Fn taskqueue_enqueue .
.Pp
The
+.Fn taskqueue_enqueue_flags
+accepts an extra
+.Va flags
+parameter which specifies a set of optional flags to alter the behavior of
+.Fn taskqueue_enqueue .
+It contains one or more of the following flags:
+.Bl -tag -width TASKQUEUE_FAIL_IF_CANCELING
+.It Dv TASKQUEUE_FAIL_IF_PENDING
+.Fn taskqueue_enqueue_flags
+fails if the task is already scheduled for execution.
+.Er EEXIST
+is returned and the
+.Va ta_pending
+counter value remains unchanged.
+.It Dv TASKQUEUE_FAIL_IF_CANCELING
+.Fn taskqueue_enqueue_flags
+fails if the task is in the canceling state and
+.Er ECANCELED
+is returned.
+.El
+.Pp
+The
.Fn taskqueue_enqueue_timeout
function is used to schedule the enqueue after the specified number of
.Va ticks .
diff --git a/sys/compat/linuxkpi/common/include/linux/kthread.h b/sys/compat/linuxkpi/common/include/linux/kthread.h
--- a/sys/compat/linuxkpi/common/include/linux/kthread.h
+++ b/sys/compat/linuxkpi/common/include/linux/kthread.h
@@ -33,8 +33,29 @@
#include <linux/sched.h>
-#include <sys/unistd.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/unistd.h>
+
+struct task_struct;
+struct kthread_work;
+
+typedef void (*kthread_work_func_t)(struct kthread_work *work);
+
+struct kthread_worker {
+ struct task_struct *task;
+ struct taskqueue *tq;
+};
+
+struct kthread_work {
+ struct taskqueue *tq;
+ struct task task;
+ kthread_work_func_t func;
+};
#define kthread_run(fn, data, fmt, ...) ({ \
struct task_struct *__task; \
@@ -70,4 +91,78 @@
#define in_atomic() linux_in_atomic()
+/* Only kthread_(create|destroy)_worker interface is allowed */
+#define kthread_init_worker(worker) \
+ _Static_assert(false, "pre-4.9 worker interface is not supported");
+
+task_fn_t lkpi_kthread_work_fn;
+task_fn_t lkpi_kthread_worker_init_fn;
+
+#define kthread_create_worker(flags, fmt, ...) ({ \
+ struct kthread_worker *__w; \
+ struct task __task; \
+ \
+ __w = malloc(sizeof(*__w), M_KMALLOC, M_WAITOK | M_ZERO); \
+ __w->tq = taskqueue_create("lkpi kthread taskq", M_WAITOK, \
+ taskqueue_thread_enqueue, &__w->tq); \
+ taskqueue_start_threads(&__w->tq, 1, PWAIT, fmt, ##__VA_ARGS__);\
+ TASK_INIT(&__task, 0, lkpi_kthread_worker_init_fn, __w); \
+ taskqueue_enqueue(__w->tq, &__task); \
+ taskqueue_drain(__w->tq, &__task); \
+ __w; \
+})
+
+static inline void
+kthread_destroy_worker(struct kthread_worker *worker)
+{
+ taskqueue_drain_all(worker->tq);
+ taskqueue_free(worker->tq);
+ free(worker, M_KMALLOC);
+}
+
+static inline void
+kthread_init_work(struct kthread_work *work, kthread_work_func_t func)
+{
+ work->tq = NULL;
+ work->func = func;
+ TASK_INIT(&work->task, 0, lkpi_kthread_work_fn, work);
+}
+
+static inline bool
+kthread_queue_work(struct kthread_worker *worker, struct kthread_work *work)
+{
+ int error;
+
+ error = taskqueue_enqueue_flags(worker->tq, &work->task,
+ TASKQUEUE_FAIL_IF_CANCELING | TASKQUEUE_FAIL_IF_PENDING);
+ if (error == 0)
+ work->tq = worker->tq;
+ return (error == 0);
+}
+
+static inline bool
+kthread_cancel_work_sync(struct kthread_work *work)
+{
+ u_int pending = 0;
+
+ if (work->tq != NULL &&
+ taskqueue_cancel(work->tq, &work->task, &pending) != 0)
+ taskqueue_drain(work->tq, &work->task);
+
+ return (pending != 0);
+}
+
+static inline void
+kthread_flush_work(struct kthread_work *work)
+{
+ if (work->tq != NULL)
+ taskqueue_drain(work->tq, &work->task);
+}
+
+static inline void
+kthread_flush_worker(struct kthread_worker *worker)
+{
+ taskqueue_drain_all(worker->tq);
+}
+
#endif /* _LINUXKPI_LINUX_KTHREAD_H_ */
diff --git a/sys/compat/linuxkpi/common/src/linux_kthread.c b/sys/compat/linuxkpi/common/src/linux_kthread.c
--- a/sys/compat/linuxkpi/common/src/linux_kthread.c
+++ b/sys/compat/linuxkpi/common/src/linux_kthread.c
@@ -165,3 +165,19 @@
}
kthread_exit();
}
+
+void
+lkpi_kthread_work_fn(void *context, int pending __unused)
+{
+ struct kthread_work *work = context;
+
+ work->func(work);
+}
+
+void
+lkpi_kthread_worker_init_fn(void *context, int pending __unused)
+{
+ struct kthread_worker *worker = context;
+
+ worker->task = current;
+}
diff --git a/sys/kern/subr_taskqueue.c b/sys/kern/subr_taskqueue.c
--- a/sys/kern/subr_taskqueue.c
+++ b/sys/kern/subr_taskqueue.c
@@ -59,6 +59,7 @@
struct taskqueue_busy {
struct task *tb_running;
u_int tb_seq;
+ bool tb_canceling;
LIST_ENTRY(taskqueue_busy) tb_link;
};
@@ -125,6 +126,19 @@
return (msleep(p, &tq->tq_mutex, 0, wm, 0));
}
+static struct taskqueue_busy *
+task_get_busy(struct taskqueue *queue, struct task *task)
+{
+ struct taskqueue_busy *tb;
+
+ TQ_ASSERT_LOCKED(queue);
+ LIST_FOREACH(tb, &queue->tq_active, tb_link) {
+ if (tb->tb_running == task)
+ return (tb);
+ }
+ return (NULL);
+}
+
static struct taskqueue *
_taskqueue_create(const char *name, int mflags,
taskqueue_enqueue_fn enqueue, void *context,
@@ -217,16 +231,32 @@
}
static int
-taskqueue_enqueue_locked(struct taskqueue *queue, struct task *task)
+taskqueue_enqueue_locked(struct taskqueue *queue, struct task *task, int flags)
{
struct task *ins;
struct task *prev;
+ struct taskqueue_busy *tb;
KASSERT(task->ta_func != NULL, ("enqueueing task with NULL func"));
+ /*
+ * Ignore canceling task if requested.
+ */
+ if (__predict_false((flags & TASKQUEUE_FAIL_IF_CANCELING) != 0)) {
+ tb = task_get_busy(queue, task);
+ if (tb != NULL && tb->tb_canceling) {
+ TQ_UNLOCK(queue);
+ return (ECANCELED);
+ }
+ }
+
/*
* Count multiple enqueues.
*/
if (task->ta_pending) {
+ if (__predict_false((flags & TASKQUEUE_FAIL_IF_PENDING) != 0)) {
+ TQ_UNLOCK(queue);
+ return (EEXIST);
+ }
if (task->ta_pending < USHRT_MAX)
task->ta_pending++;
TQ_UNLOCK(queue);
@@ -274,17 +304,23 @@
}
int
-taskqueue_enqueue(struct taskqueue *queue, struct task *task)
+taskqueue_enqueue_flags(struct taskqueue *queue, struct task *task, int flags)
{
int res;
TQ_LOCK(queue);
- res = taskqueue_enqueue_locked(queue, task);
+ res = taskqueue_enqueue_locked(queue, task, flags);
/* The lock is released inside. */
return (res);
}
+int
+taskqueue_enqueue(struct taskqueue *queue, struct task *task)
+{
+ return (taskqueue_enqueue_flags(queue, task, 0));
+}
+
static void
taskqueue_timeout_func(void *arg)
{
@@ -296,7 +332,7 @@
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);
+ taskqueue_enqueue_locked(timeout_task->q, &timeout_task->t, 0);
/* The lock is released inside. */
}
@@ -316,7 +352,7 @@
TQ_UNLOCK(queue);
res = -1;
} else if (sbt == 0) {
- taskqueue_enqueue_locked(queue, &timeout_task->t);
+ taskqueue_enqueue_locked(queue, &timeout_task->t, 0);
/* The lock is released inside. */
} else {
if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) {
@@ -464,6 +500,7 @@
task->ta_pending = 0;
tb.tb_running = task;
tb.tb_seq = ++queue->tq_seq;
+ tb.tb_canceling = false;
TQ_UNLOCK(queue);
KASSERT(task->ta_func != NULL, ("task->ta_func is NULL"));
@@ -493,19 +530,6 @@
TQ_UNLOCK(queue);
}
-static int
-task_is_running(struct taskqueue *queue, struct task *task)
-{
- struct taskqueue_busy *tb;
-
- TQ_ASSERT_LOCKED(queue);
- LIST_FOREACH(tb, &queue->tq_active, tb_link) {
- if (tb->tb_running == task)
- return (1);
- }
- return (0);
-}
-
/*
* Only use this function in single threaded contexts. It returns
* non-zero if the given task is either pending or running. Else the
@@ -517,7 +541,7 @@
int retval;
TQ_LOCK(queue);
- retval = task->ta_pending > 0 || task_is_running(queue, task);
+ retval = task->ta_pending > 0 || task_get_busy(queue, task) != NULL;
TQ_UNLOCK(queue);
return (retval);
@@ -527,6 +551,8 @@
taskqueue_cancel_locked(struct taskqueue *queue, struct task *task,
u_int *pendp)
{
+ struct taskqueue_busy *tb;
+ int retval = 0;
if (task->ta_pending > 0) {
STAILQ_REMOVE(&queue->tq_queue, task, task, ta_link);
@@ -536,7 +562,13 @@
if (pendp != NULL)
*pendp = task->ta_pending;
task->ta_pending = 0;
- return (task_is_running(queue, task) ? EBUSY : 0);
+ tb = task_get_busy(queue, task);
+ if (tb != NULL) {
+ tb->tb_canceling = true;
+ retval = EBUSY;
+ }
+
+ return (retval);
}
int
@@ -580,7 +612,7 @@
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, __func__);
TQ_LOCK(queue);
- while (task->ta_pending != 0 || task_is_running(queue, task))
+ while (task->ta_pending != 0 || task_get_busy(queue, task) != NULL)
TQ_SLEEP(queue, task, "tq_drain");
TQ_UNLOCK(queue);
}
diff --git a/sys/sys/taskqueue.h b/sys/sys/taskqueue.h
--- a/sys/sys/taskqueue.h
+++ b/sys/sys/taskqueue.h
@@ -61,6 +61,10 @@
#define TASKQUEUE_NUM_CALLBACKS TASKQUEUE_CALLBACK_TYPE_MAX + 1
#define TASKQUEUE_NAMELEN 32
+/* taskqueue_enqueue flags */
+#define TASKQUEUE_FAIL_IF_PENDING (1 << 0)
+#define TASKQUEUE_FAIL_IF_CANCELING (1 << 1)
+
typedef void (*taskqueue_callback_fn)(void *context);
/*
@@ -82,6 +86,8 @@
int taskqueue_start_threads_cpuset(struct taskqueue **tqp, int count,
int pri, cpuset_t *mask, const char *name, ...) __printflike(5, 6);
int taskqueue_enqueue(struct taskqueue *queue, struct task *task);
+int taskqueue_enqueue_flags(struct taskqueue *queue, struct task *task,
+ int flags);
int taskqueue_enqueue_timeout(struct taskqueue *queue,
struct timeout_task *timeout_task, int ticks);
int taskqueue_enqueue_timeout_sbt(struct taskqueue *queue,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Mar 27, 2:01 PM (16 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30430743
Default Alt Text
D35051.id106104.diff (10 KB)
Attached To
Mode
D35051: LinuxKPI: Implement kthread_worker related functions
Attached
Detach File
Event Timeline
Log In to Comment