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 5996d6ed7e104b54fd9ec8370828372c06bb5ef3..e9f30f8a81da2bed3b2a2e58b0ca72f633512556 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 1ac3b6ecb27b480937287e1d8d76a582ee2ea2c5..01c6345e9370d3e57986e33dfd9781c1c218d590 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