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