Date: Fri, 5 Nov 2010 19:30:38 +0100 From: Hans Petter Selasky <hselasky@c2i.net> To: freebsd-current@freebsd.org Cc: Weongyo Jeong <weongyo.jeong@gmail.com>, Matthew Fleming <mdf356@gmail.com>, freebsd-usb@freebsd.org, freebsd-arch@freebsd.org Subject: Re: [RFC] Outline of USB process integration in the kernel taskqueue system Message-ID: <201011051930.38530.hselasky@c2i.net> In-Reply-To: <201011051836.39697.hselasky@c2i.net> References: <201011012054.59551.hselasky@c2i.net> <AANLkTimR7MpZ3nyoWqkCR9a=-S6DR_MCNjPA0q5qg3U4@mail.gmail.com> <201011051836.39697.hselasky@c2i.net>
next in thread | previous in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
Hi,
In the patch attached to this e-mail I included Matthew Fleming's patch
aswell.
1) I renamed taskqueue_cancel() into taskqueue_stop(), hence that resembles
the words of the callout and USB API's terminology for doing the same.
2) I turns out I need to have code in subr_taskqueue.c to be able to make the
operations atomic.
3) I did not update the manpage in this patch. Will do that before a commit.
4) My patch implements separate state keeping in "struct task_pair", which
avoids having to change any KPI's for now, like suggested by John Baldwin I
think.
5) In my implementation I hard-coded the priority argument to zero, so that
enqueuing is fast.
Comments are welcome!
--HPS
[-- Attachment #2 --]
=== kern/subr_taskqueue.c
==================================================================
--- kern/subr_taskqueue.c (revision 214796)
+++ kern/subr_taskqueue.c (local)
@@ -275,6 +275,25 @@
return (0);
}
+int
+taskqueue_stop(struct taskqueue *queue, struct task *task)
+{
+ int retval = 0;
+
+ TQ_LOCK(queue);
+
+ if (task->ta_pending != 0) {
+ STAILQ_REMOVE(&queue->tq_queue, task, task, ta_link);
+ task->ta_pending = 0;
+ }
+ if (task_is_running(queue, task))
+ retval = EBUSY;
+
+ TQ_UNLOCK(queue);
+
+ return (retval);
+}
+
void
taskqueue_drain(struct taskqueue *queue, struct task *task)
{
@@ -288,6 +307,113 @@
TQ_UNLOCK(queue);
}
+
+int
+taskqueue_pair_enqueue(struct taskqueue *queue, struct task_pair *tp)
+{
+ struct task *task;
+ int retval;
+ int j;
+
+ TQ_LOCK(queue);
+
+ j = 0;
+ if (tp->tp_task[0].ta_pending > 0)
+ j |= 1;
+ if (tp->tp_task[1].ta_pending > 0)
+ j |= 2;
+
+ if (j == 0) {
+ /* No entries are queued. Just pick a last task. */
+ tp->tp_last = 0;
+ /* Re-queue the last queued task. */
+ task = &tp->tp_task[0];
+ } else if (j == 1) {
+ /* There is only one task pending and the other becomes last. */
+ tp->tp_last = 1;
+ /* Re-queue the last queued task. */
+ task = &tp->tp_task[1];
+ } else if (j == 2) {
+ /* There is only one task pending and the other becomes last. */
+ tp->tp_last = 0;
+ /* Re-queue the last queued task. */
+ task = &tp->tp_task[0];
+ } else {
+ /* Re-queue the last queued task. */
+ task = &tp->tp_task[tp->tp_last];
+ STAILQ_REMOVE(&queue->tq_queue, task, task, ta_link);
+ }
+
+ STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link);
+
+ retval = tp->tp_last + 1;
+ /* store the actual order in the pending count */
+ task->ta_pending = retval;
+
+ if ((queue->tq_flags & TQ_FLAGS_BLOCKED) == 0)
+ queue->tq_enqueue(queue->tq_context);
+ else
+ queue->tq_flags |= TQ_FLAGS_PENDING;
+
+ TQ_UNLOCK(queue);
+
+ return (retval);
+}
+
+int
+taskqueue_pair_stop(struct taskqueue *queue, struct task_pair *tp)
+{
+ struct task *task;
+ int retval = 0;
+
+ TQ_LOCK(queue);
+
+ task = &tp->tp_task[0];
+ if (task->ta_pending != 0) {
+ STAILQ_REMOVE(&queue->tq_queue, task, task, ta_link);
+ task->ta_pending = 0;
+ }
+ if (task_is_running(queue, task))
+ retval = EBUSY;
+
+ task = &tp->tp_task[1];
+ if (task->ta_pending != 0) {
+ STAILQ_REMOVE(&queue->tq_queue, task, task, ta_link);
+ task->ta_pending = 0;
+ }
+ if (task_is_running(queue, task))
+ retval = EBUSY;
+
+ TQ_UNLOCK(queue);
+
+ return (retval);
+}
+
+void
+taskqueue_pair_drain(struct taskqueue *queue, struct task_pair *tp)
+{
+ struct task *task;
+
+ if (!queue->tq_spin)
+ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, __func__);
+
+ TQ_LOCK(queue);
+top:
+ task = &tp->tp_task[0];
+ if (task->ta_pending != 0 || task_is_running(queue, task)) {
+ TQ_SLEEP(queue, task, &queue->tq_mutex, PWAIT, "-", 0);
+ goto top;
+ }
+
+ task = &tp->tp_task[1];
+ if (task->ta_pending != 0 || task_is_running(queue, task)) {
+ TQ_SLEEP(queue, task, &queue->tq_mutex, PWAIT, "-", 0);
+ goto top;
+ }
+
+ TQ_UNLOCK(queue);
+}
+
static void
taskqueue_swi_enqueue(void *context)
{
=== sys/_task.h
==================================================================
--- sys/_task.h (revision 214796)
+++ sys/_task.h (local)
@@ -51,4 +51,9 @@
void *ta_context; /* (c) argument for handler */
};
+struct task_pair {
+ struct task tp_task[2];
+ int tp_last; /* (q) index of last queued task */
+};
+
#endif /* !_SYS__TASK_H_ */
=== sys/taskqueue.h
==================================================================
--- sys/taskqueue.h (revision 214796)
+++ sys/taskqueue.h (local)
@@ -53,7 +53,11 @@
void *context);
int taskqueue_start_threads(struct taskqueue **tqp, int count, int pri,
const char *name, ...) __printflike(4, 5);
+int taskqueue_pair_enqueue(struct taskqueue *queue, struct task_pair *tp);
+int taskqueue_pair_stop(struct taskqueue *queue, struct task_pair *tp);
+void taskqueue_pair_drain(struct taskqueue *queue, struct task_pair *tp);
int taskqueue_enqueue(struct taskqueue *queue, struct task *task);
+int taskqueue_stop(struct taskqueue *queue, struct task *task);
void taskqueue_drain(struct taskqueue *queue, struct task *task);
void taskqueue_free(struct taskqueue *queue);
void taskqueue_run(struct taskqueue *queue);
@@ -78,6 +82,15 @@
} while (0)
/*
+ * Initialise a task pair structure.
+ */
+#define TASK_PAIR_INIT(tp, func, context) do { \
+ TASK_INIT(&(tp)->tp_task[0], 0, func, context); \
+ TASK_INIT(&(tp)->tp_task[1], 0, func, context); \
+ (tp)->tp_last = 0; \
+} while (0)
+
+/*
* Declare a reference to a taskqueue.
*/
#define TASKQUEUE_DECLARE(name) \
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201011051930.38530.hselasky>
