Netdev List
 help / color / mirror / Atom feed
* on bnx2x firmware...
From: Maciej Żenczykowski @ 2011-11-02  1:01 UTC (permalink / raw)
  To: Linux NetDev

((v3.1))$ cat drivers/net/bnx2x/bnx2x_hsi.h | egrep BCM_5710_FW_

#define BCM_5710_FW_MAJOR_VERSION                       7
#define BCM_5710_FW_MINOR_VERSION                       0
#define BCM_5710_FW_REVISION_VERSION            23
#define BCM_5710_FW_ENGINEERING_VERSION         0

So the desired firmware is version 7.0.23.0

((v3.1))$ find | egrep bnx2x | egrep fw
./firmware/bnx2x/bnx2x-e1-6.2.9.0.fw.ihex
./firmware/bnx2x/bnx2x-e2-6.2.9.0.fw.ihex
./firmware/bnx2x/bnx2x-e1h-6.2.9.0.fw.ihex
./drivers/net/bnx2x/bnx2x_fw_defs.h
./drivers/net/bnx2x/bnx2x_fw_file_hdr.h

So it doesn't look like the in-tree fw matches the bnx2x driver in
released v3.1.
I can find this firmware in the firmware repository.

My questions are:
 - is this a bug (and the firmware repo will be merged into a stable
3.1 update),
 - is this a feature, and the 6.2.9.0 firmwares will be removed from the tree?

I guess I'm just not clear on what the current firmware policy is, we
seem to be in some weird half-state.

Thanks,
Maciej.

^ permalink raw reply

* On IP_FREEBIND and IPv6...
From: Maciej Żenczykowski @ 2011-11-02  0:57 UTC (permalink / raw)
  To: Linux NetDev

Short summary:
  IPV6 + IP_FREEBIND doesn't work the way IPV4 + IP_FREEBIND does.
  The native IPv6 bind path ignores 'freebind', but honours 'transparent'.
  The native and dual-stack IPv4 bind paths honour both.

Does anyone know if this was a (security?) feature?  Or is this just a bug?

I'll follow this up with a patch to support freebind for v6 bind (and
another one for v6 udp sendmsg).
Unless I hear some compelling story about why stuff is the way it is.

---

Please find test program source later on.

It basically does:
   for test_mode in {native_ipv4, ipv4_on_ipv6_socket, native_ipv6} do:
       create a udp socket
       set IP_FREEBIND=1
       set IP_TRANSPARENT=1 (will fail if not root, ignore failure)
       bind socket to an IP address we don't own (one of: 1.2.3.4,
::FFFF:1.2.3.4, 2001:4860:DEAD:CAFE::6006:13) [fails without root for
native ipv6]
       send a packet to another IP address (one of: 5.6.7.8,
::FFFF:5.6.7.8, 2001:4860:DEAD:BEEF::6006:13)

Running it generates:

$ ./test
setsockopt(TRANSPARENT=1): Operation not permitted [requires root]
setsockopt(TRANSPARENT=1): Operation not permitted [requires root]
setsockopt(TRANSPARENT=1): Operation not permitted [requires root]
bind(): Cannot assign requested address [native ipv6 bind does not
honour IP_FREEBIND, does honour IP{,V6}_TRANSPARENT]
$ sudo ./test
<no errors, everything succeeds, including bind native ipv6>

While running tcpdump shows:
# tcpdump -s 1555 -n -nn -i eth0 port 11111 or port 22222

>From ./a [ie. with IP_FREEBIND=1, IP_TRANSPARENT=0]:

IP 1.2.3.4.11111 > 5.6.7.8.22222: UDP, length 6 [native IPv4]
IP 1.2.3.4.11111 > 5.6.7.8.22222: UDP, length 6 [dual stack IPv4 on IPv6 socket]
IP6 [machines_true_ipv6_address].51912 >
2001:4860:dead:beef::6006:13.22222: UDP, length 6 [native IPv6, wrong
source since bind failed]

>From sudo ./a [ie. with IP_FREEBIND=1, IP_TRANSPARENT=1]:

IP 1.2.3.4.11111 > 5.6.7.8.22222: UDP, length 6 [native IPv4]
IP 1.2.3.4.11111 > 5.6.7.8.22222: UDP, length 6 [dual stack IPv4 on IPv6 socket]
IP6 2001:4860:dead:cafe::6006:13.vce >
2001:4860:dead:beef::6006:13.22222: UDP, length 6 [native IPv6]

This seems to prove that IP_TRANSPARENT requires root - this is as
expected, while IP_FREEBIND does not require root - again as expected.
However, as apparent above, we are successfully spoofing outgoing
source address on IPv4 UDP (whether native IPv4 or dual-stack IPv4
doesn't matter),
but there doesn't seem to be a way to do this with native IPv6.

ie. the native IPv6 bind path ignores the "freebind" setting, but does
honour the "transparent", while the IPv4 code paths honour both.

- Maciej

---
#include <string.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#define NATIVE_IPv4 0
#define DUAL_STACK  1
#define NATIVE_IPv6 2

int main(int argc, char const * argv[], char const * envp[]) {
  struct sockaddr_in saddr4, daddr4;
  struct sockaddr_in6 saddr6, daddr6;
  int fd, rv, v, mode;

  for (mode = 0; mode <= 2; ++mode) {

    if (mode == NATIVE_IPv4) {
      fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if (fd < 0) perror("socket(IPv4 UDP)");
    } else {
      fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
      if (fd < 0) perror("socket(IPv6 UDP)");
    }

    v = 1;
    rv = setsockopt(fd, SOL_IP, IP_FREEBIND, &v, sizeof(v));
    if (rv < 0) perror("setsockopt(FREEBIND=1)");

    v = 1;
    rv = setsockopt(fd, SOL_IP, IP_TRANSPARENT, &v, sizeof(v));
    if (rv < 0) perror("setsockopt(TRANSPARENT=1)");

    if (mode == NATIVE_IPv4) {
      memset(&saddr4, 0, sizeof(saddr4));
      memset(&daddr4, 0, sizeof(daddr4));
      saddr4.sin_family = AF_INET;
      daddr4.sin_family = AF_INET;
      saddr4.sin_port = htons(11111);
      daddr4.sin_port = htons(22222);
      inet_pton(AF_INET, "1.2.3.4", &saddr4.sin_addr.s_addr);
      inet_pton(AF_INET, "5.6.7.8", &daddr4.sin_addr.s_addr);

      rv = bind(fd, (struct sockaddr const *)&saddr4, sizeof(saddr4));
      if (rv < 0) perror("bind()");

      rv = sendto(fd, "Hello!", 6, 0, (struct sockaddr const
*)&daddr4, sizeof(daddr4));
      if (rv < 0) perror("write");
    } else {
      memset(&saddr6, 0, sizeof(saddr6));
      memset(&daddr6, 0, sizeof(daddr6));
      saddr6.sin6_family = AF_INET6;
      daddr6.sin6_family = AF_INET6;
      saddr6.sin6_port = htons(11111);
      daddr6.sin6_port = htons(22222);
      //saddr6.sin6_flowinfo = 0;
      //daddr6.sin6_flowinfo = 0;
      if (mode == DUAL_STACK) {
        inet_pton(AF_INET6, "::FFFF:1.2.3.4", &saddr6.sin6_addr);
        inet_pton(AF_INET6, "::FFFF:5.6.7.8", &daddr6.sin6_addr);
      } else {
        inet_pton(AF_INET6, "2001:4860:DEAD:CAFE::6006:0013",
&saddr6.sin6_addr);
        inet_pton(AF_INET6, "2001:4860:DEAD:BEEF::6006:0013",
&daddr6.sin6_addr);
      }
      //saddr6.sin6_scope_id = 0;
      //daddr6.sin6_scope_id = 0;

      rv = bind(fd, (struct sockaddr const *)&saddr6, sizeof(saddr6));
      if (rv < 0) perror("bind()");

      rv = sendto(fd, "Hello!", 6, 0, (struct sockaddr const
*)&daddr6, sizeof(daddr6));
      if (rv < 0) perror("write");
    }

    rv = close(fd);
    if (rv < 0) perror("close");
  }

  return 0;
}

^ permalink raw reply

* Re: [PATCH] ipv4: fix ipsec forward performance regression
From: David Miller @ 2011-11-02  0:34 UTC (permalink / raw)
  To: kim.phillips; +Cc: stable, eric.dumazet, zheng.z.yan, netdev
In-Reply-To: <20111101185022.b156ea957baf286f1bf00f9c@freescale.com>

From: Kim Phillips <kim.phillips@freescale.com>
Date: Tue, 1 Nov 2011 18:50:22 -0500

> -stable maintainers, please consider the following two upstream
> commits for inclusion in upcoming v3.0.x [1] stable releases:

I submit networking stable fixes as needed, so please make such
requests to me.

I haven't submitted this change yet because I want it to sit a bit
longer in Linus's tree to make sure there aren't any unwanted
side-effects.

^ permalink raw reply

* RE: [PATCH] flexcan: Fix CAN_RAW_RECV_OWN_MSGS and CAN_RAW_LOOPBACK
From: Reuben Dowle @ 2011-11-02  0:25 UTC (permalink / raw)
  To: Marc Kleine-Budde; +Cc: netdev, linux-can
In-Reply-To: <4EAF21A6.702@pengutronix.de>

> 
> Patch looks quite good. Can you please wrap the description to about 72
> chars?

Will do.

> >  	}
> >
> > @@ -670,6 +671,8 @@ static int flexcan_chip_start(struct net_device
> *dev)
> >  	int err;
> >  	u32 reg_mcr, reg_ctrl;
> >
> > +	can_free_echo_skb(dev, 0);
> 
> what about putting this to flexcan_chip_stop? Otherwise you risk a
> memleak if you do "ifconfig down; rmmod flexcan"

I originally thought this needs to be in flexcan_chip_start, so the bus off followed by restart sequence will drop the loopback packet. I investigated a bit deeper and can_flush_echo_skb is called in can_restart(). So my call of that function is not really needed. can_flush_echo_skb is also called in close_candev, which is called by the flexcan driver. So I actually think this function does not need to be called by the driver at all. What do you think?

> >
> >  	dev->netdev_ops = &flexcan_netdev_ops;
> >  	dev->irq = irq;
> > -	dev->flags |= IFF_ECHO; /* we support local echo in hardware */
> > +	
> > +	/* Driver supports local echo.
> > +	 * We support local echo in hardware, however this is not used
> because
> > +	 * hardware local echo loses the sending socket reference
> > +	 * (thus CAN_RAW_RECV_OWN_MSGS and CAN_RAW_LOOPBACK socket
> options
> > +	 *  would not work)
> > +	 */
> 
> IMHO, you can skip this comment. The patch description is good enough.

Ok, I will drop the extra comments from the patch.

^ permalink raw reply

* Re: >Re: [RFC] should VM_BUG_ON(cond) really evaluate cond
From: Eric Dumazet @ 2011-11-02  0:14 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Andi Kleen, Ben Hutchings, linux-kernel, netdev, Andrew Morton
In-Reply-To: <CA+55aFws7bm1mr7fhAheD2t3wTchWx_gEbKhSggSROf2UT86Hg@mail.gmail.com>

Le dimanche 30 octobre 2011 à 11:09 -0700, Linus Torvalds a écrit :

> Argh. Ok. Testing a refcount in a const struct doesn't make much
> sense, but there does seem to be perfectly valid uses of it
> (sk_wmem_alloc etc).
> 
> Annoying. I guess we have to have those casts. Grr.
> 

OK, please check following patch then.

Thanks !

[PATCH v3] atomic: introduce ACCESS_AT_MOST_ONCE() helper

In commit 4e60c86bd9e (gcc-4.6: mm: fix unused but set warnings)
Andi forced VM_BUG_ON(cond) to evaluate cond, even if CONFIG_DEBUG_VM is
not set :

#ifdef CONFIG_DEBUG_VM
#define VM_BUG_ON(cond) BUG_ON(cond)
#else
#define VM_BUG_ON(cond) do { (void)(cond); } while (0)
#endif

As a side effect, get_page()/put_page_testzero() are performing more bus
transactions on contended cache line on some workloads (tcp_sendmsg()
for example, where a page is acting as a shared buffer)

0,05 :  ffffffff815e4775:    je     ffffffff815e4970 <tcp_sendmsg+0xc80>
0,05 :  ffffffff815e477b:    mov    0x1c(%r9),%eax    // useless
3,32 :  ffffffff815e477f:    mov    (%r9),%rax        // useless
0,51 :  ffffffff815e4782:    lock incl 0x1c(%r9)
3,87 :  ffffffff815e4787:    mov    (%r9),%rax
0,00 :  ffffffff815e478a:    test   $0x80,%ah
0,00 :  ffffffff815e478d:    jne    ffffffff815e49f2 <tcp_sendmsg+0xd02>

Thats because both atomic_read() and constant_test_bit() use a volatile
attribute and thus compiler is forced to perform a read, even if the
result is optimized away.

Linus suggested using an asm("") trick and place it in a variant of
ACCESS_ONCE(), allowing compiler to omit reading memory if result is
unused.

This patch introduces ACCESS_AT_MOST_ONCE() helper and use it in the x86
implementation of atomic_read() and constant_test_bit()

It's also used on x86_64 atomic64_read() implementation.

on x86_64, we thus reduce vmlinux text a bit (if CONFIG_DEBUG_VM=n)

# size vmlinux.old vmlinux.new
   text    data     bss     dec     hex filename
10706848        2894216 1540096 15141160         e70928 vmlinux.old
10704040        2894216 1540096 15138352         e6fe30 vmlinux.new

Basically an extension of a prior patch from Linus

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
 arch/x86/include/asm/atomic.h      |    5 +----
 arch/x86/include/asm/atomic64_64.h |    6 ++----
 arch/x86/include/asm/bitops.h      |    6 ++++--
 include/asm-generic/atomic.h       |    2 +-
 include/linux/compiler.h           |   10 ++++++++++
 5 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h
index 58cb6d4..2581008 100644
--- a/arch/x86/include/asm/atomic.h
+++ b/arch/x86/include/asm/atomic.h
@@ -20,10 +20,7 @@
  *
  * Atomically reads the value of @v.
  */
-static inline int atomic_read(const atomic_t *v)
-{
-	return (*(volatile int *)&(v)->counter);
-}
+#define atomic_read(v) ACCESS_AT_MOST_ONCE(*(int *)&(v)->counter)
 
 /**
  * atomic_set - set atomic variable
diff --git a/arch/x86/include/asm/atomic64_64.h b/arch/x86/include/asm/atomic64_64.h
index 0e1cbfc..15bbad4 100644
--- a/arch/x86/include/asm/atomic64_64.h
+++ b/arch/x86/include/asm/atomic64_64.h
@@ -1,6 +1,7 @@
 #ifndef _ASM_X86_ATOMIC64_64_H
 #define _ASM_X86_ATOMIC64_64_H
 
+#include <linux/compiler.h>
 #include <linux/types.h>
 #include <asm/alternative.h>
 #include <asm/cmpxchg.h>
@@ -16,10 +17,7 @@
  * Atomically reads the value of @v.
  * Doesn't imply a read memory barrier.
  */
-static inline long atomic64_read(const atomic64_t *v)
-{
-	return (*(volatile long *)&(v)->counter);
-}
+#define atomic64_read(v) ACCESS_AT_MOST_ONCE(*(long *)&(v)->counter);
 
 /**
  * atomic64_set - set atomic64 variable
diff --git a/arch/x86/include/asm/bitops.h b/arch/x86/include/asm/bitops.h
index 1775d6e..6dcf4b1 100644
--- a/arch/x86/include/asm/bitops.h
+++ b/arch/x86/include/asm/bitops.h
@@ -308,8 +308,10 @@ static inline int test_and_change_bit(int nr, volatile unsigned long *addr)
 
 static __always_inline int constant_test_bit(unsigned int nr, const volatile unsigned long *addr)
 {
-	return ((1UL << (nr % BITS_PER_LONG)) &
-		(addr[nr / BITS_PER_LONG])) != 0;
+	unsigned long *word = (unsigned long *)addr + (nr / BITS_PER_LONG);
+	unsigned long bit = 1UL << (nr % BITS_PER_LONG);
+
+	return (bit & ACCESS_AT_MOST_ONCE(*word)) != 0;
 }
 
 static inline int variable_test_bit(int nr, volatile const unsigned long *addr)
diff --git a/include/asm-generic/atomic.h b/include/asm-generic/atomic.h
index e37963c..76ab683 100644
--- a/include/asm-generic/atomic.h
+++ b/include/asm-generic/atomic.h
@@ -39,7 +39,7 @@
  * Atomically reads the value of @v.
  */
 #ifndef atomic_read
-#define atomic_read(v)	(*(volatile int *)&(v)->counter)
+#define atomic_read(v)	ACCESS_AT_MOST_ONCE(*(int *)&(v)->counter)
 #endif
 
 /**
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 320d6c9..307f342 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -308,4 +308,14 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
  */
 #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
 
+/*
+ * Like ACCESS_ONCE, but can be optimized away if nothing uses the value,
+ * and/or merged with previous non-ONCE accesses.
+ */
+#define ACCESS_AT_MOST_ONCE(x)			\
+	({	typeof(x) __y;			\
+		asm("":"=r" (__y):"0" (x));	\
+		__y;				\
+	})
+
 #endif /* __LINUX_COMPILER_H */

^ permalink raw reply related

* Re: PROBLEM: pppol2tp over pppoe NULL pointer dereference
From: Eric Dumazet @ 2011-11-01 23:58 UTC (permalink / raw)
  To: Misha Labjuk, David Miller; +Cc: netdev
In-Reply-To: <CAKpUy7Z5KOfHbKfBp-qg+77cED=OxUAkm2P4pQacHaHKmKBQpw@mail.gmail.com>

Le mercredi 02 novembre 2011 à 01:00 +0300, Misha Labjuk a écrit :
> pppol2tp over pppoe NULL pointer dereference
> 
> Kernel panic after establishing pppol2tp tunnel over pppoe connection.
> Get panic in 5-15 min with 10 mbit/s data transfer speed.
> pppoe and pppol2tp connections stable separately.
> 
> Linux version 3.1.0 (user@host) (gcc version 4.6.1 (Gentoo 4.6.1-r1
> p1.0, pie-0.4.5) ) #1 SMP Mon Oct 31 18:48:18 MSK 2011
> 
> [  151.913193] L2TP core driver, V2.0
> [  151.974584] L2TP netlink interface
> [  151.993803] PPPoL2TP kernel driver, V2.0
> [  437.496670] BUG: unable to handle kernel NULL pointer dereference
> at 0000000000000008
> [  437.496683] IP: [<ffffffffa03679dc>] l2tp_recv_common+0x4d3/0x621 [l2tp_core]
> [  437.496691] PGD d7840067 PUD cd4e7067 PMD 0
> [  437.496697] Oops: 0002 [#1] SMP
> [  437.496702] CPU 0
> [  437.496704] Modules linked in: l2tp_ppp l2tp_netlink l2tp_core
> firewire_sbp2 sit tunnel4 netconsole it87 hwmon_vid coretemp pppoe
> pppox ppp_generic slhc ipt_MASQUERADE iptable_nat nf_nat
> nf_conntrack_ipv4 nf_conntrack nf_defrag_ipv4 xt_TCPMSS iptable_mangle
> ip_tables snd_seq_midi snd_emu10k1_synth snd_emux_synth
> snd_seq_virmidi snd_seq_midi_emul snd_seq_dummy snd_seq_oss
> snd_seq_midi_event snd_seq snd_pcm_oss snd_mixer_oss nfsd lockd
> nfs_acl auth_rpcgss sunrpc usb_storage usb_libusual uas usbhid ipv6
> snd_emu10k1 8250_pnp snd_rawmidi snd_hda_codec_realtek snd_ac97_codec
> snd_hda_intel snd_hda_codec uhci_hcd ac97_bus snd_pcm ehci_hcd usbcore
> snd_seq_device snd_timer 8250 snd_util_mem snd_hwdep psmouse snd
> firewire_ohci firewire_core serial_core intel_agp intel_gtt pcspkr
> soundcore r8169 crc_itu_t mii snd_page_alloc processor button
> [  437.497005]
> [  437.497005] Pid: 3274, comm: qbittorrent Not tainted 3.1.0 #1
> Gigabyte Technology Co., Ltd. EP45-EXTREME/EP45-EXTREME
> [  437.497005] RIP: 0010:[<ffffffffa03679dc>]  [<ffffffffa03679dc>]
> l2tp_recv_common+0x4d3/0x621 [l2tp_core]
> [  437.497005] RSP: 0000:ffff88011fc03b90  EFLAGS: 00010296
> [  437.497005] RAX: 0000000000000000 RBX: ffff8800d79e8200 RCX: ffff88011fc10bd0
> [  437.497005] RDX: 0000000000000000 RSI: 0000000000004002 RDI: ffff8800d79e8254
> [  437.497005] RBP: ffff88011fc03be0 R08: 0000000000004002 R09: 0000000000004002
> [  437.497005] R10: ffff8801091ec87a R11: ffff88011b300000 R12: ffff880118922300
> [  437.497005] R13: 0000000000000000 R14: ffff8800d79e8254 R15: ffff8800d79e826c
> [  437.497005] FS:  00007f0ced3e8700(0000) GS:ffff88011fc00000(0000)
> knlGS:0000000000000000
> [  437.497005] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [  437.497005] CR2: 0000000000000008 CR3: 00000000c8811000 CR4: 00000000000406f0
> [  437.497005] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> [  437.497005] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> [  437.497005] Process qbittorrent (pid: 3274, threadinfo
> ffff8800c9906000, task ffff8800db999200)
> [  437.497005] Stack:
> [  437.497005]  ffff88011fc03bb0 ffff8801091ec872 ffff8800d79e8240
> 0000052000000520
> [  437.497005]  ffff88011fc03be0 ffff8800d7844e00 ffff880119099200
> ffff8801091ec872
> [  437.497005]  ffff8800db8b6400 00000000000050cd ffff88011fc03c60
> ffffffffa0367e65
> [  437.497005] Call Trace:
> [  437.497005]  <IRQ>
> [  437.497005]  [<ffffffffa0367e65>] l2tp_udp_encap_recv+0x33b/0x3e6 [l2tp_core]
> [  437.497005]  [<ffffffffa0377d96>] ? pppol2tp_setsockopt+0x2e0/0x2e0
> [l2tp_ppp]
> [  437.497005]  [<ffffffffa030640c>] ? ipv4_confirm+0x17e/0x198
> [nf_conntrack_ipv4]
> [  437.497005]  [<ffffffffa0377d96>] ? pppol2tp_setsockopt+0x2e0/0x2e0
> [l2tp_ppp]
> [  437.497005]  [<ffffffff812eb32b>] udp_queue_rcv_skb+0xee/0x2ce
> [  437.497005]  [<ffffffff812ebba3>] __udp4_lib_rcv+0x2d2/0x536
> [  437.497005]  [<ffffffff812cb046>] ? ip_rcv_finish+0x29a/0x29a
> [  437.497005]  [<ffffffff812ebe1c>] udp_rcv+0x15/0x17
> [  437.497005]  [<ffffffff812cb165>] ip_local_deliver_finish+0x11f/0x1c7
> [  437.497005]  [<ffffffff812cb361>] ip_local_deliver+0x75/0x7c
> [  437.497005]  [<ffffffff812cb023>] ip_rcv_finish+0x277/0x29a
> [  437.497005]  [<ffffffff812cb5a1>] ip_rcv+0x239/0x260
> [  437.497005]  [<ffffffff812a46cd>] ? napi_skb_finish+0x21/0x38
> [  437.497005]  [<ffffffff812a3adb>] __netif_receive_skb+0x430/0x462
> [  437.497005]  [<ffffffff8102342f>] ? update_curr+0x53/0x89
> [  437.497005]  [<ffffffff812a3b9d>] process_backlog+0x90/0x151
> [  437.497005]  [<ffffffff812a3e6a>] net_rx_action+0x9e/0x171
> [  437.497005]  [<ffffffff810344b0>] __do_softirq+0x93/0x129
> [  437.497005]  [<ffffffff8133034c>] call_softirq+0x1c/0x30
> [  437.497005]  [<ffffffff8100351c>] do_softirq+0x33/0x6b
> [  437.497005]  [<ffffffff8103470b>] irq_exit+0x52/0xac
> [  437.497005]  [<ffffffff81003251>] do_IRQ+0x98/0xaf
> [  437.497005]  [<ffffffff8132eb6b>] common_interrupt+0x6b/0x6b
> [  437.497005]  <EOI>
> [  437.497005]  [<ffffffff8132f17b>] ? system_call_fastpath+0x16/0x1b
> [  437.497005] Code: 6c e8 57 03 fc e0 e9 22 01 00 00 ff 4b 50 4c 89
> f7 49 8b 14 24 49 c7 04 24 00 00 00 00 49 8b 44 24 08 49 c7 44 24 08
> 00 00 00 00
> [  437.497005]  89 42 08 48 89 10 e8 1d 6f fc e0 41 0f b7 54 24 3e 48 8b 43
> [  437.497005] RIP  [<ffffffffa03679dc>] l2tp_recv_common+0x4d3/0x621
> [l2tp_core]
> [  437.497005]  RSP <ffff88011fc03b90>
> [  437.497005] CR2: 0000000000000008
> [  437.498126] ---[ end trace 053df4c7c6743d26 ]---
> [  437.498184] Kernel panic - not syncing: Fatal exception in interrupt
> [  437.498187] Pid: 3274, comm: qbittorrent Tainted: G      D     3.1.0 #1
> [  437.498189] Call Trace:
> [  437.498190]  <IRQ>  [<ffffffff81327c11>] panic+0x8c/0x189
> [  437.498197]  [<ffffffff8100471d>] oops_end+0x81/0x8e
> [  437.498200]  [<ffffffff8132765b>] no_context+0x1fe/0x20d
> [  437.498203]  [<ffffffff81327829>] __bad_area_nosemaphore+0x1bf/0x1e0
> [  437.498206]  [<ffffffff812a5308>] ? dev_hard_start_xmit+0x412/0x51b
> [  437.498210]  [<ffffffff81327858>] bad_area_nosemaphore+0xe/0x10
> [  437.498213]  [<ffffffff8101c858>] do_page_fault+0x175/0x371
> [  437.498217]  [<ffffffff812a1eba>] ? netif_rx+0xc5/0xd0
> [  437.498281]  [<ffffffffa031ee7d>] ?
> ppp_receive_nonmp_frame+0x58f/0x5cf [ppp_generic]
> [  437.498286]  [<ffffffffa0320625>] ? ppp_receive_frame+0x5c1/0x5e2
> [ppp_generic]
> [  437.498290]  [<ffffffff8132ed6f>] page_fault+0x1f/0x30
> [  437.498293]  [<ffffffffa03679dc>] ? l2tp_recv_common+0x4d3/0x621 [l2tp_core]
> [  437.498298]  [<ffffffffa0367e65>] l2tp_udp_encap_recv+0x33b/0x3e6 [l2tp_core]
> [  437.498302]  [<ffffffffa0377d96>] ? pppol2tp_setsockopt+0x2e0/0x2e0
> [l2tp_ppp]
> [  437.498306]  [<ffffffffa030640c>] ? ipv4_confirm+0x17e/0x198
> [nf_conntrack_ipv4]
> [  437.498310]  [<ffffffffa0377d96>] ? pppol2tp_setsockopt+0x2e0/0x2e0
> [l2tp_ppp]
> [  437.498314]  [<ffffffff812eb32b>] udp_queue_rcv_skb+0xee/0x2ce
> [  437.498317]  [<ffffffff812ebba3>] __udp4_lib_rcv+0x2d2/0x536
> [  437.498321]  [<ffffffff812cb046>] ? ip_rcv_finish+0x29a/0x29a
> [  437.498324]  [<ffffffff812ebe1c>] udp_rcv+0x15/0x17
> [  437.498328]  [<ffffffff812cb165>] ip_local_deliver_finish+0x11f/0x1c7
> [  437.498332]  [<ffffffff812cb361>] ip_local_deliver+0x75/0x7c
> [  437.498391]  [<ffffffff812cb023>] ip_rcv_finish+0x277/0x29a
> [  437.498394]  [<ffffffff812cb5a1>] ip_rcv+0x239/0x260
> [  437.498398]  [<ffffffff812a46cd>] ? napi_skb_finish+0x21/0x38
> [  437.498401]  [<ffffffff812a3adb>] __netif_receive_skb+0x430/0x462
> [  437.498404]  [<ffffffff8102342f>] ? update_curr+0x53/0x89
> [  437.498408]  [<ffffffff812a3b9d>] process_backlog+0x90/0x151
> 
> 
> Software:
> Gnu C                  4.6.1
> Gnu make            3.82
> binutils                 2.21.1
> openl2tp               1.8-r3
> 
> l2tp_recv_common+0x4d3/0x621 is match to
> net/l2tp/l2tp_core.c:429:__skb_unlink(skb, &session->reorder_q);
> skb->next is NULL.

Please try following patch, thanks !

[PATCH] l2tp: handle fragmented skbs in receive path

Modern drivers provide skb with fragments, and L2TP doesnt properly
handles them.

Some bad frames can also trigger panics because of insufficent checks.

Reported-by: Misha Labjuk <spiked.yar@gmail.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
 net/l2tp/l2tp_core.c |   71 +++++++++++++++++++++++------------------
 net/l2tp/l2tp_core.h |    2 -
 net/l2tp/l2tp_ip.c   |   22 +++++-------
 3 files changed, 50 insertions(+), 45 deletions(-)

diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 34b2dde..e04d3a3 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -525,11 +525,10 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk,
  * after the session-id.
  */
 void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
-		      unsigned char *ptr, unsigned char *optr, u16 hdrflags,
+		      int offset, u16 hdrflags,
 		      int length, int (*payload_hook)(struct sk_buff *skb))
 {
 	struct l2tp_tunnel *tunnel = session->tunnel;
-	int offset;
 	u32 ns, nr;
 
 	/* The ref count is increased since we now hold a pointer to
@@ -542,14 +541,17 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
 
 	/* Parse and check optional cookie */
 	if (session->peer_cookie_len > 0) {
-		if (memcmp(ptr, &session->peer_cookie[0], session->peer_cookie_len)) {
+		if (!pskb_may_pull(skb, offset + session->peer_cookie_len) ||
+		    memcmp(skb->data + offset,
+			   &session->peer_cookie[0],
+			   session->peer_cookie_len)) {
 			PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_INFO,
 			       "%s: cookie mismatch (%u/%u). Discarding.\n",
 			       tunnel->name, tunnel->tunnel_id, session->session_id);
 			session->stats.rx_cookie_discards++;
 			goto discard;
 		}
-		ptr += session->peer_cookie_len;
+		offset += session->peer_cookie_len;
 	}
 
 	/* Handle the optional sequence numbers. Sequence numbers are
@@ -563,10 +565,12 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
 	L2TP_SKB_CB(skb)->has_seq = 0;
 	if (tunnel->version == L2TP_HDR_VER_2) {
 		if (hdrflags & L2TP_HDRFLAG_S) {
-			ns = ntohs(*(__be16 *) ptr);
-			ptr += 2;
-			nr = ntohs(*(__be16 *) ptr);
-			ptr += 2;
+			if (!pskb_may_pull(skb, offset + 4))
+				goto discard;
+			ns = ntohs(*(__be16 *) (skb->data + offset));
+			offset += 2;
+			nr = ntohs(*(__be16 *) (skb->data + offset));
+			offset += 2;
 
 			/* Store L2TP info in the skb */
 			L2TP_SKB_CB(skb)->ns = ns;
@@ -577,8 +581,11 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
 			       session->name, ns, nr, session->nr);
 		}
 	} else if (session->l2specific_type == L2TP_L2SPECTYPE_DEFAULT) {
-		u32 l2h = ntohl(*(__be32 *) ptr);
+		u32 l2h;
 
+		if (!pskb_may_pull(skb, offset + 4))
+			goto discard;
+		l2h = ntohl(*(__be32 *) (skb->data + offset));
 		if (l2h & 0x40000000) {
 			ns = l2h & 0x00ffffff;
 
@@ -593,7 +600,7 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
 	}
 
 	/* Advance past L2-specific header, if present */
-	ptr += session->l2specific_len;
+	offset += session->l2specific_len;
 
 	if (L2TP_SKB_CB(skb)->has_seq) {
 		/* Received a packet with sequence numbers. If we're the LNS,
@@ -647,13 +654,13 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
 	if (tunnel->version == L2TP_HDR_VER_2) {
 		/* If offset bit set, skip it. */
 		if (hdrflags & L2TP_HDRFLAG_O) {
-			offset = ntohs(*(__be16 *)ptr);
-			ptr += 2 + offset;
+			if (!pskb_may_pull(skb, offset + 2))
+				goto discard;
+			offset += 2 + ntohs(*(__be16 *)(skb->data + offset));
 		}
 	} else
-		ptr += session->offset;
+		offset += session->offset;
 
-	offset = ptr - optr;
 	if (!pskb_may_pull(skb, offset))
 		goto discard;
 
@@ -735,10 +742,10 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
 			      int (*payload_hook)(struct sk_buff *skb))
 {
 	struct l2tp_session *session = NULL;
-	unsigned char *ptr, *optr;
+	unsigned char *ptr;
 	u16 hdrflags;
 	u32 tunnel_id, session_id;
-	int offset;
+	int hlen;
 	u16 version;
 	int length;
 
@@ -756,20 +763,22 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
 	}
 
 	/* Point to L2TP header */
-	optr = ptr = skb->data;
+	ptr = skb->data;
 
 	/* Trace packet contents, if enabled */
 	if (tunnel->debug & L2TP_MSG_DATA) {
+		int i;
+
 		length = min(32u, skb->len);
 		if (!pskb_may_pull(skb, length))
 			goto error;
-
+		ptr = skb->data;
 		printk(KERN_DEBUG "%s: recv: ", tunnel->name);
 
-		offset = 0;
+		i = 0;
 		do {
-			printk(" %02X", ptr[offset]);
-		} while (++offset < length);
+			printk(" %02X", ptr[i]);
+		} while (++i < length);
 
 		printk("\n");
 	}
@@ -797,23 +806,23 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
 	}
 
 	/* Skip flags */
-	ptr += 2;
+	hlen = 2;
 
 	if (tunnel->version == L2TP_HDR_VER_2) {
 		/* If length is present, skip it */
 		if (hdrflags & L2TP_HDRFLAG_L)
-			ptr += 2;
+			hlen += 2;
 
 		/* Extract tunnel and session ID */
-		tunnel_id = ntohs(*(__be16 *) ptr);
-		ptr += 2;
-		session_id = ntohs(*(__be16 *) ptr);
-		ptr += 2;
+		tunnel_id = ntohs(*(__be16 *) (ptr + hlen));
+		hlen += 2;
+		session_id = ntohs(*(__be16 *) (ptr + hlen));
+		hlen += 2;
 	} else {
-		ptr += 2;	/* skip reserved bits */
+		hlen += 2;	/* skip reserved bits */
 		tunnel_id = tunnel->tunnel_id;
-		session_id = ntohl(*(__be32 *) ptr);
-		ptr += 4;
+		session_id = ntohl(*(__be32 *) (ptr + hlen));
+		hlen += 4;
 	}
 
 	/* Find the session context */
@@ -826,7 +835,7 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
 		goto error;
 	}
 
-	l2tp_recv_common(session, skb, ptr, optr, hdrflags, length, payload_hook);
+	l2tp_recv_common(session, skb, hlen, hdrflags, length, payload_hook);
 
 	return 0;
 
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index a16a48e..5341228 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -232,7 +232,7 @@ extern int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel);
 extern struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg);
 extern int l2tp_session_delete(struct l2tp_session *session);
 extern void l2tp_session_free(struct l2tp_session *session);
-extern void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, unsigned char *ptr, unsigned char *optr, u16 hdrflags, int length, int (*payload_hook)(struct sk_buff *skb));
+extern void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, int offset, u16 hdrflags, int length, int (*payload_hook)(struct sk_buff *skb));
 extern int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb);
 
 extern int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len);
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index d21e7eb..9046865 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -123,20 +123,15 @@ static int l2tp_ip_recv(struct sk_buff *skb)
 	struct sock *sk;
 	u32 session_id;
 	u32 tunnel_id;
-	unsigned char *ptr, *optr;
 	struct l2tp_session *session;
 	struct l2tp_tunnel *tunnel = NULL;
-	int length;
-	int offset;
-
-	/* Point to L2TP header */
-	optr = ptr = skb->data;
+	int hlen;
 
 	if (!pskb_may_pull(skb, 4))
 		goto discard;
 
-	session_id = ntohl(*((__be32 *) ptr));
-	ptr += 4;
+	session_id = ntohl(*(__be32 *) skb->data);
+	hlen = 4;
 
 	/* RFC3931: L2TP/IP packets have the first 4 bytes containing
 	 * the session_id. If it is 0, the packet is a L2TP control
@@ -158,21 +153,22 @@ static int l2tp_ip_recv(struct sk_buff *skb)
 
 	/* Trace packet contents, if enabled */
 	if (tunnel->debug & L2TP_MSG_DATA) {
-		length = min(32u, skb->len);
+		int i, length = min(32u, skb->len);
+
 		if (!pskb_may_pull(skb, length))
 			goto discard;
 
 		printk(KERN_DEBUG "%s: ip recv: ", tunnel->name);
 
-		offset = 0;
+		i = 0;
 		do {
-			printk(" %02X", ptr[offset]);
-		} while (++offset < length);
+			printk(" %02X", skb->data[i]);
+		} while (++i < length);
 
 		printk("\n");
 	}
 
