[PKTGEN]: Updates version.
[linux-block.git] / net / core / pktgen.c
index e49b006a76540855d3247a9732b5a41a1d929ca8..8eedaedba743d69ae71693d5632438bc437a5a95 100644 (file)
 #include <linux/moduleparam.h>
 #include <linux/kernel.h>
 #include <linux/smp_lock.h>
+#include <linux/mutex.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/capability.h>
 #include <linux/delay.h>
 #include <linux/timer.h>
+#include <linux/list.h>
 #include <linux/init.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <asm/div64.h>         /* do_div */
 #include <asm/timex.h>
 
-#define VERSION  "pktgen v2.64: Packet Generator for packet performance testing.\n"
+#define VERSION  "pktgen v2.66: Packet Generator for packet performance testing.\n"
 
 /* #define PG_DEBUG(a) a */
 #define PG_DEBUG(a)
 #define T_REMDEVALL   (1<<3)   /* Remove all devs */
 #define T_REMDEV      (1<<4)   /* Remove one dev */
 
-/* Locks */
-#define   thread_lock()        down(&pktgen_sem)
-#define   thread_unlock()      up(&pktgen_sem)
-
 /* If lock -- can be removed after some work */
 #define   if_lock(t)           spin_lock(&(t->if_lock));
 #define   if_unlock(t)           spin_unlock(&(t->if_lock));
