Change log
----------
v2->v3:
- made get_mm return int instead of void
- deleted ETHTOOL_A_MM_SUPPORTED
- renamed ETHTOOL_A_MM_ADD_FRAG_SIZE to ETHTOOL_A_MM_TX_MIN_FRAG_SIZE
- introduced ETHTOOL_A_MM_RX_MIN_FRAG_SIZE
- cleaned up documentation
- rebased on top of PLCA changes
- renamed ETHTOOL_STATS_SRC_* to ETHTOOL_MAC_STATS_SRC_*
v2 at:
https://patchwork.kernel.org/project/netdevbpf/cover/[email protected]/
v1->v2:
I've decided to focus just on the MAC Merge layer for now, which is why
I am able to submit this patch set as non-RFC.
v1 (RFC) at:
https://patchwork.kernel.org/project/netdevbpf/cover/[email protected]/
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 fragment size 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 | 107 ++++++
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 | 214 ++++++++++++
drivers/net/ethernet/mscc/ocelot_stats.c | 331 +++++++++++++++++--
include/linux/ethtool.h | 248 +++++++++++---
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 | 37 +++
net/ethtool/Makefile | 4 +-
net/ethtool/common.h | 2 +
net/ethtool/mm.c | 271 +++++++++++++++
net/ethtool/netlink.c | 19 ++
net/ethtool/netlink.h | 34 +-
net/ethtool/pause.c | 48 +++
net/ethtool/stats.c | 159 ++++++++-
23 files changed, 1688 insertions(+), 78 deletions(-)
create mode 100644 drivers/net/ethernet/mscc/ocelot_mm.c
create mode 100644 net/ethtool/mm.c
--
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]>
---
v2->v3: get_mm now returns int
v1->v2: patch is new
include/net/dsa.h | 11 +++++++++++
net/dsa/slave.c | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 48 insertions(+)
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 96086289aa9b..a15f17a38eca 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
+ */
+ int (*get_mm)(struct dsa_switch *ds, int port,
+ struct ethtool_mm_state *state);
+ int (*set_mm)(struct dsa_switch *ds, int port,
+ struct ethtool_mm_cfg *cfg,
+ struct netlink_ext_ack *extack);
+ void (*get_mm_stats)(struct dsa_switch *ds, int port,
+ struct ethtool_mm_stats *stats);
+
/*
* DCB ops
*/
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index aab79c355224..6014ac3aad34 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1117,6 +1117,40 @@ static void dsa_slave_net_selftest(struct net_device *ndev,
net_selftest(ndev, etest, buf);
}
+static int dsa_slave_get_mm(struct net_device *dev,
+ struct ethtool_mm_state *state)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+
+ if (!ds->ops->get_mm)
+ return -EOPNOTSUPP;
+
+ return ds->ops->get_mm(ds, dp->index, state);
+}
+
+static int dsa_slave_set_mm(struct net_device *dev, struct ethtool_mm_cfg *cfg,
+ struct netlink_ext_ack *extack)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+
+ if (!ds->ops->set_mm)
+ return -EOPNOTSUPP;
+
+ return ds->ops->set_mm(ds, dp->index, cfg, extack);
+}
+
+static void dsa_slave_get_mm_stats(struct net_device *dev,
+ struct ethtool_mm_stats *stats)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+
+ if (ds->ops->get_mm_stats)
+ ds->ops->get_mm_stats(ds, dp->index, stats);
+}
+
static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
@@ -2205,6 +2239,9 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
.set_rxnfc = dsa_slave_set_rxnfc,
.get_ts_info = dsa_slave_get_ts_info,
.self_test = dsa_slave_net_selftest,
+ .get_mm = dsa_slave_get_mm,
+ .set_mm = dsa_slave_set_mm,
+ .get_mm_stats = dsa_slave_get_mm_stats,
};
static const struct dcbnl_rtnl_ops __maybe_unused dsa_slave_dcbnl_ops = {
--
2.34.1
Due to the fact that the kernel-side data structures have been carried
over from the ioctl-based ethtool, we are now in the situation where we
have an ethnl_update_bool32() function, but the plain function that
operates on a boolean value kept in an actual u8 netlink attribute
doesn't exist.
With new ethtool features that are exposed solely over netlink, the
kernel data structures will use the "bool" type, so we will need this
kind of helper. Introduce it now; it's needed for things like
verify-disabled for the MAC merge configuration.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v2->v3: none
v1->v2: rewrite commit message
net/ethtool/netlink.h | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index f271266f6e28..f675f62fe181 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -111,6 +111,32 @@ static inline void ethnl_update_u8(u8 *dst, const struct nlattr *attr,
*mod = true;
}
+/**
+ * ethnl_update_bool() - update bool from NLA_U8 attribute
+ * @dst: value to update
+ * @attr: netlink attribute with new value or null
+ * @mod: pointer to bool for modification tracking
+ *
+ * Use the u8 value from NLA_U8 netlink attribute @attr to set bool variable
+ * pointed to by @dst to false (if zero) or 1 (if not); do nothing if @attr is
+ * null. Bool pointed to by @mod is set to true if this function changed the
+ * logical value of *dst, otherwise it is left as is.
+ */
+static inline void ethnl_update_bool(bool *dst, const struct nlattr *attr,
+ bool *mod)
+{
+ u8 val;
+
+ if (!attr)
+ return;
+ val = !!nla_get_u8(attr);
+ if (*dst == val)
+ return;
+
+ *dst = val;
+ *mod = true;
+}
+
/**
* ethnl_update_bool32() - update u32 used as bool from NLA_U8 attribute
* @dst: value to update
--
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]>
---
v2->v3: none
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
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]>
---
v2->v3: none
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
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_mac_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]>
---
v2->v3:
- rename enum ethtool_stats_src to enum ethtool_mac_stats_src
- rename ETHTOOL_STATS_SRC_* to ETHTOOL_MAC_STATS_SRC_*
- change how __ethtool_dev_mm_supported() is implemented
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 | 16 ++++++++++
net/ethtool/netlink.h | 4 +--
net/ethtool/pause.c | 48 ++++++++++++++++++++++++++++
net/ethtool/stats.c | 32 ++++++++++++++++++-
8 files changed, 129 insertions(+), 3 deletions(-)
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 37eba38da502..0ccba6612190 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -311,6 +311,7 @@ static inline void ethtool_stats_init(u64 *stats, unsigned int n)
* via a more targeted API.
*/
struct ethtool_eth_mac_stats {
+ enum ethtool_mac_stats_src src;
u64 FramesTransmittedOK;
u64 SingleCollisionFrames;
u64 MultipleCollisionFrames;
@@ -339,6 +340,7 @@ struct ethtool_eth_mac_stats {
* via a more targeted API.
*/
struct ethtool_eth_phy_stats {
+ enum ethtool_mac_stats_src src;
u64 SymbolErrorDuringCarrier;
};
@@ -346,6 +348,7 @@ struct ethtool_eth_phy_stats {
* via a more targeted API.
*/
struct ethtool_eth_ctrl_stats {
+ enum ethtool_mac_stats_src src;
u64 MACControlFramesTransmitted;
u64 MACControlFramesReceived;
u64 UnsupportedOpcodesReceived;
@@ -353,6 +356,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.
*
@@ -366,6 +371,7 @@ struct ethtool_eth_ctrl_stats {
* from the standard.
*/
struct ethtool_pause_stats {
+ enum ethtool_mac_stats_src src;
u64 tx_pause_frames;
u64 rx_pause_frames;
};
@@ -417,6 +423,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.
@@ -432,6 +440,7 @@ struct ethtool_rmon_hist_range {
* ranges is left to the driver.
*/
struct ethtool_rmon_stats {
+ enum ethtool_mac_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 529a93696ab6..f7fba0dc87e5 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_mac_stats_src - source of ethtool MAC statistics
+ * @ETHTOOL_MAC_STATS_SRC_AGGREGATE:
+ * if device supports a MAC merge layer, this retrieves the aggregate
+ * statistics of the eMAC and pMAC. Otherwise, it retrieves just the
+ * statistics of the single (express) MAC.
+ * @ETHTOOL_MAC_STATS_SRC_EMAC:
+ * if device supports a MM layer, this retrieves the eMAC statistics.
+ * Otherwise, it retrieves the statistics of the single (express) MAC.
+ * @ETHTOOL_MAC_STATS_SRC_PMAC:
+ * if device supports a MM layer, this retrieves the pMAC statistics.
+ */
+enum ethtool_mac_stats_src {
+ ETHTOOL_MAC_STATS_SRC_AGGREGATE,
+ ETHTOOL_MAC_STATS_SRC_EMAC,
+ ETHTOOL_MAC_STATS_SRC_PMAC,
+};
+
/**
* enum ethtool_module_power_mode_policy - plug-in module power mode policy
* @ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH: Module is always in high power mode.
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 58af390823b0..ffb073c0dbb4 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -428,6 +428,7 @@ enum {
ETHTOOL_A_PAUSE_RX, /* u8 */
ETHTOOL_A_PAUSE_TX, /* u8 */
ETHTOOL_A_PAUSE_STATS, /* nest - _PAUSE_STAT_* */
+ ETHTOOL_A_PAUSE_STATS_SRC, /* u32 */
/* add new constants above here */
__ETHTOOL_A_PAUSE_CNT,
@@ -744,6 +745,8 @@ enum {
ETHTOOL_A_STATS_GRP, /* nest - _A_STATS_GRP_* */
+ ETHTOOL_A_STATS_SRC, /* u32 */
+
/* add new constants above here */
__ETHTOOL_A_STATS_CNT,
ETHTOOL_A_STATS_MAX = (__ETHTOOL_A_STATS_CNT - 1)
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 0c8135393c14..7ebc4e82e83d 100644
--- a/net/ethtool/mm.c
+++ b/net/ethtool/mm.c
@@ -253,3 +253,19 @@ int ethnl_set_mm(struct sk_buff *skb, struct genl_info *info)
ethnl_parse_header_dev_put(&req_info);
return ret;
}
+
+/* Returns whether a given device supports the MAC merge layer
+ * (has an eMAC and a pMAC). Must be called under rtnl_lock() and
+ * ethnl_ops_begin().
+ */
+bool __ethtool_dev_mm_supported(struct net_device *dev)
+{
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ struct ethtool_mm_state state = {};
+ int ret = -EOPNOTSUPP;
+
+ if (ops && ops->get_mm)
+ ret = ops->get_mm(dev, &state);
+
+ return !!ret;
+}
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 60278485b00b..29aef39476eb 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -399,7 +399,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];
@@ -410,7 +410,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..e2be9e89c9d9 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_mac_stats_src src;
};
+#define PAUSE_REQINFO(__req_base) \
+ container_of(__req_base, struct pause_req_info, base)
+
struct pause_reply_data {
struct ethnl_reply_data base;
struct ethtool_pauseparam pauseparam;
@@ -19,13 +23,40 @@ struct pause_reply_data {
const struct nla_policy ethnl_pause_get_policy[] = {
[ETHTOOL_A_PAUSE_HEADER] =
NLA_POLICY_NESTED(ethnl_header_policy_stats),
+ [ETHTOOL_A_PAUSE_STATS_SRC] =
+ NLA_POLICY_MAX(NLA_U32, ETHTOOL_MAC_STATS_SRC_PMAC),
};
+static int pause_parse_request(struct ethnl_req_info *req_base,
+ struct nlattr **tb,
+ struct netlink_ext_ack *extack)
+{
+ enum ethtool_mac_stats_src src = ETHTOOL_MAC_STATS_SRC_AGGREGATE;
+ struct pause_req_info *req_info = PAUSE_REQINFO(req_base);
+
+ if (tb[ETHTOOL_A_PAUSE_STATS_SRC]) {
+ if (!(req_base->flags & ETHTOOL_FLAG_STATS)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "ETHTOOL_FLAG_STATS must be set when requesting a source of stats");
+ return -EINVAL;
+ }
+
+ src = nla_get_u32(tb[ETHTOOL_A_PAUSE_STATS_SRC]);
+ }
+
+ req_info->src = src;
+
+ return 0;
+}
+
static int pause_prepare_data(const struct ethnl_req_info *req_base,
struct ethnl_reply_data *reply_base,
struct genl_info *info)
{
+ const struct pause_req_info *req_info = PAUSE_REQINFO(req_base);
struct pause_reply_data *data = PAUSE_REPDATA(reply_base);
+ enum ethtool_mac_stats_src src = req_info->src;
+ struct netlink_ext_ack *extack = info->extack;
struct net_device *dev = reply_base->dev;
int ret;
@@ -34,14 +65,26 @@ static int pause_prepare_data(const struct ethnl_req_info *req_base,
ethtool_stats_init((u64 *)&data->pausestat,
sizeof(data->pausestat) / 8);
+ data->pausestat.src = src;
ret = ethnl_ops_begin(dev);
if (ret < 0)
return ret;
+
+ if ((src == ETHTOOL_MAC_STATS_SRC_EMAC ||
+ src == ETHTOOL_MAC_STATS_SRC_PMAC) &&
+ !__ethtool_dev_mm_supported(dev)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Device does not support MAC merge layer");
+ ethnl_ops_complete(dev);
+ return -EOPNOTSUPP;
+ }
+
dev->ethtool_ops->get_pauseparam(dev, &data->pauseparam);
if (req_base->flags & ETHTOOL_FLAG_STATS &&
dev->ethtool_ops->get_pause_stats)
dev->ethtool_ops->get_pause_stats(dev, &data->pausestat);
+
ethnl_ops_complete(dev);
return 0;
@@ -56,6 +99,7 @@ static int pause_reply_size(const struct ethnl_req_info *req_base,
if (req_base->flags & ETHTOOL_FLAG_STATS)
n += nla_total_size(0) + /* _PAUSE_STATS */
+ nla_total_size(sizeof(u32)) + /* _PAUSE_STATS_SRC */
nla_total_size_64bit(sizeof(u64)) * ETHTOOL_PAUSE_STAT_CNT;
return n;
}
@@ -77,6 +121,9 @@ static int pause_put_stats(struct sk_buff *skb,
const u16 pad = ETHTOOL_A_PAUSE_STAT_PAD;
struct nlattr *nest;
+ if (nla_put_u32(skb, ETHTOOL_A_PAUSE_STATS_SRC, pause_stats->src))
+ return -EMSGSIZE;
+
nest = nla_nest_start(skb, ETHTOOL_A_PAUSE_STATS);
if (!nest)
return -EMSGSIZE;
@@ -121,6 +168,7 @@ const struct ethnl_request_ops ethnl_pause_request_ops = {
.req_info_size = sizeof(struct pause_req_info),
.reply_data_size = sizeof(struct pause_reply_data),
+ .parse_request = pause_parse_request,
.prepare_data = pause_prepare_data,
.reply_size = pause_reply_size,
.fill_reply = pause_fill_reply,
diff --git a/net/ethtool/stats.c b/net/ethtool/stats.c
index a20e0a24ff61..26a70320e01d 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_mac_stats_src src;
};
#define STATS_REQINFO(__req_base) \
@@ -75,16 +76,19 @@ const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][ETH_GSTRING_LEN] = {
[ETHTOOL_A_STATS_RMON_JABBER] = "etherStatsJabbers",
};
-const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1] = {
+const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_SRC + 1] = {
[ETHTOOL_A_STATS_HEADER] =
NLA_POLICY_NESTED(ethnl_header_policy),
[ETHTOOL_A_STATS_GROUPS] = { .type = NLA_NESTED },
+ [ETHTOOL_A_STATS_SRC] =
+ NLA_POLICY_MAX(NLA_U32, ETHTOOL_MAC_STATS_SRC_PMAC),
};
static int stats_parse_request(struct ethnl_req_info *req_base,
struct nlattr **tb,
struct netlink_ext_ack *extack)
{
+ enum ethtool_mac_stats_src src = ETHTOOL_MAC_STATS_SRC_AGGREGATE;
struct stats_req_info *req_info = STATS_REQINFO(req_base);
bool mod = false;
int err;
@@ -100,6 +104,11 @@ static int stats_parse_request(struct ethnl_req_info *req_base,
return -EINVAL;
}
+ if (tb[ETHTOOL_A_STATS_SRC])
+ src = nla_get_u32(tb[ETHTOOL_A_STATS_SRC]);
+
+ req_info->src = src;
+
return 0;
}
@@ -109,6 +118,8 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
{
const struct stats_req_info *req_info = STATS_REQINFO(req_base);
struct stats_reply_data *data = STATS_REPDATA(reply_base);
+ enum ethtool_mac_stats_src src = req_info->src;
+ struct netlink_ext_ack *extack = info->extack;
struct net_device *dev = reply_base->dev;
int ret;
@@ -116,11 +127,25 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
if (ret < 0)
return ret;
+ if ((src == ETHTOOL_MAC_STATS_SRC_EMAC ||
+ src == ETHTOOL_MAC_STATS_SRC_PMAC) &&
+ !__ethtool_dev_mm_supported(dev)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Device does not support MAC merge layer");
+ ethnl_ops_complete(dev);
+ return -EOPNOTSUPP;
+ }
+
/* Mark all stats as unset (see ETHTOOL_STAT_NOT_SET) to prevent them
* from being reported to user space in case driver did not set them.
*/
memset(&data->stats, 0xff, sizeof(data->stats));
+ data->phy_stats.src = src;
+ data->mac_stats.src = src;
+ data->ctrl_stats.src = src;
+ data->rmon_stats.src = src;
+
if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) &&
dev->ethtool_ops->get_eth_phy_stats)
dev->ethtool_ops->get_eth_phy_stats(dev, &data->phy_stats);
@@ -146,6 +171,8 @@ static int stats_reply_size(const struct ethnl_req_info *req_base,
unsigned int n_grps = 0, n_stats = 0;
int len = 0;
+ len += nla_total_size(sizeof(u32)); /* _STATS_SRC */
+
if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) {
n_stats += sizeof(struct ethtool_eth_phy_stats) / sizeof(u64);
n_grps++;
@@ -379,6 +406,9 @@ static int stats_fill_reply(struct sk_buff *skb,
const struct stats_reply_data *data = STATS_REPDATA(reply_base);
int ret = 0;
+ if (nla_put_u32(skb, ETHTOOL_A_STATS_SRC, req_info->src))
+ return -EMSGSIZE;
+
if (!ret && test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask))
ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_PHY,
ETH_SS_STATS_ETH_PHY,
--
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]>
---
v2->v3: adapt to ETHTOOL_STATS_SRC_* renaming
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 0ccba6612190..6746dee5a3fd 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;
@@ -312,28 +307,30 @@ static inline void ethtool_stats_init(u64 *stats, unsigned int n)
*/
struct ethtool_eth_mac_stats {
enum ethtool_mac_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
@@ -341,7 +338,9 @@ struct ethtool_eth_mac_stats {
*/
struct ethtool_eth_phy_stats {
enum ethtool_mac_stats_src src;
- u64 SymbolErrorDuringCarrier;
+ struct_group(stats,
+ u64 SymbolErrorDuringCarrier;
+ );
};
/* Basic IEEE 802.3 MAC Ctrl statistics (30.3.3.*), not otherwise exposed
@@ -349,9 +348,11 @@ struct ethtool_eth_phy_stats {
*/
struct ethtool_eth_ctrl_stats {
enum ethtool_mac_stats_src src;
- u64 MACControlFramesTransmitted;
- u64 MACControlFramesReceived;
- u64 UnsupportedOpcodesReceived;
+ struct_group(stats,
+ u64 MACControlFramesTransmitted;
+ u64 MACControlFramesReceived;
+ u64 UnsupportedOpcodesReceived;
+ );
};
/**
@@ -372,8 +373,10 @@ struct ethtool_eth_ctrl_stats {
*/
struct ethtool_pause_stats {
enum ethtool_mac_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
@@ -441,13 +444,15 @@ struct ethtool_rmon_hist_range {
*/
struct ethtool_rmon_stats {
enum ethtool_mac_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
@@ -981,6 +986,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 26a70320e01d..7294be5855d4 100644
--- a/net/ethtool/stats.c
+++ b/net/ethtool/stats.c
@@ -440,3 +440,130 @@ const struct ethnl_request_ops ethnl_stats_request_ops = {
.reply_size = stats_reply_size,
.fill_reply = stats_fill_reply,
};
+
+static u64 ethtool_stats_sum(u64 a, u64 b)
+{
+ if (a == ETHTOOL_STAT_NOT_SET)
+ return b;
+ if (b == ETHTOOL_STAT_NOT_SET)
+ return a;
+ return a + b;
+}
+
+/* Avoid modifying the aggregation procedure every time a new counter is added
+ * by treating the structures as an array of u64 statistics.
+ */
+static void ethtool_aggregate_stats(void *aggr_stats, const void *emac_stats,
+ const void *pmac_stats, size_t stats_size,
+ size_t stats_offset)
+{
+ size_t num_stats = stats_size / sizeof(u64);
+ const u64 *s1 = emac_stats + stats_offset;
+ const u64 *s2 = pmac_stats + stats_offset;
+ u64 *s = aggr_stats + stats_offset;
+ int i;
+
+ for (i = 0; i < num_stats; i++)
+ s[i] = ethtool_stats_sum(s1[i], s2[i]);
+}
+
+void ethtool_aggregate_mac_stats(struct net_device *dev,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ struct ethtool_eth_mac_stats pmac, emac;
+
+ memset(&emac, 0xff, sizeof(emac));
+ memset(&pmac, 0xff, sizeof(pmac));
+ emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
+ pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
+
+ ops->get_eth_mac_stats(dev, &emac);
+ ops->get_eth_mac_stats(dev, &pmac);
+
+ ethtool_aggregate_stats(mac_stats, &emac, &pmac,
+ sizeof(mac_stats->stats),
+ offsetof(struct ethtool_eth_mac_stats, stats));
+}
+EXPORT_SYMBOL(ethtool_aggregate_mac_stats);
+
+void ethtool_aggregate_phy_stats(struct net_device *dev,
+ struct ethtool_eth_phy_stats *phy_stats)
+{
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ struct ethtool_eth_phy_stats pmac, emac;
+
+ memset(&emac, 0xff, sizeof(emac));
+ memset(&pmac, 0xff, sizeof(pmac));
+ emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
+ pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
+
+ ops->get_eth_phy_stats(dev, &emac);
+ ops->get_eth_phy_stats(dev, &pmac);
+
+ ethtool_aggregate_stats(phy_stats, &emac, &pmac,
+ sizeof(phy_stats->stats),
+ offsetof(struct ethtool_eth_phy_stats, stats));
+}
+EXPORT_SYMBOL(ethtool_aggregate_phy_stats);
+
+void ethtool_aggregate_ctrl_stats(struct net_device *dev,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ struct ethtool_eth_ctrl_stats pmac, emac;
+
+ memset(&emac, 0xff, sizeof(emac));
+ memset(&pmac, 0xff, sizeof(pmac));
+ emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
+ pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
+
+ ops->get_eth_ctrl_stats(dev, &emac);
+ ops->get_eth_ctrl_stats(dev, &pmac);
+
+ ethtool_aggregate_stats(ctrl_stats, &emac, &pmac,
+ sizeof(ctrl_stats->stats),
+ offsetof(struct ethtool_eth_ctrl_stats, stats));
+}
+EXPORT_SYMBOL(ethtool_aggregate_ctrl_stats);
+
+void ethtool_aggregate_pause_stats(struct net_device *dev,
+ struct ethtool_pause_stats *pause_stats)
+{
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ struct ethtool_pause_stats pmac, emac;
+
+ memset(&emac, 0xff, sizeof(emac));
+ memset(&pmac, 0xff, sizeof(pmac));
+ emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
+ pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
+
+ ops->get_pause_stats(dev, &emac);
+ ops->get_pause_stats(dev, &pmac);
+
+ ethtool_aggregate_stats(pause_stats, &emac, &pmac,
+ sizeof(pause_stats->stats),
+ offsetof(struct ethtool_pause_stats, stats));
+}
+EXPORT_SYMBOL(ethtool_aggregate_pause_stats);
+
+void ethtool_aggregate_rmon_stats(struct net_device *dev,
+ struct ethtool_rmon_stats *rmon_stats)
+{
+ const struct ethtool_ops *ops = dev->ethtool_ops;
+ const struct ethtool_rmon_hist_range *dummy;
+ struct ethtool_rmon_stats pmac, emac;
+
+ memset(&emac, 0xff, sizeof(emac));
+ memset(&pmac, 0xff, sizeof(pmac));
+ emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
+ pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
+
+ ops->get_rmon_stats(dev, &emac, &dummy);
+ ops->get_rmon_stats(dev, &pmac, &dummy);
+
+ ethtool_aggregate_stats(rmon_stats, &emac, &pmac,
+ sizeof(rmon_stats->stats),
+ offsetof(struct ethtool_rmon_stats, stats));
+}
+EXPORT_SYMBOL(ethtool_aggregate_rmon_stats);
--
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]>
---
v2->v3: adapt to ETHTOOL_STATS_SRC_* -> ETHTOOL_MAC_STATS_SRC_* rename
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 3b738cb2ae6e..7867ca85410f 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -2024,6 +2024,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,
@@ -2031,6 +2039,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 cbcc457499f3..535512280f12 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),
@@ -2623,6 +2660,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..f660eef4a287 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_MAC_STATS_SRC_EMAC:
+ ocelot_port_stats_run(ocelot, port, pause_stats,
+ ocelot_port_pause_stats_cb);
+ break;
+ case ETHTOOL_MAC_STATS_SRC_PMAC:
+ if (ocelot->mm_supported)
+ ocelot_port_stats_run(ocelot, port, pause_stats,
+ ocelot_port_pmac_pause_stats_cb);
+ break;
+ case ETHTOOL_MAC_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_MAC_STATS_SRC_EMAC:
+ ocelot_port_stats_run(ocelot, port, rmon_stats,
+ ocelot_port_rmon_stats_cb);
+ break;
+ case ETHTOOL_MAC_STATS_SRC_PMAC:
+ if (ocelot->mm_supported)
+ ocelot_port_stats_run(ocelot, port, rmon_stats,
+ ocelot_port_pmac_rmon_stats_cb);
+ break;
+ case ETHTOOL_MAC_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_MAC_STATS_SRC_EMAC:
+ ocelot_port_stats_run(ocelot, port, ctrl_stats,
+ ocelot_port_ctrl_stats_cb);
+ break;
+ case ETHTOOL_MAC_STATS_SRC_PMAC:
+ if (ocelot->mm_supported)
+ ocelot_port_stats_run(ocelot, port, ctrl_stats,
+ ocelot_port_pmac_ctrl_stats_cb);
+ break;
+ case ETHTOOL_MAC_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_MAC_STATS_SRC_EMAC:
+ ocelot_port_stats_run(ocelot, port, mac_stats,
+ ocelot_port_mac_stats_cb);
+ break;
+ case ETHTOOL_MAC_STATS_SRC_PMAC:
+ if (ocelot->mm_supported)
+ ocelot_port_stats_run(ocelot, port, mac_stats,
+ ocelot_port_pmac_mac_stats_cb);
+ break;
+ case ETHTOOL_MAC_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_MAC_STATS_SRC_EMAC:
+ ocelot_port_stats_run(ocelot, port, phy_stats,
+ ocelot_port_phy_stats_cb);
+ break;
+ case ETHTOOL_MAC_STATS_SRC_PMAC:
+ if (ocelot->mm_supported)
+ ocelot_port_stats_run(ocelot, port, phy_stats,
+ ocelot_port_pmac_phy_stats_cb);
+ break;
+ case ETHTOOL_MAC_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]>
---
v2->v3:
- adapt to renaming of constants
- add a single kernel-doc reference to enum ethtool_mac_stats_src
(second one gives warning apparently)
v1->v2: patch is new
Documentation/networking/ethtool-netlink.rst | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index 31413535dce5..1626e863eec9 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -1092,8 +1092,18 @@ Request contents:
===================================== ====== ==========================
``ETHTOOL_A_PAUSE_HEADER`` nested request header
+ ``ETHTOOL_A_PAUSE_STATS_SRC`` u32 source of statistics
===================================== ====== ==========================
+``ETHTOOL_A_PAUSE_STATS_SRC`` is optional. It takes values from:
+
+.. kernel-doc:: include/uapi/linux/ethtool.h
+ :identifiers: ethtool_mac_stats_src
+
+If absent from the request, stats will be provided with
+an ``ETHTOOL_A_PAUSE_STATS_SRC`` attribute in the response equal to
+``ETHTOOL_MAC_STATS_SRC_AGGREGATE``.
+
Kernel response contents:
===================================== ====== ==========================
@@ -1508,6 +1518,7 @@ Request contents:
======================================= ====== ==========================
``ETHTOOL_A_STATS_HEADER`` nested request header
+ ``ETHTOOL_A_STATS_SRC`` u32 source of statistics
``ETHTOOL_A_STATS_GROUPS`` bitset requested groups of stats
======================================= ====== ==========================
@@ -1516,6 +1527,8 @@ Kernel response contents:
+-----------------------------------+--------+--------------------------------+
| ``ETHTOOL_A_STATS_HEADER`` | nested | reply header |
+-----------------------------------+--------+--------------------------------+
+ | ``ETHTOOL_A_STATS_SRC`` | u32 | source of statistics |
+ +-----------------------------------+--------+--------------------------------+
| ``ETHTOOL_A_STATS_GRP`` | nested | one or more group of stats |
+-+---------------------------------+--------+--------------------------------+
| | ``ETHTOOL_A_STATS_GRP_ID`` | u32 | group ID - ``ETHTOOL_STATS_*`` |
@@ -1577,6 +1590,11 @@ Low and high bounds are inclusive, for example:
etherStatsPkts512to1023Octets 512 1023
============================= ==== ====
+``ETHTOOL_A_STATS_SRC`` is optional. Similar to ``PAUSE_GET``, it takes values
+from ``enum ethtool_mac_stats_src``. If absent from the request, stats will be
+provided with an ``ETHTOOL_A_STATS_SRC`` attribute in the response equal to
+``ETHTOOL_MAC_STATS_SRC_AGGREGATE``.
+
PHC_VCLOCKS_GET
===============
--
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]>
---
v2->v3:
- adapt to add_frag_size -> tx_min_frag_size rename
- populate rx_min_frag_size = ETH_ZLEN
- demote log level of distracting prints to debug
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 | 214 +++++++++++++++++++++++++
include/soc/mscc/ocelot.h | 18 +++
include/soc/mscc/ocelot_dev.h | 23 +++
8 files changed, 302 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 7867ca85410f..d21e7be2f8c7 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -2024,6 +2024,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 int felix_get_mm(struct dsa_switch *ds, int port,
+ struct ethtool_mm_state *state)
+{
+ struct ocelot *ocelot = ds->priv;
+
+ return ocelot_port_get_mm(ocelot, port, state);
+}
+
+static int felix_set_mm(struct dsa_switch *ds, int port,
+ struct ethtool_mm_cfg *cfg,
+ struct netlink_ext_ack *extack)
+{
+ struct ocelot *ocelot = ds->priv;
+
+ return ocelot_port_set_mm(ocelot, port, cfg, extack);
+}
+
static void felix_get_mm_stats(struct dsa_switch *ds, int port,
struct ethtool_mm_stats *stats)
{
@@ -2039,6 +2056,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 535512280f12..43dc8ed4854d 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),
@@ -2599,20 +2603,19 @@ static const struct felix_info felix_info_vsc9959 = {
.tas_guard_bands_update = vsc9959_tas_guard_bands_update,
};
+/* The INTB interrupt is shared between for PTP TX timestamp availability
+ * notification and MAC Merge status change on each port.
+ */
static irqreturn_t felix_irq_handler(int irq, void *data)
{
struct ocelot *ocelot = (struct ocelot *)data;
-
- /* The INTB interrupt is used for both PTP TX timestamp interrupt
- * and preemption status change interrupt on each port.
- *
- * - Get txtstamp if have
- * - TODO: handle preemption. Without handling it, driver may get
- * interrupt storm.
- */
+ int port;
ocelot_get_txtstamp(ocelot);
+ for (port = 0; port < ocelot->num_phys_ports; port++)
+ ocelot_port_mm_irq(ocelot, port);
+
return IRQ_HANDLED;
}
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..fcec1a0fbb0c
--- /dev/null
+++ b/drivers/net/ethernet/mscc/ocelot_mm.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable
+ * switches (VSC9959)
+ *
+ * Copyright 2022-2023 NXP
+ */
+#include <linux/ethtool.h>
+#include <soc/mscc/ocelot.h>
+#include <soc/mscc/ocelot_dev.h>
+#include <soc/mscc/ocelot_qsys.h>
+
+#include "ocelot.h"
+
+static const char *
+mm_verify_state_to_string(enum ethtool_mm_verify_status state)
+{
+ switch (state) {
+ case ETHTOOL_MM_VERIFY_STATUS_INITIAL:
+ return "INITIAL";
+ case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
+ return "VERIFYING";
+ case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
+ return "SUCCEEDED";
+ case ETHTOOL_MM_VERIFY_STATUS_FAILED:
+ return "FAILED";
+ case ETHTOOL_MM_VERIFY_STATUS_DISABLED:
+ return "DISABLED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val)
+{
+ switch (DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(val)) {
+ case 0:
+ return ETHTOOL_MM_VERIFY_STATUS_INITIAL;
+ case 1:
+ return ETHTOOL_MM_VERIFY_STATUS_VERIFYING;
+ case 2:
+ return ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
+ case 3:
+ return ETHTOOL_MM_VERIFY_STATUS_FAILED;
+ case 4:
+ return ETHTOOL_MM_VERIFY_STATUS_DISABLED;
+ default:
+ return ETHTOOL_MM_VERIFY_STATUS_UNKNOWN;
+ }
+}
+
+void ocelot_port_mm_irq(struct ocelot *ocelot, int port)
+{
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
+ struct ocelot_mm_state *mm = &ocelot->mm[port];
+ enum ethtool_mm_verify_status verify_status;
+ u32 val;
+
+ mutex_lock(&mm->lock);
+
+ val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
+
+ verify_status = ocelot_mm_verify_status(val);
+ if (mm->verify_status != verify_status)
+ dev_dbg(ocelot->dev,
+ "Port %d MAC Merge verification state %s\n",
+ port, mm_verify_state_to_string(verify_status));
+ mm->verify_status = verify_status;
+ }
+
+ if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) {
+ mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS);
+
+ dev_dbg(ocelot->dev, "Port %d TX preemption %s\n",
+ port, mm->tx_active ? "active" : "inactive");
+ }
+
+ if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) {
+ dev_err(ocelot->dev,
+ "Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n",
+ port);
+ }
+
+ if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) {
+ dev_err(ocelot->dev,
+ "Unexpected P-frame requested to be transmitted on port %d while verification was unsuccessful or not yet verified, or MM_TX_ENA=0\n",
+ port);
+ }
+
+ ocelot_port_writel(ocelot_port, val, DEV_MM_STATUS);
+
+ mutex_unlock(&mm->lock);
+}
+EXPORT_SYMBOL_GPL(ocelot_port_mm_irq);
+
+int ocelot_port_set_mm(struct ocelot *ocelot, int port,
+ struct ethtool_mm_cfg *cfg,
+ struct netlink_ext_ack *extack)
+{
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
+ u32 mm_enable = 0, verify_disable = 0, add_frag_size;
+ struct ocelot_mm_state *mm;
+ int err;
+
+ if (!ocelot->mm_supported)
+ return -EOPNOTSUPP;
+
+ mm = &ocelot->mm[port];
+
+ err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size,
+ &add_frag_size, extack);
+ if (err)
+ return err;
+
+ if (cfg->pmac_enabled)
+ mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA;
+
+ if (cfg->tx_enabled)
+ mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA;
+
+ if (!cfg->verify_enabled)
+ verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS;
+
+ mutex_lock(&mm->lock);
+
+ ocelot_port_rmwl(ocelot_port, mm_enable,
+ DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA |
+ DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA,
+ DEV_MM_ENABLE_CONFIG);
+
+ ocelot_port_rmwl(ocelot_port, verify_disable |
+ DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time),
+ DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS |
+ DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M,
+ DEV_MM_VERIF_CONFIG);
+
+ ocelot_rmw_rix(ocelot,
+ QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size),
+ QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M,
+ QSYS_PREEMPTION_CFG,
+ port);
+
+ mutex_unlock(&mm->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ocelot_port_set_mm);
+
+int ocelot_port_get_mm(struct ocelot *ocelot, int port,
+ struct ethtool_mm_state *state)
+{
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
+ struct ocelot_mm_state *mm;
+ u32 val, add_frag_size;
+
+ if (!ocelot->mm_supported)
+ return -EOPNOTSUPP;
+
+ mm = &ocelot->mm[port];
+
+ mutex_lock(&mm->lock);
+
+ val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG);
+ state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA);
+ state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA);
+
+ val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG);
+ state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val);
+ state->max_verify_time = 128;
+
+ val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port);
+ add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val);
+ state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size);
+ state->rx_min_frag_size = ETH_ZLEN;
+
+ state->verify_status = mm->verify_status;
+ state->tx_active = mm->tx_active;
+
+ mutex_unlock(&mm->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ocelot_port_get_mm);
+
+int ocelot_mm_init(struct ocelot *ocelot)
+{
+ struct ocelot_port *ocelot_port;
+ struct ocelot_mm_state *mm;
+ int port;
+
+ if (!ocelot->mm_supported)
+ return 0;
+
+ ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
+ sizeof(*ocelot->mm), GFP_KERNEL);
+ if (!ocelot->mm)
+ return -ENOMEM;
+
+ for (port = 0; port < ocelot->num_phys_ports; port++) {
+ u32 val;
+
+ mm = &ocelot->mm[port];
+ mutex_init(&mm->lock);
+ ocelot_port = ocelot->ports[port];
+
+ /* Update initial status variable for the
+ * verification state machine
+ */
+ val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
+ mm->verify_status = ocelot_mm_verify_status(val);
+ }
+
+ return 0;
+}
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 6de909d79896..afb11680a793 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);
+int ocelot_port_get_mm(struct ocelot *ocelot, int port,
+ struct ethtool_mm_state *state);
+
#if IS_ENABLED(CONFIG_BRIDGE_MRP)
int ocelot_mrp_add(struct ocelot *ocelot, int port,
const struct switchdev_obj_mrp *mrp);
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
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]>
---
v2->v3:
- reformat tables
- delete obsolete netlink attributes and document new ones
v1->v2: patch is new
Documentation/networking/ethtool-netlink.rst | 89 ++++++++++++++++++++
Documentation/networking/statistics.rst | 1 +
2 files changed, 90 insertions(+)
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index d345f5df248e..31413535dce5 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
@@ -1868,6 +1871,90 @@ When set, the ``ETHTOOL_A_PLCA_STATUS`` attribute indicates whether the node is
detecting the presence of the BEACON on the network. This flag is
corresponding to ``IEEE 802.3cg-2019`` 30.16.1.1.2 aPLCAStatus.
+MM_GET
+======
+
+Retrieve 802.3 MAC Merge parameters.
+
+Request contents:
+
+ ==================================== ====== ==========================
+ ``ETHTOOL_A_MM_HEADER`` nested request header
+ ==================================== ====== ==========================
+
+Kernel response contents:
+
+ ================================= ====== ===================================
+ ``ETHTOOL_A_MM_HEADER`` nested request header
+ ``ETHTOOL_A_MM_PMAC_ENABLED`` bool set if RX of preemptible and SMD-V
+ frames is enabled
+ ``ETHTOOL_A_MM_TX_ENABLED`` bool set if TX of preemptible frames is
+ administratively enabled (might be
+ inactive if verification failed)
+ ``ETHTOOL_A_MM_TX_ACTIVE`` bool set if TX of preemptible frames is
+ operationally enabled
+ ``ETHTOOL_A_MM_TX_MIN_FRAG_SIZE`` u32 minimum size of transmitted
+ non-final fragments, in octets
+ ``ETHTOOL_A_MM_RX_MIN_FRAG_SIZE`` u32 minimum size of received non-final
+ fragments, in octets
+ ``ETHTOOL_A_MM_VERIFY_ENABLED`` bool set if TX of SMD-V frames is
+ administratively enabled
+ ``ETHTOOL_A_MM_VERIFY_STATUS`` u8 state of the verification function
+ ``ETHTOOL_A_MM_VERIFY_TIME`` u32 delay between verification attempts
+ ``ETHTOOL_A_MM_MAX_VERIFY_TIME``` u32 maximum verification interval
+ supported by device
+ ``ETHTOOL_A_MM_STATS`` nested IEEE 802.3-2018 subclause 30.14.1
+ oMACMergeEntity statistics counters
+ ================================= ====== ===================================
+
+The attributes are populated by the device driver through the following
+structure:
+
+.. kernel-doc:: include/linux/ethtool.h
+ :identifiers: ethtool_mm_state
+
+The ``ETHTOOL_A_MM_VERIFY_STATUS`` will report one of the values from
+
+.. kernel-doc:: include/uapi/linux/ethtool.h
+ :identifiers: ethtool_mm_verify_status
+
+If ``ETHTOOL_A_MM_VERIFY_ENABLED`` was passed as false in the ``MM_SET``
+command, ``ETHTOOL_A_MM_VERIFY_STATUS`` will report either
+``ETHTOOL_MM_VERIFY_STATUS_INITIAL`` or ``ETHTOOL_MM_VERIFY_STATUS_DISABLED``,
+otherwise it should report one of the other states.
+
+It is recommended that drivers start with the pMAC disabled, and enable it upon
+user space request. It is also recommended that user space does not depend upon
+the default values from ``ETHTOOL_MSG_MM_GET`` requests.
+
+``ETHTOOL_A_MM_STATS`` are reported if ``ETHTOOL_FLAG_STATS`` was set in
+``ETHTOOL_A_HEADER_FLAGS``. The attribute will be empty if driver did not
+report any statistics. Drivers fill in the statistics in the following
+structure:
+
+.. kernel-doc:: include/linux/ethtool.h
+ :identifiers: ethtool_mm_stats
+
+MM_SET
+======
+
+Modifies the configuration of the 802.3 MAC Merge layer.
+
+Request contents:
+
+ ================================= ====== ==========================
+ ``ETHTOOL_A_MM_VERIFY_TIME`` u32 see MM_GET description
+ ``ETHTOOL_A_MM_VERIFY_ENABLED`` bool see MM_GET description
+ ``ETHTOOL_A_MM_TX_ENABLED`` bool see MM_GET description
+ ``ETHTOOL_A_MM_PMAC_ENABLED`` bool see MM_GET description
+ ``ETHTOOL_A_MM_TX_MIN_FRAG_SIZE`` u32 see MM_GET description
+ ================================= ====== ==========================
+
+The attributes are propagated to the driver through the following structure:
+
+.. kernel-doc:: include/linux/ethtool.h
+ :identifiers: ethtool_mm_cfg
+
Request translation
===================
@@ -1972,4 +2059,6 @@ are netlink only.
n/a ``ETHTOOL_MSG_PLCA_GET_CFG``
n/a ``ETHTOOL_MSG_PLCA_SET_CFG``
n/a ``ETHTOOL_MSG_PLCA_GET_STATUS``
+ n/a ``ETHTOOL_MSG_MM_GET``
+ n/a ``ETHTOOL_MSG_MM_SET``
=================================== =====================================
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
We deliberately make the Linux UAPI pass the minimum 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 the addFragSize multiplier,
create two helpers which translate back and forth the values passed in
octets.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v2->v3:
- adapt to renaming of "add_frag_size" to "min_frag_size"
- use some macros instead of 4 and 64
v1->v2: patch is new
include/linux/ethtool.h | 42 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 6746dee5a3fd..6a8253d3fea8 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -15,6 +15,7 @@
#include <linux/bitmap.h>
#include <linux/compat.h>
+#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <uapi/linux/ethtool.h>
@@ -1001,6 +1002,47 @@ 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_frag_size_add_to_min - Translate (standard) additional fragment
+ * size expressed as multiplier into (absolute) minimum fragment size
+ * value expressed in octets
+ * @val_add: Value of addFragSize multiplier
+ */
+static inline u32 ethtool_mm_frag_size_add_to_min(u32 val_add)
+{
+ return (ETH_ZLEN + ETH_FCS_LEN) * (1 + val_add) - ETH_FCS_LEN;
+}
+
+/**
+ * ethtool_mm_frag_size_min_to_add - Translate (absolute) minimum fragment size
+ * expressed in octets into (standard) additional fragment size expressed
+ * as multiplier
+ * @val_min: Value of addFragSize variable in octets
+ * @val_add: 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 the minimum fragment size to a
+ * continuous range. Returns error on other fragment length values.
+ */
+static inline int ethtool_mm_frag_size_min_to_add(u32 val_min, u32 *val_add,
+ struct netlink_ext_ack *extack)
+{
+ u32 add_frag_size;
+
+ for (add_frag_size = 0; add_frag_size < 4; add_frag_size++) {
+ if (ethtool_mm_frag_size_add_to_min(add_frag_size) == val_min) {
+ *val_add = add_frag_size;
+ return 0;
+ }
+ }
+
+ NL_SET_ERR_MSG_MOD(extack,
+ "minFragSize 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
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.
- ETHTOOL_A_MM_TX_MIN_FRAG_SIZE: the standard managed object is called
aMACMergeAddFragSize, and expresses the "additional" fragment size
(on top of ETH_ZLEN), whereas this expresses the absolute value of the
fragment size.
- ETHTOOL_A_MM_RX_MIN_FRAG_SIZE: there doesn't appear to exist a managed
object mandated by the standard, but user space clearly needs to know
what is the minimum supported fragment size of our local receiver,
since LLDP must advertise a value no lower than that.
Signed-off-by: Vladimir Oltean <[email protected]>
---
v2->v3:
- deleted ETHTOOL_A_MM_SUPPORTED (to be deduced from return value of
ETHTOOL_MSG_MM_GET)
- made get_mm return int instead of void
- renamed "add_frag_size" to "tx_min_frag_size"
- added "rx_min_frag_size" as a read-only netlink attribute
- rebase on top of PLCA changes
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 | 255 +++++++++++++++++++++++++++
net/ethtool/netlink.c | 19 ++
net/ethtool/netlink.h | 4 +
7 files changed, 451 insertions(+), 2 deletions(-)
create mode 100644 net/ethtool/mm.c
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 20d197693d34..37eba38da502 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -477,6 +477,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)
+ * @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.
+ * @tx_min_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.
+ * @rx_min_frag_size:
+ * the minimum size of non-final mPacket fragments that this device
+ * supports receiving, expressed in octets.
+ */
+struct ethtool_mm_state {
+ u32 verify_time;
+ u32 max_verify_time;
+ enum ethtool_mm_verify_status verify_status;
+ bool tx_enabled;
+ bool tx_active;
+ bool pmac_enabled;
+ bool verify_enabled;
+ u32 tx_min_frag_size;
+ u32 rx_min_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
+ * @tx_min_frag_size: see struct ethtool_mm_state
+ */
+struct ethtool_mm_cfg {
+ u32 verify_time;
+ bool verify_enabled;
+ bool tx_enabled;
+ bool pmac_enabled;
+ u32 tx_min_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
@@ -649,6 +741,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
@@ -787,6 +882,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);
+ int (*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 6389953c32cf..529a93696ab6 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 83557cae0b87..58af390823b0 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -55,6 +55,8 @@ enum {
ETHTOOL_MSG_PLCA_GET_CFG,
ETHTOOL_MSG_PLCA_SET_CFG,
ETHTOOL_MSG_PLCA_GET_STATUS,
+ ETHTOOL_MSG_MM_GET,
+ ETHTOOL_MSG_MM_SET,
/* add new constants above here */
__ETHTOOL_MSG_USER_CNT,
@@ -105,6 +107,8 @@ enum {
ETHTOOL_MSG_PLCA_GET_CFG_REPLY,
ETHTOOL_MSG_PLCA_GET_STATUS_REPLY,
ETHTOOL_MSG_PLCA_NTF,
+ ETHTOOL_MSG_MM_GET_REPLY,
+ ETHTOOL_MSG_MM_NTF,
/* add new constants above here */
__ETHTOOL_MSG_KERNEL_CNT,
@@ -922,6 +926,49 @@ enum {
ETHTOOL_A_PLCA_MAX = (__ETHTOOL_A_PLCA_CNT - 1)
};
+/* MAC Merge (802.3) */
+
+enum {
+ ETHTOOL_A_MM_STAT_UNSPEC,
+ ETHTOOL_A_MM_STAT_PAD,
+
+ /* aMACMergeFrameAssErrorCount */
+ ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS, /* u64 */
+ /* aMACMergeFrameSmdErrorCount */
+ ETHTOOL_A_MM_STAT_SMD_ERRORS, /* u64 */
+ /* aMACMergeFrameAssOkCount */
+ ETHTOOL_A_MM_STAT_REASSEMBLY_OK, /* u64 */
+ /* aMACMergeFragCountRx */
+ ETHTOOL_A_MM_STAT_RX_FRAG_COUNT, /* u64 */
+ /* aMACMergeFragCountTx */
+ ETHTOOL_A_MM_STAT_TX_FRAG_COUNT, /* u64 */
+ /* aMACMergeHoldCount */
+ ETHTOOL_A_MM_STAT_HOLD_COUNT, /* u64 */
+
+ /* add new constants above here */
+ __ETHTOOL_A_MM_STAT_CNT,
+ ETHTOOL_A_MM_STAT_MAX = (__ETHTOOL_A_MM_STAT_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_MM_UNSPEC,
+ ETHTOOL_A_MM_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_MM_PMAC_ENABLED, /* u8 */
+ ETHTOOL_A_MM_TX_ENABLED, /* u8 */
+ ETHTOOL_A_MM_TX_ACTIVE, /* u8 */
+ ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, /* u32 */
+ ETHTOOL_A_MM_RX_MIN_FRAG_SIZE, /* u32 */
+ ETHTOOL_A_MM_VERIFY_ENABLED, /* u8 */
+ ETHTOOL_A_MM_VERIFY_STATUS, /* u8 */
+ ETHTOOL_A_MM_VERIFY_TIME, /* u32 */
+ ETHTOOL_A_MM_MAX_VERIFY_TIME, /* u32 */
+ ETHTOOL_A_MM_STATS, /* nest - _A_MM_STAT_* */
+
+ /* add new constants above here */
+ __ETHTOOL_A_MM_CNT,
+ ETHTOOL_A_MM_MAX = (__ETHTOOL_A_MM_CNT - 1)
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 563864c1bf5a..504f954a1b28 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 plca.o
+ tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \
+ module.o pse-pd.o plca.o mm.o
diff --git a/net/ethtool/mm.c b/net/ethtool/mm.c
new file mode 100644
index 000000000000..0c8135393c14
--- /dev/null
+++ b/net/ethtool/mm.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2022-2023 NXP
+ */
+#include "common.h"
+#include "netlink.h"
+
+struct mm_req_info {
+ struct ethnl_req_info base;
+};
+
+struct mm_reply_data {
+ struct ethnl_reply_data base;
+ struct ethtool_mm_state state;
+ struct ethtool_mm_stats stats;
+};
+
+#define MM_REPDATA(__reply_base) \
+ container_of(__reply_base, struct mm_reply_data, base)
+
+#define ETHTOOL_MM_STAT_CNT \
+ (__ETHTOOL_A_MM_STAT_CNT - (ETHTOOL_A_MM_STAT_PAD + 1))
+
+const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1] = {
+ [ETHTOOL_A_MM_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_stats),
+};
+
+static int mm_prepare_data(const struct ethnl_req_info *req_base,
+ struct ethnl_reply_data *reply_base,
+ struct genl_info *info)
+{
+ struct mm_reply_data *data = MM_REPDATA(reply_base);
+ struct net_device *dev = reply_base->dev;
+ const struct ethtool_ops *ops;
+ int ret;
+
+ ops = dev->ethtool_ops;
+
+ if (!ops->get_mm)
+ return -EOPNOTSUPP;
+
+ ethtool_stats_init((u64 *)&data->stats,
+ sizeof(data->stats) / sizeof(u64));
+
+ ret = ethnl_ops_begin(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = ops->get_mm(dev, &data->state);
+ if (ret)
+ goto out_complete;
+
+ if (ops->get_mm_stats && (req_base->flags & ETHTOOL_FLAG_STATS))
+ ops->get_mm_stats(dev, &data->stats);
+
+out_complete:
+ ethnl_ops_complete(dev);
+
+ return 0;
+}
+
+static int mm_reply_size(const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ int len = 0;
+
+ len += nla_total_size(sizeof(u8)); /* _MM_PMAC_ENABLED */
+ len += nla_total_size(sizeof(u8)); /* _MM_TX_ENABLED */
+ len += nla_total_size(sizeof(u8)); /* _MM_TX_ACTIVE */
+ len += nla_total_size(sizeof(u8)); /* _MM_VERIFY_ENABLED */
+ len += nla_total_size(sizeof(u8)); /* _MM_VERIFY_STATUS */
+ len += nla_total_size(sizeof(u32)); /* _MM_VERIFY_TIME */
+ len += nla_total_size(sizeof(u32)); /* _MM_MAX_VERIFY_TIME */
+ len += nla_total_size(sizeof(u32)); /* _MM_TX_MIN_FRAG_SIZE */
+ len += nla_total_size(sizeof(u32)); /* _MM_RX_MIN_FRAG_SIZE */
+
+ if (req_base->flags & ETHTOOL_FLAG_STATS)
+ len += nla_total_size(0) + /* _MM_STATS */
+ nla_total_size_64bit(sizeof(u64)) * ETHTOOL_MM_STAT_CNT;
+
+ return len;
+}
+
+static int mm_put_stat(struct sk_buff *skb, u64 val, u16 attrtype)
+{
+ if (val == ETHTOOL_STAT_NOT_SET)
+ return 0;
+ if (nla_put_u64_64bit(skb, attrtype, val, ETHTOOL_A_MM_STAT_PAD))
+ return -EMSGSIZE;
+ return 0;
+}
+
+static int mm_put_stats(struct sk_buff *skb,
+ const struct ethtool_mm_stats *stats)
+{
+ struct nlattr *nest;
+
+ nest = nla_nest_start(skb, ETHTOOL_A_MM_STATS);
+ if (!nest)
+ return -EMSGSIZE;
+
+ if (mm_put_stat(skb, stats->MACMergeFrameAssErrorCount,
+ ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS) ||
+ mm_put_stat(skb, stats->MACMergeFrameSmdErrorCount,
+ ETHTOOL_A_MM_STAT_SMD_ERRORS) ||
+ mm_put_stat(skb, stats->MACMergeFrameAssOkCount,
+ ETHTOOL_A_MM_STAT_REASSEMBLY_OK) ||
+ mm_put_stat(skb, stats->MACMergeFragCountRx,
+ ETHTOOL_A_MM_STAT_RX_FRAG_COUNT) ||
+ mm_put_stat(skb, stats->MACMergeFragCountTx,
+ ETHTOOL_A_MM_STAT_TX_FRAG_COUNT) ||
+ mm_put_stat(skb, stats->MACMergeHoldCount,
+ ETHTOOL_A_MM_STAT_HOLD_COUNT))
+ goto err_cancel;
+
+ nla_nest_end(skb, nest);
+ return 0;
+
+err_cancel:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
+static int mm_fill_reply(struct sk_buff *skb,
+ const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ const struct mm_reply_data *data = MM_REPDATA(reply_base);
+ const struct ethtool_mm_state *state = &data->state;
+
+ if (nla_put_u8(skb, ETHTOOL_A_MM_TX_ENABLED, state->tx_enabled) ||
+ nla_put_u8(skb, ETHTOOL_A_MM_TX_ACTIVE, state->tx_active) ||
+ nla_put_u8(skb, ETHTOOL_A_MM_PMAC_ENABLED, state->pmac_enabled) ||
+ nla_put_u8(skb, ETHTOOL_A_MM_VERIFY_ENABLED, state->verify_enabled) ||
+ nla_put_u8(skb, ETHTOOL_A_MM_VERIFY_STATUS, state->verify_status) ||
+ nla_put_u32(skb, ETHTOOL_A_MM_VERIFY_TIME, state->verify_time) ||
+ nla_put_u32(skb, ETHTOOL_A_MM_MAX_VERIFY_TIME, state->max_verify_time) ||
+ nla_put_u32(skb, ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, state->tx_min_frag_size) ||
+ nla_put_u32(skb, ETHTOOL_A_MM_RX_MIN_FRAG_SIZE, state->rx_min_frag_size))
+ return -EMSGSIZE;
+
+ if (req_base->flags & ETHTOOL_FLAG_STATS &&
+ mm_put_stats(skb, &data->stats))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+const struct ethnl_request_ops ethnl_mm_request_ops = {
+ .request_cmd = ETHTOOL_MSG_MM_GET,
+ .reply_cmd = ETHTOOL_MSG_MM_GET_REPLY,
+ .hdr_attr = ETHTOOL_A_MM_HEADER,
+ .req_info_size = sizeof(struct mm_req_info),
+ .reply_data_size = sizeof(struct mm_reply_data),
+
+ .prepare_data = mm_prepare_data,
+ .reply_size = mm_reply_size,
+ .fill_reply = mm_fill_reply,
+};
+
+const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1] = {
+ [ETHTOOL_A_MM_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
+ [ETHTOOL_A_MM_VERIFY_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1),
+ [ETHTOOL_A_MM_VERIFY_TIME] = NLA_POLICY_RANGE(NLA_U32, 1, 128),
+ [ETHTOOL_A_MM_TX_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1),
+ [ETHTOOL_A_MM_PMAC_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1),
+ [ETHTOOL_A_MM_TX_MIN_FRAG_SIZE] = NLA_POLICY_RANGE(NLA_U32, 60, 252),
+};
+
+static void mm_state_to_cfg(const struct ethtool_mm_state *state,
+ struct ethtool_mm_cfg *cfg)
+{
+ /* We could also compare state->verify_status against
+ * ETHTOOL_MM_VERIFY_STATUS_DISABLED, but state->verify_enabled
+ * is more like an administrative state which should be seen in
+ * ETHTOOL_MSG_MM_GET replies. For example, a port with verification
+ * disabled might be in the ETHTOOL_MM_VERIFY_STATUS_INITIAL
+ * if it's down.
+ */
+ cfg->verify_enabled = state->verify_enabled;
+ cfg->verify_time = state->verify_time;
+ cfg->tx_enabled = state->tx_enabled;
+ cfg->pmac_enabled = state->pmac_enabled;
+ cfg->tx_min_frag_size = state->tx_min_frag_size;
+}
+
+int ethnl_set_mm(struct sk_buff *skb, struct genl_info *info)
+{
+ struct netlink_ext_ack *extack = info->extack;
+ struct ethnl_req_info req_info = {};
+ struct ethtool_mm_state state = {};
+ struct nlattr **tb = info->attrs;
+ struct ethtool_mm_cfg cfg = {};
+ const struct ethtool_ops *ops;
+ struct net_device *dev;
+ bool mod = false;
+ int ret;
+
+ ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_MM_HEADER],
+ genl_info_net(info), extack, true);
+ if (ret)
+ return ret;
+
+ dev = req_info.dev;
+ ops = dev->ethtool_ops;
+
+ if (!ops->get_mm || !ops->set_mm) {
+ ret = -EOPNOTSUPP;
+ goto out_dev_put;
+ }
+
+ rtnl_lock();
+ ret = ethnl_ops_begin(dev);
+ if (ret < 0)
+ goto out_rtnl_unlock;
+
+ ret = ops->get_mm(dev, &state);
+ if (ret)
+ goto out_complete;
+
+ mm_state_to_cfg(&state, &cfg);
+
+ if (cfg.verify_time > state.max_verify_time) {
+ NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_MM_VERIFY_TIME],
+ "verifyTime exceeds device maximum");
+ ret = -ERANGE;
+ goto out_complete;
+ }
+
+ ethnl_update_bool(&cfg.verify_enabled, tb[ETHTOOL_A_MM_VERIFY_ENABLED],
+ &mod);
+ ethnl_update_u32(&cfg.verify_time, tb[ETHTOOL_A_MM_VERIFY_TIME], &mod);
+ ethnl_update_bool(&cfg.tx_enabled, tb[ETHTOOL_A_MM_TX_ENABLED], &mod);
+ ethnl_update_bool(&cfg.pmac_enabled, tb[ETHTOOL_A_MM_PMAC_ENABLED],
+ &mod);
+ ethnl_update_u32(&cfg.tx_min_frag_size,
+ tb[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE], &mod);
+
+ if (!mod)
+ goto out_complete;
+
+ ret = ops->set_mm(dev, &cfg, extack);
+ if (ret)
+ goto out_complete;
+
+ ethtool_notify(dev, ETHTOOL_MSG_MM_NTF, NULL);
+
+out_complete:
+ ethnl_ops_complete(dev);
+out_rtnl_unlock:
+ rtnl_unlock();
+out_dev_put:
+ ethnl_parse_header_dev_put(&req_info);
+ return ret;
+}
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 9f924875bba9..6412c4dc663d 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -290,6 +290,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_RSS_GET] = ðnl_rss_request_ops,
[ETHTOOL_MSG_PLCA_GET_CFG] = ðnl_plca_cfg_request_ops,
[ETHTOOL_MSG_PLCA_GET_STATUS] = ðnl_plca_status_request_ops,
+ [ETHTOOL_MSG_MM_GET] = ðnl_mm_request_ops,
};
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@@ -606,6 +607,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = {
[ETHTOOL_MSG_FEC_NTF] = ðnl_fec_request_ops,
[ETHTOOL_MSG_MODULE_NTF] = ðnl_module_request_ops,
[ETHTOOL_MSG_PLCA_NTF] = ðnl_plca_cfg_request_ops,
+ [ETHTOOL_MSG_MM_NTF] = ðnl_mm_request_ops,
};
/* default notification handler */
@@ -700,6 +702,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = {
[ETHTOOL_MSG_FEC_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_MODULE_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_PLCA_NTF] = ethnl_default_notify,
+ [ETHTOOL_MSG_MM_NTF] = ethnl_default_notify,
};
void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data)
@@ -1076,6 +1079,22 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_plca_get_status_policy,
.maxattr = ARRAY_SIZE(ethnl_plca_get_status_policy) - 1,
},
+ {
+ .cmd = ETHTOOL_MSG_MM_GET,
+ .doit = ethnl_default_doit,
+ .start = ethnl_default_start,
+ .dumpit = ethnl_default_dumpit,
+ .done = ethnl_default_done,
+ .policy = ethnl_mm_get_policy,
+ .maxattr = ARRAY_SIZE(ethnl_mm_get_policy) - 1,
+ },
+ {
+ .cmd = ETHTOOL_MSG_MM_SET,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .doit = ethnl_set_mm,
+ .policy = ethnl_mm_set_policy,
+ .maxattr = ARRAY_SIZE(ethnl_mm_set_policy) - 1,
+ },
};
static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index f675f62fe181..60278485b00b 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -375,6 +375,7 @@ extern const struct ethnl_request_ops ethnl_pse_request_ops;
extern const struct ethnl_request_ops ethnl_rss_request_ops;
extern const struct ethnl_request_ops ethnl_plca_cfg_request_ops;
extern const struct ethnl_request_ops ethnl_plca_status_request_ops;
+extern const struct ethnl_request_ops ethnl_mm_request_ops;
extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
@@ -419,6 +420,8 @@ extern const struct nla_policy ethnl_rss_get_policy[ETHTOOL_A_RSS_CONTEXT + 1];
extern const struct nla_policy ethnl_plca_get_cfg_policy[ETHTOOL_A_PLCA_HEADER + 1];
extern const struct nla_policy ethnl_plca_set_cfg_policy[ETHTOOL_A_PLCA_MAX + 1];
extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1];
+extern const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1];
+extern const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1];
int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info);
@@ -440,6 +443,7 @@ int ethnl_set_fec(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_module(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_pse(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_plca_cfg(struct sk_buff *skb, struct genl_info *info);
+int ethnl_set_mm(struct sk_buff *skb, struct genl_info *info);
extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN];
extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
--
2.34.1
On Tue, 17 Jan 2023 10:59:47 +0200 Vladimir Oltean wrote:
> 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.
Doesn't build now.
On Tue, Jan 17, 2023 at 07:52:21PM -0800, Jakub Kicinski wrote:
> On Tue, 17 Jan 2023 10:59:47 +0200 Vladimir Oltean wrote:
> > 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.
>
> Doesn't build now.
Right. In an incredibly stupid last-minute move, I turned the dev_info()
into a dev_dbg() here, but I apparently deleted the opening bracket too:
verify_status = ocelot_mm_verify_status(val);
if (mm->verify_status != verify_status)
dev_dbg(ocelot->dev,
"Port %d MAC Merge verification state %s\n",
port, mm_verify_state_to_string(verify_status));
mm->verify_status = verify_status;
}
I'll wait for some more comments from the TSN-inclined people (if the
netlink parts are okay with you) regarding the UAPI before reposting.