Allow timeout implementations to block, by using a kernel thread instead of a timer.
authorBen Pfaff <blp@nicira.com>
Tue, 1 Apr 2008 16:41:21 +0000 (09:41 -0700)
committerBen Pfaff <blp@nicira.com>
Tue, 1 Apr 2008 20:01:07 +0000 (13:01 -0700)
This is required on the Quanta platform, which needs to down a semaphore
inside the timeout implementation.

Also, add a 2.4 compatibility implementation of kthread so that both
2.4 and 2.6 can use the kthread API.

datapath/datapath.c
datapath/datapath.h
datapath/linux-2.4/compat-2.4/compat24.c
datapath/linux-2.4/compat-2.4/compat24.h
datapath/linux-2.4/compat-2.4/include/linux/delay.h
datapath/linux-2.4/compat-2.4/include/linux/kthread.h [new file with mode: 0644]
datapath/linux-2.4/compat-2.4/kthread.c [new file with mode: 0644]

index f2aa8625a90d16069fbf21782435d24083bc630d..32e519105dd741895017131fa99997271ba02c5f 100644 (file)
 #include <linux/in.h>
 #include <net/genetlink.h>
 #include <linux/ip.h>
+#include <linux/delay.h>
 #include <linux/etherdevice.h>
 #include <linux/kernel.h>
+#include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/rtnetlink.h>
-#include <linux/timer.h>
 #include <linux/rcupdate.h>
 #include <linux/version.h>
 #include <linux/ethtool.h>
@@ -38,8 +39,8 @@
 #include "compat.h"
 
 
-/* Number of seconds between runs of the flow expiration code. */
-#define EXPIRE_SECS 1
+/* Number of milliseconds between runs of the maintenance thread. */
+#define MAINT_SLEEP_MSECS 1000
 
 #define BRIDGE_PORT_NO_FLOOD   0x00000001 
 
@@ -71,7 +72,7 @@ int dp_dev_setup(struct net_device *dev);
 static struct datapath *dps[DP_MAX];
 static DEFINE_MUTEX(dp_mutex);
 
-static void dp_timer_handler(unsigned long arg);
+static int dp_maint_func(void *data);
 static int send_port_status(struct net_bridge_port *p, uint8_t status);
 
 
@@ -174,8 +175,9 @@ static int new_dp(int dp_idx)
 
        dp->miss_send_len = OFP_DEFAULT_MISS_SEND_LEN;
 
-       setup_timer(&dp->timer, dp_timer_handler, (unsigned long) dp);
-       mod_timer(&dp->timer, round_jiffies(jiffies + (EXPIRE_SECS * HZ)));
+       dp->dp_task = kthread_run(dp_maint_func, dp, "dp%d", dp_idx);
+       if (IS_ERR(dp->dp_task))
+               goto err_free_dp;
 
        rcu_assign_pointer(dps[dp_idx], dp);
        mutex_unlock(&dp_mutex);
@@ -288,10 +290,11 @@ static void del_dp(struct datapath *dp)
        rtnl_unlock();
 #endif
 
+       kthread_stop(dp->dp_task);
+
        /* Drop references to DP. */
        list_for_each_entry_safe (p, n, &dp->port_list, node)
                del_switch_port(p);
-       del_timer_sync(&dp->timer);
        rcu_assign_pointer(dps[dp->dp_idx], NULL);
 
        /* Wait until no longer in use, then destroy it. */
@@ -301,18 +304,23 @@ static void del_dp(struct datapath *dp)
        module_put(THIS_MODULE);
 }
 
