From 7c79397fe8ff52eb9cfe4d32a9d1f8b2f23c45c8 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 31 Aug 2010 15:38:25 -0700 Subject: [PATCH] datapath: Backport workqueue functions. 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 Reviewed-by: Ben Pfaff --- datapath/datapath.c | 1 - .../compat-2.6/include/linux/workqueue.h | 65 +++++++++---------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/datapath/datapath.c b/datapath/datapath.c index 5996d6ed..e9f30f8a 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include diff --git a/datapath/linux-2.6/compat-2.6/include/linux/workqueue.h b/datapath/linux-2.6/compat-2.6/include/linux/workqueue.h index 1ac3b6ec..01c6345e 100644 --- a/datapath/linux-2.6/compat-2.6/include/linux/workqueue.h +++ b/datapath/linux-2.6/compat-2.6/include/linux/workqueue.h @@ -4,39 +4,38 @@ #include_next #include -#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 + +#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 -- 2.30.2