Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net-next v3 0/2] bpf/tracing: allow user space to query prog array on the same tp
From: Daniel Borkmann @ 2017-12-11 13:15 UTC (permalink / raw)
  To: Yonghong Song, peterz, rostedt, ast, kafai, netdev; +Cc: kernel-team
In-Reply-To: <20171206220746.4038383-1-yhs@fb.com>

Hey Yonghong,

On 12/06/2017 11:07 PM, Yonghong Song wrote:
> Commit e87c6bc3852b ("bpf: permit multiple bpf attachments
> for a single perf event") added support to attach multiple
> bpf programs to a single perf event. Given a perf event
> (kprobe, uprobe, or kernel tracepoint), the perf ioctl interface
> is used to query bpf programs attached to the same trace event.
> 
> There already exists a BPF_PROG_QUERY command for introspection
> currently used by cgroup+bpf. We did have an implementation for
> querying tracepoint+bpf through the same interface. However, it
> looks cleaner to use ioctl() style of api here, since attaching
> bpf prog to tracepoint/kuprobe is also done via ioctl.
> 
> Patch #1 had the core implementation and patch #2 added
> a test case in tools bpf selftests suite.
> 
> Changelogs:
> v2 -> v3:
>   - Change uapi structure perf_event_query_bpf to be more
>     clearer based on Peter's suggestion, and adjust
>     other codes accordingly.
> v1 -> v2:
>   - Rebase on top of net-next.
>   - Use existing bpf_prog_array_length function instead of
>     implementing the same functionality in function
>     bpf_prog_array_copy_info.
> 
> Yonghong Song (2):
>   bpf/tracing: allow user space to query prog array on the same tp
>   bpf/tracing: add a bpf test for new ioctl query interface

v3 doesn't build cleanly, could you take a look & fix?

Thanks,
Daniel

$ make -j4 kernel/trace/
  CHK     include/config/kernel.release
  HOSTCC  scripts/basic/fixdep
  CHK     include/generated/uapi/linux/version.h
  DESCEND  objtool
  CHK     include/generated/utsrelease.h
  HOSTCC   /home/darkstar/trees/bpf-next/tools/objtool/fixdep.o
  HOSTCC  scripts/basic/bin2c
  HOSTLD   /home/darkstar/trees/bpf-next/tools/objtool/fixdep-in.o
  LINK     /home/darkstar/trees/bpf-next/tools/objtool/fixdep
  CC       /home/darkstar/trees/bpf-next/tools/objtool/builtin-check.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/arch/x86/decode.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/builtin-orc.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/exec-cmd.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/help.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/pager.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/parse-options.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/run-command.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/sigchain.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/subcmd-config.o
  LD       /home/darkstar/trees/bpf-next/tools/objtool/arch/x86/objtool-in.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/check.o
  HOSTCC  arch/x86/tools/relocs_32.o
  HOSTCC  arch/x86/tools/relocs_64.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/orc_gen.o
  HOSTCC  arch/x86/tools/relocs_common.o
  LD       /home/darkstar/trees/bpf-next/tools/objtool/libsubcmd-in.o
  AR       /home/darkstar/trees/bpf-next/tools/objtool/libsubcmd.a
  HOSTLD  arch/x86/tools/relocs
  CC       /home/darkstar/trees/bpf-next/tools/objtool/orc_dump.o
  CC      scripts/mod/empty.o
  HOSTCC  scripts/mod/mk_elfconfig
  CC      arch/x86/purgatory/purgatory.o
  AS      arch/x86/purgatory/stack.o
  CC      scripts/mod/devicetable-offsets.s
  AS      arch/x86/purgatory/setup-x86_64.o
  HOSTCC  scripts/kallsyms
  CC      arch/x86/purgatory/sha256.o
  MKELF   scripts/mod/elfconfig.h
  CHK     scripts/mod/devicetable-offsets.h
  HOSTCC  scripts/mod/sumversion.o
  HOSTCC  scripts/pnmtologo
  HOSTCC  scripts/mod/modpost.o
  AS      arch/x86/purgatory/entry64.o
  CC      arch/x86/purgatory/string.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/elf.o
  LD      arch/x86/purgatory/purgatory.ro
  HOSTCC  scripts/conmakehash
  BIN2C   arch/x86/purgatory/kexec-purgatory.c
  CC      kernel/bounds.s
  CHK     include/generated/timeconst.h
  CHK     include/generated/bounds.h
  CC      arch/x86/kernel/asm-offsets.s
  HOSTCC  scripts/recordmcount
  CC       /home/darkstar/trees/bpf-next/tools/objtool/special.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/objtool.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/libstring.o
  CC       /home/darkstar/trees/bpf-next/tools/objtool/str_error_r.o
  LD       /home/darkstar/trees/bpf-next/tools/objtool/objtool-in.o
  HOSTCC  scripts/mod/file2alias.o
Warning: synced file at 'tools/objtool/arch/x86/lib/x86-opcode-map.txt' differs from latest kernel version at 'arch/x86/lib/x86-opcode-map.txt'
  LINK     /home/darkstar/trees/bpf-next/tools/objtool/objtool
  HOSTCC  scripts/sortextable
  CHK     include/generated/asm-offsets.h
  HOSTCC  scripts/asn1_compiler
  CALL    scripts/checksyscalls.sh
  HOSTCC  scripts/sign-file
  HOSTCC  scripts/extract-cert
  HOSTLD  scripts/mod/modpost
  CC      kernel/trace/trace_clock.o
  CC      kernel/trace/ftrace.o
  CC      kernel/trace/ring_buffer.o
  CC      kernel/trace/trace.o
  CC      kernel/trace/trace_output.o
  CC      kernel/trace/trace_seq.o
  CC      kernel/trace/trace_stat.o
  CC      kernel/trace/trace_printk.o
  CC      kernel/trace/trace_sched_switch.o
  CC      kernel/trace/trace_functions.o
  CC      kernel/trace/trace_nop.o
  CC      kernel/trace/trace_functions_graph.o
  CC      kernel/trace/blktrace.o
  CC      kernel/trace/trace_events.o
  CC      kernel/trace/trace_export.o
  CC      kernel/trace/trace_syscalls.o
  CC      kernel/trace/trace_event_perf.o
  CC      kernel/trace/trace_events_filter.o
  CC      kernel/trace/trace_events_trigger.o
  CC      kernel/trace/bpf_trace.o
  CC      kernel/trace/trace_kprobe.o
kernel/trace/bpf_trace.c: In function ‘bpf_event_query_prog_array’:
kernel/trace/bpf_trace.c:839:12: error: passing argument 2 of ‘bpf_prog_array_copy_info’ from incompatible pointer type [-Werror=incompatible-pointer-types]
            &uquery->ids,
            ^
In file included from kernel/trace/bpf_trace.c:11:0:
./include/linux/bpf.h:289:5: note: expected ‘__u32 * {aka unsigned int *}’ but argument is of type ‘__u32 (*)[]’
 int bpf_prog_array_copy_info(struct bpf_prog_array __rcu *array,
     ^~~~~~~~~~~~~~~~~~~~~~~~
  CC      kernel/trace/power-traces.o
  CC      kernel/trace/rpm-traces.o
cc1: some warnings being treated as errors
scripts/Makefile.build:310: recipe for target 'kernel/trace/bpf_trace.o' failed
make[1]: *** [kernel/trace/bpf_trace.o] Error 1
make[1]: *** Waiting for unfinished jobs....
Makefile:1674: recipe for target 'kernel/trace/' failed
make: *** [kernel/trace/] Error 2

^ permalink raw reply

* [PATCH net-next 0/2] net: tap: two minor issue
From: yuan linyu @ 2017-12-11 13:26 UTC (permalink / raw)
  To: netdev; +Cc: David S . Miller, yuan linyu

From: yuan linyu <Linyu.Yuan@alcatel-sbell.com.cn>

patch1: replay while() loop with % operator in tap_get_queue()
patch2: fix POLLOUT condition in tap_poll()

---
yuan linyu (2):
  net: tap: replay while() loop with % operator in tap_get_queue()
  net: tap: fix POLLOUT condition in tap_poll()

 drivers/net/tap.c | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

-- 
2.7.4

^ permalink raw reply

* [PATCH net-next 1/2] net: tap: replay while() loop with % operator in tap_get_queue()
From: yuan linyu @ 2017-12-11 13:26 UTC (permalink / raw)
  To: netdev; +Cc: David S . Miller, yuan linyu

From: yuan linyu <Linyu.Yuan@alcatel-sbell.com.cn>

Signed-off-by: yuan linyu <Linyu.Yuan@alcatel-sbell.com.cn>
---
 drivers/net/tap.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index 0a886fda..78900a0 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -275,11 +275,7 @@ static struct tap_queue *tap_get_queue(struct tap_dev *tap,
 
 	if (likely(skb_rx_queue_recorded(skb))) {
 		rxq = skb_get_rx_queue(skb);
-
-		while (unlikely(rxq >= numvtaps))
-			rxq -= numvtaps;
-
-		queue = rcu_dereference(tap->taps[rxq]);
+		queue = rcu_dereference(tap->taps[rxq % numvtaps]);
 		goto out;
 	}
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 2/2] net: tap: fix POLLOUT condition in tap_poll()
From: yuan linyu @ 2017-12-11 13:27 UTC (permalink / raw)
  To: netdev; +Cc: David S . Miller, yuan linyu

From: yuan linyu <Linyu.Yuan@alcatel-sbell.com.cn>

from logical view, if sock_writeable(&q->sk) return false,
original second condition will false too,
change it and make second condition can return true.

Signed-off-by: yuan linyu <Linyu.Yuan@alcatel-sbell.com.cn>
---
 drivers/net/tap.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index 78900a0..b809405 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -583,8 +583,7 @@ static unsigned int tap_poll(struct file *file, poll_table *wait)
 		mask |= POLLIN | POLLRDNORM;
 
 	if (sock_writeable(&q->sk) ||
-	    (!test_and_set_bit(SOCKWQ_ASYNC_NOSPACE, &q->sock.flags) &&
-	     sock_writeable(&q->sk)))
+	    !test_and_set_bit(SOCKWQ_ASYNC_NOSPACE, &q->sock.flags))
 		mask |= POLLOUT | POLLWRNORM;
 
 out:
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH net-next v5 2/2] net: ethernet: socionext: add AVE ethernet driver
From: Masami Hiramatsu @ 2017-12-11 13:34 UTC (permalink / raw)
  To: Kunihiko Hayashi
  Cc: Philippe Ombredanne, David S. Miller,
	netdev-u79uwXL29TY76Z2rM5mHXA, Andrew Lunn, Florian Fainelli,
	Rob Herring, Mark Rutland, linux-arm-kernel,
	Linux kernel mailing list, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Masahiro Yamada, Jassi Brar
In-Reply-To: <20171211210455.170A.4A936039-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>

Hello,

2017-12-11 21:04 GMT+09:00 Kunihiko Hayashi <hayashi.kunihiko-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>:
> On Mon, 11 Dec 2017 10:19:15 +0100 Phlippe Ombredanne <pombredanne-od1rfyK75/E@public.gmane.org> wrote:
>
>> Dear Kunihiko-san,
>>
>> On Mon, Dec 11, 2017 at 8:57 AM, Kunihiko Hayashi
>> <hayashi.kunihiko-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org> wrote:
>> > The UniPhier platform from Socionext provides the AVE ethernet
>> > controller that includes MAC and MDIO bus supporting RGMII/RMII
>> > modes. The controller is named AVE.
>> >
>> > Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
>> > Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> > Reviewed-by: Andrew Lunn <andrew-g2DYL2Zd6BY@public.gmane.org>
>> [...]
>> > --- /dev/null
>> > +++ b/drivers/net/ethernet/socionext/Makefile
>> > @@ -0,0 +1,5 @@
>> > +# SPDX-License-Identifier: GPL-2.0
>>
>> You are correctly using SPDX ids here....
>>
>> > +obj-$(CONFIG_SNI_AVE) += sni_ave.o
>> > diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c
>> > new file mode 100644
>> > index 0000000..7b293c2
>> > --- /dev/null
>> > +++ b/drivers/net/ethernet/socionext/sni_ave.c
>> > @@ -0,0 +1,1744 @@
>> > +/**
>> > + * sni_ave.c - Socionext UniPhier AVE ethernet driver
>> > + *
>> > + * Copyright 2014 Panasonic Corporation
>> > + * Copyright 2015-2017 Socionext Inc.
>> > + *
>> > + * This program is free software: you can redistribute it and/or modify
>> > + * it under the terms of the GNU General Public License version 2  of
>> > + * the License as published by the Free Software Foundation.
>> > + *
>> > + * This program is distributed in the hope that it will be useful,
>> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> > + * GNU General Public License for more details.
>> > + */
>>
>> ... then I guess you could also use them here, replacing at least 7
>> lines of boilerplate by a single id line?
>>
>> > +// SDPX-License-Identifier: GPL-2.0
>
> Thank you for your kindly comment.
>
> It seems there are few ethernet drivers applying SPDX-License-Identifier
> to C source, then it has been difficult for me to decide whether to replace
> boilerplate.
>
> If it's no problem to apply SPDX to sources of ethernet drivers,
> I'll replace it with the single line.
>
>> And if you go C++ style all the way, this could be even more compact:
>>
>> > +// SDPX-License-Identifier: GPL-2.0
>> > +// sni_ave.c - Socionext UniPhier AVE ethernet driver
>> > +// Copyright 2014 Panasonic Corporation
>> > +// Copyright 2015-2017 Socionext Inc.
>
> It's much simpler.
> If it's reasonable to apply this style here, I can replace it, too.

IMHO, even if we use SPDX license identifier, I recommend to use
C-style comments as many other files do, since it is C code.
If SPDX identifier requires C++ style, that is SPDX parser's issue
and should be fixed to get it from C-style comment.

https://spdx.org/sites/cpstandard/files/pages/files/using_spdx_license_list_short_identifiers.pdf

Anyway, SPDX.org itself shows C-style (short header) example. See page 6.

Thank you,

>
> Thank you,
>
> ---
> Best Regards,
> Kunihiko Hayashi
>
>



-- 
Masami Hiramatsu
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH net-next v5 1/4] phylib: Add device reset delay support
From: Andrew Lunn @ 2017-12-11 13:40 UTC (permalink / raw)
  To: Richard Leitner
  Cc: robh+dt, mark.rutland, fugang.duan, f.fainelli, frowand.list,
	davem, geert+renesas, sergei.shtylyov, baruch, david.wu, lukma,
	netdev, devicetree, linux-kernel, richard.leitner
In-Reply-To: <20171211121700.10200-2-dev@g0hl1n.net>

On Mon, Dec 11, 2017 at 01:16:57PM +0100, Richard Leitner wrote:
> From: Richard Leitner <richard.leitner@skidata.com>
> 
> Some PHYs need a minimum time after the reset gpio was asserted and/or
> deasserted. To ensure we meet these timing requirements add two new
> optional devicetree parameters for the phy: reset-delay-us and
> reset-post-delay-us.
> 
> Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply

* Re: [PATCH net-next v5 2/2] net: ethernet: socionext: add AVE ethernet driver
From: Russell King - ARM Linux @ 2017-12-11 13:46 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Kunihiko Hayashi, Mark Rutland, Andrew Lunn, Florian Fainelli,
	devicetree, netdev, Linux kernel mailing list, Masahiro Yamada,
	Rob Herring, Philippe Ombredanne, Jassi Brar, David S. Miller,
	linux-arm-kernel
In-Reply-To: <CAA93ih2i+G2uyL8O-4F1juR1DpUYCd1U1dBCRRM5Xdd3L-m95g@mail.gmail.com>

On Mon, Dec 11, 2017 at 10:34:17PM +0900, Masami Hiramatsu wrote:
> IMHO, even if we use SPDX license identifier, I recommend to use
> C-style comments as many other files do, since it is C code.
> If SPDX identifier requires C++ style, that is SPDX parser's issue
> and should be fixed to get it from C-style comment.

See the numerous emails on this subject already.  The issue of C
vs C++ comments has come up many times by many different people, but
the result is the same.  That's not going to happen.  Linux kernel
C files are required to use "//" for the SPDX identifier by order
of Linus Torvalds.

Linus has also revealed in that discussion that he has a preference
for "//" style commenting for single comments, so it seems that the
kernel coding style may change - but there is no desire for patches
to "clean up" single line comments to use "//".

For further information, and to see the discussion that has already
happened, the arguments that have been made about style, see the
threads for the patch series that tglx has been posting wrt documenting
the SPDX stuff for the kernel.