-	l2tp_recv_common(session, skb, ptr, optr, 0, skb->len, tunnel->recv_payload_hook);
+	l2tp_recv_common(session, skb, hlen, 0, skb->len, tunnel->recv_payload_hook);
 
 	return 0;
 

^ permalink raw reply related

* Re: [PATCH] ipv4: fix ipsec forward performance regression
From: Kim Phillips @ 2011-11-01 23:50 UTC (permalink / raw)
  To: stable; +Cc: eric.dumazet, zheng.z.yan, netdev, David Miller
In-Reply-To: <20111024.030203.404195331212003955.davem@davemloft.net>

On Mon, 24 Oct 2011 03:02:03 -0400
David Miller <davem@davemloft.net> wrote:

> From: Eric Dumazet <eric.dumazet@gmail.com>
> Date: Sun, 23 Oct 2011 11:03:10 +0200
> 
> > Le dimanche 23 octobre 2011 à 15:58 +0800, Yan, Zheng a écrit :
> >> There is bug in commit 5e2b61f(ipv4: Remove flowi from struct rtable).
> >> It makes xfrm4_fill_dst() modify wrong data structure.
> >> 
> >> Signed-off-by: Zheng Yan <zheng.z.yan@intel.com>
>  ...
> > Reported-by: Kim Phillips <kim.phillips@freescale.com>
> > 
> > Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
> 
> Applied, thanks everyone.

To: <stable@kernel.org>

-stable maintainers, please consider the following two upstream
commits for inclusion in upcoming v3.0.x [1] stable releases:

v3.0.8 plus this:

upstream commit b73233960a59ee66e09d642f13d0592b13651e94
(ipv4: fix ipsec forward performance regression)

increases IPSec fwding performance from 0.2kpps to ~3.5kpps.

Adding this:

upstream commit aa1c366e4febc7f5c2b84958a2dd7cd70e28f9d0
(net: Handle different key sizes between address families in flow
cache)

to that, brings it back up to 2.6.38 levels, i.e., ~44kpps.

note that for v2.6.39.4 (.39 is the first kernel version with the
40->0.2kpps regression), commit b732339 depends on a slew of
commits, presumably ending with commit 5e2b61f: ipv4: Remove flowi
from struct rtable.

However it appears commit aa1c366e alone will restore almost all
the performance (~42kpps) on that kernel version.

So to summarize, please cherry-pick:

v2.6.39.x: aa1c366: net: Handle different key sizes between address families in flow cache
v3.0.x: aa1c366: net: Handle different key sizes between address families in flow cache
v3.0.x: b732339: ipv4: fix ipsec forward performance regression
v3.1.x: b732339: ipv4: fix ipsec forward performance regression

[All figures are based on a p2020ds board configured to rx, encrypt
and forward 64-byte packets.]

Thanks,

Kim

[1] initial kernel in long-term stable series for the embedded
industry (http://lwn.net/Articles/464834/)

^ permalink raw reply

* Re: [PATCH] net/ethernet: sc92031 is not Realtek
From: David Miller @ 2011-11-01 23:33 UTC (permalink / raw)
  To: cesarb
  Cc: netdev, linux-kernel, jeffrey.t.kirsher, nic_swsd, romieu,
	jgarzik, becker
In-Reply-To: <1319581944-2707-1-git-send-email-cesarb@cesarb.net>

From: Cesar Eduardo Barros <cesarb@cesarb.net>
Date: Tue, 25 Oct 2011 20:32:24 -0200

> While the SC92031 could be found on fake "Realtek" NICs, it has no
> relationship to Realtek, and is actually from Silan.
> 
> Create a new subdirectory for silan and move sc92031 there.
> 
> Signed-off-by: Cesar Eduardo Barros <cesarb@cesarb.net>

Applied.

^ permalink raw reply

* Re: [PATCH] net/ethernet: Move mac89x0.c from apple to cirrus
From: David Miller @ 2011-11-01 23:33 UTC (permalink / raw)
  To: geert; +Cc: jeffrey.t.kirsher, netdev, linux-m68k, linux-kernel
In-Reply-To: <1319911741-9977-1-git-send-email-geert@linux-m68k.org>

From: Geert Uytterhoeven <geert@linux-m68k.org>
Date: Sat, 29 Oct 2011 20:09:01 +0200

> Macintosh CS89x0 based ethernet cards use a Crystal Semiconductor (Now
> Cirrus Logic) CS89x0 chip, so the mac89x0 driver should be in
> drivers/net/ethernet/cirrus instead of drivers/net/ethernet/apple.
> 
> This also fixes a build problem, as the driver needs a header file from the
> cirrus directory:
> 
> drivers/net/ethernet/apple/mac89x0.c:107:20: error: cs89x0.h: No such file or directory
> 
> Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>

Applied, thanks.

^ permalink raw reply

* [PATCH ethtool 21/21] Rearrange definitions and remove unnecessary forward declarations
From: Ben Hutchings @ 2011-11-01 23:24 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

Most functions in ethtool.c are defined before use.  The major
exception is that the args array refers to a large number of functions
defined after it.  Move the args array and show_usage() functions down,
and move a few other functions up.

This leaves just one forward declaration in ethtool.c, which is
unavoidable because show_usage() and args refer to each other.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.c |  447 ++++++++++++++++++++++++++++---------------------------------
 1 files changed, 204 insertions(+), 243 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index f192376..d59ca31 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -64,194 +64,6 @@ enum {
 };
 #endif
 
-static int show_usage(struct cmd_context *ctx);
-static int do_version(struct cmd_context *ctx);
-static int parse_wolopts(char *optstr, u32 *data);
-static char *unparse_wolopts(int wolopts);
-static void get_mac_addr(char *src, unsigned char *dest);
-static int do_gdrv(struct cmd_context *ctx);
-static int do_gset(struct cmd_context *ctx);
-static int do_sset(struct cmd_context *ctx);
-static int do_gregs(struct cmd_context *ctx);
-static int do_nway_rst(struct cmd_context *ctx);
-static int do_geeprom(struct cmd_context *ctx);
-static int do_seeprom(struct cmd_context *ctx);
-static int do_test(struct cmd_context *ctx);
-static int do_phys_id(struct cmd_context *ctx);
-static int do_gpause(struct cmd_context *ctx);
-static int do_spause(struct cmd_context *ctx);
-static int do_gring(struct cmd_context *ctx);
-static int do_sring(struct cmd_context *ctx);
-static int do_schannels(struct cmd_context *ctx);
-static int do_gchannels(struct cmd_context *ctx);
-static int do_gcoalesce(struct cmd_context *ctx);
-static int do_scoalesce(struct cmd_context *ctx);
-static int do_goffload(struct cmd_context *ctx);
-static int do_soffload(struct cmd_context *ctx);
-static int do_gstats(struct cmd_context *ctx);
-static int rxflow_str_to_type(const char *str);
-static int parse_rxfhashopts(char *optstr, u32 *data);
-static char *unparse_rxfhashopts(u64 opts);
-static int dump_rxfhash(int fhash, u64 val);
-static int do_srxclass(struct cmd_context *ctx);
-static int do_grxclass(struct cmd_context *ctx);
-static int do_grxfhindir(struct cmd_context *ctx);
-static int do_srxfhindir(struct cmd_context *ctx);
-static int do_srxclsrule(struct cmd_context *ctx);
-static int do_grxclsrule(struct cmd_context *ctx);
-static int do_flash(struct cmd_context *ctx);
-static int do_permaddr(struct cmd_context *ctx);
-static int do_getfwdump(struct cmd_context *ctx);
-static int do_setfwdump(struct cmd_context *ctx);
-
-static const struct option {
-	const char *opts;
-	int want_device;
-	int (*func)(struct cmd_context *);
-	char *help;
-	char *opthelp;
-} args[] = {
-	{ "-s|--change", 1, do_sset, "Change generic options",
-	  "		[ speed %d ]\n"
-	  "		[ duplex half|full ]\n"
-	  "		[ port tp|aui|bnc|mii|fibre ]\n"
-	  "		[ autoneg on|off ]\n"
-	  "		[ advertise %x ]\n"
-	  "		[ phyad %d ]\n"
-	  "		[ xcvr internal|external ]\n"
-	  "		[ wol p|u|m|b|a|g|s|d... ]\n"
-	  "		[ sopass %x:%x:%x:%x:%x:%x ]\n"
-	  "		[ msglvl %d | msglvl type on|off ... ]\n" },
-	{ "-a|--show-pause", 1, do_gpause, "Show pause options" },
-	{ "-A|--pause", 1, do_spause, "Set pause options",
-	  "		[ autoneg on|off ]\n"
-	  "		[ rx on|off ]\n"
-	  "		[ tx on|off ]\n" },
-	{ "-c|--show-coalesce", 1, do_gcoalesce, "Show coalesce options" },
-	{ "-C|--coalesce", 1, do_scoalesce, "Set coalesce options",
-	  "		[adaptive-rx on|off]\n"
-	  "		[adaptive-tx on|off]\n"
-	  "		[rx-usecs N]\n"
-	  "		[rx-frames N]\n"
-	  "		[rx-usecs-irq N]\n"
-	  "		[rx-frames-irq N]\n"
-	  "		[tx-usecs N]\n"
-	  "		[tx-frames N]\n"
-	  "		[tx-usecs-irq N]\n"
-	  "		[tx-frames-irq N]\n"
-	  "		[stats-block-usecs N]\n"
-	  "		[pkt-rate-low N]\n"
-	  "		[rx-usecs-low N]\n"
-	  "		[rx-frames-low N]\n"
-	  "		[tx-usecs-low N]\n"
-	  "		[tx-frames-low N]\n"
-	  "		[pkt-rate-high N]\n"
-	  "		[rx-usecs-high N]\n"
-	  "		[rx-frames-high N]\n"
-	  "		[tx-usecs-high N]\n"
-	  "		[tx-frames-high N]\n"
-	  "		[sample-interval N]\n" },
-	{ "-g|--show-ring", 1, do_gring, "Query RX/TX ring parameters" },
-	{ "-G|--set-ring", 1, do_sring, "Set RX/TX ring parameters",
-	  "		[ rx N ]\n"
-	  "		[ rx-mini N ]\n"
-	  "		[ rx-jumbo N ]\n"
-	  "		[ tx N ]\n" },
-	{ "-k|--show-offload", 1, do_goffload,
-	  "Get protocol offload information" },
-	{ "-K|--offload", 1, do_soffload, "Set protocol offload",
-	  "		[ rx on|off ]\n"
-	  "		[ tx on|off ]\n"
-	  "		[ sg on|off ]\n"
-	  "		[ tso on|off ]\n"
-	  "		[ ufo on|off ]\n"
-	  "		[ gso on|off ]\n"
-	  "		[ gro on|off ]\n"
-	  "		[ lro on|off ]\n"
-	  "		[ rxvlan on|off ]\n"
-	  "		[ txvlan on|off ]\n"
-	  "		[ ntuple on|off ]\n"
-	  "		[ rxhash on|off ]\n"
-	},
-	{ "-i|--driver", 1, do_gdrv, "Show driver information" },
-	{ "-d|--register-dump", 1, do_gregs, "Do a register dump",
-	  "		[ raw on|off ]\n"
-	  "		[ file FILENAME ]\n" },
-	{ "-e|--eeprom-dump", 1, do_geeprom, "Do a EEPROM dump",
-	  "		[ raw on|off ]\n"
-	  "		[ offset N ]\n"
-	  "		[ length N ]\n" },
-	{ "-E|--change-eeprom", 1, do_seeprom,
-	  "Change bytes in device EEPROM",
-	  "		[ magic N ]\n"
-	  "		[ offset N ]\n"
-	  "		[ length N ]\n"
-	  "		[ value N ]\n" },
-	{ "-r|--negotiate", 1, do_nway_rst, "Restart N-WAY negotiation" },
-	{ "-p|--identify", 1, do_phys_id,
-	  "Show visible port identification (e.g. blinking)",
-	  "               [ TIME-IN-SECONDS ]\n" },
-	{ "-t|--test", 1, do_test, "Execute adapter self test",
-	  "               [ online | offline | external_lb ]\n" },
-	{ "-S|--statistics", 1, do_gstats, "Show adapter statistics" },
-	{ "-n|--show-nfc", 1, do_grxclass,
-	  "Show Rx network flow classification options",
-	  "		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
-	  "tcp6|udp6|ah6|esp6|sctp6 ]\n" },
-	{ "-f|--flash", 1, do_flash,
-	  "Flash firmware image from the specified file to a region on the device",
-	  "               FILENAME [ REGION-NUMBER-TO-FLASH ]\n" },
-	{ "-N|--config-nfc", 1, do_srxclass,
-	  "Configure Rx network flow classification options",
-	  "		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
-	  "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... ]\n" },
-	{ "-x|--show-rxfh-indir", 1, do_grxfhindir,
-	  "Show Rx flow hash indirection" },
-	{ "-X|--set-rxfh-indir", 1, do_srxfhindir,
-	  "Set Rx flow hash indirection",
-	  "		equal N | weight W0 W1 ...\n" },
-	{ "-U|--config-ntuple", 1, do_srxclsrule,
-	  "Configure Rx ntuple filters and actions",
-	  "		[ delete %d ] |\n"
-	  "		[ flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4\n"
-	  "			[ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
-	  "			[ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
-	  "			[ proto %d [m %x] ]\n"
-	  "			[ src-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
-	  "			[ dst-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
-	  "			[ tos %d [m %x] ]\n"
-	  "			[ l4proto %d [m %x] ]\n"
-	  "			[ src-port %d [m %x] ]\n"
-	  "			[ dst-port %d [m %x] ]\n"
-	  "			[ spi %d [m %x] ]\n"
-	  "			[ vlan-etype %x [m %x] ]\n"
-	  "			[ vlan %x [m %x] ]\n"
-	  "			[ user-def %x [m %x] ]\n"
-	  "			[ action %d ]\n"
-	  "			[ loc %d]]\n" },
-	{ "-u|--show-ntuple", 1, do_grxclsrule,
-	  "Get Rx ntuple filters and actions",
-	  "		[ rule %d ]\n"},
-	{ "-P|--show-permaddr", 1, do_permaddr,
-	  "Show permanent hardware address" },
-	{ "-w|--get-dump", 1, do_getfwdump,
-	  "Get dump flag, data",
-	  "		[ data FILENAME ]\n" },
-	{ "-W|--set-dump", 1, do_setfwdump,
-	  "Set dump flag of the device",
-	  "		N\n"},
-	{ "-l|--show-channels", 1, do_gchannels, "Query Channels" },
-	{ "-L|--set-channels", 1, do_schannels, "Set Channels",
-	  "               [ rx N ]\n"
-	  "               [ tx N ]\n"
-	  "               [ other N ]\n"
-	  "               [ combined N ]\n" },
-	{ "-h|--help", 0, show_usage, "Show this help" },
-	{ "--version", 0, do_version, "Show version number" },
-	{}
-};
-
-
 static void exit_bad_args(void) __attribute__((noreturn));
 
 static void exit_bad_args(void)
@@ -262,29 +74,6 @@ static void exit_bad_args(void)
 	ethtool_exit(1);
 }
 
-static int show_usage(struct cmd_context *ctx)
-{
-	int i;
-
-	/* ethtool -h */
-	fprintf(stdout, PACKAGE " version " VERSION "\n");
-	fprintf(stdout,
-		"Usage:\n"
-		"        ethtool DEVNAME\t"
-		"Display standard information about device\n");
-	for (i = 0; args[i].opts; i++) {
-		fputs("        ethtool ", stdout);
-		fprintf(stdout, "%s %s\t%s\n",
-			args[i].opts,
-			args[i].want_device ? "DEVNAME" : "\t",
-			args[i].help);
-		if (args[i].opthelp)
-			fputs(args[i].opthelp, stdout);
-	}
-
-	return 0;
-}
-
 typedef enum {
 	CMDL_NONE,
 	CMDL_BOOL,
@@ -379,6 +168,22 @@ static u32 get_u32(char *str, int base)
 	return get_uint_range(str, base, 0xffffffff);
 }
 
+static void get_mac_addr(char *src, unsigned char *dest)
+{
+	int count;
+	int i;
+	int buf[ETH_ALEN];
+
+	count = sscanf(src, "%2x:%2x:%2x:%2x:%2x:%2x",
+		&buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]);
+	if (count != ETH_ALEN)
+		exit_bad_args();
+
+	for (i = 0; i < count; i++) {
+		dest[i] = buf[i];
+	}
+}
+
 static void parse_generic_cmdline(struct cmd_context *ctx,
 				  int *changed,
 				  struct cmdline_info *info,
@@ -791,26 +596,6 @@ static int dump_drvinfo(struct ethtool_drvinfo *info)
 	return 0;
 }
 
-static int dump_wol(struct ethtool_wolinfo *wol)
-{
-	fprintf(stdout, "	Supports Wake-on: %s\n",
-		unparse_wolopts(wol->supported));
-	fprintf(stdout, "	Wake-on: %s\n",
-		unparse_wolopts(wol->wolopts));
-	if (wol->supported & WAKE_MAGICSECURE) {
-		int i;
-		int delim = 0;
-		fprintf(stdout, "        SecureOn password: ");
-		for (i = 0; i < SOPASS_MAX; i++) {
-			fprintf(stdout, "%s%02x", delim?":":"", wol->sopass[i]);
-			delim=1;
-		}
-		fprintf(stdout, "\n");
-	}
-
-	return 0;
-}
-
 static int parse_wolopts(char *optstr, u32 *data)
 {
 	*data = 0;
@@ -877,20 +662,24 @@ static char *unparse_wolopts(int wolopts)
 	return buf;
 }
 
-static void get_mac_addr(char *src, unsigned char *dest)
+static int dump_wol(struct ethtool_wolinfo *wol)
 {
-	int count;
-	int i;
-	int buf[ETH_ALEN];
-
-	count = sscanf(src, "%2x:%2x:%2x:%2x:%2x:%2x",
-		&buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]);
-	if (count != ETH_ALEN)
-		exit_bad_args();
-
-	for (i = 0; i < count; i++) {
-		dest[i] = buf[i];
+	fprintf(stdout, "	Supports Wake-on: %s\n",
+		unparse_wolopts(wol->supported));
+	fprintf(stdout, "	Wake-on: %s\n",
+		unparse_wolopts(wol->wolopts));
+	if (wol->supported & WAKE_MAGICSECURE) {
+		int i;
+		int delim = 0;
+		fprintf(stdout, "        SecureOn password: ");
+		for (i = 0; i < SOPASS_MAX; i++) {
+			fprintf(stdout, "%s%02x", delim?":":"", wol->sopass[i]);
+			delim=1;
+		}
+		fprintf(stdout, "\n");
 	}
+
+	return 0;
 }
 
 static int parse_rxfhashopts(char *optstr, u32 *data)
@@ -3213,6 +3002,178 @@ int send_ioctl(struct cmd_context *ctx, void *cmd)
 }
 #endif
 
+static int show_usage(struct cmd_context *ctx);
+
+static const struct option {
+	const char *opts;
+	int want_device;
+	int (*func)(struct cmd_context *);
+	char *help;
+	char *opthelp;
+} args[] = {
+	{ "-s|--change", 1, do_sset, "Change generic options",
+	  "		[ speed %d ]\n"
+	  "		[ duplex half|full ]\n"
+	  "		[ port tp|aui|bnc|mii|fibre ]\n"
+	  "		[ autoneg on|off ]\n"
+	  "		[ advertise %x ]\n"
+	  "		[ phyad %d ]\n"
+	  "		[ xcvr internal|external ]\n"
+	  "		[ wol p|u|m|b|a|g|s|d... ]\n"
+	  "		[ sopass %x:%x:%x:%x:%x:%x ]\n"
+	  "		[ msglvl %d | msglvl type on|off ... ]\n" },
+	{ "-a|--show-pause", 1, do_gpause, "Show pause options" },
+	{ "-A|--pause", 1, do_spause, "Set pause options",
+	  "		[ autoneg on|off ]\n"
+	  "		[ rx on|off ]\n"
+	  "		[ tx on|off ]\n" },
+	{ "-c|--show-coalesce", 1, do_gcoalesce, "Show coalesce options" },
+	{ "-C|--coalesce", 1, do_scoalesce, "Set coalesce options",
+	  "		[adaptive-rx on|off]\n"
+	  "		[adaptive-tx on|off]\n"
+	  "		[rx-usecs N]\n"
+	  "		[rx-frames N]\n"
+	  "		[rx-usecs-irq N]\n"
+	  "		[rx-frames-irq N]\n"
+	  "		[tx-usecs N]\n"
+	  "		[tx-frames N]\n"
+	  "		[tx-usecs-irq N]\n"
+	  "		[tx-frames-irq N]\n"
+	  "		[stats-block-usecs N]\n"
+	  "		[pkt-rate-low N]\n"
+	  "		[rx-usecs-low N]\n"
+	  "		[rx-frames-low N]\n"
+	  "		[tx-usecs-low N]\n"
+	  "		[tx-frames-low N]\n"
+	  "		[pkt-rate-high N]\n"
+	  "		[rx-usecs-high N]\n"
+	  "		[rx-frames-high N]\n"
+	  "		[tx-usecs-high N]\n"
+	  "		[tx-frames-high N]\n"
+	  "		[sample-interval N]\n" },
+	{ "-g|--show-ring", 1, do_gring, "Query RX/TX ring parameters" },
+	{ "-G|--set-ring", 1, do_sring, "Set RX/TX ring parameters",
+	  "		[ rx N ]\n"
+	  "		[ rx-mini N ]\n"
+	  "		[ rx-jumbo N ]\n"
+	  "		[ tx N ]\n" },
+	{ "-k|--show-offload", 1, do_goffload,
+	  "Get protocol offload information" },
+	{ "-K|--offload", 1, do_soffload, "Set protocol offload",
+	  "		[ rx on|off ]\n"
+	  "		[ tx on|off ]\n"
+	  "		[ sg on|off ]\n"
+	  "		[ tso on|off ]\n"
+	  "		[ ufo on|off ]\n"
+	  "		[ gso on|off ]\n"
+	  "		[ gro on|off ]\n"
+	  "		[ lro on|off ]\n"
+	  "		[ rxvlan on|off ]\n"
+	  "		[ txvlan on|off ]\n"
+	  "		[ ntuple on|off ]\n"
+	  "		[ rxhash on|off ]\n"
+	},
+	{ "-i|--driver", 1, do_gdrv, "Show driver information" },
+	{ "-d|--register-dump", 1, do_gregs, "Do a register dump",
+	  "		[ raw on|off ]\n"
+	  "		[ file FILENAME ]\n" },
+	{ "-e|--eeprom-dump", 1, do_geeprom, "Do a EEPROM dump",
+	  "		[ raw on|off ]\n"
+	  "		[ offset N ]\n"
+	  "		[ length N ]\n" },
+	{ "-E|--change-eeprom", 1, do_seeprom,
+	  "Change bytes in device EEPROM",
+	  "		[ magic N ]\n"
+	  "		[ offset N ]\n"
+	  "		[ length N ]\n"
+	  "		[ value N ]\n" },
+	{ "-r|--negotiate", 1, do_nway_rst, "Restart N-WAY negotiation" },
+	{ "-p|--identify", 1, do_phys_id,
+	  "Show visible port identification (e.g. blinking)",
+	  "               [ TIME-IN-SECONDS ]\n" },
+	{ "-t|--test", 1, do_test, "Execute adapter self test",
+	  "               [ online | offline | external_lb ]\n" },
+	{ "-S|--statistics", 1, do_gstats, "Show adapter statistics" },
+	{ "-n|--show-nfc", 1, do_grxclass,
+	  "Show Rx network flow classification options",
+	  "		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
+	  "tcp6|udp6|ah6|esp6|sctp6 ]\n" },
+	{ "-f|--flash", 1, do_flash,
+	  "Flash firmware image from the specified file to a region on the device",
+	  "               FILENAME [ REGION-NUMBER-TO-FLASH ]\n" },
+	{ "-N|--config-nfc", 1, do_srxclass,
+	  "Configure Rx network flow classification options",
+	  "		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
+	  "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... ]\n" },
+	{ "-x|--show-rxfh-indir", 1, do_grxfhindir,
+	  "Show Rx flow hash indirection" },
+	{ "-X|--set-rxfh-indir", 1, do_srxfhindir,
+	  "Set Rx flow hash indirection",
+	  "		equal N | weight W0 W1 ...\n" },
+	{ "-U|--config-ntuple", 1, do_srxclsrule,
+	  "Configure Rx ntuple filters and actions",
+	  "		[ delete %d ] |\n"
+	  "		[ flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4\n"
+	  "			[ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
+	  "			[ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
+	  "			[ proto %d [m %x] ]\n"
+	  "			[ src-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
+	  "			[ dst-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
+	  "			[ tos %d [m %x] ]\n"
+	  "			[ l4proto %d [m %x] ]\n"
+	  "			[ src-port %d [m %x] ]\n"
+	  "			[ dst-port %d [m %x] ]\n"
+	  "			[ spi %d [m %x] ]\n"
+	  "			[ vlan-etype %x [m %x] ]\n"
+	  "			[ vlan %x [m %x] ]\n"
+	  "			[ user-def %x [m %x] ]\n"
+	  "			[ action %d ]\n"
+	  "			[ loc %d]]\n" },
+	{ "-u|--show-ntuple", 1, do_grxclsrule,
+	  "Get Rx ntuple filters and actions",
+	  "		[ rule %d ]\n"},
+	{ "-P|--show-permaddr", 1, do_permaddr,
+	  "Show permanent hardware address" },
+	{ "-w|--get-dump", 1, do_getfwdump,
+	  "Get dump flag, data",
+	  "		[ data FILENAME ]\n" },
+	{ "-W|--set-dump", 1, do_setfwdump,
+	  "Set dump flag of the device",
+	  "		N\n"},
+	{ "-l|--show-channels", 1, do_gchannels, "Query Channels" },
+	{ "-L|--set-channels", 1, do_schannels, "Set Channels",
+	  "               [ rx N ]\n"
+	  "               [ tx N ]\n"
+	  "               [ other N ]\n"
+	  "               [ combined N ]\n" },
+	{ "-h|--help", 0, show_usage, "Show this help" },
+	{ "--version", 0, do_version, "Show version number" },
+	{}
+};
+
+static int show_usage(struct cmd_context *ctx)
+{
+	int i;
+
+	/* ethtool -h */
+	fprintf(stdout, PACKAGE " version " VERSION "\n");
+	fprintf(stdout,
+		"Usage:\n"
+		"        ethtool DEVNAME\t"
+		"Display standard information about device\n");
+	for (i = 0; args[i].opts; i++) {
+		fputs("        ethtool ", stdout);
+		fprintf(stdout, "%s %s\t%s\n",
+			args[i].opts,
+			args[i].want_device ? "DEVNAME" : "\t",
+			args[i].help);
+		if (args[i].opthelp)
+			fputs(args[i].opthelp, stdout);
+	}
+
+	return 0;
+}
+
 int ethtool_main(int argc, char **argp)
 {
 	int (*func)(struct cmd_context *);
-- 
1.7.4.4


-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 20/21] Run tests in-process
From: Ben Hutchings @ 2011-11-01 23:23 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

Change definition of main() and use of exit() so that ethtool commands
can be tested without starting a new process.  This will allow deeper
testing that covers ioctl requests and responses.

Fix the obvious socket and memory leaks.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 Makefile.am    |    6 +---
 ethtool.c      |   25 +++++++++++++-------
 internal.h     |    5 ++++
 test-cmdline.c |    6 +++++
 test-common.c  |   65 +++++++++++++++++++++++++++++++------------------------
 5 files changed, 66 insertions(+), 41 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 4b0eb17..cfc2b79 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,11 +12,9 @@ ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h \
 		  rxclass.c
 
 TESTS = test-cmdline
-check_PROGRAMS = test-cmdline test-one-cmdline
-test_cmdline_SOURCES = test-cmdline.c test-common.c
+check_PROGRAMS = test-cmdline
+test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES) 
 test_cmdline_CFLAGS = -DTEST_ETHTOOL
-test_one_cmdline_SOURCES = $(ethtool_SOURCES)
-test_one_cmdline_CFLAGS = -DTEST_ETHTOOL
 
 dist-hook:
 	cp $(top_srcdir)/ethtool.spec $(distdir)
diff --git a/ethtool.c b/ethtool.c
index 8e757e9..f192376 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -259,7 +259,7 @@ static void exit_bad_args(void)
 	fprintf(stderr,
 		"ethtool: bad command line argument(s)\n"
 		"For more information run ethtool -h\n");
-	exit(1);
+	ethtool_exit(1);
 }
 
 static int show_usage(struct cmd_context *ctx)
@@ -2754,6 +2754,7 @@ static int do_grxfhindir(struct cmd_context *ctx)
 	err = send_ioctl(ctx, indir);
 	if (err < 0) {
 		perror("Cannot get RX flow hash indirection table");
+		free(indir);
 		return 103;
 	}
 
@@ -2766,6 +2767,8 @@ static int do_grxfhindir(struct cmd_context *ctx)
 		if (i % 8 == 7)
 			fputc('\n', stdout);
 	}
+
+	free(indir);
 	return 0;
 }
 
@@ -2817,14 +2820,16 @@ static int do_srxfhindir(struct cmd_context *ctx)
 		if (sum == 0) {
 			fprintf(stderr,
 				"At least one weight must be non-zero\n");
-			exit(1);
+			free(indir);
+			ethtool_exit(1);
 		}
 
 		if (sum > indir->size) {
 			fprintf(stderr,
 				"Total weight exceeds the size of the "
 				"indirection table\n");
-			exit(1);
+			free(indir);
+			ethtool_exit(1);
 		}
 
 		j = -1;
@@ -2844,6 +2849,7 @@ static int do_srxfhindir(struct cmd_context *ctx)
 		return 105;
 	}
 
+	free(indir);
 	return 0;
 }
 
@@ -3199,18 +3205,15 @@ static int do_setfwdump(struct cmd_context *ctx)
 	return 0;
 }
 
+#ifndef TEST_ETHTOOL
 int send_ioctl(struct cmd_context *ctx, void *cmd)
 {
-#ifndef TEST_ETHTOOL
 	ctx->ifr.ifr_data = cmd;
 	return ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr);
-#else
-	/* If we get this far then parsing succeeded */
-	exit(0);
-#endif
 }
+#endif
 
-int main(int argc, char **argp, char **envp)
+int ethtool_main(int argc, char **argp)
 {
 	int (*func)(struct cmd_context *);
 	int want_device;
@@ -3265,12 +3268,16 @@ opt_found:
 		memset(&ctx.ifr, 0, sizeof(ctx.ifr));
 		strcpy(ctx.ifr.ifr_name, ctx.devname);
 
+#ifndef TEST_ETHTOOL
 		/* Open control socket. */
 		ctx.fd = socket(AF_INET, SOCK_DGRAM, 0);
 		if (ctx.fd < 0) {
 			perror("Cannot get control socket");
 			return 70;
 		}
+#else
+		ctx.fd = -1;
+#endif
 	} else {
 		ctx.fd = -1;
 	}
diff --git a/internal.h b/internal.h
index cb126b3..8505396 100644
--- a/internal.h
+++ b/internal.h
@@ -98,7 +98,12 @@ struct cmd_context {
 };
 
 #ifdef TEST_ETHTOOL
+int ethtool_main(int argc, char **argp);
+void ethtool_exit(int rc) __attribute__((noreturn));
 int test_cmdline(const char *args);
+#else
+#define ethtool_main(...) main(__VA_ARGS__)
+#define ethtool_exit(rc) exit(rc)
 #endif
 
 int send_ioctl(struct cmd_context *ctx, void *cmd);
diff --git a/test-cmdline.c b/test-cmdline.c
index 7dd3b7c..df1aeed 100644
--- a/test-cmdline.c
+++ b/test-cmdline.c
@@ -213,6 +213,12 @@ static struct test_case {
 	{ 1, "-0" },
 };
 
+int send_ioctl(struct cmd_context *ctx, void *cmd)
+{
+	/* If we get this far then parsing succeeded */
+	ethtool_exit(0);
+}
+
 int main(void)
 {
 	struct test_case *tc;
diff --git a/test-common.c b/test-common.c
index 4ea84c8..5a06ac7 100644
--- a/test-common.c
+++ b/test-common.c
@@ -7,22 +7,27 @@
  * by the Free Software Foundation, incorporated herein by reference.
  */
 
+#include <setjmp.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/fcntl.h>
-#include <sys/wait.h>
 #include <unistd.h>
 #include "internal.h"
 
+static jmp_buf test_return;
+
+void ethtool_exit(int rc)
+{
+	longjmp(test_return, rc + 1);
+}
+
 int test_cmdline(const char *args)
 {
 	int argc, i;
 	char **argv;
 	const char *arg;
 	size_t len;
-	pid_t pid;
-	int dev_null;
-	int status;
+	int dev_null = -1, old_stdout = -1, old_stderr = -1;
 	int rc;
 
 	/* Convert line to argv */
@@ -56,35 +61,39 @@ int test_cmdline(const char *args)
 	}
 
 	fflush(NULL);
-	pid = fork();
-
-	/* Child */
-	if (pid == 0) {
-		dup2(dev_null, STDIN_FILENO);
-		if (!getenv("ETHTOOL_TEST_VERBOSE")) {
-			dup2(dev_null, STDOUT_FILENO);
-			dup2(dev_null, STDERR_FILENO);
+	dup2(dev_null, STDIN_FILENO);
+	if (!getenv("ETHTOOL_TEST_VERBOSE")) {
+		old_stdout = dup(STDOUT_FILENO);
+		if (old_stdout < 0) {
+			perror("dup stdout");
+			rc = -1;
+			goto out;
 		}
-		execv("./test-one-cmdline", argv);
-		_exit(126);
+		dup2(dev_null, STDOUT_FILENO);
+		old_stderr = dup(STDERR_FILENO);
+		if (old_stderr < 0) {
+			perror("dup stderr");
+			rc = -1;
+			goto out;
+		}
+		dup2(dev_null, STDERR_FILENO);
 	}
 
-	/* Parent */
-	if (pid < 0) {
-		perror("fork");
-		close(dev_null);
-		rc = -1;
-		goto out;
-	}
-	close(dev_null);
-	if (waitpid(pid, &status, 0) < 0) {
-		perror("waitpid");
-		rc = -1;
-		goto out;
-	}
-	rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+	rc = setjmp(test_return);
+	rc = rc ? rc - 1 : ethtool_main(argc, argv);
 
 out:
+	fflush(NULL);
+	if (old_stderr >= 0) {
+		dup2(old_stderr, STDERR_FILENO);
+		close(old_stderr);
+	}
+	if (old_stdout >= 0) {
+		dup2(old_stdout, STDOUT_FILENO);
+		close(old_stdout);
+	}
+	if (dev_null >= 0)
+		close(dev_null);
 	for (i = 0; i < argc; i++)
 		free(argv[i]);
 	free(argv);
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 19/21] Declare static variables const as appropriate
From: Ben Hutchings @ 2011-11-01 23:22 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.c |    4 ++--
 rxclass.c |    8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index 13fb0ef..8e757e9 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -104,7 +104,7 @@ static int do_permaddr(struct cmd_context *ctx);
 static int do_getfwdump(struct cmd_context *ctx);
 static int do_setfwdump(struct cmd_context *ctx);
 
-static struct option {
+static const struct option {
 	const char *opts;
 	int want_device;
 	int (*func)(struct cmd_context *);
@@ -965,7 +965,7 @@ static char *unparse_rxfhashopts(u64 opts)
 	return buf;
 }
 
-static struct {
+static const struct {
 	const char *name;
 	int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
 
diff --git a/rxclass.c b/rxclass.c
index 3b23a33..1980d0e 100644
--- a/rxclass.c
+++ b/rxclass.c
@@ -533,7 +533,7 @@ struct rule_opts {
 	int		moffset;
 };
 
-static struct rule_opts rule_nfc_tcp_ip4[] = {
+static const struct rule_opts rule_nfc_tcp_ip4[] = {
 	{ "src-ip", OPT_IP4, NFC_FLAG_SADDR,
 	  offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip4_spec.ip4src),
 	  offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip4_spec.ip4src) },
@@ -564,7 +564,7 @@ static struct rule_opts rule_nfc_tcp_ip4[] = {
 	  offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
 };
 
-static struct rule_opts rule_nfc_esp_ip4[] = {
+static const struct rule_opts rule_nfc_esp_ip4[] = {
 	{ "src-ip", OPT_IP4, NFC_FLAG_SADDR,
 	  offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip4_spec.ip4src),
 	  offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip4_spec.ip4src) },
@@ -592,7 +592,7 @@ static struct rule_opts rule_nfc_esp_ip4[] = {
 	  offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
 };
 
-static struct rule_opts rule_nfc_usr_ip4[] = {
+static const struct rule_opts rule_nfc_usr_ip4[] = {
 	{ "src-ip", OPT_IP4, NFC_FLAG_SADDR,
 	  offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.ip4src),
 	  offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.ip4src) },
@@ -632,7 +632,7 @@ static struct rule_opts rule_nfc_usr_ip4[] = {
 	  offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
 };
 
-static struct rule_opts rule_nfc_ether[] = {
+static const struct rule_opts rule_nfc_ether[] = {
 	{ "src", OPT_MAC, NFC_FLAG_SADDR,
 	  offsetof(struct ethtool_rx_flow_spec, h_u.ether_spec.h_source),
 	  offsetof(struct ethtool_rx_flow_spec, m_u.ether_spec.h_source) },
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 18/21] rxclass: Replace global rmgr with automatic variable/parameter
From: Ben Hutchings @ 2011-11-01 23:22 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers, Alexander Duyck
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 rxclass.c |   78 ++++++++++++++++++++++++++----------------------------------
 1 files changed, 34 insertions(+), 44 deletions(-)

diff --git a/rxclass.c b/rxclass.c
index 0c7b916..3b23a33 100644
--- a/rxclass.c
+++ b/rxclass.c
@@ -299,30 +299,28 @@ struct rmgr_ctrl {
 	__u32			size;
 };
 
-static struct rmgr_ctrl rmgr;
-static int rmgr_init_done = 0;
-
-static int rmgr_ins(__u32 loc)
+static int rmgr_ins(struct rmgr_ctrl *rmgr, __u32 loc)
 {
 	/* verify location is in rule manager range */
-	if (loc >= rmgr.size) {
+	if (loc >= rmgr->size) {
 		fprintf(stderr, "rmgr: Location out of range\n");
 		return -1;
 	}
 
 	/* set bit for the rule */
-	set_bit(loc, rmgr.slot);
+	set_bit(loc, rmgr->slot);
 
 	return 0;
 }
 
-static int rmgr_find_empty_slot(struct ethtool_rx_flow_spec *fsp)
+static int rmgr_find_empty_slot(struct rmgr_ctrl *rmgr,
+				struct ethtool_rx_flow_spec *fsp)
 {
 	__u32 loc;
 	__u32 slot_num;
 
 	/* start at the end of the list since it is lowest priority */
-	loc = rmgr.size - 1;
+	loc = rmgr->size - 1;
 
 	/* locate the first slot a rule can be placed in */
 	slot_num = loc / BITS_PER_LONG;
@@ -333,10 +331,10 @@ static int rmgr_find_empty_slot(struct ethtool_rx_flow_spec *fsp)
 	 * moving 1 + loc % BITS_PER_LONG we align ourselves to the last bit
 	 * in the previous word.
 	 *
-	 * If loc rolls over it should be greater than or equal to rmgr.size
+	 * If loc rolls over it should be greater than or equal to rmgr->size
 	 * and as such we know we have reached the end of the list.
 	 */
-	if (!~(rmgr.slot[slot_num] | (~1UL << rmgr.size % BITS_PER_LONG))) {
+	if (!~(rmgr->slot[slot_num] | (~1UL << rmgr->size % BITS_PER_LONG))) {
 		loc -= 1 + (loc % BITS_PER_LONG);
 		slot_num--;
 	}
@@ -345,7 +343,7 @@ static int rmgr_find_empty_slot(struct ethtool_rx_flow_spec *fsp)
 	 * Now that we are aligned with the last bit in each long we can just
 	 * go though and eliminate all the longs with no free bits
 	 */
-	while (loc < rmgr.size && !~(rmgr.slot[slot_num])) {
+	while (loc < rmgr->size && !~(rmgr->slot[slot_num])) {
 		loc -= BITS_PER_LONG;
 		slot_num--;
 	}
@@ -354,13 +352,13 @@ static int rmgr_find_empty_slot(struct ethtool_rx_flow_spec *fsp)
 	 * If we still are inside the range, test individual bits as one is
 	 * likely available for our use.
 	 */
-	while (loc < rmgr.size && test_bit(loc, rmgr.slot))
+	while (loc < rmgr->size && test_bit(loc, rmgr->slot))
 		loc--;
 
 	/* location found, insert rule */
-	if (loc < rmgr.size) {
+	if (loc < rmgr->size) {
 		fsp->location = loc;
-		return rmgr_ins(loc);
+		return rmgr_ins(rmgr, loc);
 	}
 
 	/* No space to add this rule */
@@ -369,25 +367,22 @@ static int rmgr_find_empty_slot(struct ethtool_rx_flow_spec *fsp)
 	return -1;
 }
 
-static int rmgr_init(struct cmd_context *ctx)
+static int rmgr_init(struct cmd_context *ctx, struct rmgr_ctrl *rmgr)
 {
 	struct ethtool_rxnfc *nfccmd;
 	int err, i;
 	__u32 *rule_locs;
 
-	if (rmgr_init_done)
-		return 0;
-
 	/* clear rule manager settings */
-	memset(&rmgr, 0, sizeof(struct rmgr_ctrl));
+	memset(rmgr, 0, sizeof(*rmgr));
 
-	/* request count and store in rmgr.n_rules */
-	err = rxclass_get_count(ctx, &rmgr.n_rules);
+	/* request count and store in rmgr->n_rules */
+	err = rxclass_get_count(ctx, &rmgr->n_rules);
 	if (err < 0)
 		return err;
 
 	/* alloc memory for request of location list */
-	nfccmd = calloc(1, sizeof(*nfccmd) + (rmgr.n_rules * sizeof(__u32)));
+	nfccmd = calloc(1, sizeof(*nfccmd) + (rmgr->n_rules * sizeof(__u32)));
 	if (!nfccmd) {
 		perror("rmgr: Cannot allocate memory for"
 		       " RX class rule locations");
@@ -396,7 +391,7 @@ static int rmgr_init(struct cmd_context *ctx)
 
 	/* request location list */
 	nfccmd->cmd = ETHTOOL_GRXCLSRLALL;
-	nfccmd->rule_cnt = rmgr.n_rules;
+	nfccmd->rule_cnt = rmgr->n_rules;
 	err = send_ioctl(ctx, nfccmd);
 	if (err < 0) {
 		perror("rmgr: Cannot get RX class rules");
@@ -405,61 +400,56 @@ static int rmgr_init(struct cmd_context *ctx)
 	}
 
 	/* make certain the table size is valid */
-	rmgr.size = nfccmd->data;
-	if (rmgr.size == 0 || rmgr.size < rmgr.n_rules) {
+	rmgr->size = nfccmd->data;
+	if (rmgr->size == 0 || rmgr->size < rmgr->n_rules) {
 		perror("rmgr: Invalid RX class rules table size");
 		return -1;
 	}
 
 	/* initialize bitmap for storage of valid locations */
-	rmgr.slot = calloc(1, BITS_TO_LONGS(rmgr.size) * sizeof(long));
-	if (!rmgr.slot) {
+	rmgr->slot = calloc(1, BITS_TO_LONGS(rmgr->size) * sizeof(long));
+	if (!rmgr->slot) {
 		perror("rmgr: Cannot allocate memory for RX class rules");
 		return -1;
 	}
 
 	/* write locations to bitmap */
 	rule_locs = nfccmd->rule_locs;
-	for (i = 0; i < rmgr.n_rules; i++) {
-		err = rmgr_ins(rule_locs[i]);
+	for (i = 0; i < rmgr->n_rules; i++) {
+		err = rmgr_ins(rmgr, rule_locs[i]);
 		if (err < 0)
 			break;
 	}
 
-	/* free memory and set flag to avoid reinit */
 	free(nfccmd);
-	rmgr_init_done = 1;
 
 	return err;
 }
 
-static void rmgr_cleanup(void)
+static void rmgr_cleanup(struct rmgr_ctrl *rmgr)
 {
-	if (!rmgr_init_done)
-		return;
-
-	rmgr_init_done = 0;
-
-	free(rmgr.slot);
-	rmgr.slot = NULL;
-	rmgr.size = 0;
+	free(rmgr->slot);
+	rmgr->slot = NULL;
+	rmgr->size = 0;
 }
 
 static int rmgr_set_location(struct cmd_context *ctx,
 			     struct ethtool_rx_flow_spec *fsp)
 {
+	struct rmgr_ctrl rmgr;
 	int err;
 
 	/* init table of available rules */
-	err = rmgr_init(ctx);
+	err = rmgr_init(ctx, &rmgr);
 	if (err < 0)
-		return err;
+		goto out;
 
 	/* verify rule location */
-	err = rmgr_find_empty_slot(fsp);
+	err = rmgr_find_empty_slot(&rmgr, fsp);
 
+out:
 	/* cleanup table and free resources */
-	rmgr_cleanup();
+	rmgr_cleanup(&rmgr);
 
 	return err;
 }
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 17/21] Change most static global variables into automatic variables
From: Ben Hutchings @ 2011-11-01 23:21 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

This is necessary preparation for in-process testing.  It should also
reduce the risk of some classes of bug by putting declaration and use
closer together.

Add parameters to various functions as necessary.

Leave the global constants alone.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.c |  553 ++++++++++++++++++++++++++++++++-----------------------------
 1 files changed, 293 insertions(+), 260 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index d0929c7..13fb0ef 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -285,114 +285,6 @@ static int show_usage(struct cmd_context *ctx)
 	return 0;
 }
 
-static int goffload_changed = 0;
-static int off_csum_rx_wanted = -1;
-static int off_csum_tx_wanted = -1;
-static int off_sg_wanted = -1;
-static int off_tso_wanted = -1;
-static int off_ufo_wanted = -1;
-static int off_gso_wanted = -1;
-static u32 off_flags_wanted = 0;
-static u32 off_flags_mask = 0;
-static int off_gro_wanted = -1;
-
-static struct ethtool_pauseparam epause;
-static int gpause_changed = 0;
-static int pause_autoneg_wanted = -1;
-static int pause_rx_wanted = -1;
-static int pause_tx_wanted = -1;
-
-static struct ethtool_ringparam ering;
-static int gring_changed = 0;
-static s32 ring_rx_wanted = -1;
-static s32 ring_rx_mini_wanted = -1;
-static s32 ring_rx_jumbo_wanted = -1;
-static s32 ring_tx_wanted = -1;
-
-static struct ethtool_channels echannels;
-static int gchannels_changed;
-static s32 channels_rx_wanted = -1;
-static s32 channels_tx_wanted = -1;
-static s32 channels_other_wanted = -1;
-static s32 channels_combined_wanted = -1;
-
-static struct ethtool_coalesce ecoal;
-static int gcoalesce_changed = 0;
-static s32 coal_stats_wanted = -1;
-static int coal_adaptive_rx_wanted = -1;
-static int coal_adaptive_tx_wanted = -1;
-static s32 coal_sample_rate_wanted = -1;
-static s32 coal_pkt_rate_low_wanted = -1;
-static s32 coal_pkt_rate_high_wanted = -1;
-static s32 coal_rx_usec_wanted = -1;
-static s32 coal_rx_frames_wanted = -1;
-static s32 coal_rx_usec_irq_wanted = -1;
-static s32 coal_rx_frames_irq_wanted = -1;
-static s32 coal_tx_usec_wanted = -1;
-static s32 coal_tx_frames_wanted = -1;
-static s32 coal_tx_usec_irq_wanted = -1;
-static s32 coal_tx_frames_irq_wanted = -1;
-static s32 coal_rx_usec_low_wanted = -1;
-static s32 coal_rx_frames_low_wanted = -1;
-static s32 coal_tx_usec_low_wanted = -1;
-static s32 coal_tx_frames_low_wanted = -1;
-static s32 coal_rx_usec_high_wanted = -1;
-static s32 coal_rx_frames_high_wanted = -1;
-static s32 coal_tx_usec_high_wanted = -1;
-static s32 coal_tx_frames_high_wanted = -1;
-
-static int speed_wanted = -1;
-static int duplex_wanted = -1;
-static int port_wanted = -1;
-static int autoneg_wanted = -1;
-static int phyad_wanted = -1;
-static int xcvr_wanted = -1;
-static int advertising_wanted = -1;
-static int gset_changed = 0; /* did anything in GSET change? */
-static u32  wol_wanted = 0;
-static int wol_change = 0;
-static u8 sopass_wanted[SOPASS_MAX];
-static int sopass_change = 0;
-static int gwol_changed = 0; /* did anything in GWOL change? */
-static int phys_id_time = 0;
-static int gregs_changed = 0;
-static int gregs_dump_raw = 0;
-static int gregs_dump_hex = 0;
-static char *gregs_dump_file = NULL;
-static int geeprom_changed = 0;
-static int geeprom_dump_raw = 0;
-static u32 geeprom_offset = 0;
-static u32 geeprom_length = -1;
-static int seeprom_changed = 0;
-static u32 seeprom_magic = 0;
-static u32 seeprom_length = -1;
-static u32 seeprom_offset = 0;
-static u8 seeprom_value = 0;
-static int seeprom_value_seen = 0;
-static int rx_fhash_get = 0;
-static int rx_fhash_set = 0;
-static u32 rx_fhash_val = 0;
-static int rxfhindir_equal = 0;
-static char **rxfhindir_weight = NULL;
-static char *flash_file = NULL;
-static int flash_region = -1;
-
-static int msglvl_changed;
-static u32 msglvl_wanted = 0;
-static u32 msglvl_mask = 0;
-static u32 dump_flag;
-static char *dump_file = NULL;
-
-static int rx_class_rule_get = -1;
-static int rx_class_rule_del = -1;
-static struct ethtool_rx_flow_spec rx_rule_fs;
-
-static enum {
-	ONLINE=0,
-	OFFLINE,
-	EXTERNAL_LB,
-} test_type = OFFLINE;
-
 typedef enum {
 	CMDL_NONE,
 	CMDL_BOOL,
@@ -429,91 +321,6 @@ struct flag_info {
 	u32 value;
 };
 
-static struct cmdline_info cmdline_gregs[] = {
-	{ "raw", CMDL_BOOL, &gregs_dump_raw, NULL },
-	{ "hex", CMDL_BOOL, &gregs_dump_hex, NULL },
-	{ "file", CMDL_STR, &gregs_dump_file, NULL },
-};
-
-static struct cmdline_info cmdline_geeprom[] = {
-	{ "offset", CMDL_U32, &geeprom_offset, NULL },
-	{ "length", CMDL_U32, &geeprom_length, NULL },
-	{ "raw", CMDL_BOOL, &geeprom_dump_raw, NULL },
-};
-
-static struct cmdline_info cmdline_seeprom[] = {
-	{ "magic", CMDL_U32, &seeprom_magic, NULL },
-	{ "offset", CMDL_U32, &seeprom_offset, NULL },
-	{ "length", CMDL_U32, &seeprom_length, NULL },
-	{ "value", CMDL_U8, &seeprom_value, NULL,
-	  0, &seeprom_value_seen },
-};
-
-static struct cmdline_info cmdline_offload[] = {
-	{ "rx", CMDL_BOOL, &off_csum_rx_wanted, NULL },
-	{ "tx", CMDL_BOOL, &off_csum_tx_wanted, NULL },
-	{ "sg", CMDL_BOOL, &off_sg_wanted, NULL },
-	{ "tso", CMDL_BOOL, &off_tso_wanted, NULL },
-	{ "ufo", CMDL_BOOL, &off_ufo_wanted, NULL },
-	{ "gso", CMDL_BOOL, &off_gso_wanted, NULL },
-	{ "lro", CMDL_FLAG, &off_flags_wanted, NULL,
-	  ETH_FLAG_LRO, &off_flags_mask },
-	{ "gro", CMDL_BOOL, &off_gro_wanted, NULL },
-	{ "rxvlan", CMDL_FLAG, &off_flags_wanted, NULL,
-	  ETH_FLAG_RXVLAN, &off_flags_mask },
-	{ "txvlan", CMDL_FLAG, &off_flags_wanted, NULL,
-	  ETH_FLAG_TXVLAN, &off_flags_mask },
-	{ "ntuple", CMDL_FLAG, &off_flags_wanted, NULL,
-	  ETH_FLAG_NTUPLE, &off_flags_mask },
-	{ "rxhash", CMDL_FLAG, &off_flags_wanted, NULL,
-	  ETH_FLAG_RXHASH, &off_flags_mask },
-};
-
-static struct cmdline_info cmdline_pause[] = {
-	{ "autoneg", CMDL_BOOL, &pause_autoneg_wanted, &epause.autoneg },
-	{ "rx", CMDL_BOOL, &pause_rx_wanted, &epause.rx_pause },
-	{ "tx", CMDL_BOOL, &pause_tx_wanted, &epause.tx_pause },
-};
-
-static struct cmdline_info cmdline_ring[] = {
-	{ "rx", CMDL_S32, &ring_rx_wanted, &ering.rx_pending },
-	{ "rx-mini", CMDL_S32, &ring_rx_mini_wanted, &ering.rx_mini_pending },
-	{ "rx-jumbo", CMDL_S32, &ring_rx_jumbo_wanted, &ering.rx_jumbo_pending },
-	{ "tx", CMDL_S32, &ring_tx_wanted, &ering.tx_pending },
-};
-
-static struct cmdline_info cmdline_channels[] = {
-	{ "rx", CMDL_S32, &channels_rx_wanted, &echannels.rx_count },
-	{ "tx", CMDL_S32, &channels_tx_wanted, &echannels.tx_count },
-	{ "other", CMDL_S32, &channels_other_wanted, &echannels.other_count },
-	{ "combined", CMDL_S32, &channels_combined_wanted, &echannels.combined_count },
-};
-
-static struct cmdline_info cmdline_coalesce[] = {
-	{ "adaptive-rx", CMDL_BOOL, &coal_adaptive_rx_wanted, &ecoal.use_adaptive_rx_coalesce },
-	{ "adaptive-tx", CMDL_BOOL, &coal_adaptive_tx_wanted, &ecoal.use_adaptive_tx_coalesce },
-	{ "sample-interval", CMDL_S32, &coal_sample_rate_wanted, &ecoal.rate_sample_interval },
-	{ "stats-block-usecs", CMDL_S32, &coal_stats_wanted, &ecoal.stats_block_coalesce_usecs },
-	{ "pkt-rate-low", CMDL_S32, &coal_pkt_rate_low_wanted, &ecoal.pkt_rate_low },
-	{ "pkt-rate-high", CMDL_S32, &coal_pkt_rate_high_wanted, &ecoal.pkt_rate_high },
-	{ "rx-usecs", CMDL_S32, &coal_rx_usec_wanted, &ecoal.rx_coalesce_usecs },
-	{ "rx-frames", CMDL_S32, &coal_rx_frames_wanted, &ecoal.rx_max_coalesced_frames },
-	{ "rx-usecs-irq", CMDL_S32, &coal_rx_usec_irq_wanted, &ecoal.rx_coalesce_usecs_irq },
-	{ "rx-frames-irq", CMDL_S32, &coal_rx_frames_irq_wanted, &ecoal.rx_max_coalesced_frames_irq },
-	{ "tx-usecs", CMDL_S32, &coal_tx_usec_wanted, &ecoal.tx_coalesce_usecs },
-	{ "tx-frames", CMDL_S32, &coal_tx_frames_wanted, &ecoal.tx_max_coalesced_frames },
-	{ "tx-usecs-irq", CMDL_S32, &coal_tx_usec_irq_wanted, &ecoal.tx_coalesce_usecs_irq },
-	{ "tx-frames-irq", CMDL_S32, &coal_tx_frames_irq_wanted, &ecoal.tx_max_coalesced_frames_irq },
-	{ "rx-usecs-low", CMDL_S32, &coal_rx_usec_low_wanted, &ecoal.rx_coalesce_usecs_low },
-	{ "rx-frames-low", CMDL_S32, &coal_rx_frames_low_wanted, &ecoal.rx_max_coalesced_frames_low },
-	{ "tx-usecs-low", CMDL_S32, &coal_tx_usec_low_wanted, &ecoal.tx_coalesce_usecs_low },
-	{ "tx-frames-low", CMDL_S32, &coal_tx_frames_low_wanted, &ecoal.tx_max_coalesced_frames_low },
-	{ "rx-usecs-high", CMDL_S32, &coal_rx_usec_high_wanted, &ecoal.rx_coalesce_usecs_high },
-	{ "rx-frames-high", CMDL_S32, &coal_rx_frames_high_wanted, &ecoal.rx_max_coalesced_frames_high },
-	{ "tx-usecs-high", CMDL_S32, &coal_tx_usec_high_wanted, &ecoal.tx_coalesce_usecs_high },
-	{ "tx-frames-high", CMDL_S32, &coal_tx_frames_high_wanted, &ecoal.tx_max_coalesced_frames_high },
-};
-
 static const struct flag_info flags_msglvl[] = {
 	{ "drv",	NETIF_MSG_DRV },
 	{ "probe",	NETIF_MSG_PROBE },
@@ -1189,7 +996,9 @@ static struct {
 	{ "st_gmac", st_gmac_dump_regs },
 };
 
-static int dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs)
+static int dump_regs(int gregs_dump_raw, int gregs_dump_hex,
+		     const char *gregs_dump_file,
+		     struct ethtool_drvinfo *info, struct ethtool_regs *regs)
 {
 	int i;
 
@@ -1231,7 +1040,8 @@ static int dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs)
 	return 0;
 }
 
-static int dump_eeprom(struct ethtool_drvinfo *info, struct ethtool_eeprom *ee)
+static int dump_eeprom(int geeprom_dump_raw, struct ethtool_drvinfo *info,
+		       struct ethtool_eeprom *ee)
 {
 	int i;
 
@@ -1264,7 +1074,7 @@ static int dump_test(struct ethtool_drvinfo *info, struct ethtool_test *test,
 	rc = test->flags & ETH_TEST_FL_FAILED;
 	fprintf(stdout, "The test result is %s\n", rc ? "FAIL" : "PASS");
 
-	if (test_type == EXTERNAL_LB)
+	if (test->flags & ETH_TEST_FL_EXTERNAL_LB)
 		fprintf(stdout, "External loopback test was %sexecuted\n",
 			(test->flags & ETH_TEST_FL_EXTERNAL_LB_DONE) ?
 			"" : "not ");
@@ -1282,15 +1092,16 @@ static int dump_test(struct ethtool_drvinfo *info, struct ethtool_test *test,
 	return rc;
 }
 
-static int dump_pause(u32 advertising, u32 lp_advertising)
+static int dump_pause(const struct ethtool_pauseparam *epause,
+		      u32 advertising, u32 lp_advertising)
 {
 	fprintf(stdout,
 		"Autonegotiate:	%s\n"
 		"RX:		%s\n"
 		"TX:		%s\n",
-		epause.autoneg ? "on" : "off",
-		epause.rx_pause ? "on" : "off",
-		epause.tx_pause ? "on" : "off");
+		epause->autoneg ? "on" : "off",
+		epause->rx_pause ? "on" : "off",
+		epause->tx_pause ? "on" : "off");
 
 	if (lp_advertising) {
 		int an_rx = 0, an_tx = 0;
@@ -1320,7 +1131,7 @@ static int dump_pause(u32 advertising, u32 lp_advertising)
 	return 0;
 }
 
-static int dump_ring(void)
+static int dump_ring(const struct ethtool_ringparam *ering)
 {
 	fprintf(stdout,
 		"Pre-set maximums:\n"
@@ -1328,10 +1139,10 @@ static int dump_ring(void)
 		"RX Mini:	%u\n"
 		"RX Jumbo:	%u\n"
 		"TX:		%u\n",
-		ering.rx_max_pending,
-		ering.rx_mini_max_pending,
-		ering.rx_jumbo_max_pending,
-		ering.tx_max_pending);
+		ering->rx_max_pending,
+		ering->rx_mini_max_pending,
+		ering->rx_jumbo_max_pending,
+		ering->tx_max_pending);
 
 	fprintf(stdout,
 		"Current hardware settings:\n"
@@ -1339,16 +1150,16 @@ static int dump_ring(void)
 		"RX Mini:	%u\n"
 		"RX Jumbo:	%u\n"
 		"TX:		%u\n",
-		ering.rx_pending,
-		ering.rx_mini_pending,
-		ering.rx_jumbo_pending,
-		ering.tx_pending);
+		ering->rx_pending,
+		ering->rx_mini_pending,
+		ering->rx_jumbo_pending,
+		ering->tx_pending);
 
 	fprintf(stdout, "\n");
 	return 0;
 }
 
-static int dump_channels(void)
+static int dump_channels(const struct ethtool_channels *echannels)
 {
 	fprintf(stdout,
 		"Pre-set maximums:\n"
@@ -1356,9 +1167,9 @@ static int dump_channels(void)
 		"TX:		%u\n"
 		"Other:		%u\n"
 		"Combined:	%u\n",
-		echannels.max_rx, echannels.max_tx,
-		echannels.max_other,
-		echannels.max_combined);
+		echannels->max_rx, echannels->max_tx,
+		echannels->max_other,
+		echannels->max_combined);
 
 	fprintf(stdout,
 		"Current hardware settings:\n"
@@ -1366,19 +1177,19 @@ static int dump_channels(void)
 		"TX:		%u\n"
 		"Other:		%u\n"
 		"Combined:	%u\n",
-		echannels.rx_count, echannels.tx_count,
-		echannels.other_count,
-		echannels.combined_count);
+		echannels->rx_count, echannels->tx_count,
+		echannels->other_count,
+		echannels->combined_count);
 
 	fprintf(stdout, "\n");
 	return 0;
 }
 
-static int dump_coalesce(void)
+static int dump_coalesce(const struct ethtool_coalesce *ecoal)
 {
 	fprintf(stdout, "Adaptive RX: %s  TX: %s\n",
-		ecoal.use_adaptive_rx_coalesce ? "on" : "off",
-		ecoal.use_adaptive_tx_coalesce ? "on" : "off");
+		ecoal->use_adaptive_rx_coalesce ? "on" : "off",
+		ecoal->use_adaptive_tx_coalesce ? "on" : "off");
 
 	fprintf(stdout,
 		"stats-block-usecs: %u\n"
@@ -1406,30 +1217,30 @@ static int dump_coalesce(void)
 		"tx-usecs-high: %u\n"
 		"tx-frame-high: %u\n"
 		"\n",
-		ecoal.stats_block_coalesce_usecs,
-		ecoal.rate_sample_interval,
-		ecoal.pkt_rate_low,
-		ecoal.pkt_rate_high,
-
-		ecoal.rx_coalesce_usecs,
-		ecoal.rx_max_coalesced_frames,
-		ecoal.rx_coalesce_usecs_irq,
-		ecoal.rx_max_coalesced_frames_irq,
-
-		ecoal.tx_coalesce_usecs,
-		ecoal.tx_max_coalesced_frames,
-		ecoal.tx_coalesce_usecs_irq,
-		ecoal.tx_max_coalesced_frames_irq,
-
-		ecoal.rx_coalesce_usecs_low,
-		ecoal.rx_max_coalesced_frames_low,
-		ecoal.tx_coalesce_usecs_low,
-		ecoal.tx_max_coalesced_frames_low,
-
-		ecoal.rx_coalesce_usecs_high,
-		ecoal.rx_max_coalesced_frames_high,
-		ecoal.tx_coalesce_usecs_high,
-		ecoal.tx_max_coalesced_frames_high);
+		ecoal->stats_block_coalesce_usecs,
+		ecoal->rate_sample_interval,
+		ecoal->pkt_rate_low,
+		ecoal->pkt_rate_high,
+
+		ecoal->rx_coalesce_usecs,
+		ecoal->rx_max_coalesced_frames,
+		ecoal->rx_coalesce_usecs_irq,
+		ecoal->rx_max_coalesced_frames_irq,
+
+		ecoal->tx_coalesce_usecs,
+		ecoal->tx_max_coalesced_frames,
+		ecoal->tx_coalesce_usecs_irq,
+		ecoal->tx_max_coalesced_frames_irq,
+
+		ecoal->rx_coalesce_usecs_low,
+		ecoal->rx_max_coalesced_frames_low,
+		ecoal->tx_coalesce_usecs_low,
+		ecoal->tx_max_coalesced_frames_low,
+
+		ecoal->rx_coalesce_usecs_high,
+		ecoal->rx_max_coalesced_frames_high,
+		ecoal->tx_coalesce_usecs_high,
+		ecoal->tx_max_coalesced_frames_high);
 
 	return 0;
 }
@@ -1532,6 +1343,7 @@ static int do_gdrv(struct cmd_context *ctx)
 
 static int do_gpause(struct cmd_context *ctx)
 {
+	struct ethtool_pauseparam epause;
 	struct ethtool_cmd ecmd;
 	int err;
 
@@ -1554,9 +1366,9 @@ static int do_gpause(struct cmd_context *ctx)
 			perror("Cannot get device settings");
 			return 1;
 		}
-		dump_pause(ecmd.advertising, ecmd.lp_advertising);
+		dump_pause(&epause, ecmd.advertising, ecmd.lp_advertising);
 	} else {
-		dump_pause(0, 0);
+		dump_pause(&epause, 0, 0);
 	}
 
 	return 0;
@@ -1593,6 +1405,17 @@ static void do_generic_set(struct cmdline_info *info,
 
 static int do_spause(struct cmd_context *ctx)
 {
+	struct ethtool_pauseparam epause;
+	int gpause_changed = 0;
+	int pause_autoneg_wanted = -1;
+	int pause_rx_wanted = -1;
+	int pause_tx_wanted = -1;
+	struct cmdline_info cmdline_pause[] = {
+		{ "autoneg", CMDL_BOOL, &pause_autoneg_wanted,
+		  &epause.autoneg },
+		{ "rx", CMDL_BOOL, &pause_rx_wanted, &epause.rx_pause },
+		{ "tx", CMDL_BOOL, &pause_tx_wanted, &epause.tx_pause },
+	};
 	int err, changed = 0;
 
 	parse_generic_cmdline(ctx, &gpause_changed,
@@ -1624,6 +1447,20 @@ static int do_spause(struct cmd_context *ctx)
 
 static int do_sring(struct cmd_context *ctx)
 {
+	struct ethtool_ringparam ering;
+	int gring_changed = 0;
+	s32 ring_rx_wanted = -1;
+	s32 ring_rx_mini_wanted = -1;
+	s32 ring_rx_jumbo_wanted = -1;
+	s32 ring_tx_wanted = -1;
+	struct cmdline_info cmdline_ring[] = {
+		{ "rx", CMDL_S32, &ring_rx_wanted, &ering.rx_pending },
+		{ "rx-mini", CMDL_S32, &ring_rx_mini_wanted,
+		  &ering.rx_mini_pending },
+		{ "rx-jumbo", CMDL_S32, &ring_rx_jumbo_wanted,
+		  &ering.rx_jumbo_pending },
+		{ "tx", CMDL_S32, &ring_tx_wanted, &ering.tx_pending },
+	};
 	int err, changed = 0;
 
 	parse_generic_cmdline(ctx, &gring_changed,
@@ -1655,6 +1492,7 @@ static int do_sring(struct cmd_context *ctx)
 
 static int do_gring(struct cmd_context *ctx)
 {
+	struct ethtool_ringparam ering;
 	int err;
 
 	if (ctx->argc != 0)
@@ -1665,7 +1503,7 @@ static int do_gring(struct cmd_context *ctx)
 	ering.cmd = ETHTOOL_GRINGPARAM;
 	err = send_ioctl(ctx, &ering);
 	if (err == 0) {
-		err = dump_ring();
+		err = dump_ring(&ering);
 		if (err)
 			return err;
 	} else {
@@ -1678,6 +1516,20 @@ static int do_gring(struct cmd_context *ctx)
 
 static int do_schannels(struct cmd_context *ctx)
 {
+	struct ethtool_channels echannels;
+	int gchannels_changed;
+	s32 channels_rx_wanted = -1;
+	s32 channels_tx_wanted = -1;
+	s32 channels_other_wanted = -1;
+	s32 channels_combined_wanted = -1;
+	struct cmdline_info cmdline_channels[] = {
+		{ "rx", CMDL_S32, &channels_rx_wanted, &echannels.rx_count },
+		{ "tx", CMDL_S32, &channels_tx_wanted, &echannels.tx_count },
+		{ "other", CMDL_S32, &channels_other_wanted,
+		  &echannels.other_count },
+		{ "combined", CMDL_S32, &channels_combined_wanted,
+		  &echannels.combined_count },
+	};
 	int err, changed = 0;
 
 	parse_generic_cmdline(ctx, &gchannels_changed,
@@ -1714,6 +1566,7 @@ static int do_schannels(struct cmd_context *ctx)
 
 static int do_gchannels(struct cmd_context *ctx)
 {
+	struct ethtool_channels echannels;
 	int err;
 
 	if (ctx->argc != 0)
@@ -1724,7 +1577,7 @@ static int do_gchannels(struct cmd_context *ctx)
 	echannels.cmd = ETHTOOL_GCHANNELS;
 	err = send_ioctl(ctx, &echannels);
 	if (err == 0) {
-		err = dump_channels();
+		err = dump_channels(&echannels);
 		if (err)
 			return err;
 	} else {
@@ -1737,6 +1590,7 @@ static int do_gchannels(struct cmd_context *ctx)
 
 static int do_gcoalesce(struct cmd_context *ctx)
 {
+	struct ethtool_coalesce ecoal;
 	int err;
 
 	if (ctx->argc != 0)
@@ -1747,7 +1601,7 @@ static int do_gcoalesce(struct cmd_context *ctx)
 	ecoal.cmd = ETHTOOL_GCOALESCE;
 	err = send_ioctl(ctx, &ecoal);
 	if (err == 0) {
-		err = dump_coalesce();
+		err = dump_coalesce(&ecoal);
 		if (err)
 			return err;
 	} else {
@@ -1760,6 +1614,76 @@ static int do_gcoalesce(struct cmd_context *ctx)
 
 static int do_scoalesce(struct cmd_context *ctx)
 {
+	struct ethtool_coalesce ecoal;
+	int gcoalesce_changed = 0;
+	s32 coal_stats_wanted = -1;
+	int coal_adaptive_rx_wanted = -1;
+	int coal_adaptive_tx_wanted = -1;
+	s32 coal_sample_rate_wanted = -1;
+	s32 coal_pkt_rate_low_wanted = -1;
+	s32 coal_pkt_rate_high_wanted = -1;
+	s32 coal_rx_usec_wanted = -1;
+	s32 coal_rx_frames_wanted = -1;
+	s32 coal_rx_usec_irq_wanted = -1;
+	s32 coal_rx_frames_irq_wanted = -1;
+	s32 coal_tx_usec_wanted = -1;
+	s32 coal_tx_frames_wanted = -1;
+	s32 coal_tx_usec_irq_wanted = -1;
+	s32 coal_tx_frames_irq_wanted = -1;
+	s32 coal_rx_usec_low_wanted = -1;
+	s32 coal_rx_frames_low_wanted = -1;
+	s32 coal_tx_usec_low_wanted = -1;
+	s32 coal_tx_frames_low_wanted = -1;
+	s32 coal_rx_usec_high_wanted = -1;
+	s32 coal_rx_frames_high_wanted = -1;
+	s32 coal_tx_usec_high_wanted = -1;
+	s32 coal_tx_frames_high_wanted = -1;
+	struct cmdline_info cmdline_coalesce[] = {
+		{ "adaptive-rx", CMDL_BOOL, &coal_adaptive_rx_wanted,
+		  &ecoal.use_adaptive_rx_coalesce },
+		{ "adaptive-tx", CMDL_BOOL, &coal_adaptive_tx_wanted,
+		  &ecoal.use_adaptive_tx_coalesce },
+		{ "sample-interval", CMDL_S32, &coal_sample_rate_wanted,
+		  &ecoal.rate_sample_interval },
+		{ "stats-block-usecs", CMDL_S32, &coal_stats_wanted,
+		  &ecoal.stats_block_coalesce_usecs },
+		{ "pkt-rate-low", CMDL_S32, &coal_pkt_rate_low_wanted,
+		  &ecoal.pkt_rate_low },
+		{ "pkt-rate-high", CMDL_S32, &coal_pkt_rate_high_wanted,
+		  &ecoal.pkt_rate_high },
+		{ "rx-usecs", CMDL_S32, &coal_rx_usec_wanted,
+		  &ecoal.rx_coalesce_usecs },
+		{ "rx-frames", CMDL_S32, &coal_rx_frames_wanted,
+		  &ecoal.rx_max_coalesced_frames },
+		{ "rx-usecs-irq", CMDL_S32, &coal_rx_usec_irq_wanted,
+		  &ecoal.rx_coalesce_usecs_irq },
+		{ "rx-frames-irq", CMDL_S32, &coal_rx_frames_irq_wanted,
+		  &ecoal.rx_max_coalesced_frames_irq },
+		{ "tx-usecs", CMDL_S32, &coal_tx_usec_wanted,
+		  &ecoal.tx_coalesce_usecs },
+		{ "tx-frames", CMDL_S32, &coal_tx_frames_wanted,
+		  &ecoal.tx_max_coalesced_frames },
+		{ "tx-usecs-irq", CMDL_S32, &coal_tx_usec_irq_wanted,
+		  &ecoal.tx_coalesce_usecs_irq },
+		{ "tx-frames-irq", CMDL_S32, &coal_tx_frames_irq_wanted,
+		  &ecoal.tx_max_coalesced_frames_irq },
+		{ "rx-usecs-low", CMDL_S32, &coal_rx_usec_low_wanted,
+		  &ecoal.rx_coalesce_usecs_low },
+		{ "rx-frames-low", CMDL_S32, &coal_rx_frames_low_wanted,
+		  &ecoal.rx_max_coalesced_frames_low },
+		{ "tx-usecs-low", CMDL_S32, &coal_tx_usec_low_wanted,
+		  &ecoal.tx_coalesce_usecs_low },
+		{ "tx-frames-low", CMDL_S32, &coal_tx_frames_low_wanted,
+		  &ecoal.tx_max_coalesced_frames_low },
+		{ "rx-usecs-high", CMDL_S32, &coal_rx_usec_high_wanted,
+		  &ecoal.rx_coalesce_usecs_high },
+		{ "rx-frames-high", CMDL_S32, &coal_rx_frames_high_wanted,
+		  &ecoal.rx_max_coalesced_frames_high },
+		{ "tx-usecs-high", CMDL_S32, &coal_tx_usec_high_wanted,
+		  &ecoal.tx_coalesce_usecs_high },
+		{ "tx-frames-high", CMDL_S32, &coal_tx_frames_high_wanted,
+		  &ecoal.tx_max_coalesced_frames_high },
+	};
 	int err, changed = 0;
 
 	parse_generic_cmdline(ctx, &gcoalesce_changed,
@@ -1889,6 +1813,35 @@ static int do_goffload(struct cmd_context *ctx)
 
 static int do_soffload(struct cmd_context *ctx)
 {
+	int goffload_changed = 0;
+	int off_csum_rx_wanted = -1;
+	int off_csum_tx_wanted = -1;
+	int off_sg_wanted = -1;
+	int off_tso_wanted = -1;
+	int off_ufo_wanted = -1;
+	int off_gso_wanted = -1;
+	u32 off_flags_wanted = 0;
+	u32 off_flags_mask = 0;
+	int off_gro_wanted = -1;
+	struct cmdline_info cmdline_offload[] = {
+		{ "rx", CMDL_BOOL, &off_csum_rx_wanted, NULL },
+		{ "tx", CMDL_BOOL, &off_csum_tx_wanted, NULL },
+		{ "sg", CMDL_BOOL, &off_sg_wanted, NULL },
+		{ "tso", CMDL_BOOL, &off_tso_wanted, NULL },
+		{ "ufo", CMDL_BOOL, &off_ufo_wanted, NULL },
+		{ "gso", CMDL_BOOL, &off_gso_wanted, NULL },
+		{ "lro", CMDL_FLAG, &off_flags_wanted, NULL,
+		  ETH_FLAG_LRO, &off_flags_mask },
+		{ "gro", CMDL_BOOL, &off_gro_wanted, NULL },
+		{ "rxvlan", CMDL_FLAG, &off_flags_wanted, NULL,
+		  ETH_FLAG_RXVLAN, &off_flags_mask },
+		{ "txvlan", CMDL_FLAG, &off_flags_wanted, NULL,
+		  ETH_FLAG_TXVLAN, &off_flags_mask },
+		{ "ntuple", CMDL_FLAG, &off_flags_wanted, NULL,
+		  ETH_FLAG_NTUPLE, &off_flags_mask },
+		{ "rxhash", CMDL_FLAG, &off_flags_wanted, NULL,
+		  ETH_FLAG_RXHASH, &off_flags_mask },
+	};
 	struct ethtool_value eval;
 	int err, changed = 0;
 
@@ -2064,6 +2017,22 @@ static int do_gset(struct cmd_context *ctx)
 
 static int do_sset(struct cmd_context *ctx)
 {
+	int speed_wanted = -1;
+	int duplex_wanted = -1;
+	int port_wanted = -1;
+	int autoneg_wanted = -1;
+	int phyad_wanted = -1;
+	int xcvr_wanted = -1;
+	int advertising_wanted = -1;
+	int gset_changed = 0; /* did anything in GSET change? */
+	u32 wol_wanted = 0;
+	int wol_change = 0;
+	u8 sopass_wanted[SOPASS_MAX];
+	int sopass_change = 0;
+	int gwol_changed = 0; /* did anything in GWOL change? */
+	int msglvl_changed = 0;
+	u32 msglvl_wanted = 0;
+	u32 msglvl_mask = 0;
 	struct cmdline_info cmdline_msglvl[ARRAY_SIZE(flags_msglvl)];
 	int argc = ctx->argc;
 	char **argp = ctx->argp;
@@ -2349,6 +2318,15 @@ static int do_sset(struct cmd_context *ctx)
 
 static int do_gregs(struct cmd_context *ctx)
 {
+	int gregs_changed = 0;
+	int gregs_dump_raw = 0;
+	int gregs_dump_hex = 0;
+	char *gregs_dump_file = NULL;
+	struct cmdline_info cmdline_gregs[] = {
+		{ "raw", CMDL_BOOL, &gregs_dump_raw, NULL },
+		{ "hex", CMDL_BOOL, &gregs_dump_hex, NULL },
+		{ "file", CMDL_STR, &gregs_dump_file, NULL },
+	};
 	int err;
 	struct ethtool_drvinfo drvinfo;
 	struct ethtool_regs *regs;
@@ -2376,7 +2354,8 @@ static int do_gregs(struct cmd_context *ctx)
 		free(regs);
 		return 74;
 	}
-	if(dump_regs(&drvinfo, regs) < 0) {
+	if (dump_regs(gregs_dump_raw, gregs_dump_hex, gregs_dump_file,
+		      &drvinfo, regs) < 0) {
 		perror("Cannot dump registers");
 		free(regs);
 		return 75;
@@ -2404,6 +2383,15 @@ static int do_nway_rst(struct cmd_context *ctx)
 
 static int do_geeprom(struct cmd_context *ctx)
 {
+	int geeprom_changed = 0;
+	int geeprom_dump_raw = 0;
+	u32 geeprom_offset = 0;
+	u32 geeprom_length = -1;
+	struct cmdline_info cmdline_geeprom[] = {
+		{ "offset", CMDL_U32, &geeprom_offset, NULL },
+		{ "length", CMDL_U32, &geeprom_length, NULL },
+		{ "raw", CMDL_BOOL, &geeprom_dump_raw, NULL },
+	};
 	int err;
 	struct ethtool_drvinfo drvinfo;
 	struct ethtool_eeprom *eeprom;
@@ -2438,7 +2426,7 @@ static int do_geeprom(struct cmd_context *ctx)
 		free(eeprom);
 		return 74;
 	}
-	err = dump_eeprom(&drvinfo, eeprom);
+	err = dump_eeprom(geeprom_dump_raw, &drvinfo, eeprom);
 	free(eeprom);
 
 	return err;
@@ -2446,6 +2434,19 @@ static int do_geeprom(struct cmd_context *ctx)
 
 static int do_seeprom(struct cmd_context *ctx)
 {
+	int seeprom_changed = 0;
+	u32 seeprom_magic = 0;
+	u32 seeprom_length = -1;
+	u32 seeprom_offset = 0;
+	u8 seeprom_value = 0;
+	int seeprom_value_seen = 0;
+	struct cmdline_info cmdline_seeprom[] = {
+		{ "magic", CMDL_U32, &seeprom_magic, NULL },
+		{ "offset", CMDL_U32, &seeprom_offset, NULL },
+		{ "length", CMDL_U32, &seeprom_length, NULL },
+		{ "value", CMDL_U8, &seeprom_value, NULL,
+		  0, &seeprom_value_seen },
+	};
 	int err;
 	struct ethtool_drvinfo drvinfo;
 	struct ethtool_eeprom *eeprom;
@@ -2497,6 +2498,11 @@ static int do_seeprom(struct cmd_context *ctx)
 
 static int do_test(struct cmd_context *ctx)
 {
+	enum {
+		ONLINE=0,
+		OFFLINE,
+		EXTERNAL_LB,
+	} test_type;
 	int err;
 	struct ethtool_drvinfo drvinfo;
 	struct ethtool_test *test;
@@ -2514,6 +2520,8 @@ static int do_test(struct cmd_context *ctx)
 		} else {
 			exit_bad_args();
 		}
+	} else {
+		test_type = OFFLINE;
 	}
 
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
@@ -2573,11 +2581,14 @@ static int do_phys_id(struct cmd_context *ctx)
 {
 	int err;
 	struct ethtool_value edata;
+	int phys_id_time;
 
 	if (ctx->argc > 1)
 		exit_bad_args();
 	if (ctx->argc == 1)
 		phys_id_time = get_int(*ctx->argp, 0);
+	else
+		phys_id_time = 0;
 
 	edata.cmd = ETHTOOL_PHYS_ID;
 	edata.data = phys_id_time;
@@ -2660,10 +2671,13 @@ static int do_gstats(struct cmd_context *ctx)
 
 static int do_srxclass(struct cmd_context *ctx)
 {
-	struct ethtool_rxnfc nfccmd;
 	int err;
 
 	if (ctx->argc == 3 && !strcmp(ctx->argp[0], "rx-flow-hash")) {
+		int rx_fhash_set;
+		u32 rx_fhash_val;
+		struct ethtool_rxnfc nfccmd;
+
 		rx_fhash_set = rxflow_str_to_type(ctx->argp[1]);
 		if (!rx_fhash_set)
 			exit_bad_args();
@@ -2690,6 +2704,8 @@ static int do_grxclass(struct cmd_context *ctx)
 	int err;
 
 	if (ctx->argc == 2 && !strcmp(ctx->argp[0], "rx-flow-hash")) {
+		int rx_fhash_get;
+
 		rx_fhash_get = rxflow_str_to_type(ctx->argp[1]);
 		if (!rx_fhash_get)
 			exit_bad_args();
@@ -2755,6 +2771,8 @@ static int do_grxfhindir(struct cmd_context *ctx)
 
 static int do_srxfhindir(struct cmd_context *ctx)
 {
+	int rxfhindir_equal = 0;
+	char **rxfhindir_weight = NULL;
 	struct ethtool_rxfh_indir indir_head;
 	struct ethtool_rxfh_indir *indir;
 	u32 i;
@@ -2831,6 +2849,8 @@ static int do_srxfhindir(struct cmd_context *ctx)
 
 static int do_flash(struct cmd_context *ctx)
 {
+	char *flash_file;
+	int flash_region;
 	struct ethtool_flash efl;
 	int err;
 
@@ -2841,6 +2861,8 @@ static int do_flash(struct cmd_context *ctx)
 		flash_region = strtol(ctx->argp[1], NULL, 0);
 		if (flash_region < 0)
 			exit_bad_args();
+	} else {
+		flash_region = -1;
 	}
 
 	if (strlen(flash_file) > ETHTOOL_FLASH_MAX_FILENAME - 1) {
@@ -2964,14 +2986,15 @@ static int flow_spec_to_ntuple(struct ethtool_rx_flow_spec *fsp,
 	return 0;
 }
 
-static int do_srxntuple(struct cmd_context *ctx)
+static int do_srxntuple(struct cmd_context *ctx,
+			struct ethtool_rx_flow_spec *rx_rule_fs)
 {
 	struct ethtool_rx_ntuple ntuplecmd;
 	struct ethtool_value eval;
 	int err;
 
 	/* attempt to convert the flow classifier to an ntuple classifier */
-	err = flow_spec_to_ntuple(&rx_rule_fs, &ntuplecmd.fs);
+	err = flow_spec_to_ntuple(rx_rule_fs, &ntuplecmd.fs);
 	if (err)
 		return -1;
 
@@ -3010,14 +3033,16 @@ static int do_srxclsrule(struct cmd_context *ctx)
 	if (ctx->argc < 2)
 		exit_bad_args();
 
-	if (!strcmp(ctx->argp[0], "flow-type")) {
+	if (!strcmp(ctx->argp[0], "flow-type")) {	
+		struct ethtool_rx_flow_spec rx_rule_fs;
+
 		ctx->argc--;
 		ctx->argp++;
 		if (rxclass_parse_ruleopts(ctx, &rx_rule_fs) < 0)
 			exit_bad_args();
 
 		/* attempt to add rule via N-tuple specifier */
-		err = do_srxntuple(ctx);
+		err = do_srxntuple(ctx, &rx_rule_fs);
 		if (!err)
 			return 0;
 
@@ -3029,7 +3054,8 @@ static int do_srxclsrule(struct cmd_context *ctx)
 			return 1;
 		}
 	} else if (!strcmp(ctx->argp[0], "delete")) {
-		rx_class_rule_del = get_uint_range(ctx->argp[1], 0, INT_MAX);
+		int rx_class_rule_del =
+			get_uint_range(ctx->argp[1], 0, INT_MAX);
 
 		err = rxclass_rule_del(ctx, rx_class_rule_del);
 
@@ -3051,7 +3077,8 @@ static int do_grxclsrule(struct cmd_context *ctx)
 	int err;
 
 	if (ctx->argc == 2 && !strcmp(ctx->argp[0], "rule")) {
-		rx_class_rule_get = get_uint_range(ctx->argp[1], 0, INT_MAX);
+		int rx_class_rule_get =
+			get_uint_range(ctx->argp[1], 0, INT_MAX);
 
 		err = rxclass_rule_get(ctx, rx_class_rule_get);
 		if (err < 0)
@@ -3077,7 +3104,7 @@ static int do_grxclsrule(struct cmd_context *ctx)
 	return err ? 1 : 0;
 }
 
-static int do_writefwdump(struct ethtool_dump *dump)
+static int do_writefwdump(struct ethtool_dump *dump, const char *dump_file)
 {
 	int err = 0;
 	FILE *f;
@@ -3105,6 +3132,8 @@ static int do_writefwdump(struct ethtool_dump *dump)
 
 static int do_getfwdump(struct cmd_context *ctx)
 {
+	u32 dump_flag;
+	char *dump_file;
 	int err;
 	struct ethtool_dump edata;
 	struct ethtool_dump *data;
@@ -3112,7 +3141,10 @@ static int do_getfwdump(struct cmd_context *ctx)
 	if (ctx->argc == 2 && !strcmp(ctx->argp[0], "data")) {
 		dump_flag = ETHTOOL_GET_DUMP_DATA;
 		dump_file = ctx->argp[1];
-	} else if (ctx->argc != 0) {
+	} else if (ctx->argc == 0) {
+		dump_flag = 0;
+		dump_file = NULL;
+	} else {
 		exit_bad_args();
 	}
 
@@ -3141,7 +3173,7 @@ static int do_getfwdump(struct cmd_context *ctx)
 		err = 1;
 		goto free;
 	}
-	err = do_writefwdump(data);
+	err = do_writefwdump(data, dump_file);
 free:
 	free(data);
 	return err;
@@ -3149,6 +3181,7 @@ free:
 
 static int do_setfwdump(struct cmd_context *ctx)
 {
+	u32 dump_flag;
 	int err;
 	struct ethtool_dump dump;
 
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 16/21] Replace global devname variable with a field in struct cmd_context
From: Ben Hutchings @ 2011-11-01 23:20 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.c  |   24 +++++++++++-------------
 internal.h |    1 +
 2 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index 438fc26..d0929c7 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -285,8 +285,6 @@ static int show_usage(struct cmd_context *ctx)
 	return 0;
 }
 
-static char *devname = NULL;
-
 static int goffload_changed = 0;
 static int off_csum_rx_wanted = -1;
 static int off_csum_tx_wanted = -1;
@@ -1540,7 +1538,7 @@ static int do_gpause(struct cmd_context *ctx)
 	if (ctx->argc != 0)
 		exit_bad_args();
 
-	fprintf(stdout, "Pause parameters for %s:\n", devname);
+	fprintf(stdout, "Pause parameters for %s:\n", ctx->devname);
 
 	epause.cmd = ETHTOOL_GPAUSEPARAM;
 	err = send_ioctl(ctx, &epause);
@@ -1662,7 +1660,7 @@ static int do_gring(struct cmd_context *ctx)
 	if (ctx->argc != 0)
 		exit_bad_args();
 
-	fprintf(stdout, "Ring parameters for %s:\n", devname);
+	fprintf(stdout, "Ring parameters for %s:\n", ctx->devname);
 
 	ering.cmd = ETHTOOL_GRINGPARAM;
 	err = send_ioctl(ctx, &ering);
@@ -1721,7 +1719,7 @@ static int do_gchannels(struct cmd_context *ctx)
 	if (ctx->argc != 0)
 		exit_bad_args();
 
-	fprintf(stdout, "Channel parameters for %s:\n", devname);
+	fprintf(stdout, "Channel parameters for %s:\n", ctx->devname);
 
 	echannels.cmd = ETHTOOL_GCHANNELS;
 	err = send_ioctl(ctx, &echannels);
@@ -1744,7 +1742,7 @@ static int do_gcoalesce(struct cmd_context *ctx)
 	if (ctx->argc != 0)
 		exit_bad_args();
 
-	fprintf(stdout, "Coalesce parameters for %s:\n", devname);
+	fprintf(stdout, "Coalesce parameters for %s:\n", ctx->devname);
 
 	ecoal.cmd = ETHTOOL_GCOALESCE;
 	err = send_ioctl(ctx, &ecoal);
@@ -1802,7 +1800,7 @@ static int do_goffload(struct cmd_context *ctx)
 	if (ctx->argc != 0)
 		exit_bad_args();
 
-	fprintf(stdout, "Offload parameters for %s:\n", devname);
+	fprintf(stdout, "Offload parameters for %s:\n", ctx->devname);
 
 	eval.cmd = ETHTOOL_GRXCSUM;
 	err = send_ioctl(ctx, &eval);
@@ -2009,7 +2007,7 @@ static int do_gset(struct cmd_context *ctx)
 	if (ctx->argc != 0)
 		exit_bad_args();
 
-	fprintf(stdout, "Settings for %s:\n", devname);
+	fprintf(stdout, "Settings for %s:\n", ctx->devname);
 
 	ecmd.cmd = ETHTOOL_GSET;
 	err = send_ioctl(ctx, &ecmd);
@@ -2744,7 +2742,7 @@ static int do_grxfhindir(struct cmd_context *ctx)
 	}
 
 	printf("RX flow hash indirection table for %s with %llu RX ring(s):\n",
-	       devname, ring_count.data);
+	       ctx->devname, ring_count.data);
 	for (i = 0; i < indir->size; i++) {
 		if (i % 8 == 0)
 			printf("%5u: ", i);
@@ -3222,17 +3220,17 @@ int main(int argc, char **argp, char **envp)
 
 opt_found:
 	if (want_device) {
-		devname = *argp++;
+		ctx.devname = *argp++;
 		argc--;
 
-		if (devname == NULL)
+		if (ctx.devname == NULL)
 			exit_bad_args();
-		if (strlen(devname) >= IFNAMSIZ)
+		if (strlen(ctx.devname) >= IFNAMSIZ)
 			exit_bad_args();
 
 		/* Setup our control structures. */
 		memset(&ctx.ifr, 0, sizeof(ctx.ifr));
-		strcpy(ctx.ifr.ifr_name, devname);
+		strcpy(ctx.ifr.ifr_name, ctx.devname);
 
 		/* Open control socket. */
 		ctx.fd = socket(AF_INET, SOCK_DGRAM, 0);
diff --git a/internal.h b/internal.h
index eb13db8..cb126b3 100644
--- a/internal.h
+++ b/internal.h
@@ -90,6 +90,7 @@ static inline int test_bit(unsigned int nr, const unsigned long *addr)
 
 /* Context for sub-commands */
 struct cmd_context {
+	const char *devname;	/* net device name */
 	int fd;			/* socket suitable for ethtool ioctl */
 	struct ifreq ifr;	/* ifreq suitable for ethtool ioctl */
 	int argc;		/* number of arguments to the sub-command */
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 15/21] Convert cmdline_msglvl into array of named flags; convert back at run-time
From: Ben Hutchings @ 2011-11-01 23:19 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

cmdline_msglvl is used both in do_gset() and do_sset(), but it refers
to variables only used in do_sset().  I want to get rid of the global
variables without duplicating the flag definitions.  So separate out
the flag definitions into a new structure and generate cmdline_msglvl
from that at run-time.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.c |   79 ++++++++++++++++++++++++++++++++++---------------------------
 1 files changed, 44 insertions(+), 35 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index 7bdf173..438fc26 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -426,6 +426,11 @@ struct cmdline_info {
 	void *seen_val;
 };
 
+struct flag_info {
+	const char *name;
+	u32 value;
+};
+
 static struct cmdline_info cmdline_gregs[] = {
 	{ "raw", CMDL_BOOL, &gregs_dump_raw, NULL },
 	{ "hex", CMDL_BOOL, &gregs_dump_hex, NULL },
@@ -511,37 +516,22 @@ static struct cmdline_info cmdline_coalesce[] = {
 	{ "tx-frames-high", CMDL_S32, &coal_tx_frames_high_wanted, &ecoal.tx_max_coalesced_frames_high },
 };
 
-static struct cmdline_info cmdline_msglvl[] = {
-	{ "drv", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_DRV, &msglvl_mask },
-	{ "probe", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_PROBE, &msglvl_mask },
-	{ "link", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_LINK, &msglvl_mask },
-	{ "timer", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_TIMER, &msglvl_mask },
-	{ "ifdown", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_IFDOWN, &msglvl_mask },
-	{ "ifup", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_IFUP, &msglvl_mask },
-	{ "rx_err", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_RX_ERR, &msglvl_mask },
-	{ "tx_err", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_TX_ERR, &msglvl_mask },
-	{ "tx_queued", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_TX_QUEUED, &msglvl_mask },
-	{ "intr", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_INTR, &msglvl_mask },
-	{ "tx_done", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_TX_DONE, &msglvl_mask },
-	{ "rx_status", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_RX_STATUS, &msglvl_mask },
-	{ "pktdata", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_PKTDATA, &msglvl_mask },
-	{ "hw", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_HW, &msglvl_mask },
-	{ "wol", CMDL_FLAG, &msglvl_wanted, NULL,
-	  NETIF_MSG_WOL, &msglvl_mask },
+static const struct flag_info flags_msglvl[] = {
+	{ "drv",	NETIF_MSG_DRV },
+	{ "probe",	NETIF_MSG_PROBE },
+	{ "link",	NETIF_MSG_LINK },
+	{ "timer",	NETIF_MSG_TIMER },
+	{ "ifdown",	NETIF_MSG_IFDOWN },
+	{ "ifup",	NETIF_MSG_IFUP },
+	{ "rx_err",	NETIF_MSG_RX_ERR },
+	{ "tx_err",	NETIF_MSG_TX_ERR },
+	{ "tx_queued",	NETIF_MSG_TX_QUEUED },
+	{ "intr",	NETIF_MSG_INTR },
+	{ "tx_done",	NETIF_MSG_TX_DONE },
+	{ "rx_status",	NETIF_MSG_RX_STATUS },
+	{ "pktdata",	NETIF_MSG_PKTDATA },
+	{ "hw",		NETIF_MSG_HW },
+	{ "wol",	NETIF_MSG_WOL },
 };
 
 static long long
@@ -694,16 +684,28 @@ static void parse_generic_cmdline(struct cmd_context *ctx,
 	}
 }
 
+static void flag_to_cmdline_info(const char *name, u32 value,
+				 u32 *wanted, u32 *mask,
+				 struct cmdline_info *cli)
+{
+	memset(cli, 0, sizeof(*cli));
+	cli->name = name;
+	cli->type = CMDL_FLAG;
+	cli->flag_val = value;
+	cli->wanted_val = wanted;
+	cli->seen_val = mask;
+}
+
 static void
-print_flags(const struct cmdline_info *info, unsigned int n_info, u32 value)
+print_flags(const struct flag_info *info, unsigned int n_info, u32 value)
 {
 	const char *sep = "";
 
 	while (n_info) {
-		if (info->type == CMDL_FLAG && value & info->flag_val) {
+		if (value & info->value) {
 			printf("%s%s", sep, info->name);
 			sep = " ";
-			value &= ~info->flag_val;
+			value &= ~info->value;
 		}
 		++info;
 		--n_info;
@@ -2037,7 +2039,7 @@ static int do_gset(struct cmd_context *ctx)
 		fprintf(stdout, "	Current message level: 0x%08x (%d)\n"
 			"			       ",
 			edata.data, edata.data);
-		print_flags(cmdline_msglvl, ARRAY_SIZE(cmdline_msglvl),
+		print_flags(flags_msglvl, ARRAY_SIZE(flags_msglvl),
 			    edata.data);
 		fprintf(stdout, "\n");
 		allfail = 0;
@@ -2064,11 +2066,18 @@ static int do_gset(struct cmd_context *ctx)
 
 static int do_sset(struct cmd_context *ctx)
 {
+	struct cmdline_info cmdline_msglvl[ARRAY_SIZE(flags_msglvl)];
 	int argc = ctx->argc;
 	char **argp = ctx->argp;
 	int i;
 	int err;
 
+	for (i = 0; i < ARRAY_SIZE(flags_msglvl); i++)
+		flag_to_cmdline_info(flags_msglvl[i].name,
+				     flags_msglvl[i].value,
+				     &msglvl_wanted, &msglvl_mask,
+				     &cmdline_msglvl[i]);
+
 	for (i = 0; i < argc; i++) {
 		if (!strcmp(argp[i], "speed")) {
 			gset_changed = 1;
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 14/21] Fix reference to cmdline_ring in do_schannels()
From: Ben Hutchings @ 2011-11-01 23:18 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

cmdline_ring and cmdline_channels have the same array size, so this
just happened to work.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index 9d416e1..7bdf173 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -1681,7 +1681,7 @@ static int do_schannels(struct cmd_context *ctx)
 	int err, changed = 0;
 
 	parse_generic_cmdline(ctx, &gchannels_changed,
-			      cmdline_channels, ARRAY_SIZE(cmdline_ring));
+			      cmdline_channels, ARRAY_SIZE(cmdline_channels));
 
 	echannels.cmd = ETHTOOL_GCHANNELS;
 	err = send_ioctl(ctx, &echannels);
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 13/21] Support arbitrary numbers of option names for each mode
From: Ben Hutchings @ 2011-11-01 23:18 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

Instead of supporting a single short and long option for each
mode, take a string of options separated by "|" (matching the
way they are displayed in online help).

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.c |  110 ++++++++++++++++++++++++++++++++-----------------------------
 1 files changed, 58 insertions(+), 52 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index 4eff916..9d416e1 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -105,13 +105,13 @@ static int do_getfwdump(struct cmd_context *ctx);
 static int do_setfwdump(struct cmd_context *ctx);
 
 static struct option {
-	char *srt, *lng;
+	const char *opts;
 	int want_device;
 	int (*func)(struct cmd_context *);
 	char *help;
 	char *opthelp;
 } args[] = {
-	{ "-s", "--change", 1, do_sset, "Change generic options",
+	{ "-s|--change", 1, do_sset, "Change generic options",
 	  "		[ speed %d ]\n"
 	  "		[ duplex half|full ]\n"
 	  "		[ port tp|aui|bnc|mii|fibre ]\n"
@@ -122,13 +122,13 @@ static struct option {
 	  "		[ wol p|u|m|b|a|g|s|d... ]\n"
 	  "		[ sopass %x:%x:%x:%x:%x:%x ]\n"
 	  "		[ msglvl %d | msglvl type on|off ... ]\n" },
-	{ "-a", "--show-pause", 1, do_gpause, "Show pause options" },
-	{ "-A", "--pause", 1, do_spause, "Set pause options",
+	{ "-a|--show-pause", 1, do_gpause, "Show pause options" },
+	{ "-A|--pause", 1, do_spause, "Set pause options",
 	  "		[ autoneg on|off ]\n"
 	  "		[ rx on|off ]\n"
 	  "		[ tx on|off ]\n" },
-	{ "-c", "--show-coalesce", 1, do_gcoalesce, "Show coalesce options" },
-	{ "-C", "--coalesce", 1, do_scoalesce, "Set coalesce options",
+	{ "-c|--show-coalesce", 1, do_gcoalesce, "Show coalesce options" },
+	{ "-C|--coalesce", 1, do_scoalesce, "Set coalesce options",
 	  "		[adaptive-rx on|off]\n"
 	  "		[adaptive-tx on|off]\n"
 	  "		[rx-usecs N]\n"
@@ -151,15 +151,15 @@ static struct option {
 	  "		[tx-usecs-high N]\n"
 	  "		[tx-frames-high N]\n"
 	  "		[sample-interval N]\n" },
-	{ "-g", "--show-ring", 1, do_gring, "Query RX/TX ring parameters" },
-	{ "-G", "--set-ring", 1, do_sring, "Set RX/TX ring parameters",
+	{ "-g|--show-ring", 1, do_gring, "Query RX/TX ring parameters" },
+	{ "-G|--set-ring", 1, do_sring, "Set RX/TX ring parameters",
 	  "		[ rx N ]\n"
 	  "		[ rx-mini N ]\n"
 	  "		[ rx-jumbo N ]\n"
 	  "		[ tx N ]\n" },
-	{ "-k", "--show-offload", 1, do_goffload,
+	{ "-k|--show-offload", 1, do_goffload,
 	  "Get protocol offload information" },
-	{ "-K", "--offload", 1, do_soffload, "Set protocol offload",
+	{ "-K|--offload", 1, do_soffload, "Set protocol offload",
 	  "		[ rx on|off ]\n"
 	  "		[ tx on|off ]\n"
 	  "		[ sg on|off ]\n"
@@ -173,44 +173,44 @@ static struct option {
 	  "		[ ntuple on|off ]\n"
 	  "		[ rxhash on|off ]\n"
 	},
-	{ "-i", "--driver", 1, do_gdrv, "Show driver information" },
-	{ "-d", "--register-dump", 1, do_gregs, "Do a register dump",
+	{ "-i|--driver", 1, do_gdrv, "Show driver information" },
+	{ "-d|--register-dump", 1, do_gregs, "Do a register dump",
 	  "		[ raw on|off ]\n"
 	  "		[ file FILENAME ]\n" },
-	{ "-e", "--eeprom-dump", 1, do_geeprom, "Do a EEPROM dump",
+	{ "-e|--eeprom-dump", 1, do_geeprom, "Do a EEPROM dump",
 	  "		[ raw on|off ]\n"
 	  "		[ offset N ]\n"
 	  "		[ length N ]\n" },
-	{ "-E", "--change-eeprom", 1, do_seeprom,
+	{ "-E|--change-eeprom", 1, do_seeprom,
 	  "Change bytes in device EEPROM",
 	  "		[ magic N ]\n"
 	  "		[ offset N ]\n"
 	  "		[ length N ]\n"
 	  "		[ value N ]\n" },
-	{ "-r", "--negotiate", 1, do_nway_rst, "Restart N-WAY negotiation" },
-	{ "-p", "--identify", 1, do_phys_id,
+	{ "-r|--negotiate", 1, do_nway_rst, "Restart N-WAY negotiation" },
+	{ "-p|--identify", 1, do_phys_id,
 	  "Show visible port identification (e.g. blinking)",
 	  "               [ TIME-IN-SECONDS ]\n" },
-	{ "-t", "--test", 1, do_test, "Execute adapter self test",
+	{ "-t|--test", 1, do_test, "Execute adapter self test",
 	  "               [ online | offline | external_lb ]\n" },
-	{ "-S", "--statistics", 1, do_gstats, "Show adapter statistics" },
-	{ "-n", "--show-nfc", 1, do_grxclass,
+	{ "-S|--statistics", 1, do_gstats, "Show adapter statistics" },
+	{ "-n|--show-nfc", 1, do_grxclass,
 	  "Show Rx network flow classification options",
 	  "		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
 	  "tcp6|udp6|ah6|esp6|sctp6 ]\n" },
-	{ "-f", "--flash", 1, do_flash,
+	{ "-f|--flash", 1, do_flash,
 	  "Flash firmware image from the specified file to a region on the device",
 	  "               FILENAME [ REGION-NUMBER-TO-FLASH ]\n" },
-	{ "-N", "--config-nfc", 1, do_srxclass,
+	{ "-N|--config-nfc", 1, do_srxclass,
 	  "Configure Rx network flow classification options",
 	  "		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
 	  "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... ]\n" },
-	{ "-x", "--show-rxfh-indir", 1, do_grxfhindir,
+	{ "-x|--show-rxfh-indir", 1, do_grxfhindir,
 	  "Show Rx flow hash indirection" },
-	{ "-X", "--set-rxfh-indir", 1, do_srxfhindir,
+	{ "-X|--set-rxfh-indir", 1, do_srxfhindir,
 	  "Set Rx flow hash indirection",
 	  "		equal N | weight W0 W1 ...\n" },
-	{ "-U", "--config-ntuple", 1, do_srxclsrule,
+	{ "-U|--config-ntuple", 1, do_srxclsrule,
 	  "Configure Rx ntuple filters and actions",
 	  "		[ delete %d ] |\n"
 	  "		[ flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4\n"
@@ -229,25 +229,25 @@ static struct option {
 	  "			[ user-def %x [m %x] ]\n"
 	  "			[ action %d ]\n"
 	  "			[ loc %d]]\n" },
-	{ "-u", "--show-ntuple", 1, do_grxclsrule,
+	{ "-u|--show-ntuple", 1, do_grxclsrule,
 	  "Get Rx ntuple filters and actions",
 	  "		[ rule %d ]\n"},
-	{ "-P", "--show-permaddr", 1, do_permaddr,
+	{ "-P|--show-permaddr", 1, do_permaddr,
 	  "Show permanent hardware address" },
-	{ "-w", "--get-dump", 1, do_getfwdump,
+	{ "-w|--get-dump", 1, do_getfwdump,
 	  "Get dump flag, data",
 	  "		[ data FILENAME ]\n" },
-	{ "-W", "--set-dump", 1, do_setfwdump,
+	{ "-W|--set-dump", 1, do_setfwdump,
 	  "Set dump flag of the device",
 	  "		N\n"},
-	{ "-l", "--show-channels", 1, do_gchannels, "Query Channels" },
-	{ "-L", "--set-channels", 1, do_schannels, "Set Channels",
+	{ "-l|--show-channels", 1, do_gchannels, "Query Channels" },
+	{ "-L|--set-channels", 1, do_schannels, "Set Channels",
 	  "               [ rx N ]\n"
 	  "               [ tx N ]\n"
 	  "               [ other N ]\n"
 	  "               [ combined N ]\n" },
-	{ "-h", "--help", 0, show_usage, "Show this help" },
-	{ NULL, "--version", 0, do_version, "Show version number" },
+	{ "-h|--help", 0, show_usage, "Show this help" },
+	{ "--version", 0, do_version, "Show version number" },
 	{}
 };
 
@@ -272,12 +272,10 @@ static int show_usage(struct cmd_context *ctx)
 		"Usage:\n"
 		"        ethtool DEVNAME\t"
 		"Display standard information about device\n");
-	for (i = 0; args[i].lng; i++) {
+	for (i = 0; args[i].opts; i++) {
 		fputs("        ethtool ", stdout);
-		if (args[i].srt)
-			fprintf(stdout, "%s|", args[i].srt);
 		fprintf(stdout, "%s %s\t%s\n",
-			args[i].lng,
+			args[i].opts,
 			args[i].want_device ? "DEVNAME" : "\t",
 			args[i].help);
 		if (args[i].opthelp)
@@ -3174,8 +3172,8 @@ int send_ioctl(struct cmd_context *ctx, void *cmd)
 
 int main(int argc, char **argp, char **envp)
 {
-	int (*func)(struct cmd_context *) = NULL;
-	int want_device = 0;
+	int (*func)(struct cmd_context *);
+	int want_device;
 	struct cmd_context ctx;
 	int k;
 
@@ -3189,23 +3187,31 @@ int main(int argc, char **argp, char **envp)
 	 */
 	if (argc == 0)
 		exit_bad_args();
-	for (k = 0; args[k].lng; k++) {
-		if ((args[k].srt && !strcmp(*argp, args[k].srt)) ||
-		    !strcmp(*argp, args[k].lng)) {
-			argp++;
-			argc--;
-			func = args[k].func;
-			want_device = args[k].want_device;
-			break;
+	for (k = 0; args[k].opts; k++) {
+		const char *opt;
+		size_t len;
+		opt = args[k].opts;
+		for (;;) {
+			len = strcspn(opt, "|");
+			if (strncmp(*argp, opt, len) == 0 &&
+			    (*argp)[len] == 0) {
+				argp++;
+				argc--;
+				func = args[k].func;
+				want_device = args[k].want_device;
+				goto opt_found;
+			}
+			if (opt[len] == 0)
+				break;
+			opt += len + 1;
 		}
 	}
-	if (!func) {
-		if ((*argp)[0] == '-')
-			exit_bad_args();
-		func = do_gset;
-		want_device = 1;
-	}
+	if ((*argp)[0] == '-')
+		exit_bad_args();
+	func = do_gset;
+	want_device = 1;
 
+opt_found:
 	if (want_device) {
 		devname = *argp++;
 		argc--;
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 12/21] Move argument parsing to sub-command functions
From: Ben Hutchings @ 2011-11-01 23:18 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

Instead of doing most of the argument parsing in parse_cmdline(),
find the sub-command and (if required) the device name in main()
and do the rest in the sub-command handler function.  Pass argc
and argp around in struct cmd_context.

This also tightens up argument parsing slightly: extraneous or missing
arguments will now result in an error in a few cases where they were
previously ignored.  All test cases now pass.

Replace sub-command dispatch in doit() with a function pointer
in struct option.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.8.in |    4 +-
 ethtool.c    |  988 +++++++++++++++++++++++-----------------------------------
 internal.h   |    4 +-
 rxclass.c    |    4 +-
 4 files changed, 390 insertions(+), 610 deletions(-)

diff --git a/ethtool.8.in b/ethtool.8.in
index 3304fe5..7c39629 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -252,11 +252,11 @@ ethtool \- query or control network driver and hardware settings
 .HP
 .B ethtool \-n
 .I devname
-.RB [ rx\-flow\-hash \ \*(FL]
+.BR rx\-flow\-hash \ \*(FL
 .HP
 .B ethtool \-N
 .I devname
-.RB [ rx\-flow\-hash \ \*(FL \: \*(HO]
+.BR rx\-flow\-hash \ \*(FL \: \*(HO
 .HP
 .B ethtool \-w|\-\-get\-dump
 .I devname
diff --git a/ethtool.c b/ethtool.c
index 7a26043..4eff916 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -64,6 +64,8 @@ enum {
 };
 #endif
 
+static int show_usage(struct cmd_context *ctx);
+static int do_version(struct cmd_context *ctx);
 static int parse_wolopts(char *optstr, u32 *data);
 static char *unparse_wolopts(int wolopts);
 static void get_mac_addr(char *src, unsigned char *dest);
@@ -102,48 +104,14 @@ static int do_permaddr(struct cmd_context *ctx);
 static int do_getfwdump(struct cmd_context *ctx);
 static int do_setfwdump(struct cmd_context *ctx);
 
-static enum {
-	MODE_VERSION = -2,
-	MODE_HELP = -1,
-	MODE_GSET=0,
-	MODE_SSET,
-	MODE_GDRV,
-	MODE_GREGS,
-	MODE_NWAY_RST,
-	MODE_GEEPROM,
-	MODE_SEEPROM,
-	MODE_TEST,
-	MODE_PHYS_ID,
-	MODE_GPAUSE,
-	MODE_SPAUSE,
-	MODE_GCOALESCE,
-	MODE_SCOALESCE,
-	MODE_GRING,
-	MODE_SRING,
-	MODE_GOFFLOAD,
-	MODE_SOFFLOAD,
-	MODE_GSTATS,
-	MODE_GNFC,
-	MODE_SNFC,
-	MODE_GRXFHINDIR,
-	MODE_SRXFHINDIR,
-	MODE_SCLSRULE,
-	MODE_GCLSRULE,
-	MODE_FLASHDEV,
-	MODE_PERMADDR,
-	MODE_SET_DUMP,
-	MODE_GET_DUMP,
-	MODE_GCHANNELS,
-	MODE_SCHANNELS
-} mode = MODE_GSET;
-
 static struct option {
 	char *srt, *lng;
-	int Mode;
+	int want_device;
+	int (*func)(struct cmd_context *);
 	char *help;
 	char *opthelp;
 } args[] = {
-	{ "-s", "--change", MODE_SSET, "Change generic options",
+	{ "-s", "--change", 1, do_sset, "Change generic options",
 	  "		[ speed %d ]\n"
 	  "		[ duplex half|full ]\n"
 	  "		[ port tp|aui|bnc|mii|fibre ]\n"
@@ -154,13 +122,13 @@ static struct option {
 	  "		[ wol p|u|m|b|a|g|s|d... ]\n"
 	  "		[ sopass %x:%x:%x:%x:%x:%x ]\n"
 	  "		[ msglvl %d | msglvl type on|off ... ]\n" },
-	{ "-a", "--show-pause", MODE_GPAUSE, "Show pause options" },
-	{ "-A", "--pause", MODE_SPAUSE, "Set pause options",
+	{ "-a", "--show-pause", 1, do_gpause, "Show pause options" },
+	{ "-A", "--pause", 1, do_spause, "Set pause options",
 	  "		[ autoneg on|off ]\n"
 	  "		[ rx on|off ]\n"
 	  "		[ tx on|off ]\n" },
-	{ "-c", "--show-coalesce", MODE_GCOALESCE, "Show coalesce options" },
-	{ "-C", "--coalesce", MODE_SCOALESCE, "Set coalesce options",
+	{ "-c", "--show-coalesce", 1, do_gcoalesce, "Show coalesce options" },
+	{ "-C", "--coalesce", 1, do_scoalesce, "Set coalesce options",
 	  "		[adaptive-rx on|off]\n"
 	  "		[adaptive-tx on|off]\n"
 	  "		[rx-usecs N]\n"
@@ -183,15 +151,15 @@ static struct option {
 	  "		[tx-usecs-high N]\n"
 	  "		[tx-frames-high N]\n"
 	  "		[sample-interval N]\n" },
-	{ "-g", "--show-ring", MODE_GRING, "Query RX/TX ring parameters" },
-	{ "-G", "--set-ring", MODE_SRING, "Set RX/TX ring parameters",
+	{ "-g", "--show-ring", 1, do_gring, "Query RX/TX ring parameters" },
+	{ "-G", "--set-ring", 1, do_sring, "Set RX/TX ring parameters",
 	  "		[ rx N ]\n"
 	  "		[ rx-mini N ]\n"
 	  "		[ rx-jumbo N ]\n"
 	  "		[ tx N ]\n" },
-	{ "-k", "--show-offload", MODE_GOFFLOAD,
+	{ "-k", "--show-offload", 1, do_goffload,
 	  "Get protocol offload information" },
-	{ "-K", "--offload", MODE_SOFFLOAD, "Set protocol offload",
+	{ "-K", "--offload", 1, do_soffload, "Set protocol offload",
 	  "		[ rx on|off ]\n"
 	  "		[ tx on|off ]\n"
 	  "		[ sg on|off ]\n"
@@ -205,44 +173,44 @@ static struct option {
 	  "		[ ntuple on|off ]\n"
 	  "		[ rxhash on|off ]\n"
 	},
-	{ "-i", "--driver", MODE_GDRV, "Show driver information" },
-	{ "-d", "--register-dump", MODE_GREGS, "Do a register dump",
+	{ "-i", "--driver", 1, do_gdrv, "Show driver information" },
+	{ "-d", "--register-dump", 1, do_gregs, "Do a register dump",
 	  "		[ raw on|off ]\n"
 	  "		[ file FILENAME ]\n" },
-	{ "-e", "--eeprom-dump", MODE_GEEPROM, "Do a EEPROM dump",
+	{ "-e", "--eeprom-dump", 1, do_geeprom, "Do a EEPROM dump",
 	  "		[ raw on|off ]\n"
 	  "		[ offset N ]\n"
 	  "		[ length N ]\n" },
-	{ "-E", "--change-eeprom", MODE_SEEPROM,
+	{ "-E", "--change-eeprom", 1, do_seeprom,
 	  "Change bytes in device EEPROM",
 	  "		[ magic N ]\n"
 	  "		[ offset N ]\n"
 	  "		[ length N ]\n"
 	  "		[ value N ]\n" },
-	{ "-r", "--negotiate", MODE_NWAY_RST, "Restart N-WAY negotiation" },
-	{ "-p", "--identify", MODE_PHYS_ID,
+	{ "-r", "--negotiate", 1, do_nway_rst, "Restart N-WAY negotiation" },
+	{ "-p", "--identify", 1, do_phys_id,
 	  "Show visible port identification (e.g. blinking)",
 	  "               [ TIME-IN-SECONDS ]\n" },
-	{ "-t", "--test", MODE_TEST, "Execute adapter self test",
+	{ "-t", "--test", 1, do_test, "Execute adapter self test",
 	  "               [ online | offline | external_lb ]\n" },
-	{ "-S", "--statistics", MODE_GSTATS, "Show adapter statistics" },
-	{ "-n", "--show-nfc", MODE_GNFC,
+	{ "-S", "--statistics", 1, do_gstats, "Show adapter statistics" },
+	{ "-n", "--show-nfc", 1, do_grxclass,
 	  "Show Rx network flow classification options",
 	  "		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
 	  "tcp6|udp6|ah6|esp6|sctp6 ]\n" },
-	{ "-f", "--flash", MODE_FLASHDEV,
+	{ "-f", "--flash", 1, do_flash,
 	  "Flash firmware image from the specified file to a region on the device",
 	  "               FILENAME [ REGION-NUMBER-TO-FLASH ]\n" },
-	{ "-N", "--config-nfc", MODE_SNFC,
+	{ "-N", "--config-nfc", 1, do_srxclass,
 	  "Configure Rx network flow classification options",
 	  "		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
 	  "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... ]\n" },
-	{ "-x", "--show-rxfh-indir", MODE_GRXFHINDIR,
+	{ "-x", "--show-rxfh-indir", 1, do_grxfhindir,
 	  "Show Rx flow hash indirection" },
-	{ "-X", "--set-rxfh-indir", MODE_SRXFHINDIR,
+	{ "-X", "--set-rxfh-indir", 1, do_srxfhindir,
 	  "Set Rx flow hash indirection",
 	  "		equal N | weight W0 W1 ...\n" },
-	{ "-U", "--config-ntuple", MODE_SCLSRULE,
+	{ "-U", "--config-ntuple", 1, do_srxclsrule,
 	  "Configure Rx ntuple filters and actions",
 	  "		[ delete %d ] |\n"
 	  "		[ flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4\n"
@@ -261,25 +229,25 @@ static struct option {
 	  "			[ user-def %x [m %x] ]\n"
 	  "			[ action %d ]\n"
 	  "			[ loc %d]]\n" },
-	{ "-u", "--show-ntuple", MODE_GCLSRULE,
+	{ "-u", "--show-ntuple", 1, do_grxclsrule,
 	  "Get Rx ntuple filters and actions",
 	  "		[ rule %d ]\n"},
-	{ "-P", "--show-permaddr", MODE_PERMADDR,
+	{ "-P", "--show-permaddr", 1, do_permaddr,
 	  "Show permanent hardware address" },
-	{ "-w", "--get-dump", MODE_GET_DUMP,
+	{ "-w", "--get-dump", 1, do_getfwdump,
 	  "Get dump flag, data",
 	  "		[ data FILENAME ]\n" },
-	{ "-W", "--set-dump", MODE_SET_DUMP,
+	{ "-W", "--set-dump", 1, do_setfwdump,
 	  "Set dump flag of the device",
 	  "		N\n"},
-	{ "-l", "--show-channels", MODE_GCHANNELS, "Query Channels" },
-	{ "-L", "--set-channels", MODE_SCHANNELS, "Set Channels",
+	{ "-l", "--show-channels", 1, do_gchannels, "Query Channels" },
+	{ "-L", "--set-channels", 1, do_schannels, "Set Channels",
 	  "               [ rx N ]\n"
 	  "               [ tx N ]\n"
 	  "               [ other N ]\n"
 	  "               [ combined N ]\n" },
-	{ "-h", "--help", MODE_HELP, "Show this help" },
-	{ NULL, "--version", MODE_VERSION, "Show version number" },
+	{ "-h", "--help", 0, show_usage, "Show this help" },
+	{ NULL, "--version", 0, do_version, "Show version number" },
 	{}
 };
 
@@ -294,7 +262,7 @@ static void exit_bad_args(void)
 	exit(1);
 }
 
-static void show_usage(void)
+static int show_usage(struct cmd_context *ctx)
 {
 	int i;
 
@@ -310,11 +278,13 @@ static void show_usage(void)
 			fprintf(stdout, "%s|", args[i].srt);
 		fprintf(stdout, "%s %s\t%s\n",
 			args[i].lng,
-			args[i].Mode < 0 ? "\t" : "DEVNAME",
+			args[i].want_device ? "DEVNAME" : "\t",
 			args[i].help);
 		if (args[i].opthelp)
 			fputs(args[i].opthelp, stdout);
 	}
+
+	return 0;
 }
 
 static char *devname = NULL;
@@ -406,11 +376,9 @@ static int seeprom_value_seen = 0;
 static int rx_fhash_get = 0;
 static int rx_fhash_set = 0;
 static u32 rx_fhash_val = 0;
-static int rx_fhash_changed = 0;
 static int rxfhindir_equal = 0;
 static char **rxfhindir_weight = NULL;
 static char *flash_file = NULL;
-static int flash = -1;
 static int flash_region = -1;
 
 static int msglvl_changed;
@@ -421,7 +389,6 @@ static char *dump_file = NULL;
 
 static int rx_class_rule_get = -1;
 static int rx_class_rule_del = -1;
-static int rx_class_rule_added = 0;
 static struct ethtool_rx_flow_spec rx_rule_fs;
 
 static enum {
@@ -619,15 +586,17 @@ static u32 get_u32(char *str, int base)
 	return get_uint_range(str, base, 0xffffffff);
 }
 
-static void parse_generic_cmdline(int argc, char **argp,
-				  int first_arg, int *changed,
+static void parse_generic_cmdline(struct cmd_context *ctx,
+				  int *changed,
 				  struct cmdline_info *info,
 				  unsigned int n_info)
 {
+	int argc = ctx->argc;
+	char **argp = ctx->argp;
 	int i, idx;
 	int found;
 
-	for (i = first_arg; i < argc; i++) {
+	for (i = 0; i < argc; i++) {
 		found = 0;
 		for (idx = 0; idx < n_info; idx++) {
 			if (!strcmp(info[idx].name, argp[i])) {
@@ -773,443 +742,11 @@ static int rxflow_str_to_type(const char *str)
 	return flow_type;
 }
 
-static void parse_cmdline(int argc, char **argp)
+static int do_version(struct cmd_context *ctx)
 {
-	int i, k;
-
-	for (i = 1; i < argc; i++) {
-		switch (i) {
-		case 1:
-			for (k = 0; args[k].lng; k++)
-				if ((args[k].srt &&
-				     !strcmp(argp[i], args[k].srt)) ||
-				    !strcmp(argp[i], args[k].lng)) {
-					mode = args[k].Mode;
-					break;
-				}
-			if (mode == MODE_HELP) {
-				show_usage();
-				exit(0);
-			} else if (mode == MODE_VERSION) {
-				fprintf(stdout,
-					PACKAGE " version " VERSION "\n");
-				exit(0);
-			} else if (!args[k].lng && argp[i][0] == '-') {
-				exit_bad_args();
-			} else {
-				devname = argp[i];
-			}
-			break;
-		case 2:
-			if ((mode == MODE_SSET) ||
-			    (mode == MODE_GDRV) ||
-			    (mode == MODE_GREGS)||
-			    (mode == MODE_NWAY_RST) ||
-			    (mode == MODE_TEST) ||
-			    (mode == MODE_GEEPROM) ||
-			    (mode == MODE_SEEPROM) ||
-			    (mode == MODE_GPAUSE) ||
-			    (mode == MODE_SPAUSE) ||
-			    (mode == MODE_GCOALESCE) ||
-			    (mode == MODE_SCOALESCE) ||
-			    (mode == MODE_GRING) ||
-			    (mode == MODE_GCHANNELS) ||
-			    (mode == MODE_SCHANNELS) ||
-			    (mode == MODE_SRING) ||
-			    (mode == MODE_GOFFLOAD) ||
-			    (mode == MODE_SOFFLOAD) ||
-			    (mode == MODE_GSTATS) ||
-			    (mode == MODE_GNFC) ||
-			    (mode == MODE_SNFC) ||
-			    (mode == MODE_GRXFHINDIR) ||
-			    (mode == MODE_SRXFHINDIR) ||
-			    (mode == MODE_SCLSRULE) ||
-			    (mode == MODE_GCLSRULE) ||
-			    (mode == MODE_PHYS_ID) ||
-			    (mode == MODE_FLASHDEV) ||
-			    (mode == MODE_PERMADDR) ||
-			    (mode == MODE_SET_DUMP) ||
-			    (mode == MODE_GET_DUMP)) {
-				devname = argp[i];
-				break;
-			}
-			/* fallthrough */
-		case 3:
-			if (mode == MODE_TEST) {
-				if (!strcmp(argp[i], "online")) {
-					test_type = ONLINE;
-				} else if (!strcmp(argp[i], "offline")) {
-					test_type = OFFLINE;
-				} else if (!strcmp(argp[i], "external_lb")) {
-					test_type = EXTERNAL_LB;
-				} else {
-					exit_bad_args();
-				}
-				break;
-			} else if (mode == MODE_PHYS_ID) {
-				phys_id_time = get_int(argp[i],0);
-				break;
-			} else if (mode == MODE_FLASHDEV) {
-				flash_file = argp[i];
-				flash = 1;
-				break;
-			} else if (mode == MODE_SET_DUMP) {
-				dump_flag = get_u32(argp[i], 0);
-				break;
-			}
-			/* fallthrough */
-		default:
-			if (mode == MODE_GREGS) {
-				parse_generic_cmdline(argc, argp, i,
-					&gregs_changed,
-					cmdline_gregs,
-					ARRAY_SIZE(cmdline_gregs));
-				i = argc;
-				break;
-			}
-			if (mode == MODE_GEEPROM) {
-				parse_generic_cmdline(argc, argp, i,
-					&geeprom_changed,
-					cmdline_geeprom,
-					ARRAY_SIZE(cmdline_geeprom));
-				i = argc;
-				break;
-			}
-			if (mode == MODE_SEEPROM) {
-				parse_generic_cmdline(argc, argp, i,
-					&seeprom_changed,
-					cmdline_seeprom,
-					ARRAY_SIZE(cmdline_seeprom));
-				i = argc;
-				break;
-			}
-			if (mode == MODE_SPAUSE) {
-				parse_generic_cmdline(argc, argp, i,
-					&gpause_changed,
-			      		cmdline_pause,
-			      		ARRAY_SIZE(cmdline_pause));
-				i = argc;
-				break;
-			}
-			if (mode == MODE_SRING) {
-				parse_generic_cmdline(argc, argp, i,
-					&gring_changed,
-			      		cmdline_ring,
-			      		ARRAY_SIZE(cmdline_ring));
-				i = argc;
-				break;
-			}
-			if (mode == MODE_SCHANNELS) {
-				parse_generic_cmdline(argc, argp, i,
-					&gchannels_changed,
-					cmdline_channels,
-					ARRAY_SIZE(cmdline_ring));
-				i = argc;
-				break;
-			}
-			if (mode == MODE_SCOALESCE) {
-				parse_generic_cmdline(argc, argp, i,
-					&gcoalesce_changed,
-			      		cmdline_coalesce,
-			      		ARRAY_SIZE(cmdline_coalesce));
-				i = argc;
-				break;
-			}
-			if (mode == MODE_SOFFLOAD) {
-				parse_generic_cmdline(argc, argp, i,
-					&goffload_changed,
-			      		cmdline_offload,
-			      		ARRAY_SIZE(cmdline_offload));
-				i = argc;
-				break;
-			}
-			if (mode == MODE_SCLSRULE) {
-				if (!strcmp(argp[i], "flow-type")) {
-					i += 1;
-					if (i >= argc) {
-						exit_bad_args();
-						break;
-					}
-					if (rxclass_parse_ruleopts(&argp[i],
-							argc - i,
-							&rx_rule_fs) < 0) {
-						exit_bad_args();
-					} else {
-						i = argc;
-						rx_class_rule_added = 1;
-					}
-				} else if (!strcmp(argp[i], "delete")) {
-					i += 1;
-					if (i >= argc) {
-						exit_bad_args();
-						break;
-					}
-					rx_class_rule_del =
-						get_uint_range(argp[i], 0,
-							       INT_MAX);
-				} else {
-					exit_bad_args();
-				}
-				break;
-			}
-			if (mode == MODE_GCLSRULE) {
-				if (!strcmp(argp[i], "rule")) {
-					i += 1;
-					if (i >= argc) {
-						exit_bad_args();
-						break;
-					}
-					rx_class_rule_get =
-						get_uint_range(argp[i], 0,
-							       INT_MAX);
-				} else {
-					exit_bad_args();
-				}
-				break;
-			}
-			if (mode == MODE_GNFC) {
-				if (!strcmp(argp[i], "rx-flow-hash")) {
-					i += 1;
-					if (i >= argc) {
-						exit_bad_args();
-						break;
-					}
-					rx_fhash_get =
-						rxflow_str_to_type(argp[i]);
-					if (!rx_fhash_get)
-						exit_bad_args();
-				} else
-					exit_bad_args();
-				break;
-			}
-			if (mode == MODE_FLASHDEV) {
-				flash_region = strtol(argp[i], NULL, 0);
-				if ((flash_region < 0))
-					exit_bad_args();
-				break;
-			}
-			if (mode == MODE_SNFC) {
-				if (!strcmp(argp[i], "rx-flow-hash")) {
-					i += 1;
-					if (i >= argc) {
-						exit_bad_args();
-						break;
-					}
-					rx_fhash_set =
-						rxflow_str_to_type(argp[i]);
-					if (!rx_fhash_set) {
-						exit_bad_args();
-						break;
-					}
-					i += 1;
-					if (i >= argc) {
-						exit_bad_args();
-						break;
-					}
-					if (parse_rxfhashopts(argp[i],
-						&rx_fhash_val) < 0)
-						exit_bad_args();
-					else
-						rx_fhash_changed = 1;
-				} else
-					exit_bad_args();
-				break;
-			}
-			if (mode == MODE_SRXFHINDIR) {
-				if (!strcmp(argp[i], "equal")) {
-					if (argc != i + 2) {
-						exit_bad_args();
-						break;
-					}
-					i += 1;
-					rxfhindir_equal =
-						get_int_range(argp[i], 0, 1,
-							      INT_MAX);
-					i += 1;
-				} else if (!strcmp(argp[i], "weight")) {
-					i += 1;
-					if (i >= argc) {
-						exit_bad_args();
-						break;
-					}
-					rxfhindir_weight = argp + i;
-					i = argc;
-				} else {
-					exit_bad_args();
-				}
-				break;
-			}
-			if (mode == MODE_GET_DUMP) {
-				if (argc != i + 2) {
-					exit_bad_args();
-					break;
-				}
-				if (!strcmp(argp[i++], "data"))
-					dump_flag = ETHTOOL_GET_DUMP_DATA;
-				else {
-					exit_bad_args();
-					break;
-				}
-				dump_file = argp[i];
-				i = argc;
-				break;
-			}
-			if (mode != MODE_SSET)
-				exit_bad_args();
-			if (!strcmp(argp[i], "speed")) {
-				gset_changed = 1;
-				i += 1;
-				if (i >= argc)
-					exit_bad_args();
-				speed_wanted = get_int(argp[i],10);
-				break;
-			} else if (!strcmp(argp[i], "duplex")) {
-				gset_changed = 1;
-				i += 1;
-				if (i >= argc)
-					exit_bad_args();
-				if (!strcmp(argp[i], "half"))
-					duplex_wanted = DUPLEX_HALF;
-				else if (!strcmp(argp[i], "full"))
-					duplex_wanted = DUPLEX_FULL;
-				else
-					exit_bad_args();
-				break;
-			} else if (!strcmp(argp[i], "port")) {
-				gset_changed = 1;
-				i += 1;
-				if (i >= argc)
-					exit_bad_args();
-				if (!strcmp(argp[i], "tp"))
-					port_wanted = PORT_TP;
-				else if (!strcmp(argp[i], "aui"))
-					port_wanted = PORT_AUI;
-				else if (!strcmp(argp[i], "bnc"))
-					port_wanted = PORT_BNC;
-				else if (!strcmp(argp[i], "mii"))
-					port_wanted = PORT_MII;
-				else if (!strcmp(argp[i], "fibre"))
-					port_wanted = PORT_FIBRE;
-				else
-					exit_bad_args();
-				break;
-			} else if (!strcmp(argp[i], "autoneg")) {
-				i += 1;
-				if (i >= argc)
-					exit_bad_args();
-				if (!strcmp(argp[i], "on")) {
-					gset_changed = 1;
-					autoneg_wanted = AUTONEG_ENABLE;
-				} else if (!strcmp(argp[i], "off")) {
-					gset_changed = 1;
-					autoneg_wanted = AUTONEG_DISABLE;
-				} else {
-					exit_bad_args();
-				}
-				break;
-			} else if (!strcmp(argp[i], "advertise")) {
-				gset_changed = 1;
-				i += 1;
-				if (i >= argc)
-					exit_bad_args();
-				advertising_wanted = get_int(argp[i], 16);
-				break;
-			} else if (!strcmp(argp[i], "phyad")) {
-				gset_changed = 1;
-				i += 1;
-				if (i >= argc)
-					exit_bad_args();
-				phyad_wanted = get_int(argp[i], 0);
-				break;
-			} else if (!strcmp(argp[i], "xcvr")) {
-				gset_changed = 1;
-				i += 1;
-				if (i >= argc)
-					exit_bad_args();
-				if (!strcmp(argp[i], "internal"))
-					xcvr_wanted = XCVR_INTERNAL;
-				else if (!strcmp(argp[i], "external"))
-					xcvr_wanted = XCVR_EXTERNAL;
-				else
-					exit_bad_args();
-				break;
-			} else if (!strcmp(argp[i], "wol")) {
-				gwol_changed = 1;
-				i++;
-				if (i >= argc)
-					exit_bad_args();
-				if (parse_wolopts(argp[i], &wol_wanted) < 0)
-					exit_bad_args();
-				wol_change = 1;
-				break;
-			} else if (!strcmp(argp[i], "sopass")) {
-				gwol_changed = 1;
-				i++;
-				if (i >= argc)
-					exit_bad_args();
-				get_mac_addr(argp[i], sopass_wanted);
-				sopass_change = 1;
-				break;
-			} else if (!strcmp(argp[i], "msglvl")) {
-				i++;
-				if (i >= argc)
-					exit_bad_args();
-				if (isdigit((unsigned char)argp[i][0])) {
-					msglvl_changed = 1;
-					msglvl_mask = ~0;
-					msglvl_wanted =
-						get_uint_range(argp[i], 0,
-							       0xffffffff);
-				} else {
-					parse_generic_cmdline(
-						argc, argp, i,
-						&msglvl_changed,
-						cmdline_msglvl,
-						ARRAY_SIZE(cmdline_msglvl));
-					i = argc;
-				}
-				break;
-			}
-			exit_bad_args();
-		}
-	}
-
-	if (advertising_wanted < 0) {
-		if (speed_wanted == SPEED_10 && duplex_wanted == DUPLEX_HALF)
-			advertising_wanted = ADVERTISED_10baseT_Half;
-		else if (speed_wanted == SPEED_10 &&
-			 duplex_wanted == DUPLEX_FULL)
-			advertising_wanted = ADVERTISED_10baseT_Full;
-		else if (speed_wanted == SPEED_100 &&
-			 duplex_wanted == DUPLEX_HALF)
-			advertising_wanted = ADVERTISED_100baseT_Half;
-		else if (speed_wanted == SPEED_100 &&
-			 duplex_wanted == DUPLEX_FULL)
-			advertising_wanted = ADVERTISED_100baseT_Full;
-		else if (speed_wanted == SPEED_1000 &&
-			 duplex_wanted == DUPLEX_HALF)
-			advertising_wanted = ADVERTISED_1000baseT_Half;
-		else if (speed_wanted == SPEED_1000 &&
-			 duplex_wanted == DUPLEX_FULL)
-			advertising_wanted = ADVERTISED_1000baseT_Full;
-		else if (speed_wanted == SPEED_2500 &&
-			 duplex_wanted == DUPLEX_FULL)
-			advertising_wanted = ADVERTISED_2500baseX_Full;
-		else if (speed_wanted == SPEED_10000 &&
-			 duplex_wanted == DUPLEX_FULL)
-			advertising_wanted = ADVERTISED_10000baseT_Full;
-		else
-			/* auto negotiate without forcing,
-			 * all supported speed will be assigned in do_sset()
-			 */
-			advertising_wanted = 0;
-
-	}
-
-	if (devname == NULL)
-		exit_bad_args();
-	if (strlen(devname) >= IFNAMSIZ)
-		exit_bad_args();
+	fprintf(stdout,
+		PACKAGE " version " VERSION "\n");
+	return 0;
 }
 
 static void dump_link_caps(const char *prefix, const char *an_prefix, u32 mask);
@@ -1978,92 +1515,14 @@ static int dump_rxfhash(int fhash, u64 val)
 	return 0;
 }
 
-static int doit(void)
-{
-	struct cmd_context ctx;
-
-	/* Setup our control structures. */
-	memset(&ctx.ifr, 0, sizeof(ctx.ifr));
-	strcpy(ctx.ifr.ifr_name, devname);
-
-	/* Open control socket. */
-	ctx.fd = socket(AF_INET, SOCK_DGRAM, 0);
-	if (ctx.fd < 0) {
-		perror("Cannot get control socket");
-		return 70;
-	}
-
-	/* all of these are expected to populate ifr->ifr_data as needed */
-	if (mode == MODE_GDRV) {
-		return do_gdrv(&ctx);
-	} else if (mode == MODE_GSET) {
-		return do_gset(&ctx);
-	} else if (mode == MODE_SSET) {
-		return do_sset(&ctx);
-	} else if (mode == MODE_GREGS) {
-		return do_gregs(&ctx);
-	} else if (mode == MODE_NWAY_RST) {
-		return do_nway_rst(&ctx);
-	} else if (mode == MODE_GEEPROM) {
-		return do_geeprom(&ctx);
-	} else if (mode == MODE_SEEPROM) {
-		return do_seeprom(&ctx);
-	} else if (mode == MODE_TEST) {
-		return do_test(&ctx);
-	} else if (mode == MODE_PHYS_ID) {
-		return do_phys_id(&ctx);
-	} else if (mode == MODE_GPAUSE) {
-		return do_gpause(&ctx);
-	} else if (mode == MODE_SPAUSE) {
-		return do_spause(&ctx);
-	} else if (mode == MODE_GCOALESCE) {
-		return do_gcoalesce(&ctx);
-	} else if (mode == MODE_SCOALESCE) {
-		return do_scoalesce(&ctx);
-	} else if (mode == MODE_GRING) {
-		return do_gring(&ctx);
-	} else if (mode == MODE_SRING) {
-		return do_sring(&ctx);
-	} else if (mode == MODE_GCHANNELS) {
-		return do_gchannels(&ctx);
-	} else if (mode == MODE_SCHANNELS) {
-		return do_schannels(&ctx);
-	} else if (mode == MODE_GOFFLOAD) {
-		return do_goffload(&ctx);
-	} else if (mode == MODE_SOFFLOAD) {
-		return do_soffload(&ctx);
-	} else if (mode == MODE_GSTATS) {
-		return do_gstats(&ctx);
-	} else if (mode == MODE_GNFC) {
-		return do_grxclass(&ctx);
-	} else if (mode == MODE_SNFC) {
-		return do_srxclass(&ctx);
-	} else if (mode == MODE_GRXFHINDIR) {
-		return do_grxfhindir(&ctx);
-	} else if (mode == MODE_SRXFHINDIR) {
-		return do_srxfhindir(&ctx);
-	} else if (mode == MODE_SCLSRULE) {
-		return do_srxclsrule(&ctx);
-	} else if (mode == MODE_GCLSRULE) {
-		return do_grxclsrule(&ctx);
-	} else if (mode == MODE_FLASHDEV) {
-		return do_flash(&ctx);
-	} else if (mode == MODE_PERMADDR) {
-		return do_permaddr(&ctx);
-	} else if (mode == MODE_GET_DUMP) {
-		return do_getfwdump(&ctx);
-	} else if (mode == MODE_SET_DUMP) {
-		return do_setfwdump(&ctx);
-	}
-
-	return 69;
-}
-
 static int do_gdrv(struct cmd_context *ctx)
 {
 	int err;
 	struct ethtool_drvinfo drvinfo;
 
+	if (ctx->argc != 0)
+		exit_bad_args();
+
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
 	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
@@ -2078,6 +1537,9 @@ static int do_gpause(struct cmd_context *ctx)
 	struct ethtool_cmd ecmd;
 	int err;
 
+	if (ctx->argc != 0)
+		exit_bad_args();
+
 	fprintf(stdout, "Pause parameters for %s:\n", devname);
 
 	epause.cmd = ETHTOOL_GPAUSEPARAM;
@@ -2135,6 +1597,9 @@ static int do_spause(struct cmd_context *ctx)
 {
 	int err, changed = 0;
 
+	parse_generic_cmdline(ctx, &gpause_changed,
+			      cmdline_pause, ARRAY_SIZE(cmdline_pause));
+
 	epause.cmd = ETHTOOL_GPAUSEPARAM;
 	err = send_ioctl(ctx, &epause);
 	if (err) {
@@ -2163,6 +1628,9 @@ static int do_sring(struct cmd_context *ctx)
 {
 	int err, changed = 0;
 
+	parse_generic_cmdline(ctx, &gring_changed,
+			      cmdline_ring, ARRAY_SIZE(cmdline_ring));
+
 	ering.cmd = ETHTOOL_GRINGPARAM;
 	err = send_ioctl(ctx, &ering);
 	if (err) {
@@ -2191,6 +1659,9 @@ static int do_gring(struct cmd_context *ctx)
 {
 	int err;
 
+	if (ctx->argc != 0)
+		exit_bad_args();
+
 	fprintf(stdout, "Ring parameters for %s:\n", devname);
 
 	ering.cmd = ETHTOOL_GRINGPARAM;
@@ -2211,6 +1682,9 @@ static int do_schannels(struct cmd_context *ctx)
 {
 	int err, changed = 0;
 
+	parse_generic_cmdline(ctx, &gchannels_changed,
+			      cmdline_channels, ARRAY_SIZE(cmdline_ring));
+
 	echannels.cmd = ETHTOOL_GCHANNELS;
 	err = send_ioctl(ctx, &echannels);
 	if (err) {
@@ -2244,6 +1718,9 @@ static int do_gchannels(struct cmd_context *ctx)
 {
 	int err;
 
+	if (ctx->argc != 0)
+		exit_bad_args();
+
 	fprintf(stdout, "Channel parameters for %s:\n", devname);
 
 	echannels.cmd = ETHTOOL_GCHANNELS;
@@ -2264,6 +1741,9 @@ static int do_gcoalesce(struct cmd_context *ctx)
 {
 	int err;
 
+	if (ctx->argc != 0)
+		exit_bad_args();
+
 	fprintf(stdout, "Coalesce parameters for %s:\n", devname);
 
 	ecoal.cmd = ETHTOOL_GCOALESCE;
@@ -2284,6 +1764,9 @@ static int do_scoalesce(struct cmd_context *ctx)
 {
 	int err, changed = 0;
 
+	parse_generic_cmdline(ctx, &gcoalesce_changed,
+			      cmdline_coalesce, ARRAY_SIZE(cmdline_coalesce));
+
 	ecoal.cmd = ETHTOOL_GCOALESCE;
 	err = send_ioctl(ctx, &ecoal);
 	if (err) {
@@ -2316,6 +1799,9 @@ static int do_goffload(struct cmd_context *ctx)
 	int tso = 0, ufo = 0, gso = 0, gro = 0, lro = 0, rxvlan = 0, txvlan = 0,
 	    ntuple = 0, rxhash = 0;
 
+	if (ctx->argc != 0)
+		exit_bad_args();
+
 	fprintf(stdout, "Offload parameters for %s:\n", devname);
 
 	eval.cmd = ETHTOOL_GRXCSUM;
@@ -2408,6 +1894,9 @@ static int do_soffload(struct cmd_context *ctx)
 	struct ethtool_value eval;
 	int err, changed = 0;
 
+	parse_generic_cmdline(ctx, &goffload_changed,
+			      cmdline_offload, ARRAY_SIZE(cmdline_offload));
+
 	if (off_csum_rx_wanted >= 0) {
 		changed = 1;
 		eval.cmd = ETHTOOL_SRXCSUM;
@@ -2517,6 +2006,9 @@ static int do_gset(struct cmd_context *ctx)
 	struct ethtool_value edata;
 	int allfail = 1;
 
+	if (ctx->argc != 0)
+		exit_bad_args();
+
 	fprintf(stdout, "Settings for %s:\n", devname);
 
 	ecmd.cmd = ETHTOOL_GSET;
@@ -2574,8 +2066,152 @@ static int do_gset(struct cmd_context *ctx)
 
 static int do_sset(struct cmd_context *ctx)
 {
+	int argc = ctx->argc;
+	char **argp = ctx->argp;
+	int i;
 	int err;
 
+	for (i = 0; i < argc; i++) {
+		if (!strcmp(argp[i], "speed")) {
+			gset_changed = 1;
+			i += 1;
+			if (i >= argc)
+				exit_bad_args();
+			speed_wanted = get_int(argp[i],10);
+		} else if (!strcmp(argp[i], "duplex")) {
+			gset_changed = 1;
+			i += 1;
+			if (i >= argc)
+				exit_bad_args();
+			if (!strcmp(argp[i], "half"))
+				duplex_wanted = DUPLEX_HALF;
+			else if (!strcmp(argp[i], "full"))
+				duplex_wanted = DUPLEX_FULL;
+			else
+				exit_bad_args();
+		} else if (!strcmp(argp[i], "port")) {
+			gset_changed = 1;
+			i += 1;
+			if (i >= argc)
+				exit_bad_args();
+			if (!strcmp(argp[i], "tp"))
+				port_wanted = PORT_TP;
+			else if (!strcmp(argp[i], "aui"))
+				port_wanted = PORT_AUI;
+			else if (!strcmp(argp[i], "bnc"))
+				port_wanted = PORT_BNC;
+			else if (!strcmp(argp[i], "mii"))
+				port_wanted = PORT_MII;
+			else if (!strcmp(argp[i], "fibre"))
+				port_wanted = PORT_FIBRE;
+			else
+				exit_bad_args();
+		} else if (!strcmp(argp[i], "autoneg")) {
+			i += 1;
+			if (i >= argc)
+				exit_bad_args();
+			if (!strcmp(argp[i], "on")) {
+				gset_changed = 1;
+				autoneg_wanted = AUTONEG_ENABLE;
+			} else if (!strcmp(argp[i], "off")) {
+				gset_changed = 1;
+				autoneg_wanted = AUTONEG_DISABLE;
+			} else {
+				exit_bad_args();
+			}
+		} else if (!strcmp(argp[i], "advertise")) {
+			gset_changed = 1;
+			i += 1;
+			if (i >= argc)
+				exit_bad_args();
+			advertising_wanted = get_int(argp[i], 16);
+		} else if (!strcmp(argp[i], "phyad")) {
+			gset_changed = 1;
+			i += 1;
+			if (i >= argc)
+				exit_bad_args();
+			phyad_wanted = get_int(argp[i], 0);
+		} else if (!strcmp(argp[i], "xcvr")) {
+			gset_changed = 1;
+			i += 1;
+			if (i >= argc)
+				exit_bad_args();
+			if (!strcmp(argp[i], "internal"))
+				xcvr_wanted = XCVR_INTERNAL;
+			else if (!strcmp(argp[i], "external"))
+				xcvr_wanted = XCVR_EXTERNAL;
+			else
+				exit_bad_args();
+		} else if (!strcmp(argp[i], "wol")) {
+			gwol_changed = 1;
+			i++;
+			if (i >= argc)
+				exit_bad_args();
+			if (parse_wolopts(argp[i], &wol_wanted) < 0)
+				exit_bad_args();
+			wol_change = 1;
+		} else if (!strcmp(argp[i], "sopass")) {
+			gwol_changed = 1;
+			i++;
+			if (i >= argc)
+				exit_bad_args();
+			get_mac_addr(argp[i], sopass_wanted);
+			sopass_change = 1;
+		} else if (!strcmp(argp[i], "msglvl")) {
+			i++;
+			if (i >= argc)
+				exit_bad_args();
+			if (isdigit((unsigned char)argp[i][0])) {
+				msglvl_changed = 1;
+				msglvl_mask = ~0;
+				msglvl_wanted =
+					get_uint_range(argp[i], 0,
+						       0xffffffff);
+			} else {
+				ctx->argc -= i;
+				ctx->argp += i;
+				parse_generic_cmdline(
+					ctx, &msglvl_changed,
+					cmdline_msglvl,
+					ARRAY_SIZE(cmdline_msglvl));
+				break;
+			}
+		} else {
+			exit_bad_args();
+		}
+	}
+
+	if (advertising_wanted < 0) {
+		if (speed_wanted == SPEED_10 && duplex_wanted == DUPLEX_HALF)
+			advertising_wanted = ADVERTISED_10baseT_Half;
+		else if (speed_wanted == SPEED_10 &&
+			 duplex_wanted == DUPLEX_FULL)
+			advertising_wanted = ADVERTISED_10baseT_Full;
+		else if (speed_wanted == SPEED_100 &&
+			 duplex_wanted == DUPLEX_HALF)
+			advertising_wanted = ADVERTISED_100baseT_Half;
+		else if (speed_wanted == SPEED_100 &&
+			 duplex_wanted == DUPLEX_FULL)
+			advertising_wanted = ADVERTISED_100baseT_Full;
+		else if (speed_wanted == SPEED_1000 &&
+			 duplex_wanted == DUPLEX_HALF)
+			advertising_wanted = ADVERTISED_1000baseT_Half;
+		else if (speed_wanted == SPEED_1000 &&
+			 duplex_wanted == DUPLEX_FULL)
+			advertising_wanted = ADVERTISED_1000baseT_Full;
+		else if (speed_wanted == SPEED_2500 &&
+			 duplex_wanted == DUPLEX_FULL)
+			advertising_wanted = ADVERTISED_2500baseX_Full;
+		else if (speed_wanted == SPEED_10000 &&
+			 duplex_wanted == DUPLEX_FULL)
+			advertising_wanted = ADVERTISED_10000baseT_Full;
+		else
+			/* auto negotiate without forcing,
+			 * all supported speed will be assigned below
+			 */
+			advertising_wanted = 0;
+	}
+
 	if (gset_changed) {
 		struct ethtool_cmd ecmd;
 
@@ -2712,6 +2348,9 @@ static int do_gregs(struct cmd_context *ctx)
 	struct ethtool_drvinfo drvinfo;
 	struct ethtool_regs *regs;
 
+	parse_generic_cmdline(ctx, &gregs_changed,
+			      cmdline_gregs, ARRAY_SIZE(cmdline_gregs));
+
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
 	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
@@ -2747,6 +2386,9 @@ static int do_nway_rst(struct cmd_context *ctx)
 	struct ethtool_value edata;
 	int err;
 
+	if (ctx->argc != 0)
+		exit_bad_args();
+
 	edata.cmd = ETHTOOL_NWAY_RST;
 	err = send_ioctl(ctx, &edata);
 	if (err < 0)
@@ -2761,6 +2403,9 @@ static int do_geeprom(struct cmd_context *ctx)
 	struct ethtool_drvinfo drvinfo;
 	struct ethtool_eeprom *eeprom;
 
+	parse_generic_cmdline(ctx, &geeprom_changed,
+			      cmdline_geeprom, ARRAY_SIZE(cmdline_geeprom));
+
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
 	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
@@ -2800,6 +2445,9 @@ static int do_seeprom(struct cmd_context *ctx)
 	struct ethtool_drvinfo drvinfo;
 	struct ethtool_eeprom *eeprom;
 
+	parse_generic_cmdline(ctx, &seeprom_changed,
+			      cmdline_seeprom, ARRAY_SIZE(cmdline_seeprom));
+
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
 	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
@@ -2849,6 +2497,20 @@ static int do_test(struct cmd_context *ctx)
 	struct ethtool_test *test;
 	struct ethtool_gstrings *strings;
 
+	if (ctx->argc > 1)
+		exit_bad_args();
+	if (ctx->argc == 1) {
+		if (!strcmp(ctx->argp[0], "online")) {
+			test_type = ONLINE;
+	 	} else if (!strcmp(*ctx->argp, "offline")) {
+			test_type = OFFLINE;
+		} else if (!strcmp(*ctx->argp, "external_lb")) {
+			test_type = EXTERNAL_LB;
+		} else {
+			exit_bad_args();
+		}
+	}
+
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
 	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
@@ -2907,6 +2569,11 @@ static int do_phys_id(struct cmd_context *ctx)
 	int err;
 	struct ethtool_value edata;
 
+	if (ctx->argc > 1)
+		exit_bad_args();
+	if (ctx->argc == 1)
+		phys_id_time = get_int(*ctx->argp, 0);
+
 	edata.cmd = ETHTOOL_PHYS_ID;
 	edata.data = phys_id_time;
 	err = send_ioctl(ctx, &edata);
@@ -2924,6 +2591,9 @@ static int do_gstats(struct cmd_context *ctx)
 	unsigned int n_stats, sz_str, sz_stats, i;
 	int err;
 
+	if (ctx->argc != 0)
+		exit_bad_args();
+
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
 	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
@@ -2985,10 +2655,15 @@ static int do_gstats(struct cmd_context *ctx)
 
 static int do_srxclass(struct cmd_context *ctx)
 {
+	struct ethtool_rxnfc nfccmd;
 	int err;
 
-	if (rx_fhash_changed) {
-		struct ethtool_rxnfc nfccmd;
+	if (ctx->argc == 3 && !strcmp(ctx->argp[0], "rx-flow-hash")) {
+		rx_fhash_set = rxflow_str_to_type(ctx->argp[1]);
+		if (!rx_fhash_set)
+			exit_bad_args();
+		if (parse_rxfhashopts(ctx->argp[2], &rx_fhash_val) < 0)
+			exit_bad_args();
 
 		nfccmd.cmd = ETHTOOL_SRXFH;
 		nfccmd.flow_type = rx_fhash_set;
@@ -2997,7 +2672,8 @@ static int do_srxclass(struct cmd_context *ctx)
 		err = send_ioctl(ctx, &nfccmd);
 		if (err < 0)
 			perror("Cannot change RX network flow hashing options");
-
+	} else {
+		exit_bad_args();
 	}
 
 	return 0;
@@ -3005,10 +2681,13 @@ static int do_srxclass(struct cmd_context *ctx)
 
 static int do_grxclass(struct cmd_context *ctx)
 {
+	struct ethtool_rxnfc nfccmd;
 	int err;
 
-	if (rx_fhash_get) {
-		struct ethtool_rxnfc nfccmd;
+	if (ctx->argc == 2 && !strcmp(ctx->argp[0], "rx-flow-hash")) {
+		rx_fhash_get = rxflow_str_to_type(ctx->argp[1]);
+		if (!rx_fhash_get)
+			exit_bad_args();
 
 		nfccmd.cmd = ETHTOOL_GRXFH;
 		nfccmd.flow_type = rx_fhash_get;
@@ -3017,6 +2696,8 @@ static int do_grxclass(struct cmd_context *ctx)
 			perror("Cannot get RX network flow hashing options");
 		else
 			dump_rxfhash(rx_fhash_get, nfccmd.data);
+	} else {
+		exit_bad_args();
 	}
 
 	return 0;
@@ -3074,8 +2755,17 @@ static int do_srxfhindir(struct cmd_context *ctx)
 	u32 i;
 	int err;
 
-	if (!rxfhindir_equal && !rxfhindir_weight)
+	if (ctx->argc < 2)
+		exit_bad_args();
+	if (!strcmp(ctx->argp[0], "equal")) {
+		if (ctx->argc != 2)
+			exit_bad_args();
+		rxfhindir_equal = get_int_range(ctx->argp[1], 0, 1, INT_MAX);
+	} else if (!strcmp(ctx->argp[0], "weight")) {
+		rxfhindir_weight = ctx->argp + 1;
+	} else {
 		exit_bad_args();
+	}
 
 	indir_head.cmd = ETHTOOL_GRXFHINDIR;
 	indir_head.size = 0;
@@ -3139,10 +2829,13 @@ static int do_flash(struct cmd_context *ctx)
 	struct ethtool_flash efl;
 	int err;
 
-	if (flash < 0) {
-		fprintf(stdout, "Missing filename argument\n");
+	if (ctx->argc < 1 || ctx->argc > 2)
 		exit_bad_args();
-		return 98;
+	flash_file = ctx->argp[0];
+	if (ctx->argc == 2) {
+		flash_region = strtol(ctx->argp[1], NULL, 0);
+		if (flash_region < 0)
+			exit_bad_args();
 	}
 
 	if (strlen(flash_file) > ETHTOOL_FLASH_MAX_FILENAME - 1) {
@@ -3309,7 +3002,15 @@ static int do_srxclsrule(struct cmd_context *ctx)
 {
 	int err;
 
-	if (rx_class_rule_added) {
+	if (ctx->argc < 2)
+		exit_bad_args();
+
+	if (!strcmp(ctx->argp[0], "flow-type")) {
+		ctx->argc--;
+		ctx->argp++;
+		if (rxclass_parse_ruleopts(ctx, &rx_rule_fs) < 0)
+			exit_bad_args();
+
 		/* attempt to add rule via N-tuple specifier */
 		err = do_srxntuple(ctx);
 		if (!err)
@@ -3322,7 +3023,9 @@ static int do_srxclsrule(struct cmd_context *ctx)
 				" classification rule\n");
 			return 1;
 		}
-	} else if (rx_class_rule_del >= 0) {
+	} else if (!strcmp(ctx->argp[0], "delete")) {
+		rx_class_rule_del = get_uint_range(ctx->argp[1], 0, INT_MAX);
+
 		err = rxclass_rule_del(ctx, rx_class_rule_del);
 
 		if (err < 0) {
@@ -3342,13 +3045,18 @@ static int do_grxclsrule(struct cmd_context *ctx)
 	struct ethtool_rxnfc nfccmd;
 	int err;
 
-	if (rx_class_rule_get >= 0) {
+	if (ctx->argc == 2 && !strcmp(ctx->argp[0], "rule")) {
+		rx_class_rule_get = get_uint_range(ctx->argp[1], 0, INT_MAX);
+
 		err = rxclass_rule_get(ctx, rx_class_rule_get);
 		if (err < 0)
 			fprintf(stderr, "Cannot get RX classification rule\n");
 		return err ? 1 : 0;
 	}
 
+	if (ctx->argc != 0)
+		exit_bad_args();
+
 	nfccmd.cmd = ETHTOOL_GRXRINGS;
 	err = send_ioctl(ctx, &nfccmd);
 	if (err < 0)
@@ -3396,6 +3104,13 @@ static int do_getfwdump(struct cmd_context *ctx)
 	struct ethtool_dump edata;
 	struct ethtool_dump *data;
 
+	if (ctx->argc == 2 && !strcmp(ctx->argp[0], "data")) {
+		dump_flag = ETHTOOL_GET_DUMP_DATA;
+		dump_file = ctx->argp[1];
+	} else if (ctx->argc != 0) {
+		exit_bad_args();
+	}
+
 	edata.cmd = ETHTOOL_GET_DUMP_FLAG;
 
 	err = send_ioctl(ctx, &edata);
@@ -3432,6 +3147,10 @@ static int do_setfwdump(struct cmd_context *ctx)
 	int err;
 	struct ethtool_dump dump;
 
+	if (ctx->argc != 1)
+		exit_bad_args();
+	dump_flag = get_u32(ctx->argp[0], 0);
+
 	dump.cmd = ETHTOOL_SET_DUMP;
 	dump.flag = dump_flag;
 	err = send_ioctl(ctx, &dump);
@@ -3455,6 +3174,63 @@ int send_ioctl(struct cmd_context *ctx, void *cmd)
 
 int main(int argc, char **argp, char **envp)
 {
-	parse_cmdline(argc, argp);
-	return doit();
+	int (*func)(struct cmd_context *) = NULL;
+	int want_device = 0;
+	struct cmd_context ctx;
+	int k;
+
+	/* Skip command name */
+	argp++;
+	argc--;
+
+	/* First argument must be either a valid option or a device
+	 * name to get settings for (which we don't expect to begin
+	 * with '-').
+	 */
+	if (argc == 0)
+		exit_bad_args();
+	for (k = 0; args[k].lng; k++) {
+		if ((args[k].srt && !strcmp(*argp, args[k].srt)) ||
+		    !strcmp(*argp, args[k].lng)) {
+			argp++;
+			argc--;
+			func = args[k].func;
+			want_device = args[k].want_device;
+			break;
+		}
+	}
+	if (!func) {
+		if ((*argp)[0] == '-')
+			exit_bad_args();
+		func = do_gset;
+		want_device = 1;
+	}
+
+	if (want_device) {
+		devname = *argp++;
+		argc--;
+
+		if (devname == NULL)
+			exit_bad_args();
+		if (strlen(devname) >= IFNAMSIZ)
+			exit_bad_args();
+
+		/* Setup our control structures. */
+		memset(&ctx.ifr, 0, sizeof(ctx.ifr));
+		strcpy(ctx.ifr.ifr_name, devname);
+
+		/* Open control socket. */
+		ctx.fd = socket(AF_INET, SOCK_DGRAM, 0);
+		if (ctx.fd < 0) {
+			perror("Cannot get control socket");
+			return 70;
+		}
+	} else {
+		ctx.fd = -1;
+	}
+
+	ctx.argc = argc;
+	ctx.argp = argp;
+
+	return func(&ctx);
 }
diff --git a/internal.h b/internal.h
index ba7d719..eb13db8 100644
--- a/internal.h
+++ b/internal.h
@@ -92,6 +92,8 @@ static inline int test_bit(unsigned int nr, const unsigned long *addr)
 struct cmd_context {
 	int fd;			/* socket suitable for ethtool ioctl */
 	struct ifreq ifr;	/* ifreq suitable for ethtool ioctl */
+	int argc;		/* number of arguments to the sub-command */
+	char **argp;		/* arguments to the sub-command */
 };
 
 #ifdef TEST_ETHTOOL
@@ -165,7 +167,7 @@ int st_mac100_dump_regs(struct ethtool_drvinfo *info,
 int st_gmac_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
 
 /* Rx flow classification */
-int rxclass_parse_ruleopts(char **optstr, int opt_cnt,
+int rxclass_parse_ruleopts(struct cmd_context *ctx,
 			   struct ethtool_rx_flow_spec *fsp);
 int rxclass_rule_getall(struct cmd_context *ctx);
 int rxclass_rule_get(struct cmd_context *ctx, __u32 loc);
diff --git a/rxclass.c b/rxclass.c
index dec9dd5..0c7b916 100644
--- a/rxclass.c
+++ b/rxclass.c
@@ -950,7 +950,7 @@ static int rxclass_get_mask(char *str, unsigned char *p,
 	return 0;
 }
 
-int rxclass_parse_ruleopts(char **argp, int argc,
+int rxclass_parse_ruleopts(struct cmd_context *ctx,
 			   struct ethtool_rx_flow_spec *fsp)
 {
 	const struct rule_opts *options;
@@ -958,6 +958,8 @@ int rxclass_parse_ruleopts(char **argp, int argc,
 	int i = 0, n_opts, err;
 	u32 flags = 0;
 	int flow_type;
+	int argc = ctx->argc;
+	char **argp = ctx->argp;
 
 	if (argc < 1)
 		goto syntax_err;
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 11/21] Add more test cases for command-line parsing
From: Ben Hutchings @ 2011-11-01 23:17 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

These test cases currently fail.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 test-cmdline.c |   28 ++++++++++++++++++++++++++++
 1 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/test-cmdline.c b/test-cmdline.c
index 88591df..7dd3b7c 100644
--- a/test-cmdline.c
+++ b/test-cmdline.c
@@ -55,8 +55,10 @@ static struct test_case {
 	{ 1, "-s devname msglvl hw" },
 	{ 0, "--change devname speed 100 duplex half port tp autoneg on advertise 0x1 phyad 1 xcvr external wol p sopass 01:23:45:67:89:ab msglvl 1" },
 	{ 1, "-s devname foo" },
+	{ 1, "-s" },
 	{ 0, "-a devname" },
 	{ 0, "--show-pause devname" },
+	{ 1, "-a" },
 	/* Many other sub-commands use parse_generic_cmdline() and
 	 * don't need to be check in that much detail. */
 	{ 0, "-A devname autoneg on" },
@@ -65,6 +67,7 @@ static struct test_case {
 	{ 0, "--pause devname rx off" },
 	{ 0, "-A devname tx on rx on autoneg off" },
 	{ 1, "--pause devname foo on" },
+	{ 1, "-A" },
 	{ 0, "-c devname" },
 	{ 0, "--show-coalesce devname" },
 	{ 0, "-C devname adaptive-rx on adaptive-tx off rx-usecs 1 rx-frames 2 rx-usecs-irq 3 rx-frames-irq 4 tx-usecs 5 tx-frames 6 tx-usecs-irq 7 tx-frames-irq 8 stats-block-usecs 9 pkt-rate-low 10" },
@@ -72,42 +75,53 @@ static struct test_case {
 	{ 1, "-C devname adaptive-rx foo" },
 	{ 1, "--coalesce devname adaptive-rx" },
 	{ 1, "-C devname foo on" },
+	{ 1, "-C" },
 	{ 0, "-g devname" },
 	{ 0, "--show-ring devname" },
+	{ 1, "-g" },
 	{ 0, "-G devname rx 1 rx-mini 2 rx-jumbo 3 tx 4" },
 	{ 0, "--set-ring devname rx 1 rx-mini 2 rx-jumbo 3 tx 4" },
 	{ 1, "-G devname rx foo" },
 	{ 1, "--set-ring devname rx" },
 	{ 1, "-G devname foo 1" },
+	{ 1, "-G" },
 	{ 0, "-k devname" },
 	{ 0, "--show-offload devname" },
+	{ 1, "-k" },
 	{ 0, "-K devname rx on tx off sg on tso off ufo on gso off gro on" },
 	{ 0, "--offload devname lro off rxvlan on txvlan off ntuple on rxhash off" },
 	{ 1, "-K devname rx foo" },
 	{ 1, "--offload devname rx" },
 	{ 1, "-K devname foo on" },
+	{ 1, "-K" },
 	{ 0, "-i devname" },
 	{ 0, "--driver devname" },
+	{ 1, "-i" },
 	{ 0, "-d devname" },
 	{ 0, "--register-dump devname raw on file foo" },
 	{ 1, "-d devname raw foo" },
 	{ 1, "--register-dump devname file" },
 	{ 1, "-d devname foo" },
+	{ 1, "-d" },
 	{ 0, "-e devname" },
 	{ 0, "--eeprom-dump devname raw on offset 1 length 2" },
 	{ 1, "-e devname raw foo" },
 	{ 1, "--eeprom-dump devname offset foo" },
 	{ 1, "-e devname length" },
 	{ 1, "--eeprom-dump devname foo" },
+	{ 1, "-e" },
 	{ 0, "-E devname" },
 	{ 0, "--change-eeprom devname magic 0x87654321 offset 0 value 1" },
 	{ 0, "-E devname magic 0x87654321 offset 0 length 2" },
+	{ 1, "-E" },
 	{ 0, "-r devname" },
 	{ 0, "--negotiate devname" },
+	{ 1, "-r" },
 	{ 0, "-p devname" },
 	{ 0, "--identify devname 1" },
 	{ 1, "-p devname 1 foo" },
 	{ 1, "--identify devname foo" },
+	{ 1, "-p" },
 	/* Argument parsing for -t is specialised */
 	{ 0, "-t devname" },
 	{ 0, "--test devname online" },
@@ -115,16 +129,20 @@ static struct test_case {
 	{ 1, "--test devname online foo" },
 	{ 0, "-S devname" },
 	{ 0, "--statistics devname" },
+	{ 1, "-S" },
 	/* Argument parsing for -n is specialised */
 	{ 0, "-n devname rx-flow-hash tcp4" },
 	{ 0, "--show-nfc devname rx-flow-hash udp6" },
 	{ 1, "-n devname rx-flow-hash foo" },
 	{ 1, "--show-nfc devname rx-flow-hash" },
 	{ 1, "-n devname foo" },
+	{ 1, "-n" },
 	/* Argument parsing for -f is specialised */
 	{ 1, "-f devname" },
 	{ 0, "--flash devname filename" },
 	{ 0, "-f devname filename 1" },
+	{ 1, "-f devname filename 1 foo" },
+	{ 1, "-f" },
 	/* Argument parsing for -N is specialised */
 	{ 0, "-N devname rx-flow-hash tcp4 mvtsdfn" },
 	{ 0, "--config-nfc devname rx-flow-hash tcp4 r" },
@@ -132,8 +150,10 @@ static struct test_case {
 	{ 1, "--config-nfc devname rx-flow-hash foo" },
 	{ 1, "-N devname rx-flow-hash" },
 	{ 1, "--config-nfc devname foo" },
+	{ 1, "-N" },
 	{ 0, "-x devname" },
 	{ 0, "--show-rxfh-indir devname" },
+	{ 1, "-x" },
 	/* Argument parsing for -X is specialised */
 	{ 0, "-X devname equal 2" },
 	{ 0, "--set-rxfh-indir devname equal 256" },
@@ -142,6 +162,7 @@ static struct test_case {
 	{ 1, "-X devname equal" },
 	{ 0, "--set-rxfh-indir devname weight 1 2 3 4" },
 	{ 1, "-X devname foo" },
+	{ 1, "-X" },
 	/* Argument parsing for -U is specialised */
 	{ 0, "-U devname delete 1" },
 	{ 1, "--config-ntuple devname delete foo" },
@@ -160,23 +181,30 @@ static struct test_case {
 	{ 1, "-U devname flow-type foo" },
 	{ 1, "--config-ntuple devname flow-type" },
 	{ 1, "-U devname foo" },
+	{ 1, "-U" },
 	{ 0, "-P devname" },
 	{ 0, "--show-permaddr devname" },
+	{ 1, "-P" },
 	{ 0, "-w devname" },
 	{ 0, "--get-dump devname data filename" },
 	{ 0, "-w devname data filename" },
 	{ 1, "--get-dump devname data" },
 	{ 1, "-w devname foo" },
+	{ 1, "-w" },
 	{ 0, "-W devname 1" },
 	{ 0, "--set-dump devname 2" },
+	{ 1, "-W devname 1 foo" },
 	{ 1, "-W devname foo" },
+	{ 1, "-W" },
 	{ 0, "-l devname" },
 	{ 0, "--show-channels devname" },
+	{ 1, "-l" },
 	{ 0, "-L devname rx 1 tx 2 other 3 combined 4" },
 	{ 0, "--set-channels devname rx 1 tx 2 other 3 combined 4" },
 	{ 1, "-L devname rx foo" },
 	{ 1, "--set-channels devname rx" },
 	{ 0, "-L devname" },
+	{ 1, "-L" },
 	{ 0, "-h" },
 	{ 0, "--help" },
 	{ 0, "--version" },
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 10/21] Add test cases for command-line parsing
From: Ben Hutchings @ 2011-11-01 23:17 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

Preparation for refactoring command-line parsing.
All these test cases pass.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 Makefile.am    |    7 ++
 configure.ac   |    1 +
 ethtool.c      |    5 ++
 internal.h     |    4 +
 test-cmdline.c |  206 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 test-common.c  |   92 +++++++++++++++++++++++++
 6 files changed, 315 insertions(+), 0 deletions(-)
 create mode 100644 test-cmdline.c
 create mode 100644 test-common.c

diff --git a/Makefile.am b/Makefile.am
index 1e05640..4b0eb17 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -11,6 +11,13 @@ ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h \
 		  smsc911x.c at76c50x-usb.c sfc.c stmmac.c	\
 		  rxclass.c
 
+TESTS = test-cmdline
+check_PROGRAMS = test-cmdline test-one-cmdline
+test_cmdline_SOURCES = test-cmdline.c test-common.c
+test_cmdline_CFLAGS = -DTEST_ETHTOOL
+test_one_cmdline_SOURCES = $(ethtool_SOURCES)
+test_one_cmdline_CFLAGS = -DTEST_ETHTOOL
+
 dist-hook:
 	cp $(top_srcdir)/ethtool.spec $(distdir)
 
diff --git a/configure.ac b/configure.ac
index 8bc8a56..ac5142b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -10,6 +10,7 @@ AM_MAINTAINER_MODE
 dnl Checks for programs.
 AC_PROG_CC
 AC_PROG_GCC_TRADITIONAL
+AM_PROG_CC_C_O
 
 dnl Checks for libraries.
 
diff --git a/ethtool.c b/ethtool.c
index b6f535c..7a26043 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -3444,8 +3444,13 @@ static int do_setfwdump(struct cmd_context *ctx)
 
 int send_ioctl(struct cmd_context *ctx, void *cmd)
 {
+#ifndef TEST_ETHTOOL
 	ctx->ifr.ifr_data = cmd;
 	return ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr);
+#else
+	/* If we get this far then parsing succeeded */
+	exit(0);
+#endif
 }
 
 int main(int argc, char **argp, char **envp)
diff --git a/internal.h b/internal.h
index c2f504a..ba7d719 100644
--- a/internal.h
+++ b/internal.h
@@ -94,6 +94,10 @@ struct cmd_context {
 	struct ifreq ifr;	/* ifreq suitable for ethtool ioctl */
 };
 
+#ifdef TEST_ETHTOOL
+int test_cmdline(const char *args);
+#endif
+
 int send_ioctl(struct cmd_context *ctx, void *cmd);
 
 /* National Semiconductor DP83815, DP83816 */
diff --git a/test-cmdline.c b/test-cmdline.c
new file mode 100644
index 0000000..88591df
--- /dev/null
+++ b/test-cmdline.c
@@ -0,0 +1,206 @@
+/****************************************************************************
+ * Test cases for ethtool command-line parsing
+ * Copyright 2011 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "internal.h"
+
+static struct test_case {
+	int rc;
+	const char *args;
+} test_cases[] = {
+	{ 1, "" },
+	{ 0, "devname" },
+	{ 0, "15_char_devname" },
+	{ 1, "16_char_devname!" },
+	/* Argument parsing for -s is specialised */
+	{ 0, "-s devname" },
+	{ 0, "--change devname speed 100 duplex half" },
+	{ 1, "-s devname speed foo" },
+	{ 1, "--change devname speed" },
+	{ 0, "-s devname duplex half" },
+	{ 1, "--change devname duplex foo" },
+	{ 1, "-s devname duplex" },
+	{ 0, "--change devname port tp" },
+	{ 1, "-s devname port foo" },
+	{ 1, "--change devname port" },
+	{ 0, "-s devname autoneg on" },
+	{ 1, "--change devname autoneg foo" },
+	{ 1, "-s devname autoneg" },
+	{ 0, "--change devname advertise 0x1" },
+	{ 1, "-s devname advertise foo" },
+	{ 1, "--change devname advertise" },
+	{ 0, "-s devname phyad 1" },
+	{ 1, "--change devname phyad foo" },
+	{ 1, "-s devname phyad" },
+	{ 0, "--change devname xcvr external" },
+	{ 1, "-s devname xcvr foo" },
+	{ 1, "--change devname xcvr" },
+	{ 0, "-s devname wol p" },
+	{ 1, "--change devname wol" },
+	{ 0, "-s devname sopass 01:23:45:67:89:ab" },
+	{ 1, "--change devname sopass 01:23:45:67:89:" },
+	{ 1, "-s devname sopass 01:23:45:67:89" },
+	{ 1, "--change devname sopass" },
+	{ 0, "-s devname msglvl 1" },
+	{ 1, "--change devname msglvl" },
+	{ 0, "-s devname msglvl hw on rx_status off" },
+	{ 1, "--change devname msglvl hw foo" },
+	{ 1, "-s devname msglvl hw" },
+	{ 0, "--change devname speed 100 duplex half port tp autoneg on advertise 0x1 phyad 1 xcvr external wol p sopass 01:23:45:67:89:ab msglvl 1" },
+	{ 1, "-s devname foo" },
+	{ 0, "-a devname" },
+	{ 0, "--show-pause devname" },
+	/* Many other sub-commands use parse_generic_cmdline() and
+	 * don't need to be check in that much detail. */
+	{ 0, "-A devname autoneg on" },
+	{ 1, "--pause devname autoneg foo" },
+	{ 1, "-A devname autoneg" },
+	{ 0, "--pause devname rx off" },
+	{ 0, "-A devname tx on rx on autoneg off" },
+	{ 1, "--pause devname foo on" },
+	{ 0, "-c devname" },
+	{ 0, "--show-coalesce devname" },
+	{ 0, "-C devname adaptive-rx on adaptive-tx off rx-usecs 1 rx-frames 2 rx-usecs-irq 3 rx-frames-irq 4 tx-usecs 5 tx-frames 6 tx-usecs-irq 7 tx-frames-irq 8 stats-block-usecs 9 pkt-rate-low 10" },
+	{ 0, "--coalesce devname rx-usecs-low 11 rx-frames-low 12 tx-usecs-low 13 tx-frames-low 14 pkt-rate-high 15 rx-usecs-high 16 rx-frames-high 17 tx-usecs-high 18 tx-frames-high 19 sample-interval 20" },
+	{ 1, "-C devname adaptive-rx foo" },
+	{ 1, "--coalesce devname adaptive-rx" },
+	{ 1, "-C devname foo on" },
+	{ 0, "-g devname" },
+	{ 0, "--show-ring devname" },
+	{ 0, "-G devname rx 1 rx-mini 2 rx-jumbo 3 tx 4" },
+	{ 0, "--set-ring devname rx 1 rx-mini 2 rx-jumbo 3 tx 4" },
+	{ 1, "-G devname rx foo" },
+	{ 1, "--set-ring devname rx" },
+	{ 1, "-G devname foo 1" },
+	{ 0, "-k devname" },
+	{ 0, "--show-offload devname" },
+	{ 0, "-K devname rx on tx off sg on tso off ufo on gso off gro on" },
+	{ 0, "--offload devname lro off rxvlan on txvlan off ntuple on rxhash off" },
+	{ 1, "-K devname rx foo" },
+	{ 1, "--offload devname rx" },
+	{ 1, "-K devname foo on" },
+	{ 0, "-i devname" },
+	{ 0, "--driver devname" },
+	{ 0, "-d devname" },
+	{ 0, "--register-dump devname raw on file foo" },
+	{ 1, "-d devname raw foo" },
+	{ 1, "--register-dump devname file" },
+	{ 1, "-d devname foo" },
+	{ 0, "-e devname" },
+	{ 0, "--eeprom-dump devname raw on offset 1 length 2" },
+	{ 1, "-e devname raw foo" },
+	{ 1, "--eeprom-dump devname offset foo" },
+	{ 1, "-e devname length" },
+	{ 1, "--eeprom-dump devname foo" },
+	{ 0, "-E devname" },
+	{ 0, "--change-eeprom devname magic 0x87654321 offset 0 value 1" },
+	{ 0, "-E devname magic 0x87654321 offset 0 length 2" },
+	{ 0, "-r devname" },
+	{ 0, "--negotiate devname" },
+	{ 0, "-p devname" },
+	{ 0, "--identify devname 1" },
+	{ 1, "-p devname 1 foo" },
+	{ 1, "--identify devname foo" },
+	/* Argument parsing for -t is specialised */
+	{ 0, "-t devname" },
+	{ 0, "--test devname online" },
+	{ 1, "-t devname foo" },
+	{ 1, "--test devname online foo" },
+	{ 0, "-S devname" },
+	{ 0, "--statistics devname" },
+	/* Argument parsing for -n is specialised */
+	{ 0, "-n devname rx-flow-hash tcp4" },
+	{ 0, "--show-nfc devname rx-flow-hash udp6" },
+	{ 1, "-n devname rx-flow-hash foo" },
+	{ 1, "--show-nfc devname rx-flow-hash" },
+	{ 1, "-n devname foo" },
+	/* Argument parsing for -f is specialised */
+	{ 1, "-f devname" },
+	{ 0, "--flash devname filename" },
+	{ 0, "-f devname filename 1" },
+	/* Argument parsing for -N is specialised */
+	{ 0, "-N devname rx-flow-hash tcp4 mvtsdfn" },
+	{ 0, "--config-nfc devname rx-flow-hash tcp4 r" },
+	{ 1, "-N devname rx-flow-hash tcp4" },
+	{ 1, "--config-nfc devname rx-flow-hash foo" },
+	{ 1, "-N devname rx-flow-hash" },
+	{ 1, "--config-nfc devname foo" },
+	{ 0, "-x devname" },
+	{ 0, "--show-rxfh-indir devname" },
+	/* Argument parsing for -X is specialised */
+	{ 0, "-X devname equal 2" },
+	{ 0, "--set-rxfh-indir devname equal 256" },
+	{ 1, "-X devname equal 0" },
+	{ 1, "--set-rxfh-indir devname equal foo" },
+	{ 1, "-X devname equal" },
+	{ 0, "--set-rxfh-indir devname weight 1 2 3 4" },
+	{ 1, "-X devname foo" },
+	/* Argument parsing for -U is specialised */
+	{ 0, "-U devname delete 1" },
+	{ 1, "--config-ntuple devname delete foo" },
+	{ 1, "-U devname delete" },
+	{ 0, "--config-ntuple devname flow-type ether src 01:23:45:67:89:ab m cd:ef:01:23:45:67 dst 89:ab:cd:ef:01:23 m 45:67:89:ab:cd:ef proto 0x0123 m 0x4567 vlan 0x89ab m 0xcdef action 0" },
+	{ 0, "-U devname flow-type ether src 01:23:45:67:89:ab src-mask cd:ef:01:23:45:67 dst 89:ab:cd:ef:01:23 dst-mask 45:67:89:ab:cd:ef proto 0x0123 proto-mask 0x4567 vlan 0x89ab vlan-mask 0xcdef action 1" },
+	{ 1, "--config-ntuple devname flow-type ether src 01:23:45:67:89: action 3" },
+	{ 1, "-U devname flow-type ether src 01:23:45:67:89 action 4" },
+	{ 0, "--config-ntuple devname flow-type ip4 src-ip 0.123.45.67 m 89.0.123.45 dst-ip 67.89.0.123 m 45.67.89.0 tos 1 m 1 l4proto 0x23 m 0x45 l4data 0xfedcba98 m 76543210 vlan 0x89ab m 0xcdef action 6" },
+	{ 0, "-U devname flow-type ip4 src-ip 0.123.45.67 src-ip-mask 89.0.123.45 dst-ip 67.89.0.123 dst-ip-mask 45.67.89.0 tos 1 tos-mask 1 l4proto 0x23 l4proto-mask 0x45 l4data 0xfedcba98 l4data-mask 76543210 vlan 0x89ab vlan-mask 0xcdef action 7" },
+	{ 0, "--config-ntuple devname flow-type tcp4 src-ip 0.123.45.67 m 89.0.123.45 dst-ip 67.89.0.123 m 45.67.89.0 tos 1 m 1 src-port 23456 m 7890 dst-port 12345 m 6789 vlan 0x89ab m 0xcdef action 8" },
+	{ 0, "-U devname flow-type tcp4 src-ip 0.123.45.67 src-ip-mask 89.0.123.45 dst-ip 67.89.0.123 dst-ip-mask 45.67.89.0 tos 1 tos-mask 1 src-port 23456 src-port-mask 7890 dst-port 12345 dst-port-mask 6789 vlan 0x89ab vlan-mask 0xcdef action 9" },
+	{ 0, "--config-ntuple devname flow-type ah4 src-ip 0.123.45.67 m 89.0.123.45 dst-ip 67.89.0.123 m 45.67.89.0 tos 1 m 1 spi 2 m 3 vlan 0x89ab m 0xcdef action 10" },
+	{ 0, "-U devname flow-type ah4 src-ip 0.123.45.67 src-ip-mask 89.0.123.45 dst-ip 67.89.0.123 dst-ip-mask 45.67.89.0 tos 1 tos-mask 1 spi 2 spi-mask 3 vlan 0x89ab vlan-mask 0xcdef action 11" },
+	{ 1, "--config-ntuple devname flow-type tcp4 action foo" },
+	{ 1, "-U devname flow-type foo" },
+	{ 1, "--config-ntuple devname flow-type" },
+	{ 1, "-U devname foo" },
+	{ 0, "-P devname" },
+	{ 0, "--show-permaddr devname" },
+	{ 0, "-w devname" },
+	{ 0, "--get-dump devname data filename" },
+	{ 0, "-w devname data filename" },
+	{ 1, "--get-dump devname data" },
+	{ 1, "-w devname foo" },
+	{ 0, "-W devname 1" },
+	{ 0, "--set-dump devname 2" },
+	{ 1, "-W devname foo" },
+	{ 0, "-l devname" },
+	{ 0, "--show-channels devname" },
+	{ 0, "-L devname rx 1 tx 2 other 3 combined 4" },
+	{ 0, "--set-channels devname rx 1 tx 2 other 3 combined 4" },
+	{ 1, "-L devname rx foo" },
+	{ 1, "--set-channels devname rx" },
+	{ 0, "-L devname" },
+	{ 0, "-h" },
+	{ 0, "--help" },
+	{ 0, "--version" },
+	{ 1, "--foo" },
+	{ 1, "-foo" },
+	{ 1, "-0" },
+};
+
+int main(void)
+{
+	struct test_case *tc;
+	int test_rc;
+	int rc = 0;
+
+	for (tc = test_cases; tc < test_cases + ARRAY_SIZE(test_cases); tc++) {
+		if (getenv("ETHTOOL_TEST_VERBOSE"))
+			printf("I: Test command line: ethtool %s\n", tc->args);
+		test_rc = test_cmdline(tc->args);
+		if (test_rc != tc->rc) {
+			fprintf(stderr, "E: ethtool %s returns %d\n",
+				tc->args, test_rc);
+			rc = 1;
+		}
+	}
+
+	return rc;
+}
diff --git a/test-common.c b/test-common.c
new file mode 100644
index 0000000..4ea84c8
--- /dev/null
+++ b/test-common.c
@@ -0,0 +1,92 @@
+/****************************************************************************
+ * Common test functions for ethtool
+ * Copyright 2011 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include "internal.h"
+
+int test_cmdline(const char *args)
+{
+	int argc, i;
+	char **argv;
+	const char *arg;
+	size_t len;
+	pid_t pid;
+	int dev_null;
+	int status;
+	int rc;
+
+	/* Convert line to argv */
+	argc = 1;
+	arg = args;
+	for (;;) {
+		len = strcspn(arg, " ");
+		if (len == 0)
+			break;
+		argc++;
+		if (arg[len] == 0)
+			break;
+		arg += len + 1;
+	}
+	argv = calloc(argc + 1, sizeof(argv[0]));
+	argv[0] = strdup("ethtool");
+	arg = args;
+	for (i = 1; i < argc; i++) {
+		len = strcspn(arg, " ");
+		argv[i] = malloc(len + 1);
+		memcpy(argv[i], arg, len);
+		argv[i][len] = 0;
+		arg += len + 1;
+	}
+
+	dev_null = open("/dev/null", O_RDWR);
+	if (dev_null < 0) {
+		perror("open /dev/null");
+		rc = -1;
+		goto out;
+	}
+
+	fflush(NULL);
+	pid = fork();
+
+	/* Child */
+	if (pid == 0) {
+		dup2(dev_null, STDIN_FILENO);
+		if (!getenv("ETHTOOL_TEST_VERBOSE")) {
+			dup2(dev_null, STDOUT_FILENO);
+			dup2(dev_null, STDERR_FILENO);
+		}
+		execv("./test-one-cmdline", argv);
+		_exit(126);
+	}
+
+	/* Parent */
+	if (pid < 0) {
+		perror("fork");
+		close(dev_null);
+		rc = -1;
+		goto out;
+	}
+	close(dev_null);
+	if (waitpid(pid, &status, 0) < 0) {
+		perror("waitpid");
+		rc = -1;
+		goto out;
+	}
+	rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+
+out:
+	for (i = 0; i < argc; i++)
+		free(argv[i]);
+	free(argv);
+	return rc;
+}
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 09/21] Encapsulate command context in a structure
From: Ben Hutchings @ 2011-11-01 23:16 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

Replace the fd and ifr arguments to each sub-command handler with
struct cmd_context.  Change send_ioctl() to take a pointer to
this context and a pointer to the ethtool_cmd or other structure.
Use send_ioctl() consistently.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.c  |  419 +++++++++++++++++++++++++-----------------------------------
 internal.h |   16 ++-
 rxclass.c  |   42 +++----
 3 files changed, 204 insertions(+), 273 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index f967f84..b6f535c 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -67,42 +67,40 @@ enum {
 static int parse_wolopts(char *optstr, u32 *data);
 static char *unparse_wolopts(int wolopts);
 static void get_mac_addr(char *src, unsigned char *dest);
-static int do_gdrv(int fd, struct ifreq *ifr);
-static int do_gset(int fd, struct ifreq *ifr);
-static int do_sset(int fd, struct ifreq *ifr);
-static int do_gregs(int fd, struct ifreq *ifr);
-static int do_nway_rst(int fd, struct ifreq *ifr);
-static int do_geeprom(int fd, struct ifreq *ifr);
-static int do_seeprom(int fd, struct ifreq *ifr);
-static int do_test(int fd, struct ifreq *ifr);
-static int do_phys_id(int fd, struct ifreq *ifr);
-static int do_gpause(int fd, struct ifreq *ifr);
-static int do_spause(int fd, struct ifreq *ifr);
-static int do_gring(int fd, struct ifreq *ifr);
-static int do_sring(int fd, struct ifreq *ifr);
-static int do_schannels(int fd, struct ifreq *ifr);
-static int do_gchannels(int fd, struct ifreq *ifr);
-static int do_gcoalesce(int fd, struct ifreq *ifr);
-static int do_scoalesce(int fd, struct ifreq *ifr);
-static int do_goffload(int fd, struct ifreq *ifr);
-static int do_soffload(int fd, struct ifreq *ifr);
-static int do_gstats(int fd, struct ifreq *ifr);
+static int do_gdrv(struct cmd_context *ctx);
+static int do_gset(struct cmd_context *ctx);
+static int do_sset(struct cmd_context *ctx);
+static int do_gregs(struct cmd_context *ctx);
+static int do_nway_rst(struct cmd_context *ctx);
+static int do_geeprom(struct cmd_context *ctx);
+static int do_seeprom(struct cmd_context *ctx);
+static int do_test(struct cmd_context *ctx);
+static int do_phys_id(struct cmd_context *ctx);
+static int do_gpause(struct cmd_context *ctx);
+static int do_spause(struct cmd_context *ctx);
+static int do_gring(struct cmd_context *ctx);
+static int do_sring(struct cmd_context *ctx);
+static int do_schannels(struct cmd_context *ctx);
+static int do_gchannels(struct cmd_context *ctx);
+static int do_gcoalesce(struct cmd_context *ctx);
+static int do_scoalesce(struct cmd_context *ctx);
+static int do_goffload(struct cmd_context *ctx);
+static int do_soffload(struct cmd_context *ctx);
+static int do_gstats(struct cmd_context *ctx);
 static int rxflow_str_to_type(const char *str);
 static int parse_rxfhashopts(char *optstr, u32 *data);
 static char *unparse_rxfhashopts(u64 opts);
 static int dump_rxfhash(int fhash, u64 val);
-static int do_srxclass(int fd, struct ifreq *ifr);
-static int do_grxclass(int fd, struct ifreq *ifr);
-static int do_grxfhindir(int fd, struct ifreq *ifr);
-static int do_srxfhindir(int fd, struct ifreq *ifr);
-static int do_srxclsrule(int fd, struct ifreq *ifr);
-static int do_grxclsrule(int fd, struct ifreq *ifr);
-static int do_flash(int fd, struct ifreq *ifr);
-static int do_permaddr(int fd, struct ifreq *ifr);
-static int do_getfwdump(int fd, struct ifreq *ifr);
-static int do_setfwdump(int fd, struct ifreq *ifr);
-
-static int send_ioctl(int fd, struct ifreq *ifr);
+static int do_srxclass(struct cmd_context *ctx);
+static int do_grxclass(struct cmd_context *ctx);
+static int do_grxfhindir(struct cmd_context *ctx);
+static int do_srxfhindir(struct cmd_context *ctx);
+static int do_srxclsrule(struct cmd_context *ctx);
+static int do_grxclsrule(struct cmd_context *ctx);
+static int do_flash(struct cmd_context *ctx);
+static int do_permaddr(struct cmd_context *ctx);
+static int do_getfwdump(struct cmd_context *ctx);
+static int do_setfwdump(struct cmd_context *ctx);
 
 static enum {
 	MODE_VERSION = -2,
@@ -1982,94 +1980,92 @@ static int dump_rxfhash(int fhash, u64 val)
 
 static int doit(void)
 {
-	struct ifreq ifr;
-	int fd;
+	struct cmd_context ctx;
 
 	/* Setup our control structures. */
-	memset(&ifr, 0, sizeof(ifr));
-	strcpy(ifr.ifr_name, devname);
+	memset(&ctx.ifr, 0, sizeof(ctx.ifr));
+	strcpy(ctx.ifr.ifr_name, devname);
 
 	/* Open control socket. */
-	fd = socket(AF_INET, SOCK_DGRAM, 0);
-	if (fd < 0) {
+	ctx.fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (ctx.fd < 0) {
 		perror("Cannot get control socket");
 		return 70;
 	}
 
 	/* all of these are expected to populate ifr->ifr_data as needed */
 	if (mode == MODE_GDRV) {
-		return do_gdrv(fd, &ifr);
+		return do_gdrv(&ctx);
 	} else if (mode == MODE_GSET) {
-		return do_gset(fd, &ifr);
+		return do_gset(&ctx);
 	} else if (mode == MODE_SSET) {
-		return do_sset(fd, &ifr);
+		return do_sset(&ctx);
 	} else if (mode == MODE_GREGS) {
-		return do_gregs(fd, &ifr);
+		return do_gregs(&ctx);
 	} else if (mode == MODE_NWAY_RST) {
-		return do_nway_rst(fd, &ifr);
+		return do_nway_rst(&ctx);
 	} else if (mode == MODE_GEEPROM) {
-		return do_geeprom(fd, &ifr);
+		return do_geeprom(&ctx);
 	} else if (mode == MODE_SEEPROM) {
-		return do_seeprom(fd, &ifr);
+		return do_seeprom(&ctx);
 	} else if (mode == MODE_TEST) {
-		return do_test(fd, &ifr);
+		return do_test(&ctx);
 	} else if (mode == MODE_PHYS_ID) {
-		return do_phys_id(fd, &ifr);
+		return do_phys_id(&ctx);
 	} else if (mode == MODE_GPAUSE) {
-		return do_gpause(fd, &ifr);
+		return do_gpause(&ctx);
 	} else if (mode == MODE_SPAUSE) {
-		return do_spause(fd, &ifr);
+		return do_spause(&ctx);
 	} else if (mode == MODE_GCOALESCE) {
-		return do_gcoalesce(fd, &ifr);
+		return do_gcoalesce(&ctx);
 	} else if (mode == MODE_SCOALESCE) {
-		return do_scoalesce(fd, &ifr);
+		return do_scoalesce(&ctx);
 	} else if (mode == MODE_GRING) {
-		return do_gring(fd, &ifr);
+		return do_gring(&ctx);
 	} else if (mode == MODE_SRING) {
-		return do_sring(fd, &ifr);
+		return do_sring(&ctx);
 	} else if (mode == MODE_GCHANNELS) {
-		return do_gchannels(fd, &ifr);
+		return do_gchannels(&ctx);
 	} else if (mode == MODE_SCHANNELS) {
-		return do_schannels(fd, &ifr);
+		return do_schannels(&ctx);
 	} else if (mode == MODE_GOFFLOAD) {
-		return do_goffload(fd, &ifr);
+		return do_goffload(&ctx);
 	} else if (mode == MODE_SOFFLOAD) {
-		return do_soffload(fd, &ifr);
+		return do_soffload(&ctx);
 	} else if (mode == MODE_GSTATS) {
-		return do_gstats(fd, &ifr);
+		return do_gstats(&ctx);
 	} else if (mode == MODE_GNFC) {
-		return do_grxclass(fd, &ifr);
+		return do_grxclass(&ctx);
 	} else if (mode == MODE_SNFC) {
-		return do_srxclass(fd, &ifr);
+		return do_srxclass(&ctx);
 	} else if (mode == MODE_GRXFHINDIR) {
-		return do_grxfhindir(fd, &ifr);
+		return do_grxfhindir(&ctx);
 	} else if (mode == MODE_SRXFHINDIR) {
-		return do_srxfhindir(fd, &ifr);
+		return do_srxfhindir(&ctx);
 	} else if (mode == MODE_SCLSRULE) {
-		return do_srxclsrule(fd, &ifr);
+		return do_srxclsrule(&ctx);
 	} else if (mode == MODE_GCLSRULE) {
-		return do_grxclsrule(fd, &ifr);
+		return do_grxclsrule(&ctx);
 	} else if (mode == MODE_FLASHDEV) {
-		return do_flash(fd, &ifr);
+		return do_flash(&ctx);
 	} else if (mode == MODE_PERMADDR) {
-		return do_permaddr(fd, &ifr);
+		return do_permaddr(&ctx);
 	} else if (mode == MODE_GET_DUMP) {
-		return do_getfwdump(fd, &ifr);
+		return do_getfwdump(&ctx);
 	} else if (mode == MODE_SET_DUMP) {
-		return do_setfwdump(fd, &ifr);
+		return do_setfwdump(&ctx);
 	}
 
 	return 69;
 }
 
-static int do_gdrv(int fd, struct ifreq *ifr)
+static int do_gdrv(struct cmd_context *ctx)
 {
 	int err;
 	struct ethtool_drvinfo drvinfo;
 
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
-	ifr->ifr_data = (caddr_t)&drvinfo;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
 		perror("Cannot get driver information");
 		return 71;
@@ -2077,7 +2073,7 @@ static int do_gdrv(int fd, struct ifreq *ifr)
 	return dump_drvinfo(&drvinfo);
 }
 
-static int do_gpause(int fd, struct ifreq *ifr)
+static int do_gpause(struct cmd_context *ctx)
 {
 	struct ethtool_cmd ecmd;
 	int err;
@@ -2085,8 +2081,7 @@ static int do_gpause(int fd, struct ifreq *ifr)
 	fprintf(stdout, "Pause parameters for %s:\n", devname);
 
 	epause.cmd = ETHTOOL_GPAUSEPARAM;
-	ifr->ifr_data = (caddr_t)&epause;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &epause);
 	if (err) {
 		perror("Cannot get device pause settings");
 		return 76;
@@ -2094,8 +2089,7 @@ static int do_gpause(int fd, struct ifreq *ifr)
 
 	if (epause.autoneg) {
 		ecmd.cmd = ETHTOOL_GSET;
-		ifr->ifr_data = (caddr_t)&ecmd;
-		err = send_ioctl(fd, ifr);
+		err = send_ioctl(ctx, &ecmd);
 		if (err) {
 			perror("Cannot get device settings");
 			return 1;
@@ -2137,13 +2131,12 @@ static void do_generic_set(struct cmdline_info *info,
 		do_generic_set1(&info[i], changed_out);
 }
 
-static int do_spause(int fd, struct ifreq *ifr)
+static int do_spause(struct cmd_context *ctx)
 {
 	int err, changed = 0;
 
 	epause.cmd = ETHTOOL_GPAUSEPARAM;
-	ifr->ifr_data = (caddr_t)&epause;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &epause);
 	if (err) {
 		perror("Cannot get device pause settings");
 		return 77;
@@ -2157,8 +2150,7 @@ static int do_spause(int fd, struct ifreq *ifr)
 	}
 
 	epause.cmd = ETHTOOL_SPAUSEPARAM;
-	ifr->ifr_data = (caddr_t)&epause;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &epause);
 	if (err) {
 		perror("Cannot set device pause parameters");
 		return 79;
@@ -2167,13 +2159,12 @@ static int do_spause(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_sring(int fd, struct ifreq *ifr)
+static int do_sring(struct cmd_context *ctx)
 {
 	int err, changed = 0;
 
 	ering.cmd = ETHTOOL_GRINGPARAM;
-	ifr->ifr_data = (caddr_t)&ering;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &ering);
 	if (err) {
 		perror("Cannot get device ring settings");
 		return 76;
@@ -2187,8 +2178,7 @@ static int do_sring(int fd, struct ifreq *ifr)
 	}
 
 	ering.cmd = ETHTOOL_SRINGPARAM;
-	ifr->ifr_data = (caddr_t)&ering;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &ering);
 	if (err) {
 		perror("Cannot set device ring parameters");
 		return 81;
@@ -2197,15 +2187,14 @@ static int do_sring(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_gring(int fd, struct ifreq *ifr)
+static int do_gring(struct cmd_context *ctx)
 {
 	int err;
 
 	fprintf(stdout, "Ring parameters for %s:\n", devname);
 
 	ering.cmd = ETHTOOL_GRINGPARAM;
-	ifr->ifr_data = (caddr_t)&ering;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &ering);
 	if (err == 0) {
 		err = dump_ring();
 		if (err)
@@ -2218,13 +2207,12 @@ static int do_gring(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_schannels(int fd, struct ifreq *ifr)
+static int do_schannels(struct cmd_context *ctx)
 {
 	int err, changed = 0;
 
 	echannels.cmd = ETHTOOL_GCHANNELS;
-	ifr->ifr_data = (caddr_t)&echannels;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &echannels);
 	if (err) {
 		perror("Cannot get device channel parameters");
 		return 1;
@@ -2243,8 +2231,7 @@ static int do_schannels(int fd, struct ifreq *ifr)
 	}
 
 	echannels.cmd = ETHTOOL_SCHANNELS;
-	ifr->ifr_data = (caddr_t)&echannels;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &echannels);
 	if (err) {
 		perror("Cannot set device channel parameters");
 		return 1;
@@ -2253,15 +2240,14 @@ static int do_schannels(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_gchannels(int fd, struct ifreq *ifr)
+static int do_gchannels(struct cmd_context *ctx)
 {
 	int err;
 
 	fprintf(stdout, "Channel parameters for %s:\n", devname);
 
 	echannels.cmd = ETHTOOL_GCHANNELS;
-	ifr->ifr_data = (caddr_t)&echannels;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &echannels);
 	if (err == 0) {
 		err = dump_channels();
 		if (err)
@@ -2274,15 +2260,14 @@ static int do_gchannels(int fd, struct ifreq *ifr)
 
 }
 
-static int do_gcoalesce(int fd, struct ifreq *ifr)
+static int do_gcoalesce(struct cmd_context *ctx)
 {
 	int err;
 
 	fprintf(stdout, "Coalesce parameters for %s:\n", devname);
 
 	ecoal.cmd = ETHTOOL_GCOALESCE;
-	ifr->ifr_data = (caddr_t)&ecoal;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &ecoal);
 	if (err == 0) {
 		err = dump_coalesce();
 		if (err)
@@ -2295,13 +2280,12 @@ static int do_gcoalesce(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_scoalesce(int fd, struct ifreq *ifr)
+static int do_scoalesce(struct cmd_context *ctx)
 {
 	int err, changed = 0;
 
 	ecoal.cmd = ETHTOOL_GCOALESCE;
-	ifr->ifr_data = (caddr_t)&ecoal;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &ecoal);
 	if (err) {
 		perror("Cannot get device coalesce settings");
 		return 76;
@@ -2316,8 +2300,7 @@ static int do_scoalesce(int fd, struct ifreq *ifr)
 	}
 
 	ecoal.cmd = ETHTOOL_SCOALESCE;
-	ifr->ifr_data = (caddr_t)&ecoal;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &ecoal);
 	if (err) {
 		perror("Cannot set device coalesce parameters");
 		return 81;
@@ -2326,7 +2309,7 @@ static int do_scoalesce(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_goffload(int fd, struct ifreq *ifr)
+static int do_goffload(struct cmd_context *ctx)
 {
 	struct ethtool_value eval;
 	int err, allfail = 1, rx = 0, tx = 0, sg = 0;
@@ -2336,8 +2319,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	fprintf(stdout, "Offload parameters for %s:\n", devname);
 
 	eval.cmd = ETHTOOL_GRXCSUM;
-	ifr->ifr_data = (caddr_t)&eval;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &eval);
 	if (err)
 		perror("Cannot get device rx csum settings");
 	else {
@@ -2346,8 +2328,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	}
 
 	eval.cmd = ETHTOOL_GTXCSUM;
-	ifr->ifr_data = (caddr_t)&eval;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &eval);
 	if (err)
 		perror("Cannot get device tx csum settings");
 	else {
@@ -2356,8 +2337,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	}
 
 	eval.cmd = ETHTOOL_GSG;
-	ifr->ifr_data = (caddr_t)&eval;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &eval);
 	if (err)
 		perror("Cannot get device scatter-gather settings");
 	else {
@@ -2366,8 +2346,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	}
 
 	eval.cmd = ETHTOOL_GTSO;
-	ifr->ifr_data = (caddr_t)&eval;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &eval);
 	if (err)
 		perror("Cannot get device tcp segmentation offload settings");
 	else {
@@ -2376,8 +2355,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	}
 
 	eval.cmd = ETHTOOL_GUFO;
-	ifr->ifr_data = (caddr_t)&eval;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, &eval);
 	if (err)
 		perror("Cannot get device udp large send offload settings");
 	else {
@@ -2386,8 +2364,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	}
 
 	eval.cmd = ETHTOOL_GGSO;
-	ifr->ifr_data = (caddr_t)&eval;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, &eval);
 	if (err)
 		perror("Cannot get device generic segmentation offload settings");
 	else {
@@ -2396,8 +2373,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	}
 
 	eval.cmd = ETHTOOL_GFLAGS;
-	ifr->ifr_data = (caddr_t)&eval;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, &eval);
 	if (err) {
 		perror("Cannot get device flags");
 	} else {
@@ -2410,8 +2386,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 	}
 
 	eval.cmd = ETHTOOL_GGRO;
-	ifr->ifr_data = (caddr_t)&eval;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, &eval);
 	if (err)
 		perror("Cannot get device GRO settings");
 	else {
@@ -2428,7 +2403,7 @@ static int do_goffload(int fd, struct ifreq *ifr)
 			    ntuple, rxhash);
 }
 
-static int do_soffload(int fd, struct ifreq *ifr)
+static int do_soffload(struct cmd_context *ctx)
 {
 	struct ethtool_value eval;
 	int err, changed = 0;
@@ -2437,8 +2412,7 @@ static int do_soffload(int fd, struct ifreq *ifr)
 		changed = 1;
 		eval.cmd = ETHTOOL_SRXCSUM;
 		eval.data = (off_csum_rx_wanted == 1);
-		ifr->ifr_data = (caddr_t)&eval;
-		err = send_ioctl(fd, ifr);
+		err = send_ioctl(ctx, &eval);
 		if (err) {
 			perror("Cannot set device rx csum settings");
 			return 84;
@@ -2449,8 +2423,7 @@ static int do_soffload(int fd, struct ifreq *ifr)
 		changed = 1;
 		eval.cmd = ETHTOOL_STXCSUM;
 		eval.data = (off_csum_tx_wanted == 1);
-		ifr->ifr_data = (caddr_t)&eval;
-		err = send_ioctl(fd, ifr);
+		err = send_ioctl(ctx, &eval);
 		if (err) {
 			perror("Cannot set device tx csum settings");
 			return 85;
@@ -2461,8 +2434,7 @@ static int do_soffload(int fd, struct ifreq *ifr)
 		changed = 1;
 		eval.cmd = ETHTOOL_SSG;
 		eval.data = (off_sg_wanted == 1);
-		ifr->ifr_data = (caddr_t)&eval;
-		err = send_ioctl(fd, ifr);
+		err = send_ioctl(ctx, &eval);
 		if (err) {
 			perror("Cannot set device scatter-gather settings");
 			return 86;
@@ -2473,8 +2445,7 @@ static int do_soffload(int fd, struct ifreq *ifr)
 		changed = 1;
 		eval.cmd = ETHTOOL_STSO;
 		eval.data = (off_tso_wanted == 1);
-		ifr->ifr_data = (caddr_t)&eval;
-		err = send_ioctl(fd, ifr);
+		err = send_ioctl(ctx, &eval);
 		if (err) {
 			perror("Cannot set device tcp segmentation offload settings");
 			return 88;
@@ -2484,8 +2455,7 @@ static int do_soffload(int fd, struct ifreq *ifr)
 		changed = 1;
 		eval.cmd = ETHTOOL_SUFO;
 		eval.data = (off_ufo_wanted == 1);
-		ifr->ifr_data = (caddr_t)&eval;
-		err = ioctl(fd, SIOCETHTOOL, ifr);
+		err = send_ioctl(ctx, &eval);
 		if (err) {
 			perror("Cannot set device udp large send offload settings");
 			return 89;
@@ -2495,8 +2465,7 @@ static int do_soffload(int fd, struct ifreq *ifr)
 		changed = 1;
 		eval.cmd = ETHTOOL_SGSO;
 		eval.data = (off_gso_wanted == 1);
-		ifr->ifr_data = (caddr_t)&eval;
-		err = ioctl(fd, SIOCETHTOOL, ifr);
+		err = send_ioctl(ctx, &eval);
 		if (err) {
 			perror("Cannot set device generic segmentation offload settings");
 			return 90;
@@ -2506,8 +2475,7 @@ static int do_soffload(int fd, struct ifreq *ifr)
 		changed = 1;
 		eval.cmd = ETHTOOL_GFLAGS;
 		eval.data = 0;
-		ifr->ifr_data = (caddr_t)&eval;
-		err = ioctl(fd, SIOCETHTOOL, ifr);
+		err = send_ioctl(ctx, &eval);
 		if (err) {
 			perror("Cannot get device flag settings");
 			return 91;
@@ -2517,7 +2485,7 @@ static int do_soffload(int fd, struct ifreq *ifr)
 		eval.data = ((eval.data & ~off_flags_mask) |
 			     off_flags_wanted);
 
-		err = ioctl(fd, SIOCETHTOOL, ifr);
+		err = send_ioctl(ctx, &eval);
 		if (err) {
 			perror("Cannot set device flag settings");
 			return 92;
@@ -2527,8 +2495,7 @@ static int do_soffload(int fd, struct ifreq *ifr)
 		changed = 1;
 		eval.cmd = ETHTOOL_SGRO;
 		eval.data = (off_gro_wanted == 1);
-		ifr->ifr_data = (caddr_t)&eval;
-		err = ioctl(fd, SIOCETHTOOL, ifr);
+		err = send_ioctl(ctx, &eval);
 		if (err) {
 			perror("Cannot set device GRO settings");
 			return 93;
@@ -2542,7 +2509,7 @@ static int do_soffload(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_gset(int fd, struct ifreq *ifr)
+static int do_gset(struct cmd_context *ctx)
 {
 	int err;
 	struct ethtool_cmd ecmd;
@@ -2553,8 +2520,7 @@ static int do_gset(int fd, struct ifreq *ifr)
 	fprintf(stdout, "Settings for %s:\n", devname);
 
 	ecmd.cmd = ETHTOOL_GSET;
-	ifr->ifr_data = (caddr_t)&ecmd;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &ecmd);
 	if (err == 0) {
 		err = dump_ecmd(&ecmd);
 		if (err)
@@ -2565,8 +2531,7 @@ static int do_gset(int fd, struct ifreq *ifr)
 	}
 
 	wolinfo.cmd = ETHTOOL_GWOL;
-	ifr->ifr_data = (caddr_t)&wolinfo;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &wolinfo);
 	if (err == 0) {
 		err = dump_wol(&wolinfo);
 		if (err)
@@ -2577,8 +2542,7 @@ static int do_gset(int fd, struct ifreq *ifr)
 	}
 
 	edata.cmd = ETHTOOL_GMSGLVL;
-	ifr->ifr_data = (caddr_t)&edata;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &edata);
 	if (err == 0) {
 		fprintf(stdout, "	Current message level: 0x%08x (%d)\n"
 			"			       ",
@@ -2592,8 +2556,7 @@ static int do_gset(int fd, struct ifreq *ifr)
 	}
 
 	edata.cmd = ETHTOOL_GLINK;
-	ifr->ifr_data = (caddr_t)&edata;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &edata);
 	if (err == 0) {
 		fprintf(stdout, "	Link detected: %s\n",
 			edata.data ? "yes":"no");
@@ -2609,7 +2572,7 @@ static int do_gset(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_sset(int fd, struct ifreq *ifr)
+static int do_sset(struct cmd_context *ctx)
 {
 	int err;
 
@@ -2617,8 +2580,7 @@ static int do_sset(int fd, struct ifreq *ifr)
 		struct ethtool_cmd ecmd;
 
 		ecmd.cmd = ETHTOOL_GSET;
-		ifr->ifr_data = (caddr_t)&ecmd;
-		err = send_ioctl(fd, ifr);
+		err = send_ioctl(ctx, &ecmd);
 		if (err < 0) {
 			perror("Cannot get current device settings");
 		} else {
@@ -2671,8 +2633,7 @@ static int do_sset(int fd, struct ifreq *ifr)
 
 			/* Try to perform the update. */
 			ecmd.cmd = ETHTOOL_SSET;
-			ifr->ifr_data = (caddr_t)&ecmd;
-			err = send_ioctl(fd, ifr);
+			err = send_ioctl(ctx, &ecmd);
 			if (err < 0)
 				perror("Cannot set new settings");
 		}
@@ -2696,8 +2657,7 @@ static int do_sset(int fd, struct ifreq *ifr)
 		struct ethtool_wolinfo wol;
 
 		wol.cmd = ETHTOOL_GWOL;
-		ifr->ifr_data = (caddr_t)&wol;
-		err = send_ioctl(fd, ifr);
+		err = send_ioctl(ctx, &wol);
 		if (err < 0) {
 			perror("Cannot get current wake-on-lan settings");
 		} else {
@@ -2714,8 +2674,7 @@ static int do_sset(int fd, struct ifreq *ifr)
 
 			/* Try to perform the update. */
 			wol.cmd = ETHTOOL_SWOL;
-			ifr->ifr_data = (caddr_t)&wol;
-			err = send_ioctl(fd, ifr);
+			err = send_ioctl(ctx, &wol);
 			if (err < 0)
 				perror("Cannot set new wake-on-lan settings");
 		}
@@ -2731,16 +2690,14 @@ static int do_sset(int fd, struct ifreq *ifr)
 		struct ethtool_value edata;
 
 		edata.cmd = ETHTOOL_GMSGLVL;
-		ifr->ifr_data = (caddr_t)&edata;
-		err = send_ioctl(fd, ifr);
+		err = send_ioctl(ctx, &edata);
 		if (err < 0) {
 			perror("Cannot get msglvl");
 		} else {
 			edata.cmd = ETHTOOL_SMSGLVL;
 			edata.data = ((edata.data & ~msglvl_mask) |
 				      msglvl_wanted);
-			ifr->ifr_data = (caddr_t)&edata;
-			err = send_ioctl(fd, ifr);
+			err = send_ioctl(ctx, &edata);
 			if (err < 0)
 				perror("Cannot set new msglvl");
 		}
@@ -2749,15 +2706,14 @@ static int do_sset(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_gregs(int fd, struct ifreq *ifr)
+static int do_gregs(struct cmd_context *ctx)
 {
 	int err;
 	struct ethtool_drvinfo drvinfo;
 	struct ethtool_regs *regs;
 
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
-	ifr->ifr_data = (caddr_t)&drvinfo;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
 		perror("Cannot get driver information");
 		return 72;
@@ -2770,8 +2726,7 @@ static int do_gregs(int fd, struct ifreq *ifr)
 	}
 	regs->cmd = ETHTOOL_GREGS;
 	regs->len = drvinfo.regdump_len;
-	ifr->ifr_data = (caddr_t)regs;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, regs);
 	if (err < 0) {
 		perror("Cannot get register dump");
 		free(regs);
@@ -2787,29 +2742,27 @@ static int do_gregs(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_nway_rst(int fd, struct ifreq *ifr)
+static int do_nway_rst(struct cmd_context *ctx)
 {
 	struct ethtool_value edata;
 	int err;
 
 	edata.cmd = ETHTOOL_NWAY_RST;
-	ifr->ifr_data = (caddr_t)&edata;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &edata);
 	if (err < 0)
 		perror("Cannot restart autonegotiation");
 
 	return err;
 }
 
-static int do_geeprom(int fd, struct ifreq *ifr)
+static int do_geeprom(struct cmd_context *ctx)
 {
 	int err;
 	struct ethtool_drvinfo drvinfo;
 	struct ethtool_eeprom *eeprom;
 
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
-	ifr->ifr_data = (caddr_t)&drvinfo;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
 		perror("Cannot get driver information");
 		return 74;
@@ -2829,8 +2782,7 @@ static int do_geeprom(int fd, struct ifreq *ifr)
 	eeprom->cmd = ETHTOOL_GEEPROM;
 	eeprom->len = geeprom_length;
 	eeprom->offset = geeprom_offset;
-	ifr->ifr_data = (caddr_t)eeprom;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, eeprom);
 	if (err < 0) {
 		perror("Cannot get EEPROM data");
 		free(eeprom);
@@ -2842,15 +2794,14 @@ static int do_geeprom(int fd, struct ifreq *ifr)
 	return err;
 }
 
-static int do_seeprom(int fd, struct ifreq *ifr)
+static int do_seeprom(struct cmd_context *ctx)
 {
 	int err;
 	struct ethtool_drvinfo drvinfo;
 	struct ethtool_eeprom *eeprom;
 
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
-	ifr->ifr_data = (caddr_t)&drvinfo;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
 		perror("Cannot get driver information");
 		return 74;
@@ -2881,8 +2832,7 @@ static int do_seeprom(int fd, struct ifreq *ifr)
 	if (!seeprom_value_seen)
 		eeprom->len = fread(eeprom->data, 1, eeprom->len, stdin);
 
-	ifr->ifr_data = (caddr_t)eeprom;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, eeprom);
 	if (err < 0) {
 		perror("Cannot set EEPROM data");
 		err = 87;
@@ -2892,7 +2842,7 @@ static int do_seeprom(int fd, struct ifreq *ifr)
 	return err;
 }
 
-static int do_test(int fd, struct ifreq *ifr)
+static int do_test(struct cmd_context *ctx)
 {
 	int err;
 	struct ethtool_drvinfo drvinfo;
@@ -2900,8 +2850,7 @@ static int do_test(int fd, struct ifreq *ifr)
 	struct ethtool_gstrings *strings;
 
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
-	ifr->ifr_data = (caddr_t)&drvinfo;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
 		perror("Cannot get driver information");
 		return 72;
@@ -2921,8 +2870,7 @@ static int do_test(int fd, struct ifreq *ifr)
 		test->flags = ETH_TEST_FL_OFFLINE;
 	else
 		test->flags = 0;
-	ifr->ifr_data = (caddr_t)test;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, test);
 	if (err < 0) {
 		perror("Cannot test");
 		free (test);
@@ -2940,8 +2888,7 @@ static int do_test(int fd, struct ifreq *ifr)
 	strings->cmd = ETHTOOL_GSTRINGS;
 	strings->string_set = ETH_SS_TEST;
 	strings->len = drvinfo.testinfo_len;
-	ifr->ifr_data = (caddr_t)strings;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, strings);
 	if (err < 0) {
 		perror("Cannot get strings");
 		free (test);
@@ -2955,22 +2902,21 @@ static int do_test(int fd, struct ifreq *ifr)
 	return err;
 }
 
-static int do_phys_id(int fd, struct ifreq *ifr)
+static int do_phys_id(struct cmd_context *ctx)
 {
 	int err;
 	struct ethtool_value edata;
 
 	edata.cmd = ETHTOOL_PHYS_ID;
 	edata.data = phys_id_time;
-	ifr->ifr_data = (caddr_t)&edata;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &edata);
 	if (err < 0)
 		perror("Cannot identify NIC");
 
 	return err;
 }
 
-static int do_gstats(int fd, struct ifreq *ifr)
+static int do_gstats(struct cmd_context *ctx)
 {
 	struct ethtool_drvinfo drvinfo;
 	struct ethtool_gstrings *strings;
@@ -2979,8 +2925,7 @@ static int do_gstats(int fd, struct ifreq *ifr)
 	int err;
 
 	drvinfo.cmd = ETHTOOL_GDRVINFO;
-	ifr->ifr_data = (caddr_t)&drvinfo;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &drvinfo);
 	if (err < 0) {
 		perror("Cannot get driver information");
 		return 71;
@@ -3005,8 +2950,7 @@ static int do_gstats(int fd, struct ifreq *ifr)
 	strings->cmd = ETHTOOL_GSTRINGS;
 	strings->string_set = ETH_SS_STATS;
 	strings->len = n_stats;
-	ifr->ifr_data = (caddr_t) strings;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, strings);
 	if (err < 0) {
 		perror("Cannot get stats strings information");
 		free(strings);
@@ -3016,8 +2960,7 @@ static int do_gstats(int fd, struct ifreq *ifr)
 
 	stats->cmd = ETHTOOL_GSTATS;
 	stats->n_stats = n_stats;
-	ifr->ifr_data = (caddr_t) stats;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, stats);
 	if (err < 0) {
 		perror("Cannot get stats information");
 		free(strings);
@@ -3040,7 +2983,7 @@ static int do_gstats(int fd, struct ifreq *ifr)
 }
 

-static int do_srxclass(int fd, struct ifreq *ifr)
+static int do_srxclass(struct cmd_context *ctx)
 {
 	int err;
 
@@ -3051,8 +2994,7 @@ static int do_srxclass(int fd, struct ifreq *ifr)
 		nfccmd.flow_type = rx_fhash_set;
 		nfccmd.data = rx_fhash_val;
 
-		ifr->ifr_data = (caddr_t)&nfccmd;
-		err = ioctl(fd, SIOCETHTOOL, ifr);
+		err = send_ioctl(ctx, &nfccmd);
 		if (err < 0)
 			perror("Cannot change RX network flow hashing options");
 
@@ -3061,7 +3003,7 @@ static int do_srxclass(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_grxclass(int fd, struct ifreq *ifr)
+static int do_grxclass(struct cmd_context *ctx)
 {
 	int err;
 
@@ -3070,8 +3012,7 @@ static int do_grxclass(int fd, struct ifreq *ifr)
 
 		nfccmd.cmd = ETHTOOL_GRXFH;
 		nfccmd.flow_type = rx_fhash_get;
-		ifr->ifr_data = (caddr_t)&nfccmd;
-		err = ioctl(fd, SIOCETHTOOL, ifr);
+		err = send_ioctl(ctx, &nfccmd);
 		if (err < 0)
 			perror("Cannot get RX network flow hashing options");
 		else
@@ -3081,7 +3022,7 @@ static int do_grxclass(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_grxfhindir(int fd, struct ifreq *ifr)
+static int do_grxfhindir(struct cmd_context *ctx)
 {
 	struct ethtool_rxnfc ring_count;
 	struct ethtool_rxfh_indir indir_head;
@@ -3090,8 +3031,7 @@ static int do_grxfhindir(int fd, struct ifreq *ifr)
 	int err;
 
 	ring_count.cmd = ETHTOOL_GRXRINGS;
-	ifr->ifr_data = (caddr_t) &ring_count;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &ring_count);
 	if (err < 0) {
 		perror("Cannot get RX ring count");
 		return 102;
@@ -3099,8 +3039,7 @@ static int do_grxfhindir(int fd, struct ifreq *ifr)
 
 	indir_head.cmd = ETHTOOL_GRXFHINDIR;
 	indir_head.size = 0;
-	ifr->ifr_data = (caddr_t) &indir_head;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &indir_head);
 	if (err < 0) {
 		perror("Cannot get RX flow hash indirection table size");
 		return 103;
@@ -3110,8 +3049,7 @@ static int do_grxfhindir(int fd, struct ifreq *ifr)
 		       indir_head.size * sizeof(*indir->ring_index));
 	indir->cmd = ETHTOOL_GRXFHINDIR;
 	indir->size = indir_head.size;
-	ifr->ifr_data = (caddr_t) indir;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, indir);
 	if (err < 0) {
 		perror("Cannot get RX flow hash indirection table");
 		return 103;
@@ -3129,7 +3067,7 @@ static int do_grxfhindir(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_srxfhindir(int fd, struct ifreq *ifr)
+static int do_srxfhindir(struct cmd_context *ctx)
 {
 	struct ethtool_rxfh_indir indir_head;
 	struct ethtool_rxfh_indir *indir;
@@ -3141,8 +3079,7 @@ static int do_srxfhindir(int fd, struct ifreq *ifr)
 
 	indir_head.cmd = ETHTOOL_GRXFHINDIR;
 	indir_head.size = 0;
-	ifr->ifr_data = (caddr_t) &indir_head;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &indir_head);
 	if (err < 0) {
 		perror("Cannot get RX flow hash indirection table size");
 		return 104;
@@ -3188,8 +3125,7 @@ static int do_srxfhindir(int fd, struct ifreq *ifr)
 		}
 	}
 
-	ifr->ifr_data = (caddr_t) indir;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, indir);
 	if (err < 0) {
 		perror("Cannot set RX flow hash indirection table");
 		return 105;
@@ -3198,7 +3134,7 @@ static int do_srxfhindir(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_flash(int fd, struct ifreq *ifr)
+static int do_flash(struct cmd_context *ctx)
 {
 	struct ethtool_flash efl;
 	int err;
@@ -3222,15 +3158,14 @@ static int do_flash(int fd, struct ifreq *ifr)
 	else
 		efl.region = flash_region;
 
-	ifr->ifr_data = (caddr_t)&efl;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &efl);
 	if (err < 0)
 		perror("Flashing failed");
 
 	return err;
 }
 
-static int do_permaddr(int fd, struct ifreq *ifr)
+static int do_permaddr(struct cmd_context *ctx)
 {
 	int i, err;
 	struct ethtool_perm_addr *epaddr;
@@ -3238,9 +3173,8 @@ static int do_permaddr(int fd, struct ifreq *ifr)
 	epaddr = malloc(sizeof(struct ethtool_perm_addr) + MAX_ADDR_LEN);
 	epaddr->cmd = ETHTOOL_GPERMADDR;
 	epaddr->size = MAX_ADDR_LEN;
-	ifr->ifr_data = (caddr_t)epaddr;
 
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, epaddr);
 	if (err < 0)
 		perror("Cannot read permanent address");
 	else {
@@ -3332,7 +3266,7 @@ static int flow_spec_to_ntuple(struct ethtool_rx_flow_spec *fsp,
 	return 0;
 }
 
-static int do_srxntuple(int fd, struct ifreq *ifr)
+static int do_srxntuple(struct cmd_context *ctx)
 {
 	struct ethtool_rx_ntuple ntuplecmd;
 	struct ethtool_value eval;
@@ -3349,15 +3283,13 @@ static int do_srxntuple(int fd, struct ifreq *ifr)
 	 * flag not being set on the device
 	 */
 	eval.cmd = ETHTOOL_GFLAGS;
-	ifr->ifr_data = (caddr_t)&eval;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, &eval);
 	if (err || !(eval.data & ETH_FLAG_NTUPLE))
 		return -1;
 
 	/* send rule via N-tuple */
 	ntuplecmd.cmd = ETHTOOL_SRXNTUPLE;
-	ifr->ifr_data = (caddr_t)&ntuplecmd;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, &ntuplecmd);
 
 	/*
 	 * Display error only if reponse is something other than op not
@@ -3373,25 +3305,25 @@ static int do_srxntuple(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_srxclsrule(int fd, struct ifreq *ifr)
+static int do_srxclsrule(struct cmd_context *ctx)
 {
 	int err;
 
 	if (rx_class_rule_added) {
 		/* attempt to add rule via N-tuple specifier */
-		err = do_srxntuple(fd, ifr);
+		err = do_srxntuple(ctx);
 		if (!err)
 			return 0;
 
 		/* attempt to add rule via network flow classifier */
-		err = rxclass_rule_ins(fd, ifr, &rx_rule_fs);
+		err = rxclass_rule_ins(ctx, &rx_rule_fs);
 		if (err < 0) {
 			fprintf(stderr, "Cannot insert"
 				" classification rule\n");
 			return 1;
 		}
 	} else if (rx_class_rule_del >= 0) {
-		err = rxclass_rule_del(fd, ifr, rx_class_rule_del);
+		err = rxclass_rule_del(ctx, rx_class_rule_del);
 
 		if (err < 0) {
 			fprintf(stderr, "Cannot delete"
@@ -3405,28 +3337,27 @@ static int do_srxclsrule(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int do_grxclsrule(int fd, struct ifreq *ifr)
+static int do_grxclsrule(struct cmd_context *ctx)
 {
 	struct ethtool_rxnfc nfccmd;
 	int err;
 
 	if (rx_class_rule_get >= 0) {
-		err = rxclass_rule_get(fd, ifr, rx_class_rule_get);
+		err = rxclass_rule_get(ctx, rx_class_rule_get);
 		if (err < 0)
 			fprintf(stderr, "Cannot get RX classification rule\n");
 		return err ? 1 : 0;
 	}
 
 	nfccmd.cmd = ETHTOOL_GRXRINGS;
-	ifr->ifr_data = (caddr_t)&nfccmd;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, &nfccmd);
 	if (err < 0)
 		perror("Cannot get RX rings");
 	else
 		fprintf(stdout, "%d RX rings available\n",
 			(int)nfccmd.data);
 
-	err = rxclass_rule_getall(fd, ifr);
+	err = rxclass_rule_getall(ctx);
 	if (err < 0)
 		fprintf(stderr, "RX classification rule retrieval failed\n");
 
@@ -3459,7 +3390,7 @@ static int do_writefwdump(struct ethtool_dump *dump)
 	return err;
 }
 
-static int do_getfwdump(int fd, struct ifreq *ifr)
+static int do_getfwdump(struct cmd_context *ctx)
 {
 	int err;
 	struct ethtool_dump edata;
@@ -3467,8 +3398,7 @@ static int do_getfwdump(int fd, struct ifreq *ifr)
 
 	edata.cmd = ETHTOOL_GET_DUMP_FLAG;
 
-	ifr->ifr_data = (caddr_t) &edata;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &edata);
 	if (err < 0) {
 		perror("Can not get dump level\n");
 		return 1;
@@ -3485,8 +3415,7 @@ static int do_getfwdump(int fd, struct ifreq *ifr)
 	}
 	data->cmd = ETHTOOL_GET_DUMP_DATA;
 	data->len = edata.len;
-	ifr->ifr_data = (caddr_t) data;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, data);
 	if (err < 0) {
 		perror("Can not get dump data\n");
 		err = 1;
@@ -3498,15 +3427,14 @@ free:
 	return err;
 }
 
-static int do_setfwdump(int fd, struct ifreq *ifr)
+static int do_setfwdump(struct cmd_context *ctx)
 {
 	int err;
 	struct ethtool_dump dump;
 
 	dump.cmd = ETHTOOL_SET_DUMP;
 	dump.flag = dump_flag;
-	ifr->ifr_data = (caddr_t)&dump;
-	err = send_ioctl(fd, ifr);
+	err = send_ioctl(ctx, &dump);
 	if (err < 0) {
 		perror("Can not set dump level\n");
 		return 1;
@@ -3514,9 +3442,10 @@ static int do_setfwdump(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-static int send_ioctl(int fd, struct ifreq *ifr)
+int send_ioctl(struct cmd_context *ctx, void *cmd)
 {
-	return ioctl(fd, SIOCETHTOOL, ifr);
+	ctx->ifr.ifr_data = cmd;
+	return ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr);
 }
 
 int main(int argc, char **argp, char **envp)
diff --git a/internal.h b/internal.h
index 2b6a54a..c2f504a 100644
--- a/internal.h
+++ b/internal.h
@@ -88,6 +88,14 @@ static inline int test_bit(unsigned int nr, const unsigned long *addr)
 
 #define	RX_CLS_LOC_UNSPEC	0xffffffffUL
 
+/* Context for sub-commands */
+struct cmd_context {
+	int fd;			/* socket suitable for ethtool ioctl */
+	struct ifreq ifr;	/* ifreq suitable for ethtool ioctl */
+};
+
+int send_ioctl(struct cmd_context *ctx, void *cmd);
+
 /* National Semiconductor DP83815, DP83816 */
 int natsemi_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
 int natsemi_dump_eeprom(struct ethtool_drvinfo *info,
@@ -155,10 +163,10 @@ int st_gmac_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
 /* Rx flow classification */
 int rxclass_parse_ruleopts(char **optstr, int opt_cnt,
 			   struct ethtool_rx_flow_spec *fsp);
-int rxclass_rule_getall(int fd, struct ifreq *ifr);
-int rxclass_rule_get(int fd, struct ifreq *ifr, __u32 loc);
-int rxclass_rule_ins(int fd, struct ifreq *ifr,
+int rxclass_rule_getall(struct cmd_context *ctx);
+int rxclass_rule_get(struct cmd_context *ctx, __u32 loc);
+int rxclass_rule_ins(struct cmd_context *ctx,
 		     struct ethtool_rx_flow_spec *fsp);
-int rxclass_rule_del(int fd, struct ifreq *ifr, __u32 loc);
+int rxclass_rule_del(struct cmd_context *ctx, __u32 loc);
 
 #endif /* ETHTOOL_INTERNAL_H__ */
diff --git a/rxclass.c b/rxclass.c
index 809b073..dec9dd5 100644
--- a/rxclass.c
+++ b/rxclass.c
@@ -203,7 +203,7 @@ static void rxclass_print_rule(struct ethtool_rx_flow_spec *fsp)
 	}
 }
 
-static int rxclass_get_count(int fd, struct ifreq *ifr, __u32 *count)
+static int rxclass_get_count(struct cmd_context *ctx, __u32 *count)
 {
 	struct ethtool_rxnfc nfccmd;
 	int err;
@@ -211,8 +211,7 @@ static int rxclass_get_count(int fd, struct ifreq *ifr, __u32 *count)
 	/* request count and store */
 	nfccmd.cmd = ETHTOOL_GRXCLSRLCNT;
 	nfccmd.rule_cnt = 0;
-	ifr->ifr_data = (caddr_t)&nfccmd;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, &nfccmd);
 	*count = nfccmd.rule_cnt;
 	if (err < 0)
 		perror("rxclass: Cannot get RX class rule count");
@@ -220,7 +219,7 @@ static int rxclass_get_count(int fd, struct ifreq *ifr, __u32 *count)
 	return err;
 }
 
-int rxclass_rule_get(int fd, struct ifreq *ifr, __u32 loc)
+int rxclass_rule_get(struct cmd_context *ctx, __u32 loc)
 {
 	struct ethtool_rxnfc nfccmd;
 	int err;
@@ -229,8 +228,7 @@ int rxclass_rule_get(int fd, struct ifreq *ifr, __u32 loc)
 	nfccmd.cmd = ETHTOOL_GRXCLSRULE;
 	memset(&nfccmd.fs, 0, sizeof(struct ethtool_rx_flow_spec));
 	nfccmd.fs.location = loc;
-	ifr->ifr_data = (caddr_t)&nfccmd;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, &nfccmd);
 	if (err < 0) {
 		perror("rxclass: Cannot get RX class rule");
 		return err;
@@ -241,7 +239,7 @@ int rxclass_rule_get(int fd, struct ifreq *ifr, __u32 loc)
 	return err;
 }
 
-int rxclass_rule_getall(int fd, struct ifreq *ifr)
+int rxclass_rule_getall(struct cmd_context *ctx)
 {
 	struct ethtool_rxnfc *nfccmd;
 	__u32 *rule_locs;
@@ -249,7 +247,7 @@ int rxclass_rule_getall(int fd, struct ifreq *ifr)
 	__u32 count;
 
 	/* determine rule count */
-	err = rxclass_get_count(fd, ifr, &count);
+	err = rxclass_get_count(ctx, &count);
 	if (err < 0)
 		return err;
 
@@ -266,8 +264,7 @@ int rxclass_rule_getall(int fd, struct ifreq *ifr)
 	/* request location list */
 	nfccmd->cmd = ETHTOOL_GRXCLSRLALL;
 	nfccmd->rule_cnt = count;
-	ifr->ifr_data = (caddr_t)nfccmd;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, nfccmd);
 	if (err < 0) {
 		perror("rxclass: Cannot get RX class rules");
 		free(nfccmd);
@@ -277,7 +274,7 @@ int rxclass_rule_getall(int fd, struct ifreq *ifr)
 	/* write locations to bitmap */
 	rule_locs = nfccmd->rule_locs;
 	for (i = 0; i < count; i++) {
-		err = rxclass_rule_get(fd, ifr, rule_locs[i]);
+		err = rxclass_rule_get(ctx, rule_locs[i]);
 		if (err < 0)
 			break;
 	}
@@ -372,7 +369,7 @@ static int rmgr_find_empty_slot(struct ethtool_rx_flow_spec *fsp)
 	return -1;
 }
 
-static int rmgr_init(int fd, struct ifreq *ifr)
+static int rmgr_init(struct cmd_context *ctx)
 {
 	struct ethtool_rxnfc *nfccmd;
 	int err, i;
@@ -385,7 +382,7 @@ static int rmgr_init(int fd, struct ifreq *ifr)
 	memset(&rmgr, 0, sizeof(struct rmgr_ctrl));
 
 	/* request count and store in rmgr.n_rules */
-	err = rxclass_get_count(fd, ifr, &rmgr.n_rules);
+	err = rxclass_get_count(ctx, &rmgr.n_rules);
 	if (err < 0)
 		return err;
 
@@ -400,8 +397,7 @@ static int rmgr_init(int fd, struct ifreq *ifr)
 	/* request location list */
 	nfccmd->cmd = ETHTOOL_GRXCLSRLALL;
 	nfccmd->rule_cnt = rmgr.n_rules;
-	ifr->ifr_data = (caddr_t)nfccmd;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, nfccmd);
 	if (err < 0) {
 		perror("rmgr: Cannot get RX class rules");
 		free(nfccmd);
@@ -449,13 +445,13 @@ static void rmgr_cleanup(void)
 	rmgr.size = 0;
 }
 
-static int rmgr_set_location(int fd, struct ifreq *ifr,
+static int rmgr_set_location(struct cmd_context *ctx,
 			     struct ethtool_rx_flow_spec *fsp)
 {
 	int err;
 
 	/* init table of available rules */
-	err = rmgr_init(fd, ifr);
+	err = rmgr_init(ctx);
 	if (err < 0)
 		return err;
 
@@ -468,7 +464,7 @@ static int rmgr_set_location(int fd, struct ifreq *ifr,
 	return err;
 }
 
-int rxclass_rule_ins(int fd, struct ifreq *ifr,
+int rxclass_rule_ins(struct cmd_context *ctx,
 		     struct ethtool_rx_flow_spec *fsp)
 {
 	struct ethtool_rxnfc nfccmd;
@@ -480,7 +476,7 @@ int rxclass_rule_ins(int fd, struct ifreq *ifr,
 	 * and allocate a free rule for our use
 	 */
 	if (loc == RX_CLS_LOC_UNSPEC) {
-		err = rmgr_set_location(fd, ifr, fsp);
+		err = rmgr_set_location(ctx, fsp);
 		if (err < 0)
 			return err;
 	}
@@ -488,8 +484,7 @@ int rxclass_rule_ins(int fd, struct ifreq *ifr,
 	/* notify netdev of new rule */
 	nfccmd.cmd = ETHTOOL_SRXCLSRLINS;
 	nfccmd.fs = *fsp;
-	ifr->ifr_data = (caddr_t)&nfccmd;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, &nfccmd);
 	if (err < 0)
 		perror("rmgr: Cannot insert RX class rule");
 	else if (loc == RX_CLS_LOC_UNSPEC)
@@ -498,7 +493,7 @@ int rxclass_rule_ins(int fd, struct ifreq *ifr,
 	return 0;
 }
 
-int rxclass_rule_del(int fd, struct ifreq *ifr, __u32 loc)
+int rxclass_rule_del(struct cmd_context *ctx, __u32 loc)
 {
 	struct ethtool_rxnfc nfccmd;
 	int err;
@@ -506,8 +501,7 @@ int rxclass_rule_del(int fd, struct ifreq *ifr, __u32 loc)
 	/* notify netdev of rule removal */
 	nfccmd.cmd = ETHTOOL_SRXCLSRLDEL;
 	nfccmd.fs.location = loc;
-	ifr->ifr_data = (caddr_t)&nfccmd;
-	err = ioctl(fd, SIOCETHTOOL, ifr);
+	err = send_ioctl(ctx, &nfccmd);
 	if (err < 0)
 		perror("rmgr: Cannot delete RX class rule");
 
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 08/21] Use standard indentation for definition of struct option args
From: Ben Hutchings @ 2011-11-01 23:15 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

Also put the whole of the help string on a line of its own where
necessary.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.c |  280 +++++++++++++++++++++++++++++++------------------------------
 1 files changed, 142 insertions(+), 138 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index aab1e41..f967f84 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -140,145 +140,149 @@ static enum {
 } mode = MODE_GSET;
 
 static struct option {
-    char *srt, *lng;
-    int Mode;
-    char *help;
-    char *opthelp;
+	char *srt, *lng;
+	int Mode;
+	char *help;
+	char *opthelp;
 } args[] = {
-    { "-s", "--change", MODE_SSET, "Change generic options",
-		"		[ speed %d ]\n"
-		"		[ duplex half|full ]\n"
-		"		[ port tp|aui|bnc|mii|fibre ]\n"
-		"		[ autoneg on|off ]\n"
-		"		[ advertise %x ]\n"
-		"		[ phyad %d ]\n"
-		"		[ xcvr internal|external ]\n"
-		"		[ wol p|u|m|b|a|g|s|d... ]\n"
-		"		[ sopass %x:%x:%x:%x:%x:%x ]\n"
-		"		[ msglvl %d | msglvl type on|off ... ]\n" },
-    { "-a", "--show-pause", MODE_GPAUSE, "Show pause options" },
-    { "-A", "--pause", MODE_SPAUSE, "Set pause options",
-      "		[ autoneg on|off ]\n"
-      "		[ rx on|off ]\n"
-      "		[ tx on|off ]\n" },
-    { "-c", "--show-coalesce", MODE_GCOALESCE, "Show coalesce options" },
-    { "-C", "--coalesce", MODE_SCOALESCE, "Set coalesce options",
-		"		[adaptive-rx on|off]\n"
-		"		[adaptive-tx on|off]\n"
-		"		[rx-usecs N]\n"
-		"		[rx-frames N]\n"
-		"		[rx-usecs-irq N]\n"
-		"		[rx-frames-irq N]\n"
-		"		[tx-usecs N]\n"
-		"		[tx-frames N]\n"
-		"		[tx-usecs-irq N]\n"
-		"		[tx-frames-irq N]\n"
-		"		[stats-block-usecs N]\n"
-		"		[pkt-rate-low N]\n"
-		"		[rx-usecs-low N]\n"
-		"		[rx-frames-low N]\n"
-		"		[tx-usecs-low N]\n"
-		"		[tx-frames-low N]\n"
-		"		[pkt-rate-high N]\n"
-		"		[rx-usecs-high N]\n"
-		"		[rx-frames-high N]\n"
-		"		[tx-usecs-high N]\n"
-		"		[tx-frames-high N]\n"
-	        "		[sample-interval N]\n" },
-    { "-g", "--show-ring", MODE_GRING, "Query RX/TX ring parameters" },
-    { "-G", "--set-ring", MODE_SRING, "Set RX/TX ring parameters",
-		"		[ rx N ]\n"
-		"		[ rx-mini N ]\n"
-		"		[ rx-jumbo N ]\n"
-	        "		[ tx N ]\n" },
-    { "-k", "--show-offload", MODE_GOFFLOAD, "Get protocol offload information" },
-    { "-K", "--offload", MODE_SOFFLOAD, "Set protocol offload",
-		"		[ rx on|off ]\n"
-		"		[ tx on|off ]\n"
-		"		[ sg on|off ]\n"
-	        "		[ tso on|off ]\n"
-	        "		[ ufo on|off ]\n"
-		"		[ gso on|off ]\n"
-		"		[ gro on|off ]\n"
-		"		[ lro on|off ]\n"
-		"		[ rxvlan on|off ]\n"
-		"		[ txvlan on|off ]\n"
-		"		[ ntuple on|off ]\n"
-		"		[ rxhash on|off ]\n"
-    },
-    { "-i", "--driver", MODE_GDRV, "Show driver information" },
-    { "-d", "--register-dump", MODE_GREGS, "Do a register dump",
-		"		[ raw on|off ]\n"
-		"		[ file FILENAME ]\n" },
-    { "-e", "--eeprom-dump", MODE_GEEPROM, "Do a EEPROM dump",
-		"		[ raw on|off ]\n"
-		"		[ offset N ]\n"
-		"		[ length N ]\n" },
-    { "-E", "--change-eeprom", MODE_SEEPROM, "Change bytes in device EEPROM",
-		"		[ magic N ]\n"
-		"		[ offset N ]\n"
-		"		[ length N ]\n"
-		"		[ value N ]\n" },
-    { "-r", "--negotiate", MODE_NWAY_RST, "Restart N-WAY negotiation" },
-    { "-p", "--identify", MODE_PHYS_ID, "Show visible port identification (e.g. blinking)",
-                "               [ TIME-IN-SECONDS ]\n" },
-    { "-t", "--test", MODE_TEST, "Execute adapter self test",
-		"               [ online | offline | external_lb ]\n" },
-    { "-S", "--statistics", MODE_GSTATS, "Show adapter statistics" },
-    { "-n", "--show-nfc", MODE_GNFC, "Show Rx network flow classification "
-		"options",
-		"		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
-		"tcp6|udp6|ah6|esp6|sctp6 ]\n" },
-    { "-f", "--flash", MODE_FLASHDEV, "Flash firmware image "
-    		"from the specified file to a region on the device",
-		"               FILENAME [ REGION-NUMBER-TO-FLASH ]\n" },
-    { "-N", "--config-nfc", MODE_SNFC, "Configure Rx network flow "
-		"classification options",
-		"		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
-		"tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... ]\n" },
-    { "-x", "--show-rxfh-indir", MODE_GRXFHINDIR, "Show Rx flow hash "
-		"indirection" },
-    { "-X", "--set-rxfh-indir", MODE_SRXFHINDIR, "Set Rx flow hash indirection",
-		"		equal N | weight W0 W1 ...\n" },
-    { "-U", "--config-ntuple", MODE_SCLSRULE, "Configure Rx ntuple filters "
-		"and actions",
-		"		[ delete %d ] |\n"
-		"		[ flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4\n"
-		"			[ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
-		"			[ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
-		"			[ proto %d [m %x] ]\n"
-		"			[ src-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
-		"			[ dst-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
-		"			[ tos %d [m %x] ]\n"
-		"			[ l4proto %d [m %x] ]\n"
-		"			[ src-port %d [m %x] ]\n"
-		"			[ dst-port %d [m %x] ]\n"
-		"			[ spi %d [m %x] ]\n"
-		"			[ vlan-etype %x [m %x] ]\n"
-		"			[ vlan %x [m %x] ]\n"
-		"			[ user-def %x [m %x] ]\n"
-		"			[ action %d ]\n"
-		"			[ loc %d]]\n" },
-    { "-u", "--show-ntuple", MODE_GCLSRULE,
-		"Get Rx ntuple filters and actions",
-		"		[ rule %d ]\n"},
-    { "-P", "--show-permaddr", MODE_PERMADDR,
-		"Show permanent hardware address" },
-    { "-w", "--get-dump", MODE_GET_DUMP,
-		"Get dump flag, data",
-		"		[ data FILENAME ]\n" },
-    { "-W", "--set-dump", MODE_SET_DUMP,
-		"Set dump flag of the device",
-		"		N\n"},
-    { "-l", "--show-channels", MODE_GCHANNELS, "Query Channels" },
-    { "-L", "--set-channels", MODE_SCHANNELS, "Set Channels",
-		"               [ rx N ]\n"
-		"               [ tx N ]\n"
-		"               [ other N ]\n"
-		"               [ combined N ]\n" },
-    { "-h", "--help", MODE_HELP, "Show this help" },
-    { NULL, "--version", MODE_VERSION, "Show version number" },
-    {}
+	{ "-s", "--change", MODE_SSET, "Change generic options",
+	  "		[ speed %d ]\n"
+	  "		[ duplex half|full ]\n"
+	  "		[ port tp|aui|bnc|mii|fibre ]\n"
+	  "		[ autoneg on|off ]\n"
+	  "		[ advertise %x ]\n"
+	  "		[ phyad %d ]\n"
+	  "		[ xcvr internal|external ]\n"
+	  "		[ wol p|u|m|b|a|g|s|d... ]\n"
+	  "		[ sopass %x:%x:%x:%x:%x:%x ]\n"
+	  "		[ msglvl %d | msglvl type on|off ... ]\n" },
+	{ "-a", "--show-pause", MODE_GPAUSE, "Show pause options" },
+	{ "-A", "--pause", MODE_SPAUSE, "Set pause options",
+	  "		[ autoneg on|off ]\n"
+	  "		[ rx on|off ]\n"
+	  "		[ tx on|off ]\n" },
+	{ "-c", "--show-coalesce", MODE_GCOALESCE, "Show coalesce options" },
+	{ "-C", "--coalesce", MODE_SCOALESCE, "Set coalesce options",
+	  "		[adaptive-rx on|off]\n"
+	  "		[adaptive-tx on|off]\n"
+	  "		[rx-usecs N]\n"
+	  "		[rx-frames N]\n"
+	  "		[rx-usecs-irq N]\n"
+	  "		[rx-frames-irq N]\n"
+	  "		[tx-usecs N]\n"
+	  "		[tx-frames N]\n"
+	  "		[tx-usecs-irq N]\n"
+	  "		[tx-frames-irq N]\n"
+	  "		[stats-block-usecs N]\n"
+	  "		[pkt-rate-low N]\n"
+	  "		[rx-usecs-low N]\n"
+	  "		[rx-frames-low N]\n"
+	  "		[tx-usecs-low N]\n"
+	  "		[tx-frames-low N]\n"
+	  "		[pkt-rate-high N]\n"
+	  "		[rx-usecs-high N]\n"
+	  "		[rx-frames-high N]\n"
+	  "		[tx-usecs-high N]\n"
+	  "		[tx-frames-high N]\n"
+	  "		[sample-interval N]\n" },
+	{ "-g", "--show-ring", MODE_GRING, "Query RX/TX ring parameters" },
+	{ "-G", "--set-ring", MODE_SRING, "Set RX/TX ring parameters",
+	  "		[ rx N ]\n"
+	  "		[ rx-mini N ]\n"
+	  "		[ rx-jumbo N ]\n"
+	  "		[ tx N ]\n" },
+	{ "-k", "--show-offload", MODE_GOFFLOAD,
+	  "Get protocol offload information" },
+	{ "-K", "--offload", MODE_SOFFLOAD, "Set protocol offload",
+	  "		[ rx on|off ]\n"
+	  "		[ tx on|off ]\n"
+	  "		[ sg on|off ]\n"
+	  "		[ tso on|off ]\n"
+	  "		[ ufo on|off ]\n"
+	  "		[ gso on|off ]\n"
+	  "		[ gro on|off ]\n"
+	  "		[ lro on|off ]\n"
+	  "		[ rxvlan on|off ]\n"
+	  "		[ txvlan on|off ]\n"
+	  "		[ ntuple on|off ]\n"
+	  "		[ rxhash on|off ]\n"
+	},
+	{ "-i", "--driver", MODE_GDRV, "Show driver information" },
+	{ "-d", "--register-dump", MODE_GREGS, "Do a register dump",
+	  "		[ raw on|off ]\n"
+	  "		[ file FILENAME ]\n" },
+	{ "-e", "--eeprom-dump", MODE_GEEPROM, "Do a EEPROM dump",
+	  "		[ raw on|off ]\n"
+	  "		[ offset N ]\n"
+	  "		[ length N ]\n" },
+	{ "-E", "--change-eeprom", MODE_SEEPROM,
+	  "Change bytes in device EEPROM",
+	  "		[ magic N ]\n"
+	  "		[ offset N ]\n"
+	  "		[ length N ]\n"
+	  "		[ value N ]\n" },
+	{ "-r", "--negotiate", MODE_NWAY_RST, "Restart N-WAY negotiation" },
+	{ "-p", "--identify", MODE_PHYS_ID,
+	  "Show visible port identification (e.g. blinking)",
+	  "               [ TIME-IN-SECONDS ]\n" },
+	{ "-t", "--test", MODE_TEST, "Execute adapter self test",
+	  "               [ online | offline | external_lb ]\n" },
+	{ "-S", "--statistics", MODE_GSTATS, "Show adapter statistics" },
+	{ "-n", "--show-nfc", MODE_GNFC,
+	  "Show Rx network flow classification options",
+	  "		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
+	  "tcp6|udp6|ah6|esp6|sctp6 ]\n" },
+	{ "-f", "--flash", MODE_FLASHDEV,
+	  "Flash firmware image from the specified file to a region on the device",
+	  "               FILENAME [ REGION-NUMBER-TO-FLASH ]\n" },
+	{ "-N", "--config-nfc", MODE_SNFC,
+	  "Configure Rx network flow classification options",
+	  "		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
+	  "tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... ]\n" },
+	{ "-x", "--show-rxfh-indir", MODE_GRXFHINDIR,
+	  "Show Rx flow hash indirection" },
+	{ "-X", "--set-rxfh-indir", MODE_SRXFHINDIR,
+	  "Set Rx flow hash indirection",
+	  "		equal N | weight W0 W1 ...\n" },
+	{ "-U", "--config-ntuple", MODE_SCLSRULE,
+	  "Configure Rx ntuple filters and actions",
+	  "		[ delete %d ] |\n"
+	  "		[ flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4\n"
+	  "			[ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
+	  "			[ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
+	  "			[ proto %d [m %x] ]\n"
+	  "			[ src-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
+	  "			[ dst-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
+	  "			[ tos %d [m %x] ]\n"
+	  "			[ l4proto %d [m %x] ]\n"
+	  "			[ src-port %d [m %x] ]\n"
+	  "			[ dst-port %d [m %x] ]\n"
+	  "			[ spi %d [m %x] ]\n"
+	  "			[ vlan-etype %x [m %x] ]\n"
+	  "			[ vlan %x [m %x] ]\n"
+	  "			[ user-def %x [m %x] ]\n"
+	  "			[ action %d ]\n"
+	  "			[ loc %d]]\n" },
+	{ "-u", "--show-ntuple", MODE_GCLSRULE,
+	  "Get Rx ntuple filters and actions",
+	  "		[ rule %d ]\n"},
+	{ "-P", "--show-permaddr", MODE_PERMADDR,
+	  "Show permanent hardware address" },
+	{ "-w", "--get-dump", MODE_GET_DUMP,
+	  "Get dump flag, data",
+	  "		[ data FILENAME ]\n" },
+	{ "-W", "--set-dump", MODE_SET_DUMP,
+	  "Set dump flag of the device",
+	  "		N\n"},
+	{ "-l", "--show-channels", MODE_GCHANNELS, "Query Channels" },
+	{ "-L", "--set-channels", MODE_SCHANNELS, "Set Channels",
+	  "               [ rx N ]\n"
+	  "               [ tx N ]\n"
+	  "               [ other N ]\n"
+	  "               [ combined N ]\n" },
+	{ "-h", "--help", MODE_HELP, "Show this help" },
+	{ NULL, "--version", MODE_VERSION, "Show version number" },
+	{}
 };
 

-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 07/21] Fix format of help text for -f option
From: Ben Hutchings @ 2011-11-01 23:15 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index f772f61..aab1e41 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -229,9 +229,9 @@ static struct option {
 		"options",
 		"		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
 		"tcp6|udp6|ah6|esp6|sctp6 ]\n" },
-    { "-f", "--flash", MODE_FLASHDEV, "FILENAME " "Flash firmware image "
+    { "-f", "--flash", MODE_FLASHDEV, "Flash firmware image "
     		"from the specified file to a region on the device",
-		"               [ REGION-NUMBER-TO-FLASH ]\n" },
+		"               FILENAME [ REGION-NUMBER-TO-FLASH ]\n" },
     { "-N", "--config-nfc", MODE_SNFC, "Configure Rx network flow "
 		"classification options",
 		"		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related

* [PATCH ethtool 06/21] ethtool.8: Allow line-break in description of parameters after -N
From: Ben Hutchings @ 2011-11-01 23:15 UTC (permalink / raw)
  To: netdev; +Cc: linux-net-drivers
In-Reply-To: <1320186901.2758.30.camel@bwh-desktop>

These lines are currently unbreakable and result in ugly wrapping
in an 80-column window.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 ethtool.8.in |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/ethtool.8.in b/ethtool.8.in
index 65392d6..3304fe5 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -256,7 +256,7 @@ ethtool \- query or control network driver and hardware settings
 .HP
 .B ethtool \-N
 .I devname
-.RB [ rx\-flow\-hash \ \*(FL \  \*(HO]
+.RB [ rx\-flow\-hash \ \*(FL \: \*(HO]
 .HP
 .B ethtool \-w|\-\-get\-dump
 .I devname
@@ -605,7 +605,7 @@ sctp6	SCTP over IPv6
 .B \-N \-\-config\-nfc
 Configures the receive network flow classification.
 .TP
-.BR rx\-flow\-hash \ \*(FL \ \*(HO
+.BR rx\-flow\-hash \ \*(FL \: \*(HO
 Configures the hash options for the specified network traffic type.
 .TS
 nokeep;
-- 
1.7.4.4



-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox