diff options
Diffstat (limited to 'net/core/pktgen.c')
| -rw-r--r-- | net/core/pktgen.c | 1252 | 
1 files changed, 558 insertions, 694 deletions
diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 2953b2abc97..fc17a9d309a 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -156,13 +156,17 @@  #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> +#include <net/udp.h> +#include <net/ip6_checksum.h>  #include <net/addrconf.h>  #ifdef CONFIG_XFRM  #include <net/xfrm.h>  #endif +#include <net/netns/generic.h>  #include <asm/byteorder.h>  #include <linux/rcupdate.h>  #include <linux/bitops.h> @@ -196,6 +200,7 @@  #define F_QUEUE_MAP_RND (1<<13)	/* queue map Random */  #define F_QUEUE_MAP_CPU (1<<14)	/* queue map mirrors smp_processor_id() */  #define F_NODE          (1<<15)	/* Node memory alloc*/ +#define F_UDPCSUM       (1<<16)	/* Include UDP checksum */  /* Thread control flag bits */  #define T_STOP        (1<<0)	/* Stop run */ @@ -211,7 +216,6 @@  #define PKTGEN_MAGIC 0xbe9be955  #define PG_PROC_DIR "pktgen"  #define PGCTRL	    "pgctrl" -static struct proc_dir_entry *pg_proc_dir;  #define MAX_CFLOWS  65536 @@ -247,10 +251,11 @@ struct pktgen_dev {  	int removal_mark;	/* non-zero => the device is marked for  				 * removal by worker thread */ -	int min_pkt_size;	/* = ETH_ZLEN; */ -	int max_pkt_size;	/* = ETH_ZLEN; */ +	int min_pkt_size; +	int max_pkt_size;  	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 */ @@ -318,7 +323,7 @@ struct pktgen_dev {  				(see RFC 3260, sec. 4) */  	/* MPLS */ -	unsigned nr_labels;	/* Depth of stack, 0 = no MPLS */ +	unsigned int nr_labels;	/* Depth of stack, 0 = no MPLS */  	__be32 labels[MAX_MPLS_LABELS];  	/* VLAN/SVLAN (802.1Q/Q-in-Q) */ @@ -371,10 +376,10 @@ struct pktgen_dev {  				  */  	char odevname[32];  	struct flow_state *flows; -	unsigned cflows;	/* Concurrent flows (config) */ -	unsigned lflow;		/* Flow length  (config) */ -	unsigned nflows;	/* accumulated flows (stats) */ -	unsigned curfl;		/* current sequenced flow (state)*/ +	unsigned int cflows;	/* Concurrent flows (config) */ +	unsigned int lflow;		/* Flow length  (config) */ +	unsigned int nflows;	/* accumulated flows (stats) */ +	unsigned int curfl;		/* current sequenced flow (state)*/  	u16 queue_map_min;  	u16 queue_map_max; @@ -384,6 +389,9 @@ struct pktgen_dev {  #ifdef CONFIG_XFRM  	__u8	ipsmode;		/* IPSEC mode (config) */  	__u8	ipsproto;		/* IPSEC type (config) */ +	__u32	spi; +	struct dst_entry dst; +	struct dst_ops dstops;  #endif  	char result[512];  }; @@ -395,7 +403,15 @@ struct pktgen_hdr {  	__be32 tv_usec;  }; -static bool pktgen_exiting __read_mostly; + +static int pg_net_id __read_mostly; + +struct pktgen_net { +	struct net		*net; +	struct proc_dir_entry	*proc_dir; +	struct list_head	pktgen_threads; +	bool			pktgen_exiting; +};  struct pktgen_thread {  	spinlock_t if_lock;		/* for list of devices */ @@ -412,25 +428,12 @@ struct pktgen_thread {  	wait_queue_head_t queue;  	struct completion start_done; +	struct pktgen_net *net;  };  #define REMOVE 1  #define FIND   0 -static inline ktime_t ktime_now(void) -{ -	struct timespec ts; -	ktime_get_ts(&ts); - -	return timespec_to_ktime(ts); -} - -/* This works even if 32 bit because of careful byte order choice */ -static inline int ktime_lt(const ktime_t cmp1, const ktime_t cmp2) -{ -	return cmp1.tv64 < cmp2.tv64; -} -  static const char version[] =  	"Packet Generator for packet performance testing. "  	"Version: " VERSION "\n"; @@ -440,16 +443,13 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname);  static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,  					  const char *ifname, bool exact);  static int pktgen_device_event(struct notifier_block *, unsigned long, void *); -static void pktgen_run_all_threads(void); -static void pktgen_reset_all_threads(void); -static void pktgen_stop_all_threads_ifs(void); +static void pktgen_run_all_threads(struct pktgen_net *pn); +static void pktgen_reset_all_threads(struct pktgen_net *pn); +static void pktgen_stop_all_threads_ifs(struct pktgen_net *pn);  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;  static int pg_delay_d __read_mostly; @@ -457,7 +457,6 @@ static int pg_clone_skb_d  __read_mostly;  static int debug  __read_mostly;  static DEFINE_MUTEX(pktgen_thread_lock); -static LIST_HEAD(pktgen_threads);  static struct notifier_block pktgen_notifier_block = {  	.notifier_call = pktgen_device_event, @@ -477,44 +476,41 @@ static int pgctrl_show(struct seq_file *seq, void *v)  static ssize_t pgctrl_write(struct file *file, const char __user *buf,  			    size_t count, loff_t *ppos)  { -	int err = 0;  	char data[128]; +	struct pktgen_net *pn = net_generic(current->nsproxy->net_ns, pg_net_id); -	if (!capable(CAP_NET_ADMIN)) { -		err = -EPERM; -		goto out; -	} +	if (!capable(CAP_NET_ADMIN)) +		return -EPERM; + +	if (count == 0) +		return -EINVAL;  	if (count > sizeof(data))  		count = sizeof(data); -	if (copy_from_user(data, buf, count)) { -		err = -EFAULT; -		goto out; -	} -	data[count - 1] = 0;	/* Make string */ +	if (copy_from_user(data, buf, count)) +		return -EFAULT; + +	data[count - 1] = 0;	/* Strip trailing '\n' and terminate string */  	if (!strcmp(data, "stop")) -		pktgen_stop_all_threads_ifs(); +		pktgen_stop_all_threads_ifs(pn);  	else if (!strcmp(data, "start")) -		pktgen_run_all_threads(); +		pktgen_run_all_threads(pn);  	else if (!strcmp(data, "reset")) -		pktgen_reset_all_threads(); +		pktgen_reset_all_threads(pn);  	else  		pr_warning("Unknown command: %s\n", data); -	err = count; - -out: -	return err; +	return count;  }  static int pgctrl_open(struct inode *inode, struct file *file)  { -	return single_open(file, pgctrl_show, PDE(inode)->data); +	return single_open(file, pgctrl_show, PDE_DATA(inode));  }  static const struct file_operations pktgen_fops = { @@ -555,21 +551,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", @@ -585,7 +573,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v)  		   is_zero_ether_addr(pkt_dev->src_mac) ?  			     pkt_dev->odev->dev_addr : pkt_dev->src_mac); -	seq_printf(seq, "dst_mac: "); +	seq_puts(seq, "dst_mac: ");  	seq_printf(seq, "%pM\n", pkt_dev->dst_mac);  	seq_printf(seq, @@ -599,8 +587,8 @@ static int pktgen_if_show(struct seq_file *seq, void *v)  		   pkt_dev->src_mac_count, pkt_dev->dst_mac_count);  	if (pkt_dev->nr_labels) { -		unsigned i; -		seq_printf(seq, "     mpls: "); +		unsigned int i; +		seq_puts(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" : ", "); @@ -625,66 +613,72 @@ static int pktgen_if_show(struct seq_file *seq, void *v)  	if (pkt_dev->node >= 0)  		seq_printf(seq, "     node: %d\n", pkt_dev->node); -	seq_printf(seq, "     Flags: "); +	seq_puts(seq, "     Flags: ");  	if (pkt_dev->flags & F_IPV6) -		seq_printf(seq, "IPV6  "); +		seq_puts(seq, "IPV6  ");  	if (pkt_dev->flags & F_IPSRC_RND) -		seq_printf(seq, "IPSRC_RND  "); +		seq_puts(seq, "IPSRC_RND  ");  	if (pkt_dev->flags & F_IPDST_RND) -		seq_printf(seq, "IPDST_RND  "); +		seq_puts(seq, "IPDST_RND  ");  	if (pkt_dev->flags & F_TXSIZE_RND) -		seq_printf(seq, "TXSIZE_RND  "); +		seq_puts(seq, "TXSIZE_RND  ");  	if (pkt_dev->flags & F_UDPSRC_RND) -		seq_printf(seq, "UDPSRC_RND  "); +		seq_puts(seq, "UDPSRC_RND  ");  	if (pkt_dev->flags & F_UDPDST_RND) -		seq_printf(seq, "UDPDST_RND  "); +		seq_puts(seq, "UDPDST_RND  "); + +	if (pkt_dev->flags & F_UDPCSUM) +		seq_puts(seq, "UDPCSUM  ");  	if (pkt_dev->flags & F_MPLS_RND) -		seq_printf(seq,  "MPLS_RND  "); +		seq_puts(seq,  "MPLS_RND  ");  	if (pkt_dev->flags & F_QUEUE_MAP_RND) -		seq_printf(seq,  "QUEUE_MAP_RND  "); +		seq_puts(seq,  "QUEUE_MAP_RND  ");  	if (pkt_dev->flags & F_QUEUE_MAP_CPU) -		seq_printf(seq,  "QUEUE_MAP_CPU  "); +		seq_puts(seq,  "QUEUE_MAP_CPU  ");  	if (pkt_dev->cflows) {  		if (pkt_dev->flags & F_FLOW_SEQ) -			seq_printf(seq,  "FLOW_SEQ  "); /*in sequence flows*/ +			seq_puts(seq,  "FLOW_SEQ  "); /*in sequence flows*/  		else -			seq_printf(seq,  "FLOW_RND  "); +			seq_puts(seq,  "FLOW_RND  ");  	}  #ifdef CONFIG_XFRM -	if (pkt_dev->flags & F_IPSEC_ON) -		seq_printf(seq,  "IPSEC  "); +	if (pkt_dev->flags & F_IPSEC_ON) { +		seq_puts(seq,  "IPSEC  "); +		if (pkt_dev->spi) +			seq_printf(seq, "spi:%u", pkt_dev->spi); +	}  #endif  	if (pkt_dev->flags & F_MACSRC_RND) -		seq_printf(seq, "MACSRC_RND  "); +		seq_puts(seq, "MACSRC_RND  ");  	if (pkt_dev->flags & F_MACDST_RND) -		seq_printf(seq, "MACDST_RND  "); +		seq_puts(seq, "MACDST_RND  ");  	if (pkt_dev->flags & F_VID_RND) -		seq_printf(seq, "VID_RND  "); +		seq_puts(seq, "VID_RND  ");  	if (pkt_dev->flags & F_SVID_RND) -		seq_printf(seq, "SVID_RND  "); +		seq_puts(seq, "SVID_RND  ");  	if (pkt_dev->flags & F_NODE) -		seq_printf(seq, "NODE_ALLOC  "); +		seq_puts(seq, "NODE_ALLOC  ");  	seq_puts(seq, "\n");  	/* not really stopped, more like last-running-at */ -	stopped = pkt_dev->running ? ktime_now() : pkt_dev->stopped_at; +	stopped = pkt_dev->running ? ktime_get() : pkt_dev->stopped_at;  	idle = pkt_dev->idle_acc;  	do_div(idle, NSEC_PER_USEC); @@ -705,13 +699,12 @@ 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); +		seq_printf(seq, "     cur_saddr: %pI4  cur_daddr: %pI4\n", +			   &pkt_dev->cur_saddr, &pkt_dev->cur_daddr);  	seq_printf(seq, "     cur_udp_dst: %d  cur_udp_src: %d\n",  		   pkt_dev->cur_udp_dst, pkt_dev->cur_udp_src); @@ -723,7 +716,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v)  	if (pkt_dev->result[0])  		seq_printf(seq, "Result: %s\n", pkt_dev->result);  	else -		seq_printf(seq, "Result: Idle\n"); +		seq_puts(seq, "Result: Idle\n");  	return 0;  } @@ -775,8 +768,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; @@ -820,7 +813,7 @@ done_str:  static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)  { -	unsigned n = 0; +	unsigned int n = 0;  	char c;  	ssize_t i = 0;  	int len; @@ -899,8 +892,8 @@ static ssize_t pktgen_if_write(struct file *file,  		if (copy_from_user(tb, user_buffer, copy))  			return -EFAULT;  		tb[copy] = 0; -		printk(KERN_DEBUG "pktgen: %s,%lu  buffer -:%s:-\n", name, -		       (unsigned long)count, tb); +		pr_debug("%s,%lu  buffer -:%s:-\n", +			 name, (unsigned long)count, tb);  	}  	if (!strcmp(name, "min_pkt_size")) { @@ -1078,7 +1071,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 +1129,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"); @@ -1237,12 +1236,24 @@ static ssize_t pktgen_if_write(struct file *file,  		else if (strcmp(f, "!NODE_ALLOC") == 0)  			pkt_dev->flags &= ~F_NODE; +		else if (strcmp(f, "UDPCSUM") == 0) +			pkt_dev->flags |= F_UDPCSUM; + +		else if (strcmp(f, "!UDPCSUM") == 0) +			pkt_dev->flags &= ~F_UDPCSUM; +  		else {  			sprintf(pg_result,  				"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",  				f,  				"IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, " -				"MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ, IPSEC, NODE_ALLOC\n"); +				"MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, " +				"MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ, " +				"QUEUE_MAP_RND, QUEUE_MAP_CPU, UDPCSUM, " +#ifdef CONFIG_XFRM +				"IPSEC, " +#endif +				"NODE_ALLOC\n");  			return count;  		}  		sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags); @@ -1263,8 +1274,7 @@ static ssize_t pktgen_if_write(struct file *file,  			pkt_dev->cur_daddr = pkt_dev->daddr_min;  		}  		if (debug) -			printk(KERN_DEBUG "pktgen: dst_min set to: %s\n", -			       pkt_dev->dst_min); +			pr_debug("dst_min set to: %s\n", pkt_dev->dst_min);  		i += len;  		sprintf(pg_result, "OK: dst_min=%s", pkt_dev->dst_min);  		return count; @@ -1286,8 +1296,7 @@ static ssize_t pktgen_if_write(struct file *file,  			pkt_dev->cur_daddr = pkt_dev->daddr_max;  		}  		if (debug) -			printk(KERN_DEBUG "pktgen: dst_max set to: %s\n", -			       pkt_dev->dst_max); +			pr_debug("dst_max set to: %s\n", pkt_dev->dst_max);  		i += len;  		sprintf(pg_result, "OK: dst_max=%s", pkt_dev->dst_max);  		return count; @@ -1303,13 +1312,13 @@ static ssize_t pktgen_if_write(struct file *file,  			return -EFAULT;  		buf[len] = 0; -		scan_ip6(buf, pkt_dev->in6_daddr.s6_addr); -		fmt_ip6(buf, pkt_dev->in6_daddr.s6_addr); +		in6_pton(buf, -1, pkt_dev->in6_daddr.s6_addr, -1, NULL); +		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); +			pr_debug("dst6 set to: %s\n", buf);  		i += len;  		sprintf(pg_result, "OK: dst6=%s", buf); @@ -1326,13 +1335,12 @@ static ssize_t pktgen_if_write(struct file *file,  			return -EFAULT;  		buf[len] = 0; -		scan_ip6(buf, pkt_dev->min_in6_daddr.s6_addr); -		fmt_ip6(buf, pkt_dev->min_in6_daddr.s6_addr); +		in6_pton(buf, -1, pkt_dev->min_in6_daddr.s6_addr, -1, NULL); +		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); +			pr_debug("dst6_min set to: %s\n", buf);  		i += len;  		sprintf(pg_result, "OK: dst6_min=%s", buf); @@ -1349,11 +1357,11 @@ static ssize_t pktgen_if_write(struct file *file,  			return -EFAULT;  		buf[len] = 0; -		scan_ip6(buf, pkt_dev->max_in6_daddr.s6_addr); -		fmt_ip6(buf, pkt_dev->max_in6_daddr.s6_addr); +		in6_pton(buf, -1, pkt_dev->max_in6_daddr.s6_addr, -1, NULL); +		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->max_in6_daddr);  		if (debug) -			printk(KERN_DEBUG "pktgen: dst6_max set to: %s\n", buf); +			pr_debug("dst6_max set to: %s\n", buf);  		i += len;  		sprintf(pg_result, "OK: dst6_max=%s", buf); @@ -1370,13 +1378,13 @@ static ssize_t pktgen_if_write(struct file *file,  			return -EFAULT;  		buf[len] = 0; -		scan_ip6(buf, pkt_dev->in6_saddr.s6_addr); -		fmt_ip6(buf, pkt_dev->in6_saddr.s6_addr); +		in6_pton(buf, -1, pkt_dev->in6_saddr.s6_addr, -1, NULL); +		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); +			pr_debug("src6 set to: %s\n", buf);  		i += len;  		sprintf(pg_result, "OK: src6=%s", buf); @@ -1397,8 +1405,7 @@ static ssize_t pktgen_if_write(struct file *file,  			pkt_dev->cur_saddr = pkt_dev->saddr_min;  		}  		if (debug) -			printk(KERN_DEBUG "pktgen: src_min set to: %s\n", -			       pkt_dev->src_min); +			pr_debug("src_min set to: %s\n", pkt_dev->src_min);  		i += len;  		sprintf(pg_result, "OK: src_min=%s", pkt_dev->src_min);  		return count; @@ -1418,18 +1425,12 @@ static ssize_t pktgen_if_write(struct file *file,  			pkt_dev->cur_saddr = pkt_dev->saddr_max;  		}  		if (debug) -			printk(KERN_DEBUG "pktgen: src_max set to: %s\n", -			       pkt_dev->src_max); +			pr_debug("src_max set to: %s\n", pkt_dev->src_max);  		i += len;  		sprintf(pg_result, "OK: src_max=%s", pkt_dev->src_max);  		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 +1438,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); +		ether_addr_copy(&pkt_dev->hh[0], pkt_dev->dst_mac); -		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 +1455,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); +		ether_addr_copy(&pkt_dev->hh[6], pkt_dev->src_mac); -		sprintf(pg_result, "OK: srcmac"); +		sprintf(pg_result, "OK: srcmac %pM", pkt_dev->src_mac);  		return count;  	} @@ -1515,7 +1484,18 @@ static ssize_t pktgen_if_write(struct file *file,  		sprintf(pg_result, "OK: flows=%u", pkt_dev->cflows);  		return count;  	} +#ifdef CONFIG_XFRM +	if (!strcmp(name, "spi")) { +		len = num_arg(&user_buffer[i], 10, &value); +		if (len < 0) +			return len; +		i += len; +		pkt_dev->spi = value; +		sprintf(pg_result, "OK: spi=%u", pkt_dev->spi); +		return count; +	} +#endif  	if (!strcmp(name, "flowlen")) {  		len = num_arg(&user_buffer[i], 10, &value);  		if (len < 0) @@ -1550,7 +1530,7 @@ static ssize_t pktgen_if_write(struct file *file,  	}  	if (!strcmp(name, "mpls")) { -		unsigned n, cnt; +		unsigned int n, cnt;  		len = get_labels(&user_buffer[i], pkt_dev);  		if (len < 0) @@ -1567,7 +1547,7 @@ static ssize_t pktgen_if_write(struct file *file,  			pkt_dev->svlan_id = 0xffff;  			if (debug) -				printk(KERN_DEBUG "pktgen: VLAN/SVLAN auto turned off\n"); +				pr_debug("VLAN/SVLAN auto turned off\n");  		}  		return count;  	} @@ -1582,10 +1562,10 @@ static ssize_t pktgen_if_write(struct file *file,  			pkt_dev->vlan_id = value;  /* turn on VLAN */  			if (debug) -				printk(KERN_DEBUG "pktgen: VLAN turned on\n"); +				pr_debug("VLAN turned on\n");  			if (debug && pkt_dev->nr_labels) -				printk(KERN_DEBUG "pktgen: MPLS auto turned off\n"); +				pr_debug("MPLS auto turned off\n");  			pkt_dev->nr_labels = 0;    /* turn off MPLS */  			sprintf(pg_result, "OK: vlan_id=%u", pkt_dev->vlan_id); @@ -1594,7 +1574,7 @@ static ssize_t pktgen_if_write(struct file *file,  			pkt_dev->svlan_id = 0xffff;  			if (debug) -				printk(KERN_DEBUG "pktgen: VLAN/SVLAN turned off\n"); +				pr_debug("VLAN/SVLAN turned off\n");  		}  		return count;  	} @@ -1639,10 +1619,10 @@ static ssize_t pktgen_if_write(struct file *file,  			pkt_dev->svlan_id = value;  /* turn on SVLAN */  			if (debug) -				printk(KERN_DEBUG "pktgen: SVLAN turned on\n"); +				pr_debug("SVLAN turned on\n");  			if (debug && pkt_dev->nr_labels) -				printk(KERN_DEBUG "pktgen: MPLS auto turned off\n"); +				pr_debug("MPLS auto turned off\n");  			pkt_dev->nr_labels = 0;    /* turn off MPLS */  			sprintf(pg_result, "OK: svlan_id=%u", pkt_dev->svlan_id); @@ -1651,7 +1631,7 @@ static ssize_t pktgen_if_write(struct file *file,  			pkt_dev->svlan_id = 0xffff;  			if (debug) -				printk(KERN_DEBUG "pktgen: VLAN/SVLAN turned off\n"); +				pr_debug("VLAN/SVLAN turned off\n");  		}  		return count;  	} @@ -1736,7 +1716,7 @@ static ssize_t pktgen_if_write(struct file *file,  static int pktgen_if_open(struct inode *inode, struct file *file)  { -	return single_open(file, pktgen_if_show, PDE(inode)->data); +	return single_open(file, pktgen_if_show, PDE_DATA(inode));  }  static const struct file_operations pktgen_if_fops = { @@ -1755,14 +1735,14 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)  	BUG_ON(!t); -	seq_printf(seq, "Running: "); +	seq_puts(seq, "Running: ");  	if_lock(t);  	list_for_each_entry(pkt_dev, &t->if_list, list)  		if (pkt_dev->running)  			seq_printf(seq, "%s ", pkt_dev->odevname); -	seq_printf(seq, "\nStopped: "); +	seq_puts(seq, "\nStopped: ");  	list_for_each_entry(pkt_dev, &t->if_list, list)  		if (!pkt_dev->running) @@ -1771,7 +1751,7 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)  	if (t->result[0])  		seq_printf(seq, "\nResult: %s\n", t->result);  	else -		seq_printf(seq, "\nResult: NA\n"); +		seq_puts(seq, "\nResult: NA\n");  	if_unlock(t); @@ -1819,8 +1799,7 @@ static ssize_t pktgen_thread_write(struct file *file,  	i += len;  	if (debug) -		printk(KERN_DEBUG "pktgen: t=%s, count=%lu\n", -		       name, (unsigned long)count); +		pr_debug("t=%s, count=%lu\n", name, (unsigned long)count);  	if (!t) {  		pr_err("ERROR: No thread\n"); @@ -1842,10 +1821,13 @@ static ssize_t pktgen_thread_write(struct file *file,  			return -EFAULT;  		i += len;  		mutex_lock(&pktgen_thread_lock); -		pktgen_add_device(t, f); +		ret = pktgen_add_device(t, f);  		mutex_unlock(&pktgen_thread_lock); -		ret = count; -		sprintf(pg_result, "OK: add_device=%s", f); +		if (!ret) { +			ret = count; +			sprintf(pg_result, "OK: add_device=%s", f); +		} else +			sprintf(pg_result, "ERROR: can not add device %s", f);  		goto out;  	} @@ -1872,7 +1854,7 @@ out:  static int pktgen_thread_open(struct inode *inode, struct file *file)  { -	return single_open(file, pktgen_thread_show, PDE(inode)->data); +	return single_open(file, pktgen_thread_show, PDE_DATA(inode));  }  static const struct file_operations pktgen_thread_fops = { @@ -1885,13 +1867,14 @@ static const struct file_operations pktgen_thread_fops = {  };  /* Think find or remove for NN */ -static struct pktgen_dev *__pktgen_NN_threads(const char *ifname, int remove) +static struct pktgen_dev *__pktgen_NN_threads(const struct pktgen_net *pn, +					      const char *ifname, int remove)  {  	struct pktgen_thread *t;  	struct pktgen_dev *pkt_dev = NULL;  	bool exact = (remove == FIND); -	list_for_each_entry(t, &pktgen_threads, th_list) { +	list_for_each_entry(t, &pn->pktgen_threads, th_list) {  		pkt_dev = pktgen_find_dev(t, ifname, exact);  		if (pkt_dev) {  			if (remove) { @@ -1909,7 +1892,7 @@ static struct pktgen_dev *__pktgen_NN_threads(const char *ifname, int remove)  /*   * mark a device for removal   */ -static void pktgen_mark_device(const char *ifname) +static void pktgen_mark_device(const struct pktgen_net *pn, const char *ifname)  {  	struct pktgen_dev *pkt_dev = NULL;  	const int max_tries = 10, msec_per_try = 125; @@ -1920,7 +1903,7 @@ static void pktgen_mark_device(const char *ifname)  	while (1) { -		pkt_dev = __pktgen_NN_threads(ifname, REMOVE); +		pkt_dev = __pktgen_NN_threads(pn, ifname, REMOVE);  		if (pkt_dev == NULL)  			break;	/* success */ @@ -1941,21 +1924,21 @@ static void pktgen_mark_device(const char *ifname)  	mutex_unlock(&pktgen_thread_lock);  } -static void pktgen_change_name(struct net_device *dev) +static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *dev)  {  	struct pktgen_thread *t; -	list_for_each_entry(t, &pktgen_threads, th_list) { +	list_for_each_entry(t, &pn->pktgen_threads, th_list) {  		struct pktgen_dev *pkt_dev;  		list_for_each_entry(pkt_dev, &t->if_list, list) {  			if (pkt_dev->odev != dev)  				continue; -			remove_proc_entry(pkt_dev->entry->name, pg_proc_dir); +			proc_remove(pkt_dev->entry);  			pkt_dev->entry = proc_create_data(dev->name, 0600, -							  pg_proc_dir, +							  pn->proc_dir,  							  &pktgen_if_fops,  							  pkt_dev);  			if (!pkt_dev->entry) @@ -1969,9 +1952,10 @@ static void pktgen_change_name(struct net_device *dev)  static int pktgen_device_event(struct notifier_block *unused,  			       unsigned long event, void *ptr)  { -	struct net_device *dev = ptr; +	struct net_device *dev = netdev_notifier_info_to_dev(ptr); +	struct pktgen_net *pn = net_generic(dev_net(dev), pg_net_id); -	if (!net_eq(dev_net(dev), &init_net)) +	if (pn->pktgen_exiting)  		return NOTIFY_DONE;  	/* It is OK that we do not hold the group lock right now, @@ -1980,18 +1964,19 @@ static int pktgen_device_event(struct notifier_block *unused,  	switch (event) {  	case NETDEV_CHANGENAME: -		pktgen_change_name(dev); +		pktgen_change_name(pn, dev);  		break;  	case NETDEV_UNREGISTER: -		pktgen_mark_device(dev->name); +		pktgen_mark_device(pn, dev->name);  		break;  	}  	return NOTIFY_DONE;  } -static struct net_device *pktgen_dev_get_by_name(struct pktgen_dev *pkt_dev, +static struct net_device *pktgen_dev_get_by_name(const struct pktgen_net *pn, +						 struct pktgen_dev *pkt_dev,  						 const char *ifname)  {  	char b[IFNAMSIZ+5]; @@ -2005,13 +1990,14 @@ static struct net_device *pktgen_dev_get_by_name(struct pktgen_dev *pkt_dev,  	}  	b[i] = 0; -	return dev_get_by_name(&init_net, b); +	return dev_get_by_name(pn->net, b);  }  /* Associate pktgen_dev with a device. */ -static int pktgen_setup_dev(struct pktgen_dev *pkt_dev, const char *ifname) +static int pktgen_setup_dev(const struct pktgen_net *pn, +			    struct pktgen_dev *pkt_dev, const char *ifname)  {  	struct net_device *odev;  	int err; @@ -2022,7 +2008,7 @@ static int pktgen_setup_dev(struct pktgen_dev *pkt_dev, const char *ifname)  		pkt_dev->odev = NULL;  	} -	odev = pktgen_dev_get_by_name(pkt_dev, ifname); +	odev = pktgen_dev_get_by_name(pn, pkt_dev, ifname);  	if (!odev) {  		pr_err("no such netdevice: \"%s\"\n", ifname);  		return -ENODEV; @@ -2064,36 +2050,34 @@ 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. */  	if (is_zero_ether_addr(pkt_dev->src_mac)) -		memcpy(&(pkt_dev->hh[6]), pkt_dev->odev->dev_addr, ETH_ALEN); +		ether_addr_copy(&(pkt_dev->hh[6]), pkt_dev->odev->dev_addr);  	/* Set up Dest MAC */ -	memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN); - -	/* Set up pkt size */ -	pkt_dev->cur_pkt_size = pkt_dev->min_pkt_size; +	ether_addr_copy(&(pkt_dev->hh[0]), pkt_dev->dst_mac);  	if (pkt_dev->flags & F_IPV6) { -		/* -		 * Skip this automatic address setting until locks or functions -		 * gets exported -		 */ - -#ifdef NOTNOW  		int i, set = 0, err = 1;  		struct inet6_dev *idev; +		if (pkt_dev->min_pkt_size == 0) { +			pkt_dev->min_pkt_size = 14 + sizeof(struct ipv6hdr) +						+ sizeof(struct udphdr) +						+ sizeof(struct pktgen_hdr) +						+ pkt_dev->pkt_overhead; +		} +  		for (i = 0; i < IN6_ADDR_HSIZE; i++)  			if (pkt_dev->cur_in6_saddr.s6_addr[i]) {  				set = 1; @@ -2114,13 +2098,10 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)  				struct inet6_ifaddr *ifp;  				read_lock_bh(&idev->lock); -				for (ifp = idev->addr_list; ifp; -				     ifp = ifp->if_next) { -					if (ifp->scope == IFA_LINK && +				list_for_each_entry(ifp, &idev->addr_list, if_list) { +					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;  					} @@ -2131,8 +2112,14 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)  			if (err)  				pr_err("ERROR: IPv6 link address not available\n");  		} -#endif  	} else { +		if (pkt_dev->min_pkt_size == 0) { +			pkt_dev->min_pkt_size = 14 + sizeof(struct iphdr) +						+ sizeof(struct udphdr) +						+ sizeof(struct pktgen_hdr) +						+ pkt_dev->pkt_overhead; +		} +  		pkt_dev->saddr_min = 0;  		pkt_dev->saddr_max = 0;  		if (strlen(pkt_dev->src_min) == 0) { @@ -2158,6 +2145,10 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)  		pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);  	}  	/* Initialize current values. */ +	pkt_dev->cur_pkt_size = pkt_dev->min_pkt_size; +	if (pkt_dev->min_pkt_size > pkt_dev->max_pkt_size) +		pkt_dev->max_pkt_size = pkt_dev->min_pkt_size; +  	pkt_dev->cur_dst_mac_offset = 0;  	pkt_dev->cur_src_mac_offset = 0;  	pkt_dev->cur_saddr = pkt_dev->saddr_min; @@ -2183,10 +2174,13 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)  		return;  	} -	start_time = ktime_now(); -	if (remaining < 100000) -		ndelay(remaining);	/* really small just spin */ -	else { +	start_time = ktime_get(); +	if (remaining < 100000) { +		/* for small delays (<100us), just loop until limit is reached */ +		do { +			end_time = ktime_get(); +		} while (ktime_compare(end_time, spin_until) < 0); +	} else {  		/* see do_nanosleep */  		hrtimer_init_sleeper(&t, current);  		do { @@ -2201,8 +2195,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_get();  	} -	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); @@ -2235,7 +2229,7 @@ static inline int f_pick(struct pktgen_dev *pkt_dev)  				pkt_dev->curfl = 0; /*reset */  		}  	} else { -		flow = random32() % pkt_dev->cflows; +		flow = prandom_u32() % pkt_dev->cflows;  		pkt_dev->curfl = flow;  		if (pkt_dev->flows[flow].count > pkt_dev->lflow) { @@ -2256,14 +2250,23 @@ static inline int f_pick(struct pktgen_dev *pkt_dev)  static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)  {  	struct xfrm_state *x = pkt_dev->flows[flow].x; +	struct pktgen_net *pn = net_generic(dev_net(pkt_dev->odev), pg_net_id);  	if (!x) { -		/*slow path: we dont already have xfrm_state*/ -		x = xfrm_stateonly_find(&init_net, DUMMY_MARK, -					(xfrm_address_t *)&pkt_dev->cur_daddr, -					(xfrm_address_t *)&pkt_dev->cur_saddr, -					AF_INET, -					pkt_dev->ipsmode, -					pkt_dev->ipsproto, 0); + +		if (pkt_dev->spi) { +			/* We need as quick as possible to find the right SA +			 * Searching with minimum criteria to archieve this. +			 */ +			x = xfrm_state_lookup_byspi(pn->net, htonl(pkt_dev->spi), AF_INET); +		} else { +			/* slow path: we dont already have xfrm_state */ +			x = xfrm_stateonly_find(pn->net, DUMMY_MARK, +						(xfrm_address_t *)&pkt_dev->cur_daddr, +						(xfrm_address_t *)&pkt_dev->cur_saddr, +						AF_INET, +						pkt_dev->ipsmode, +						pkt_dev->ipsproto, 0); +		}  		if (x) {  			pkt_dev->flows[flow].x = x;  			set_pkt_overhead(pkt_dev); @@ -2282,7 +2285,7 @@ static void set_cur_queue_map(struct pktgen_dev *pkt_dev)  	else if (pkt_dev->queue_map_min <= pkt_dev->queue_map_max) {  		__u16 t;  		if (pkt_dev->flags & F_QUEUE_MAP_RND) { -			t = random32() % +			t = prandom_u32() %  				(pkt_dev->queue_map_max -  				 pkt_dev->queue_map_min + 1)  				+ pkt_dev->queue_map_min; @@ -2314,7 +2317,7 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)  		__u32 tmp;  		if (pkt_dev->flags & F_MACSRC_RND) -			mc = random32() % pkt_dev->src_mac_count; +			mc = prandom_u32() % pkt_dev->src_mac_count;  		else {  			mc = pkt_dev->cur_src_mac_offset++;  			if (pkt_dev->cur_src_mac_offset >= @@ -2340,7 +2343,7 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)  		__u32 tmp;  		if (pkt_dev->flags & F_MACDST_RND) -			mc = random32() % pkt_dev->dst_mac_count; +			mc = prandom_u32() % pkt_dev->dst_mac_count;  		else {  			mc = pkt_dev->cur_dst_mac_offset++; @@ -2363,25 +2366,25 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)  	}  	if (pkt_dev->flags & F_MPLS_RND) { -		unsigned i; +		unsigned int 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 | -					     ((__force __be32)random32() & +					     ((__force __be32)prandom_u32() &  						      htonl(0x000fffff));  	}  	if ((pkt_dev->flags & F_VID_RND) && (pkt_dev->vlan_id != 0xffff)) { -		pkt_dev->vlan_id = random32() & (4096-1); +		pkt_dev->vlan_id = prandom_u32() & (4096 - 1);  	}  	if ((pkt_dev->flags & F_SVID_RND) && (pkt_dev->svlan_id != 0xffff)) { -		pkt_dev->svlan_id = random32() & (4096 - 1); +		pkt_dev->svlan_id = prandom_u32() & (4096 - 1);  	}  	if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {  		if (pkt_dev->flags & F_UDPSRC_RND) -			pkt_dev->cur_udp_src = random32() % +			pkt_dev->cur_udp_src = prandom_u32() %  				(pkt_dev->udp_src_max - pkt_dev->udp_src_min)  				+ pkt_dev->udp_src_min; @@ -2394,7 +2397,7 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)  	if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) {  		if (pkt_dev->flags & F_UDPDST_RND) { -			pkt_dev->cur_udp_dst = random32() % +			pkt_dev->cur_udp_dst = prandom_u32() %  				(pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)  				+ pkt_dev->udp_dst_min;  		} else { @@ -2411,7 +2414,7 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)  		if (imn < imx) {  			__u32 t;  			if (pkt_dev->flags & F_IPSRC_RND) -				t = random32() % (imx - imn) + imn; +				t = prandom_u32() % (imx - imn) + imn;  			else {  				t = ntohl(pkt_dev->cur_saddr);  				t++; @@ -2432,17 +2435,15 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)  				__be32 s;  				if (pkt_dev->flags & F_IPDST_RND) { -					t = random32() % (imx - imn) + imn; -					s = htonl(t); - -					while (ipv4_is_loopback(s) || -					       ipv4_is_multicast(s) || -					       ipv4_is_lbcast(s) || -					       ipv4_is_zeronet(s) || -					       ipv4_is_local_multicast(s)) { -						t = random32() % (imx - imn) + imn; +					do { +						t = prandom_u32() % +							(imx - imn) + imn;  						s = htonl(t); -					} +					} while (ipv4_is_loopback(s) || +						ipv4_is_multicast(s) || +						ipv4_is_lbcast(s) || +						ipv4_is_zeronet(s) || +						ipv4_is_local_multicast(s));  					pkt_dev->cur_daddr = s;  				} else {  					t = ntohl(pkt_dev->cur_daddr); @@ -2466,18 +2467,14 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)  		}  	} else {		/* IPV6 * */ -		if (pkt_dev->min_in6_daddr.s6_addr32[0] == 0 && -		    pkt_dev->min_in6_daddr.s6_addr32[1] == 0 && -		    pkt_dev->min_in6_daddr.s6_addr32[2] == 0 && -		    pkt_dev->min_in6_daddr.s6_addr32[3] == 0) ; -		else { +		if (!ipv6_addr_any(&pkt_dev->min_in6_daddr)) {  			int i;  			/* Only random destinations yet */  			for (i = 0; i < 4; i++) {  				pkt_dev->cur_in6_daddr.s6_addr32[i] = -				    (((__force __be32)random32() | +				    (((__force __be32)prandom_u32() |  				      pkt_dev->min_in6_daddr.s6_addr32[i]) &  				     pkt_dev->max_in6_daddr.s6_addr32[i]);  			} @@ -2487,7 +2484,7 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)  	if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) {  		__u32 t;  		if (pkt_dev->flags & F_TXSIZE_RND) { -			t = random32() % +			t = prandom_u32() %  				(pkt_dev->max_pkt_size - pkt_dev->min_pkt_size)  				+ pkt_dev->min_pkt_size;  		} else { @@ -2505,33 +2502,47 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)  #ifdef CONFIG_XFRM +static u32 pktgen_dst_metrics[RTAX_MAX + 1] = { + +	[RTAX_HOPLIMIT] = 0x5, /* Set a static hoplimit */ +}; +  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; +	struct net *net = dev_net(pkt_dev->odev);  	if (!x)  		return 0;  	/* XXX: we dont support tunnel mode for now until  	 * we resolve the dst issue */ -	if (x->props.mode != XFRM_MODE_TRANSPORT) +	if ((x->props.mode != XFRM_MODE_TRANSPORT) && (pkt_dev->spi == 0))  		return 0; -	spin_lock(&x->lock); -	iph = ip_hdr(skb); +	/* But when user specify an valid SPI, transformation +	 * supports both transport/tunnel mode + ESP/AH type. +	 */ +	if ((x->props.mode == XFRM_MODE_TUNNEL) && (pkt_dev->spi != 0)) +		skb->_skb_refdst = (unsigned long)&pkt_dev->dst | SKB_DST_NOREF; +	rcu_read_lock_bh();  	err = x->outer_mode->output(x, skb); -	if (err) +	rcu_read_unlock_bh(); +	if (err) { +		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR);  		goto error; +	}  	err = x->type->output(x, skb); -	if (err) +	if (err) { +		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEPROTOERROR);  		goto error; - +	} +	spin_lock_bh(&x->lock);  	x->curlft.bytes += skb->len;  	x->curlft.packets++; +	spin_unlock_bh(&x->lock);  error: -	spin_unlock(&x->lock);  	return err;  } @@ -2559,6 +2570,8 @@ static int process_ipsec(struct pktgen_dev *pkt_dev,  		if (x) {  			int ret;  			__u8 *eth; +			struct iphdr *iph; +  			nhead = x->props.header_len - skb_headroom(skb);  			if (nhead > 0) {  				ret = pskb_expand_head(skb, nhead, 0, GFP_ATOMIC); @@ -2580,6 +2593,11 @@ static int process_ipsec(struct pktgen_dev *pkt_dev,  			eth = (__u8 *) skb_push(skb, ETH_HLEN);  			memcpy(eth, pkt_dev->hh, 12);  			*(u16 *) ð[12] = protocol; + +			/* Update IPv4 header len as well as checksum value */ +			iph = ip_hdr(skb); +			iph->tot_len = htons(skb->len - ETH_HLEN); +			ip_send_check(iph);  		}  	}  	return 1; @@ -2591,7 +2609,7 @@ err:  static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)  { -	unsigned i; +	unsigned int i;  	for (i = 0; i < pkt_dev->nr_labels; i++)  		*mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM; @@ -2605,6 +2623,95 @@ 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(×tamp); +	pgh->tv_sec = htonl(timestamp.tv_sec); +	pgh->tv_usec = htonl(timestamp.tv_usec); +} + +static struct sk_buff *pktgen_alloc_skb(struct net_device *dev, +					struct pktgen_dev *pkt_dev, +					unsigned int extralen) +{ +	struct sk_buff *skb = NULL; +	unsigned int size = pkt_dev->cur_pkt_size + 64 + extralen + +			    pkt_dev->pkt_overhead; + +	if (pkt_dev->flags & F_NODE) { +		int node = pkt_dev->node >= 0 ? pkt_dev->node : numa_node_id(); + +		skb = __alloc_skb(NET_SKB_PAD + size, GFP_NOWAIT, 0, node); +		if (likely(skb)) { +			skb_reserve(skb, NET_SKB_PAD); +			skb->dev = dev; +		} +	} else { +		 skb = __netdev_alloc_skb(dev, size, GFP_NOWAIT); +	} + +	return skb; +} +  static struct sk_buff *fill_packet_ipv4(struct net_device *odev,  					struct pktgen_dev *pkt_dev)  { @@ -2613,7 +2720,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 */ @@ -2636,31 +2742,13 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,  	datalen = (odev->hard_header_len + 16) & ~0xf; -	if (pkt_dev->flags & F_NODE) { -		int node; - -		if (pkt_dev->node >= 0) -			node = pkt_dev->node; -		else -			node =  numa_node_id(); - -		skb = __alloc_skb(NET_SKB_PAD + pkt_dev->cur_pkt_size + 64 -				  + datalen + pkt_dev->pkt_overhead, GFP_NOWAIT, 0, node); -		if (likely(skb)) { -			skb_reserve(skb, NET_SKB_PAD); -			skb->dev = odev; -		} -	} -	else -	  skb = __netdev_alloc_skb(odev, -				   pkt_dev->cur_pkt_size + 64 -				   + datalen + pkt_dev->pkt_overhead, GFP_NOWAIT); - +	skb = pktgen_alloc_skb(odev, pkt_dev, datalen);  	if (!skb) {  		sprintf(pkt_dev->result, "No memory");  		return NULL;  	} +	prefetchw(skb->data);  	skb_reserve(skb, datalen);  	/*  Reserve for ethernet and IP header  */ @@ -2686,28 +2774,28 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,  		*vlan_encapsulated_proto = htons(ETH_P_IP);  	} -	skb->network_header = skb->tail; -	skb->transport_header = skb->network_header + sizeof(struct iphdr); -	skb_put(skb, sizeof(struct iphdr) + sizeof(struct udphdr)); +	skb_set_mac_header(skb, 0); +	skb_set_network_header(skb, skb->len); +	iph = (struct iphdr *) skb_put(skb, sizeof(struct iphdr)); + +	skb_set_transport_header(skb, skb->len); +	udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));  	skb_set_queue_mapping(skb, queue_map);  	skb->priority = pkt_dev->skb_priority; -	iph = ip_hdr(skb); -	udph = udp_hdr(skb); -  	memcpy(eth, pkt_dev->hh, 12);  	*(__be16 *) & eth[12] = protocol;  	/* Eth + IPh + UDPh + mpls */  	datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -  		  pkt_dev->pkt_overhead; -	if (datalen < sizeof(struct pktgen_hdr)) +	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr))  		datalen = sizeof(struct pktgen_hdr);  	udph->source = htons(pkt_dev->cur_udp_src);  	udph->dest = htons(pkt_dev->cur_udp_dst);  	udph->len = htons(datalen + 8);	/* DATA + udphdr */ -	udph->check = 0;	/* No checksum */ +	udph->check = 0;  	iph->ihl = 5;  	iph->version = 4; @@ -2721,83 +2809,29 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,  	iph->frag_off = 0;  	iplen = 20 + 8 + datalen;  	iph->tot_len = htons(iplen); -	iph->check = 0; -	iph->check = ip_fast_csum((void *)iph, iph->ihl); +	ip_send_check(iph);  	skb->protocol = protocol; -	skb->mac_header = (skb->network_header - ETH_HLEN - -			   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)); +	if (!(pkt_dev->flags & F_UDPCSUM)) { +		skb->ip_summed = CHECKSUM_NONE; +	} else if (odev->features & NETIF_F_V4_CSUM) { +		skb->ip_summed = CHECKSUM_PARTIAL; +		skb->csum = 0; +		udp4_hwcsum(skb, udph->source, udph->dest);  	} 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; +		__wsum csum = udp_csum(skb); -			skb_shinfo(skb)->frags[i - 1].size -= rem; +		/* add protocol-dependent pseudo-header */ +		udph->check = csum_tcpudp_magic(udph->source, udph->dest, +						datalen + 8, IPPROTO_UDP, csum); -			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; -		} +		if (udph->check == 0) +			udph->check = CSUM_MANGLED_0;  	} -	/* 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(×tamp); -		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)) @@ -2807,179 +2841,14 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,  	return skb;  } -/* - * scan_ip6, fmt_ip taken from dietlibc-0.21 - * Author Felix von Leitner <felix-dietlibc@fefe.de> - * - * Slightly modified for kernel. - * Should be candidate for net/ipv4/utils.c - * --ro - */ - -static unsigned int scan_ip6(const char *s, char ip[16]) -{ -	unsigned int i; -	unsigned int len = 0; -	unsigned long u; -	char suffix[16]; -	unsigned int prefixlen = 0; -	unsigned int suffixlen = 0; -	__be32 tmp; -	char *pos; - -	for (i = 0; i < 16; i++) -		ip[i] = 0; - -	for (;;) { -		if (*s == ':') { -			len++; -			if (s[1] == ':') {	/* Found "::", skip to part 2 */ -				s += 2; -				len++; -				break; -			} -			s++; -		} - -		u = simple_strtoul(s, &pos, 16); -		i = pos - s; -		if (!i) -			return 0; -		if (prefixlen == 12 && s[i] == '.') { - -			/* the last 4 bytes may be written as IPv4 address */ - -			tmp = in_aton(s); -			memcpy((struct in_addr *)(ip + 12), &tmp, sizeof(tmp)); -			return i + len; -		} -		ip[prefixlen++] = (u >> 8); -		ip[prefixlen++] = (u & 255); -		s += i; -		len += i; -		if (prefixlen == 16) -			return len; -	} - -/* part 2, after "::" */ -	for (;;) { -		if (*s == ':') { -			if (suffixlen == 0) -				break; -			s++; -			len++; -		} else if (suffixlen != 0) -			break; - -		u = simple_strtol(s, &pos, 16); -		i = pos - s; -		if (!i) { -			if (*s) -				len--; -			break; -		} -		if (suffixlen + prefixlen <= 12 && s[i] == '.') { -			tmp = in_aton(s); -			memcpy((struct in_addr *)(suffix + suffixlen), &tmp, -			       sizeof(tmp)); -			suffixlen += 4; -			len += strlen(s); -			break; -		} -		suffix[suffixlen++] = (u >> 8); -		suffix[suffixlen++] = (u & 255); -		s += i; -		len += i; -		if (prefixlen + suffixlen == 16) -			break; -	} -	for (i = 0; i < suffixlen; i++) -		ip[16 - suffixlen + i] = suffix[i]; -	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)  {  	struct sk_buff *skb = NULL;  	__u8 *eth;  	struct udphdr *udph; -	int datalen; +	int datalen, udplen;  	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 */ @@ -3000,14 +2869,13 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,  	mod_cur_headers(pkt_dev);  	queue_map = pkt_dev->cur_queue_map; -	skb = __netdev_alloc_skb(odev, -				 pkt_dev->cur_pkt_size + 64 -				 + 16 + pkt_dev->pkt_overhead, GFP_NOWAIT); +	skb = pktgen_alloc_skb(odev, pkt_dev, 16);  	if (!skb) {  		sprintf(pkt_dev->result, "No memory");  		return NULL;  	} +	prefetchw(skb->data);  	skb_reserve(skb, 16);  	/*  Reserve for ethernet and IP header  */ @@ -3033,13 +2901,14 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,  		*vlan_encapsulated_proto = htons(ETH_P_IPV6);  	} -	skb->network_header = skb->tail; -	skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); -	skb_put(skb, sizeof(struct ipv6hdr) + sizeof(struct udphdr)); +	skb_set_mac_header(skb, 0); +	skb_set_network_header(skb, skb->len); +	iph = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr)); + +	skb_set_transport_header(skb, skb->len); +	udph = (struct udphdr *) skb_put(skb, sizeof(struct udphdr));  	skb_set_queue_mapping(skb, queue_map);  	skb->priority = pkt_dev->skb_priority; -	iph = ipv6_hdr(skb); -	udph = udp_hdr(skb);  	memcpy(eth, pkt_dev->hh, 12);  	*(__be16 *) ð[12] = protocol; @@ -3049,16 +2918,16 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,  		  sizeof(struct ipv6hdr) - sizeof(struct udphdr) -  		  pkt_dev->pkt_overhead; -	if (datalen < sizeof(struct pktgen_hdr)) { +	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr)) {  		datalen = sizeof(struct pktgen_hdr); -		if (net_ratelimit()) -			pr_info("increased datalen to %d\n", datalen); +		net_info_ratelimited("increased datalen to %d\n", datalen);  	} +	udplen = datalen + sizeof(struct udphdr);  	udph->source = htons(pkt_dev->cur_udp_src);  	udph->dest = htons(pkt_dev->cur_udp_dst); -	udph->len = htons(datalen + sizeof(struct udphdr)); -	udph->check = 0;	/* No checksum */ +	udph->len = htons(udplen); +	udph->check = 0;  	*(__be32 *) iph = htonl(0x60000000);	/* Version + flow */ @@ -3069,87 +2938,34 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,  	iph->hop_limit = 32; -	iph->payload_len = htons(sizeof(struct udphdr) + datalen); +	iph->payload_len = htons(udplen);  	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);  	skb->protocol = protocol;  	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; +	if (!(pkt_dev->flags & F_UDPCSUM)) { +		skb->ip_summed = CHECKSUM_NONE; +	} else if (odev->features & NETIF_F_V6_CSUM) { +		skb->ip_summed = CHECKSUM_PARTIAL; +		skb->csum_start = skb_transport_header(skb) - skb->head; +		skb->csum_offset = offsetof(struct udphdr, check); +		udph->check = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, 0); +	} else { +		__wsum csum = udp_csum(skb); -			skb_shinfo(skb)->frags[i - 1].size -= rem; +		/* add protocol-dependent pseudo-header */ +		udph->check = csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, csum); -			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; -		} +		if (udph->check == 0) +			udph->check = CSUM_MANGLED_0;  	} -	/* 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(×tamp); -		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;  } @@ -3193,8 +3009,7 @@ static void pktgen_run(struct pktgen_thread *t)  			pktgen_clear_counters(pkt_dev);  			pkt_dev->running = 1;	/* Cranke yeself! */  			pkt_dev->skb = NULL; -			pkt_dev->started_at = -				pkt_dev->next_tx = ktime_now(); +			pkt_dev->started_at = pkt_dev->next_tx = ktime_get();  			set_pkt_overhead(pkt_dev); @@ -3208,7 +3023,7 @@ static void pktgen_run(struct pktgen_thread *t)  		t->control &= ~(T_STOP);  } -static void pktgen_stop_all_threads_ifs(void) +static void pktgen_stop_all_threads_ifs(struct pktgen_net *pn)  {  	struct pktgen_thread *t; @@ -3216,7 +3031,7 @@ static void pktgen_stop_all_threads_ifs(void)  	mutex_lock(&pktgen_thread_lock); -	list_for_each_entry(t, &pktgen_threads, th_list) +	list_for_each_entry(t, &pn->pktgen_threads, th_list)  		t->control |= T_STOP;  	mutex_unlock(&pktgen_thread_lock); @@ -3252,28 +3067,28 @@ signal:  	return 0;  } -static int pktgen_wait_all_threads_run(void) +static int pktgen_wait_all_threads_run(struct pktgen_net *pn)  {  	struct pktgen_thread *t;  	int sig = 1;  	mutex_lock(&pktgen_thread_lock); -	list_for_each_entry(t, &pktgen_threads, th_list) { +	list_for_each_entry(t, &pn->pktgen_threads, th_list) {  		sig = pktgen_wait_thread_run(t);  		if (sig == 0)  			break;  	}  	if (sig == 0) -		list_for_each_entry(t, &pktgen_threads, th_list) +		list_for_each_entry(t, &pn->pktgen_threads, th_list)  			t->control |= (T_STOP);  	mutex_unlock(&pktgen_thread_lock);  	return sig;  } -static void pktgen_run_all_threads(void) +static void pktgen_run_all_threads(struct pktgen_net *pn)  {  	struct pktgen_thread *t; @@ -3281,7 +3096,7 @@ static void pktgen_run_all_threads(void)  	mutex_lock(&pktgen_thread_lock); -	list_for_each_entry(t, &pktgen_threads, th_list) +	list_for_each_entry(t, &pn->pktgen_threads, th_list)  		t->control |= (T_RUN);  	mutex_unlock(&pktgen_thread_lock); @@ -3289,10 +3104,10 @@ static void pktgen_run_all_threads(void)  	/* Propagate thread->control  */  	schedule_timeout_interruptible(msecs_to_jiffies(125)); -	pktgen_wait_all_threads_run(); +	pktgen_wait_all_threads_run(pn);  } -static void pktgen_reset_all_threads(void) +static void pktgen_reset_all_threads(struct pktgen_net *pn)  {  	struct pktgen_thread *t; @@ -3300,7 +3115,7 @@ static void pktgen_reset_all_threads(void)  	mutex_lock(&pktgen_thread_lock); -	list_for_each_entry(t, &pktgen_threads, th_list) +	list_for_each_entry(t, &pn->pktgen_threads, th_list)  		t->control |= (T_REMDEVALL);  	mutex_unlock(&pktgen_thread_lock); @@ -3308,7 +3123,7 @@ static void pktgen_reset_all_threads(void)  	/* Propagate thread->control  */  	schedule_timeout_interruptible(msecs_to_jiffies(125)); -	pktgen_wait_all_threads_run(); +	pktgen_wait_all_threads_run(pn);  }  static void show_results(struct pktgen_dev *pkt_dev, int nr_frags) @@ -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), @@ -3353,7 +3168,7 @@ static int pktgen_stop_device(struct pktgen_dev *pkt_dev)  	kfree_skb(pkt_dev->skb);  	pkt_dev->skb = NULL; -	pkt_dev->stopped_at = ktime_now(); +	pkt_dev->stopped_at = ktime_get();  	pkt_dev->running = 0;  	show_results(pkt_dev, nr_frags); @@ -3372,7 +3187,7 @@ static struct pktgen_dev *next_to_run(struct pktgen_thread *t)  			continue;  		if (best == NULL)  			best = pkt_dev; -		else if (ktime_lt(pkt_dev->next_tx, best->next_tx)) +		else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0)  			best = pkt_dev;  	}  	if_unlock(t); @@ -3450,21 +3265,19 @@ static void pktgen_rem_all_ifs(struct pktgen_thread *t)  static void pktgen_rem_thread(struct pktgen_thread *t)  {  	/* Remove from the thread list */ - -	remove_proc_entry(t->tsk->comm, pg_proc_dir); - +	remove_proc_entry(t->tsk->comm, t->net->proc_dir);  }  static void pktgen_resched(struct pktgen_dev *pkt_dev)  { -	ktime_t idle_start = ktime_now(); +	ktime_t idle_start = ktime_get();  	schedule(); -	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start)); +	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));  }  static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)  { -	ktime_t idle_start = ktime_now(); +	ktime_t idle_start = ktime_get();  	while (atomic_read(&(pkt_dev->skb->users)) != 1) {  		if (signal_pending(current)) @@ -3475,7 +3288,7 @@ static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)  		else  			cpu_relax();  	} -	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start)); +	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));  }  static void pktgen_xmit(struct pktgen_dev *pkt_dev) @@ -3497,7 +3310,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)  	 * "never transmit"  	 */  	if (unlikely(pkt_dev->delay == ULLONG_MAX)) { -		pkt_dev->next_tx = ktime_add_ns(ktime_now(), ULONG_MAX); +		pkt_dev->next_tx = ktime_add_ns(ktime_get(), ULONG_MAX);  		return;  	} @@ -3525,9 +3338,11 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)  	queue_map = skb_get_queue_mapping(pkt_dev->skb);  	txq = netdev_get_tx_queue(odev, queue_map); -	__netif_tx_lock_bh(txq); +	local_bh_disable(); -	if (unlikely(netif_tx_queue_frozen_or_stopped(txq))) { +	HARD_TX_LOCK(odev, txq, smp_processor_id()); + +	if (unlikely(netif_xmit_frozen_or_drv_stopped(txq))) {  		ret = NETDEV_TX_BUSY;  		pkt_dev->last_ok = 0;  		goto unlock; @@ -3550,9 +3365,8 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)  		pkt_dev->errors++;  		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); +		net_info_ratelimited("%s xmit error: %d\n", +				     pkt_dev->odevname, ret);  		pkt_dev->errors++;  		/* fallthru */  	case NETDEV_TX_LOCKED: @@ -3562,7 +3376,9 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)  		pkt_dev->last_ok = 0;  	}  unlock: -	__netif_tx_unlock_bh(txq); +	HARD_TX_UNLOCK(odev, txq); + +	local_bh_enable();  	/* If pkt_dev->count is zero, then run forever */  	if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { @@ -3599,7 +3415,7 @@ static int pktgen_thread_worker(void *arg)  		pkt_dev = next_to_run(t);  		if (unlikely(!pkt_dev && t->control == 0)) { -			if (pktgen_exiting) +			if (t->net->pktgen_exiting)  				break;  			wait_event_interruptible_timeout(t->queue,  							 t->control != 0, @@ -3721,7 +3537,7 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)  	/* We don't allow a device to be on several threads */ -	pkt_dev = __pktgen_NN_threads(ifname, FIND); +	pkt_dev = __pktgen_NN_threads(t->net, ifname, FIND);  	if (pkt_dev) {  		pr_err("ERROR: interface already used\n");  		return -EBUSY; @@ -3732,19 +3548,15 @@ 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; @@ -3761,11 +3572,13 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)  	pkt_dev->svlan_id = 0xffff;  	pkt_dev->node = -1; -	err = pktgen_setup_dev(pkt_dev, ifname); +	err = pktgen_setup_dev(t->net, 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, +	pkt_dev->entry = proc_create_data(ifname, 0600, t->net->proc_dir,  					  &pktgen_if_fops, pkt_dev);  	if (!pkt_dev->entry) {  		pr_err("cannot create %s/%s procfs entry\n", @@ -3776,6 +3589,17 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)  #ifdef CONFIG_XFRM  	pkt_dev->ipsmode = XFRM_MODE_TRANSPORT;  	pkt_dev->ipsproto = IPPROTO_ESP; + +	/* xfrm tunnel mode needs additional dst to extract outter +	 * ip header protocol/ttl/id field, here creat a phony one. +	 * instead of looking for a valid rt, which definitely hurting +	 * performance under such circumstance. +	 */ +	pkt_dev->dstops.family = AF_INET; +	pkt_dev->dst.dev = pkt_dev->odev; +	dst_init_metrics(&pkt_dev->dst, pktgen_dst_metrics, false); +	pkt_dev->dst.child = &pkt_dev->dst; +	pkt_dev->dst.ops = &pkt_dev->dstops;  #endif  	return add_dev_to_thread(t, pkt_dev); @@ -3790,7 +3614,7 @@ out1:  	return err;  } -static int __init pktgen_create_thread(int cpu) +static int __net_init pktgen_create_thread(int cpu, struct pktgen_net *pn)  {  	struct pktgen_thread *t;  	struct proc_dir_entry *pe; @@ -3808,10 +3632,13 @@ static int __init pktgen_create_thread(int cpu)  	INIT_LIST_HEAD(&t->if_list); -	list_add_tail(&t->th_list, &pktgen_threads); +	list_add_tail(&t->th_list, &pn->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); @@ -3821,7 +3648,7 @@ static int __init pktgen_create_thread(int cpu)  	kthread_bind(p, cpu);  	t->tsk = p; -	pe = proc_create_data(t->tsk->comm, 0600, pg_proc_dir, +	pe = proc_create_data(t->tsk->comm, 0600, pn->proc_dir,  			      &pktgen_thread_fops, t);  	if (!pe) {  		pr_err("cannot create %s/%s procfs entry\n", @@ -3832,6 +3659,7 @@ static int __init pktgen_create_thread(int cpu)  		return -EINVAL;  	} +	t->net = pn;  	wake_up_process(p);  	wait_for_completion(&t->start_done); @@ -3857,7 +3685,6 @@ static void _rem_dev_from_if_list(struct pktgen_thread *t,  static int pktgen_remove_device(struct pktgen_thread *t,  				struct pktgen_dev *pkt_dev)  { -  	pr_debug("remove_device pkt_dev=%p\n", pkt_dev);  	if (pkt_dev->running) { @@ -3877,77 +3704,114 @@ static int pktgen_remove_device(struct pktgen_thread *t,  	_rem_dev_from_if_list(t, pkt_dev);  	if (pkt_dev->entry) -		remove_proc_entry(pkt_dev->entry->name, pg_proc_dir); +		proc_remove(pkt_dev->entry);  #ifdef CONFIG_XFRM  	free_SAs(pkt_dev);  #endif  	vfree(pkt_dev->flows); +	if (pkt_dev->page) +		put_page(pkt_dev->page);  	kfree(pkt_dev);  	return 0;  } -static int __init pg_init(void) +static int __net_init pg_net_init(struct net *net)  { -	int cpu; +	struct pktgen_net *pn = net_generic(net, pg_net_id);  	struct proc_dir_entry *pe; - -	pr_info("%s", version); - -	pg_proc_dir = proc_mkdir(PG_PROC_DIR, init_net.proc_net); -	if (!pg_proc_dir) +	int cpu, ret = 0; + +	pn->net = net; +	INIT_LIST_HEAD(&pn->pktgen_threads); +	pn->pktgen_exiting = false; +	pn->proc_dir = proc_mkdir(PG_PROC_DIR, pn->net->proc_net); +	if (!pn->proc_dir) { +		pr_warn("cannot create /proc/net/%s\n", PG_PROC_DIR);  		return -ENODEV; - -	pe = proc_create(PGCTRL, 0600, pg_proc_dir, &pktgen_fops); +	} +	pe = proc_create(PGCTRL, 0600, pn->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; +		pr_err("cannot create %s procfs entry\n", PGCTRL); +		ret = -EINVAL; +		goto remove;  	} -	/* Register us to receive netdevice events */ -	register_netdevice_notifier(&pktgen_notifier_block); -  	for_each_online_cpu(cpu) {  		int err; -		err = pktgen_create_thread(cpu); +		err = pktgen_create_thread(cpu, pn);  		if (err) -			pr_warning("WARNING: Cannot create thread for cpu %d (%d)\n", +			pr_warn("Cannot create thread for cpu %d (%d)\n",  				   cpu, err);  	} -	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; +	if (list_empty(&pn->pktgen_threads)) { +		pr_err("Initialization failed for all threads\n"); +		ret = -ENODEV; +		goto remove_entry;  	}  	return 0; + +remove_entry: +	remove_proc_entry(PGCTRL, pn->proc_dir); +remove: +	remove_proc_entry(PG_PROC_DIR, pn->net->proc_net); +	return ret;  } -static void __exit pg_cleanup(void) +static void __net_exit pg_net_exit(struct net *net)  { +	struct pktgen_net *pn = net_generic(net, pg_net_id);  	struct pktgen_thread *t;  	struct list_head *q, *n; +	LIST_HEAD(list);  	/* Stop all interfaces & threads */ -	pktgen_exiting = true; +	pn->pktgen_exiting = true; -	list_for_each_safe(q, n, &pktgen_threads) { +	mutex_lock(&pktgen_thread_lock); +	list_splice_init(&pn->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);  	} -	/* Un-register us from receiving netdevice events */ -	unregister_netdevice_notifier(&pktgen_notifier_block); +	remove_proc_entry(PGCTRL, pn->proc_dir); +	remove_proc_entry(PG_PROC_DIR, pn->net->proc_net); +} + +static struct pernet_operations pg_net_ops = { +	.init = pg_net_init, +	.exit = pg_net_exit, +	.id   = &pg_net_id, +	.size = sizeof(struct pktgen_net), +}; + +static int __init pg_init(void) +{ +	int ret = 0; + +	pr_info("%s", version); +	ret = register_pernet_subsys(&pg_net_ops); +	if (ret) +		return ret; +	ret = register_netdevice_notifier(&pktgen_notifier_block); +	if (ret) +		unregister_pernet_subsys(&pg_net_ops); -	/* Clean up proc file system */ -	remove_proc_entry(PGCTRL, pg_proc_dir); -	proc_net_remove(&init_net, PG_PROC_DIR); +	return ret; +} + +static void __exit pg_cleanup(void) +{ +	unregister_netdevice_notifier(&pktgen_notifier_block); +	unregister_pernet_subsys(&pg_net_ops);  }  module_init(pg_init);  | 
