Merge "master" into "wdp".
[sliver-openvswitch.git] / datapath / linux-2.6 / compat-2.6 / include / linux / workqueue.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