Thanks (let's stop rehashing the same arguments.)

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up
According to speedtest.net: 8.21Mbps down 510kbps up

^ permalink raw reply

* 76f43b4 fix for stable
From: Richard Schütz @ 2017-12-11 13:51 UTC (permalink / raw)
  To: netdev, linux-wireless

Hello,

as per netdev-FAQ.txt I'm requesting the submission of commit 
57629915d568c522ac1422df7bba4bee5b5c7a7c ("mac80211: Fix addition of 
mesh configuration element") to stable. Because of automatic selection 
commit 76f43b4c0a9337af22827d78de4f2b8fd5328489 ("mac80211: Remove 
invalid flag operations in mesh TSF synchronization") was introduced 
into stable recently without this accompanying fix.

-- 
Richard

^ permalink raw reply

* [RFC PATCH 0/9] ethtool netlink interface (WiP)
From: Michal Kubecek @ 2017-12-11 13:53 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel

This is still work in progress and only a very small part of the ioctl
interface is reimplemented but I would like to get some comments before
the patchset becomes too big and changing things becomes too tedious.

The interface used for communication between ethtool and kernel is based on
ioctl() and suffers from many problems. The most pressing seems the be the
lack of extensibility. While some of the newer commands use structures
designed to allow future extensions (e.g. GFEATURES or TEST), most either
allow no extension at all (GPAUSEPARAM, GCOALESCE) or only limited set of
reserved fields (GDRVINFO, GEEE). Even most of those which support future
extensions limit the data types that can be used.

This series aims to provide an alternative interface based on netlink which
is what other network configuration utilities use. In particular, it uses
generic netlink (family "ethtool"). The goal is to provide an interface
which would be extensible, flexible and practical both for ethtool and for
other network configuration tools (e.g. wicked or systemd-networkd).

The interface is documented in Documentation/networking/ethtool-netlink.txt

I'll post RFC patch series for ethtool in a few days, it still needs some
more polishing.

Basic concepts:

- the interface is based on generic netlink (family name "ethtool")

- provide everything ioctl can do but allow easier future extensions

- inextensibility of ioctl interface resulted in way too many commands,
  many of them obsoleted by newer ones; reduce the number by  ignoring the
  obsolete commands and grouping some together

- for "set" type commands, netlink allows providing only the attributes to
  be changed; therefore we don't need a get-modify-set cycle, userspace can
  simply say what it wants to change and how

- be less dependent on ethtool and kernel being in sync; allow e.g. saying
  "ethtool -s eth0 advertise xyz off" without knowing what "xyz" means;
  it's kernel's job to know what mode "xyz" is and if it exists and is
  supported

Unresolved questions/tasks:

- allow dumps for "get" type requests, e.g. listing EEE settings for all
  interfaces in current netns

- find reasonable format for data transfers (e.g. eeprom dump or flash)

- while the netlink interface allows easy future extensions, ethtool_ops
  interface does not; some settings could be implemented using tunables and
  accessed via relevant netlink messages (as well as tunables) from
  userspace but in the long term, something better will be needed

- it would be nice if driver could provide useful error/warning messages to
  be passed to userspace via extended ACK; example: while testing, I found
  a driver which only allows values 0, 1, 3 and 10000 for certain parameter
  but the only way poor user can find out is either by trying all values or
  by checking driver source

Michal Kubecek (9):
  netlink: introduce nla_put_bitfield32()
  ethtool: introduce ethtool netlink interface
  ethtool: helper functions for netlink interface
  ethtool: netlink bitset handling
  ethtool: implement GET_DRVINFO message
  ethtool: implement GET_SETTINGS message
  ethtool: implement SET_SETTINGS message
  ethtool: implement GET_PARAMS message
  ethtool: implement SET_PARAMS message

 Documentation/networking/ethtool-netlink.txt |  466 ++++++
 include/linux/ethtool_netlink.h              |   12 +
 include/linux/netdevice.h                    |    2 +
 include/net/netlink.h                        |   15 +
 include/uapi/linux/ethtool.h                 |    3 +
 include/uapi/linux/ethtool_netlink.h         |  239 +++
 net/Kconfig                                  |    7 +
 net/core/Makefile                            |    3 +-
 net/core/ethtool.c                           |  150 +-
 net/core/ethtool_common.c                    |  158 ++
 net/core/ethtool_common.h                    |   19 +
 net/core/ethtool_netlink.c                   | 2323 ++++++++++++++++++++++++++
 12 files changed, 3260 insertions(+), 137 deletions(-)
 create mode 100644 Documentation/networking/ethtool-netlink.txt
 create mode 100644 include/linux/ethtool_netlink.h
 create mode 100644 include/uapi/linux/ethtool_netlink.h
 create mode 100644 net/core/ethtool_common.c
 create mode 100644 net/core/ethtool_common.h
 create mode 100644 net/core/ethtool_netlink.c

-- 
2.15.1

^ permalink raw reply

* [RFC PATCH 1/9] netlink: introduce nla_put_bitfield32()
From: Michal Kubecek @ 2017-12-11 13:53 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel
In-Reply-To: <cover.1513000306.git.mkubecek@suse.cz>

Similar to other data types, this helper puts NLA_BITFIELD32 attribute into
a netlink message. It takes separate value and selector arguments, if you
already have struct nla_bitfield32, you can use nla_put().

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 include/net/netlink.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/include/net/netlink.h b/include/net/netlink.h
index 0c154f98e987..6d4eb6bd9235 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -1064,6 +1064,21 @@ static inline int nla_put_in6_addr(struct sk_buff *skb, int attrtype,
 	return nla_put(skb, attrtype, sizeof(*addr), addr);
 }
 
+/**
+ * nla_put_bitfield32 - Add a bitfield32 value/selector attribute to
+ * a socket buffer
+ * @skb: socket buffer to add attribute to
+ * @value: 32-bit value bitmap
+ * @selector: 32-bit selector bitmap
+ */
+static inline int nla_put_bitfield32(struct sk_buff *skb, int attrtype,
+				     u32 value, u32 selector)
+{
+	struct nla_bitfield32 tmp = { .value = value, .selector = selector };
+
+	return nla_put(skb, attrtype, sizeof(tmp), &tmp);
+}
+
 /**
  * nla_get_u32 - return payload of u32 attribute
  * @nla: u32 netlink attribute
-- 
2.15.1

^ permalink raw reply related

* [RFC PATCH 2/9] ethtool: introduce ethtool netlink interface
From: Michal Kubecek @ 2017-12-11 13:53 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel
In-Reply-To: <cover.1513000306.git.mkubecek@suse.cz>

No function implemented yet, only genetlink and module infrastructure.
Register/unregister genetlink family "ethtool" and allow the module to be
autoloaded by genetlink code (if built as a module, distributions would
probably prefer "y").

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Documentation/networking/ethtool-netlink.txt | 155 +++++++++++++++++++++++++++
 include/linux/ethtool_netlink.h              |   9 ++
 include/uapi/linux/ethtool_netlink.h         |  33 ++++++
 net/Kconfig                                  |   7 ++
 net/core/Makefile                            |   1 +
 net/core/ethtool_netlink.c                   |  46 ++++++++
 6 files changed, 251 insertions(+)
 create mode 100644 Documentation/networking/ethtool-netlink.txt
 create mode 100644 include/linux/ethtool_netlink.h
 create mode 100644 include/uapi/linux/ethtool_netlink.h
 create mode 100644 net/core/ethtool_netlink.c

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
new file mode 100644
index 000000000000..c94da66cb5fb
--- /dev/null
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -0,0 +1,155 @@
+                        Netlink interface for ethtool
+                        =============================
+
+
+Basic information
+-----------------
+
+Netlink interface for ethtool uses generic netlink family "ethtool" (userspace
+application should use macros ETHTOOL_GENL_NAME and ETHTOOL_GENL_VERSION
+defined in <linux/ethtool_netlink.h> uapi header). This family uses a message
+header of 24 bytes:
+
+struct ethnlmsghdr {
+	__u32   ifindex;
+	__u16   flags;
+	union {
+		__u16   info_mask;
+		__u16	index;
+	}
+	char    ifname[IFNAMSIZ];
+};
+
+In requests, device can be identified by ifindex or by name; if both are used,
+they must match. In replies, kernel fills both. The meaning of flags,
+info_mask and index fields depends on request type.
+
+The ethtool netlink interface uses extended ACK for error and warning
+reporting, userspace application developers are encouraged to make these
+messages available to user in a suitable way.
+
+Requests can be divided into three categories: "get" (retrieving information),
+"set" (setting parameters) and "action" (invoking an action).
+
+All "set" and "action" type requests require admin privileges (CAP_NET_ADMIN
+in the namespace). Most "get" type request are allowed for anyone but there
+are exceptions (where the response contains sensitive information). In some
+cases, the request as such is allowed for anyone but unprivileged users have
+attributes with sensitive information (e.g. wake-on-lan password) omitted.
+
+
+Conventions
+-----------
+
+Attributes which represent a boolean value usually use u8 type so that we can
+distinguish three states: "on", "off" and "not present" (meaning the
+information is not available in "get" requests or value is not to be changed
+in "set" requests). For these attributes, the "true" value should be passed as
+number 1 but any non-zero value should be understood as "true" by recipient.
+
+
+List of message types
+---------------------
+
+All constants use ETHTOOL_CMD_ prefix followed by "GET", "SET" or "ACT" to
+indicate the type.
+
+Messages of type "get" are used by userspace to request information and
+usually do not contain any attributes (that may be added later for dump
+filtering). Kernel response is in the form of corresopinding "set" message;
+the same message can be also used to set (some of) the parameters, except for
+messages marked as "response only" in the table above.
+
+Later sections describe the format and semantics of these request messages.
+
+
+Request translation
+-------------------
+
+The following table maps iosctl commands to netlink commands providing their
+functionality. Entries with "n/a" in right column are commands which do not
+have their netlink replacement yet.
+
+ioctl command			netlink command
+---------------------------------------------------------------------
+ETHTOOL_GSET			n/a
+ETHTOOL_SSET			n/a
+ETHTOOL_GDRVINFO		n/a
+ETHTOOL_GREGS			n/a
+ETHTOOL_GWOL			n/a
+ETHTOOL_SWOL			n/a
+ETHTOOL_GMSGLVL			n/a
+ETHTOOL_SMSGLVL			n/a
+ETHTOOL_NWAY_RST		n/a
+ETHTOOL_GLINK			n/a
+ETHTOOL_GEEPROM			n/a
+ETHTOOL_SEEPROM			n/a
+ETHTOOL_GCOALESCE		n/a
+ETHTOOL_SCOALESCE		n/a
+ETHTOOL_GRINGPARAM		n/a
+ETHTOOL_SRINGPARAM		n/a
+ETHTOOL_GPAUSEPARAM		n/a
+ETHTOOL_SPAUSEPARAM		n/a
+ETHTOOL_GRXCSUM			n/a
+ETHTOOL_SRXCSUM			n/a
+ETHTOOL_GTXCSUM			n/a
+ETHTOOL_STXCSUM			n/a
+ETHTOOL_GSG			n/a
+ETHTOOL_SSG			n/a
+ETHTOOL_TEST			n/a
+ETHTOOL_GSTRINGS		n/a
+ETHTOOL_PHYS_ID			n/a
+ETHTOOL_GSTATS			n/a
+ETHTOOL_GTSO			n/a
+ETHTOOL_STSO			n/a
+ETHTOOL_GPERMADDR		n/a
+ETHTOOL_GUFO			n/a
+ETHTOOL_SUFO			n/a
+ETHTOOL_GGSO			n/a
+ETHTOOL_SGSO			n/a
+ETHTOOL_GFLAGS			n/a
+ETHTOOL_SFLAGS			n/a
+ETHTOOL_GPFLAGS			n/a
+ETHTOOL_SPFLAGS			n/a
+ETHTOOL_GRXFH			n/a
+ETHTOOL_SRXFH			n/a
+ETHTOOL_GGRO			n/a
+ETHTOOL_SGRO			n/a
+ETHTOOL_GRXRINGS		n/a
+ETHTOOL_GRXCLSRLCNT		n/a
+ETHTOOL_GRXCLSRULE		n/a
+ETHTOOL_GRXCLSRLALL		n/a
+ETHTOOL_SRXCLSRLDEL		n/a
+ETHTOOL_SRXCLSRLINS		n/a
+ETHTOOL_FLASHDEV		n/a
+ETHTOOL_RESET			n/a
+ETHTOOL_SRXNTUPLE		n/a
+ETHTOOL_GRXNTUPLE		n/a
+ETHTOOL_GSSET_INFO		n/a
+ETHTOOL_GRXFHINDIR		n/a
+ETHTOOL_SRXFHINDIR		n/a
+ETHTOOL_GFEATURES		n/a
+ETHTOOL_SFEATURES		n/a
+ETHTOOL_GCHANNELS		n/a
+ETHTOOL_SCHANNELS		n/a
+ETHTOOL_SET_DUMP		n/a
+ETHTOOL_GET_DUMP_FLAG		n/a
+ETHTOOL_GET_DUMP_DATA		n/a
+ETHTOOL_GET_TS_INFO		n/a
+ETHTOOL_GMODULEINFO		n/a
+ETHTOOL_GMODULEEEPROM		n/a
+ETHTOOL_GEEE			n/a
+ETHTOOL_SEEE			n/a
+ETHTOOL_GRSSH			n/a
+ETHTOOL_SRSSH			n/a
+ETHTOOL_GTUNABLE		n/a
+ETHTOOL_STUNABLE		n/a
+ETHTOOL_GPHYSTATS		n/a
+ETHTOOL_PERQUEUE		n/a
+ETHTOOL_GLINKSETTINGS		n/a
+ETHTOOL_SLINKSETTINGS		n/a
+ETHTOOL_PHY_GTUNABLE		n/a
+ETHTOOL_PHY_STUNABLE		n/a
+ETHTOOL_GFECPARAM		n/a
+ETHTOOL_SFECPARAM		n/a
+
diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h
new file mode 100644
index 000000000000..0412adb4f42f
--- /dev/null
+++ b/include/linux/ethtool_netlink.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef _LINUX_ETHTOOL_NETLINK_H_
+#define _LINUX_ETHTOOL_NETLINK_H_
+
+#include <uapi/linux/ethtool_netlink.h>
+#include <linux/ethtool.h>
+
+#endif /* _LINUX_ETHTOOL_NETLINK_H_ */
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
new file mode 100644
index 000000000000..06cff2b52dfe
--- /dev/null
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef _UAPI_LINUX_ETHTOOL_NETLINK_H_
+#define _UAPI_LINUX_ETHTOOL_NETLINK_H_
+
+#include <linux/ethtool.h>
+
+/* identifies the device to query/set
+ * - use either ifindex or ifname, not both
+ * - for dumps and messages not related to a particular devices, fill neither
+ * - info_mask is a bitfield, interpretation depends on the command
+ */
+struct ethnlmsghdr {
+	__u32	ifindex;		/* device ifindex */
+	__u16	flags;			/* request/response flags */
+	__u16	info_mask;		/* request/response info mask */
+	char	ifname[IFNAMSIZ];	/* device name */
+};
+
+#define ETHNL_HDRLEN NLMSG_ALIGN(sizeof(struct ethnlmsghdr))
+
+enum {
+	ETHTOOL_CMD_NOOP,
+
+	__ETHTOOL_CMD_MAX,
+	ETHTOOL_CMD_MAX = (__ETHTOOL_CMD_MAX - 1),
+};
+
+/* generic netlink info */
+#define ETHTOOL_GENL_NAME "ethtool"
+#define ETHTOOL_GENL_VERSION 1
+
+#endif /* _UAPI_LINUX_ETHTOOL_NETLINK_H_ */
diff --git a/net/Kconfig b/net/Kconfig
index 9dba2715919d..a5e3c89a2495 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -440,6 +440,13 @@ config MAY_USE_DEVLINK
 	  on MAY_USE_DEVLINK to ensure they do not cause link errors when
 	  devlink is a loadable module and the driver using it is built-in.
 
+config ETHTOOL_NETLINK
+	tristate "Netlink interface for ethtool"
+	default m
+	help
+	   New netlink based interface for ethtool which is going to obsolete
+	   the old ioctl based one once it provides all features.
+
 endif   # if NET
 
 # Used by archs to tell that they support BPF JIT compiler plus which flavour.
diff --git a/net/core/Makefile b/net/core/Makefile
index 1fd0a9c88b1b..617ab2abecdf 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_DST_CACHE) += dst_cache.o
 obj-$(CONFIG_HWBM) += hwbm.o
 obj-$(CONFIG_NET_DEVLINK) += devlink.o
 obj-$(CONFIG_GRO_CELLS) += gro_cells.o
+obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_netlink.o
diff --git a/net/core/ethtool_netlink.c b/net/core/ethtool_netlink.c
new file mode 100644
index 000000000000..46a226bb9a2c
--- /dev/null
+++ b/net/core/ethtool_netlink.c
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#include <linux/module.h>
+#include <linux/ethtool_netlink.h>
+#include <linux/netdevice.h>
+#include <net/genetlink.h>
+#include "ethtool_common.h"
+
+static struct genl_family ethtool_genl_family;
+
+/* genetlink paperwork */
+
+static const struct genl_ops ethtool_genl_ops[] = {
+};
+
+static struct genl_family ethtool_genl_family = {
+	.hdrsize	= ETHNL_HDRLEN,
+	.name		= ETHTOOL_GENL_NAME,
+	.version	= ETHTOOL_GENL_VERSION,
+	.netnsok	= true,
+	.ops		= ethtool_genl_ops,
+	.n_ops		= ARRAY_SIZE(ethtool_genl_ops),
+};
+
+/* module paperwork */
+
+static int __init ethtool_nl_init(void)
+{
+	return genl_register_family(&ethtool_genl_family);
+}
+
+static void __exit ethtool_nl_exit(void)
+{
+	genl_unregister_family(&ethtool_genl_family);
+}
+
+module_init(ethtool_nl_init);
+module_exit(ethtool_nl_exit);
+
+/* this alias is for autoload */
+MODULE_ALIAS("net-pf-" __stringify(PF_NETLINK)
+	     "-proto-" __stringify(NETLINK_GENERIC)
+	     "-family-" ETHTOOL_GENL_NAME);
+MODULE_AUTHOR("Michal Kubecek <mkubecek@suse.cz>");
+MODULE_DESCRIPTION("Netlink interface for ethtool");
+MODULE_LICENSE("GPL");
-- 
2.15.1

^ permalink raw reply related

* [RFC PATCH 3/9] ethtool: helper functions for netlink interface
From: Michal Kubecek @ 2017-12-11 13:53 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel
In-Reply-To: <cover.1513000306.git.mkubecek@suse.cz>

Misc helpers used by ethtool netlink code.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 net/core/ethtool_netlink.c | 177 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 177 insertions(+)

diff --git a/net/core/ethtool_netlink.c b/net/core/ethtool_netlink.c
index 46a226bb9a2c..22d23d057623 100644
--- a/net/core/ethtool_netlink.c
+++ b/net/core/ethtool_netlink.c
@@ -8,6 +8,183 @@
 
 static struct genl_family ethtool_genl_family;
 
+/* misc helper functions */
+
+static int ethnl_str_size(const char *s)
+{
+	return nla_total_size(strlen(s) + 1);
+}
+
+static int ethnl_str_ifne_size(const char *s)
+{
+	return s[0] ? ethnl_str_size(s) : 0;
+}
+
+static int ethnl_put_str_ifne(struct sk_buff *skb, int attrtype, const char *s)
+{
+	if (!s[0])
+		return 0;
+	return nla_put_string(skb, attrtype, s);
+}
+
+static struct nlattr *ethnl_nest_start(struct sk_buff *skb, int attrtype)
+{
+	return nla_nest_start(skb, attrtype | NLA_F_NESTED);
+}
+
+/* ethnl_update_* return true if the value is changed */
+static bool ethnl_update_u32(u32 *dst, struct nlattr *attr)
+{
+	u32 val;
+
+	if (!attr)
+		return false;
+	val = nla_get_u32(attr);
+	if (*dst == val)
+		return false;
+
+	*dst = val;
+	return true;
+}
+
+static bool ethnl_update_u8(u8 *dst, struct nlattr *attr)
+{
+	u8 val;
+
+	if (!attr)
+		return false;
+	val = nla_get_u8(attr);
+	if (*dst == val)
+		return false;
+
+	*dst = val;
+	return true;
+}
+
+/* update u32 value used as bool from NLA_U8 */
+static bool ethnl_update_bool32(u32 *dst, struct nlattr *attr)
+{
+	u8 val;
+
+	if (!attr)
+		return false;
+	val = !!nla_get_u8(attr);
+	if (!!*dst == val)
+		return false;
+
+	*dst = val;
+	return true;
+}
+
+static bool ethnl_update_binary(u8 *dst, unsigned int len, struct nlattr *attr)
+{
+	if (!attr)
+		return false;
+	if (nla_len(attr) < len)
+		len = nla_len(attr);
+	if (!memcmp(dst, nla_data(attr), len))
+		return false;
+
+	memcpy(dst, nla_data(attr), len);
+	return true;
+}
+
+static bool ethnl_update_bitfield32(u32 *dst, struct nlattr *attr)
+{
+	struct nla_bitfield32 change;
+	u32 newval;
+
+	if (!attr)
+		return false;
+	change = nla_get_bitfield32(attr);
+	newval = (*dst & ~change.selector) | (change.value & change.selector);
+	if (*dst == newval)
+		return false;
+
+	*dst = newval;
+	return true;
+}
+
+static struct net_device *ethnl_dev_get(struct genl_info *info)
+{
+	const struct ethnlmsghdr *hdr = info->userhdr;
+	struct net *net = genl_info_net(info);
+	struct net_device *dev;
+
+	if (hdr->ifindex) {
+		dev = dev_get_by_index(net, hdr->ifindex);
+		if (!dev)
+			return ERR_PTR(-ENODEV);
+		/* if both ifindex and ifname are passed, both must match */
+		if (hdr->ifname && strncmp(dev->name, hdr->ifname, IFNAMSIZ)) {
+			GENL_SET_ERR_MSG(info,
+					 "ifindex and ifname do not match");
+			return ERR_PTR(-ENODEV);
+		}
+		return dev;
+	} else if (hdr->ifname[0]) {
+		dev = dev_get_by_name(net, hdr->ifname);
+		return dev ?: ERR_PTR(-ENODEV);
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+
+static void warn_partial_info(struct genl_info *info)
+{
+	GENL_SET_ERR_MSG(info, "not all requested data could be retrieved");
+}
+
+/* Check user privileges explicitly to allow finer access control based on
+ * context of the request or hiding part of the information from unprivileged
+ * users
+ */
+static bool ethnl_is_privileged(struct sk_buff *skb, struct genl_info *info)
+{
+	struct net *net = genl_info_net(info);
+
+	return netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN);
+}
+
+/* create skb for a reply
+ * payload: payload length (without netlink, genetlink and ethnl headers)
+ * dev:     device the reply is about
+ * cmd:     ETHTOOL_CMD_* command for reply
+ * info:   info for the received packet we respond to
+ * ehdrp:   place to store pointer to ethtool specific header (may be null)
+ * returns: skb or null on error
+ */
+static struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev,
+					u8 cmd, struct genl_info *info,
+					struct ethnlmsghdr **ehdrp)
+{
+	struct ethnlmsghdr *ehdr;
+	struct sk_buff *rskb;
+
+	rskb = genlmsg_new(ETHNL_HDRLEN + payload, GFP_KERNEL);
+	if (!rskb) {
+		GENL_SET_ERR_MSG(info,
+				 "failed to allocate reply message");
+		return NULL;
+	}
+
+	ehdr = genlmsg_put_reply(rskb, info, &ethtool_genl_family, 0, cmd);
+	if (!ehdr) {
+		nlmsg_free(rskb);
+		return NULL;
+	}
+	if (ehdrp)
+		*ehdrp = ehdr;
+
+	ehdr->ifindex = dev->ifindex;
+	ehdr->flags = 0;
+	ehdr->info_mask = 0;
+	strncpy(ehdr->ifname, dev->name, sizeof(ehdr->ifname));
+	ehdr->ifname[sizeof(ehdr->ifname) - 1] = '\0';
+
+	return rskb;
+}
+
 /* genetlink paperwork */
 
 static const struct genl_ops ethtool_genl_ops[] = {
-- 
2.15.1

^ permalink raw reply related

* [RFC PATCH 4/9] ethtool: netlink bitset handling
From: Michal Kubecek @ 2017-12-11 13:53 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel
In-Reply-To: <cover.1513000306.git.mkubecek@suse.cz>

Declare attribute type constants and add helper functions to handle
arbitrary length bit sets.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Documentation/networking/ethtool-netlink.txt |  56 +++++
 include/uapi/linux/ethtool_netlink.h         |  31 +++
 net/core/ethtool_netlink.c                   | 346 +++++++++++++++++++++++++++
 3 files changed, 433 insertions(+)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index c94da66cb5fb..893e5156f6a7 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -48,6 +48,62 @@ in "set" requests). For these attributes, the "true" value should be passed as
 number 1 but any non-zero value should be understood as "true" by recipient.
 
 
+Bit sets
+--------
+
+For short bitmaps of (reasonably) fixed length, standard NLA_BITFIELD32 type
+is used. For arbitrary length bitmaps, ethtool netlink uses a nested attribute
+with contents of one of two forms: compact (two binary bitmaps representing
+bit values and mask of affected bits) and bit-by-bit (list of bits identified
+by either index or name).
+
+Compact form: nested (bitset) atrribute contents:
+
+    ETHA_BITSET_SIZE	(u32)		number of significant bits
+    ETHA_BITSET_VALUE	(binary)	bitmap of bit values
+    ETHA_BITSET_MASK	(binary)	bitmap of valid bits
+
+Value and mask must have length at least ETHA_BITSET_SIZE bits rounded up to
+a multiple of 32 bits. They consist of 32-bit words in host byt order, words
+ordered from least significant to most significant (i.e. the same way as
+bitmaps are passed with ioctl interface).
+
+For compact form, ETHA_BITSET_SIZE and ETHA_BITSET_VALUE are mandatory.
+Similar to BITFIELD32, a compact form bit set requests to set bits in the mask
+to 1 (if set in value) or 0 (if not) and preserve the rest. If the mask is
+omitted, it is supposed to be "all ones", i.e. set all bits according to
+value.
+
+Kernel bit set length may differ from userspace length if older application is
+used on newer kernel or vice versa. If userspace bitmaps are longer, error is
+only issued if request actually tries to set bits not implemented in kernel.
+
+Bit-by-bit form: nested (bitset) attribute contents:
+
+    ETHA_BITSET_SIZE	(u32)		number of significant bits (optional)
+    ETHA_BITSET_BITS	(nested)	array of bits
+	ETHA_BITSET_BIT
+	    ETHA_BIT_INDEX	(u32)		bit index (0 for LSB)
+            ETHA_BIT_NAME	(string)	bit name
+	    ETHA_BIT_VALUE	(flag)		present if bit is set
+        ETHA_BITSET_BIT
+	...
+
+Bit size is optional for bit-by-bit form. ETHA_BITSET_BITS nest can only
+contain ETHA_BITS_BIT attributes but there can be an arbitrary number of them.
+A bit may be identified by its index or by its name. When used in requests,
+listed bits are set to 0 or 1 according to ETHA_BIT_VALUE, the rest is
+preserved. A request fails if index exceeds kernel bit length or if name is
+not recognized.
+
+In requests, application can use either form. Form used by kernel in reply is
+determined by a flag in flags field of request header. Semantics of value and
+mask depends on the attribute. General idea is that flags control request
+processing, info_mask control which parts of the information are returned in
+"get" request and index identifies a particular subcommand or an object to
+which the request applies.
+
+
 List of message types
 ---------------------
 
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 06cff2b52dfe..6db35f00ea32 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -26,6 +26,37 @@ enum {
 	ETHTOOL_CMD_MAX = (__ETHTOOL_CMD_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),
+};
+
 /* generic netlink info */
 #define ETHTOOL_GENL_NAME "ethtool"
 #define ETHTOOL_GENL_VERSION 1
diff --git a/net/core/ethtool_netlink.c b/net/core/ethtool_netlink.c
index 22d23d057623..9f287e67f30b 100644
--- a/net/core/ethtool_netlink.c
+++ b/net/core/ethtool_netlink.c
@@ -3,6 +3,7 @@
 #include <linux/module.h>
 #include <linux/ethtool_netlink.h>
 #include <linux/netdevice.h>
+#include <linux/bitmap.h>
 #include <net/genetlink.h>
 #include "ethtool_common.h"
 
@@ -130,6 +131,351 @@ static struct net_device *ethnl_dev_get(struct genl_info *info)
 	return ERR_PTR(-EINVAL);
 }
 
+/* bitset helper functions */
+
+static bool ethnl_test_bit(u32 *val, unsigned int index)
+{
+	if (val)
+		return val[index / 32] & (1 << (index % 32));
+	else
+		return true;
+}
+
+static void ethnl_copy_bitmap(u32 *dst, u32 *src, unsigned int size)
+{
+	unsigned int full_words = size / 32;
+
+	memcpy(dst, src, full_words * sizeof(u32));
+	if (size % 32 != 0)
+		dst[full_words] = src[full_words] & ((1U << (size % 32)) - 1);
+}
+
+static void ethnl_fill_bitmap(u32 *dst, unsigned int size)
+{
+	unsigned int full_words = size / 32;
+
+	memset(dst, 0xff, full_words * sizeof(u32));
+	if (size % 32 != 0)
+		dst[full_words] = (1U << (size % 32)) - 1;
+}
+
+/* convert standard kernel bitmap (long sized words) to ethtool one (u32 words)
+ * bitmap_to_u32array() can in fact do an "in place" conversion but it's not
+ * documented so we cannot rely on it; moreover, we can use the fact that this
+ * conversion is no-op except for 64-bit big endian architectures
+ */
+static void ethnl_bitmap_to_u32(unsigned long *bitmap, unsigned int nwords)
+{
+#if BITS_PER_LONG == 64 && defined(__BIG_ENDIAN)
+	u32 *dst = (u32 *)bitmap;
+	unsigned int i;
+
+	for (i = 0; i < nwords; i++) {
+		unsigned long tmp = READ_ONCE(bitmap[i]);
+
+		dst[2 * i] = tmp & 0xffffffff;
+		dst[2 * i + 1] = tmp >> 32;
+	}
+#endif
+}
+
+/* calculate size for a bitset attribute
+ * see ethnl_put_bitset() for arguments
+ */
+static int ethnl_bitset_size(bool compact, unsigned int size, u32 *val,
+			     u32 *mask, const char *const *names)
+{
+	unsigned int nwords = (size + 31) / 32;
+	unsigned int len;
+
+	if (WARN_ON(!compact && !names))
+		return -EINVAL;
+	/* size */
+	len = nla_total_size(sizeof(u32));
+
+	if (compact) {
+		/* values, mask */
+		len += 2 * nla_total_size(nwords * sizeof(u32));
+	} else {
+		unsigned int bits_len = 0;
+		unsigned int bit_len, i;
+
+		for (i = 0; i < size; i++) {
+			if (!ethnl_test_bit(mask, i))
+				continue;
+			/* index */
+			bit_len = nla_total_size(sizeof(u32));
+			/* name */
+			bit_len += ethnl_str_size(names[i]);
+			/* value */
+			if (ethnl_test_bit(val, i))
+				bit_len += nla_total_size(0);
+
+			/* bit nest */
+			bits_len += nla_total_size(bit_len);
+		}
+		len += nla_total_size(bits_len);
+	}
+
+	/* outermost nest */
+	return nla_total_size(len);
+}
+
+/* put bitset into a message
+ * skb:      skb with the message
+ * attrtype: attribute type for the bitset
+ * compact:  compact (bitmaps) or verbose (bit-by-bit with names) format
+ * size:     size of the set in bits
+ * val:      bitset values
+ * mask:     mask of valid bits
+ * names:    bit names (only used for verbose format)
+ */
+static int ethnl_put_bitset(struct sk_buff *skb, int attrtype, bool compact,
+			    unsigned int size, u32 *val, u32 *mask,
+			    const char *const *names)
+{
+	struct nlattr *nest;
+	struct nlattr *attr;
+	int ret;
+
+	if (WARN_ON(!compact && !names))
+		return -EINVAL;
+	nest = ethnl_nest_start(skb, attrtype);
+	if (!nest)
+		return -EMSGSIZE;
+
+	ret = -EMSGSIZE;
+	if (nla_put_u32(skb, ETHA_BITSET_SIZE, size))
+		goto err;
+	if (compact) {
+		unsigned int bytesize = ((size + 31) / 32) * sizeof(u32);
+
+		ret = -EMSGSIZE;
+		attr = nla_reserve(skb, ETHA_BITSET_VALUE, bytesize);
+		if (!attr)
+			goto err;
+		ethnl_copy_bitmap(nla_data(attr), val, size);
+		attr = nla_reserve(skb, ETHA_BITSET_MASK, bytesize);
+		if (!attr)
+			goto err;
+		if (mask)
+			ethnl_copy_bitmap(nla_data(attr), mask, size);
+		else
+			ethnl_fill_bitmap(nla_data(attr), size);
+	} else {
+		struct nlattr *bits;
+		unsigned int i;
+
+		bits = ethnl_nest_start(skb, ETHA_BITSET_BITS);
+		if (!bits)
+			goto err;
+		for (i = 0; i < size; i++) {
+			if (!ethnl_test_bit(mask, i))
+				continue;
+			attr = ethnl_nest_start(skb, ETHA_BITS_BIT);
+			if (!attr ||
+			    nla_put_u32(skb, ETHA_BIT_INDEX, i) ||
+			    nla_put_string(skb, ETHA_BIT_NAME, names[i]))
+				goto err;
+			if (ethnl_test_bit(val, i))
+				if (nla_put_flag(skb, ETHA_BIT_VALUE))
+					goto err;
+			nla_nest_end(skb, attr);
+		}
+		nla_nest_end(skb, bits);
+	}
+
+	nla_nest_end(skb, nest);
+	return 0;
+err:
+	nla_nest_cancel(skb, nest);
+	return ret;
+}
+
+static const struct nla_policy bitset_policy[ETHA_BITSET_MAX + 1] = {
+	[ETHA_BITSET_UNSPEC]		= { .type = NLA_UNSPEC },
+	[ETHA_BITSET_SIZE]		= { .type = NLA_U32 },
+	[ETHA_BITSET_BITS]		= { .type = NLA_NESTED },
+	[ETHA_BITSET_VALUE]		= { .type = NLA_BINARY },
+	[ETHA_BITSET_MASK]		= { .type = NLA_BINARY },
+};
+
+static const struct nla_policy bit_policy[ETHA_BIT_MAX + 1] = {
+	[ETHA_BIT_UNSPEC]		= { .type = NLA_UNSPEC },
+	[ETHA_BIT_INDEX]		= { .type = NLA_U32 },
+	[ETHA_BIT_NAME]			= { .type = NLA_STRING },
+	[ETHA_BIT_VALUE]		= { .type = NLA_FLAG },
+};
+
+static int ethnl_name_to_idx(const char *const *names, unsigned int n_names,
+			     const char *name, unsigned int name_len)
+{
+	unsigned int i;
+
+	for (i = 0; i < n_names; i++)
+		if (!strncmp(names[i], name, name_len))
+			return i;
+
+	return n_names;
+}
+
+static int ethnl_update_bit(unsigned long *bitmap, unsigned int nbits,
+			    struct nlattr *bit_attr, const char *const *names,
+			    struct genl_info *info)
+{
+	struct nlattr *tb[ETHA_BIT_MAX + 1];
+	int ret, idx;
+
+	if (nla_type(bit_attr) != ETHA_BITS_BIT) {
+		GENL_SET_ERR_MSG(info,
+				 "ETHA_BITSET_BITS can contain only ETHA_BITS_BIT");
+		return genl_err_attr(info, -EINVAL, bit_attr);
+	}
+	ret = nla_parse_nested(tb, ETHA_BIT_MAX, bit_attr, bit_policy,
+			       info->extack);
+	if (ret < 0)
+		return ret;
+
+	if (tb[ETHA_BIT_INDEX]) {
+		idx = nla_get_u32(tb[ETHA_BIT_INDEX]);
+		if (idx >= nbits) {
+			GENL_SET_ERR_MSG(info, "bit index too high");
+			return genl_err_attr(info, -EOPNOTSUPP,
+					     tb[ETHA_BIT_INDEX]);
+		}
+		if (tb[ETHA_BIT_NAME] &&
+		    strncmp(nla_data(tb[ETHA_BIT_NAME]), names[idx],
+			    nla_len(tb[ETHA_BIT_NAME]))) {
+			GENL_SET_ERR_MSG(info, "bit index and name mismatch");
+			return genl_err_attr(info, -EINVAL, bit_attr);
+		}
+	} else if (tb[ETHA_BIT_NAME]) {
+		idx = ethnl_name_to_idx(names, nbits,
+					nla_data(tb[ETHA_BIT_NAME]),
+					nla_len(tb[ETHA_BIT_NAME]));
+		if (idx >= nbits) {
+			GENL_SET_ERR_MSG(info, "bit index too high");
+			return genl_err_attr(info, -EOPNOTSUPP,
+					     tb[ETHA_BIT_NAME]);
+		}
+	} else {
+		GENL_SET_ERR_MSG(info, "neither bit index nor name specified");
+		return genl_err_attr(info, -EINVAL, bit_attr);
+	}
+
+	if (tb[ETHA_BIT_VALUE])
+		set_bit(idx, bitmap);
+	else
+		clear_bit(idx, bitmap);
+	return 0;
+}
+
+static bool ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits,
+				struct nlattr *attr, int *err,
+				const char *const *names,
+				struct genl_info *info)
+{
+	struct nlattr *tb[ETHA_BITSET_MAX + 1];
+	unsigned int change_bits = 0;
+	bool mod = false;
+
+	if (!attr)
+		return false;
+	*err = nla_parse_nested(tb, ETHA_BITSET_MAX, attr, bitset_policy,
+				info->extack);
+	if (*err < 0)
+		return mod;
+
+	/* To let new userspace to work with old kernel, we allow bitmaps
+	 * from userspace to be longer than kernel ones and only issue an
+	 * error if userspace actually tries to change a bit not existing
+	 * in kernel.
+	 */
+	if (tb[ETHA_BITSET_SIZE])
+		change_bits = nla_get_u32(tb[ETHA_BITSET_SIZE]);
+
+	if (tb[ETHA_BITSET_BITS]) {
+		DECLARE_BITMAP(val, nbits);
+		struct nlattr *bit_attr;
+		int rem;
+
+		*err = -EINVAL;
+		if (tb[ETHA_BITSET_VALUE] || tb[ETHA_BITSET_MASK])
+			return mod;
+		bitmap_copy(val, bitmap, __ETHTOOL_LINK_MODE_MASK_NBITS);
+		nla_for_each_nested(bit_attr, tb[ETHA_BITSET_BITS], rem) {
+			*err = ethnl_update_bit(val, nbits, bit_attr, names,
+						info);
+			if (*err < 0)
+				return mod;
+		}
+		mod = !bitmap_equal(val, bitmap, nbits);
+		if (mod)
+			bitmap_copy(bitmap, val, nbits);
+	} else {
+		const unsigned int max_bits =
+			max_t(unsigned int, nbits, change_bits);
+		unsigned int nwords = (change_bits + 31) / 32;
+		DECLARE_BITMAP(mask, max_bits);
+		DECLARE_BITMAP(val, max_bits);
+
+		*err = -EINVAL;
+		if (!change_bits || !tb[ETHA_BITSET_VALUE])
+			return mod;
+		if (nla_len(tb[ETHA_BITSET_VALUE]) < nwords * sizeof(u32))
+			return mod;
+		if (tb[ETHA_BITSET_MASK] &&
+		    nla_len(tb[ETHA_BITSET_VALUE]) < nwords * sizeof(u32))
+			return mod;
+
+		bitmap_zero(val, max_bits);
+		bitmap_from_u32array(val, change_bits,
+				     nla_data(tb[ETHA_BITSET_VALUE]), nwords);
+		bitmap_zero(mask, max_bits);
+		if (tb[ETHA_BITSET_MASK])
+			bitmap_from_u32array(mask, change_bits,
+					     nla_data(tb[ETHA_BITSET_MASK]),
+					     nwords);
+		else
+			bitmap_fill(mask, change_bits);
+
+		if (nbits < change_bits) {
+			unsigned int idx = find_next_bit(mask, max_bits, nbits);
+
+			*err = -EINVAL;
+			if (idx >= nbits)
+				return mod;
+		}
+
+		bitmap_and(val, val, mask, nbits);
+		bitmap_complement(mask, mask, nbits);
+		bitmap_and(mask, mask, bitmap, nbits);
+		bitmap_or(val, val, mask, nbits);
+		mod = !bitmap_equal(val, bitmap, nbits);
+		if (mod)
+			bitmap_copy(bitmap, val, nbits);
+	}
+
+	*err = 0;
+	return mod;
+}
+
+static bool ethnl_update_bitset32(u32 *bitmap, unsigned int nbits,
+				  struct nlattr *attr, int *err,
+				  const char *const *names,
+				  struct genl_info *info)
+{
+	unsigned int nwords = (nbits + 31) / 32;
+	DECLARE_BITMAP(tmp, nbits);
+	bool mod;
+
+	bitmap_from_u32array(tmp, nbits, bitmap, nwords);
+	mod = ethnl_update_bitset(tmp, nbits, attr, err, names, info);
+	if (!*err && mod)
+		bitmap_to_u32array(bitmap, nwords, tmp, nbits);
+	return (!*err && mod);
+}
+
 static void warn_partial_info(struct genl_info *info)
 {
 	GENL_SET_ERR_MSG(info, "not all requested data could be retrieved");
-- 
2.15.1

^ permalink raw reply related

* [RFC PATCH 5/9] ethtool: implement GET_DRVINFO message
From: Michal Kubecek @ 2017-12-11 13:54 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel
In-Reply-To: <cover.1513000306.git.mkubecek@suse.cz>

Request the same information as ETHTOOL_GDRVINFO. This is read-only so that
corresponding SET_DRVINFO exists but is only used in kernel replies. Rip
the code to query the driver out of the legacy interface and move it to
a new file ethtool_common.c so that both interfaces can use it.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Documentation/networking/ethtool-netlink.txt | 30 ++++++++++-
 include/uapi/linux/ethtool_netlink.h         | 21 ++++++++
 net/core/Makefile                            |  2 +-
 net/core/ethtool.c                           | 42 +++-------------
 net/core/ethtool_common.c                    | 46 +++++++++++++++++
 net/core/ethtool_common.h                    | 11 ++++
 net/core/ethtool_netlink.c                   | 75 ++++++++++++++++++++++++++++
 7 files changed, 189 insertions(+), 38 deletions(-)
 create mode 100644 net/core/ethtool_common.c
 create mode 100644 net/core/ethtool_common.h

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 893e5156f6a7..cb992180b211 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -107,6 +107,9 @@ which the request applies.
 List of message types
 ---------------------
 
+    ETHTOOL_CMD_GET_DRVINFO
+    ETHTOOL_CMD_SET_DRVINFO		response only
+
 All constants use ETHTOOL_CMD_ prefix followed by "GET", "SET" or "ACT" to
 indicate the type.
 
@@ -119,6 +122,31 @@ messages marked as "response only" in the table above.
 Later sections describe the format and semantics of these request messages.
 
 
+GET_DRVINFO
+-----------
+
+GET_DRVINFO request corresponds to ETHTOOL_GDRVINFO ioctl command and provides
+basic driver information. The request doesn't use any attributes and flags,
+info_mask and index field in request header are ignored. Kernel response
+contents:
+
+    ETHA_DRVINFO_DRIVER		(string)	driver name
+    ETHA_DRVINFO_VERSION	(string)	driver version
+    ETHA_DRVINFO_FWVERSION	(string)	firmware version
+    ETHA_DRVINFO_BUSINFO	(string)	device bus address
+    ETHA_DRVINFO_EROM_VER	(string)	expansion ROM version
+    ETHA_DRVINFO_N_PRIV_FLAGS	(u32)		number of private flags
+    ETHA_DRVINFO_N_STATS	(u32)		number of device stats
+    ETHA_DRVINFO_TESTINFO_LEN	(u32)		number of test results
+    ETHA_DRVINFO_EEDUMP_LEN	(u32)		EEPROM dump size
+    ETHA_DRVINFO_REGDUMP_LEN	(u32)		register dump size
+
+The meaning of these follows the corresponding fields of ETHTOOL_GDRVINFO
+response.
+
+All information is read only, SET_DRVINFO request is not implemented.
+
+
 Request translation
 -------------------
 
@@ -130,7 +158,7 @@ ioctl command			netlink command
 ---------------------------------------------------------------------
 ETHTOOL_GSET			n/a
 ETHTOOL_SSET			n/a
-ETHTOOL_GDRVINFO		n/a
+ETHTOOL_GDRVINFO		ETHTOOL_CMD_GET_DRVINFO
 ETHTOOL_GREGS			n/a
 ETHTOOL_GWOL			n/a
 ETHTOOL_SWOL			n/a
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 6db35f00ea32..d6ab1d73d494 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -21,6 +21,8 @@ struct ethnlmsghdr {
 
 enum {
 	ETHTOOL_CMD_NOOP,
+	ETHTOOL_CMD_GET_DRVINFO,
+	ETHTOOL_CMD_SET_DRVINFO,	/* only for reply */
 
 	__ETHTOOL_CMD_MAX,
 	ETHTOOL_CMD_MAX = (__ETHTOOL_CMD_MAX - 1),
@@ -57,6 +59,25 @@ enum {
 	ETHA_BITSET_MAX = (__ETHA_BITSET_MAX - 1),
 };
 
+/* GET_DRVINFO / SET_DRVINFO */
+
+enum {
+	ETHA_DRVINFO_UNSPEC,
+	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),
+};
+
 /* generic netlink info */
 #define ETHTOOL_GENL_NAME "ethtool"
 #define ETHTOOL_GENL_VERSION 1
diff --git a/net/core/Makefile b/net/core/Makefile
index 617ab2abecdf..3196641c0e70 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -11,7 +11,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
 obj-y		     += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \
 			neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
 			sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \
-			fib_notifier.o
+			fib_notifier.o ethtool_common.o
 
 obj-y += net-sysfs.o
 obj-$(CONFIG_PROC_FS) += net-procfs.o
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index f8fcf450a36e..09e780a748f9 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -26,6 +26,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/sched/signal.h>
 #include <linux/net.h>
+#include "ethtool_common.h"
 
 /*
  * Some useful ethtool_ops methods that're device independent.
@@ -885,45 +886,14 @@ static noinline_for_stack int ethtool_get_drvinfo(struct net_device *dev,
 						  void __user *useraddr)
 {
 	struct ethtool_drvinfo info;
-	const struct ethtool_ops *ops = dev->ethtool_ops;
-
-	memset(&info, 0, sizeof(info));
-	info.cmd = ETHTOOL_GDRVINFO;
-	if (ops->get_drvinfo) {
-		ops->get_drvinfo(dev, &info);
-	} else if (dev->dev.parent && dev->dev.parent->driver) {
-		strlcpy(info.bus_info, dev_name(dev->dev.parent),
-			sizeof(info.bus_info));
-		strlcpy(info.driver, dev->dev.parent->driver->name,
-			sizeof(info.driver));
-	} else {
-		return -EOPNOTSUPP;
-	}
-
-	/*
-	 * this method of obtaining string set info is deprecated;
-	 * Use ETHTOOL_GSSET_INFO instead.
-	 */
-	if (ops->get_sset_count) {
-		int rc;
-
-		rc = ops->get_sset_count(dev, ETH_SS_TEST);
-		if (rc >= 0)
-			info.testinfo_len = rc;
-		rc = ops->get_sset_count(dev, ETH_SS_STATS);
-		if (rc >= 0)
-			info.n_stats = rc;
-		rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
-		if (rc >= 0)
-			info.n_priv_flags = rc;
-	}
-	if (ops->get_regs_len)
-		info.regdump_len = ops->get_regs_len(dev);
-	if (ops->get_eeprom_len)
-		info.eedump_len = ops->get_eeprom_len(dev);
+	int rc;
 
+	rc = __ethtool_get_drvinfo(dev, &info);
+	if (rc < 0)
+		return rc;
 	if (copy_to_user(useraddr, &info, sizeof(info)))
 		return -EFAULT;
+
 	return 0;
 }
 
diff --git a/net/core/ethtool_common.c b/net/core/ethtool_common.c
new file mode 100644
index 000000000000..2c0abab0e43c
--- /dev/null
+++ b/net/core/ethtool_common.c
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#include <linux/rtnetlink.h>
+#include "ethtool_common.h"
+
+int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+	const struct ethtool_ops *ops = dev->ethtool_ops;
+
+	memset(info, 0, sizeof(*info));
+	info->cmd = ETHTOOL_GDRVINFO;
+	if (ops->get_drvinfo) {
+		ops->get_drvinfo(dev, info);
+	} else if (dev->dev.parent && dev->dev.parent->driver) {
+		strlcpy(info->bus_info, dev_name(dev->dev.parent),
+			sizeof(info->bus_info));
+		strlcpy(info->driver, dev->dev.parent->driver->name,
+			sizeof(info->driver));
+	} else {
+		return -EOPNOTSUPP;
+	}
+
+	/* this method of obtaining string set info is deprecated;
+	 * Use ETHTOOL_GSSET_INFO instead.
+	 */
+	if (ops->get_sset_count) {
+		int rc;
+
+		rc = ops->get_sset_count(dev, ETH_SS_TEST);
+		if (rc >= 0)
+			info->testinfo_len = rc;
+		rc = ops->get_sset_count(dev, ETH_SS_STATS);
+		if (rc >= 0)
+			info->n_stats = rc;
+		rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
+		if (rc >= 0)
+			info->n_priv_flags = rc;
+	}
+	if (ops->get_regs_len)
+		info->regdump_len = ops->get_regs_len(dev);
+	if (ops->get_eeprom_len)
+		info->eedump_len = ops->get_eeprom_len(dev);
+
+	return 0;
+}
+EXPORT_SYMBOL(__ethtool_get_drvinfo);
diff --git a/net/core/ethtool_common.h b/net/core/ethtool_common.h
new file mode 100644
index 000000000000..1f031c1d943a
--- /dev/null
+++ b/net/core/ethtool_common.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef _ETHTOOL_COMMON_H
+#define _ETHTOOL_COMMON_H
+
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+
+int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
+
+#endif /* _ETHTOOL_COMMON_H */
diff --git a/net/core/ethtool_netlink.c b/net/core/ethtool_netlink.c
index 9f287e67f30b..077814fd36bd 100644
--- a/net/core/ethtool_netlink.c
+++ b/net/core/ethtool_netlink.c
@@ -531,9 +531,84 @@ static struct sk_buff *ethnl_reply_init(size_t payload, struct net_device *dev,
 	return rskb;
 }
 
+/* GET_DRVINFO */
+
+static int ethnl_drvinfo_size(struct ethtool_drvinfo *drvinfo)
+{
+	int len = 0;
+
+	len += ethnl_str_ifne_size(drvinfo->driver);
+	len += ethnl_str_ifne_size(drvinfo->version);
+	len += ethnl_str_ifne_size(drvinfo->fw_version);
+	len += ethnl_str_ifne_size(drvinfo->bus_info);
+	len += ethnl_str_ifne_size(drvinfo->erom_version);
+	/* n_priv_flags, n_stats, testinfo_len, eedump_len, regdump_len */
+	len += 5 * nla_total_size(sizeof(u32));
+
+	return len;
+}
+
+static int ethnl_get_drvinfo(struct sk_buff *skb, struct genl_info *info)
+{
+	struct ethnlmsghdr *ehdr;
+	struct ethtool_drvinfo drvinfo = {};
+	struct net_device *dev;
+	struct sk_buff *rskb;
+	int reply_len;
+	int rc = 0;
+
+	dev = ethnl_dev_get(info);
+	if (IS_ERR(dev))
+		return PTR_ERR(dev);
+	rc = __ethtool_get_drvinfo(dev, &drvinfo);
+	if (rc < 0) {
+		dev_put(dev);
+		GENL_SET_ERR_MSG(info,
+				 "failed to retrieve driver info from driver");
+		return rc;
+	}
+
+	reply_len = ethnl_drvinfo_size(&drvinfo);
+	rskb = ethnl_reply_init(reply_len, dev, ETHTOOL_CMD_SET_DRVINFO, info,
+				&ehdr);
+	dev_put(dev);
+	if (!rskb)
+		return -ENOMEM;
+
+	if (ethnl_put_str_ifne(rskb, ETHA_DRVINFO_DRIVER, drvinfo.driver) ||
+	    ethnl_put_str_ifne(rskb, ETHA_DRVINFO_VERSION, drvinfo.version) ||
+	    ethnl_put_str_ifne(rskb, ETHA_DRVINFO_FWVERSION,
+			       drvinfo.fw_version) ||
+	    ethnl_put_str_ifne(rskb, ETHA_DRVINFO_BUSINFO, drvinfo.bus_info) ||
+	    ethnl_put_str_ifne(rskb, ETHA_DRVINFO_EROM_VER,
+			       drvinfo.erom_version) ||
+	    nla_put_u32(rskb, ETHA_DRVINFO_N_PRIV_FLAGS,
+			drvinfo.n_priv_flags) ||
+	    nla_put_u32(rskb, ETHA_DRVINFO_N_STATS, drvinfo.n_stats) ||
+	    nla_put_u32(rskb, ETHA_DRVINFO_TESTINFO_LEN,
+			drvinfo.testinfo_len) ||
+	    nla_put_u32(rskb, ETHA_DRVINFO_EEDUMP_LEN, drvinfo.eedump_len) ||
+	    nla_put_u32(rskb, ETHA_DRVINFO_REGDUMP_LEN, drvinfo.regdump_len))
+		goto err;
+
+	genlmsg_end(rskb, ehdr);
+	return genlmsg_reply(rskb, info);
+
+err:
+	nlmsg_free(rskb);
+	GENL_SET_ERR_MSG(info, "kernel error, see kernel log for details");
+	WARN_ONCE(1, "calculated message payload length (%d) not sufficient\n",
+		  reply_len);
+	return -EMSGSIZE;
+}
+
 /* genetlink paperwork */
 
 static const struct genl_ops ethtool_genl_ops[] = {
+	{
+		.cmd	= ETHTOOL_CMD_GET_DRVINFO,
+		.doit	= ethnl_get_drvinfo,
+	},
 };
 
 static struct genl_family ethtool_genl_family = {
-- 
2.15.1

^ permalink raw reply related

* [RFC PATCH 6/9] ethtool: implement GET_SETTINGS message
From: Michal Kubecek @ 2017-12-11 13:54 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel
In-Reply-To: <cover.1513000306.git.mkubecek@suse.cz>

Requests the information provided by ETHTOOL_GLINKSETTINGS, ETHTOOL_GWOL
and ETHTOOL_GMSGLVL. The info_mask header field can be used to request only
part of the information. Flag ETH_SETTINGS_RF_COMPACT_BITSETS switches
between flag-by-flag list and compact bitmaps for link modes in the reply.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Documentation/networking/ethtool-netlink.txt |  62 +++++-
 include/linux/ethtool_netlink.h              |   3 +
 include/linux/netdevice.h                    |   2 +
 include/uapi/linux/ethtool.h                 |   3 +
 include/uapi/linux/ethtool_netlink.h         |  36 ++++
 net/core/ethtool.c                           | 108 +---------
 net/core/ethtool_common.c                    | 112 ++++++++++
 net/core/ethtool_common.h                    |   8 +
 net/core/ethtool_netlink.c                   | 299 +++++++++++++++++++++++++++
 9 files changed, 528 insertions(+), 105 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index cb992180b211..7aabc87c9f09 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -109,6 +109,8 @@ List of message types
 
     ETHTOOL_CMD_GET_DRVINFO
     ETHTOOL_CMD_SET_DRVINFO		response only
+    ETHTOOL_CMD_GET_SETTINGS
+    ETHTOOL_CMD_SET_SETTINGS		response only (for now)
 
 All constants use ETHTOOL_CMD_ prefix followed by "GET", "SET" or "ACT" to
 indicate the type.
@@ -147,6 +149,56 @@ response.
 All information is read only, SET_DRVINFO request is not implemented.
 
 
+GET_SETTINGS
+------------
+
+GET_SETTINGS request retrieves information provided by ETHTOOL_GLINKSETTINGS,
+ETHTOOL_GWOL, ETHTOOL_GMSGLVL and ETHTOOL_GLINK ioctl requests. The request
+doesn't use any attributes.
+
+Header flags:
+    ETH_SETTINGS_RF_COMPACT_BITSETS	bitset form in response (1 is compact)
+
+Header info_mask bits:
+    ETH_SETTINGS_IM_LINKINFO		link_ksettings except link modes
+    ETH_SETTINGS_IM_LINKMODES		link modes from link_ksettings
+    ETH_SETTINGS_IM_MSGLEVEL		msglevel
+    ETH_SETTINGS_IM_WOLINFO		struct ethtool_wolinfo
+    ETH_SETTINGS_IM_LINK		link state
+
+Zero info_mask
+
+Response contents:
+
+    ETHA_SETTINGS_SPEED		(u32)		link speed (Mb/s)
+    ETHA_SETTINGS_DUPLEX	(u8)		duplex mode
+    ETHA_SETTINGS_PORT		(u8)		physical port
+    ETHA_SETTINGS_PHYADDR	(u8)		MDIO address of phy
+    ETHA_SETTINGS_AUTONEG	(u8)		autoneotiation status
+    ETHA_SETTINGS_MDIO_SUPPORT	(bitfield32)	MDIO support flags
+    ETHA_SETTINGS_TP_MDIX	(u8)		MDI(-X) status
+    ETHA_SETTINGS_TP_MDIX_CTRL	(u8)		MDI(-X) control
+    ETHA_SETTINGS_TRANSCEIVER	(u8)		transceiver
+    ETHA_SETTINGS_WOL_MODES	(bitfield32)	wake-on-lan modes
+    ETHA_SETTINGS_SOPASS	(binary)	SecureOn(tm) password
+    ETHA_SETTINGS_MSGLVL	(bitfield32)	debug level
+    ETHA_SETTINGS_LINK_MODES	(bitset)	device link modes
+    ETHA_SETTINGS_PEER_MODES	(bitset)	link partner link modes
+    ETHA_SETTINGS_LINK		(u32)		link state
+
+Most of the attributes have the same meaning (including values) as
+corresponding members of ioctl structures. For ETHA_SETTINGS_MDIO_SUPPORT and
+ETHA_SETTINGS_MSGLVL, selector reports flags supported by kernel. For
+ETHA_SETTINGS_WOL_MODES it reports flags supported by the device. For
+ETHA_SETTINGS_LINK_MODES, value represent advertised modes and mask represents
+supported modes. For ETHA_SETTINGS_PEER_MODES, both value and mask represent
+partner advertised link modes.
+
+GET_SETTINGS request is allowed for unprivileged user but ETHA_SETTINGS_SOPASS
+is only provided by kernel in response to privileged (netns CAP_NET_ADMIN)
+requests.
+
+
 Request translation
 -------------------
 
@@ -156,16 +208,16 @@ have their netlink replacement yet.
 
 ioctl command			netlink command
 ---------------------------------------------------------------------
-ETHTOOL_GSET			n/a
+ETHTOOL_GSET			ETHTOOL_CMD_GET_SETTINGS
 ETHTOOL_SSET			n/a
 ETHTOOL_GDRVINFO		ETHTOOL_CMD_GET_DRVINFO
 ETHTOOL_GREGS			n/a
-ETHTOOL_GWOL			n/a
+ETHTOOL_GWOL			ETHTOOL_CMD_GET_SETTINGS
 ETHTOOL_SWOL			n/a
-ETHTOOL_GMSGLVL			n/a
+ETHTOOL_GMSGLVL			ETHTOOL_CMD_GET_SETTINGS
 ETHTOOL_SMSGLVL			n/a
 ETHTOOL_NWAY_RST		n/a
-ETHTOOL_GLINK			n/a
+ETHTOOL_GLINK			ETHTOOL_CMD_GET_SETTINGS
 ETHTOOL_GEEPROM			n/a
 ETHTOOL_SEEPROM			n/a
 ETHTOOL_GCOALESCE		n/a
@@ -230,7 +282,7 @@ ETHTOOL_GTUNABLE		n/a
 ETHTOOL_STUNABLE		n/a
 ETHTOOL_GPHYSTATS		n/a
 ETHTOOL_PERQUEUE		n/a
-ETHTOOL_GLINKSETTINGS		n/a
+ETHTOOL_GLINKSETTINGS		ETHTOOL_CMD_GET_SETTINGS
 ETHTOOL_SLINKSETTINGS		n/a
 ETHTOOL_PHY_GTUNABLE		n/a
 ETHTOOL_PHY_STUNABLE		n/a
diff --git a/include/linux/ethtool_netlink.h b/include/linux/ethtool_netlink.h
index 0412adb4f42f..1787359f9e5d 100644
--- a/include/linux/ethtool_netlink.h
+++ b/include/linux/ethtool_netlink.h
@@ -6,4 +6,7 @@
 #include <uapi/linux/ethtool_netlink.h>
 #include <linux/ethtool.h>
 
+#define __ETHTOOL_LINK_MODE_MASK_NWORDS \
+	((__ETHTOOL_LINK_MODE_MASK_NBITS + 31) / 32)
+
 #endif /* _LINUX_ETHTOOL_NETLINK_H_ */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index cc4ce7456e38..0e1d0a04c3cc 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3506,6 +3506,8 @@ enum {
 	NETIF_MSG_PKTDATA	= 0x1000,
 	NETIF_MSG_HW		= 0x2000,
 	NETIF_MSG_WOL		= 0x4000,
+
+	NETIF_MSG_ALL		= 0x7fff,
 };
 
 #define netif_msg_drv(p)	((p)->msg_enable & NETIF_MSG_DRV)
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 44a0b675a6bc..a9076a76cdb4 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -143,6 +143,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
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index d6ab1d73d494..9520d13fc9ab 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -23,6 +23,8 @@ enum {
 	ETHTOOL_CMD_NOOP,
 	ETHTOOL_CMD_GET_DRVINFO,
 	ETHTOOL_CMD_SET_DRVINFO,	/* only for reply */
+	ETHTOOL_CMD_GET_SETTINGS,
+	ETHTOOL_CMD_SET_SETTINGS,
 
 	__ETHTOOL_CMD_MAX,
 	ETHTOOL_CMD_MAX = (__ETHTOOL_CMD_MAX - 1),
@@ -78,6 +80,40 @@ enum {
 	ETHA_DRVINFO_MAX = (__ETHA_DRVINFO_MAX - 1),
 };
 
+/* GET_SETTINGS / SET_SETTINGS */
+
+enum {
+	ETHA_SETTINGS_UNSPEC,
+	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_MAX,
+	ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_MAX - 1),
+};
+
+#define ETH_SETTINGS_RF_COMPACT_BITSETS		0x1
+
+#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_DEFAULT			0x1f
+
 /* generic netlink info */
 #define ETHTOOL_GENL_NAME "ethtool"
 #define ETHTOOL_GENL_VERSION 1
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 09e780a748f9..b08a2efa2e89 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -452,54 +452,6 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
 }
 EXPORT_SYMBOL(ethtool_convert_link_mode_to_legacy_u32);
 
-/* return false if legacy contained non-0 deprecated fields
- * maxtxpkt/maxrxpkt. rest of ksettings always updated
- */
-static bool
-convert_legacy_settings_to_link_ksettings(
-	struct ethtool_link_ksettings *link_ksettings,
-	const struct ethtool_cmd *legacy_settings)
-{
-	bool retval = true;
-
-	memset(link_ksettings, 0, sizeof(*link_ksettings));
-
-	/* This is used to tell users that driver is still using these
-	 * deprecated legacy fields, and they should not use
-	 * %ETHTOOL_GLINKSETTINGS/%ETHTOOL_SLINKSETTINGS
-	 */
-	if (legacy_settings->maxtxpkt ||
-	    legacy_settings->maxrxpkt)
-		retval = false;
-
-	ethtool_convert_legacy_u32_to_link_mode(
-		link_ksettings->link_modes.supported,
-		legacy_settings->supported);
-	ethtool_convert_legacy_u32_to_link_mode(
-		link_ksettings->link_modes.advertising,
-		legacy_settings->advertising);
-	ethtool_convert_legacy_u32_to_link_mode(
-		link_ksettings->link_modes.lp_advertising,
-		legacy_settings->lp_advertising);
-	link_ksettings->base.speed
-		= ethtool_cmd_speed(legacy_settings);
-	link_ksettings->base.duplex
-		= legacy_settings->duplex;
-	link_ksettings->base.port
-		= legacy_settings->port;
-	link_ksettings->base.phy_address
-		= legacy_settings->phy_address;
-	link_ksettings->base.autoneg
-		= legacy_settings->autoneg;
-	link_ksettings->base.mdio_support
-		= legacy_settings->mdio_support;
-	link_ksettings->base.eth_tp_mdix
-		= legacy_settings->eth_tp_mdix;
-	link_ksettings->base.eth_tp_mdix_ctrl
-		= legacy_settings->eth_tp_mdix_ctrl;
-	return retval;
-}
-
 /* return false if ksettings link modes had higher bits
  * set. legacy_settings always updated (best effort)
  */
@@ -560,50 +512,6 @@ struct ethtool_link_usettings {
 	} link_modes;
 };
 
-/* Internal kernel helper to query a device ethtool_link_settings.
- *
- * Backward compatibility note: for compatibility with legacy drivers
- * that implement only the ethtool_cmd API, this has to work with both
- * drivers implementing get_link_ksettings API and drivers
- * implementing get_settings API. When drivers implement get_settings
- * and report ethtool_cmd deprecated fields
- * (transceiver/maxrxpkt/maxtxpkt), these fields are silently ignored
- * because the resulting struct ethtool_link_settings does not report them.
- */
-int __ethtool_get_link_ksettings(struct net_device *dev,
-				 struct ethtool_link_ksettings *link_ksettings)
-{
-	int err;
-	struct ethtool_cmd cmd;
-
-	ASSERT_RTNL();
-
-	if (dev->ethtool_ops->get_link_ksettings) {
-		memset(link_ksettings, 0, sizeof(*link_ksettings));
-		return dev->ethtool_ops->get_link_ksettings(dev,
-							    link_ksettings);
-	}
-
-	/* driver doesn't support %ethtool_link_ksettings API. revert to
-	 * legacy %ethtool_cmd API, unless it's not supported either.
-	 * TODO: remove when ethtool_ops::get_settings disappears internally
-	 */
-	if (!dev->ethtool_ops->get_settings)
-		return -EOPNOTSUPP;
-
-	memset(&cmd, 0, sizeof(cmd));
-	cmd.cmd = ETHTOOL_GSET;
-	err = dev->ethtool_ops->get_settings(dev, &cmd);
-	if (err < 0)
-		return err;
-
-	/* we ignore deprecated fields transceiver/maxrxpkt/maxtxpkt
-	 */
-	convert_legacy_settings_to_link_ksettings(link_ksettings, &cmd);
-	return err;
-}
-EXPORT_SYMBOL(__ethtool_get_link_ksettings);
-
 /* convert ethtool_link_usettings in user space to a kernel internal
  * ethtool_link_ksettings. return 0 on success, errno on error.
  */
@@ -1437,11 +1345,11 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
 static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+	int rc;
 
-	if (!dev->ethtool_ops->get_wol)
-		return -EOPNOTSUPP;
-
-	dev->ethtool_ops->get_wol(dev, &wol);
+	rc = __ethtool_get_wol(dev, &wol);
+	if (rc < 0)
+		return rc;
 
 	if (copy_to_user(useraddr, &wol, sizeof(wol)))
 		return -EFAULT;
@@ -1506,12 +1414,12 @@ static int ethtool_nway_reset(struct net_device *dev)
 static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_value edata = { .cmd = ETHTOOL_GLINK };
+	int link = __ethtool_get_link(dev);
 
-	if (!dev->ethtool_ops->get_link)
-		return -EOPNOTSUPP;
-
-	edata.data = netif_running(dev) && dev->ethtool_ops->get_link(dev);
+	if (link < 0)
+		return link;
 
+	edata.data = link;
 	if (copy_to_user(useraddr, &edata, sizeof(edata)))
 		return -EFAULT;
 	return 0;
diff --git a/net/core/ethtool_common.c b/net/core/ethtool_common.c
index 2c0abab0e43c..30bc2b14cf2a 100644
--- a/net/core/ethtool_common.c
+++ b/net/core/ethtool_common.c
@@ -44,3 +44,115 @@ int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 	return 0;
 }
 EXPORT_SYMBOL(__ethtool_get_drvinfo);
+
+/* return false if legacy contained non-0 deprecated fields
+ * maxtxpkt/maxrxpkt. rest of ksettings always updated
+ */
+bool
+convert_legacy_settings_to_link_ksettings(
+	struct ethtool_link_ksettings *link_ksettings,
+	const struct ethtool_cmd *legacy_settings)
+{
+	bool retval = true;
+
+	memset(link_ksettings, 0, sizeof(*link_ksettings));
+
+	/* This is used to tell users that driver is still using these
+	 * deprecated legacy fields, and they should not use
+	 * %ETHTOOL_GLINKSETTINGS/%ETHTOOL_SLINKSETTINGS
+	 */
+	if (legacy_settings->maxtxpkt ||
+	    legacy_settings->maxrxpkt)
+		retval = false;
+
+	ethtool_convert_legacy_u32_to_link_mode(
+		link_ksettings->link_modes.supported,
+		legacy_settings->supported);
+	ethtool_convert_legacy_u32_to_link_mode(
+		link_ksettings->link_modes.advertising,
+		legacy_settings->advertising);
+	ethtool_convert_legacy_u32_to_link_mode(
+		link_ksettings->link_modes.lp_advertising,
+		legacy_settings->lp_advertising);
+	link_ksettings->base.speed
+		= ethtool_cmd_speed(legacy_settings);
+	link_ksettings->base.duplex
+		= legacy_settings->duplex;
+	link_ksettings->base.port
+		= legacy_settings->port;
+	link_ksettings->base.phy_address
+		= legacy_settings->phy_address;
+	link_ksettings->base.autoneg
+		= legacy_settings->autoneg;
+	link_ksettings->base.mdio_support
+		= legacy_settings->mdio_support;
+	link_ksettings->base.eth_tp_mdix
+		= legacy_settings->eth_tp_mdix;
+	link_ksettings->base.eth_tp_mdix_ctrl
+		= legacy_settings->eth_tp_mdix_ctrl;
+	return retval;
+}
+
+/* Internal kernel helper to query a device ethtool_link_settings.
+ *
+ * Backward compatibility note: for compatibility with legacy drivers
+ * that implement only the ethtool_cmd API, this has to work with both
+ * drivers implementing get_link_ksettings API and drivers
+ * implementing get_settings API. When drivers implement get_settings
+ * and report ethtool_cmd deprecated fields
+ * (transceiver/maxrxpkt/maxtxpkt), these fields are silently ignored
+ * because the resulting struct ethtool_link_settings does not report them.
+ */
+int __ethtool_get_link_ksettings(struct net_device *dev,
+				 struct ethtool_link_ksettings *link_ksettings)
+{
+	int err;
+	struct ethtool_cmd cmd;
+
+	ASSERT_RTNL();
+
+	if (dev->ethtool_ops->get_link_ksettings) {
+		memset(link_ksettings, 0, sizeof(*link_ksettings));
+		return dev->ethtool_ops->get_link_ksettings(dev,
+							    link_ksettings);
+	}
+
+	/* driver doesn't support %ethtool_link_ksettings API. revert to
+	 * legacy %ethtool_cmd API, unless it's not supported either.
+	 * TODO: remove when ethtool_ops::get_settings disappears internally
+	 */
+	if (!dev->ethtool_ops->get_settings)
+		return -EOPNOTSUPP;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd = ETHTOOL_GSET;
+	err = dev->ethtool_ops->get_settings(dev, &cmd);
+	if (err < 0)
+		return err;
+
+	/* we ignore deprecated fields transceiver/maxrxpkt/maxtxpkt
+	 */
+	convert_legacy_settings_to_link_ksettings(link_ksettings, &cmd);
+	return err;
+}
+EXPORT_SYMBOL(__ethtool_get_link_ksettings);
+
+int __ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	if (!dev->ethtool_ops->get_wol)
+		return -EOPNOTSUPP;
+
+	dev->ethtool_ops->get_wol(dev, wol);
+
+	return 0;
+}
+EXPORT_SYMBOL(__ethtool_get_wol);
+
+int __ethtool_get_link(struct net_device *dev)
+{
+	if (!dev->ethtool_ops->get_link)
+		return -EOPNOTSUPP;
+
+	return netif_running(dev) && dev->ethtool_ops->get_link(dev);
+}
+EXPORT_SYMBOL(__ethtool_get_link);
diff --git a/net/core/ethtool_common.h b/net/core/ethtool_common.h
index 1f031c1d943a..92e236952f18 100644
--- a/net/core/ethtool_common.h
+++ b/net/core/ethtool_common.h
@@ -7,5 +7,13 @@
 #include <linux/ethtool.h>
 
 int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
