datapath: Backport workqueue functions.
authorJesse Gross <jesse@nicira.com>
Tue, 31 Aug 2010 22:38:25 +0000 (15:38 -0700)
committerJesse Gross <jesse@nicira.com>
Wed, 22 Sep 2010 20:43:02 +0000 (13:43 -0700)
An upcoming commit will use some workqueue functions that weren't
available on earlier kernels, so this backports those functions.
The backporting uses timers instead of delayed work queues because
the earlier versions of work queues have some unsafe corner cases.
In addition, this removes some unused work queue backporting code
that is no longer used because it is potentially unsafe.

Note that this commit changes the behavior of work queues: normally
they run in process context but the backported version runs in
softirq context.

Signed-off-by: Jesse Gross <jesse@nicira.com>
Reviewed-by: Ben Pfaff <blp@nicira.com>
datapath/datapath.c
datapath/linux-2.6/compat-2.6/include/linux/workqueue.h

index 5996d6e..e9f30f8 100644 (file)
@@ -39,7 +39,6 @@
 #include <linux/inetdevice.h>
 #include <linux/list.h>
 #include <linux/rculist.h>
-#include <linux/workqueue.h>
 #include <linux/dmi.h>
 #include <net/inet_ecn.h>
 #include <linux/compat.h>
index 1ac3b6e..01c6345 100644 (file)
@@ -4,39 +4,38 @@
 #include_next <linux/workqueue.h>
 
 #include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-
-#ifdef __KERNEL__
-/*
- * initialize a work-struct's func and data pointers:
- */
-#undef PREPARE_WORK
-#define PREPARE_WORK(_work, _func)                              \
-        do {                                                    \
-               (_work)->func = (void(*)(void*)) _func;         \
-                (_work)->data = _work;                         \
-        } while (0)
-
-/*
- * initialize all of a work-struct:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+
+/* Older kernels have an implementation of work queues with some very bad
+ * characteristics when trying to cancel work (potential deadlocks, use after
+ * free, etc.  Here we directly use timers instead for delayed work.  It's not
+ * optimal but it is better than the alternative.  Note that work queues
+ * normally run in process context but this will cause them to operate in
+ * softirq context.
  */
-#undef INIT_WORK
-#define INIT_WORK(_work, _func)                                 \
-        do {                                                    \
-                INIT_LIST_HEAD(&(_work)->entry);                \
-                (_work)->pending = 0;                           \
-                PREPARE_WORK((_work), (_func));                 \
-                init_timer(&(_work)->timer);                    \
-        } while (0)
-
-#endif /* __KERNEL__ */
-
-#endif /* linux kernel < 2.6.20 */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
-/* There is no equivalent to cancel_work_sync() so just flush all
- * pending work. */
-#define cancel_work_sync(_work) flush_scheduled_work()
-#endif
+
+#include <linux/timer.h>
+
+#undef DECLARE_DELAYED_WORK
+#define DECLARE_DELAYED_WORK(n, f) \
+       struct timer_list n = TIMER_INITIALIZER((void (*)(unsigned long))f, 0, 0)
+
+#define schedule_delayed_work rpl_schedule_delayed_work
+static inline int schedule_delayed_work(struct timer_list *timer, unsigned long delay)
+{
+       if (timer_pending(timer))
+               return 0;
+
+       mod_timer(timer, jiffies + delay);
+       return 1;
+}
+
+#define cancel_delayed_work_sync rpl_cancel_delayed_work_sync
+static inline int cancel_delayed_work_sync(struct timer_list *timer)
+{
+       return del_timer_sync(timer);
+}
+
+#endif /* kernel version < 2.6.23 */
 
 #endif