Module Name: src
Committed By: riastradh
Date: Sun Sep 8 16:40:36 UTC 2013
Modified Files:
src/sys/external/bsd/drm2/include/linux [riastradh-drm2]: workqueue.h
Log Message:
Rework Linux `work' to use NetBSD workqueues, not callouts.
Callers expect to be able to allocate in the workers, which callouts
don't allow.
Delayed work uses callouts only to delay enqueueing work.
Linux `workqueues' are still stubs.
To generate a diff of this commit:
cvs rdiff -u -r1.1.2.9 -r1.1.2.10 \
src/sys/external/bsd/drm2/include/linux/workqueue.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/external/bsd/drm2/include/linux/workqueue.h
diff -u src/sys/external/bsd/drm2/include/linux/workqueue.h:1.1.2.9 src/sys/external/bsd/drm2/include/linux/workqueue.h:1.1.2.10
--- src/sys/external/bsd/drm2/include/linux/workqueue.h:1.1.2.9 Sun Sep 8 15:58:24 2013
+++ src/sys/external/bsd/drm2/include/linux/workqueue.h Sun Sep 8 16:40:36 2013
@@ -1,4 +1,4 @@
-/* $NetBSD: workqueue.h,v 1.1.2.9 2013/09/08 15:58:24 riastradh Exp $ */
+/* $NetBSD: workqueue.h,v 1.1.2.10 2013/09/08 16:40:36 riastradh Exp $ */
/*-
* Copyright (c) 2013 The NetBSD Foundation, Inc.
@@ -33,81 +33,220 @@
#define _LINUX_WORKQUEUE_H_
#include <sys/callout.h>
+#include <sys/condvar.h>
+#include <sys/mutex.h>
+#include <sys/workqueue.h>
#include <asm/bug.h>
#include <linux/kernel.h>
/*
- * XXX This implementation is a load of bollocks -- callouts are
- * expedient, but wrong, if for no reason other than that we never call
- * callout_destroy.
+ * XXX This implementation is a load of bollocks -- callouts and
+ * workqueues are expedient, but wrong, if for no reason other than
+ * that there is no destroy operation.
+ *
+ * XXX The amount of code in here is absurd; it should be given a
+ * proper source file.
*/
struct work_struct {
- struct callout ws_callout;
+ void (*w_fn)(struct work_struct *);
+ struct workqueue *w_wq;
+ struct work w_wk;
+ kmutex_t w_lock;
+ kcondvar_t w_cv;
+ enum {
+ WORK_IDLE,
+ WORK_QUEUED,
+ WORK_CANCELLED,
+ WORK_INFLIGHT,
+ WORK_REQUEUED,
+ } w_state;
};
-struct delayed_work {
- struct work_struct work;
-};
+static void __unused
+linux_work_fn(struct work *wk __unused, void *arg)
+{
+ struct work_struct *const work = arg;
+
+ mutex_spin_enter(&work->w_lock);
+ switch (work->w_state) {
+ case WORK_IDLE:
+ panic("work ran while idle: %p", work);
+ break;
+
+ case WORK_QUEUED:
+ work->w_state = WORK_INFLIGHT;
+ mutex_spin_exit(&work->w_lock);
+ (*work->w_fn)(work);
+ mutex_spin_enter(&work->w_lock);
+ switch (work->w_state) {
+ case WORK_IDLE:
+ case WORK_QUEUED:
+ panic("work hosed while in flight: %p", work);
+ break;
+
+ case WORK_INFLIGHT:
+ case WORK_CANCELLED:
+ work->w_state = WORK_IDLE;
+ cv_broadcast(&work->w_cv);
+ break;
+
+ case WORK_REQUEUED:
+ workqueue_enqueue(work->w_wq, &work->w_wk, NULL);
+ work->w_state = WORK_QUEUED;
+ break;
+
+ default:
+ panic("work %p in bad state: %d", work,
+ (int)work->w_state);
+ break;
+ }
+ break;
+
+ case WORK_CANCELLED:
+ work->w_state = WORK_IDLE;
+ cv_broadcast(&work->w_cv);
+ break;
+
+ case WORK_INFLIGHT:
+ panic("work already in flight: %p", work);
+ break;
+
+ case WORK_REQUEUED:
+ panic("work requeued while not in flight: %p", work);
+ break;
+
+ default:
+ panic("work %p in bad state: %d", work, (int)work->w_state);
+ break;
+ }
+ mutex_spin_exit(&work->w_lock);
+}
static inline void
INIT_WORK(struct work_struct *work, void (*fn)(struct work_struct *))
{
+ int error;
- callout_init(&work->ws_callout, 0);
-
- /* XXX This cast business is sketchy. */
- callout_setfunc(&work->ws_callout, (void (*)(void *))fn, work);
+ work->w_fn = fn;
+ error = workqueue_create(&work->w_wq, "lnxworkq", &linux_work_fn,
+ work, PRI_NONE, IPL_VM, WQ_MPSAFE);
+ if (error)
+ panic("workqueue creation failed: %d", error); /* XXX */
+
+ mutex_init(&work->w_lock, MUTEX_DEFAULT, IPL_VM);
+ cv_init(&work->w_cv, "linxwork");
+ work->w_state = WORK_IDLE;
}
static inline void
-INIT_DELAYED_WORK(struct delayed_work *dw, void (*fn)(struct work_struct *))
+schedule_work(struct work_struct *work)
{
- INIT_WORK(&dw->work, fn);
+
+ mutex_spin_enter(&work->w_lock);
+ switch (work->w_state) {
+ case WORK_IDLE:
+ workqueue_enqueue(work->w_wq, &work->w_wk, NULL);
+ work->w_state = WORK_QUEUED;
+ break;
+
+ case WORK_CANCELLED:
+ break;
+
+ case WORK_INFLIGHT:
+ work->w_state = WORK_REQUEUED;
+ break;
+
+ case WORK_QUEUED:
+ case WORK_REQUEUED:
+ break;
+
+ default:
+ panic("work %p in bad state: %d", work, (int)work->w_state);
+ break;
+ }
+ mutex_spin_exit(&work->w_lock);
}
-static inline struct delayed_work *
-to_delayed_work(struct work_struct *work)
+/*
+ * XXX This API can't possibly be right because there is no interlock.
+ */
+static inline bool
+cancel_work_sync(struct work_struct *work)
{
- return container_of(work, struct delayed_work, work);
+ bool was_pending = false;
+
+ mutex_spin_enter(&work->w_lock);
+retry: switch (work->w_state) {
+ case WORK_IDLE:
+ break;
+
+ case WORK_QUEUED:
+ case WORK_INFLIGHT:
+ case WORK_REQUEUED:
+ work->w_state = WORK_CANCELLED;
+ /* FALLTHROUGH */
+ case WORK_CANCELLED:
+ cv_wait(&work->w_cv, &work->w_lock);
+ was_pending = true;
+ goto retry;
+
+ default:
+ panic("work %p in bad state: %d", work, (int)work->w_state);
+ }
+ mutex_spin_exit(&work->w_lock);
+
+ return was_pending;
}
-static inline void
-schedule_work(struct work_struct *work)
+struct delayed_work {
+ struct callout dw_callout;
+ struct work_struct work; /* not dw_work; name must match Linux */
+};
+
+static void __unused
+linux_delayed_work_fn(void *arg)
{
- callout_schedule(&work->ws_callout, 0);
+ struct delayed_work *const dw = arg;
+
+ schedule_work(&dw->work);
}
static inline void
-schedule_delayed_work(struct delayed_work *dw, unsigned long ticks)
+INIT_DELAYED_WORK(struct delayed_work *dw, void (*fn)(struct work_struct *))
{
- KASSERT(ticks < INT_MAX);
- callout_schedule(&dw->work.ws_callout, (int)ticks);
+ callout_init(&dw->dw_callout, CALLOUT_MPSAFE);
+ callout_setfunc(&dw->dw_callout, linux_delayed_work_fn, dw);
+ INIT_WORK(&dw->work, fn);
}
-static inline bool
-cancel_work(struct work_struct *work)
+static inline struct delayed_work *
+to_delayed_work(struct work_struct *work)
{
- return !callout_stop(&work->ws_callout);
+ return container_of(work, struct delayed_work, work);
}
-static inline bool
-cancel_work_sync(struct work_struct *work)
+static inline void
+schedule_delayed_work(struct delayed_work *dw, unsigned long ticks)
{
- return !callout_halt(&work->ws_callout, NULL);
+ KASSERT(ticks < INT_MAX);
+ callout_schedule(&dw->dw_callout, (int)ticks);
}
static inline bool
cancel_delayed_work(struct delayed_work *dw)
{
- return cancel_work(&dw->work);
+ return !callout_stop(&dw->dw_callout);
}
static inline bool
cancel_delayed_work_sync(struct delayed_work *dw)
{
- return cancel_work_sync(&dw->work);
+ const bool callout_was_pending = !callout_stop(&dw->dw_callout);
+ const bool work_was_pending = cancel_work_sync(&dw->work);
+
+ return (callout_was_pending || work_was_pending);
}
/*