+int __ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
+int __ethtool_get_link_ksettings(struct net_device *dev,
+				 struct ethtool_link_ksettings *link_ksettings);
+int __ethtool_get_link(struct net_device *dev);
+
+bool convert_legacy_settings_to_link_ksettings(
+	struct ethtool_link_ksettings *link_ksettings,
+	const struct ethtool_cmd *legacy_settings);
 
 #endif /* _ETHTOOL_COMMON_H */
diff --git a/net/core/ethtool_netlink.c b/net/core/ethtool_netlink.c
index 077814fd36bd..0c2bed7850bc 100644
--- a/net/core/ethtool_netlink.c
+++ b/net/core/ethtool_netlink.c
@@ -4,11 +4,69 @@
 #include <linux/ethtool_netlink.h>
 #include <linux/netdevice.h>
 #include <linux/bitmap.h>
+#include <linux/rtnetlink.h>
 #include <net/genetlink.h>
 #include "ethtool_common.h"
 
 static struct genl_family ethtool_genl_family;
 
+/* dictionary */
+
+static const char *const link_mode_names[] = {
+	[ETHTOOL_LINK_MODE_10baseT_Half_BIT]		= "10baseT/Half",
+	[ETHTOOL_LINK_MODE_10baseT_Full_BIT]		= "10baseT/Full",
+	[ETHTOOL_LINK_MODE_100baseT_Half_BIT]		= "100baseT/Half",
+	[ETHTOOL_LINK_MODE_100baseT_Full_BIT]		= "100baseT/Full",
+	[ETHTOOL_LINK_MODE_1000baseT_Half_BIT]		= "1000baseT/Half",
+	[ETHTOOL_LINK_MODE_1000baseT_Full_BIT]		= "1000baseT/Full",
+	[ETHTOOL_LINK_MODE_Autoneg_BIT]			= "Autoneg",
+	[ETHTOOL_LINK_MODE_TP_BIT]			= "TP",
+	[ETHTOOL_LINK_MODE_AUI_BIT]			= "AUI",
+	[ETHTOOL_LINK_MODE_MII_BIT]			= "MII",
+	[ETHTOOL_LINK_MODE_FIBRE_BIT]			= "FIBRE",
+	[ETHTOOL_LINK_MODE_BNC_BIT]			= "BNC",
+	[ETHTOOL_LINK_MODE_10000baseT_Full_BIT]		= "10000baseT/Full",
+	[ETHTOOL_LINK_MODE_Pause_BIT]			= "Pause",
+	[ETHTOOL_LINK_MODE_Asym_Pause_BIT]		= "Asym_Pause",
+	[ETHTOOL_LINK_MODE_2500baseX_Full_BIT]		= "2500baseX/Full",
+	[ETHTOOL_LINK_MODE_Backplane_BIT]		= "Backplane",
+	[ETHTOOL_LINK_MODE_1000baseKX_Full_BIT]		= "1000baseKX/Full",
+	[ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT]	= "10000baseKX4/Full",
+	[ETHTOOL_LINK_MODE_10000baseKR_Full_BIT]	= "10000baseKR/Full",
+	[ETHTOOL_LINK_MODE_10000baseR_FEC_BIT]		= "10000baseR/FEC",
+	[ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT]	= "20000baseMLD2/Full",
+	[ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT]	= "20000baseKR2/Full",
+	[ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT]	= "40000baseKR4/Full",
+	[ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT]	= "40000baseCR4/Full",
+	[ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT]	= "40000baseSR4/Full",
+	[ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT]	= "40000baseLR4/Full",
+	[ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT]	= "56000baseKR4/Full",
+	[ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT]	= "56000baseCR4/Full",
+	[ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT]	= "56000baseSR4/Full",
+	[ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT]	= "56000baseLR4/Full",
+	[ETHTOOL_LINK_MODE_25000baseCR_Full_BIT]	= "25000baseCR/Full",
+	[ETHTOOL_LINK_MODE_25000baseKR_Full_BIT]	= "25000baseKR/Full",
+	[ETHTOOL_LINK_MODE_25000baseSR_Full_BIT]	= "25000baseSR/Full",
+	[ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT]	= "50000baseCR2/Full",
+	[ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT]	= "50000baseKR2/Full",
+	[ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT]	= "100000baseKR4/Full",
+	[ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT]	= "100000baseSR4/Full",
+	[ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT]	= "100000baseCR4/Full",
+	[ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT]	= "100000baseLR4/ER4_Full",
+	[ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT]	= "50000baseSR2/Full",
+	[ETHTOOL_LINK_MODE_1000baseX_Full_BIT]		= "1000baseX/Full",
+	[ETHTOOL_LINK_MODE_10000baseCR_Full_BIT]	= "10000baseCR/Full",
+	[ETHTOOL_LINK_MODE_10000baseSR_Full_BIT]	= "10000baseSR/Full",
+	[ETHTOOL_LINK_MODE_10000baseLR_Full_BIT]	= "10000baseLR/Full",
+	[ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT]	= "10000baseLRM/Full",
+	[ETHTOOL_LINK_MODE_10000baseER_Full_BIT]	= "10000baseER/Full",
+	[ETHTOOL_LINK_MODE_2500baseT_Full_BIT]		= "2500baseT/Full",
+	[ETHTOOL_LINK_MODE_5000baseT_Full_BIT]		= "5000baseT/Full",
+	[ETHTOOL_LINK_MODE_FEC_NONE_BIT]		= "None",
+	[ETHTOOL_LINK_MODE_FEC_RS_BIT]			= "RS",
+	[ETHTOOL_LINK_MODE_FEC_BASER_BIT]		= "BASER",
+};
+
 /* misc helper functions */
 
 static int ethnl_str_size(const char *s)
@@ -602,6 +660,243 @@ static int ethnl_get_drvinfo(struct sk_buff *skb, struct genl_info *info)
 	return -EMSGSIZE;
 }
 
+/* GET_SETTINGS */
+
+/* To keep things simple, reserve space for some attributes which may not
+ * be added to the message (e.g. ETHA_SETTINGS_SOPASS); therefore the length
+ * returned may be bigger than the actual length of the message sent
+ */
+static int ethnl_settings_size(struct ethnlmsghdr *ehdr,
+			       struct ethtool_link_ksettings *ksettings,
+			       struct net_device *dev, u16 req_mask)
+{
+	size_t len = 0;
+	int rc = 0;
+
+	if (req_mask & ETH_SETTINGS_IM_LINKINFO) {
+		/* speed */
+		len += nla_total_size(sizeof(u32));
+		/* duplex, autoneg, port, phyaddr, mdix, mdixctrl, transcvr */
+		len += 7 * nla_total_size(sizeof(u8));
+		/* mdio_support */
+		len += nla_total_size(sizeof(struct nla_bitfield32));
+	}
+	if (req_mask & ETH_SETTINGS_IM_LINKMODES) {
+		u32 *supported = (u32 *)ksettings->link_modes.supported;
+		u32 *advertising = (u32 *)ksettings->link_modes.advertising;
+		u32 *lp_advertising =
+			(u32 *)ksettings->link_modes.lp_advertising;
+		bool compact = ehdr->flags & ETH_SETTINGS_RF_COMPACT_BITSETS;
+
+		rc = ethnl_bitset_size(compact, __ETHTOOL_LINK_MODE_MASK_NBITS,
+				       advertising, supported, link_mode_names);
+		if (rc < 0)
+			return rc;
+		len += rc;
+		rc = ethnl_bitset_size(compact, __ETHTOOL_LINK_MODE_MASK_NBITS,
+				       lp_advertising, lp_advertising,
+				       link_mode_names);
+		if (rc < 0)
+			return rc;
+		len += rc;
+	}
+	if (req_mask & ETH_SETTINGS_IM_MSGLEVEL)
+		len += nla_total_size(sizeof(struct nla_bitfield32));
+	if (req_mask & ETH_SETTINGS_IM_WOLINFO) {
+		/* wolopts / wol_supported */
+		len += nla_total_size(sizeof(struct nla_bitfield32));
+		/* sopass */
+		len += nla_total_size(SOPASS_MAX);
+	}
+	if (req_mask & ETH_SETTINGS_IM_LINK)
+		len += nla_total_size(sizeof(u32));
+
+	return len;
+}
+
+static int ethnl_get_link_ksettings(struct genl_info *info,
+				    struct net_device *dev,
+				    struct ethtool_link_ksettings *ksettings)
+{
+	int ret;
+
+	ret = __ethtool_get_link_ksettings(dev, ksettings);
+
+	if (ret < 0)
+		GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
+	return ret;
+}
+
+static int ethnl_get_wol(struct genl_info *info, struct net_device *dev,
+			 struct ethtool_wolinfo *wolinfo)
+{
+	int ret = __ethtool_get_wol(dev, wolinfo);
+
+	if (ret < 0)
+		GENL_SET_ERR_MSG(info, "failed to retrieve wol info");
+	return ret;
+}
+
+static int ethnl_get_settings(struct sk_buff *skb, struct genl_info *info)
+{
+	struct ethtool_link_ksettings ksettings = {};
+	struct ethtool_link_settings *lsettings;
+	struct ethnlmsghdr *ehdr;
+	struct ethtool_wolinfo wolinfo = {};
+	struct net_device *dev;
+	struct sk_buff *rskb;
+	unsigned int reply_len;
+	bool lpm_empty = true;
+	u16 req_flags;
+	u16 req_mask;
+	u32 msglevel;
+	int ret = 0;
+	int link = -EOPNOTSUPP;
+
+	lsettings = &ksettings.base;
+	ehdr = info->userhdr;
+	if (!ehdr->info_mask)
+		ehdr->info_mask = ETH_SETTINGS_IM_DEFAULT;
+	req_mask = ehdr->info_mask;
+	req_flags = ehdr->flags;
+
+	dev = ethnl_dev_get(info);
+	if (IS_ERR(dev))
+		return PTR_ERR(dev);
+	if (req_mask & (ETH_SETTINGS_IM_LINKINFO | ETH_SETTINGS_IM_LINKMODES)) {
+		rtnl_lock();
+		ret = ethnl_get_link_ksettings(info, dev, &ksettings);
+		rtnl_unlock();
+		if (ret < 0) {
+			warn_partial_info(info);
+			req_mask &= ~(ETH_SETTINGS_IM_LINKINFO |
+				      ETH_SETTINGS_IM_LINKMODES);
+		}
+	}
+	if (req_mask & ETH_SETTINGS_IM_LINKMODES) {
+		lpm_empty = bitmap_empty(ksettings.link_modes.lp_advertising,
+					 __ETHTOOL_LINK_MODE_MASK_NBITS);
+		ethnl_bitmap_to_u32(ksettings.link_modes.supported,
+				    __ETHTOOL_LINK_MODE_MASK_NWORDS);
+		ethnl_bitmap_to_u32(ksettings.link_modes.advertising,
+				    __ETHTOOL_LINK_MODE_MASK_NWORDS);
+		ethnl_bitmap_to_u32(ksettings.link_modes.lp_advertising,
+				    __ETHTOOL_LINK_MODE_MASK_NWORDS);
+	}
+	if (req_mask & ETH_SETTINGS_IM_MSGLEVEL) {
+		if (dev->ethtool_ops->get_msglevel) {
+			msglevel = dev->ethtool_ops->get_msglevel(dev);
+		} else {
+			warn_partial_info(info);
+			req_mask &= ~ETH_SETTINGS_IM_MSGLEVEL;
+		}
+	}
+	if (req_mask & ETH_SETTINGS_IM_WOLINFO) {
+		ret = ethnl_get_wol(info, dev, &wolinfo);
+		if (ret < 0) {
+			warn_partial_info(info);
+			req_mask &= ~ETH_SETTINGS_IM_WOLINFO;
+		}
+	}
+	if (req_mask & ETH_SETTINGS_IM_LINK)
+		link = __ethtool_get_link(dev);
+
+	ret = ethnl_settings_size(ehdr, &ksettings, dev, req_mask);
+	if (ret < 0)
+		goto err_putdev;
+	else
+		reply_len = ret;
+	rskb = ethnl_reply_init(reply_len, dev, ETHTOOL_CMD_SET_SETTINGS, info,
+				&ehdr);
+	ret = -ENOMEM;
+	if (!rskb)
+		goto err_putdev;
+	if (req_mask != ETH_SETTINGS_IM_DEFAULT)
+		ehdr->info_mask = req_mask;
+
+	ret = -EMSGSIZE;
+	if (req_mask & ETH_SETTINGS_IM_LINKINFO) {
+		if (nla_put_u32(rskb, ETHA_SETTINGS_SPEED, lsettings->speed) ||
+		    nla_put_u8(rskb, ETHA_SETTINGS_DUPLEX, lsettings->duplex) ||
+		    nla_put_u8(rskb, ETHA_SETTINGS_PORT, lsettings->port) ||
+		    nla_put_u8(rskb, ETHA_SETTINGS_PHYADDR,
+			       lsettings->phy_address) ||
+		    nla_put_u8(rskb, ETHA_SETTINGS_AUTONEG,
+			       lsettings->autoneg) ||
+		    nla_put_bitfield32(rskb, ETHA_SETTINGS_MDIO_SUPPORT,
+				       lsettings->mdio_support,
+				       ETH_MDIO_SUPPORTS_ALL) ||
+		    nla_put_u8(rskb, ETHA_SETTINGS_TP_MDIX,
+			       lsettings->eth_tp_mdix) ||
+		    nla_put_u8(rskb, ETHA_SETTINGS_TP_MDIX_CTRL,
+			       lsettings->eth_tp_mdix_ctrl) ||
+		    nla_put_u8(rskb, ETHA_SETTINGS_TRANSCEIVER,
+			       lsettings->transceiver))
+			goto err;
+	}
+	if (req_mask & ETH_SETTINGS_IM_LINKMODES) {
+		u32 *supported = (u32 *)ksettings.link_modes.supported;
+		u32 *advertising = (u32 *)ksettings.link_modes.advertising;
+		u32 *lp_advertising =
+			(u32 *)ksettings.link_modes.lp_advertising;
+		bool compact = req_flags & ETH_SETTINGS_RF_COMPACT_BITSETS;
+
+		ret = ethnl_put_bitset(rskb, ETHA_SETTINGS_LINK_MODES, compact,
+				       __ETHTOOL_LINK_MODE_MASK_NBITS,
+				       advertising, supported, link_mode_names);
+		if (ret < 0)
+			goto err;
+		if (!lpm_empty) {
+			ret = ethnl_put_bitset(rskb, ETHA_SETTINGS_PEER_MODES,
+					       compact,
+					       __ETHTOOL_LINK_MODE_MASK_NBITS,
+					       lp_advertising, lp_advertising,
+					       link_mode_names);
+			if (ret < 0)
+				goto err;
+		}
+		ret = -EMSGSIZE;
+	}
+	if (req_mask & ETH_SETTINGS_IM_MSGLEVEL) {
+		if (nla_put_bitfield32(rskb, ETHA_SETTINGS_MSGLVL, msglevel,
+				       NETIF_MSG_ALL))
+			goto err;
+	}
+	if (req_mask & ETH_SETTINGS_IM_WOLINFO) {
+		/* ioctl() restricts read access to wolinfo but the actual
+		 * reason is to hide sopass from unprivileged users; netlink
+		 * can show wol modes without sopass
+		 */
+		if (nla_put_bitfield32(rskb, ETHA_SETTINGS_WOL_MODES,
+				       wolinfo.wolopts, wolinfo.supported))
+			goto err;
+		if (ethnl_is_privileged(skb, info) &&
+		    nla_put(rskb, ETHA_SETTINGS_SOPASS, sizeof(wolinfo.sopass),
+			    wolinfo.sopass))
+			goto err;
+	}
+	if (req_mask & ETH_SETTINGS_IM_LINK && link >= 0) {
+		if (nla_put_u32(rskb, ETHA_SETTINGS_LINK, link))
+			goto err;
+	}
+
+	dev_put(dev);
+	genlmsg_end(rskb, ehdr);
+	return genlmsg_reply(rskb, info);
+
+err:
+	nlmsg_free(rskb);
+err_putdev:
+	dev_put(dev);
+	if (ret == -EMSGSIZE)
+		GENL_SET_ERR_MSG(info,
+				 "kernel error, see kernel log for details");
+	WARN_ONCE(ret == -EMSGSIZE,
+		  "calculated message payload length (%d) not sufficient\n",
+		  reply_len);
+	return ret;
+}
+
 /* genetlink paperwork */
 
 static const struct genl_ops ethtool_genl_ops[] = {
@@ -609,6 +904,10 @@ static const struct genl_ops ethtool_genl_ops[] = {
 		.cmd	= ETHTOOL_CMD_GET_DRVINFO,
 		.doit	= ethnl_get_drvinfo,
 	},
+	{
+		.cmd	= ETHTOOL_CMD_GET_SETTINGS,
+		.doit	= ethnl_get_settings,
+	},
 };
 
 static struct genl_family ethtool_genl_family = {
-- 
2.15.1

^ permalink raw reply related

* [RFC PATCH 7/9] ethtool: implement SET_SETTINGS message
From: Michal Kubecek @ 2017-12-11 13:54 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel
In-Reply-To: <cover.1513000306.git.mkubecek@suse.cz>

Sets the information provided by ETHTOOL_SLINKSETTINGS, ETHTOOL_SWOL and
ETHTOOL_SMSGLVL. Unlike with ioctl(), userspace can send only some
attributes so that we only need to call ethtool_ops callbacks which we
really need (and the "set" callback is only called when we actually changed
some setting).

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Documentation/networking/ethtool-netlink.txt |  42 +-
 net/core/ethtool_netlink.c                   | 547 +++++++++++++++++++++++++++
 2 files changed, 584 insertions(+), 5 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 7aabc87c9f09..187fab33f721 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -110,7 +110,7 @@ List of message types
     ETHTOOL_CMD_GET_DRVINFO
     ETHTOOL_CMD_SET_DRVINFO		response only
     ETHTOOL_CMD_GET_SETTINGS
-    ETHTOOL_CMD_SET_SETTINGS		response only (for now)
+    ETHTOOL_CMD_SET_SETTINGS
 
 All constants use ETHTOOL_CMD_ prefix followed by "GET", "SET" or "ACT" to
 indicate the type.
@@ -199,6 +199,38 @@ is only provided by kernel in response to privileged (netns CAP_NET_ADMIN)
 requests.
 
 
+SET_SETTINGS
+------------
+
+SET_SETTINGS request allows setting some of the data reported by GET_SETTINGS.
+Request flags, info_mask and index are ignored. These attributes are allowed
+to be passed with SET_SETTINGS request:
+
+    ETHA_SETTINGS_SPEED         (u32)           link speed (Mb/s)
+    ETHA_SETTINGS_DUPLEX        (u8)            duplex mode
+    ETHA_SETTINGS_PORT          (u8)            physical port
+    ETHA_SETTINGS_PHYADDR       (u8)            MDIO address of phy
+    ETHA_SETTINGS_AUTONEG       (u8)            autoneotiation status
+    ETHA_SETTINGS_TP_MDIX_CTRL  (u8)            MDI(-X) control
+    ETHA_SETTINGS_WOL_MODES     (bitfield32)    wake-on-lan modes
+    ETHA_SETTINGS_SOPASS        (binary)        SecureOn(tm) password
+    ETHA_SETTINGS_MSGLVL        (bitfield32)    debug level
+    ETHA_SETTINGS_LINK_MODES    (bitset)        device link modes
+
+For both bitfield32 types, value and selector work the usual way, i.e. bits
+set in selector are set to corresponding bits from value and the rest is
+preserved. In a similar fashion, ETHA_SETTINGS_LINK_MODES allows setting
+advertised link modes.
+
+If autonegotiation is on (either set now or kept from before), advertised
+modes are not changed (no ETHA_SETTINGS_LINK_MODES attribute) and at least one
+of speed and duplex is specified, kernel adjusts advertised modes to all
+supported modes matching speed, duplex or both (whatever is specified). This
+autoselection is done on ethtool side with ioctl interface, netlink interface
+is supposed to allow requesting changes without knowing what exactly kernel
+supports.
+
+
 Request translation
 -------------------
 
@@ -209,13 +241,13 @@ have their netlink replacement yet.
 ioctl command			netlink command
 ---------------------------------------------------------------------
 ETHTOOL_GSET			ETHTOOL_CMD_GET_SETTINGS
-ETHTOOL_SSET			n/a
+ETHTOOL_SSET			ETHTOOL_CMD_SET_SETTINGS
 ETHTOOL_GDRVINFO		ETHTOOL_CMD_GET_DRVINFO
 ETHTOOL_GREGS			n/a
 ETHTOOL_GWOL			ETHTOOL_CMD_GET_SETTINGS
-ETHTOOL_SWOL			n/a
+ETHTOOL_SWOL			ETHTOOL_CMD_SET_SETTINGS
 ETHTOOL_GMSGLVL			ETHTOOL_CMD_GET_SETTINGS
-ETHTOOL_SMSGLVL			n/a
+ETHTOOL_SMSGLVL			ETHTOOL_CMD_SET_SETTINGS
 ETHTOOL_NWAY_RST		n/a
 ETHTOOL_GLINK			ETHTOOL_CMD_GET_SETTINGS
 ETHTOOL_GEEPROM			n/a
@@ -283,7 +315,7 @@ ETHTOOL_STUNABLE		n/a
 ETHTOOL_GPHYSTATS		n/a
 ETHTOOL_PERQUEUE		n/a
 ETHTOOL_GLINKSETTINGS		ETHTOOL_CMD_GET_SETTINGS
-ETHTOOL_SLINKSETTINGS		n/a
+ETHTOOL_SLINKSETTINGS		ETHTOOL_CMD_SET_SETTINGS
 ETHTOOL_PHY_GTUNABLE		n/a
 ETHTOOL_PHY_STUNABLE		n/a
 ETHTOOL_GFECPARAM		n/a
diff --git a/net/core/ethtool_netlink.c b/net/core/ethtool_netlink.c
index 0c2bed7850bc..4b14a02be12a 100644
--- a/net/core/ethtool_netlink.c
+++ b/net/core/ethtool_netlink.c
@@ -67,6 +67,222 @@ static const char *const link_mode_names[] = {
 	[ETHTOOL_LINK_MODE_FEC_BASER_BIT]		= "BASER",
 };
 
+struct link_mode_info {
+	int speed;
+	u8 duplex;
+};
+
+static const struct link_mode_info link_mode_params[] = {
+	[ETHTOOL_LINK_MODE_10baseT_Half_BIT]		= {
+		.speed	= 10,
+		.duplex	= DUPLEX_HALF,
+	},
+	[ETHTOOL_LINK_MODE_10baseT_Full_BIT]		= {
+		.speed	= 10,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_100baseT_Half_BIT]		= {
+		.speed	= 100,
+		.duplex	= DUPLEX_HALF,
+	},
+	[ETHTOOL_LINK_MODE_100baseT_Full_BIT]		= {
+		.speed	= 100,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_1000baseT_Half_BIT]		= {
+		.speed	= 1000,
+		.duplex	= DUPLEX_HALF,
+	},
+	[ETHTOOL_LINK_MODE_1000baseT_Full_BIT]		= {
+		.speed	= 1000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_Autoneg_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_TP_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_AUI_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_MII_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_FIBRE_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_BNC_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_10000baseT_Full_BIT]		= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_Pause_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_Asym_Pause_BIT]		= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_2500baseX_Full_BIT]		= {
+		.speed	= 2500,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_Backplane_BIT]		= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_1000baseKX_Full_BIT]		= {
+		.speed	= 1000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseKR_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseR_FEC_BIT]		= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT]	= {
+		.speed	= 20000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT]	= {
+		.speed	= 20000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT]	= {
+		.speed	= 40000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT]	= {
+		.speed	= 40000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT]	= {
+		.speed	= 40000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT]	= {
+		.speed	= 40000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT]	= {
+		.speed	= 56000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT]	= {
+		.speed	= 56000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT]	= {
+		.speed	= 56000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT]	= {
+		.speed	= 56000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_25000baseCR_Full_BIT]	= {
+		.speed	= 25000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_25000baseKR_Full_BIT]	= {
+		.speed	= 25000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_25000baseSR_Full_BIT]	= {
+		.speed	= 25000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT]	= {
+		.speed	= 50000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT]	= {
+		.speed	= 50000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT]	= {
+		.speed	= 100000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT]	= {
+		.speed	= 100000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT]	= {
+		.speed	= 100000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT]	= {
+		.speed	= 100000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT]	= {
+		.speed	= 50000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_1000baseX_Full_BIT]		= {
+		.speed	= 1000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseCR_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseSR_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseLR_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_10000baseER_Full_BIT]	= {
+		.speed	= 10000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_2500baseT_Full_BIT]		= {
+		.speed	= 2500,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_5000baseT_Full_BIT]		= {
+		.speed	= 5000,
+		.duplex	= DUPLEX_FULL,
+	},
+	[ETHTOOL_LINK_MODE_FEC_NONE_BIT]		= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_FEC_RS_BIT]			= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+	[ETHTOOL_LINK_MODE_FEC_BASER_BIT]		= {
+		.speed	= SPEED_UNKNOWN,
+		.duplex	= DUPLEX_UNKNOWN,
+	},
+};
+
 /* misc helper functions */
 
 static int ethnl_str_size(const char *s)
@@ -662,6 +878,34 @@ static int ethnl_get_drvinfo(struct sk_buff *skb, struct genl_info *info)
 
 /* GET_SETTINGS */
 
