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
=== 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)			\
_______________________________________________
freebsd-usb@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-usb
To unsubscribe, send any mail to "freebsd-usb-unsubscr...@freebsd.org"

Reply via email to