pktgen: fix module unload for good
[linux-flexiantxendom0-3.2.10.git] / net / core / pktgen.c
index 2953b2a..b81369b 100644 (file)
 #include <linux/wait.h>
 #include <linux/etherdevice.h>
 #include <linux/kthread.h>
+#include <linux/prefetch.h>
 #include <net/net_namespace.h>
 #include <net/checksum.h>
 #include <net/ipv6.h>
@@ -251,6 +252,7 @@ struct pktgen_dev {
        int max_pkt_size;       /* = ETH_ZLEN; */
        int pkt_overhead;       /* overhead for MPLS, VLANs, IPSEC etc */
        int nfrags;
+       struct page *page;
        u64 delay;              /* nano-seconds */
 
        __u64 count;            /* Default No packets to send */
@@ -448,7 +450,6 @@ static void pktgen_stop(struct pktgen_thread *t);
 static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
 
 static unsigned int scan_ip6(const char *s, char ip[16]);
-static unsigned int fmt_ip6(char *s, const char ip[16]);
 
 /* Module parameters, defaults. */
 static int pg_count_d __read_mostly = 1000;
@@ -555,21 +556,13 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
                           pkt_dev->skb_priority);
 
        if (pkt_dev->flags & F_IPV6) {
-               char b1[128], b2[128], b3[128];
-               fmt_ip6(b1, pkt_dev->in6_saddr.s6_addr);
-               fmt_ip6(b2, pkt_dev->min_in6_saddr.s6_addr);
-               fmt_ip6(b3, pkt_dev->max_in6_saddr.s6_addr);
                seq_printf(seq,
-                          "     saddr: %s  min_saddr: %s  max_saddr: %s\n", b1,
-                          b2, b3);
-
-               fmt_ip6(b1, pkt_dev->in6_daddr.s6_addr);
-               fmt_ip6(b2, pkt_dev->min_in6_daddr.s6_addr);
-               fmt_ip6(b3, pkt_dev->max_in6_daddr.s6_addr);
-               seq_printf(seq,
-                          "     daddr: %s  min_daddr: %s  max_daddr: %s\n", b1,
-                          b2, b3);
-
+                          "     saddr: %pI6c  min_saddr: %pI6c  max_saddr: %pI6c\n"
+                          "     daddr: %pI6c  min_daddr: %pI6c  max_daddr: %pI6c\n",
+                          &pkt_dev->in6_saddr,
+                          &pkt_dev->min_in6_saddr, &pkt_dev->max_in6_saddr,
+                          &pkt_dev->in6_daddr,
+                          &pkt_dev->min_in6_daddr, &pkt_dev->max_in6_daddr);
        } else {
                seq_printf(seq,
                           "     dst_min: %s  dst_max: %s\n",
@@ -705,10 +698,9 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
                   pkt_dev->cur_src_mac_offset);
 
        if (pkt_dev->flags & F_IPV6) {
-               char b1[128], b2[128];
-               fmt_ip6(b1, pkt_dev->cur_in6_daddr.s6_addr);
-               fmt_ip6(b2, pkt_dev->cur_in6_saddr.s6_addr);
-               seq_printf(seq, "     cur_saddr: %s  cur_daddr: %s\n", b2, b1);
+               seq_printf(seq, "     cur_saddr: %pI6c  cur_daddr: %pI6c\n",
+                               &pkt_dev->cur_in6_saddr,
+                               &pkt_dev->cur_in6_daddr);
        } else
                seq_printf(seq, "     cur_saddr: 0x%x  cur_daddr: 0x%x\n",
                           pkt_dev->cur_saddr, pkt_dev->cur_daddr);
@@ -775,8 +767,8 @@ done:
        return i;
 }
 
