xref: /openbmc/linux/net/core/pktgen.c (revision b181f7029bd71238ac2754ce7052dffd69432085)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Authors:
41da177e4SLinus Torvalds  * Copyright 2001, 2002 by Robert Olsson <robert.olsson@its.uu.se>
51da177e4SLinus Torvalds  *                             Uppsala University and
61da177e4SLinus Torvalds  *                             Swedish University of Agricultural Sciences
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * Alexey Kuznetsov  <kuznet@ms2.inr.ac.ru>
91da177e4SLinus Torvalds  * Ben Greear <greearb@candelatech.com>
1096de0e25SJan Engelhardt  * Jens Låås <jens.laas@data.slu.se>
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  * A tool for loading the network with preconfigurated packets.
131da177e4SLinus Torvalds  * The tool is implemented as a linux module.  Parameters are output
141da177e4SLinus Torvalds  * device, delay (to hard_xmit), number of packets, and whether
151da177e4SLinus Torvalds  * to use multiple SKBs or just the same one.
161da177e4SLinus Torvalds  * pktgen uses the installed interface's output routine.
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  * Additional hacking by:
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  * Jens.Laas@data.slu.se
211da177e4SLinus Torvalds  * Improved by ANK. 010120.
221da177e4SLinus Torvalds  * Improved by ANK even more. 010212.
231da177e4SLinus Torvalds  * MAC address typo fixed. 010417 --ro
241da177e4SLinus Torvalds  * Integrated.  020301 --DaveM
251da177e4SLinus Torvalds  * Added multiskb option 020301 --DaveM
261da177e4SLinus Torvalds  * Scaling of results. 020417--sigurdur@linpro.no
271da177e4SLinus Torvalds  * Significant re-work of the module:
281da177e4SLinus Torvalds  *   *  Convert to threaded model to more efficiently be able to transmit
291da177e4SLinus Torvalds  *       and receive on multiple interfaces at once.
301da177e4SLinus Torvalds  *   *  Converted many counters to __u64 to allow longer runs.
311da177e4SLinus Torvalds  *   *  Allow configuration of ranges, like min/max IP address, MACs,
321da177e4SLinus Torvalds  *       and UDP-ports, for both source and destination, and can
331da177e4SLinus Torvalds  *       set to use a random distribution or sequentially walk the range.
341da177e4SLinus Torvalds  *   *  Can now change most values after starting.
351da177e4SLinus Torvalds  *   *  Place 12-byte packet in UDP payload with magic number,
361da177e4SLinus Torvalds  *       sequence number, and timestamp.
371da177e4SLinus Torvalds  *   *  Add receiver code that detects dropped pkts, re-ordered pkts, and
381da177e4SLinus Torvalds  *       latencies (with micro-second) precision.
391da177e4SLinus Torvalds  *   *  Add IOCTL interface to easily get counters & configuration.
401da177e4SLinus Torvalds  *   --Ben Greear <greearb@candelatech.com>
411da177e4SLinus Torvalds  *
421da177e4SLinus Torvalds  * Renamed multiskb to clone_skb and cleaned up sending core for two distinct
431da177e4SLinus Torvalds  * skb modes. A clone_skb=0 mode for Ben "ranges" work and a clone_skb != 0
441da177e4SLinus Torvalds  * as a "fastpath" with a configurable number of clones after alloc's.
451da177e4SLinus Torvalds  * clone_skb=0 means all packets are allocated this also means ranges time
461da177e4SLinus Torvalds  * stamps etc can be used. clone_skb=100 means 1 malloc is followed by 100
471da177e4SLinus Torvalds  * clones.
481da177e4SLinus Torvalds  *
491da177e4SLinus Torvalds  * Also moved to /proc/net/pktgen/
501da177e4SLinus Torvalds  * --ro
511da177e4SLinus Torvalds  *
521da177e4SLinus Torvalds  * Sept 10:  Fixed threading/locking.  Lots of bone-headed and more clever
531da177e4SLinus Torvalds  *    mistakes.  Also merged in DaveM's patch in the -pre6 patch.
541da177e4SLinus Torvalds  * --Ben Greear <greearb@candelatech.com>
551da177e4SLinus Torvalds  *
561da177e4SLinus Torvalds  * Integrated to 2.5.x 021029 --Lucio Maciel (luciomaciel@zipmail.com.br)
571da177e4SLinus Torvalds  *
581da177e4SLinus Torvalds  * 021124 Finished major redesign and rewrite for new functionality.
59c1e4535fSMauro Carvalho Chehab  * See Documentation/networking/pktgen.rst for how to use this.
601da177e4SLinus Torvalds  *
611da177e4SLinus Torvalds  * The new operation:
621da177e4SLinus Torvalds  * For each CPU one thread/process is created at start. This process checks
631da177e4SLinus Torvalds  * for running devices in the if_list and sends packets until count is 0 it
641da177e4SLinus Torvalds  * also the thread checks the thread->control which is used for inter-process
651da177e4SLinus Torvalds  * communication. controlling process "posts" operations to the threads this
668788370aSJesper Dangaard Brouer  * way.
678788370aSJesper Dangaard Brouer  * The if_list is RCU protected, and the if_lock remains to protect updating
688788370aSJesper Dangaard Brouer  * of if_list, from "add_device" as it invoked from userspace (via proc write).
691da177e4SLinus Torvalds  *
701da177e4SLinus Torvalds  * By design there should only be *one* "controlling" process. In practice
711da177e4SLinus Torvalds  * multiple write accesses gives unpredictable result. Understood by "write"
721da177e4SLinus Torvalds  * to /proc gives result code thats should be read be the "writer".
73b4099fabSStephen Hemminger  * For practical use this should be no problem.
741da177e4SLinus Torvalds  *
751da177e4SLinus Torvalds  * Note when adding devices to a specific CPU there good idea to also assign
761da177e4SLinus Torvalds  * /proc/irq/XX/smp_affinity so TX-interrupts gets bound to the same CPU.
771da177e4SLinus Torvalds  * --ro
781da177e4SLinus Torvalds  *
791da177e4SLinus Torvalds  * Fix refcount off by one if first packet fails, potential null deref,
801da177e4SLinus Torvalds  * memleak 030710- KJP
811da177e4SLinus Torvalds  *
821da177e4SLinus Torvalds  * First "ranges" functionality for ipv6 030726 --ro
831da177e4SLinus Torvalds  *
841da177e4SLinus Torvalds  * Included flow support. 030802 ANK.
851da177e4SLinus Torvalds  *
861da177e4SLinus Torvalds  * Fixed unaligned access on IA-64 Grant Grundler <grundler@parisc-linux.org>
871da177e4SLinus Torvalds  *
881da177e4SLinus Torvalds  * Remove if fix from added Harald Welte <laforge@netfilter.org> 040419
891da177e4SLinus Torvalds  * ia64 compilation fix from  Aron Griffis <aron@hp.com> 040604
901da177e4SLinus Torvalds  *
911da177e4SLinus Torvalds  * New xmit() return, do_div and misc clean up by Stephen Hemminger
921da177e4SLinus Torvalds  * <shemminger@osdl.org> 040923
931da177e4SLinus Torvalds  *
94ca9f1fd2SStephen Hemminger  * Randy Dunlap fixed u64 printk compiler warning
951da177e4SLinus Torvalds  *
961da177e4SLinus Torvalds  * Remove FCS from BW calculation.  Lennert Buytenhek <buytenh@wantstofly.org>
971da177e4SLinus Torvalds  * New time handling. Lennert Buytenhek <buytenh@wantstofly.org> 041213
981da177e4SLinus Torvalds  *
991da177e4SLinus Torvalds  * Corrections from Nikolai Malykh (nmalykh@bilim.com)
1001da177e4SLinus Torvalds  * Removed unused flags F_SET_SRCMAC & F_SET_SRCIP 041230
1011da177e4SLinus Torvalds  *
1021da177e4SLinus Torvalds  * interruptible_sleep_on_timeout() replaced Nishanth Aravamudan <nacc@us.ibm.com>
1031da177e4SLinus Torvalds  * 050103
104ca6549afSSteven Whitehouse  *
105ca6549afSSteven Whitehouse  * MPLS support by Steven Whitehouse <steve@chygwyn.com>
106ca6549afSSteven Whitehouse  *
10734954ddcSFrancesco Fondelli  * 802.1Q/Q-in-Q support by Francesco Fondelli (FF) <francesco.fondelli@gmail.com>
10834954ddcSFrancesco Fondelli  *
109ce5d0b47SAdit Ranadive  * Fixed src_mac command to set source mac of packet to value specified in
110ce5d0b47SAdit Ranadive  * command by Adit Ranadive <adit.262@gmail.com>
1111da177e4SLinus Torvalds  */
112f9467eaeSJoe Perches 
113f9467eaeSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
114f9467eaeSJoe Perches 
1151da177e4SLinus Torvalds #include <linux/sys.h>
1161da177e4SLinus Torvalds #include <linux/types.h>
1171da177e4SLinus Torvalds #include <linux/module.h>
1181da177e4SLinus Torvalds #include <linux/moduleparam.h>
1191da177e4SLinus Torvalds #include <linux/kernel.h>
120222fa076SLuiz Capitulino #include <linux/mutex.h>
1211da177e4SLinus Torvalds #include <linux/sched.h>
1221da177e4SLinus Torvalds #include <linux/slab.h>
1231da177e4SLinus Torvalds #include <linux/vmalloc.h>
1241da177e4SLinus Torvalds #include <linux/unistd.h>
1251da177e4SLinus Torvalds #include <linux/string.h>
1261da177e4SLinus Torvalds #include <linux/ptrace.h>
1271da177e4SLinus Torvalds #include <linux/errno.h>
1281da177e4SLinus Torvalds #include <linux/ioport.h>
1291da177e4SLinus Torvalds #include <linux/interrupt.h>
1304fc268d2SRandy Dunlap #include <linux/capability.h>
1312bc481cfSStephen Hemminger #include <linux/hrtimer.h>
13209fe3ef4SAndrew Morton #include <linux/freezer.h>
1331da177e4SLinus Torvalds #include <linux/delay.h>
1341da177e4SLinus Torvalds #include <linux/timer.h>
135cdcdbe0bSLuiz Capitulino #include <linux/list.h>
1361da177e4SLinus Torvalds #include <linux/init.h>
1371da177e4SLinus Torvalds #include <linux/skbuff.h>
1381da177e4SLinus Torvalds #include <linux/netdevice.h>
1391da177e4SLinus Torvalds #include <linux/inet.h>
1401da177e4SLinus Torvalds #include <linux/inetdevice.h>
1411da177e4SLinus Torvalds #include <linux/rtnetlink.h>
1421da177e4SLinus Torvalds #include <linux/if_arp.h>
14334954ddcSFrancesco Fondelli #include <linux/if_vlan.h>
1441da177e4SLinus Torvalds #include <linux/in.h>
1451da177e4SLinus Torvalds #include <linux/ip.h>
1461da177e4SLinus Torvalds #include <linux/ipv6.h>
1471da177e4SLinus Torvalds #include <linux/udp.h>
1481da177e4SLinus Torvalds #include <linux/proc_fs.h>
149d50a6b56SStephen Hemminger #include <linux/seq_file.h>
1501da177e4SLinus Torvalds #include <linux/wait.h>
151f404e9a6SKris Katterjohn #include <linux/etherdevice.h>
152ee74baa7SDavid S. Miller #include <linux/kthread.h>
153268bb0ceSLinus Torvalds #include <linux/prefetch.h>
15498fa15f3SAnshuman Khandual #include <linux/mmzone.h>
155457c4cbcSEric W. Biederman #include <net/net_namespace.h>
1561da177e4SLinus Torvalds #include <net/checksum.h>
1571da177e4SLinus Torvalds #include <net/ipv6.h>
158c26bf4a5SThomas Graf #include <net/udp.h>
15973d94e94SStephen Rothwell #include <net/ip6_checksum.h>
1601da177e4SLinus Torvalds #include <net/addrconf.h>
161a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
162a553e4a6SJamal Hadi Salim #include <net/xfrm.h>
163a553e4a6SJamal Hadi Salim #endif
1644e58a027SCong Wang #include <net/netns/generic.h>
1651da177e4SLinus Torvalds #include <asm/byteorder.h>
1661da177e4SLinus Torvalds #include <linux/rcupdate.h>
1671977f032SJiri Slaby #include <linux/bitops.h>
16863adc6fbSStephen Hemminger #include <linux/io.h>
16963adc6fbSStephen Hemminger #include <linux/timex.h>
17063adc6fbSStephen Hemminger #include <linux/uaccess.h>
1711da177e4SLinus Torvalds #include <asm/dma.h>
1721da177e4SLinus Torvalds #include <asm/div64.h>		/* do_div */
1731da177e4SLinus Torvalds 
17440207264SJesper Dangaard Brouer #define VERSION	"2.75"
1751da177e4SLinus Torvalds #define IP_NAME_SZ 32
176ca6549afSSteven Whitehouse #define MAX_MPLS_LABELS 16 /* This is the max label stack depth */
177d5f1ce9aSStephen Hemminger #define MPLS_STACK_BOTTOM htonl(0x00000100)
17852a62f86SNick Richardson /* Max number of internet mix entries that can be specified in imix_weights. */
17952a62f86SNick Richardson #define MAX_IMIX_ENTRIES 20
18090149031SNick Richardson #define IMIX_PRECISION 100 /* Precision of IMIX distribution */
1811da177e4SLinus Torvalds 
182f9467eaeSJoe Perches #define func_enter() pr_debug("entering %s\n", __func__);
183f9467eaeSJoe Perches 
1846f107c74SDmitry Safonov #define PKT_FLAGS							\
1856f107c74SDmitry Safonov 	pf(IPV6)		/* Interface in IPV6 Mode */		\
1866f107c74SDmitry Safonov 	pf(IPSRC_RND)		/* IP-Src Random  */			\
1876f107c74SDmitry Safonov 	pf(IPDST_RND)		/* IP-Dst Random  */			\
1886f107c74SDmitry Safonov 	pf(TXSIZE_RND)		/* Transmit size is random */		\
1896f107c74SDmitry Safonov 	pf(UDPSRC_RND)		/* UDP-Src Random */			\
1906f107c74SDmitry Safonov 	pf(UDPDST_RND)		/* UDP-Dst Random */			\
1916f107c74SDmitry Safonov 	pf(UDPCSUM)		/* Include UDP checksum */		\
1926f107c74SDmitry Safonov 	pf(NO_TIMESTAMP)	/* Don't timestamp packets (default TS) */ \
1936f107c74SDmitry Safonov 	pf(MPLS_RND)		/* Random MPLS labels */		\
1946f107c74SDmitry Safonov 	pf(QUEUE_MAP_RND)	/* queue map Random */			\
1956f107c74SDmitry Safonov 	pf(QUEUE_MAP_CPU)	/* queue map mirrors smp_processor_id() */ \
1966f107c74SDmitry Safonov 	pf(FLOW_SEQ)		/* Sequential flows */			\
1976f107c74SDmitry Safonov 	pf(IPSEC)		/* ipsec on for flows */		\
1986f107c74SDmitry Safonov 	pf(MACSRC_RND)		/* MAC-Src Random */			\
1996f107c74SDmitry Safonov 	pf(MACDST_RND)		/* MAC-Dst Random */			\
2006f107c74SDmitry Safonov 	pf(VID_RND)		/* Random VLAN ID */			\
2016f107c74SDmitry Safonov 	pf(SVID_RND)		/* Random SVLAN ID */			\
2026f107c74SDmitry Safonov 	pf(NODE)		/* Node memory alloc*/			\
2036f107c74SDmitry Safonov 
2046f107c74SDmitry Safonov #define pf(flag)		flag##_SHIFT,
2056f107c74SDmitry Safonov enum pkt_flags {
2066f107c74SDmitry Safonov 	PKT_FLAGS
2076f107c74SDmitry Safonov };
2086f107c74SDmitry Safonov #undef pf
2096f107c74SDmitry Safonov 
2101da177e4SLinus Torvalds /* Device flag bits */
2116f107c74SDmitry Safonov #define pf(flag)		static const __u32 F_##flag = (1<<flag##_SHIFT);
2126f107c74SDmitry Safonov PKT_FLAGS
2136f107c74SDmitry Safonov #undef pf
2141da177e4SLinus Torvalds 
21599c6d3d2SDmitry Safonov #define pf(flag)		__stringify(flag),
21699c6d3d2SDmitry Safonov static char *pkt_flag_names[] = {
21799c6d3d2SDmitry Safonov 	PKT_FLAGS
21899c6d3d2SDmitry Safonov };
21999c6d3d2SDmitry Safonov #undef pf
22099c6d3d2SDmitry Safonov 
22199c6d3d2SDmitry Safonov #define NR_PKT_FLAGS		ARRAY_SIZE(pkt_flag_names)
22299c6d3d2SDmitry Safonov 
2231da177e4SLinus Torvalds /* Thread control flag bits */
2246b80d6a6SStephen Hemminger #define T_STOP        (1<<0)	/* Stop run */
2256b80d6a6SStephen Hemminger #define T_RUN         (1<<1)	/* Start run */
2266b80d6a6SStephen Hemminger #define T_REMDEVALL   (1<<2)	/* Remove all devs */
2276b80d6a6SStephen Hemminger #define T_REMDEV      (1<<3)	/* Remove one dev */
2281da177e4SLinus Torvalds 
22962f64aedSAlexei Starovoitov /* Xmit modes */
23062f64aedSAlexei Starovoitov #define M_START_XMIT		0	/* Default normal TX */
23162f64aedSAlexei Starovoitov #define M_NETIF_RECEIVE 	1	/* Inject packets into stack */
2320967f244SJohn Fastabend #define M_QUEUE_XMIT		2	/* Inject packet into qdisc */
23362f64aedSAlexei Starovoitov 
2348788370aSJesper Dangaard Brouer /* If lock -- protects updating of if_list */
2359a0b1e8bSEric Dumazet #define   if_lock(t)           mutex_lock(&(t->if_lock));
2369a0b1e8bSEric Dumazet #define   if_unlock(t)           mutex_unlock(&(t->if_lock));
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds /* Used to help with determining the pkts on receive */
2391da177e4SLinus Torvalds #define PKTGEN_MAGIC 0xbe9be955
240d50a6b56SStephen Hemminger #define PG_PROC_DIR "pktgen"
241d50a6b56SStephen Hemminger #define PGCTRL	    "pgctrl"
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds #define MAX_CFLOWS  65536
2441da177e4SLinus Torvalds 
24534954ddcSFrancesco Fondelli #define VLAN_TAG_SIZE(x) ((x)->vlan_id == 0xffff ? 0 : 4)
24634954ddcSFrancesco Fondelli #define SVLAN_TAG_SIZE(x) ((x)->svlan_id == 0xffff ? 0 : 4)
24734954ddcSFrancesco Fondelli 
24852a62f86SNick Richardson struct imix_pkt {
24952a62f86SNick Richardson 	u64 size;
25052a62f86SNick Richardson 	u64 weight;
25152a62f86SNick Richardson 	u64 count_so_far;
25252a62f86SNick Richardson };
25352a62f86SNick Richardson 
254222f1806SLuiz Capitulino struct flow_state {
255252e3346SAl Viro 	__be32 cur_daddr;
2561da177e4SLinus Torvalds 	int count;
257a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
258a553e4a6SJamal Hadi Salim 	struct xfrm_state *x;
259a553e4a6SJamal Hadi Salim #endif
260007a531bSJamal Hadi Salim 	__u32 flags;
2611da177e4SLinus Torvalds };
2621da177e4SLinus Torvalds 
263007a531bSJamal Hadi Salim /* flow flag bits */
264007a531bSJamal Hadi Salim #define F_INIT   (1<<0)		/* flow has been initialized */
265007a531bSJamal Hadi Salim 
2661da177e4SLinus Torvalds struct pktgen_dev {
2671da177e4SLinus Torvalds 	/*
2681da177e4SLinus Torvalds 	 * Try to keep frequent/infrequent used vars. separated.
2691da177e4SLinus Torvalds 	 */
27039df232fSStephen Hemminger 	struct proc_dir_entry *entry;	/* proc file */
2711da177e4SLinus Torvalds 	struct pktgen_thread *pg_thread;/* the owner */
27263adc6fbSStephen Hemminger 	struct list_head list;		/* chaining in the thread's run-queue */
2738788370aSJesper Dangaard Brouer 	struct rcu_head	 rcu;		/* freed by RCU */
2741da177e4SLinus Torvalds 
27563adc6fbSStephen Hemminger 	int running;		/* if false, the test will stop */
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	/* If min != max, then we will either do a linear iteration, or
2781da177e4SLinus Torvalds 	 * we will do a random selection from within the range.
2791da177e4SLinus Torvalds 	 */
2801da177e4SLinus Torvalds 	__u32 flags;
28162f64aedSAlexei Starovoitov 	int xmit_mode;
28268bf9f0bSAmerigo Wang 	int min_pkt_size;
28368bf9f0bSAmerigo Wang 	int max_pkt_size;
28416dab72fSJamal Hadi Salim 	int pkt_overhead;	/* overhead for MPLS, VLANs, IPSEC etc */
2851da177e4SLinus Torvalds 	int nfrags;
28662f64aedSAlexei Starovoitov 	int removal_mark;	/* non-zero => the device is marked for
28762f64aedSAlexei Starovoitov 				 * removal by worker thread */
28862f64aedSAlexei Starovoitov 
28926ad7879SEric Dumazet 	struct page *page;
290fd29cf72SStephen Hemminger 	u64 delay;		/* nano-seconds */
291fd29cf72SStephen Hemminger 
2921da177e4SLinus Torvalds 	__u64 count;		/* Default No packets to send */
2931da177e4SLinus Torvalds 	__u64 sofar;		/* How many pkts we've sent so far */
2941da177e4SLinus Torvalds 	__u64 tx_bytes;		/* How many bytes we've transmitted */
295f466dba1SJohn Fastabend 	__u64 errors;		/* Errors when trying to transmit, */
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 	/* runtime counters relating to clone_skb */
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds 	__u32 clone_count;
3001da177e4SLinus Torvalds 	int last_ok;		/* Was last skb sent?
30163adc6fbSStephen Hemminger 				 * Or a failed transmit of some sort?
30263adc6fbSStephen Hemminger 				 * This will keep sequence numbers in order
3031da177e4SLinus Torvalds 				 */
304fd29cf72SStephen Hemminger 	ktime_t next_tx;
305fd29cf72SStephen Hemminger 	ktime_t started_at;
306fd29cf72SStephen Hemminger 	ktime_t stopped_at;
307fd29cf72SStephen Hemminger 	u64	idle_acc;	/* nano-seconds */
308fd29cf72SStephen Hemminger 
3091da177e4SLinus Torvalds 	__u32 seq_num;
3101da177e4SLinus Torvalds 
31163adc6fbSStephen Hemminger 	int clone_skb;		/*
31263adc6fbSStephen Hemminger 				 * Use multiple SKBs during packet gen.
31363adc6fbSStephen Hemminger 				 * If this number is greater than 1, then
31463adc6fbSStephen Hemminger 				 * that many copies of the same packet will be
31563adc6fbSStephen Hemminger 				 * sent before a new packet is allocated.
31663adc6fbSStephen Hemminger 				 * If you want to send 1024 identical packets
31763adc6fbSStephen Hemminger 				 * before creating a new packet,
31863adc6fbSStephen Hemminger 				 * set clone_skb to 1024.
3191da177e4SLinus Torvalds 				 */
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	char dst_min[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
3221da177e4SLinus Torvalds 	char dst_max[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
3231da177e4SLinus Torvalds 	char src_min[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
3241da177e4SLinus Torvalds 	char src_max[IP_NAME_SZ];	/* IP, ie 1.2.3.4 */
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds 	struct in6_addr in6_saddr;
3271da177e4SLinus Torvalds 	struct in6_addr in6_daddr;
3281da177e4SLinus Torvalds 	struct in6_addr cur_in6_daddr;
3291da177e4SLinus Torvalds 	struct in6_addr cur_in6_saddr;
3301da177e4SLinus Torvalds 	/* For ranges */
3311da177e4SLinus Torvalds 	struct in6_addr min_in6_daddr;
3321da177e4SLinus Torvalds 	struct in6_addr max_in6_daddr;
3331da177e4SLinus Torvalds 	struct in6_addr min_in6_saddr;
3341da177e4SLinus Torvalds 	struct in6_addr max_in6_saddr;
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds 	/* If we're doing ranges, random or incremental, then this
3371da177e4SLinus Torvalds 	 * defines the min/max for those ranges.
3381da177e4SLinus Torvalds 	 */
339252e3346SAl Viro 	__be32 saddr_min;	/* inclusive, source IP address */
340252e3346SAl Viro 	__be32 saddr_max;	/* exclusive, source IP address */
341252e3346SAl Viro 	__be32 daddr_min;	/* inclusive, dest IP address */
342252e3346SAl Viro 	__be32 daddr_max;	/* exclusive, dest IP address */
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds 	__u16 udp_src_min;	/* inclusive, source UDP port */
3451da177e4SLinus Torvalds 	__u16 udp_src_max;	/* exclusive, source UDP port */
3461da177e4SLinus Torvalds 	__u16 udp_dst_min;	/* inclusive, dest UDP port */
3471da177e4SLinus Torvalds 	__u16 udp_dst_max;	/* exclusive, dest UDP port */
3481da177e4SLinus Torvalds 
3491ca7768cSFrancesco Fondelli 	/* DSCP + ECN */
35063adc6fbSStephen Hemminger 	__u8 tos;            /* six MSB of (former) IPv4 TOS
35163adc6fbSStephen Hemminger 				are for dscp codepoint */
35263adc6fbSStephen Hemminger 	__u8 traffic_class;  /* ditto for the (former) Traffic Class in IPv6
35363adc6fbSStephen Hemminger 				(see RFC 3260, sec. 4) */
3541ca7768cSFrancesco Fondelli 
35552a62f86SNick Richardson 	/* IMIX */
35652a62f86SNick Richardson 	unsigned int n_imix_entries;
35752a62f86SNick Richardson 	struct imix_pkt imix_entries[MAX_IMIX_ENTRIES];
35890149031SNick Richardson 	/* Maps 0-IMIX_PRECISION range to imix_entry based on probability*/
35990149031SNick Richardson 	__u8 imix_distribution[IMIX_PRECISION];
36052a62f86SNick Richardson 
361ca6549afSSteven Whitehouse 	/* MPLS */
36295c96174SEric Dumazet 	unsigned int nr_labels;	/* Depth of stack, 0 = no MPLS */
363ca6549afSSteven Whitehouse 	__be32 labels[MAX_MPLS_LABELS];
364ca6549afSSteven Whitehouse 
36534954ddcSFrancesco Fondelli 	/* VLAN/SVLAN (802.1Q/Q-in-Q) */
36634954ddcSFrancesco Fondelli 	__u8  vlan_p;
36734954ddcSFrancesco Fondelli 	__u8  vlan_cfi;
36834954ddcSFrancesco Fondelli 	__u16 vlan_id;  /* 0xffff means no vlan tag */
36934954ddcSFrancesco Fondelli 
37034954ddcSFrancesco Fondelli 	__u8  svlan_p;
37134954ddcSFrancesco Fondelli 	__u8  svlan_cfi;
37234954ddcSFrancesco Fondelli 	__u16 svlan_id; /* 0xffff means no svlan tag */
37334954ddcSFrancesco Fondelli 
3741da177e4SLinus Torvalds 	__u32 src_mac_count;	/* How many MACs to iterate through */
3751da177e4SLinus Torvalds 	__u32 dst_mac_count;	/* How many MACs to iterate through */
3761da177e4SLinus Torvalds 
377f404e9a6SKris Katterjohn 	unsigned char dst_mac[ETH_ALEN];
378f404e9a6SKris Katterjohn 	unsigned char src_mac[ETH_ALEN];
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 	__u32 cur_dst_mac_offset;
3811da177e4SLinus Torvalds 	__u32 cur_src_mac_offset;
382252e3346SAl Viro 	__be32 cur_saddr;
383252e3346SAl Viro 	__be32 cur_daddr;
38466ed1e5eSEric Dumazet 	__u16 ip_id;
3851da177e4SLinus Torvalds 	__u16 cur_udp_dst;
3861da177e4SLinus Torvalds 	__u16 cur_udp_src;
38745b270f8SRobert Olsson 	__u16 cur_queue_map;
3881da177e4SLinus Torvalds 	__u32 cur_pkt_size;
389baac8564SEric Dumazet 	__u32 last_pkt_size;
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 	__u8 hh[14];
3921da177e4SLinus Torvalds 	/* = {
3931da177e4SLinus Torvalds 	   0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds 	   We fill in SRC address later
3961da177e4SLinus Torvalds 	   0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3971da177e4SLinus Torvalds 	   0x08, 0x00
3981da177e4SLinus Torvalds 	   };
3991da177e4SLinus Torvalds 	 */
4001da177e4SLinus Torvalds 	__u16 pad;		/* pad out the hh struct to an even 16 bytes */
4011da177e4SLinus Torvalds 
40263adc6fbSStephen Hemminger 	struct sk_buff *skb;	/* skb we are to transmit next, used for when we
4031da177e4SLinus Torvalds 				 * are transmitting the same one multiple times
4041da177e4SLinus Torvalds 				 */
40563adc6fbSStephen Hemminger 	struct net_device *odev; /* The out-going device.
40663adc6fbSStephen Hemminger 				  * Note that the device should have it's
40763adc6fbSStephen Hemminger 				  * pg_info pointer pointing back to this
40863adc6fbSStephen Hemminger 				  * device.
40963adc6fbSStephen Hemminger 				  * Set when the user specifies the out-going
41063adc6fbSStephen Hemminger 				  * device name (not when the inject is
4111da177e4SLinus Torvalds 				  * started as it used to do.)
4121da177e4SLinus Torvalds 				  */
413035f1f2bSEric Dumazet 	netdevice_tracker dev_tracker;
414593f63b0SEric Dumazet 	char odevname[32];
4151da177e4SLinus Torvalds 	struct flow_state *flows;
41695c96174SEric Dumazet 	unsigned int cflows;	/* Concurrent flows (config) */
41795c96174SEric Dumazet 	unsigned int lflow;		/* Flow length  (config) */
41895c96174SEric Dumazet 	unsigned int nflows;	/* accumulated flows (stats) */
41995c96174SEric Dumazet 	unsigned int curfl;		/* current sequenced flow (state)*/
42045b270f8SRobert Olsson 
42145b270f8SRobert Olsson 	u16 queue_map_min;
42245b270f8SRobert Olsson 	u16 queue_map_max;
4239e50e3acSJohn Fastabend 	__u32 skb_priority;	/* skb priority field */
42438b2cf29SAlexei Starovoitov 	unsigned int burst;	/* number of duplicated packets to burst */
425e99b99b4SRobert Olsson 	int node;               /* Memory node */
42645b270f8SRobert Olsson 
427a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
428a553e4a6SJamal Hadi Salim 	__u8	ipsmode;		/* IPSEC mode (config) */
429a553e4a6SJamal Hadi Salim 	__u8	ipsproto;		/* IPSEC type (config) */
430de4aee7dSFan Du 	__u32	spi;
431b6ca8bd5SDavid Miller 	struct xfrm_dst xdst;
432cf93d47eSFan Du 	struct dst_ops dstops;
433a553e4a6SJamal Hadi Salim #endif
43439df232fSStephen Hemminger 	char result[512];
4351da177e4SLinus Torvalds };
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds struct pktgen_hdr {
438252e3346SAl Viro 	__be32 pgh_magic;
439252e3346SAl Viro 	__be32 seq_num;
440252e3346SAl Viro 	__be32 tv_sec;
441252e3346SAl Viro 	__be32 tv_usec;
4421da177e4SLinus Torvalds };
4431da177e4SLinus Torvalds 
4444e58a027SCong Wang 
445c7d03a00SAlexey Dobriyan static unsigned int pg_net_id __read_mostly;
4464e58a027SCong Wang 
4474e58a027SCong Wang struct pktgen_net {
4484e58a027SCong Wang 	struct net		*net;
4494e58a027SCong Wang 	struct proc_dir_entry	*proc_dir;
4504e58a027SCong Wang 	struct list_head	pktgen_threads;
4514e58a027SCong Wang 	bool			pktgen_exiting;
4524e58a027SCong Wang };
453551eaff1SEric Dumazet 
4541da177e4SLinus Torvalds struct pktgen_thread {
4559a0b1e8bSEric Dumazet 	struct mutex if_lock;		/* for list of devices */
456c26a8016SLuiz Capitulino 	struct list_head if_list;	/* All device here */
457cdcdbe0bSLuiz Capitulino 	struct list_head th_list;
458ee74baa7SDavid S. Miller 	struct task_struct *tsk;
4591da177e4SLinus Torvalds 	char result[512];
4601da177e4SLinus Torvalds 
46163adc6fbSStephen Hemminger 	/* Field for thread to receive "posted" events terminate,
46263adc6fbSStephen Hemminger 	   stop ifs etc. */
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 	u32 control;
4651da177e4SLinus Torvalds 	int cpu;
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 	wait_queue_head_t queue;
468d3ede327SDenis V. Lunev 	struct completion start_done;
4694e58a027SCong Wang 	struct pktgen_net *net;
4701da177e4SLinus Torvalds };
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds #define REMOVE 1
4731da177e4SLinus Torvalds #define FIND   0
4741da177e4SLinus Torvalds 
475c3d2f52dSStephen Hemminger static const char version[] =
476f9467eaeSJoe Perches 	"Packet Generator for packet performance testing. "
477f9467eaeSJoe Perches 	"Version: " VERSION "\n";
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i);
4801da177e4SLinus Torvalds static int pktgen_add_device(struct pktgen_thread *t, const char *ifname);
481222f1806SLuiz Capitulino static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
4823e984840SEric Dumazet 					  const char *ifname, bool exact);
4831da177e4SLinus Torvalds static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
4844e58a027SCong Wang static void pktgen_run_all_threads(struct pktgen_net *pn);
4854e58a027SCong Wang static void pktgen_reset_all_threads(struct pktgen_net *pn);
486cda9de0bSYejune Deng static void pktgen_stop_all_threads(struct pktgen_net *pn);
4873bda06a3SStephen Hemminger 
4881da177e4SLinus Torvalds static void pktgen_stop(struct pktgen_thread *t);
4891da177e4SLinus Torvalds static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
49090149031SNick Richardson static void fill_imix_distribution(struct pktgen_dev *pkt_dev);
49139df232fSStephen Hemminger 
4921da177e4SLinus Torvalds /* Module parameters, defaults. */
49365c5b786SStephen Hemminger static int pg_count_d __read_mostly = 1000;
49465c5b786SStephen Hemminger static int pg_delay_d __read_mostly;
49565c5b786SStephen Hemminger static int pg_clone_skb_d  __read_mostly;
49665c5b786SStephen Hemminger static int debug  __read_mostly;
4971da177e4SLinus Torvalds 
498222fa076SLuiz Capitulino static DEFINE_MUTEX(pktgen_thread_lock);
4991da177e4SLinus Torvalds 
5001da177e4SLinus Torvalds static struct notifier_block pktgen_notifier_block = {
5011da177e4SLinus Torvalds 	.notifier_call = pktgen_device_event,
5021da177e4SLinus Torvalds };
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds /*
5051da177e4SLinus Torvalds  * /proc handling functions
5061da177e4SLinus Torvalds  *
5071da177e4SLinus Torvalds  */
5081da177e4SLinus Torvalds 
pgctrl_show(struct seq_file * seq,void * v)509d50a6b56SStephen Hemminger static int pgctrl_show(struct seq_file *seq, void *v)
510d50a6b56SStephen Hemminger {
511c3d2f52dSStephen Hemminger 	seq_puts(seq, version);
512d50a6b56SStephen Hemminger 	return 0;
513d50a6b56SStephen Hemminger }
5141da177e4SLinus Torvalds 
pgctrl_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)515d50a6b56SStephen Hemminger static ssize_t pgctrl_write(struct file *file, const char __user *buf,
5161da177e4SLinus Torvalds 			    size_t count, loff_t *ppos)
5171da177e4SLinus Torvalds {
518d50a6b56SStephen Hemminger 	char data[128];
5194e58a027SCong Wang 	struct pktgen_net *pn = net_generic(current->nsproxy->net_ns, pg_net_id);
5201da177e4SLinus Torvalds 
52109455747SMathias Krause 	if (!capable(CAP_NET_ADMIN))
52209455747SMathias Krause 		return -EPERM;
5231da177e4SLinus Torvalds 
52420b0c718SMathias Krause 	if (count == 0)
52520b0c718SMathias Krause 		return -EINVAL;
52620b0c718SMathias Krause 
527d50a6b56SStephen Hemminger 	if (count > sizeof(data))
528d50a6b56SStephen Hemminger 		count = sizeof(data);
5291da177e4SLinus Torvalds 
53009455747SMathias Krause 	if (copy_from_user(data, buf, count))
53109455747SMathias Krause 		return -EFAULT;
53209455747SMathias Krause 
53320b0c718SMathias Krause 	data[count - 1] = 0;	/* Strip trailing '\n' and terminate string */
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds 	if (!strcmp(data, "stop"))
536cda9de0bSYejune Deng 		pktgen_stop_all_threads(pn);
5371da177e4SLinus Torvalds 	else if (!strcmp(data, "start"))
5384e58a027SCong Wang 		pktgen_run_all_threads(pn);
539eb37b41cSJesse Brandeburg 	else if (!strcmp(data, "reset"))
5404e58a027SCong Wang 		pktgen_reset_all_threads(pn);
5411da177e4SLinus Torvalds 	else
54240207264SJesper Dangaard Brouer 		return -EINVAL;
5431da177e4SLinus Torvalds 
54409455747SMathias Krause 	return count;
5451da177e4SLinus Torvalds }
5461da177e4SLinus Torvalds 
pgctrl_open(struct inode * inode,struct file * file)547d50a6b56SStephen Hemminger static int pgctrl_open(struct inode *inode, struct file *file)
5481da177e4SLinus Torvalds {
549359745d7SMuchun Song 	return single_open(file, pgctrl_show, pde_data(inode));
550d50a6b56SStephen Hemminger }
551d50a6b56SStephen Hemminger 
55297a32539SAlexey Dobriyan static const struct proc_ops pktgen_proc_ops = {
55397a32539SAlexey Dobriyan 	.proc_open	= pgctrl_open,
55497a32539SAlexey Dobriyan 	.proc_read	= seq_read,
55597a32539SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
55697a32539SAlexey Dobriyan 	.proc_write	= pgctrl_write,
55797a32539SAlexey Dobriyan 	.proc_release	= single_release,
558d50a6b56SStephen Hemminger };
559d50a6b56SStephen Hemminger 
pktgen_if_show(struct seq_file * seq,void * v)560d50a6b56SStephen Hemminger static int pktgen_if_show(struct seq_file *seq, void *v)
561d50a6b56SStephen Hemminger {
562648fda74SStephen Hemminger 	const struct pktgen_dev *pkt_dev = seq->private;
563fd29cf72SStephen Hemminger 	ktime_t stopped;
56499c6d3d2SDmitry Safonov 	unsigned int i;
565fd29cf72SStephen Hemminger 	u64 idle;
5661da177e4SLinus Torvalds 
567222f1806SLuiz Capitulino 	seq_printf(seq,
568222f1806SLuiz Capitulino 		   "Params: count %llu  min_pkt_size: %u  max_pkt_size: %u\n",
569222f1806SLuiz Capitulino 		   (unsigned long long)pkt_dev->count, pkt_dev->min_pkt_size,
570222f1806SLuiz Capitulino 		   pkt_dev->max_pkt_size);
5711da177e4SLinus Torvalds 
57252a62f86SNick Richardson 	if (pkt_dev->n_imix_entries > 0) {
57352a62f86SNick Richardson 		seq_puts(seq, "     imix_weights: ");
57452a62f86SNick Richardson 		for (i = 0; i < pkt_dev->n_imix_entries; i++) {
57552a62f86SNick Richardson 			seq_printf(seq, "%llu,%llu ",
57652a62f86SNick Richardson 				   pkt_dev->imix_entries[i].size,
57752a62f86SNick Richardson 				   pkt_dev->imix_entries[i].weight);
57852a62f86SNick Richardson 		}
57952a62f86SNick Richardson 		seq_puts(seq, "\n");
58052a62f86SNick Richardson 	}
58152a62f86SNick Richardson 
582222f1806SLuiz Capitulino 	seq_printf(seq,
583fd29cf72SStephen Hemminger 		   "     frags: %d  delay: %llu  clone_skb: %d  ifname: %s\n",
584fd29cf72SStephen Hemminger 		   pkt_dev->nfrags, (unsigned long long) pkt_dev->delay,
585593f63b0SEric Dumazet 		   pkt_dev->clone_skb, pkt_dev->odevname);
5861da177e4SLinus Torvalds 
587222f1806SLuiz Capitulino 	seq_printf(seq, "     flows: %u flowlen: %u\n", pkt_dev->cflows,
588222f1806SLuiz Capitulino 		   pkt_dev->lflow);
5891da177e4SLinus Torvalds 
59045b270f8SRobert Olsson 	seq_printf(seq,
59145b270f8SRobert Olsson 		   "     queue_map_min: %u  queue_map_max: %u\n",
59245b270f8SRobert Olsson 		   pkt_dev->queue_map_min,
59345b270f8SRobert Olsson 		   pkt_dev->queue_map_max);
59445b270f8SRobert Olsson 
5959e50e3acSJohn Fastabend 	if (pkt_dev->skb_priority)
5969e50e3acSJohn Fastabend 		seq_printf(seq, "     skb_priority: %u\n",
5979e50e3acSJohn Fastabend 			   pkt_dev->skb_priority);
5989e50e3acSJohn Fastabend 
5991da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
600222f1806SLuiz Capitulino 		seq_printf(seq,
60147a0200dSAlexey Dobriyan 			   "     saddr: %pI6c  min_saddr: %pI6c  max_saddr: %pI6c\n"
60247a0200dSAlexey Dobriyan 			   "     daddr: %pI6c  min_daddr: %pI6c  max_daddr: %pI6c\n",
60347a0200dSAlexey Dobriyan 			   &pkt_dev->in6_saddr,
60447a0200dSAlexey Dobriyan 			   &pkt_dev->min_in6_saddr, &pkt_dev->max_in6_saddr,
60547a0200dSAlexey Dobriyan 			   &pkt_dev->in6_daddr,
60647a0200dSAlexey Dobriyan 			   &pkt_dev->min_in6_daddr, &pkt_dev->max_in6_daddr);
60763adc6fbSStephen Hemminger 	} else {
608222f1806SLuiz Capitulino 		seq_printf(seq,
60963adc6fbSStephen Hemminger 			   "     dst_min: %s  dst_max: %s\n",
61063adc6fbSStephen Hemminger 			   pkt_dev->dst_min, pkt_dev->dst_max);
61163adc6fbSStephen Hemminger 		seq_printf(seq,
61263adc6fbSStephen Hemminger 			   "     src_min: %s  src_max: %s\n",
61363adc6fbSStephen Hemminger 			   pkt_dev->src_min, pkt_dev->src_max);
61463adc6fbSStephen Hemminger 	}
6151da177e4SLinus Torvalds 
616d50a6b56SStephen Hemminger 	seq_puts(seq, "     src_mac: ");
6171da177e4SLinus Torvalds 
618e174961cSJohannes Berg 	seq_printf(seq, "%pM ",
619e174961cSJohannes Berg 		   is_zero_ether_addr(pkt_dev->src_mac) ?
620e174961cSJohannes Berg 			     pkt_dev->odev->dev_addr : pkt_dev->src_mac);
6211da177e4SLinus Torvalds 
62297dc48e2SThomas Graf 	seq_puts(seq, "dst_mac: ");
623e174961cSJohannes Berg 	seq_printf(seq, "%pM\n", pkt_dev->dst_mac);
6241da177e4SLinus Torvalds 
625222f1806SLuiz Capitulino 	seq_printf(seq,
62663adc6fbSStephen Hemminger 		   "     udp_src_min: %d  udp_src_max: %d"
62763adc6fbSStephen Hemminger 		   "  udp_dst_min: %d  udp_dst_max: %d\n",
628222f1806SLuiz Capitulino 		   pkt_dev->udp_src_min, pkt_dev->udp_src_max,
629222f1806SLuiz Capitulino 		   pkt_dev->udp_dst_min, pkt_dev->udp_dst_max);
6301da177e4SLinus Torvalds 
631222f1806SLuiz Capitulino 	seq_printf(seq,
632ca6549afSSteven Whitehouse 		   "     src_mac_count: %d  dst_mac_count: %d\n",
6331da177e4SLinus Torvalds 		   pkt_dev->src_mac_count, pkt_dev->dst_mac_count);
6341da177e4SLinus Torvalds 
635ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels) {
63697dc48e2SThomas Graf 		seq_puts(seq, "     mpls: ");
637ca6549afSSteven Whitehouse 		for (i = 0; i < pkt_dev->nr_labels; i++)
638ca6549afSSteven Whitehouse 			seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]),
639ca6549afSSteven Whitehouse 				   i == pkt_dev->nr_labels-1 ? "\n" : ", ");
640ca6549afSSteven Whitehouse 	}
641ca6549afSSteven Whitehouse 
64263adc6fbSStephen Hemminger 	if (pkt_dev->vlan_id != 0xffff)
64334954ddcSFrancesco Fondelli 		seq_printf(seq, "     vlan_id: %u  vlan_p: %u  vlan_cfi: %u\n",
64463adc6fbSStephen Hemminger 			   pkt_dev->vlan_id, pkt_dev->vlan_p,
64563adc6fbSStephen Hemminger 			   pkt_dev->vlan_cfi);
64634954ddcSFrancesco Fondelli 
64763adc6fbSStephen Hemminger 	if (pkt_dev->svlan_id != 0xffff)
64834954ddcSFrancesco Fondelli 		seq_printf(seq, "     svlan_id: %u  vlan_p: %u  vlan_cfi: %u\n",
64963adc6fbSStephen Hemminger 			   pkt_dev->svlan_id, pkt_dev->svlan_p,
65063adc6fbSStephen Hemminger 			   pkt_dev->svlan_cfi);
65134954ddcSFrancesco Fondelli 
65263adc6fbSStephen Hemminger 	if (pkt_dev->tos)
6531ca7768cSFrancesco Fondelli 		seq_printf(seq, "     tos: 0x%02x\n", pkt_dev->tos);
6541ca7768cSFrancesco Fondelli 
65563adc6fbSStephen Hemminger 	if (pkt_dev->traffic_class)
6561ca7768cSFrancesco Fondelli 		seq_printf(seq, "     traffic_class: 0x%02x\n", pkt_dev->traffic_class);
6571ca7768cSFrancesco Fondelli 
65838b2cf29SAlexei Starovoitov 	if (pkt_dev->burst > 1)
65938b2cf29SAlexei Starovoitov 		seq_printf(seq, "     burst: %d\n", pkt_dev->burst);
66038b2cf29SAlexei Starovoitov 
661e99b99b4SRobert Olsson 	if (pkt_dev->node >= 0)
662e99b99b4SRobert Olsson 		seq_printf(seq, "     node: %d\n", pkt_dev->node);
663e99b99b4SRobert Olsson 
66462f64aedSAlexei Starovoitov 	if (pkt_dev->xmit_mode == M_NETIF_RECEIVE)
66562f64aedSAlexei Starovoitov 		seq_puts(seq, "     xmit_mode: netif_receive\n");
6660967f244SJohn Fastabend 	else if (pkt_dev->xmit_mode == M_QUEUE_XMIT)
6670967f244SJohn Fastabend 		seq_puts(seq, "     xmit_mode: xmit_queue\n");
66862f64aedSAlexei Starovoitov 
66997dc48e2SThomas Graf 	seq_puts(seq, "     Flags: ");
670ca6549afSSteven Whitehouse 
67199c6d3d2SDmitry Safonov 	for (i = 0; i < NR_PKT_FLAGS; i++) {
6721d30162fSGavrilov Ilia 		if (i == FLOW_SEQ_SHIFT)
67399c6d3d2SDmitry Safonov 			if (!pkt_dev->cflows)
67499c6d3d2SDmitry Safonov 				continue;
6751da177e4SLinus Torvalds 
6761d30162fSGavrilov Ilia 		if (pkt_dev->flags & (1 << i)) {
67799c6d3d2SDmitry Safonov 			seq_printf(seq, "%s  ", pkt_flag_names[i]);
678a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
6791d30162fSGavrilov Ilia 			if (i == IPSEC_SHIFT && pkt_dev->spi)
6808101328bSFan Du 				seq_printf(seq, "spi:%u  ", pkt_dev->spi);
681a553e4a6SJamal Hadi Salim #endif
6821d30162fSGavrilov Ilia 		} else if (i == FLOW_SEQ_SHIFT) {
6831d30162fSGavrilov Ilia 			seq_puts(seq, "FLOW_RND  ");
6841d30162fSGavrilov Ilia 		}
68599c6d3d2SDmitry Safonov 	}
686e99b99b4SRobert Olsson 
687d50a6b56SStephen Hemminger 	seq_puts(seq, "\n");
6881da177e4SLinus Torvalds 
689fd29cf72SStephen Hemminger 	/* not really stopped, more like last-running-at */
690398f382cSDaniel Borkmann 	stopped = pkt_dev->running ? ktime_get() : pkt_dev->stopped_at;
691fd29cf72SStephen Hemminger 	idle = pkt_dev->idle_acc;
692fd29cf72SStephen Hemminger 	do_div(idle, NSEC_PER_USEC);
6931da177e4SLinus Torvalds 
694222f1806SLuiz Capitulino 	seq_printf(seq,
695fd29cf72SStephen Hemminger 		   "Current:\n     pkts-sofar: %llu  errors: %llu\n",
6961da177e4SLinus Torvalds 		   (unsigned long long)pkt_dev->sofar,
697fd29cf72SStephen Hemminger 		   (unsigned long long)pkt_dev->errors);
698fd29cf72SStephen Hemminger 
699769afb3fSNick Richardson 	if (pkt_dev->n_imix_entries > 0) {
700769afb3fSNick Richardson 		int i;
701769afb3fSNick Richardson 
702769afb3fSNick Richardson 		seq_puts(seq, "     imix_size_counts: ");
703769afb3fSNick Richardson 		for (i = 0; i < pkt_dev->n_imix_entries; i++) {
704769afb3fSNick Richardson 			seq_printf(seq, "%llu,%llu ",
705769afb3fSNick Richardson 				   pkt_dev->imix_entries[i].size,
706769afb3fSNick Richardson 				   pkt_dev->imix_entries[i].count_so_far);
707769afb3fSNick Richardson 		}
708769afb3fSNick Richardson 		seq_puts(seq, "\n");
709769afb3fSNick Richardson 	}
710769afb3fSNick Richardson 
711fd29cf72SStephen Hemminger 	seq_printf(seq,
712fd29cf72SStephen Hemminger 		   "     started: %lluus  stopped: %lluus idle: %lluus\n",
713fd29cf72SStephen Hemminger 		   (unsigned long long) ktime_to_us(pkt_dev->started_at),
714fd29cf72SStephen Hemminger 		   (unsigned long long) ktime_to_us(stopped),
715fd29cf72SStephen Hemminger 		   (unsigned long long) idle);
7161da177e4SLinus Torvalds 
717222f1806SLuiz Capitulino 	seq_printf(seq,
718222f1806SLuiz Capitulino 		   "     seq_num: %d  cur_dst_mac_offset: %d  cur_src_mac_offset: %d\n",
719d50a6b56SStephen Hemminger 		   pkt_dev->seq_num, pkt_dev->cur_dst_mac_offset,
720d50a6b56SStephen Hemminger 		   pkt_dev->cur_src_mac_offset);
7211da177e4SLinus Torvalds 
7221da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
72347a0200dSAlexey Dobriyan 		seq_printf(seq, "     cur_saddr: %pI6c  cur_daddr: %pI6c\n",
72447a0200dSAlexey Dobriyan 				&pkt_dev->cur_in6_saddr,
72547a0200dSAlexey Dobriyan 				&pkt_dev->cur_in6_daddr);
726222f1806SLuiz Capitulino 	} else
7270373a946SAmerigo Wang 		seq_printf(seq, "     cur_saddr: %pI4  cur_daddr: %pI4\n",
7280373a946SAmerigo Wang 			   &pkt_dev->cur_saddr, &pkt_dev->cur_daddr);
7291da177e4SLinus Torvalds 
730d50a6b56SStephen Hemminger 	seq_printf(seq, "     cur_udp_dst: %d  cur_udp_src: %d\n",
7311da177e4SLinus Torvalds 		   pkt_dev->cur_udp_dst, pkt_dev->cur_udp_src);
7321da177e4SLinus Torvalds 
73345b270f8SRobert Olsson 	seq_printf(seq, "     cur_queue_map: %u\n", pkt_dev->cur_queue_map);
73445b270f8SRobert Olsson 
735d50a6b56SStephen Hemminger 	seq_printf(seq, "     flows: %u\n", pkt_dev->nflows);
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds 	if (pkt_dev->result[0])
738d50a6b56SStephen Hemminger 		seq_printf(seq, "Result: %s\n", pkt_dev->result);
7391da177e4SLinus Torvalds 	else
74097dc48e2SThomas Graf 		seq_puts(seq, "Result: Idle\n");
7411da177e4SLinus Torvalds 
742d50a6b56SStephen Hemminger 	return 0;
7431da177e4SLinus Torvalds }
7441da177e4SLinus Torvalds 
745ca6549afSSteven Whitehouse 
hex32_arg(const char __user * user_buffer,unsigned long maxlen,__u32 * num)74663adc6fbSStephen Hemminger static int hex32_arg(const char __user *user_buffer, unsigned long maxlen,
74763adc6fbSStephen Hemminger 		     __u32 *num)
748ca6549afSSteven Whitehouse {
749ca6549afSSteven Whitehouse 	int i = 0;
750ca6549afSSteven Whitehouse 	*num = 0;
751ca6549afSSteven Whitehouse 
7521ca7768cSFrancesco Fondelli 	for (; i < maxlen; i++) {
75382fd5b5dSAndy Shevchenko 		int value;
754ca6549afSSteven Whitehouse 		char c;
755ca6549afSSteven Whitehouse 		*num <<= 4;
756ca6549afSSteven Whitehouse 		if (get_user(c, &user_buffer[i]))
757ca6549afSSteven Whitehouse 			return -EFAULT;
75882fd5b5dSAndy Shevchenko 		value = hex_to_bin(c);
75982fd5b5dSAndy Shevchenko 		if (value >= 0)
76082fd5b5dSAndy Shevchenko 			*num |= value;
761ca6549afSSteven Whitehouse 		else
762ca6549afSSteven Whitehouse 			break;
763ca6549afSSteven Whitehouse 	}
764ca6549afSSteven Whitehouse 	return i;
765ca6549afSSteven Whitehouse }
766ca6549afSSteven Whitehouse 
count_trail_chars(const char __user * user_buffer,unsigned int maxlen)767222f1806SLuiz Capitulino static int count_trail_chars(const char __user * user_buffer,
768222f1806SLuiz Capitulino 			     unsigned int maxlen)
7691da177e4SLinus Torvalds {
7701da177e4SLinus Torvalds 	int i;
7711da177e4SLinus Torvalds 
7721da177e4SLinus Torvalds 	for (i = 0; i < maxlen; i++) {
7731da177e4SLinus Torvalds 		char c;
7741da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
7751da177e4SLinus Torvalds 			return -EFAULT;
7761da177e4SLinus Torvalds 		switch (c) {
7771da177e4SLinus Torvalds 		case '\"':
7781da177e4SLinus Torvalds 		case '\n':
7791da177e4SLinus Torvalds 		case '\r':
7801da177e4SLinus Torvalds 		case '\t':
7811da177e4SLinus Torvalds 		case ' ':
7821da177e4SLinus Torvalds 		case '=':
7831da177e4SLinus Torvalds 			break;
7841da177e4SLinus Torvalds 		default:
7851da177e4SLinus Torvalds 			goto done;
7863ff50b79SStephen Hemminger 		}
7871da177e4SLinus Torvalds 	}
7881da177e4SLinus Torvalds done:
7891da177e4SLinus Torvalds 	return i;
7901da177e4SLinus Torvalds }
7911da177e4SLinus Torvalds 
num_arg(const char __user * user_buffer,unsigned long maxlen,unsigned long * num)792bf0813bdSPaul Gortmaker static long num_arg(const char __user *user_buffer, unsigned long maxlen,
793bf0813bdSPaul Gortmaker 				unsigned long *num)
7941da177e4SLinus Torvalds {
795d6182223SPaul Gortmaker 	int i;
7961da177e4SLinus Torvalds 	*num = 0;
7971da177e4SLinus Torvalds 
798d6182223SPaul Gortmaker 	for (i = 0; i < maxlen; i++) {
7991da177e4SLinus Torvalds 		char c;
8001da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
8011da177e4SLinus Torvalds 			return -EFAULT;
8021da177e4SLinus Torvalds 		if ((c >= '0') && (c <= '9')) {
8031da177e4SLinus Torvalds 			*num *= 10;
8041da177e4SLinus Torvalds 			*num += c - '0';
8051da177e4SLinus Torvalds 		} else
8061da177e4SLinus Torvalds 			break;
8071da177e4SLinus Torvalds 	}
8081da177e4SLinus Torvalds 	return i;
8091da177e4SLinus Torvalds }
8101da177e4SLinus Torvalds 
strn_len(const char __user * user_buffer,unsigned int maxlen)8111da177e4SLinus Torvalds static int strn_len(const char __user * user_buffer, unsigned int maxlen)
8121da177e4SLinus Torvalds {
813d6182223SPaul Gortmaker 	int i;
8141da177e4SLinus Torvalds 
815d6182223SPaul Gortmaker 	for (i = 0; i < maxlen; i++) {
8161da177e4SLinus Torvalds 		char c;
8171da177e4SLinus Torvalds 		if (get_user(c, &user_buffer[i]))
8181da177e4SLinus Torvalds 			return -EFAULT;
8191da177e4SLinus Torvalds 		switch (c) {
8201da177e4SLinus Torvalds 		case '\"':
8211da177e4SLinus Torvalds 		case '\n':
8221da177e4SLinus Torvalds 		case '\r':
8231da177e4SLinus Torvalds 		case '\t':
8241da177e4SLinus Torvalds 		case ' ':
8251da177e4SLinus Torvalds 			goto done_str;
8261da177e4SLinus Torvalds 		default:
8271da177e4SLinus Torvalds 			break;
8283ff50b79SStephen Hemminger 		}
8291da177e4SLinus Torvalds 	}
8301da177e4SLinus Torvalds done_str:
8311da177e4SLinus Torvalds 	return i;
8321da177e4SLinus Torvalds }
8331da177e4SLinus Torvalds 
83452a62f86SNick Richardson /* Parses imix entries from user buffer.
83552a62f86SNick Richardson  * The user buffer should consist of imix entries separated by spaces
83652a62f86SNick Richardson  * where each entry consists of size and weight delimited by commas.
83752a62f86SNick Richardson  * "size1,weight_1 size2,weight_2 ... size_n,weight_n" for example.
83852a62f86SNick Richardson  */
get_imix_entries(const char __user * buffer,struct pktgen_dev * pkt_dev)83952a62f86SNick Richardson static ssize_t get_imix_entries(const char __user *buffer,
84052a62f86SNick Richardson 				struct pktgen_dev *pkt_dev)
84152a62f86SNick Richardson {
84252a62f86SNick Richardson 	const int max_digits = 10;
84352a62f86SNick Richardson 	int i = 0;
84452a62f86SNick Richardson 	long len;
84552a62f86SNick Richardson 	char c;
84652a62f86SNick Richardson 
84752a62f86SNick Richardson 	pkt_dev->n_imix_entries = 0;
84852a62f86SNick Richardson 
84952a62f86SNick Richardson 	do {
85052a62f86SNick Richardson 		unsigned long weight;
85152a62f86SNick Richardson 		unsigned long size;
85252a62f86SNick Richardson 
85352a62f86SNick Richardson 		len = num_arg(&buffer[i], max_digits, &size);
85452a62f86SNick Richardson 		if (len < 0)
85552a62f86SNick Richardson 			return len;
85652a62f86SNick Richardson 		i += len;
85752a62f86SNick Richardson 		if (get_user(c, &buffer[i]))
85852a62f86SNick Richardson 			return -EFAULT;
85952a62f86SNick Richardson 		/* Check for comma between size_i and weight_i */
86052a62f86SNick Richardson 		if (c != ',')
86152a62f86SNick Richardson 			return -EINVAL;
86252a62f86SNick Richardson 		i++;
86352a62f86SNick Richardson 
86452a62f86SNick Richardson 		if (size < 14 + 20 + 8)
86552a62f86SNick Richardson 			size = 14 + 20 + 8;
86652a62f86SNick Richardson 
86752a62f86SNick Richardson 		len = num_arg(&buffer[i], max_digits, &weight);
86852a62f86SNick Richardson 		if (len < 0)
86952a62f86SNick Richardson 			return len;
87052a62f86SNick Richardson 		if (weight <= 0)
87152a62f86SNick Richardson 			return -EINVAL;
87252a62f86SNick Richardson 
87352a62f86SNick Richardson 		pkt_dev->imix_entries[pkt_dev->n_imix_entries].size = size;
87452a62f86SNick Richardson 		pkt_dev->imix_entries[pkt_dev->n_imix_entries].weight = weight;
87552a62f86SNick Richardson 
87652a62f86SNick Richardson 		i += len;
87752a62f86SNick Richardson 		if (get_user(c, &buffer[i]))
87852a62f86SNick Richardson 			return -EFAULT;
87952a62f86SNick Richardson 
88052a62f86SNick Richardson 		i++;
88152a62f86SNick Richardson 		pkt_dev->n_imix_entries++;
88252a62f86SNick Richardson 
88352a62f86SNick Richardson 		if (pkt_dev->n_imix_entries > MAX_IMIX_ENTRIES)
88452a62f86SNick Richardson 			return -E2BIG;
88552a62f86SNick Richardson 	} while (c == ' ');
88652a62f86SNick Richardson 
88752a62f86SNick Richardson 	return i;
88852a62f86SNick Richardson }
88952a62f86SNick Richardson 
get_labels(const char __user * buffer,struct pktgen_dev * pkt_dev)890ca6549afSSteven Whitehouse static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
891ca6549afSSteven Whitehouse {
89295c96174SEric Dumazet 	unsigned int n = 0;
893ca6549afSSteven Whitehouse 	char c;
894ca6549afSSteven Whitehouse 	ssize_t i = 0;
895ca6549afSSteven Whitehouse 	int len;
896ca6549afSSteven Whitehouse 
897ca6549afSSteven Whitehouse 	pkt_dev->nr_labels = 0;
898ca6549afSSteven Whitehouse 	do {
899ca6549afSSteven Whitehouse 		__u32 tmp;
9001ca7768cSFrancesco Fondelli 		len = hex32_arg(&buffer[i], 8, &tmp);
901ca6549afSSteven Whitehouse 		if (len <= 0)
902ca6549afSSteven Whitehouse 			return len;
903ca6549afSSteven Whitehouse 		pkt_dev->labels[n] = htonl(tmp);
904ca6549afSSteven Whitehouse 		if (pkt_dev->labels[n] & MPLS_STACK_BOTTOM)
905ca6549afSSteven Whitehouse 			pkt_dev->flags |= F_MPLS_RND;
906ca6549afSSteven Whitehouse 		i += len;
907ca6549afSSteven Whitehouse 		if (get_user(c, &buffer[i]))
908ca6549afSSteven Whitehouse 			return -EFAULT;
909ca6549afSSteven Whitehouse 		i++;
910ca6549afSSteven Whitehouse 		n++;
911ca6549afSSteven Whitehouse 		if (n >= MAX_MPLS_LABELS)
912ca6549afSSteven Whitehouse 			return -E2BIG;
913ca6549afSSteven Whitehouse 	} while (c == ',');
914ca6549afSSteven Whitehouse 
915ca6549afSSteven Whitehouse 	pkt_dev->nr_labels = n;
916ca6549afSSteven Whitehouse 	return i;
917ca6549afSSteven Whitehouse }
918ca6549afSSteven Whitehouse 
pktgen_read_flag(const char * f,bool * disable)91952e12d5dSDmitry Safonov static __u32 pktgen_read_flag(const char *f, bool *disable)
92052e12d5dSDmitry Safonov {
92152e12d5dSDmitry Safonov 	__u32 i;
92252e12d5dSDmitry Safonov 
92352e12d5dSDmitry Safonov 	if (f[0] == '!') {
92452e12d5dSDmitry Safonov 		*disable = true;
92552e12d5dSDmitry Safonov 		f++;
92652e12d5dSDmitry Safonov 	}
92752e12d5dSDmitry Safonov 
92852e12d5dSDmitry Safonov 	for (i = 0; i < NR_PKT_FLAGS; i++) {
92952e12d5dSDmitry Safonov 		if (!IS_ENABLED(CONFIG_XFRM) && i == IPSEC_SHIFT)
93052e12d5dSDmitry Safonov 			continue;
93152e12d5dSDmitry Safonov 
93252e12d5dSDmitry Safonov 		/* allow only disabling ipv6 flag */
93352e12d5dSDmitry Safonov 		if (!*disable && i == IPV6_SHIFT)
93452e12d5dSDmitry Safonov 			continue;
93552e12d5dSDmitry Safonov 
93652e12d5dSDmitry Safonov 		if (strcmp(f, pkt_flag_names[i]) == 0)
93752e12d5dSDmitry Safonov 			return 1 << i;
93852e12d5dSDmitry Safonov 	}
93952e12d5dSDmitry Safonov 
94052e12d5dSDmitry Safonov 	if (strcmp(f, "FLOW_RND") == 0) {
94152e12d5dSDmitry Safonov 		*disable = !*disable;
94252e12d5dSDmitry Safonov 		return F_FLOW_SEQ;
94352e12d5dSDmitry Safonov 	}
94452e12d5dSDmitry Safonov 
94552e12d5dSDmitry Safonov 	return 0;
94652e12d5dSDmitry Safonov }
94752e12d5dSDmitry Safonov 
pktgen_if_write(struct file * file,const char __user * user_buffer,size_t count,loff_t * offset)948222f1806SLuiz Capitulino static ssize_t pktgen_if_write(struct file *file,
949222f1806SLuiz Capitulino 			       const char __user * user_buffer, size_t count,
950222f1806SLuiz Capitulino 			       loff_t * offset)
9511da177e4SLinus Torvalds {
9528a994a71SJoe Perches 	struct seq_file *seq = file->private_data;
953d50a6b56SStephen Hemminger 	struct pktgen_dev *pkt_dev = seq->private;
954d6182223SPaul Gortmaker 	int i, max, len;
9551da177e4SLinus Torvalds 	char name[16], valstr[32];
9561da177e4SLinus Torvalds 	unsigned long value = 0;
9571da177e4SLinus Torvalds 	char *pg_result = NULL;
9581da177e4SLinus Torvalds 	int tmp = 0;
9591da177e4SLinus Torvalds 	char buf[128];
9601da177e4SLinus Torvalds 
9611da177e4SLinus Torvalds 	pg_result = &(pkt_dev->result[0]);
9621da177e4SLinus Torvalds 
9631da177e4SLinus Torvalds 	if (count < 1) {
964294a0b7fSJoe Perches 		pr_warn("wrong command format\n");
9651da177e4SLinus Torvalds 		return -EINVAL;
9661da177e4SLinus Torvalds 	}
9671da177e4SLinus Torvalds 
968d6182223SPaul Gortmaker 	max = count;
969d6182223SPaul Gortmaker 	tmp = count_trail_chars(user_buffer, max);
9701da177e4SLinus Torvalds 	if (tmp < 0) {
971294a0b7fSJoe Perches 		pr_warn("illegal format\n");
9721da177e4SLinus Torvalds 		return tmp;
9731da177e4SLinus Torvalds 	}
974d6182223SPaul Gortmaker 	i = tmp;
9751da177e4SLinus Torvalds 
9761da177e4SLinus Torvalds 	/* Read variable name */
9771da177e4SLinus Torvalds 
9781da177e4SLinus Torvalds 	len = strn_len(&user_buffer[i], sizeof(name) - 1);
97963adc6fbSStephen Hemminger 	if (len < 0)
980222f1806SLuiz Capitulino 		return len;
98163adc6fbSStephen Hemminger 
9821da177e4SLinus Torvalds 	memset(name, 0, sizeof(name));
9831da177e4SLinus Torvalds 	if (copy_from_user(name, &user_buffer[i], len))
9841da177e4SLinus Torvalds 		return -EFAULT;
9851da177e4SLinus Torvalds 	i += len;
9861da177e4SLinus Torvalds 
9871da177e4SLinus Torvalds 	max = count - i;
9881da177e4SLinus Torvalds 	len = count_trail_chars(&user_buffer[i], max);
9891da177e4SLinus Torvalds 	if (len < 0)
9901da177e4SLinus Torvalds 		return len;
9911da177e4SLinus Torvalds 
9921da177e4SLinus Torvalds 	i += len;
9931da177e4SLinus Torvalds 
9941da177e4SLinus Torvalds 	if (debug) {
995a870a02cSArnd Bergmann 		size_t copy = min_t(size_t, count + 1, 1024);
996a870a02cSArnd Bergmann 		char *tp = strndup_user(user_buffer, copy);
997a870a02cSArnd Bergmann 
998a870a02cSArnd Bergmann 		if (IS_ERR(tp))
999a870a02cSArnd Bergmann 			return PTR_ERR(tp);
1000a870a02cSArnd Bergmann 
1001a870a02cSArnd Bergmann 		pr_debug("%s,%zu  buffer -:%s:-\n", name, count, tp);
100229d1df72SGustavo A. R. Silva 		kfree(tp);
10031da177e4SLinus Torvalds 	}
10041da177e4SLinus Torvalds 
10051da177e4SLinus Torvalds 	if (!strcmp(name, "min_pkt_size")) {
10061da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
100763adc6fbSStephen Hemminger 		if (len < 0)
1008222f1806SLuiz Capitulino 			return len;
100963adc6fbSStephen Hemminger 
10101da177e4SLinus Torvalds 		i += len;
10111da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
10121da177e4SLinus Torvalds 			value = 14 + 20 + 8;
10131da177e4SLinus Torvalds 		if (value != pkt_dev->min_pkt_size) {
10141da177e4SLinus Torvalds 			pkt_dev->min_pkt_size = value;
10151da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
10161da177e4SLinus Torvalds 		}
101732be425bSYe Bin 		sprintf(pg_result, "OK: min_pkt_size=%d",
1018222f1806SLuiz Capitulino 			pkt_dev->min_pkt_size);
10191da177e4SLinus Torvalds 		return count;
10201da177e4SLinus Torvalds 	}
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds 	if (!strcmp(name, "max_pkt_size")) {
10231da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
102463adc6fbSStephen Hemminger 		if (len < 0)
1025222f1806SLuiz Capitulino 			return len;
102663adc6fbSStephen Hemminger 
10271da177e4SLinus Torvalds 		i += len;
10281da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
10291da177e4SLinus Torvalds 			value = 14 + 20 + 8;
10301da177e4SLinus Torvalds 		if (value != pkt_dev->max_pkt_size) {
10311da177e4SLinus Torvalds 			pkt_dev->max_pkt_size = value;
10321da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
10331da177e4SLinus Torvalds 		}
103432be425bSYe Bin 		sprintf(pg_result, "OK: max_pkt_size=%d",
1035222f1806SLuiz Capitulino 			pkt_dev->max_pkt_size);
10361da177e4SLinus Torvalds 		return count;
10371da177e4SLinus Torvalds 	}
10381da177e4SLinus Torvalds 
10391da177e4SLinus Torvalds 	/* Shortcut for min = max */
10401da177e4SLinus Torvalds 
10411da177e4SLinus Torvalds 	if (!strcmp(name, "pkt_size")) {
10421da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
104363adc6fbSStephen Hemminger 		if (len < 0)
1044222f1806SLuiz Capitulino 			return len;
104563adc6fbSStephen Hemminger 
10461da177e4SLinus Torvalds 		i += len;
10471da177e4SLinus Torvalds 		if (value < 14 + 20 + 8)
10481da177e4SLinus Torvalds 			value = 14 + 20 + 8;
10491da177e4SLinus Torvalds 		if (value != pkt_dev->min_pkt_size) {
10501da177e4SLinus Torvalds 			pkt_dev->min_pkt_size = value;
10511da177e4SLinus Torvalds 			pkt_dev->max_pkt_size = value;
10521da177e4SLinus Torvalds 			pkt_dev->cur_pkt_size = value;
10531da177e4SLinus Torvalds 		}
105432be425bSYe Bin 		sprintf(pg_result, "OK: pkt_size=%d", pkt_dev->min_pkt_size);
10551da177e4SLinus Torvalds 		return count;
10561da177e4SLinus Torvalds 	}
10571da177e4SLinus Torvalds 
105852a62f86SNick Richardson 	if (!strcmp(name, "imix_weights")) {
105952a62f86SNick Richardson 		if (pkt_dev->clone_skb > 0)
106052a62f86SNick Richardson 			return -EINVAL;
106152a62f86SNick Richardson 
106252a62f86SNick Richardson 		len = get_imix_entries(&user_buffer[i], pkt_dev);
106352a62f86SNick Richardson 		if (len < 0)
106452a62f86SNick Richardson 			return len;
106552a62f86SNick Richardson 
106690149031SNick Richardson 		fill_imix_distribution(pkt_dev);
106790149031SNick Richardson 
106852a62f86SNick Richardson 		i += len;
106952a62f86SNick Richardson 		return count;
107052a62f86SNick Richardson 	}
107152a62f86SNick Richardson 
10721da177e4SLinus Torvalds 	if (!strcmp(name, "debug")) {
10731da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
107463adc6fbSStephen Hemminger 		if (len < 0)
1075222f1806SLuiz Capitulino 			return len;
107663adc6fbSStephen Hemminger 
10771da177e4SLinus Torvalds 		i += len;
10781da177e4SLinus Torvalds 		debug = value;
10791da177e4SLinus Torvalds 		sprintf(pg_result, "OK: debug=%u", debug);
10801da177e4SLinus Torvalds 		return count;
10811da177e4SLinus Torvalds 	}
10821da177e4SLinus Torvalds 
10831da177e4SLinus Torvalds 	if (!strcmp(name, "frags")) {
10841da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
108563adc6fbSStephen Hemminger 		if (len < 0)
1086222f1806SLuiz Capitulino 			return len;
108763adc6fbSStephen Hemminger 
10881da177e4SLinus Torvalds 		i += len;
10891da177e4SLinus Torvalds 		pkt_dev->nfrags = value;
109032be425bSYe Bin 		sprintf(pg_result, "OK: frags=%d", pkt_dev->nfrags);
10911da177e4SLinus Torvalds 		return count;
10921da177e4SLinus Torvalds 	}
10931da177e4SLinus Torvalds 	if (!strcmp(name, "delay")) {
10941da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
109563adc6fbSStephen Hemminger 		if (len < 0)
1096222f1806SLuiz Capitulino 			return len;
109763adc6fbSStephen Hemminger 
10981da177e4SLinus Torvalds 		i += len;
1099fd29cf72SStephen Hemminger 		if (value == 0x7FFFFFFF)
1100fd29cf72SStephen Hemminger 			pkt_dev->delay = ULLONG_MAX;
1101fd29cf72SStephen Hemminger 		else
11029240d715SEric Dumazet 			pkt_dev->delay = (u64)value;
1103fd29cf72SStephen Hemminger 
1104fd29cf72SStephen Hemminger 		sprintf(pg_result, "OK: delay=%llu",
1105fd29cf72SStephen Hemminger 			(unsigned long long) pkt_dev->delay);
11061da177e4SLinus Torvalds 		return count;
11071da177e4SLinus Torvalds 	}
110843d28b65SDaniel Turull 	if (!strcmp(name, "rate")) {
110943d28b65SDaniel Turull 		len = num_arg(&user_buffer[i], 10, &value);
111043d28b65SDaniel Turull 		if (len < 0)
111143d28b65SDaniel Turull 			return len;
111243d28b65SDaniel Turull 
111343d28b65SDaniel Turull 		i += len;
111443d28b65SDaniel Turull 		if (!value)
111543d28b65SDaniel Turull 			return len;
111643d28b65SDaniel Turull 		pkt_dev->delay = pkt_dev->min_pkt_size*8*NSEC_PER_USEC/value;
111743d28b65SDaniel Turull 		if (debug)
1118f9467eaeSJoe Perches 			pr_info("Delay set at: %llu ns\n", pkt_dev->delay);
111943d28b65SDaniel Turull 
112043d28b65SDaniel Turull 		sprintf(pg_result, "OK: rate=%lu", value);
112143d28b65SDaniel Turull 		return count;
112243d28b65SDaniel Turull 	}
112343d28b65SDaniel Turull 	if (!strcmp(name, "ratep")) {
112443d28b65SDaniel Turull 		len = num_arg(&user_buffer[i], 10, &value);
112543d28b65SDaniel Turull 		if (len < 0)
112643d28b65SDaniel Turull 			return len;
112743d28b65SDaniel Turull 
112843d28b65SDaniel Turull 		i += len;
112943d28b65SDaniel Turull 		if (!value)
113043d28b65SDaniel Turull 			return len;
113143d28b65SDaniel Turull 		pkt_dev->delay = NSEC_PER_SEC/value;
113243d28b65SDaniel Turull 		if (debug)
1133f9467eaeSJoe Perches 			pr_info("Delay set at: %llu ns\n", pkt_dev->delay);
113443d28b65SDaniel Turull 
113543d28b65SDaniel Turull 		sprintf(pg_result, "OK: rate=%lu", value);
113643d28b65SDaniel Turull 		return count;
113743d28b65SDaniel Turull 	}
11381da177e4SLinus Torvalds 	if (!strcmp(name, "udp_src_min")) {
11391da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
114063adc6fbSStephen Hemminger 		if (len < 0)
1141222f1806SLuiz Capitulino 			return len;
114263adc6fbSStephen Hemminger 
11431da177e4SLinus Torvalds 		i += len;
11441da177e4SLinus Torvalds 		if (value != pkt_dev->udp_src_min) {
11451da177e4SLinus Torvalds 			pkt_dev->udp_src_min = value;
11461da177e4SLinus Torvalds 			pkt_dev->cur_udp_src = value;
11471da177e4SLinus Torvalds 		}
11481da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_src_min=%u", pkt_dev->udp_src_min);
11491da177e4SLinus Torvalds 		return count;
11501da177e4SLinus Torvalds 	}
11511da177e4SLinus Torvalds 	if (!strcmp(name, "udp_dst_min")) {
11521da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
115363adc6fbSStephen Hemminger 		if (len < 0)
1154222f1806SLuiz Capitulino 			return len;
115563adc6fbSStephen Hemminger 
11561da177e4SLinus Torvalds 		i += len;
11571da177e4SLinus Torvalds 		if (value != pkt_dev->udp_dst_min) {
11581da177e4SLinus Torvalds 			pkt_dev->udp_dst_min = value;
11591da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst = value;
11601da177e4SLinus Torvalds 		}
11611da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_dst_min=%u", pkt_dev->udp_dst_min);
11621da177e4SLinus Torvalds 		return count;
11631da177e4SLinus Torvalds 	}
11641da177e4SLinus Torvalds 	if (!strcmp(name, "udp_src_max")) {
11651da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
116663adc6fbSStephen Hemminger 		if (len < 0)
1167222f1806SLuiz Capitulino 			return len;
116863adc6fbSStephen Hemminger 
11691da177e4SLinus Torvalds 		i += len;
11701da177e4SLinus Torvalds 		if (value != pkt_dev->udp_src_max) {
11711da177e4SLinus Torvalds 			pkt_dev->udp_src_max = value;
11721da177e4SLinus Torvalds 			pkt_dev->cur_udp_src = value;
11731da177e4SLinus Torvalds 		}
11741da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_src_max=%u", pkt_dev->udp_src_max);
11751da177e4SLinus Torvalds 		return count;
11761da177e4SLinus Torvalds 	}
11771da177e4SLinus Torvalds 	if (!strcmp(name, "udp_dst_max")) {
11781da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
117963adc6fbSStephen Hemminger 		if (len < 0)
1180222f1806SLuiz Capitulino 			return len;
118163adc6fbSStephen Hemminger 
11821da177e4SLinus Torvalds 		i += len;
11831da177e4SLinus Torvalds 		if (value != pkt_dev->udp_dst_max) {
11841da177e4SLinus Torvalds 			pkt_dev->udp_dst_max = value;
11851da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst = value;
11861da177e4SLinus Torvalds 		}
11871da177e4SLinus Torvalds 		sprintf(pg_result, "OK: udp_dst_max=%u", pkt_dev->udp_dst_max);
11881da177e4SLinus Torvalds 		return count;
11891da177e4SLinus Torvalds 	}
11901da177e4SLinus Torvalds 	if (!strcmp(name, "clone_skb")) {
11911da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
119263adc6fbSStephen Hemminger 		if (len < 0)
1193222f1806SLuiz Capitulino 			return len;
119452a62f86SNick Richardson 		/* clone_skb is not supported for netif_receive xmit_mode and
119552a62f86SNick Richardson 		 * IMIX mode.
119652a62f86SNick Richardson 		 */
1197d8873315SNeil Horman 		if ((value > 0) &&
119862f64aedSAlexei Starovoitov 		    ((pkt_dev->xmit_mode == M_NETIF_RECEIVE) ||
119962f64aedSAlexei Starovoitov 		     !(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
1200d8873315SNeil Horman 			return -ENOTSUPP;
120152a62f86SNick Richardson 		if (value > 0 && pkt_dev->n_imix_entries > 0)
120252a62f86SNick Richardson 			return -EINVAL;
120352a62f86SNick Richardson 
12041da177e4SLinus Torvalds 		i += len;
12051da177e4SLinus Torvalds 		pkt_dev->clone_skb = value;
12061da177e4SLinus Torvalds 
12071da177e4SLinus Torvalds 		sprintf(pg_result, "OK: clone_skb=%d", pkt_dev->clone_skb);
12081da177e4SLinus Torvalds 		return count;
12091da177e4SLinus Torvalds 	}
12101da177e4SLinus Torvalds 	if (!strcmp(name, "count")) {
12111da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
121263adc6fbSStephen Hemminger 		if (len < 0)
1213222f1806SLuiz Capitulino 			return len;
121463adc6fbSStephen Hemminger 
12151da177e4SLinus Torvalds 		i += len;
12161da177e4SLinus Torvalds 		pkt_dev->count = value;
12171da177e4SLinus Torvalds 		sprintf(pg_result, "OK: count=%llu",
12181da177e4SLinus Torvalds 			(unsigned long long)pkt_dev->count);
12191da177e4SLinus Torvalds 		return count;
12201da177e4SLinus Torvalds 	}
12211da177e4SLinus Torvalds 	if (!strcmp(name, "src_mac_count")) {
12221da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
122363adc6fbSStephen Hemminger 		if (len < 0)
1224222f1806SLuiz Capitulino 			return len;
122563adc6fbSStephen Hemminger 
12261da177e4SLinus Torvalds 		i += len;
12271da177e4SLinus Torvalds 		if (pkt_dev->src_mac_count != value) {
12281da177e4SLinus Torvalds 			pkt_dev->src_mac_count = value;
12291da177e4SLinus Torvalds 			pkt_dev->cur_src_mac_offset = 0;
12301da177e4SLinus Torvalds 		}
1231222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: src_mac_count=%d",
1232222f1806SLuiz Capitulino 			pkt_dev->src_mac_count);
12331da177e4SLinus Torvalds 		return count;
12341da177e4SLinus Torvalds 	}
12351da177e4SLinus Torvalds 	if (!strcmp(name, "dst_mac_count")) {
12361da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
123763adc6fbSStephen Hemminger 		if (len < 0)
1238222f1806SLuiz Capitulino 			return len;
123963adc6fbSStephen Hemminger 
12401da177e4SLinus Torvalds 		i += len;
12411da177e4SLinus Torvalds 		if (pkt_dev->dst_mac_count != value) {
12421da177e4SLinus Torvalds 			pkt_dev->dst_mac_count = value;
12431da177e4SLinus Torvalds 			pkt_dev->cur_dst_mac_offset = 0;
12441da177e4SLinus Torvalds 		}
1245222f1806SLuiz Capitulino 		sprintf(pg_result, "OK: dst_mac_count=%d",
1246222f1806SLuiz Capitulino 			pkt_dev->dst_mac_count);
12471da177e4SLinus Torvalds 		return count;
12481da177e4SLinus Torvalds 	}
124938b2cf29SAlexei Starovoitov 	if (!strcmp(name, "burst")) {
125038b2cf29SAlexei Starovoitov 		len = num_arg(&user_buffer[i], 10, &value);
125138b2cf29SAlexei Starovoitov 		if (len < 0)
125238b2cf29SAlexei Starovoitov 			return len;
125338b2cf29SAlexei Starovoitov 
125438b2cf29SAlexei Starovoitov 		i += len;
12550967f244SJohn Fastabend 		if ((value > 1) &&
12560967f244SJohn Fastabend 		    ((pkt_dev->xmit_mode == M_QUEUE_XMIT) ||
12570967f244SJohn Fastabend 		     ((pkt_dev->xmit_mode == M_START_XMIT) &&
12580967f244SJohn Fastabend 		     (!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))))
125952d6c8c6SEric Dumazet 			return -ENOTSUPP;
126038b2cf29SAlexei Starovoitov 		pkt_dev->burst = value < 1 ? 1 : value;
126132be425bSYe Bin 		sprintf(pg_result, "OK: burst=%u", pkt_dev->burst);
126238b2cf29SAlexei Starovoitov 		return count;
126338b2cf29SAlexei Starovoitov 	}
1264e99b99b4SRobert Olsson 	if (!strcmp(name, "node")) {
1265e99b99b4SRobert Olsson 		len = num_arg(&user_buffer[i], 10, &value);
1266e99b99b4SRobert Olsson 		if (len < 0)
1267e99b99b4SRobert Olsson 			return len;
1268e99b99b4SRobert Olsson 
1269e99b99b4SRobert Olsson 		i += len;
1270e99b99b4SRobert Olsson 
1271e99b99b4SRobert Olsson 		if (node_possible(value)) {
1272e99b99b4SRobert Olsson 			pkt_dev->node = value;
1273e99b99b4SRobert Olsson 			sprintf(pg_result, "OK: node=%d", pkt_dev->node);
127426ad7879SEric Dumazet 			if (pkt_dev->page) {
127526ad7879SEric Dumazet 				put_page(pkt_dev->page);
127626ad7879SEric Dumazet 				pkt_dev->page = NULL;
127726ad7879SEric Dumazet 			}
1278e99b99b4SRobert Olsson 		}
1279e99b99b4SRobert Olsson 		else
1280e99b99b4SRobert Olsson 			sprintf(pg_result, "ERROR: node not possible");
1281e99b99b4SRobert Olsson 		return count;
1282e99b99b4SRobert Olsson 	}
128362f64aedSAlexei Starovoitov 	if (!strcmp(name, "xmit_mode")) {
128462f64aedSAlexei Starovoitov 		char f[32];
128562f64aedSAlexei Starovoitov 
128662f64aedSAlexei Starovoitov 		memset(f, 0, 32);
128762f64aedSAlexei Starovoitov 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
128862f64aedSAlexei Starovoitov 		if (len < 0)
128962f64aedSAlexei Starovoitov 			return len;
129062f64aedSAlexei Starovoitov 
129162f64aedSAlexei Starovoitov 		if (copy_from_user(f, &user_buffer[i], len))
129262f64aedSAlexei Starovoitov 			return -EFAULT;
129362f64aedSAlexei Starovoitov 		i += len;
129462f64aedSAlexei Starovoitov 
129562f64aedSAlexei Starovoitov 		if (strcmp(f, "start_xmit") == 0) {
129662f64aedSAlexei Starovoitov 			pkt_dev->xmit_mode = M_START_XMIT;
129762f64aedSAlexei Starovoitov 		} else if (strcmp(f, "netif_receive") == 0) {
129862f64aedSAlexei Starovoitov 			/* clone_skb set earlier, not supported in this mode */
129962f64aedSAlexei Starovoitov 			if (pkt_dev->clone_skb > 0)
130062f64aedSAlexei Starovoitov 				return -ENOTSUPP;
130162f64aedSAlexei Starovoitov 
130262f64aedSAlexei Starovoitov 			pkt_dev->xmit_mode = M_NETIF_RECEIVE;
13039eea9222SAlexei Starovoitov 
13049eea9222SAlexei Starovoitov 			/* make sure new packet is allocated every time
13059eea9222SAlexei Starovoitov 			 * pktgen_xmit() is called
13069eea9222SAlexei Starovoitov 			 */
13079eea9222SAlexei Starovoitov 			pkt_dev->last_ok = 1;
13080967f244SJohn Fastabend 		} else if (strcmp(f, "queue_xmit") == 0) {
13090967f244SJohn Fastabend 			pkt_dev->xmit_mode = M_QUEUE_XMIT;
13100967f244SJohn Fastabend 			pkt_dev->last_ok = 1;
131162f64aedSAlexei Starovoitov 		} else {
131262f64aedSAlexei Starovoitov 			sprintf(pg_result,
131362f64aedSAlexei Starovoitov 				"xmit_mode -:%s:- unknown\nAvailable modes: %s",
131462f64aedSAlexei Starovoitov 				f, "start_xmit, netif_receive\n");
131562f64aedSAlexei Starovoitov 			return count;
131662f64aedSAlexei Starovoitov 		}
131762f64aedSAlexei Starovoitov 		sprintf(pg_result, "OK: xmit_mode=%s", f);
131862f64aedSAlexei Starovoitov 		return count;
131962f64aedSAlexei Starovoitov 	}
13201da177e4SLinus Torvalds 	if (!strcmp(name, "flag")) {
132152e12d5dSDmitry Safonov 		__u32 flag;
13221da177e4SLinus Torvalds 		char f[32];
132352e12d5dSDmitry Safonov 		bool disable = false;
132452e12d5dSDmitry Safonov 
13251da177e4SLinus Torvalds 		memset(f, 0, 32);
13261da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
132763adc6fbSStephen Hemminger 		if (len < 0)
1328222f1806SLuiz Capitulino 			return len;
132963adc6fbSStephen Hemminger 
13301da177e4SLinus Torvalds 		if (copy_from_user(f, &user_buffer[i], len))
13311da177e4SLinus Torvalds 			return -EFAULT;
13321da177e4SLinus Torvalds 		i += len;
13331da177e4SLinus Torvalds 
133452e12d5dSDmitry Safonov 		flag = pktgen_read_flag(f, &disable);
13351da177e4SLinus Torvalds 
133652e12d5dSDmitry Safonov 		if (flag) {
133752e12d5dSDmitry Safonov 			if (disable)
133852e12d5dSDmitry Safonov 				pkt_dev->flags &= ~flag;
133952e12d5dSDmitry Safonov 			else
134052e12d5dSDmitry Safonov 				pkt_dev->flags |= flag;
134152e12d5dSDmitry Safonov 		} else {
1342222f1806SLuiz Capitulino 			sprintf(pg_result,
1343222f1806SLuiz Capitulino 				"Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
13441da177e4SLinus Torvalds 				f,
13451ca7768cSFrancesco Fondelli 				"IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, "
134672f8e06fSMathias Krause 				"MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, "
134772f8e06fSMathias Krause 				"MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ, "
134872f8e06fSMathias Krause 				"QUEUE_MAP_RND, QUEUE_MAP_CPU, UDPCSUM, "
1349afb84b62SJesper Dangaard Brouer 				"NO_TIMESTAMP, "
135072f8e06fSMathias Krause #ifdef CONFIG_XFRM
135172f8e06fSMathias Krause 				"IPSEC, "
135272f8e06fSMathias Krause #endif
135372f8e06fSMathias Krause 				"NODE_ALLOC\n");
13541da177e4SLinus Torvalds 			return count;
13551da177e4SLinus Torvalds 		}
13561da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags);
13571da177e4SLinus Torvalds 		return count;
13581da177e4SLinus Torvalds 	}
13591da177e4SLinus Torvalds 	if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
13601da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_min) - 1);
136163adc6fbSStephen Hemminger 		if (len < 0)
1362222f1806SLuiz Capitulino 			return len;
13631da177e4SLinus Torvalds 
13641da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13651da177e4SLinus Torvalds 			return -EFAULT;
13661da177e4SLinus Torvalds 		buf[len] = 0;
13671da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->dst_min) != 0) {
13681da177e4SLinus Torvalds 			memset(pkt_dev->dst_min, 0, sizeof(pkt_dev->dst_min));
1369f15f084fSJakub Kicinski 			strcpy(pkt_dev->dst_min, buf);
13701da177e4SLinus Torvalds 			pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
13711da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->daddr_min;
13721da177e4SLinus Torvalds 		}
13731da177e4SLinus Torvalds 		if (debug)
1374f342cda7SJoe Perches 			pr_debug("dst_min set to: %s\n", pkt_dev->dst_min);
13751da177e4SLinus Torvalds 		i += len;
13761da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst_min=%s", pkt_dev->dst_min);
13771da177e4SLinus Torvalds 		return count;
13781da177e4SLinus Torvalds 	}
13791da177e4SLinus Torvalds 	if (!strcmp(name, "dst_max")) {
13801da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_max) - 1);
138163adc6fbSStephen Hemminger 		if (len < 0)
1382222f1806SLuiz Capitulino 			return len;
138363adc6fbSStephen Hemminger 
13841da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
13851da177e4SLinus Torvalds 			return -EFAULT;
13861da177e4SLinus Torvalds 		buf[len] = 0;
13871da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->dst_max) != 0) {
13881da177e4SLinus Torvalds 			memset(pkt_dev->dst_max, 0, sizeof(pkt_dev->dst_max));
1389f15f084fSJakub Kicinski 			strcpy(pkt_dev->dst_max, buf);
13901da177e4SLinus Torvalds 			pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
13911da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->daddr_max;
13921da177e4SLinus Torvalds 		}
13931da177e4SLinus Torvalds 		if (debug)
1394f342cda7SJoe Perches 			pr_debug("dst_max set to: %s\n", pkt_dev->dst_max);
13951da177e4SLinus Torvalds 		i += len;
13961da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst_max=%s", pkt_dev->dst_max);
13971da177e4SLinus Torvalds 		return count;
13981da177e4SLinus Torvalds 	}
13991da177e4SLinus Torvalds 	if (!strcmp(name, "dst6")) {
14001da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1401222f1806SLuiz Capitulino 		if (len < 0)
1402222f1806SLuiz Capitulino 			return len;
14031da177e4SLinus Torvalds 
14041da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
14051da177e4SLinus Torvalds 
14061da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14071da177e4SLinus Torvalds 			return -EFAULT;
14081da177e4SLinus Torvalds 		buf[len] = 0;
14091da177e4SLinus Torvalds 
1410c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->in6_daddr.s6_addr, -1, NULL);
141147a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_daddr);
14121da177e4SLinus Torvalds 
14134e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_daddr = pkt_dev->in6_daddr;
14141da177e4SLinus Torvalds 
14151da177e4SLinus Torvalds 		if (debug)
1416f342cda7SJoe Perches 			pr_debug("dst6 set to: %s\n", buf);
14171da177e4SLinus Torvalds 
14181da177e4SLinus Torvalds 		i += len;
14191da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6=%s", buf);
14201da177e4SLinus Torvalds 		return count;
14211da177e4SLinus Torvalds 	}
14221da177e4SLinus Torvalds 	if (!strcmp(name, "dst6_min")) {
14231da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1424222f1806SLuiz Capitulino 		if (len < 0)
1425222f1806SLuiz Capitulino 			return len;
14261da177e4SLinus Torvalds 
14271da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
14281da177e4SLinus Torvalds 
14291da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14301da177e4SLinus Torvalds 			return -EFAULT;
14311da177e4SLinus Torvalds 		buf[len] = 0;
14321da177e4SLinus Torvalds 
1433c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->min_in6_daddr.s6_addr, -1, NULL);
143447a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->min_in6_daddr);
14351da177e4SLinus Torvalds 
14364e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_daddr = pkt_dev->min_in6_daddr;
14371da177e4SLinus Torvalds 		if (debug)
1438f342cda7SJoe Perches 			pr_debug("dst6_min set to: %s\n", buf);
14391da177e4SLinus Torvalds 
14401da177e4SLinus Torvalds 		i += len;
14411da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6_min=%s", buf);
14421da177e4SLinus Torvalds 		return count;
14431da177e4SLinus Torvalds 	}
14441da177e4SLinus Torvalds 	if (!strcmp(name, "dst6_max")) {
14451da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1446222f1806SLuiz Capitulino 		if (len < 0)
1447222f1806SLuiz Capitulino 			return len;
14481da177e4SLinus Torvalds 
14491da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
14501da177e4SLinus Torvalds 
14511da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14521da177e4SLinus Torvalds 			return -EFAULT;
14531da177e4SLinus Torvalds 		buf[len] = 0;
14541da177e4SLinus Torvalds 
1455c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->max_in6_daddr.s6_addr, -1, NULL);
145647a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->max_in6_daddr);
14571da177e4SLinus Torvalds 
14581da177e4SLinus Torvalds 		if (debug)
1459f342cda7SJoe Perches 			pr_debug("dst6_max set to: %s\n", buf);
14601da177e4SLinus Torvalds 
14611da177e4SLinus Torvalds 		i += len;
14621da177e4SLinus Torvalds 		sprintf(pg_result, "OK: dst6_max=%s", buf);
14631da177e4SLinus Torvalds 		return count;
14641da177e4SLinus Torvalds 	}
14651da177e4SLinus Torvalds 	if (!strcmp(name, "src6")) {
14661da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
1467222f1806SLuiz Capitulino 		if (len < 0)
1468222f1806SLuiz Capitulino 			return len;
14691da177e4SLinus Torvalds 
14701da177e4SLinus Torvalds 		pkt_dev->flags |= F_IPV6;
14711da177e4SLinus Torvalds 
14721da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14731da177e4SLinus Torvalds 			return -EFAULT;
14741da177e4SLinus Torvalds 		buf[len] = 0;
14751da177e4SLinus Torvalds 
1476c468fb13SAmerigo Wang 		in6_pton(buf, -1, pkt_dev->in6_saddr.s6_addr, -1, NULL);
147747a0200dSAlexey Dobriyan 		snprintf(buf, sizeof(buf), "%pI6c", &pkt_dev->in6_saddr);
14781da177e4SLinus Torvalds 
14794e3fd7a0SAlexey Dobriyan 		pkt_dev->cur_in6_saddr = pkt_dev->in6_saddr;
14801da177e4SLinus Torvalds 
14811da177e4SLinus Torvalds 		if (debug)
1482f342cda7SJoe Perches 			pr_debug("src6 set to: %s\n", buf);
14831da177e4SLinus Torvalds 
14841da177e4SLinus Torvalds 		i += len;
14851da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src6=%s", buf);
14861da177e4SLinus Torvalds 		return count;
14871da177e4SLinus Torvalds 	}
14881da177e4SLinus Torvalds 	if (!strcmp(name, "src_min")) {
14891da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_min) - 1);
149063adc6fbSStephen Hemminger 		if (len < 0)
1491222f1806SLuiz Capitulino 			return len;
149263adc6fbSStephen Hemminger 
14931da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
14941da177e4SLinus Torvalds 			return -EFAULT;
14951da177e4SLinus Torvalds 		buf[len] = 0;
14961da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->src_min) != 0) {
14971da177e4SLinus Torvalds 			memset(pkt_dev->src_min, 0, sizeof(pkt_dev->src_min));
1498f15f084fSJakub Kicinski 			strcpy(pkt_dev->src_min, buf);
14991da177e4SLinus Torvalds 			pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
15001da177e4SLinus Torvalds 			pkt_dev->cur_saddr = pkt_dev->saddr_min;
15011da177e4SLinus Torvalds 		}
15021da177e4SLinus Torvalds 		if (debug)
1503f342cda7SJoe Perches 			pr_debug("src_min set to: %s\n", pkt_dev->src_min);
15041da177e4SLinus Torvalds 		i += len;
15051da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src_min=%s", pkt_dev->src_min);
15061da177e4SLinus Torvalds 		return count;
15071da177e4SLinus Torvalds 	}
15081da177e4SLinus Torvalds 	if (!strcmp(name, "src_max")) {
15091da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_max) - 1);
151063adc6fbSStephen Hemminger 		if (len < 0)
1511222f1806SLuiz Capitulino 			return len;
151263adc6fbSStephen Hemminger 
15131da177e4SLinus Torvalds 		if (copy_from_user(buf, &user_buffer[i], len))
15141da177e4SLinus Torvalds 			return -EFAULT;
15151da177e4SLinus Torvalds 		buf[len] = 0;
15161da177e4SLinus Torvalds 		if (strcmp(buf, pkt_dev->src_max) != 0) {
15171da177e4SLinus Torvalds 			memset(pkt_dev->src_max, 0, sizeof(pkt_dev->src_max));
1518f15f084fSJakub Kicinski 			strcpy(pkt_dev->src_max, buf);
15191da177e4SLinus Torvalds 			pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
15201da177e4SLinus Torvalds 			pkt_dev->cur_saddr = pkt_dev->saddr_max;
15211da177e4SLinus Torvalds 		}
15221da177e4SLinus Torvalds 		if (debug)
1523f342cda7SJoe Perches 			pr_debug("src_max set to: %s\n", pkt_dev->src_max);
15241da177e4SLinus Torvalds 		i += len;
15251da177e4SLinus Torvalds 		sprintf(pg_result, "OK: src_max=%s", pkt_dev->src_max);
15261da177e4SLinus Torvalds 		return count;
15271da177e4SLinus Torvalds 	}
15281da177e4SLinus Torvalds 	if (!strcmp(name, "dst_mac")) {
15291da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
153063adc6fbSStephen Hemminger 		if (len < 0)
1531222f1806SLuiz Capitulino 			return len;
153263adc6fbSStephen Hemminger 
15331da177e4SLinus Torvalds 		memset(valstr, 0, sizeof(valstr));
15341da177e4SLinus Torvalds 		if (copy_from_user(valstr, &user_buffer[i], len))
15351da177e4SLinus Torvalds 			return -EFAULT;
15361da177e4SLinus Torvalds 
15374940fc88SAlexey Dobriyan 		if (!mac_pton(valstr, pkt_dev->dst_mac))
15384940fc88SAlexey Dobriyan 			return -EINVAL;
15391da177e4SLinus Torvalds 		/* Set up Dest MAC */
15409ea08b12SJoe Perches 		ether_addr_copy(&pkt_dev->hh[0], pkt_dev->dst_mac);
15411da177e4SLinus Torvalds 
15424940fc88SAlexey Dobriyan 		sprintf(pg_result, "OK: dstmac %pM", pkt_dev->dst_mac);
15431da177e4SLinus Torvalds 		return count;
15441da177e4SLinus Torvalds 	}
15451da177e4SLinus Torvalds 	if (!strcmp(name, "src_mac")) {
15461da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
154763adc6fbSStephen Hemminger 		if (len < 0)
1548222f1806SLuiz Capitulino 			return len;
154963adc6fbSStephen Hemminger 
15501da177e4SLinus Torvalds 		memset(valstr, 0, sizeof(valstr));
15511da177e4SLinus Torvalds 		if (copy_from_user(valstr, &user_buffer[i], len))
15521da177e4SLinus Torvalds 			return -EFAULT;
15531da177e4SLinus Torvalds 
15544940fc88SAlexey Dobriyan 		if (!mac_pton(valstr, pkt_dev->src_mac))
15554940fc88SAlexey Dobriyan 			return -EINVAL;
1556ce5d0b47SAdit Ranadive 		/* Set up Src MAC */
15579ea08b12SJoe Perches 		ether_addr_copy(&pkt_dev->hh[6], pkt_dev->src_mac);
1558ce5d0b47SAdit Ranadive 
15594940fc88SAlexey Dobriyan 		sprintf(pg_result, "OK: srcmac %pM", pkt_dev->src_mac);
15601da177e4SLinus Torvalds 		return count;
15611da177e4SLinus Torvalds 	}
15621da177e4SLinus Torvalds 
15631da177e4SLinus Torvalds 	if (!strcmp(name, "clear_counters")) {
15641da177e4SLinus Torvalds 		pktgen_clear_counters(pkt_dev);
15651da177e4SLinus Torvalds 		sprintf(pg_result, "OK: Clearing counters.\n");
15661da177e4SLinus Torvalds 		return count;
15671da177e4SLinus Torvalds 	}
15681da177e4SLinus Torvalds 
15691da177e4SLinus Torvalds 	if (!strcmp(name, "flows")) {
15701da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
157163adc6fbSStephen Hemminger 		if (len < 0)
1572222f1806SLuiz Capitulino 			return len;
157363adc6fbSStephen Hemminger 
15741da177e4SLinus Torvalds 		i += len;
15751da177e4SLinus Torvalds 		if (value > MAX_CFLOWS)
15761da177e4SLinus Torvalds 			value = MAX_CFLOWS;
15771da177e4SLinus Torvalds 
15781da177e4SLinus Torvalds 		pkt_dev->cflows = value;
15791da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flows=%u", pkt_dev->cflows);
15801da177e4SLinus Torvalds 		return count;
15811da177e4SLinus Torvalds 	}
15826bae9190SFan Du #ifdef CONFIG_XFRM
1583de4aee7dSFan Du 	if (!strcmp(name, "spi")) {
1584de4aee7dSFan Du 		len = num_arg(&user_buffer[i], 10, &value);
1585de4aee7dSFan Du 		if (len < 0)
1586de4aee7dSFan Du 			return len;
1587de4aee7dSFan Du 
1588de4aee7dSFan Du 		i += len;
1589de4aee7dSFan Du 		pkt_dev->spi = value;
1590de4aee7dSFan Du 		sprintf(pg_result, "OK: spi=%u", pkt_dev->spi);
1591de4aee7dSFan Du 		return count;
1592de4aee7dSFan Du 	}
15936bae9190SFan Du #endif
15941da177e4SLinus Torvalds 	if (!strcmp(name, "flowlen")) {
15951da177e4SLinus Torvalds 		len = num_arg(&user_buffer[i], 10, &value);
159663adc6fbSStephen Hemminger 		if (len < 0)
1597222f1806SLuiz Capitulino 			return len;
159863adc6fbSStephen Hemminger 
15991da177e4SLinus Torvalds 		i += len;
16001da177e4SLinus Torvalds 		pkt_dev->lflow = value;
16011da177e4SLinus Torvalds 		sprintf(pg_result, "OK: flowlen=%u", pkt_dev->lflow);
16021da177e4SLinus Torvalds 		return count;
16031da177e4SLinus Torvalds 	}
16041da177e4SLinus Torvalds 
160545b270f8SRobert Olsson 	if (!strcmp(name, "queue_map_min")) {
160645b270f8SRobert Olsson 		len = num_arg(&user_buffer[i], 5, &value);
160763adc6fbSStephen Hemminger 		if (len < 0)
160845b270f8SRobert Olsson 			return len;
160963adc6fbSStephen Hemminger 
161045b270f8SRobert Olsson 		i += len;
161145b270f8SRobert Olsson 		pkt_dev->queue_map_min = value;
161245b270f8SRobert Olsson 		sprintf(pg_result, "OK: queue_map_min=%u", pkt_dev->queue_map_min);
161345b270f8SRobert Olsson 		return count;
161445b270f8SRobert Olsson 	}
161545b270f8SRobert Olsson 
161645b270f8SRobert Olsson 	if (!strcmp(name, "queue_map_max")) {
161745b270f8SRobert Olsson 		len = num_arg(&user_buffer[i], 5, &value);
161863adc6fbSStephen Hemminger 		if (len < 0)
161945b270f8SRobert Olsson 			return len;
162063adc6fbSStephen Hemminger 
162145b270f8SRobert Olsson 		i += len;
162245b270f8SRobert Olsson 		pkt_dev->queue_map_max = value;
162345b270f8SRobert Olsson 		sprintf(pg_result, "OK: queue_map_max=%u", pkt_dev->queue_map_max);
162445b270f8SRobert Olsson 		return count;
162545b270f8SRobert Olsson 	}
162645b270f8SRobert Olsson 
1627ca6549afSSteven Whitehouse 	if (!strcmp(name, "mpls")) {
162895c96174SEric Dumazet 		unsigned int n, cnt;
1629cfcabdccSStephen Hemminger 
1630ca6549afSSteven Whitehouse 		len = get_labels(&user_buffer[i], pkt_dev);
1631cfcabdccSStephen Hemminger 		if (len < 0)
1632cfcabdccSStephen Hemminger 			return len;
1633ca6549afSSteven Whitehouse 		i += len;
1634cfcabdccSStephen Hemminger 		cnt = sprintf(pg_result, "OK: mpls=");
1635ca6549afSSteven Whitehouse 		for (n = 0; n < pkt_dev->nr_labels; n++)
1636cfcabdccSStephen Hemminger 			cnt += sprintf(pg_result + cnt,
1637ca6549afSSteven Whitehouse 				       "%08x%s", ntohl(pkt_dev->labels[n]),
1638ca6549afSSteven Whitehouse 				       n == pkt_dev->nr_labels-1 ? "" : ",");
163934954ddcSFrancesco Fondelli 
164034954ddcSFrancesco Fondelli 		if (pkt_dev->nr_labels && pkt_dev->vlan_id != 0xffff) {
164134954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
164234954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
164334954ddcSFrancesco Fondelli 
164434954ddcSFrancesco Fondelli 			if (debug)
1645f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN auto turned off\n");
164634954ddcSFrancesco Fondelli 		}
164734954ddcSFrancesco Fondelli 		return count;
164834954ddcSFrancesco Fondelli 	}
164934954ddcSFrancesco Fondelli 
165034954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_id")) {
165134954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 4, &value);
165263adc6fbSStephen Hemminger 		if (len < 0)
165334954ddcSFrancesco Fondelli 			return len;
165463adc6fbSStephen Hemminger 
165534954ddcSFrancesco Fondelli 		i += len;
165634954ddcSFrancesco Fondelli 		if (value <= 4095) {
165734954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = value;  /* turn on VLAN */
165834954ddcSFrancesco Fondelli 
165934954ddcSFrancesco Fondelli 			if (debug)
1660f342cda7SJoe Perches 				pr_debug("VLAN turned on\n");
166134954ddcSFrancesco Fondelli 
166234954ddcSFrancesco Fondelli 			if (debug && pkt_dev->nr_labels)
1663f342cda7SJoe Perches 				pr_debug("MPLS auto turned off\n");
166434954ddcSFrancesco Fondelli 
166534954ddcSFrancesco Fondelli 			pkt_dev->nr_labels = 0;    /* turn off MPLS */
166634954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_id=%u", pkt_dev->vlan_id);
166734954ddcSFrancesco Fondelli 		} else {
166834954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
166934954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
167034954ddcSFrancesco Fondelli 
167134954ddcSFrancesco Fondelli 			if (debug)
1672f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN turned off\n");
167334954ddcSFrancesco Fondelli 		}
167434954ddcSFrancesco Fondelli 		return count;
167534954ddcSFrancesco Fondelli 	}
167634954ddcSFrancesco Fondelli 
167734954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_p")) {
167834954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
167963adc6fbSStephen Hemminger 		if (len < 0)
168034954ddcSFrancesco Fondelli 			return len;
168163adc6fbSStephen Hemminger 
168234954ddcSFrancesco Fondelli 		i += len;
168334954ddcSFrancesco Fondelli 		if ((value <= 7) && (pkt_dev->vlan_id != 0xffff)) {
168434954ddcSFrancesco Fondelli 			pkt_dev->vlan_p = value;
168534954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_p=%u", pkt_dev->vlan_p);
168634954ddcSFrancesco Fondelli 		} else {
168734954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: vlan_p must be 0-7");
168834954ddcSFrancesco Fondelli 		}
168934954ddcSFrancesco Fondelli 		return count;
169034954ddcSFrancesco Fondelli 	}
169134954ddcSFrancesco Fondelli 
169234954ddcSFrancesco Fondelli 	if (!strcmp(name, "vlan_cfi")) {
169334954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
169463adc6fbSStephen Hemminger 		if (len < 0)
169534954ddcSFrancesco Fondelli 			return len;
169663adc6fbSStephen Hemminger 
169734954ddcSFrancesco Fondelli 		i += len;
169834954ddcSFrancesco Fondelli 		if ((value <= 1) && (pkt_dev->vlan_id != 0xffff)) {
169934954ddcSFrancesco Fondelli 			pkt_dev->vlan_cfi = value;
170034954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: vlan_cfi=%u", pkt_dev->vlan_cfi);
170134954ddcSFrancesco Fondelli 		} else {
170234954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: vlan_cfi must be 0-1");
170334954ddcSFrancesco Fondelli 		}
170434954ddcSFrancesco Fondelli 		return count;
170534954ddcSFrancesco Fondelli 	}
170634954ddcSFrancesco Fondelli 
170734954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_id")) {
170834954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 4, &value);
170963adc6fbSStephen Hemminger 		if (len < 0)
171034954ddcSFrancesco Fondelli 			return len;
171163adc6fbSStephen Hemminger 
171234954ddcSFrancesco Fondelli 		i += len;
171334954ddcSFrancesco Fondelli 		if ((value <= 4095) && ((pkt_dev->vlan_id != 0xffff))) {
171434954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = value;  /* turn on SVLAN */
171534954ddcSFrancesco Fondelli 
171634954ddcSFrancesco Fondelli 			if (debug)
1717f342cda7SJoe Perches 				pr_debug("SVLAN turned on\n");
171834954ddcSFrancesco Fondelli 
171934954ddcSFrancesco Fondelli 			if (debug && pkt_dev->nr_labels)
1720f342cda7SJoe Perches 				pr_debug("MPLS auto turned off\n");
172134954ddcSFrancesco Fondelli 
172234954ddcSFrancesco Fondelli 			pkt_dev->nr_labels = 0;    /* turn off MPLS */
172334954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_id=%u", pkt_dev->svlan_id);
172434954ddcSFrancesco Fondelli 		} else {
172534954ddcSFrancesco Fondelli 			pkt_dev->vlan_id = 0xffff; /* turn off VLAN/SVLAN */
172634954ddcSFrancesco Fondelli 			pkt_dev->svlan_id = 0xffff;
172734954ddcSFrancesco Fondelli 
172834954ddcSFrancesco Fondelli 			if (debug)
1729f342cda7SJoe Perches 				pr_debug("VLAN/SVLAN turned off\n");
173034954ddcSFrancesco Fondelli 		}
173134954ddcSFrancesco Fondelli 		return count;
173234954ddcSFrancesco Fondelli 	}
173334954ddcSFrancesco Fondelli 
173434954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_p")) {
173534954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
173663adc6fbSStephen Hemminger 		if (len < 0)
173734954ddcSFrancesco Fondelli 			return len;
173863adc6fbSStephen Hemminger 
173934954ddcSFrancesco Fondelli 		i += len;
174034954ddcSFrancesco Fondelli 		if ((value <= 7) && (pkt_dev->svlan_id != 0xffff)) {
174134954ddcSFrancesco Fondelli 			pkt_dev->svlan_p = value;
174234954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_p=%u", pkt_dev->svlan_p);
174334954ddcSFrancesco Fondelli 		} else {
174434954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: svlan_p must be 0-7");
174534954ddcSFrancesco Fondelli 		}
174634954ddcSFrancesco Fondelli 		return count;
174734954ddcSFrancesco Fondelli 	}
174834954ddcSFrancesco Fondelli 
174934954ddcSFrancesco Fondelli 	if (!strcmp(name, "svlan_cfi")) {
175034954ddcSFrancesco Fondelli 		len = num_arg(&user_buffer[i], 1, &value);
175163adc6fbSStephen Hemminger 		if (len < 0)
175234954ddcSFrancesco Fondelli 			return len;
175363adc6fbSStephen Hemminger 
175434954ddcSFrancesco Fondelli 		i += len;
175534954ddcSFrancesco Fondelli 		if ((value <= 1) && (pkt_dev->svlan_id != 0xffff)) {
175634954ddcSFrancesco Fondelli 			pkt_dev->svlan_cfi = value;
175734954ddcSFrancesco Fondelli 			sprintf(pg_result, "OK: svlan_cfi=%u", pkt_dev->svlan_cfi);
175834954ddcSFrancesco Fondelli 		} else {
175934954ddcSFrancesco Fondelli 			sprintf(pg_result, "ERROR: svlan_cfi must be 0-1");
176034954ddcSFrancesco Fondelli 		}
1761ca6549afSSteven Whitehouse 		return count;
1762ca6549afSSteven Whitehouse 	}
1763ca6549afSSteven Whitehouse 
17641ca7768cSFrancesco Fondelli 	if (!strcmp(name, "tos")) {
17651ca7768cSFrancesco Fondelli 		__u32 tmp_value = 0;
17661ca7768cSFrancesco Fondelli 		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
176763adc6fbSStephen Hemminger 		if (len < 0)
17681ca7768cSFrancesco Fondelli 			return len;
176963adc6fbSStephen Hemminger 
17701ca7768cSFrancesco Fondelli 		i += len;
17711ca7768cSFrancesco Fondelli 		if (len == 2) {
17721ca7768cSFrancesco Fondelli 			pkt_dev->tos = tmp_value;
17731ca7768cSFrancesco Fondelli 			sprintf(pg_result, "OK: tos=0x%02x", pkt_dev->tos);
17741ca7768cSFrancesco Fondelli 		} else {
17751ca7768cSFrancesco Fondelli 			sprintf(pg_result, "ERROR: tos must be 00-ff");
17761ca7768cSFrancesco Fondelli 		}
17771ca7768cSFrancesco Fondelli 		return count;
17781ca7768cSFrancesco Fondelli 	}
17791ca7768cSFrancesco Fondelli 
17801ca7768cSFrancesco Fondelli 	if (!strcmp(name, "traffic_class")) {
17811ca7768cSFrancesco Fondelli 		__u32 tmp_value = 0;
17821ca7768cSFrancesco Fondelli 		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
178363adc6fbSStephen Hemminger 		if (len < 0)
17841ca7768cSFrancesco Fondelli 			return len;
178563adc6fbSStephen Hemminger 
17861ca7768cSFrancesco Fondelli 		i += len;
17871ca7768cSFrancesco Fondelli 		if (len == 2) {
17881ca7768cSFrancesco Fondelli 			pkt_dev->traffic_class = tmp_value;
17891ca7768cSFrancesco Fondelli 			sprintf(pg_result, "OK: traffic_class=0x%02x", pkt_dev->traffic_class);
17901ca7768cSFrancesco Fondelli 		} else {
17911ca7768cSFrancesco Fondelli 			sprintf(pg_result, "ERROR: traffic_class must be 00-ff");
17921ca7768cSFrancesco Fondelli 		}
17931ca7768cSFrancesco Fondelli 		return count;
17941ca7768cSFrancesco Fondelli 	}
17951ca7768cSFrancesco Fondelli 
17969e50e3acSJohn Fastabend 	if (!strcmp(name, "skb_priority")) {
17979e50e3acSJohn Fastabend 		len = num_arg(&user_buffer[i], 9, &value);
17989e50e3acSJohn Fastabend 		if (len < 0)
17999e50e3acSJohn Fastabend 			return len;
18009e50e3acSJohn Fastabend 
18019e50e3acSJohn Fastabend 		i += len;
18029e50e3acSJohn Fastabend 		pkt_dev->skb_priority = value;
18039e50e3acSJohn Fastabend 		sprintf(pg_result, "OK: skb_priority=%i",
18049e50e3acSJohn Fastabend 			pkt_dev->skb_priority);
18059e50e3acSJohn Fastabend 		return count;
18069e50e3acSJohn Fastabend 	}
18079e50e3acSJohn Fastabend 
18081da177e4SLinus Torvalds 	sprintf(pkt_dev->result, "No such parameter \"%s\"", name);
18091da177e4SLinus Torvalds 	return -EINVAL;
18101da177e4SLinus Torvalds }
18111da177e4SLinus Torvalds 
pktgen_if_open(struct inode * inode,struct file * file)1812d50a6b56SStephen Hemminger static int pktgen_if_open(struct inode *inode, struct file *file)
18131da177e4SLinus Torvalds {
1814359745d7SMuchun Song 	return single_open(file, pktgen_if_show, pde_data(inode));
18151da177e4SLinus Torvalds }
18161da177e4SLinus Torvalds 
181797a32539SAlexey Dobriyan static const struct proc_ops pktgen_if_proc_ops = {
181897a32539SAlexey Dobriyan 	.proc_open	= pktgen_if_open,
181997a32539SAlexey Dobriyan 	.proc_read	= seq_read,
182097a32539SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
182197a32539SAlexey Dobriyan 	.proc_write	= pktgen_if_write,
182297a32539SAlexey Dobriyan 	.proc_release	= single_release,
1823d50a6b56SStephen Hemminger };
1824d50a6b56SStephen Hemminger 
pktgen_thread_show(struct seq_file * seq,void * v)1825d50a6b56SStephen Hemminger static int pktgen_thread_show(struct seq_file *seq, void *v)
1826d50a6b56SStephen Hemminger {
1827d50a6b56SStephen Hemminger 	struct pktgen_thread *t = seq->private;
1828648fda74SStephen Hemminger 	const struct pktgen_dev *pkt_dev;
1829d50a6b56SStephen Hemminger 
1830d50a6b56SStephen Hemminger 	BUG_ON(!t);
1831d50a6b56SStephen Hemminger 
183297dc48e2SThomas Graf 	seq_puts(seq, "Running: ");
18331da177e4SLinus Torvalds 
18348788370aSJesper Dangaard Brouer 	rcu_read_lock();
18358788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
18361da177e4SLinus Torvalds 		if (pkt_dev->running)
1837593f63b0SEric Dumazet 			seq_printf(seq, "%s ", pkt_dev->odevname);
18381da177e4SLinus Torvalds 
183997dc48e2SThomas Graf 	seq_puts(seq, "\nStopped: ");
18401da177e4SLinus Torvalds 
18418788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
18421da177e4SLinus Torvalds 		if (!pkt_dev->running)
1843593f63b0SEric Dumazet 			seq_printf(seq, "%s ", pkt_dev->odevname);
18441da177e4SLinus Torvalds 
18451da177e4SLinus Torvalds 	if (t->result[0])
1846d50a6b56SStephen Hemminger 		seq_printf(seq, "\nResult: %s\n", t->result);
18471da177e4SLinus Torvalds 	else
184897dc48e2SThomas Graf 		seq_puts(seq, "\nResult: NA\n");
18491da177e4SLinus Torvalds 
18508788370aSJesper Dangaard Brouer 	rcu_read_unlock();
18511da177e4SLinus Torvalds 
1852d50a6b56SStephen Hemminger 	return 0;
18531da177e4SLinus Torvalds }
18541da177e4SLinus Torvalds 
pktgen_thread_write(struct file * file,const char __user * user_buffer,size_t count,loff_t * offset)1855d50a6b56SStephen Hemminger static ssize_t pktgen_thread_write(struct file *file,
1856d50a6b56SStephen Hemminger 				   const char __user * user_buffer,
1857d50a6b56SStephen Hemminger 				   size_t count, loff_t * offset)
18581da177e4SLinus Torvalds {
18598a994a71SJoe Perches 	struct seq_file *seq = file->private_data;
1860d50a6b56SStephen Hemminger 	struct pktgen_thread *t = seq->private;
1861d6182223SPaul Gortmaker 	int i, max, len, ret;
18621da177e4SLinus Torvalds 	char name[40];
18631da177e4SLinus Torvalds 	char *pg_result;
18641da177e4SLinus Torvalds 
18651da177e4SLinus Torvalds 	if (count < 1) {
18661da177e4SLinus Torvalds 		//      sprintf(pg_result, "Wrong command format");
18671da177e4SLinus Torvalds 		return -EINVAL;
18681da177e4SLinus Torvalds 	}
18691da177e4SLinus Torvalds 
1870d6182223SPaul Gortmaker 	max = count;
1871d6182223SPaul Gortmaker 	len = count_trail_chars(user_buffer, max);
18721da177e4SLinus Torvalds 	if (len < 0)
18731da177e4SLinus Torvalds 		return len;
18741da177e4SLinus Torvalds 
1875d6182223SPaul Gortmaker 	i = len;
18761da177e4SLinus Torvalds 
18771da177e4SLinus Torvalds 	/* Read variable name */
18781da177e4SLinus Torvalds 
18791da177e4SLinus Torvalds 	len = strn_len(&user_buffer[i], sizeof(name) - 1);
18801da177e4SLinus Torvalds 	if (len < 0)
18811da177e4SLinus Torvalds 		return len;
18821da177e4SLinus Torvalds 
18831da177e4SLinus Torvalds 	memset(name, 0, sizeof(name));
18841da177e4SLinus Torvalds 	if (copy_from_user(name, &user_buffer[i], len))
18851da177e4SLinus Torvalds 		return -EFAULT;
18861da177e4SLinus Torvalds 	i += len;
18871da177e4SLinus Torvalds 
18881da177e4SLinus Torvalds 	max = count - i;
18891da177e4SLinus Torvalds 	len = count_trail_chars(&user_buffer[i], max);
18901da177e4SLinus Torvalds 	if (len < 0)
18911da177e4SLinus Torvalds 		return len;
18921da177e4SLinus Torvalds 
18931da177e4SLinus Torvalds 	i += len;
18941da177e4SLinus Torvalds 
18951da177e4SLinus Torvalds 	if (debug)
1896f342cda7SJoe Perches 		pr_debug("t=%s, count=%lu\n", name, (unsigned long)count);
18971da177e4SLinus Torvalds 
18981da177e4SLinus Torvalds 	if (!t) {
1899f9467eaeSJoe Perches 		pr_err("ERROR: No thread\n");
19001da177e4SLinus Torvalds 		ret = -EINVAL;
19011da177e4SLinus Torvalds 		goto out;
19021da177e4SLinus Torvalds 	}
19031da177e4SLinus Torvalds 
19041da177e4SLinus Torvalds 	pg_result = &(t->result[0]);
19051da177e4SLinus Torvalds 
19061da177e4SLinus Torvalds 	if (!strcmp(name, "add_device")) {
19071da177e4SLinus Torvalds 		char f[32];
19081da177e4SLinus Torvalds 		memset(f, 0, 32);
19091da177e4SLinus Torvalds 		len = strn_len(&user_buffer[i], sizeof(f) - 1);
19101da177e4SLinus Torvalds 		if (len < 0) {
19111da177e4SLinus Torvalds 			ret = len;
19121da177e4SLinus Torvalds 			goto out;
19131da177e4SLinus Torvalds 		}
19141da177e4SLinus Torvalds 		if (copy_from_user(f, &user_buffer[i], len))
19151da177e4SLinus Torvalds 			return -EFAULT;
19161da177e4SLinus Torvalds 		i += len;
19176146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
1918604dfd6eSCong Wang 		ret = pktgen_add_device(t, f);
19196146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1920604dfd6eSCong Wang 		if (!ret) {
19211da177e4SLinus Torvalds 			ret = count;
19221da177e4SLinus Torvalds 			sprintf(pg_result, "OK: add_device=%s", f);
1923604dfd6eSCong Wang 		} else
1924604dfd6eSCong Wang 			sprintf(pg_result, "ERROR: can not add device %s", f);
19251da177e4SLinus Torvalds 		goto out;
19261da177e4SLinus Torvalds 	}
19271da177e4SLinus Torvalds 
19281da177e4SLinus Torvalds 	if (!strcmp(name, "rem_device_all")) {
19296146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
193095ed63f7SArthur Kepner 		t->control |= T_REMDEVALL;
19316146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
1932121caf57SNishanth Aravamudan 		schedule_timeout_interruptible(msecs_to_jiffies(125));	/* Propagate thread->control  */
19331da177e4SLinus Torvalds 		ret = count;
19341da177e4SLinus Torvalds 		sprintf(pg_result, "OK: rem_device_all");
19351da177e4SLinus Torvalds 		goto out;
19361da177e4SLinus Torvalds 	}
19371da177e4SLinus Torvalds 
19381da177e4SLinus Torvalds 	if (!strcmp(name, "max_before_softirq")) {
1939b163911fSRobert Olsson 		sprintf(pg_result, "OK: Note! max_before_softirq is obsoleted -- Do not use");
19401da177e4SLinus Torvalds 		ret = count;
19411da177e4SLinus Torvalds 		goto out;
19421da177e4SLinus Torvalds 	}
19431da177e4SLinus Torvalds 
19441da177e4SLinus Torvalds 	ret = -EINVAL;
19451da177e4SLinus Torvalds out:
19461da177e4SLinus Torvalds 	return ret;
19471da177e4SLinus Torvalds }
19481da177e4SLinus Torvalds 
pktgen_thread_open(struct inode * inode,struct file * file)1949d50a6b56SStephen Hemminger static int pktgen_thread_open(struct inode *inode, struct file *file)
19501da177e4SLinus Torvalds {
1951359745d7SMuchun Song 	return single_open(file, pktgen_thread_show, pde_data(inode));
19521da177e4SLinus Torvalds }
19531da177e4SLinus Torvalds 
195497a32539SAlexey Dobriyan static const struct proc_ops pktgen_thread_proc_ops = {
195597a32539SAlexey Dobriyan 	.proc_open	= pktgen_thread_open,
195697a32539SAlexey Dobriyan 	.proc_read	= seq_read,
195797a32539SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
195897a32539SAlexey Dobriyan 	.proc_write	= pktgen_thread_write,
195997a32539SAlexey Dobriyan 	.proc_release	= single_release,
1960d50a6b56SStephen Hemminger };
19611da177e4SLinus Torvalds 
19621da177e4SLinus Torvalds /* Think find or remove for NN */
__pktgen_NN_threads(const struct pktgen_net * pn,const char * ifname,int remove)19634e58a027SCong Wang static struct pktgen_dev *__pktgen_NN_threads(const struct pktgen_net *pn,
19644e58a027SCong Wang 					      const char *ifname, int remove)
19651da177e4SLinus Torvalds {
19661da177e4SLinus Torvalds 	struct pktgen_thread *t;
19671da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
19683e984840SEric Dumazet 	bool exact = (remove == FIND);
19691da177e4SLinus Torvalds 
19704e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
19713e984840SEric Dumazet 		pkt_dev = pktgen_find_dev(t, ifname, exact);
19721da177e4SLinus Torvalds 		if (pkt_dev) {
19731da177e4SLinus Torvalds 			if (remove) {
197495ed63f7SArthur Kepner 				pkt_dev->removal_mark = 1;
197595ed63f7SArthur Kepner 				t->control |= T_REMDEV;
19761da177e4SLinus Torvalds 			}
19771da177e4SLinus Torvalds 			break;
19781da177e4SLinus Torvalds 		}
19791da177e4SLinus Torvalds 	}
19801da177e4SLinus Torvalds 	return pkt_dev;
19811da177e4SLinus Torvalds }
19821da177e4SLinus Torvalds 
198395ed63f7SArthur Kepner /*
198495ed63f7SArthur Kepner  * mark a device for removal
198595ed63f7SArthur Kepner  */
pktgen_mark_device(const struct pktgen_net * pn,const char * ifname)19864e58a027SCong Wang static void pktgen_mark_device(const struct pktgen_net *pn, const char *ifname)
19871da177e4SLinus Torvalds {
19881da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
198995ed63f7SArthur Kepner 	const int max_tries = 10, msec_per_try = 125;
199095ed63f7SArthur Kepner 	int i = 0;
199195ed63f7SArthur Kepner 
19926146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
1993f9467eaeSJoe Perches 	pr_debug("%s: marking %s for removal\n", __func__, ifname);
199495ed63f7SArthur Kepner 
199595ed63f7SArthur Kepner 	while (1) {
199695ed63f7SArthur Kepner 
19974e58a027SCong Wang 		pkt_dev = __pktgen_NN_threads(pn, ifname, REMOVE);
1998222f1806SLuiz Capitulino 		if (pkt_dev == NULL)
1999222f1806SLuiz Capitulino 			break;	/* success */
200095ed63f7SArthur Kepner 
20016146e6a4SLuiz Capitulino 		mutex_unlock(&pktgen_thread_lock);
2002f9467eaeSJoe Perches 		pr_debug("%s: waiting for %s to disappear....\n",
2003f9467eaeSJoe Perches 			 __func__, ifname);
200495ed63f7SArthur Kepner 		schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try));
20056146e6a4SLuiz Capitulino 		mutex_lock(&pktgen_thread_lock);
200695ed63f7SArthur Kepner 
200795ed63f7SArthur Kepner 		if (++i >= max_tries) {
2008f9467eaeSJoe Perches 			pr_err("%s: timed out after waiting %d msec for device %s to be removed\n",
2009f9467eaeSJoe Perches 			       __func__, msec_per_try * i, ifname);
201095ed63f7SArthur Kepner 			break;
201195ed63f7SArthur Kepner 		}
201295ed63f7SArthur Kepner 
201395ed63f7SArthur Kepner 	}
201495ed63f7SArthur Kepner 
20156146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
201639df232fSStephen Hemminger }
201795ed63f7SArthur Kepner 
pktgen_change_name(const struct pktgen_net * pn,struct net_device * dev)20184e58a027SCong Wang static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *dev)
201939df232fSStephen Hemminger {
202039df232fSStephen Hemminger 	struct pktgen_thread *t;
202139df232fSStephen Hemminger 
20229a0b1e8bSEric Dumazet 	mutex_lock(&pktgen_thread_lock);
20239a0b1e8bSEric Dumazet 
20244e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
202539df232fSStephen Hemminger 		struct pktgen_dev *pkt_dev;
202639df232fSStephen Hemminger 
20279a0b1e8bSEric Dumazet 		if_lock(t);
20289a0b1e8bSEric Dumazet 		list_for_each_entry(pkt_dev, &t->if_list, list) {
202939df232fSStephen Hemminger 			if (pkt_dev->odev != dev)
203039df232fSStephen Hemminger 				continue;
203139df232fSStephen Hemminger 
2032a8ca16eaSDavid Howells 			proc_remove(pkt_dev->entry);
203339df232fSStephen Hemminger 
20342975315bSAlexey Dobriyan 			pkt_dev->entry = proc_create_data(dev->name, 0600,
20354e58a027SCong Wang 							  pn->proc_dir,
203697a32539SAlexey Dobriyan 							  &pktgen_if_proc_ops,
20372975315bSAlexey Dobriyan 							  pkt_dev);
203839df232fSStephen Hemminger 			if (!pkt_dev->entry)
2039f9467eaeSJoe Perches 				pr_err("can't move proc entry for '%s'\n",
2040f9467eaeSJoe Perches 				       dev->name);
204139df232fSStephen Hemminger 			break;
204239df232fSStephen Hemminger 		}
20439a0b1e8bSEric Dumazet 		if_unlock(t);
204439df232fSStephen Hemminger 	}
20459a0b1e8bSEric Dumazet 	mutex_unlock(&pktgen_thread_lock);
20461da177e4SLinus Torvalds }
20471da177e4SLinus Torvalds 
pktgen_device_event(struct notifier_block * unused,unsigned long event,void * ptr)2048222f1806SLuiz Capitulino static int pktgen_device_event(struct notifier_block *unused,
2049222f1806SLuiz Capitulino 			       unsigned long event, void *ptr)
20501da177e4SLinus Torvalds {
2051351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
20524e58a027SCong Wang 	struct pktgen_net *pn = net_generic(dev_net(dev), pg_net_id);
20531da177e4SLinus Torvalds 
20544e58a027SCong Wang 	if (pn->pktgen_exiting)
2055e9dc8653SEric W. Biederman 		return NOTIFY_DONE;
2056e9dc8653SEric W. Biederman 
20571da177e4SLinus Torvalds 	/* It is OK that we do not hold the group lock right now,
20581da177e4SLinus Torvalds 	 * as we run under the RTNL lock.
20591da177e4SLinus Torvalds 	 */
20601da177e4SLinus Torvalds 
20611da177e4SLinus Torvalds 	switch (event) {
206239df232fSStephen Hemminger 	case NETDEV_CHANGENAME:
20634e58a027SCong Wang 		pktgen_change_name(pn, dev);
20641da177e4SLinus Torvalds 		break;
20651da177e4SLinus Torvalds 
20661da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
20674e58a027SCong Wang 		pktgen_mark_device(pn, dev->name);
20681da177e4SLinus Torvalds 		break;
20693ff50b79SStephen Hemminger 	}
20701da177e4SLinus Torvalds 
20711da177e4SLinus Torvalds 	return NOTIFY_DONE;
20721da177e4SLinus Torvalds }
20731da177e4SLinus Torvalds 
pktgen_dev_get_by_name(const struct pktgen_net * pn,struct pktgen_dev * pkt_dev,const char * ifname)20744e58a027SCong Wang static struct net_device *pktgen_dev_get_by_name(const struct pktgen_net *pn,
20754e58a027SCong Wang 						 struct pktgen_dev *pkt_dev,
207663adc6fbSStephen Hemminger 						 const char *ifname)
2077e6fce5b9SRobert Olsson {
2078e6fce5b9SRobert Olsson 	char b[IFNAMSIZ+5];
2079d6182223SPaul Gortmaker 	int i;
2080e6fce5b9SRobert Olsson 
2081e6fce5b9SRobert Olsson 	for (i = 0; ifname[i] != '@'; i++) {
2082e6fce5b9SRobert Olsson 		if (i == IFNAMSIZ)
2083e6fce5b9SRobert Olsson 			break;
2084e6fce5b9SRobert Olsson 
2085e6fce5b9SRobert Olsson 		b[i] = ifname[i];
2086e6fce5b9SRobert Olsson 	}
2087e6fce5b9SRobert Olsson 	b[i] = 0;
2088e6fce5b9SRobert Olsson 
20894e58a027SCong Wang 	return dev_get_by_name(pn->net, b);
2090e6fce5b9SRobert Olsson }
2091e6fce5b9SRobert Olsson 
2092e6fce5b9SRobert Olsson 
20931da177e4SLinus Torvalds /* Associate pktgen_dev with a device. */
20941da177e4SLinus Torvalds 
pktgen_setup_dev(const struct pktgen_net * pn,struct pktgen_dev * pkt_dev,const char * ifname)20954e58a027SCong Wang static int pktgen_setup_dev(const struct pktgen_net *pn,
20964e58a027SCong Wang 			    struct pktgen_dev *pkt_dev, const char *ifname)
2097222f1806SLuiz Capitulino {
20981da177e4SLinus Torvalds 	struct net_device *odev;
209939df232fSStephen Hemminger 	int err;
21001da177e4SLinus Torvalds 
21011da177e4SLinus Torvalds 	/* Clean old setups */
21021da177e4SLinus Torvalds 	if (pkt_dev->odev) {
2103d62607c3SJakub Kicinski 		netdev_put(pkt_dev->odev, &pkt_dev->dev_tracker);
21041da177e4SLinus Torvalds 		pkt_dev->odev = NULL;
21051da177e4SLinus Torvalds 	}
21061da177e4SLinus Torvalds 
21074e58a027SCong Wang 	odev = pktgen_dev_get_by_name(pn, pkt_dev, ifname);
21081da177e4SLinus Torvalds 	if (!odev) {
2109f9467eaeSJoe Perches 		pr_err("no such netdevice: \"%s\"\n", ifname);
211039df232fSStephen Hemminger 		return -ENODEV;
21111da177e4SLinus Torvalds 	}
211239df232fSStephen Hemminger 
21131e09e581SLukas Wunner 	if (odev->type != ARPHRD_ETHER && odev->type != ARPHRD_LOOPBACK) {
21141e09e581SLukas Wunner 		pr_err("not an ethernet or loopback device: \"%s\"\n", ifname);
211539df232fSStephen Hemminger 		err = -EINVAL;
211639df232fSStephen Hemminger 	} else if (!netif_running(odev)) {
2117f9467eaeSJoe Perches 		pr_err("device is down: \"%s\"\n", ifname);
211839df232fSStephen Hemminger 		err = -ENETDOWN;
211939df232fSStephen Hemminger 	} else {
21201da177e4SLinus Torvalds 		pkt_dev->odev = odev;
2121035f1f2bSEric Dumazet 		netdev_tracker_alloc(odev, &pkt_dev->dev_tracker, GFP_KERNEL);
212239df232fSStephen Hemminger 		return 0;
212339df232fSStephen Hemminger 	}
21241da177e4SLinus Torvalds 
21251da177e4SLinus Torvalds 	dev_put(odev);
212639df232fSStephen Hemminger 	return err;
21271da177e4SLinus Torvalds }
21281da177e4SLinus Torvalds 
21291da177e4SLinus Torvalds /* Read pkt_dev from the interface and set up internal pktgen_dev
21301da177e4SLinus Torvalds  * structure to have the right information to create/send packets
21311da177e4SLinus Torvalds  */
pktgen_setup_inject(struct pktgen_dev * pkt_dev)21321da177e4SLinus Torvalds static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
21331da177e4SLinus Torvalds {
213464c00d81SAndrew Gallatin 	int ntxq;
213564c00d81SAndrew Gallatin 
21361da177e4SLinus Torvalds 	if (!pkt_dev->odev) {
2137f9467eaeSJoe Perches 		pr_err("ERROR: pkt_dev->odev == NULL in setup_inject\n");
2138222f1806SLuiz Capitulino 		sprintf(pkt_dev->result,
2139222f1806SLuiz Capitulino 			"ERROR: pkt_dev->odev == NULL in setup_inject.\n");
21401da177e4SLinus Torvalds 		return;
21411da177e4SLinus Torvalds 	}
21421da177e4SLinus Torvalds 
214364c00d81SAndrew Gallatin 	/* make sure that we don't pick a non-existing transmit queue */
214464c00d81SAndrew Gallatin 	ntxq = pkt_dev->odev->real_num_tx_queues;
2145bfdbc0acSRobert Olsson 
214664c00d81SAndrew Gallatin 	if (ntxq <= pkt_dev->queue_map_min) {
2147294a0b7fSJoe Perches 		pr_warn("WARNING: Requested queue_map_min (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting\n",
214888271660SJesse Brandeburg 			pkt_dev->queue_map_min, (ntxq ?: 1) - 1, ntxq,
2149593f63b0SEric Dumazet 			pkt_dev->odevname);
215026e29eedSDan Carpenter 		pkt_dev->queue_map_min = (ntxq ?: 1) - 1;
215164c00d81SAndrew Gallatin 	}
215288271660SJesse Brandeburg 	if (pkt_dev->queue_map_max >= ntxq) {
2153294a0b7fSJoe Perches 		pr_warn("WARNING: Requested queue_map_max (zero-based) (%d) exceeds valid range [0 - %d] for (%d) queues on %s, resetting\n",
215488271660SJesse Brandeburg 			pkt_dev->queue_map_max, (ntxq ?: 1) - 1, ntxq,
2155593f63b0SEric Dumazet 			pkt_dev->odevname);
215626e29eedSDan Carpenter 		pkt_dev->queue_map_max = (ntxq ?: 1) - 1;
215764c00d81SAndrew Gallatin 	}
215864c00d81SAndrew Gallatin 
21591da177e4SLinus Torvalds 	/* Default to the interface's mac if not explicitly set. */
21601da177e4SLinus Torvalds 
2161f404e9a6SKris Katterjohn 	if (is_zero_ether_addr(pkt_dev->src_mac))
21629ea08b12SJoe Perches 		ether_addr_copy(&(pkt_dev->hh[6]), pkt_dev->odev->dev_addr);
21631da177e4SLinus Torvalds 
21641da177e4SLinus Torvalds 	/* Set up Dest MAC */
21659ea08b12SJoe Perches 	ether_addr_copy(&(pkt_dev->hh[0]), pkt_dev->dst_mac);
21661da177e4SLinus Torvalds 
21671da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6) {
21684c139b8cSAmerigo Wang 		int i, set = 0, err = 1;
21694c139b8cSAmerigo Wang 		struct inet6_dev *idev;
21704c139b8cSAmerigo Wang 
217168bf9f0bSAmerigo Wang 		if (pkt_dev->min_pkt_size == 0) {
217268bf9f0bSAmerigo Wang 			pkt_dev->min_pkt_size = 14 + sizeof(struct ipv6hdr)
217368bf9f0bSAmerigo Wang 						+ sizeof(struct udphdr)
217468bf9f0bSAmerigo Wang 						+ sizeof(struct pktgen_hdr)
217568bf9f0bSAmerigo Wang 						+ pkt_dev->pkt_overhead;
217668bf9f0bSAmerigo Wang 		}
217768bf9f0bSAmerigo Wang 
2178df7e8e2eSEric Dumazet 		for (i = 0; i < sizeof(struct in6_addr); i++)
21791da177e4SLinus Torvalds 			if (pkt_dev->cur_in6_saddr.s6_addr[i]) {
21801da177e4SLinus Torvalds 				set = 1;
21811da177e4SLinus Torvalds 				break;
21821da177e4SLinus Torvalds 			}
21831da177e4SLinus Torvalds 
21841da177e4SLinus Torvalds 		if (!set) {
21851da177e4SLinus Torvalds 
21861da177e4SLinus Torvalds 			/*
21871da177e4SLinus Torvalds 			 * Use linklevel address if unconfigured.
21881da177e4SLinus Torvalds 			 *
21891da177e4SLinus Torvalds 			 * use ipv6_get_lladdr if/when it's get exported
21901da177e4SLinus Torvalds 			 */
21911da177e4SLinus Torvalds 
21928814c4b5SYOSHIFUJI Hideaki 			rcu_read_lock();
219363adc6fbSStephen Hemminger 			idev = __in6_dev_get(pkt_dev->odev);
219463adc6fbSStephen Hemminger 			if (idev) {
21951da177e4SLinus Torvalds 				struct inet6_ifaddr *ifp;
21961da177e4SLinus Torvalds 
21971da177e4SLinus Torvalds 				read_lock_bh(&idev->lock);
21984c139b8cSAmerigo Wang 				list_for_each_entry(ifp, &idev->addr_list, if_list) {
21994c139b8cSAmerigo Wang 					if ((ifp->scope & IFA_LINK) &&
2200f64f9e71SJoe Perches 					    !(ifp->flags & IFA_F_TENTATIVE)) {
22014e3fd7a0SAlexey Dobriyan 						pkt_dev->cur_in6_saddr = ifp->addr;
22021da177e4SLinus Torvalds 						err = 0;
22031da177e4SLinus Torvalds 						break;
22041da177e4SLinus Torvalds 					}
22051da177e4SLinus Torvalds 				}
22061da177e4SLinus Torvalds 				read_unlock_bh(&idev->lock);
22071da177e4SLinus Torvalds 			}
22088814c4b5SYOSHIFUJI Hideaki 			rcu_read_unlock();
2209222f1806SLuiz Capitulino 			if (err)
2210f9467eaeSJoe Perches 				pr_err("ERROR: IPv6 link address not available\n");
22111da177e4SLinus Torvalds 		}
2212222f1806SLuiz Capitulino 	} else {
221368bf9f0bSAmerigo Wang 		if (pkt_dev->min_pkt_size == 0) {
221468bf9f0bSAmerigo Wang 			pkt_dev->min_pkt_size = 14 + sizeof(struct iphdr)
221568bf9f0bSAmerigo Wang 						+ sizeof(struct udphdr)
221668bf9f0bSAmerigo Wang 						+ sizeof(struct pktgen_hdr)
221768bf9f0bSAmerigo Wang 						+ pkt_dev->pkt_overhead;
221868bf9f0bSAmerigo Wang 		}
221968bf9f0bSAmerigo Wang 
22201da177e4SLinus Torvalds 		pkt_dev->saddr_min = 0;
22211da177e4SLinus Torvalds 		pkt_dev->saddr_max = 0;
22221da177e4SLinus Torvalds 		if (strlen(pkt_dev->src_min) == 0) {
22231da177e4SLinus Torvalds 
22241da177e4SLinus Torvalds 			struct in_device *in_dev;
22251da177e4SLinus Torvalds 
22261da177e4SLinus Torvalds 			rcu_read_lock();
2227e5ed6399SHerbert Xu 			in_dev = __in_dev_get_rcu(pkt_dev->odev);
22281da177e4SLinus Torvalds 			if (in_dev) {
22292638eb8bSFlorian Westphal 				const struct in_ifaddr *ifa;
22302638eb8bSFlorian Westphal 
22312638eb8bSFlorian Westphal 				ifa = rcu_dereference(in_dev->ifa_list);
22322638eb8bSFlorian Westphal 				if (ifa) {
22332638eb8bSFlorian Westphal 					pkt_dev->saddr_min = ifa->ifa_address;
22341da177e4SLinus Torvalds 					pkt_dev->saddr_max = pkt_dev->saddr_min;
22351da177e4SLinus Torvalds 				}
22361da177e4SLinus Torvalds 			}
22371da177e4SLinus Torvalds 			rcu_read_unlock();
2238222f1806SLuiz Capitulino 		} else {
22391da177e4SLinus Torvalds 			pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
22401da177e4SLinus Torvalds 			pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
22411da177e4SLinus Torvalds 		}
22421da177e4SLinus Torvalds 
22431da177e4SLinus Torvalds 		pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
22441da177e4SLinus Torvalds 		pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
22451da177e4SLinus Torvalds 	}
22461da177e4SLinus Torvalds 	/* Initialize current values. */
224768bf9f0bSAmerigo Wang 	pkt_dev->cur_pkt_size = pkt_dev->min_pkt_size;
224868bf9f0bSAmerigo Wang 	if (pkt_dev->min_pkt_size > pkt_dev->max_pkt_size)
224968bf9f0bSAmerigo Wang 		pkt_dev->max_pkt_size = pkt_dev->min_pkt_size;
225068bf9f0bSAmerigo Wang 
22511da177e4SLinus Torvalds 	pkt_dev->cur_dst_mac_offset = 0;
22521da177e4SLinus Torvalds 	pkt_dev->cur_src_mac_offset = 0;
22531da177e4SLinus Torvalds 	pkt_dev->cur_saddr = pkt_dev->saddr_min;
22541da177e4SLinus Torvalds 	pkt_dev->cur_daddr = pkt_dev->daddr_min;
22551da177e4SLinus Torvalds 	pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
22561da177e4SLinus Torvalds 	pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
22571da177e4SLinus Torvalds 	pkt_dev->nflows = 0;
22581da177e4SLinus Torvalds }
22591da177e4SLinus Torvalds 
22601da177e4SLinus Torvalds 
spin(struct pktgen_dev * pkt_dev,ktime_t spin_until)2261fd29cf72SStephen Hemminger static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
2262fd29cf72SStephen Hemminger {
2263ef87979cSStephen Hemminger 	ktime_t start_time, end_time;
2264417bc4b8SEric Dumazet 	s64 remaining;
22652bc481cfSStephen Hemminger 	struct hrtimer_sleeper t;
2266fd29cf72SStephen Hemminger 
2267dbc1625fSSebastian Andrzej Siewior 	hrtimer_init_sleeper_on_stack(&t, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
22682bc481cfSStephen Hemminger 	hrtimer_set_expires(&t.timer, spin_until);
2269fd29cf72SStephen Hemminger 
227043d28b65SDaniel Turull 	remaining = ktime_to_ns(hrtimer_expires_remaining(&t.timer));
2271bcf91bdbSGuenter Roeck 	if (remaining <= 0)
2272bcf91bdbSGuenter Roeck 		goto out;
22732bc481cfSStephen Hemminger 
2274398f382cSDaniel Borkmann 	start_time = ktime_get();
227533136d12SEric Dumazet 	if (remaining < 100000) {
227633136d12SEric Dumazet 		/* for small delays (<100us), just loop until limit is reached */
227733136d12SEric Dumazet 		do {
2278398f382cSDaniel Borkmann 			end_time = ktime_get();
2279398f382cSDaniel Borkmann 		} while (ktime_compare(end_time, spin_until) < 0);
228033136d12SEric Dumazet 	} else {
22812bc481cfSStephen Hemminger 		do {
22822bc481cfSStephen Hemminger 			set_current_state(TASK_INTERRUPTIBLE);
22839dd8813eSThomas Gleixner 			hrtimer_sleeper_start_expires(&t, HRTIMER_MODE_ABS);
22842bc481cfSStephen Hemminger 
22852bc481cfSStephen Hemminger 			if (likely(t.task))
22861da177e4SLinus Torvalds 				schedule();
22871da177e4SLinus Torvalds 
22882bc481cfSStephen Hemminger 			hrtimer_cancel(&t.timer);
22892bc481cfSStephen Hemminger 		} while (t.task && pkt_dev->running && !signal_pending(current));
22902bc481cfSStephen Hemminger 		__set_current_state(TASK_RUNNING);
2291398f382cSDaniel Borkmann 		end_time = ktime_get();
229233136d12SEric Dumazet 	}
2293ef87979cSStephen Hemminger 
2294ef87979cSStephen Hemminger 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(end_time, start_time));
2295bcf91bdbSGuenter Roeck out:
229607a0f0f0SDaniel Turull 	pkt_dev->next_tx = ktime_add_ns(spin_until, pkt_dev->delay);
2297bcf91bdbSGuenter Roeck 	destroy_hrtimer_on_stack(&t.timer);
22981da177e4SLinus Torvalds }
22991da177e4SLinus Torvalds 
set_pkt_overhead(struct pktgen_dev * pkt_dev)230016dab72fSJamal Hadi Salim static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
230116dab72fSJamal Hadi Salim {
230263d75463SPaolo Abeni 	pkt_dev->pkt_overhead = 0;
230316dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += pkt_dev->nr_labels*sizeof(u32);
230416dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += VLAN_TAG_SIZE(pkt_dev);
230516dab72fSJamal Hadi Salim 	pkt_dev->pkt_overhead += SVLAN_TAG_SIZE(pkt_dev);
230616dab72fSJamal Hadi Salim }
230716dab72fSJamal Hadi Salim 
f_seen(const struct pktgen_dev * pkt_dev,int flow)2308648fda74SStephen Hemminger static inline int f_seen(const struct pktgen_dev *pkt_dev, int flow)
2309007a531bSJamal Hadi Salim {
2310648fda74SStephen Hemminger 	return !!(pkt_dev->flows[flow].flags & F_INIT);
2311007a531bSJamal Hadi Salim }
2312007a531bSJamal Hadi Salim 
f_pick(struct pktgen_dev * pkt_dev)2313007a531bSJamal Hadi Salim static inline int f_pick(struct pktgen_dev *pkt_dev)
2314007a531bSJamal Hadi Salim {
2315007a531bSJamal Hadi Salim 	int flow = pkt_dev->curfl;
2316007a531bSJamal Hadi Salim 
2317007a531bSJamal Hadi Salim 	if (pkt_dev->flags & F_FLOW_SEQ) {
2318007a531bSJamal Hadi Salim 		if (pkt_dev->flows[flow].count >= pkt_dev->lflow) {
2319007a531bSJamal Hadi Salim 			/* reset time */
2320007a531bSJamal Hadi Salim 			pkt_dev->flows[flow].count = 0;
23211211a645SRobert Olsson 			pkt_dev->flows[flow].flags = 0;
2322007a531bSJamal Hadi Salim 			pkt_dev->curfl += 1;
2323007a531bSJamal Hadi Salim 			if (pkt_dev->curfl >= pkt_dev->cflows)
2324007a531bSJamal Hadi Salim 				pkt_dev->curfl = 0; /*reset */
2325007a531bSJamal Hadi Salim 		}
2326007a531bSJamal Hadi Salim 	} else {
23278032bf12SJason A. Donenfeld 		flow = get_random_u32_below(pkt_dev->cflows);
23281211a645SRobert Olsson 		pkt_dev->curfl = flow;
2329007a531bSJamal Hadi Salim 
23301211a645SRobert Olsson 		if (pkt_dev->flows[flow].count > pkt_dev->lflow) {
2331007a531bSJamal Hadi Salim 			pkt_dev->flows[flow].count = 0;
23321211a645SRobert Olsson 			pkt_dev->flows[flow].flags = 0;
23331211a645SRobert Olsson 		}
2334007a531bSJamal Hadi Salim 	}
2335007a531bSJamal Hadi Salim 
2336007a531bSJamal Hadi Salim 	return pkt_dev->curfl;
2337007a531bSJamal Hadi Salim }
2338007a531bSJamal Hadi Salim 
2339a553e4a6SJamal Hadi Salim 
2340a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2341a553e4a6SJamal Hadi Salim /* If there was already an IPSEC SA, we keep it as is, else
2342a553e4a6SJamal Hadi Salim  * we go look for it ...
2343a553e4a6SJamal Hadi Salim */
2344bd55775cSJamal Hadi Salim #define DUMMY_MARK 0
get_ipsec_sa(struct pktgen_dev * pkt_dev,int flow)2345fea1ab0fSAdrian Bunk static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
2346a553e4a6SJamal Hadi Salim {
2347a553e4a6SJamal Hadi Salim 	struct xfrm_state *x = pkt_dev->flows[flow].x;
23484e58a027SCong Wang 	struct pktgen_net *pn = net_generic(dev_net(pkt_dev->odev), pg_net_id);
2349a553e4a6SJamal Hadi Salim 	if (!x) {
2350c454997eSFan Du 
2351c454997eSFan Du 		if (pkt_dev->spi) {
2352c454997eSFan Du 			/* We need as quick as possible to find the right SA
2353c454997eSFan Du 			 * Searching with minimum criteria to archieve this.
2354c454997eSFan Du 			 */
2355c454997eSFan Du 			x = xfrm_state_lookup_byspi(pn->net, htonl(pkt_dev->spi), AF_INET);
2356c454997eSFan Du 		} else {
2357a553e4a6SJamal Hadi Salim 			/* slow path: we dont already have xfrm_state */
23587e652640SSteffen Klassert 			x = xfrm_stateonly_find(pn->net, DUMMY_MARK, 0,
23595447c5e4SAlexey Dobriyan 						(xfrm_address_t *)&pkt_dev->cur_daddr,
2360a553e4a6SJamal Hadi Salim 						(xfrm_address_t *)&pkt_dev->cur_saddr,
2361a553e4a6SJamal Hadi Salim 						AF_INET,
2362a553e4a6SJamal Hadi Salim 						pkt_dev->ipsmode,
2363a553e4a6SJamal Hadi Salim 						pkt_dev->ipsproto, 0);
2364c454997eSFan Du 		}
2365a553e4a6SJamal Hadi Salim 		if (x) {
2366a553e4a6SJamal Hadi Salim 			pkt_dev->flows[flow].x = x;
2367a553e4a6SJamal Hadi Salim 			set_pkt_overhead(pkt_dev);
2368a553e4a6SJamal Hadi Salim 			pkt_dev->pkt_overhead += x->props.header_len;
2369a553e4a6SJamal Hadi Salim 		}
2370a553e4a6SJamal Hadi Salim 
2371a553e4a6SJamal Hadi Salim 	}
2372a553e4a6SJamal Hadi Salim }
2373a553e4a6SJamal Hadi Salim #endif
set_cur_queue_map(struct pktgen_dev * pkt_dev)2374fd2ea0a7SDavid S. Miller static void set_cur_queue_map(struct pktgen_dev *pkt_dev)
2375fd2ea0a7SDavid S. Miller {
2376e6fce5b9SRobert Olsson 
2377e6fce5b9SRobert Olsson 	if (pkt_dev->flags & F_QUEUE_MAP_CPU)
2378e6fce5b9SRobert Olsson 		pkt_dev->cur_queue_map = smp_processor_id();
2379e6fce5b9SRobert Olsson 
2380896a7cf8SEric Dumazet 	else if (pkt_dev->queue_map_min <= pkt_dev->queue_map_max) {
2381fd2ea0a7SDavid S. Miller 		__u16 t;
2382fd2ea0a7SDavid S. Miller 		if (pkt_dev->flags & F_QUEUE_MAP_RND) {
2383e8a533cbSJason A. Donenfeld 			t = get_random_u32_inclusive(pkt_dev->queue_map_min,
2384e8a533cbSJason A. Donenfeld 						     pkt_dev->queue_map_max);
2385fd2ea0a7SDavid S. Miller 		} else {
2386fd2ea0a7SDavid S. Miller 			t = pkt_dev->cur_queue_map + 1;
2387fd2ea0a7SDavid S. Miller 			if (t > pkt_dev->queue_map_max)
2388fd2ea0a7SDavid S. Miller 				t = pkt_dev->queue_map_min;
2389fd2ea0a7SDavid S. Miller 		}
2390fd2ea0a7SDavid S. Miller 		pkt_dev->cur_queue_map = t;
2391fd2ea0a7SDavid S. Miller 	}
2392bfdbc0acSRobert Olsson 	pkt_dev->cur_queue_map  = pkt_dev->cur_queue_map % pkt_dev->odev->real_num_tx_queues;
2393fd2ea0a7SDavid S. Miller }
2394fd2ea0a7SDavid S. Miller 
23951da177e4SLinus Torvalds /* Increment/randomize headers according to flags and current values
23961da177e4SLinus Torvalds  * for IP src/dest, UDP src/dst port, MAC-Addr src/dst
23971da177e4SLinus Torvalds  */
mod_cur_headers(struct pktgen_dev * pkt_dev)2398222f1806SLuiz Capitulino static void mod_cur_headers(struct pktgen_dev *pkt_dev)
2399222f1806SLuiz Capitulino {
24001da177e4SLinus Torvalds 	__u32 imn;
24011da177e4SLinus Torvalds 	__u32 imx;
24021da177e4SLinus Torvalds 	int flow = 0;
24031da177e4SLinus Torvalds 
2404007a531bSJamal Hadi Salim 	if (pkt_dev->cflows)
2405007a531bSJamal Hadi Salim 		flow = f_pick(pkt_dev);
24061da177e4SLinus Torvalds 
24071da177e4SLinus Torvalds 	/*  Deal with source MAC */
24081da177e4SLinus Torvalds 	if (pkt_dev->src_mac_count > 1) {
24091da177e4SLinus Torvalds 		__u32 mc;
24101da177e4SLinus Torvalds 		__u32 tmp;
24111da177e4SLinus Torvalds 
24121da177e4SLinus Torvalds 		if (pkt_dev->flags & F_MACSRC_RND)
24138032bf12SJason A. Donenfeld 			mc = get_random_u32_below(pkt_dev->src_mac_count);
24141da177e4SLinus Torvalds 		else {
24151da177e4SLinus Torvalds 			mc = pkt_dev->cur_src_mac_offset++;
2416ff2a79a5SRobert Olsson 			if (pkt_dev->cur_src_mac_offset >=
2417222f1806SLuiz Capitulino 			    pkt_dev->src_mac_count)
24181da177e4SLinus Torvalds 				pkt_dev->cur_src_mac_offset = 0;
24191da177e4SLinus Torvalds 		}
24201da177e4SLinus Torvalds 
24211da177e4SLinus Torvalds 		tmp = pkt_dev->src_mac[5] + (mc & 0xFF);
24221da177e4SLinus Torvalds 		pkt_dev->hh[11] = tmp;
24231da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
24241da177e4SLinus Torvalds 		pkt_dev->hh[10] = tmp;
24251da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
24261da177e4SLinus Torvalds 		pkt_dev->hh[9] = tmp;
24271da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
24281da177e4SLinus Torvalds 		pkt_dev->hh[8] = tmp;
24291da177e4SLinus Torvalds 		tmp = (pkt_dev->src_mac[1] + (tmp >> 8));
24301da177e4SLinus Torvalds 		pkt_dev->hh[7] = tmp;
24311da177e4SLinus Torvalds 	}
24321da177e4SLinus Torvalds 
24331da177e4SLinus Torvalds 	/*  Deal with Destination MAC */
24341da177e4SLinus Torvalds 	if (pkt_dev->dst_mac_count > 1) {
24351da177e4SLinus Torvalds 		__u32 mc;
24361da177e4SLinus Torvalds 		__u32 tmp;
24371da177e4SLinus Torvalds 
24381da177e4SLinus Torvalds 		if (pkt_dev->flags & F_MACDST_RND)
24398032bf12SJason A. Donenfeld 			mc = get_random_u32_below(pkt_dev->dst_mac_count);
24401da177e4SLinus Torvalds 
24411da177e4SLinus Torvalds 		else {
24421da177e4SLinus Torvalds 			mc = pkt_dev->cur_dst_mac_offset++;
2443ff2a79a5SRobert Olsson 			if (pkt_dev->cur_dst_mac_offset >=
2444222f1806SLuiz Capitulino 			    pkt_dev->dst_mac_count) {
24451da177e4SLinus Torvalds 				pkt_dev->cur_dst_mac_offset = 0;
24461da177e4SLinus Torvalds 			}
24471da177e4SLinus Torvalds 		}
24481da177e4SLinus Torvalds 
24491da177e4SLinus Torvalds 		tmp = pkt_dev->dst_mac[5] + (mc & 0xFF);
24501da177e4SLinus Torvalds 		pkt_dev->hh[5] = tmp;
24511da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
24521da177e4SLinus Torvalds 		pkt_dev->hh[4] = tmp;
24531da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
24541da177e4SLinus Torvalds 		pkt_dev->hh[3] = tmp;
24551da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
24561da177e4SLinus Torvalds 		pkt_dev->hh[2] = tmp;
24571da177e4SLinus Torvalds 		tmp = (pkt_dev->dst_mac[1] + (tmp >> 8));
24581da177e4SLinus Torvalds 		pkt_dev->hh[1] = tmp;
24591da177e4SLinus Torvalds 	}
24601da177e4SLinus Torvalds 
2461ca6549afSSteven Whitehouse 	if (pkt_dev->flags & F_MPLS_RND) {
246295c96174SEric Dumazet 		unsigned int i;
2463ca6549afSSteven Whitehouse 		for (i = 0; i < pkt_dev->nr_labels; i++)
2464ca6549afSSteven Whitehouse 			if (pkt_dev->labels[i] & MPLS_STACK_BOTTOM)
2465ca6549afSSteven Whitehouse 				pkt_dev->labels[i] = MPLS_STACK_BOTTOM |
2466a251c17aSJason A. Donenfeld 					     ((__force __be32)get_random_u32() &
2467ca6549afSSteven Whitehouse 						      htonl(0x000fffff));
2468ca6549afSSteven Whitehouse 	}
2469ca6549afSSteven Whitehouse 
247034954ddcSFrancesco Fondelli 	if ((pkt_dev->flags & F_VID_RND) && (pkt_dev->vlan_id != 0xffff)) {
24718032bf12SJason A. Donenfeld 		pkt_dev->vlan_id = get_random_u32_below(4096);
247234954ddcSFrancesco Fondelli 	}
247334954ddcSFrancesco Fondelli 
247434954ddcSFrancesco Fondelli 	if ((pkt_dev->flags & F_SVID_RND) && (pkt_dev->svlan_id != 0xffff)) {
24758032bf12SJason A. Donenfeld 		pkt_dev->svlan_id = get_random_u32_below(4096);
247634954ddcSFrancesco Fondelli 	}
247734954ddcSFrancesco Fondelli 
24781da177e4SLinus Torvalds 	if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {
24791da177e4SLinus Torvalds 		if (pkt_dev->flags & F_UDPSRC_RND)
2480e8a533cbSJason A. Donenfeld 			pkt_dev->cur_udp_src = get_random_u32_inclusive(pkt_dev->udp_src_min,
2481e8a533cbSJason A. Donenfeld 									pkt_dev->udp_src_max - 1);
24821da177e4SLinus Torvalds 
24831da177e4SLinus Torvalds 		else {
24841da177e4SLinus Torvalds 			pkt_dev->cur_udp_src++;
24851da177e4SLinus Torvalds 			if (pkt_dev->cur_udp_src >= pkt_dev->udp_src_max)
24861da177e4SLinus Torvalds 				pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
24871da177e4SLinus Torvalds 		}
24881da177e4SLinus Torvalds 	}
24891da177e4SLinus Torvalds 
24901da177e4SLinus Torvalds 	if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) {
24911da177e4SLinus Torvalds 		if (pkt_dev->flags & F_UDPDST_RND) {
2492e8a533cbSJason A. Donenfeld 			pkt_dev->cur_udp_dst = get_random_u32_inclusive(pkt_dev->udp_dst_min,
2493e8a533cbSJason A. Donenfeld 									pkt_dev->udp_dst_max - 1);
2494222f1806SLuiz Capitulino 		} else {
24951da177e4SLinus Torvalds 			pkt_dev->cur_udp_dst++;
24961da177e4SLinus Torvalds 			if (pkt_dev->cur_udp_dst >= pkt_dev->udp_dst_max)
24971da177e4SLinus Torvalds 				pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
24981da177e4SLinus Torvalds 		}
24991da177e4SLinus Torvalds 	}
25001da177e4SLinus Torvalds 
25011da177e4SLinus Torvalds 	if (!(pkt_dev->flags & F_IPV6)) {
25021da177e4SLinus Torvalds 
250363adc6fbSStephen Hemminger 		imn = ntohl(pkt_dev->saddr_min);
250463adc6fbSStephen Hemminger 		imx = ntohl(pkt_dev->saddr_max);
250563adc6fbSStephen Hemminger 		if (imn < imx) {
25061da177e4SLinus Torvalds 			__u32 t;
25071da177e4SLinus Torvalds 			if (pkt_dev->flags & F_IPSRC_RND)
2508e8a533cbSJason A. Donenfeld 				t = get_random_u32_inclusive(imn, imx - 1);
25091da177e4SLinus Torvalds 			else {
25101da177e4SLinus Torvalds 				t = ntohl(pkt_dev->cur_saddr);
25111da177e4SLinus Torvalds 				t++;
251263adc6fbSStephen Hemminger 				if (t > imx)
25131da177e4SLinus Torvalds 					t = imn;
251463adc6fbSStephen Hemminger 
25151da177e4SLinus Torvalds 			}
25161da177e4SLinus Torvalds 			pkt_dev->cur_saddr = htonl(t);
25171da177e4SLinus Torvalds 		}
25181da177e4SLinus Torvalds 
2519007a531bSJamal Hadi Salim 		if (pkt_dev->cflows && f_seen(pkt_dev, flow)) {
25201da177e4SLinus Torvalds 			pkt_dev->cur_daddr = pkt_dev->flows[flow].cur_daddr;
25211da177e4SLinus Torvalds 		} else {
2522252e3346SAl Viro 			imn = ntohl(pkt_dev->daddr_min);
2523252e3346SAl Viro 			imx = ntohl(pkt_dev->daddr_max);
2524252e3346SAl Viro 			if (imn < imx) {
25251da177e4SLinus Torvalds 				__u32 t;
2526252e3346SAl Viro 				__be32 s;
25271da177e4SLinus Torvalds 				if (pkt_dev->flags & F_IPDST_RND) {
25281da177e4SLinus Torvalds 
252970e3ba72SAkinobu Mita 					do {
2530e8a533cbSJason A. Donenfeld 						t = get_random_u32_inclusive(imn, imx - 1);
2531252e3346SAl Viro 						s = htonl(t);
253270e3ba72SAkinobu Mita 					} while (ipv4_is_loopback(s) ||
253370e3ba72SAkinobu Mita 						ipv4_is_multicast(s) ||
253470e3ba72SAkinobu Mita 						ipv4_is_lbcast(s) ||
253570e3ba72SAkinobu Mita 						ipv4_is_zeronet(s) ||
253670e3ba72SAkinobu Mita 						ipv4_is_local_multicast(s));
2537252e3346SAl Viro 					pkt_dev->cur_daddr = s;
2538252e3346SAl Viro 				} else {
25391da177e4SLinus Torvalds 					t = ntohl(pkt_dev->cur_daddr);
25401da177e4SLinus Torvalds 					t++;
25411da177e4SLinus Torvalds 					if (t > imx) {
25421da177e4SLinus Torvalds 						t = imn;
25431da177e4SLinus Torvalds 					}
25441da177e4SLinus Torvalds 					pkt_dev->cur_daddr = htonl(t);
25451da177e4SLinus Torvalds 				}
25461da177e4SLinus Torvalds 			}
25471da177e4SLinus Torvalds 			if (pkt_dev->cflows) {
2548007a531bSJamal Hadi Salim 				pkt_dev->flows[flow].flags |= F_INIT;
2549222f1806SLuiz Capitulino 				pkt_dev->flows[flow].cur_daddr =
2550222f1806SLuiz Capitulino 				    pkt_dev->cur_daddr;
2551a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
25526f107c74SDmitry Safonov 				if (pkt_dev->flags & F_IPSEC)
2553a553e4a6SJamal Hadi Salim 					get_ipsec_sa(pkt_dev, flow);
2554a553e4a6SJamal Hadi Salim #endif
25551da177e4SLinus Torvalds 				pkt_dev->nflows++;
25561da177e4SLinus Torvalds 			}
25571da177e4SLinus Torvalds 		}
2558222f1806SLuiz Capitulino 	} else {		/* IPV6 * */
2559222f1806SLuiz Capitulino 
256006e30411SJoe Perches 		if (!ipv6_addr_any(&pkt_dev->min_in6_daddr)) {
25611da177e4SLinus Torvalds 			int i;
25621da177e4SLinus Torvalds 
25631da177e4SLinus Torvalds 			/* Only random destinations yet */
25641da177e4SLinus Torvalds 
25651da177e4SLinus Torvalds 			for (i = 0; i < 4; i++) {
25661da177e4SLinus Torvalds 				pkt_dev->cur_in6_daddr.s6_addr32[i] =
2567a251c17aSJason A. Donenfeld 				    (((__force __be32)get_random_u32() |
25681da177e4SLinus Torvalds 				      pkt_dev->min_in6_daddr.s6_addr32[i]) &
25691da177e4SLinus Torvalds 				     pkt_dev->max_in6_daddr.s6_addr32[i]);
25701da177e4SLinus Torvalds 			}
25711da177e4SLinus Torvalds 		}
25721da177e4SLinus Torvalds 	}
25731da177e4SLinus Torvalds 
25741da177e4SLinus Torvalds 	if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) {
25751da177e4SLinus Torvalds 		__u32 t;
25761da177e4SLinus Torvalds 		if (pkt_dev->flags & F_TXSIZE_RND) {
2577e8a533cbSJason A. Donenfeld 			t = get_random_u32_inclusive(pkt_dev->min_pkt_size,
2578e8a533cbSJason A. Donenfeld 						     pkt_dev->max_pkt_size - 1);
2579222f1806SLuiz Capitulino 		} else {
25801da177e4SLinus Torvalds 			t = pkt_dev->cur_pkt_size + 1;
25811da177e4SLinus Torvalds 			if (t > pkt_dev->max_pkt_size)
25821da177e4SLinus Torvalds 				t = pkt_dev->min_pkt_size;
25831da177e4SLinus Torvalds 		}
25841da177e4SLinus Torvalds 		pkt_dev->cur_pkt_size = t;
258590149031SNick Richardson 	} else if (pkt_dev->n_imix_entries > 0) {
258690149031SNick Richardson 		struct imix_pkt *entry;
25878032bf12SJason A. Donenfeld 		__u32 t = get_random_u32_below(IMIX_PRECISION);
258890149031SNick Richardson 		__u8 entry_index = pkt_dev->imix_distribution[t];
258990149031SNick Richardson 
259090149031SNick Richardson 		entry = &pkt_dev->imix_entries[entry_index];
259190149031SNick Richardson 		entry->count_so_far++;
259290149031SNick Richardson 		pkt_dev->cur_pkt_size = entry->size;
25931da177e4SLinus Torvalds 	}
25941da177e4SLinus Torvalds 
2595fd2ea0a7SDavid S. Miller 	set_cur_queue_map(pkt_dev);
259645b270f8SRobert Olsson 
25971da177e4SLinus Torvalds 	pkt_dev->flows[flow].count++;
25981da177e4SLinus Torvalds }
25991da177e4SLinus Torvalds 
fill_imix_distribution(struct pktgen_dev * pkt_dev)26007e5a3ef6SNick Richardson static void fill_imix_distribution(struct pktgen_dev *pkt_dev)
26017e5a3ef6SNick Richardson {
26027e5a3ef6SNick Richardson 	int cumulative_probabilites[MAX_IMIX_ENTRIES];
26037e5a3ef6SNick Richardson 	int j = 0;
26047e5a3ef6SNick Richardson 	__u64 cumulative_prob = 0;
26057e5a3ef6SNick Richardson 	__u64 total_weight = 0;
26067e5a3ef6SNick Richardson 	int i = 0;
26077e5a3ef6SNick Richardson 
26087e5a3ef6SNick Richardson 	for (i = 0; i < pkt_dev->n_imix_entries; i++)
26097e5a3ef6SNick Richardson 		total_weight += pkt_dev->imix_entries[i].weight;
26107e5a3ef6SNick Richardson 
26117e5a3ef6SNick Richardson 	/* Fill cumulative_probabilites with sum of normalized probabilities */
26127e5a3ef6SNick Richardson 	for (i = 0; i < pkt_dev->n_imix_entries - 1; i++) {
26137e5a3ef6SNick Richardson 		cumulative_prob += div64_u64(pkt_dev->imix_entries[i].weight *
26147e5a3ef6SNick Richardson 						     IMIX_PRECISION,
26157e5a3ef6SNick Richardson 					     total_weight);
26167e5a3ef6SNick Richardson 		cumulative_probabilites[i] = cumulative_prob;
26177e5a3ef6SNick Richardson 	}
26187e5a3ef6SNick Richardson 	cumulative_probabilites[pkt_dev->n_imix_entries - 1] = 100;
26197e5a3ef6SNick Richardson 
26207e5a3ef6SNick Richardson 	for (i = 0; i < IMIX_PRECISION; i++) {
26217e5a3ef6SNick Richardson 		if (i == cumulative_probabilites[j])
26227e5a3ef6SNick Richardson 			j++;
26237e5a3ef6SNick Richardson 		pkt_dev->imix_distribution[i] = j;
26247e5a3ef6SNick Richardson 	}
26257e5a3ef6SNick Richardson }
2626a553e4a6SJamal Hadi Salim 
2627a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
26285537a055SFengguang Wu static u32 pktgen_dst_metrics[RTAX_MAX + 1] = {
2629cf93d47eSFan Du 
2630cf93d47eSFan Du 	[RTAX_HOPLIMIT] = 0x5, /* Set a static hoplimit */
2631cf93d47eSFan Du };
2632cf93d47eSFan Du 
pktgen_output_ipsec(struct sk_buff * skb,struct pktgen_dev * pkt_dev)2633a553e4a6SJamal Hadi Salim static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)
2634a553e4a6SJamal Hadi Salim {
2635a553e4a6SJamal Hadi Salim 	struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
2636a553e4a6SJamal Hadi Salim 	int err = 0;
26376de9ace4SFan Du 	struct net *net = dev_net(pkt_dev->odev);
2638a553e4a6SJamal Hadi Salim 
2639a553e4a6SJamal Hadi Salim 	if (!x)
2640a553e4a6SJamal Hadi Salim 		return 0;
2641a553e4a6SJamal Hadi Salim 	/* XXX: we dont support tunnel mode for now until
2642a553e4a6SJamal Hadi Salim 	 * we resolve the dst issue */
2643cf93d47eSFan Du 	if ((x->props.mode != XFRM_MODE_TRANSPORT) && (pkt_dev->spi == 0))
2644a553e4a6SJamal Hadi Salim 		return 0;
2645a553e4a6SJamal Hadi Salim 
2646cf93d47eSFan Du 	/* But when user specify an valid SPI, transformation
2647cf93d47eSFan Du 	 * supports both transport/tunnel mode + ESP/AH type.
2648cf93d47eSFan Du 	 */
2649cf93d47eSFan Du 	if ((x->props.mode == XFRM_MODE_TUNNEL) && (pkt_dev->spi != 0))
2650b6ca8bd5SDavid Miller 		skb->_skb_refdst = (unsigned long)&pkt_dev->xdst.u.dst | SKB_DST_NOREF;
2651cf93d47eSFan Du 
2652cf93d47eSFan Du 	rcu_read_lock_bh();
26530c620e97SFlorian Westphal 	err = pktgen_xfrm_outer_mode_output(x, skb);
2654cf93d47eSFan Du 	rcu_read_unlock_bh();
26556de9ace4SFan Du 	if (err) {
26566de9ace4SFan Du 		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR);
2657a553e4a6SJamal Hadi Salim 		goto error;
26586de9ace4SFan Du 	}
2659a553e4a6SJamal Hadi Salim 	err = x->type->output(x, skb);
26606de9ace4SFan Du 	if (err) {
26616de9ace4SFan Du 		XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEPROTOERROR);
2662a553e4a6SJamal Hadi Salim 		goto error;
26636de9ace4SFan Du 	}
26640af0a413SFan Du 	spin_lock_bh(&x->lock);
2665a553e4a6SJamal Hadi Salim 	x->curlft.bytes += skb->len;
2666a553e4a6SJamal Hadi Salim 	x->curlft.packets++;
26670af0a413SFan Du 	spin_unlock_bh(&x->lock);
2668a553e4a6SJamal Hadi Salim error:
2669a553e4a6SJamal Hadi Salim 	return err;
2670a553e4a6SJamal Hadi Salim }
2671a553e4a6SJamal Hadi Salim 
free_SAs(struct pktgen_dev * pkt_dev)2672475ac1e4SStephen Hemminger static void free_SAs(struct pktgen_dev *pkt_dev)
2673a553e4a6SJamal Hadi Salim {
2674a553e4a6SJamal Hadi Salim 	if (pkt_dev->cflows) {
2675a553e4a6SJamal Hadi Salim 		/* let go of the SAs if we have them */
2676d6182223SPaul Gortmaker 		int i;
2677d6182223SPaul Gortmaker 		for (i = 0; i < pkt_dev->cflows; i++) {
2678a553e4a6SJamal Hadi Salim 			struct xfrm_state *x = pkt_dev->flows[i].x;
2679a553e4a6SJamal Hadi Salim 			if (x) {
2680a553e4a6SJamal Hadi Salim 				xfrm_state_put(x);
2681a553e4a6SJamal Hadi Salim 				pkt_dev->flows[i].x = NULL;
2682a553e4a6SJamal Hadi Salim 			}
2683a553e4a6SJamal Hadi Salim 		}
2684a553e4a6SJamal Hadi Salim 	}
2685a553e4a6SJamal Hadi Salim }
2686a553e4a6SJamal Hadi Salim 
process_ipsec(struct pktgen_dev * pkt_dev,struct sk_buff * skb,__be16 protocol)2687475ac1e4SStephen Hemminger static int process_ipsec(struct pktgen_dev *pkt_dev,
2688a553e4a6SJamal Hadi Salim 			      struct sk_buff *skb, __be16 protocol)
2689a553e4a6SJamal Hadi Salim {
26906f107c74SDmitry Safonov 	if (pkt_dev->flags & F_IPSEC) {
2691a553e4a6SJamal Hadi Salim 		struct xfrm_state *x = pkt_dev->flows[pkt_dev->curfl].x;
2692a553e4a6SJamal Hadi Salim 		int nhead = 0;
2693a553e4a6SJamal Hadi Salim 		if (x) {
2694d4969581SEric Dumazet 			struct ethhdr *eth;
26953868204dSfan.du 			struct iphdr *iph;
2696d4969581SEric Dumazet 			int ret;
26973868204dSfan.du 
2698a553e4a6SJamal Hadi Salim 			nhead = x->props.header_len - skb_headroom(skb);
2699a553e4a6SJamal Hadi Salim 			if (nhead > 0) {
2700a553e4a6SJamal Hadi Salim 				ret = pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
2701a553e4a6SJamal Hadi Salim 				if (ret < 0) {
2702f9467eaeSJoe Perches 					pr_err("Error expanding ipsec packet %d\n",
2703f9467eaeSJoe Perches 					       ret);
2704b4bb4ac8SIlpo Järvinen 					goto err;
2705a553e4a6SJamal Hadi Salim 				}
2706a553e4a6SJamal Hadi Salim 			}
2707a553e4a6SJamal Hadi Salim 
2708a553e4a6SJamal Hadi Salim 			/* ipsec is not expecting ll header */
2709a553e4a6SJamal Hadi Salim 			skb_pull(skb, ETH_HLEN);
2710a553e4a6SJamal Hadi Salim 			ret = pktgen_output_ipsec(skb, pkt_dev);
2711a553e4a6SJamal Hadi Salim 			if (ret) {
2712f9467eaeSJoe Perches 				pr_err("Error creating ipsec packet %d\n", ret);
2713b4bb4ac8SIlpo Järvinen 				goto err;
2714a553e4a6SJamal Hadi Salim 			}
2715a553e4a6SJamal Hadi Salim 			/* restore ll */
2716d58ff351SJohannes Berg 			eth = skb_push(skb, ETH_HLEN);
2717d4969581SEric Dumazet 			memcpy(eth, pkt_dev->hh, 2 * ETH_ALEN);
2718d4969581SEric Dumazet 			eth->h_proto = protocol;
27193868204dSfan.du 
27203868204dSfan.du 			/* Update IPv4 header len as well as checksum value */
27213868204dSfan.du 			iph = ip_hdr(skb);
27223868204dSfan.du 			iph->tot_len = htons(skb->len - ETH_HLEN);
27233868204dSfan.du 			ip_send_check(iph);
2724a553e4a6SJamal Hadi Salim 		}
2725a553e4a6SJamal Hadi Salim 	}
2726a553e4a6SJamal Hadi Salim 	return 1;
2727b4bb4ac8SIlpo Järvinen err:
2728b4bb4ac8SIlpo Järvinen 	kfree_skb(skb);
2729b4bb4ac8SIlpo Järvinen 	return 0;
2730a553e4a6SJamal Hadi Salim }
2731a553e4a6SJamal Hadi Salim #endif
2732a553e4a6SJamal Hadi Salim 
mpls_push(__be32 * mpls,struct pktgen_dev * pkt_dev)2733ca6549afSSteven Whitehouse static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)
2734ca6549afSSteven Whitehouse {
273595c96174SEric Dumazet 	unsigned int i;
273663adc6fbSStephen Hemminger 	for (i = 0; i < pkt_dev->nr_labels; i++)
2737ca6549afSSteven Whitehouse 		*mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM;
273863adc6fbSStephen Hemminger 
2739ca6549afSSteven Whitehouse 	mpls--;
2740ca6549afSSteven Whitehouse 	*mpls |= MPLS_STACK_BOTTOM;
2741ca6549afSSteven Whitehouse }
2742ca6549afSSteven Whitehouse 
build_tci(unsigned int id,unsigned int cfi,unsigned int prio)27430f37c605SAl Viro static inline __be16 build_tci(unsigned int id, unsigned int cfi,
27440f37c605SAl Viro 			       unsigned int prio)
27450f37c605SAl Viro {
27460f37c605SAl Viro 	return htons(id | (cfi << 12) | (prio << 13));
27470f37c605SAl Viro }
27480f37c605SAl Viro 
pktgen_finalize_skb(struct pktgen_dev * pkt_dev,struct sk_buff * skb,int datalen)274926ad7879SEric Dumazet static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
275026ad7879SEric Dumazet 				int datalen)
275126ad7879SEric Dumazet {
27527f5d3f27SArnd Bergmann 	struct timespec64 timestamp;
275326ad7879SEric Dumazet 	struct pktgen_hdr *pgh;
275426ad7879SEric Dumazet 
27554df864c1SJohannes Berg 	pgh = skb_put(skb, sizeof(*pgh));
275626ad7879SEric Dumazet 	datalen -= sizeof(*pgh);
275726ad7879SEric Dumazet 
275826ad7879SEric Dumazet 	if (pkt_dev->nfrags <= 0) {
2759b080db58SJohannes Berg 		skb_put_zero(skb, datalen);
276026ad7879SEric Dumazet 	} else {
276126ad7879SEric Dumazet 		int frags = pkt_dev->nfrags;
276226ad7879SEric Dumazet 		int i, len;
27637d36a991Samit salecha 		int frag_len;
276426ad7879SEric Dumazet 
276526ad7879SEric Dumazet 
276626ad7879SEric Dumazet 		if (frags > MAX_SKB_FRAGS)
276726ad7879SEric Dumazet 			frags = MAX_SKB_FRAGS;
276826ad7879SEric Dumazet 		len = datalen - frags * PAGE_SIZE;
276926ad7879SEric Dumazet 		if (len > 0) {
2770b080db58SJohannes Berg 			skb_put_zero(skb, len);
277126ad7879SEric Dumazet 			datalen = frags * PAGE_SIZE;
277226ad7879SEric Dumazet 		}
277326ad7879SEric Dumazet 
277426ad7879SEric Dumazet 		i = 0;
2775dc91e3beSDavid S. Miller 		frag_len = (datalen/frags) < PAGE_SIZE ?
2776dc91e3beSDavid S. Miller 			   (datalen/frags) : PAGE_SIZE;
277726ad7879SEric Dumazet 		while (datalen > 0) {
277826ad7879SEric Dumazet 			if (unlikely(!pkt_dev->page)) {
277926ad7879SEric Dumazet 				int node = numa_node_id();
278026ad7879SEric Dumazet 
278126ad7879SEric Dumazet 				if (pkt_dev->node >= 0 && (pkt_dev->flags & F_NODE))
278226ad7879SEric Dumazet 					node = pkt_dev->node;
278326ad7879SEric Dumazet 				pkt_dev->page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
278426ad7879SEric Dumazet 				if (!pkt_dev->page)
278526ad7879SEric Dumazet 					break;
278626ad7879SEric Dumazet 			}
2787a0bec1cdSIan Campbell 			get_page(pkt_dev->page);
2788b51f4113SYunsheng Lin 
27897d36a991Samit salecha 			/*last fragment, fill rest of data*/
27907d36a991Samit salecha 			if (i == (frags - 1))
2791b51f4113SYunsheng Lin 				skb_frag_fill_page_desc(&skb_shinfo(skb)->frags[i],
2792b51f4113SYunsheng Lin 							pkt_dev->page, 0,
2793b51f4113SYunsheng Lin 							(datalen < PAGE_SIZE ?
2794b51f4113SYunsheng Lin 							 datalen : PAGE_SIZE));
27957d36a991Samit salecha 			else
2796b51f4113SYunsheng Lin 				skb_frag_fill_page_desc(&skb_shinfo(skb)->frags[i],
2797b51f4113SYunsheng Lin 							pkt_dev->page, 0, frag_len);
2798b51f4113SYunsheng Lin 
27999e903e08SEric Dumazet 			datalen -= skb_frag_size(&skb_shinfo(skb)->frags[i]);
28009e903e08SEric Dumazet 			skb->len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
28019e903e08SEric Dumazet 			skb->data_len += skb_frag_size(&skb_shinfo(skb)->frags[i]);
280226ad7879SEric Dumazet 			i++;
280326ad7879SEric Dumazet 			skb_shinfo(skb)->nr_frags = i;
280426ad7879SEric Dumazet 		}
280526ad7879SEric Dumazet 	}
280626ad7879SEric Dumazet 
280726ad7879SEric Dumazet 	/* Stamp the time, and sequence number,
280826ad7879SEric Dumazet 	 * convert them to network byte order
280926ad7879SEric Dumazet 	 */
281026ad7879SEric Dumazet 	pgh->pgh_magic = htonl(PKTGEN_MAGIC);
281126ad7879SEric Dumazet 	pgh->seq_num = htonl(pkt_dev->seq_num);
281226ad7879SEric Dumazet 
2813afb84b62SJesper Dangaard Brouer 	if (pkt_dev->flags & F_NO_TIMESTAMP) {
2814afb84b62SJesper Dangaard Brouer 		pgh->tv_sec = 0;
2815afb84b62SJesper Dangaard Brouer 		pgh->tv_usec = 0;
2816afb84b62SJesper Dangaard Brouer 	} else {
28177f5d3f27SArnd Bergmann 		/*
28187f5d3f27SArnd Bergmann 		 * pgh->tv_sec wraps in y2106 when interpreted as unsigned
28197f5d3f27SArnd Bergmann 		 * as done by wireshark, or y2038 when interpreted as signed.
28207f5d3f27SArnd Bergmann 		 * This is probably harmless, but if anyone wants to improve
28217f5d3f27SArnd Bergmann 		 * it, we could introduce a variant that puts 64-bit nanoseconds
28227f5d3f27SArnd Bergmann 		 * into the respective header bytes.
28237f5d3f27SArnd Bergmann 		 * This would also be slightly faster to read.
28247f5d3f27SArnd Bergmann 		 */
28257f5d3f27SArnd Bergmann 		ktime_get_real_ts64(&timestamp);
282626ad7879SEric Dumazet 		pgh->tv_sec = htonl(timestamp.tv_sec);
28277f5d3f27SArnd Bergmann 		pgh->tv_usec = htonl(timestamp.tv_nsec / NSEC_PER_USEC);
282826ad7879SEric Dumazet 	}
2829afb84b62SJesper Dangaard Brouer }
283026ad7879SEric Dumazet 
pktgen_alloc_skb(struct net_device * dev,struct pktgen_dev * pkt_dev)28317a6e288dSDaniel Borkmann static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
283263d75463SPaolo Abeni 					struct pktgen_dev *pkt_dev)
28337a6e288dSDaniel Borkmann {
283463d75463SPaolo Abeni 	unsigned int extralen = LL_RESERVED_SPACE(dev);
28357a6e288dSDaniel Borkmann 	struct sk_buff *skb = NULL;
283663d75463SPaolo Abeni 	unsigned int size;
28377a6e288dSDaniel Borkmann 
283863d75463SPaolo Abeni 	size = pkt_dev->cur_pkt_size + 64 + extralen + pkt_dev->pkt_overhead;
28397a6e288dSDaniel Borkmann 	if (pkt_dev->flags & F_NODE) {
28407a6e288dSDaniel Borkmann 		int node = pkt_dev->node >= 0 ? pkt_dev->node : numa_node_id();
28417a6e288dSDaniel Borkmann 
28427a6e288dSDaniel Borkmann 		skb = __alloc_skb(NET_SKB_PAD + size, GFP_NOWAIT, 0, node);
28437a6e288dSDaniel Borkmann 		if (likely(skb)) {
28447a6e288dSDaniel Borkmann 			skb_reserve(skb, NET_SKB_PAD);
28457a6e288dSDaniel Borkmann 			skb->dev = dev;
28467a6e288dSDaniel Borkmann 		}
28477a6e288dSDaniel Borkmann 	} else {
28487a6e288dSDaniel Borkmann 		 skb = __netdev_alloc_skb(dev, size, GFP_NOWAIT);
28497a6e288dSDaniel Borkmann 	}
28503de03596SJohn Fastabend 
285163d75463SPaolo Abeni 	/* the caller pre-fetches from skb->data and reserves for the mac hdr */
28523de03596SJohn Fastabend 	if (likely(skb))
285363d75463SPaolo Abeni 		skb_reserve(skb, extralen - 16);
28547a6e288dSDaniel Borkmann 
28557a6e288dSDaniel Borkmann 	return skb;
28567a6e288dSDaniel Borkmann }
28577a6e288dSDaniel Borkmann 
fill_packet_ipv4(struct net_device * odev,struct pktgen_dev * pkt_dev)28581da177e4SLinus Torvalds static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
28591da177e4SLinus Torvalds 					struct pktgen_dev *pkt_dev)
28601da177e4SLinus Torvalds {
28611da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
28621da177e4SLinus Torvalds 	__u8 *eth;
28631da177e4SLinus Torvalds 	struct udphdr *udph;
28641da177e4SLinus Torvalds 	int datalen, iplen;
28651da177e4SLinus Torvalds 	struct iphdr *iph;
2866d5f1ce9aSStephen Hemminger 	__be16 protocol = htons(ETH_P_IP);
2867ca6549afSSteven Whitehouse 	__be32 *mpls;
286834954ddcSFrancesco Fondelli 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
286934954ddcSFrancesco Fondelli 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
287034954ddcSFrancesco Fondelli 	__be16 *svlan_tci = NULL;                /* Encapsulates priority and SVLAN ID */
287134954ddcSFrancesco Fondelli 	__be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
2872fd2ea0a7SDavid S. Miller 	u16 queue_map;
2873ca6549afSSteven Whitehouse 
2874ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2875d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_MPLS_UC);
28761da177e4SLinus Torvalds 
287734954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff)
2878d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_8021Q);
287934954ddcSFrancesco Fondelli 
288064053beeSRobert Olsson 	/* Update any of the values, used when we're incrementing various
288164053beeSRobert Olsson 	 * fields.
288264053beeSRobert Olsson 	 */
288364053beeSRobert Olsson 	mod_cur_headers(pkt_dev);
2884eb589063SJunchang Wang 	queue_map = pkt_dev->cur_queue_map;
288564053beeSRobert Olsson 
288663d75463SPaolo Abeni 	skb = pktgen_alloc_skb(odev, pkt_dev);
28871da177e4SLinus Torvalds 	if (!skb) {
28881da177e4SLinus Torvalds 		sprintf(pkt_dev->result, "No memory");
28891da177e4SLinus Torvalds 		return NULL;
28901da177e4SLinus Torvalds 	}
28911da177e4SLinus Torvalds 
28927a6e288dSDaniel Borkmann 	prefetchw(skb->data);
289363d75463SPaolo Abeni 	skb_reserve(skb, 16);
28941da177e4SLinus Torvalds 
28951da177e4SLinus Torvalds 	/*  Reserve for ethernet and IP header  */
2896d58ff351SJohannes Berg 	eth = skb_push(skb, 14);
28974df864c1SJohannes Berg 	mpls = skb_put(skb, pkt_dev->nr_labels * sizeof(__u32));
2898ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
2899ca6549afSSteven Whitehouse 		mpls_push(mpls, pkt_dev);
290034954ddcSFrancesco Fondelli 
290134954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff) {
290234954ddcSFrancesco Fondelli 		if (pkt_dev->svlan_id != 0xffff) {
29034df864c1SJohannes Berg 			svlan_tci = skb_put(skb, sizeof(__be16));
29040f37c605SAl Viro 			*svlan_tci = build_tci(pkt_dev->svlan_id,
29050f37c605SAl Viro 					       pkt_dev->svlan_cfi,
29060f37c605SAl Viro 					       pkt_dev->svlan_p);
29074df864c1SJohannes Berg 			svlan_encapsulated_proto = skb_put(skb,
29084df864c1SJohannes Berg 							   sizeof(__be16));
2909d5f1ce9aSStephen Hemminger 			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
291034954ddcSFrancesco Fondelli 		}
29114df864c1SJohannes Berg 		vlan_tci = skb_put(skb, sizeof(__be16));
29120f37c605SAl Viro 		*vlan_tci = build_tci(pkt_dev->vlan_id,
29130f37c605SAl Viro 				      pkt_dev->vlan_cfi,
29140f37c605SAl Viro 				      pkt_dev->vlan_p);
29154df864c1SJohannes Berg 		vlan_encapsulated_proto = skb_put(skb, sizeof(__be16));
2916d5f1ce9aSStephen Hemminger 		*vlan_encapsulated_proto = htons(ETH_P_IP);
291734954ddcSFrancesco Fondelli 	}
291834954ddcSFrancesco Fondelli 
2919c145aeb3SZhang Shengju 	skb_reset_mac_header(skb);
2920525cebedSThomas Graf 	skb_set_network_header(skb, skb->len);
29214df864c1SJohannes Berg 	iph = skb_put(skb, sizeof(struct iphdr));
2922525cebedSThomas Graf 
2923525cebedSThomas Graf 	skb_set_transport_header(skb, skb->len);
29244df864c1SJohannes Berg 	udph = skb_put(skb, sizeof(struct udphdr));
2925fd2ea0a7SDavid S. Miller 	skb_set_queue_mapping(skb, queue_map);
29269e50e3acSJohn Fastabend 	skb->priority = pkt_dev->skb_priority;
29279e50e3acSJohn Fastabend 
29281da177e4SLinus Torvalds 	memcpy(eth, pkt_dev->hh, 12);
2929252e3346SAl Viro 	*(__be16 *) & eth[12] = protocol;
29301da177e4SLinus Torvalds 
2931ca6549afSSteven Whitehouse 	/* Eth + IPh + UDPh + mpls */
2932ca6549afSSteven Whitehouse 	datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
293316dab72fSJamal Hadi Salim 		  pkt_dev->pkt_overhead;
29346af773e7SNishank Trivedi 	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr))
29351da177e4SLinus Torvalds 		datalen = sizeof(struct pktgen_hdr);
29361da177e4SLinus Torvalds 
29371da177e4SLinus Torvalds 	udph->source = htons(pkt_dev->cur_udp_src);
29381da177e4SLinus Torvalds 	udph->dest = htons(pkt_dev->cur_udp_dst);
29391da177e4SLinus Torvalds 	udph->len = htons(datalen + 8);	/* DATA + udphdr */
2940c26bf4a5SThomas Graf 	udph->check = 0;
29411da177e4SLinus Torvalds 
29421da177e4SLinus Torvalds 	iph->ihl = 5;
29431da177e4SLinus Torvalds 	iph->version = 4;
29441da177e4SLinus Torvalds 	iph->ttl = 32;
29451ca7768cSFrancesco Fondelli 	iph->tos = pkt_dev->tos;
29461da177e4SLinus Torvalds 	iph->protocol = IPPROTO_UDP;	/* UDP */
29471da177e4SLinus Torvalds 	iph->saddr = pkt_dev->cur_saddr;
29481da177e4SLinus Torvalds 	iph->daddr = pkt_dev->cur_daddr;
294966ed1e5eSEric Dumazet 	iph->id = htons(pkt_dev->ip_id);
295066ed1e5eSEric Dumazet 	pkt_dev->ip_id++;
29511da177e4SLinus Torvalds 	iph->frag_off = 0;
29521da177e4SLinus Torvalds 	iplen = 20 + 8 + datalen;
29531da177e4SLinus Torvalds 	iph->tot_len = htons(iplen);
295403c633e7SThomas Graf 	ip_send_check(iph);
2955ca6549afSSteven Whitehouse 	skb->protocol = protocol;
29561da177e4SLinus Torvalds 	skb->dev = odev;
29571da177e4SLinus Torvalds 	skb->pkt_type = PACKET_HOST;
2958c26bf4a5SThomas Graf 
29597744b5f3SSabrina Dubroca 	pktgen_finalize_skb(pkt_dev, skb, datalen);
29607744b5f3SSabrina Dubroca 
2961c26bf4a5SThomas Graf 	if (!(pkt_dev->flags & F_UDPCSUM)) {
2962c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_NONE;
2963c8cd0989STom Herbert 	} else if (odev->features & (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM)) {
2964c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_PARTIAL;
2965c26bf4a5SThomas Graf 		skb->csum = 0;
29667744b5f3SSabrina Dubroca 		udp4_hwcsum(skb, iph->saddr, iph->daddr);
2967c26bf4a5SThomas Graf 	} else {
29687744b5f3SSabrina Dubroca 		__wsum csum = skb_checksum(skb, skb_transport_offset(skb), datalen + 8, 0);
2969c26bf4a5SThomas Graf 
2970c26bf4a5SThomas Graf 		/* add protocol-dependent pseudo-header */
29717744b5f3SSabrina Dubroca 		udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
2972c26bf4a5SThomas Graf 						datalen + 8, IPPROTO_UDP, csum);
2973c26bf4a5SThomas Graf 
2974c26bf4a5SThomas Graf 		if (udph->check == 0)
2975c26bf4a5SThomas Graf 			udph->check = CSUM_MANGLED_0;
2976c26bf4a5SThomas Graf 	}
2977c26bf4a5SThomas Graf 
2978a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
2979a553e4a6SJamal Hadi Salim 	if (!process_ipsec(pkt_dev, skb, protocol))
2980a553e4a6SJamal Hadi Salim 		return NULL;
2981a553e4a6SJamal Hadi Salim #endif
2982a553e4a6SJamal Hadi Salim 
29831da177e4SLinus Torvalds 	return skb;
29841da177e4SLinus Torvalds }
29851da177e4SLinus Torvalds 
fill_packet_ipv6(struct net_device * odev,struct pktgen_dev * pkt_dev)29861da177e4SLinus Torvalds static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
29871da177e4SLinus Torvalds 					struct pktgen_dev *pkt_dev)
29881da177e4SLinus Torvalds {
29891da177e4SLinus Torvalds 	struct sk_buff *skb = NULL;
29901da177e4SLinus Torvalds 	__u8 *eth;
29911da177e4SLinus Torvalds 	struct udphdr *udph;
2992c26bf4a5SThomas Graf 	int datalen, udplen;
29931da177e4SLinus Torvalds 	struct ipv6hdr *iph;
2994d5f1ce9aSStephen Hemminger 	__be16 protocol = htons(ETH_P_IPV6);
2995ca6549afSSteven Whitehouse 	__be32 *mpls;
299634954ddcSFrancesco Fondelli 	__be16 *vlan_tci = NULL;                 /* Encapsulates priority and VLAN ID */
299734954ddcSFrancesco Fondelli 	__be16 *vlan_encapsulated_proto = NULL;  /* packet type ID field (or len) for VLAN tag */
299834954ddcSFrancesco Fondelli 	__be16 *svlan_tci = NULL;                /* Encapsulates priority and SVLAN ID */
299934954ddcSFrancesco Fondelli 	__be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */
3000fd2ea0a7SDavid S. Miller 	u16 queue_map;
3001ca6549afSSteven Whitehouse 
3002ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
3003d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_MPLS_UC);
30041da177e4SLinus Torvalds 
300534954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff)
3006d5f1ce9aSStephen Hemminger 		protocol = htons(ETH_P_8021Q);
300734954ddcSFrancesco Fondelli 
300864053beeSRobert Olsson 	/* Update any of the values, used when we're incrementing various
300964053beeSRobert Olsson 	 * fields.
301064053beeSRobert Olsson 	 */
301164053beeSRobert Olsson 	mod_cur_headers(pkt_dev);
3012eb589063SJunchang Wang 	queue_map = pkt_dev->cur_queue_map;
301364053beeSRobert Olsson 
301463d75463SPaolo Abeni 	skb = pktgen_alloc_skb(odev, pkt_dev);
30151da177e4SLinus Torvalds 	if (!skb) {
30161da177e4SLinus Torvalds 		sprintf(pkt_dev->result, "No memory");
30171da177e4SLinus Torvalds 		return NULL;
30181da177e4SLinus Torvalds 	}
30191da177e4SLinus Torvalds 
30207a6e288dSDaniel Borkmann 	prefetchw(skb->data);
30211da177e4SLinus Torvalds 	skb_reserve(skb, 16);
30221da177e4SLinus Torvalds 
30231da177e4SLinus Torvalds 	/*  Reserve for ethernet and IP header  */
3024d58ff351SJohannes Berg 	eth = skb_push(skb, 14);
30254df864c1SJohannes Berg 	mpls = skb_put(skb, pkt_dev->nr_labels * sizeof(__u32));
3026ca6549afSSteven Whitehouse 	if (pkt_dev->nr_labels)
3027ca6549afSSteven Whitehouse 		mpls_push(mpls, pkt_dev);
302834954ddcSFrancesco Fondelli 
302934954ddcSFrancesco Fondelli 	if (pkt_dev->vlan_id != 0xffff) {
303034954ddcSFrancesco Fondelli 		if (pkt_dev->svlan_id != 0xffff) {
30314df864c1SJohannes Berg 			svlan_tci = skb_put(skb, sizeof(__be16));
30320f37c605SAl Viro 			*svlan_tci = build_tci(pkt_dev->svlan_id,
30330f37c605SAl Viro 					       pkt_dev->svlan_cfi,
30340f37c605SAl Viro 					       pkt_dev->svlan_p);
30354df864c1SJohannes Berg 			svlan_encapsulated_proto = skb_put(skb,
30364df864c1SJohannes Berg 							   sizeof(__be16));
3037d5f1ce9aSStephen Hemminger 			*svlan_encapsulated_proto = htons(ETH_P_8021Q);
303834954ddcSFrancesco Fondelli 		}
30394df864c1SJohannes Berg 		vlan_tci = skb_put(skb, sizeof(__be16));
30400f37c605SAl Viro 		*vlan_tci = build_tci(pkt_dev->vlan_id,
30410f37c605SAl Viro 				      pkt_dev->vlan_cfi,
30420f37c605SAl Viro 				      pkt_dev->vlan_p);
30434df864c1SJohannes Berg 		vlan_encapsulated_proto = skb_put(skb, sizeof(__be16));
3044d5f1ce9aSStephen Hemminger 		*vlan_encapsulated_proto = htons(ETH_P_IPV6);
304534954ddcSFrancesco Fondelli 	}
304634954ddcSFrancesco Fondelli 
3047c145aeb3SZhang Shengju 	skb_reset_mac_header(skb);
3048525cebedSThomas Graf 	skb_set_network_header(skb, skb->len);
30494df864c1SJohannes Berg 	iph = skb_put(skb, sizeof(struct ipv6hdr));
3050525cebedSThomas Graf 
3051525cebedSThomas Graf 	skb_set_transport_header(skb, skb->len);
30524df864c1SJohannes Berg 	udph = skb_put(skb, sizeof(struct udphdr));
3053fd2ea0a7SDavid S. Miller 	skb_set_queue_mapping(skb, queue_map);
30549e50e3acSJohn Fastabend 	skb->priority = pkt_dev->skb_priority;
30551da177e4SLinus Torvalds 
30561da177e4SLinus Torvalds 	memcpy(eth, pkt_dev->hh, 12);
3057252e3346SAl Viro 	*(__be16 *) &eth[12] = protocol;
30581da177e4SLinus Torvalds 
3059ca6549afSSteven Whitehouse 	/* Eth + IPh + UDPh + mpls */
3060ca6549afSSteven Whitehouse 	datalen = pkt_dev->cur_pkt_size - 14 -
3061ca6549afSSteven Whitehouse 		  sizeof(struct ipv6hdr) - sizeof(struct udphdr) -
306216dab72fSJamal Hadi Salim 		  pkt_dev->pkt_overhead;
30631da177e4SLinus Torvalds 
30645aa8b572SAmerigo Wang 	if (datalen < 0 || datalen < sizeof(struct pktgen_hdr)) {
30651da177e4SLinus Torvalds 		datalen = sizeof(struct pktgen_hdr);
3066e87cc472SJoe Perches 		net_info_ratelimited("increased datalen to %d\n", datalen);
30671da177e4SLinus Torvalds 	}
30681da177e4SLinus Torvalds 
3069c26bf4a5SThomas Graf 	udplen = datalen + sizeof(struct udphdr);
30701da177e4SLinus Torvalds 	udph->source = htons(pkt_dev->cur_udp_src);
30711da177e4SLinus Torvalds 	udph->dest = htons(pkt_dev->cur_udp_dst);
3072c26bf4a5SThomas Graf 	udph->len = htons(udplen);
3073c26bf4a5SThomas Graf 	udph->check = 0;
30741da177e4SLinus Torvalds 
3075d5f1ce9aSStephen Hemminger 	*(__be32 *) iph = htonl(0x60000000);	/* Version + flow */
30761da177e4SLinus Torvalds 
30771ca7768cSFrancesco Fondelli 	if (pkt_dev->traffic_class) {
30781ca7768cSFrancesco Fondelli 		/* Version + traffic class + flow (0) */
3079252e3346SAl Viro 		*(__be32 *)iph |= htonl(0x60000000 | (pkt_dev->traffic_class << 20));
30801ca7768cSFrancesco Fondelli 	}
30811ca7768cSFrancesco Fondelli 
30821da177e4SLinus Torvalds 	iph->hop_limit = 32;
30831da177e4SLinus Torvalds 
3084c26bf4a5SThomas Graf 	iph->payload_len = htons(udplen);
30851da177e4SLinus Torvalds 	iph->nexthdr = IPPROTO_UDP;
30861da177e4SLinus Torvalds 
30874e3fd7a0SAlexey Dobriyan 	iph->daddr = pkt_dev->cur_in6_daddr;
30884e3fd7a0SAlexey Dobriyan 	iph->saddr = pkt_dev->cur_in6_saddr;
30891da177e4SLinus Torvalds 
3090ca6549afSSteven Whitehouse 	skb->protocol = protocol;
30911da177e4SLinus Torvalds 	skb->dev = odev;
30921da177e4SLinus Torvalds 	skb->pkt_type = PACKET_HOST;
30931da177e4SLinus Torvalds 
30947744b5f3SSabrina Dubroca 	pktgen_finalize_skb(pkt_dev, skb, datalen);
30957744b5f3SSabrina Dubroca 
3096c26bf4a5SThomas Graf 	if (!(pkt_dev->flags & F_UDPCSUM)) {
3097c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_NONE;
3098c8cd0989STom Herbert 	} else if (odev->features & (NETIF_F_HW_CSUM | NETIF_F_IPV6_CSUM)) {
3099c26bf4a5SThomas Graf 		skb->ip_summed = CHECKSUM_PARTIAL;
3100c26bf4a5SThomas Graf 		skb->csum_start = skb_transport_header(skb) - skb->head;
3101c26bf4a5SThomas Graf 		skb->csum_offset = offsetof(struct udphdr, check);
3102c26bf4a5SThomas Graf 		udph->check = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, 0);
3103c26bf4a5SThomas Graf 	} else {
31047744b5f3SSabrina Dubroca 		__wsum csum = skb_checksum(skb, skb_transport_offset(skb), udplen, 0);
3105c26bf4a5SThomas Graf 
3106c26bf4a5SThomas Graf 		/* add protocol-dependent pseudo-header */
3107c26bf4a5SThomas Graf 		udph->check = csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, csum);
3108c26bf4a5SThomas Graf 
3109c26bf4a5SThomas Graf 		if (udph->check == 0)
3110c26bf4a5SThomas Graf 			udph->check = CSUM_MANGLED_0;
3111c26bf4a5SThomas Graf 	}
3112c26bf4a5SThomas Graf 
31131da177e4SLinus Torvalds 	return skb;
31141da177e4SLinus Torvalds }
31151da177e4SLinus Torvalds 
fill_packet(struct net_device * odev,struct pktgen_dev * pkt_dev)3116475ac1e4SStephen Hemminger static struct sk_buff *fill_packet(struct net_device *odev,
31171da177e4SLinus Torvalds 				   struct pktgen_dev *pkt_dev)
31181da177e4SLinus Torvalds {
31191da177e4SLinus Torvalds 	if (pkt_dev->flags & F_IPV6)
31201da177e4SLinus Torvalds 		return fill_packet_ipv6(odev, pkt_dev);
31211da177e4SLinus Torvalds 	else
31221da177e4SLinus Torvalds 		return fill_packet_ipv4(odev, pkt_dev);
31231da177e4SLinus Torvalds }
31241da177e4SLinus Torvalds 
pktgen_clear_counters(struct pktgen_dev * pkt_dev)31251da177e4SLinus Torvalds static void pktgen_clear_counters(struct pktgen_dev *pkt_dev)
31261da177e4SLinus Torvalds {
31271da177e4SLinus Torvalds 	pkt_dev->seq_num = 1;
31281da177e4SLinus Torvalds 	pkt_dev->idle_acc = 0;
31291da177e4SLinus Torvalds 	pkt_dev->sofar = 0;
31301da177e4SLinus Torvalds 	pkt_dev->tx_bytes = 0;
31311da177e4SLinus Torvalds 	pkt_dev->errors = 0;
31321da177e4SLinus Torvalds }
31331da177e4SLinus Torvalds 
31341da177e4SLinus Torvalds /* Set up structure for sending pkts, clear counters */
31351da177e4SLinus Torvalds 
pktgen_run(struct pktgen_thread * t)31361da177e4SLinus Torvalds static void pktgen_run(struct pktgen_thread *t)
31371da177e4SLinus Torvalds {
3138c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
31391da177e4SLinus Torvalds 	int started = 0;
31401da177e4SLinus Torvalds 
3141f9467eaeSJoe Perches 	func_enter();
31421da177e4SLinus Torvalds 
31438788370aSJesper Dangaard Brouer 	rcu_read_lock();
31448788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
31451da177e4SLinus Torvalds 
31461da177e4SLinus Torvalds 		/*
31471da177e4SLinus Torvalds 		 * setup odev and create initial packet.
31481da177e4SLinus Torvalds 		 */
31491da177e4SLinus Torvalds 		pktgen_setup_inject(pkt_dev);
31501da177e4SLinus Torvalds 
31511da177e4SLinus Torvalds 		if (pkt_dev->odev) {
31521da177e4SLinus Torvalds 			pktgen_clear_counters(pkt_dev);
31531da177e4SLinus Torvalds 			pkt_dev->skb = NULL;
3154398f382cSDaniel Borkmann 			pkt_dev->started_at = pkt_dev->next_tx = ktime_get();
3155fd29cf72SStephen Hemminger 
315616dab72fSJamal Hadi Salim 			set_pkt_overhead(pkt_dev);
31571da177e4SLinus Torvalds 
31581da177e4SLinus Torvalds 			strcpy(pkt_dev->result, "Starting");
31598788370aSJesper Dangaard Brouer 			pkt_dev->running = 1;	/* Cranke yeself! */
31601da177e4SLinus Torvalds 			started++;
3161222f1806SLuiz Capitulino 		} else
31621da177e4SLinus Torvalds 			strcpy(pkt_dev->result, "Error starting");
31631da177e4SLinus Torvalds 	}
31648788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3165222f1806SLuiz Capitulino 	if (started)
3166222f1806SLuiz Capitulino 		t->control &= ~(T_STOP);
31671da177e4SLinus Torvalds }
31681da177e4SLinus Torvalds 
pktgen_handle_all_threads(struct pktgen_net * pn,u32 flags)3169cda9de0bSYejune Deng static void pktgen_handle_all_threads(struct pktgen_net *pn, u32 flags)
31701da177e4SLinus Torvalds {
3171cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
31721da177e4SLinus Torvalds 
31736146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
3174cdcdbe0bSLuiz Capitulino 
31754e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list)
3176cda9de0bSYejune Deng 		t->control |= (flags);
3177cdcdbe0bSLuiz Capitulino 
31786146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
31791da177e4SLinus Torvalds }
31801da177e4SLinus Torvalds 
pktgen_stop_all_threads(struct pktgen_net * pn)3181cda9de0bSYejune Deng static void pktgen_stop_all_threads(struct pktgen_net *pn)
3182cda9de0bSYejune Deng {
3183cda9de0bSYejune Deng 	func_enter();
3184cda9de0bSYejune Deng 
3185cda9de0bSYejune Deng 	pktgen_handle_all_threads(pn, T_STOP);
3186cda9de0bSYejune Deng }
3187cda9de0bSYejune Deng 
thread_is_running(const struct pktgen_thread * t)3188648fda74SStephen Hemminger static int thread_is_running(const struct pktgen_thread *t)
31891da177e4SLinus Torvalds {
3190648fda74SStephen Hemminger 	const struct pktgen_dev *pkt_dev;
31911da177e4SLinus Torvalds 
31928788370aSJesper Dangaard Brouer 	rcu_read_lock();
31938788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list)
31948788370aSJesper Dangaard Brouer 		if (pkt_dev->running) {
31958788370aSJesper Dangaard Brouer 			rcu_read_unlock();
3196648fda74SStephen Hemminger 			return 1;
31978788370aSJesper Dangaard Brouer 		}
31988788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3199648fda74SStephen Hemminger 	return 0;
32001da177e4SLinus Torvalds }
32011da177e4SLinus Torvalds 
pktgen_wait_thread_run(struct pktgen_thread * t)32021da177e4SLinus Torvalds static int pktgen_wait_thread_run(struct pktgen_thread *t)
32031da177e4SLinus Torvalds {
32041da177e4SLinus Torvalds 	while (thread_is_running(t)) {
32051da177e4SLinus Torvalds 
3206720f1de4SPaolo Abeni 		/* note: 't' will still be around even after the unlock/lock
3207720f1de4SPaolo Abeni 		 * cycle because pktgen_thread threads are only cleared at
3208720f1de4SPaolo Abeni 		 * net exit
3209720f1de4SPaolo Abeni 		 */
3210720f1de4SPaolo Abeni 		mutex_unlock(&pktgen_thread_lock);
32111da177e4SLinus Torvalds 		msleep_interruptible(100);
3212720f1de4SPaolo Abeni 		mutex_lock(&pktgen_thread_lock);
32131da177e4SLinus Torvalds 
32141da177e4SLinus Torvalds 		if (signal_pending(current))
32151da177e4SLinus Torvalds 			goto signal;
32161da177e4SLinus Torvalds 	}
32171da177e4SLinus Torvalds 	return 1;
32181da177e4SLinus Torvalds signal:
32191da177e4SLinus Torvalds 	return 0;
32201da177e4SLinus Torvalds }
32211da177e4SLinus Torvalds 
pktgen_wait_all_threads_run(struct pktgen_net * pn)32224e58a027SCong Wang static int pktgen_wait_all_threads_run(struct pktgen_net *pn)
32231da177e4SLinus Torvalds {
3224cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
32251da177e4SLinus Torvalds 	int sig = 1;
32261da177e4SLinus Torvalds 
3227720f1de4SPaolo Abeni 	/* prevent from racing with rmmod */
3228720f1de4SPaolo Abeni 	if (!try_module_get(THIS_MODULE))
3229720f1de4SPaolo Abeni 		return sig;
3230720f1de4SPaolo Abeni 
32316146e6a4SLuiz Capitulino 	mutex_lock(&pktgen_thread_lock);
3232cdcdbe0bSLuiz Capitulino 
32334e58a027SCong Wang 	list_for_each_entry(t, &pn->pktgen_threads, th_list) {
32341da177e4SLinus Torvalds 		sig = pktgen_wait_thread_run(t);
3235222f1806SLuiz Capitulino 		if (sig == 0)
3236222f1806SLuiz Capitulino 			break;
32371da177e4SLinus Torvalds 	}
3238cdcdbe0bSLuiz Capitulino 
3239cdcdbe0bSLuiz Capitulino 	if (sig == 0)
32404e58a027SCong Wang 		list_for_each_entry(t, &pn->pktgen_threads, th_list)
32411da177e4SLinus Torvalds 			t->control |= (T_STOP);
3242cdcdbe0bSLuiz Capitulino 
32436146e6a4SLuiz Capitulino 	mutex_unlock(&pktgen_thread_lock);
3244720f1de4SPaolo Abeni 	module_put(THIS_MODULE);
32451da177e4SLinus Torvalds 	return sig;
32461da177e4SLinus Torvalds }
32471da177e4SLinus Torvalds 
pktgen_run_all_threads(struct pktgen_net * pn)32484e58a027SCong Wang static void pktgen_run_all_threads(struct pktgen_net *pn)
32491da177e4SLinus Torvalds {
3250f9467eaeSJoe Perches 	func_enter();
32511da177e4SLinus Torvalds 
3252cda9de0bSYejune Deng 	pktgen_handle_all_threads(pn, T_RUN);
32531da177e4SLinus Torvalds 
325463adc6fbSStephen Hemminger 	/* Propagate thread->control  */
325563adc6fbSStephen Hemminger 	schedule_timeout_interruptible(msecs_to_jiffies(125));
32561da177e4SLinus Torvalds 
32574e58a027SCong Wang 	pktgen_wait_all_threads_run(pn);
32581da177e4SLinus Torvalds }
32591da177e4SLinus Torvalds 
pktgen_reset_all_threads(struct pktgen_net * pn)32604e58a027SCong Wang static void pktgen_reset_all_threads(struct pktgen_net *pn)
3261eb37b41cSJesse Brandeburg {
3262f9467eaeSJoe Perches 	func_enter();
3263eb37b41cSJesse Brandeburg 
3264cda9de0bSYejune Deng 	pktgen_handle_all_threads(pn, T_REMDEVALL);
3265eb37b41cSJesse Brandeburg 
326663adc6fbSStephen Hemminger 	/* Propagate thread->control  */
326763adc6fbSStephen Hemminger 	schedule_timeout_interruptible(msecs_to_jiffies(125));
3268eb37b41cSJesse Brandeburg 
32694e58a027SCong Wang 	pktgen_wait_all_threads_run(pn);
3270eb37b41cSJesse Brandeburg }
3271eb37b41cSJesse Brandeburg 
show_results(struct pktgen_dev * pkt_dev,int nr_frags)32721da177e4SLinus Torvalds static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
32731da177e4SLinus Torvalds {
3274fd29cf72SStephen Hemminger 	__u64 bps, mbps, pps;
32751da177e4SLinus Torvalds 	char *p = pkt_dev->result;
3276fd29cf72SStephen Hemminger 	ktime_t elapsed = ktime_sub(pkt_dev->stopped_at,
3277fd29cf72SStephen Hemminger 				    pkt_dev->started_at);
3278fd29cf72SStephen Hemminger 	ktime_t idle = ns_to_ktime(pkt_dev->idle_acc);
32791da177e4SLinus Torvalds 
328003a14ab1SDaniel Turull 	p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n",
3281fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(elapsed),
3282fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(ktime_sub(elapsed, idle)),
3283fd29cf72SStephen Hemminger 		     (unsigned long long)ktime_to_us(idle),
32841da177e4SLinus Torvalds 		     (unsigned long long)pkt_dev->sofar,
32851da177e4SLinus Torvalds 		     pkt_dev->cur_pkt_size, nr_frags);
32861da177e4SLinus Torvalds 
3287fd29cf72SStephen Hemminger 	pps = div64_u64(pkt_dev->sofar * NSEC_PER_SEC,
3288fd29cf72SStephen Hemminger 			ktime_to_ns(elapsed));
32891da177e4SLinus Torvalds 
3290769afb3fSNick Richardson 	if (pkt_dev->n_imix_entries > 0) {
3291769afb3fSNick Richardson 		int i;
3292769afb3fSNick Richardson 		struct imix_pkt *entry;
3293769afb3fSNick Richardson 
3294769afb3fSNick Richardson 		bps = 0;
3295769afb3fSNick Richardson 		for (i = 0; i < pkt_dev->n_imix_entries; i++) {
3296769afb3fSNick Richardson 			entry = &pkt_dev->imix_entries[i];
3297769afb3fSNick Richardson 			bps += entry->size * entry->count_so_far;
3298769afb3fSNick Richardson 		}
3299769afb3fSNick Richardson 		bps = div64_u64(bps * 8 * NSEC_PER_SEC, ktime_to_ns(elapsed));
3300769afb3fSNick Richardson 	} else {
33011da177e4SLinus Torvalds 		bps = pps * 8 * pkt_dev->cur_pkt_size;
3302769afb3fSNick Richardson 	}
33031da177e4SLinus Torvalds 
33041da177e4SLinus Torvalds 	mbps = bps;
33051da177e4SLinus Torvalds 	do_div(mbps, 1000000);
33061da177e4SLinus Torvalds 	p += sprintf(p, "  %llupps %lluMb/sec (%llubps) errors: %llu",
33071da177e4SLinus Torvalds 		     (unsigned long long)pps,
33081da177e4SLinus Torvalds 		     (unsigned long long)mbps,
33091da177e4SLinus Torvalds 		     (unsigned long long)bps,
33101da177e4SLinus Torvalds 		     (unsigned long long)pkt_dev->errors);
33111da177e4SLinus Torvalds }
33121da177e4SLinus Torvalds 
33131da177e4SLinus Torvalds /* Set stopped-at timer, remove from running list, do counters & statistics */
pktgen_stop_device(struct pktgen_dev * pkt_dev)33141da177e4SLinus Torvalds static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
33151da177e4SLinus Torvalds {
3316222f1806SLuiz Capitulino 	int nr_frags = pkt_dev->skb ? skb_shinfo(pkt_dev->skb)->nr_frags : -1;
33171da177e4SLinus Torvalds 
33181da177e4SLinus Torvalds 	if (!pkt_dev->running) {
3319294a0b7fSJoe Perches 		pr_warn("interface: %s is already stopped\n",
3320f9467eaeSJoe Perches 			pkt_dev->odevname);
33211da177e4SLinus Torvalds 		return -EINVAL;
33221da177e4SLinus Torvalds 	}
33231da177e4SLinus Torvalds 
33248788370aSJesper Dangaard Brouer 	pkt_dev->running = 0;
33253bda06a3SStephen Hemminger 	kfree_skb(pkt_dev->skb);
33263bda06a3SStephen Hemminger 	pkt_dev->skb = NULL;
3327398f382cSDaniel Borkmann 	pkt_dev->stopped_at = ktime_get();
33281da177e4SLinus Torvalds 
332995ed63f7SArthur Kepner 	show_results(pkt_dev, nr_frags);
33301da177e4SLinus Torvalds 
33311da177e4SLinus Torvalds 	return 0;
33321da177e4SLinus Torvalds }
33331da177e4SLinus Torvalds 
next_to_run(struct pktgen_thread * t)33341da177e4SLinus Torvalds static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
33351da177e4SLinus Torvalds {
3336c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev, *best = NULL;
33371da177e4SLinus Torvalds 
33388788370aSJesper Dangaard Brouer 	rcu_read_lock();
33398788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
3340c26a8016SLuiz Capitulino 		if (!pkt_dev->running)
3341222f1806SLuiz Capitulino 			continue;
3342222f1806SLuiz Capitulino 		if (best == NULL)
3343c26a8016SLuiz Capitulino 			best = pkt_dev;
3344398f382cSDaniel Borkmann 		else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0)
3345c26a8016SLuiz Capitulino 			best = pkt_dev;
33461da177e4SLinus Torvalds 	}
33478788370aSJesper Dangaard Brouer 	rcu_read_unlock();
33488788370aSJesper Dangaard Brouer 
33491da177e4SLinus Torvalds 	return best;
33501da177e4SLinus Torvalds }
33511da177e4SLinus Torvalds 
pktgen_stop(struct pktgen_thread * t)3352222f1806SLuiz Capitulino static void pktgen_stop(struct pktgen_thread *t)
3353222f1806SLuiz Capitulino {
3354c26a8016SLuiz Capitulino 	struct pktgen_dev *pkt_dev;
33551da177e4SLinus Torvalds 
3356f9467eaeSJoe Perches 	func_enter();
33571da177e4SLinus Torvalds 
33588788370aSJesper Dangaard Brouer 	rcu_read_lock();
33591da177e4SLinus Torvalds 
33608788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
3361c26a8016SLuiz Capitulino 		pktgen_stop_device(pkt_dev);
336295ed63f7SArthur Kepner 	}
336395ed63f7SArthur Kepner 
33648788370aSJesper Dangaard Brouer 	rcu_read_unlock();
336595ed63f7SArthur Kepner }
336695ed63f7SArthur Kepner 
336795ed63f7SArthur Kepner /*
336895ed63f7SArthur Kepner  * one of our devices needs to be removed - find it
336995ed63f7SArthur Kepner  * and remove it
337095ed63f7SArthur Kepner  */
pktgen_rem_one_if(struct pktgen_thread * t)337195ed63f7SArthur Kepner static void pktgen_rem_one_if(struct pktgen_thread *t)
337295ed63f7SArthur Kepner {
3373c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3374c26a8016SLuiz Capitulino 	struct pktgen_dev *cur;
337595ed63f7SArthur Kepner 
3376f9467eaeSJoe Perches 	func_enter();
337795ed63f7SArthur Kepner 
3378c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3379c26a8016SLuiz Capitulino 		cur = list_entry(q, struct pktgen_dev, list);
338095ed63f7SArthur Kepner 
3381222f1806SLuiz Capitulino 		if (!cur->removal_mark)
3382222f1806SLuiz Capitulino 			continue;
338395ed63f7SArthur Kepner 
338495ed63f7SArthur Kepner 		kfree_skb(cur->skb);
338595ed63f7SArthur Kepner 		cur->skb = NULL;
338695ed63f7SArthur Kepner 
338795ed63f7SArthur Kepner 		pktgen_remove_device(t, cur);
338895ed63f7SArthur Kepner 
338995ed63f7SArthur Kepner 		break;
339095ed63f7SArthur Kepner 	}
33911da177e4SLinus Torvalds }
33921da177e4SLinus Torvalds 
pktgen_rem_all_ifs(struct pktgen_thread * t)33931da177e4SLinus Torvalds static void pktgen_rem_all_ifs(struct pktgen_thread *t)
33941da177e4SLinus Torvalds {
3395c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3396c26a8016SLuiz Capitulino 	struct pktgen_dev *cur;
33971da177e4SLinus Torvalds 
3398f9467eaeSJoe Perches 	func_enter();
3399f9467eaeSJoe Perches 
34001da177e4SLinus Torvalds 	/* Remove all devices, free mem */
34011da177e4SLinus Torvalds 
3402c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3403c26a8016SLuiz Capitulino 		cur = list_entry(q, struct pktgen_dev, list);
340495ed63f7SArthur Kepner 
340595ed63f7SArthur Kepner 		kfree_skb(cur->skb);
340695ed63f7SArthur Kepner 		cur->skb = NULL;
340795ed63f7SArthur Kepner 
34081da177e4SLinus Torvalds 		pktgen_remove_device(t, cur);
34091da177e4SLinus Torvalds 	}
34101da177e4SLinus Torvalds }
34111da177e4SLinus Torvalds 
pktgen_rem_thread(struct pktgen_thread * t)34121da177e4SLinus Torvalds static void pktgen_rem_thread(struct pktgen_thread *t)
34131da177e4SLinus Torvalds {
34141da177e4SLinus Torvalds 	/* Remove from the thread list */
34154e58a027SCong Wang 	remove_proc_entry(t->tsk->comm, t->net->proc_dir);
34161da177e4SLinus Torvalds }
34171da177e4SLinus Torvalds 
pktgen_resched(struct pktgen_dev * pkt_dev)3418ef87979cSStephen Hemminger static void pktgen_resched(struct pktgen_dev *pkt_dev)
34193791decbSStephen Hemminger {
3420398f382cSDaniel Borkmann 	ktime_t idle_start = ktime_get();
34213791decbSStephen Hemminger 	schedule();
3422398f382cSDaniel Borkmann 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
34233791decbSStephen Hemminger }
34243791decbSStephen Hemminger 
pktgen_wait_for_skb(struct pktgen_dev * pkt_dev)3425ef87979cSStephen Hemminger static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)
3426ef87979cSStephen Hemminger {
3427398f382cSDaniel Borkmann 	ktime_t idle_start = ktime_get();
3428ef87979cSStephen Hemminger 
342963354797SReshetova, Elena 	while (refcount_read(&(pkt_dev->skb->users)) != 1) {
3430ef87979cSStephen Hemminger 		if (signal_pending(current))
3431ef87979cSStephen Hemminger 			break;
3432ef87979cSStephen Hemminger 
3433ef87979cSStephen Hemminger 		if (need_resched())
3434ef87979cSStephen Hemminger 			pktgen_resched(pkt_dev);
3435ef87979cSStephen Hemminger 		else
3436ef87979cSStephen Hemminger 			cpu_relax();
3437ef87979cSStephen Hemminger 	}
3438398f382cSDaniel Borkmann 	pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
3439ef87979cSStephen Hemminger }
3440fd29cf72SStephen Hemminger 
pktgen_xmit(struct pktgen_dev * pkt_dev)3441475ac1e4SStephen Hemminger static void pktgen_xmit(struct pktgen_dev *pkt_dev)
34421da177e4SLinus Torvalds {
34436aa7de05SMark Rutland 	unsigned int burst = READ_ONCE(pkt_dev->burst);
344400829823SStephen Hemminger 	struct net_device *odev = pkt_dev->odev;
3445fd2ea0a7SDavid S. Miller 	struct netdev_queue *txq;
344662f64aedSAlexei Starovoitov 	struct sk_buff *skb;
34471da177e4SLinus Torvalds 	int ret;
34481da177e4SLinus Torvalds 
3449ef87979cSStephen Hemminger 	/* If device is offline, then don't send */
3450ef87979cSStephen Hemminger 	if (unlikely(!netif_running(odev) || !netif_carrier_ok(odev))) {
34511da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
34523791decbSStephen Hemminger 		return;
34531da177e4SLinus Torvalds 	}
34541da177e4SLinus Torvalds 
3455ef87979cSStephen Hemminger 	/* This is max DELAY, this has special meaning of
3456ef87979cSStephen Hemminger 	 * "never transmit"
3457ef87979cSStephen Hemminger 	 */
3458ef87979cSStephen Hemminger 	if (unlikely(pkt_dev->delay == ULLONG_MAX)) {
3459398f382cSDaniel Borkmann 		pkt_dev->next_tx = ktime_add_ns(ktime_get(), ULONG_MAX);
3460ef87979cSStephen Hemminger 		return;
3461ef87979cSStephen Hemminger 	}
3462ef87979cSStephen Hemminger 
3463ef87979cSStephen Hemminger 	/* If no skb or clone count exhausted then get new one */
34647d7bb1cfSStephen Hemminger 	if (!pkt_dev->skb || (pkt_dev->last_ok &&
34657d7bb1cfSStephen Hemminger 			      ++pkt_dev->clone_count >= pkt_dev->clone_skb)) {
34661da177e4SLinus Torvalds 		/* build a new pkt */
34671da177e4SLinus Torvalds 		kfree_skb(pkt_dev->skb);
34681da177e4SLinus Torvalds 
34691da177e4SLinus Torvalds 		pkt_dev->skb = fill_packet(odev, pkt_dev);
34701da177e4SLinus Torvalds 		if (pkt_dev->skb == NULL) {
3471f9467eaeSJoe Perches 			pr_err("ERROR: couldn't allocate skb in fill_packet\n");
34721da177e4SLinus Torvalds 			schedule();
34731da177e4SLinus Torvalds 			pkt_dev->clone_count--;	/* back out increment, OOM */
34743791decbSStephen Hemminger 			return;
34751da177e4SLinus Torvalds 		}
3476baac8564SEric Dumazet 		pkt_dev->last_pkt_size = pkt_dev->skb->len;
34771da177e4SLinus Torvalds 		pkt_dev->clone_count = 0;	/* reset counter */
34781da177e4SLinus Torvalds 	}
34791da177e4SLinus Torvalds 
3480ef87979cSStephen Hemminger 	if (pkt_dev->delay && pkt_dev->last_ok)
3481ef87979cSStephen Hemminger 		spin(pkt_dev, pkt_dev->next_tx);
3482ef87979cSStephen Hemminger 
348362f64aedSAlexei Starovoitov 	if (pkt_dev->xmit_mode == M_NETIF_RECEIVE) {
348462f64aedSAlexei Starovoitov 		skb = pkt_dev->skb;
348562f64aedSAlexei Starovoitov 		skb->protocol = eth_type_trans(skb, skb->dev);
348663354797SReshetova, Elena 		refcount_add(burst, &skb->users);
348762f64aedSAlexei Starovoitov 		local_bh_disable();
348862f64aedSAlexei Starovoitov 		do {
348962f64aedSAlexei Starovoitov 			ret = netif_receive_skb(skb);
349062f64aedSAlexei Starovoitov 			if (ret == NET_RX_DROP)
349162f64aedSAlexei Starovoitov 				pkt_dev->errors++;
349262f64aedSAlexei Starovoitov 			pkt_dev->sofar++;
349362f64aedSAlexei Starovoitov 			pkt_dev->seq_num++;
349463354797SReshetova, Elena 			if (refcount_read(&skb->users) != burst) {
349562f64aedSAlexei Starovoitov 				/* skb was queued by rps/rfs or taps,
349662f64aedSAlexei Starovoitov 				 * so cannot reuse this skb
349762f64aedSAlexei Starovoitov 				 */
349863354797SReshetova, Elena 				WARN_ON(refcount_sub_and_test(burst - 1, &skb->users));
349962f64aedSAlexei Starovoitov 				/* get out of the loop and wait
350062f64aedSAlexei Starovoitov 				 * until skb is consumed
350162f64aedSAlexei Starovoitov 				 */
350262f64aedSAlexei Starovoitov 				break;
350362f64aedSAlexei Starovoitov 			}
350462f64aedSAlexei Starovoitov 			/* skb was 'freed' by stack, so clean few
350562f64aedSAlexei Starovoitov 			 * bits and reuse it
350662f64aedSAlexei Starovoitov 			 */
35072c64605bSPablo Neira Ayuso 			skb_reset_redirect(skb);
350862f64aedSAlexei Starovoitov 		} while (--burst > 0);
350962f64aedSAlexei Starovoitov 		goto out; /* Skips xmit_mode M_START_XMIT */
35100967f244SJohn Fastabend 	} else if (pkt_dev->xmit_mode == M_QUEUE_XMIT) {
35110967f244SJohn Fastabend 		local_bh_disable();
351263354797SReshetova, Elena 		refcount_inc(&pkt_dev->skb->users);
35130967f244SJohn Fastabend 
35140967f244SJohn Fastabend 		ret = dev_queue_xmit(pkt_dev->skb);
35150967f244SJohn Fastabend 		switch (ret) {
35160967f244SJohn Fastabend 		case NET_XMIT_SUCCESS:
35170967f244SJohn Fastabend 			pkt_dev->sofar++;
35180967f244SJohn Fastabend 			pkt_dev->seq_num++;
35190967f244SJohn Fastabend 			pkt_dev->tx_bytes += pkt_dev->last_pkt_size;
35200967f244SJohn Fastabend 			break;
35210967f244SJohn Fastabend 		case NET_XMIT_DROP:
35220967f244SJohn Fastabend 		case NET_XMIT_CN:
35230967f244SJohn Fastabend 		/* These are all valid return codes for a qdisc but
35240967f244SJohn Fastabend 		 * indicate packets are being dropped or will likely
35250967f244SJohn Fastabend 		 * be dropped soon.
35260967f244SJohn Fastabend 		 */
35270967f244SJohn Fastabend 		case NETDEV_TX_BUSY:
35280967f244SJohn Fastabend 		/* qdisc may call dev_hard_start_xmit directly in cases
35290967f244SJohn Fastabend 		 * where no queues exist e.g. loopback device, virtual
35300967f244SJohn Fastabend 		 * devices, etc. In this case we need to handle
35310967f244SJohn Fastabend 		 * NETDEV_TX_ codes.
35320967f244SJohn Fastabend 		 */
35330967f244SJohn Fastabend 		default:
35340967f244SJohn Fastabend 			pkt_dev->errors++;
35350967f244SJohn Fastabend 			net_info_ratelimited("%s xmit error: %d\n",
35360967f244SJohn Fastabend 					     pkt_dev->odevname, ret);
35370967f244SJohn Fastabend 			break;
35380967f244SJohn Fastabend 		}
35390967f244SJohn Fastabend 		goto out;
354062f64aedSAlexei Starovoitov 	}
354162f64aedSAlexei Starovoitov 
354210c51b56SDaniel Borkmann 	txq = skb_get_tx_queue(odev, pkt_dev->skb);
3543fd2ea0a7SDavid S. Miller 
35440f2eea4bSDaniel Borkmann 	local_bh_disable();
35450f2eea4bSDaniel Borkmann 
35460f2eea4bSDaniel Borkmann 	HARD_TX_LOCK(odev, txq, smp_processor_id());
35475b8db2f5SStephen Hemminger 
35486f25cd47SDaniel Borkmann 	if (unlikely(netif_xmit_frozen_or_drv_stopped(txq))) {
35490835acfeSEric Dumazet 		pkt_dev->last_ok = 0;
35500835acfeSEric Dumazet 		goto unlock;
35510835acfeSEric Dumazet 	}
355263354797SReshetova, Elena 	refcount_add(burst, &pkt_dev->skb->users);
355338b2cf29SAlexei Starovoitov 
355438b2cf29SAlexei Starovoitov xmit_more:
355538b2cf29SAlexei Starovoitov 	ret = netdev_start_xmit(pkt_dev->skb, odev, txq, --burst > 0);
3556ef87979cSStephen Hemminger 
35575b8db2f5SStephen Hemminger 	switch (ret) {
35585b8db2f5SStephen Hemminger 	case NETDEV_TX_OK:
35591da177e4SLinus Torvalds 		pkt_dev->last_ok = 1;
35601da177e4SLinus Torvalds 		pkt_dev->sofar++;
35611da177e4SLinus Torvalds 		pkt_dev->seq_num++;
3562baac8564SEric Dumazet 		pkt_dev->tx_bytes += pkt_dev->last_pkt_size;
356338b2cf29SAlexei Starovoitov 		if (burst > 0 && !netif_xmit_frozen_or_drv_stopped(txq))
356438b2cf29SAlexei Starovoitov 			goto xmit_more;
35655b8db2f5SStephen Hemminger 		break;
3566f466dba1SJohn Fastabend 	case NET_XMIT_DROP:
3567f466dba1SJohn Fastabend 	case NET_XMIT_CN:
3568f466dba1SJohn Fastabend 		/* skb has been consumed */
3569f466dba1SJohn Fastabend 		pkt_dev->errors++;
3570f466dba1SJohn Fastabend 		break;
35715b8db2f5SStephen Hemminger 	default: /* Drivers are not supposed to return other values! */
3572e87cc472SJoe Perches 		net_info_ratelimited("%s xmit error: %d\n",
3573e87cc472SJoe Perches 				     pkt_dev->odevname, ret);
35741da177e4SLinus Torvalds 		pkt_dev->errors++;
3575df561f66SGustavo A. R. Silva 		fallthrough;
35765b8db2f5SStephen Hemminger 	case NETDEV_TX_BUSY:
35775b8db2f5SStephen Hemminger 		/* Retry it next time */
357863354797SReshetova, Elena 		refcount_dec(&(pkt_dev->skb->users));
35791da177e4SLinus Torvalds 		pkt_dev->last_ok = 0;
35801da177e4SLinus Torvalds 	}
358138b2cf29SAlexei Starovoitov 	if (unlikely(burst))
358263354797SReshetova, Elena 		WARN_ON(refcount_sub_and_test(burst, &pkt_dev->skb->users));
35830835acfeSEric Dumazet unlock:
35840f2eea4bSDaniel Borkmann 	HARD_TX_UNLOCK(odev, txq);
35850f2eea4bSDaniel Borkmann 
358662f64aedSAlexei Starovoitov out:
35870f2eea4bSDaniel Borkmann 	local_bh_enable();
35881da177e4SLinus Torvalds 
35891da177e4SLinus Torvalds 	/* If pkt_dev->count is zero, then run forever */
35901da177e4SLinus Torvalds 	if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
3591ef87979cSStephen Hemminger 		pktgen_wait_for_skb(pkt_dev);
35921da177e4SLinus Torvalds 
35931da177e4SLinus Torvalds 		/* Done with this */
35941da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
35951da177e4SLinus Torvalds 	}
35961da177e4SLinus Torvalds }
35971da177e4SLinus Torvalds 
35981da177e4SLinus Torvalds /*
35991da177e4SLinus Torvalds  * Main loop of the thread goes here
36001da177e4SLinus Torvalds  */
36011da177e4SLinus Torvalds 
pktgen_thread_worker(void * arg)3602ee74baa7SDavid S. Miller static int pktgen_thread_worker(void *arg)
36031da177e4SLinus Torvalds {
3604ee74baa7SDavid S. Miller 	struct pktgen_thread *t = arg;
36051da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev = NULL;
36061da177e4SLinus Torvalds 	int cpu = t->cpu;
36071da177e4SLinus Torvalds 
3608275b1e88SDi Zhu 	WARN_ON(smp_processor_id() != cpu);
36091da177e4SLinus Torvalds 
36101da177e4SLinus Torvalds 	init_waitqueue_head(&t->queue);
3611d3ede327SDenis V. Lunev 	complete(&t->start_done);
36121da177e4SLinus Torvalds 
3613f9467eaeSJoe Perches 	pr_debug("starting pktgen/%d:  pid=%d\n", cpu, task_pid_nr(current));
36141da177e4SLinus Torvalds 
361583144186SRafael J. Wysocki 	set_freezable();
361683144186SRafael J. Wysocki 
3617ee74baa7SDavid S. Miller 	while (!kthread_should_stop()) {
3618ee74baa7SDavid S. Miller 		pkt_dev = next_to_run(t);
3619ee74baa7SDavid S. Miller 
3620ef87979cSStephen Hemminger 		if (unlikely(!pkt_dev && t->control == 0)) {
36214e58a027SCong Wang 			if (t->net->pktgen_exiting)
3622551eaff1SEric Dumazet 				break;
3623ef87979cSStephen Hemminger 			wait_event_interruptible_timeout(t->queue,
3624ef87979cSStephen Hemminger 							 t->control != 0,
3625ef87979cSStephen Hemminger 							 HZ/10);
36261b3f720bSRafael J. Wysocki 			try_to_freeze();
3627ef87979cSStephen Hemminger 			continue;
3628ee74baa7SDavid S. Miller 		}
36291da177e4SLinus Torvalds 
3630ef87979cSStephen Hemminger 		if (likely(pkt_dev)) {
36311da177e4SLinus Torvalds 			pktgen_xmit(pkt_dev);
36321da177e4SLinus Torvalds 
3633ef87979cSStephen Hemminger 			if (need_resched())
3634ef87979cSStephen Hemminger 				pktgen_resched(pkt_dev);
3635ef87979cSStephen Hemminger 			else
3636ef87979cSStephen Hemminger 				cpu_relax();
3637ef87979cSStephen Hemminger 		}
3638ef87979cSStephen Hemminger 
36391da177e4SLinus Torvalds 		if (t->control & T_STOP) {
36401da177e4SLinus Torvalds 			pktgen_stop(t);
36411da177e4SLinus Torvalds 			t->control &= ~(T_STOP);
36421da177e4SLinus Torvalds 		}
36431da177e4SLinus Torvalds 
36441da177e4SLinus Torvalds 		if (t->control & T_RUN) {
36451da177e4SLinus Torvalds 			pktgen_run(t);
36461da177e4SLinus Torvalds 			t->control &= ~(T_RUN);
36471da177e4SLinus Torvalds 		}
36481da177e4SLinus Torvalds 
364995ed63f7SArthur Kepner 		if (t->control & T_REMDEVALL) {
36501da177e4SLinus Torvalds 			pktgen_rem_all_ifs(t);
365195ed63f7SArthur Kepner 			t->control &= ~(T_REMDEVALL);
365295ed63f7SArthur Kepner 		}
365395ed63f7SArthur Kepner 
365495ed63f7SArthur Kepner 		if (t->control & T_REMDEV) {
365595ed63f7SArthur Kepner 			pktgen_rem_one_if(t);
36561da177e4SLinus Torvalds 			t->control &= ~(T_REMDEV);
36571da177e4SLinus Torvalds 		}
36581da177e4SLinus Torvalds 
365909fe3ef4SAndrew Morton 		try_to_freeze();
36601da177e4SLinus Torvalds 	}
36611da177e4SLinus Torvalds 
3662f9467eaeSJoe Perches 	pr_debug("%s stopping all device\n", t->tsk->comm);
36631da177e4SLinus Torvalds 	pktgen_stop(t);
36641da177e4SLinus Torvalds 
3665f9467eaeSJoe Perches 	pr_debug("%s removing all device\n", t->tsk->comm);
36661da177e4SLinus Torvalds 	pktgen_rem_all_ifs(t);
36671da177e4SLinus Torvalds 
3668f9467eaeSJoe Perches 	pr_debug("%s removing thread\n", t->tsk->comm);
36691da177e4SLinus Torvalds 	pktgen_rem_thread(t);
3670cdcdbe0bSLuiz Capitulino 
3671ee74baa7SDavid S. Miller 	return 0;
36721da177e4SLinus Torvalds }
36731da177e4SLinus Torvalds 
pktgen_find_dev(struct pktgen_thread * t,const char * ifname,bool exact)3674222f1806SLuiz Capitulino static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
36753e984840SEric Dumazet 					  const char *ifname, bool exact)
36761da177e4SLinus Torvalds {
3677c26a8016SLuiz Capitulino 	struct pktgen_dev *p, *pkt_dev = NULL;
36783e984840SEric Dumazet 	size_t len = strlen(ifname);
36791da177e4SLinus Torvalds 
36808788370aSJesper Dangaard Brouer 	rcu_read_lock();
36818788370aSJesper Dangaard Brouer 	list_for_each_entry_rcu(p, &t->if_list, list)
36823e984840SEric Dumazet 		if (strncmp(p->odevname, ifname, len) == 0) {
36833e984840SEric Dumazet 			if (p->odevname[len]) {
36843e984840SEric Dumazet 				if (exact || p->odevname[len] != '@')
36853e984840SEric Dumazet 					continue;
36863e984840SEric Dumazet 			}
3687c26a8016SLuiz Capitulino 			pkt_dev = p;
36881da177e4SLinus Torvalds 			break;
36891da177e4SLinus Torvalds 		}
36901da177e4SLinus Torvalds 
36918788370aSJesper Dangaard Brouer 	rcu_read_unlock();
3692f9467eaeSJoe Perches 	pr_debug("find_dev(%s) returning %p\n", ifname, pkt_dev);
36931da177e4SLinus Torvalds 	return pkt_dev;
36941da177e4SLinus Torvalds }
36951da177e4SLinus Torvalds 
36961da177e4SLinus Torvalds /*
36971da177e4SLinus Torvalds  * Adds a dev at front of if_list.
36981da177e4SLinus Torvalds  */
36991da177e4SLinus Torvalds 
add_dev_to_thread(struct pktgen_thread * t,struct pktgen_dev * pkt_dev)3700222f1806SLuiz Capitulino static int add_dev_to_thread(struct pktgen_thread *t,
3701222f1806SLuiz Capitulino 			     struct pktgen_dev *pkt_dev)
37021da177e4SLinus Torvalds {
37031da177e4SLinus Torvalds 	int rv = 0;
37041da177e4SLinus Torvalds 
37058788370aSJesper Dangaard Brouer 	/* This function cannot be called concurrently, as its called
37068788370aSJesper Dangaard Brouer 	 * under pktgen_thread_lock mutex, but it can run from
37078788370aSJesper Dangaard Brouer 	 * userspace on another CPU than the kthread.  The if_lock()
37088788370aSJesper Dangaard Brouer 	 * is used here to sync with concurrent instances of
37098788370aSJesper Dangaard Brouer 	 * _rem_dev_from_if_list() invoked via kthread, which is also
37108788370aSJesper Dangaard Brouer 	 * updating the if_list */
37111da177e4SLinus Torvalds 	if_lock(t);
37121da177e4SLinus Torvalds 
37131da177e4SLinus Torvalds 	if (pkt_dev->pg_thread) {
3714f9467eaeSJoe Perches 		pr_err("ERROR: already assigned to a thread\n");
37151da177e4SLinus Torvalds 		rv = -EBUSY;
37161da177e4SLinus Torvalds 		goto out;
37171da177e4SLinus Torvalds 	}
3718c26a8016SLuiz Capitulino 
37191da177e4SLinus Torvalds 	pkt_dev->running = 0;
37208788370aSJesper Dangaard Brouer 	pkt_dev->pg_thread = t;
37218788370aSJesper Dangaard Brouer 	list_add_rcu(&pkt_dev->list, &t->if_list);
37221da177e4SLinus Torvalds 
37231da177e4SLinus Torvalds out:
37241da177e4SLinus Torvalds 	if_unlock(t);
37251da177e4SLinus Torvalds 	return rv;
37261da177e4SLinus Torvalds }
37271da177e4SLinus Torvalds 
37281da177e4SLinus Torvalds /* Called under thread lock */
37291da177e4SLinus Torvalds 
pktgen_add_device(struct pktgen_thread * t,const char * ifname)37301da177e4SLinus Torvalds static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
37311da177e4SLinus Torvalds {
37321da177e4SLinus Torvalds 	struct pktgen_dev *pkt_dev;
373339df232fSStephen Hemminger 	int err;
37343291b9dbSEric Dumazet 	int node = cpu_to_node(t->cpu);
37351da177e4SLinus Torvalds 
37361da177e4SLinus Torvalds 	/* We don't allow a device to be on several threads */
37371da177e4SLinus Torvalds 
37384e58a027SCong Wang 	pkt_dev = __pktgen_NN_threads(t->net, ifname, FIND);
3739d50a6b56SStephen Hemminger 	if (pkt_dev) {
3740f9467eaeSJoe Perches 		pr_err("ERROR: interface already used\n");
3741d50a6b56SStephen Hemminger 		return -EBUSY;
3742d50a6b56SStephen Hemminger 	}
37431da177e4SLinus Torvalds 
37443291b9dbSEric Dumazet 	pkt_dev = kzalloc_node(sizeof(struct pktgen_dev), GFP_KERNEL, node);
37451da177e4SLinus Torvalds 	if (!pkt_dev)
37461da177e4SLinus Torvalds 		return -ENOMEM;
37471da177e4SLinus Torvalds 
3748593f63b0SEric Dumazet 	strcpy(pkt_dev->odevname, ifname);
3749fd7becedSKees Cook 	pkt_dev->flows = vzalloc_node(array_size(MAX_CFLOWS,
3750fd7becedSKees Cook 						 sizeof(struct flow_state)),
37513291b9dbSEric Dumazet 				      node);
37521da177e4SLinus Torvalds 	if (pkt_dev->flows == NULL) {
37531da177e4SLinus Torvalds 		kfree(pkt_dev);
37541da177e4SLinus Torvalds 		return -ENOMEM;
37551da177e4SLinus Torvalds 	}
37561da177e4SLinus Torvalds 
375795ed63f7SArthur Kepner 	pkt_dev->removal_mark = 0;
37581da177e4SLinus Torvalds 	pkt_dev->nfrags = 0;
3759fd29cf72SStephen Hemminger 	pkt_dev->delay = pg_delay_d;
37601da177e4SLinus Torvalds 	pkt_dev->count = pg_count_d;
37611da177e4SLinus Torvalds 	pkt_dev->sofar = 0;
37621da177e4SLinus Torvalds 	pkt_dev->udp_src_min = 9;	/* sink port */
37631da177e4SLinus Torvalds 	pkt_dev->udp_src_max = 9;
37641da177e4SLinus Torvalds 	pkt_dev->udp_dst_min = 9;
37651da177e4SLinus Torvalds 	pkt_dev->udp_dst_max = 9;
376634954ddcSFrancesco Fondelli 	pkt_dev->vlan_p = 0;
376734954ddcSFrancesco Fondelli 	pkt_dev->vlan_cfi = 0;
376834954ddcSFrancesco Fondelli 	pkt_dev->vlan_id = 0xffff;
376934954ddcSFrancesco Fondelli 	pkt_dev->svlan_p = 0;
377034954ddcSFrancesco Fondelli 	pkt_dev->svlan_cfi = 0;
377134954ddcSFrancesco Fondelli 	pkt_dev->svlan_id = 0xffff;
377238b2cf29SAlexei Starovoitov 	pkt_dev->burst = 1;
377398fa15f3SAnshuman Khandual 	pkt_dev->node = NUMA_NO_NODE;
377434954ddcSFrancesco Fondelli 
37754e58a027SCong Wang 	err = pktgen_setup_dev(t->net, pkt_dev, ifname);
377639df232fSStephen Hemminger 	if (err)
377739df232fSStephen Hemminger 		goto out1;
3778d8873315SNeil Horman 	if (pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)
3779d8873315SNeil Horman 		pkt_dev->clone_skb = pg_clone_skb_d;
37801da177e4SLinus Torvalds 
37814e58a027SCong Wang 	pkt_dev->entry = proc_create_data(ifname, 0600, t->net->proc_dir,
378297a32539SAlexey Dobriyan 					  &pktgen_if_proc_ops, pkt_dev);
378339df232fSStephen Hemminger 	if (!pkt_dev->entry) {
3784f9467eaeSJoe Perches 		pr_err("cannot create %s/%s procfs entry\n",
3785d50a6b56SStephen Hemminger 		       PG_PROC_DIR, ifname);
378639df232fSStephen Hemminger 		err = -EINVAL;
378739df232fSStephen Hemminger 		goto out2;
378839df232fSStephen Hemminger 	}
3789a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3790a553e4a6SJamal Hadi Salim 	pkt_dev->ipsmode = XFRM_MODE_TRANSPORT;
3791a553e4a6SJamal Hadi Salim 	pkt_dev->ipsproto = IPPROTO_ESP;
3792cf93d47eSFan Du 
3793cf93d47eSFan Du 	/* xfrm tunnel mode needs additional dst to extract outter
3794cf93d47eSFan Du 	 * ip header protocol/ttl/id field, here creat a phony one.
3795cf93d47eSFan Du 	 * instead of looking for a valid rt, which definitely hurting
3796cf93d47eSFan Du 	 * performance under such circumstance.
3797cf93d47eSFan Du 	 */
3798cf93d47eSFan Du 	pkt_dev->dstops.family = AF_INET;
3799b6ca8bd5SDavid Miller 	pkt_dev->xdst.u.dst.dev = pkt_dev->odev;
3800b6ca8bd5SDavid Miller 	dst_init_metrics(&pkt_dev->xdst.u.dst, pktgen_dst_metrics, false);
3801b6ca8bd5SDavid Miller 	pkt_dev->xdst.child = &pkt_dev->xdst.u.dst;
3802b6ca8bd5SDavid Miller 	pkt_dev->xdst.u.dst.ops = &pkt_dev->dstops;
3803a553e4a6SJamal Hadi Salim #endif
380439df232fSStephen Hemminger 
380539df232fSStephen Hemminger 	return add_dev_to_thread(t, pkt_dev);
380639df232fSStephen Hemminger out2:
3807d62607c3SJakub Kicinski 	netdev_put(pkt_dev->odev, &pkt_dev->dev_tracker);
380839df232fSStephen Hemminger out1:
3809a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3810a553e4a6SJamal Hadi Salim 	free_SAs(pkt_dev);
3811a553e4a6SJamal Hadi Salim #endif
38121da177e4SLinus Torvalds 	vfree(pkt_dev->flows);
38131da177e4SLinus Torvalds 	kfree(pkt_dev);
381439df232fSStephen Hemminger 	return err;
38151da177e4SLinus Torvalds }
38161da177e4SLinus Torvalds 
pktgen_create_thread(int cpu,struct pktgen_net * pn)38174e58a027SCong Wang static int __net_init pktgen_create_thread(int cpu, struct pktgen_net *pn)
38181da177e4SLinus Torvalds {
3819cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
3820d50a6b56SStephen Hemminger 	struct proc_dir_entry *pe;
3821ee74baa7SDavid S. Miller 	struct task_struct *p;
38221da177e4SLinus Torvalds 
38233291b9dbSEric Dumazet 	t = kzalloc_node(sizeof(struct pktgen_thread), GFP_KERNEL,
38243291b9dbSEric Dumazet 			 cpu_to_node(cpu));
38251da177e4SLinus Torvalds 	if (!t) {
3826f9467eaeSJoe Perches 		pr_err("ERROR: out of memory, can't create new thread\n");
38271da177e4SLinus Torvalds 		return -ENOMEM;
38281da177e4SLinus Torvalds 	}
38291da177e4SLinus Torvalds 
38309a0b1e8bSEric Dumazet 	mutex_init(&t->if_lock);
38311da177e4SLinus Torvalds 	t->cpu = cpu;
38321da177e4SLinus Torvalds 
3833ee74baa7SDavid S. Miller 	INIT_LIST_HEAD(&t->if_list);
3834ee74baa7SDavid S. Miller 
38354e58a027SCong Wang 	list_add_tail(&t->th_list, &pn->pktgen_threads);
3836d3ede327SDenis V. Lunev 	init_completion(&t->start_done);
3837ee74baa7SDavid S. Miller 
383894dcf29aSEric Dumazet 	p = kthread_create_on_node(pktgen_thread_worker,
383994dcf29aSEric Dumazet 				   t,
384094dcf29aSEric Dumazet 				   cpu_to_node(cpu),
384194dcf29aSEric Dumazet 				   "kpktgend_%d", cpu);
3842ee74baa7SDavid S. Miller 	if (IS_ERR(p)) {
3843355db391SLeesoo Ahn 		pr_err("kthread_create_on_node() failed for cpu %d\n", t->cpu);
3844ee74baa7SDavid S. Miller 		list_del(&t->th_list);
3845ee74baa7SDavid S. Miller 		kfree(t);
3846ee74baa7SDavid S. Miller 		return PTR_ERR(p);
3847ee74baa7SDavid S. Miller 	}
3848ee74baa7SDavid S. Miller 	kthread_bind(p, cpu);
3849ee74baa7SDavid S. Miller 	t->tsk = p;
3850ee74baa7SDavid S. Miller 
38514e58a027SCong Wang 	pe = proc_create_data(t->tsk->comm, 0600, pn->proc_dir,
385297a32539SAlexey Dobriyan 			      &pktgen_thread_proc_ops, t);
3853d50a6b56SStephen Hemminger 	if (!pe) {
3854f9467eaeSJoe Perches 		pr_err("cannot create %s/%s procfs entry\n",
3855ee74baa7SDavid S. Miller 		       PG_PROC_DIR, t->tsk->comm);
3856ee74baa7SDavid S. Miller 		kthread_stop(p);
3857ee74baa7SDavid S. Miller 		list_del(&t->th_list);
38581da177e4SLinus Torvalds 		kfree(t);
38591da177e4SLinus Torvalds 		return -EINVAL;
38601da177e4SLinus Torvalds 	}
3861d50a6b56SStephen Hemminger 
38624e58a027SCong Wang 	t->net = pn;
38631fbe4b46SOleg Nesterov 	get_task_struct(p);
3864ee74baa7SDavid S. Miller 	wake_up_process(p);
3865d3ede327SDenis V. Lunev 	wait_for_completion(&t->start_done);
38661da177e4SLinus Torvalds 
38671da177e4SLinus Torvalds 	return 0;
38681da177e4SLinus Torvalds }
38691da177e4SLinus Torvalds 
38701da177e4SLinus Torvalds /*
38711da177e4SLinus Torvalds  * Removes a device from the thread if_list.
38721da177e4SLinus Torvalds  */
_rem_dev_from_if_list(struct pktgen_thread * t,struct pktgen_dev * pkt_dev)3873222f1806SLuiz Capitulino static void _rem_dev_from_if_list(struct pktgen_thread *t,
3874222f1806SLuiz Capitulino 				  struct pktgen_dev *pkt_dev)
38751da177e4SLinus Torvalds {
3876c26a8016SLuiz Capitulino 	struct list_head *q, *n;
3877c26a8016SLuiz Capitulino 	struct pktgen_dev *p;
38781da177e4SLinus Torvalds 
38798788370aSJesper Dangaard Brouer 	if_lock(t);
3880c26a8016SLuiz Capitulino 	list_for_each_safe(q, n, &t->if_list) {
3881c26a8016SLuiz Capitulino 		p = list_entry(q, struct pktgen_dev, list);
3882c26a8016SLuiz Capitulino 		if (p == pkt_dev)
38838788370aSJesper Dangaard Brouer 			list_del_rcu(&p->list);
38841da177e4SLinus Torvalds 	}
38858788370aSJesper Dangaard Brouer 	if_unlock(t);
38861da177e4SLinus Torvalds }
38871da177e4SLinus Torvalds 
pktgen_remove_device(struct pktgen_thread * t,struct pktgen_dev * pkt_dev)3888222f1806SLuiz Capitulino static int pktgen_remove_device(struct pktgen_thread *t,
3889222f1806SLuiz Capitulino 				struct pktgen_dev *pkt_dev)
38901da177e4SLinus Torvalds {
3891f9467eaeSJoe Perches 	pr_debug("remove_device pkt_dev=%p\n", pkt_dev);
38921da177e4SLinus Torvalds 
38931da177e4SLinus Torvalds 	if (pkt_dev->running) {
3894294a0b7fSJoe Perches 		pr_warn("WARNING: trying to remove a running interface, stopping it now\n");
38951da177e4SLinus Torvalds 		pktgen_stop_device(pkt_dev);
38961da177e4SLinus Torvalds 	}
38971da177e4SLinus Torvalds 
38981da177e4SLinus Torvalds 	/* Dis-associate from the interface */
38991da177e4SLinus Torvalds 
39001da177e4SLinus Torvalds 	if (pkt_dev->odev) {
3901d62607c3SJakub Kicinski 		netdev_put(pkt_dev->odev, &pkt_dev->dev_tracker);
39021da177e4SLinus Torvalds 		pkt_dev->odev = NULL;
39031da177e4SLinus Torvalds 	}
39041da177e4SLinus Torvalds 
39058788370aSJesper Dangaard Brouer 	/* Remove proc before if_list entry, because add_device uses
39068788370aSJesper Dangaard Brouer 	 * list to determine if interface already exist, avoid race
39078788370aSJesper Dangaard Brouer 	 * with proc_create_data() */
3908a8ca16eaSDavid Howells 	proc_remove(pkt_dev->entry);
39091da177e4SLinus Torvalds 
39108788370aSJesper Dangaard Brouer 	/* And update the thread if_list */
39118788370aSJesper Dangaard Brouer 	_rem_dev_from_if_list(t, pkt_dev);
39128788370aSJesper Dangaard Brouer 
3913a553e4a6SJamal Hadi Salim #ifdef CONFIG_XFRM
3914a553e4a6SJamal Hadi Salim 	free_SAs(pkt_dev);
3915a553e4a6SJamal Hadi Salim #endif
39161da177e4SLinus Torvalds 	vfree(pkt_dev->flows);
391726ad7879SEric Dumazet 	if (pkt_dev->page)
391826ad7879SEric Dumazet 		put_page(pkt_dev->page);
39198788370aSJesper Dangaard Brouer 	kfree_rcu(pkt_dev, rcu);
39201da177e4SLinus Torvalds 	return 0;
39211da177e4SLinus Torvalds }
39221da177e4SLinus Torvalds 
pg_net_init(struct net * net)39234e58a027SCong Wang static int __net_init pg_net_init(struct net *net)
39241da177e4SLinus Torvalds {
39254e58a027SCong Wang 	struct pktgen_net *pn = net_generic(net, pg_net_id);
3926d50a6b56SStephen Hemminger 	struct proc_dir_entry *pe;
39274e58a027SCong Wang 	int cpu, ret = 0;
3928d50a6b56SStephen Hemminger 
39294e58a027SCong Wang 	pn->net = net;
39304e58a027SCong Wang 	INIT_LIST_HEAD(&pn->pktgen_threads);
39314e58a027SCong Wang 	pn->pktgen_exiting = false;
39324e58a027SCong Wang 	pn->proc_dir = proc_mkdir(PG_PROC_DIR, pn->net->proc_net);
39334e58a027SCong Wang 	if (!pn->proc_dir) {
39344e58a027SCong Wang 		pr_warn("cannot create /proc/net/%s\n", PG_PROC_DIR);
3935d50a6b56SStephen Hemminger 		return -ENODEV;
39361da177e4SLinus Torvalds 	}
393797a32539SAlexey Dobriyan 	pe = proc_create(PGCTRL, 0600, pn->proc_dir, &pktgen_proc_ops);
39384e58a027SCong Wang 	if (pe == NULL) {
39394e58a027SCong Wang 		pr_err("cannot create %s procfs entry\n", PGCTRL);
39404e58a027SCong Wang 		ret = -EINVAL;
39414e58a027SCong Wang 		goto remove;
39424e58a027SCong Wang 	}
39431da177e4SLinus Torvalds 
3944670c02c2SJohn Hawkes 	for_each_online_cpu(cpu) {
39458024bb24SLuiz Capitulino 		int err;
39461da177e4SLinus Torvalds 
39474e58a027SCong Wang 		err = pktgen_create_thread(cpu, pn);
39488024bb24SLuiz Capitulino 		if (err)
39494e58a027SCong Wang 			pr_warn("Cannot create thread for cpu %d (%d)\n",
3950f9467eaeSJoe Perches 				   cpu, err);
39511da177e4SLinus Torvalds 	}
39528024bb24SLuiz Capitulino 
39534e58a027SCong Wang 	if (list_empty(&pn->pktgen_threads)) {
39544e58a027SCong Wang 		pr_err("Initialization failed for all threads\n");
3955ce14f894SWANG Cong 		ret = -ENODEV;
39564e58a027SCong Wang 		goto remove_entry;
39578024bb24SLuiz Capitulino 	}
39588024bb24SLuiz Capitulino 
39591da177e4SLinus Torvalds 	return 0;
3960ce14f894SWANG Cong 
39614e58a027SCong Wang remove_entry:
39624e58a027SCong Wang 	remove_proc_entry(PGCTRL, pn->proc_dir);
39634e58a027SCong Wang remove:
3964ece31ffdSGao feng 	remove_proc_entry(PG_PROC_DIR, pn->net->proc_net);
3965ce14f894SWANG Cong 	return ret;
39661da177e4SLinus Torvalds }
39671da177e4SLinus Torvalds 
pg_net_exit(struct net * net)39684e58a027SCong Wang static void __net_exit pg_net_exit(struct net *net)
39691da177e4SLinus Torvalds {
39704e58a027SCong Wang 	struct pktgen_net *pn = net_generic(net, pg_net_id);
3971cdcdbe0bSLuiz Capitulino 	struct pktgen_thread *t;
3972cdcdbe0bSLuiz Capitulino 	struct list_head *q, *n;
3973d4b11335SEric Dumazet 	LIST_HEAD(list);
39741da177e4SLinus Torvalds 
39751da177e4SLinus Torvalds 	/* Stop all interfaces & threads */
39764e58a027SCong Wang 	pn->pktgen_exiting = true;
39771da177e4SLinus Torvalds 
3978c57b5468SEric Dumazet 	mutex_lock(&pktgen_thread_lock);
39794e58a027SCong Wang 	list_splice_init(&pn->pktgen_threads, &list);
3980c57b5468SEric Dumazet 	mutex_unlock(&pktgen_thread_lock);
3981c57b5468SEric Dumazet 
3982c57b5468SEric Dumazet 	list_for_each_safe(q, n, &list) {
3983cdcdbe0bSLuiz Capitulino 		t = list_entry(q, struct pktgen_thread, th_list);
3984c57b5468SEric Dumazet 		list_del(&t->th_list);
3985*a9da6ddaSAndreas Gruenbacher 		kthread_stop_put(t->tsk);
3986ee74baa7SDavid S. Miller 		kfree(t);
39871da177e4SLinus Torvalds 	}
39881da177e4SLinus Torvalds 
39894e58a027SCong Wang 	remove_proc_entry(PGCTRL, pn->proc_dir);
3990ece31ffdSGao feng 	remove_proc_entry(PG_PROC_DIR, pn->net->proc_net);
39914e58a027SCong Wang }
39921da177e4SLinus Torvalds 
39934e58a027SCong Wang static struct pernet_operations pg_net_ops = {
39944e58a027SCong Wang 	.init = pg_net_init,
39954e58a027SCong Wang 	.exit = pg_net_exit,
39964e58a027SCong Wang 	.id   = &pg_net_id,
39974e58a027SCong Wang 	.size = sizeof(struct pktgen_net),
39984e58a027SCong Wang };
39994e58a027SCong Wang 
pg_init(void)40004e58a027SCong Wang static int __init pg_init(void)
40014e58a027SCong Wang {
40024e58a027SCong Wang 	int ret = 0;
40034e58a027SCong Wang 
40044e58a027SCong Wang 	pr_info("%s", version);
40054e58a027SCong Wang 	ret = register_pernet_subsys(&pg_net_ops);
40064e58a027SCong Wang 	if (ret)
40074e58a027SCong Wang 		return ret;
40084e58a027SCong Wang 	ret = register_netdevice_notifier(&pktgen_notifier_block);
40094e58a027SCong Wang 	if (ret)
40104e58a027SCong Wang 		unregister_pernet_subsys(&pg_net_ops);
40114e58a027SCong Wang 
40124e58a027SCong Wang 	return ret;
40134e58a027SCong Wang }
40144e58a027SCong Wang 
pg_cleanup(void)40154e58a027SCong Wang static void __exit pg_cleanup(void)
40164e58a027SCong Wang {
40174e58a027SCong Wang 	unregister_netdevice_notifier(&pktgen_notifier_block);
40184e58a027SCong Wang 	unregister_pernet_subsys(&pg_net_ops);
40198788370aSJesper Dangaard Brouer 	/* Don't need rcu_barrier() due to use of kfree_rcu() */
40201da177e4SLinus Torvalds }
40211da177e4SLinus Torvalds 
40221da177e4SLinus Torvalds module_init(pg_init);
40231da177e4SLinus Torvalds module_exit(pg_cleanup);
40241da177e4SLinus Torvalds 
4025c3d2f52dSStephen Hemminger MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se>");
40261da177e4SLinus Torvalds MODULE_DESCRIPTION("Packet Generator tool");
40271da177e4SLinus Torvalds MODULE_LICENSE("GPL");
4028c3d2f52dSStephen Hemminger MODULE_VERSION(VERSION);
40291da177e4SLinus Torvalds module_param(pg_count_d, int, 0);
4030c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_count_d, "Default number of packets to inject");
40311da177e4SLinus Torvalds module_param(pg_delay_d, int, 0);
4032c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_delay_d, "Default delay between packets (nanoseconds)");
40331da177e4SLinus Torvalds module_param(pg_clone_skb_d, int, 0);
4034c3d2f52dSStephen Hemminger MODULE_PARM_DESC(pg_clone_skb_d, "Default number of copies of the same packet");
40351da177e4SLinus Torvalds module_param(debug, int, 0);
4036c3d2f52dSStephen Hemminger MODULE_PARM_DESC(debug, "Enable debugging of pktgen module");
4037