datapath: Fix OVS compat workqueue support.
[openvswitch] / datapath / linux / compat / include / linux / workqueue.h
index 01c6345e9370d3e57986e33dfd9781c1c218d590..919afe350b1036657f5221e563014bd36bf0d1f8 100644 (file)
@@ -1,41 +1,73 @@
 #ifndef __LINUX_WORKQUEUE_WRAPPER_H
 #define __LINUX_WORKQUEUE_WRAPPER_H 1
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
 #include_next <linux/workqueue.h>
+static inline int __init ovs_workqueues_init(void) { return 0; }
+static inline void  ovs_workqueues_exit(void) {}
+
+#else
+#include <linux/timer.h>
+
+int __init ovs_workqueues_init(void);
+void ovs_workqueues_exit(void);
 
-#include <linux/version.h>
-#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.
+ * free, etc.  Therefore we implement simple ovs specific work queue using
+ * single worker thread. work-queue API are kept similar for compatibility.
  */
+struct work_struct;
 
-#include <linux/timer.h>
+typedef void (*work_func_t)(struct work_struct *work);
 
-#undef DECLARE_DELAYED_WORK
-#define DECLARE_DELAYED_WORK(n, f) \
-       struct timer_list n = TIMER_INITIALIZER((void (*)(unsigned long))f, 0, 0)
+#define work_data_bits(work) ((unsigned long *)(&(work)->data))
 
-#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;
+struct work_struct {
+#define WORK_STRUCT_PENDING 0           /* T if work item pending execution */
+       atomic_long_t data;
+       struct list_head entry;
+       work_func_t func;
+};
+
+#define WORK_DATA_INIT()        ATOMIC_LONG_INIT(0)
+
+#define work_clear_pending(work)                               \
+       clear_bit(WORK_STRUCT_PENDING, work_data_bits(work))
+
+struct delayed_work {
+       struct work_struct work;
+       struct timer_list timer;
+};
 
-       mod_timer(timer, jiffies + delay);
-       return 1;
+#define __WORK_INITIALIZER(n, f) {                             \
+       .data = WORK_DATA_INIT(),                               \
+       .entry  = { &(n).entry, &(n).entry },                   \
+       .func = (f),                                            \
 }
 
-#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);
+#define __DELAYED_WORK_INITIALIZER(n, f) {                     \
+       .work = __WORK_INITIALIZER((n).work, (f)),              \
+       .timer = TIMER_INITIALIZER(NULL, 0, 0),                 \
 }
 
+#define DECLARE_DELAYED_WORK(n, f)                             \
+       struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f)
+
+#define schedule_delayed_work rpl_schedule_delayed_work
+int schedule_delayed_work(struct delayed_work *dwork, unsigned long delay);
+
+#define cancel_delayed_work_sync rpl_cancel_delayed_work_sync
+int cancel_delayed_work_sync(struct delayed_work *dwork);
+
+#define INIT_WORK(_work, _func)                                        \
+       do {                                                    \
+               (_work)->data = (atomic_long_t) WORK_DATA_INIT();       \
+               INIT_LIST_HEAD(&(_work)->entry);                \
+               (_work)->func = (_func);                        \
+       } while (0)
+
 #endif /* kernel version < 2.6.23 */
 
 #endif