-static void dp_timer_handler(unsigned long arg)
+static int dp_maint_func(void *data)
 {
-       struct datapath *dp = (struct datapath *) arg;
+       struct datapath *dp = (struct datapath *) data;
+
+       while (!kthread_should_stop()) {
 #if 1
-       chain_timeout(dp->chain);
+               chain_timeout(dp->chain);
 #else
-       int count = chain_timeout(dp->chain);
-       chain_print_stats(dp->chain);
-       if (count)
-               printk("%d flows timed out\n", count);
+               int count = chain_timeout(dp->chain);
+               chain_print_stats(dp->chain);
+               if (count)
+                       printk("%d flows timed out\n", count);
 #endif
-       mod_timer(&dp->timer, round_jiffies(jiffies + (EXPIRE_SECS * HZ)));
+               msleep_interruptible(MAINT_SLEEP_MSECS);
+       }
+               
+       return 0;
 }
 
 /*
index cba2b7935103b7664447e02e60e2ca1e1358c8ac..b478eaef6747dbb41015288a6cdd5b2f03648225 100644 (file)
@@ -42,6 +42,7 @@ struct datapath {
 
        struct timer_list timer;        /* Expiration timer. */
        struct sw_chain *chain;  /* Forwarding rules. */
+       struct task_struct *dp_task; /* Kernel thread for maintenance. */
 
        /* Data related to the "of" device of this datapath */
        struct net_device dev;
index 13641ff16124dc2d818707ad54027ae7906575e9..bbacf311fdc77fd33c1e44c52f5035872ccda259 100644 (file)
@@ -15,6 +15,8 @@ int __init compat24_init(void)
        if (err)
                return err;
 
+       init_kthread();
+
        return genl_init();
 
 }
index 4e7038d80f00508fcf06bc3dbbd56e4607f1cc51..48f4ec8ea7eb4333220fd284782282759b0727e9 100644 (file)
@@ -6,6 +6,8 @@ void genl_exit(void);
 
 int random32_init(void);
 
+void init_kthread(void);
+
 void rcu_init(void);
 
 #endif /* compat24.h */
index 2db751874fbbf4126aabc6a417c0c67a4b4003e7..a50f96c0b1eb0a885b16c96a3aaac4e50d3a5e16 100644 (file)
@@ -56,4 +56,19 @@ static inline unsigned long msecs_to_jiffies(const unsigned int m)
 
 #endif /* linux kernel < 2.4.29 */
 
+/**
+ * msleep_interruptible - sleep waiting for waitqueue interruptions
+ * @msecs: Time in milliseconds to sleep for
+ */
+static inline unsigned long msleep_interruptible(unsigned int msecs)
+{
+       unsigned long timeout = msecs_to_jiffies(msecs);
+
+       while (timeout && !signal_pending(current)) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               timeout = schedule_timeout(timeout);
+       }
+       return jiffies_to_msecs(timeout);
+}
+
 #endif
diff --git a/datapath/linux-2.4/compat-2.4/include/linux/kthread.h b/datapath/linux-2.4/compat-2.4/include/linux/kthread.h
new file mode 100644 (file)
index 0000000..15432a7
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef _LINUX_KTHREAD_H
+#define _LINUX_KTHREAD_H
+/* Simple interface for creating and stopping kernel threads without mess. */
+#include <linux/sched.h>
+
+struct task_struct *kthread_create(int (*threadfn)(void *data),
+                                  void *data,
+                                  const char namefmt[], ...);
+
+/**
+ * kthread_run - create and wake a thread.
+ * @threadfn: the function to run until signal_pending(current).
+ * @data: data ptr for @threadfn.
+ * @namefmt: printf-style name for the thread.
+ *
+ * Description: Convenient wrapper for kthread_create() followed by
+ * wake_up_process().  Returns the kthread or ERR_PTR(-ENOMEM).
+ */
+#define kthread_run(threadfn, data, namefmt, ...)                         \
+({                                                                        \
+       struct task_struct *__k                                            \
+               = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
+       if (!IS_ERR(__k))                                                  \
+               wake_up_process(__k);                                      \
+       __k;                                                               \
+})
+
+int kthread_stop(struct task_struct *k);
+int kthread_should_stop(void);
+
+int kthreadd(void *unused);
+extern struct task_struct *kthreadd_task;
+
+#endif /* _LINUX_KTHREAD_H */
diff --git a/datapath/linux-2.4/compat-2.4/kthread.c b/datapath/linux-2.4/compat-2.4/kthread.c
new file mode 100644 (file)
index 0000000..afc3b23
--- /dev/null
@@ -0,0 +1,260 @@
+/* Kernel thread helper functions.
+ *   Copyright (C) 2004 IBM Corporation, Rusty Russell.
+ *
+ * Creation is done via kthreadd, so that we get a clean environment
+ * even if we're invoked from userspace (think modprobe, hotplug cpu,
+ * etc.).
+ */
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/completion.h>
+#include <linux/unistd.h>
+#include <linux/file.h>
+#include <linux/mutex.h>
+#include <asm/semaphore.h>
+#include <asm/smplock.h>
+#include "compat24.h"
+
+static DEFINE_SPINLOCK(kthread_create_lock);
+static LIST_HEAD(kthread_create_list);
+struct task_struct *kthreadd_task;
+
+struct kthread_create_info
+{
+       /* Information passed to kthread() from kthreadd. */
+       int (*threadfn)(void *data);
+       void *data;
+       struct completion started;
+
+       /* Result passed back to kthread_create() from kthreadd. */
+       struct task_struct *result;
+       struct completion done;
+
+       struct list_head list;
+};
+
+struct kthread_stop_info
+{
+       struct task_struct *k;
+       int err;
+       struct completion done;
+};
+
+/* Thread stopping is done by setthing this var: lock serializes
+ * multiple kthread_stop calls. */
+static DEFINE_MUTEX(kthread_stop_lock);
+static struct kthread_stop_info kthread_stop_info;
+
+/**
+ * kthread_should_stop - should this kthread return now?
+ *
+ * When someone calls kthread_stop() on your kthread, it will be woken
+ * and this will return true.  You should then return, and your return
+ * value will be passed through to kthread_stop().
+ */
+int kthread_should_stop(void)
+{
+       return (kthread_stop_info.k == current);
+}
+EXPORT_SYMBOL(kthread_should_stop);
+
+static int kthread(void *_create)
+{
+       struct kthread_create_info *create = _create;
+       int (*threadfn)(void *data);
+       void *data;
+       int ret = -EINTR;
+
+       /* Copy data: it's on kthread's stack */
+       threadfn = create->threadfn;
+       data = create->data;
+
+       /* OK, tell user we're spawned, wait for stop or wakeup */
+       __set_current_state(TASK_UNINTERRUPTIBLE);
+       complete(&create->started);
+       schedule();
+
+       if (!kthread_should_stop())
+               ret = threadfn(data);
+
+       /* It might have exited on its own, w/o kthread_stop.  Check. */
+       if (kthread_should_stop()) {
+               kthread_stop_info.err = ret;
+               complete(&kthread_stop_info.done);
+       }
+       return 0;
+}
+
+static void create_kthread(struct kthread_create_info *create)
+{
+       int pid;
+
+       /* We want our own signal handler (we take no signals by default). */
+       pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
+       if (pid < 0) {
+               create->result = ERR_PTR(pid);
+       } else {
+               wait_for_completion(&create->started);
+               read_lock(&tasklist_lock);
+               create->result = find_task_by_pid(pid);
+               read_unlock(&tasklist_lock);
+       }
+       complete(&create->done);
+}
+
+/**
+ * kthread_create - create a kthread.
+ * @threadfn: the function to run until signal_pending(current).
+ * @data: data ptr for @threadfn.
+ * @namefmt: printf-style name for the thread.
+ *
+ * Description: This helper function creates and names a kernel
+ * thread.  The thread will be stopped: use wake_up_process() to start
+ * it.  See also kthread_run(), kthread_create_on_cpu().
+ *
+ * When woken, the thread will run @threadfn() with @data as its
+ * argument. @threadfn() can either call do_exit() directly if it is a
+ * standalone thread for which noone will call kthread_stop(), or
+ * return when 'kthread_should_stop()' is true (which means
+ * kthread_stop() has been called).  The return value should be zero
+ * or a negative error number; it will be passed to kthread_stop().
+ *
+ * Returns a task_struct or ERR_PTR(-ENOMEM).
+ */
+struct task_struct *kthread_create(int (*threadfn)(void *data),
+                                  void *data,
+                                  const char namefmt[],
+                                  ...)
+{
+       struct kthread_create_info create;
+
+       create.threadfn = threadfn;
+       create.data = data;
+       init_completion(&create.started);
+       init_completion(&create.done);
+
+       spin_lock(&kthread_create_lock);
+       list_add_tail(&create.list, &kthread_create_list);
+       wake_up_process(kthreadd_task);
+       spin_unlock(&kthread_create_lock);
+
+       wait_for_completion(&create.done);
+
+       if (!IS_ERR(create.result)) {
+               va_list args;
+               va_start(args, namefmt);
+               vsnprintf(create.result->comm, sizeof(create.result->comm),
+                         namefmt, args);
+               va_end(args);
+       }
+       return create.result;
+}
+EXPORT_SYMBOL(kthread_create);
+
+/**
+ * kthread_stop - stop a thread created by kthread_create().
+ * @k: thread created by kthread_create().
+ *
+ * Sets kthread_should_stop() for @k to return true, wakes it, and
+ * waits for it to exit.  Your threadfn() must not call do_exit()
+ * itself if you use this function!  This can also be called after
+ * kthread_create() instead of calling wake_up_process(): the thread
+ * will exit without calling threadfn().
+ *
+ * Returns the result of threadfn(), or %-EINTR if wake_up_process()
+ * was never called.
+ */
+int kthread_stop(struct task_struct *k)
+{
+       int ret;
+
+       mutex_lock(&kthread_stop_lock);
+
+       /* Thread exit requires BKL, so prevent it while while we're messing
+        * with the thread. */
+       lock_kernel();
+
+       /* Must init completion *before* thread sees kthread_stop_info.k */
+       init_completion(&kthread_stop_info.done);
+       smp_wmb();
+
+       /* Now set kthread_should_stop() to true, and wake it up. */
+       kthread_stop_info.k = k;
+       wake_up_process(k);
+
+       unlock_kernel();
+
+       /* Once it dies, reset stop ptr, gather result and we're done. */
+       wait_for_completion(&kthread_stop_info.done);
+       kthread_stop_info.k = NULL;
+       ret = kthread_stop_info.err;
+       mutex_unlock(&kthread_stop_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(kthread_stop);
+
+void ignore_signals(struct task_struct *t)
+{
+       int i;
+
+       for (i = 0; i < _NSIG; ++i)
+               t->sig->action[i].sa.sa_handler = SIG_IGN;
+
+       flush_signals(t);
+}
+
+/* This is a copy of the set_task_comm() function, which is not exported to
+ * modules. */
+static void do_set_task_comm(struct task_struct *tsk, char *buf)
+{
+       task_lock(tsk);
+       strncpy(tsk->comm, buf, sizeof(tsk->comm));
+       tsk->comm[sizeof(tsk->comm)-1]='\0';
+       task_unlock(tsk);
+}
+
+int kthreadd(void *unused)
+{
+       struct task_struct *tsk = current;
+
+       /* Setup a clean context for our children to inherit. */
+       do_set_task_comm(tsk, "kthreadd");
+       ignore_signals(tsk);
+       tsk->nice = -5;
+       set_cpus_allowed(tsk, CPU_MASK_ALL);
+
+       for (;;) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (list_empty(&kthread_create_list))
+                       schedule();
+               __set_current_state(TASK_RUNNING);
+
+               spin_lock(&kthread_create_lock);
+               while (!list_empty(&kthread_create_list)) {
+                       struct kthread_create_info *create;
+
+                       create = list_entry(kthread_create_list.next,
+                                           struct kthread_create_info, list);
+                       list_del_init(&create->list);
+                       spin_unlock(&kthread_create_lock);
+
+                       create_kthread(create);
+
+                       spin_lock(&kthread_create_lock);
+               }
+               spin_unlock(&kthread_create_lock);
+       }
+
+       return 0;
+}
+
+void init_kthread(void) 
+{
+       int pid;
+
+        pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
+        kthreadd_task = find_task_by_pid(pid);
+}