Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/riastradh-drm2]: src/sys/external/bsd/drm2/include/linux Rework Linux `w...
details: https://anonhg.NetBSD.org/src/rev/d9fcdf100402
branches: riastradh-drm2
changeset: 788569:d9fcdf100402
user: riastradh <riastradh%NetBSD.org@localhost>
date: Sun Sep 08 16:40:36 2013 +0000
description:
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.
diffstat:
sys/external/bsd/drm2/include/linux/workqueue.h | 203 ++++++++++++++++++++---
1 files changed, 171 insertions(+), 32 deletions(-)
diffs (258 lines):
diff -r 55aee70cfa52 -r d9fcdf100402 sys/external/bsd/drm2/include/linux/workqueue.h
--- a/sys/external/bsd/drm2/include/linux/workqueue.h Sun Sep 08 16:38:51 2013 +0000
+++ b/sys/external/bsd/drm2/include/linux/workqueue.h Sun Sep 08 16:40:36 2013 +0000
@@ -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,37 +33,191 @@
#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);
+ 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
+schedule_work(struct work_struct *work)
+{
+
+ 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);
+}
- /* XXX This cast business is sketchy. */
- callout_setfunc(&work->ws_callout, (void (*)(void *))fn, work);
+/*
+ * XXX This API can't possibly be right because there is no interlock.
+ */
+static inline bool
+cancel_work_sync(struct work_struct *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;
+}
+
+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)
+{
+ struct delayed_work *const dw = arg;
+
+ schedule_work(&dw->work);
}
static inline void
INIT_DELAYED_WORK(struct delayed_work *dw, void (*fn)(struct work_struct *))
{
+ callout_init(&dw->dw_callout, CALLOUT_MPSAFE);
+ callout_setfunc(&dw->dw_callout, linux_delayed_work_fn, dw);
INIT_WORK(&dw->work, fn);
}
@@ -74,40 +228,25 @@
}
static inline void
-schedule_work(struct work_struct *work)
-{
- callout_schedule(&work->ws_callout, 0);
-}
-
-static inline void
schedule_delayed_work(struct delayed_work *dw, unsigned long ticks)
{
KASSERT(ticks < INT_MAX);
- callout_schedule(&dw->work.ws_callout, (int)ticks);
-}
-
-static inline bool
-cancel_work(struct work_struct *work)
-{
- return !callout_stop(&work->ws_callout);
-}
-
-static inline bool
-cancel_work_sync(struct work_struct *work)
-{
- return !callout_halt(&work->ws_callout, NULL);
+ 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);
}
/*
Home |
Main Index |
Thread Index |
Old Index