@@ -209,7 +207,7 @@ struct pktgen_dev {
        char result[512];
 
        struct pktgen_thread *pg_thread;        /* the owner */
-       struct pktgen_dev *next;        /* Used for chaining in the thread's run-queue */
+       struct list_head list;          /* Used for chaining in the thread's run-queue */
 
        int running;            /* if this changes to false, the test will stop */
 
@@ -329,8 +327,9 @@ struct pktgen_hdr {
 
 struct pktgen_thread {
        spinlock_t if_lock;
-       struct pktgen_dev *if_list;     /* All device here */
-       struct pktgen_thread *next;
+       struct list_head if_list;       /* All device here */
+       struct list_head th_list;
+       int removed;
        char name[32];
        char result[512];
        u32 max_before_softirq; /* We'll call do_softirq to prevent starvation. */
@@ -491,8 +490,8 @@ static int pg_delay_d;
 static int pg_clone_skb_d;
 static int debug;
 
-static DECLARE_MUTEX(pktgen_sem);
-static struct pktgen_thread *pktgen_threads = NULL;
+static DEFINE_MUTEX(pktgen_thread_lock);
+static LIST_HEAD(pktgen_threads);
 
 static struct notifier_block pktgen_notifier_block = {
        .notifier_call = pktgen_device_event,
@@ -1376,7 +1375,7 @@ static struct file_operations pktgen_if_fops = {
 static int pktgen_thread_show(struct seq_file *seq, void *v)
 {
        struct pktgen_thread *t = seq->private;
-       struct pktgen_dev *pkt_dev = NULL;
+       struct pktgen_dev *pkt_dev;
 
        BUG_ON(!t);
 
@@ -1386,13 +1385,13 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)
        seq_printf(seq, "Running: ");
 
        if_lock(t);
-       for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next)
+       list_for_each_entry(pkt_dev, &t->if_list, list)
                if (pkt_dev->running)
                        seq_printf(seq, "%s ", pkt_dev->ifname);
 
        seq_printf(seq, "\nStopped: ");
 
-       for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next)
+       list_for_each_entry(pkt_dev, &t->if_list, list)
                if (!pkt_dev->running)
                        seq_printf(seq, "%s ", pkt_dev->ifname);
 
@@ -1469,18 +1468,18 @@ static ssize_t pktgen_thread_write(struct file *file,
                if (copy_from_user(f, &user_buffer[i], len))
                        return -EFAULT;
                i += len;
-               thread_lock();
+               mutex_lock(&pktgen_thread_lock);
                pktgen_add_device(t, f);
-               thread_unlock();
+               mutex_unlock(&pktgen_thread_lock);
                ret = count;
                sprintf(pg_result, "OK: add_device=%s", f);
                goto out;
        }
 
        if (!strcmp(name, "rem_device_all")) {
-               thread_lock();
+               mutex_lock(&pktgen_thread_lock);
                t->control |= T_REMDEVALL;
-               thread_unlock();
+               mutex_unlock(&pktgen_thread_lock);
                schedule_timeout_interruptible(msecs_to_jiffies(125));  /* Propagate thread->control  */
                ret = count;
                sprintf(pg_result, "OK: rem_device_all");
@@ -1489,9 +1488,9 @@ static ssize_t pktgen_thread_write(struct file *file,
 
        if (!strcmp(name, "max_before_softirq")) {
                len = num_arg(&user_buffer[i], 10, &value);
-               thread_lock();
+               mutex_lock(&pktgen_thread_lock);
                t->max_before_softirq = value;
-               thread_unlock();
+               mutex_unlock(&pktgen_thread_lock);
                ret = count;
                sprintf(pg_result, "OK: max_before_softirq=%lu", value);
                goto out;
@@ -1522,9 +1521,7 @@ static struct pktgen_dev *__pktgen_NN_threads(const char *ifname, int remove)
        struct pktgen_thread *t;
        struct pktgen_dev *pkt_dev = NULL;
 
-       t = pktgen_threads;
-
-       while (t) {
+       list_for_each_entry(t, &pktgen_threads, th_list) {
                pkt_dev = pktgen_find_dev(t, ifname);
                if (pkt_dev) {
                        if (remove) {
@@ -1535,7 +1532,6 @@ static struct pktgen_dev *__pktgen_NN_threads(const char *ifname, int remove)
                        }
                        break;
                }
-               t = t->next;
        }
        return pkt_dev;
 }
@@ -1550,7 +1546,7 @@ static int pktgen_mark_device(const char *ifname)
        int i = 0;
        int ret = 0;
 
-       thread_lock();
+       mutex_lock(&pktgen_thread_lock);
        PG_DEBUG(printk("pktgen: pktgen_mark_device marking %s for removal\n",
                        ifname));
 
@@ -1560,11 +1556,11 @@ static int pktgen_mark_device(const char *ifname)
                if (pkt_dev == NULL)
                        break;  /* success */
 
-               thread_unlock();
+               mutex_unlock(&pktgen_thread_lock);
                PG_DEBUG(printk("pktgen: pktgen_mark_device waiting for %s "
                                "to disappear....\n", ifname));
                schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try));
-               thread_lock();
+               mutex_lock(&pktgen_thread_lock);
 
                if (++i >= max_tries) {
                        printk("pktgen_mark_device: timed out after waiting "
@@ -1576,7 +1572,7 @@ static int pktgen_mark_device(const char *ifname)
 
        }
 
-       thread_unlock();
+       mutex_unlock(&pktgen_thread_lock);
 
        return ret;
 }
@@ -2422,13 +2418,13 @@ static void pktgen_clear_counters(struct pktgen_dev *pkt_dev)
 
 static void pktgen_run(struct pktgen_thread *t)
 {
-       struct pktgen_dev *pkt_dev = NULL;
+       struct pktgen_dev *pkt_dev;
        int started = 0;
 
        PG_DEBUG(printk("pktgen: entering pktgen_run. %p\n", t));
 
        if_lock(t);
-       for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next) {
+       list_for_each_entry(pkt_dev, &t->if_list, list) {
 
                /*
                 * setup odev and create initial packet.
@@ -2455,29 +2451,28 @@ static void pktgen_run(struct pktgen_thread *t)
 
 static void pktgen_stop_all_threads_ifs(void)
 {
-       struct pktgen_thread *t = pktgen_threads;
+       struct pktgen_thread *t;
 
        PG_DEBUG(printk("pktgen: entering pktgen_stop_all_threads_ifs.\n"));
 
-       thread_lock();
-       while (t) {
+       mutex_lock(&pktgen_thread_lock);
+
+       list_for_each_entry(t, &pktgen_threads, th_list)
                t->control |= T_STOP;
-               t = t->next;
-       }
-       thread_unlock();
+
+       mutex_unlock(&pktgen_thread_lock);
 }
 
 static int thread_is_running(struct pktgen_thread *t)
 {
-       struct pktgen_dev *next;
+       struct pktgen_dev *pkt_dev;
        int res = 0;
 
-       for (next = t->if_list; next; next = next->next) {
-               if (next->running) {
+       list_for_each_entry(pkt_dev, &t->if_list, list)
+               if (pkt_dev->running) {
                        res = 1;
                        break;
                }
-       }
        return res;
 }
 
@@ -2503,41 +2498,37 @@ signal:
 
 static int pktgen_wait_all_threads_run(void)
 {
-       struct pktgen_thread *t = pktgen_threads;
+       struct pktgen_thread *t;
        int sig = 1;
 
-       while (t) {
+       mutex_lock(&pktgen_thread_lock);
+
+       list_for_each_entry(t, &pktgen_threads, th_list) {
                sig = pktgen_wait_thread_run(t);
                if (sig == 0)
                        break;
-               thread_lock();
-               t = t->next;
-               thread_unlock();
        }
-       if (sig == 0) {
-               thread_lock();
-               while (t) {
+
+       if (sig == 0)
+               list_for_each_entry(t, &pktgen_threads, th_list)
                        t->control |= (T_STOP);
-                       t = t->next;
-               }
-               thread_unlock();
-       }
+
+       mutex_unlock(&pktgen_thread_lock);
        return sig;
 }
 
 static void pktgen_run_all_threads(void)
 {
-       struct pktgen_thread *t = pktgen_threads;
+       struct pktgen_thread *t;
 
        PG_DEBUG(printk("pktgen: entering pktgen_run_all_threads.\n"));
 
-       thread_lock();
+       mutex_lock(&pktgen_thread_lock);
 
-       while (t) {
+       list_for_each_entry(t, &pktgen_threads, th_list)
                t->control |= (T_RUN);
-               t = t->next;
-       }
-       thread_unlock();
+
+       mutex_unlock(&pktgen_thread_lock);
 
        schedule_timeout_interruptible(msecs_to_jiffies(125));  /* Propagate thread->control  */
 
@@ -2602,17 +2593,17 @@ static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
 
 static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
 {
-       struct pktgen_dev *next, *best = NULL;
+       struct pktgen_dev *pkt_dev, *best = NULL;
 
        if_lock(t);
 
-       for (next = t->if_list; next; next = next->next) {
-               if (!next->running)
+       list_for_each_entry(pkt_dev, &t->if_list, list) {
+               if (!pkt_dev->running)
                        continue;
                if (best == NULL)
-                       best = next;
-               else if (next->next_tx_us < best->next_tx_us)
-                       best = next;
+                       best = pkt_dev;
+               else if (pkt_dev->next_tx_us < best->next_tx_us)
+                       best = pkt_dev;
        }
        if_unlock(t);
        return best;
@@ -2620,18 +2611,18 @@ static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
 
 static void pktgen_stop(struct pktgen_thread *t)
 {
-       struct pktgen_dev *next = NULL;
+       struct pktgen_dev *pkt_dev;
 
        PG_DEBUG(printk("pktgen: entering pktgen_stop\n"));
 
        if_lock(t);
 
-       for (next = t->if_list; next; next = next->next) {
-               pktgen_stop_device(next);
-               if (next->skb)
-                       kfree_skb(next->skb);
+       list_for_each_entry(pkt_dev, &t->if_list, list) {
+               pktgen_stop_device(pkt_dev);
+               if (pkt_dev->skb)
+                       kfree_skb(pkt_dev->skb);
 
-               next->skb = NULL;
+               pkt_dev->skb = NULL;
        }
 
        if_unlock(t);
@@ -2643,14 +2634,15 @@ static void pktgen_stop(struct pktgen_thread *t)
  */
 static void pktgen_rem_one_if(struct pktgen_thread *t)
 {
-       struct pktgen_dev *cur, *next = NULL;
+       struct list_head *q, *n;
+       struct pktgen_dev *cur;
 
        PG_DEBUG(printk("pktgen: entering pktgen_rem_one_if\n"));
 
        if_lock(t);
 
-       for (cur = t->if_list; cur; cur = next) {
-               next = cur->next;
+       list_for_each_safe(q, n, &t->if_list) {
+               cur = list_entry(q, struct pktgen_dev, list);
 
                if (!cur->removal_mark)
                        continue;
@@ -2669,15 +2661,16 @@ static void pktgen_rem_one_if(struct pktgen_thread *t)
 
 static void pktgen_rem_all_ifs(struct pktgen_thread *t)
 {
-       struct pktgen_dev *cur, *next = NULL;
+       struct list_head *q, *n;
+       struct pktgen_dev *cur;
 
        /* Remove all devices, free mem */
 
        PG_DEBUG(printk("pktgen: entering pktgen_rem_all_ifs\n"));
        if_lock(t);
 
-       for (cur = t->if_list; cur; cur = next) {
-               next = cur->next;
+       list_for_each_safe(q, n, &t->if_list) {
+               cur = list_entry(q, struct pktgen_dev, list);
 
                if (cur->skb)
                        kfree_skb(cur->skb);
@@ -2693,25 +2686,13 @@ static void pktgen_rem_thread(struct pktgen_thread *t)
 {
        /* Remove from the thread list */
 
-       struct pktgen_thread *tmp = pktgen_threads;
-
        remove_proc_entry(t->name, pg_proc_dir);
 
-       thread_lock();
+       mutex_lock(&pktgen_thread_lock);
 
-       if (tmp == t)
-               pktgen_threads = tmp->next;
-       else {
-               while (tmp) {
-                       if (tmp->next == t) {
-                               tmp->next = t->next;
-                               t->next = NULL;
-                               break;
-                       }
-                       tmp = tmp->next;
-               }
-       }
-       thread_unlock();
+       list_del(&t->th_list);
+
+       mutex_unlock(&pktgen_thread_lock);
 }
 
 static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
@@ -2969,19 +2950,21 @@ static void pktgen_thread_worker(struct pktgen_thread *t)
 
        PG_DEBUG(printk("pktgen: %s removing thread.\n", t->name));
        pktgen_rem_thread(t);
+
+       t->removed = 1;
 }
 
 static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
                                          const char *ifname)
 {
-       struct pktgen_dev *pkt_dev = NULL;
+       struct pktgen_dev *p, *pkt_dev = NULL;
        if_lock(t);
 
-       for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next) {
-               if (strncmp(pkt_dev->ifname, ifname, IFNAMSIZ) == 0) {
+       list_for_each_entry(p, &t->if_list, list)
+               if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0) {
+                       pkt_dev = p;
                        break;
                }
-       }
 
        if_unlock(t);
        PG_DEBUG(printk("pktgen: find_dev(%s) returning %p\n", ifname, pkt_dev));
@@ -3004,8 +2987,8 @@ static int add_dev_to_thread(struct pktgen_thread *t,
                rv = -EBUSY;
                goto out;
        }
-       pkt_dev->next = t->if_list;
-       t->if_list = pkt_dev;
+
+       list_add(&pkt_dev->list, &t->if_list);
        pkt_dev->pg_thread = t;
        pkt_dev->running = 0;
 
@@ -3081,23 +3064,23 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
 
 static struct pktgen_thread *__init pktgen_find_thread(const char *name)
 {
-       struct pktgen_thread *t = NULL;
+       struct pktgen_thread *t;
 
-       thread_lock();
+       mutex_lock(&pktgen_thread_lock);
 
-       t = pktgen_threads;
-       while (t) {
-               if (strcmp(t->name, name) == 0)
-                       break;
+       list_for_each_entry(t, &pktgen_threads, th_list)
+               if (strcmp(t->name, name) == 0) {
+                       mutex_unlock(&pktgen_thread_lock);
+                       return t;
+               }
 
-               t = t->next;
-       }
-       thread_unlock();
-       return t;
+       mutex_unlock(&pktgen_thread_lock);
+       return NULL;
 }
 
 static int __init pktgen_create_thread(const char *name, int cpu)
 {
+       int err;
        struct pktgen_thread *t = NULL;
        struct proc_dir_entry *pe;
 
@@ -3132,12 +3115,21 @@ static int __init pktgen_create_thread(const char *name, int cpu)
        pe->proc_fops = &pktgen_thread_fops;
        pe->data = t;
 
-       t->next = pktgen_threads;
-       pktgen_threads = t;
+       INIT_LIST_HEAD(&t->if_list);
+
+       list_add_tail(&t->th_list, &pktgen_threads);
 
-       if (kernel_thread((void *)pktgen_thread_worker, (void *)t,
-                         CLONE_FS | CLONE_FILES | CLONE_SIGHAND) < 0)
+       t->removed = 0;
+
+       err = kernel_thread((void *)pktgen_thread_worker, (void *)t,
+                         CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+       if (err < 0) {
                printk("pktgen: kernel_thread() failed for cpu %d\n", t->cpu);
+               remove_proc_entry(t->name, pg_proc_dir);
+               list_del(&t->th_list);
+               kfree(t);
+               return err;
+       }
 
        return 0;
 }
@@ -3148,20 +3140,13 @@ static int __init pktgen_create_thread(const char *name, int cpu)
 static void _rem_dev_from_if_list(struct pktgen_thread *t,
                                  struct pktgen_dev *pkt_dev)
 {
-       struct pktgen_dev *i, *prev = NULL;
-
-       i = t->if_list;
+       struct list_head *q, *n;
+       struct pktgen_dev *p;
 
-       while (i) {
-               if (i == pkt_dev) {
-                       if (prev)
-                               prev->next = i->next;
-                       else
-                               t->if_list = NULL;
-                       break;
-               }
-               prev = i;
-               i = i->next;
+       list_for_each_safe(q, n, &t->if_list) {
+               p = list_entry(q, struct pktgen_dev, list);
+               if (p == pkt_dev)
+                       list_del(&p->list);
        }
 }
 
@@ -3224,27 +3209,41 @@ static int __init pg_init(void)
        register_netdevice_notifier(&pktgen_notifier_block);
 
        for_each_online_cpu(cpu) {
+               int err;
                char buf[30];
 
                sprintf(buf, "kpktgend_%i", cpu);
-               pktgen_create_thread(buf, cpu);
+               err = pktgen_create_thread(buf, cpu);
+               if (err)
+                       printk("pktgen: WARNING: Cannot create thread for cpu %d (%d)\n",
+                                       cpu, err);
        }
+
+       if (list_empty(&pktgen_threads)) {
+               printk("pktgen: ERROR: Initialization failed for all threads\n");
+               unregister_netdevice_notifier(&pktgen_notifier_block);
+               remove_proc_entry(PGCTRL, pg_proc_dir);
+               proc_net_remove(PG_PROC_DIR);
+               return -ENODEV;
+       }
+
        return 0;
 }
 
 static void __exit pg_cleanup(void)
 {
+       struct pktgen_thread *t;
+       struct list_head *q, *n;
        wait_queue_head_t queue;
        init_waitqueue_head(&queue);
 
        /* Stop all interfaces & threads */
 
-       while (pktgen_threads) {
-               struct pktgen_thread *t = pktgen_threads;
-               pktgen_threads->control |= (T_TERMINATE);
+       list_for_each_safe(q, n, &pktgen_threads) {
+               t = list_entry(q, struct pktgen_thread, th_list);
+               t->control |= (T_TERMINATE);
 
-               wait_event_interruptible_timeout(queue, (t != pktgen_threads),
-                                                HZ);
+               wait_event_interruptible_timeout(queue, (t->removed == 1), HZ);
        }
 
        /* Un-register us from receiving netdevice events */