-static unsigned long num_arg(const char __user * user_buffer,
-                            unsigned long maxlen, unsigned long *num)
+static long num_arg(const char __user *user_buffer, unsigned long maxlen,
+                               unsigned long *num)
 {
        int i;
        *num = 0;
@@ -1078,7 +1070,9 @@ static ssize_t pktgen_if_write(struct file *file,
                len = num_arg(&user_buffer[i], 10, &value);
                if (len < 0)
                        return len;
-
+               if ((value > 0) &&
+                   (!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
+                       return -ENOTSUPP;
                i += len;
                pkt_dev->clone_skb = value;
 
@@ -1134,6 +1128,10 @@ static ssize_t pktgen_if_write(struct file *file,
                if (node_possible(value)) {
                        pkt_dev->node = value;
                        sprintf(pg_result, "OK: node=%d", pkt_dev->node);
+                       if (pkt_dev->page) {
+                               put_page(pkt_dev->page);
+                               pkt_dev->page = NULL;
+                       }
                }
                else
                        sprintf(pg_result, "ERROR: node not possible");
@@ -1304,9 +1302,9 @@ static ssize_t pktgen_if_write(struct file *file,
                buf[len] = 0;
 
                scan_ip6(buf, pkt_dev->in6_daddr.s6_addr);
-               fmt_ip6(buf, pkt_dev->in6_daddr.s6_addr);
+               snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_daddr);
 
-               ipv6_addr_copy(&pkt_dev->cur_in6_daddr, &pkt_dev->in6_daddr);
+               pkt_dev->cur_in6_daddr = pkt_dev->in6_daddr;
 
                if (debug)
                        printk(KERN_DEBUG "pktgen: dst6 set to: %s\n", buf);
@@ -1327,10 +1325,9 @@ static ssize_t pktgen_if_write(struct file *file,
                buf[len] = 0;
 
                scan_ip6(buf, pkt_dev->min_in6_daddr.s6_addr);
-               fmt_ip6(buf, pkt_dev->min_in6_daddr.s6_addr);
+               snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->min_in6_daddr);
 
-               ipv6_addr_copy(&pkt_dev->cur_in6_daddr,
-                              &pkt_dev->min_in6_daddr);
+               pkt_dev->cur_in6_daddr = pkt_dev->min_in6_daddr;
                if (debug)
                        printk(KERN_DEBUG "pktgen: dst6_min set to: %s\n", buf);
 
@@ -1350,7 +1347,7 @@ static ssize_t pktgen_if_write(struct file *file,
                buf[len] = 0;
 
                scan_ip6(buf, pkt_dev->max_in6_daddr.s6_addr);
-               fmt_ip6(buf, pkt_dev->max_in6_daddr.s6_addr);
+               snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->max_in6_daddr);
 
                if (debug)
                        printk(KERN_DEBUG "pktgen: dst6_max set to: %s\n", buf);
@@ -1371,9 +1368,9 @@ static ssize_t pktgen_if_write(struct file *file,
                buf[len] = 0;
 
                scan_ip6(buf, pkt_dev->in6_saddr.s6_addr);
-               fmt_ip6(buf, pkt_dev->in6_saddr.s6_addr);
+               snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_saddr);
 
-               ipv6_addr_copy(&pkt_dev->cur_in6_saddr, &pkt_dev->in6_saddr);
+               pkt_dev->cur_in6_saddr = pkt_dev->in6_saddr;
 
                if (debug)
                        printk(KERN_DEBUG "pktgen: src6 set to: %s\n", buf);
@@ -1425,11 +1422,6 @@ static ssize_t pktgen_if_write(struct file *file,
                return count;
        }
        if (!strcmp(name, "dst_mac")) {
-               char *v = valstr;
-               unsigned char old_dmac[ETH_ALEN];
-               unsigned char *m = pkt_dev->dst_mac;
-               memcpy(old_dmac, pkt_dev->dst_mac, ETH_ALEN);
-
                len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
                if (len < 0)
                        return len;
@@ -1437,35 +1429,16 @@ static ssize_t pktgen_if_write(struct file *file,
                memset(valstr, 0, sizeof(valstr));
                if (copy_from_user(valstr, &user_buffer[i], len))
                        return -EFAULT;
-               i += len;
-
-               for (*m = 0; *v && m < pkt_dev->dst_mac + 6; v++) {
-                       int value;
-
-                       value = hex_to_bin(*v);
-                       if (value >= 0)
-                               *m = *m * 16 + value;
-
-                       if (*v == ':') {
-                               m++;
-                               *m = 0;
-                       }
-               }
 
+               if (!mac_pton(valstr, pkt_dev->dst_mac))
+                       return -EINVAL;
                /* Set up Dest MAC */
-               if (compare_ether_addr(old_dmac, pkt_dev->dst_mac))
-                       memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN);
+               memcpy(&pkt_dev->hh[0], pkt_dev->dst_mac, ETH_ALEN);
 
-               sprintf(pg_result, "OK: dstmac");
+               sprintf(pg_result, "OK: dstmac %pM", pkt_dev->dst_mac);
                return count;
        }
        if (!strcmp(name, "src_mac")) {
-               char *v = valstr;
-               unsigned char old_smac[ETH_ALEN];
-               unsigned char *m = pkt_dev->src_mac;
-
-               memcpy(old_smac, pkt_dev->src_mac, ETH_ALEN);
-
                len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
                if (len < 0)
                        return len;
@@ -1473,26 +1446,13 @@ static ssize_t pktgen_if_write(struct file *file,
                memset(valstr, 0, sizeof(valstr));
                if (copy_from_user(valstr, &user_buffer[i], len))
                        return -EFAULT;
-               i += len;
-
-               for (*m = 0; *v && m < pkt_dev->src_mac + 6; v++) {
-                       int value;
-
-                       value = hex_to_bin(*v);
-                       if (value >= 0)
-                               *m = *m * 16 + value;
-
-                       if (*v == ':') {
-                               m++;
-                               *m = 0;
-                       }
-               }
 
+               if (!mac_pton(valstr, pkt_dev->src_mac))
+                       return -EINVAL;
                /* Set up Src MAC */
-               if (compare_ether_addr(old_smac, pkt_dev->src_mac))
-                       memcpy(&(pkt_dev->hh[6]), pkt_dev->src_mac, ETH_ALEN);
+               memcpy(&pkt_dev->hh[6], pkt_dev->src_mac, ETH_ALEN);
 
-               sprintf(pg_result, "OK: srcmac");
+               sprintf(pg_result, "OK: srcmac %pM", pkt_dev->src_mac);
                return count;
        }
 
@@ -1971,7 +1931,7 @@ static int pktgen_device_event(struct notifier_block *unused,
 {
        struct net_device *dev = ptr;
 
-       if (!net_eq(dev_net(dev), &init_net))
+       if (!net_eq(dev_net(dev), &init_net) || pktgen_exiting)
                return NOTIFY_DONE;
 
        /* It is OK that we do not hold the group lock right now,
@@ -2064,13 +2024,13 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
                pr_warning("WARNING: Requested queue_map_min (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting\n",
                           pkt_dev->queue_map_min, (ntxq ?: 1) - 1, ntxq,
                           pkt_dev->odevname);
-               pkt_dev->queue_map_min = ntxq - 1;
+               pkt_dev->queue_map_min = (ntxq ?: 1) - 1;
        }
        if (pkt_dev->queue_map_max >= ntxq) {
                pr_warning("WARNING: Requested queue_map_max (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting\n",
                           pkt_dev->queue_map_max, (ntxq ?: 1) - 1, ntxq,
                           pkt_dev->odevname);
-               pkt_dev->queue_map_max = ntxq - 1;
+               pkt_dev->queue_map_max = (ntxq ?: 1) - 1;
        }
 
        /* Default to the interface's mac if not explicitly set. */
@@ -2118,9 +2078,7 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
                                     ifp = ifp->if_next) {
                                        if (ifp->scope == IFA_LINK &&
                                            !(ifp->flags & IFA_F_TENTATIVE)) {
-                                               ipv6_addr_copy(&pkt_dev->
-                                                              cur_in6_saddr,
-                                                              &ifp->addr);
+                                               pkt_dev->cur_in6_saddr = ifp->addr;
                                                err = 0;
                                                break;
                                        }
@@ -2184,9 +2142,12 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
        }
 
        start_time = ktime_now();
-       if (remaining < 100000)
-               ndelay(remaining);      /* really small just spin */
-       else {
+       if (remaining < 100000) {
+               /* for small delays (<100us), just loop until limit is reached */
+               do {
+                       end_time = ktime_now();
+               } while (ktime_lt(end_time, spin_until));
+       } else {
                /* see do_nanosleep */
                hrtimer_init_sleeper(&t, current);
                do {
@@ -2201,8 +2162,8 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
                        hrtimer_cancel(&t.timer);
                } while (t.task && pkt_dev->running && !signal_pending(current));
                __set_current_state(TASK_RUNNING);
+               end_time = ktime_now();
        }
-       end_time = ktime_now();
 
        pkt_dev->idle_acc += ktime_to_ns(ktime_sub(end_time, start_time));
        pkt_dev->next_tx = ktime_add_ns(spin_until, pkt_dev->delay);
@@ -2509,7 +2470,6 @@ static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)
 {
        struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
        int err = 0;
-       struct iphdr *iph;
 
        if (!x)
                return 0;
@@ -2519,7 +2479,6 @@ static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)
                return 0;
 
        spin_lock(&x->lock);
-       iph = ip_hdr(skb);
 
        err = x->outer_mode->output(x, skb);
        if (err)
@@ -2605,6 +2564,72 @@ static inline __be16 build_tci(unsigned int id, unsigned int cfi,
        return htons(id | (cfi << 12) | (prio << 13));
 }
 
+static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
+                               int datalen)
+{
+       struct timeval timestamp;
+       struct pktgen_hdr *pgh;
+
+       pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
+       datalen -= sizeof(*pgh);
+
+       if (pkt_dev->nfrags <= 0) {
+               memset(skb_put(skb, datalen), 0, datalen);
+       } else {
+               int frags = pkt_dev->nfrags;
+               int i, len;
+               int frag_len;
+
+
+               if (frags > MAX_SKB_FRAGS)
+                       frags = MAX_SKB_FRAGS;
+               len = datalen - frags * PAGE_SIZE;
+               if (len > 0) {
+                       memset(skb_put(skb, len), 0, len);
+                       datalen = frags * PAGE_SIZE;
+               }
+
+               i = 0;
+               frag_len = (datalen/frags) < PAGE_SIZE ?
+                          (datalen/frags) : PAGE_SIZE;
+               while (datalen > 0) {
+                       if (unlikely(!pkt_dev->page)) {
+                               int node = numa_node_id();
+
+                               if (pkt_dev->node >= 0 && (pkt_dev->flags & F_NODE))
+                                       node = pkt_dev->node;
+                               pkt_dev->page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
+                               if (!pkt_dev->page)
+                                       break;
+                       }
+                       get_page(pkt_dev->page);
+                       skb_frag_set_page(skb, i, pkt_dev->page);
+                       skb_shinfo(skb)->frags[i].page_offset = 0;
+                       /*last fragment, fill rest of data*/
+                       if (i == (frags - 1))
+                               skb_frag_size_set(&skb_shinfo(skb)->frags[i],
+                                   (datalen < PAGE_SIZE ? datalen : PAGE_SIZE));
+                       else
+                               skb_frag_size_set(&skb_shinfo(skb)->frags[i], frag_len);
+                       datalen -= skb_frag_size(&skb_shinfo(skb)->frags[i]);
+                       skb->len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
+                       skb->data_len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
+                       i++;
+                       skb_shinfo(skb)->nr_frags = i;
+               }
+       }
+
+       /* Stamp the time, and sequence number,
+        * convert them to network byte order
+        */
+       pgh->pgh_magic = htonl(PKTGEN_MAGIC);
+       pgh->seq_num = htonl(pkt_dev->seq_num);
+
+       do_gettimeofday(&timestamp);
+       pgh->tv_sec = htonl(timestamp.tv_sec);
+       pgh->tv_usec = htonl(timestamp.tv_usec);
+}
+
 static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
                                        struct pktgen_dev *pkt_dev)
 {
@@ -2613,7 +2638,6 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
        struct udphdr *udph;
        int datalen, iplen;
        struct iphdr *iph;
-       struct pktgen_hdr *pgh = NULL;
        __be16 protocol = htons(ETH_P_IP);
        __be32 *mpls;
        __be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
@@ -2660,6 +2684,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
                sprintf(pkt_dev->result, "No memory");
                return NULL;
        }
+       prefetchw(skb->data);
 
        skb_reserve(skb, datalen);
 
@@ -2728,76 +2753,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
                           pkt_dev->pkt_overhead);
        skb->dev = odev;
        skb->pkt_type = PACKET_HOST;
-
-       if (pkt_dev->nfrags <= 0) {
-               pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
-               memset(pgh + 1, 0, datalen - sizeof(struct pktgen_hdr));
-       } else {
-               int frags = pkt_dev->nfrags;
-               int i, len;
-
-               pgh = (struct pktgen_hdr *)(((char *)(udph)) + 8);
-
-               if (frags > MAX_SKB_FRAGS)
-                       frags = MAX_SKB_FRAGS;
-               if (datalen > frags * PAGE_SIZE) {
-                       len = datalen - frags * PAGE_SIZE;
-                       memset(skb_put(skb, len), 0, len);
-                       datalen = frags * PAGE_SIZE;
-               }
-
-               i = 0;
-               while (datalen > 0) {
-                       struct page *page = alloc_pages(GFP_KERNEL | __GFP_ZERO, 0);
-                       skb_shinfo(skb)->frags[i].page = page;
-                       skb_shinfo(skb)->frags[i].page_offset = 0;
-                       skb_shinfo(skb)->frags[i].size =
-                           (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
-                       datalen -= skb_shinfo(skb)->frags[i].size;
-                       skb->len += skb_shinfo(skb)->frags[i].size;
-                       skb->data_len += skb_shinfo(skb)->frags[i].size;
-                       i++;
-                       skb_shinfo(skb)->nr_frags = i;
-               }
-
-               while (i < frags) {
-                       int rem;
-
-                       if (i == 0)
-                               break;
-
-                       rem = skb_shinfo(skb)->frags[i - 1].size / 2;
-                       if (rem == 0)
-                               break;
-
-                       skb_shinfo(skb)->frags[i - 1].size -= rem;
-
-                       skb_shinfo(skb)->frags[i] =
-                           skb_shinfo(skb)->frags[i - 1];
-                       get_page(skb_shinfo(skb)->frags[i].page);
-                       skb_shinfo(skb)->frags[i].page =
-                           skb_shinfo(skb)->frags[i - 1].page;
-                       skb_shinfo(skb)->frags[i].page_offset +=
-                           skb_shinfo(skb)->frags[i - 1].size;
-                       skb_shinfo(skb)->frags[i].size = rem;
-                       i++;
-                       skb_shinfo(skb)->nr_frags = i;
-               }
-       }
-
-       /* Stamp the time, and sequence number,
-        * convert them to network byte order
-        */
-       if (pgh) {
-               struct timeval timestamp;
-
-               pgh->pgh_magic = htonl(PKTGEN_MAGIC);
-               pgh->seq_num = htonl(pkt_dev->seq_num);
-
-               do_gettimeofday(&timestamp);
-               pgh->tv_sec = htonl(timestamp.tv_sec);
-               pgh->tv_usec = htonl(timestamp.tv_usec);
-       }
+       pktgen_finalize_skb(pkt_dev, skb, datalen);
 
 #ifdef CONFIG_XFRM
        if (!process_ipsec(pkt_dev, skb, protocol))
@@ -2898,79 +2854,6 @@ static unsigned int scan_ip6(const char *s, char ip[16])
        return len;
 }
 
-static char tohex(char hexdigit)
-{
-       return hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
-}
-
-static int fmt_xlong(char *s, unsigned int i)
-{
-       char *bak = s;
-       *s = tohex((i >> 12) & 0xf);
-       if (s != bak || *s != '0')
-               ++s;
-       *s = tohex((i >> 8) & 0xf);
-       if (s != bak || *s != '0')
-               ++s;
-       *s = tohex((i >> 4) & 0xf);
-       if (s != bak || *s != '0')
-               ++s;
-       *s = tohex(i & 0xf);
-       return s - bak + 1;
-}
-
-static unsigned int fmt_ip6(char *s, const char ip[16])
-{
-       unsigned int len;
-       unsigned int i;
-       unsigned int temp;
-       unsigned int compressing;
-       int j;
-
-       len = 0;
-       compressing = 0;
-       for (j = 0; j < 16; j += 2) {
-
-#ifdef V4MAPPEDPREFIX
-               if (j == 12 && !memcmp(ip, V4mappedprefix, 12)) {
-                       inet_ntoa_r(*(struct in_addr *)(ip + 12), s);
-                       temp = strlen(s);
-                       return len + temp;
-               }
-#endif
-               temp = ((unsigned long)(unsigned char)ip[j] << 8) +
-                   (unsigned long)(unsigned char)ip[j + 1];
-               if (temp == 0) {
-                       if (!compressing) {
-                               compressing = 1;
-                               if (j == 0) {
-                                       *s++ = ':';
-                                       ++len;
-                               }
-                       }
-               } else {
-                       if (compressing) {
-                               compressing = 0;
-                               *s++ = ':';
-                               ++len;
-                       }
-                       i = fmt_xlong(s, temp);
-                       len += i;
-                       s += i;
-                       if (j < 14) {
-                               *s++ = ':';
-                               ++len;
-                       }
-               }
-       }
-       if (compressing) {
-               *s++ = ':';
-               ++len;
-       }
-       *s = 0;
-       return len;
-}
-
 static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
                                        struct pktgen_dev *pkt_dev)
 {
@@ -2979,7 +2862,6 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
        struct udphdr *udph;
        int datalen;
        struct ipv6hdr *iph;
-       struct pktgen_hdr *pgh = NULL;
        __be16 protocol = htons(ETH_P_IPV6);
        __be32 *mpls;
        __be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
@@ -3007,6 +2889,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
                sprintf(pkt_dev->result, "No memory");
                return NULL;
        }
+       prefetchw(skb->data);
 
        skb_reserve(skb, 16);
 
@@ -3072,8 +2955,8 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
        iph->payload_len = htons(sizeof(struct udphdr) + datalen);
        iph->nexthdr = IPPROTO_UDP;
 
-       ipv6_addr_copy(&iph->daddr, &pkt_dev->cur_in6_daddr);
-       ipv6_addr_copy(&iph->saddr, &pkt_dev->cur_in6_saddr);
+       iph->daddr = pkt_dev->cur_in6_daddr;
+       iph->saddr = pkt_dev->cur_in6_saddr;
 
        skb->mac_header = (skb->network_header - ETH_HLEN -
                           pkt_dev->pkt_overhead);
@@ -3081,75 +2964,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
        skb->dev = odev;
        skb->pkt_type = PACKET_HOST;
 
-       if (pkt_dev->nfrags <= 0)
-               pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
-       else {
-               int frags = pkt_dev->nfrags;
-               int i;
-
-               pgh = (struct pktgen_hdr *)(((char *)(udph)) + 8);
-
-               if (frags > MAX_SKB_FRAGS)
-                       frags = MAX_SKB_FRAGS;
-               if (datalen > frags * PAGE_SIZE) {
-                       skb_put(skb, datalen - frags * PAGE_SIZE);
-                       datalen = frags * PAGE_SIZE;
-               }
-
-               i = 0;
-               while (datalen > 0) {
-                       struct page *page = alloc_pages(GFP_KERNEL, 0);
-                       skb_shinfo(skb)->frags[i].page = page;
-                       skb_shinfo(skb)->frags[i].page_offset = 0;
-                       skb_shinfo(skb)->frags[i].size =
-                           (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
-                       datalen -= skb_shinfo(skb)->frags[i].size;
-                       skb->len += skb_shinfo(skb)->frags[i].size;
-                       skb->data_len += skb_shinfo(skb)->frags[i].size;
-                       i++;
-                       skb_shinfo(skb)->nr_frags = i;
-               }
-
-               while (i < frags) {
-                       int rem;
-
-                       if (i == 0)
-                               break;
-
-                       rem = skb_shinfo(skb)->frags[i - 1].size / 2;
-                       if (rem == 0)
-                               break;
-
-                       skb_shinfo(skb)->frags[i - 1].size -= rem;
-
-                       skb_shinfo(skb)->frags[i] =
-                           skb_shinfo(skb)->frags[i - 1];
-                       get_page(skb_shinfo(skb)->frags[i].page);
-                       skb_shinfo(skb)->frags[i].page =
-                           skb_shinfo(skb)->frags[i - 1].page;
-                       skb_shinfo(skb)->frags[i].page_offset +=
-                           skb_shinfo(skb)->frags[i - 1].size;
-                       skb_shinfo(skb)->frags[i].size = rem;
-                       i++;
-                       skb_shinfo(skb)->nr_frags = i;
-               }
-       }
-
-       /* Stamp the time, and sequence number,
-        * convert them to network byte order
-        * should we update cloned packets too ?
-        */
-       if (pgh) {
-               struct timeval timestamp;
-
-               pgh->pgh_magic = htonl(PKTGEN_MAGIC);
-               pgh->seq_num = htonl(pkt_dev->seq_num);
-
-               do_gettimeofday(&timestamp);
-               pgh->tv_sec = htonl(timestamp.tv_sec);
-               pgh->tv_usec = htonl(timestamp.tv_usec);
-       }
-       /* pkt_dev->seq_num++; FF: you really mean this? */
+       pktgen_finalize_skb(pkt_dev, skb, datalen);
 
        return skb;
 }
@@ -3319,7 +3134,7 @@ static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
                                    pkt_dev->started_at);
        ktime_t idle = ns_to_ktime(pkt_dev->idle_acc);
 
-       p += sprintf(p, "OK: %llu(c%llu+d%llu) nsec, %llu (%dbyte,%dfrags)\n",
+       p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n",
                     (unsigned long long)ktime_to_us(elapsed),
                     (unsigned long long)ktime_to_us(ktime_sub(elapsed, idle)),
                     (unsigned long long)ktime_to_us(idle),
@@ -3527,7 +3342,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
 
        __netif_tx_lock_bh(txq);
 
-       if (unlikely(netif_tx_queue_frozen_or_stopped(txq))) {
+       if (unlikely(netif_xmit_frozen_or_stopped(txq))) {
                ret = NETDEV_TX_BUSY;
                pkt_dev->last_ok = 0;
                goto unlock;
@@ -3551,8 +3366,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
                break;
        default: /* Drivers are not supposed to return other values! */
                if (net_ratelimit())
-                       pr_info("pktgen: %s xmit error: %d\n",
-                               pkt_dev->odevname, ret);
+                       pr_info("%s xmit error: %d\n", pkt_dev->odevname, ret);
                pkt_dev->errors++;
                /* fallthru */
        case NETDEV_TX_LOCKED:
@@ -3732,19 +3546,17 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
                return -ENOMEM;
 
        strcpy(pkt_dev->odevname, ifname);
-       pkt_dev->flows = vmalloc_node(MAX_CFLOWS * sizeof(struct flow_state),
+       pkt_dev->flows = vzalloc_node(MAX_CFLOWS * sizeof(struct flow_state),
                                      node);
        if (pkt_dev->flows == NULL) {
                kfree(pkt_dev);
                return -ENOMEM;
        }
-       memset(pkt_dev->flows, 0, MAX_CFLOWS * sizeof(struct flow_state));
 
        pkt_dev->removal_mark = 0;
        pkt_dev->min_pkt_size = ETH_ZLEN;
        pkt_dev->max_pkt_size = ETH_ZLEN;
        pkt_dev->nfrags = 0;
-       pkt_dev->clone_skb = pg_clone_skb_d;
        pkt_dev->delay = pg_delay_d;
        pkt_dev->count = pg_count_d;
        pkt_dev->sofar = 0;
@@ -3752,7 +3564,6 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
        pkt_dev->udp_src_max = 9;
        pkt_dev->udp_dst_min = 9;
        pkt_dev->udp_dst_max = 9;
-
        pkt_dev->vlan_p = 0;
        pkt_dev->vlan_cfi = 0;
        pkt_dev->vlan_id = 0xffff;
@@ -3764,6 +3575,8 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
        err = pktgen_setup_dev(pkt_dev, ifname);
        if (err)
                goto out1;
+       if (pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)
+               pkt_dev->clone_skb = pg_clone_skb_d;
 
        pkt_dev->entry = proc_create_data(ifname, 0600, pg_proc_dir,
                                          &pktgen_if_fops, pkt_dev);
@@ -3811,7 +3624,10 @@ static int __init pktgen_create_thread(int cpu)
        list_add_tail(&t->th_list, &pktgen_threads);
        init_completion(&t->start_done);
 
-       p = kthread_create(pktgen_thread_worker, t, "kpktgend_%d", cpu);
+       p = kthread_create_on_node(pktgen_thread_worker,
+                                  t,
+                                  cpu_to_node(cpu),
+                                  "kpktgend_%d", cpu);
        if (IS_ERR(p)) {
                pr_err("kernel_thread() failed for cpu %d\n", t->cpu);
                list_del(&t->th_list);
@@ -3883,6 +3699,8 @@ static int pktgen_remove_device(struct pktgen_thread *t,
        free_SAs(pkt_dev);
 #endif
        vfree(pkt_dev->flows);
+       if (pkt_dev->page)
+               put_page(pkt_dev->page);
        kfree(pkt_dev);
        return 0;
 }
@@ -3891,6 +3709,7 @@ static int __init pg_init(void)
 {
        int cpu;
        struct proc_dir_entry *pe;
+       int ret = 0;
 
        pr_info("%s", version);
 
@@ -3901,11 +3720,10 @@ static int __init pg_init(void)
        pe = proc_create(PGCTRL, 0600, pg_proc_dir, &pktgen_fops);
        if (pe == NULL) {
                pr_err("ERROR: cannot create %s procfs entry\n", PGCTRL);
-               proc_net_remove(&init_net, PG_PROC_DIR);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto remove_dir;
        }
 
-       /* Register us to receive netdevice events */
        register_netdevice_notifier(&pktgen_notifier_block);
 
        for_each_online_cpu(cpu) {
@@ -3919,25 +3737,36 @@ static int __init pg_init(void)
 
        if (list_empty(&pktgen_threads)) {
                pr_err("ERROR: Initialization failed for all threads\n");
-               unregister_netdevice_notifier(&pktgen_notifier_block);
-               remove_proc_entry(PGCTRL, pg_proc_dir);
-               proc_net_remove(&init_net, PG_PROC_DIR);
-               return -ENODEV;
+               ret = -ENODEV;
+               goto unregister;
        }
 
        return 0;
+
+ unregister:
+       unregister_netdevice_notifier(&pktgen_notifier_block);
+       remove_proc_entry(PGCTRL, pg_proc_dir);
+ remove_dir:
+       proc_net_remove(&init_net, PG_PROC_DIR);
+       return ret;
 }
 
 static void __exit pg_cleanup(void)
 {
        struct pktgen_thread *t;
        struct list_head *q, *n;
+       LIST_HEAD(list);
 
        /* Stop all interfaces & threads */
        pktgen_exiting = true;
 
-       list_for_each_safe(q, n, &pktgen_threads) {
+       mutex_lock(&pktgen_thread_lock);
+       list_splice_init(&pktgen_threads, &list);
+       mutex_unlock(&pktgen_thread_lock);
+
+       list_for_each_safe(q, n, &list) {
                t = list_entry(q, struct pktgen_thread, th_list);
+               list_del(&t->th_list);
                kthread_stop(t->tsk);
                kfree(t);
        }