Commit f3c6e128 authored by David S. Miller's avatar David S. Miller

Merge branch 'ethtool-mac-merge'

Vladimir Oltean says:

====================
ethtool support for IEEE 802.3 MAC Merge layer

Change log
----------

v3->v4:
- add missing opening bracket in ocelot_port_mm_irq()
- moved cfg.verify_time range checking so that it actually takes place
  for the updated rather than old value
v3 at:
https://patchwork.kernel.org/project/netdevbpf/cover/20230117085947.2176464-1-vladimir.oltean@nxp.com/

v2->v3:
- made get_mm return int instead of void
- deleted ETHTOOL_A_MM_SUPPORTED
- renamed ETHTOOL_A_MM_ADD_FRAG_SIZE to ETHTOOL_A_MM_TX_MIN_FRAG_SIZE
- introduced ETHTOOL_A_MM_RX_MIN_FRAG_SIZE
- cleaned up documentation
- rebased on top of PLCA changes
- renamed ETHTOOL_STATS_SRC_* to ETHTOOL_MAC_STATS_SRC_*
v2 at:
https://patchwork.kernel.org/project/netdevbpf/cover/20230111161706.1465242-1-vladimir.oltean@nxp.com/

v1->v2:
I've decided to focus just on the MAC Merge layer for now, which is why
I am able to submit this patch set as non-RFC.
v1 (RFC) at:
https://patchwork.kernel.org/project/netdevbpf/cover/20220816222920.1952936-1-vladimir.oltean@nxp.com/

What is being introduced
------------------------

TL;DR: a MAC Merge layer as defined by IEEE 802.3-2018, clause 99
(interspersing of express traffic). This is controlled through ethtool
netlink (ETHTOOL_MSG_MM_GET, ETHTOOL_MSG_MM_SET). The raw ethtool
commands are posted here:
https://patchwork.kernel.org/project/netdevbpf/cover/20230111153638.1454687-1-vladimir.oltean@nxp.com/

The MAC Merge layer has its own statistics counters
(ethtool --include-statistics --show-mm swp0) as well as two member
MACs, the statistics of which can be queried individually, through a new
ethtool netlink attribute, corresponding to:

$ ethtool -I --show-pause eno2 --src aggregate
$ ethtool -S eno2 --groups eth-mac eth-phy eth-ctrl rmon -- --src pmac

The core properties of the MAC Merge layer are described in great detail
in patches 02/12 and 03/12. They can be viewed in "make htmldocs" format.

Devices for which the API is supported
--------------------------------------

I decided to start with the Ethernet switch on NXP LS1028A (Felix)
because of the smaller patch set. I also have support for the ENETC
controller pending.

I would like to get confirmation that the UAPI being proposed here will
not restrict any use cases known by other hardware vendors.

Why is support for preemptible traffic classes not here?
--------------------------------------------------------

There is legitimate concern whether the 802.1Q portion of the standard
(which traffic classes go to the eMAC and which to the pMAC) should be
modeled in Linux using tc or using another UAPI. I think that is
stalling the entire series, but should be discussed separately instead.
Removing FP adminStatus support makes me confident enough to submit this
patch set without an RFC tag (meaning: I wouldn't mind if it was merged
as is).

What is submitted here is sufficient for an LLDP daemon to do its job.
I've patched openlldp to advertise and configure frame preemption:
https://github.com/vladimiroltean/openlldp/tree/frame-preemption-v3

In case someone wants to try it out, here are some commands I've used.

 # Configure the interfaces to receive and transmit LLDP Data Units
 lldptool -L -i eno0 adminStatus=rxtx
 lldptool -L -i swp0 adminStatus=rxtx
 # Enable the transmission of certain TLVs on switch's interface
 lldptool -T -i eno0 -V addEthCap enableTx=yes
 lldptool -T -i swp0 -V addEthCap enableTx=yes
 # Query LLDP statistics on switch's interface
 lldptool -S -i swp0
 # Query the received neighbor TLVs
 lldptool -i swp0 -t -n -V addEthCap
 Additional Ethernet Capabilities TLV
         Preemption capability supported
         Preemption capability enabled
         Preemption capability active
         Additional fragment size: 60 octets

So using this patch set, lldpad will be able to advertise and configure
frame preemption, but still, no data packet will be sent as preemptible
over the link, because there is no UAPI to control which traffic classes
are sent as preemptible and which as express.

Preemptable or preemptible?
---------------------------

IEEE 802.3 uses "preemptable" throughout. IEEE 802.1Q uses "preemptible"
throughout. Because the definition of "preemptible" falls under 802.1Q's
jurisdiction and 802.3 just references it, I went with the 802.1Q naming
even where supporting an 802.3 feature. Also, checkpatch agrees with this.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 40e0b090 6505b680
......@@ -223,6 +223,8 @@ Userspace to kernel:
``ETHTOOL_MSG_PSE_SET`` set PSE parameters
``ETHTOOL_MSG_PSE_GET`` get PSE parameters
``ETHTOOL_MSG_RSS_GET`` get RSS settings
``ETHTOOL_MSG_MM_GET`` get MAC merge layer state
``ETHTOOL_MSG_MM_SET`` set MAC merge layer parameters
===================================== =================================
Kernel to userspace:
......@@ -265,6 +267,7 @@ Kernel to userspace:
``ETHTOOL_MSG_MODULE_GET_REPLY`` transceiver module parameters
``ETHTOOL_MSG_PSE_GET_REPLY`` PSE parameters
``ETHTOOL_MSG_RSS_GET_REPLY`` RSS settings
``ETHTOOL_MSG_MM_GET_REPLY`` MAC merge layer status
======================================== =================================
``GET`` requests are sent by userspace applications to retrieve device
......@@ -1089,8 +1092,18 @@ Request contents:
===================================== ====== ==========================
``ETHTOOL_A_PAUSE_HEADER`` nested request header
``ETHTOOL_A_PAUSE_STATS_SRC`` u32 source of statistics
===================================== ====== ==========================
``ETHTOOL_A_PAUSE_STATS_SRC`` is optional. It takes values from:
.. kernel-doc:: include/uapi/linux/ethtool.h
:identifiers: ethtool_mac_stats_src
If absent from the request, stats will be provided with
an ``ETHTOOL_A_PAUSE_STATS_SRC`` attribute in the response equal to
``ETHTOOL_MAC_STATS_SRC_AGGREGATE``.
Kernel response contents:
===================================== ====== ==========================
......@@ -1505,6 +1518,7 @@ Request contents:
======================================= ====== ==========================
``ETHTOOL_A_STATS_HEADER`` nested request header
``ETHTOOL_A_STATS_SRC`` u32 source of statistics
``ETHTOOL_A_STATS_GROUPS`` bitset requested groups of stats
======================================= ====== ==========================
......@@ -1513,6 +1527,8 @@ Kernel response contents:
+-----------------------------------+--------+--------------------------------+
| ``ETHTOOL_A_STATS_HEADER`` | nested | reply header |
+-----------------------------------+--------+--------------------------------+
| ``ETHTOOL_A_STATS_SRC`` | u32 | source of statistics |
+-----------------------------------+--------+--------------------------------+
| ``ETHTOOL_A_STATS_GRP`` | nested | one or more group of stats |
+-+---------------------------------+--------+--------------------------------+
| | ``ETHTOOL_A_STATS_GRP_ID`` | u32 | group ID - ``ETHTOOL_STATS_*`` |
......@@ -1574,6 +1590,11 @@ Low and high bounds are inclusive, for example:
etherStatsPkts512to1023Octets 512 1023
============================= ==== ====
``ETHTOOL_A_STATS_SRC`` is optional. Similar to ``PAUSE_GET``, it takes values
from ``enum ethtool_mac_stats_src``. If absent from the request, stats will be
provided with an ``ETHTOOL_A_STATS_SRC`` attribute in the response equal to
``ETHTOOL_MAC_STATS_SRC_AGGREGATE``.
PHC_VCLOCKS_GET
===============
......@@ -1868,6 +1889,90 @@ When set, the ``ETHTOOL_A_PLCA_STATUS`` attribute indicates whether the node is
detecting the presence of the BEACON on the network. This flag is
corresponding to ``IEEE 802.3cg-2019`` 30.16.1.1.2 aPLCAStatus.
MM_GET
======
Retrieve 802.3 MAC Merge parameters.
Request contents:
==================================== ====== ==========================
``ETHTOOL_A_MM_HEADER`` nested request header
==================================== ====== ==========================
Kernel response contents:
================================= ====== ===================================
``ETHTOOL_A_MM_HEADER`` nested request header
``ETHTOOL_A_MM_PMAC_ENABLED`` bool set if RX of preemptible and SMD-V
frames is enabled
``ETHTOOL_A_MM_TX_ENABLED`` bool set if TX of preemptible frames is
administratively enabled (might be
inactive if verification failed)
``ETHTOOL_A_MM_TX_ACTIVE`` bool set if TX of preemptible frames is
operationally enabled
``ETHTOOL_A_MM_TX_MIN_FRAG_SIZE`` u32 minimum size of transmitted
non-final fragments, in octets
``ETHTOOL_A_MM_RX_MIN_FRAG_SIZE`` u32 minimum size of received non-final
fragments, in octets
``ETHTOOL_A_MM_VERIFY_ENABLED`` bool set if TX of SMD-V frames is
administratively enabled
``ETHTOOL_A_MM_VERIFY_STATUS`` u8 state of the verification function
``ETHTOOL_A_MM_VERIFY_TIME`` u32 delay between verification attempts
``ETHTOOL_A_MM_MAX_VERIFY_TIME``` u32 maximum verification interval
supported by device
``ETHTOOL_A_MM_STATS`` nested IEEE 802.3-2018 subclause 30.14.1
oMACMergeEntity statistics counters
================================= ====== ===================================
The attributes are populated by the device driver through the following
structure:
.. kernel-doc:: include/linux/ethtool.h
:identifiers: ethtool_mm_state
The ``ETHTOOL_A_MM_VERIFY_STATUS`` will report one of the values from
.. kernel-doc:: include/uapi/linux/ethtool.h
:identifiers: ethtool_mm_verify_status
If ``ETHTOOL_A_MM_VERIFY_ENABLED`` was passed as false in the ``MM_SET``
command, ``ETHTOOL_A_MM_VERIFY_STATUS`` will report either
``ETHTOOL_MM_VERIFY_STATUS_INITIAL`` or ``ETHTOOL_MM_VERIFY_STATUS_DISABLED``,
otherwise it should report one of the other states.
It is recommended that drivers start with the pMAC disabled, and enable it upon
user space request. It is also recommended that user space does not depend upon
the default values from ``ETHTOOL_MSG_MM_GET`` requests.
``ETHTOOL_A_MM_STATS`` are reported if ``ETHTOOL_FLAG_STATS`` was set in
``ETHTOOL_A_HEADER_FLAGS``. The attribute will be empty if driver did not
report any statistics. Drivers fill in the statistics in the following
structure:
.. kernel-doc:: include/linux/ethtool.h
:identifiers: ethtool_mm_stats
MM_SET
======
Modifies the configuration of the 802.3 MAC Merge layer.
Request contents:
================================= ====== ==========================
``ETHTOOL_A_MM_VERIFY_TIME`` u32 see MM_GET description
``ETHTOOL_A_MM_VERIFY_ENABLED`` bool see MM_GET description
``ETHTOOL_A_MM_TX_ENABLED`` bool see MM_GET description
``ETHTOOL_A_MM_PMAC_ENABLED`` bool see MM_GET description
``ETHTOOL_A_MM_TX_MIN_FRAG_SIZE`` u32 see MM_GET description
================================= ====== ==========================
The attributes are propagated to the driver through the following structure:
.. kernel-doc:: include/linux/ethtool.h
:identifiers: ethtool_mm_cfg
Request translation
===================
......@@ -1972,4 +2077,6 @@ are netlink only.
n/a ``ETHTOOL_MSG_PLCA_GET_CFG``
n/a ``ETHTOOL_MSG_PLCA_SET_CFG``
n/a ``ETHTOOL_MSG_PLCA_GET_STATUS``
n/a ``ETHTOOL_MSG_MM_GET``
n/a ``ETHTOOL_MSG_MM_SET``
=================================== =====================================
......@@ -171,6 +171,7 @@ statistics are supported in the following commands:
- `ETHTOOL_MSG_PAUSE_GET`
- `ETHTOOL_MSG_FEC_GET`
- `ETHTOOL_MSG_MM_GET`
debugfs
-------
......
......@@ -2024,6 +2024,31 @@ static int felix_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp,
return ocelot_port_del_dscp_prio(ocelot, port, dscp, prio);
}
static int felix_get_mm(struct dsa_switch *ds, int port,
struct ethtool_mm_state *state)
{
struct ocelot *ocelot = ds->priv;
return ocelot_port_get_mm(ocelot, port, state);
}
static int felix_set_mm(struct dsa_switch *ds, int port,
struct ethtool_mm_cfg *cfg,
struct netlink_ext_ack *extack)
{
struct ocelot *ocelot = ds->priv;
return ocelot_port_set_mm(ocelot, port, cfg, extack);
}
static void felix_get_mm_stats(struct dsa_switch *ds, int port,
struct ethtool_mm_stats *stats)
{
struct ocelot *ocelot = ds->priv;
ocelot_port_get_mm_stats(ocelot, port, stats);
}
const struct dsa_switch_ops felix_switch_ops = {
.get_tag_protocol = felix_get_tag_protocol,
.change_tag_protocol = felix_change_tag_protocol,
......@@ -2031,6 +2056,9 @@ const struct dsa_switch_ops felix_switch_ops = {
.setup = felix_setup,
.teardown = felix_teardown,
.set_ageing_time = felix_set_ageing_time,
.get_mm = felix_get_mm,
.set_mm = felix_set_mm,
.get_mm_stats = felix_get_mm_stats,
.get_stats64 = felix_get_stats64,
.get_pause_stats = felix_get_pause_stats,
.get_rmon_stats = felix_get_rmon_stats,
......
......@@ -6,6 +6,7 @@
#include <soc/mscc/ocelot_qsys.h>
#include <soc/mscc/ocelot_vcap.h>
#include <soc/mscc/ocelot_ana.h>
#include <soc/mscc/ocelot_dev.h>
#include <soc/mscc/ocelot_ptp.h>
#include <soc/mscc/ocelot_sys.h>
#include <net/tc_act/tc_gate.h>
......@@ -318,6 +319,29 @@ static const u32 vsc9959_sys_regmap[] = {
REG(SYS_COUNT_RX_GREEN_PRIO_5, 0x0000a4),
REG(SYS_COUNT_RX_GREEN_PRIO_6, 0x0000a8),
REG(SYS_COUNT_RX_GREEN_PRIO_7, 0x0000ac),
REG(SYS_COUNT_RX_ASSEMBLY_ERRS, 0x0000b0),
REG(SYS_COUNT_RX_SMD_ERRS, 0x0000b4),
REG(SYS_COUNT_RX_ASSEMBLY_OK, 0x0000b8),
REG(SYS_COUNT_RX_MERGE_FRAGMENTS, 0x0000bc),
REG(SYS_COUNT_RX_PMAC_OCTETS, 0x0000c0),
REG(SYS_COUNT_RX_PMAC_UNICAST, 0x0000c4),
REG(SYS_COUNT_RX_PMAC_MULTICAST, 0x0000c8),
REG(SYS_COUNT_RX_PMAC_BROADCAST, 0x0000cc),
REG(SYS_COUNT_RX_PMAC_SHORTS, 0x0000d0),
REG(SYS_COUNT_RX_PMAC_FRAGMENTS, 0x0000d4),
REG(SYS_COUNT_RX_PMAC_JABBERS, 0x0000d8),
REG(SYS_COUNT_RX_PMAC_CRC_ALIGN_ERRS, 0x0000dc),
REG(SYS_COUNT_RX_PMAC_SYM_ERRS, 0x0000e0),
REG(SYS_COUNT_RX_PMAC_64, 0x0000e4),
REG(SYS_COUNT_RX_PMAC_65_127, 0x0000e8),
REG(SYS_COUNT_RX_PMAC_128_255, 0x0000ec),
REG(SYS_COUNT_RX_PMAC_256_511, 0x0000f0),
REG(SYS_COUNT_RX_PMAC_512_1023, 0x0000f4),
REG(SYS_COUNT_RX_PMAC_1024_1526, 0x0000f8),
REG(SYS_COUNT_RX_PMAC_1527_MAX, 0x0000fc),
REG(SYS_COUNT_RX_PMAC_PAUSE, 0x000100),
REG(SYS_COUNT_RX_PMAC_CONTROL, 0x000104),
REG(SYS_COUNT_RX_PMAC_LONGS, 0x000108),
REG(SYS_COUNT_TX_OCTETS, 0x000200),
REG(SYS_COUNT_TX_UNICAST, 0x000204),
REG(SYS_COUNT_TX_MULTICAST, 0x000208),
......@@ -349,6 +373,20 @@ static const u32 vsc9959_sys_regmap[] = {
REG(SYS_COUNT_TX_GREEN_PRIO_6, 0x000270),
REG(SYS_COUNT_TX_GREEN_PRIO_7, 0x000274),
REG(SYS_COUNT_TX_AGED, 0x000278),
REG(SYS_COUNT_TX_MM_HOLD, 0x00027c),
REG(SYS_COUNT_TX_MERGE_FRAGMENTS, 0x000280),
REG(SYS_COUNT_TX_PMAC_OCTETS, 0x000284),
REG(SYS_COUNT_TX_PMAC_UNICAST, 0x000288),
REG(SYS_COUNT_TX_PMAC_MULTICAST, 0x00028c),
REG(SYS_COUNT_TX_PMAC_BROADCAST, 0x000290),
REG(SYS_COUNT_TX_PMAC_PAUSE, 0x000294),
REG(SYS_COUNT_TX_PMAC_64, 0x000298),
REG(SYS_COUNT_TX_PMAC_65_127, 0x00029c),
REG(SYS_COUNT_TX_PMAC_128_255, 0x0002a0),
REG(SYS_COUNT_TX_PMAC_256_511, 0x0002a4),
REG(SYS_COUNT_TX_PMAC_512_1023, 0x0002a8),
REG(SYS_COUNT_TX_PMAC_1024_1526, 0x0002ac),
REG(SYS_COUNT_TX_PMAC_1527_MAX, 0x0002b0),
REG(SYS_COUNT_DROP_LOCAL, 0x000400),
REG(SYS_COUNT_DROP_TAIL, 0x000404),
REG(SYS_COUNT_DROP_YELLOW_PRIO_0, 0x000408),
......@@ -439,6 +477,9 @@ static const u32 vsc9959_dev_gmii_regmap[] = {
REG(DEV_MAC_FC_MAC_LOW_CFG, 0x3c),
REG(DEV_MAC_FC_MAC_HIGH_CFG, 0x40),
REG(DEV_MAC_STICKY, 0x44),
REG(DEV_MM_ENABLE_CONFIG, 0x48),
REG(DEV_MM_VERIF_CONFIG, 0x4C),
REG(DEV_MM_STATUS, 0x50),
REG_RESERVED(PCS1G_CFG),
REG_RESERVED(PCS1G_MODE_CFG),
REG_RESERVED(PCS1G_SD_CFG),
......@@ -2562,20 +2603,19 @@ static const struct felix_info felix_info_vsc9959 = {
.tas_guard_bands_update = vsc9959_tas_guard_bands_update,
};
/* The INTB interrupt is shared between for PTP TX timestamp availability
* notification and MAC Merge status change on each port.
*/
static irqreturn_t felix_irq_handler(int irq, void *data)
{
struct ocelot *ocelot = (struct ocelot *)data;
/* The INTB interrupt is used for both PTP TX timestamp interrupt
* and preemption status change interrupt on each port.
*
* - Get txtstamp if have
* - TODO: handle preemption. Without handling it, driver may get
* interrupt storm.
*/
int port;
ocelot_get_txtstamp(ocelot);
for (port = 0; port < ocelot->num_phys_ports; port++)
ocelot_port_mm_irq(ocelot, port);
return IRQ_HANDLED;
}
......@@ -2623,6 +2663,7 @@ static int felix_pci_probe(struct pci_dev *pdev,
}
ocelot->ptp = 1;
ocelot->mm_supported = true;
ds = kzalloc(sizeof(struct dsa_switch), GFP_KERNEL);
if (!ds) {
......
......@@ -5,6 +5,7 @@ mscc_ocelot_switch_lib-y := \
ocelot_devlink.o \
ocelot_flower.o \
ocelot_io.o \
ocelot_mm.o \
ocelot_police.o \
ocelot_ptp.o \
ocelot_stats.o \
......
......@@ -2738,10 +2738,8 @@ int ocelot_init(struct ocelot *ocelot)
return -ENOMEM;
ret = ocelot_stats_init(ocelot);
if (ret) {
destroy_workqueue(ocelot->owq);
return ret;
}
if (ret)
goto err_stats_init;
INIT_LIST_HEAD(&ocelot->multicast);
INIT_LIST_HEAD(&ocelot->pgids);
......@@ -2756,6 +2754,12 @@ int ocelot_init(struct ocelot *ocelot)
if (ocelot->ops->psfp_init)
ocelot->ops->psfp_init(ocelot);
if (ocelot->mm_supported) {
ret = ocelot_mm_init(ocelot);
if (ret)
goto err_mm_init;
}
for (port = 0; port < ocelot->num_phys_ports; port++) {
/* Clear all counters (5 groups) */
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port) |
......@@ -2853,6 +2857,12 @@ int ocelot_init(struct ocelot *ocelot)
ANA_CPUQ_8021_CFG, i);
return 0;
err_mm_init:
ocelot_stats_deinit(ocelot);
err_stats_init:
destroy_workqueue(ocelot->owq);
return ret;
}
EXPORT_SYMBOL(ocelot_init);
......
......@@ -109,6 +109,8 @@ void ocelot_mirror_put(struct ocelot *ocelot);
int ocelot_stats_init(struct ocelot *ocelot);
void ocelot_stats_deinit(struct ocelot *ocelot);
int ocelot_mm_init(struct ocelot *ocelot);
extern struct notifier_block ocelot_netdevice_nb;
extern struct notifier_block ocelot_switchdev_nb;
extern struct notifier_block ocelot_switchdev_blocking_nb;
......
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable
* switches (VSC9959)
*
* Copyright 2022-2023 NXP
*/
#include <linux/ethtool.h>
#include <soc/mscc/ocelot.h>
#include <soc/mscc/ocelot_dev.h>
#include <soc/mscc/ocelot_qsys.h>
#include "ocelot.h"
static const char *
mm_verify_state_to_string(enum ethtool_mm_verify_status state)
{
switch (state) {
case ETHTOOL_MM_VERIFY_STATUS_INITIAL:
return "INITIAL";
case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
return "VERIFYING";
case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
return "SUCCEEDED";
case ETHTOOL_MM_VERIFY_STATUS_FAILED:
return "FAILED";
case ETHTOOL_MM_VERIFY_STATUS_DISABLED:
return "DISABLED";
default:
return "UNKNOWN";
}
}
static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val)
{
switch (DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(val)) {
case 0:
return ETHTOOL_MM_VERIFY_STATUS_INITIAL;
case 1:
return ETHTOOL_MM_VERIFY_STATUS_VERIFYING;
case 2:
return ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
case 3:
return ETHTOOL_MM_VERIFY_STATUS_FAILED;
case 4:
return ETHTOOL_MM_VERIFY_STATUS_DISABLED;
default:
return ETHTOOL_MM_VERIFY_STATUS_UNKNOWN;
}
}
void ocelot_port_mm_irq(struct ocelot *ocelot, int port)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_mm_state *mm = &ocelot->mm[port];
enum ethtool_mm_verify_status verify_status;
u32 val;
mutex_lock(&mm->lock);
val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
verify_status = ocelot_mm_verify_status(val);
if (mm->verify_status != verify_status) {
dev_dbg(ocelot->dev,
"Port %d MAC Merge verification state %s\n",
port, mm_verify_state_to_string(verify_status));
mm->verify_status = verify_status;
}
if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) {
mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS);
dev_dbg(ocelot->dev, "Port %d TX preemption %s\n",
port, mm->tx_active ? "active" : "inactive");
}
if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) {
dev_err(ocelot->dev,
"Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n",
port);
}
if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) {
dev_err(ocelot->dev,
"Unexpected P-frame requested to be transmitted on port %d while verification was unsuccessful or not yet verified, or MM_TX_ENA=0\n",
port);
}
ocelot_port_writel(ocelot_port, val, DEV_MM_STATUS);
mutex_unlock(&mm->lock);
}
EXPORT_SYMBOL_GPL(ocelot_port_mm_irq);
int ocelot_port_set_mm(struct ocelot *ocelot, int port,
struct ethtool_mm_cfg *cfg,
struct netlink_ext_ack *extack)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
u32 mm_enable = 0, verify_disable = 0, add_frag_size;
struct ocelot_mm_state *mm;
int err;
if (!ocelot->mm_supported)
return -EOPNOTSUPP;
mm = &ocelot->mm[port];
err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size,
&add_frag_size, extack);
if (err)
return err;
if (cfg->pmac_enabled)
mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA;
if (cfg->tx_enabled)
mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA;
if (!cfg->verify_enabled)
verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS;
mutex_lock(&mm->lock);
ocelot_port_rmwl(ocelot_port, mm_enable,
DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA |
DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA,
DEV_MM_ENABLE_CONFIG);
ocelot_port_rmwl(ocelot_port, verify_disable |
DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time),
DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS |
DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M,
DEV_MM_VERIF_CONFIG);
ocelot_rmw_rix(ocelot,
QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size),
QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M,
QSYS_PREEMPTION_CFG,
port);
mutex_unlock(&mm->lock);
return 0;
}
EXPORT_SYMBOL_GPL(ocelot_port_set_mm);
int ocelot_port_get_mm(struct ocelot *ocelot, int port,
struct ethtool_mm_state *state)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_mm_state *mm;
u32 val, add_frag_size;
if (!ocelot->mm_supported)
return -EOPNOTSUPP;
mm = &ocelot->mm[port];
mutex_lock(&mm->lock);
val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG);
state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA);
state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA);
val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG);
state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val);
state->max_verify_time = 128;
val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port);
add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val);
state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size);
state->rx_min_frag_size = ETH_ZLEN;
state->verify_status = mm->verify_status;
state->tx_active = mm->tx_active;
mutex_unlock(&mm->lock);
return 0;
}
EXPORT_SYMBOL_GPL(ocelot_port_get_mm);
int ocelot_mm_init(struct ocelot *ocelot)
{
struct ocelot_port *ocelot_port;
struct ocelot_mm_state *mm;
int port;
if (!ocelot->mm_supported)
return 0;
ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
sizeof(*ocelot->mm), GFP_KERNEL);
if (!ocelot->mm)
return -ENOMEM;
for (port = 0; port < ocelot->num_phys_ports; port++) {
u32 val;
mm = &ocelot->mm[port];
mutex_init(&mm->lock);
ocelot_port = ocelot->ports[port];
/* Update initial status variable for the
* verification state machine
*/
val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
mm->verify_status = ocelot_mm_verify_status(val);
}
return 0;
}
This diff is collapsed.
This diff is collapsed.
......@@ -937,6 +937,17 @@ struct dsa_switch_ops {
int (*get_ts_info)(struct dsa_switch *ds, int port,
struct ethtool_ts_info *ts);
/*
* ethtool MAC merge layer
*/
int (*get_mm)(struct dsa_switch *ds, int port,
struct ethtool_mm_state *state);
int (*set_mm)(struct dsa_switch *ds, int port,
struct ethtool_mm_cfg *cfg,
struct netlink_ext_ack *extack);
void (*get_mm_stats)(struct dsa_switch *ds, int port,
struct ethtool_mm_stats *stats);
/*
* DCB ops
*/
......
......@@ -362,6 +362,29 @@ enum ocelot_reg {
SYS_COUNT_RX_GREEN_PRIO_5,
SYS_COUNT_RX_GREEN_PRIO_6,
SYS_COUNT_RX_GREEN_PRIO_7,
SYS_COUNT_RX_ASSEMBLY_ERRS,
SYS_COUNT_RX_SMD_ERRS,
SYS_COUNT_RX_ASSEMBLY_OK,
SYS_COUNT_RX_MERGE_FRAGMENTS,
SYS_COUNT_RX_PMAC_OCTETS,
SYS_COUNT_RX_PMAC_UNICAST,
SYS_COUNT_RX_PMAC_MULTICAST,
SYS_COUNT_RX_PMAC_BROADCAST,
SYS_COUNT_RX_PMAC_SHORTS,
SYS_COUNT_RX_PMAC_FRAGMENTS,
SYS_COUNT_RX_PMAC_JABBERS,
SYS_COUNT_RX_PMAC_CRC_ALIGN_ERRS,
SYS_COUNT_RX_PMAC_SYM_ERRS,
SYS_COUNT_RX_PMAC_64,
SYS_COUNT_RX_PMAC_65_127,
SYS_COUNT_RX_PMAC_128_255,
SYS_COUNT_RX_PMAC_256_511,
SYS_COUNT_RX_PMAC_512_1023,
SYS_COUNT_RX_PMAC_1024_1526,
SYS_COUNT_RX_PMAC_1527_MAX,
SYS_COUNT_RX_PMAC_PAUSE,
SYS_COUNT_RX_PMAC_CONTROL,
SYS_COUNT_RX_PMAC_LONGS,
SYS_COUNT_TX_OCTETS,
SYS_COUNT_TX_UNICAST,
SYS_COUNT_TX_MULTICAST,
......@@ -393,6 +416,20 @@ enum ocelot_reg {
SYS_COUNT_TX_GREEN_PRIO_6,
SYS_COUNT_TX_GREEN_PRIO_7,
SYS_COUNT_TX_AGED,
SYS_COUNT_TX_MM_HOLD,
SYS_COUNT_TX_MERGE_FRAGMENTS,
SYS_COUNT_TX_PMAC_OCTETS,
SYS_COUNT_TX_PMAC_UNICAST,
SYS_COUNT_TX_PMAC_MULTICAST,
SYS_COUNT_TX_PMAC_BROADCAST,
SYS_COUNT_TX_PMAC_PAUSE,
SYS_COUNT_TX_PMAC_64,
SYS_COUNT_TX_PMAC_65_127,
SYS_COUNT_TX_PMAC_128_255,
SYS_COUNT_TX_PMAC_256_511,
SYS_COUNT_TX_PMAC_512_1023,
SYS_COUNT_TX_PMAC_1024_1526,
SYS_COUNT_TX_PMAC_1527_MAX,
SYS_COUNT_DROP_LOCAL,
SYS_COUNT_DROP_TAIL,
SYS_COUNT_DROP_YELLOW_PRIO_0,
......@@ -478,6 +515,9 @@ enum ocelot_reg {
DEV_MAC_FC_MAC_LOW_CFG,
DEV_MAC_FC_MAC_HIGH_CFG,
DEV_MAC_STICKY,
DEV_MM_ENABLE_CONFIG,
DEV_MM_VERIF_CONFIG,
DEV_MM_STATUS,
PCS1G_CFG,
PCS1G_MODE_CFG,
PCS1G_SD_CFG,
......@@ -702,6 +742,12 @@ struct ocelot_mirror {
int to;
};
struct ocelot_mm_state {
struct mutex lock;
enum ethtool_mm_verify_status verify_status;
bool tx_active;
};
struct ocelot_port;
struct ocelot_port {
......@@ -814,6 +860,7 @@ struct ocelot {
struct workqueue_struct *owq;
u8 ptp:1;
u8 mm_supported:1;
struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_info;
struct hwtstamp_config hwtstamp_config;
......@@ -826,6 +873,8 @@ struct ocelot {
spinlock_t ptp_clock_lock;
struct ptp_pin_desc ptp_pins[OCELOT_PTP_PINS_NUM];
struct ocelot_mm_state *mm;
struct ocelot_fdma *fdma;
};
......@@ -937,6 +986,8 @@ void ocelot_port_get_stats64(struct ocelot *ocelot, int port,
struct rtnl_link_stats64 *stats);
void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port,
struct ethtool_pause_stats *pause_stats);
void ocelot_port_get_mm_stats(struct ocelot *ocelot, int port,
struct ethtool_mm_stats *stats);
void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges);
......@@ -1082,6 +1133,13 @@ int ocelot_vcap_policer_add(struct ocelot *ocelot, u32 pol_ix,
struct ocelot_policer *pol);
int ocelot_vcap_policer_del(struct ocelot *ocelot, u32 pol_ix);
void ocelot_port_mm_irq(struct ocelot *ocelot, int port);
int ocelot_port_set_mm(struct ocelot *ocelot, int port,
struct ethtool_mm_cfg *cfg,
struct netlink_ext_ack *extack);
int ocelot_port_get_mm(struct ocelot *ocelot, int port,
struct ethtool_mm_state *state);
#if IS_ENABLED(CONFIG_BRIDGE_MRP)
int ocelot_mrp_add(struct ocelot *ocelot, int port,
const struct switchdev_obj_mrp *mrp);
......
......@@ -93,6 +93,29 @@
#define DEV_MAC_STICKY_TX_FRM_LEN_OVR_STICKY BIT(1)
#define DEV_MAC_STICKY_TX_ABORT_STICKY BIT(0)
#define DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA BIT(0)
#define DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA BIT(4)
#define DEV_MM_CONFIG_ENABLE_CONFIG_KEEP_S_AFTER_D BIT(8)
#define DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS BIT(0)
#define DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(x) (((x) << 4) & GENMASK(11, 4))
#define DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M GENMASK(11, 4)
#define DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(x) (((x) & GENMASK(11, 4)) >> 4)
#define DEV_MM_CONFIG_VERIF_CONFIG_VERIF_TIMER_UNITS(x) (((x) << 12) & GENMASK(13, 12))
#define DEV_MM_CONFIG_VERIF_CONFIG_VERIF_TIMER_UNITS_M GENMASK(13, 12)
#define DEV_MM_CONFIG_VERIF_CONFIG_VERIF_TIMER_UNITS_X(x) (((x) & GENMASK(13, 12)) >> 12)
#define DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS BIT(0)
#define DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY BIT(4)
#define DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE(x) (((x) << 8) & GENMASK(10, 8))
#define DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_M GENMASK(10, 8)
#define DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(x) (((x) & GENMASK(10, 8)) >> 8)
#define DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY BIT(12)
#define DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY BIT(16)
#define DEV_MM_STAT_MM_STATUS_MM_RX_FRAME_STATUS BIT(20)
#define DEV_MM_STAT_MM_STATUS_MM_TX_FRAME_STATUS BIT(24)
#define DEV_MM_STAT_MM_STATUS_MM_TX_PRMPT_STATUS BIT(28)
#define PCS1G_CFG_LINK_STATUS_TYPE BIT(4)
#define PCS1G_CFG_AN_LINK_CTRL_ENA BIT(1)
#define PCS1G_CFG_PCS_ENA BIT(0)
......
......@@ -711,6 +711,24 @@ enum ethtool_stringset {
ETH_SS_COUNT
};
/**
* enum ethtool_mac_stats_src - source of ethtool MAC statistics
* @ETHTOOL_MAC_STATS_SRC_AGGREGATE:
* if device supports a MAC merge layer, this retrieves the aggregate
* statistics of the eMAC and pMAC. Otherwise, it retrieves just the
* statistics of the single (express) MAC.
* @ETHTOOL_MAC_STATS_SRC_EMAC:
* if device supports a MM layer, this retrieves the eMAC statistics.
* Otherwise, it retrieves the statistics of the single (express) MAC.
* @ETHTOOL_MAC_STATS_SRC_PMAC:
* if device supports a MM layer, this retrieves the pMAC statistics.
*/
enum ethtool_mac_stats_src {
ETHTOOL_MAC_STATS_SRC_AGGREGATE,
ETHTOOL_MAC_STATS_SRC_EMAC,
ETHTOOL_MAC_STATS_SRC_PMAC,
};
/**
* enum ethtool_module_power_mode_policy - plug-in module power mode policy
* @ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH: Module is always in high power mode.
......@@ -779,6 +797,31 @@ enum ethtool_podl_pse_pw_d_status {
ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR,
};
/**
* enum ethtool_mm_verify_status - status of MAC Merge Verify function
* @ETHTOOL_MM_VERIFY_STATUS_UNKNOWN:
* verification status is unknown
* @ETHTOOL_MM_VERIFY_STATUS_INITIAL:
* the 802.3 Verify State diagram is in the state INIT_VERIFICATION
* @ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
* the Verify State diagram is in the state VERIFICATION_IDLE,
* SEND_VERIFY or WAIT_FOR_RESPONSE
* @ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
* indicates that the Verify State diagram is in the state VERIFIED
* @ETHTOOL_MM_VERIFY_STATUS_FAILED:
* the Verify State diagram is in the state VERIFY_FAIL
* @ETHTOOL_MM_VERIFY_STATUS_DISABLED:
* verification of preemption operation is disabled
*/
enum ethtool_mm_verify_status {
ETHTOOL_MM_VERIFY_STATUS_UNKNOWN,
ETHTOOL_MM_VERIFY_STATUS_INITIAL,
ETHTOOL_MM_VERIFY_STATUS_VERIFYING,
ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED,
ETHTOOL_MM_VERIFY_STATUS_FAILED,
ETHTOOL_MM_VERIFY_STATUS_DISABLED,
};
/**
* struct ethtool_gstrings - string set for data tagging
* @cmd: Command number = %ETHTOOL_GSTRINGS
......
......@@ -55,6 +55,8 @@ enum {
ETHTOOL_MSG_PLCA_GET_CFG,
ETHTOOL_MSG_PLCA_SET_CFG,
ETHTOOL_MSG_PLCA_GET_STATUS,
ETHTOOL_MSG_MM_GET,
ETHTOOL_MSG_MM_SET,
/* add new constants above here */
__ETHTOOL_MSG_USER_CNT,
......@@ -105,6 +107,8 @@ enum {
ETHTOOL_MSG_PLCA_GET_CFG_REPLY,
ETHTOOL_MSG_PLCA_GET_STATUS_REPLY,
ETHTOOL_MSG_PLCA_NTF,
ETHTOOL_MSG_MM_GET_REPLY,
ETHTOOL_MSG_MM_NTF,
/* add new constants above here */
__ETHTOOL_MSG_KERNEL_CNT,
......@@ -424,6 +428,7 @@ enum {
ETHTOOL_A_PAUSE_RX, /* u8 */
ETHTOOL_A_PAUSE_TX, /* u8 */
ETHTOOL_A_PAUSE_STATS, /* nest - _PAUSE_STAT_* */
ETHTOOL_A_PAUSE_STATS_SRC, /* u32 */
/* add new constants above here */
__ETHTOOL_A_PAUSE_CNT,
......@@ -740,6 +745,8 @@ enum {
ETHTOOL_A_STATS_GRP, /* nest - _A_STATS_GRP_* */
ETHTOOL_A_STATS_SRC, /* u32 */
/* add new constants above here */
__ETHTOOL_A_STATS_CNT,
ETHTOOL_A_STATS_MAX = (__ETHTOOL_A_STATS_CNT - 1)
......@@ -922,6 +929,49 @@ enum {
ETHTOOL_A_PLCA_MAX = (__ETHTOOL_A_PLCA_CNT - 1)
};
/* MAC Merge (802.3) */
enum {
ETHTOOL_A_MM_STAT_UNSPEC,
ETHTOOL_A_MM_STAT_PAD,
/* aMACMergeFrameAssErrorCount */
ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS, /* u64 */
/* aMACMergeFrameSmdErrorCount */
ETHTOOL_A_MM_STAT_SMD_ERRORS, /* u64 */
/* aMACMergeFrameAssOkCount */
ETHTOOL_A_MM_STAT_REASSEMBLY_OK, /* u64 */
/* aMACMergeFragCountRx */
ETHTOOL_A_MM_STAT_RX_FRAG_COUNT, /* u64 */
/* aMACMergeFragCountTx */
ETHTOOL_A_MM_STAT_TX_FRAG_COUNT, /* u64 */
/* aMACMergeHoldCount */
ETHTOOL_A_MM_STAT_HOLD_COUNT, /* u64 */
/* add new constants above here */
__ETHTOOL_A_MM_STAT_CNT,
ETHTOOL_A_MM_STAT_MAX = (__ETHTOOL_A_MM_STAT_CNT - 1)
};
enum {
ETHTOOL_A_MM_UNSPEC,
ETHTOOL_A_MM_HEADER, /* nest - _A_HEADER_* */
ETHTOOL_A_MM_PMAC_ENABLED, /* u8 */
ETHTOOL_A_MM_TX_ENABLED, /* u8 */
ETHTOOL_A_MM_TX_ACTIVE, /* u8 */
ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, /* u32 */
ETHTOOL_A_MM_RX_MIN_FRAG_SIZE, /* u32 */
ETHTOOL_A_MM_VERIFY_ENABLED, /* u8 */
ETHTOOL_A_MM_VERIFY_STATUS, /* u8 */
ETHTOOL_A_MM_VERIFY_TIME, /* u32 */
ETHTOOL_A_MM_MAX_VERIFY_TIME, /* u32 */
ETHTOOL_A_MM_STATS, /* nest - _A_MM_STAT_* */
/* add new constants above here */
__ETHTOOL_A_MM_CNT,
ETHTOOL_A_MM_MAX = (__ETHTOOL_A_MM_CNT - 1)
};
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
......
......@@ -1117,6 +1117,40 @@ static void dsa_slave_net_selftest(struct net_device *ndev,
net_selftest(ndev, etest, buf);
}
static int dsa_slave_get_mm(struct net_device *dev,
struct ethtool_mm_state *state)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
if (!ds->ops->get_mm)
return -EOPNOTSUPP;
return ds->ops->get_mm(ds, dp->index, state);
}
static int dsa_slave_set_mm(struct net_device *dev, struct ethtool_mm_cfg *cfg,
struct netlink_ext_ack *extack)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
if (!ds->ops->set_mm)
return -EOPNOTSUPP;
return ds->ops->set_mm(ds, dp->index, cfg, extack);
}
static void dsa_slave_get_mm_stats(struct net_device *dev,
struct ethtool_mm_stats *stats)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct dsa_switch *ds = dp->ds;
if (ds->ops->get_mm_stats)
ds->ops->get_mm_stats(ds, dp->index, stats);
}
static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
......@@ -2205,6 +2239,9 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
.set_rxnfc = dsa_slave_set_rxnfc,
.get_ts_info = dsa_slave_get_ts_info,
.self_test = dsa_slave_net_selftest,
.get_mm = dsa_slave_get_mm,
.set_mm = dsa_slave_set_mm,
.get_mm_stats = dsa_slave_get_mm_stats,
};
static const struct dcbnl_rtnl_ops __maybe_unused dsa_slave_dcbnl_ops = {
......
......@@ -7,5 +7,5 @@ obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o
ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \
linkstate.o debug.o wol.o features.o privflags.o rings.o \
channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \
tunnels.o fec.o eeprom.o stats.o phc_vclocks.o module.o \
pse-pd.o plca.o
tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \
module.o pse-pd.o plca.o mm.o
......@@ -54,4 +54,6 @@ int ethtool_get_module_info_call(struct net_device *dev,
int ethtool_get_module_eeprom_call(struct net_device *dev,
struct ethtool_eeprom *ee, u8 *data);
bool __ethtool_dev_mm_supported(struct net_device *dev);
#endif /* _ETHTOOL_COMMON_H */
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022-2023 NXP
*/
#include "common.h"
#include "netlink.h"
struct mm_req_info {
struct ethnl_req_info base;
};
struct mm_reply_data {
struct ethnl_reply_data base;
struct ethtool_mm_state state;
struct ethtool_mm_stats stats;
};
#define MM_REPDATA(__reply_base) \
container_of(__reply_base, struct mm_reply_data, base)
#define ETHTOOL_MM_STAT_CNT \
(__ETHTOOL_A_MM_STAT_CNT - (ETHTOOL_A_MM_STAT_PAD + 1))
const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1] = {
[ETHTOOL_A_MM_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_stats),
};
static int mm_prepare_data(const struct ethnl_req_info *req_base,
struct ethnl_reply_data *reply_base,
struct genl_info *info)
{
struct mm_reply_data *data = MM_REPDATA(reply_base);
struct net_device *dev = reply_base->dev;
const struct ethtool_ops *ops;
int ret;
ops = dev->ethtool_ops;
if (!ops->get_mm)
return -EOPNOTSUPP;
ethtool_stats_init((u64 *)&data->stats,
sizeof(data->stats) / sizeof(u64));
ret = ethnl_ops_begin(dev);
if (ret < 0)
return ret;
ret = ops->get_mm(dev, &data->state);
if (ret)
goto out_complete;
if (ops->get_mm_stats && (req_base->flags & ETHTOOL_FLAG_STATS))
ops->get_mm_stats(dev, &data->stats);
out_complete:
ethnl_ops_complete(dev);
return 0;
}
static int mm_reply_size(const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
int len = 0;
len += nla_total_size(sizeof(u8)); /* _MM_PMAC_ENABLED */
len += nla_total_size(sizeof(u8)); /* _MM_TX_ENABLED */
len += nla_total_size(sizeof(u8)); /* _MM_TX_ACTIVE */
len += nla_total_size(sizeof(u8)); /* _MM_VERIFY_ENABLED */
len += nla_total_size(sizeof(u8)); /* _MM_VERIFY_STATUS */
len += nla_total_size(sizeof(u32)); /* _MM_VERIFY_TIME */
len += nla_total_size(sizeof(u32)); /* _MM_MAX_VERIFY_TIME */
len += nla_total_size(sizeof(u32)); /* _MM_TX_MIN_FRAG_SIZE */
len += nla_total_size(sizeof(u32)); /* _MM_RX_MIN_FRAG_SIZE */
if (req_base->flags & ETHTOOL_FLAG_STATS)
len += nla_total_size(0) + /* _MM_STATS */
nla_total_size_64bit(sizeof(u64)) * ETHTOOL_MM_STAT_CNT;
return len;
}
static int mm_put_stat(struct sk_buff *skb, u64 val, u16 attrtype)
{
if (val == ETHTOOL_STAT_NOT_SET)
return 0;
if (nla_put_u64_64bit(skb, attrtype, val, ETHTOOL_A_MM_STAT_PAD))
return -EMSGSIZE;
return 0;
}
static int mm_put_stats(struct sk_buff *skb,
const struct ethtool_mm_stats *stats)
{
struct nlattr *nest;
nest = nla_nest_start(skb, ETHTOOL_A_MM_STATS);
if (!nest)
return -EMSGSIZE;
if (mm_put_stat(skb, stats->MACMergeFrameAssErrorCount,
ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS) ||
mm_put_stat(skb, stats->MACMergeFrameSmdErrorCount,
ETHTOOL_A_MM_STAT_SMD_ERRORS) ||
mm_put_stat(skb, stats->MACMergeFrameAssOkCount,
ETHTOOL_A_MM_STAT_REASSEMBLY_OK) ||
mm_put_stat(skb, stats->MACMergeFragCountRx,
ETHTOOL_A_MM_STAT_RX_FRAG_COUNT) ||
mm_put_stat(skb, stats->MACMergeFragCountTx,
ETHTOOL_A_MM_STAT_TX_FRAG_COUNT) ||
mm_put_stat(skb, stats->MACMergeHoldCount,
ETHTOOL_A_MM_STAT_HOLD_COUNT))
goto err_cancel;
nla_nest_end(skb, nest);
return 0;
err_cancel:
nla_nest_cancel(skb, nest);
return -EMSGSIZE;
}
static int mm_fill_reply(struct sk_buff *skb,
const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct mm_reply_data *data = MM_REPDATA(reply_base);
const struct ethtool_mm_state *state = &data->state;
if (nla_put_u8(skb, ETHTOOL_A_MM_TX_ENABLED, state->tx_enabled) ||
nla_put_u8(skb, ETHTOOL_A_MM_TX_ACTIVE, state->tx_active) ||
nla_put_u8(skb, ETHTOOL_A_MM_PMAC_ENABLED, state->pmac_enabled) ||
nla_put_u8(skb, ETHTOOL_A_MM_VERIFY_ENABLED, state->verify_enabled) ||
nla_put_u8(skb, ETHTOOL_A_MM_VERIFY_STATUS, state->verify_status) ||
nla_put_u32(skb, ETHTOOL_A_MM_VERIFY_TIME, state->verify_time) ||
nla_put_u32(skb, ETHTOOL_A_MM_MAX_VERIFY_TIME, state->max_verify_time) ||
nla_put_u32(skb, ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, state->tx_min_frag_size) ||
nla_put_u32(skb, ETHTOOL_A_MM_RX_MIN_FRAG_SIZE, state->rx_min_frag_size))
return -EMSGSIZE;
if (req_base->flags & ETHTOOL_FLAG_STATS &&
mm_put_stats(skb, &data->stats))
return -EMSGSIZE;
return 0;
}
const struct ethnl_request_ops ethnl_mm_request_ops = {
.request_cmd = ETHTOOL_MSG_MM_GET,
.reply_cmd = ETHTOOL_MSG_MM_GET_REPLY,
.hdr_attr = ETHTOOL_A_MM_HEADER,
.req_info_size = sizeof(struct mm_req_info),
.reply_data_size = sizeof(struct mm_reply_data),
.prepare_data = mm_prepare_data,
.reply_size = mm_reply_size,
.fill_reply = mm_fill_reply,
};
const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1] = {
[ETHTOOL_A_MM_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
[ETHTOOL_A_MM_VERIFY_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1),
[ETHTOOL_A_MM_VERIFY_TIME] = NLA_POLICY_RANGE(NLA_U32, 1, 128),
[ETHTOOL_A_MM_TX_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1),
[ETHTOOL_A_MM_PMAC_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1),
[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE] = NLA_POLICY_RANGE(NLA_U32, 60, 252),
};
static void mm_state_to_cfg(const struct ethtool_mm_state *state,
struct ethtool_mm_cfg *cfg)
{
/* We could also compare state->verify_status against
* ETHTOOL_MM_VERIFY_STATUS_DISABLED, but state->verify_enabled
* is more like an administrative state which should be seen in
* ETHTOOL_MSG_MM_GET replies. For example, a port with verification
* disabled might be in the ETHTOOL_MM_VERIFY_STATUS_INITIAL
* if it's down.
*/
cfg->verify_enabled = state->verify_enabled;
cfg->verify_time = state->verify_time;
cfg->tx_enabled = state->tx_enabled;
cfg->pmac_enabled = state->pmac_enabled;
cfg->tx_min_frag_size = state->tx_min_frag_size;
}
int ethnl_set_mm(struct sk_buff *skb, struct genl_info *info)
{
struct netlink_ext_ack *extack = info->extack;
struct ethnl_req_info req_info = {};
struct ethtool_mm_state state = {};
struct nlattr **tb = info->attrs;
struct ethtool_mm_cfg cfg = {};
const struct ethtool_ops *ops;
struct net_device *dev;
bool mod = false;
int ret;
ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_MM_HEADER],
genl_info_net(info), extack, true);
if (ret)
return ret;
dev = req_info.dev;
ops = dev->ethtool_ops;
if (!ops->get_mm || !ops->set_mm) {
ret = -EOPNOTSUPP;
goto out_dev_put;
}
rtnl_lock();
ret = ethnl_ops_begin(dev);
if (ret < 0)
goto out_rtnl_unlock;
ret = ops->get_mm(dev, &state);
if (ret)
goto out_complete;
mm_state_to_cfg(&state, &cfg);
ethnl_update_bool(&cfg.verify_enabled, tb[ETHTOOL_A_MM_VERIFY_ENABLED],
&mod);
ethnl_update_u32(&cfg.verify_time, tb[ETHTOOL_A_MM_VERIFY_TIME], &mod);
ethnl_update_bool(&cfg.tx_enabled, tb[ETHTOOL_A_MM_TX_ENABLED], &mod);
ethnl_update_bool(&cfg.pmac_enabled, tb[ETHTOOL_A_MM_PMAC_ENABLED],
&mod);
ethnl_update_u32(&cfg.tx_min_frag_size,
tb[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE], &mod);
if (!mod)
goto out_complete;
if (cfg.verify_time > state.max_verify_time) {
NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_MM_VERIFY_TIME],
"verifyTime exceeds device maximum");
ret = -ERANGE;
goto out_complete;
}
ret = ops->set_mm(dev, &cfg, extack);
if (ret)
goto out_complete;
ethtool_notify(dev, ETHTOOL_MSG_MM_NTF, NULL);
out_complete:
ethnl_ops_complete(dev);
out_rtnl_unlock:
rtnl_unlock();
out_dev_put:
ethnl_parse_header_dev_put(&req_info);
return ret;
}
/* Returns whether a given device supports the MAC merge layer
* (has an eMAC and a pMAC). Must be called under rtnl_lock() and
* ethnl_ops_begin().
*/
bool __ethtool_dev_mm_supported(struct net_device *dev)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
struct ethtool_mm_state state = {};
int ret = -EOPNOTSUPP;
if (ops && ops->get_mm)
ret = ops->get_mm(dev, &state);
return !!ret;
}
......@@ -290,6 +290,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_RSS_GET] = &ethnl_rss_request_ops,
[ETHTOOL_MSG_PLCA_GET_CFG] = &ethnl_plca_cfg_request_ops,
[ETHTOOL_MSG_PLCA_GET_STATUS] = &ethnl_plca_status_request_ops,
[ETHTOOL_MSG_MM_GET] = &ethnl_mm_request_ops,
};
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
......@@ -606,6 +607,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = {
[ETHTOOL_MSG_FEC_NTF] = &ethnl_fec_request_ops,
[ETHTOOL_MSG_MODULE_NTF] = &ethnl_module_request_ops,
[ETHTOOL_MSG_PLCA_NTF] = &ethnl_plca_cfg_request_ops,
[ETHTOOL_MSG_MM_NTF] = &ethnl_mm_request_ops,
};
/* default notification handler */
......@@ -700,6 +702,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = {
[ETHTOOL_MSG_FEC_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_MODULE_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_PLCA_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_MM_NTF] = ethnl_default_notify,
};
void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data)
......@@ -1076,6 +1079,22 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_plca_get_status_policy,
.maxattr = ARRAY_SIZE(ethnl_plca_get_status_policy) - 1,
},
{
.cmd = ETHTOOL_MSG_MM_GET,
.doit = ethnl_default_doit,
.start = ethnl_default_start,
.dumpit = ethnl_default_dumpit,
.done = ethnl_default_done,
.policy = ethnl_mm_get_policy,
.maxattr = ARRAY_SIZE(ethnl_mm_get_policy) - 1,
},
{
.cmd = ETHTOOL_MSG_MM_SET,
.flags = GENL_UNS_ADMIN_PERM,
.doit = ethnl_set_mm,
.policy = ethnl_mm_set_policy,
.maxattr = ARRAY_SIZE(ethnl_mm_set_policy) - 1,
},
};
static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
......
......@@ -349,6 +349,7 @@ extern const struct ethnl_request_ops ethnl_pse_request_ops;
extern const struct ethnl_request_ops ethnl_rss_request_ops;
extern const struct ethnl_request_ops ethnl_plca_cfg_request_ops;
extern const struct ethnl_request_ops ethnl_plca_status_request_ops;
extern const struct ethnl_request_ops ethnl_mm_request_ops;
extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
......@@ -372,7 +373,7 @@ extern const struct nla_policy ethnl_channels_get_policy[ETHTOOL_A_CHANNELS_HEAD
extern const struct nla_policy ethnl_channels_set_policy[ETHTOOL_A_CHANNELS_COMBINED_COUNT + 1];
extern const struct nla_policy ethnl_coalesce_get_policy[ETHTOOL_A_COALESCE_HEADER + 1];
extern const struct nla_policy ethnl_coalesce_set_policy[ETHTOOL_A_COALESCE_MAX + 1];
extern const struct nla_policy ethnl_pause_get_policy[ETHTOOL_A_PAUSE_HEADER + 1];
extern const struct nla_policy ethnl_pause_get_policy[ETHTOOL_A_PAUSE_STATS_SRC + 1];
extern const struct nla_policy ethnl_pause_set_policy[ETHTOOL_A_PAUSE_TX + 1];
extern const struct nla_policy ethnl_eee_get_policy[ETHTOOL_A_EEE_HEADER + 1];
extern const struct nla_policy ethnl_eee_set_policy[ETHTOOL_A_EEE_TX_LPI_TIMER + 1];
......@@ -383,7 +384,7 @@ extern const struct nla_policy ethnl_tunnel_info_get_policy[ETHTOOL_A_TUNNEL_INF
extern const struct nla_policy ethnl_fec_get_policy[ETHTOOL_A_FEC_HEADER + 1];
extern const struct nla_policy ethnl_fec_set_policy[ETHTOOL_A_FEC_AUTO + 1];
extern const struct nla_policy ethnl_module_eeprom_get_policy[ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS + 1];
extern const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1];
extern const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_SRC + 1];
extern const struct nla_policy ethnl_phc_vclocks_get_policy[ETHTOOL_A_PHC_VCLOCKS_HEADER + 1];
extern const struct nla_policy ethnl_module_get_policy[ETHTOOL_A_MODULE_HEADER + 1];
extern const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MODE_POLICY + 1];
......@@ -393,6 +394,8 @@ extern const struct nla_policy ethnl_rss_get_policy[ETHTOOL_A_RSS_CONTEXT + 1];
extern const struct nla_policy ethnl_plca_get_cfg_policy[ETHTOOL_A_PLCA_HEADER + 1];
extern const struct nla_policy ethnl_plca_set_cfg_policy[ETHTOOL_A_PLCA_MAX + 1];
extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1];
extern const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1];
extern const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1];
int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info);
......@@ -414,6 +417,7 @@ int ethnl_set_fec(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_module(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_pse(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_plca_cfg(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_mm(struct sk_buff *skb, struct genl_info *info);
extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN];
extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
......
......@@ -5,8 +5,12 @@
struct pause_req_info {
struct ethnl_req_info base;
enum ethtool_mac_stats_src src;
};
#define PAUSE_REQINFO(__req_base) \
container_of(__req_base, struct pause_req_info, base)
struct pause_reply_data {
struct ethnl_reply_data base;
struct ethtool_pauseparam pauseparam;
......@@ -19,13 +23,40 @@ struct pause_reply_data {
const struct nla_policy ethnl_pause_get_policy[] = {
[ETHTOOL_A_PAUSE_HEADER] =
NLA_POLICY_NESTED(ethnl_header_policy_stats),
[ETHTOOL_A_PAUSE_STATS_SRC] =
NLA_POLICY_MAX(NLA_U32, ETHTOOL_MAC_STATS_SRC_PMAC),
};
static int pause_parse_request(struct ethnl_req_info *req_base,
struct nlattr **tb,
struct netlink_ext_ack *extack)
{
enum ethtool_mac_stats_src src = ETHTOOL_MAC_STATS_SRC_AGGREGATE;
struct pause_req_info *req_info = PAUSE_REQINFO(req_base);
if (tb[ETHTOOL_A_PAUSE_STATS_SRC]) {
if (!(req_base->flags & ETHTOOL_FLAG_STATS)) {
NL_SET_ERR_MSG_MOD(extack,
"ETHTOOL_FLAG_STATS must be set when requesting a source of stats");
return -EINVAL;
}
src = nla_get_u32(tb[ETHTOOL_A_PAUSE_STATS_SRC]);
}
req_info->src = src;
return 0;
}
static int pause_prepare_data(const struct ethnl_req_info *req_base,
struct ethnl_reply_data *reply_base,
struct genl_info *info)
{
const struct pause_req_info *req_info = PAUSE_REQINFO(req_base);
struct pause_reply_data *data = PAUSE_REPDATA(reply_base);
enum ethtool_mac_stats_src src = req_info->src;
struct netlink_ext_ack *extack = info->extack;
struct net_device *dev = reply_base->dev;
int ret;
......@@ -34,14 +65,26 @@ static int pause_prepare_data(const struct ethnl_req_info *req_base,
ethtool_stats_init((u64 *)&data->pausestat,
sizeof(data->pausestat) / 8);
data->pausestat.src = src;
ret = ethnl_ops_begin(dev);
if (ret < 0)
return ret;
if ((src == ETHTOOL_MAC_STATS_SRC_EMAC ||
src == ETHTOOL_MAC_STATS_SRC_PMAC) &&
!__ethtool_dev_mm_supported(dev)) {
NL_SET_ERR_MSG_MOD(extack,
"Device does not support MAC merge layer");
ethnl_ops_complete(dev);
return -EOPNOTSUPP;
}
dev->ethtool_ops->get_pauseparam(dev, &data->pauseparam);
if (req_base->flags & ETHTOOL_FLAG_STATS &&
dev->ethtool_ops->get_pause_stats)
dev->ethtool_ops->get_pause_stats(dev, &data->pausestat);
ethnl_ops_complete(dev);
return 0;
......@@ -56,6 +99,7 @@ static int pause_reply_size(const struct ethnl_req_info *req_base,
if (req_base->flags & ETHTOOL_FLAG_STATS)
n += nla_total_size(0) + /* _PAUSE_STATS */
nla_total_size(sizeof(u32)) + /* _PAUSE_STATS_SRC */
nla_total_size_64bit(sizeof(u64)) * ETHTOOL_PAUSE_STAT_CNT;
return n;
}
......@@ -77,6 +121,9 @@ static int pause_put_stats(struct sk_buff *skb,
const u16 pad = ETHTOOL_A_PAUSE_STAT_PAD;
struct nlattr *nest;
if (nla_put_u32(skb, ETHTOOL_A_PAUSE_STATS_SRC, pause_stats->src))
return -EMSGSIZE;
nest = nla_nest_start(skb, ETHTOOL_A_PAUSE_STATS);
if (!nest)
return -EMSGSIZE;
......@@ -121,6 +168,7 @@ const struct ethnl_request_ops ethnl_pause_request_ops = {
.req_info_size = sizeof(struct pause_req_info),
.reply_data_size = sizeof(struct pause_reply_data),
.parse_request = pause_parse_request,
.prepare_data = pause_prepare_data,
.reply_size = pause_reply_size,
.fill_reply = pause_fill_reply,
......
......@@ -7,6 +7,7 @@
struct stats_req_info {
struct ethnl_req_info base;
DECLARE_BITMAP(stat_mask, __ETHTOOL_STATS_CNT);
enum ethtool_mac_stats_src src;
};
#define STATS_REQINFO(__req_base) \
......@@ -75,16 +76,19 @@ const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][ETH_GSTRING_LEN] = {
[ETHTOOL_A_STATS_RMON_JABBER] = "etherStatsJabbers",
};
const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1] = {
const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_SRC + 1] = {
[ETHTOOL_A_STATS_HEADER] =
NLA_POLICY_NESTED(ethnl_header_policy),
[ETHTOOL_A_STATS_GROUPS] = { .type = NLA_NESTED },
[ETHTOOL_A_STATS_SRC] =
NLA_POLICY_MAX(NLA_U32, ETHTOOL_MAC_STATS_SRC_PMAC),
};
static int stats_parse_request(struct ethnl_req_info *req_base,
struct nlattr **tb,
struct netlink_ext_ack *extack)
{
enum ethtool_mac_stats_src src = ETHTOOL_MAC_STATS_SRC_AGGREGATE;
struct stats_req_info *req_info = STATS_REQINFO(req_base);
bool mod = false;
int err;
......@@ -100,6 +104,11 @@ static int stats_parse_request(struct ethnl_req_info *req_base,
return -EINVAL;
}
if (tb[ETHTOOL_A_STATS_SRC])
src = nla_get_u32(tb[ETHTOOL_A_STATS_SRC]);
req_info->src = src;
return 0;
}
......@@ -109,6 +118,8 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
{
const struct stats_req_info *req_info = STATS_REQINFO(req_base);
struct stats_reply_data *data = STATS_REPDATA(reply_base);
enum ethtool_mac_stats_src src = req_info->src;
struct netlink_ext_ack *extack = info->extack;
struct net_device *dev = reply_base->dev;
int ret;
......@@ -116,11 +127,25 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
if (ret < 0)
return ret;
if ((src == ETHTOOL_MAC_STATS_SRC_EMAC ||
src == ETHTOOL_MAC_STATS_SRC_PMAC) &&
!__ethtool_dev_mm_supported(dev)) {
NL_SET_ERR_MSG_MOD(extack,
"Device does not support MAC merge layer");
ethnl_ops_complete(dev);
return -EOPNOTSUPP;
}
/* Mark all stats as unset (see ETHTOOL_STAT_NOT_SET) to prevent them
* from being reported to user space in case driver did not set them.
*/
memset(&data->stats, 0xff, sizeof(data->stats));
data->phy_stats.src = src;
data->mac_stats.src = src;
data->ctrl_stats.src = src;
data->rmon_stats.src = src;
if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) &&
dev->ethtool_ops->get_eth_phy_stats)
dev->ethtool_ops->get_eth_phy_stats(dev, &data->phy_stats);
......@@ -146,6 +171,8 @@ static int stats_reply_size(const struct ethnl_req_info *req_base,
unsigned int n_grps = 0, n_stats = 0;
int len = 0;
len += nla_total_size(sizeof(u32)); /* _STATS_SRC */
if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) {
n_stats += sizeof(struct ethtool_eth_phy_stats) / sizeof(u64);
n_grps++;
......@@ -379,6 +406,9 @@ static int stats_fill_reply(struct sk_buff *skb,
const struct stats_reply_data *data = STATS_REPDATA(reply_base);
int ret = 0;
if (nla_put_u32(skb, ETHTOOL_A_STATS_SRC, req_info->src))
return -EMSGSIZE;
if (!ret && test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask))
ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_PHY,
ETH_SS_STATS_ETH_PHY,
......@@ -410,3 +440,130 @@ const struct ethnl_request_ops ethnl_stats_request_ops = {
.reply_size = stats_reply_size,
.fill_reply = stats_fill_reply,
};
static u64 ethtool_stats_sum(u64 a, u64 b)
{
if (a == ETHTOOL_STAT_NOT_SET)
return b;
if (b == ETHTOOL_STAT_NOT_SET)
return a;
return a + b;
}
/* Avoid modifying the aggregation procedure every time a new counter is added
* by treating the structures as an array of u64 statistics.
*/
static void ethtool_aggregate_stats(void *aggr_stats, const void *emac_stats,
const void *pmac_stats, size_t stats_size,
size_t stats_offset)
{
size_t num_stats = stats_size / sizeof(u64);
const u64 *s1 = emac_stats + stats_offset;
const u64 *s2 = pmac_stats + stats_offset;
u64 *s = aggr_stats + stats_offset;
int i;
for (i = 0; i < num_stats; i++)
s[i] = ethtool_stats_sum(s1[i], s2[i]);
}
void ethtool_aggregate_mac_stats(struct net_device *dev,
struct ethtool_eth_mac_stats *mac_stats)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
struct ethtool_eth_mac_stats pmac, emac;
memset(&emac, 0xff, sizeof(emac));
memset(&pmac, 0xff, sizeof(pmac));
emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
ops->get_eth_mac_stats(dev, &emac);
ops->get_eth_mac_stats(dev, &pmac);
ethtool_aggregate_stats(mac_stats, &emac, &pmac,
sizeof(mac_stats->stats),
offsetof(struct ethtool_eth_mac_stats, stats));
}
EXPORT_SYMBOL(ethtool_aggregate_mac_stats);
void ethtool_aggregate_phy_stats(struct net_device *dev,
struct ethtool_eth_phy_stats *phy_stats)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
struct ethtool_eth_phy_stats pmac, emac;
memset(&emac, 0xff, sizeof(emac));
memset(&pmac, 0xff, sizeof(pmac));
emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
ops->get_eth_phy_stats(dev, &emac);
ops->get_eth_phy_stats(dev, &pmac);
ethtool_aggregate_stats(phy_stats, &emac, &pmac,
sizeof(phy_stats->stats),
offsetof(struct ethtool_eth_phy_stats, stats));
}
EXPORT_SYMBOL(ethtool_aggregate_phy_stats);
void ethtool_aggregate_ctrl_stats(struct net_device *dev,
struct ethtool_eth_ctrl_stats *ctrl_stats)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
struct ethtool_eth_ctrl_stats pmac, emac;
memset(&emac, 0xff, sizeof(emac));
memset(&pmac, 0xff, sizeof(pmac));
emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
ops->get_eth_ctrl_stats(dev, &emac);
ops->get_eth_ctrl_stats(dev, &pmac);
ethtool_aggregate_stats(ctrl_stats, &emac, &pmac,
sizeof(ctrl_stats->stats),
offsetof(struct ethtool_eth_ctrl_stats, stats));
}
EXPORT_SYMBOL(ethtool_aggregate_ctrl_stats);
void ethtool_aggregate_pause_stats(struct net_device *dev,
struct ethtool_pause_stats *pause_stats)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
struct ethtool_pause_stats pmac, emac;
memset(&emac, 0xff, sizeof(emac));
memset(&pmac, 0xff, sizeof(pmac));
emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
ops->get_pause_stats(dev, &emac);
ops->get_pause_stats(dev, &pmac);
ethtool_aggregate_stats(pause_stats, &emac, &pmac,
sizeof(pause_stats->stats),
offsetof(struct ethtool_pause_stats, stats));
}
EXPORT_SYMBOL(ethtool_aggregate_pause_stats);
void ethtool_aggregate_rmon_stats(struct net_device *dev,
struct ethtool_rmon_stats *rmon_stats)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
const struct ethtool_rmon_hist_range *dummy;
struct ethtool_rmon_stats pmac, emac;
memset(&emac, 0xff, sizeof(emac));
memset(&pmac, 0xff, sizeof(pmac));
emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
ops->get_rmon_stats(dev, &emac, &dummy);
ops->get_rmon_stats(dev, &pmac, &dummy);
ethtool_aggregate_stats(rmon_stats, &emac, &pmac,
sizeof(rmon_stats->stats),
offsetof(struct ethtool_rmon_stats, stats));
}
EXPORT_SYMBOL(ethtool_aggregate_rmon_stats);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment