]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/core/pktgen.c
Pull acpica-20060707 into test branch
[linux-2.6-omap-h63xx.git] / net / core / pktgen.c
index eef1392b7f8e3e62170ec0df8927dc6b5e1ae0ec..67ed14ddabd2b83a32bef68b002c67298bcc2f42 100644 (file)
  *
  * interruptible_sleep_on_timeout() replaced Nishanth Aravamudan <nacc@us.ibm.com> 
  * 050103
+ *
+ * MPLS support by Steven Whitehouse <steve@chygwyn.com>
+ *
  */
 #include <linux/sys.h>
 #include <linux/types.h>
 #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 <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.67: Packet Generator for packet performance testing.\n"
 
 /* #define PG_DEBUG(a) a */
 #define PG_DEBUG(a)
 /* The buckets are exponential in 'width' */
 #define LAT_BUCKETS_MAX 32
 #define IP_NAME_SZ 32
+#define MAX_MPLS_LABELS 16 /* This is the max label stack depth */
+#define MPLS_STACK_BOTTOM __constant_htonl(0x00000100)
 
 /* Device flag bits */
 #define F_IPSRC_RND   (1<<0)   /* IP-Src Random  */
 #define F_MACDST_RND  (1<<5)   /* MAC-Dst Random */
 #define F_TXSIZE_RND  (1<<6)   /* Transmit size is random */
 #define F_IPV6        (1<<7)   /* Interface in IPV6 Mode */
+#define F_MPLS_RND    (1<<8)   /* Random MPLS labels */
 
 /* Thread control flag bits */
 #define T_TERMINATE   (1<<0)
 #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));
@@ -210,7 +213,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 */
 
@@ -281,6 +284,10 @@ struct pktgen_dev {
        __u16 udp_dst_min;      /* inclusive, dest UDP port */
        __u16 udp_dst_max;      /* exclusive, dest UDP port */
 
+       /* MPLS */
+       unsigned nr_labels;     /* Depth of stack, 0 = no MPLS */
+       __be32 labels[MAX_MPLS_LABELS];
+
        __u32 src_mac_count;    /* How many MACs to iterate through */
        __u32 dst_mac_count;    /* How many MACs to iterate through */
 
@@ -330,7 +337,7 @@ struct pktgen_hdr {
 
 struct pktgen_thread {
        spinlock_t if_lock;
-       struct pktgen_dev *if_list;     /* All device here */
+       struct list_head if_list;       /* All device here */
        struct list_head th_list;
        int removed;
        char name[32];
@@ -493,7 +500,7 @@ static int pg_delay_d;
 static int pg_clone_skb_d;
 static int debug;
 
-static DECLARE_MUTEX(pktgen_sem);
+static DEFINE_MUTEX(pktgen_thread_lock);
 static LIST_HEAD(pktgen_threads);
 
 static struct notifier_block pktgen_notifier_block = {
@@ -626,9 +633,19 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
                   pkt_dev->udp_dst_min, pkt_dev->udp_dst_max);
 
        seq_printf(seq,
-                  "     src_mac_count: %d  dst_mac_count: %d \n     Flags: ",
+                  "     src_mac_count: %d  dst_mac_count: %d\n",
                   pkt_dev->src_mac_count, pkt_dev->dst_mac_count);
 
+       if (pkt_dev->nr_labels) {
+               unsigned i;
+               seq_printf(seq, "     mpls: ");
+               for(i = 0; i < pkt_dev->nr_labels; i++)
+                       seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]),
+                                  i == pkt_dev->nr_labels-1 ? "\n" : ", ");
+       }
+
+       seq_printf(seq, "     Flags: ");
+
        if (pkt_dev->flags & F_IPV6)
                seq_printf(seq, "IPV6  ");
 
@@ -647,6 +664,9 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
        if (pkt_dev->flags & F_UDPDST_RND)
                seq_printf(seq, "UDPDST_RND  ");
 
+       if (pkt_dev->flags & F_MPLS_RND)
+               seq_printf(seq,  "MPLS_RND  ");
+
        if (pkt_dev->flags & F_MACSRC_RND)
                seq_printf(seq, "MACSRC_RND  ");
 
@@ -694,6 +714,29 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
        return 0;
 }
 
+
+static int hex32_arg(const char __user *user_buffer, __u32 *num)
+{
+       int i = 0;
+       *num = 0;
+
+       for(; i < 8; i++) {
+               char c;
+               *num <<= 4;
+               if (get_user(c, &user_buffer[i]))
+                       return -EFAULT;
+               if ((c >= '0') && (c <= '9'))
+                       *num |= c - '0';
+               else if ((c >= 'a') && (c <= 'f'))
+                       *num |= c - 'a' + 10;
+               else if ((c >= 'A') && (c <= 'F'))
+                       *num |= c - 'A' + 10;
+               else
+                       break;
+       }
+       return i;
+}
+
 static int count_trail_chars(const char __user * user_buffer,
                             unsigned int maxlen)
 {
@@ -762,6 +805,35 @@ done_str:
        return i;
 }
 
+static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
+{
+       unsigned n = 0;
+       char c;
+       ssize_t i = 0;
+       int len;
+
+       pkt_dev->nr_labels = 0;
+       do {
+               __u32 tmp;
+               len = hex32_arg(&buffer[i], &tmp);
+               if (len <= 0)
+                       return len;
+               pkt_dev->labels[n] = htonl(tmp);
+               if (pkt_dev->labels[n] & MPLS_STACK_BOTTOM)
+                       pkt_dev->flags |= F_MPLS_RND;
+               i += len;
+               if (get_user(c, &buffer[i]))
+                       return -EFAULT;
+               i++;
+               n++;
+               if (n >= MAX_MPLS_LABELS)
+                       return -E2BIG;
+       } while(c == ',');
+
+       pkt_dev->nr_labels = n;
+       return i;
+}
+
 static ssize_t pktgen_if_write(struct file *file,
                               const char __user * user_buffer, size_t count,
                               loff_t * offset)
@@ -1062,6 +1134,12 @@ static ssize_t pktgen_if_write(struct file *file,
                else if (strcmp(f, "!MACDST_RND") == 0)
                        pkt_dev->flags &= ~F_MACDST_RND;
 
+               else if (strcmp(f, "MPLS_RND") == 0)
+                       pkt_dev->flags |= F_MPLS_RND;
+
+               else if (strcmp(f, "!MPLS_RND") == 0)
+                       pkt_dev->flags &= ~F_MPLS_RND;
+
                else {
                        sprintf(pg_result,
                                "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
@@ -1357,6 +1435,19 @@ static ssize_t pktgen_if_write(struct file *file,
                return count;
        }
 
+       if (!strcmp(name, "mpls")) {
+               unsigned n, offset;
+               len = get_labels(&user_buffer[i], pkt_dev);
+               if (len < 0) { return len; }
+               i += len;
+               offset = sprintf(pg_result, "OK: mpls=");
+               for(n = 0; n < pkt_dev->nr_labels; n++)
+                       offset += sprintf(pg_result + offset,
+                                         "%08x%s", ntohl(pkt_dev->labels[n]),
+                                         n == pkt_dev->nr_labels-1 ? "" : ",");
+               return count;
+       }
+
        sprintf(pkt_dev->result, "No such parameter \"%s\"", name);
        return -EINVAL;
 }
@@ -1378,7 +1469,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);
 
@@ -1388,13 +1479,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);
 
@@ -1471,18 +1562,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");
@@ -1491,9 +1582,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;
@@ -1549,7 +1640,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));
 
@@ -1559,11 +1650,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 "
@@ -1575,7 +1666,7 @@ static int pktgen_mark_device(const char *ifname)
 
        }
 
-       thread_unlock();
+       mutex_unlock(&pktgen_thread_lock);
 
        return ret;
 }
@@ -1849,6 +1940,15 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)
                pkt_dev->hh[1] = tmp;
        }
 
+       if (pkt_dev->flags & F_MPLS_RND) {
+               unsigned i;
+               for(i = 0; i < pkt_dev->nr_labels; i++)
+                       if (pkt_dev->labels[i] & MPLS_STACK_BOTTOM)
+                               pkt_dev->labels[i] = MPLS_STACK_BOTTOM |
+                                                    (pktgen_random() &
+                                                     htonl(0x000fffff));
+       }
+
        if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {
                if (pkt_dev->flags & F_UDPSRC_RND)
                        pkt_dev->cur_udp_src =
@@ -1971,6 +2071,16 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)
        pkt_dev->flows[flow].count++;
 }
 
+static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)
+{
+       unsigned i;
+       for(i = 0; i < pkt_dev->nr_labels; i++) {
+               *mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM;
+       }
+       mpls--;
+       *mpls |= MPLS_STACK_BOTTOM;
+}
+
 static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
                                        struct pktgen_dev *pkt_dev)
 {
@@ -1980,6 +2090,11 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
        int datalen, iplen;
        struct iphdr *iph;
        struct pktgen_hdr *pgh = NULL;
+       __be16 protocol = __constant_htons(ETH_P_IP);
+       __be32 *mpls;
+
+       if (pkt_dev->nr_labels)
+               protocol = __constant_htons(ETH_P_MPLS_UC);
 
        /* Update any of the values, used when we're incrementing various
         * fields.
@@ -1987,7 +2102,8 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
        mod_cur_headers(pkt_dev);
 
        datalen = (odev->hard_header_len + 16) & ~0xf;
-       skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + datalen, GFP_ATOMIC);
+       skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + datalen +
+                       pkt_dev->nr_labels*sizeof(u32), GFP_ATOMIC);
        if (!skb) {
                sprintf(pkt_dev->result, "No memory");
                return NULL;
@@ -1997,13 +2113,18 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
 
        /*  Reserve for ethernet and IP header  */
        eth = (__u8 *) skb_push(skb, 14);
+       mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
+       if (pkt_dev->nr_labels)
+               mpls_push(mpls, pkt_dev);
        iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr));
        udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
 
        memcpy(eth, pkt_dev->hh, 12);
-       *(u16 *) & eth[12] = __constant_htons(ETH_P_IP);
+       *(u16 *) & eth[12] = protocol;
 
-       datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8;  /* Eth + IPh + UDPh */
+       /* Eth + IPh + UDPh + mpls */
+       datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
+                 pkt_dev->nr_labels*sizeof(u32);
        if (datalen < sizeof(struct pktgen_hdr))
                datalen = sizeof(struct pktgen_hdr);
 
@@ -2024,8 +2145,8 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
        iph->tot_len = htons(iplen);
        iph->check = 0;
        iph->check = ip_fast_csum((void *)iph, iph->ihl);
-       skb->protocol = __constant_htons(ETH_P_IP);
-       skb->mac.raw = ((u8 *) iph) - 14;
+       skb->protocol = protocol;
+       skb->mac.raw = ((u8 *) iph) - 14 - pkt_dev->nr_labels*sizeof(u32);
        skb->dev = odev;
        skb->pkt_type = PACKET_HOST;
 
@@ -2277,13 +2398,19 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
        int datalen;
        struct ipv6hdr *iph;
        struct pktgen_hdr *pgh = NULL;
+       __be16 protocol = __constant_htons(ETH_P_IPV6);
+       __be32 *mpls;
+
+       if (pkt_dev->nr_labels)
+               protocol = __constant_htons(ETH_P_MPLS_UC);
 
        /* Update any of the values, used when we're incrementing various
         * fields.
         */
        mod_cur_headers(pkt_dev);
 
-       skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + 16, GFP_ATOMIC);
+       skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + 16 +
+                       pkt_dev->nr_labels*sizeof(u32), GFP_ATOMIC);
        if (!skb) {
                sprintf(pkt_dev->result, "No memory");
                return NULL;
@@ -2293,13 +2420,19 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
 
        /*  Reserve for ethernet and IP header  */
        eth = (__u8 *) skb_push(skb, 14);
+       mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
+       if (pkt_dev->nr_labels)
+               mpls_push(mpls, pkt_dev);
        iph = (struct ipv6hdr *)skb_put(skb, sizeof(struct ipv6hdr));
        udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
 
        memcpy(eth, pkt_dev->hh, 12);
        *(u16 *) & eth[12] = __constant_htons(ETH_P_IPV6);
 
-       datalen = pkt_dev->cur_pkt_size - 14 - sizeof(struct ipv6hdr) - sizeof(struct udphdr);  /* Eth + IPh + UDPh */
+       /* Eth + IPh + UDPh + mpls */
+       datalen = pkt_dev->cur_pkt_size - 14 -
+                 sizeof(struct ipv6hdr) - sizeof(struct udphdr) -
+                 pkt_dev->nr_labels*sizeof(u32);
 
        if (datalen < sizeof(struct pktgen_hdr)) {
                datalen = sizeof(struct pktgen_hdr);
@@ -2323,8 +2456,8 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
        ipv6_addr_copy(&iph->daddr, &pkt_dev->cur_in6_daddr);
        ipv6_addr_copy(&iph->saddr, &pkt_dev->cur_in6_saddr);
 
-       skb->mac.raw = ((u8 *) iph) - 14;
-       skb->protocol = __constant_htons(ETH_P_IPV6);
+       skb->mac.raw = ((u8 *) iph) - 14 - pkt_dev->nr_labels*sizeof(u32);
+       skb->protocol = protocol;
        skb->dev = odev;
        skb->pkt_type = PACKET_HOST;
 
@@ -2421,13 +2554,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.
@@ -2458,25 +2591,24 @@ static void pktgen_stop_all_threads_ifs(void)
 
        PG_DEBUG(printk("pktgen: entering pktgen_stop_all_threads_ifs.\n"));
 
-       thread_lock();
+       mutex_lock(&pktgen_thread_lock);
 
        list_for_each_entry(t, &pktgen_threads, th_list)
                t->control |= T_STOP;
 
-       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;
 }
 
@@ -2505,7 +2637,7 @@ static int pktgen_wait_all_threads_run(void)
        struct pktgen_thread *t;
        int sig = 1;
 
-       thread_lock();
+       mutex_lock(&pktgen_thread_lock);
 
        list_for_each_entry(t, &pktgen_threads, th_list) {
                sig = pktgen_wait_thread_run(t);
@@ -2517,7 +2649,7 @@ static int pktgen_wait_all_threads_run(void)
                list_for_each_entry(t, &pktgen_threads, th_list)
                        t->control |= (T_STOP);
 
-       thread_unlock();
+       mutex_unlock(&pktgen_thread_lock);
        return sig;
 }
 
@@ -2527,12 +2659,12 @@ static void pktgen_run_all_threads(void)
 
        PG_DEBUG(printk("pktgen: entering pktgen_run_all_threads.\n"));
 
-       thread_lock();
+       mutex_lock(&pktgen_thread_lock);
 
        list_for_each_entry(t, &pktgen_threads, th_list)
                t->control |= (T_RUN);
 
-       thread_unlock();
+       mutex_unlock(&pktgen_thread_lock);
 
        schedule_timeout_interruptible(msecs_to_jiffies(125));  /* Propagate thread->control  */
 
@@ -2597,17 +2729,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;
@@ -2615,18 +2747,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);
@@ -2638,14 +2770,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;
@@ -2664,15 +2797,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);
@@ -2690,11 +2824,11 @@ static void pktgen_rem_thread(struct pktgen_thread *t)
 
        remove_proc_entry(t->name, pg_proc_dir);
 
-       thread_lock();
+       mutex_lock(&pktgen_thread_lock);
 
        list_del(&t->th_list);
 
-       thread_unlock();
+       mutex_unlock(&pktgen_thread_lock);
 }
 
 static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
@@ -2763,7 +2897,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
                }
        }
 
-       spin_lock_bh(&odev->xmit_lock);
+       netif_tx_lock_bh(odev);
        if (!netif_queue_stopped(odev)) {
 
                atomic_inc(&(pkt_dev->skb->users));
@@ -2808,7 +2942,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
                pkt_dev->next_tx_ns = 0;
        }
 
-       spin_unlock_bh(&odev->xmit_lock);
+       netif_tx_unlock_bh(odev);
 
        /* If pkt_dev->count is zero, then run forever */
        if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
@@ -2959,14 +3093,14 @@ static void pktgen_thread_worker(struct pktgen_thread *t)
 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));
@@ -2989,8 +3123,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;
 
@@ -3068,15 +3202,15 @@ static struct pktgen_thread *__init pktgen_find_thread(const char *name)
 {
        struct pktgen_thread *t;
 
-       thread_lock();
+       mutex_lock(&pktgen_thread_lock);
 
        list_for_each_entry(t, &pktgen_threads, th_list)
                if (strcmp(t->name, name) == 0) {
-                       thread_unlock();
+                       mutex_unlock(&pktgen_thread_lock);
                        return t;
                }
 
-       thread_unlock();
+       mutex_unlock(&pktgen_thread_lock);
        return NULL;
 }
 
@@ -3117,6 +3251,8 @@ static int __init pktgen_create_thread(const char *name, int cpu)
        pe->proc_fops = &pktgen_thread_fops;
        pe->data = t;
 
+       INIT_LIST_HEAD(&t->if_list);
+
        list_add_tail(&t->th_list, &pktgen_threads);
 
        t->removed = 0;
@@ -3140,20 +3276,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);
        }
 }
 
@@ -3216,11 +3345,24 @@ 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;
 }