This is a continuation of my work started here:
https://patchwork.kernel.org/project/netdevbpf/cover/[email protected]/
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.
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/[email protected]/
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.
Vladimir Oltean (12):
net: ethtool: netlink: introduce ethnl_update_bool()
net: ethtool: add support for MAC Merge layer
docs: ethtool-netlink: document interface for MAC Merge layer
net: ethtool: netlink: retrieve stats from multiple sources (eMAC,
pMAC)
docs: ethtool: document ETHTOOL_A_STATS_SRC and
ETHTOOL_A_PAUSE_STATS_SRC
net: ethtool: add helpers for aggregate statistics
net: ethtool: add helpers for MM addFragSize translation
net: dsa: add plumbing for changing and getting MAC merge layer state
net: mscc: ocelot: allow ocelot_stat_layout elements with no name
net: mscc: ocelot: hide access to ocelot_stats_layout behind a helper
net: mscc: ocelot: export ethtool MAC Merge stats for Felix VSC9959
net: mscc: ocelot: add MAC Merge layer support for VSC9959
Documentation/networking/ethtool-netlink.rst | 123 +++++++
Documentation/networking/statistics.rst | 1 +
drivers/net/dsa/ocelot/felix.c | 28 ++
drivers/net/dsa/ocelot/felix_vsc9959.c | 57 +++-
drivers/net/ethernet/mscc/Makefile | 1 +
drivers/net/ethernet/mscc/ocelot.c | 18 +-
drivers/net/ethernet/mscc/ocelot.h | 2 +
drivers/net/ethernet/mscc/ocelot_mm.c | 213 ++++++++++++
drivers/net/ethernet/mscc/ocelot_stats.c | 331 +++++++++++++++++--
include/linux/ethtool.h | 245 +++++++++++---
include/net/dsa.h | 11 +
include/soc/mscc/ocelot.h | 58 ++++
include/soc/mscc/ocelot_dev.h | 23 ++
include/uapi/linux/ethtool.h | 43 +++
include/uapi/linux/ethtool_netlink.h | 50 +++
net/dsa/slave.c | 35 ++
net/ethtool/Makefile | 4 +-
net/ethtool/common.h | 2 +
net/ethtool/mm.c | 273 +++++++++++++++
net/ethtool/netlink.c | 19 ++
net/ethtool/netlink.h | 34 +-
net/ethtool/pause.c | 47 +++
net/ethtool/stats.c | 158 ++++++++-
23 files changed, 1697 insertions(+), 79 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_mm.c
create mode 100644 net/ethtool/mm.c
--
2.34.1
Show details about the structures passed back and forth related to MAC
Merge layer configuration, state and statistics. The rendered htmldocs
will be much more verbose due to the kerneldoc references.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v2: patch is new
Documentation/networking/ethtool-netlink.rst | 103 +++++++++++++++++++
Documentation/networking/statistics.rst | 1 +
2 files changed, 104 insertions(+)
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index f10f8eb44255..490c2280ce4f 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -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
@@ -1716,6 +1719,104 @@ being used. Current supported options are toeplitz, xor or crc32.
ETHTOOL_A_RSS_INDIR attribute returns RSS indrection table where each byte
indicates queue number.
+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_SUPPORTED`` Bool set if device supports the MM layer
+
+ ``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_ADD_FRAG_SIZE`` U32 minimum size of transmitted
+ non-final fragments, in octets
+
+ ``ETHTOOL_A_MM_VERIFY_ENABLED`` Bool set if TX of SMD-V frames is
+ administratively enabled (TX will
+ not take place when port is not up)
+
+ ``ETHTOOL_A_MM_VERIFY_STATUS`` U8 state of the Verify function
+
+ ``ETHTOOL_A_MM_VERIFY_TIME`` U32 delay between verification attempts
+
+ ``ETHTOOL_A_MM_MAX_VERIFY_TIME`` U32 maximum interval supported by
+ device
+
+ ``ETHTOOL_A_MM_STATS`` Nested IEEE 802.3-2018 subclause 30.14.1
+ oMACMergeEntity statistics counters
+
+ ================================ ====== ===================================
+
+If ``ETHTOOL_A_MM_SUPPORTED`` is reported as false, the other netlink
+attributes will be absent.
+
+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_ADD_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
===================
@@ -1817,4 +1918,6 @@ are netlink only.
n/a ``ETHTOOL_MSG_PHC_VCLOCKS_GET``
n/a ``ETHTOOL_MSG_MODULE_GET``
n/a ``ETHTOOL_MSG_MODULE_SET``
+ n/a ``ETHTOOL_MSG_MM_GET``
+ n/a ``ETHTOOL_MSG_MM_SET``
=================================== =====================================
diff --git a/Documentation/networking/statistics.rst b/Documentation/networking/statistics.rst
index c9aeb70dafa2..551b3cc29a41 100644
--- a/Documentation/networking/statistics.rst
+++ b/Documentation/networking/statistics.rst
@@ -171,6 +171,7 @@ statistics are supported in the following commands:
- `ETHTOOL_MSG_PAUSE_GET`
- `ETHTOOL_MSG_FEC_GET`
+ - `ETHTOOL_MSG_MM_GET`
debugfs
-------
--
2.34.1
Some hardware instances of the ocelot driver support the MAC Merge
layer, which gives access to an extra preemptible MAC. This has
implications upon the statistics. There will be a stats layout when MM
isn't supported, and a different one when it is.
The ocelot_stats_layout() helper will return the correct one.
In preparation of that, refactor the existing code to use this helper.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v2: patch is new (v1 was written for enetc)
drivers/net/ethernet/mscc/ocelot_stats.c | 40 +++++++++++++++++-------
1 file changed, 29 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/mscc/ocelot_stats.c b/drivers/net/ethernet/mscc/ocelot_stats.c
index 01306172b7f7..551e3cbfae79 100644
--- a/drivers/net/ethernet/mscc/ocelot_stats.c
+++ b/drivers/net/ethernet/mscc/ocelot_stats.c
@@ -228,6 +228,12 @@ static const struct ocelot_stat_layout ocelot_stats_layout[OCELOT_NUM_STATS] = {
OCELOT_COMMON_STATS,
};
+static const struct ocelot_stat_layout *
+ocelot_get_stats_layout(struct ocelot *ocelot)
+{
+ return ocelot_stats_layout;
+}
+
/* Read the counters from hardware and keep them in region->buf.
* Caller must hold &ocelot->stat_view_lock.
*/
@@ -306,16 +312,19 @@ static void ocelot_check_stats_work(struct work_struct *work)
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
{
+ const struct ocelot_stat_layout *layout;
int i;
if (sset != ETH_SS_STATS)
return;
+ layout = ocelot_get_stats_layout(ocelot);
+
for (i = 0; i < OCELOT_NUM_STATS; i++) {
- if (ocelot_stats_layout[i].name[0] == '\0')
+ if (layout[i].name[0] == '\0')
continue;
- memcpy(data, ocelot_stats_layout[i].name, ETH_GSTRING_LEN);
+ memcpy(data, layout[i].name, ETH_GSTRING_LEN);
data += ETH_GSTRING_LEN;
}
}
@@ -350,13 +359,16 @@ static void ocelot_port_stats_run(struct ocelot *ocelot, int port, void *priv,
int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
{
+ const struct ocelot_stat_layout *layout;
int i, num_stats = 0;
if (sset != ETH_SS_STATS)
return -EOPNOTSUPP;
+ layout = ocelot_get_stats_layout(ocelot);
+
for (i = 0; i < OCELOT_NUM_STATS; i++)
- if (ocelot_stats_layout[i].name[0] != '\0')
+ if (layout[i].name[0] != '\0')
num_stats++;
return num_stats;
@@ -366,14 +378,17 @@ EXPORT_SYMBOL(ocelot_get_sset_count);
static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port,
void *priv)
{
+ const struct ocelot_stat_layout *layout;
u64 *data = priv;
int i;
+ layout = ocelot_get_stats_layout(ocelot);
+
/* Copy all supported counters */
for (i = 0; i < OCELOT_NUM_STATS; i++) {
int index = port * OCELOT_NUM_STATS + i;
- if (ocelot_stats_layout[i].name[0] == '\0')
+ if (layout[i].name[0] == '\0')
continue;
*data++ = ocelot->stats[index];
@@ -602,16 +617,19 @@ EXPORT_SYMBOL(ocelot_port_get_stats64);
static int ocelot_prepare_stats_regions(struct ocelot *ocelot)
{
struct ocelot_stats_region *region = NULL;
+ const struct ocelot_stat_layout *layout;
unsigned int last = 0;
int i;
INIT_LIST_HEAD(&ocelot->stats_regions);
+ layout = ocelot_get_stats_layout(ocelot);
+
for (i = 0; i < OCELOT_NUM_STATS; i++) {
- if (!ocelot_stats_layout[i].reg)
+ if (!layout[i].reg)
continue;
- if (region && ocelot_stats_layout[i].reg == last + 4) {
+ if (region && layout[i].reg == last + 4) {
region->count++;
} else {
region = devm_kzalloc(ocelot->dev, sizeof(*region),
@@ -620,17 +638,17 @@ static int ocelot_prepare_stats_regions(struct ocelot *ocelot)
return -ENOMEM;
/* enum ocelot_stat must be kept sorted in the same
- * order as ocelot_stats_layout[i].reg in order to have
- * efficient bulking
+ * order as layout[i].reg in order to have efficient
+ * bulking
*/
- WARN_ON(last >= ocelot_stats_layout[i].reg);
+ WARN_ON(last >= layout[i].reg);
- region->base = ocelot_stats_layout[i].reg;
+ region->base = layout[i].reg;
region->count = 1;
list_add_tail(®ion->node, &ocelot->stats_regions);
}
- last = ocelot_stats_layout[i].reg;
+ last = layout[i].reg;
}
list_for_each_entry(region, &ocelot->stats_regions, node) {
--
2.34.1
We will add support for pMAC counters and MAC merge layer counters,
which are only reported via the structured stats, and the current
ocelot_get_strings() stands in our way, because it expects that the
statistics should be placed in the data array at the same index as found
in the ocelot_stats_layout array.
That is not true. Statistics which don't have a name should not be
exported to the unstructured ethtool -S, so we need to have different
indices into the ocelot_stats_layout array (i) and into the data array
(data itself).
Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v2: patch is new (v1 was written for enetc)
drivers/net/ethernet/mscc/ocelot_stats.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/mscc/ocelot_stats.c b/drivers/net/ethernet/mscc/ocelot_stats.c
index 1478c3b21af1..01306172b7f7 100644
--- a/drivers/net/ethernet/mscc/ocelot_stats.c
+++ b/drivers/net/ethernet/mscc/ocelot_stats.c
@@ -315,8 +315,8 @@ void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
if (ocelot_stats_layout[i].name[0] == '\0')
continue;
- memcpy(data + i * ETH_GSTRING_LEN, ocelot_stats_layout[i].name,
- ETH_GSTRING_LEN);
+ memcpy(data, ocelot_stats_layout[i].name, ETH_GSTRING_LEN);
+ data += ETH_GSTRING_LEN;
}
}
EXPORT_SYMBOL(ocelot_get_strings);
--
2.34.1
The MAC merge sublayer (IEEE 802.3-2018 clause 99) is one of 2
specifications (the other being Frame Preemption; IEEE 802.1Q-2018
clause 6.7.2), which work together to minimize latency caused by frame
interference at TX. The overall goal of TSN is for normal traffic and
traffic with a bounded deadline to be able to cohabitate on the same L2
network and not bother each other too much.
The standards achieve this (partly) by introducing the concept of
preemptible traffic, i.e. Ethernet frames that have a custom value for
the Start-of-Frame-Delimiter (SFD), and these frames can be fragmented
and reassembled at L2 on a link-local basis. The non-preemptible frames
are called express traffic, they are transmitted using a normal SFD, and
they can preempt preemptible frames, therefore having lower latency,
which can matter at lower (100 Mbps) link speeds, or at high MTUs (jumbo
frames around 9K). Preemption is not recursive, i.e. a P frame cannot
preempt another P frame. Preemption also does not depend upon priority,
or otherwise said, an E frame with prio 0 will still preempt a P frame
with prio 7.
In terms of implementation, the standards talk about the presence of an
express MAC (eMAC) which handles express traffic, and a preemptible MAC
(pMAC) which handles preemptible traffic, and these MACs are multiplexed
on the same MII by a MAC merge layer.
To support frame preemption, the definition of the SFD was generalized
to SMD (Start-of-mPacket-Delimiter), where an mPacket is essentially an
Ethernet frame fragment, or a complete frame. Stations unaware of an SMD
value different from the standard SFD will treat P frames as error
frames. To prevent that from happening, a negotiation process is
defined.
On RX, packets are dispatched to the eMAC or pMAC after being filtered
by their SMD. On TX, the eMAC/pMAC classification decision is taken by
the 802.1Q spec, based on packet priority (each of the 8 user priority
values may have an admin-status of preemptible or express).
The MAC Merge layer and the Frame Preemption parameters have some degree
of independence in terms of how software stacks are supposed to deal
with them. The activation of the MM layer is supposed to be controlled
by an LLDP daemon (after it has been communicated that the link partner
also supports it), after which a (hardware-based or not) verification
handshake takes place, before actually enabling the feature. So the
process is intended to be relatively plug-and-play. Whereas FP settings
are supposed to be coordinated across a network using something
approximating NETCONF.
The support contained here is exclusively for the 802.3 (MAC Merge)
portions and not for the 802.1Q (Frame Preemption) parts. This API is
sufficient for an LLDP daemon to do its job. The FP adminStatus variable
from 802.1Q is outside the scope of an LLDP daemon.
I have taken a few creative licenses and augmented the Linux kernel UAPI
compared to the standard managed objects recommended by IEEE 802.3.
These are:
- ETHTOOL_A_MM_PMAC_ENABLED: According to Figure 99-6: Receive
Processing state diagram, a MAC Merge layer is always supposed to be
able to receive P frames. However, this implies keeping the pMAC
powered on, which will consume needless power in applications where FP
will never be used. If LLDP is used, the reception of an Additional
Ethernet Capabilities TLV from the link partner is sufficient
indication that the pMAC should be enabled. So my proposal is that in
Linux, we keep the pMAC turned off by default and that user space
turns it on when needed.
- ETHTOOL_A_MM_VERIFY_ENABLED: The IEEE managed object is called
aMACMergeVerifyDisableTx. I opted for consistency (positive logic) in
the boolean netlink attributes offered, so this is also positive here.
Other than the meaning being reversed, they correspond to the same
thing.
- ETHTOOL_A_MM_MAX_VERIFY_TIME: I found it most reasonable for a LLDP
daemon to maximize the verifyTime variable (delay between SMD-V
transmissions), to maximize its chances that the LP replies. IEEE says
that the verifyTime can range between 1 and 128 ms, but the NXP ENETC
stupidly keeps this variable in a 7 bit register, so the maximum
supported value is 127 ms. I could have chosen to hardcode this in the
LLDP daemon to a lower value, but why not let the kernel expose its
supported range directly.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v2:
- added documentation
- introduced pmac_enabled
- transformed verify_disable into verify_enabled
- made add_frag_size take a value in octets
- removed FP params (adminStatus)
- renamed "enabled" to "tx_enabled" and "active" to "tx_active"
include/linux/ethtool.h | 99 ++++++++++
include/uapi/linux/ethtool.h | 25 +++
include/uapi/linux/ethtool_netlink.h | 47 +++++
net/ethtool/Makefile | 4 +-
net/ethtool/mm.c | 258 +++++++++++++++++++++++++++
net/ethtool/netlink.c | 19 ++
net/ethtool/netlink.h | 4 +
7 files changed, 454 insertions(+), 2 deletions(-)
create mode 100644 net/ethtool/mm.c
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 9e0a76fc7de9..6336f105e667 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -467,6 +467,98 @@ struct ethtool_module_power_mode_params {
enum ethtool_module_power_mode mode;
};
+/**
+ * struct ethtool_mm_state - 802.3 MAC merge layer state
+ * @verify_time:
+ * wait time between verification attempts in ms (according to clause
+ * 30.14.1.6 aMACMergeVerifyTime)
+ * @max_verify_time:
+ * maximum accepted value for the @verify_time variable in set requests
+ * @verify_status:
+ * state of the verification state machine of the MM layer (according to
+ * clause 30.14.1.2 aMACMergeStatusVerify)
+ * @supported:
+ * set if device supports the MM layer (according to clause 30.14.1.1
+ * aMACMergeSupport)
+ * @tx_enabled:
+ * set if the MM layer is administratively enabled in the TX direction
+ * (according to clause 30.14.1.3 aMACMergeEnableTx)
+ * @tx_active:
+ * set if the MM layer is enabled in the TX direction, which makes FP
+ * possible (according to 30.14.1.5 aMACMergeStatusTx). This should be
+ * true if MM is enabled, and the verification status is either verified,
+ * or disabled.
+ * @pmac_enabled:
+ * set if the preemptible MAC is powered on and is able to receive
+ * preemptible packets and respond to verification frames.
+ * @verify_enabled:
+ * set if the Verify function of the MM layer (which sends SMD-V
+ * verification requests) is administratively enabled (regardless of
+ * whether it is currently in the ETHTOOL_MM_VERIFY_STATUS_DISABLED state
+ * or not), according to clause 30.14.1.4 aMACMergeVerifyDisableTx (but
+ * using positive rather than negative logic). The device should always
+ * respond to received SMD-V requests as long as @pmac_enabled is set.
+ * @add_frag_size:
+ * the minimum size of non-final mPacket fragments that the link partner
+ * supports receiving, expressed in octets. Compared to the definition
+ * from clause 30.14.1.7 aMACMergeAddFragSize which is expressed in the
+ * range 0 to 3 (requiring a translation to the size in octets according
+ * to the formula 64 * (1 + addFragSize) – 4), a value in a continuous and
+ * unbounded range can be specified here.
+ */
+struct ethtool_mm_state {
+ u32 verify_time;
+ u32 max_verify_time;
+ enum ethtool_mm_verify_status verify_status;
+ bool supported;
+ bool tx_enabled;
+ bool tx_active;
+ bool pmac_enabled;
+ bool verify_enabled;
+ u32 add_frag_size;
+};
+
+/**
+ * struct ethtool_mm_cfg - 802.3 MAC merge layer configuration
+ * @verify_time: see struct ethtool_mm_state
+ * @verify_enabled: see struct ethtool_mm_state
+ * @tx_enabled: see struct ethtool_mm_state
+ * @pmac_enabled: see struct ethtool_mm_state
+ * @add_frag_size: see struct ethtool_mm_state
+ */
+struct ethtool_mm_cfg {
+ u32 verify_time;
+ bool verify_enabled;
+ bool tx_enabled;
+ bool pmac_enabled;
+ u32 add_frag_size;
+};
+
+/**
+ * struct ethtool_mm_stats - 802.3 MAC merge layer statistics
+ * @MACMergeFrameAssErrorCount:
+ * received MAC frames with reassembly errors
+ * @MACMergeFrameSmdErrorCount:
+ * received MAC frames/fragments rejected due to unknown or incorrect SMD
+ * @MACMergeFrameAssOkCount:
+ * received MAC frames that were successfully reassembled and passed up
+ * @MACMergeFragCountRx:
+ * number of additional correct SMD-C mPackets received due to preemption
+ * @MACMergeFragCountTx:
+ * number of additional mPackets sent due to preemption
+ * @MACMergeHoldCount:
+ * number of times the MM layer entered the HOLD state, which blocks
+ * transmission of preemptible traffic
+ */
+struct ethtool_mm_stats {
+ u64 MACMergeFrameAssErrorCount;
+ u64 MACMergeFrameSmdErrorCount;
+ u64 MACMergeFrameAssOkCount;
+ u64 MACMergeFragCountRx;
+ u64 MACMergeFragCountTx;
+ u64 MACMergeHoldCount;
+};
+
/**
* struct ethtool_ops - optional netdev operations
* @cap_link_lanes_supported: indicates if the driver supports lanes
@@ -639,6 +731,9 @@ struct ethtool_module_power_mode_params {
* plugged-in.
* @set_module_power_mode: Set the power mode policy for the plug-in module
* used by the network device.
+ * @get_mm: Query the 802.3 MAC Merge layer state.
+ * @set_mm: Set the 802.3 MAC Merge layer parameters.
+ * @get_mm_stats: Query the 802.3 MAC Merge layer statistics.
*
* All operations are optional (i.e. the function pointer may be set
* to %NULL) and callers must take this into account. Callers must
@@ -777,6 +872,10 @@ struct ethtool_ops {
int (*set_module_power_mode)(struct net_device *dev,
const struct ethtool_module_power_mode_params *params,
struct netlink_ext_ack *extack);
+ void (*get_mm)(struct net_device *dev, struct ethtool_mm_state *state);
+ int (*set_mm)(struct net_device *dev, struct ethtool_mm_cfg *cfg,
+ struct netlink_ext_ack *extack);
+ void (*get_mm_stats)(struct net_device *dev, struct ethtool_mm_stats *stats);
};
int ethtool_check_ops(const struct ethtool_ops *ops);
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 3135fa0ba9a4..7ddc47a3fb32 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -779,6 +779,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
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 5799a9db034e..e84a80957138 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -52,6 +52,8 @@ enum {
ETHTOOL_MSG_PSE_GET,
ETHTOOL_MSG_PSE_SET,
ETHTOOL_MSG_RSS_GET,
+ ETHTOOL_MSG_MM_GET,
+ ETHTOOL_MSG_MM_SET,
/* add new constants above here */
__ETHTOOL_MSG_USER_CNT,
@@ -99,6 +101,8 @@ enum {
ETHTOOL_MSG_MODULE_NTF,
ETHTOOL_MSG_PSE_GET_REPLY,
ETHTOOL_MSG_RSS_GET_REPLY,
+ ETHTOOL_MSG_MM_GET_REPLY,
+ ETHTOOL_MSG_MM_NTF,
/* add new constants above here */
__ETHTOOL_MSG_KERNEL_CNT,
@@ -894,6 +898,49 @@ enum {
ETHTOOL_A_RSS_MAX = (__ETHTOOL_A_RSS_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_SUPPORTED, /* u8 */
+ ETHTOOL_A_MM_PMAC_ENABLED, /* u8 */
+ ETHTOOL_A_MM_TX_ENABLED, /* u8 */
+ ETHTOOL_A_MM_TX_ACTIVE, /* u8 */
+ ETHTOOL_A_MM_ADD_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
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 228f13df2e18..ab824b2d3b7d 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -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
+ tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \
+ module.o pse-pd.o
diff --git a/net/ethtool/mm.c b/net/ethtool/mm.c
new file mode 100644
index 000000000000..01a2acc40046
--- /dev/null
+++ b/net/ethtool/mm.c
@@ -0,0 +1,258 @@
+// 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;
+
+ ops->get_mm(dev, &data->state);
+
+ if (ops->get_mm_stats && (req_base->flags & ETHTOOL_FLAG_STATS))
+ ops->get_mm_stats(dev, &data->stats);
+
+ 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)
+{
+ struct mm_reply_data *data = MM_REPDATA(reply_base);
+ const struct ethtool_mm_state *state = &data->state;
+ int len = nla_total_size(sizeof(u8)); /* _MM_SUPPORTED */
+
+ if (!state->supported)
+ return len;
+
+ 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_ADD_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_SUPPORTED, state->supported))
+ return -EMSGSIZE;
+
+ if (!state->supported)
+ return 0;
+
+ 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_ADD_FRAG_SIZE, state->add_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_ADD_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->add_frag_size = state->add_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;
+ }
+
+ rtnl_lock();
+ ret = ethnl_ops_begin(dev);
+ if (ret)
+ goto out_rtnl;
+
+ ops->get_mm(dev, &state);
+
+ mm_state_to_cfg(&state, &cfg);
+
+ if (cfg.verify_time > state.max_verify_time) {
+ NL_SET_ERR_MSG_MOD(extack, "verifyTime exceeds device maximum");
+ return -ERANGE;
+ }
+
+ 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.add_frag_size, tb[ETHTOOL_A_MM_ADD_FRAG_SIZE],
+ &mod);
+
+ ret = ops->set_mm(dev, &cfg, extack);
+ if (ret) {
+ if (!extack->_msg)
+ NL_SET_ERR_MSG(extack,
+ "Failed to update MAC merge configuration");
+ goto out_ops;
+ }
+
+ ethtool_notify(dev, ETHTOOL_MSG_MM_NTF, NULL);
+
+out_ops:
+ ethnl_ops_complete(dev);
+out_rtnl:
+ rtnl_unlock();
+out_dev:
+ dev_put(dev);
+ return ret;
+}
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index aee98be6237f..a8c5b2521c46 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -288,6 +288,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_MODULE_GET] = ðnl_module_request_ops,
[ETHTOOL_MSG_PSE_GET] = ðnl_pse_request_ops,
[ETHTOOL_MSG_RSS_GET] = ðnl_rss_request_ops,
+ [ETHTOOL_MSG_MM_GET] = ðnl_mm_request_ops,
};
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@@ -603,6 +604,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = {
[ETHTOOL_MSG_EEE_NTF] = ðnl_eee_request_ops,
[ETHTOOL_MSG_FEC_NTF] = ðnl_fec_request_ops,
[ETHTOOL_MSG_MODULE_NTF] = ðnl_module_request_ops,
+ [ETHTOOL_MSG_MM_NTF] = ðnl_mm_request_ops,
};
/* default notification handler */
@@ -696,6 +698,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = {
[ETHTOOL_MSG_EEE_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_FEC_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_MODULE_NTF] = ethnl_default_notify,
+ [ETHTOOL_MSG_MM_NTF] = ethnl_default_notify,
};
void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data)
@@ -1047,6 +1050,22 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_rss_get_policy,
.maxattr = ARRAY_SIZE(ethnl_rss_get_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[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 744b3ab966b0..a8012dbe39bb 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -373,6 +373,7 @@ extern const struct ethnl_request_ops ethnl_phc_vclocks_request_ops;
extern const struct ethnl_request_ops ethnl_module_request_ops;
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_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];
@@ -414,6 +415,8 @@ extern const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MO
extern const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1];
extern const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1];
extern const struct nla_policy ethnl_rss_get_policy[ETHTOOL_A_RSS_CONTEXT + 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);
@@ -434,6 +437,7 @@ int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
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_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];
--
2.34.1
The DSA core is in charge of the ethtool_ops of the net devices
associated with switch ports, so in case a hardware driver supports the
MAC merge layer, DSA must pass the callbacks through to the driver.
Add support for precisely that.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v2: patch is new
include/net/dsa.h | 11 +++++++++++
net/dsa/slave.c | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 46 insertions(+)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 96086289aa9b..51b0dc0d0bc1 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -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
+ */
+ void (*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
*/
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index aab79c355224..b6ed0f40a0ef 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1117,6 +1117,38 @@ static void dsa_slave_net_selftest(struct net_device *ndev,
net_selftest(ndev, etest, buf);
}
+static void 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)
+ 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 +2237,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 = {
--
2.34.1
When a pMAC exists but the driver is unable to atomically query the
aggregate eMAC+pMAC statistics, the user should be given back at least
the sum of eMAC and pMAC counters queried separately.
This is a generic problem, so add helpers in ethtool to do this
operation, if the driver doesn't have a better way to report aggregate
stats. Do this in a way that does not require changes to these functions
when new stats are added (basically treat the structures as an array of
u64 values, except for the first element which is the stats source).
In include/linux/ethtool.h, there is already a section where helper
function prototypes should be placed. The trouble is, this section is
too early, before the definitions of struct ethtool_eth_mac_stats et.al.
Move that section at the end and append these new helpers to it.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v2: patch is new
include/linux/ethtool.h | 100 ++++++++++++++++++-------------
net/ethtool/stats.c | 127 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 187 insertions(+), 40 deletions(-)
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 06672b76b2c6..01b1e34dc30e 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -106,11 +106,6 @@ enum ethtool_supported_ring_param {
struct net_device;
struct netlink_ext_ack;
-/* Some generic methods drivers may use in their ethtool_ops */
-u32 ethtool_op_get_link(struct net_device *dev);
-int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *eti);
-
-
/* Link extended state and substate. */
struct ethtool_link_ext_state_info {
enum ethtool_link_ext_state link_ext_state;
@@ -302,28 +297,30 @@ static inline void ethtool_stats_init(u64 *stats, unsigned int n)
*/
struct ethtool_eth_mac_stats {
enum ethtool_stats_src src;
- u64 FramesTransmittedOK;
- u64 SingleCollisionFrames;
- u64 MultipleCollisionFrames;
- u64 FramesReceivedOK;
- u64 FrameCheckSequenceErrors;
- u64 AlignmentErrors;
- u64 OctetsTransmittedOK;
- u64 FramesWithDeferredXmissions;
- u64 LateCollisions;
- u64 FramesAbortedDueToXSColls;
- u64 FramesLostDueToIntMACXmitError;
- u64 CarrierSenseErrors;
- u64 OctetsReceivedOK;
- u64 FramesLostDueToIntMACRcvError;
- u64 MulticastFramesXmittedOK;
- u64 BroadcastFramesXmittedOK;
- u64 FramesWithExcessiveDeferral;
- u64 MulticastFramesReceivedOK;
- u64 BroadcastFramesReceivedOK;
- u64 InRangeLengthErrors;
- u64 OutOfRangeLengthField;
- u64 FrameTooLongErrors;
+ struct_group(stats,
+ u64 FramesTransmittedOK;
+ u64 SingleCollisionFrames;
+ u64 MultipleCollisionFrames;
+ u64 FramesReceivedOK;
+ u64 FrameCheckSequenceErrors;
+ u64 AlignmentErrors;
+ u64 OctetsTransmittedOK;
+ u64 FramesWithDeferredXmissions;
+ u64 LateCollisions;
+ u64 FramesAbortedDueToXSColls;
+ u64 FramesLostDueToIntMACXmitError;
+ u64 CarrierSenseErrors;
+ u64 OctetsReceivedOK;
+ u64 FramesLostDueToIntMACRcvError;
+ u64 MulticastFramesXmittedOK;
+ u64 BroadcastFramesXmittedOK;
+ u64 FramesWithExcessiveDeferral;
+ u64 MulticastFramesReceivedOK;
+ u64 BroadcastFramesReceivedOK;
+ u64 InRangeLengthErrors;
+ u64 OutOfRangeLengthField;
+ u64 FrameTooLongErrors;
+ );
};
/* Basic IEEE 802.3 PHY statistics (30.3.2.1.*), not otherwise exposed
@@ -331,7 +328,9 @@ struct ethtool_eth_mac_stats {
*/
struct ethtool_eth_phy_stats {
enum ethtool_stats_src src;
- u64 SymbolErrorDuringCarrier;
+ struct_group(stats,
+ u64 SymbolErrorDuringCarrier;
+ );
};
/* Basic IEEE 802.3 MAC Ctrl statistics (30.3.3.*), not otherwise exposed
@@ -339,9 +338,11 @@ struct ethtool_eth_phy_stats {
*/
struct ethtool_eth_ctrl_stats {
enum ethtool_stats_src src;
- u64 MACControlFramesTransmitted;
- u64 MACControlFramesReceived;
- u64 UnsupportedOpcodesReceived;
+ struct_group(stats,
+ u64 MACControlFramesTransmitted;
+ u64 MACControlFramesReceived;
+ u64 UnsupportedOpcodesReceived;
+ );
};
/**
@@ -362,8 +363,10 @@ struct ethtool_eth_ctrl_stats {
*/
struct ethtool_pause_stats {
enum ethtool_stats_src src;
- u64 tx_pause_frames;
- u64 rx_pause_frames;
+ struct_group(stats,
+ u64 tx_pause_frames;
+ u64 rx_pause_frames;
+ );
};
#define ETHTOOL_MAX_LANES 8
@@ -431,13 +434,15 @@ struct ethtool_rmon_hist_range {
*/
struct ethtool_rmon_stats {
enum ethtool_stats_src src;
- u64 undersize_pkts;
- u64 oversize_pkts;
- u64 fragments;
- u64 jabbers;
-
- u64 hist[ETHTOOL_RMON_HIST_MAX];
- u64 hist_tx[ETHTOOL_RMON_HIST_MAX];
+ struct_group(stats,
+ u64 undersize_pkts;
+ u64 oversize_pkts;
+ u64 fragments;
+ u64 jabbers;
+
+ u64 hist[ETHTOOL_RMON_HIST_MAX];
+ u64 hist_tx[ETHTOOL_RMON_HIST_MAX];
+ );
};
#define ETH_MODULE_EEPROM_PAGE_LEN 128
@@ -959,6 +964,21 @@ ethtool_params_from_link_mode(struct ethtool_link_ksettings *link_ksettings,
*/
int ethtool_get_phc_vclocks(struct net_device *dev, int **vclock_index);
+/* Some generic methods drivers may use in their ethtool_ops */
+u32 ethtool_op_get_link(struct net_device *dev);
+int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *eti);
+
+void ethtool_aggregate_mac_stats(struct net_device *dev,
+ struct ethtool_eth_mac_stats *mac_stats);
+void ethtool_aggregate_phy_stats(struct net_device *dev,
+ struct ethtool_eth_phy_stats *phy_stats);
+void ethtool_aggregate_ctrl_stats(struct net_device *dev,
+ struct ethtool_eth_ctrl_stats *ctrl_stats);
+void ethtool_aggregate_pause_stats(struct net_device *dev,
+ struct ethtool_pause_stats *pause_stats);
+void ethtool_aggregate_rmon_stats(struct net_device *dev,
+ struct ethtool_rmon_stats *rmon_stats);
+
/**
* ethtool_sprintf - Write formatted string to ethtool string data
* @data: Pointer to start of string to update
diff --git a/net/ethtool/stats.c b/net/ethtool/stats.c
index 9238d99f560b..a38516181e46 100644
--- a/net/ethtool/stats.c
+++ b/net/ethtool/stats.c
@@ -437,3 +437,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_STATS_SRC_EMAC;
+ pmac.src = ETHTOOL_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_STATS_SRC_EMAC;
+ pmac.src = ETHTOOL_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_STATS_SRC_EMAC;
+ pmac.src = ETHTOOL_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_STATS_SRC_EMAC;
+ pmac.src = ETHTOOL_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_STATS_SRC_EMAC;
+ pmac.src = ETHTOOL_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);
--
2.34.1
Felix (VSC9959) has a DEV_GMII:MM_CONFIG block composed of 2 registers
(ENABLE_CONFIG and VERIF_CONFIG). Because the MAC Merge statistics and
pMAC statistics are already in the Ocelot switch lib even if just Felix
supports them, I'm adding support for the whole MAC Merge layer in the
common Ocelot library too.
There is an interrupt (shared with the PTP interrupt) which signals
changes to the MM verification state. This is done because the
preemptible traffic classes should be committed to hardware only once
the verification procedure has declared the link partner of being
capable of receiving preemptible frames.
We implement ethtool getters and setters for the MAC Merge layer state.
The "TX enabled" and "verify status" are taken from the IRQ handler,
using a mutex to ensure serialized access.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v2: patch is new (v1 was written for enetc)
drivers/net/dsa/ocelot/felix.c | 19 +++
drivers/net/dsa/ocelot/felix_vsc9959.c | 19 ++-
drivers/net/ethernet/mscc/Makefile | 1 +
drivers/net/ethernet/mscc/ocelot.c | 18 ++-
drivers/net/ethernet/mscc/ocelot.h | 2 +
drivers/net/ethernet/mscc/ocelot_mm.c | 213 +++++++++++++++++++++++++
include/soc/mscc/ocelot.h | 18 +++
include/soc/mscc/ocelot_dev.h | 23 +++
8 files changed, 301 insertions(+), 12 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_mm.c
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 09f839dc9e64..336ad7263042 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -2066,6 +2066,23 @@ 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 void felix_get_mm(struct dsa_switch *ds, int port,
+ struct ethtool_mm_state *state)
+{
+ struct ocelot *ocelot = ds->priv;
+
+ 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)
{
@@ -2081,6 +2098,8 @@ 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,
diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
index 260d861c7072..e7020f9081f9 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -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>
@@ -476,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),
@@ -2602,20 +2606,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;
}
diff --git a/drivers/net/ethernet/mscc/Makefile b/drivers/net/ethernet/mscc/Makefile
index 5d435a565d4c..16987b72dfc0 100644
--- a/drivers/net/ethernet/mscc/Makefile
+++ b/drivers/net/ethernet/mscc/Makefile
@@ -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 \
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index da56f9bfeaf0..c060b03f7e27 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -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);
diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
index 70dbd9c4e512..e9a0179448bf 100644
--- a/drivers/net/ethernet/mscc/ocelot.h
+++ b/drivers/net/ethernet/mscc/ocelot.h
@@ -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;
diff --git a/drivers/net/ethernet/mscc/ocelot_mm.c b/drivers/net/ethernet/mscc/ocelot_mm.c
new file mode 100644
index 000000000000..99c774f56aa8
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_mm.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable
+ * switches (VSC9959)
+ *
+ * Copyright 2022 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_info(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_info(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_add_frag_size_oct_to_std(cfg->add_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(cfg->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);
+
+void 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;
+
+ 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->add_frag_size = ethtool_mm_add_frag_size_std_to_oct(add_frag_size);
+
+ state->verify_status = mm->verify_status;
+ state->supported = ocelot->mm_supported;
+ state->tx_active = mm->tx_active;
+
+ mutex_unlock(&mm->lock);
+}
+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;
+}
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 6de909d79896..81c4ff9a3405 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -515,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,
@@ -739,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 {
@@ -864,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;
};
@@ -1122,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);
+void 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);
diff --git a/include/soc/mscc/ocelot_dev.h b/include/soc/mscc/ocelot_dev.h
index 0c6021f02fee..fcf02baa76b2 100644
--- a/include/soc/mscc/ocelot_dev.h
+++ b/include/soc/mscc/ocelot_dev.h
@@ -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)
--
2.34.1
The Felix VSC9959 switch supports frame preemption and has a MAC Merge
layer. In addition to the structured stats that exist for the eMAC,
export the counters associated with its pMAC (pause, RMON, MAC, PHY,
control) plus the high-level MAC Merge layer stats. The unstructured
ethtool counters, as well as the rtnl_link_stats64 were left to report
only the eMAC counters.
Because statistics processing is quite self-contained in ocelot_stats.c
now, I've opted for introducing an ocelot->mm_supported bool, based on
which the common switch lib does everything, rather than pushing the
TSN-specific code in felix_vsc9959.c, as happens for other TSN stuff.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v2: patch is new (v1 was written for enetc)
drivers/net/dsa/ocelot/felix.c | 9 +
drivers/net/dsa/ocelot/felix_vsc9959.c | 38 +++
drivers/net/ethernet/mscc/ocelot_stats.c | 289 ++++++++++++++++++++++-
include/soc/mscc/ocelot.h | 40 ++++
4 files changed, 366 insertions(+), 10 deletions(-)
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 00dda0e07d4b..09f839dc9e64 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -2066,6 +2066,14 @@ 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 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,
@@ -2073,6 +2081,7 @@ const struct dsa_switch_ops felix_switch_ops = {
.setup = felix_setup,
.teardown = felix_teardown,
.set_ageing_time = felix_set_ageing_time,
+ .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,
diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c
index ccdc25d47fe1..260d861c7072 100644
--- a/drivers/net/dsa/ocelot/felix_vsc9959.c
+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c
@@ -318,6 +318,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 +372,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),
@@ -2626,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) {
diff --git a/drivers/net/ethernet/mscc/ocelot_stats.c b/drivers/net/ethernet/mscc/ocelot_stats.c
index 551e3cbfae79..1932cea46cd0 100644
--- a/drivers/net/ethernet/mscc/ocelot_stats.c
+++ b/drivers/net/ethernet/mscc/ocelot_stats.c
@@ -54,6 +54,29 @@ enum ocelot_stat {
OCELOT_STAT_RX_GREEN_PRIO_5,
OCELOT_STAT_RX_GREEN_PRIO_6,
OCELOT_STAT_RX_GREEN_PRIO_7,
+ OCELOT_STAT_RX_ASSEMBLY_ERRS,
+ OCELOT_STAT_RX_SMD_ERRS,
+ OCELOT_STAT_RX_ASSEMBLY_OK,
+ OCELOT_STAT_RX_MERGE_FRAGMENTS,
+ OCELOT_STAT_RX_PMAC_OCTETS,
+ OCELOT_STAT_RX_PMAC_UNICAST,
+ OCELOT_STAT_RX_PMAC_MULTICAST,
+ OCELOT_STAT_RX_PMAC_BROADCAST,
+ OCELOT_STAT_RX_PMAC_SHORTS,
+ OCELOT_STAT_RX_PMAC_FRAGMENTS,
+ OCELOT_STAT_RX_PMAC_JABBERS,
+ OCELOT_STAT_RX_PMAC_CRC_ALIGN_ERRS,
+ OCELOT_STAT_RX_PMAC_SYM_ERRS,
+ OCELOT_STAT_RX_PMAC_64,
+ OCELOT_STAT_RX_PMAC_65_127,
+ OCELOT_STAT_RX_PMAC_128_255,
+ OCELOT_STAT_RX_PMAC_256_511,
+ OCELOT_STAT_RX_PMAC_512_1023,
+ OCELOT_STAT_RX_PMAC_1024_1526,
+ OCELOT_STAT_RX_PMAC_1527_MAX,
+ OCELOT_STAT_RX_PMAC_PAUSE,
+ OCELOT_STAT_RX_PMAC_CONTROL,
+ OCELOT_STAT_RX_PMAC_LONGS,
OCELOT_STAT_TX_OCTETS,
OCELOT_STAT_TX_UNICAST,
OCELOT_STAT_TX_MULTICAST,
@@ -85,6 +108,20 @@ enum ocelot_stat {
OCELOT_STAT_TX_GREEN_PRIO_6,
OCELOT_STAT_TX_GREEN_PRIO_7,
OCELOT_STAT_TX_AGED,
+ OCELOT_STAT_TX_MM_HOLD,
+ OCELOT_STAT_TX_MERGE_FRAGMENTS,
+ OCELOT_STAT_TX_PMAC_OCTETS,
+ OCELOT_STAT_TX_PMAC_UNICAST,
+ OCELOT_STAT_TX_PMAC_MULTICAST,
+ OCELOT_STAT_TX_PMAC_BROADCAST,
+ OCELOT_STAT_TX_PMAC_PAUSE,
+ OCELOT_STAT_TX_PMAC_64,
+ OCELOT_STAT_TX_PMAC_65_127,
+ OCELOT_STAT_TX_PMAC_128_255,
+ OCELOT_STAT_TX_PMAC_256_511,
+ OCELOT_STAT_TX_PMAC_512_1023,
+ OCELOT_STAT_TX_PMAC_1024_1526,
+ OCELOT_STAT_TX_PMAC_1527_MAX,
OCELOT_STAT_DROP_LOCAL,
OCELOT_STAT_DROP_TAIL,
OCELOT_STAT_DROP_YELLOW_PRIO_0,
@@ -228,9 +265,52 @@ static const struct ocelot_stat_layout ocelot_stats_layout[OCELOT_NUM_STATS] = {
OCELOT_COMMON_STATS,
};
+static const struct ocelot_stat_layout ocelot_mm_stats_layout[OCELOT_NUM_STATS] = {
+ OCELOT_COMMON_STATS,
+ OCELOT_STAT(RX_ASSEMBLY_ERRS),
+ OCELOT_STAT(RX_SMD_ERRS),
+ OCELOT_STAT(RX_ASSEMBLY_OK),
+ OCELOT_STAT(RX_MERGE_FRAGMENTS),
+ OCELOT_STAT(TX_MERGE_FRAGMENTS),
+ OCELOT_STAT(RX_PMAC_OCTETS),
+ OCELOT_STAT(RX_PMAC_UNICAST),
+ OCELOT_STAT(RX_PMAC_MULTICAST),
+ OCELOT_STAT(RX_PMAC_BROADCAST),
+ OCELOT_STAT(RX_PMAC_SHORTS),
+ OCELOT_STAT(RX_PMAC_FRAGMENTS),
+ OCELOT_STAT(RX_PMAC_JABBERS),
+ OCELOT_STAT(RX_PMAC_CRC_ALIGN_ERRS),
+ OCELOT_STAT(RX_PMAC_SYM_ERRS),
+ OCELOT_STAT(RX_PMAC_64),
+ OCELOT_STAT(RX_PMAC_65_127),
+ OCELOT_STAT(RX_PMAC_128_255),
+ OCELOT_STAT(RX_PMAC_256_511),
+ OCELOT_STAT(RX_PMAC_512_1023),
+ OCELOT_STAT(RX_PMAC_1024_1526),
+ OCELOT_STAT(RX_PMAC_1527_MAX),
+ OCELOT_STAT(RX_PMAC_PAUSE),
+ OCELOT_STAT(RX_PMAC_CONTROL),
+ OCELOT_STAT(RX_PMAC_LONGS),
+ OCELOT_STAT(TX_PMAC_OCTETS),
+ OCELOT_STAT(TX_PMAC_UNICAST),
+ OCELOT_STAT(TX_PMAC_MULTICAST),
+ OCELOT_STAT(TX_PMAC_BROADCAST),
+ OCELOT_STAT(TX_PMAC_PAUSE),
+ OCELOT_STAT(TX_PMAC_64),
+ OCELOT_STAT(TX_PMAC_65_127),
+ OCELOT_STAT(TX_PMAC_128_255),
+ OCELOT_STAT(TX_PMAC_256_511),
+ OCELOT_STAT(TX_PMAC_512_1023),
+ OCELOT_STAT(TX_PMAC_1024_1526),
+ OCELOT_STAT(TX_PMAC_1527_MAX),
+};
+
static const struct ocelot_stat_layout *
ocelot_get_stats_layout(struct ocelot *ocelot)
{
+ if (ocelot->mm_supported)
+ return ocelot_mm_stats_layout;
+
return ocelot_stats_layout;
}
@@ -410,14 +490,63 @@ static void ocelot_port_pause_stats_cb(struct ocelot *ocelot, int port, void *pr
pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PAUSE];
}
+static void ocelot_port_pmac_pause_stats_cb(struct ocelot *ocelot, int port,
+ void *priv)
+{
+ u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
+ struct ethtool_pause_stats *pause_stats = priv;
+
+ pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PMAC_PAUSE];
+ pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PMAC_PAUSE];
+}
+
+static void ocelot_port_mm_stats_cb(struct ocelot *ocelot, int port,
+ void *priv)
+{
+ u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
+ struct ethtool_mm_stats *stats = priv;
+
+ stats->MACMergeFrameAssErrorCount = s[OCELOT_STAT_RX_ASSEMBLY_ERRS];
+ stats->MACMergeFrameSmdErrorCount = s[OCELOT_STAT_RX_SMD_ERRS];
+ stats->MACMergeFrameAssOkCount = s[OCELOT_STAT_RX_ASSEMBLY_OK];
+ stats->MACMergeFragCountRx = s[OCELOT_STAT_RX_MERGE_FRAGMENTS];
+ stats->MACMergeFragCountTx = s[OCELOT_STAT_TX_MERGE_FRAGMENTS];
+ stats->MACMergeHoldCount = s[OCELOT_STAT_TX_MM_HOLD];
+}
+
void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port,
struct ethtool_pause_stats *pause_stats)
{
- ocelot_port_stats_run(ocelot, port, pause_stats,
- ocelot_port_pause_stats_cb);
+ struct net_device *dev;
+
+ switch (pause_stats->src) {
+ case ETHTOOL_STATS_SRC_EMAC:
+ ocelot_port_stats_run(ocelot, port, pause_stats,
+ ocelot_port_pause_stats_cb);
+ break;
+ case ETHTOOL_STATS_SRC_PMAC:
+ if (ocelot->mm_supported)
+ ocelot_port_stats_run(ocelot, port, pause_stats,
+ ocelot_port_pmac_pause_stats_cb);
+ break;
+ case ETHTOOL_STATS_SRC_AGGREGATE:
+ dev = ocelot->ops->port_to_netdev(ocelot, port);
+ ethtool_aggregate_pause_stats(dev, pause_stats);
+ break;
+ }
}
EXPORT_SYMBOL_GPL(ocelot_port_get_pause_stats);
+void ocelot_port_get_mm_stats(struct ocelot *ocelot, int port,
+ struct ethtool_mm_stats *stats)
+{
+ if (!ocelot->mm_supported)
+ return;
+
+ ocelot_port_stats_run(ocelot, port, stats, ocelot_port_mm_stats_cb);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_get_mm_stats);
+
static const struct ethtool_rmon_hist_range ocelot_rmon_ranges[] = {
{ 64, 64 },
{ 65, 127 },
@@ -456,14 +585,57 @@ static void ocelot_port_rmon_stats_cb(struct ocelot *ocelot, int port, void *pri
rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1024_1526];
}
+static void ocelot_port_pmac_rmon_stats_cb(struct ocelot *ocelot, int port,
+ void *priv)
+{
+ u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
+ struct ethtool_rmon_stats *rmon_stats = priv;
+
+ rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_PMAC_SHORTS];
+ rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_PMAC_LONGS];
+ rmon_stats->fragments = s[OCELOT_STAT_RX_PMAC_FRAGMENTS];
+ rmon_stats->jabbers = s[OCELOT_STAT_RX_PMAC_JABBERS];
+
+ rmon_stats->hist[0] = s[OCELOT_STAT_RX_PMAC_64];
+ rmon_stats->hist[1] = s[OCELOT_STAT_RX_PMAC_65_127];
+ rmon_stats->hist[2] = s[OCELOT_STAT_RX_PMAC_128_255];
+ rmon_stats->hist[3] = s[OCELOT_STAT_RX_PMAC_256_511];
+ rmon_stats->hist[4] = s[OCELOT_STAT_RX_PMAC_512_1023];
+ rmon_stats->hist[5] = s[OCELOT_STAT_RX_PMAC_1024_1526];
+ rmon_stats->hist[6] = s[OCELOT_STAT_RX_PMAC_1527_MAX];
+
+ rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_PMAC_64];
+ rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_PMAC_65_127];
+ rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_PMAC_128_255];
+ rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_PMAC_128_255];
+ rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_PMAC_256_511];
+ rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_PMAC_512_1023];
+ rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_PMAC_1024_1526];
+}
+
void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges)
{
+ struct net_device *dev;
+
*ranges = ocelot_rmon_ranges;
- ocelot_port_stats_run(ocelot, port, rmon_stats,
- ocelot_port_rmon_stats_cb);
+ switch (rmon_stats->src) {
+ case ETHTOOL_STATS_SRC_EMAC:
+ ocelot_port_stats_run(ocelot, port, rmon_stats,
+ ocelot_port_rmon_stats_cb);
+ break;
+ case ETHTOOL_STATS_SRC_PMAC:
+ if (ocelot->mm_supported)
+ ocelot_port_stats_run(ocelot, port, rmon_stats,
+ ocelot_port_pmac_rmon_stats_cb);
+ break;
+ case ETHTOOL_STATS_SRC_AGGREGATE:
+ dev = ocelot->ops->port_to_netdev(ocelot, port);
+ ethtool_aggregate_rmon_stats(dev, rmon_stats);
+ break;
+ }
}
EXPORT_SYMBOL_GPL(ocelot_port_get_rmon_stats);
@@ -475,11 +647,35 @@ static void ocelot_port_ctrl_stats_cb(struct ocelot *ocelot, int port, void *pri
ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_CONTROL];
}
+static void ocelot_port_pmac_ctrl_stats_cb(struct ocelot *ocelot, int port,
+ void *priv)
+{
+ u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
+ struct ethtool_eth_ctrl_stats *ctrl_stats = priv;
+
+ ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_PMAC_CONTROL];
+}
+
void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats)
{
- ocelot_port_stats_run(ocelot, port, ctrl_stats,
- ocelot_port_ctrl_stats_cb);
+ struct net_device *dev;
+
+ switch (ctrl_stats->src) {
+ case ETHTOOL_STATS_SRC_EMAC:
+ ocelot_port_stats_run(ocelot, port, ctrl_stats,
+ ocelot_port_ctrl_stats_cb);
+ break;
+ case ETHTOOL_STATS_SRC_PMAC:
+ if (ocelot->mm_supported)
+ ocelot_port_stats_run(ocelot, port, ctrl_stats,
+ ocelot_port_pmac_ctrl_stats_cb);
+ break;
+ case ETHTOOL_STATS_SRC_AGGREGATE:
+ dev = ocelot->ops->port_to_netdev(ocelot, port);
+ ethtool_aggregate_ctrl_stats(dev, ctrl_stats);
+ break;
+ }
}
EXPORT_SYMBOL_GPL(ocelot_port_get_eth_ctrl_stats);
@@ -525,11 +721,60 @@ static void ocelot_port_mac_stats_cb(struct ocelot *ocelot, int port, void *priv
mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS];
}
+static void ocelot_port_pmac_mac_stats_cb(struct ocelot *ocelot, int port,
+ void *priv)
+{
+ u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
+ struct ethtool_eth_mac_stats *mac_stats = priv;
+
+ mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_PMAC_OCTETS];
+ mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_PMAC_64] +
+ s[OCELOT_STAT_TX_PMAC_65_127] +
+ s[OCELOT_STAT_TX_PMAC_128_255] +
+ s[OCELOT_STAT_TX_PMAC_256_511] +
+ s[OCELOT_STAT_TX_PMAC_512_1023] +
+ s[OCELOT_STAT_TX_PMAC_1024_1526] +
+ s[OCELOT_STAT_TX_PMAC_1527_MAX];
+ mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_PMAC_OCTETS];
+ mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_PMAC_64] +
+ s[OCELOT_STAT_RX_PMAC_65_127] +
+ s[OCELOT_STAT_RX_PMAC_128_255] +
+ s[OCELOT_STAT_RX_PMAC_256_511] +
+ s[OCELOT_STAT_RX_PMAC_512_1023] +
+ s[OCELOT_STAT_RX_PMAC_1024_1526] +
+ s[OCELOT_STAT_RX_PMAC_1527_MAX];
+ mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_PMAC_MULTICAST];
+ mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_PMAC_BROADCAST];
+ mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_PMAC_MULTICAST];
+ mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_PMAC_BROADCAST];
+ mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_PMAC_LONGS];
+ /* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not
+ * counted individually.
+ */
+ mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_PMAC_CRC_ALIGN_ERRS];
+ mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_PMAC_CRC_ALIGN_ERRS];
+}
+
void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port,
struct ethtool_eth_mac_stats *mac_stats)
{
- ocelot_port_stats_run(ocelot, port, mac_stats,
- ocelot_port_mac_stats_cb);
+ struct net_device *dev;
+
+ switch (mac_stats->src) {
+ case ETHTOOL_STATS_SRC_EMAC:
+ ocelot_port_stats_run(ocelot, port, mac_stats,
+ ocelot_port_mac_stats_cb);
+ break;
+ case ETHTOOL_STATS_SRC_PMAC:
+ if (ocelot->mm_supported)
+ ocelot_port_stats_run(ocelot, port, mac_stats,
+ ocelot_port_pmac_mac_stats_cb);
+ break;
+ case ETHTOOL_STATS_SRC_AGGREGATE:
+ dev = ocelot->ops->port_to_netdev(ocelot, port);
+ ethtool_aggregate_mac_stats(dev, mac_stats);
+ break;
+ }
}
EXPORT_SYMBOL_GPL(ocelot_port_get_eth_mac_stats);
@@ -541,11 +786,35 @@ static void ocelot_port_phy_stats_cb(struct ocelot *ocelot, int port, void *priv
phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_SYM_ERRS];
}
+static void ocelot_port_pmac_phy_stats_cb(struct ocelot *ocelot, int port,
+ void *priv)
+{
+ u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS];
+ struct ethtool_eth_phy_stats *phy_stats = priv;
+
+ phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_PMAC_SYM_ERRS];
+}
+
void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port,
struct ethtool_eth_phy_stats *phy_stats)
{
- ocelot_port_stats_run(ocelot, port, phy_stats,
- ocelot_port_phy_stats_cb);
+ struct net_device *dev;
+
+ switch (phy_stats->src) {
+ case ETHTOOL_STATS_SRC_EMAC:
+ ocelot_port_stats_run(ocelot, port, phy_stats,
+ ocelot_port_phy_stats_cb);
+ break;
+ case ETHTOOL_STATS_SRC_PMAC:
+ if (ocelot->mm_supported)
+ ocelot_port_stats_run(ocelot, port, phy_stats,
+ ocelot_port_pmac_phy_stats_cb);
+ break;
+ case ETHTOOL_STATS_SRC_AGGREGATE:
+ dev = ocelot->ops->port_to_netdev(ocelot, port);
+ ethtool_aggregate_phy_stats(dev, phy_stats);
+ break;
+ }
}
EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats);
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index df62be80a193..6de909d79896 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -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,
@@ -814,6 +851,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;
@@ -937,6 +975,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);
--
2.34.1
Two new netlink attributes were added to PAUSE_GET and STATS_GET and
their replies. Document them.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v2: patch is new
Documentation/networking/ethtool-netlink.rst | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index 490c2280ce4f..3805e57f9888 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -1075,8 +1075,17 @@ Request contents:
===================================== ====== ==========================
``ETHTOOL_A_PAUSE_HEADER`` nested request header
+ ``ETHTOOL_A_PAUSE_STATS_SRC`` u32 source of statistics
===================================== ====== ==========================
+``ETHTOOL_A_STATS_SRC`` is optional. It takes values from:
+
+.. kernel-doc:: include/uapi/linux/ethtool.h
+ :identifiers: ethtool_stats_src
+
+If absent from the request, stats will be provided with
+``ETHTOOL_A_STATS_SRC_AGGREGATE`` in the response.
+
Kernel response contents:
===================================== ====== ==========================
@@ -1491,6 +1500,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
======================================= ====== ==========================
@@ -1499,6 +1509,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_*`` |
@@ -1560,6 +1572,14 @@ Low and high bounds are inclusive, for example:
etherStatsPkts512to1023Octets 512 1023
============================= ==== ====
+``ETHTOOL_A_STATS_SRC`` is optional. It takes values from:
+
+.. kernel-doc:: include/uapi/linux/ethtool.h
+ :identifiers: ethtool_stats_src
+
+If absent from the request, stats will be provided with
+``ETHTOOL_A_STATS_SRC_AGGREGATE`` in the response.
+
PHC_VCLOCKS_GET
===============
--
2.34.1
We deliberately make the Linux UAPI pass the additional fragment size in
octets, even though IEEE 802.3 defines it as discrete values, and
addFragSize is just the multiplier. This is because there is nothing
impossible in operating with an in-between value for the fragment size
of non-final preempted fragments, and there may even appear hardware
which supports the in-between sizes.
For the hardware which just understands addFragSize as being a
multiplier, create two helpers which translate back and forth the values
passed in octets.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v2: patch is new
include/linux/ethtool.h | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 01b1e34dc30e..1c3e8fc53609 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -979,6 +979,45 @@ void ethtool_aggregate_pause_stats(struct net_device *dev,
void ethtool_aggregate_rmon_stats(struct net_device *dev,
struct ethtool_rmon_stats *rmon_stats);
+/**
+ * ethtool_mm_add_frag_size_std_to_oct - Translate standard add_frag_size into
+ * value in octets
+ * @val_std: Value of addFragSize variable in octets
+ */
+static inline u32 ethtool_mm_add_frag_size_std_to_oct(u32 val_std)
+{
+ return 64 * (1 + val_std) - 4;
+}
+
+/**
+ * ethtool_mm_add_frag_size_oct_to_std - Translate add_frag_size into
+ * standard value
+ * @val_oct: Value of addFragSize variable in octets
+ * @val_std: Pointer where the standard addFragSize value is to be returned
+ * @extack: Netlink extended ack
+ *
+ * Translate a value in octets to one of 0, 1, 2, 3 according to the reverse
+ * application of the 802.3 formula 64 * (1 + addFragSize) - 4. To be called
+ * by drivers which do not support programming addFragSize to a continuous
+ * range. Returns error on other fragment length values.
+ */
+static inline int ethtool_mm_add_frag_size_oct_to_std(u32 val_oct, u32 *val_std,
+ struct netlink_ext_ack *extack)
+{
+ u32 add_frag_size;
+
+ for (add_frag_size = 0; add_frag_size < 4; add_frag_size++) {
+ if (ethtool_mm_add_frag_size_std_to_oct(add_frag_size) == val_oct) {
+ *val_std = add_frag_size;
+ return 0;
+ }
+ }
+
+ NL_SET_ERR_MSG_MOD(extack,
+ "addFragSize required to be one of 60, 124, 188 or 252");
+ return -EINVAL;
+}
+
/**
* ethtool_sprintf - Write formatted string to ethtool string data
* @data: Pointer to start of string to update
--
2.34.1
IEEE 802.3-2018 clause 99 defines a MAC Merge sublayer which contains an
Express MAC and a Preemptable MAC. Both MACs are hidden to higher and
lower layers and visible as a single MAC (packet classification to eMAC
or pMAC on TX is done based on priority; classification on RX is done
based on SFD).
For devices which support a MAC Merge sublayer, it is desirable to
retrieve individual packet counters from the eMAC and the pMAC, as well
as aggregate statistics (their sum).
Introduce a new ETHTOOL_A_STATS_SRC attribute which is part of the
policy of ETHTOOL_MSG_STATS_GET and, and an ETHTOOL_A_PAUSE_STATS_SRC
which is part of the policy of ETHTOOL_MSG_PAUSE_GET (accepted when
ETHTOOL_FLAG_STATS is set in the common ethtool header). Both of these
take values from enum ethtool_stats_src, defaulting to "aggregate" in
the absence of the attribute.
Existing drivers do not need to pay attention to this enum which was
added to all driver-facing structures, just the ones which report the
MAC merge layer as supported.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v1->v2: patch is new (Jakub's suggestion)
include/linux/ethtool.h | 9 ++++++
include/uapi/linux/ethtool.h | 18 +++++++++++
include/uapi/linux/ethtool_netlink.h | 3 ++
net/ethtool/common.h | 2 ++
net/ethtool/mm.c | 15 +++++++++
net/ethtool/netlink.h | 4 +--
net/ethtool/pause.c | 47 ++++++++++++++++++++++++++++
net/ethtool/stats.c | 31 ++++++++++++++++--
8 files changed, 125 insertions(+), 4 deletions(-)
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 6336f105e667..06672b76b2c6 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -301,6 +301,7 @@ static inline void ethtool_stats_init(u64 *stats, unsigned int n)
* via a more targeted API.
*/
struct ethtool_eth_mac_stats {
+ enum ethtool_stats_src src;
u64 FramesTransmittedOK;
u64 SingleCollisionFrames;
u64 MultipleCollisionFrames;
@@ -329,6 +330,7 @@ struct ethtool_eth_mac_stats {
* via a more targeted API.
*/
struct ethtool_eth_phy_stats {
+ enum ethtool_stats_src src;
u64 SymbolErrorDuringCarrier;
};
@@ -336,6 +338,7 @@ struct ethtool_eth_phy_stats {
* via a more targeted API.
*/
struct ethtool_eth_ctrl_stats {
+ enum ethtool_stats_src src;
u64 MACControlFramesTransmitted;
u64 MACControlFramesReceived;
u64 UnsupportedOpcodesReceived;
@@ -343,6 +346,8 @@ struct ethtool_eth_ctrl_stats {
/**
* struct ethtool_pause_stats - statistics for IEEE 802.3x pause frames
+ * @src: input field denoting whether stats should be queried from the eMAC or
+ * pMAC (if the MM layer is supported). To be ignored otherwise.
* @tx_pause_frames: transmitted pause frame count. Reported to user space
* as %ETHTOOL_A_PAUSE_STAT_TX_FRAMES.
*
@@ -356,6 +361,7 @@ struct ethtool_eth_ctrl_stats {
* from the standard.
*/
struct ethtool_pause_stats {
+ enum ethtool_stats_src src;
u64 tx_pause_frames;
u64 rx_pause_frames;
};
@@ -407,6 +413,8 @@ struct ethtool_rmon_hist_range {
/**
* struct ethtool_rmon_stats - selected RMON (RFC 2819) statistics
+ * @src: input field denoting whether stats should be queried from the eMAC or
+ * pMAC (if the MM layer is supported). To be ignored otherwise.
* @undersize_pkts: Equivalent to `etherStatsUndersizePkts` from the RFC.
* @oversize_pkts: Equivalent to `etherStatsOversizePkts` from the RFC.
* @fragments: Equivalent to `etherStatsFragments` from the RFC.
@@ -422,6 +430,7 @@ struct ethtool_rmon_hist_range {
* ranges is left to the driver.
*/
struct ethtool_rmon_stats {
+ enum ethtool_stats_src src;
u64 undersize_pkts;
u64 oversize_pkts;
u64 fragments;
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index 7ddc47a3fb32..16230dc6a8c1 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -711,6 +711,24 @@ enum ethtool_stringset {
ETH_SS_COUNT
};
+/**
+ * enum ethtool_stats_src - source of ethtool statistics
+ * @ETHTOOL_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_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_STATS_SRC_PMAC:
+ * if device supports a MM layer, this retrieves the pMAC statistics.
+ */
+enum ethtool_stats_src {
+ ETHTOOL_STATS_SRC_AGGREGATE,
+ ETHTOOL_STATS_SRC_EMAC,
+ ETHTOOL_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.
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index e84a80957138..e938a7a50944 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -419,6 +419,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,
@@ -735,6 +736,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)
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index b1b9db810eca..28b8aaaf9bcb 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -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 */
diff --git a/net/ethtool/mm.c b/net/ethtool/mm.c
index 01a2acc40046..b99fdfa49a32 100644
--- a/net/ethtool/mm.c
+++ b/net/ethtool/mm.c
@@ -256,3 +256,18 @@ int ethnl_set_mm(struct sk_buff *skb, struct genl_info *info)
dev_put(dev);
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 = {};
+
+ if (ops && ops->get_mm)
+ ops->get_mm(dev, &state);
+
+ return state.supported;
+}
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index a8012dbe39bb..43d2a7c98a3d 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -397,7 +397,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];
@@ -408,7 +408,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];
diff --git a/net/ethtool/pause.c b/net/ethtool/pause.c
index a8c113d244db..f3eac539f9a4 100644
--- a/net/ethtool/pause.c
+++ b/net/ethtool/pause.c
@@ -5,8 +5,12 @@
struct pause_req_info {
struct ethnl_req_info base;
+ enum ethtool_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_STATS_SRC_PMAC),
};
+static int pause_parse_request(struct ethnl_req_info *req_base,
+ struct nlattr **tb,
+ struct netlink_ext_ack *extack)
+{
+ struct pause_req_info *req_info = PAUSE_REQINFO(req_base);
+ enum ethtool_stats_src src = ETHTOOL_STATS_SRC_AGGREGATE;
+
+ 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);
+ struct netlink_ext_ack *extack = info->extack;
+ enum ethtool_stats_src src = req_info->src;
struct net_device *dev = reply_base->dev;
int ret;
@@ -34,14 +65,25 @@ 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_STATS_SRC_EMAC || src == ETHTOOL_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 +98,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 +120,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 +167,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,
diff --git a/net/ethtool/stats.c b/net/ethtool/stats.c
index a20e0a24ff61..9238d99f560b 100644
--- a/net/ethtool/stats.c
+++ b/net/ethtool/stats.c
@@ -7,6 +7,7 @@
struct stats_req_info {
struct ethnl_req_info base;
DECLARE_BITMAP(stat_mask, __ETHTOOL_STATS_CNT);
+ enum ethtool_stats_src src;
};
#define STATS_REQINFO(__req_base) \
@@ -75,10 +76,12 @@ 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_STATS_SRC_PMAC),
};
static int stats_parse_request(struct ethnl_req_info *req_base,
@@ -86,6 +89,7 @@ static int stats_parse_request(struct ethnl_req_info *req_base,
struct netlink_ext_ack *extack)
{
struct stats_req_info *req_info = STATS_REQINFO(req_base);
+ enum ethtool_stats_src src = ETHTOOL_STATS_SRC_AGGREGATE;
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);
+ struct netlink_ext_ack *extack = info->extack;
+ enum ethtool_stats_src src = req_info->src;
struct net_device *dev = reply_base->dev;
int ret;
@@ -116,11 +127,24 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
if (ret < 0)
return ret;
+ if ((src == ETHTOOL_STATS_SRC_EMAC || src == ETHTOOL_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);
@@ -143,8 +167,8 @@ static int stats_reply_size(const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct stats_req_info *req_info = STATS_REQINFO(req_base);
+ int len = nla_total_size(sizeof(u32)); /* _STATS_SRC */
unsigned int n_grps = 0, n_stats = 0;
- int len = 0;
if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) {
n_stats += sizeof(struct ethtool_eth_phy_stats) / sizeof(u64);
@@ -379,6 +403,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,
--
2.34.1
On Wed, Jan 11, 2023 at 06:16:54PM +0200, Vladimir Oltean wrote:
> This is a continuation of my work started here:
> https://patchwork.kernel.org/project/netdevbpf/cover/[email protected]/
>
> 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.
As it happens, this patch set seems to conflict with the recently merged
8580e16c28f3 ("net/ethtool: add netlink interface for the PLCA RS").
It is still possible to apply and test on a set of kernel UAPI headers
prior to that commit, so I will leave reviewers/testers more time to
comment, and will not resend kernel + user space patches just for this
fact.
On Wed, 11 Jan 2023 18:16:57 +0200 Vladimir Oltean wrote:
> Show details about the structures passed back and forth related to MAC
> Merge layer configuration, state and statistics. The rendered htmldocs
> will be much more verbose due to the kerneldoc references.
>
> Signed-off-by: Vladimir Oltean <[email protected]>
> ---
> v1->v2: patch is new
>
> Documentation/networking/ethtool-netlink.rst | 103 +++++++++++++++++++
> Documentation/networking/statistics.rst | 1 +
> 2 files changed, 104 insertions(+)
>
> diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
> index f10f8eb44255..490c2280ce4f 100644
> --- a/Documentation/networking/ethtool-netlink.rst
> +++ b/Documentation/networking/ethtool-netlink.rst
> @@ -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
> @@ -1716,6 +1719,104 @@ being used. Current supported options are toeplitz, xor or crc32.
> ETHTOOL_A_RSS_INDIR attribute returns RSS indrection table where each byte
> indicates queue number.
>
> +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_SUPPORTED`` Bool set if device supports the MM layer
I'm guessing the empty lines are to improve readability?
(They are not required for the table to render correctly.)
Why did you capitalize the types, tho?
> + ``ETHTOOL_A_MM_PMAC_ENABLED`` Bool set if RX of preemptible and SMD-V
> + frames is enabled
s/is/are/ ?
> + ``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_ADD_FRAG_SIZE`` U32 minimum size of transmitted
> + non-final fragments, in octets
> +
> + ``ETHTOOL_A_MM_VERIFY_ENABLED`` Bool set if TX of SMD-V frames is
> + administratively enabled (TX will
> + not take place when port is not up)
The sentence in the brackets seems obvious, is there some special
MM meaning to "port is not up"? You're not talking about the link
being up?
> + ``ETHTOOL_A_MM_VERIFY_STATUS`` U8 state of the Verify function
Only places you say "Verify function" rather than just "verification",
not sure that's on purpose.
> + ``ETHTOOL_A_MM_VERIFY_TIME`` U32 delay between verification attempts
> +
> + ``ETHTOOL_A_MM_MAX_VERIFY_TIME`` U32 maximum interval supported by
s/interval/verification interval/ ?
> + device
> +
> + ``ETHTOOL_A_MM_STATS`` Nested IEEE 802.3-2018 subclause 30.14.1
> + oMACMergeEntity statistics counters
> +
The empty line between last entry and delimiter can go
> + ================================ ====== ===================================
> +
> +If ``ETHTOOL_A_MM_SUPPORTED`` is reported as false, the other netlink
> +attributes will be absent.
Why not return -EOPNOTSUPP?
You do so in case driver does not have the op:
+ if (!ops->get_mm)
+ return -EOPNOTSUPP;
On Wed, 11 Jan 2023 18:16:56 +0200 Vladimir Oltean wrote:
> +/**
> + * 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,
funky characters here
> + * 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
> + */
> +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)
nit: in set you do if (ret) after begin
> + return ret;
> +
> + ops->get_mm(dev, &data->state);
> +
> + if (ops->get_mm_stats && (req_base->flags & ETHTOOL_FLAG_STATS))
> + ops->get_mm_stats(dev, &data->stats);
> +
> + ethnl_ops_complete(dev);
> +
> + return 0;
> +}
> +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;
> + }
> +
> + rtnl_lock();
> + ret = ethnl_ops_begin(dev);
> + if (ret)
> + goto out_rtnl;
> +
> + ops->get_mm(dev, &state);
> +
> + mm_state_to_cfg(&state, &cfg);
> +
> + if (cfg.verify_time > state.max_verify_time) {
> + NL_SET_ERR_MSG_MOD(extack, "verifyTime exceeds device maximum");
Drop the _MOD() add _ATTR()
> + return -ERANGE;
goto out_ops;
> + }
> +
> + 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.add_frag_size, tb[ETHTOOL_A_MM_ADD_FRAG_SIZE],
> + &mod);
you never use the @mod?
> + ret = ops->set_mm(dev, &cfg, extack);
> + if (ret) {
> + if (!extack->_msg)
> + NL_SET_ERR_MSG(extack,
> + "Failed to update MAC merge configuration");
Why add this extack? It's it evident that the config wasn't updated
from the fact the netlink ACK carries a non-zero error?
> + goto out_ops;
> + }
> +
> + ethtool_notify(dev, ETHTOOL_MSG_MM_NTF, NULL);
> +
> +out_ops:
> + ethnl_ops_complete(dev);
> +out_rtnl:
> + rtnl_unlock();
> +out_dev:
> + dev_put(dev);
ethnl_parse_header_dev_put()
> + return ret;
> +}
I'm out of time for the day, feel free to send v3, or I'll just pick up
here tomorrow.
On Wed, 11 Jan 2023 18:16:58 +0200 Vladimir Oltean wrote:
> + int len = nla_total_size(sizeof(u32)); /* _STATS_SRC */
> unsigned int n_grps = 0, n_stats = 0;
> - int len = 0;
nit: don't hide the first field in init
On Wed, 11 Jan 2023 18:16:58 +0200 Vladimir Oltean wrote:
> +/**
> + * enum ethtool_stats_src - source of ethtool statistics
> + * @ETHTOOL_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_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_STATS_SRC_PMAC:
> + * if device supports a MM layer, this retrieves the pMAC statistics.
> + */
> +enum ethtool_stats_src {
> + ETHTOOL_STATS_SRC_AGGREGATE,
> + ETHTOOL_STATS_SRC_EMAC,
> + ETHTOOL_STATS_SRC_PMAC,
> +};
Should we somehow call it "MAC stats"?
Right now its named like a generic attribute, but it's not part of
the header nest (ETHTOOL_A_HEADER_*).
I'm not sure myself which way is better, but feels like either it
should be generic, in the header nest, and parsed by the common code;
or named more specifically and stay in the per-cmd attrs.
On Fri, Jan 13, 2023 at 08:43:36PM -0800, Jakub Kicinski wrote:
> On Wed, 11 Jan 2023 18:16:58 +0200 Vladimir Oltean wrote:
> > +/**
> > + * enum ethtool_stats_src - source of ethtool statistics
> > + * @ETHTOOL_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_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_STATS_SRC_PMAC:
> > + * if device supports a MM layer, this retrieves the pMAC statistics.
> > + */
> > +enum ethtool_stats_src {
> > + ETHTOOL_STATS_SRC_AGGREGATE,
> > + ETHTOOL_STATS_SRC_EMAC,
> > + ETHTOOL_STATS_SRC_PMAC,
> > +};
>
> Should we somehow call it "MAC stats"?
>
> Right now its named like a generic attribute, but it's not part of
> the header nest (ETHTOOL_A_HEADER_*).
>
> I'm not sure myself which way is better, but feels like either it
> should be generic, in the header nest, and parsed by the common code;
> or named more specifically and stay in the per-cmd attrs.
Considering that I currently have separate netlink attributes for
ETHTOOL_MSG_STATS_GET (ETHTOOL_A_STATS_SRC) and for
ETHTOOL_MSG_PAUSE_GET (ETHTOOL_A_PAUSE_STATS_SRC), I'm going to add just
a single attribute right under ETHTOOL_A_HEADER_FLAGS for v3 and go from
there. Is it ok if I keep naming it ETHTOOL_A_STATS_SRC, or would you
prefer something else?
> -----Original Message-----
> From: Vladimir Oltean <[email protected]>
> Sent: Wednesday, January 11, 2023 9:47 PM
> To: [email protected]
> Cc: [email protected]; David S. Miller <[email protected]>;
> Eric Dumazet <[email protected]>; Jakub Kicinski <[email protected]>;
> Paolo Abeni <[email protected]>; Michal Kubecek <[email protected]>;
> Claudiu Manoil <[email protected]>; Vinicius Costa Gomes
> <[email protected]>; Xiaoliang Yang <[email protected]>;
> Kurt Kanzenbach <[email protected]>; Rui Sousa <[email protected]>;
> Ferenc Fejes <[email protected]>; Somisetty, Pranavi
> <[email protected]>; Katakam, Harini
> <[email protected]>; Colin Foster <[email protected]>;
> [email protected]; Alexandre Belloni
> <[email protected]>; Andrew Lunn <[email protected]>; Florian
> Fainelli <[email protected]>
> Subject: [PATCH v2 net-next 02/12] net: ethtool: add support for MAC Merge
> layer
>
> The MAC merge sublayer (IEEE 802.3-2018 clause 99) is one of 2
> specifications (the other being Frame Preemption; IEEE 802.1Q-2018 clause
> 6.7.2), which work together to minimize latency caused by frame
> interference at TX. The overall goal of TSN is for normal traffic and traffic
> with a bounded deadline to be able to cohabitate on the same L2 network
> and not bother each other too much.
>
> The standards achieve this (partly) by introducing the concept of preemptible
> traffic, i.e. Ethernet frames that have a custom value for the Start-of-Frame-
> Delimiter (SFD), and these frames can be fragmented and reassembled at L2
> on a link-local basis. The non-preemptible frames are called express traffic,
> they are transmitted using a normal SFD, and they can preempt preemptible
> frames, therefore having lower latency, which can matter at lower (100
> Mbps) link speeds, or at high MTUs (jumbo frames around 9K). Preemption is
> not recursive, i.e. a P frame cannot preempt another P frame. Preemption
> also does not depend upon priority, or otherwise said, an E frame with prio 0
> will still preempt a P frame with prio 7.
>
> In terms of implementation, the standards talk about the presence of an
> express MAC (eMAC) which handles express traffic, and a preemptible MAC
> (pMAC) which handles preemptible traffic, and these MACs are multiplexed
> on the same MII by a MAC merge layer.
>
> To support frame preemption, the definition of the SFD was generalized to
> SMD (Start-of-mPacket-Delimiter), where an mPacket is essentially an
> Ethernet frame fragment, or a complete frame. Stations unaware of an SMD
> value different from the standard SFD will treat P frames as error frames. To
> prevent that from happening, a negotiation process is defined.
>
> On RX, packets are dispatched to the eMAC or pMAC after being filtered by
> their SMD. On TX, the eMAC/pMAC classification decision is taken by the
> 802.1Q spec, based on packet priority (each of the 8 user priority values may
> have an admin-status of preemptible or express).
>
> The MAC Merge layer and the Frame Preemption parameters have some
> degree of independence in terms of how software stacks are supposed to
> deal with them. The activation of the MM layer is supposed to be controlled
> by an LLDP daemon (after it has been communicated that the link partner
> also supports it), after which a (hardware-based or not) verification
> handshake takes place, before actually enabling the feature. So the process is
> intended to be relatively plug-and-play. Whereas FP settings are supposed to
> be coordinated across a network using something approximating NETCONF.
>
> The support contained here is exclusively for the 802.3 (MAC Merge)
> portions and not for the 802.1Q (Frame Preemption) parts. This API is
> sufficient for an LLDP daemon to do its job. The FP adminStatus variable from
> 802.1Q is outside the scope of an LLDP daemon.
>
> I have taken a few creative licenses and augmented the Linux kernel UAPI
> compared to the standard managed objects recommended by IEEE 802.3.
> These are:
>
> - ETHTOOL_A_MM_PMAC_ENABLED: According to Figure 99-6: Receive
> Processing state diagram, a MAC Merge layer is always supposed to be
> able to receive P frames. However, this implies keeping the pMAC
> powered on, which will consume needless power in applications where FP
> will never be used. If LLDP is used, the reception of an Additional
> Ethernet Capabilities TLV from the link partner is sufficient
> indication that the pMAC should be enabled. So my proposal is that in
> Linux, we keep the pMAC turned off by default and that user space
> turns it on when needed.
>
> - ETHTOOL_A_MM_VERIFY_ENABLED: The IEEE managed object is called
> aMACMergeVerifyDisableTx. I opted for consistency (positive logic) in
> the boolean netlink attributes offered, so this is also positive here.
> Other than the meaning being reversed, they correspond to the same
> thing.
>
> - ETHTOOL_A_MM_MAX_VERIFY_TIME: I found it most reasonable for a LLDP
> daemon to maximize the verifyTime variable (delay between SMD-V
> transmissions), to maximize its chances that the LP replies. IEEE says
> that the verifyTime can range between 1 and 128 ms, but the NXP ENETC
> stupidly keeps this variable in a 7 bit register, so the maximum
> supported value is 127 ms. I could have chosen to hardcode this in the
> LLDP daemon to a lower value, but why not let the kernel expose its
> supported range directly.
>
> Signed-off-by: Vladimir Oltean <[email protected]>
> ---
> v1->v2:
> - added documentation
> - introduced pmac_enabled
> - transformed verify_disable into verify_enabled
> - made add_frag_size take a value in octets
> - removed FP params (adminStatus)
> - renamed "enabled" to "tx_enabled" and "active" to "tx_active"
>
> include/linux/ethtool.h | 99 ++++++++++
> include/uapi/linux/ethtool.h | 25 +++
> include/uapi/linux/ethtool_netlink.h | 47 +++++
> net/ethtool/Makefile | 4 +-
> net/ethtool/mm.c | 258 +++++++++++++++++++++++++++
> net/ethtool/netlink.c | 19 ++
> net/ethtool/netlink.h | 4 +
> 7 files changed, 454 insertions(+), 2 deletions(-) create mode 100644
> net/ethtool/mm.c
>
> diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index
> 9e0a76fc7de9..6336f105e667 100644
> --- a/include/linux/ethtool.h
> +++ b/include/linux/ethtool.h
> @@ -467,6 +467,98 @@ struct ethtool_module_power_mode_params {
> enum ethtool_module_power_mode mode;
> };
>
> +/**
> + * struct ethtool_mm_state - 802.3 MAC merge layer state
> + * @verify_time:
> + * wait time between verification attempts in ms (according to clause
> + * 30.14.1.6 aMACMergeVerifyTime)
> + * @max_verify_time:
> + * maximum accepted value for the @verify_time variable in set
> requests
> + * @verify_status:
> + * state of the verification state machine of the MM layer (according to
> + * clause 30.14.1.2 aMACMergeStatusVerify)
> + * @supported:
> + * set if device supports the MM layer (according to clause 30.14.1.1
> + * aMACMergeSupport)
> + * @tx_enabled:
> + * set if the MM layer is administratively enabled in the TX direction
> + * (according to clause 30.14.1.3 aMACMergeEnableTx)
> + * @tx_active:
> + * set if the MM layer is enabled in the TX direction, which makes FP
> + * possible (according to 30.14.1.5 aMACMergeStatusTx). This should
> be
> + * true if MM is enabled, and the verification status is either verified,
> + * or disabled.
> + * @pmac_enabled:
> + * set if the preemptible MAC is powered on and is able to receive
> + * preemptible packets and respond to verification frames.
> + * @verify_enabled:
> + * set if the Verify function of the MM layer (which sends SMD-V
> + * verification requests) is administratively enabled (regardless of
> + * whether it is currently in the
> ETHTOOL_MM_VERIFY_STATUS_DISABLED state
> + * or not), according to clause 30.14.1.4 aMACMergeVerifyDisableTx
> (but
> + * using positive rather than negative logic). The device should always
> + * respond to received SMD-V requests as long as @pmac_enabled is
> set.
> + * @add_frag_size:
> + * the minimum size of non-final mPacket fragments that the link
> partner
> + * supports receiving, expressed in octets. Compared to the definition
> + * from clause 30.14.1.7 aMACMergeAddFragSize which is expressed in
> the
> + * range 0 to 3 (requiring a translation to the size in octets according
> + * to the formula 64 * (1 + addFragSize) – 4), a value in a continuous
> and
> + * unbounded range can be specified here.
> + */
> +struct ethtool_mm_state {
> + u32 verify_time;
> + u32 max_verify_time;
> + enum ethtool_mm_verify_status verify_status;
> + bool supported;
> + bool tx_enabled;
> + bool tx_active;
> + bool pmac_enabled;
> + bool verify_enabled;
> + u32 add_frag_size;
> +};
> +
> +/**
> + * struct ethtool_mm_cfg - 802.3 MAC merge layer configuration
> + * @verify_time: see struct ethtool_mm_state
> + * @verify_enabled: see struct ethtool_mm_state
> + * @tx_enabled: see struct ethtool_mm_state
> + * @pmac_enabled: see struct ethtool_mm_state
> + * @add_frag_size: see struct ethtool_mm_state */ struct
> +ethtool_mm_cfg {
> + u32 verify_time;
> + bool verify_enabled;
> + bool tx_enabled;
> + bool pmac_enabled;
> + u32 add_frag_size;
> +};
> +
> +/**
> + * struct ethtool_mm_stats - 802.3 MAC merge layer statistics
> + * @MACMergeFrameAssErrorCount:
> + * received MAC frames with reassembly errors
> + * @MACMergeFrameSmdErrorCount:
> + * received MAC frames/fragments rejected due to unknown or
> incorrect SMD
> + * @MACMergeFrameAssOkCount:
> + * received MAC frames that were successfully reassembled and passed
> up
> + * @MACMergeFragCountRx:
> + * number of additional correct SMD-C mPackets received due to
> preemption
> + * @MACMergeFragCountTx:
> + * number of additional mPackets sent due to preemption
> + * @MACMergeHoldCount:
> + * number of times the MM layer entered the HOLD state, which blocks
> + * transmission of preemptible traffic
> + */
> +struct ethtool_mm_stats {
> + u64 MACMergeFrameAssErrorCount;
> + u64 MACMergeFrameSmdErrorCount;
> + u64 MACMergeFrameAssOkCount;
> + u64 MACMergeFragCountRx;
> + u64 MACMergeFragCountTx;
> + u64 MACMergeHoldCount;
> +};
> +
> /**
> * struct ethtool_ops - optional netdev operations
> * @cap_link_lanes_supported: indicates if the driver supports lanes @@ -
> 639,6 +731,9 @@ struct ethtool_module_power_mode_params {
> * plugged-in.
> * @set_module_power_mode: Set the power mode policy for the plug-in
> module
> * used by the network device.
> + * @get_mm: Query the 802.3 MAC Merge layer state.
> + * @set_mm: Set the 802.3 MAC Merge layer parameters.
> + * @get_mm_stats: Query the 802.3 MAC Merge layer statistics.
> *
> * All operations are optional (i.e. the function pointer may be set
> * to %NULL) and callers must take this into account. Callers must @@ -
> 777,6 +872,10 @@ struct ethtool_ops {
> int (*set_module_power_mode)(struct net_device *dev,
> const struct
> ethtool_module_power_mode_params *params,
> struct netlink_ext_ack *extack);
> + void (*get_mm)(struct net_device *dev, struct ethtool_mm_state
> *state);
> + int (*set_mm)(struct net_device *dev, struct ethtool_mm_cfg
> *cfg,
> + struct netlink_ext_ack *extack);
> + void (*get_mm_stats)(struct net_device *dev, struct
> ethtool_mm_stats *stats);
> };
>
Does it make sense to have two separate functions, one to enable/disable pmac and tx preemption status and configure additional fragment size (both parameters can be configured after negotiating with the link partner), and another to modify the verification related parameters, i.e, enable/disable verification and verify time value?
For example, if the user wants to change the verify time value, Is there a way to change it without disturbing the value of the additional fragment size that has been set through LLDP after negotiating with the link partner?
> int ethtool_check_ops(const struct ethtool_ops *ops); diff --git
> a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index
> 3135fa0ba9a4..7ddc47a3fb32 100644
> --- a/include/uapi/linux/ethtool.h
> +++ b/include/uapi/linux/ethtool.h
> @@ -779,6 +779,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 diff --git
> a/include/uapi/linux/ethtool_netlink.h
> b/include/uapi/linux/ethtool_netlink.h
> index 5799a9db034e..e84a80957138 100644
> --- a/include/uapi/linux/ethtool_netlink.h
> +++ b/include/uapi/linux/ethtool_netlink.h
> @@ -52,6 +52,8 @@ enum {
> ETHTOOL_MSG_PSE_GET,
> ETHTOOL_MSG_PSE_SET,
> ETHTOOL_MSG_RSS_GET,
> + ETHTOOL_MSG_MM_GET,
> + ETHTOOL_MSG_MM_SET,
>
> /* add new constants above here */
> __ETHTOOL_MSG_USER_CNT,
> @@ -99,6 +101,8 @@ enum {
> ETHTOOL_MSG_MODULE_NTF,
> ETHTOOL_MSG_PSE_GET_REPLY,
> ETHTOOL_MSG_RSS_GET_REPLY,
> + ETHTOOL_MSG_MM_GET_REPLY,
> + ETHTOOL_MSG_MM_NTF,
>
> /* add new constants above here */
> __ETHTOOL_MSG_KERNEL_CNT,
> @@ -894,6 +898,49 @@ enum {
> ETHTOOL_A_RSS_MAX = (__ETHTOOL_A_RSS_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_SUPPORTED, /* u8 */
> + ETHTOOL_A_MM_PMAC_ENABLED, /* u8 */
> + ETHTOOL_A_MM_TX_ENABLED, /* u8 */
> + ETHTOOL_A_MM_TX_ACTIVE, /* u8 */
> + ETHTOOL_A_MM_ADD_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
> diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index
> 228f13df2e18..ab824b2d3b7d 100644
> --- a/net/ethtool/Makefile
> +++ b/net/ethtool/Makefile
> @@ -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
> + tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \
> + module.o pse-pd.o
> diff --git a/net/ethtool/mm.c b/net/ethtool/mm.c new file mode 100644
> index 000000000000..01a2acc40046
> --- /dev/null
> +++ b/net/ethtool/mm.c
> @@ -0,0 +1,258 @@
> +// 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;
> +
> + ops->get_mm(dev, &data->state);
> +
> + if (ops->get_mm_stats && (req_base->flags &
> ETHTOOL_FLAG_STATS))
> + ops->get_mm_stats(dev, &data->stats);
> +
> + 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) {
> + struct mm_reply_data *data = MM_REPDATA(reply_base);
> + const struct ethtool_mm_state *state = &data->state;
> + int len = nla_total_size(sizeof(u8)); /* _MM_SUPPORTED */
> +
> + if (!state->supported)
> + return len;
> +
> + 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_ADD_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_SUPPORTED, state-
> >supported))
> + return -EMSGSIZE;
> +
> + if (!state->supported)
> + return 0;
> +
> + 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_ADD_FRAG_SIZE, state-
> >add_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_ADD_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->add_frag_size = state->add_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;
> + }
> +
> + rtnl_lock();
> + ret = ethnl_ops_begin(dev);
> + if (ret)
> + goto out_rtnl;
> +
> + ops->get_mm(dev, &state);
> +
> + mm_state_to_cfg(&state, &cfg);
> +
> + if (cfg.verify_time > state.max_verify_time) {
> + NL_SET_ERR_MSG_MOD(extack, "verifyTime exceeds device
> maximum");
> + return -ERANGE;
> + }
> +
> + 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.add_frag_size,
> tb[ETHTOOL_A_MM_ADD_FRAG_SIZE],
> + &mod);
> +
> + ret = ops->set_mm(dev, &cfg, extack);
> + if (ret) {
> + if (!extack->_msg)
> + NL_SET_ERR_MSG(extack,
> + "Failed to update MAC merge
> configuration");
> + goto out_ops;
> + }
> +
> + ethtool_notify(dev, ETHTOOL_MSG_MM_NTF, NULL);
> +
> +out_ops:
> + ethnl_ops_complete(dev);
> +out_rtnl:
> + rtnl_unlock();
> +out_dev:
> + dev_put(dev);
> + return ret;
> +}
> diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index
> aee98be6237f..a8c5b2521c46 100644
> --- a/net/ethtool/netlink.c
> +++ b/net/ethtool/netlink.c
> @@ -288,6 +288,7 @@
> ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
> [ETHTOOL_MSG_MODULE_GET] =
> ðnl_module_request_ops,
> [ETHTOOL_MSG_PSE_GET] = ðnl_pse_request_ops,
> [ETHTOOL_MSG_RSS_GET] = ðnl_rss_request_ops,
> + [ETHTOOL_MSG_MM_GET] = ðnl_mm_request_ops,
> };
>
> static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback
> *cb) @@ -603,6 +604,7 @@
> ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = {
> [ETHTOOL_MSG_EEE_NTF] = ðnl_eee_request_ops,
> [ETHTOOL_MSG_FEC_NTF] = ðnl_fec_request_ops,
> [ETHTOOL_MSG_MODULE_NTF] =
> ðnl_module_request_ops,
> + [ETHTOOL_MSG_MM_NTF] = ðnl_mm_request_ops,
> };
>
> /* default notification handler */
> @@ -696,6 +698,7 @@ static const ethnl_notify_handler_t
> ethnl_notify_handlers[] = {
> [ETHTOOL_MSG_EEE_NTF] = ethnl_default_notify,
> [ETHTOOL_MSG_FEC_NTF] = ethnl_default_notify,
> [ETHTOOL_MSG_MODULE_NTF] = ethnl_default_notify,
> + [ETHTOOL_MSG_MM_NTF] = ethnl_default_notify,
> };
>
> void ethtool_notify(struct net_device *dev, unsigned int cmd, const void
> *data) @@ -1047,6 +1050,22 @@ static const struct genl_ops
> ethtool_genl_ops[] = {
> .policy = ethnl_rss_get_policy,
> .maxattr = ARRAY_SIZE(ethnl_rss_get_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[] = { diff --git
> a/net/ethtool/netlink.h b/net/ethtool/netlink.h index
> 744b3ab966b0..a8012dbe39bb 100644
> --- a/net/ethtool/netlink.h
> +++ b/net/ethtool/netlink.h
> @@ -373,6 +373,7 @@ extern const struct ethnl_request_ops
> ethnl_phc_vclocks_request_ops; extern const struct ethnl_request_ops
> ethnl_module_request_ops; 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_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]; @@
> -414,6 +415,8 @@ extern const struct nla_policy
> ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MO
> extern const struct nla_policy
> ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1]; extern const struct
> nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1]; extern const
> struct nla_policy ethnl_rss_get_policy[ETHTOOL_A_RSS_CONTEXT + 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); @@ -434,6
> +437,7 @@ int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct
> netlink_callback *cb); 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_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];
> --
> 2.34.1
On Mon, Jan 16, 2023 at 09:02:55AM +0000, Somisetty, Pranavi wrote:
> Does it make sense to have two separate functions, one to
> enable/disable pmac and tx preemption status and configure additional
> fragment size (both parameters can be configured after negotiating
> with the link partner), and another to modify the verification related
> parameters, i.e, enable/disable verification and verify time value?
>
> For example, if the user wants to change the verify time value, Is
> there a way to change it without disturbing the value of the
> additional fragment size that has been set through LLDP after
> negotiating with the link partner?
Could you please clarify what you mean by "disturbing"? The handler for
the ETHTOOL_MSG_MM_SET message first calls get_mm(struct ethtool_mm_state),
then mm_state_to_cfg(struct ethtool_mm_state, struct ethtool_mm_cfg),
then set_mm(struct ethtool_mm_cfg). In other words, a SET ethtool
command translates into a read-modify-write in the kernel. If user space
did not request the modification of the additional fragment size, just
the verify time, then the set_mm() call will contain the old additional
fragment size and the new verify time.
On Sun, Jan 15, 2023 at 01:22:14AM +0200, Vladimir Oltean wrote:
> On Fri, Jan 13, 2023 at 08:43:36PM -0800, Jakub Kicinski wrote:
> > On Wed, 11 Jan 2023 18:16:58 +0200 Vladimir Oltean wrote:
> > > +/**
> > > + * enum ethtool_stats_src - source of ethtool statistics
> > > + * @ETHTOOL_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_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_STATS_SRC_PMAC:
> > > + * if device supports a MM layer, this retrieves the pMAC statistics.
> > > + */
> > > +enum ethtool_stats_src {
> > > + ETHTOOL_STATS_SRC_AGGREGATE,
> > > + ETHTOOL_STATS_SRC_EMAC,
> > > + ETHTOOL_STATS_SRC_PMAC,
> > > +};
> >
> > Should we somehow call it "MAC stats"?
> >
> > Right now its named like a generic attribute, but it's not part of
> > the header nest (ETHTOOL_A_HEADER_*).
> >
> > I'm not sure myself which way is better, but feels like either it
> > should be generic, in the header nest, and parsed by the common code;
> > or named more specifically and stay in the per-cmd attrs.
>
> Considering that I currently have separate netlink attributes for
> ETHTOOL_MSG_STATS_GET (ETHTOOL_A_STATS_SRC) and for
> ETHTOOL_MSG_PAUSE_GET (ETHTOOL_A_PAUSE_STATS_SRC), I'm going to add just
> a single attribute right under ETHTOOL_A_HEADER_FLAGS for v3 and go from
> there. Is it ok if I keep naming it ETHTOOL_A_STATS_SRC, or would you
> prefer something else?
I'm already lost while trying to implement this change request.
ETHTOOL_A_STATS_HEADER uses NLA_POLICY_NESTED(ethnl_header_policy),
while ETHTOOL_A_PAUSE_HEADER uses NLA_POLICY_NESTED(ethnl_header_policy_stats).
The two header nest policies look like this:
const struct nla_policy ethnl_header_policy[] = {
[ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 },
[ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING,
.len = ALTIFNAMSIZ - 1 },
[ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
ETHTOOL_FLAGS_BASIC),
};
const struct nla_policy ethnl_header_policy_stats[] = {
[ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 },
[ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING,
.len = ALTIFNAMSIZ - 1 },
[ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
ETHTOOL_FLAGS_STATS),
};
The request seems to be for ETHTOOL_A_PAUSE_HEADER to use a policy like this:
const struct nla_policy ethnl_header_policy_mac_stats[] = {
[ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 },
[ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING,
.len = ALTIFNAMSIZ - 1 },
[ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
ETHTOOL_FLAGS_STATS),
+ [ETHTOOL_A_HEADER_MAC_STATS_SRC] = NLA_POLICY_MASK(NLA_U32,
+ ETHTOOL_MAC_STATS_SRC_PMAC),
};
and for ETHTOOL_A_STATS_HEADER to use a policy like this:
const struct nla_policy ethnl_header_policy_mac_stats_src_basic[] = {
[ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 },
[ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING,
.len = ALTIFNAMSIZ - 1 },
[ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
ETHTOOL_FLAGS_BASIC),
+ [ETHTOOL_A_HEADER_MAC_STATS_SRC] = NLA_POLICY_MASK(NLA_U32,
+ ETHTOOL_MAC_STATS_SRC_PMAC),
};
Did I get this right?
On Mon, 16 Jan 2023 19:42:34 +0200 Vladimir Oltean wrote:
> The request seems to be for ETHTOOL_A_PAUSE_HEADER to use a policy like this:
>
> const struct nla_policy ethnl_header_policy_mac_stats[] = {
> [ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 },
> [ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING,
> .len = ALTIFNAMSIZ - 1 },
> [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
> ETHTOOL_FLAGS_STATS),
> + [ETHTOOL_A_HEADER_MAC_STATS_SRC] = NLA_POLICY_MASK(NLA_U32,
> + ETHTOOL_MAC_STATS_SRC_PMAC),
> };
>
> and for ETHTOOL_A_STATS_HEADER to use a policy like this:
>
> const struct nla_policy ethnl_header_policy_mac_stats_src_basic[] = {
> [ETHTOOL_A_HEADER_DEV_INDEX] = { .type = NLA_U32 },
> [ETHTOOL_A_HEADER_DEV_NAME] = { .type = NLA_NUL_STRING,
> .len = ALTIFNAMSIZ - 1 },
> [ETHTOOL_A_HEADER_FLAGS] = NLA_POLICY_MASK(NLA_U32,
> ETHTOOL_FLAGS_BASIC),
> + [ETHTOOL_A_HEADER_MAC_STATS_SRC] = NLA_POLICY_MASK(NLA_U32,
> + ETHTOOL_MAC_STATS_SRC_PMAC),
> };
>
> Did I get this right?
Sorry for the delay, I was out for $national-holiday.
This would be right, but it seems like you went for the other option
in v3, which is also fine.