+/* We want to allow ~0 as selector for backward compatibility (to just set
+ * given set of modes, whatever kernel supports) so that we allow all bits
+ * on validation and do our own sanity check later.
+ */
+static u32 all_bits = ~(u32)0;
+
+static const struct nla_policy settings_policy[ETHA_SETTINGS_MAX + 1] = {
+	[ETHA_SETTINGS_UNSPEC]		= { .type = NLA_UNSPEC },
+	[ETHA_SETTINGS_SPEED]		= { .type = NLA_U32 },
+	[ETHA_SETTINGS_DUPLEX]		= { .type = NLA_U8 },
+	[ETHA_SETTINGS_PORT]		= { .type = NLA_U8 },
+	[ETHA_SETTINGS_PHYADDR]		= { .type = NLA_U8 },
+	[ETHA_SETTINGS_AUTONEG]		= { .type = NLA_U8 },
+	[ETHA_SETTINGS_MDIO_SUPPORT]	= { .type = NLA_BITFIELD32 },
+	[ETHA_SETTINGS_TP_MDIX]		= { .type = NLA_U8 },
+	[ETHA_SETTINGS_TP_MDIX_CTRL]	= { .type = NLA_U8 },
+	[ETHA_SETTINGS_TRANSCEIVER]	= { .type = NLA_U8 },
+	[ETHA_SETTINGS_WOL_MODES]	= { .type = NLA_BITFIELD32,
+					    .validation_data = &all_bits },
+	[ETHA_SETTINGS_SOPASS]		= { .type = NLA_BINARY,
+					    .len = SOPASS_MAX },
+	[ETHA_SETTINGS_MSGLVL]		= { .type = NLA_BITFIELD32,
+					    .validation_data = &all_bits },
+	[ETHA_SETTINGS_LINK_MODES]	= { .type = NLA_NESTED },
+	[ETHA_SETTINGS_PEER_MODES]	= { .type = NLA_NESTED },
+	[ETHA_SETTINGS_LINK]		= { .type = NLA_FLAG },
+};
+
 /* To keep things simple, reserve space for some attributes which may not
  * be added to the message (e.g. ETHA_SETTINGS_SOPASS); therefore the length
  * returned may be bigger than the actual length of the message sent
@@ -727,6 +971,24 @@ static int ethnl_get_link_ksettings(struct genl_info *info,
 	return ret;
 }
 
+static int ethnl_get_legacy_settings(struct genl_info *info,
+				     struct net_device *dev,
+				     struct ethtool_cmd *cmd)
+{
+	int ret;
+
+	if (!dev->ethtool_ops->get_settings) {
+		/* we already tried ->get_link_ksettings */
+		GENL_SET_ERR_MSG(info, "link settings retrieval unsupported");
+		return -EOPNOTSUPP;
+	}
+	ret = dev->ethtool_ops->get_settings(dev, cmd);
+	if (ret < 0)
+		GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
+
+	return ret;
+}
+
 static int ethnl_get_wol(struct genl_info *info, struct net_device *dev,
 			 struct ethtool_wolinfo *wolinfo)
 {
@@ -737,6 +999,79 @@ static int ethnl_get_wol(struct genl_info *info, struct net_device *dev,
 	return ret;
 }
 
+static int ethnl_set_link_ksettings(struct genl_info *info,
+				    struct net_device *dev,
+				    struct ethtool_link_ksettings *ksettings)
+{
+	int ret = dev->ethtool_ops->set_link_ksettings(dev, ksettings);
+
+	if (ret < 0)
+		GENL_SET_ERR_MSG(info, "link settings update failed");
+	return ret;
+}
+
+static int ethnl_set_legacy_settings(struct genl_info *info,
+				     struct net_device *dev,
+				     struct ethtool_cmd *cmd)
+{
+	int ret;
+
+	if (!dev->ethtool_ops->set_settings) {
+		/* we already tried ->set_link_ksettings */
+		GENL_SET_ERR_MSG(info, "link settings update unsupported");
+		return -EOPNOTSUPP;
+	}
+	ret = dev->ethtool_ops->set_settings(dev, cmd);
+	if (ret < 0)
+		GENL_SET_ERR_MSG(info, "link settings update failed");
+
+	return ret;
+}
+
+static int ethnl_set_wol(struct genl_info *info, struct net_device *dev,
+			 struct ethtool_wolinfo *wolinfo)
+{
+	int ret = dev->ethtool_ops->set_wol(dev, wolinfo);
+
+	if (ret < 0)
+		GENL_SET_ERR_MSG(info, "wol info update failed");
+	return ret;
+}
+
+/* Set advertised link modes to all supported modes matching requested speed
+ * and duplex values. Called when autonegotiation is on, speed or duplex is
+ * requested but no link mode change. This is done in userspace with ioctl()
+ * interface, move it into kernel for netlink.
+ * Returns true if advertised modes bitmap was modified.
+ */
+static bool auto_link_modes(unsigned long *supported,
+			    unsigned long *advertising, unsigned int nbits,
+			    struct nlattr *speed_attr,
+			    struct nlattr *duplex_attr)
+{
+	u8 duplex = duplex_attr ? nla_get_u8(duplex_attr) : DUPLEX_UNKNOWN;
+	u32 speed = speed_attr ? nla_get_u32(speed_attr) : SPEED_UNKNOWN;
+	DECLARE_BITMAP(old_adv, nbits);
+	unsigned int i;
+
+	bitmap_copy(old_adv, advertising, nbits);
+
+	for (i = 0; i < nbits; i++) {
+		const struct link_mode_info *info = &link_mode_params[i];
+
+		if (info->speed == SPEED_UNKNOWN)
+			continue;
+		if (test_bit(i, supported) &&
+		    (!speed_attr || info->speed == speed) &&
+		    (!duplex_attr || info->duplex == duplex))
+			set_bit(i, advertising);
+		else
+			clear_bit(i, advertising);
+	}
+
+	return !bitmap_equal(old_adv, advertising, nbits);
+}
+
 static int ethnl_get_settings(struct sk_buff *skb, struct genl_info *info)
 {
 	struct ethtool_link_ksettings ksettings = {};
@@ -897,6 +1232,212 @@ static int ethnl_get_settings(struct sk_buff *skb, struct genl_info *info)
 	return ret;
 }
 
+/* Update device settings using ->set_link_ksettings() callback */
+static int ethnl_update_ksettings(struct genl_info *info, struct nlattr **tb,
+				  struct net_device *dev)
+{
+	struct ethtool_link_ksettings ksettings = {};
+	struct ethtool_link_settings *lsettings;
+	bool mod = false;
+	int ret;
+
+	rtnl_lock();
+	ret = ethnl_get_link_ksettings(info, dev, &ksettings);
+	if (ret < 0)
+		goto out_unlock;
+	lsettings = &ksettings.base;
+
+	mod = false;
+	if (ethnl_update_u32(&lsettings->speed, tb[ETHA_SETTINGS_SPEED]))
+		mod = true;
+	if (ethnl_update_u8(&lsettings->duplex, tb[ETHA_SETTINGS_DUPLEX]))
+		mod = true;
+	if (ethnl_update_u8(&lsettings->port, tb[ETHA_SETTINGS_PORT]))
+		mod = true;
+	if (ethnl_update_u8(&lsettings->phy_address, tb[ETHA_SETTINGS_PHYADDR]))
+		mod = true;
+	if (ethnl_update_u8(&lsettings->autoneg, tb[ETHA_SETTINGS_AUTONEG]))
+		mod = true;
+	if (ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl,
+			    tb[ETHA_SETTINGS_TP_MDIX_CTRL]))
+		mod = true;
+	if (ethnl_update_bitset(ksettings.link_modes.advertising,
+				__ETHTOOL_LINK_MODE_MASK_NBITS,
+				tb[ETHA_SETTINGS_LINK_MODES],
+				&ret, link_mode_names, info))
+		mod = true;
+	if (ret < 0)
+		goto out_unlock;
+
+	if (!tb[ETHA_SETTINGS_LINK_MODES] && lsettings->autoneg &&
+	    (tb[ETHA_SETTINGS_SPEED] || tb[ETHA_SETTINGS_DUPLEX])) {
+		if (auto_link_modes(ksettings.link_modes.supported,
+				    ksettings.link_modes.advertising,
+				    __ETHTOOL_LINK_MODE_MASK_NBITS,
+				    tb[ETHA_SETTINGS_SPEED],
+				    tb[ETHA_SETTINGS_DUPLEX]))
+			mod = true;
+	}
+
+	if (mod) {
+		ret = ethnl_set_link_ksettings(info, dev, &ksettings);
+		if (ret < 0)
+			goto out_unlock;
+	}
+
+	ret = 0;
+out_unlock:
+	rtnl_unlock();
+	return ret;
+}
+
+/* Update legacy settings using ->set_settings() callback. */
+static int ethnl_update_lsettings(struct genl_info *info, struct nlattr **tb,
+				  struct net_device *dev)
+{
+	struct ethtool_cmd cmd = {};
+	DECLARE_BITMAP(advertising, sizeof(u32));
+	DECLARE_BITMAP(supported, sizeof(u32));
+	bool mod = false;
+	u32 speed;
+	int ret;
+
+	rtnl_lock();
+	ret = ethnl_get_legacy_settings(info, dev, &cmd);
+	if (ret < 0)
+		goto out_unlock;
+	bitmap_from_u32array(supported, sizeof(u32), &cmd.supported, 1);
+	bitmap_from_u32array(advertising, sizeof(u32), &cmd.advertising, 1);
+
+	mod = false;
+	speed = ethtool_cmd_speed(&cmd);
+	if (ethnl_update_u32(&speed, tb[ETHA_SETTINGS_SPEED])) {
+		ethtool_cmd_speed_set(&cmd, speed);
+		mod = true;
+	}
+	if (ethnl_update_u8(&cmd.duplex, tb[ETHA_SETTINGS_DUPLEX]))
+		mod = true;
+	if (ethnl_update_u8(&cmd.port, tb[ETHA_SETTINGS_PORT]))
+		mod = true;
+	if (ethnl_update_u8(&cmd.phy_address, tb[ETHA_SETTINGS_PHYADDR]))
+		mod = true;
+	if (ethnl_update_u8(&cmd.autoneg, tb[ETHA_SETTINGS_AUTONEG]))
+		mod = true;
+	if (ethnl_update_u8(&cmd.eth_tp_mdix_ctrl,
+			    tb[ETHA_SETTINGS_TP_MDIX_CTRL]))
+		mod = true;
+	if (ethnl_update_bitset(advertising, sizeof(cmd.advertising),
+				tb[ETHA_SETTINGS_LINK_MODES],
+				&ret, link_mode_names, info)) {
+		bitmap_to_u32array(&cmd.advertising, 1, advertising,
+				   sizeof(cmd.advertising));
+		mod = true;
+	}
+	if (ret < 0)
+		goto out_unlock;
+
+	if (!tb[ETHA_SETTINGS_LINK_MODES] && cmd.autoneg &&
+	    (tb[ETHA_SETTINGS_SPEED] || tb[ETHA_SETTINGS_DUPLEX])) {
+		if (auto_link_modes(supported, advertising,
+				    sizeof(cmd.advertising),
+				    tb[ETHA_SETTINGS_SPEED],
+				    tb[ETHA_SETTINGS_DUPLEX])) {
+			bitmap_to_u32array(&cmd.advertising, 1, advertising,
+					   sizeof(cmd.advertising));
+			mod = true;
+		}
+	}
+
+	if (mod) {
+		ret = ethnl_set_legacy_settings(info, dev, &cmd);
+		if (ret < 0)
+			goto out_unlock;
+	}
+
+	ret = 0;
+out_unlock:
+	rtnl_unlock();
+	return ret;
+}
+
+static int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr *tb[ETHA_SETTINGS_MAX + 1];
+	struct ethnlmsghdr *ehdr;
+	struct ethtool_wolinfo wolinfo = {};
+	struct net_device *dev;
+	bool mod;
+	int ret;
+
+	ehdr = info->userhdr;
+	dev = ethnl_dev_get(info);
+	if (IS_ERR(dev))
+		return PTR_ERR(dev);
+
+	ret = genlmsg_parse(info->nlhdr, &ethtool_genl_family, tb,
+			    ETHA_SETTINGS_MAX, settings_policy, info->extack);
+	if (ret < 0)
+		goto err_putdev;
+
+	/* read only attributes */
+	ret = -EINVAL;
+	if (tb[ETHA_SETTINGS_MDIO_SUPPORT] || tb[ETHA_SETTINGS_TP_MDIX] ||
+	    tb[ETHA_SETTINGS_TRANSCEIVER] || tb[ETHA_SETTINGS_PEER_MODES] ||
+	    tb[ETHA_SETTINGS_LINK]) {
+		GENL_SET_ERR_MSG(info, "attempt to set a read only attribute");
+		goto err_putdev;
+	}
+
+	if (tb[ETHA_SETTINGS_SPEED] || tb[ETHA_SETTINGS_DUPLEX] ||
+	    tb[ETHA_SETTINGS_PORT] || tb[ETHA_SETTINGS_PHYADDR] ||
+	    tb[ETHA_SETTINGS_AUTONEG] || tb[ETHA_SETTINGS_TP_MDIX_CTRL] ||
+	    tb[ETHA_SETTINGS_LINK_MODES]) {
+		if (dev->ethtool_ops->get_link_ksettings)
+			ret = ethnl_update_ksettings(info, tb, dev);
+		else
+			ret = ethnl_update_lsettings(info, tb, dev);
+		if (ret < 0)
+			goto err_putdev;
+	}
+	if (tb[ETHA_SETTINGS_WOL_MODES] || tb[ETHA_SETTINGS_SOPASS]) {
+		ret = ethnl_get_wol(info, dev, &wolinfo);
+		if (ret < 0)
+			goto err_putdev;
+
+		mod = false;
+		if (ethnl_update_bitfield32(&wolinfo.wolopts,
+					    tb[ETHA_SETTINGS_WOL_MODES]))
+			mod = true;
+		if (ethnl_update_binary(wolinfo.sopass, SOPASS_MAX,
+					tb[ETHA_SETTINGS_SOPASS]))
+			mod = true;
+		if (mod) {
+			ret = ethnl_set_wol(info, dev, &wolinfo);
+			if (ret < 0)
+				goto err_putdev;
+		}
+	}
+	if (tb[ETHA_SETTINGS_MSGLVL]) {
+		u32 msglvl;
+
+		ret = -EOPNOTSUPP;
+		if (!dev->ethtool_ops->get_msglevel ||
+		    !dev->ethtool_ops->set_msglevel) {
+			GENL_SET_ERR_MSG(info,
+					 "device does not provide msglvl access");
+			goto err_putdev;
+		}
+		msglvl = dev->ethtool_ops->get_msglevel(dev);
+		if (ethnl_update_bitfield32(&msglvl, tb[ETHA_SETTINGS_MSGLVL]))
+			dev->ethtool_ops->set_msglevel(dev, msglvl);
+	}
+
+	ret = 0;
+err_putdev:
+	dev_put(dev);
+	return ret;
+}
+
 /* genetlink paperwork */
 
 static const struct genl_ops ethtool_genl_ops[] = {
@@ -908,6 +1449,12 @@ static const struct genl_ops ethtool_genl_ops[] = {
 		.cmd	= ETHTOOL_CMD_GET_SETTINGS,
 		.doit	= ethnl_get_settings,
 	},
+	{
+		.cmd	= ETHTOOL_CMD_SET_SETTINGS,
+		.flags	= GENL_UNS_ADMIN_PERM,
+		.policy	= settings_policy,
+		.doit	= ethnl_set_settings,
+	},
 };
 
 static struct genl_family ethtool_genl_family = {
-- 
2.15.1

^ permalink raw reply related

* [RFC PATCH 8/9] ethtool: implement GET_PARAMS message
From: Michal Kubecek @ 2017-12-11 13:54 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel
In-Reply-To: <cover.1513000306.git.mkubecek@suse.cz>

Requests the information provide by ETHTOOL_GCOALESCE, ETHTOOL_GRINGPARAM,
ETHTOOL_GPAUSEPARAM, ETHTOOL_GCHANNELS, ETHTOOL_GEEE and ETHTOOL_GFECPARAM.
Flags in info_mask allow selecting only some (or on) of these categories.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Documentation/networking/ethtool-netlink.txt |  98 ++++++-
 include/uapi/linux/ethtool_netlink.h         | 118 ++++++++
 net/core/ethtool_netlink.c                   | 411 +++++++++++++++++++++++++++
 3 files changed, 621 insertions(+), 6 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 187fab33f721..45ed1801ab50 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -111,6 +111,8 @@ List of message types
     ETHTOOL_CMD_SET_DRVINFO		response only
     ETHTOOL_CMD_GET_SETTINGS
     ETHTOOL_CMD_SET_SETTINGS
+    ETHTOOL_CMD_GET_PARAMS
+    ETHTOOL_CMD_SET_PARAMS		response only (for now)
 
 All constants use ETHTOOL_CMD_ prefix followed by "GET", "SET" or "ACT" to
 indicate the type.
@@ -231,6 +233,90 @@ is supposed to allow requesting changes without knowing what exactly kernel
 supports.
 
 
+GET_PARAMS
+----------
+
+GET_PARAMS request retrieves information provided by legacy comands
+ETHTOOL_GCOALESCE (coalescing setting), ETHTOOL_GRINGPARAM (ring parameters),
+ETHTOOL_GPAUSEPARAM (pause parameters), ETHTOOL_GCHANNELS (channel settings),
+ETHTOOL_GEEE (EEE settings) and ETHTOOL_GFECPARAM (FEC parameters). For each
+of these, there is a bit in header info_mask so that only one type of
+information can be requested.
+
+Header flags:
+    ETH_PARAMS_RF_COMPACT_BITSETS	bitset form in response (1 is compact)
+
+Header info_mask bits:
+    ETH_PARAMS_IM_COALESCE		coalescing settings
+    ETH_PARAMS_IM_RING			ring parameters
+    ETH_PARAMS_IM_PAUSE			pause parameters
+    ETH_PARAMS_IM_CHANNELS		channel settings
+    ETH_PARAMS_IM_EEE			EEE settings
+    ETH_PARAMS_IM_FEC			FEC parameters
+
+On top level, there is one attribute for each of the information categories,
+the information is nested in it.
+
+    ETHA_PARAMS_COALESCE	(nest)		coalescing
+        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		(bool)
+        ETHA_COALESCE_TX_USE_ADAPTIVE		(bool)
+        ETHA_COALESCE_RATE_SAMPLE_INTERVAL	(u32)
+        ETHA_COALESCE_STATS_BLOCK_USECS		(u32)
+    ETHA_PARAMS_RING		(nest)		ring parameters
+        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_PARAMS_PAUSE		(nest)		pause parameters
+        ETHA_PAUSE_AUTONEG			(bool)
+        ETHA_PAUSE_RX				(bool)
+        ETHA_PAUSE_TX				(bool)
+    ETHA_PARAMS_CHANNELS	(nest)		channel settings
+        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_PARAMS_EEE		(nest)		EEE settings
+        ETHA_EEE_LINK_MODES			(bitset)
+		- modes for which EEE is advertised (value) or supported (mask)
+        ETHA_EEE_PEER_MODES			(bitset)
+		- modes for which link partner advertises EEE
+        ETHA_EEE_ACTIVE				(bool)
+        ETHA_EEE_ENABLED			(bool)
+        ETHA_EEE_TX_LPI_ENABLED			(bool)
+        ETHA_EEE_TX_LPI_TIMER			(u32)
+    ETHA_PARAMS_FEC		(nest)		FEC parameters
+        ETHA_FEC_MODES				(bitfield32)
+		- active (value) and configured (selector) FEC encodings
+
+
+
 Request translation
 -------------------
 
@@ -252,11 +338,11 @@ ETHTOOL_NWAY_RST		n/a
 ETHTOOL_GLINK			ETHTOOL_CMD_GET_SETTINGS
 ETHTOOL_GEEPROM			n/a
 ETHTOOL_SEEPROM			n/a
-ETHTOOL_GCOALESCE		n/a
+ETHTOOL_GCOALESCE		ETHTOOL_CMD_GET_PARAMS
 ETHTOOL_SCOALESCE		n/a
-ETHTOOL_GRINGPARAM		n/a
+ETHTOOL_GRINGPARAM		ETHTOOL_CMD_GET_PARAMS
 ETHTOOL_SRINGPARAM		n/a
-ETHTOOL_GPAUSEPARAM		n/a
+ETHTOOL_GPAUSEPARAM		ETHTOOL_CMD_GET_PARAMS
 ETHTOOL_SPAUSEPARAM		n/a
 ETHTOOL_GRXCSUM			n/a
 ETHTOOL_SRXCSUM			n/a
@@ -298,7 +384,7 @@ ETHTOOL_GRXFHINDIR		n/a
 ETHTOOL_SRXFHINDIR		n/a
 ETHTOOL_GFEATURES		n/a
 ETHTOOL_SFEATURES		n/a
-ETHTOOL_GCHANNELS		n/a
+ETHTOOL_GCHANNELS		ETHTOOL_CMD_GET_PARAMS
 ETHTOOL_SCHANNELS		n/a
 ETHTOOL_SET_DUMP		n/a
 ETHTOOL_GET_DUMP_FLAG		n/a
@@ -306,7 +392,7 @@ ETHTOOL_GET_DUMP_DATA		n/a
 ETHTOOL_GET_TS_INFO		n/a
 ETHTOOL_GMODULEINFO		n/a
 ETHTOOL_GMODULEEEPROM		n/a
-ETHTOOL_GEEE			n/a
+ETHTOOL_GEEE			ETHTOOL_CMD_GET_PARAMS
 ETHTOOL_SEEE			n/a
 ETHTOOL_GRSSH			n/a
 ETHTOOL_SRSSH			n/a
@@ -318,6 +404,6 @@ ETHTOOL_GLINKSETTINGS		ETHTOOL_CMD_GET_SETTINGS
 ETHTOOL_SLINKSETTINGS		ETHTOOL_CMD_SET_SETTINGS
 ETHTOOL_PHY_GTUNABLE		n/a
 ETHTOOL_PHY_STUNABLE		n/a
-ETHTOOL_GFECPARAM		n/a
+ETHTOOL_GFECPARAM		ETHTOOL_CMD_GET_PARAMS
 ETHTOOL_SFECPARAM		n/a
 
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 9520d13fc9ab..8621e0a9d481 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -25,6 +25,8 @@ enum {
 	ETHTOOL_CMD_SET_DRVINFO,	/* only for reply */
 	ETHTOOL_CMD_GET_SETTINGS,
 	ETHTOOL_CMD_SET_SETTINGS,
+	ETHTOOL_CMD_GET_PARAMS,
+	ETHTOOL_CMD_SET_PARAMS,
 
 	__ETHTOOL_CMD_MAX,
 	ETHTOOL_CMD_MAX = (__ETHTOOL_CMD_MAX - 1),
@@ -114,6 +116,122 @@ enum {
 
 #define ETH_SETTINGS_IM_DEFAULT			0x1f
 
+/* GET_PARAMS / SET_PARAMS */
+
+enum {
+	ETHA_PARAMS_UNSPEC,
+	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_RF_COMPACT_BITSETS		0x1
+
+#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
diff --git a/net/core/ethtool_netlink.c b/net/core/ethtool_netlink.c
index 4b14a02be12a..3fb49427f211 100644
--- a/net/core/ethtool_netlink.c
+++ b/net/core/ethtool_netlink.c
@@ -1438,6 +1438,413 @@ static int ethnl_set_settings(struct sk_buff *skb, struct genl_info *info)
 	return ret;
 }
 
+/* GET_PARAMS */
+
+static int ethnl_get_coalesce(struct net_device *dev,
+			      struct ethtool_coalesce *data)
+{
+	if (!dev->ethtool_ops->get_coalesce)
+		return -EOPNOTSUPP;
+	return dev->ethtool_ops->get_coalesce(dev, data);
+}
+
+static int ethnl_get_ring(struct net_device *dev,
+			  struct ethtool_ringparam *data)
+{
+	if (!dev->ethtool_ops->get_ringparam)
+		return -EOPNOTSUPP;
+	dev->ethtool_ops->get_ringparam(dev, data);
+	return 0;
+}
+
+static int ethnl_get_pause(struct net_device *dev,
+			   struct ethtool_pauseparam *data)
+{
+	if (!dev->ethtool_ops->get_pauseparam)
+		return -EOPNOTSUPP;
+	dev->ethtool_ops->get_pauseparam(dev, data);
+	return 0;
+}
+
+static int ethnl_get_channels(struct net_device *dev,
+			      struct ethtool_channels *data)
+{
+	if (!dev->ethtool_ops->get_channels)
+		return -EOPNOTSUPP;
+	dev->ethtool_ops->get_channels(dev, data);
+	return 0;
+}
+
+static int ethnl_get_eee(struct net_device *dev, struct ethtool_eee *data)
+{
+	if (!dev->ethtool_ops->get_eee)
+		return -EOPNOTSUPP;
+	return dev->ethtool_ops->get_eee(dev, data);
+}
+
+static int ethnl_get_fec(struct net_device *dev, struct ethtool_fecparam *data)
+{
+	if (!dev->ethtool_ops->get_fecparam)
+		return -EOPNOTSUPP;
+	return dev->ethtool_ops->get_fecparam(dev, data);
+}
+
+static int ethnl_params_size(struct ethtool_eee *eee, u16 flags, u16 mask)
+{
+	int len = 0;
+
+	if (mask & ETH_PARAMS_IM_COALESCE)
+		len += nla_total_size(20 * nla_total_size(sizeof(u32)) +
+				      2 * nla_total_size(sizeof(u8)));
+	if (mask & ETH_PARAMS_IM_RING)
+		len += nla_total_size(8 * nla_total_size(sizeof(u32)));
+	if (mask & ETH_PARAMS_IM_PAUSE)
+		len += nla_total_size(3 * nla_total_size(sizeof(u8)));
+	if (mask & ETH_PARAMS_IM_CHANNELS)
+		len += nla_total_size(8 * nla_total_size(sizeof(u32)));
+	if (mask & ETH_PARAMS_IM_EEE) {
+		int nlen = 0;
+		int ret;
+
+		/* link_modes */
+		ret = ethnl_bitset_size(flags & ETH_PARAMS_RF_COMPACT_BITSETS,
+					sizeof(u32) * 8, &eee->advertised,
+					&eee->supported, link_mode_names);
+		if (ret < 0)
+			return ret;
+		nlen += ret;
+		/* peer_modes */
+		ret = ethnl_bitset_size(flags & ETH_PARAMS_RF_COMPACT_BITSETS,
+					sizeof(u32) * 8, &eee->lp_advertised,
+					&eee->lp_advertised, link_mode_names);
+		if (ret < 0)
+			return ret;
+		nlen += ret;
+		/* active, enabled, tx_lpi_enabled */
+		nlen += 3 * nla_total_size(sizeof(u8));
+		/* tx_lpi_timer */
+		nlen += nla_total_size(sizeof(u32));
+		/* nest */
+		len += nla_total_size(nlen);
+	}
+	if (mask & ETH_PARAMS_IM_FEC) {
+		int nlen = nla_total_size(sizeof(struct nla_bitfield32));
+
+		len += nla_total_size(nlen);
+	}
+
+	return len;
+}
+
+static int fill_coalesce(struct sk_buff *skb, struct ethtool_coalesce *data)
+{
+	struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_COALESCE);
+
+	if (!nest)
+		return -EMSGSIZE;
+	if (nla_put_u32(skb, ETHA_COALESCE_RX_USECS,
+			data->rx_coalesce_usecs) ||
+	    nla_put_u32(skb, ETHA_COALESCE_RX_MAXFRM,
+			data->rx_max_coalesced_frames) ||
+	    nla_put_u32(skb, ETHA_COALESCE_RX_USECS_IRQ,
+			data->rx_coalesce_usecs_irq) ||
+	    nla_put_u32(skb, ETHA_COALESCE_RX_MAXFRM_IRQ,
+			data->rx_max_coalesced_frames_irq) ||
+	    nla_put_u32(skb, ETHA_COALESCE_RX_USECS_LOW,
+			data->rx_coalesce_usecs_low) ||
+	    nla_put_u32(skb, ETHA_COALESCE_RX_MAXFRM_LOW,
+			data->rx_max_coalesced_frames_low) ||
+	    nla_put_u32(skb, ETHA_COALESCE_RX_USECS_HIGH,
+			data->rx_coalesce_usecs_high) ||
+	    nla_put_u32(skb, ETHA_COALESCE_RX_MAXFRM_HIGH,
+			data->rx_max_coalesced_frames_high) ||
+	    nla_put_u32(skb, ETHA_COALESCE_TX_USECS,
+			data->tx_coalesce_usecs) ||
+	    nla_put_u32(skb, ETHA_COALESCE_TX_MAXFRM,
+			data->tx_max_coalesced_frames) ||
+	    nla_put_u32(skb, ETHA_COALESCE_TX_USECS_IRQ,
+			data->tx_coalesce_usecs_irq) ||
+	    nla_put_u32(skb, ETHA_COALESCE_TX_MAXFRM_IRQ,
+			data->tx_max_coalesced_frames_irq) ||
+	    nla_put_u32(skb, ETHA_COALESCE_TX_USECS_LOW,
+			data->tx_coalesce_usecs_low) ||
+	    nla_put_u32(skb, ETHA_COALESCE_TX_MAXFRM_LOW,
+			data->tx_max_coalesced_frames_low) ||
+	    nla_put_u32(skb, ETHA_COALESCE_TX_USECS_HIGH,
+			data->tx_coalesce_usecs_high) ||
+	    nla_put_u32(skb, ETHA_COALESCE_TX_MAXFRM_HIGH,
+			data->tx_max_coalesced_frames_high) ||
+	    nla_put_u32(skb, ETHA_COALESCE_PKT_RATE_LOW,
+			data->pkt_rate_low) ||
+	    nla_put_u32(skb, ETHA_COALESCE_PKT_RATE_HIGH,
+			data->pkt_rate_high) ||
+	    nla_put_u8(skb, ETHA_COALESCE_RX_USE_ADAPTIVE,
+		       !!data->use_adaptive_rx_coalesce) ||
+	    nla_put_u8(skb, ETHA_COALESCE_TX_USE_ADAPTIVE,
+		       !!data->use_adaptive_tx_coalesce) ||
+	    nla_put_u32(skb, ETHA_COALESCE_RATE_SAMPLE_INTERVAL,
+			data->rate_sample_interval) ||
+	    nla_put_u32(skb, ETHA_COALESCE_STATS_BLOCK_USECS,
+			data->stats_block_coalesce_usecs)) {
+		nla_nest_cancel(skb, nest);
+		return -EMSGSIZE;
+	}
+
+	nla_nest_end(skb, nest);
+	return 0;
+}
+
+static int fill_ring(struct sk_buff *skb, struct ethtool_ringparam *data)
+{
+	struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_RING);
+
+	if (!nest)
+		return -EMSGSIZE;
+	if (nla_put_u32(skb, ETHA_RING_RX_MAX_PENDING,
+			data->rx_max_pending) ||
+	    nla_put_u32(skb, ETHA_RING_RX_MINI_MAX_PENDING,
+			data->rx_mini_max_pending) ||
+	    nla_put_u32(skb, ETHA_RING_RX_JUMBO_MAX_PENDING,
+			data->rx_jumbo_max_pending) ||
+	    nla_put_u32(skb, ETHA_RING_TX_MAX_PENDING,
+			data->tx_max_pending) ||
+	    nla_put_u32(skb, ETHA_RING_RX_PENDING,
+			data->rx_pending) ||
+	    nla_put_u32(skb, ETHA_RING_RX_MINI_PENDING,
+			data->rx_mini_pending) ||
+	    nla_put_u32(skb, ETHA_RING_RX_JUMBO_PENDING,
+			data->rx_jumbo_pending) ||
+	    nla_put_u32(skb, ETHA_RING_TX_PENDING,
+			data->tx_pending)) {
+		nla_nest_cancel(skb, nest);
+		return -EMSGSIZE;
+	}
+
+	nla_nest_end(skb, nest);
+	return 0;
+}
+
+static int fill_pause(struct sk_buff *skb, struct ethtool_pauseparam *data)
+{
+	struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_PAUSE);
+
+	if (!nest)
+		return -EMSGSIZE;
+	if (nla_put_u8(skb, ETHA_PAUSE_AUTONEG, !!data->autoneg) ||
+	    nla_put_u8(skb, ETHA_PAUSE_RX, !!data->rx_pause) ||
+	    nla_put_u8(skb, ETHA_PAUSE_TX, !!data->tx_pause)) {
+		nla_nest_cancel(skb, nest);
+		return -EMSGSIZE;
+	}
+
+	nla_nest_end(skb, nest);
+	return 0;
+}
+
+static int fill_channels(struct sk_buff *skb, struct ethtool_channels *data)
+{
+	struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_CHANNELS);
+
+	if (!nest)
+		return -EMSGSIZE;
+	if (nla_put_u32(skb, ETHA_CHANNELS_MAX_RX, data->max_rx) ||
+	    nla_put_u32(skb, ETHA_CHANNELS_MAX_TX, data->max_tx) ||
+	    nla_put_u32(skb, ETHA_CHANNELS_MAX_OTHER, data->max_other) ||
+	    nla_put_u32(skb, ETHA_CHANNELS_MAX_COMBINED, data->max_combined) ||
+	    nla_put_u32(skb, ETHA_CHANNELS_RX_COUNT, data->rx_count) ||
+	    nla_put_u32(skb, ETHA_CHANNELS_TX_COUNT, data->tx_count) ||
+	    nla_put_u32(skb, ETHA_CHANNELS_OTHER_COUNT, data->other_count) ||
+	    nla_put_u32(skb, ETHA_CHANNELS_COMBINED_COUNT,
+			data->combined_count)) {
+		return -EMSGSIZE;
+	}
+
+	nla_nest_end(skb, nest);
+	return 0;
+}
+
+static int fill_eee(struct sk_buff *skb, struct ethtool_eee *data, u16 flags)
+{
+	struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_EEE);
+	int ret;
+
+	if (!nest)
+		return -EMSGSIZE;
+	ret = ethnl_put_bitset(skb, ETHA_EEE_LINK_MODES,
+			       flags & ETH_PARAMS_RF_COMPACT_BITSETS,
+			       sizeof(data->advertised) * 8, &data->advertised,
+			       &data->supported, link_mode_names);
+	if (ret < 0)
+		goto err;
+	ret = ethnl_put_bitset(skb, ETHA_EEE_PEER_MODES,
+			       flags & ETH_PARAMS_RF_COMPACT_BITSETS,
+			       sizeof(data->advertised) * 8,
+			       &data->lp_advertised, &data->lp_advertised,
+			       link_mode_names);
+	if (ret < 0)
+		goto err;
+
+	if (nla_put_u8(skb, ETHA_EEE_ACTIVE, !!data->eee_active) ||
+	    nla_put_u8(skb, ETHA_EEE_ENABLED, !!data->eee_enabled) ||
+	    nla_put_u8(skb, ETHA_EEE_TX_LPI_ENABLED, !!data->tx_lpi_enabled) ||
+	    nla_put_u32(skb, ETHA_EEE_TX_LPI_TIMER, data->tx_lpi_timer)) {
+		ret = -EMSGSIZE;
+		goto err;
+	}
+
+	nla_nest_end(skb, nest);
+	return 0;
+err:
+	nla_nest_cancel(skb, nest);
+	return ret;
+}
+
+static int fill_fec(struct sk_buff *skb, struct ethtool_fecparam *data)
+{
+	struct nlattr *nest = ethnl_nest_start(skb, ETHA_PARAMS_FEC);
+
+	if (!nest)
+		return -EMSGSIZE;
+	if (nla_put_bitfield32(skb, ETHA_FEC_MODES, data->active_fec,
+			       data->fec)) {
+		nla_nest_cancel(skb, nest);
+		return -EMSGSIZE;
+	}
+
+	nla_nest_end(skb, nest);
+	return 0;
+}
+
+static int ethnl_get_params(struct sk_buff *skb, struct genl_info *info)
+{
+	struct ethtool_coalesce coalesce = {};
+	struct ethtool_channels channels = {};
+	struct ethtool_pauseparam pause = {};
+	struct ethtool_ringparam ring = {};
+	struct ethtool_fecparam fec = {};
+	struct ethtool_eee eee = {};
+	unsigned int reply_len = 0;
+	struct ethnlmsghdr *ehdr;
+	struct net_device *dev;
+	struct sk_buff *rskb;
+	u16 req_flags;
+	u16 req_mask;
+	int ret = 0;
+
+	ehdr = info->userhdr;
+	if (!ehdr->info_mask)
+		ehdr->info_mask = ETH_PARAMS_IM_DEFAULT;
+	req_mask = ehdr->info_mask;
+	req_flags = ehdr->flags;
+
+	dev = ethnl_dev_get(info);
+	if (IS_ERR(dev))
+		return PTR_ERR(dev);
+
+	if (req_mask & ETH_PARAMS_IM_COALESCE) {
+		ret = ethnl_get_coalesce(dev, &coalesce);
+		if (ret < 0) {
+			warn_partial_info(info);
+			req_mask &= ~ETH_PARAMS_IM_COALESCE;
+		}
+	}
+	if (req_mask & ETH_PARAMS_IM_RING) {
+		ret = ethnl_get_ring(dev, &ring);
+		if (ret < 0) {
+			warn_partial_info(info);
+			req_mask &= ~ETH_PARAMS_IM_RING;
+		}
+	}
+	if (req_mask & ETH_PARAMS_IM_PAUSE) {
+		ret = ethnl_get_pause(dev, &pause);
+		if (ret < 0) {
+			warn_partial_info(info);
+			req_mask &= ~ETH_PARAMS_IM_PAUSE;
+		}
+	}
+	if (req_mask & ETH_PARAMS_IM_CHANNELS) {
+		ret = ethnl_get_channels(dev, &channels);
+		if (ret < 0) {
+			warn_partial_info(info);
+			req_mask &= ~ETH_PARAMS_IM_CHANNELS;
+		}
+	}
+	if (req_mask & ETH_PARAMS_IM_EEE) {
+		ret = ethnl_get_eee(dev, &eee);
+		if (ret < 0) {
+			warn_partial_info(info);
+			req_mask &= ~ETH_PARAMS_IM_EEE;
+		}
+	}
+	if (req_mask & ETH_PARAMS_IM_FEC) {
+		ret = ethnl_get_fec(dev, &fec);
+		if (ret < 0) {
+			warn_partial_info(info);
+			req_mask &= ~ETH_PARAMS_IM_FEC;
+		}
+	}
+
+	ret = ethnl_params_size(&eee, req_flags, req_mask);
+	if (ret < 0)
+		goto err_putdev;
+	else
+		reply_len = ret;
+	rskb = ethnl_reply_init(reply_len, dev, ETHTOOL_CMD_SET_PARAMS, info,
+				&ehdr);
+	ret = -ENOMEM;
+	if (!rskb)
+		goto err_putdev;
+	if (req_mask != ETH_SETTINGS_IM_DEFAULT)
+		ehdr->info_mask = req_mask;
+
+	if (req_mask & ETH_PARAMS_IM_COALESCE) {
+		ret = fill_coalesce(rskb, &coalesce);
+		if (ret < 0)
+			goto err;
+	}
+	if (req_mask & ETH_PARAMS_IM_RING) {
+		ret = fill_ring(rskb, &ring);
+		if (ret < 0)
+			goto err;
+	}
+	if (req_mask & ETH_PARAMS_IM_PAUSE) {
+		ret = fill_pause(rskb, &pause);
+		if (ret < 0)
+			goto err;
+	}
+	if (req_mask & ETH_PARAMS_IM_CHANNELS) {
+		ret = fill_channels(rskb, &channels);
+		if (ret < 0)
+			goto err;
+	}
+	if (req_mask & ETH_PARAMS_IM_EEE) {
+		ret = fill_eee(rskb, &eee, req_flags);
+		if (ret < 0)
+			goto err;
+	}
+	if (req_mask & ETH_PARAMS_IM_FEC) {
+		ret = fill_fec(rskb, &fec);
+		if (ret < 0)
+			goto err;
+	}
+
+	dev_put(dev);
+	genlmsg_end(rskb, ehdr);
+	return genlmsg_reply(rskb, info);
+
+err:
+	nlmsg_free(rskb);
+err_putdev:
+	dev_put(dev);
+	if (ret == -EMSGSIZE)
+		GENL_SET_ERR_MSG(info,
+				 "kernel error, see kernel log for details");
+	WARN_ONCE(ret == -EMSGSIZE,
+		  "calculated message payload length (%d) not sufficient\n",
+		  reply_len);
+	return ret;
+}
+
 /* genetlink paperwork */
 
 static const struct genl_ops ethtool_genl_ops[] = {
@@ -1455,6 +1862,10 @@ static const struct genl_ops ethtool_genl_ops[] = {
 		.policy	= settings_policy,
 		.doit	= ethnl_set_settings,
 	},
+	{
+		.cmd	= ETHTOOL_CMD_GET_PARAMS,
+		.doit	= ethnl_get_params,
+	},
 };
 
 static struct genl_family ethtool_genl_family = {
-- 
2.15.1

^ permalink raw reply related

* [RFC PATCH 9/9] ethtool: implement SET_PARAMS message
From: Michal Kubecek @ 2017-12-11 13:54 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel
In-Reply-To: <cover.1513000306.git.mkubecek@suse.cz>

Sets the information provided by ETHTOOL_SCOALESCE, ETHTOOL_SRINGPARAM,
ETHTOOL_SPAUSEPARAM, ETHTOOL_SCHANNELS, ETHTOOL_SEEE and ETHTOOL_SFECPARAM.
Each of these has corresponding nesting attribute containing attributes for
its settings. This way, userspace can request changes equivalent to one or
more of the legacy requests (and provide only part of the data to update).

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Documentation/networking/ethtool-netlink.txt |  71 ++++-
 net/core/ethtool_netlink.c                   | 422 +++++++++++++++++++++++++++
 2 files changed, 486 insertions(+), 7 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index 45ed1801ab50..e96c18f52356 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -112,7 +112,7 @@ List of message types
     ETHTOOL_CMD_GET_SETTINGS
     ETHTOOL_CMD_SET_SETTINGS
     ETHTOOL_CMD_GET_PARAMS
-    ETHTOOL_CMD_SET_PARAMS		response only (for now)
+    ETHTOOL_CMD_SET_PARAMS
 
 All constants use ETHTOOL_CMD_ prefix followed by "GET", "SET" or "ACT" to
 indicate the type.
@@ -316,6 +316,63 @@ the information is nested in it.
 		- active (value) and configured (selector) FEC encodings
 
 
+SET_PARAMS
+----------
+
+SET_PARAMS request modifies the settings retrieved by GET_PARAMS, i.e. it
+replaces ETHTOOL_SCOALESCE, ETHTOOL_SRINGPARAM, ETHTOOL_SPAUSEPARAM,
+ETHTOOL_SCHANNELS, ETHTOOL_SEEE and ETHTOOL_SFECPARAM legacy commands. For
+each of these, relevant data attributes are contained in a corresponding nest
+attribute. Some of the attributes provided by GET_SETPARAMS are read only and
+cannot be set by SET_PARAMS request.
+
+    ETHA_PARAMS_COALESCE	(nest)		coalescing
+        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		(bool)
+        ETHA_COALESCE_TX_USE_ADAPTIVE		(bool)
+        ETHA_COALESCE_RATE_SAMPLE_INTERVAL	(u32)
+        ETHA_COALESCE_STATS_BLOCK_USECS		(u32)
+    ETHA_PARAMS_RING		(nest)		ring parameters
+        ETHA_RING_RX_PENDING			(u32)
+        ETHA_RING_RX_MINI_PENDING		(u32)
+        ETHA_RING_RX_JUMBO_PENDING		(u32)
+        ETHA_RING_TX_PENDING			(u32)
+    ETHA_PARAMS_PAUSE		(nest)		pause parameters
+        ETHA_PAUSE_AUTONEG			(bool)
+        ETHA_PAUSE_RX				(bool)
+        ETHA_PAUSE_TX				(bool)
+    ETHA_PARAMS_CHANNELS	(nest)		channel settings
+        ETHA_CHANNELS_RX_COUNT			(u32)
+        ETHA_CHANNELS_TX_COUNT			(u32)
+        ETHA_CHANNELS_OTHER_COUNT		(u32)
+        ETHA_CHANNELS_COMBINED_COUNT		(u32)
+    ETHA_PARAMS_EEE		(nest)		EEE settings
+        ETHA_EEE_LINK_MODES			(bitset)
+		- change modes for which EEE is advertised
+        ETHA_EEE_ENABLED			(bool)
+        ETHA_EEE_TX_LPI_ENABLED			(bool)
+        ETHA_EEE_TX_LPI_TIMER			(u32)
+    ETHA_PARAMS_FEC		(nest)		FEC parameters
+        ETHA_FEC_MODES				(bitfield32)
+		- change configured FEC encodings
+
 
 Request translation
 -------------------
@@ -339,11 +396,11 @@ ETHTOOL_GLINK			ETHTOOL_CMD_GET_SETTINGS
 ETHTOOL_GEEPROM			n/a
 ETHTOOL_SEEPROM			n/a
 ETHTOOL_GCOALESCE		ETHTOOL_CMD_GET_PARAMS
-ETHTOOL_SCOALESCE		n/a
+ETHTOOL_SCOALESCE		ETHTOOL_CMD_SET_PARAMS
 ETHTOOL_GRINGPARAM		ETHTOOL_CMD_GET_PARAMS
-ETHTOOL_SRINGPARAM		n/a
+ETHTOOL_SRINGPARAM		ETHTOOL_CMD_SET_PARAMS
 ETHTOOL_GPAUSEPARAM		ETHTOOL_CMD_GET_PARAMS
-ETHTOOL_SPAUSEPARAM		n/a
+ETHTOOL_SPAUSEPARAM		ETHTOOL_CMD_SET_PARAMS
 ETHTOOL_GRXCSUM			n/a
 ETHTOOL_SRXCSUM			n/a
 ETHTOOL_GTXCSUM			n/a
@@ -385,7 +442,7 @@ ETHTOOL_SRXFHINDIR		n/a
 ETHTOOL_GFEATURES		n/a
 ETHTOOL_SFEATURES		n/a
 ETHTOOL_GCHANNELS		ETHTOOL_CMD_GET_PARAMS
-ETHTOOL_SCHANNELS		n/a
+ETHTOOL_SCHANNELS		ETHTOOL_CMD_SET_PARAMS
 ETHTOOL_SET_DUMP		n/a
 ETHTOOL_GET_DUMP_FLAG		n/a
 ETHTOOL_GET_DUMP_DATA		n/a
@@ -393,7 +450,7 @@ ETHTOOL_GET_TS_INFO		n/a
 ETHTOOL_GMODULEINFO		n/a
 ETHTOOL_GMODULEEEPROM		n/a
 ETHTOOL_GEEE			ETHTOOL_CMD_GET_PARAMS
-ETHTOOL_SEEE			n/a
+ETHTOOL_SEEE			ETHTOOL_CMD_SET_PARAMS
 ETHTOOL_GRSSH			n/a
 ETHTOOL_SRSSH			n/a
 ETHTOOL_GTUNABLE		n/a
@@ -405,5 +462,5 @@ ETHTOOL_SLINKSETTINGS		ETHTOOL_CMD_SET_SETTINGS
 ETHTOOL_PHY_GTUNABLE		n/a
 ETHTOOL_PHY_STUNABLE		n/a
 ETHTOOL_GFECPARAM		ETHTOOL_CMD_GET_PARAMS
-ETHTOOL_SFECPARAM		n/a
+ETHTOOL_SFECPARAM		ETHTOOL_CMD_SET_PARAMS
 
diff --git a/net/core/ethtool_netlink.c b/net/core/ethtool_netlink.c
index 3fb49427f211..874716baaec3 100644
--- a/net/core/ethtool_netlink.c
+++ b/net/core/ethtool_netlink.c
@@ -1845,6 +1845,422 @@ static int ethnl_get_params(struct sk_buff *skb, struct genl_info *info)
 	return ret;
 }
 
+/* SET_PARAMS */
+
+static const struct nla_policy params_policy[ETHA_SETTINGS_MAX + 1] = {
+	[ETHA_PARAMS_UNSPEC]		= { .type = NLA_UNSPEC },
+	[ETHA_PARAMS_COALESCE]		= { .type = NLA_NESTED },
+	[ETHA_PARAMS_RING]		= { .type = NLA_NESTED },
+	[ETHA_PARAMS_PAUSE]		= { .type = NLA_NESTED },
+	[ETHA_PARAMS_CHANNELS]		= { .type = NLA_NESTED },
+	[ETHA_PARAMS_EEE]		= { .type = NLA_NESTED },
+	[ETHA_PARAMS_FEC]		= { .type = NLA_NESTED },
+};
+
+static const struct nla_policy coalesce_policy[ETHA_COALESCE_MAX + 1] = {
+	[ETHA_COALESCE_UNSPEC]			= { .type = NLA_UNSPEC },
+	[ETHA_COALESCE_RX_USECS]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_RX_MAXFRM]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_RX_USECS_IRQ]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_RX_MAXFRM_IRQ]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_RX_USECS_LOW]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_RX_MAXFRM_LOW]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_RX_USECS_HIGH]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_RX_MAXFRM_HIGH]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_TX_USECS]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_TX_MAXFRM]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_TX_USECS_IRQ]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_TX_MAXFRM_IRQ]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_TX_USECS_LOW]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_TX_MAXFRM_LOW]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_TX_USECS_HIGH]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_TX_MAXFRM_HIGH]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_PKT_RATE_LOW]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_PKT_RATE_HIGH]		= { .type = NLA_U32 },
+	[ETHA_COALESCE_RX_USE_ADAPTIVE]		= { .type = NLA_U8 },
+	[ETHA_COALESCE_TX_USE_ADAPTIVE]		= { .type = NLA_U8 },
+	[ETHA_COALESCE_RATE_SAMPLE_INTERVAL]	= { .type = NLA_U32 },
+	[ETHA_COALESCE_STATS_BLOCK_USECS]	= { .type = NLA_U32 },
+};
+
+static int update_coalesce(struct genl_info *info, struct net_device *dev,
+			   struct nlattr *nest)
+{
+	struct nlattr *tb[ETHA_COALESCE_MAX + 1];
+	struct ethtool_coalesce data = {};
+	bool mod = false;
+	int ret;
+
+	if (!nest)
+		return 0;
+	if (!dev->ethtool_ops->get_coalesce || !dev->ethtool_ops->set_coalesce)
+		return -EOPNOTSUPP;
+	ret = dev->ethtool_ops->get_coalesce(dev, &data);
+	if (ret < 0)
+		return ret;
+
+	ret = nla_parse_nested(tb, ETHA_COALESCE_MAX, nest, coalesce_policy,
+			       info->extack);
+	if (ret < 0)
+		return ret;
+
+	if (ethnl_update_u32(&data.rx_coalesce_usecs,
+			     tb[ETHA_COALESCE_RX_USECS]))
+		mod = true;
+	if (ethnl_update_u32(&data.rx_max_coalesced_frames,
+			     tb[ETHA_COALESCE_RX_MAXFRM]))
+		mod = true;
+	if (ethnl_update_u32(&data.rx_coalesce_usecs_irq,
+			     tb[ETHA_COALESCE_RX_USECS_IRQ]))
+		mod = true;
+	if (ethnl_update_u32(&data.rx_max_coalesced_frames_irq,
+			     tb[ETHA_COALESCE_RX_MAXFRM_IRQ]))
+		mod = true;
+	if (ethnl_update_u32(&data.rx_coalesce_usecs_low,
+			     tb[ETHA_COALESCE_RX_USECS_LOW]))
+		mod = true;
+	if (ethnl_update_u32(&data.rx_max_coalesced_frames_low,
+			     tb[ETHA_COALESCE_RX_MAXFRM_LOW]))
+		mod = true;
+	if (ethnl_update_u32(&data.rx_coalesce_usecs_high,
+			     tb[ETHA_COALESCE_RX_USECS_HIGH]))
+		mod = true;
+	if (ethnl_update_u32(&data.rx_max_coalesced_frames_high,
+			     tb[ETHA_COALESCE_RX_MAXFRM_HIGH]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_coalesce_usecs,
+			     tb[ETHA_COALESCE_TX_USECS]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_max_coalesced_frames,
+			     tb[ETHA_COALESCE_TX_MAXFRM]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_coalesce_usecs_irq,
+			     tb[ETHA_COALESCE_TX_USECS_IRQ]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_max_coalesced_frames_irq,
+			     tb[ETHA_COALESCE_TX_MAXFRM_IRQ]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_coalesce_usecs_low,
+			     tb[ETHA_COALESCE_TX_USECS_LOW]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_max_coalesced_frames_low,
+			     tb[ETHA_COALESCE_TX_MAXFRM_LOW]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_coalesce_usecs_high,
+			     tb[ETHA_COALESCE_TX_USECS_HIGH]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_max_coalesced_frames_high,
+			     tb[ETHA_COALESCE_TX_MAXFRM_HIGH]))
+		mod = true;
+	if (ethnl_update_u32(&data.pkt_rate_low,
+			     tb[ETHA_COALESCE_PKT_RATE_LOW]))
+		mod = true;
+	if (ethnl_update_u32(&data.pkt_rate_high,
+			     tb[ETHA_COALESCE_PKT_RATE_HIGH]))
+		mod = true;
+	if (ethnl_update_bool32(&data.use_adaptive_rx_coalesce,
+				tb[ETHA_COALESCE_RX_USE_ADAPTIVE]))
+		mod = true;
+	if (ethnl_update_bool32(&data.use_adaptive_tx_coalesce,
+				tb[ETHA_COALESCE_TX_USE_ADAPTIVE]))
+		mod = true;
+	if (ethnl_update_u32(&data.rate_sample_interval,
+			     tb[ETHA_COALESCE_RATE_SAMPLE_INTERVAL]))
+		mod = true;
+	if (ethnl_update_u32(&data.stats_block_coalesce_usecs,
+			     tb[ETHA_COALESCE_STATS_BLOCK_USECS]))
+		mod = true;
+
+	if (mod)
+		return dev->ethtool_ops->set_coalesce(dev, &data);
+	else
+		return 0;
+}
+
+static const struct nla_policy ring_policy[ETHA_RING_MAX + 1] = {
+	[ETHA_RING_UNSPEC]			= { .type = NLA_UNSPEC },
+	[ETHA_RING_RX_MAX_PENDING]		= { .type = NLA_U32 },
+	[ETHA_RING_RX_MINI_MAX_PENDING]		= { .type = NLA_U32 },
+	[ETHA_RING_RX_JUMBO_MAX_PENDING]	= { .type = NLA_U32 },
+	[ETHA_RING_TX_MAX_PENDING]		= { .type = NLA_U32 },
+	[ETHA_RING_RX_PENDING]			= { .type = NLA_U32 },
+	[ETHA_RING_RX_MINI_PENDING]		= { .type = NLA_U32 },
+	[ETHA_RING_RX_JUMBO_PENDING]		= { .type = NLA_U32 },
+	[ETHA_RING_TX_PENDING]			= { .type = NLA_U32 },
+};
+
+static int update_ring(struct genl_info *info, struct net_device *dev,
+		       struct nlattr *nest)
+{
+	struct nlattr *tb[ETHA_RING_MAX + 1];
+	struct ethtool_ringparam data = {};
+	bool mod = false;
+	int ret;
+
+	if (!nest)
+		return 0;
+	if (!dev->ethtool_ops->get_ringparam ||
+	    !dev->ethtool_ops->set_ringparam)
+		return -EOPNOTSUPP;
+	dev->ethtool_ops->get_ringparam(dev, &data);
+
+	ret = nla_parse_nested(tb, ETHA_RING_MAX, nest, ring_policy,
+			       info->extack);
+	if (ret < 0)
+		return ret;
+	/* read only attributes */
+	if (tb[ETHA_RING_RX_MAX_PENDING] || tb[ETHA_RING_RX_MINI_MAX_PENDING] ||
+	    tb[ETHA_RING_RX_JUMBO_MAX_PENDING] ||
+	    tb[ETHA_RING_TX_MAX_PENDING]) {
+		GENL_SET_ERR_MSG(info, "attempt to set a read only attribute");
+		return -EINVAL;
+	}
+
+	if (ethnl_update_u32(&data.rx_pending, tb[ETHA_RING_RX_PENDING]))
+		mod = true;
+	if (ethnl_update_u32(&data.rx_mini_pending,
+			     tb[ETHA_RING_RX_MINI_PENDING]))
+		mod = true;
+	if (ethnl_update_u32(&data.rx_jumbo_pending,
+			     tb[ETHA_RING_RX_JUMBO_PENDING]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_pending, tb[ETHA_RING_TX_PENDING]))
+		mod = true;
+
+	if (mod)
+		return dev->ethtool_ops->set_ringparam(dev, &data);
+	else
+		return 0;
+}
+
+static const struct nla_policy pause_policy[ETHA_PAUSE_MAX + 1] = {
+	[ETHA_PAUSE_UNSPEC]	= { .type = NLA_UNSPEC },
+	[ETHA_PAUSE_AUTONEG]	= { .type = NLA_U8 },
+	[ETHA_PAUSE_RX]		= { .type = NLA_U8 },
+	[ETHA_PAUSE_TX]		= { .type = NLA_U8 },
+};
+
+static int update_pause(struct genl_info *info, struct net_device *dev,
+			struct nlattr *nest)
+{
+	struct nlattr *tb[ETHA_RING_MAX + 1];
+	struct ethtool_pauseparam data = {};
+	bool mod = false;
+	int ret;
+
+	if (!nest)
+		return 0;
+	if (!dev->ethtool_ops->get_pauseparam ||
+	    !dev->ethtool_ops->set_pauseparam)
+		return -EOPNOTSUPP;
+	dev->ethtool_ops->get_pauseparam(dev, &data);
+
+	ret = nla_parse_nested(tb, ETHA_PAUSE_MAX, nest, pause_policy,
+			       info->extack);
+	if (ret < 0)
+		return ret;
+
+	if (ethnl_update_u32(&data.autoneg, tb[ETHA_PAUSE_AUTONEG]))
+		mod = true;
+	if (ethnl_update_u32(&data.rx_pause, tb[ETHA_PAUSE_RX]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_pause, tb[ETHA_PAUSE_TX]))
+		mod = true;
+
+	if (mod)
+		return dev->ethtool_ops->set_pauseparam(dev, &data);
+	else
+		return 0;
+}
+
+static const struct nla_policy channels_policy[ETHA_CHANNELS_MAX + 1] = {
+	[ETHA_CHANNELS_UNSPEC]		= { .type = NLA_UNSPEC },
+	[ETHA_CHANNELS_MAX_RX]		= { .type = NLA_U32 },
+	[ETHA_CHANNELS_MAX_TX]		= { .type = NLA_U32 },
+	[ETHA_CHANNELS_MAX_OTHER]	= { .type = NLA_U32 },
+	[ETHA_CHANNELS_MAX_COMBINED]	= { .type = NLA_U32 },
+	[ETHA_CHANNELS_RX_COUNT]	= { .type = NLA_U32 },
+	[ETHA_CHANNELS_TX_COUNT]	= { .type = NLA_U32 },
+	[ETHA_CHANNELS_OTHER_COUNT]	= { .type = NLA_U32 },
+	[ETHA_CHANNELS_COMBINED_COUNT]	= { .type = NLA_U32 },
+};
+
+static int update_channels(struct genl_info *info, struct net_device *dev,
+			   struct nlattr *nest)
+{
+	struct nlattr *tb[ETHA_CHANNELS_MAX + 1];
+	struct ethtool_channels data = {};
+	bool mod = false;
+	int ret;
+
+	if (!nest)
+		return 0;
+	if (!dev->ethtool_ops->get_channels ||
+	    !dev->ethtool_ops->set_channels)
+		return -EOPNOTSUPP;
+	dev->ethtool_ops->get_channels(dev, &data);
+
+	ret = nla_parse_nested(tb, ETHA_CHANNELS_MAX, nest, channels_policy,
+			       info->extack);
+	if (ret < 0)
+		return ret;
+	/* read only attributes */
+	if (tb[ETHA_CHANNELS_MAX_RX] || tb[ETHA_CHANNELS_MAX_TX] ||
+	    tb[ETHA_CHANNELS_MAX_OTHER] || tb[ETHA_CHANNELS_MAX_COMBINED]) {
+		GENL_SET_ERR_MSG(info, "attempt to set a read only attribute");
+		return -EINVAL;
+	}
+
+	if (ethnl_update_u32(&data.rx_count, tb[ETHA_CHANNELS_RX_COUNT]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_count, tb[ETHA_CHANNELS_TX_COUNT]))
+		mod = true;
+	if (ethnl_update_u32(&data.other_count, tb[ETHA_CHANNELS_OTHER_COUNT]))
+		mod = true;
+	if (ethnl_update_u32(&data.combined_count,
+			     tb[ETHA_CHANNELS_COMBINED_COUNT]))
+		mod = true;
+
+	if (mod)
+		return dev->ethtool_ops->set_channels(dev, &data);
+	else
+		return 0;
+}
+
+static const struct nla_policy eee_policy[ETHA_EEE_MAX + 1] = {
+	[ETHA_EEE_UNSPEC]		= { .type = NLA_UNSPEC },
+	[ETHA_EEE_LINK_MODES]		= { .type = NLA_NESTED },
+	[ETHA_EEE_PEER_MODES]		= { .type = NLA_NESTED },
+	[ETHA_EEE_ACTIVE]		= { .type = NLA_U8 },
+	[ETHA_EEE_ENABLED]		= { .type = NLA_U8 },
+	[ETHA_EEE_TX_LPI_ENABLED]	= { .type = NLA_U8 },
+	[ETHA_EEE_TX_LPI_TIMER]		= { .type = NLA_U32 },
+};
+
+static int update_eee(struct genl_info *info, struct net_device *dev,
+		      struct nlattr *nest)
+{
+	struct nlattr *tb[ETHA_EEE_MAX + 1];
+	struct ethtool_eee data = {};
+	bool mod = false;
+	int ret;
+
+	if (!nest)
+		return 0;
+	if (!dev->ethtool_ops->get_eee ||
+	    !dev->ethtool_ops->set_eee)
+		return -EOPNOTSUPP;
+	ret = dev->ethtool_ops->get_eee(dev, &data);
+	if (ret < 0)
+		return ret;
+
+	ret = nla_parse_nested(tb, ETHA_EEE_MAX, nest, eee_policy,
+			       info->extack);
+	if (ret < 0)
+		return ret;
+	/* read only attributes */
+	if (tb[ETHA_EEE_PEER_MODES] || tb[ETHA_EEE_ACTIVE]) {
+		GENL_SET_ERR_MSG(info, "attempt to set a read only attribute");
+		return -EINVAL;
+	}
+
+	if (ethnl_update_bitset32(&data.advertised, 32, tb[ETHA_EEE_LINK_MODES],
+				  &ret, link_mode_names, info))
+		mod = true;
+	if (ret < 0)
+		return ret;
+	if (ethnl_update_bool32(&data.eee_enabled, tb[ETHA_EEE_ENABLED]))
+		mod = true;
+	if (ethnl_update_bool32(&data.tx_lpi_enabled,
+				tb[ETHA_EEE_TX_LPI_ENABLED]))
+		mod = true;
+	if (ethnl_update_u32(&data.tx_lpi_timer, tb[ETHA_EEE_TX_LPI_TIMER]))
+		mod = true;
+
+	if (mod)
+		return dev->ethtool_ops->set_eee(dev, &data);
+	else
+		return 0;
+}
+
+static const struct nla_policy fec_policy[ETHA_FEC_MAX + 1] = {
+	[ETHA_FEC_UNSPEC]		= { .type = NLA_UNSPEC },
+	[ETHA_FEC_MODES]		= { .type = NLA_U32 },
+};
+
+static int update_fec(struct genl_info *info, struct net_device *dev,
+		      struct nlattr *nest)
+{
+	struct nlattr *tb[ETHA_FEC_MAX + 1];
+	struct ethtool_fecparam data = {};
+	bool mod = false;
+	int ret;
+
+	if (!nest)
+		return 0;
+	if (!dev->ethtool_ops->get_fecparam ||
+	    !dev->ethtool_ops->set_fecparam)
+		return -EOPNOTSUPP;
+	ret = dev->ethtool_ops->get_fecparam(dev, &data);
+	if (ret < 0)
+		return ret;
+
+	ret = nla_parse_nested(tb, ETHA_FEC_MAX, nest, fec_policy,
+			       info->extack);
+	if (ret < 0)
+		return ret;
+
+	if (ethnl_update_bitfield32(&data.fec, tb[ETHA_FEC_MODES]))
+		mod = true;
+
+	if (mod)
+		return dev->ethtool_ops->set_fecparam(dev, &data);
+	else
+		return 0;
+}
+
+static int ethnl_set_params(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr *tb[ETHA_PARAMS_MAX + 1];
+	struct ethnlmsghdr *ehdr;
+	struct net_device *dev;
+	int ret;
+
+	ehdr = info->userhdr;
+	dev = ethnl_dev_get(info);
+	if (IS_ERR(dev))
+		return PTR_ERR(dev);
+
+	ret = genlmsg_parse(info->nlhdr, &ethtool_genl_family, tb,
+			    ETHA_PARAMS_MAX, params_policy, info->extack);
+	if (ret < 0)
+		goto err_putdev;
+
+	ret = update_coalesce(info, dev, tb[ETHA_PARAMS_COALESCE]);
+	if (ret < 0)
+		goto err_putdev;
+	ret = update_ring(info, dev, tb[ETHA_PARAMS_RING]);
+	if (ret < 0)
+		goto err_putdev;
+	ret = update_pause(info, dev, tb[ETHA_PARAMS_PAUSE]);
+	if (ret < 0)
+		goto err_putdev;
+	ret = update_channels(info, dev, tb[ETHA_PARAMS_CHANNELS]);
+	if (ret < 0)
+		goto err_putdev;
+	ret = update_eee(info, dev, tb[ETHA_PARAMS_EEE]);
+	if (ret < 0)
+		goto err_putdev;
+	ret = update_fec(info, dev, tb[ETHA_PARAMS_FEC]);
+	if (ret < 0)
+		goto err_putdev;
+
+	ret = 0;
+err_putdev:
+	dev_put(dev);
+	return ret;
+}
+
 /* genetlink paperwork */
 
 static const struct genl_ops ethtool_genl_ops[] = {
@@ -1866,6 +2282,12 @@ static const struct genl_ops ethtool_genl_ops[] = {
 		.cmd	= ETHTOOL_CMD_GET_PARAMS,
 		.doit	= ethnl_get_params,
 	},
+	{
+		.cmd	= ETHTOOL_CMD_SET_PARAMS,
+		.flags	= GENL_UNS_ADMIN_PERM,
+		.policy	= params_policy,
+		.doit	= ethnl_set_params,
+	},
 };
 
 static struct genl_family ethtool_genl_family = {
-- 
2.15.1

^ permalink raw reply related

* Re: [PATCH] Revert "ravb: add workaround for clock when resuming with WoL enabled"
From: Niklas Söderlund @ 2017-12-11 13:55 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Sergei Shtylyov, David S . Miller, netdev, linux-renesas-soc
In-Reply-To: <1512982449-11051-1-git-send-email-geert+renesas@glider.be>

Hi Geert,

Thanks for your patch.

Feels good to get rid of this hack :-)

On 2017-12-11 09:54:09 +0100, Geert Uytterhoeven wrote:
> This reverts commit fbf3d034f2ff6264183cfa6845770e8cc2a986c8.
> 
> As of commit 560869100b99a3da ("clk: renesas: cpg-mssr: Restore module
> clocks during resume"), the workaround is no longer needed.
> 
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>

Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

> ---
>  drivers/net/ethernet/renesas/ravb_main.c | 27 ++-------------------------
>  1 file changed, 2 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
> index 2b962d349f5f415b..009780df664b4a09 100644
> --- a/drivers/net/ethernet/renesas/ravb_main.c
> +++ b/drivers/net/ethernet/renesas/ravb_main.c
> @@ -2308,32 +2308,9 @@ static int __maybe_unused ravb_resume(struct device *dev)
>  	struct ravb_private *priv = netdev_priv(ndev);
>  	int ret = 0;
>  
> -	if (priv->wol_enabled) {
> -		/* Reduce the usecount of the clock to zero and then
> -		 * restore it to its original value. This is done to force
> -		 * the clock to be re-enabled which is a workaround
> -		 * for renesas-cpg-mssr driver which do not enable clocks
> -		 * when resuming from PSCI suspend/resume.
> -		 *
> -		 * Without this workaround the driver fails to communicate
> -		 * with the hardware if WoL was enabled when the system
> -		 * entered PSCI suspend. This is due to that if WoL is enabled
> -		 * we explicitly keep the clock from being turned off when
> -		 * suspending, but in PSCI sleep power is cut so the clock
> -		 * is disabled anyhow, the clock driver is not aware of this
> -		 * so the clock is not turned back on when resuming.
> -		 *
> -		 * TODO: once the renesas-cpg-mssr suspend/resume is working
> -		 *       this clock dance should be removed.
> -		 */
> -		clk_disable(priv->clk);
> -		clk_disable(priv->clk);
> -		clk_enable(priv->clk);
> -		clk_enable(priv->clk);
> -
> -		/* Set reset mode to rearm the WoL logic */
> +	/* If WoL is enabled set reset mode to rearm the WoL logic */
> +	if (priv->wol_enabled)
>  		ravb_write(ndev, CCC_OPC_RESET, CCC);
> -	}
>  
>  	/* All register have been reset to default values.
>  	 * Restore all registers which where setup at probe time and
> -- 
> 2.7.4
> 

-- 
Regards,
Niklas Söderlund

^ permalink raw reply

* Re: 76f43b4 fix for stable
From: Johannes Berg @ 2017-12-11 14:12 UTC (permalink / raw)
  To: Richard Schütz, netdev, linux-wireless; +Cc: Stable, gregkh
In-Reply-To: <c0a512a7-e615-bbab-5c88-4a85946fefb5@uni-koblenz.de>

On Mon, 2017-12-11 at 14:51 +0100, Richard Schütz wrote:
> Hello,
> 
> as per netdev-FAQ.txt I'm requesting the submission of commit 
> 57629915d568c522ac1422df7bba4bee5b5c7a7c ("mac80211: Fix addition of 
> mesh configuration element") to stable. Because of automatic selection 
> commit 76f43b4c0a9337af22827d78de4f2b8fd5328489 ("mac80211: Remove 
> invalid flag operations in mesh TSF synchronization") was introduced 
> into stable recently without this accompanying fix.

It seems that due to the Fixes: tag Greg should pick it up anyway, but
in any case I don't really deal with stable myself (in that sense, I
guess wireless isn't really part of netdev...)

johannes

^ permalink raw reply

* [PATCH net-next v5 0/2] net: thunderx: add support for PTP clock
From: Aleksey Makarov @ 2017-12-11 14:14 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Goutham, Sunil,
	Radoslaw Biernacki, Aleksey Makarov, Robert Richter, David Daney,
	Richard Cochran, Philippe Ombredanne

This series adds support for IEEE 1588 Precision Time Protocol
to Cavium ethernet driver.

The first patch adds support for the Precision Time Protocol Clocks and
Timestamping coprocessor (PTP) found on Cavium processors.
It registers a new PTP clock in the PTP core and provides functions
to use the counter in BGX, TNS, GTI, and NIC blocks.

The second patch introduces support for the PTP protocol to the
Cavium ThunderX ethernet driver.

v5:
- fix the file headers (add SPDX tags, remove advertisment) (Philippe Ombredanne)
- use "imply" instead of "select" (Richard Cochran)
- add some code in cavium_ptp_get() for the case when the PTP driver has not been
  registered with the PTP core

v4: https://lkml.kernel.org/r/20171208103442.19354-1-aleksey.makarov@cavium.com
- use IS_ENABLED. This fixes compilation of the ptp as a module (David Miller)
- select PTP_1588_CLOCK, not depend on it.  This fixes a build warning.
- change u64 to __be64.  This fixes the sparse warning
  "warning: cast to restricted __be64"
- make nicvf_config_hwtstamp() static. This fixes the sparse warning
  "warning: symbol 'nicvf_config_hwtstamp' was not declared. Should it be static?"

v3: https://lkml.kernel.org/r/20171206133100.26436-1-aleksey.makarov@cavium.com
- rebase to net-next

v2: https://lkml.kernel.org/r/20171117134909.8954-1-aleksey.makarov@cavium.com
- use readq()/writeq() in place of cavium_ptp_reg_read()/cavium_ptp_reg_write(),
  don't use readq_relaxed()/writeq_relaxed() (David Daney)

v1: https://lkml.kernel.org/r/20171107190704.15458-1-aleksey.makarov@cavium.com

Radoslaw Biernacki (1):
  net: add support for Cavium PTP coprocessor

Sunil Goutham (1):
  net: thunderx: add timestamping support

 drivers/net/ethernet/cavium/Kconfig                |  13 +
 drivers/net/ethernet/cavium/Makefile               |   1 +
 drivers/net/ethernet/cavium/common/Makefile        |   1 +
 drivers/net/ethernet/cavium/common/cavium_ptp.c    | 329 +++++++++++++++++++++
 drivers/net/ethernet/cavium/common/cavium_ptp.h    |  70 +++++
 drivers/net/ethernet/cavium/thunder/nic.h          |  15 +
 drivers/net/ethernet/cavium/thunder/nic_main.c     |  58 +++-
 drivers/net/ethernet/cavium/thunder/nic_reg.h      |   1 +
 .../net/ethernet/cavium/thunder/nicvf_ethtool.c    |  29 +-
 drivers/net/ethernet/cavium/thunder/nicvf_main.c   | 173 ++++++++++-
 drivers/net/ethernet/cavium/thunder/nicvf_queues.c |  26 ++
 drivers/net/ethernet/cavium/thunder/thunder_bgx.c  |  29 ++
 drivers/net/ethernet/cavium/thunder/thunder_bgx.h  |   4 +
 13 files changed, 743 insertions(+), 6 deletions(-)
 create mode 100644 drivers/net/ethernet/cavium/common/Makefile
 create mode 100644 drivers/net/ethernet/cavium/common/cavium_ptp.c
 create mode 100644 drivers/net/ethernet/cavium/common/cavium_ptp.h

-- 
2.15.1

^ permalink raw reply

* [PATCH net-next v5 1/2] net: add support for Cavium PTP coprocessor
From: Aleksey Makarov @ 2017-12-11 14:14 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Goutham, Sunil,
	Radoslaw Biernacki, Aleksey Makarov, Robert Richter, David Daney,
	Richard Cochran, Philippe Ombredanne
In-Reply-To: <20171211141435.2915-1-aleksey.makarov@cavium.com>

From: Radoslaw Biernacki <rad@semihalf.com>

This patch adds support for the Precision Time Protocol
Clocks and Timestamping hardware found on Cavium ThunderX
processors.

Signed-off-by: Radoslaw Biernacki <rad@semihalf.com>
Signed-off-by: Aleksey Makarov <aleksey.makarov@cavium.com>
---
 drivers/net/ethernet/cavium/Kconfig             |  12 +
 drivers/net/ethernet/cavium/Makefile            |   1 +
 drivers/net/ethernet/cavium/common/Makefile     |   1 +
 drivers/net/ethernet/cavium/common/cavium_ptp.c | 329 ++++++++++++++++++++++++
 drivers/net/ethernet/cavium/common/cavium_ptp.h |  70 +++++
 5 files changed, 413 insertions(+)
 create mode 100644 drivers/net/ethernet/cavium/common/Makefile
 create mode 100644 drivers/net/ethernet/cavium/common/cavium_ptp.c
 create mode 100644 drivers/net/ethernet/cavium/common/cavium_ptp.h

diff --git a/drivers/net/ethernet/cavium/Kconfig b/drivers/net/ethernet/cavium/Kconfig
index 63be75eb34d2..96586c0b4490 100644
--- a/drivers/net/ethernet/cavium/Kconfig
+++ b/drivers/net/ethernet/cavium/Kconfig
@@ -50,6 +50,18 @@ config	THUNDER_NIC_RGX
 	  This driver supports configuring XCV block of RGX interface
 	  present on CN81XX chip.
 
+config CAVIUM_PTP
+	tristate "Cavium PTP coprocessor as PTP clock"
+	depends on 64BIT
+	imply PTP_1588_CLOCK
+	default y
+	---help---
+	  This driver adds support for the Precision Time Protocol Clocks and
+	  Timestamping coprocessor (PTP) found on Cavium processors.
+	  PTP provides timestamping mechanism that is suitable for use in IEEE 1588
+	  Precision Time Protocol or other purposes.  Timestamps can be used in
+	  BGX, TNS, GTI, and NIC blocks.
+
 config LIQUIDIO
 	tristate "Cavium LiquidIO support"
 	depends on 64BIT
diff --git a/drivers/net/ethernet/cavium/Makefile b/drivers/net/ethernet/cavium/Makefile
index 872da9f7c31a..946bba84e81d 100644
--- a/drivers/net/ethernet/cavium/Makefile
+++ b/drivers/net/ethernet/cavium/Makefile
@@ -1,6 +1,7 @@
 #
 # Makefile for the Cavium ethernet device drivers.
 #
+obj-$(CONFIG_NET_VENDOR_CAVIUM) += common/
 obj-$(CONFIG_NET_VENDOR_CAVIUM) += thunder/
 obj-$(CONFIG_NET_VENDOR_CAVIUM) += liquidio/
 obj-$(CONFIG_NET_VENDOR_CAVIUM) += octeon/
diff --git a/drivers/net/ethernet/cavium/common/Makefile b/drivers/net/ethernet/cavium/common/Makefile
new file mode 100644
index 000000000000..dd8561b8060b
--- /dev/null
+++ b/drivers/net/ethernet/cavium/common/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CAVIUM_PTP) += cavium_ptp.o
diff --git a/drivers/net/ethernet/cavium/common/cavium_ptp.c b/drivers/net/ethernet/cavium/common/cavium_ptp.c
new file mode 100644
index 000000000000..8b48863bfdec
--- /dev/null
+++ b/drivers/net/ethernet/cavium/common/cavium_ptp.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0
+/* cavium_ptp.c - PTP 1588 clock on Cavium hardware
+ * Copyright (c) 2003-2015, 2017 Cavium, Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/timecounter.h>
+#include <linux/pci.h>
+
+#include "cavium_ptp.h"
+
+#define DRV_NAME	"Cavium PTP Driver"
+
+#define PCI_DEVICE_ID_CAVIUM_PTP	0xA00C
+#define PCI_DEVICE_ID_CAVIUM_RST	0xA00E
+
+#define PCI_PTP_BAR_NO	0
+#define PCI_RST_BAR_NO	0
+
+#define PTP_CLOCK_CFG		0xF00ULL
+#define  PTP_CLOCK_CFG_PTP_EN	BIT(0)
+#define PTP_CLOCK_LO		0xF08ULL
+#define PTP_CLOCK_HI		0xF10ULL
+#define PTP_CLOCK_COMP		0xF18ULL
+
+#define RST_BOOT	0x1600ULL
+#define CLOCK_BASE_RATE	50000000ULL
+
+static u64 ptp_cavium_clock_get(void)
+{
+	struct pci_dev *pdev;
+	void __iomem *base;
+	u64 ret = CLOCK_BASE_RATE * 16;
+
+	pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
+			      PCI_DEVICE_ID_CAVIUM_RST, NULL);
+	if (!pdev)
+		goto error;
+
+	base = pci_ioremap_bar(pdev, PCI_RST_BAR_NO);
+	if (!base)
+		goto error_put_pdev;
+
+	ret = CLOCK_BASE_RATE * ((readq(base + RST_BOOT) >> 33) & 0x3f);
+
+	iounmap(base);
+
+error_put_pdev:
+	pci_dev_put(pdev);
+
+error:
+	return ret;
+}
+
+struct cavium_ptp *cavium_ptp_get(void)
+{
+	struct cavium_ptp *ptp;
+	struct pci_dev *pdev;
+
+	pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
+			      PCI_DEVICE_ID_CAVIUM_PTP, NULL);
+	if (!pdev)
+		return ERR_PTR(-ENODEV);
+
+	ptp = pci_get_drvdata(pdev);
+	if (!ptp) {
+		pci_dev_put(pdev);
+		ptp = ERR_PTR(-EPROBE_DEFER);
+	} else if (!ptp->ptp_clock) {
+		pci_dev_put(pdev);
+		ptp = ERR_PTR(-ENODEV);
+	}
+
+	return ptp;
+}
+EXPORT_SYMBOL(cavium_ptp_get);
+
+void cavium_ptp_put(struct cavium_ptp *ptp)
+{
+	pci_dev_put(ptp->pdev);
+}
+EXPORT_SYMBOL(cavium_ptp_put);
+
+/**
+ * cavium_ptp_adjfreq() - Adjust ptp frequency
+ * @ptp: PTP clock info
+ * @ppb: how much to adjust by, in parts-per-billion
+ */
+static int cavium_ptp_adjfreq(struct ptp_clock_info *ptp_info, s32 ppb)
+{
+	struct cavium_ptp *clock =
+		container_of(ptp_info, struct cavium_ptp, ptp_info);
+	unsigned long flags;
+	u64 comp;
+	u64 adj;
+	bool neg_adj = false;
+
+	if (ppb < 0) {
+		neg_adj = true;
+		ppb = -ppb;
+	}
+
+	/* The hardware adds the clock compensation value to the PTP clock
+	 * on every coprocessor clock cycle. Typical convention is that it
+	 * represent number of nanosecond betwen each cycle. In this
+	 * convention compensation value is in 64 bit fixed-point
+	 * representation where upper 32 bits are number of nanoseconds
+	 * and lower is fractions of nanosecond.
+	 * The ppb represent the ratio in "parts per bilion" by which the
+	 * compensation value should be corrected.
+	 * To calculate new compenstation value we use 64bit fixed point
+	 * arithmetic on following formula comp = tbase + tbase * ppb / 1G
+	 * where tbase is the basic compensation value calculated initialy
+	 * in cavium_ptp_init() -> tbase = 1/Hz. Then we use endian
+	 * independent structure definition to write data to PTP register.
+	 */
+	comp = ((u64)1000000000ull << 32) / clock->clock_rate;
+	adj = comp * ppb;
+	adj = div_u64(adj, 1000000000ull);
+	comp = neg_adj ? comp - adj : comp + adj;
+
+	spin_lock_irqsave(&clock->spin_lock, flags);
+	writeq(comp, clock->reg_base + PTP_CLOCK_COMP);
+	spin_unlock_irqrestore(&clock->spin_lock, flags);
+
+	return 0;
+}
+
+/**
+ * cavium_ptp_adjtime() - Adjust ptp time
+ * @ptp:   PTP clock info
+ * @delta: how much to adjust by, in nanosecs
+ */
+static int cavium_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta)
+{
+	struct cavium_ptp *clock =
+		container_of(ptp_info, struct cavium_ptp, ptp_info);
+	unsigned long flags;
+
+	spin_lock_irqsave(&clock->spin_lock, flags);
+	timecounter_adjtime(&clock->time_counter, delta);
+	spin_unlock_irqrestore(&clock->spin_lock, flags);
+
+	/* Sync, for network driver to get latest value */
+	smp_mb();
+
+	return 0;
+}
+
+/**
+ * cavium_ptp_gettime() - Get hardware clock time with adjustment
+ * @ptp: PTP clock info
+ * @ts:  timespec
+ */
+static int cavium_ptp_gettime(struct ptp_clock_info *ptp_info,
+			      struct timespec64 *ts)
+{
+	struct cavium_ptp *clock =
+		container_of(ptp_info, struct cavium_ptp, ptp_info);
+	unsigned long flags;
+	u64 nsec;
+
+	spin_lock_irqsave(&clock->spin_lock, flags);
+	nsec = timecounter_read(&clock->time_counter);
+	spin_unlock_irqrestore(&clock->spin_lock, flags);
+
+	*ts = ns_to_timespec64(nsec);
+
+	return 0;
+}
+
+/**
+ * cavium_ptp_settime() - Set hardware clock time. Reset adjustment
+ * @ptp: PTP clock info
+ * @ts:  timespec
+ */
+static int cavium_ptp_settime(struct ptp_clock_info *ptp_info,
+			      const struct timespec64 *ts)
+{
+	struct cavium_ptp *clock =
+		container_of(ptp_info, struct cavium_ptp, ptp_info);
+	unsigned long flags;
+	u64 nsec;
+
+	nsec = timespec64_to_ns(ts);
+
+	spin_lock_irqsave(&clock->spin_lock, flags);
+	timecounter_init(&clock->time_counter, &clock->cycle_counter, nsec);
+	spin_unlock_irqrestore(&clock->spin_lock, flags);
+
+	return 0;
+}
+
+/**
+ * cavium_ptp_enable() - Check if PTP is enabled
+ * @ptp: PTP clock info
+ * @rq:  request
+ * @on:  is it on
+ */
+static int cavium_ptp_enable(struct ptp_clock_info *ptp_info,
+			     struct ptp_clock_request *rq, int on)
+{
+	return -EOPNOTSUPP;
+}
+
+static u64 cavium_ptp_cc_read(const struct cyclecounter *cc)
+{
+	struct cavium_ptp *clock =
+		container_of(cc, struct cavium_ptp, cycle_counter);
+
+	return readq(clock->reg_base + PTP_CLOCK_HI);
+}
+
+static int cavium_ptp_probe(struct pci_dev *pdev,
+			    const struct pci_device_id *ent)
+{
+	struct device *dev = &pdev->dev;
+	struct cavium_ptp *clock;
+	struct cyclecounter *cc;
+	u64 clock_cfg;
+	u64 clock_comp;
+	int err;
+
+	clock = devm_kzalloc(dev, sizeof(*clock), GFP_KERNEL);
+	if (!clock)
+		return -ENOMEM;
+
+	clock->pdev = pdev;
+
+	err = pcim_enable_device(pdev);
+	if (err)
+		return err;
+
+	err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev));
+	if (err)
+		return err;
+
+	clock->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO];
+
+	spin_lock_init(&clock->spin_lock);
+
+	cc = &clock->cycle_counter;
+	cc->read = cavium_ptp_cc_read;
+	cc->mask = CYCLECOUNTER_MASK(64);
+	cc->mult = 1;
+	cc->shift = 0;
+
+	timecounter_init(&clock->time_counter, &clock->cycle_counter,
+			 ktime_to_ns(ktime_get_real()));
+
+	clock->clock_rate = ptp_cavium_clock_get();
+
+	clock->ptp_info = (struct ptp_clock_info) {
+		.owner		= THIS_MODULE,
+		.name		= "ThunderX PTP",
+		.max_adj	= 1000000000ull,
+		.n_ext_ts	= 0,
+		.n_pins		= 0,
+		.pps		= 0,
+		.adjfreq	= cavium_ptp_adjfreq,
+		.adjtime	= cavium_ptp_adjtime,
+		.gettime64	= cavium_ptp_gettime,
+		.settime64	= cavium_ptp_settime,
+		.enable		= cavium_ptp_enable,
+	};
+
+	clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
+	clock_cfg |= PTP_CLOCK_CFG_PTP_EN;
+	writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
+
+	clock_comp = ((u64)1000000000ull << 32) / clock->clock_rate;
+	writeq(clock_comp, clock->reg_base + PTP_CLOCK_COMP);
+
+	clock->ptp_clock = ptp_clock_register(&clock->ptp_info, dev);
+	if (IS_ERR(clock->ptp_clock)) {
+		clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
+		clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
+		writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
+		return PTR_ERR(clock->ptp_clock);
+	}
+
+	pci_set_drvdata(pdev, clock);
+	return 0;
+}
+
+static void cavium_ptp_remove(struct pci_dev *pdev)
+{
+	struct cavium_ptp *clock = pci_get_drvdata(pdev);
+	u64 clock_cfg;
+
+	pci_set_drvdata(pdev, NULL);
+
+	ptp_clock_unregister(clock->ptp_clock);
+
+	clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
+	clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
+	writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
+}
+
+static const struct pci_device_id cavium_ptp_id_table[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_PTP) },
+	{ 0, }
+};
+
+static struct pci_driver cavium_ptp_driver = {
+	.name = DRV_NAME,
+	.id_table = cavium_ptp_id_table,
+	.probe = cavium_ptp_probe,
+	.remove = cavium_ptp_remove,
+};
+
+static int __init cavium_ptp_init_module(void)
+{
+	return pci_register_driver(&cavium_ptp_driver);
+}
+
+static void __exit cavium_ptp_cleanup_module(void)
+{
+	pci_unregister_driver(&cavium_ptp_driver);
+}
+
+module_init(cavium_ptp_init_module);
+module_exit(cavium_ptp_cleanup_module);
+
+MODULE_DESCRIPTION(DRV_NAME);
+MODULE_AUTHOR("Cavium Networks <support@cavium.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(pci, cavium_ptp_id_table);
diff --git a/drivers/net/ethernet/cavium/common/cavium_ptp.h b/drivers/net/ethernet/cavium/common/cavium_ptp.h
new file mode 100644
index 000000000000..be2bafc7beeb
--- /dev/null
+++ b/drivers/net/ethernet/cavium/common/cavium_ptp.h
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+/* cavium_ptp.h - PTP 1588 clock on Cavium hardware
+ * Copyright (c) 2003-2015, 2017 Cavium, Inc.
+ */
+
+#ifndef CAVIUM_PTP_H
+#define CAVIUM_PTP_H
+
+#include <linux/ptp_clock_kernel.h>
+#include <linux/timecounter.h>
+
+struct cavium_ptp {
+	struct pci_dev *pdev;
+
+	/* Serialize access to cycle_counter, time_counter and hw_registers */
+	spinlock_t spin_lock;
+	struct cyclecounter cycle_counter;
+	struct timecounter time_counter;
+	void __iomem *reg_base;
+
+	u32 clock_rate;
+
+	struct ptp_clock_info ptp_info;
+	struct ptp_clock *ptp_clock;
+};
+
+#if IS_ENABLED(CONFIG_CAVIUM_PTP)
+
+struct cavium_ptp *cavium_ptp_get(void);
+void cavium_ptp_put(struct cavium_ptp *ptp);
+
+static inline u64 cavium_ptp_tstamp2time(struct cavium_ptp *ptp, u64 tstamp)
+{
+	unsigned long flags;
+	u64 ret;
+
+	spin_lock_irqsave(&ptp->spin_lock, flags);
+	ret = timecounter_cyc2time(&ptp->time_counter, tstamp);
+	spin_unlock_irqrestore(&ptp->spin_lock, flags);
+
+	return ret;
+}
+
+static inline int cavium_ptp_clock_index(struct cavium_ptp *clock)
+{
+	return ptp_clock_index(clock->ptp_clock);
+}
+
+#else
+
+static inline struct cavium_ptp *cavium_ptp_get(void)
+{
+	return ERR_PTR(-ENODEV);
+}
+
+static inline void cavium_ptp_put(struct cavium_ptp *ptp) {}
+
+static inline u64 cavium_ptp_tstamp2time(struct cavium_ptp *ptp, u64 tstamp)
+{
+	return 0;
+}
+
+static inline int cavium_ptp_clock_index(struct cavium_ptp *clock)
+{
+	return -1;
+}
+
+#endif
+
+#endif
-- 
2.15.1

^ permalink raw reply related

* [PATCH net-next v5 2/2] net: thunderx: add timestamping support
From: Aleksey Makarov @ 2017-12-11 14:14 UTC (permalink / raw)
  To: netdev
  Cc: linux-arm-kernel, linux-kernel, Goutham, Sunil,
	Radoslaw Biernacki, Aleksey Makarov, Robert Richter, David Daney,
	Richard Cochran, Philippe Ombredanne, Sunil Goutham
In-Reply-To: <20171211141435.2915-1-aleksey.makarov@cavium.com>

From: Sunil Goutham <sgoutham@cavium.com>

This adds timestamping support for both receive and transmit
paths. On the receive side no filters are supported i.e either
all pkts will get a timestamp appended infront of the packet or none.
On the transmit side HW doesn't support timestamp insertion but
only generates a separate CQE with transmitted packet's timestamp.
Also HW supports only one packet at a time for timestamping on the
transmit side.

Signed-off-by: Sunil Goutham <sgoutham@cavium.com>
Signed-off-by: Aleksey Makarov <aleksey.makarov@cavium.com>
---
 drivers/net/ethernet/cavium/Kconfig                |   1 +
 drivers/net/ethernet/cavium/thunder/nic.h          |  15 ++
 drivers/net/ethernet/cavium/thunder/nic_main.c     |  58 ++++++-
 drivers/net/ethernet/cavium/thunder/nic_reg.h      |   1 +
 .../net/ethernet/cavium/thunder/nicvf_ethtool.c    |  29 +++-
 drivers/net/ethernet/cavium/thunder/nicvf_main.c   | 173 ++++++++++++++++++++-
 drivers/net/ethernet/cavium/thunder/nicvf_queues.c |  26 ++++
 drivers/net/ethernet/cavium/thunder/thunder_bgx.c  |  29 ++++
 drivers/net/ethernet/cavium/thunder/thunder_bgx.h  |   4 +
 9 files changed, 330 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/cavium/Kconfig b/drivers/net/ethernet/cavium/Kconfig
index 96586c0b4490..043e3c11c42b 100644
--- a/drivers/net/ethernet/cavium/Kconfig
+++ b/drivers/net/ethernet/cavium/Kconfig
@@ -27,6 +27,7 @@ config THUNDER_NIC_PF
 
 config THUNDER_NIC_VF
 	tristate "Thunder Virtual function driver"
+	imply CAVIUM_PTP
 	depends on 64BIT
 	---help---
 	  This driver supports Thunder's NIC virtual function
diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h
index 4a02e618e318..204b234beb9d 100644
--- a/drivers/net/ethernet/cavium/thunder/nic.h
+++ b/drivers/net/ethernet/cavium/thunder/nic.h
@@ -263,6 +263,8 @@ struct nicvf_drv_stats {
 	struct u64_stats_sync   syncp;
 };
 
+struct cavium_ptp;
+
 struct nicvf {
 	struct nicvf		*pnicvf;
 	struct net_device	*netdev;
@@ -312,6 +314,12 @@ struct nicvf {
 	struct tasklet_struct	qs_err_task;
 	struct work_struct	reset_task;
 
+	/* PTP timestamp */
+	struct cavium_ptp	*ptp_clock;
+	bool			hw_rx_tstamp;
+	struct sk_buff		*ptp_skb;
+	atomic_t		tx_ptp_skbs;
+
 	/* Interrupt coalescing settings */
 	u32			cq_coalesce_usecs;
 	u32			msg_enable;
@@ -371,6 +379,7 @@ struct nicvf {
 #define	NIC_MBOX_MSG_LOOPBACK		0x16	/* Set interface in loopback */
 #define	NIC_MBOX_MSG_RESET_STAT_COUNTER 0x17	/* Reset statistics counters */
 #define	NIC_MBOX_MSG_PFC		0x18	/* Pause frame control */
+#define	NIC_MBOX_MSG_PTP_CFG		0x19	/* HW packet timestamp */
 #define	NIC_MBOX_MSG_CFG_DONE		0xF0	/* VF configuration done */
 #define	NIC_MBOX_MSG_SHUTDOWN		0xF1	/* VF is being shutdown */
 
@@ -521,6 +530,11 @@ struct pfc {
 	u8    fc_tx;
 };
 
+struct set_ptp {
+	u8    msg;
+	bool  enable;
+};
+
 /* 128 bit shared memory between PF and each VF */
 union nic_mbx {
 	struct { u8 msg; }	msg;
@@ -540,6 +554,7 @@ union nic_mbx {
 	struct set_loopback	lbk;
 	struct reset_stat_cfg	reset_stat;
 	struct pfc		pfc;
+	struct set_ptp		ptp;
 };
 
 #define NIC_NODE_ID_MASK	0x03
diff --git a/drivers/net/ethernet/cavium/thunder/nic_main.c b/drivers/net/ethernet/cavium/thunder/nic_main.c
index 8f1dd55b3e08..4c1c5414a162 100644
--- a/drivers/net/ethernet/cavium/thunder/nic_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nic_main.c
@@ -426,13 +426,22 @@ static void nic_init_hw(struct nicpf *nic)
 	/* Enable backpressure */
 	nic_reg_write(nic, NIC_PF_BP_CFG, (1ULL << 6) | 0x03);
 
-	/* TNS and TNS bypass modes are present only on 88xx */
+	/* TNS and TNS bypass modes are present only on 88xx
+	 * Also offset of this CSR has changed in 81xx and 83xx.
+	 */
 	if (nic->pdev->subsystem_device == PCI_SUBSYS_DEVID_88XX_NIC_PF) {
 		/* Disable TNS mode on both interfaces */
 		nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG,
-			      (NIC_TNS_BYPASS_MODE << 7) | BGX0_BLOCK);
+			      (NIC_TNS_BYPASS_MODE << 7) |
+			      BGX0_BLOCK | (1ULL << 16));
 		nic_reg_write(nic, NIC_PF_INTF_0_1_SEND_CFG | (1 << 8),
-			      (NIC_TNS_BYPASS_MODE << 7) | BGX1_BLOCK);
+			      (NIC_TNS_BYPASS_MODE << 7) |
+			      BGX1_BLOCK | (1ULL << 16));
+	} else {
+		/* Configure timestamp generation timeout to 10us */
+		for (i = 0; i < nic->hw->bgx_cnt; i++)
+			nic_reg_write(nic, NIC_PF_INTFX_SEND_CFG | (i << 3),
+				      (1ULL << 16));
 	}
 
 	nic_reg_write(nic, NIC_PF_INTF_0_1_BP_CFG,
@@ -880,6 +889,46 @@ static void nic_pause_frame(struct nicpf *nic, int vf, struct pfc *cfg)
 	}
 }
 
+/* Enable or disable HW timestamping by BGX for pkts received on a LMAC */
+static void nic_config_timestamp(struct nicpf *nic, int vf, struct set_ptp *ptp)
+{
+	struct pkind_cfg *pkind;
+	u8 lmac, bgx_idx;
+	u64 pkind_val, pkind_idx;
+
+	if (vf >= nic->num_vf_en)
+		return;
+
+	bgx_idx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+	lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+
+	pkind_idx = lmac + bgx_idx * MAX_LMAC_PER_BGX;
+	pkind_val = nic_reg_read(nic, NIC_PF_PKIND_0_15_CFG | (pkind_idx << 3));
+	pkind = (struct pkind_cfg *)&pkind_val;
+
+	if (ptp->enable && !pkind->hdr_sl) {
+		/* Skiplen to exclude 8byte timestamp while parsing pkt
+		 * If not configured, will result in L2 errors.
+		 */
+		pkind->hdr_sl = 4;
+		/* Adjust max packet length allowed */
+		pkind->maxlen += (pkind->hdr_sl * 2);
+		bgx_config_timestamping(nic->node, bgx_idx, lmac, true);
+		nic_reg_write(nic,
+			      NIC_PF_RX_ETYPE_0_7 | (1 << 3),
+			      (ETYPE_ALG_ENDPARSE << 16) | ETH_P_1588);
+	} else if (!ptp->enable && pkind->hdr_sl) {
+		pkind->maxlen -= (pkind->hdr_sl * 2);
+		pkind->hdr_sl = 0;
+		bgx_config_timestamping(nic->node, bgx_idx, lmac, false);
+		nic_reg_write(nic,
+			      NIC_PF_RX_ETYPE_0_7 | (1 << 3),
+			      (1ULL << 16) | ETH_P_8021Q); /* reset value */
+	}
+
+	nic_reg_write(nic, NIC_PF_PKIND_0_15_CFG | (pkind_idx << 3), pkind_val);
+}
+
 /* Interrupt handler to handle mailbox messages from VFs */
 static void nic_handle_mbx_intr(struct nicpf *nic, int vf)
 {
@@ -1022,6 +1071,9 @@ static void nic_handle_mbx_intr(struct nicpf *nic, int vf)
 	case NIC_MBOX_MSG_PFC:
 		nic_pause_frame(nic, vf, &mbx.pfc);
 		goto unlock;
+	case NIC_MBOX_MSG_PTP_CFG:
+		nic_config_timestamp(nic, vf, &mbx.ptp);
+		break;
 	default:
 		dev_err(&nic->pdev->dev,
 			"Invalid msg from VF%d, msg 0x%x\n", vf, mbx.msg.msg);
diff --git a/drivers/net/ethernet/cavium/thunder/nic_reg.h b/drivers/net/ethernet/cavium/thunder/nic_reg.h
index 80d46337cf29..a16c48a1ebb2 100644
--- a/drivers/net/ethernet/cavium/thunder/nic_reg.h
+++ b/drivers/net/ethernet/cavium/thunder/nic_reg.h
@@ -99,6 +99,7 @@
 #define   NIC_PF_ECC3_DBE_INT_W1S		(0x2708)
 #define   NIC_PF_ECC3_DBE_ENA_W1C		(0x2710)
 #define   NIC_PF_ECC3_DBE_ENA_W1S		(0x2718)
+#define   NIC_PF_INTFX_SEND_CFG			(0x4000)
 #define   NIC_PF_MCAM_0_191_ENA			(0x100000)
 #define   NIC_PF_MCAM_0_191_M_0_5_DATA		(0x110000)
 #define   NIC_PF_MCAM_CTRL			(0x120000)
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
index b9ece9cbf98b..ed9f10bdf41e 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
@@ -9,12 +9,14 @@
 /* ETHTOOL Support for VNIC_VF Device*/
 
 #include <linux/pci.h>
+#include <linux/net_tstamp.h>
 
 #include "nic_reg.h"
 #include "nic.h"
 #include "nicvf_queues.h"
 #include "q_struct.h"
 #include "thunder_bgx.h"
+#include "../common/cavium_ptp.h"
 
 #define DRV_NAME	"thunder-nicvf"
 #define DRV_VERSION     "1.0"
@@ -824,6 +826,31 @@ static int nicvf_set_pauseparam(struct net_device *dev,
 	return 0;
 }
 
+static int nicvf_get_ts_info(struct net_device *netdev,
+			     struct ethtool_ts_info *info)
+{
+	struct nicvf *nic = netdev_priv(netdev);
+
+	if (!nic->ptp_clock)
+		return ethtool_op_get_ts_info(netdev, info);
+
+	info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
+				SOF_TIMESTAMPING_RX_SOFTWARE |
+				SOF_TIMESTAMPING_SOFTWARE |
+				SOF_TIMESTAMPING_TX_HARDWARE |
+				SOF_TIMESTAMPING_RX_HARDWARE |
+				SOF_TIMESTAMPING_RAW_HARDWARE;
+
+	info->phc_index = cavium_ptp_clock_index(nic->ptp_clock);
+
+	info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON);
+
+	info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
+			   (1 << HWTSTAMP_FILTER_ALL);
+
+	return 0;
+}
+
 static const struct ethtool_ops nicvf_ethtool_ops = {
 	.get_link		= nicvf_get_link,
 	.get_drvinfo		= nicvf_get_drvinfo,
@@ -847,7 +874,7 @@ static const struct ethtool_ops nicvf_ethtool_ops = {
 	.set_channels		= nicvf_set_channels,
 	.get_pauseparam         = nicvf_get_pauseparam,
 	.set_pauseparam         = nicvf_set_pauseparam,
-	.get_ts_info		= ethtool_op_get_ts_info,
+	.get_ts_info		= nicvf_get_ts_info,
 	.get_link_ksettings	= nicvf_get_link_ksettings,
 };
 
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index 52b3a6044f85..8e3338f31e54 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -20,11 +20,13 @@
 #include <linux/bpf.h>
 #include <linux/bpf_trace.h>
 #include <linux/filter.h>
+#include <linux/net_tstamp.h>
 
 #include "nic_reg.h"
 #include "nic.h"
 #include "nicvf_queues.h"
 #include "thunder_bgx.h"
+#include "../common/cavium_ptp.h"
 
 #define DRV_NAME	"thunder-nicvf"
 #define DRV_VERSION	"1.0"
@@ -601,6 +603,44 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog,
 	return false;
 }
 
+static void nicvf_snd_ptp_handler(struct net_device *netdev,
+				  struct cqe_send_t *cqe_tx)
+{
+	struct nicvf *nic = netdev_priv(netdev);
+	struct skb_shared_hwtstamps ts;
+	u64 ns;
+
+	nic = nic->pnicvf;
+
+	/* Sync for 'ptp_skb' */
+	smp_rmb();
+
+	/* New timestamp request can be queued now */
+	atomic_set(&nic->tx_ptp_skbs, 0);
+
+	/* Check for timestamp requested skb */
+	if (!nic->ptp_skb)
+		return;
+
+	/* Check if timestamping is timedout, which is set to 10us */
+	if (cqe_tx->send_status == CQ_TX_ERROP_TSTMP_TIMEOUT ||
+	    cqe_tx->send_status == CQ_TX_ERROP_TSTMP_CONFLICT)
+		goto no_tstamp;
+
+	/* Get the timestamp */
+	memset(&ts, 0, sizeof(ts));
+	ns = cavium_ptp_tstamp2time(nic->ptp_clock, cqe_tx->ptp_timestamp);
+	ts.hwtstamp = ns_to_ktime(ns);
+	skb_tstamp_tx(nic->ptp_skb, &ts);
+
+no_tstamp:
+	/* Free the original skb */
+	dev_kfree_skb_any(nic->ptp_skb);
+	nic->ptp_skb = NULL;
+	/* Sync 'ptp_skb' */
+	smp_wmb();
+}
+
 static void nicvf_snd_pkt_handler(struct net_device *netdev,
 				  struct cqe_send_t *cqe_tx,
 				  int budget, int *subdesc_cnt,
@@ -657,7 +697,12 @@ static void nicvf_snd_pkt_handler(struct net_device *netdev,
 		prefetch(skb);
 		(*tx_pkts)++;
 		*tx_bytes += skb->len;
-		napi_consume_skb(skb, budget);
+		/* If timestamp is requested for this skb, don't free it */
+		if (skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS &&
+		    !nic->pnicvf->ptp_skb)
+			nic->pnicvf->ptp_skb = skb;
+		else
+			napi_consume_skb(skb, budget);
 		sq->skbuff[cqe_tx->sqe_ptr] = (u64)NULL;
 	} else {
 		/* In case of SW TSO on 88xx, only last segment will have
@@ -696,6 +741,21 @@ static inline void nicvf_set_rxhash(struct net_device *netdev,
 	skb_set_hash(skb, hash, hash_type);
 }
 
+static inline void nicvf_set_rxtstamp(struct nicvf *nic, struct sk_buff *skb)
+{
+	u64 ns;
+
+	if (!nic->ptp_clock || !nic->hw_rx_tstamp)
+		return;
+
+	/* The first 8 bytes is the timestamp */
+	ns = cavium_ptp_tstamp2time(nic->ptp_clock,
+				    be64_to_cpu(*(__be64 *)skb->data));
+	skb_hwtstamps(skb)->hwtstamp = ns_to_ktime(ns);
+
+	__skb_pull(skb, 8);
+}
+
 static void nicvf_rcv_pkt_handler(struct net_device *netdev,
 				  struct napi_struct *napi,
 				  struct cqe_rx_t *cqe_rx, struct snd_queue *sq)
@@ -746,6 +806,7 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev,
 		return;
 	}
 
+	nicvf_set_rxtstamp(nic, skb);
 	nicvf_set_rxhash(netdev, cqe_rx, skb);
 
 	skb_record_rx_queue(skb, rq_idx);
@@ -820,10 +881,12 @@ static int nicvf_cq_intr_handler(struct net_device *netdev, u8 cq_idx,
 					      &tx_pkts, &tx_bytes);
 			tx_done++;
 		break;
+		case CQE_TYPE_SEND_PTP:
+			nicvf_snd_ptp_handler(netdev, (void *)cq_desc);
+		break;
 		case CQE_TYPE_INVALID:
 		case CQE_TYPE_RX_SPLIT:
 		case CQE_TYPE_RX_TCP:
-		case CQE_TYPE_SEND_PTP:
 			/* Ignore for now */
 		break;
 		}
@@ -1319,12 +1382,28 @@ int nicvf_stop(struct net_device *netdev)
 
 	nicvf_free_cq_poll(nic);
 
+	/* Free any pending SKB saved to receive timestamp */
+	if (nic->ptp_skb) {
+		dev_kfree_skb_any(nic->ptp_skb);
+		nic->ptp_skb = NULL;
+	}
+
 	/* Clear multiqset info */
 	nic->pnicvf = nic;
 
 	return 0;
 }
 
+static int nicvf_config_hw_rx_tstamp(struct nicvf *nic, bool enable)
+{
+	union nic_mbx mbx = {};
+
+	mbx.ptp.msg = NIC_MBOX_MSG_PTP_CFG;
+	mbx.ptp.enable = enable;
+
+	return nicvf_send_msg_to_pf(nic, &mbx);
+}
+
 static int nicvf_update_hw_max_frs(struct nicvf *nic, int mtu)
 {
 	union nic_mbx mbx = {};
@@ -1394,6 +1473,12 @@ int nicvf_open(struct net_device *netdev)
 	if (nic->sqs_mode)
 		nicvf_get_primary_vf_struct(nic);
 
+	/* Configure PTP timestamp */
+	if (nic->ptp_clock)
+		nicvf_config_hw_rx_tstamp(nic, nic->hw_rx_tstamp);
+	atomic_set(&nic->tx_ptp_skbs, 0);
+	nic->ptp_skb = NULL;
+
 	/* Configure receive side scaling and MTU */
 	if (!nic->sqs_mode) {
 		nicvf_rss_init(nic);
@@ -1820,6 +1905,77 @@ static void nicvf_xdp_flush(struct net_device *dev)
 	return;
 }
 
+static int nicvf_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
+{
+	struct hwtstamp_config config;
+	struct nicvf *nic = netdev_priv(netdev);
+
+	if (!nic->ptp_clock)
+		return -ENODEV;
+
+	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+		return -EFAULT;
+
+	/* reserved for future extensions */
+	if (config.flags)
+		return -EINVAL;
+
+	switch (config.tx_type) {
+	case HWTSTAMP_TX_OFF:
+	case HWTSTAMP_TX_ON:
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	switch (config.rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		nic->hw_rx_tstamp = false;
+		break;
+	case HWTSTAMP_FILTER_ALL:
+	case HWTSTAMP_FILTER_SOME:
+	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+	case HWTSTAMP_FILTER_PTP_V2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+		nic->hw_rx_tstamp = true;
+		config.rx_filter = HWTSTAMP_FILTER_ALL;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	if (netif_running(netdev)) {
+		if (nic->hw_rx_tstamp)
+			nicvf_config_hw_rx_tstamp(nic, true);
+		else
+			nicvf_config_hw_rx_tstamp(nic, false);
+	}
+
+	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int nicvf_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
+{
+	switch (cmd) {
+	case SIOCSHWTSTAMP:
+		return nicvf_config_hwtstamp(netdev, req);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
 static const struct net_device_ops nicvf_netdev_ops = {
 	.ndo_open		= nicvf_open,
 	.ndo_stop		= nicvf_stop,
@@ -1833,6 +1989,7 @@ static const struct net_device_ops nicvf_netdev_ops = {
 	.ndo_bpf		= nicvf_xdp,
 	.ndo_xdp_xmit		= nicvf_xdp_xmit,
 	.ndo_xdp_flush          = nicvf_xdp_flush,
+	.ndo_do_ioctl           = nicvf_ioctl,
 };
 
 static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -1842,6 +1999,16 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	struct nicvf *nic;
 	int    err, qcount;
 	u16    sdevid;
+	struct cavium_ptp *ptp_clock;
+
+	ptp_clock = cavium_ptp_get();
+	if (IS_ERR(ptp_clock)) {
+		if (PTR_ERR(ptp_clock) == -ENODEV)
+			/* In virtualized environment we proceed without ptp */
+			ptp_clock = NULL;
+		else
+			return PTR_ERR(ptp_clock);
+	}
 
 	err = pci_enable_device(pdev);
 	if (err) {
@@ -1896,6 +2063,7 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	 */
 	if (!nic->t88)
 		nic->max_queues *= 2;
+	nic->ptp_clock = ptp_clock;
 
 	/* MAP VF's configuration registers */
 	nic->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
@@ -2009,6 +2177,7 @@ static void nicvf_remove(struct pci_dev *pdev)
 	pci_set_drvdata(pdev, NULL);
 	if (nic->drv_stats)
 		free_percpu(nic->drv_stats);
+	cavium_ptp_put(nic->ptp_clock);
 	free_netdev(netdev);
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
index f38ea349aa00..853fbeb84bd2 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
@@ -978,6 +978,9 @@ void nicvf_qset_config(struct nicvf *nic, bool enable)
 		qs_cfg->be = 1;
 #endif
 		qs_cfg->vnic = qs->vnic_id;
+		/* Enable Tx timestamping capability */
+		if (nic->ptp_clock)
+			qs_cfg->send_tstmp_ena = 1;
 	}
 	nicvf_send_msg_to_pf(nic, &mbx);
 }
@@ -1385,6 +1388,29 @@ nicvf_sq_add_hdr_subdesc(struct nicvf *nic, struct snd_queue *sq, int qentry,
 		hdr->inner_l3_offset = skb_network_offset(skb) - 2;
 		this_cpu_inc(nic->pnicvf->drv_stats->tx_tso);
 	}
+
+	/* Check if timestamp is requested */
+	if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
+		skb_tx_timestamp(skb);
+		return;
+	}
+
+	/* Tx timestamping not supported along with TSO, so ignore request */
+	if (skb_shinfo(skb)->gso_size)
+		return;
+
+	/* HW supports only a single outstanding packet to timestamp */
+	if (!atomic_add_unless(&nic->pnicvf->tx_ptp_skbs, 1, 1))
+		return;
+
+	/* Mark the SKB for later reference */
+	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+
+	/* Finally enable timestamp generation
+	 * Since 'post_cqe' is also set, two CQEs will be posted
+	 * for this packet i.e CQE_TYPE_SEND and CQE_TYPE_SEND_PTP.
+	 */
+	hdr->tstmp = 1;
 }
 
 /* SQ GATHER subdescriptor
diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
index 5e5c4d7796b8..0f23999c5bcf 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
@@ -245,6 +245,35 @@ void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable)
 }
 EXPORT_SYMBOL(bgx_lmac_rx_tx_enable);
 
+/* Enables or disables timestamp insertion by BGX for Rx packets */
+void bgx_config_timestamping(int node, int bgx_idx, int lmacid, bool enable)
+{
+	struct bgx *bgx = get_bgx(node, bgx_idx);
+	struct lmac *lmac;
+	u64 csr_offset, cfg;
+
+	if (!bgx)
+		return;
+
+	lmac = &bgx->lmac[lmacid];
+
+	if (lmac->lmac_type == BGX_MODE_SGMII ||
+	    lmac->lmac_type == BGX_MODE_QSGMII ||
+	    lmac->lmac_type == BGX_MODE_RGMII)
+		csr_offset = BGX_GMP_GMI_RXX_FRM_CTL;
+	else
+		csr_offset = BGX_SMUX_RX_FRM_CTL;
+
+	cfg = bgx_reg_read(bgx, lmacid, csr_offset);
+
+	if (enable)
+		cfg |= BGX_PKT_RX_PTP_EN;
+	else
+		cfg &= ~BGX_PKT_RX_PTP_EN;
+	bgx_reg_write(bgx, lmacid, csr_offset, cfg);
+}
+EXPORT_SYMBOL(bgx_config_timestamping);
+
 void bgx_lmac_get_pfc(int node, int bgx_idx, int lmacid, void *pause)
 {
 	struct pfc *pfc = (struct pfc *)pause;
diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
index 23acdc5ab896..5a7567d31138 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
@@ -122,6 +122,8 @@
 #define  SPU_DBG_CTL_AN_NONCE_MCT_DIS		BIT_ULL(29)
 
 #define BGX_SMUX_RX_INT			0x20000
+#define BGX_SMUX_RX_FRM_CTL		0x20020
+#define  BGX_PKT_RX_PTP_EN			BIT_ULL(12)
 #define BGX_SMUX_RX_JABBER		0x20030
 #define BGX_SMUX_RX_CTL			0x20048
 #define  SMU_RX_CTL_STATUS			(3ull << 0)
@@ -172,6 +174,7 @@
 #define  GMI_PORT_CFG_SPEED_MSB			BIT_ULL(8)
 #define  GMI_PORT_CFG_RX_IDLE			BIT_ULL(12)
 #define  GMI_PORT_CFG_TX_IDLE			BIT_ULL(13)
+#define BGX_GMP_GMI_RXX_FRM_CTL		0x38028
 #define BGX_GMP_GMI_RXX_JABBER		0x38038
 #define BGX_GMP_GMI_TXX_THRESH		0x38210
 #define BGX_GMP_GMI_TXX_APPEND		0x38218
@@ -223,6 +226,7 @@ void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac);
 void bgx_get_lmac_link_state(int node, int bgx_idx, int lmacid, void *status);
 void bgx_lmac_internal_loopback(int node, int bgx_idx,
 				int lmac_idx, bool enable);
+void bgx_config_timestamping(int node, int bgx_idx, int lmacid, bool enable);
 void bgx_lmac_get_pfc(int node, int bgx_idx, int lmacid, void *pause);
 void bgx_lmac_set_pfc(int node, int bgx_idx, int lmacid, void *pause);
 
-- 
2.15.1

^ permalink raw reply related

* Re: Waiting for the PHY to complete auto-negotiation
From: Mason @ 2017-12-11 14:29 UTC (permalink / raw)
  To: Florian Fainelli, Andrew Lunn, Mans Rullgard; +Cc: netdev, David Miller
In-Reply-To: <babc13c4-b0ad-03b3-b635-c14cfef19399@gmail.com>

+ Mans

On 09/12/2017 19:49, Florian Fainelli wrote:

> Having any HW state machine requiring X number of clock cycles to
> guarantee a full transition to a stopped state is not unusual, however,
> the fact that you need to send 5 packets to guarantee an EOC descriptor
> is hit is completely unusual. Ideally there is a single bit that tells
> the DMA engine: you are enabled, do your thing, or you are now disabled,
> and you must stop all accesses to DRAM *now*.
> 
> So what would be the correct way to quiesce that controller according to
> your HW folks?

He (it's a single person) offered to run some RTL-level simulations,
but then moved on to more important tasks. At some point he wrote:

> If you reset the DMA enable in the middle of a DMA, the hardware
> state machine doesn't return to the IDLE state if it has more
> descriptors to process. It should be noted that the RX DMA has been
> designed by our IP vendor with no intention of stopping the DMA in
> the middle of its operation.


While the documentation for the IP states:

> Receive DMA Channel Disabling
> 
> When the entire receive frame has been read from the Receive FIFO and
> sent over the AMBA bus, the DMA operation ends, and the Receive DMA
> Channel is automatically disabled.  To do this, hardware resets the
> Enable bit in the Receive Channel Control Register to "0" after the
> last data has been read from the Receive FIFO and sent over the AMBA
> bus.
> 
> When operating in descriptor mode, upon completion of a receive frame
> DMA operation, if the descriptor chain has not ended when a receive
> frame DMA operation completes, the next receive frame DMA operation
> begins.  The last descriptor in a descriptor chain is indicated by
> having its End Of Chain- EOC, flag set to "1".  If this EOC flag is
> "0", to begin the next receive frame DMA operation, the next
> descriptor is automatically retrieved and used to configure the
> Receive DMA Channel.  The Receive DMA Channel is then automatically
> re-enabled and the next receive frame DMA operation begins.
> 
> In descriptor mode, an AMBA bus error can occur when reading receive
> descriptor data.  If this happens, receive descriptor processing ends
> and the Receive DMA Channel is turned off.  The Descriptor Error bit
> in the Receive Status Register is set to "1".


I don't see how sending "fake" packets through the loop back would be
considered "resetting the DMA enable in the middle of a DMA".
(I'm afraid the HW dev didn't grasp what the driver is doing.)

I suppose I should test forcefully setting the enable bit to 0 in
the driver, and see if hell breaks loose.

Regards.

^ permalink raw reply

* Re: [PATCH net-next v5 0/2] net: thunderx: add support for PTP clock
From: Philippe Ombredanne @ 2017-12-11 14:33 UTC (permalink / raw)
  To: Aleksey Makarov
  Cc: netdev, moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	LKML, Goutham, Sunil, Radoslaw Biernacki, Robert Richter,
	David Daney, Richard Cochran
In-Reply-To: <20171211141435.2915-1-aleksey.makarov@cavium.com>

Aleksey,

On Mon, Dec 11, 2017 at 3:14 PM, Aleksey Makarov
<aleksey.makarov@cavium.com> wrote:
> This series adds support for IEEE 1588 Precision Time Protocol
> to Cavium ethernet driver.
>
> The first patch adds support for the Precision Time Protocol Clocks and
> Timestamping coprocessor (PTP) found on Cavium processors.
> It registers a new PTP clock in the PTP core and provides functions
> to use the counter in BGX, TNS, GTI, and NIC blocks.
>
> The second patch introduces support for the PTP protocol to the
> Cavium ThunderX ethernet driver.
>
> v5:
> - fix the file headers (add SPDX tags, remove advertisment) (Philippe Ombredanne)

Thank you.

Acked-by: Philippe Ombredanne <pombredanne@nexb.com>

-- 
Cordially
Philippe Ombredanne

^ permalink raw reply


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