* [RFC PATCH ethtool v2 00/23] ethtool netlink interface (userspace side) (WiP)
From: Michal Kubecek @ 2018-07-30 12:55 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
(The series is based on v4.17)
This patch series is and ethtool counterpart to kernel series "ethtool
netlink interface (WiP)"; it's marked as "v2" to match kernel series
version even if I never really sent "v1" to the list. It's only an RFC
as the netlink API it implements is still only a proposal.
The series modifies ethtool to use the netlink based kernel API when
available. If not, it falls back to the ioctl API. The aim is to
preserve command line syntax (with few extensions) and preserve the
output as much as possible.
This brings a natural question: why? It would be certainly tempting to
start with a new tool without the limitations of the old command line
syntax and without having to immitatte strange artefacts of the past.
It may still come to that but experience with iproute2 shows even if you
provide a new tool with cleaner syntax, more features and better
organized output, 19 years later you may still meet (a lot of) people
still using the old tool and claiming they are not going to learn some
new tools with "strange syntax and incomprehensible output" (the sadest
part being that many of them haven't learn using ifconfig before it had
been already obsolete for ten years). That's why I tried to give this
approach at least a try.
As both parser and dump functions in current ethtool code are closely
tied to data structures used by ioctl API, only very little of existing
code could be reused. The parts used also by netlink code were moved to
common.h and common.c. New netlink related code resides in netlink/
subdirectory. Copies of kernel uapi headers were moved to uapi/
directory to make their status more apparent. Netlink code uses libmnl
for message composition and parsing.
Currently reimplemented functions are
-i / --driver
(no option)
-s
-k / --show-features / --show-offload
-K / --features / --offload
-c / --show-coalesce
-C / --coalesce
-g / --show-ring
-G / --set-ring
-a / --show-pause
-A / --pause
-l / --show-channels
-L / --set-channels
--show-eee
--set-eee
--show-fec
--set-fec
For all "get" type options, "*" can be used as device name to request
information about all interfaces supporting that request type.
The most interesting new feature is notification monitoring. When
started as
ethtool --monitor --all
ethtool will listen to netlink notifications and display information
about changes. Replacing "--all" with one of the options above will show
only notifications about changes related to that option. A device name
can be also used to display only notifications related to that device.
ToDo / open questions:
- the most important question still is if we want to reimplement ethtool
or come with a completely new utility instead
- if we go on with teaching ethtool netlink, another interesting
question is how closely we need to stick to current output and its
formatting; even if people are told not to, there are certainly tons
of scripts parsing ethtool output which may break on any change
- an example of this are legacy "flags" for netdev features; we will
have to make sure "ethtool -K dev tso off" will do what users expect
it to but can we get rid of them at least in "ethtool -k" output?
- having to say "--monitor -all" for monitoring all events while just
"--monitor" only watches for sset ("-s") type changes is probably
counterintuitive; would it be better to make "--all" default and leave
only "--monitor -s" for watching only "sset" events
Michal Kubecek (23):
move UAPI header copies to a separate directory
update UAPI header copies
netlink: add netlink interface
netlink: add support for string sets
netlink: add notification monitor
netlink: add netlink handler for gdrv (-i)
netlink: add netlink handler for gset (no option)
netlink: add helpers for command line parsing
netlink: add netlink handler for sset (-s)
netlink: add netlink handler for gfeatures (-k)
netlink: add netlink handler for sfeatures (-K)
netlink: add netlink handler for gcoalesce (-c)
netlink: add netlink handler for gring (-g)
netlink: add netlink handler for gpause (-a)
netlink: add netlink handler for gchannels (-l)
netlink: add netlink handler for geee (--show-eee)
netlink: add netlink handler for gfec (--show-fec)
netlink: add netlink handler for scoalesce (-C)
netlink: add netlink handler for sring (-G)
netlink: add netlink handler for spause (-A)
netlink: add netlink handler for schannels (-L)
netlink: add netlink handler for seee (--set-eee)
netlink: add netlink handler for sfec (--set-fec)
Makefile.am | 20 +-
common.c | 325 ++++++
common.h | 83 ++
configure.ac | 14 +-
ethtool.c | 437 +++-----
internal.h | 12 +-
netlink/drvinfo.c | 43 +
netlink/extapi.h | 37 +
netlink/monitor.c | 283 +++++
netlink/netlink.c | 681 ++++++++++++
netlink/netlink.h | 189 ++++
netlink/params.c | 659 +++++++++++
netlink/parser.c | 559 ++++++++++
netlink/parser.h | 49 +
netlink/settings.c | 1035 ++++++++++++++++++
netlink/strset.c | 265 +++++
netlink/strset.h | 16 +
ethtool-copy.h => uapi/linux/ethtool.h | 13 +-
uapi/linux/ethtool_netlink.h | 325 ++++++
uapi/linux/genetlink.h | 89 ++
net_tstamp-copy.h => uapi/linux/net_tstamp.h | 19 +
uapi/linux/netlink.h | 247 +++++
22 files changed, 5134 insertions(+), 266 deletions(-)
create mode 100644 common.c
create mode 100644 common.h
create mode 100644 netlink/drvinfo.c
create mode 100644 netlink/extapi.h
create mode 100644 netlink/monitor.c
create mode 100644 netlink/netlink.c
create mode 100644 netlink/netlink.h
create mode 100644 netlink/params.c
create mode 100644 netlink/parser.c
create mode 100644 netlink/parser.h
create mode 100644 netlink/settings.c
create mode 100644 netlink/strset.c
create mode 100644 netlink/strset.h
rename ethtool-copy.h => uapi/linux/ethtool.h (99%)
create mode 100644 uapi/linux/ethtool_netlink.h
create mode 100644 uapi/linux/genetlink.h
rename net_tstamp-copy.h => uapi/linux/net_tstamp.h (89%)
create mode 100644 uapi/linux/netlink.h
--
2.18.0
^ permalink raw reply
* [RFC PATCH ethtool v2 01/23] move UAPI header copies to a separate directory
From: Michal Kubecek @ 2018-07-30 12:55 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
The upcoming netlink series is going to add more local copies of kernel
UAPI header files and some of them are going to include others. Keeping
them in the main directory under modified name would require modifying
those includes as well which would be impractical.
Create a subdirectory uapi and move the UAPI headers there to allow
including them in the usual way.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
Makefile.am | 6 +++---
internal.h | 4 ++--
ethtool-copy.h => uapi/linux/ethtool.h | 0
net_tstamp-copy.h => uapi/linux/net_tstamp.h | 0
4 files changed, 5 insertions(+), 5 deletions(-)
rename ethtool-copy.h => uapi/linux/ethtool.h (100%)
rename net_tstamp-copy.h => uapi/linux/net_tstamp.h (100%)
diff --git a/Makefile.am b/Makefile.am
index 14f79b68a9ee..e7da37a85499 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,12 +1,12 @@
-AM_CFLAGS = -Wall
+AM_CFLAGS = -I./uapi -Wall
LDADD = -lm
man_MANS = ethtool.8
EXTRA_DIST = LICENSE ethtool.8 ethtool.spec.in aclocal.m4 ChangeLog autogen.sh
sbin_PROGRAMS = ethtool
-ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h net_tstamp-copy.h \
- rxclass.c
+ethtool_SOURCES = ethtool.c uapi/linux/ethtool.h internal.h \
+ uapi/linux/net_tstamp.h rxclass.c
if ETHTOOL_ENABLE_PRETTY_DUMP
ethtool_SOURCES += \
amd8111e.c de2104x.c e100.c e1000.c et131x.c igb.c \
diff --git a/internal.h b/internal.h
index b239dc7acbd8..0b04a9794b6b 100644
--- a/internal.h
+++ b/internal.h
@@ -42,8 +42,8 @@ typedef int32_t s32;
#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#endif
-#include "ethtool-copy.h"
-#include "net_tstamp-copy.h"
+#include <linux/ethtool.h>
+#include <linux/net_tstamp.h>
#if __BYTE_ORDER == __BIG_ENDIAN
static inline u16 cpu_to_be16(u16 value)
diff --git a/ethtool-copy.h b/uapi/linux/ethtool.h
similarity index 100%
rename from ethtool-copy.h
rename to uapi/linux/ethtool.h
diff --git a/net_tstamp-copy.h b/uapi/linux/net_tstamp.h
similarity index 100%
rename from net_tstamp-copy.h
rename to uapi/linux/net_tstamp.h
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 02/23] update UAPI header copies
From: Michal Kubecek @ 2018-07-30 12:55 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Update to current net-next (commit 3bc950c34b28).
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
uapi/linux/ethtool.h | 6 +++++-
uapi/linux/net_tstamp.h | 19 +++++++++++++++++++
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/uapi/linux/ethtool.h b/uapi/linux/ethtool.h
index 8cc61e9ab40b..9dd347caf4ed 100644
--- a/uapi/linux/ethtool.h
+++ b/uapi/linux/ethtool.h
@@ -215,12 +215,16 @@ struct ethtool_value {
__u32 data;
};
+#define PFC_STORM_PREVENTION_AUTO 0xffff
+#define PFC_STORM_PREVENTION_DISABLE 0
+
enum tunable_id {
ETHTOOL_ID_UNSPEC,
ETHTOOL_RX_COPYBREAK,
ETHTOOL_TX_COPYBREAK,
+ ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */
/*
- * Add your fresh new tubale attribute above and remember to update
+ * Add your fresh new tunable attribute above and remember to update
* tunable_strings[] in net/core/ethtool.c
*/
__ETHTOOL_TUNABLE_COUNT,
diff --git a/uapi/linux/net_tstamp.h b/uapi/linux/net_tstamp.h
index 3d421d912193..97ff3c17ec4d 100644
--- a/uapi/linux/net_tstamp.h
+++ b/uapi/linux/net_tstamp.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* Userspace API for hardware time stamping of network packets
*
@@ -140,4 +141,22 @@ struct scm_ts_pktinfo {
__u32 reserved[2];
};
+/*
+ * SO_TXTIME gets a struct sock_txtime with flags being an integer bit
+ * field comprised of these values.
+ */
+enum txtime_flags {
+ SOF_TXTIME_DEADLINE_MODE = (1 << 0),
+ SOF_TXTIME_REPORT_ERRORS = (1 << 1),
+
+ SOF_TXTIME_FLAGS_LAST = SOF_TXTIME_REPORT_ERRORS,
+ SOF_TXTIME_FLAGS_MASK = (SOF_TXTIME_FLAGS_LAST - 1) |
+ SOF_TXTIME_FLAGS_LAST
+};
+
+struct sock_txtime {
+ clockid_t clockid; /* reference clockid */
+ __u32 flags; /* as defined by enum txtime_flags */
+};
+
#endif /* _NET_TIMESTAMPING_H */
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 03/23] netlink: add netlink interface
From: Michal Kubecek @ 2018-07-30 12:55 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Add basic netlink interface based on genetlink and libmnl. This commit only
adds the generic infrastructure but does not override any ethtool command
(so that there is no actual behaviour change).
Netlink handlers for ethtool subcommands are added as nl_func members to
args array in ethtool.c. Netlink handler is used if it is available (i.e.
nl_func is not null) and ethtool succeeds to open a netlink socket and get
id of genetlink family "ethtool". At the moment, all nl_func are null so
that ioctl() is always used.
Running configure with --disable-netlink disables netlink interface
completely.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
Makefile.am | 11 +
configure.ac | 14 +-
ethtool.c | 178 ++++++++----
internal.h | 8 +
netlink/extapi.h | 16 ++
netlink/netlink.c | 522 +++++++++++++++++++++++++++++++++++
netlink/netlink.h | 154 +++++++++++
uapi/linux/ethtool.h | 7 +
uapi/linux/ethtool_netlink.h | 325 ++++++++++++++++++++++
uapi/linux/genetlink.h | 89 ++++++
uapi/linux/netlink.h | 247 +++++++++++++++++
11 files changed, 1514 insertions(+), 57 deletions(-)
create mode 100644 netlink/extapi.h
create mode 100644 netlink/netlink.c
create mode 100644 netlink/netlink.h
create mode 100644 uapi/linux/ethtool_netlink.h
create mode 100644 uapi/linux/genetlink.h
create mode 100644 uapi/linux/netlink.h
diff --git a/Makefile.am b/Makefile.am
index e7da37a85499..bdb4f4df2b0f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,6 +7,8 @@ EXTRA_DIST = LICENSE ethtool.8 ethtool.spec.in aclocal.m4 ChangeLog autogen.sh
sbin_PROGRAMS = ethtool
ethtool_SOURCES = ethtool.c uapi/linux/ethtool.h internal.h \
uapi/linux/net_tstamp.h rxclass.c
+ethtool_CFLAGS = -I./uapi -Wall
+ethtool_LDADD = -lm
if ETHTOOL_ENABLE_PRETTY_DUMP
ethtool_SOURCES += \
amd8111e.c de2104x.c e100.c e1000.c et131x.c igb.c \
@@ -17,6 +19,15 @@ ethtool_SOURCES += \
ixgbevf.c tse.c vmxnet3.c qsfp.c qsfp.h fjes.c lan78xx.c
endif
+if ETHTOOL_ENABLE_NETLINK
+ethtool_SOURCES += \
+ netlink/netlink.c netlink/netlink.h netlink/extapi.h \
+ uapi/linux/ethtool_netlink.h \
+ uapi/linux/netlink.h uapi/linux/genetlink.h
+ethtool_CFLAGS += @MNL_CFLAGS@
+ethtool_LDADD += @MNL_LIBS@
+endif
+
TESTS = test-cmdline test-features
check_PROGRAMS = test-cmdline test-features
test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES)
diff --git a/configure.ac b/configure.ac
index e891d917cf11..138039d49a2c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script.
AC_INIT(ethtool, 4.17, netdev@vger.kernel.org)
AC_PREREQ(2.52)
AC_CONFIG_SRCDIR([ethtool.c])
-AM_INIT_AUTOMAKE([gnu])
+AM_INIT_AUTOMAKE([gnu subdir-objects])
AC_CONFIG_HEADERS([ethtool-config.h])
AM_MAINTAINER_MODE
@@ -40,5 +40,17 @@ if test x$enable_pretty_dump = xyes; then
fi
AM_CONDITIONAL([ETHTOOL_ENABLE_PRETTY_DUMP], [test x$enable_pretty_dump = xyes])
+AC_ARG_ENABLE(netlink,
+ [ --enable-netlink enable netlink interface (enabled by default)],
+ ,
+ enable_netlink=yes)
+if test x$enable_netlink = xyes; then
+ PKG_PROG_PKG_CONFIG
+ PKG_CHECK_MODULES([MNL], [libmnl])
+ AC_DEFINE(ETHTOOL_ENABLE_NETLINK, 1,
+ Define this to support netlink interface to talk to kernel.)
+fi
+AM_CONDITIONAL([ETHTOOL_ENABLE_NETLINK], [test x$enable_netlink = xyes])
+
AC_CONFIG_FILES([Makefile ethtool.spec ethtool.8])
AC_OUTPUT
diff --git a/ethtool.c b/ethtool.c
index fb93ae898312..e8044a6af76c 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -48,6 +48,10 @@
#include <linux/sockios.h>
#include <linux/netlink.h>
+#ifdef ETHTOOL_ENABLE_NETLINK
+#include "netlink/extapi.h"
+#endif
+
#ifndef MAX_ADDR_LEN
#define MAX_ADDR_LEN 32
#endif
@@ -5048,14 +5052,22 @@ int send_ioctl(struct cmd_context *ctx, void *cmd)
static int show_usage(struct cmd_context *ctx);
+#ifndef ETHTOOL_ENABLE_NETLINK
+/* Just define all netlink handlers as null when building without netlink
+ * support so that we do not get unresolved symbols in args array below
+ */
+#endif
+
static const struct option {
const char *opts;
int want_device;
int (*func)(struct cmd_context *);
+ int (*nl_func)(struct cmd_context *);
char *help;
char *opthelp;
} args[] = {
- { "-s|--change", 1, do_sset, "Change generic options",
+ { "-s|--change", 1, do_sset, NULL,
+ "Change generic options",
" [ speed %d ]\n"
" [ duplex half|full ]\n"
" [ port tp|aui|bnc|mii|fibre ]\n"
@@ -5067,13 +5079,17 @@ static const 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, NULL,
+ "Show pause options" },
+ { "-A|--pause", 1, do_spause, NULL,
+ "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, NULL,
+ "Show coalesce options" },
+ { "-C|--coalesce", 1, do_scoalesce, NULL,
+ "Set coalesce options",
" [adaptive-rx on|off]\n"
" [adaptive-tx on|off]\n"
" [rx-usecs N]\n"
@@ -5096,46 +5112,54 @@ static const 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, NULL,
+ "Query RX/TX ring parameters" },
+ { "-G|--set-ring", 1, do_sring, NULL,
+ "Set RX/TX ring parameters",
" [ rx N ]\n"
" [ rx-mini N ]\n"
" [ rx-jumbo N ]\n"
" [ tx N ]\n" },
- { "-k|--show-features|--show-offload", 1, do_gfeatures,
+ { "-k|--show-features|--show-offload", 1, do_gfeatures, NULL,
"Get state of protocol offload and other features" },
- { "-K|--features|--offload", 1, do_sfeatures,
+ { "-K|--features|--offload", 1, do_sfeatures, NULL,
"Set protocol offload and other features",
" FEATURE 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, NULL,
+ "Show driver information" },
+ { "-d|--register-dump", 1, do_gregs, NULL,
+ "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, NULL,
+ "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, NULL,
"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, NULL,
+ "Restart N-WAY negotiation" },
+ { "-p|--identify", 1, do_phys_id, NULL,
"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, NULL,
+ "Execute adapter self test",
" [ online | offline | external_lb ]\n" },
- { "-S|--statistics", 1, do_gnicstats, "Show adapter statistics" },
- { "--phy-statistics", 1, do_gphystats,
+ { "-S|--statistics", 1, do_gnicstats, NULL,
+ "Show adapter statistics" },
+ { "--phy-statistics", 1, do_gphystats, NULL,
"Show phy statistics" },
- { "-n|-u|--show-nfc|--show-ntuple", 1, do_grxclass,
+ { "-n|-u|--show-nfc|--show-ntuple", 1, do_grxclass, NULL,
"Show Rx network flow classification options or rules",
" [ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
"tcp6|udp6|ah6|esp6|sctp6 [context %d] |\n"
" rule %d ]\n" },
- { "-N|-U|--config-nfc|--config-ntuple", 1, do_srxclass,
+ { "-N|-U|--config-nfc|--config-ntuple", 1, do_srxclass, NULL,
"Configure Rx network flow classification options or rules",
" rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
"tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... [context %d] |\n"
@@ -5160,55 +5184,64 @@ static const struct option {
" [ context %d ]\n"
" [ loc %d]] |\n"
" delete %d\n" },
- { "-T|--show-time-stamping", 1, do_tsinfo,
+ { "-T|--show-time-stamping", 1, do_tsinfo, NULL,
"Show time stamping capabilities" },
- { "-x|--show-rxfh-indir|--show-rxfh", 1, do_grxfh,
+ { "-x|--show-rxfh-indir|--show-rxfh", 1, do_grxfh, NULL,
"Show Rx flow hash indirection table and/or RSS hash key",
" [ context %d ]\n" },
- { "-X|--set-rxfh-indir|--rxfh", 1, do_srxfh,
+ { "-X|--set-rxfh-indir|--rxfh", 1, do_srxfh, NULL,
"Set Rx flow hash indirection table and/or RSS hash key",
" [ context %d|new ]\n"
" [ equal N | weight W0 W1 ... | default ]\n"
" [ hkey %x:%x:%x:%x:%x:.... ]\n"
" [ hfunc FUNC ]\n"
" [ delete ]\n" },
- { "-f|--flash", 1, do_flash,
+ { "-f|--flash", 1, do_flash, NULL,
"Flash firmware image from the specified file to a region on the device",
" FILENAME [ REGION-NUMBER-TO-FLASH ]\n" },
- { "-P|--show-permaddr", 1, do_permaddr,
+ { "-P|--show-permaddr", 1, do_permaddr, NULL,
"Show permanent hardware address" },
- { "-w|--get-dump", 1, do_getfwdump,
+ { "-w|--get-dump", 1, do_getfwdump, NULL,
"Get dump flag, data",
" [ data FILENAME ]\n" },
- { "-W|--set-dump", 1, do_setfwdump,
+ { "-W|--set-dump", 1, do_setfwdump, NULL,
"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, NULL,
+ "Query Channels" },
+ { "-L|--set-channels", 1, do_schannels, NULL,
+ "Set Channels",
" [ rx N ]\n"
" [ tx N ]\n"
" [ other N ]\n"
" [ combined N ]\n" },
- { "--show-priv-flags", 1, do_gprivflags, "Query private flags" },
- { "--set-priv-flags", 1, do_sprivflags, "Set private flags",
+ { "--show-priv-flags", 1, do_gprivflags, NULL,
+ "Query private flags" },
+ { "--set-priv-flags", 1, do_sprivflags, NULL,
+ "Set private flags",
" FLAG on|off ...\n" },
- { "-m|--dump-module-eeprom|--module-info", 1, do_getmodule,
+ { "-m|--dump-module-eeprom|--module-info", 1, do_getmodule, NULL,
"Query/Decode Module EEPROM information and optical diagnostics if available",
" [ raw on|off ]\n"
" [ hex on|off ]\n"
" [ offset N ]\n"
" [ length N ]\n" },
- { "--show-eee", 1, do_geee, "Show EEE settings"},
- { "--set-eee", 1, do_seee, "Set EEE settings",
+ { "--show-eee", 1, do_geee, NULL,
+ "Show EEE settings"},
+ { "--set-eee", 1, do_seee, NULL,
+ "Set EEE settings",
" [ eee on|off ]\n"
" [ advertise %x ]\n"
" [ tx-lpi on|off ]\n"
" [ tx-timer %d ]\n"},
- { "--set-phy-tunable", 1, do_set_phy_tunable, "Set PHY tunable",
+ { "--set-phy-tunable", 1, do_set_phy_tunable, NULL,
+ "Set PHY tunable",
" [ downshift on|off [count N] ]\n"},
- { "--get-phy-tunable", 1, do_get_phy_tunable, "Get PHY tunable",
+ { "--get-phy-tunable", 1, do_get_phy_tunable, NULL,
+ "Get PHY tunable",
" [ downshift ]\n"},
- { "--reset", 1, do_reset, "Reset components",
+ { "--reset", 1, do_reset, NULL,
+ "Reset components",
" [ flags %x ]\n"
" [ mgmt ]\n"
" [ mgmt-shared ]\n"
@@ -5230,11 +5263,15 @@ static const struct option {
" [ ap-shared ]\n"
" [ dedicated ]\n"
" [ all ]\n"},
- { "--show-fec", 1, do_gfec, "Show FEC settings"},
- { "--set-fec", 1, do_sfec, "Set FEC settings",
+ { "--show-fec", 1, do_gfec, NULL,
+ "Show FEC settings"},
+ { "--set-fec", 1, do_sfec, NULL,
+ "Set FEC settings",
" [ encoding auto|off|rs|baser ]\n"},
- { "-h|--help", 0, show_usage, "Show this help" },
- { "--version", 0, do_version, "Show version number" },
+ { "-h|--help", 0, show_usage, NULL,
+ "Show this help" },
+ { "--version", 0, do_version, NULL,
+ "Show version number" },
{}
};
@@ -5261,11 +5298,35 @@ static int show_usage(struct cmd_context *ctx)
return 0;
}
+static int ioctl_init(struct cmd_context *ctx, int want_device)
+{
+ if (want_device) {
+ /* Setup our control structures. */
+ memset(&ctx->ifr, 0, sizeof(ctx->ifr));
+ strcpy(ctx->ifr.ifr_name, ctx->devname);
+
+ /* Open control socket. */
+ ctx->fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ctx->fd < 0)
+ ctx->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
+ if (ctx->fd < 0) {
+ perror("Cannot get control socket");
+ return 70;
+ }
+ } else {
+ ctx->fd = -1;
+ }
+
+ return 0;
+}
+
int main(int argc, char **argp)
{
int (*func)(struct cmd_context *);
+ int (*nl_func)(struct cmd_context *) = NULL;
int want_device;
struct cmd_context ctx;
+ int ret;
int k;
init_global_link_mode_masks();
@@ -5291,6 +5352,7 @@ int main(int argc, char **argp)
argp++;
argc--;
func = args[k].func;
+ nl_func = args[k].nl_func;
want_device = args[k].want_device;
goto opt_found;
}
@@ -5305,6 +5367,15 @@ int main(int argc, char **argp)
want_device = 1;
opt_found:
+#ifdef ETHTOOL_ENABLE_NETLINK
+ if (nl_func) {
+ if (netlink_init(&ctx))
+ nl_func = NULL; /* fallback to ioctl() */
+ }
+#else
+ nl_func = NULL;
+#endif
+
if (want_device) {
ctx.devname = *argp++;
argc--;
@@ -5313,25 +5384,20 @@ opt_found:
exit_bad_args();
if (strlen(ctx.devname) >= IFNAMSIZ)
exit_bad_args();
-
- /* Setup our control structures. */
- memset(&ctx.ifr, 0, sizeof(ctx.ifr));
- strcpy(ctx.ifr.ifr_name, ctx.devname);
-
- /* Open control socket. */
- ctx.fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (ctx.fd < 0)
- ctx.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
- if (ctx.fd < 0) {
- perror("Cannot get control socket");
- return 70;
- }
- } else {
- ctx.fd = -1;
}
ctx.argc = argc;
ctx.argp = argp;
+ if (nl_func) {
+ ret = nl_func(&ctx);
+ if ((ret != -EOPNOTSUPP) || !func)
+ return (ret >= 0) ? ret : 1;
+ }
+
+ ret = ioctl_init(&ctx, want_device);
+ if (ret)
+ return ret;
+
return func(&ctx);
}
diff --git a/internal.h b/internal.h
index 0b04a9794b6b..f7feade9f57e 100644
--- a/internal.h
+++ b/internal.h
@@ -23,6 +23,11 @@
#include <sys/ioctl.h>
#include <net/if.h>
+/* internal for netlink interface */
+#ifdef ETHTOOL_ENABLE_NETLINK
+struct nl_context;
+#endif
+
/* ethtool.h expects these to be defined by <linux/types.h> */
#ifndef HAVE_BE_TYPES
typedef uint16_t __be16;
@@ -195,6 +200,9 @@ struct cmd_context {
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 ETHTOOL_ENABLE_NETLINK
+ struct nl_context *nlctx; /* netlink context (opaque) */
+#endif
};
#ifdef TEST_ETHTOOL
diff --git a/netlink/extapi.h b/netlink/extapi.h
new file mode 100644
index 000000000000..05ab083d9b9c
--- /dev/null
+++ b/netlink/extapi.h
@@ -0,0 +1,16 @@
+/*
+ * extapi.h - external netlink interface
+ *
+ * interface for general non-netlink code
+ */
+
+#ifndef ETHTOOL_EXTAPI_H__
+#define ETHTOOL_EXTAPI_H__
+
+struct cmd_context;
+struct nl_context;
+
+int netlink_init(struct cmd_context *ctx);
+int netlink_done(struct cmd_context *ctx);
+
+#endif /* ETHTOOL_EXTAPI_H__ */
diff --git a/netlink/netlink.c b/netlink/netlink.c
new file mode 100644
index 000000000000..0671c4589c72
--- /dev/null
+++ b/netlink/netlink.c
@@ -0,0 +1,522 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include "../internal.h"
+#include "netlink.h"
+#include "extapi.h"
+
+/* misc helpers */
+
+unsigned int nl_copy_payload(void *buff, unsigned int maxlen,
+ const struct nlattr *attr)
+{
+ unsigned int len = mnl_attr_get_payload_len(attr);
+
+ if (len > maxlen)
+ len = maxlen;
+ memcpy(buff, mnl_attr_get_payload(attr), len);
+
+ return len;
+}
+
+/* standard attribute parser callback
+ * While we trust kernel not to send us malformed messages, we must expect
+ * to run on top of newer kernel which may send attributes that we do not
+ * know (yet). Rather than treating them as an error, just ignore them.
+ */
+int attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct attr_tb_info *tb_info = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (type >= 0 && type <= tb_info->max_type)
+ tb_info->tb[type] = attr;
+
+ return MNL_CB_OK;
+}
+
+uint32_t bitset_get_count(const struct nlattr *bitset, int *retptr)
+{
+ const struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, bitset) {
+ if (mnl_attr_get_type(attr) != ETHA_BITSET_SIZE)
+ continue;
+ *retptr = 0;
+ return mnl_attr_get_u32(attr);
+ }
+
+ *retptr = -EFAULT;
+ return 0;
+}
+
+bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx,
+ int *retptr)
+{
+ const struct nlattr *bitset_tb[ETHA_BITSET_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(bitset_tb);
+ const struct nlattr *bits;
+ const struct nlattr *bit;
+ int ret;
+
+ *retptr = 0;
+ ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
+ if (ret < 0)
+ goto err;
+
+ bits = mask ? bitset_tb[ETHA_BITSET_MASK] :
+ bitset_tb[ETHA_BITSET_VALUE];
+ if (bits) {
+ const uint32_t *bitmap =
+ (const uint32_t *)mnl_attr_get_payload(bits);
+
+ if (idx >= 8 * mnl_attr_get_payload_len(bits))
+ return false;
+ return bitmap[idx / 32] & (1U << (idx % 32));
+ }
+
+ bits = bitset_tb[ETHA_BITSET_BITS];
+ if (!bits)
+ goto err;
+ mnl_attr_for_each_nested(bit, bits) {
+ const struct nlattr *tb[ETHA_BIT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ unsigned int my_idx;
+
+ if (mnl_attr_get_type(bit) != ETHA_BITS_BIT)
+ continue;
+ ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info);
+ if (ret < 0)
+ goto err;
+ ret = -EFAULT;
+ if (!tb[ETHA_BIT_INDEX])
+ goto err;
+
+ my_idx = mnl_attr_get_u32(tb[ETHA_BIT_INDEX]);
+ if (my_idx == idx)
+ return mask || tb[ETHA_BIT_VALUE];
+ }
+
+ return false;
+err:
+ fprintf(stderr, "malformed netlink message (bitset)\n");
+ *retptr = ret;
+ return false;
+}
+
+bool bitset_is_empty(const struct nlattr *bitset, bool mask, int *retptr)
+{
+ const struct nlattr *bitset_tb[ETHA_BITSET_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(bitset_tb);
+ const struct nlattr *bits;
+ const struct nlattr *bit;
+ int ret;
+
+ *retptr = 0;
+ ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
+ if (ret < 0)
+ goto err;
+
+ bits = mask ? bitset_tb[ETHA_BITSET_MASK] :
+ bitset_tb[ETHA_BITSET_VALUE];
+ if (bits) {
+ const uint32_t *bitmap =
+ (const uint32_t *)mnl_attr_get_payload(bits);
+ unsigned int n = mnl_attr_get_payload_len(bits);
+ unsigned int i;
+
+ ret = -EFAULT;
+ if (n % 4)
+ goto err;
+ for (i = 0; i < n / 4; i++)
+ if (bitmap[i])
+ return false;
+ return true;
+ }
+
+ bits = bitset_tb[ETHA_BITSET_BITS];
+ if (!bits)
+ goto err;
+ mnl_attr_for_each_nested(bit, bits) {
+ const struct nlattr *tb[ETHA_BIT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+
+ if (mnl_attr_get_type(bit) != ETHA_BITS_BIT)
+ continue;
+ if (mask)
+ return false;
+
+ ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info);
+ if (ret < 0)
+ goto err;
+ if (tb[ETHA_BIT_VALUE])
+ return false;
+ }
+
+ return true;
+err:
+ fprintf(stderr, "malformed netlink message (bitset)\n");
+ *retptr = ret;
+ return true;
+}
+
+static int __msg_init(struct nl_context *nlctx, int family, int cmd,
+ unsigned int flags, int version)
+{
+ struct nlmsghdr *nlhdr;
+ struct genlmsghdr *gnlhdr;
+
+ nlctx->buff = malloc(MNL_SOCKET_BUFFER_SIZE);
+ if (!nlctx->buff)
+ return -ENOMEM;
+ nlctx->buffsize = MNL_SOCKET_BUFFER_SIZE;
+ nlctx->seq++;
+ memset(nlctx->buff, '\0', NLMSG_HDRLEN + GENL_HDRLEN);
+
+ nlhdr = mnl_nlmsg_put_header(nlctx->buff);
+ nlhdr->nlmsg_type = family;
+ nlhdr->nlmsg_flags = flags;
+ nlhdr->nlmsg_seq = nlctx->seq;
+ nlctx->nlhdr = nlhdr;
+
+ gnlhdr = mnl_nlmsg_put_extra_header(nlhdr, sizeof(*gnlhdr));
+ gnlhdr->cmd = cmd;
+ gnlhdr->version = version;
+ nlctx->gnlhdr = gnlhdr;
+
+ return 0;
+}
+
+static int msg_realloc(struct nl_context *nlctx, unsigned int new_size)
+{
+ unsigned int nlhdr_offset = (char *)nlctx->nlhdr - nlctx->buff;
+ unsigned int gnlhdr_offset = (char *)nlctx->gnlhdr - nlctx->buff;
+ unsigned int msg_offset = (char *)nlctx->msg - nlctx->buff;
+ unsigned int old_size = nlctx->buffsize;
+ char *new_buff;
+
+ if (!new_size)
+ new_size = old_size + MNL_SOCKET_BUFFER_SIZE;
+ if (new_size > MAX_MSG_SIZE)
+ return -EMSGSIZE;
+ new_buff = realloc(nlctx->buff, new_size);
+ if (!new_buff)
+ return -ENOMEM;
+ if (new_buff != nlctx->buff) {
+ memset(new_buff + old_size, '\0', new_size - old_size);
+ nlctx->nlhdr = (struct nlmsghdr *)(new_buff + nlhdr_offset);
+ nlctx->gnlhdr = (struct genlmsghdr *)(new_buff + gnlhdr_offset);
+ nlctx->msg = new_buff + msg_offset;
+ nlctx->buff = new_buff;
+ }
+ nlctx->buffsize = new_size;
+
+ return 0;
+}
+
+int msg_init(struct nl_context *nlctx, int cmd, unsigned int flags)
+{
+ int ret;
+
+ ret = __msg_init(nlctx, nlctx->ethnl_fam, cmd, flags,
+ ETHTOOL_GENL_VERSION);
+ if (ret < 0)
+ return ret;
+ nlctx->msg = mnl_nlmsg_get_payload_offset(nlctx->nlhdr, GENL_HDRLEN);
+
+ return 0;
+}
+
+static int ethnl_process_ack(struct nl_context *nlctx, ssize_t len)
+{
+ struct nlmsghdr *nlhdr = (struct nlmsghdr *)nlctx->buff;
+ struct nlmsgerr *nlerr = mnl_nlmsg_get_payload(nlhdr);
+ const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ unsigned int tlv_offset = sizeof(*nlerr);
+
+ if (len < NLMSG_HDRLEN + sizeof(*nlerr))
+ return -EFAULT;
+ if (!(nlhdr->nlmsg_flags & NLM_F_ACK_TLVS))
+ goto out;
+ if (!(nlhdr->nlmsg_flags & NLM_F_CAPPED))
+ tlv_offset += MNL_ALIGN(mnl_nlmsg_get_payload_len(&nlerr->msg));
+
+ if (mnl_attr_parse(nlhdr, tlv_offset, attr_cb, &tb_info) < 0)
+ goto out;
+ if (tb[NLMSGERR_ATTR_MSG]) {
+ const char *msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
+
+ fprintf(stderr, "netlink %s: %s\n",
+ nlerr->error ? "error" : "warning", msg);
+ }
+
+out:
+ if (nlerr->error) {
+ errno = -nlerr->error;
+ perror("netlink error");
+ }
+ return nlerr->error;
+}
+
+int ethnl_process_reply(struct nl_context *nlctx, mnl_cb_t reply_cb)
+{
+ struct nlmsghdr *nlhdr;
+ ssize_t len;
+ int ret;
+
+ do {
+ msg_realloc(nlctx, 65536);
+ len = mnl_socket_recvfrom(nlctx->sk, nlctx->buff,
+ nlctx->buffsize);
+ if (len <= 0)
+ return (len ? -EFAULT : 0);
+ if (len < NLMSG_HDRLEN)
+ return -EFAULT;
+
+ nlhdr = (struct nlmsghdr *)nlctx->buff;
+ if (nlhdr->nlmsg_type == NLMSG_ERROR)
+ return ethnl_process_ack(nlctx, len);
+
+ nlctx->nlhdr = nlhdr;
+ nlctx->gnlhdr = mnl_nlmsg_get_payload(nlhdr);
+ nlctx->msg = mnl_nlmsg_get_payload_offset(nlhdr, GENL_HDRLEN);
+ ret = mnl_cb_run(nlctx->buff, len, nlctx->seq, nlctx->port,
+ reply_cb, nlctx);
+ } while (ret > 0);
+
+ return ret;
+}
+
+/* safe message composition */
+
+bool ethnla_put(struct nl_context *nlctx, uint16_t type, size_t len,
+ const void *data)
+{
+ struct nlmsghdr *nlhdr = nlctx->nlhdr;
+
+ while (!mnl_attr_put_check(nlhdr, nlctx->buffsize, type, len, data)) {
+ int ret = msg_realloc(nlctx, 0);
+
+ if (ret < 0)
+ return true;
+ }
+
+ return false;
+}
+
+struct nlattr *ethnla_nest_start(struct nl_context *nlctx, uint16_t type)
+{
+ struct nlmsghdr *nlhdr = nlctx->nlhdr;
+ struct nlattr *attr;
+
+ do {
+ attr = mnl_attr_nest_start_check(nlhdr, nlctx->buffsize, type);
+ if (attr)
+ return attr;
+ } while (msg_realloc(nlctx, 0) == 0);
+
+ return NULL;
+}
+
+bool ethnla_put_dev(struct nl_context *nlctx, uint16_t type,
+ const char *devname)
+{
+ struct nlattr *nest = ethnla_nest_start(nlctx, type);
+ struct nlmsghdr *nlhdr = nlctx->nlhdr;
+
+ if (!nest)
+ return true;
+ if (ethnla_put_strz(nlctx, ETHA_DEV_NAME, devname)) {
+ mnl_attr_nest_cancel(nlhdr, nest);
+ return true;
+ }
+ mnl_attr_nest_end(nlhdr, nest);
+
+ return false;
+}
+
+const char *get_dev_name(const struct nlattr *nest)
+{
+ const struct nlattr *dev_tb[ETHA_DEV_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(dev_tb);
+ int ret;
+
+ if (!nest)
+ return NULL;
+ ret = mnl_attr_parse_nested(nest, attr_cb, &dev_tb_info);
+ if (ret < 0 || !dev_tb[ETHA_DEV_NAME])
+ return "(none)";
+ return mnl_attr_get_str(dev_tb[ETHA_DEV_NAME]);
+}
+
+/* request helpers */
+
+int ethnl_prep_get_request(struct cmd_context *ctx, unsigned int nlcmd,
+ uint16_t dev_attrtype)
+{
+ bool is_dev = ctx->devname && strcmp(ctx->devname, WILDCARD_DEVNAME);
+ struct nl_context *nlctx = ctx->nlctx;
+ int ret;
+
+ nlctx->is_dump = !is_dev;
+ ret = msg_init(nlctx, nlcmd,
+ NLM_F_REQUEST | NLM_F_ACK | (is_dev ? 0 : NLM_F_DUMP));
+ if (ret < 0)
+ return ret;
+
+ if (is_dev) {
+ if (ethnla_put_dev(nlctx, dev_attrtype, ctx->devname))
+ return -EMSGSIZE;
+ }
+
+ return 0;
+}
+
+int ethnl_send_get_request(struct nl_context *nlctx, mnl_cb_t cb)
+{
+ int ret;
+
+ ret = ethnl_sendmsg(nlctx);
+ if (ret < 0)
+ goto err;
+ ret = ethnl_process_reply(nlctx, cb);
+ if (ret == 0)
+ return 0;
+err:
+ return nlctx->exit_code ?: 1;
+}
+
+/* get ethtool family id */
+
+static int ethnl_family_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ struct nl_context *nlctx = data;
+ struct nlattr *attr;
+
+ nlctx->ethnl_fam = 0;
+ mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) {
+ if (mnl_attr_get_type(attr) == CTRL_ATTR_FAMILY_ID) {
+ nlctx->ethnl_fam = mnl_attr_get_u16(attr);
+ break;
+ }
+ }
+
+ return (nlctx->ethnl_fam ? MNL_CB_OK : MNL_CB_ERROR);
+}
+
+static int get_ethnl_family(struct nl_context *nlctx)
+{
+ int ret;
+
+ ret = __msg_init(nlctx, GENL_ID_CTRL, CTRL_CMD_GETFAMILY,
+ NLM_F_REQUEST | NLM_F_ACK, 1);
+ if (ret < 0)
+ return ret;
+ mnl_attr_put_strz(nlctx->nlhdr, CTRL_ATTR_FAMILY_NAME,
+ ETHTOOL_GENL_NAME);
+
+ ethnl_sendmsg(nlctx);
+ ethnl_process_reply(nlctx, ethnl_family_cb);
+
+ return (nlctx->ethnl_fam ? 0 : -EADDRNOTAVAIL);
+}
+
+/* initialization */
+
+static int nlctx_init(struct nl_context *nlctx)
+{
+ int ret;
+ int val;
+
+ memset(nlctx, '\0', sizeof(*nlctx));
+ nlctx->seq = (int)time(NULL);
+ nlctx->sk = mnl_socket_open(NETLINK_GENERIC);
+ if (!nlctx->sk)
+ return -ECONNREFUSED;
+ val = 1;
+ mnl_socket_setsockopt(nlctx->sk, NETLINK_EXT_ACK, &val, sizeof(val));
+ ret = mnl_socket_bind(nlctx->sk, 0, MNL_SOCKET_AUTOPID);
+ if (ret < 0)
+ return ret;
+ nlctx->port = mnl_socket_get_portid(nlctx->sk);
+
+ return 0;
+}
+
+static int nlctx_done(struct nl_context *nlctx)
+{
+ if (nlctx->sk)
+ mnl_socket_close(nlctx->sk);
+ free(nlctx->buff);
+ if (nlctx->aux_nlctx) {
+ nlctx_done(nlctx->aux_nlctx);
+ free(nlctx->aux_nlctx);
+ }
+ free(nlctx->cmd_private);
+ memset(nlctx, '\0', sizeof(*nlctx));
+
+ return 0;
+}
+
+int __init_aux_nlctx(struct nl_context *nlctx)
+{
+ struct nl_context *aux;
+ int ret;
+
+ if (nlctx->aux_nlctx)
+ return 0;
+ aux = malloc(sizeof(*aux));
+ if (!aux)
+ return -ENOMEM;
+ ret = nlctx_init(aux);
+ if (ret < 0) {
+ free(aux);
+ return ret;
+ }
+
+ aux->ethnl_fam = nlctx->ethnl_fam;
+ nlctx->aux_nlctx = aux;
+
+ return 0;
+}
+
+int netlink_init(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx;
+ int ret;
+
+ nlctx = malloc(sizeof(*nlctx));
+ if (!nlctx)
+ return -ENOMEM;
+ ret = nlctx_init(nlctx);
+ if (ret < 0)
+ goto err_freenlctx;
+
+ ret = get_ethnl_family(nlctx);
+ if (ret < 0)
+ goto err_uninit;
+
+ ctx->nlctx = nlctx;
+ return 0;
+
+err_uninit:
+ nlctx_done(nlctx);
+err_freenlctx:
+ free(nlctx);
+ ctx->nlctx = NULL;
+ return ret;
+}
+
+int netlink_done(struct cmd_context *ctx)
+{
+ if (ctx->nlctx) {
+ nlctx_done(ctx->nlctx);
+ free(ctx->nlctx);
+ ctx->nlctx = NULL;
+ }
+
+ return 0;
+}
diff --git a/netlink/netlink.h b/netlink/netlink.h
new file mode 100644
index 000000000000..547c865ed535
--- /dev/null
+++ b/netlink/netlink.h
@@ -0,0 +1,154 @@
+/*
+ * netlink.h - common interface for all netlink code
+ *
+ * Declarations of data structures, global data and helpers for netlink code
+ */
+
+#ifndef ETHTOOL_NETLINK_INT_H__
+#define ETHTOOL_NETLINK_INT_H__
+
+#include <libmnl/libmnl.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#include <linux/ethtool_netlink.h>
+
+#define MAX_MSG_SIZE (4 << 20) /* 4 MB */
+#define WILDCARD_DEVNAME "*"
+
+struct nl_context {
+ int ethnl_fam;
+ struct mnl_socket *sk;
+ struct nl_context *aux_nlctx;
+ void *cmd_private;
+ char *buff;
+ unsigned int buffsize;
+ unsigned int port;
+ unsigned int seq;
+ struct nlmsghdr *nlhdr;
+ struct genlmsghdr *gnlhdr;
+ void *msg;
+ const char *devname;
+ bool is_dump;
+ int exit_code;
+};
+
+struct attr_tb_info {
+ const struct nlattr **tb;
+ unsigned int max_type;
+};
+#define DECLARE_ATTR_TB_INFO(tbl) \
+ struct attr_tb_info tbl ## _info = { (tbl), (MNL_ARRAY_SIZE(tbl) - 1) }
+
+unsigned int nl_copy_payload(void *buff, unsigned int maxlen,
+ const struct nlattr *attr);
+bool ethnla_put(struct nl_context *nlctx, uint16_t type, size_t len,
+ const void *data);
+struct nlattr *ethnla_nest_start(struct nl_context *nlctx, uint16_t type);
+bool ethnla_put_dev(struct nl_context *nlctx, uint16_t type,
+ const char *devname);
+const char *get_dev_name(const struct nlattr *nest);
+
+uint32_t bitset_get_count(const struct nlattr *bitset, int *retptr);
+bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx,
+ int *retptr);
+bool bitset_is_empty(const struct nlattr *bitset, bool mask, int *retptr);
+
+int msg_init(struct nl_context *nlctx, int cmd, unsigned int flags);
+int ethnl_process_reply(struct nl_context *nlctx, mnl_cb_t reply_cb);
+int attr_cb(const struct nlattr *attr, void *data);
+int ethnl_prep_get_request(struct cmd_context *ctx, unsigned int nlcmd,
+ uint16_t dev_attrtype);
+int ethnl_send_get_request(struct nl_context *nlctx, mnl_cb_t cb);
+int __init_aux_nlctx(struct nl_context *nlctx);
+
+/* put data wrappers */
+
+static inline bool ethnla_put_u32(struct nl_context *nlctx, uint16_t type,
+ u32 data)
+{
+ return ethnla_put(nlctx, type, sizeof(u32), &data);
+}
+
+static inline bool ethnla_put_u8(struct nl_context *nlctx, uint16_t type,
+ u8 data)
+{
+ return ethnla_put(nlctx, type, sizeof(u8), &data);
+}
+
+static inline bool ethnla_put_flag(struct nl_context *nlctx, uint16_t type,
+ bool val)
+{
+ if (val)
+ return ethnla_put(nlctx, type, 0, &val);
+ else
+ return false;
+}
+
+static inline bool ethnla_put_bitfield32(struct nl_context *nlctx,
+ uint16_t type, u32 value, u32 selector)
+{
+ struct nla_bitfield32 val = {
+ .value = value,
+ .selector = selector,
+ };
+
+ return ethnla_put(nlctx, type, sizeof(val), &val);
+}
+
+static inline bool ethnla_put_strz(struct nl_context *nlctx, uint16_t type,
+ const char *data)
+{
+ return ethnla_put(nlctx, type, strlen(data) + 1, data);
+}
+
+static inline ssize_t ethnl_sendmsg(struct nl_context *nlctx)
+{
+ struct nlmsghdr *nlhdr = nlctx->nlhdr;
+
+ return mnl_socket_sendto(nlctx->sk, nlhdr, nlhdr->nlmsg_len);
+}
+
+/* dump helpers */
+
+static inline const char *u8_to_bool(const struct nlattr *attr)
+{
+ if (attr)
+ return mnl_attr_get_u8(attr) ? "on" : "off";
+ else
+ return "n/a";
+}
+
+static inline void show_u32(const struct nlattr *attr, const char *lbl)
+{
+ if (attr)
+ printf("%s%u\n", lbl, mnl_attr_get_u32(attr));
+}
+
+static inline void show_bool(const struct nlattr *attr, const char *lbl)
+{
+ if (attr)
+ printf("%s%s\n", lbl, mnl_attr_get_u8(attr) ? "on" : "off");
+}
+
+static inline void show_string(const struct nlattr **tb, unsigned int idx,
+ const char *label)
+{
+ printf("%s: %s\n", label, tb[idx] ? mnl_attr_get_str(tb[idx]) : "");
+}
+
+static inline void show_u32_yn(const struct nlattr **tb, unsigned int idx,
+ const char *label)
+{
+ if (tb[idx])
+ printf("%s: %s\n", label,
+ mnl_attr_get_u32(tb[idx]) ? "yes" : "no");
+}
+
+/* misc */
+
+static inline int init_aux_nlctx(struct nl_context *nlctx)
+{
+ return nlctx->aux_nlctx ? 0 : __init_aux_nlctx(nlctx);
+}
+
+#endif /* ETHTOOL_NETLINK_INT_H__ */
diff --git a/uapi/linux/ethtool.h b/uapi/linux/ethtool.h
index 9dd347caf4ed..c51a966bb46e 100644
--- a/uapi/linux/ethtool.h
+++ b/uapi/linux/ethtool.h
@@ -141,6 +141,9 @@ static __inline__ __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep)
*/
#define ETH_MDIO_SUPPORTS_C45 2
+/* All defined ETH_MDIO_SUPPORTS_* flags */
+#define ETH_MDIO_SUPPORTS_ALL (ETH_MDIO_SUPPORTS_C22 | ETH_MDIO_SUPPORTS_C45)
+
#define ETHTOOL_FWVERS_LEN 32
#define ETHTOOL_BUSINFO_LEN 32
#define ETHTOOL_EROMVERS_LEN 32
@@ -576,6 +579,10 @@ enum ethtool_stringset {
ETH_SS_TUNABLES,
ETH_SS_PHY_STATS,
ETH_SS_PHY_TUNABLES,
+ ETH_SS_LINK_MODES,
+
+ __ETH_SS_MAX,
+ ETH_SS_MAX = (__ETH_SS_MAX - 1)
};
/**
diff --git a/uapi/linux/ethtool_netlink.h b/uapi/linux/ethtool_netlink.h
new file mode 100644
index 000000000000..13882326976e
--- /dev/null
+++ b/uapi/linux/ethtool_netlink.h
@@ -0,0 +1,325 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef _LINUX_ETHTOOL_NETLINK_H_
+#define _LINUX_ETHTOOL_NETLINK_H_
+
+#include <linux/ethtool.h>
+
+enum {
+ ETHNL_CMD_NOOP,
+ ETHNL_CMD_EVENT, /* only for notifications */
+ ETHNL_CMD_GET_STRSET,
+ ETHNL_CMD_SET_STRSET, /* only for reply */
+ ETHNL_CMD_GET_DRVINFO,
+ ETHNL_CMD_SET_DRVINFO, /* only for reply */
+ ETHNL_CMD_GET_SETTINGS,
+ ETHNL_CMD_SET_SETTINGS,
+ ETHNL_CMD_GET_PARAMS,
+ ETHNL_CMD_SET_PARAMS,
+
+ __ETHNL_CMD_MAX,
+ ETHNL_CMD_MAX = (__ETHNL_CMD_MAX - 1)
+};
+
+/* device specification */
+
+enum {
+ ETHA_DEV_UNSPEC,
+ ETHA_DEV_INDEX, /* u32 */
+ ETHA_DEV_NAME, /* string */
+
+ __ETHA_DEV_MAX,
+ ETHA_DEV_MAX = (__ETHA_DEV_MAX - 1)
+};
+
+/* bit sets */
+
+enum {
+ ETHA_BIT_UNSPEC,
+ ETHA_BIT_INDEX, /* u32 */
+ ETHA_BIT_NAME, /* string */
+ ETHA_BIT_VALUE, /* flag */
+
+ __ETHA_BIT_MAX,
+ ETHA_BIT_MAX = (__ETHA_BIT_MAX - 1)
+};
+
+enum {
+ ETHA_BITS_UNSPEC,
+ ETHA_BITS_BIT,
+
+ __ETHA_BITS_MAX,
+ ETHA_BITS_MAX = (__ETHA_BITS_MAX - 1)
+};
+
+enum {
+ ETHA_BITSET_UNSPEC,
+ ETHA_BITSET_SIZE, /* u32 */
+ ETHA_BITSET_BITS, /* nest - ETHA_BITS_* */
+ ETHA_BITSET_VALUE, /* binary */
+ ETHA_BITSET_MASK, /* binary */
+
+ __ETHA_BITSET_MAX,
+ ETHA_BITSET_MAX = (__ETHA_BITSET_MAX - 1)
+};
+
+/* events */
+
+enum {
+ ETHA_NEWDEV_UNSPEC,
+ ETHA_NEWDEV_DEV, /* nest - ETHA_DEV_* */
+
+ __ETHA_NEWDEV_MAX,
+ ETHA_NEWDEV_MAX = (__ETHA_NEWDEV_MAX - 1)
+};
+
+enum {
+ ETHA_DELDEV_UNSPEC,
+ ETHA_DELDEV_DEV, /* nest - ETHA_DEV_* */
+
+ __ETHA_DELDEV_MAX,
+ ETHA_DELDEV_MAX = (__ETHA_DELDEV_MAX - 1)
+};
+
+enum {
+ ETHA_EVENT_UNSPEC,
+ ETHA_EVENT_NEWDEV, /* nest - ETHA_NEWDEV_* */
+ ETHA_EVENT_DELDEV, /* nest - ETHA_DELDEV_* */
+
+ __ETHA_EVENT_MAX,
+ ETHA_EVENT_MAX = (__ETHA_EVENT_MAX - 1)
+};
+
+/* string sets */
+
+enum {
+ ETHA_STRING_UNSPEC,
+ ETHA_STRING_INDEX, /* u32 */
+ ETHA_STRING_VALUE, /* string */
+
+ __ETHA_STRING_MAX,
+ ETHA_STRING_MAX = (__ETHA_STRING_MAX - 1)
+};
+
+enum {
+ ETHA_STRINGS_UNSPEC,
+ ETHA_STRINGS_STRING, /* nest - ETHA_STRINGS_* */
+
+ __ETHA_STRINGS_MAX,
+ ETHA_STRINGS_MAX = (__ETHA_STRINGS_MAX - 1)
+};
+
+enum {
+ ETHA_STRINGSET_UNSPEC,
+ ETHA_STRINGSET_ID, /* u32 */
+ ETHA_STRINGSET_COUNT, /* u32 */
+ ETHA_STRINGSET_STRINGS, /* nest - ETHA_STRINGS_* */
+
+ __ETHA_STRINGSET_MAX,
+ ETHA_STRINGSET_MAX = (__ETHA_STRINGSET_MAX - 1)
+};
+
+/* GET_STRINGSET / SET_STRINGSET */
+
+enum {
+ ETHA_STRSET_UNSPEC,
+ ETHA_STRSET_DEV, /* nest - ETHA_DEV_* */
+ ETHA_STRSET_STRINGSET, /* nest - ETHA_STRSET_* */
+
+ __ETHA_STRSET_MAX,
+ ETHA_STRSET_MAX = (__ETHA_STRSET_MAX - 1)
+};
+
+/* GET_DRVINFO / SET_DRVINFO */
+
+enum {
+ ETHA_DRVINFO_UNSPEC,
+ ETHA_DRVINFO_DEV, /* nest - ETHA_DEV_* */
+ ETHA_DRVINFO_DRIVER, /* string */
+ ETHA_DRVINFO_VERSION, /* string */
+ ETHA_DRVINFO_FWVERSION, /* string */
+ ETHA_DRVINFO_BUSINFO, /* string */
+ ETHA_DRVINFO_EROM_VER, /* string */
+ ETHA_DRVINFO_N_PRIV_FLAGS, /* u32 */
+ ETHA_DRVINFO_N_STATS, /* u32 */
+ ETHA_DRVINFO_TESTINFO_LEN, /* u32 */
+ ETHA_DRVINFO_EEDUMP_LEN, /* u32 */
+ ETHA_DRVINFO_REGDUMP_LEN, /* u32 */
+
+ __ETHA_DRVINFO_MAX,
+ ETHA_DRVINFO_MAX = (__ETHA_DRVINFO_MAX - 1)
+};
+
+/* GET_SETTINGS / SET_SETTINGS */
+
+enum {
+ ETHA_SETTINGS_UNSPEC,
+ ETHA_SETTINGS_DEV, /* nest - ETHA_DEV_* */
+ ETHA_SETTINGS_INFOMASK, /* u32 */
+ ETHA_SETTINGS_COMPACT, /* flag */
+ ETHA_SETTINGS_SPEED, /* u32 */
+ ETHA_SETTINGS_DUPLEX, /* u8 */
+ ETHA_SETTINGS_PORT, /* u8 */
+ ETHA_SETTINGS_PHYADDR, /* u8 */
+ ETHA_SETTINGS_AUTONEG, /* u8 */
+ ETHA_SETTINGS_MDIO_SUPPORT, /* bitfield32 */
+ ETHA_SETTINGS_TP_MDIX, /* u8 */
+ ETHA_SETTINGS_TP_MDIX_CTRL, /* u8 */
+ ETHA_SETTINGS_TRANSCEIVER, /* u8 */
+ ETHA_SETTINGS_WOL_MODES, /* bitfield32 */
+ ETHA_SETTINGS_SOPASS, /* binary */
+ ETHA_SETTINGS_MSGLVL, /* bitfield32 */
+ ETHA_SETTINGS_LINK_MODES, /* bitset */
+ ETHA_SETTINGS_PEER_MODES, /* bitset */
+ ETHA_SETTINGS_LINK, /* u32 */
+ ETHA_SETTINGS_FEATURES, /* nest - ETHA_FEATURES_* */
+
+ __ETHA_SETTINGS_MAX,
+ ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_MAX - 1)
+};
+
+#define ETH_SETTINGS_IM_LINKINFO 0x01
+#define ETH_SETTINGS_IM_LINKMODES 0x02
+#define ETH_SETTINGS_IM_MSGLEVEL 0x04
+#define ETH_SETTINGS_IM_WOLINFO 0x08
+#define ETH_SETTINGS_IM_LINK 0x10
+#define ETH_SETTINGS_IM_FEATURES 0x20
+
+#define ETH_SETTINGS_IM_DEFAULT 0x3f
+
+enum {
+ ETHA_FEATURES_UNSPEC,
+ ETHA_FEATURES_HW, /* bitset */
+ ETHA_FEATURES_WANTED, /* bitset */
+ ETHA_FEATURES_ACTIVE, /* bitset */
+ ETHA_FEATURES_NOCHANGE, /* bitset */
+ ETHA_FEATURES_WANT_DIFF, /* flag */
+
+ __ETHA_FEATURES_MAX,
+ ETHA_FEATURES_MAX = (__ETHA_FEATURES_MAX - 1)
+};
+
+/* GET_PARAMS / SET_PARAMS */
+
+enum {
+ ETHA_PARAMS_UNSPEC,
+ ETHA_PARAMS_DEV, /* nest - ETHA_DEV_* */
+ ETHA_PARAMS_INFOMASK, /* u32 */
+ ETHA_PARAMS_COMPACT, /* flag */
+ ETHA_PARAMS_COALESCE, /* nest - ETHA_COALESCE_* */
+ ETHA_PARAMS_RING, /* nest - ETHA_RING_* */
+ ETHA_PARAMS_PAUSE, /* nest - ETHA_PAUSE_* */
+ ETHA_PARAMS_CHANNELS, /* nest - ETHA_CHANNELS_* */
+ ETHA_PARAMS_EEE, /* nest - ETHA_EEE_* */
+ ETHA_PARAMS_FEC, /* nest - ETHA_FEC_* */
+
+ __ETHA_PARAMS_MAX,
+ ETHA_PARAMS_MAX = (__ETHA_PARAMS_MAX - 1)
+};
+
+#define ETH_PARAMS_IM_COALESCE 0x01
+#define ETH_PARAMS_IM_RING 0x02
+#define ETH_PARAMS_IM_PAUSE 0x04
+#define ETH_PARAMS_IM_CHANNELS 0x08
+#define ETH_PARAMS_IM_EEE 0x10
+#define ETH_PARAMS_IM_FEC 0x20
+
+#define ETH_PARAMS_IM_DEFAULT 0x3f
+
+enum {
+ ETHA_COALESCE_UNSPEC,
+ ETHA_COALESCE_RX_USECS, /* u32 */
+ ETHA_COALESCE_RX_MAXFRM, /* u32 */
+ ETHA_COALESCE_RX_USECS_IRQ, /* u32 */
+ ETHA_COALESCE_RX_MAXFRM_IRQ, /* u32 */
+ ETHA_COALESCE_RX_USECS_LOW, /* u32 */
+ ETHA_COALESCE_RX_MAXFRM_LOW, /* u32 */
+ ETHA_COALESCE_RX_USECS_HIGH, /* u32 */
+ ETHA_COALESCE_RX_MAXFRM_HIGH, /* u32 */
+ ETHA_COALESCE_TX_USECS, /* u32 */
+ ETHA_COALESCE_TX_MAXFRM, /* u32 */
+ ETHA_COALESCE_TX_USECS_IRQ, /* u32 */
+ ETHA_COALESCE_TX_MAXFRM_IRQ, /* u32 */
+ ETHA_COALESCE_TX_USECS_LOW, /* u32 */
+ ETHA_COALESCE_TX_MAXFRM_LOW, /* u32 */
+ ETHA_COALESCE_TX_USECS_HIGH, /* u32 */
+ ETHA_COALESCE_TX_MAXFRM_HIGH, /* u32 */
+ ETHA_COALESCE_PKT_RATE_LOW, /* u32 */
+ ETHA_COALESCE_PKT_RATE_HIGH, /* u32 */
+ ETHA_COALESCE_RX_USE_ADAPTIVE, /* u8 */
+ ETHA_COALESCE_TX_USE_ADAPTIVE, /* u8 */
+ ETHA_COALESCE_RATE_SAMPLE_INTERVAL, /* u32 */
+ ETHA_COALESCE_STATS_BLOCK_USECS, /* u32 */
+
+ __ETHA_COALESCE_MAX,
+ ETHA_COALESCE_MAX = (__ETHA_COALESCE_MAX - 1)
+};
+
+enum {
+ ETHA_RING_UNSPEC,
+ ETHA_RING_RX_MAX_PENDING, /* u32 */
+ ETHA_RING_RX_MINI_MAX_PENDING, /* u32 */
+ ETHA_RING_RX_JUMBO_MAX_PENDING, /* u32 */
+ ETHA_RING_TX_MAX_PENDING, /* u32 */
+ ETHA_RING_RX_PENDING, /* u32 */
+ ETHA_RING_RX_MINI_PENDING, /* u32 */
+ ETHA_RING_RX_JUMBO_PENDING, /* u32 */
+ ETHA_RING_TX_PENDING, /* u32 */
+
+ __ETHA_RING_MAX,
+ ETHA_RING_MAX = (__ETHA_RING_MAX - 1)
+};
+
+enum {
+ ETHA_PAUSE_UNSPEC,
+ ETHA_PAUSE_AUTONEG, /* u8 */
+ ETHA_PAUSE_RX, /* u8 */
+ ETHA_PAUSE_TX, /* u8 */
+
+ __ETHA_PAUSE_MAX,
+ ETHA_PAUSE_MAX = (__ETHA_PAUSE_MAX - 1)
+};
+
+enum {
+ ETHA_CHANNELS_UNSPEC,
+ ETHA_CHANNELS_MAX_RX, /* u32 */
+ ETHA_CHANNELS_MAX_TX, /* u32 */
+ ETHA_CHANNELS_MAX_OTHER, /* u32 */
+ ETHA_CHANNELS_MAX_COMBINED, /* u32 */
+ ETHA_CHANNELS_RX_COUNT, /* u32 */
+ ETHA_CHANNELS_TX_COUNT, /* u32 */
+ ETHA_CHANNELS_OTHER_COUNT, /* u32 */
+ ETHA_CHANNELS_COMBINED_COUNT, /* u32 */
+
+ __ETHA_CHANNELS_MAX,
+ ETHA_CHANNELS_MAX = (__ETHA_CHANNELS_MAX - 1)
+};
+
+enum {
+ ETHA_EEE_UNSPEC,
+ ETHA_EEE_LINK_MODES, /* bitset */
+ ETHA_EEE_PEER_MODES, /* bitset */
+ ETHA_EEE_ACTIVE, /* u8 */
+ ETHA_EEE_ENABLED, /* u8 */
+ ETHA_EEE_TX_LPI_ENABLED, /* u8 */
+ ETHA_EEE_TX_LPI_TIMER, /* u32 */
+
+ __ETHA_EEE_MAX,
+ ETHA_EEE_MAX = (__ETHA_EEE_MAX - 1)
+};
+
+enum {
+ ETHA_FEC_UNSPEC,
+ ETHA_FEC_MODES, /* bitfield32 */
+
+ __ETHA_FEC_MAX,
+ ETHA_FEC_MAX = (__ETHA_FEC_MAX - 1)
+};
+
+/* generic netlink info */
+#define ETHTOOL_GENL_NAME "ethtool"
+#define ETHTOOL_GENL_VERSION 1
+
+#define ETHTOOL_MCGRP_MONITOR_NAME "monitor"
+
+#endif /* _LINUX_ETHTOOL_NETLINK_H_ */
diff --git a/uapi/linux/genetlink.h b/uapi/linux/genetlink.h
new file mode 100644
index 000000000000..1317119cbff8
--- /dev/null
+++ b/uapi/linux/genetlink.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_GENERIC_NETLINK_H
+#define __LINUX_GENERIC_NETLINK_H
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+#define GENL_NAMSIZ 16 /* length of family name */
+
+#define GENL_MIN_ID NLMSG_MIN_TYPE
+#define GENL_MAX_ID 1023
+
+struct genlmsghdr {
+ __u8 cmd;
+ __u8 version;
+ __u16 reserved;
+};
+
+#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr))
+
+#define GENL_ADMIN_PERM 0x01
+#define GENL_CMD_CAP_DO 0x02
+#define GENL_CMD_CAP_DUMP 0x04
+#define GENL_CMD_CAP_HASPOL 0x08
+#define GENL_UNS_ADMIN_PERM 0x10
+
+/*
+ * List of reserved static generic netlink identifiers:
+ */
+#define GENL_ID_CTRL NLMSG_MIN_TYPE
+#define GENL_ID_VFS_DQUOT (NLMSG_MIN_TYPE + 1)
+#define GENL_ID_PMCRAID (NLMSG_MIN_TYPE + 2)
+/* must be last reserved + 1 */
+#define GENL_START_ALLOC (NLMSG_MIN_TYPE + 3)
+
+/**************************************************************************
+ * Controller
+ **************************************************************************/
+
+enum {
+ CTRL_CMD_UNSPEC,
+ CTRL_CMD_NEWFAMILY,
+ CTRL_CMD_DELFAMILY,
+ CTRL_CMD_GETFAMILY,
+ CTRL_CMD_NEWOPS,
+ CTRL_CMD_DELOPS,
+ CTRL_CMD_GETOPS,
+ CTRL_CMD_NEWMCAST_GRP,
+ CTRL_CMD_DELMCAST_GRP,
+ CTRL_CMD_GETMCAST_GRP, /* unused */
+ __CTRL_CMD_MAX,
+};
+
+#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1)
+
+enum {
+ CTRL_ATTR_UNSPEC,
+ CTRL_ATTR_FAMILY_ID,
+ CTRL_ATTR_FAMILY_NAME,
+ CTRL_ATTR_VERSION,
+ CTRL_ATTR_HDRSIZE,
+ CTRL_ATTR_MAXATTR,
+ CTRL_ATTR_OPS,
+ CTRL_ATTR_MCAST_GROUPS,
+ __CTRL_ATTR_MAX,
+};
+
+#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1)
+
+enum {
+ CTRL_ATTR_OP_UNSPEC,
+ CTRL_ATTR_OP_ID,
+ CTRL_ATTR_OP_FLAGS,
+ __CTRL_ATTR_OP_MAX,
+};
+
+#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1)
+
+enum {
+ CTRL_ATTR_MCAST_GRP_UNSPEC,
+ CTRL_ATTR_MCAST_GRP_NAME,
+ CTRL_ATTR_MCAST_GRP_ID,
+ __CTRL_ATTR_MCAST_GRP_MAX,
+};
+
+#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
+
+
+#endif /* __LINUX_GENERIC_NETLINK_H */
diff --git a/uapi/linux/netlink.h b/uapi/linux/netlink.h
new file mode 100644
index 000000000000..0b2c29bd081f
--- /dev/null
+++ b/uapi/linux/netlink.h
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LINUX_NETLINK_H
+#define __LINUX_NETLINK_H
+
+#include <linux/kernel.h>
+#include <linux/socket.h> /* for __kernel_sa_family_t */
+#include <linux/types.h>
+
+#define NETLINK_ROUTE 0 /* Routing/device hook */
+#define NETLINK_UNUSED 1 /* Unused number */
+#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
+#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
+#define NETLINK_SOCK_DIAG 4 /* socket monitoring */
+#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
+#define NETLINK_XFRM 6 /* ipsec */
+#define NETLINK_SELINUX 7 /* SELinux event notifications */
+#define NETLINK_ISCSI 8 /* Open-iSCSI */
+#define NETLINK_AUDIT 9 /* auditing */
+#define NETLINK_FIB_LOOKUP 10
+#define NETLINK_CONNECTOR 11
+#define NETLINK_NETFILTER 12 /* netfilter subsystem */
+#define NETLINK_IP6_FW 13
+#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
+#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
+#define NETLINK_GENERIC 16
+/* leave room for NETLINK_DM (DM Events) */
+#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
+#define NETLINK_ECRYPTFS 19
+#define NETLINK_RDMA 20
+#define NETLINK_CRYPTO 21 /* Crypto layer */
+#define NETLINK_SMC 22 /* SMC monitoring */
+
+#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG
+
+#define MAX_LINKS 32
+
+struct sockaddr_nl {
+ __kernel_sa_family_t nl_family; /* AF_NETLINK */
+ unsigned short nl_pad; /* zero */
+ __u32 nl_pid; /* port ID */
+ __u32 nl_groups; /* multicast groups mask */
+};
+
+struct nlmsghdr {
+ __u32 nlmsg_len; /* Length of message including header */
+ __u16 nlmsg_type; /* Message content */
+ __u16 nlmsg_flags; /* Additional flags */
+ __u32 nlmsg_seq; /* Sequence number */
+ __u32 nlmsg_pid; /* Sending process port ID */
+};
+
+/* Flags values */
+
+#define NLM_F_REQUEST 0x01 /* It is request message. */
+#define NLM_F_MULTI 0x02 /* Multipart message, terminated by NLMSG_DONE */
+#define NLM_F_ACK 0x04 /* Reply with ack, with zero or error code */
+#define NLM_F_ECHO 0x08 /* Echo this request */
+#define NLM_F_DUMP_INTR 0x10 /* Dump was inconsistent due to sequence change */
+#define NLM_F_DUMP_FILTERED 0x20 /* Dump was filtered as requested */
+
+/* Modifiers to GET request */
+#define NLM_F_ROOT 0x100 /* specify tree root */
+#define NLM_F_MATCH 0x200 /* return all matching */
+#define NLM_F_ATOMIC 0x400 /* atomic GET */
+#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
+
+/* Modifiers to NEW request */
+#define NLM_F_REPLACE 0x100 /* Override existing */
+#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */
+#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
+#define NLM_F_APPEND 0x800 /* Add to end of list */
+
+/* Modifiers to DELETE request */
+#define NLM_F_NONREC 0x100 /* Do not delete recursively */
+
+/* Flags for ACK message */
+#define NLM_F_CAPPED 0x100 /* request was capped */
+#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */
+
+/*
+ 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL
+ 4.4BSD CHANGE NLM_F_REPLACE
+
+ True CHANGE NLM_F_CREATE|NLM_F_REPLACE
+ Append NLM_F_CREATE
+ Check NLM_F_EXCL
+ */
+
+#define NLMSG_ALIGNTO 4U
+#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
+#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
+#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+ (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
+ (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+ (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define NLMSG_NOOP 0x1 /* Nothing. */
+#define NLMSG_ERROR 0x2 /* Error */
+#define NLMSG_DONE 0x3 /* End of a dump */
+#define NLMSG_OVERRUN 0x4 /* Data lost */
+
+#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */
+
+struct nlmsgerr {
+ int error;
+ struct nlmsghdr msg;
+ /*
+ * followed by the message contents unless NETLINK_CAP_ACK was set
+ * or the ACK indicates success (error == 0)
+ * message length is aligned with NLMSG_ALIGN()
+ */
+ /*
+ * followed by TLVs defined in enum nlmsgerr_attrs
+ * if NETLINK_EXT_ACK was set
+ */
+};
+
+/**
+ * enum nlmsgerr_attrs - nlmsgerr attributes
+ * @NLMSGERR_ATTR_UNUSED: unused
+ * @NLMSGERR_ATTR_MSG: error message string (string)
+ * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original
+ * message, counting from the beginning of the header (u32)
+ * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to
+ * be used - in the success case - to identify a created
+ * object or operation or similar (binary)
+ * @__NLMSGERR_ATTR_MAX: number of attributes
+ * @NLMSGERR_ATTR_MAX: highest attribute number
+ */
+enum nlmsgerr_attrs {
+ NLMSGERR_ATTR_UNUSED,
+ NLMSGERR_ATTR_MSG,
+ NLMSGERR_ATTR_OFFS,
+ NLMSGERR_ATTR_COOKIE,
+
+ __NLMSGERR_ATTR_MAX,
+ NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
+};
+
+#define NETLINK_ADD_MEMBERSHIP 1
+#define NETLINK_DROP_MEMBERSHIP 2
+#define NETLINK_PKTINFO 3
+#define NETLINK_BROADCAST_ERROR 4
+#define NETLINK_NO_ENOBUFS 5
+#define NETLINK_RX_RING 6
+#define NETLINK_TX_RING 7
+#define NETLINK_LISTEN_ALL_NSID 8
+#define NETLINK_LIST_MEMBERSHIPS 9
+#define NETLINK_CAP_ACK 10
+#define NETLINK_EXT_ACK 11
+
+struct nl_pktinfo {
+ __u32 group;
+};
+
+struct nl_mmap_req {
+ unsigned int nm_block_size;
+ unsigned int nm_block_nr;
+ unsigned int nm_frame_size;
+ unsigned int nm_frame_nr;
+};
+
+struct nl_mmap_hdr {
+ unsigned int nm_status;
+ unsigned int nm_len;
+ __u32 nm_group;
+ /* credentials */
+ __u32 nm_pid;
+ __u32 nm_uid;
+ __u32 nm_gid;
+};
+
+enum nl_mmap_status {
+ NL_MMAP_STATUS_UNUSED,
+ NL_MMAP_STATUS_RESERVED,
+ NL_MMAP_STATUS_VALID,
+ NL_MMAP_STATUS_COPY,
+ NL_MMAP_STATUS_SKIP,
+};
+
+#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO
+#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT)
+#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr))
+
+#define NET_MAJOR 36 /* Major 36 is reserved for networking */
+
+enum {
+ NETLINK_UNCONNECTED = 0,
+ NETLINK_CONNECTED,
+};
+
+/*
+ * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * | Header | Pad | Payload | Pad |
+ * | (struct nlattr) | ing | | ing |
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * <-------------- nlattr->nla_len -------------->
+ */
+
+struct nlattr {
+ __u16 nla_len;
+ __u16 nla_type;
+};
+
+/*
+ * nla_type (16 bits)
+ * +---+---+-------------------------------+
+ * | N | O | Attribute Type |
+ * +---+---+-------------------------------+
+ * N := Carries nested attributes
+ * O := Payload stored in network byte order
+ *
+ * Note: The N and O flag are mutually exclusive.
+ */
+#define NLA_F_NESTED (1 << 15)
+#define NLA_F_NET_BYTEORDER (1 << 14)
+#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
+
+#define NLA_ALIGNTO 4
+#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
+#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
+
+/* Generic 32 bitflags attribute content sent to the kernel.
+ *
+ * The value is a bitmap that defines the values being set
+ * The selector is a bitmask that defines which value is legit
+ *
+ * Examples:
+ * value = 0x0, and selector = 0x1
+ * implies we are selecting bit 1 and we want to set its value to 0.
+ *
+ * value = 0x2, and selector = 0x2
+ * implies we are selecting bit 2 and we want to set its value to 1.
+ *
+ */
+struct nla_bitfield32 {
+ __u32 value;
+ __u32 selector;
+};
+
+#endif /* __LINUX_NETLINK_H */
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 05/23] netlink: add notification monitor
From: Michal Kubecek @ 2018-07-30 12:56 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
With 'ethtool --monitor [ --all | opt ] [dev]', let ethtool listen to
netlink notification and display them in a format similar to output of
related "get" commands. With --all, show all types of notifications. If
device name is specified, show only notifications for that device, if no
device name or "*" is passed, show notifications for all devices.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
Makefile.am | 2 +-
ethtool.c | 17 ++++
netlink/extapi.h | 4 +
netlink/monitor.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++
netlink/netlink.c | 32 ++++++-
netlink/netlink.h | 26 ++++++
6 files changed, 297 insertions(+), 2 deletions(-)
create mode 100644 netlink/monitor.c
diff --git a/Makefile.am b/Makefile.am
index 6a4d7083919e..a412734fffd1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -22,7 +22,7 @@ endif
if ETHTOOL_ENABLE_NETLINK
ethtool_SOURCES += \
netlink/netlink.c netlink/netlink.h netlink/extapi.h \
- netlink/strset.c netlink/strset.h \
+ netlink/strset.c netlink/strset.h netlink/monitor.c \
uapi/linux/ethtool_netlink.h \
uapi/linux/netlink.h uapi/linux/genetlink.h
ethtool_CFLAGS += @MNL_CFLAGS@
diff --git a/ethtool.c b/ethtool.c
index e8044a6af76c..b7f40d2f3826 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5294,6 +5294,9 @@ static int show_usage(struct cmd_context *ctx)
if (args[i].opthelp)
fputs(args[i].opthelp, stdout);
}
+#ifdef ETHTOOL_ENABLE_NETLINK
+ monitor_usage();
+#endif
return 0;
}
@@ -5335,6 +5338,20 @@ int main(int argc, char **argp)
argp++;
argc--;
+#ifdef ETHTOOL_ENABLE_NETLINK
+ if (*argp && !strcmp(*argp, "--monitor")) {
+ if (netlink_init(&ctx)) {
+ fprintf(stderr,
+ "Option --monitor is only available with netlink.\n");
+ return 1;
+ } else {
+ ctx.argp = ++argp;
+ ctx.argc = --argc;
+ return nl_monitor(&ctx);
+ }
+ }
+#endif
+
/* First argument must be either a valid option or a device
* name to get settings for (which we don't expect to begin
* with '-').
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 05ab083d9b9c..827e3732888a 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -13,4 +13,8 @@ struct nl_context;
int netlink_init(struct cmd_context *ctx);
int netlink_done(struct cmd_context *ctx);
+int nl_monitor(struct cmd_context *ctx);
+
+void monitor_usage();
+
#endif /* ETHTOOL_EXTAPI_H__ */
diff --git a/netlink/monitor.c b/netlink/monitor.c
new file mode 100644
index 000000000000..d7ed562356f1
--- /dev/null
+++ b/netlink/monitor.c
@@ -0,0 +1,218 @@
+#include <errno.h>
+
+#include "../internal.h"
+#include "netlink.h"
+#include "strset.h"
+
+static void monitor_newdev(struct nl_context *nlctx, struct nlattr *evattr)
+{
+ const struct nlattr *tb[ETHA_NEWDEV_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ const char *devname;
+ int ret;
+
+ ret = mnl_attr_parse_nested(evattr, attr_cb, &tb_info);
+ if (ret < 0)
+ return;
+ if (!tb[ETHA_NEWDEV_DEV])
+ return;
+ devname = get_dev_name(tb[ETHA_NEWDEV_DEV]);
+ if (!devname)
+ return;
+ printf("New device %s registered.\n", devname);
+
+ ret = init_aux_nlctx(nlctx);
+ if (ret < 0)
+ return;
+ load_perdev_strings(nlctx->aux_nlctx, devname);
+}
+
+static void monitor_deldev(struct nl_context *nlctx, struct nlattr *evattr)
+{
+ const struct nlattr *tb[ETHA_DELDEV_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ const char *devname;
+ int ret;
+
+ ret = mnl_attr_parse_nested(evattr, attr_cb, &tb_info);
+ if (ret < 0)
+ return;
+ if (!tb[ETHA_DELDEV_DEV])
+ return;
+ devname = get_dev_name(tb[ETHA_DELDEV_DEV]);
+ if (!devname)
+ return;
+ printf("Device %s unregistered.\n", devname);
+
+ free_perdev_strings(devname);
+}
+
+static int monitor_event_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ struct nl_context *nlctx = data;
+ struct nlattr *evattr;
+
+ mnl_attr_for_each(evattr, nlhdr, GENL_HDRLEN) {
+ switch(mnl_attr_get_type(evattr)) {
+ case ETHA_EVENT_NEWDEV:
+ monitor_newdev(nlctx, evattr);
+ break;
+ case ETHA_EVENT_DELDEV:
+ monitor_deldev(nlctx, evattr);
+ break;
+ }
+ }
+
+ return MNL_CB_OK;
+}
+
+static struct {
+ uint8_t cmd;
+ mnl_cb_t cb;
+} monitor_callbacks[] = {
+ {
+ .cmd = ETHNL_CMD_EVENT,
+ .cb = monitor_event_cb,
+ },
+};
+
+static int monitor_any_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
+ struct nl_context *nlctx = data;
+ unsigned i;
+
+ if (nlctx->filter_cmd && ghdr->cmd != nlctx->filter_cmd)
+ return MNL_CB_OK;
+
+ for (i = 0; i < MNL_ARRAY_SIZE(monitor_callbacks); i++)
+ if (monitor_callbacks[i].cmd == ghdr->cmd)
+ return monitor_callbacks[i].cb(nlhdr, data);
+
+ return MNL_CB_OK;
+}
+
+struct monitor_option {
+ const char *pattern;
+ uint8_t cmd;
+ uint32_t info_mask;
+};
+
+static struct monitor_option monitor_opts[] = {
+ {
+ .pattern = "--all",
+ .cmd = 0,
+ },
+};
+
+static bool pattern_match(const char *s, const char *pattern)
+{
+ const char *opt = pattern;
+ const char *next;
+ int slen = strlen(s);
+ int optlen;
+
+ do {
+ next = opt;
+ while (*next && *next != '|')
+ next++;
+ optlen = next - opt;
+ if (slen == optlen && !strncmp(s, opt, optlen))
+ return true;
+
+ opt = next;
+ if (*opt == '|')
+ opt++;
+ } while (*opt);
+
+ return false;
+}
+
+static int parse_monitor(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ char **argp = ctx->argp;
+ int argc = ctx->argc;
+ const char *opt = "";
+ unsigned int i;
+
+ if (*argp && argp[0][0] == '-') {
+ opt = *argp;
+ argp++;
+ argc--;
+ }
+ for (i = 0; i < MNL_ARRAY_SIZE(monitor_opts); i++) {
+ if (pattern_match(opt, monitor_opts[i].pattern)) {
+ nlctx->filter_cmd = monitor_opts[i].cmd;
+ nlctx->filter_mask = monitor_opts[i].info_mask;
+ goto opt_found;
+ }
+ }
+ fprintf(stderr, "monitoring for option '%s' not supported\n", *argp);
+ return -1;
+
+opt_found:
+ if (*argp && strcmp(*argp, WILDCARD_DEVNAME))
+ ctx->devname = *argp;
+ return 0;
+}
+
+int nl_monitor(struct cmd_context *ctx)
+{
+ bool is_dev;
+ struct nl_context *nlctx = ctx->nlctx;
+ uint32_t grpid = nlctx->mon_mcgrp_id;
+ int ret;
+
+ if (!grpid) {
+ fprintf(stderr, "multicast group 'monitor' not found\n");
+ return -EOPNOTSUPP;
+ }
+ if (parse_monitor(ctx) < 0)
+ return 1;
+ is_dev = ctx->devname && strcmp(ctx->devname, WILDCARD_DEVNAME);
+
+ ret = load_global_strings(nlctx);
+ if (ret < 0)
+ return ret;
+ ret = mnl_socket_setsockopt(nlctx->sk, NETLINK_ADD_MEMBERSHIP,
+ &grpid, sizeof(grpid));
+ if (ret < 0)
+ return ret;
+ ret = load_perdev_strings(nlctx, is_dev ? ctx->devname : NULL);
+ if (ret < 0)
+ return ret;
+
+ nlctx->filter_devname = ctx->devname;
+ nlctx->is_monitor = true;
+ nlctx->port = 0;
+ nlctx->seq = 0;
+
+ fputs("listening...\n", stdout);
+ fflush(stdout);
+ ret = ethnl_process_reply(nlctx, monitor_any_cb);
+ free_perdev_strings(NULL);
+ return ret;
+}
+
+void monitor_usage()
+{
+ const char *p;
+ unsigned i;
+
+ fputs(" ethtool --monitor Show kernel notifications\n",
+ stdout);
+ for (i = 0; i < MNL_ARRAY_SIZE(monitor_opts); i++) {
+ if (i > 0)
+ fputs("\n | ", stdout);
+ else
+ fputs(" ( ", stdout);
+ for (p = monitor_opts[i].pattern; *p; p++)
+ if (*p == '|')
+ fputs(" | ", stdout);
+ else
+ fputc(*p, stdout);
+ }
+ fputs(" )\n", stdout);
+ fputs(" [ DEVNAME | * ]\n", stdout);
+}
diff --git a/netlink/netlink.c b/netlink/netlink.c
index 0671c4589c72..69f692d2cf04 100644
--- a/netlink/netlink.c
+++ b/netlink/netlink.c
@@ -391,6 +391,31 @@ err:
/* get ethtool family id */
+static void ethnl_find_monitor_group(struct nl_context *nlctx,
+ struct nlattr *nest)
+{
+ const struct nlattr *grp_tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(grp_tb);
+ struct nlattr *grp_attr;
+ int ret;
+
+ nlctx->mon_mcgrp_id = 0;
+ mnl_attr_for_each_nested(grp_attr, nest) {
+ ret = mnl_attr_parse_nested(grp_attr, attr_cb, &grp_tb_info);
+ if (ret < 0)
+ return;
+ if (!grp_tb[CTRL_ATTR_MCAST_GRP_NAME] ||
+ !grp_tb[CTRL_ATTR_MCAST_GRP_ID])
+ continue;
+ if (strcmp(mnl_attr_get_str(grp_tb[CTRL_ATTR_MCAST_GRP_NAME]),
+ ETHTOOL_MCGRP_MONITOR_NAME))
+ continue;
+ nlctx->mon_mcgrp_id =
+ mnl_attr_get_u32(grp_tb[CTRL_ATTR_MCAST_GRP_ID]);
+ return;
+ }
+}
+
static int ethnl_family_cb(const struct nlmsghdr *nlhdr, void *data)
{
struct nl_context *nlctx = data;
@@ -398,9 +423,13 @@ static int ethnl_family_cb(const struct nlmsghdr *nlhdr, void *data)
nlctx->ethnl_fam = 0;
mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) {
- if (mnl_attr_get_type(attr) == CTRL_ATTR_FAMILY_ID) {
+ switch(mnl_attr_get_type(attr)) {
+ case CTRL_ATTR_FAMILY_ID:
nlctx->ethnl_fam = mnl_attr_get_u16(attr);
break;
+ case CTRL_ATTR_MCAST_GROUPS:
+ ethnl_find_monitor_group(nlctx, attr);
+ break;
}
}
@@ -478,6 +507,7 @@ int __init_aux_nlctx(struct nl_context *nlctx)
}
aux->ethnl_fam = nlctx->ethnl_fam;
+ aux->mon_mcgrp_id = nlctx->mon_mcgrp_id;
nlctx->aux_nlctx = aux;
return 0;
diff --git a/netlink/netlink.h b/netlink/netlink.h
index 547c865ed535..15bf9f3873b0 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -17,6 +17,7 @@
struct nl_context {
int ethnl_fam;
+ uint32_t mon_mcgrp_id;
struct mnl_socket *sk;
struct nl_context *aux_nlctx;
void *cmd_private;
@@ -30,6 +31,10 @@ struct nl_context {
const char *devname;
bool is_dump;
int exit_code;
+ bool is_monitor;
+ uint8_t filter_cmd;
+ uint32_t filter_mask;
+ const char *filter_devname;
};
struct attr_tb_info {
@@ -144,6 +149,27 @@ static inline void show_u32_yn(const struct nlattr **tb, unsigned int idx,
mnl_attr_get_u32(tb[idx]) ? "yes" : "no");
}
+/* reply content filtering */
+
+static inline bool mask_ok(const struct nl_context *nlctx, uint32_t bits)
+{
+ return !nlctx->filter_mask || (nlctx->filter_mask & bits);
+}
+
+static inline bool dev_ok(const struct nl_context *nlctx)
+{
+ return !nlctx->filter_devname ||
+ (nlctx->devname &&
+ !strcmp(nlctx->devname, nlctx->filter_devname));
+}
+
+static inline bool show_only(const struct nl_context *nlctx, uint32_t bits)
+{
+ if (nlctx->is_monitor || !nlctx->filter_mask)
+ return false;
+ return nlctx->filter_mask & ~bits;
+}
+
/* misc */
static inline int init_aux_nlctx(struct nl_context *nlctx)
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 06/23] netlink: add netlink handler for gdrv (-i)
From: Michal Kubecek @ 2018-07-30 12:56 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "-i" subcommand using netlink interface command
ETHNL_CMD_GET_DRVINFO.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
Makefile.am | 1 +
ethtool.c | 3 ++-
netlink/drvinfo.c | 43 +++++++++++++++++++++++++++++++++++++++++++
netlink/extapi.h | 1 +
netlink/monitor.c | 10 ++++++++++
5 files changed, 57 insertions(+), 1 deletion(-)
create mode 100644 netlink/drvinfo.c
diff --git a/Makefile.am b/Makefile.am
index a412734fffd1..7e24fe8c50f4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,6 +23,7 @@ if ETHTOOL_ENABLE_NETLINK
ethtool_SOURCES += \
netlink/netlink.c netlink/netlink.h netlink/extapi.h \
netlink/strset.c netlink/strset.h netlink/monitor.c \
+ netlink/drvinfo.c \
uapi/linux/ethtool_netlink.h \
uapi/linux/netlink.h uapi/linux/genetlink.h
ethtool_CFLAGS += @MNL_CFLAGS@
diff --git a/ethtool.c b/ethtool.c
index b7f40d2f3826..0edb80b19b9d 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5056,6 +5056,7 @@ static int show_usage(struct cmd_context *ctx);
/* Just define all netlink handlers as null when building without netlink
* support so that we do not get unresolved symbols in args array below
*/
+#define nl_gdrv NULL
#endif
static const struct option {
@@ -5125,7 +5126,7 @@ static const struct option {
{ "-K|--features|--offload", 1, do_sfeatures, NULL,
"Set protocol offload and other features",
" FEATURE on|off ...\n" },
- { "-i|--driver", 1, do_gdrv, NULL,
+ { "-i|--driver", 1, do_gdrv, nl_gdrv,
"Show driver information" },
{ "-d|--register-dump", 1, do_gregs, NULL,
"Do a register dump",
diff --git a/netlink/drvinfo.c b/netlink/drvinfo.c
new file mode 100644
index 000000000000..78b4394788a5
--- /dev/null
+++ b/netlink/drvinfo.c
@@ -0,0 +1,43 @@
+#include "../internal.h"
+#include "netlink.h"
+
+int drvinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHA_DRVINFO_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ int ret;
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ nlctx->devname = get_dev_name(tb[ETHA_DRVINFO_DEV]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+ if (nlctx->is_dump || nlctx->is_monitor)
+ fprintf(stdout, "\nDriver info for %s:\n", nlctx->devname);
+
+ show_string(tb, ETHA_DRVINFO_DRIVER, "driver");
+ show_string(tb, ETHA_DRVINFO_VERSION, "version");
+ show_string(tb, ETHA_DRVINFO_FWVERSION, "firmware-version");
+ show_string(tb, ETHA_DRVINFO_EROM_VER, "expansion-rom-version");
+ show_string(tb, ETHA_DRVINFO_BUSINFO, "bus-info");
+ show_u32_yn(tb, ETHA_DRVINFO_N_STATS, "supports-statistics");
+ show_u32_yn(tb, ETHA_DRVINFO_TESTINFO_LEN, "supports-test");
+ show_u32_yn(tb, ETHA_DRVINFO_EEDUMP_LEN, "supports-eeprom-access");
+ show_u32_yn(tb, ETHA_DRVINFO_REGDUMP_LEN, "supports-register-dump");
+ show_u32_yn(tb, ETHA_DRVINFO_N_PRIV_FLAGS, "supports-priv-flags");
+
+ return MNL_CB_OK;
+}
+
+int nl_gdrv(struct cmd_context *ctx)
+{
+ int ret;
+
+ ret = ethnl_prep_get_request(ctx, ETHNL_CMD_GET_DRVINFO,
+ ETHA_DRVINFO_DEV);
+ if (ret < 0)
+ return ret;
+ return ethnl_send_get_request(ctx->nlctx, drvinfo_reply_cb);
+}
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 827e3732888a..546090a02a0d 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -13,6 +13,7 @@ struct nl_context;
int netlink_init(struct cmd_context *ctx);
int netlink_done(struct cmd_context *ctx);
+int nl_gdrv(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/monitor.c b/netlink/monitor.c
index d7ed562356f1..ae7d6080e03d 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -66,6 +66,8 @@ static int monitor_event_cb(const struct nlmsghdr *nlhdr, void *data)
return MNL_CB_OK;
}
+int drvinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+
static struct {
uint8_t cmd;
mnl_cb_t cb;
@@ -74,6 +76,10 @@ static struct {
.cmd = ETHNL_CMD_EVENT,
.cb = monitor_event_cb,
},
+ {
+ .cmd = ETHNL_CMD_SET_DRVINFO,
+ .cb = drvinfo_reply_cb,
+ },
};
static int monitor_any_cb(const struct nlmsghdr *nlhdr, void *data)
@@ -103,6 +109,10 @@ static struct monitor_option monitor_opts[] = {
.pattern = "--all",
.cmd = 0,
},
+ {
+ .pattern = "-i|--driver",
+ .cmd = ETHNL_CMD_SET_DRVINFO,
+ },
};
static bool pattern_match(const char *s, const char *pattern)
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 07/23] netlink: add netlink handler for gset (no option)
From: Michal Kubecek @ 2018-07-30 12:56 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool <dev>" subcommand using netlink interface command
ETHNL_CMD_GET_SETTINGS.
Move some output helpers used by both ioctl() and netlink from ethtool.c
into new file common.c.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
Makefile.am | 4 +-
common.c | 285 ++++++++++++++++++++++++++++++++++++++++++
common.h | 60 +++++++++
ethtool.c | 151 ++---------------------
netlink/extapi.h | 1 +
netlink/monitor.c | 14 +++
netlink/netlink.c | 120 ++++++++++++++++++
netlink/netlink.h | 4 +
netlink/settings.c | 301 +++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 796 insertions(+), 144 deletions(-)
create mode 100644 common.c
create mode 100644 common.h
create mode 100644 netlink/settings.c
diff --git a/Makefile.am b/Makefile.am
index 7e24fe8c50f4..629f04e1202b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = LICENSE ethtool.8 ethtool.spec.in aclocal.m4 ChangeLog autogen.sh
sbin_PROGRAMS = ethtool
ethtool_SOURCES = ethtool.c uapi/linux/ethtool.h internal.h \
- uapi/linux/net_tstamp.h rxclass.c
+ uapi/linux/net_tstamp.h rxclass.c common.c common.h
ethtool_CFLAGS = -I./uapi -Wall
ethtool_LDADD = -lm
if ETHTOOL_ENABLE_PRETTY_DUMP
@@ -23,7 +23,7 @@ if ETHTOOL_ENABLE_NETLINK
ethtool_SOURCES += \
netlink/netlink.c netlink/netlink.h netlink/extapi.h \
netlink/strset.c netlink/strset.h netlink/monitor.c \
- netlink/drvinfo.c \
+ netlink/drvinfo.c netlink/settings.c \
uapi/linux/ethtool_netlink.h \
uapi/linux/netlink.h uapi/linux/genetlink.h
ethtool_CFLAGS += @MNL_CFLAGS@
diff --git a/common.c b/common.c
new file mode 100644
index 000000000000..59897acd2512
--- /dev/null
+++ b/common.c
@@ -0,0 +1,285 @@
+#include "internal.h"
+#include "common.h"
+
+#ifndef HAVE_NETIF_MSG
+enum {
+ NETIF_MSG_DRV = 0x0001,
+ NETIF_MSG_PROBE = 0x0002,
+ NETIF_MSG_LINK = 0x0004,
+ NETIF_MSG_TIMER = 0x0008,
+ NETIF_MSG_IFDOWN = 0x0010,
+ NETIF_MSG_IFUP = 0x0020,
+ NETIF_MSG_RX_ERR = 0x0040,
+ NETIF_MSG_TX_ERR = 0x0080,
+ NETIF_MSG_TX_QUEUED = 0x0100,
+ NETIF_MSG_INTR = 0x0200,
+ NETIF_MSG_TX_DONE = 0x0400,
+ NETIF_MSG_RX_STATUS = 0x0800,
+ NETIF_MSG_PKTDATA = 0x1000,
+ NETIF_MSG_HW = 0x2000,
+ NETIF_MSG_WOL = 0x4000,
+};
+#endif
+
+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 },
+ {}
+};
+const unsigned int n_flags_msglvl = ARRAY_SIZE(flags_msglvl) - 1;
+
+const char *names_duplex[] = {
+ [DUPLEX_HALF] = "Half",
+ [DUPLEX_FULL] = "Full",
+};
+DEFINE_ENUM_COUNT(duplex);
+
+const char *names_port[] = {
+ [PORT_TP] = "Twisted Pair",
+ [PORT_AUI] = "AUI",
+ [PORT_BNC] = "BNC",
+ [PORT_MII] = "MII",
+ [PORT_FIBRE] = "FIBRE",
+ [PORT_DA] = "Direct Attach Copper",
+ [PORT_NONE] = "None",
+ [PORT_OTHER] = "Other",
+};
+DEFINE_ENUM_COUNT(port);
+
+const char *names_transceiver[] = {
+ [XCVR_INTERNAL] = "internal",
+ [XCVR_EXTERNAL] = "external",
+};
+DEFINE_ENUM_COUNT(transceiver);
+
+/* the practice of putting completely unrelated flags into link mode bitmaps
+ * is rather unfortunate but as even ethtool_link_ksettings preserved that,
+ * there is little chance of getting them separated any time soon so let's
+ * sort them out ourselves
+ */
+const struct link_mode_info link_modes[] = {
+ [ETHTOOL_LINK_MODE_10baseT_Half_BIT] =
+ { LM_CLASS_REAL, 10, DUPLEX_HALF },
+ [ETHTOOL_LINK_MODE_10baseT_Full_BIT] =
+ { LM_CLASS_REAL, 10, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_100baseT_Half_BIT] =
+ { LM_CLASS_REAL, 100, DUPLEX_HALF },
+ [ETHTOOL_LINK_MODE_100baseT_Full_BIT] =
+ { LM_CLASS_REAL, 100, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] =
+ { LM_CLASS_REAL, 1000, DUPLEX_HALF },
+ [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] =
+ { LM_CLASS_REAL, 1000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_Autoneg_BIT] =
+ { LM_CLASS_AUTONEG },
+ [ETHTOOL_LINK_MODE_TP_BIT] =
+ { LM_CLASS_PORT },
+ [ETHTOOL_LINK_MODE_AUI_BIT] =
+ { LM_CLASS_PORT },
+ [ETHTOOL_LINK_MODE_MII_BIT] =
+ { LM_CLASS_PORT },
+ [ETHTOOL_LINK_MODE_FIBRE_BIT] =
+ { LM_CLASS_PORT },
+ [ETHTOOL_LINK_MODE_BNC_BIT] =
+ { LM_CLASS_PORT },
+ [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] =
+ { LM_CLASS_REAL, 10000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_Pause_BIT] =
+ { LM_CLASS_PAUSE },
+ [ETHTOOL_LINK_MODE_Asym_Pause_BIT] =
+ { LM_CLASS_PAUSE },
+ [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] =
+ { LM_CLASS_REAL, 2500, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_Backplane_BIT] =
+ { LM_CLASS_PORT },
+ [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] =
+ { LM_CLASS_REAL, 1000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] =
+ { LM_CLASS_REAL, 10000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] =
+ { LM_CLASS_REAL, 10000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] =
+ { LM_CLASS_REAL, 10000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] =
+ { LM_CLASS_REAL, 20000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] =
+ { LM_CLASS_REAL, 20000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] =
+ { LM_CLASS_REAL, 40000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] =
+ { LM_CLASS_REAL, 40000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] =
+ { LM_CLASS_REAL, 40000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] =
+ { LM_CLASS_REAL, 40000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] =
+ { LM_CLASS_REAL, 56000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] =
+ { LM_CLASS_REAL, 56000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] =
+ { LM_CLASS_REAL, 56000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] =
+ { LM_CLASS_REAL, 56000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] =
+ { LM_CLASS_REAL, 25000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] =
+ { LM_CLASS_REAL, 25000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] =
+ { LM_CLASS_REAL, 25000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] =
+ { LM_CLASS_REAL, 50000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] =
+ { LM_CLASS_REAL, 50000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] =
+ { LM_CLASS_REAL, 100000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] =
+ { LM_CLASS_REAL, 100000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] =
+ { LM_CLASS_REAL, 100000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] =
+ { LM_CLASS_REAL, 100000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] =
+ { LM_CLASS_REAL, 50000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] =
+ { LM_CLASS_REAL, 1000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] =
+ { LM_CLASS_REAL, 10000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] =
+ { LM_CLASS_REAL, 10000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] =
+ { LM_CLASS_REAL, 10000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] =
+ { LM_CLASS_REAL, 10000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] =
+ { LM_CLASS_REAL, 10000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] =
+ { LM_CLASS_REAL, 2500, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] =
+ { LM_CLASS_REAL, 5000, DUPLEX_FULL },
+ [ETHTOOL_LINK_MODE_FEC_NONE_BIT] =
+ { LM_CLASS_FEC },
+ [ETHTOOL_LINK_MODE_FEC_RS_BIT] =
+ { LM_CLASS_FEC },
+ [ETHTOOL_LINK_MODE_FEC_BASER_BIT] =
+ { LM_CLASS_FEC },
+};
+const unsigned int link_modes_count = ARRAY_SIZE(link_modes);
+
+void print_flags(const struct flag_info *info, unsigned int n_info, u32 value)
+{
+ const char *sep = "";
+
+ while (n_info) {
+ if (value & info->value) {
+ printf("%s%s", sep, info->name);
+ sep = " ";
+ value &= ~info->value;
+ }
+ ++info;
+ --n_info;
+ }
+
+ /* Print any unrecognised flags in hex */
+ if (value)
+ printf("%s%#x", sep, value);
+}
+
+void __print_enum(const char *const *info, unsigned int n_info, unsigned int val,
+ const char *label, const char *unknown)
+{
+ if (val >= n_info || !info[val]) {
+ printf("%s", label);
+ printf(unknown, val);
+ fputc('\n', stdout);
+ } else {
+ printf("%s%s\n", label, info[val]);
+ }
+}
+
+static char *unparse_wolopts(int wolopts)
+{
+ static char buf[16];
+ char *p = buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (wolopts) {
+ if (wolopts & WAKE_PHY)
+ *p++ = 'p';
+ if (wolopts & WAKE_UCAST)
+ *p++ = 'u';
+ if (wolopts & WAKE_MCAST)
+ *p++ = 'm';
+ if (wolopts & WAKE_BCAST)
+ *p++ = 'b';
+ if (wolopts & WAKE_ARP)
+ *p++ = 'a';
+ if (wolopts & WAKE_MAGIC)
+ *p++ = 'g';
+ if (wolopts & WAKE_MAGICSECURE)
+ *p++ = 's';
+ } else {
+ *p = 'd';
+ }
+
+ return buf;
+}
+
+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;
+}
+
+void dump_mdix(u8 mdix, u8 mdix_ctrl)
+{
+ fprintf(stdout, " MDI-X: ");
+ if (mdix_ctrl == ETH_TP_MDI) {
+ fprintf(stdout, "off (forced)\n");
+ } else if (mdix_ctrl == ETH_TP_MDI_X) {
+ fprintf(stdout, "on (forced)\n");
+ } else {
+ switch (mdix) {
+ case ETH_TP_MDI:
+ fprintf(stdout, "off");
+ break;
+ case ETH_TP_MDI_X:
+ fprintf(stdout, "on");
+ break;
+ default:
+ fprintf(stdout, "Unknown");
+ break;
+ }
+ if (mdix_ctrl == ETH_TP_MDI_AUTO)
+ fprintf(stdout, " (auto)");
+ fprintf(stdout, "\n");
+ }
+}
diff --git a/common.h b/common.h
new file mode 100644
index 000000000000..a980b844a0ed
--- /dev/null
+++ b/common.h
@@ -0,0 +1,60 @@
+#ifndef ETHTOOL_COMMON_H__
+#define ETHTOOL_COMMON_H__
+
+struct flag_info {
+ const char *name;
+ u32 value;
+};
+
+extern const struct flag_info flags_msglvl[];
+extern const unsigned int n_flags_msglvl;
+
+
+enum link_mode_class {
+ LM_CLASS_UNKNOWN,
+ LM_CLASS_REAL,
+ LM_CLASS_AUTONEG,
+ LM_CLASS_PORT,
+ LM_CLASS_PAUSE,
+ LM_CLASS_FEC,
+};
+
+struct link_mode_info {
+ enum link_mode_class class;
+ u32 speed;
+ u8 duplex;
+};
+
+extern const struct link_mode_info link_modes[];
+extern const unsigned int link_modes_count;
+
+static inline bool lm_class_match(unsigned int mode, enum link_mode_class class)
+{
+ unsigned int mode_class = (mode < link_modes_count) ?
+ link_modes[mode].class : LM_CLASS_UNKNOWN;
+
+ if (mode_class == class)
+ return true;
+ return (mode_class == class) ||
+ ((class == LM_CLASS_REAL) && (mode_class = LM_CLASS_UNKNOWN));
+}
+
+#define DECLARE_ENUM_NAMES(obj) \
+ extern const char *names_ ## obj[]; \
+ extern const unsigned int names_ ## obj ## _count
+#define DEFINE_ENUM_COUNT(obj) \
+ const unsigned int names_ ## obj ## _count = ARRAY_SIZE(names_ ## obj)
+#define print_enum(array, val, label, unknown) \
+ __print_enum(array, array ## _count, val, label, unknown)
+
+DECLARE_ENUM_NAMES(duplex);
+DECLARE_ENUM_NAMES(port);
+DECLARE_ENUM_NAMES(transceiver);
+
+void print_flags(const struct flag_info *info, unsigned int n_info, u32 value);
+void __print_enum(const char *const *info, unsigned int n_info,
+ unsigned int val, const char *label, const char *unknown);
+int dump_wol(struct ethtool_wolinfo *wol);
+void dump_mdix(u8 mdix, u8 mdix_ctrl);
+
+#endif /* ETHTOOL_COMMON_H__ */
diff --git a/ethtool.c b/ethtool.c
index 0edb80b19b9d..bc7969f829f6 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -30,6 +30,7 @@
*/
#include "internal.h"
+#include "common.h"
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
@@ -56,26 +57,6 @@
#define MAX_ADDR_LEN 32
#endif
-#ifndef HAVE_NETIF_MSG
-enum {
- NETIF_MSG_DRV = 0x0001,
- NETIF_MSG_PROBE = 0x0002,
- NETIF_MSG_LINK = 0x0004,
- NETIF_MSG_TIMER = 0x0008,
- NETIF_MSG_IFDOWN = 0x0010,
- NETIF_MSG_IFUP = 0x0020,
- NETIF_MSG_RX_ERR = 0x0040,
- NETIF_MSG_TX_ERR = 0x0080,
- NETIF_MSG_TX_QUEUED = 0x0100,
- NETIF_MSG_INTR = 0x0200,
- NETIF_MSG_TX_DONE = 0x0400,
- NETIF_MSG_RX_STATUS = 0x0800,
- NETIF_MSG_PKTDATA = 0x1000,
- NETIF_MSG_HW = 0x2000,
- NETIF_MSG_WOL = 0x4000,
-};
-#endif
-
#ifndef NETLINK_GENERIC
#define NETLINK_GENERIC 16
#endif
@@ -123,29 +104,6 @@ struct cmdline_info {
void *seen_val;
};
-struct flag_info {
- const char *name;
- u32 value;
-};
-
-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 },
-};
-
struct off_flag_def {
const char *short_name;
const char *long_name;
@@ -428,26 +386,6 @@ static void flag_to_cmdline_info(const char *name, u32 value,
cli->seen_val = mask;
}
-static void
-print_flags(const struct flag_info *info, unsigned int n_info, u32 value)
-{
- const char *sep = "";
-
- while (n_info) {
- if (value & info->value) {
- printf("%s%s", sep, info->name);
- sep = " ";
- value &= ~info->value;
- }
- ++info;
- --n_info;
- }
-
- /* Print any unrecognised flags in hex */
- if (value)
- printf("%s%#x", sep, value);
-}
-
static int rxflow_str_to_type(const char *str)
{
int flow_type = 0;
@@ -853,31 +791,9 @@ dump_link_usettings(const struct ethtool_link_usettings *link_usettings)
(link_usettings->base.autoneg == AUTONEG_DISABLE) ?
"off" : "on");
- if (link_usettings->base.port == PORT_TP) {
- fprintf(stdout, " MDI-X: ");
- if (link_usettings->base.eth_tp_mdix_ctrl == ETH_TP_MDI) {
- fprintf(stdout, "off (forced)\n");
- } else if (link_usettings->base.eth_tp_mdix_ctrl
- == ETH_TP_MDI_X) {
- fprintf(stdout, "on (forced)\n");
- } else {
- switch (link_usettings->base.eth_tp_mdix) {
- case ETH_TP_MDI:
- fprintf(stdout, "off");
- break;
- case ETH_TP_MDI_X:
- fprintf(stdout, "on");
- break;
- default:
- fprintf(stdout, "Unknown");
- break;
- }
- if (link_usettings->base.eth_tp_mdix_ctrl
- == ETH_TP_MDI_AUTO)
- fprintf(stdout, " (auto)");
- fprintf(stdout, "\n");
- }
- }
+ if (link_usettings->base.port == PORT_TP)
+ dump_mdix(link_usettings->base.eth_tp_mdix,
+ link_usettings->base.eth_tp_mdix_ctrl);
return 0;
}
@@ -946,56 +862,6 @@ static int parse_wolopts(char *optstr, u32 *data)
return 0;
}
-static char *unparse_wolopts(int wolopts)
-{
- static char buf[16];
- char *p = buf;
-
- memset(buf, 0, sizeof(buf));
-
- if (wolopts) {
- if (wolopts & WAKE_PHY)
- *p++ = 'p';
- if (wolopts & WAKE_UCAST)
- *p++ = 'u';
- if (wolopts & WAKE_MCAST)
- *p++ = 'm';
- if (wolopts & WAKE_BCAST)
- *p++ = 'b';
- if (wolopts & WAKE_ARP)
- *p++ = 'a';
- if (wolopts & WAKE_MAGIC)
- *p++ = 'g';
- if (wolopts & WAKE_MAGICSECURE)
- *p++ = 's';
- } else {
- *p = 'd';
- }
-
- return buf;
-}
-
-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_rxfhashopts(char *optstr, u32 *data)
{
*data = 0;
@@ -2738,8 +2604,7 @@ static int do_gset(struct cmd_context *ctx)
fprintf(stdout, " Current message level: 0x%08x (%d)\n"
" ",
edata.data, edata.data);
- print_flags(flags_msglvl, ARRAY_SIZE(flags_msglvl),
- edata.data);
+ print_flags(flags_msglvl, n_flags_msglvl, edata.data);
fprintf(stdout, "\n");
allfail = 0;
} else if (errno != EOPNOTSUPP) {
@@ -2785,13 +2650,13 @@ static int do_sset(struct cmd_context *ctx)
int msglvl_changed = 0;
u32 msglvl_wanted = 0;
u32 msglvl_mask = 0;
- struct cmdline_info cmdline_msglvl[ARRAY_SIZE(flags_msglvl)];
+ struct cmdline_info cmdline_msglvl[n_flags_msglvl];
int argc = ctx->argc;
char **argp = ctx->argp;
int i;
int err = 0;
- for (i = 0; i < ARRAY_SIZE(flags_msglvl); i++)
+ for (i = 0; i < n_flags_msglvl; i++)
flag_to_cmdline_info(flags_msglvl[i].name,
flags_msglvl[i].value,
&msglvl_wanted, &msglvl_mask,
@@ -5057,6 +4922,7 @@ static int show_usage(struct cmd_context *ctx);
* support so that we do not get unresolved symbols in args array below
*/
#define nl_gdrv NULL
+#define nl_gset NULL
#endif
static const struct option {
@@ -5381,6 +5247,7 @@ int main(int argc, char **argp)
}
if ((*argp)[0] == '-')
exit_bad_args();
+ nl_func = nl_gset;
func = do_gset;
want_device = 1;
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 546090a02a0d..20c9b03b2d3c 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -14,6 +14,7 @@ int netlink_init(struct cmd_context *ctx);
int netlink_done(struct cmd_context *ctx);
int nl_gdrv(struct cmd_context *ctx);
+int nl_gset(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/monitor.c b/netlink/monitor.c
index ae7d6080e03d..32d842611011 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -67,6 +67,7 @@ static int monitor_event_cb(const struct nlmsghdr *nlhdr, void *data)
}
int drvinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int settings_reply_cb(const struct nlmsghdr *nlhdr, void *data);
static struct {
uint8_t cmd;
@@ -80,6 +81,10 @@ static struct {
.cmd = ETHNL_CMD_SET_DRVINFO,
.cb = drvinfo_reply_cb,
},
+ {
+ .cmd = ETHNL_CMD_SET_SETTINGS,
+ .cb = settings_reply_cb,
+ },
};
static int monitor_any_cb(const struct nlmsghdr *nlhdr, void *data)
@@ -113,6 +118,15 @@ static struct monitor_option monitor_opts[] = {
.pattern = "-i|--driver",
.cmd = ETHNL_CMD_SET_DRVINFO,
},
+ {
+ .pattern = "|-s|--change",
+ .cmd = ETHNL_CMD_SET_SETTINGS,
+ .info_mask = ETH_SETTINGS_IM_LINKINFO |
+ ETH_SETTINGS_IM_LINKMODES |
+ ETH_SETTINGS_IM_MSGLEVEL |
+ ETH_SETTINGS_IM_WOLINFO |
+ ETH_SETTINGS_IM_LINK,
+ },
};
static bool pattern_match(const char *s, const char *pattern)
diff --git a/netlink/netlink.c b/netlink/netlink.c
index 69f692d2cf04..fc87577191f0 100644
--- a/netlink/netlink.c
+++ b/netlink/netlink.c
@@ -4,8 +4,10 @@
#include <errno.h>
#include "../internal.h"
+#include "../common.h"
#include "netlink.h"
#include "extapi.h"
+#include "strset.h"
/* misc helpers */
@@ -352,6 +354,124 @@ const char *get_dev_name(const struct nlattr *nest)
return mnl_attr_get_str(dev_tb[ETHA_DEV_NAME]);
}
+int dump_link_modes(const struct nlattr *bitset, bool mask, unsigned class,
+ const char *before, const char *between, const char *after,
+ const char *if_none)
+{
+ const struct nlattr *bitset_tb[ETHA_BITSET_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(bitset_tb);
+ const unsigned int before_len = strlen(before);
+ const struct nlattr *bits;
+ const struct nlattr *bit;
+ bool first = true;
+ int prev = -2;
+ int ret;
+
+ ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
+ bits = bitset_tb[ETHA_BITSET_BITS];
+ if (ret < 0)
+ goto err_nonl;
+ if (!bits) {
+ const struct stringset *lm_strings =
+ global_stringset(ETH_SS_LINK_MODES);
+ unsigned int count;
+ unsigned int idx;
+ const char *name;
+
+ bits = mask ? bitset_tb[ETHA_BITSET_MASK] :
+ bitset_tb[ETHA_BITSET_VALUE];
+ ret = -EFAULT;
+ if (!bits || !bitset_tb[ETHA_BITSET_SIZE])
+ goto err_nonl;
+ count = mnl_attr_get_u32(bitset_tb[ETHA_BITSET_SIZE]);
+ if (mnl_attr_get_payload_len(bits) / 4 < (count + 31) / 32)
+ goto err_nonl;
+
+ printf("\t%s", before);
+ for (idx = 0; idx < count; idx++) {
+ const uint32_t *raw_data = mnl_attr_get_payload(bits);
+ char buff[10];
+
+ if (!(raw_data[idx / 32] & (1U << (idx % 32))))
+ continue;
+ if (!lm_class_match(idx, class))
+ continue;
+ name = get_string(lm_strings, idx);
+ if (!name) {
+ snprintf(buff, sizeof(buff), "b%d", idx);
+ name = buff;
+ }
+ if (first)
+ first = false;
+ /* ugly hack to preserve old output format */
+ if ((class == LM_CLASS_REAL) && (prev == idx - 1) &&
+ (prev < link_modes_count) &&
+ (link_modes[prev].class == LM_CLASS_REAL) &&
+ (link_modes[prev].duplex == DUPLEX_HALF))
+ putchar(' ');
+ else if (between)
+ printf("\t%s", between);
+ else
+ printf("\n\t%*s", before_len, "");
+ printf("%s", name);
+ prev = idx;
+ }
+ goto after;
+ }
+
+ printf("\t%s", before);
+ mnl_attr_for_each_nested(bit, bits) {
+ const struct nlattr *tb[ETHA_BIT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ unsigned int idx;
+ const char *name;
+
+ if (mnl_attr_get_type(bit) != ETHA_BITS_BIT)
+ continue;
+ ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info);
+ if (ret < 0)
+ goto err;
+ ret = -EFAULT;
+ if (!tb[ETHA_BIT_INDEX] || !tb[ETHA_BIT_NAME])
+ goto err;
+ if (!mask && !tb[ETHA_BIT_VALUE])
+ continue;
+
+ idx = mnl_attr_get_u32(tb[ETHA_BIT_INDEX]);
+ name = mnl_attr_get_str(tb[ETHA_BIT_NAME]);
+ if (!lm_class_match(idx, class))
+ continue;
+ if (first) {
+ first = false;
+ } else {
+ /* ugly hack to preserve old output format */
+ if ((class == LM_CLASS_REAL) && (prev == idx - 1) &&
+ (prev < link_modes_count) &&
+ (link_modes[prev].class == LM_CLASS_REAL) &&
+ (link_modes[prev].duplex == DUPLEX_HALF))
+ putchar(' ');
+ else if (between)
+ printf("\t%s", between);
+ else
+ printf("\n\t%*s", before_len, "");
+ }
+ printf("%s", name);
+ prev = idx;
+ }
+after:
+ if (first && if_none)
+ printf("%s", if_none);
+ printf(after);
+
+ return 0;
+err:
+ putchar('\n');
+err_nonl:
+ fflush(stdout);
+ fprintf(stderr, "malformed netlink message (link_modes)\n");
+ return ret;
+}
+
/* request helpers */
int ethnl_prep_get_request(struct cmd_context *ctx, unsigned int nlcmd,
diff --git a/netlink/netlink.h b/netlink/netlink.h
index 15bf9f3873b0..a572a165718e 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -58,6 +58,10 @@ bool bitset_get_bit(const struct nlattr *bitset, bool mask, unsigned int idx,
int *retptr);
bool bitset_is_empty(const struct nlattr *bitset, bool mask, int *retptr);
+int dump_link_modes(const struct nlattr *bitset, bool mask, unsigned class,
+ const char *before, const char *between, const char *after,
+ const char *if_none);
+
int msg_init(struct nl_context *nlctx, int cmd, unsigned int flags);
int ethnl_process_reply(struct nl_context *nlctx, mnl_cb_t reply_cb);
int attr_cb(const struct nlattr *attr, void *data);
diff --git a/netlink/settings.c b/netlink/settings.c
new file mode 100644
index 000000000000..120fc95aad19
--- /dev/null
+++ b/netlink/settings.c
@@ -0,0 +1,301 @@
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+
+/* GET_SETTINGS */
+
+static int dump_pause(const struct nlattr *attr, bool mask, const char *label)
+{
+ bool pause, asym;
+ int ret = 0;
+
+ pause = bitset_get_bit(attr, mask, ETHTOOL_LINK_MODE_Pause_BIT, &ret);
+ if (ret < 0)
+ goto err;
+ asym = bitset_get_bit(attr, mask, ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ &ret);
+ if (ret < 0)
+ goto err;
+
+ printf("\t%s", label);
+ if (pause)
+ printf("%s\n", asym ? "Symmetric Receive-only" : "Symmetric");
+ else
+ printf("%s\n", asym ? "Transmit-only" : "No");
+
+ return 0;
+err:
+ fprintf(stderr, "malformed netlink message (pause modes)\n");
+ return ret;
+}
+
+int settings_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHA_SETTINGS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool allfail = true;
+ bool first = true;
+ int port = -1;
+ int ret;
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ nlctx->devname = get_dev_name(tb[ETHA_SETTINGS_DEV]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ if (tb[ETHA_SETTINGS_LINK_MODES] &&
+ mask_ok(nlctx, ETH_SETTINGS_IM_LINKMODES)) {
+ const struct nlattr *attr = tb[ETHA_SETTINGS_LINK_MODES];
+ bool autoneg;
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ ret = dump_link_modes(attr, true, LM_CLASS_PORT,
+ "Supported ports: [ ", " ", " ]\n", NULL);
+ if (ret < 0)
+ return ret;
+ ret = dump_link_modes(attr, true, LM_CLASS_REAL,
+ "Supported link modes: ", NULL, "\n",
+ "Not reported");
+ if (ret < 0)
+ return ret;
+ ret = dump_pause(attr, true, "Supported pause frame use: ");
+ if (ret < 0)
+ return ret;
+ autoneg = bitset_get_bit(attr, true,
+ ETHTOOL_LINK_MODE_Autoneg_BIT, &ret);
+ if (ret < 0)
+ return ret;
+ printf("\tSupports auto-negotiation: %s\n",
+ autoneg ? "Yes" : "No");
+ ret = dump_link_modes(attr, true, LM_CLASS_FEC,
+ "Supported FEC modes: ", " ", "\n",
+ "No");
+ if (ret < 0)
+ return ret;
+
+ ret = dump_link_modes(attr, false, LM_CLASS_REAL,
+ "Advertised link modes: ", NULL, "\n",
+ "Not reported");
+ if (ret < 0)
+ return ret;
+ ret = dump_pause(attr, false, "Advertised pause frame use: ");
+ if (ret < 0)
+ return ret;
+ autoneg = bitset_get_bit(attr, false,
+ ETHTOOL_LINK_MODE_Autoneg_BIT, &ret);
+ if (ret < 0)
+ return ret;
+ printf("\tAdvertised auto-negotiation: %s\n",
+ autoneg ? "Yes" : "No");
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ ret = dump_link_modes(attr, true, LM_CLASS_FEC,
+ "Advertised FEC modes: ", " ", "\n",
+ "No");
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[ETHA_SETTINGS_PEER_MODES] &&
+ mask_ok(nlctx, ETH_SETTINGS_IM_LINKMODES)) {
+ const struct nlattr *attr = tb[ETHA_SETTINGS_PEER_MODES];
+ bool autoneg;
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ ret = dump_link_modes(attr, false, LM_CLASS_REAL,
+ "Link partner advertised link modes: ",
+ NULL, "\n", "Not reported");
+ if (ret < 0)
+ return ret;
+ ret = dump_pause(attr, false,
+ "Link partner advertised pause frame use: ");
+ if (ret < 0)
+ return ret;
+ autoneg = bitset_get_bit(attr, false,
+ ETHTOOL_LINK_MODE_Autoneg_BIT, &ret);
+ if (ret < 0)
+ return ret;
+ printf("\tLink partner advertised auto-negotiation: %s\n",
+ autoneg ? "Yes" : "No");
+ ret = dump_link_modes(attr, true, LM_CLASS_FEC,
+ "Link partner advertised FEC modes: ",
+ " ", "\n", "No");
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[ETHA_SETTINGS_SPEED] &&
+ mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) {
+ u32 val = mnl_attr_get_u32(tb[ETHA_SETTINGS_SPEED]);
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ if (val == 0 || val == (u16)(-1) || val == (u32)(-1))
+ printf("\tSpeed: Unknown!\n");
+ else
+ printf("\tSpeed: %uMb/s\n", val);
+ }
+ if (tb[ETHA_SETTINGS_DUPLEX] &&
+ mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) {
+ u8 val = mnl_attr_get_u8(tb[ETHA_SETTINGS_DUPLEX]);
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ print_enum(names_duplex, val, "\tDuplex: ", "Unknown! (%i)");
+ }
+ if (tb[ETHA_SETTINGS_PORT] &&
+ mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) {
+ u8 val = mnl_attr_get_u8(tb[ETHA_SETTINGS_PORT]);
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ print_enum(names_port, val, "\tPort: ", "Unknown! (%i)\n");
+ port = val;
+ }
+ if (tb[ETHA_SETTINGS_PHYADDR] &&
+ mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) {
+ u8 val = mnl_attr_get_u8(tb[ETHA_SETTINGS_PHYADDR]);
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ printf("\tPHYAD: %u\n", val);
+ }
+ if (tb[ETHA_SETTINGS_TRANSCEIVER] &&
+ mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) {
+ u8 val = mnl_attr_get_u8(tb[ETHA_SETTINGS_TRANSCEIVER]);
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ print_enum(names_transceiver, val, "\tTransceiver: ",
+ "Unknown!");
+ }
+ if (tb[ETHA_SETTINGS_AUTONEG] &&
+ mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) {
+ u8 val = mnl_attr_get_u8(tb[ETHA_SETTINGS_AUTONEG]);
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ printf("\tAuto-negotiation: %s\n",
+ (val == AUTONEG_DISABLE) ? "off" : "on");
+ }
+ if (tb[ETHA_SETTINGS_TP_MDIX] && tb[ETHA_SETTINGS_TP_MDIX] &&
+ port == PORT_TP && mask_ok(nlctx, ETH_SETTINGS_IM_LINKINFO)) {
+ u8 mdix = mnl_attr_get_u8(tb[ETHA_SETTINGS_TP_MDIX]);
+ u8 mdix_ctrl = mnl_attr_get_u8(tb[ETHA_SETTINGS_TP_MDIX_CTRL]);
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ dump_mdix(mdix, mdix_ctrl);
+ }
+ if (tb[ETHA_SETTINGS_WOL_MODES] &&
+ mask_ok(nlctx, ETH_SETTINGS_IM_WOLINFO)) {
+ struct ethtool_wolinfo wolinfo = {};
+ const struct nla_bitfield32 *wol_bf =
+ mnl_attr_get_payload(tb[ETHA_SETTINGS_WOL_MODES]);
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ wolinfo.wolopts = wol_bf->value;
+ wolinfo.supported = wol_bf->selector;
+ if (tb[ETHA_SETTINGS_SOPASS])
+ nl_copy_payload(wolinfo.sopass, SOPASS_MAX,
+ tb[ETHA_SETTINGS_SOPASS]);
+ ret = dump_wol(&wolinfo);
+ if (ret)
+ return ret;
+ allfail = false;
+ }
+ if (tb[ETHA_SETTINGS_MSGLVL] &&
+ mask_ok(nlctx, ETH_SETTINGS_IM_MSGLEVEL)) {
+ struct nla_bitfield32 *val =
+ mnl_attr_get_payload(tb[ETHA_SETTINGS_MSGLVL]);
+ u32 msglvl = val->value;
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ printf(" Current message level: 0x%08x (%d)\n"
+ " ",
+ msglvl, msglvl);
+ print_flags(flags_msglvl, n_flags_msglvl, msglvl);
+ fputc('\n', stdout);
+ allfail = false;
+ }
+ if (tb[ETHA_SETTINGS_LINK] && mask_ok(nlctx, ETH_SETTINGS_IM_LINK)) {
+ u32 link = mnl_attr_get_u32(tb[ETHA_SETTINGS_LINK]);
+
+ if (first) {
+ printf("Settings for %s:\n", nlctx->devname);
+ first = false;
+ }
+ printf("\tLink detected: %s\n", link ? "yes" : "no");
+ allfail = false;
+ };
+
+ if (allfail && !nlctx->is_monitor) {
+ fputs("No data available\n", stdout);
+ nlctx->exit_code = 75;
+ return MNL_CB_ERROR;
+ }
+ return MNL_CB_OK;
+}
+
+int settings_request(struct cmd_context *ctx, uint32_t info_mask)
+{
+ bool compact = info_mask & ETH_SETTINGS_IM_FEATURES;
+ struct nl_context *nlctx = ctx->nlctx;
+ int ret;
+
+ if (compact)
+ load_global_strings(nlctx);
+
+ ret = ethnl_prep_get_request(ctx, ETHNL_CMD_GET_SETTINGS,
+ ETHA_SETTINGS_DEV);
+ if (ret < 0)
+ return ret;
+ if (ethnla_put_u32(nlctx, ETHA_SETTINGS_INFOMASK, info_mask) ||
+ ethnla_put_flag(nlctx, ETHA_SETTINGS_COMPACT, compact))
+ return -EMSGSIZE;
+ return ethnl_send_get_request(nlctx, settings_reply_cb);
+}
+
+int nl_gset(struct cmd_context *ctx)
+{
+ return settings_request(ctx, ETH_SETTINGS_IM_LINKINFO |
+ ETH_SETTINGS_IM_LINKMODES |
+ ETH_SETTINGS_IM_MSGLEVEL |
+ ETH_SETTINGS_IM_WOLINFO |
+ ETH_SETTINGS_IM_LINK);
+}
+
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 08/23] netlink: add helpers for command line parsing
From: Michal Kubecek @ 2018-07-30 12:56 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Support for easier parsing of subcommand parameters and their values from
command line. Existing parser helpers in ethtool.c are closely tied to
ioctl() interface and using them would be often inconvenient.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
Makefile.am | 1 +
netlink/netlink.h | 4 +
netlink/parser.c | 527 ++++++++++++++++++++++++++++++++++++++++++++++
netlink/parser.h | 45 ++++
4 files changed, 577 insertions(+)
create mode 100644 netlink/parser.c
create mode 100644 netlink/parser.h
diff --git a/Makefile.am b/Makefile.am
index 629f04e1202b..7cc4adc53c59 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,6 +23,7 @@ if ETHTOOL_ENABLE_NETLINK
ethtool_SOURCES += \
netlink/netlink.c netlink/netlink.h netlink/extapi.h \
netlink/strset.c netlink/strset.h netlink/monitor.c \
+ netlink/parser.c netlink/parser.h \
netlink/drvinfo.c netlink/settings.c \
uapi/linux/ethtool_netlink.h \
uapi/linux/netlink.h uapi/linux/genetlink.h
diff --git a/netlink/netlink.h b/netlink/netlink.h
index a572a165718e..32c2f9f33ba1 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -30,6 +30,10 @@ struct nl_context {
void *msg;
const char *devname;
bool is_dump;
+ const char *cmd;
+ const char *param;
+ char **argp;
+ int argc;
int exit_code;
bool is_monitor;
uint8_t filter_cmd;
diff --git a/netlink/parser.c b/netlink/parser.c
new file mode 100644
index 000000000000..589004d1f9f9
--- /dev/null
+++ b/netlink/parser.c
@@ -0,0 +1,527 @@
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+static void parser_err_unknown_param(struct nl_context *nlctx)
+{
+ fprintf(stderr, "ethtool (%s): unknown parameter '%s'\n", nlctx->cmd,
+ nlctx->param);
+}
+
+static void parser_err_dup_param(struct nl_context *nlctx)
+{
+ fprintf(stderr, "ethtool (%s): duplicate parameter '%s'\n", nlctx->cmd,
+ nlctx->param);
+}
+
+static void parser_err_min_argc(struct nl_context *nlctx, unsigned int min_argc)
+{
+ if (min_argc == 1)
+ fprintf(stderr, "ethtool (%s): no value for parameter '%s'\n",
+ nlctx->cmd, nlctx->param);
+ else
+ fprintf(stderr,
+ "ethtool (%s): parameter '%s' requires %u words\n",
+ nlctx->cmd, nlctx->param, min_argc);
+}
+
+static void parser_err_invalid_value(struct nl_context *nlctx, const char *val)
+{
+ fprintf(stderr, "ethtool (%s): invalid value '%s' for parameter '%s'\n",
+ nlctx->cmd, val, nlctx->param);
+}
+
+static void parser_err_invalid_flag(struct nl_context *nlctx, const char *flag)
+{
+ fprintf(stderr, "ethtool (%s): flag '%s' for parameter '%s' is not "
+ "followed by 'on' or 'off'\n",
+ nlctx->cmd, flag, nlctx->param);
+}
+
+static int __parse_u32(const char *arg, u32 *result, u32 min, u32 max, int base)
+{
+ unsigned long long val;
+ char *endptr;
+
+ if (!arg || !arg[0])
+ return -EINVAL;
+ val = strtoul(arg, &endptr, base);
+ if (*endptr || val < min || val > max)
+ return -EINVAL;
+
+ *result = (u32)val;
+ return 0;
+}
+
+static int parse_u32d(const char *arg, u32 *result)
+{
+ return __parse_u32(arg, result, 0, 0xffffffff, 10);
+}
+
+static int parse_x32(const char *arg, u32 *result)
+{
+ return __parse_u32(arg, result, 0, 0xffffffff, 16);
+}
+
+static int parse_u32(const char *arg, u32 *result)
+{
+ if (!arg)
+ return -EINVAL;
+ if ((arg[0] == '0') && (arg[1] == 'x' || arg[1] == 'X'))
+ return parse_x32(arg + 2, result);
+ else
+ return parse_u32d(arg, result);
+}
+
+static int parse_u8(const char *arg, u8 *result)
+{
+ u32 val;
+ int ret = parse_u32(arg, &val);
+
+ if (ret < 0)
+ return ret;
+ if (val > UINT8_MAX)
+ return -EINVAL;
+
+ *result = (u8)val;
+ return 0;
+}
+
+static int lookup_u8(const char *arg, u8 *result,
+ const struct lookup_entry_u8 *tbl)
+{
+ if (!arg)
+ return -EINVAL;
+ while (tbl->arg) {
+ if (!strcmp(tbl->arg, arg)) {
+ *result = tbl->val;
+ return 0;
+ }
+ tbl++;
+ }
+
+ return -EINVAL;
+}
+
+int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
+ const void *data)
+{
+ const char *arg = *nlctx->argp;
+ u32 val;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+ ret = parse_u32(arg, &val);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ return ret;
+ }
+
+ return ethnla_put_u32(nlctx, type, val) ? -EMSGSIZE : 0;
+}
+
+int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
+ const void *data)
+{
+ const char *arg = *nlctx->argp;
+ u8 val;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+ ret = parse_u8(arg, &val);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ return ret;
+ }
+
+ return ethnla_put_u8(nlctx, type, val) ? -EMSGSIZE : 0;
+}
+
+int nl_parse_bool(struct nl_context *nlctx, uint16_t type, const void *data)
+{
+ const char *arg = *nlctx->argp;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+ if (!strcmp(arg, "on"))
+ ret = ethnla_put_u8(nlctx, type, 1);
+ else if (!strcmp(arg, "off"))
+ ret = ethnla_put_u8(nlctx, type, 0);
+ else {
+ parser_err_invalid_value(nlctx, arg);
+ return -EINVAL;
+ }
+
+ return ret ? -EMSGSIZE : 0;
+}
+
+int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type,
+ const void *data)
+{
+ const char *arg = *nlctx->argp;
+ u8 val;
+ int ret;
+
+ nlctx->argp++;
+ nlctx->argc--;
+ ret = lookup_u8(arg, &val, data);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ return ret;
+ }
+
+ return ethnla_put_u8(nlctx, type, val) ? -EMSGSIZE : 0;
+}
+
+int nl_parse_bitfield32(struct nl_context *nlctx, uint16_t type,
+ const void *data)
+{
+ const char *arg = *nlctx->argp;
+ const struct flag_info *flags = data;
+ u32 value, selector;
+ int ret;
+
+ ret = parse_u32(arg, &value);
+ if (isdigit(arg[0])) {
+ char *mask = strchr(arg, '/');
+
+ if (mask) {
+ /* numeric value / mask */
+ *mask = '\0';
+ mask++;
+ ret = parse_u32(mask, &selector);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, mask);
+ return ret;
+ }
+ } else {
+ /* numeric value */
+ selector = ~(u32)0;
+ }
+ ret = parse_u32(arg, &value);
+ if (ret < 0) {
+ parser_err_invalid_value(nlctx, arg);
+ return ret;
+ }
+ nlctx->argp++;
+ nlctx->argc--;
+ } else {
+ /* flag on/off... [--] */
+ value = 0;
+ selector = 0;
+ while (nlctx->argc > 0) {
+ const struct flag_info *flag = flags;
+
+ if (!strcmp(*nlctx->argp, "--")) {
+ nlctx->argp++;
+ nlctx->argc--;
+ break;
+ }
+ if (nlctx->argc < 2 ||
+ (strcmp(nlctx->argp[1], "on") &&
+ strcmp(nlctx->argp[1], "off"))) {
+ parser_err_invalid_flag(nlctx, *nlctx->argp);
+ return -EINVAL;
+ }
+ while (flag->name && strcmp(*nlctx->argp, flag->name))
+ flag++;
+ if (!flag->name) {
+ parser_err_invalid_value(nlctx, *nlctx->argp);
+ return -EINVAL;
+ }
+ selector |= flag->value;
+ if (!strcmp(nlctx->argp[1], "on"))
+ value |= flag->value;
+
+ nlctx->argp += 2;
+ nlctx->argc -= 2;
+ }
+ }
+
+ return ethnla_put_bitfield32(nlctx, type, value, selector);
+}
+
+static bool is_hex(char c)
+{
+ if (isdigit(c))
+ return true;
+ else
+ return (c >= 'a' && c <= 'f');
+}
+
+/* Return true if a bitset argument should be parsed as numeric, i.e.
+ * (a) it starts with '0x'
+ * (b) it consists only of hex digits and at most one slash which can be
+ * optionally followed by "0x"
+ */
+static bool is_numeric_bitset(const char *arg)
+{
+ const char *p = arg;
+ bool has_slash = false;
+
+ if (arg[0] == '0' && arg[1] == 'x')
+ return true;
+ while (*p) {
+ if (*p == '/') {
+ if (has_slash)
+ return false;
+ has_slash = true;
+ p++;
+ if (p[0] == '0' && p[1] == 'x')
+ p += 2;
+ continue;
+ }
+ if (!is_hex(*p))
+ return false;
+ p++;
+ }
+ return true;
+}
+
+/* number of significant bits */
+static unsigned int nsb(u32 x)
+{
+ unsigned int ret = 0;
+
+ if (!(x & 0xffff0000U)) {
+ x >>= 16;
+ ret += 16;
+ }
+ if (!(x & 0xff00U)) {
+ x >>= 8;
+ ret += 8;
+ }
+ if (!(x & 0xf0U)) {
+ x >>= 4;
+ ret += 4;
+ }
+ if (!(x & 0xcU)) {
+ x >>= 2;
+ ret += 2;
+ }
+ if (!(x & 0x2U)) {
+ x >>= 1;
+ ret += 1;
+ }
+
+ return ret + x;
+}
+
+/* Parse hex string (without leading "0x") into a bitmap consisting of 32-bit
+ * words. Caller must make sure arg is at least len characters long and dst has
+ * place for at least (len + 7) / 8 32-bit words.
+ * Returns number of significant bits in the bitmap on success and negative
+ * value on error.
+ */
+static int parse_hex_string(const char *arg, unsigned int len, u32 *dst)
+{
+ const char *p = arg;
+ unsigned int nwords = (len + 7) / 8;
+ unsigned int nbits = 0;
+ char buff[9];
+
+ memset(dst, '\0', nwords * sizeof(u32));
+ while (len > 0) {
+ unsigned int chunk = (len % 8) ?: 8;
+ unsigned long val;
+ char *endp;
+
+ memcpy(buff, p, chunk);
+ buff[chunk] = '\0';
+ val = strtoul(buff, &endp, 16);
+ if (*endp)
+ return -EINVAL;
+ *dst++ = (u32)val;
+ if (nbits)
+ nbits += 8 * chunk;
+ else
+ nbits = nsb(val);
+
+ p += chunk;
+ len -= chunk;
+ }
+ return nbits;
+}
+
+static int nl_parse_bitset_compact(struct nl_context *nlctx, uint16_t type)
+{
+ const char *arg = *nlctx->argp;
+ unsigned int nwords, len1, len2;
+ bool has_mask = false;
+ struct nlattr *nest;
+ const char *maskptr;
+ u32 *value = NULL;
+ u32 *mask = NULL;
+ int nbits;
+ int ret;
+
+ if (arg[0] == '0' && arg[1] == 'x')
+ arg += 2;
+
+ maskptr = strchr(arg, '/');
+ len1 = maskptr ? ( maskptr - arg) : strlen(arg);
+ nwords = (len1 + 7) / 8;
+ nbits = 0;
+
+ value = malloc(nwords * sizeof(u32));
+ if (!value)
+ return -ENOMEM;
+ ret = -ENOMEM;
+ mask = malloc(nwords * sizeof(u32));
+ if (!mask)
+ goto free_value;
+
+ if (maskptr) {
+ has_mask = true;
+ maskptr++;
+ if (maskptr[0] == '0' && maskptr[1] == 'x')
+ maskptr += 2;
+ len2 = strlen(maskptr);
+ if (len2 > len1)
+ nwords = (len2 + 7) / 8;
+ mask = malloc(nwords * sizeof(u32));
+ if (!mask)
+ return -ENOMEM;
+ ret = parse_hex_string(maskptr, strlen(maskptr),
+ mask);
+ if (ret < 0)
+ goto free_mask;
+ nbits = ret;
+ }
+
+ ret = parse_hex_string(arg, len1, value);
+ if (ret < 0)
+ goto free_value;
+ nbits = nbits ?: ret;
+ nwords = (nbits + 31) / 32;
+
+ ret = -EMSGSIZE;
+ if (!(nest = ethnla_nest_start(nlctx, type)) ||
+ ethnla_put_u32(nlctx, ETHA_BITSET_SIZE, nbits) ||
+ ethnla_put(nlctx, ETHA_BITSET_VALUE, nwords * sizeof(u32), value) ||
+ (has_mask &&
+ ethnla_put(nlctx, ETHA_BITSET_MASK, nwords * sizeof(u32), mask)))
+ goto free_mask;
+ mnl_attr_nest_end(nlctx->nlhdr, nest);
+ ret = 0;
+
+free_mask:
+ free(mask);
+free_value:
+ free(value);
+ nlctx->argp++;
+ nlctx->argc--;
+ return ret;
+}
+
+static int nl_parse_bitset_list(struct nl_context *nlctx, uint16_t type,
+ const void *data)
+{
+ struct nlmsghdr *nlhdr = nlctx->nlhdr;
+ struct nlattr *bitset_attr;
+ struct nlattr *bits_attr;
+ struct nlattr *bit_attr;
+ int ret;
+
+ ret = -EMSGSIZE;
+ bitset_attr = ethnla_nest_start(nlctx, type);
+ if (!bitset_attr)
+ return ret;
+ bits_attr = ethnla_nest_start(nlctx, ETHA_BITSET_BITS);
+ if (!bits_attr)
+ goto err;
+
+ while (nlctx->argc > 0) {
+ if (!strcmp(*nlctx->argp, "--")) {
+ nlctx->argp++;
+ nlctx->argc--;
+ break;
+ }
+ ret = -EINVAL;
+ if (nlctx->argc < 2 ||
+ (strcmp(nlctx->argp[1], "on") &&
+ strcmp(nlctx->argp[1], "off"))) {
+ parser_err_invalid_flag(nlctx, *nlctx->argp);
+ goto err;
+ }
+
+ ret = -EMSGSIZE;
+ bit_attr = ethnla_nest_start(nlctx, ETHA_BITS_BIT);
+ if (!bit_attr)
+ goto err;
+ if (ethnla_put_strz(nlctx, ETHA_BIT_NAME, nlctx->argp[0]))
+ goto err;
+ if (ethnla_put_flag(nlctx, ETHA_BIT_VALUE,
+ !strcmp(nlctx->argp[1], "on")))
+ goto err;
+ mnl_attr_nest_end(nlhdr, bit_attr);
+
+ nlctx->argp += 2;
+ nlctx->argc -= 2;
+ }
+
+ mnl_attr_nest_end(nlhdr, bits_attr);
+ mnl_attr_nest_end(nlhdr, bitset_attr);
+ return 0;
+err:
+ mnl_attr_nest_cancel(nlhdr, bitset_attr);
+ return ret;
+}
+
+int nl_parse_bitset(struct nl_context *nlctx, uint16_t type, const void *data)
+{
+ if (is_numeric_bitset(*nlctx->argp))
+ return nl_parse_bitset_compact(nlctx, type);
+ else
+ return nl_parse_bitset_list(nlctx, type, data);
+}
+
+int nl_parser(struct cmd_context *ctx, const struct param_parser *params,
+ unsigned int max_type)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ bool attr_set[max_type];
+ int ret;
+
+ memset(attr_set, '\0', max_type * sizeof(attr_set[0]));
+
+ while (nlctx->argc > 0) {
+ const struct param_parser *parser = params;
+
+ nlctx->param = *nlctx->argp;
+ while (parser->arg) {
+ if (!strcmp(nlctx->param, parser->arg))
+ break;
+ parser++;
+ }
+ if (!parser->arg) {
+ parser_err_unknown_param(nlctx);
+ return -EINVAL;
+ }
+ nlctx->argp++;
+ nlctx->argc--;
+ if (attr_set[parser - params]) {
+ parser_err_dup_param(nlctx);
+ return -EINVAL;
+ }
+ if (nlctx->argc < parser->min_argc) {
+ parser_err_min_argc(nlctx, parser->min_argc);
+ return -EINVAL;
+ }
+
+ ret = parser->handler(nlctx, parser->type, parser->handler_data);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/netlink/parser.h b/netlink/parser.h
new file mode 100644
index 000000000000..7308da25f093
--- /dev/null
+++ b/netlink/parser.h
@@ -0,0 +1,45 @@
+#ifndef ETHTOOL_NETLINK_PARSER_H__
+#define ETHTOOL_NETLINK_PARSER_H__
+
+#include "netlink.h"
+
+struct lookup_entry_u32 {
+ const char *arg;
+ u32 val;
+};
+
+struct lookup_entry_u16 {
+ const char *arg;
+ u16 val;
+};
+
+struct lookup_entry_u8{
+ const char *arg;
+ u8 val;
+};
+
+typedef int (*param_parser_cb_t)(struct nl_context *, uint16_t, const void *);
+
+struct param_parser {
+ const char *arg;
+ uint16_t type;
+ param_parser_cb_t handler;
+ const void *handler_data;
+ unsigned int min_argc;
+};
+
+int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
+ const void *data);
+int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
+ const void *data);
+int nl_parse_bool(struct nl_context *nlctx, uint16_t type, const void *data);
+int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type,
+ const void *data);
+int nl_parse_bitfield32(struct nl_context *nlctx, uint16_t type,
+ const void *data);
+int nl_parse_bitset(struct nl_context *nlctx, uint16_t type, const void *data);
+
+int nl_parser(struct cmd_context *ctx, const struct param_parser *params,
+ unsigned int max_type);
+
+#endif /* ETHTOOL_NETLINK_PARSER_H__ */
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 11/23] netlink: add netlink handler for sfeatures (-K)
From: Michal Kubecek @ 2018-07-30 12:56 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool -K <dev>" subcommand using netlink interface command
ETHNL_CMD_SET_SETTINGS.
The implementation tries to emulate legacy flags but the result is not
exactly equal to the ioctl code. In particular, we map netdev features to
legacy flags based on name patterns in off_flag_def[] but this mapping is
slightly different from mapping used by kernel ioctl code. It also does not
try to emulate legacy flags in "actual changes" output show when actual
change of device features does not match userspace request exactly.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
ethtool.c | 3 +-
netlink/extapi.h | 1 +
netlink/settings.c | 292 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 295 insertions(+), 1 deletion(-)
diff --git a/ethtool.c b/ethtool.c
index b29086bbabde..b2f93a331098 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4878,6 +4878,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_gset NULL
#define nl_sset NULL
#define nl_gfeatures NULL
+#define nl_sfeatures NULL
#endif
static const struct option {
@@ -4944,7 +4945,7 @@ static const struct option {
" [ tx N ]\n" },
{ "-k|--show-features|--show-offload", 1, do_gfeatures, nl_gfeatures,
"Get state of protocol offload and other features" },
- { "-K|--features|--offload", 1, do_sfeatures, NULL,
+ { "-K|--features|--offload", 1, do_sfeatures, nl_sfeatures,
"Set protocol offload and other features",
" FEATURE on|off ...\n" },
{ "-i|--driver", 1, do_gdrv, nl_gdrv,
diff --git a/netlink/extapi.h b/netlink/extapi.h
index c656ff862687..ad67e81449ba 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -17,6 +17,7 @@ int nl_gdrv(struct cmd_context *ctx);
int nl_gset(struct cmd_context *ctx);
int nl_sset(struct cmd_context *ctx);
int nl_gfeatures(struct cmd_context *ctx);
+int nl_sfeatures(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/settings.c b/netlink/settings.c
index 51a01b400224..b7bfe14668df 100644
--- a/netlink/settings.c
+++ b/netlink/settings.c
@@ -741,3 +741,295 @@ int nl_sset(struct cmd_context *ctx)
return 0;
return nlctx->exit_code ?: 75;
}
+
+/* features */
+
+struct sfeatures_context {
+ uint32_t req_mask[0];
+};
+
+static void show_feature_changes(struct nl_context *nlctx,
+ const struct nlattr *feat_attr)
+{
+ struct sfeatures_context *sfctx = nlctx->cmd_private;
+ const struct stringset *feature_names =
+ global_stringset(ETH_SS_FEATURES);
+ const unsigned int count = get_count(feature_names);
+ const unsigned int words = (count + 31) / 32;
+ const struct nlattr *tb[ETHA_FEATURES_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ const uint32_t *wanted_val;
+ const uint32_t *wanted_mask;
+ const uint32_t *active_val;
+ const uint32_t *active_mask;
+ unsigned int i;
+ bool diff;
+ int ret;
+
+ ret = mnl_attr_parse_nested(feat_attr, attr_cb, &tb_info);
+ if (ret < 0)
+ goto err;
+ if (!tb[ETHA_FEATURES_WANTED] || !tb[ETHA_FEATURES_ACTIVE])
+ goto err;
+ if (bitset_get_count(tb[ETHA_FEATURES_WANTED], &ret) != count ||
+ ret < 0)
+ goto err;
+ if (bitset_get_count(tb[ETHA_FEATURES_ACTIVE], &ret) != count ||
+ ret < 0)
+ goto err;
+ wanted_val = get_compact_bitset_value(tb[ETHA_FEATURES_WANTED]);
+ wanted_mask = get_compact_bitset_mask(tb[ETHA_FEATURES_WANTED]);
+ active_val = get_compact_bitset_value(tb[ETHA_FEATURES_ACTIVE]);
+ active_mask = get_compact_bitset_mask(tb[ETHA_FEATURES_ACTIVE]);
+ if (!wanted_val || !wanted_mask || !active_val || !active_mask)
+ goto err;
+
+ diff = false;
+ for (i = 0; i < words; i++)
+ if (wanted_mask[i] || active_mask[i])
+ diff = true;
+ if (!diff)
+ return;
+
+ /* result is not exactly as requested, show differences */
+ printf("Actual changes:\n");
+ for (i = 0; i < count; i++) {
+ const char *name = get_string(feature_names, i);
+
+ if (!name)
+ continue;
+ if (!feature_on(wanted_mask, i) && !feature_on(active_mask, i))
+ continue;
+ printf("%s: ", name);
+ if (feature_on(wanted_mask, i))
+ /* we requested a value but result is different */
+ printf("%s [requested %s]",
+ feature_on(wanted_val, i) ? "off" : "on",
+ feature_on(wanted_val, i) ? "on" : "off");
+ else if (!feature_on(sfctx->req_mask, i))
+ /* not requested but changed anyway */
+ printf("%s [not requested]",
+ feature_on(active_val, i) ? "on" : "off");
+ else
+ printf("%s", feature_on(active_val, i) ? "on" : "off");
+ fputc('\n', stdout);
+ }
+
+ return;
+err:
+ fprintf(stderr, "malformed diff info from kernel\n");
+}
+
+static int find_feature(const char *name)
+{
+ const struct stringset *feature_names =
+ global_stringset(ETH_SS_FEATURES);
+ const unsigned int count = get_count(feature_names);
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ if (!strcmp(name, get_string(feature_names, i)))
+ return i;
+
+ return -1;
+}
+
+static int fill_feature(struct nl_context *nlctx, const char *name, bool val)
+{
+ struct nlattr *bit_attr;
+
+ bit_attr = ethnla_nest_start(nlctx, ETHA_BITS_BIT);
+ if (!bit_attr)
+ return -EMSGSIZE;
+ if (ethnla_put_strz(nlctx, ETHA_BIT_NAME, name))
+ return -EMSGSIZE;
+ if (ethnla_put_flag(nlctx, ETHA_BIT_VALUE, val))
+ return -EMSGSIZE;
+ mnl_attr_nest_end(nlctx->nlhdr, bit_attr);
+
+ return 0;
+}
+
+static void set_sf_req_mask(struct nl_context *nlctx, unsigned int idx)
+{
+ struct sfeatures_context *sfctx = nlctx->cmd_private;
+
+ sfctx->req_mask[idx / 32] |= (1 << (idx % 32));
+}
+
+static int fill_legacy_flag(struct nl_context *nlctx, const char *flag_name,
+ bool val)
+{
+ const struct stringset *feature_names =
+ global_stringset(ETH_SS_FEATURES);
+ const unsigned int count = get_count(feature_names);
+ unsigned int i, j;
+ int ret;
+
+ for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
+ const char *pattern;
+
+ if (strcmp(flag_name, off_flag_def[i].short_name) &&
+ strcmp(flag_name, off_flag_def[i].long_name))
+ continue;
+ pattern = off_flag_def[i].kernel_name;
+
+ for (j = 0; j < count; j++) {
+ const char *name = get_string(feature_names, j);
+
+ if (flag_pattern_match(name, pattern)) {
+ ret = fill_feature(nlctx, name, val);
+ if (ret < 0)
+ return ret;
+ set_sf_req_mask(nlctx, j);
+ }
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+int sfeatures_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
+ const struct nlattr *tb[ETHA_SETTINGS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ const char *devname;
+ int ret;
+
+ if (ghdr->cmd != ETHNL_CMD_SET_SETTINGS) {
+ fprintf(stderr, "warning: unexpected reply message type %u\n",
+ ghdr->cmd);
+ return MNL_CB_OK;
+ }
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ devname = get_dev_name(tb[ETHA_SETTINGS_DEV]);
+ if (strcmp(devname, nlctx->devname)) {
+ fprintf(stderr, "warning: unexpected message for device %s\n",
+ devname);
+ return MNL_CB_OK;
+ }
+ if (!tb[ETHA_SETTINGS_FEATURES])
+ return MNL_CB_OK;
+
+ show_feature_changes(nlctx, tb[ETHA_SETTINGS_FEATURES]);
+ return MNL_CB_OK;
+}
+
+int fill_sfeatures_bitmap(struct nl_context *nlctx)
+{
+ struct nlmsghdr *nlhdr = nlctx->nlhdr;
+ struct nlattr *bitset_attr;
+ struct nlattr *bits_attr;
+ int ret;
+
+ ret = -EMSGSIZE;
+ bitset_attr = ethnla_nest_start(nlctx, ETHA_FEATURES_WANTED);
+ if (!bitset_attr)
+ return ret;
+ bits_attr = ethnla_nest_start(nlctx, ETHA_BITSET_BITS);
+ if (!bits_attr)
+ goto err;
+
+ while (nlctx->argc > 0) {
+ bool val;
+
+ if (!strcmp(*nlctx->argp, "--")) {
+ nlctx->argp++;
+ nlctx->argc--;
+ break;
+ }
+ ret = -EINVAL;
+ if (nlctx->argc < 2 ||
+ (strcmp(nlctx->argp[1], "on") &&
+ strcmp(nlctx->argp[1], "off"))) {
+ fprintf(stderr,
+ "ethtool (%s): flag '%s' for parameter '%s' is"
+ " not followed by 'on' or 'off'\n",
+ nlctx->cmd, nlctx->argp[1], nlctx->param);
+ goto err;
+ }
+
+ val = !strcmp(nlctx->argp[1], "on");
+ ret = fill_legacy_flag(nlctx, nlctx->argp[0], val);
+ if (ret > 0) {
+ ret = fill_feature(nlctx, nlctx->argp[0], val);
+ if (ret == 0) {
+ int idx = find_feature(nlctx->argp[0]);
+
+ if (idx >= 0)
+ set_sf_req_mask(nlctx, idx);
+ }
+ }
+ if (ret < 0)
+ goto err;
+
+ nlctx->argp += 2;
+ nlctx->argc -= 2;
+ }
+
+ mnl_attr_nest_end(nlhdr, bits_attr);
+ mnl_attr_nest_end(nlhdr, bitset_attr);
+ return 0;
+err:
+ mnl_attr_nest_cancel(nlhdr, bitset_attr);
+ return ret;
+}
+
+int nl_sfeatures(struct cmd_context *ctx)
+{
+ const struct stringset *feature_names;
+ struct sfeatures_context *sfctx;
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nlattr *feat_attr;
+ unsigned int words;
+ int ret;
+
+ nlctx->cmd = "-K";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->cmd_private = &sfctx;
+
+ load_global_strings(nlctx);
+ feature_names = global_stringset(ETH_SS_FEATURES);
+ words = (get_count(feature_names) + 31) / 32;
+ sfctx = malloc(sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0]));
+ if (!sfctx)
+ return -ENOMEM;
+ memset(sfctx, '\0',
+ sizeof(*sfctx) + words * sizeof(sfctx->req_mask[0]));
+ nlctx->cmd_private = sfctx;
+
+ nlctx->devname = ctx->devname;
+ ret = msg_init(nlctx, ETHNL_CMD_SET_SETTINGS,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_put_dev(nlctx, ETHA_SETTINGS_DEV, ctx->devname))
+ return -EMSGSIZE;
+ if (ethnla_put_flag(nlctx, ETHA_SETTINGS_COMPACT, true))
+ return -EMSGSIZE;
+
+ feat_attr = ethnla_nest_start(nlctx, ETHA_SETTINGS_FEATURES);
+ if (!feat_attr)
+ return -EMSGSIZE;
+ ret = fill_sfeatures_bitmap(nlctx);
+ if (ret < 0)
+ return ret;
+ if (ethnla_put_flag(nlctx, ETHA_FEATURES_WANT_DIFF, true))
+ return -EMSGSIZE;
+ mnl_attr_nest_end(nlctx->nlhdr, feat_attr);
+
+ ret = ethnl_sendmsg(nlctx);
+ if (ret < 0)
+ return 92;
+ ret = ethnl_process_reply(nlctx, sfeatures_reply_cb);
+ if (ret == 0)
+ return 0;
+ return nlctx->exit_code ?: 92;
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 12/23] netlink: add netlink handler for gcoalesce (-c)
From: Michal Kubecek @ 2018-07-30 12:56 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool -c <dev>" subcommand using netlink interface command
ETHNL_CMD_GET_PARAMS with ETH_PARAMS_IM_COALESCE mask.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
Makefile.am | 2 +-
ethtool.c | 3 +-
netlink/extapi.h | 1 +
netlink/monitor.c | 10 +++++
netlink/params.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 115 insertions(+), 2 deletions(-)
create mode 100644 netlink/params.c
diff --git a/Makefile.am b/Makefile.am
index 7cc4adc53c59..bedf9edaac47 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -24,7 +24,7 @@ ethtool_SOURCES += \
netlink/netlink.c netlink/netlink.h netlink/extapi.h \
netlink/strset.c netlink/strset.h netlink/monitor.c \
netlink/parser.c netlink/parser.h \
- netlink/drvinfo.c netlink/settings.c \
+ netlink/drvinfo.c netlink/settings.c netlink/params.c \
uapi/linux/ethtool_netlink.h \
uapi/linux/netlink.h uapi/linux/genetlink.h
ethtool_CFLAGS += @MNL_CFLAGS@
diff --git a/ethtool.c b/ethtool.c
index b2f93a331098..24c70c372914 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4879,6 +4879,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_sset NULL
#define nl_gfeatures NULL
#define nl_sfeatures NULL
+#define nl_gcoalesce NULL
#endif
static const struct option {
@@ -4909,7 +4910,7 @@ static const struct option {
" [ autoneg on|off ]\n"
" [ rx on|off ]\n"
" [ tx on|off ]\n" },
- { "-c|--show-coalesce", 1, do_gcoalesce, NULL,
+ { "-c|--show-coalesce", 1, do_gcoalesce, nl_gcoalesce,
"Show coalesce options" },
{ "-C|--coalesce", 1, do_scoalesce, NULL,
"Set coalesce options",
diff --git a/netlink/extapi.h b/netlink/extapi.h
index ad67e81449ba..8030bda4cbc9 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -18,6 +18,7 @@ int nl_gset(struct cmd_context *ctx);
int nl_sset(struct cmd_context *ctx);
int nl_gfeatures(struct cmd_context *ctx);
int nl_sfeatures(struct cmd_context *ctx);
+int nl_gcoalesce(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/monitor.c b/netlink/monitor.c
index e3b8bb46f561..5d494ba43923 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -68,6 +68,7 @@ static int monitor_event_cb(const struct nlmsghdr *nlhdr, void *data)
int drvinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data);
int settings_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int params_reply_cb(const struct nlmsghdr *nlhdr, void *data);
static struct {
uint8_t cmd;
@@ -85,6 +86,10 @@ static struct {
.cmd = ETHNL_CMD_SET_SETTINGS,
.cb = settings_reply_cb,
},
+ {
+ .cmd = ETHNL_CMD_SET_PARAMS,
+ .cb = params_reply_cb,
+ },
};
static int monitor_any_cb(const struct nlmsghdr *nlhdr, void *data)
@@ -133,6 +138,11 @@ static struct monitor_option monitor_opts[] = {
.cmd = ETHNL_CMD_SET_SETTINGS,
.info_mask = ETH_SETTINGS_IM_FEATURES,
},
+ {
+ .pattern = "-c|--show-coalesce|-C|--coalesce",
+ .cmd = ETHNL_CMD_SET_PARAMS,
+ .info_mask = ETH_PARAMS_IM_COALESCE,
+ },
};
static bool pattern_match(const char *s, const char *pattern)
diff --git a/netlink/params.c b/netlink/params.c
new file mode 100644
index 000000000000..30aee5758c42
--- /dev/null
+++ b/netlink/params.c
@@ -0,0 +1,101 @@
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "strset.h"
+#include "parser.h"
+
+/* GET_PARAMS */
+
+static int show_coalesce(struct nl_context *nlctx, const struct nlattr *nest)
+{
+ const struct nlattr *tb[ETHA_COALESCE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ if (!nest)
+ return -EOPNOTSUPP;
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ printf("Coalesce parameters for %s:\n", nlctx->devname);
+ printf("Adaptive RX: %s TX: %s\n",
+ u8_to_bool(tb[ETHA_COALESCE_RX_USE_ADAPTIVE]),
+ u8_to_bool(tb[ETHA_COALESCE_TX_USE_ADAPTIVE]));
+ show_u32(tb[ETHA_COALESCE_STATS_BLOCK_USECS], "stats-block-usecs: ");
+ show_u32(tb[ETHA_COALESCE_RATE_SAMPLE_INTERVAL], "sample-interval: ");
+ show_u32(tb[ETHA_COALESCE_PKT_RATE_LOW], "pkt-rate-low: ");
+ show_u32(tb[ETHA_COALESCE_PKT_RATE_HIGH], "pkt-rate-high: ");
+ putchar('\n');
+ show_u32(tb[ETHA_COALESCE_RX_USECS], "rx-usecs: ");
+ show_u32(tb[ETHA_COALESCE_RX_MAXFRM], "rx-frames: ");
+ show_u32(tb[ETHA_COALESCE_RX_USECS_IRQ], "rx-usecs-irq: ");
+ show_u32(tb[ETHA_COALESCE_RX_MAXFRM_IRQ], "rx-frames-irq: ");
+ putchar('\n');
+ show_u32(tb[ETHA_COALESCE_TX_USECS], "tx-usecs: ");
+ show_u32(tb[ETHA_COALESCE_TX_MAXFRM], "tx-frames: ");
+ show_u32(tb[ETHA_COALESCE_TX_USECS_IRQ], "tx-usecs-irq: ");
+ show_u32(tb[ETHA_COALESCE_TX_MAXFRM_IRQ], "tx-frames-irq: ");
+ putchar('\n');
+ show_u32(tb[ETHA_COALESCE_RX_USECS_LOW], "rx-usecs-low: ");
+ show_u32(tb[ETHA_COALESCE_RX_MAXFRM_LOW], "rx-frame-low: ");
+ show_u32(tb[ETHA_COALESCE_TX_USECS_LOW], "tx-usecs-low: ");
+ show_u32(tb[ETHA_COALESCE_TX_MAXFRM_LOW], "tx-frame-low: ");
+ putchar('\n');
+ show_u32(tb[ETHA_COALESCE_RX_USECS_HIGH], "rx-usecs-high: ");
+ show_u32(tb[ETHA_COALESCE_RX_MAXFRM_HIGH], "rx-frame-high: ");
+ show_u32(tb[ETHA_COALESCE_TX_USECS_HIGH], "tx-usecs-high: ");
+ show_u32(tb[ETHA_COALESCE_TX_MAXFRM_HIGH], "tx-frame-high: ");
+ putchar('\n');
+
+ return 0;
+}
+
+int params_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHA_PARAMS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ int ret;
+
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ nlctx->devname = get_dev_name(tb[ETHA_PARAMS_DEV]);
+ if (!dev_ok(nlctx))
+ return MNL_CB_OK;
+
+ if (mask_ok(nlctx, ETH_PARAMS_IM_COALESCE)) {
+ ret = show_coalesce(nlctx, tb[ETHA_PARAMS_COALESCE]);
+ if ((ret < 0) && show_only(nlctx, ETH_PARAMS_IM_COALESCE)) {
+ nlctx->exit_code = 82;
+ errno = -ret;
+ perror("Cannot get device coalesce settings");
+ return MNL_CB_ERROR;
+ }
+ }
+
+ return MNL_CB_OK;
+}
+
+static int params_request(struct cmd_context *ctx, uint32_t info_mask)
+{
+ int ret;
+
+ ret = ethnl_prep_get_request(ctx, ETHNL_CMD_GET_PARAMS,
+ ETHA_PARAMS_DEV);
+ if (ret < 0)
+ return ret;
+ if (ethnla_put_u32(ctx->nlctx, ETHA_PARAMS_INFOMASK, info_mask))
+ return -EMSGSIZE;
+ return ethnl_send_get_request(ctx->nlctx, params_reply_cb);
+}
+
+int nl_gcoalesce(struct cmd_context *ctx)
+{
+ return params_request(ctx, ETH_PARAMS_IM_COALESCE);
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 13/23] netlink: add netlink handler for gring (-g)
From: Michal Kubecek @ 2018-07-30 12:56 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool -g <dev>" subcommand using netlink interface command
ETHNL_CMD_GET_PARAMS with ETH_PARAMS_IM_RING mask.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
ethtool.c | 3 ++-
netlink/extapi.h | 1 +
netlink/monitor.c | 5 +++++
netlink/params.c | 42 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/ethtool.c b/ethtool.c
index 24c70c372914..ba5c73e9abbe 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4880,6 +4880,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_gfeatures NULL
#define nl_sfeatures NULL
#define nl_gcoalesce NULL
+#define nl_gring NULL
#endif
static const struct option {
@@ -4936,7 +4937,7 @@ static const struct option {
" [tx-usecs-high N]\n"
" [tx-frames-high N]\n"
" [sample-interval N]\n" },
- { "-g|--show-ring", 1, do_gring, NULL,
+ { "-g|--show-ring", 1, do_gring, nl_gring,
"Query RX/TX ring parameters" },
{ "-G|--set-ring", 1, do_sring, NULL,
"Set RX/TX ring parameters",
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 8030bda4cbc9..cc13436c4517 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -19,6 +19,7 @@ int nl_sset(struct cmd_context *ctx);
int nl_gfeatures(struct cmd_context *ctx);
int nl_sfeatures(struct cmd_context *ctx);
int nl_gcoalesce(struct cmd_context *ctx);
+int nl_gring(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/monitor.c b/netlink/monitor.c
index 5d494ba43923..10dad082de98 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -143,6 +143,11 @@ static struct monitor_option monitor_opts[] = {
.cmd = ETHNL_CMD_SET_PARAMS,
.info_mask = ETH_PARAMS_IM_COALESCE,
},
+ {
+ .pattern = "-g|--show-ring|-G|--set-ring",
+ .cmd = ETHNL_CMD_SET_PARAMS,
+ .info_mask = ETH_PARAMS_IM_RING,
+ },
};
static bool pattern_match(const char *s, const char *pattern)
diff --git a/netlink/params.c b/netlink/params.c
index 30aee5758c42..6782d98a6956 100644
--- a/netlink/params.c
+++ b/netlink/params.c
@@ -55,6 +55,34 @@ static int show_coalesce(struct nl_context *nlctx, const struct nlattr *nest)
return 0;
}
+static int show_ring(struct nl_context *nlctx, const struct nlattr *nest)
+{
+ const struct nlattr *tb[ETHA_RING_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ if (!nest)
+ return -EOPNOTSUPP;
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ printf("Ring parameters for %s:\n", nlctx->devname);
+ printf("Pre-set maximums:\n");
+ show_u32(tb[ETHA_RING_RX_MAX_PENDING], "RX:\t\t");
+ show_u32(tb[ETHA_RING_RX_MINI_MAX_PENDING], "RX Mini:\t");
+ show_u32(tb[ETHA_RING_RX_JUMBO_MAX_PENDING], "RX Jumbo:\t");
+ show_u32(tb[ETHA_RING_TX_MAX_PENDING], "TX:\t\t");
+ printf("Current hardware settings:\n");
+ show_u32(tb[ETHA_RING_RX_PENDING], "RX:\t\t");
+ show_u32(tb[ETHA_RING_RX_MINI_PENDING], "RX Mini:\t");
+ show_u32(tb[ETHA_RING_RX_JUMBO_PENDING], "RX Jumbo:\t");
+ show_u32(tb[ETHA_RING_TX_PENDING], "TX:\t\t");
+ putchar('\n');
+
+ return 0;
+}
+
int params_reply_cb(const struct nlmsghdr *nlhdr, void *data)
{
const struct nlattr *tb[ETHA_PARAMS_MAX + 1] = {};
@@ -78,6 +106,15 @@ int params_reply_cb(const struct nlmsghdr *nlhdr, void *data)
return MNL_CB_ERROR;
}
}
+ if (mask_ok(nlctx, ETH_PARAMS_IM_RING)) {
+ ret = show_ring(nlctx, tb[ETHA_PARAMS_RING]);
+ if ((ret < 0) && show_only(nlctx, ETH_PARAMS_IM_RING)) {
+ nlctx->exit_code = 76;
+ errno = -ret;
+ perror("Cannot get device ring settings");
+ return MNL_CB_ERROR;
+ }
+ }
return MNL_CB_OK;
}
@@ -99,3 +136,8 @@ int nl_gcoalesce(struct cmd_context *ctx)
{
return params_request(ctx, ETH_PARAMS_IM_COALESCE);
}
+
+int nl_gring(struct cmd_context *ctx)
+{
+ return params_request(ctx, ETH_PARAMS_IM_RING);
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 14/23] netlink: add netlink handler for gpause (-a)
From: Michal Kubecek @ 2018-07-30 12:56 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool -a <dev>" subcommand using netlink interface command
ETHNL_CMD_GET_PARAMS with ETH_PARAMS_IM_PAUSE mask.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
ethtool.c | 3 ++-
netlink/extapi.h | 1 +
netlink/monitor.c | 5 +++++
netlink/params.c | 36 ++++++++++++++++++++++++++++++++++++
4 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/ethtool.c b/ethtool.c
index ba5c73e9abbe..3a12b7c0c7ce 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4881,6 +4881,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_sfeatures NULL
#define nl_gcoalesce NULL
#define nl_gring NULL
+#define nl_gpause NULL
#endif
static const struct option {
@@ -4904,7 +4905,7 @@ static const struct option {
" [ wol %d[/%d] | p|u|m|b|a|g|s|d...[/p|u|m|b|a|g|s|d...] ]\n"
" [ sopass %x:%x:%x:%x:%x:%x ]\n"
" [ msglvl %d[/%d] | type on|off ... [--] ]\n" },
- { "-a|--show-pause", 1, do_gpause, NULL,
+ { "-a|--show-pause", 1, do_gpause, nl_gpause,
"Show pause options" },
{ "-A|--pause", 1, do_spause, NULL,
"Set pause options",
diff --git a/netlink/extapi.h b/netlink/extapi.h
index cc13436c4517..3b918a443b96 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -20,6 +20,7 @@ int nl_gfeatures(struct cmd_context *ctx);
int nl_sfeatures(struct cmd_context *ctx);
int nl_gcoalesce(struct cmd_context *ctx);
int nl_gring(struct cmd_context *ctx);
+int nl_gpause(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/monitor.c b/netlink/monitor.c
index 10dad082de98..18e151980c3f 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -148,6 +148,11 @@ static struct monitor_option monitor_opts[] = {
.cmd = ETHNL_CMD_SET_PARAMS,
.info_mask = ETH_PARAMS_IM_RING,
},
+ {
+ .pattern = "-a|--show-pause|-A|--pause",
+ .cmd = ETHNL_CMD_SET_PARAMS,
+ .info_mask = ETH_PARAMS_IM_PAUSE,
+ },
};
static bool pattern_match(const char *s, const char *pattern)
diff --git a/netlink/params.c b/netlink/params.c
index 6782d98a6956..b45aac166dcd 100644
--- a/netlink/params.c
+++ b/netlink/params.c
@@ -83,6 +83,28 @@ static int show_ring(struct nl_context *nlctx, const struct nlattr *nest)
return 0;
}
+static int show_pause(struct nl_context *nlctx, const struct nlattr *nest)
+{
+ const struct nlattr *tb[ETHA_PAUSE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ if (!nest)
+ return -EOPNOTSUPP;
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ printf("Pause parameters for %s:\n", nlctx->devname);
+ show_bool(tb[ETHA_PAUSE_AUTONEG], "Autonegotiate:\t");
+ show_bool(tb[ETHA_PAUSE_RX], "RX:\t\t");
+ show_bool(tb[ETHA_PAUSE_TX], "TX:\t\t");
+ /* ToDo: query negotiated pause frame usage */
+ putchar('\n');
+
+ return 0;
+}
+
int params_reply_cb(const struct nlmsghdr *nlhdr, void *data)
{
const struct nlattr *tb[ETHA_PARAMS_MAX + 1] = {};
@@ -115,6 +137,15 @@ int params_reply_cb(const struct nlmsghdr *nlhdr, void *data)
return MNL_CB_ERROR;
}
}
+ if (mask_ok(nlctx, ETH_PARAMS_IM_PAUSE)) {
+ ret = show_pause(nlctx, tb[ETHA_PARAMS_PAUSE]);
+ if ((ret < 0) && show_only(nlctx, ETH_PARAMS_IM_PAUSE)) {
+ nlctx->exit_code = 76;
+ errno = -ret;
+ perror("Cannot get device pause settings");
+ return MNL_CB_ERROR;
+ }
+ }
return MNL_CB_OK;
}
@@ -141,3 +172,8 @@ int nl_gring(struct cmd_context *ctx)
{
return params_request(ctx, ETH_PARAMS_IM_RING);
}
+
+int nl_gpause(struct cmd_context *ctx)
+{
+ return params_request(ctx, ETH_PARAMS_IM_PAUSE);
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 15/23] netlink: add netlink handler for gchannels (-l)
From: Michal Kubecek @ 2018-07-30 12:56 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool -l <dev>" subcommand using netlink interface command
ETHNL_CMD_GET_PARAMS with ETH_PARAMS_IM_CHANNELS mask.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
ethtool.c | 3 ++-
netlink/extapi.h | 1 +
netlink/monitor.c | 5 +++++
netlink/params.c | 42 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/ethtool.c b/ethtool.c
index 3a12b7c0c7ce..2bcc1944ba29 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4882,6 +4882,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_gcoalesce NULL
#define nl_gring NULL
#define nl_gpause NULL
+#define nl_gchannels NULL
#endif
static const struct option {
@@ -5033,7 +5034,7 @@ static const struct option {
{ "-W|--set-dump", 1, do_setfwdump, NULL,
"Set dump flag of the device",
" N\n"},
- { "-l|--show-channels", 1, do_gchannels, NULL,
+ { "-l|--show-channels", 1, do_gchannels, nl_gchannels,
"Query Channels" },
{ "-L|--set-channels", 1, do_schannels, NULL,
"Set Channels",
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 3b918a443b96..fc027f62398c 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -21,6 +21,7 @@ int nl_sfeatures(struct cmd_context *ctx);
int nl_gcoalesce(struct cmd_context *ctx);
int nl_gring(struct cmd_context *ctx);
int nl_gpause(struct cmd_context *ctx);
+int nl_gchannels(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/monitor.c b/netlink/monitor.c
index 18e151980c3f..1b277606a78c 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -153,6 +153,11 @@ static struct monitor_option monitor_opts[] = {
.cmd = ETHNL_CMD_SET_PARAMS,
.info_mask = ETH_PARAMS_IM_PAUSE,
},
+ {
+ .pattern = "-l|--show-channels|-L|--set-channels",
+ .cmd = ETHNL_CMD_SET_PARAMS,
+ .info_mask = ETH_PARAMS_IM_CHANNELS,
+ },
};
static bool pattern_match(const char *s, const char *pattern)
diff --git a/netlink/params.c b/netlink/params.c
index b45aac166dcd..81547492532f 100644
--- a/netlink/params.c
+++ b/netlink/params.c
@@ -105,6 +105,34 @@ static int show_pause(struct nl_context *nlctx, const struct nlattr *nest)
return 0;
}
+static int show_channels(struct nl_context *nlctx, const struct nlattr *nest)
+{
+ const struct nlattr *tb[ETHA_CHANNELS_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ if (!nest)
+ return -EOPNOTSUPP;
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ printf("Channel parameters for %s:\n", nlctx->devname);
+ printf("Pre-set maximums:\n");
+ show_u32(tb[ETHA_CHANNELS_MAX_RX], "RX:\t\t");
+ show_u32(tb[ETHA_CHANNELS_MAX_TX], "TX:\t\t");
+ show_u32(tb[ETHA_CHANNELS_MAX_OTHER], "Other:\t\t");
+ show_u32(tb[ETHA_CHANNELS_MAX_COMBINED], "Combined:\t");
+ printf("Current hardware settings:\n");
+ show_u32(tb[ETHA_CHANNELS_RX_COUNT], "RX:\t\t");
+ show_u32(tb[ETHA_CHANNELS_TX_COUNT], "TX:\t\t");
+ show_u32(tb[ETHA_CHANNELS_OTHER_COUNT], "Other:\t\t");
+ show_u32(tb[ETHA_CHANNELS_COMBINED_COUNT], "Combined:\t");
+ putchar('\n');
+
+ return 0;
+}
+
int params_reply_cb(const struct nlmsghdr *nlhdr, void *data)
{
const struct nlattr *tb[ETHA_PARAMS_MAX + 1] = {};
@@ -146,6 +174,15 @@ int params_reply_cb(const struct nlmsghdr *nlhdr, void *data)
return MNL_CB_ERROR;
}
}
+ if (mask_ok(nlctx, ETH_PARAMS_IM_CHANNELS)) {
+ ret = show_channels(nlctx, tb[ETHA_PARAMS_CHANNELS]);
+ if ((ret < 0) && show_only(nlctx, ETH_PARAMS_IM_CHANNELS)) {
+ nlctx->exit_code = 1;
+ errno = -ret;
+ perror("Cannot get device channel parameters");
+ return MNL_CB_ERROR;
+ }
+ }
return MNL_CB_OK;
}
@@ -177,3 +214,8 @@ int nl_gpause(struct cmd_context *ctx)
{
return params_request(ctx, ETH_PARAMS_IM_PAUSE);
}
+
+int nl_gchannels(struct cmd_context *ctx)
+{
+ return params_request(ctx, ETH_PARAMS_IM_CHANNELS);
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 16/23] netlink: add netlink handler for geee (--show-eee)
From: Michal Kubecek @ 2018-07-30 12:57 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool --show-eee <dev>" subcommand using netlink interface
command ETHNL_CMD_GET_PARAMS with ETH_PARAMS_IM_EEE mask.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
ethtool.c | 3 ++-
netlink/extapi.h | 1 +
netlink/monitor.c | 5 ++++
netlink/params.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/ethtool.c b/ethtool.c
index 2bcc1944ba29..1ad0cd10735a 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4883,6 +4883,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_gring NULL
#define nl_gpause NULL
#define nl_gchannels NULL
+#define nl_geee NULL
#endif
static const struct option {
@@ -5053,7 +5054,7 @@ static const struct option {
" [ hex on|off ]\n"
" [ offset N ]\n"
" [ length N ]\n" },
- { "--show-eee", 1, do_geee, NULL,
+ { "--show-eee", 1, do_geee, nl_geee,
"Show EEE settings"},
{ "--set-eee", 1, do_seee, NULL,
"Set EEE settings",
diff --git a/netlink/extapi.h b/netlink/extapi.h
index fc027f62398c..40d708a4cb85 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -22,6 +22,7 @@ int nl_gcoalesce(struct cmd_context *ctx);
int nl_gring(struct cmd_context *ctx);
int nl_gpause(struct cmd_context *ctx);
int nl_gchannels(struct cmd_context *ctx);
+int nl_geee(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/monitor.c b/netlink/monitor.c
index 1b277606a78c..799371fdebc2 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -158,6 +158,11 @@ static struct monitor_option monitor_opts[] = {
.cmd = ETHNL_CMD_SET_PARAMS,
.info_mask = ETH_PARAMS_IM_CHANNELS,
},
+ {
+ .pattern = "--show-eee|--set-eee",
+ .cmd = ETHNL_CMD_SET_PARAMS,
+ .info_mask = ETH_PARAMS_IM_EEE,
+ },
};
static bool pattern_match(const char *s, const char *pattern)
diff --git a/netlink/params.c b/netlink/params.c
index 81547492532f..55138b6fdc83 100644
--- a/netlink/params.c
+++ b/netlink/params.c
@@ -133,6 +133,58 @@ static int show_channels(struct nl_context *nlctx, const struct nlattr *nest)
return 0;
}
+static int show_eee(struct nl_context *nlctx, const struct nlattr *nest)
+{
+ const struct nlattr *tb[ETHA_EEE_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ bool active, enabled, tx_lpi_enabled;
+ int ret;
+
+ if (!nest)
+ return -EOPNOTSUPP;
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+ if (!tb[ETHA_EEE_LINK_MODES] || !tb[ETHA_EEE_PEER_MODES] ||
+ !tb[ETHA_EEE_ACTIVE] || !tb[ETHA_EEE_ENABLED] ||
+ !tb[ETHA_EEE_TX_LPI_ENABLED] || !tb[ETHA_EEE_TX_LPI_TIMER])
+ return -EFAULT;
+
+
+ active = mnl_attr_get_u8(tb[ETHA_EEE_ACTIVE]);
+ enabled = mnl_attr_get_u8(tb[ETHA_EEE_ENABLED]);
+ tx_lpi_enabled = mnl_attr_get_u8(tb[ETHA_EEE_TX_LPI_ENABLED]);
+
+ printf("EEE Settings for %s:\n", nlctx->devname);
+ printf("\tEEE status: ");
+ if (bitset_is_empty(tb[ETHA_EEE_LINK_MODES], true, &ret)) {
+ printf("not supported\n");
+ return 0;
+ }
+ if (!enabled)
+ printf("disabled\n");
+ else
+ printf("enabled - %s\n", active ? "active" : "inactive");
+ printf("\tTx LPI: ");
+ if (tx_lpi_enabled)
+ printf("%u (us)\n",
+ mnl_attr_get_u32(tb[ETHA_EEE_TX_LPI_TIMER]));
+ else
+ printf("disabled\n");
+
+ ret = dump_link_modes(tb[ETHA_EEE_LINK_MODES], true, LM_CLASS_REAL,
+ "Supported EEE link modes: ", NULL, "\n",
+ "Not reported");
+ ret = dump_link_modes(tb[ETHA_EEE_LINK_MODES], false, LM_CLASS_REAL,
+ "Advertised EEE link modes: ", NULL, "\n",
+ "Not reported");
+ ret = dump_link_modes(tb[ETHA_EEE_PEER_MODES], false, LM_CLASS_REAL,
+ "Link partner advertised EEE link modes: ", NULL,
+ "\n", "Not reported");
+
+ return 0;
+}
+
int params_reply_cb(const struct nlmsghdr *nlhdr, void *data)
{
const struct nlattr *tb[ETHA_PARAMS_MAX + 1] = {};
@@ -183,6 +235,15 @@ int params_reply_cb(const struct nlmsghdr *nlhdr, void *data)
return MNL_CB_ERROR;
}
}
+ if (mask_ok(nlctx, ETH_PARAMS_IM_EEE)) {
+ ret = show_eee(nlctx, tb[ETHA_PARAMS_EEE]);
+ if ((ret < 0) && show_only(nlctx, ETH_PARAMS_IM_EEE)) {
+ nlctx->exit_code = 1;
+ errno = -ret;
+ perror("Cannot get device EEE settings");
+ return MNL_CB_ERROR;
+ }
+ }
return MNL_CB_OK;
}
@@ -219,3 +280,8 @@ int nl_gchannels(struct cmd_context *ctx)
{
return params_request(ctx, ETH_PARAMS_IM_CHANNELS);
}
+
+int nl_geee(struct cmd_context *ctx)
+{
+ return params_request(ctx, ETH_PARAMS_IM_EEE);
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 17/23] netlink: add netlink handler for gfec (--show-fec)
From: Michal Kubecek @ 2018-07-30 12:57 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool --show-fec <dev>" subcommand showing Forward Error
Correction (FEC) settings using netlink interface command
ETHNL_CMD_GET_PARAMS with ETH_PARAMS_IM_FEC mask.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
common.c | 10 ++++++++++
common.h | 2 ++
ethtool.c | 3 ++-
netlink/extapi.h | 1 +
netlink/monitor.c | 5 +++++
netlink/params.c | 43 +++++++++++++++++++++++++++++++++++++++++++
6 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/common.c b/common.c
index db9a360ffae6..085014e895bf 100644
--- a/common.c
+++ b/common.c
@@ -41,6 +41,16 @@ const struct flag_info flags_msglvl[] = {
};
const unsigned int n_flags_msglvl = ARRAY_SIZE(flags_msglvl) - 1;
+const struct flag_info flags_fecenc[] = {
+ { "none", ETHTOOL_FEC_NONE },
+ { "auto", ETHTOOL_FEC_AUTO },
+ { "off", ETHTOOL_FEC_OFF },
+ { "RS", ETHTOOL_FEC_RS },
+ { "baser", ETHTOOL_FEC_BASER },
+ {}
+};
+const unsigned int n_flags_fecenc = ARRAY_SIZE(flags_fecenc) - 1;
+
const char *names_duplex[] = {
[DUPLEX_HALF] = "Half",
[DUPLEX_FULL] = "Full",
diff --git a/common.h b/common.h
index 36f96ddaa3bd..c05e51b645f8 100644
--- a/common.h
+++ b/common.h
@@ -10,6 +10,8 @@ struct flag_info {
extern const struct flag_info flags_msglvl[];
extern const unsigned int n_flags_msglvl;
+extern const struct flag_info flags_fecenc[];
+extern const unsigned int n_flags_fecenc;
enum link_mode_class {
diff --git a/ethtool.c b/ethtool.c
index 1ad0cd10735a..2c838b7c7f53 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4884,6 +4884,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_gpause NULL
#define nl_gchannels NULL
#define nl_geee NULL
+#define nl_gfec NULL
#endif
static const struct option {
@@ -5091,7 +5092,7 @@ static const struct option {
" [ ap-shared ]\n"
" [ dedicated ]\n"
" [ all ]\n"},
- { "--show-fec", 1, do_gfec, NULL,
+ { "--show-fec", 1, do_gfec, nl_gfec,
"Show FEC settings"},
{ "--set-fec", 1, do_sfec, NULL,
"Set FEC settings",
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 40d708a4cb85..b40db721a4c5 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -23,6 +23,7 @@ int nl_gring(struct cmd_context *ctx);
int nl_gpause(struct cmd_context *ctx);
int nl_gchannels(struct cmd_context *ctx);
int nl_geee(struct cmd_context *ctx);
+int nl_gfec(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/monitor.c b/netlink/monitor.c
index 799371fdebc2..92031052a425 100644
--- a/netlink/monitor.c
+++ b/netlink/monitor.c
@@ -163,6 +163,11 @@ static struct monitor_option monitor_opts[] = {
.cmd = ETHNL_CMD_SET_PARAMS,
.info_mask = ETH_PARAMS_IM_EEE,
},
+ {
+ .pattern = "--show-fec|--set-fec",
+ .cmd = ETHNL_CMD_SET_PARAMS,
+ .info_mask = ETH_PARAMS_IM_FEC,
+ },
};
static bool pattern_match(const char *s, const char *pattern)
diff --git a/netlink/params.c b/netlink/params.c
index 55138b6fdc83..c187ca0b37c5 100644
--- a/netlink/params.c
+++ b/netlink/params.c
@@ -185,6 +185,35 @@ static int show_eee(struct nl_context *nlctx, const struct nlattr *nest)
return 0;
}
+static int show_fec(struct nl_context *nlctx, const struct nlattr *nest)
+{
+ const struct nlattr *tb[ETHA_FEC_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ int ret;
+
+ if (!nest)
+ return -EOPNOTSUPP;
+ ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+ if (ret < 0)
+ return ret;
+
+ printf("FEC parameters for %s:\n", nlctx->devname);
+ if (tb[ETHA_FEC_MODES]) {
+ const struct nla_bitfield32 *fec_encs =
+ mnl_attr_get_payload(tb[ETHA_FEC_MODES]);
+
+ printf("Active FEC encodings:\t");
+ print_flags(flags_fecenc, n_flags_fecenc, fec_encs->value);
+ putchar('\n');
+ printf("Configured FEC encodings:\t");
+ print_flags(flags_fecenc, n_flags_fecenc, fec_encs->selector);
+ putchar('\n');
+ }
+ putchar('\n');
+
+ return 0;
+}
+
int params_reply_cb(const struct nlmsghdr *nlhdr, void *data)
{
const struct nlattr *tb[ETHA_PARAMS_MAX + 1] = {};
@@ -244,6 +273,15 @@ int params_reply_cb(const struct nlmsghdr *nlhdr, void *data)
return MNL_CB_ERROR;
}
}
+ if (mask_ok(nlctx, ETH_PARAMS_IM_FEC)) {
+ ret = show_fec(nlctx, tb[ETHA_PARAMS_FEC]);
+ if ((ret < 0) && show_only(nlctx, ETH_PARAMS_IM_FEC)) {
+ nlctx->exit_code = 1;
+ errno = -ret;
+ perror("Cannot get device FEC settings");
+ return MNL_CB_ERROR;
+ }
+ }
return MNL_CB_OK;
}
@@ -285,3 +323,8 @@ int nl_geee(struct cmd_context *ctx)
{
return params_request(ctx, ETH_PARAMS_IM_EEE);
}
+
+int nl_gfec(struct cmd_context *ctx)
+{
+ return params_request(ctx, ETH_PARAMS_IM_FEC);
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 18/23] netlink: add netlink handler for scoalesce (-C)
From: Michal Kubecek @ 2018-07-30 12:57 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool -C <dev>" subcommand using netlink interface command
ETHNL_CMD_SET_PARAMS.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
ethtool.c | 3 +-
netlink/extapi.h | 1 +
netlink/params.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 185 insertions(+), 1 deletion(-)
diff --git a/ethtool.c b/ethtool.c
index 2c838b7c7f53..8d755b3933a9 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4885,6 +4885,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_gchannels NULL
#define nl_geee NULL
#define nl_gfec NULL
+#define nl_scoalesce NULL
#endif
static const struct option {
@@ -4917,7 +4918,7 @@ static const struct option {
" [ tx on|off ]\n" },
{ "-c|--show-coalesce", 1, do_gcoalesce, nl_gcoalesce,
"Show coalesce options" },
- { "-C|--coalesce", 1, do_scoalesce, NULL,
+ { "-C|--coalesce", 1, do_scoalesce, nl_scoalesce,
"Set coalesce options",
" [adaptive-rx on|off]\n"
" [adaptive-tx on|off]\n"
diff --git a/netlink/extapi.h b/netlink/extapi.h
index b40db721a4c5..2379309f084e 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -24,6 +24,7 @@ int nl_gpause(struct cmd_context *ctx);
int nl_gchannels(struct cmd_context *ctx);
int nl_geee(struct cmd_context *ctx);
int nl_gfec(struct cmd_context *ctx);
+int nl_scoalesce(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/params.c b/netlink/params.c
index c187ca0b37c5..b86922b8d215 100644
--- a/netlink/params.c
+++ b/netlink/params.c
@@ -328,3 +328,185 @@ int nl_gfec(struct cmd_context *ctx)
{
return params_request(ctx, ETH_PARAMS_IM_FEC);
}
+
+/* SET_PARAMS */
+
+static int nl_set_param(struct cmd_context *ctx, const char *opt,
+ const struct param_parser *params, u16 nest_type,
+ u16 max_type)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nlattr *nest;
+ int ret;
+
+ nlctx->cmd = opt;
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ ret = msg_init(nlctx, ETHNL_CMD_SET_PARAMS, NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_put_dev(nlctx, ETHA_PARAMS_DEV, ctx->devname))
+ return -EMSGSIZE;
+
+ nest = ethnla_nest_start(nlctx, nest_type);
+ if (!nest) {
+ fprintf(stderr, "ethtool(%s): failed to allocate message\n",
+ opt);
+ return 76;
+ }
+
+ ret = nl_parser(ctx, params, max_type);
+ if (ret < 0)
+ return 2;
+ mnl_attr_nest_end(nlctx->nlhdr, nest);
+
+ ret = ethnl_sendmsg(nlctx);
+ if (ret < 0)
+ return 75;
+ ret = ethnl_process_reply(nlctx, nomsg_reply_cb);
+ if (ret == 0)
+ return 0;
+ return nlctx->exit_code ?: 81;
+}
+
+static const struct param_parser scoalesce_params[] = {
+ {
+ .arg = "adaptive-rx",
+ .type = ETHA_COALESCE_RX_USE_ADAPTIVE,
+ .handler = nl_parse_bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "adaptive-tx",
+ .type = ETHA_COALESCE_TX_USE_ADAPTIVE,
+ .handler = nl_parse_bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "sample-interval",
+ .type = ETHA_COALESCE_RATE_SAMPLE_INTERVAL,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "stats-block-usecs",
+ .type = ETHA_COALESCE_STATS_BLOCK_USECS,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "pkt-rate-low",
+ .type = ETHA_COALESCE_PKT_RATE_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "pkt-rate-high",
+ .type = ETHA_COALESCE_PKT_RATE_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs",
+ .type = ETHA_COALESCE_RX_USECS,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames",
+ .type = ETHA_COALESCE_RX_MAXFRM,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs-irq",
+ .type = ETHA_COALESCE_RX_USECS_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames-irq",
+ .type = ETHA_COALESCE_RX_MAXFRM_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs",
+ .type = ETHA_COALESCE_TX_USECS,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames",
+ .type = ETHA_COALESCE_TX_MAXFRM,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs-irq",
+ .type = ETHA_COALESCE_TX_USECS_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames-irq",
+ .type = ETHA_COALESCE_TX_MAXFRM_IRQ,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs-low",
+ .type = ETHA_COALESCE_RX_USECS_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames-low",
+ .type = ETHA_COALESCE_RX_MAXFRM_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs-low",
+ .type = ETHA_COALESCE_TX_USECS_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames-low",
+ .type = ETHA_COALESCE_TX_MAXFRM_LOW,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-usecs-high",
+ .type = ETHA_COALESCE_RX_USECS_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-frames-high",
+ .type = ETHA_COALESCE_RX_MAXFRM_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-usecs-high",
+ .type = ETHA_COALESCE_TX_USECS_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-frames-high",
+ .type = ETHA_COALESCE_TX_MAXFRM_HIGH,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_scoalesce(struct cmd_context *ctx)
+{
+ return nl_set_param(ctx, "-C", scoalesce_params, ETHA_PARAMS_COALESCE,
+ ETHA_COALESCE_MAX);
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 19/23] netlink: add netlink handler for sring (-G)
From: Michal Kubecek @ 2018-07-30 12:57 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool -G <dev>" subcommand using netlink interface command
ETHNL_CMD_SET_PARAMS.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
ethtool.c | 3 ++-
netlink/extapi.h | 1 +
netlink/params.c | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/ethtool.c b/ethtool.c
index 8d755b3933a9..792283c46d35 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4886,6 +4886,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_geee NULL
#define nl_gfec NULL
#define nl_scoalesce NULL
+#define nl_sring NULL
#endif
static const struct option {
@@ -4944,7 +4945,7 @@ static const struct option {
" [sample-interval N]\n" },
{ "-g|--show-ring", 1, do_gring, nl_gring,
"Query RX/TX ring parameters" },
- { "-G|--set-ring", 1, do_sring, NULL,
+ { "-G|--set-ring", 1, do_sring, nl_sring,
"Set RX/TX ring parameters",
" [ rx N ]\n"
" [ rx-mini N ]\n"
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 2379309f084e..d6d01ee9eae3 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -25,6 +25,7 @@ int nl_gchannels(struct cmd_context *ctx);
int nl_geee(struct cmd_context *ctx);
int nl_gfec(struct cmd_context *ctx);
int nl_scoalesce(struct cmd_context *ctx);
+int nl_sring(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/params.c b/netlink/params.c
index b86922b8d215..ee0d5e0c51c6 100644
--- a/netlink/params.c
+++ b/netlink/params.c
@@ -510,3 +510,37 @@ int nl_scoalesce(struct cmd_context *ctx)
return nl_set_param(ctx, "-C", scoalesce_params, ETHA_PARAMS_COALESCE,
ETHA_COALESCE_MAX);
}
+
+static const struct param_parser sring_params[] = {
+ {
+ .arg = "rx",
+ .type = ETHA_RING_RX_PENDING,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-mini",
+ .type = ETHA_RING_RX_MINI_PENDING,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-jumbo",
+ .type = ETHA_RING_RX_JUMBO_PENDING,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx",
+ .type = ETHA_RING_TX_PENDING,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_sring(struct cmd_context *ctx)
+{
+ return nl_set_param(ctx, "-G", sring_params, ETHA_PARAMS_RING,
+ ETHA_RING_MAX);
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 21/23] netlink: add netlink handler for schannels (-L)
From: Michal Kubecek @ 2018-07-30 12:57 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool -L <dev>" subcommand using netlink interface command
ETHNL_CMD_SET_PARAMS.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
ethtool.c | 3 ++-
netlink/extapi.h | 1 +
netlink/params.c | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/ethtool.c b/ethtool.c
index b6e322af9c8a..9ac7f3c57ecb 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4888,6 +4888,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_scoalesce NULL
#define nl_sring NULL
#define nl_spause NULL
+#define nl_schannels NULL
#endif
static const struct option {
@@ -5041,7 +5042,7 @@ static const struct option {
" N\n"},
{ "-l|--show-channels", 1, do_gchannels, nl_gchannels,
"Query Channels" },
- { "-L|--set-channels", 1, do_schannels, NULL,
+ { "-L|--set-channels", 1, do_schannels, nl_schannels,
"Set Channels",
" [ rx N ]\n"
" [ tx N ]\n"
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 8cf1d0093aaa..bc33be82bdd5 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -27,6 +27,7 @@ int nl_gfec(struct cmd_context *ctx);
int nl_scoalesce(struct cmd_context *ctx);
int nl_sring(struct cmd_context *ctx);
int nl_spause(struct cmd_context *ctx);
+int nl_schannels(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/params.c b/netlink/params.c
index 953678a0b227..5e0f0d264187 100644
--- a/netlink/params.c
+++ b/netlink/params.c
@@ -572,3 +572,37 @@ int nl_spause(struct cmd_context *ctx)
return nl_set_param(ctx, "-A", spause_params, ETHA_PARAMS_PAUSE,
ETHA_PAUSE_MAX);
}
+
+static const struct param_parser schannels_params[] = {
+ {
+ .arg = "rx",
+ .type = ETHA_CHANNELS_RX_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx",
+ .type = ETHA_CHANNELS_TX_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "other",
+ .type = ETHA_CHANNELS_OTHER_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "combined",
+ .type = ETHA_CHANNELS_COMBINED_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_schannels(struct cmd_context *ctx)
+{
+ return nl_set_param(ctx, "-L", schannels_params, ETHA_PARAMS_CHANNELS,
+ ETHA_CHANNELS_MAX);
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 22/23] netlink: add netlink handler for seee (--set-eee)
From: Michal Kubecek @ 2018-07-30 12:57 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool --set-eee <dev>" subcommand using netlink interface
command ETHNL_CMD_SET_PARAMS.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
ethtool.c | 5 +++--
netlink/extapi.h | 1 +
netlink/params.c | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/ethtool.c b/ethtool.c
index 9ac7f3c57ecb..bd85b0c50ce2 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4889,6 +4889,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_sring NULL
#define nl_spause NULL
#define nl_schannels NULL
+#define nl_seee NULL
#endif
static const struct option {
@@ -5061,10 +5062,10 @@ static const struct option {
" [ length N ]\n" },
{ "--show-eee", 1, do_geee, nl_geee,
"Show EEE settings"},
- { "--set-eee", 1, do_seee, NULL,
+ { "--set-eee", 1, do_seee, nl_seee,
"Set EEE settings",
" [ eee on|off ]\n"
- " [ advertise %x ]\n"
+ " [ advertise %x[/%x] | mode on|off ... ]\n"
" [ tx-lpi on|off ]\n"
" [ tx-timer %d ]\n"},
{ "--set-phy-tunable", 1, do_set_phy_tunable, NULL,
diff --git a/netlink/extapi.h b/netlink/extapi.h
index bc33be82bdd5..80ed4afb5227 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -28,6 +28,7 @@ int nl_scoalesce(struct cmd_context *ctx);
int nl_sring(struct cmd_context *ctx);
int nl_spause(struct cmd_context *ctx);
int nl_schannels(struct cmd_context *ctx);
+int nl_seee(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/params.c b/netlink/params.c
index 5e0f0d264187..1cc2ffbc4b27 100644
--- a/netlink/params.c
+++ b/netlink/params.c
@@ -606,3 +606,37 @@ int nl_schannels(struct cmd_context *ctx)
return nl_set_param(ctx, "-L", schannels_params, ETHA_PARAMS_CHANNELS,
ETHA_CHANNELS_MAX);
}
+
+static const struct param_parser seee_params[] = {
+ {
+ .arg = "advertise",
+ .type = ETHA_EEE_LINK_MODES,
+ .handler = nl_parse_bitset,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-lpi",
+ .type = ETHA_EEE_TX_LPI_ENABLED,
+ .handler = nl_parse_bool,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-timer",
+ .type = ETHA_EEE_TX_LPI_TIMER,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "eee",
+ .type = ETHA_EEE_ENABLED,
+ .handler = nl_parse_bool,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_seee(struct cmd_context *ctx)
+{
+ return nl_set_param(ctx, "--set-eee", seee_params, ETHA_PARAMS_EEE,
+ ETHA_EEE_MAX);
+}
--
2.18.0
^ permalink raw reply related
* [RFC PATCH ethtool v2 23/23] netlink: add netlink handler for sfec (--set-fec)
From: Michal Kubecek @ 2018-07-30 12:57 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, John W. Linville
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>
Implement "ethtool --set-fec <dev>" subcommand using netlink interface
command ETHNL_CMD_SET_PARAMS.
Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
ethtool.c | 3 ++-
netlink/extapi.h | 1 +
netlink/params.c | 17 +++++++++++++++++
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/ethtool.c b/ethtool.c
index bd85b0c50ce2..8160ab200a16 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -4890,6 +4890,7 @@ static int show_usage(struct cmd_context *ctx);
#define nl_spause NULL
#define nl_schannels NULL
#define nl_seee NULL
+#define nl_sfec NULL
#endif
static const struct option {
@@ -5099,7 +5100,7 @@ static const struct option {
" [ all ]\n"},
{ "--show-fec", 1, do_gfec, nl_gfec,
"Show FEC settings"},
- { "--set-fec", 1, do_sfec, NULL,
+ { "--set-fec", 1, do_sfec, nl_sfec,
"Set FEC settings",
" [ encoding auto|off|rs|baser ]\n"},
{ "-h|--help", 0, show_usage, NULL,
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 80ed4afb5227..1938c6075758 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -29,6 +29,7 @@ int nl_sring(struct cmd_context *ctx);
int nl_spause(struct cmd_context *ctx);
int nl_schannels(struct cmd_context *ctx);
int nl_seee(struct cmd_context *ctx);
+int nl_sfec(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void monitor_usage();
diff --git a/netlink/params.c b/netlink/params.c
index 1cc2ffbc4b27..95a54d0d2978 100644
--- a/netlink/params.c
+++ b/netlink/params.c
@@ -640,3 +640,20 @@ int nl_seee(struct cmd_context *ctx)
return nl_set_param(ctx, "--set-eee", seee_params, ETHA_PARAMS_EEE,
ETHA_EEE_MAX);
}
+
+static const struct param_parser sfec_params[] = {
+ {
+ .arg = "modes",
+ .type = ETHA_FEC_MODES,
+ .handler = nl_parse_bitfield32,
+ .handler_data = flags_fecenc,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_sfec(struct cmd_context *ctx)
+{
+ return nl_set_param(ctx, "--set-fec", sfec_params, ETHA_PARAMS_FEC,
+ ETHA_FEC_MAX);
+}
--
2.18.0
^ permalink raw reply related
* Re: [PATCH 00/10] mscc: ocelot: add support for SerDes muxing configuration
From: Quentin Schulz @ 2018-07-30 13:01 UTC (permalink / raw)
To: alexandre.belloni, ralf, paul.burton, jhogan, robh+dt,
mark.rutland, davem
Cc: kishon, andrew, f.fainelli, linux-mips, devicetree, linux-kernel,
netdev, allan.nielsen, thomas.petazzoni
In-Reply-To: <cover.aa759035f6eefdd0bb2a5ae335dab5bd5399bd46.1532954208.git-series.quentin.schulz@bootlin.com>
[-- Attachment #1: Type: text/plain, Size: 3558 bytes --]
Hi again,
On Mon, Jul 30, 2018 at 02:43:45PM +0200, Quentin Schulz wrote:
> The Ocelot switch has currently a hardcoded SerDes muxing that suits only
> a particular use case. Any other board setup will fail to work.
>
> To prepare for upcoming boards' support that do not have the same muxing,
> create a PHY driver that will handle all possible cases.
>
> A SerDes can work in SGMII, QSGMII or PCIe and is also muxed to use a
> given port depending on the selected mode or board design.
>
> The SerDes configuration is in the middle of an address space (HSIO) that
> is used to configure some parts in the MAC controller driver, that is why
> we need to use a syscon so that we can write to the same address space from
> different drivers safely using regmap.
>
> Patches from generic PHY and net should be safe to be merged separately.
>
> I suggest patches 1 to 5 and 10 go through net while the others (6 to 9)
> go through the generic PHY subsystem.
>
Actually more like the following:
1 and 8 through MIPS tree, 2 to 5 and 10 through net, 6, 7 and 9 through
PHY.
Quentin
> Thanks,
> Quentin
>
> Quentin Schulz (10):
> MIPS: mscc: ocelot: make HSIO registers address range a syscon
> dt-bindings: net: ocelot: remove hsio from the list of register address spaces
> net: mscc: ocelot: get HSIO regmap from syscon
> net: mscc: ocelot: move the HSIO header to include/soc
> net: mscc: ocelot: simplify register access for PLL5 configuration
> phy: add QSGMII and PCIE modes
> dt-bindings: phy: add DT binding for Microsemi Ocelot SerDes muxing
> MIPS: mscc: ocelot: add SerDes mux DT node
> phy: add driver for Microsemi Ocelot SerDes muxing
> net: mscc: ocelot: make use of SerDes PHYs for handling their configuration
>
> Documentation/devicetree/bindings/mips/mscc.txt | 16 +-
> Documentation/devicetree/bindings/net/mscc-ocelot.txt | 9 +-
> Documentation/devicetree/bindings/phy/phy-ocelot-serdes.txt | 42 +-
> arch/mips/boot/dts/mscc/ocelot.dtsi | 19 +-
> drivers/net/ethernet/mscc/Kconfig | 2 +-
> drivers/net/ethernet/mscc/ocelot.c | 16 +-
> drivers/net/ethernet/mscc/ocelot.h | 79 +-
> drivers/net/ethernet/mscc/ocelot_board.c | 54 +-
> drivers/net/ethernet/mscc/ocelot_hsio.h | 785 +------
> drivers/net/ethernet/mscc/ocelot_regs.c | 93 +-
> drivers/phy/Kconfig | 1 +-
> drivers/phy/Makefile | 1 +-
> drivers/phy/mscc/Kconfig | 11 +-
> drivers/phy/mscc/Makefile | 5 +-
> drivers/phy/mscc/phy-ocelot-serdes.c | 314 +++-
> include/linux/phy/phy.h | 2 +-
> include/soc/mscc/ocelot_hsio.h | 859 +++++++-
> 17 files changed, 1343 insertions(+), 965 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/phy/phy-ocelot-serdes.txt
> delete mode 100644 drivers/net/ethernet/mscc/ocelot_hsio.h
> create mode 100644 drivers/phy/mscc/Kconfig
> create mode 100644 drivers/phy/mscc/Makefile
> create mode 100644 drivers/phy/mscc/phy-ocelot-serdes.c
> create mode 100644 include/soc/mscc/ocelot_hsio.h
>
> base-commit: d6e74c71c4de5222f147b64bf747e8a3c523c690
> --
> git-series 0.9.1
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [PATCH net-next 1/4] net: phy: mscc: factorize code for LEDs mode
From: Quentin Schulz @ 2018-07-30 13:02 UTC (permalink / raw)
To: andrew, f.fainelli, davem, robh+dt, mark.rutland, devicetree
Cc: netdev, alexandre.belloni, linux-kernel, thomas.petazzoni,
Quentin Schulz
LEDs modes are set the same way, except they are offset by 4 times the
index of the LED.
Let's factorize all the code so that it's easier to add support for the
4 LEDs of the VSC8584 PHY.
Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com>
---
drivers/net/phy/mscc.c | 66 ++++++++++++++++++++----------------------
1 file changed, 32 insertions(+), 34 deletions(-)
diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c
index 84ca9ff40ae0..02954cc0d2ad 100644
--- a/drivers/net/phy/mscc.c
+++ b/drivers/net/phy/mscc.c
@@ -54,9 +54,9 @@ enum rgmii_rx_clock_delay {
#define HP_AUTO_MDIX_X_OVER_IND_MASK 0x2000
#define MSCC_PHY_LED_MODE_SEL 29
-#define LED_1_MODE_SEL_MASK 0x00F0
-#define LED_0_MODE_SEL_MASK 0x000F
-#define LED_1_MODE_SEL_POS 4
+#define LED_MODE_SEL_POS(x) ((x) * 4)
+#define LED_MODE_SEL_MASK(x) (GENMASK(3, 0) << LED_MODE_SEL_POS(x))
+#define LED_MODE_SEL(x, mode) (((mode) << LED_MODE_SEL_POS(x)) & LED_MODE_SEL_MASK(x))
#define MSCC_EXT_PAGE_ACCESS 31
#define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */
@@ -103,10 +103,11 @@ enum rgmii_rx_clock_delay {
#define DOWNSHIFT_COUNT_MAX 5
+#define MAX_LEDS 4
struct vsc8531_private {
int rate_magic;
- u8 led_0_mode;
- u8 led_1_mode;
+ u8 leds_mode[MAX_LEDS];
+ u8 nleds;
};
#ifdef CONFIG_OF_MDIO
@@ -140,14 +141,8 @@ static int vsc85xx_led_cntl_set(struct phy_device *phydev,
mutex_lock(&phydev->lock);
reg_val = phy_read(phydev, MSCC_PHY_LED_MODE_SEL);
- if (led_num) {
- reg_val &= ~LED_1_MODE_SEL_MASK;
- reg_val |= (((u16)mode << LED_1_MODE_SEL_POS) &
- LED_1_MODE_SEL_MASK);
- } else {
- reg_val &= ~LED_0_MODE_SEL_MASK;
- reg_val |= ((u16)mode & LED_0_MODE_SEL_MASK);
- }
+ reg_val &= ~LED_MODE_SEL_MASK(led_num);
+ reg_val |= LED_MODE_SEL(led_num, (u16)mode);
rc = phy_write(phydev, MSCC_PHY_LED_MODE_SEL, reg_val);
mutex_unlock(&phydev->lock);
@@ -545,7 +540,7 @@ static int vsc85xx_set_tunable(struct phy_device *phydev,
static int vsc85xx_config_init(struct phy_device *phydev)
{
- int rc;
+ int rc, i;
struct vsc8531_private *vsc8531 = phydev->priv;
rc = vsc85xx_default_config(phydev);
@@ -560,13 +555,12 @@ static int vsc85xx_config_init(struct phy_device *phydev)
if (rc)
return rc;
- rc = vsc85xx_led_cntl_set(phydev, 1, vsc8531->led_1_mode);
- if (rc)
- return rc;
-
- rc = vsc85xx_led_cntl_set(phydev, 0, vsc8531->led_0_mode);
- if (rc)
- return rc;
+ /* Support for only 2 LEDs */
+ for (i = 0; i < vsc8531->nleds; i++) {
+ rc = vsc85xx_led_cntl_set(phydev, i, vsc8531->leds_mode[i]);
+ if (rc)
+ return rc;
+ }
rc = genphy_config_init(phydev);
@@ -627,6 +621,10 @@ static int vsc85xx_probe(struct phy_device *phydev)
struct vsc8531_private *vsc8531;
int rate_magic;
int led_mode;
+ int i;
+ char led_dt_prop[19];
+ u8 default_mode[2] = {VSC8531_LINK_1000_ACTIVITY,
+ VSC8531_LINK_100_ACTIVITY};
rate_magic = vsc85xx_edge_rate_magic_get(phydev);
if (rate_magic < 0)
@@ -639,19 +637,19 @@ static int vsc85xx_probe(struct phy_device *phydev)
phydev->priv = vsc8531;
vsc8531->rate_magic = rate_magic;
-
- /* LED[0] and LED[1] mode */
- led_mode = vsc85xx_dt_led_mode_get(phydev, "vsc8531,led-0-mode",
- VSC8531_LINK_1000_ACTIVITY);
- if (led_mode < 0)
- return led_mode;
- vsc8531->led_0_mode = led_mode;
-
- led_mode = vsc85xx_dt_led_mode_get(phydev, "vsc8531,led-1-mode",
- VSC8531_LINK_100_ACTIVITY);
- if (led_mode < 0)
- return led_mode;
- vsc8531->led_1_mode = led_mode;
+ vsc8531->nleds = 2;
+
+ for (i = 0; i < vsc8531->nleds; i++) {
+ led_mode = sprintf(led_dt_prop, "vsc8531,led-%d-mode", i);
+ if (led_mode < 0)
+ return led_mode;
+
+ led_mode = vsc85xx_dt_led_mode_get(phydev, led_dt_prop,
+ default_mode[i]);
+ if (led_mode < 0)
+ return led_mode;
+ vsc8531->leds_mode[i] = led_mode;
+ }
return 0;
}
--
2.17.1
^ permalink raw reply related
* [PATCH 2/4] dt-bindings: net: phy: mscc: vsc8531: remove compatible from required properties
From: Quentin Schulz @ 2018-07-30 13:02 UTC (permalink / raw)
To: andrew, f.fainelli, davem, robh+dt, mark.rutland, devicetree
Cc: netdev, alexandre.belloni, linux-kernel, thomas.petazzoni,
Quentin Schulz
In-Reply-To: <20180730130236.3837-1-quentin.schulz@bootlin.com>
Compatible isn't a required property for PHYs so let's remove it from
the binding DT of the VSC8531 PHYs.
Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com>
---
Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt | 5 -----
1 file changed, 5 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt b/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt
index 0eedabe22cc3..664d9d0543fc 100644
--- a/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt
+++ b/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt
@@ -1,10 +1,5 @@
* Microsemi - vsc8531 Giga bit ethernet phy
-Required properties:
-- compatible : Should contain phy id as "ethernet-phy-idAAAA.BBBB"
- The PHY device uses the binding described in
- Documentation/devicetree/bindings/net/phy.txt
-
Optional properties:
- vsc8531,vddmac : The vddmac in mV. Allowed values is listed
in the first row of Table 1 (below).
--
2.17.1
^ permalink raw reply related
* [PATCH 3/4] dt-bindings: net: phy: mscc: vsc8531: fix missing "/bits/ 8" in example
From: Quentin Schulz @ 2018-07-30 13:02 UTC (permalink / raw)
To: andrew, f.fainelli, davem, robh+dt, mark.rutland, devicetree
Cc: netdev, alexandre.belloni, linux-kernel, thomas.petazzoni,
Quentin Schulz
In-Reply-To: <20180730130236.3837-1-quentin.schulz@bootlin.com>
The "vsc8531,led-N-mode" property is read as a u8 in the driver and
there aren't a lot of modes anyway.
Without the "/bits/ 8" in front of the value of the property, the
value is stored as an u32 resulting in of_read_property_u8 to always
return 0.
Fix the example so that people using the property can actually use it.
Signed-off-by: Quentin Schulz <quentin.schulz@bootlin.com>
---
Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt b/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt
index 664d9d0543fc..4c7d1d384df0 100644
--- a/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt
+++ b/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt
@@ -63,6 +63,6 @@ Example:
compatible = "ethernet-phy-id0007.0570";
vsc8531,vddmac = <3300>;
vsc8531,edge-slowdown = <7>;
- vsc8531,led-0-mode = <LINK_1000_ACTIVITY>;
- vsc8531,led-1-mode = <LINK_100_ACTIVITY>;
+ vsc8531,led-0-mode = /bits/ 8 <LINK_1000_ACTIVITY>;
+ vsc8531,led-1-mode = /bits/ 8 <LINK_100_ACTIVITY>;
};
--
2.17.1
^ permalink raw reply related
* [PATCH net-next] fib_rules: NULL check before kfree is not needed
From: YueHaibing @ 2018-07-30 13:07 UTC (permalink / raw)
To: davem, roopa
Cc: linux-kernel, netdev, idosch, dsahern, ktkhai, sharpd, YueHaibing
kfree(NULL) is safe,so this removes NULL check before freeing the mem
Signed-off-by: YueHaibing <yuehaibing@huawei.com>
---
net/core/fib_rules.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index f64aa13..0ff3953 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -924,8 +924,7 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
return 0;
errout:
- if (nlrule)
- kfree(nlrule);
+ kfree(nlrule);
rules_ops_put(ops);
return err;
}
--
2.7.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox