Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: dsa: append ethtool counters of all hidden ports to conduit

Currently there is no way to see packet counters on cascade ports, and
no clarity on how the API for that would look like.

Because it's something that is currently needed, just extend the hack
where ethtool -S on the conduit interface dumps CPU port counters, and
also use it to dump counters of cascade ports.

Note that the "pXX_" naming convention changes to "sXX_pYY", to
distinguish between ports having the same index but belonging to
different switches. This has a slight chance of causing regressions to
existing tooling:

- grepping for "p04_counter_name" still works, but might return more
than one string now
- grepping for " p04_counter_name" no longer works

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20251122112311.138784-4-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Vladimir Oltean and committed by
Jakub Kicinski
f647ed2c 8afabd27

+94 -34
+94 -34
net/dsa/conduit.c
··· 87 87 } 88 88 } 89 89 90 + static ssize_t dsa_conduit_append_port_stats(struct dsa_switch *ds, int port, 91 + u64 *data, size_t start) 92 + { 93 + int count; 94 + 95 + if (!ds->ops->get_sset_count) 96 + return 0; 97 + 98 + count = ds->ops->get_sset_count(ds, port, ETH_SS_STATS); 99 + if (count < 0) 100 + return count; 101 + 102 + if (ds->ops->get_ethtool_stats) 103 + ds->ops->get_ethtool_stats(ds, port, data + start); 104 + 105 + return count; 106 + } 107 + 90 108 static void dsa_conduit_get_ethtool_stats(struct net_device *dev, 91 109 struct ethtool_stats *stats, 92 110 u64 *data) 93 111 { 94 - struct dsa_port *cpu_dp = dev->dsa_ptr; 112 + struct dsa_port *dp, *cpu_dp = dev->dsa_ptr; 95 113 const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 96 - struct dsa_switch *ds = cpu_dp->ds; 97 - int port = cpu_dp->index; 98 - int count = 0; 114 + struct dsa_switch_tree *dst = cpu_dp->dst; 115 + int count, mcount = 0; 99 116 100 117 if (ops && ops->get_sset_count && ops->get_ethtool_stats) { 101 118 netdev_lock_ops(dev); 102 - count = ops->get_sset_count(dev, ETH_SS_STATS); 119 + mcount = ops->get_sset_count(dev, ETH_SS_STATS); 103 120 ops->get_ethtool_stats(dev, stats, data); 104 121 netdev_unlock_ops(dev); 105 122 } 106 123 107 - if (ds->ops->get_ethtool_stats) 108 - ds->ops->get_ethtool_stats(ds, port, data + count); 124 + list_for_each_entry(dp, &dst->ports, list) { 125 + if (!dsa_port_is_dsa(dp) && !dsa_port_is_cpu(dp)) 126 + continue; 127 + 128 + count = dsa_conduit_append_port_stats(dp->ds, dp->index, 129 + data, mcount); 130 + if (count < 0) 131 + return; 132 + 133 + mcount += count; 134 + } 109 135 } 110 136 111 137 static void dsa_conduit_get_ethtool_phy_stats(struct net_device *dev, ··· 162 136 ds->ops->get_ethtool_phy_stats(ds, port, data + count); 163 137 } 164 138 139 + static void dsa_conduit_append_port_sset_count(struct dsa_switch *ds, int port, 140 + int sset, int *count) 141 + { 142 + if (ds->ops->get_sset_count) 143 + *count += ds->ops->get_sset_count(ds, port, sset); 144 + } 145 + 165 146 static int dsa_conduit_get_sset_count(struct net_device *dev, int sset) 166 147 { 167 - struct dsa_port *cpu_dp = dev->dsa_ptr; 148 + struct dsa_port *dp, *cpu_dp = dev->dsa_ptr; 168 149 const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 169 - struct dsa_switch *ds = cpu_dp->ds; 150 + struct dsa_switch_tree *dst = cpu_dp->dst; 170 151 int count = 0; 171 152 172 153 netdev_lock_ops(dev); ··· 187 154 if (count < 0) 188 155 count = 0; 189 156 190 - if (ds->ops->get_sset_count) 191 - count += ds->ops->get_sset_count(ds, cpu_dp->index, sset); 157 + list_for_each_entry(dp, &dst->ports, list) { 158 + if (!dsa_port_is_dsa(dp) && !dsa_port_is_cpu(dp)) 159 + continue; 160 + 161 + dsa_conduit_append_port_sset_count(dp->ds, dp->index, sset, 162 + &count); 163 + } 164 + 165 + return count; 166 + } 167 + 168 + static ssize_t dsa_conduit_append_port_strings(struct dsa_switch *ds, int port, 169 + u32 stringset, u8 *data, 170 + size_t start) 171 + { 172 + int len = ETH_GSTRING_LEN; 173 + u8 pfx[8], *ndata; 174 + int count, i; 175 + 176 + if (!ds->ops->get_strings) 177 + return 0; 178 + 179 + snprintf(pfx, sizeof(pfx), "s%.2d_p%.2d", ds->index, port); 180 + /* We do not want to be NULL-terminated, since this is a prefix */ 181 + pfx[sizeof(pfx) - 1] = '_'; 182 + ndata = data + start * len; 183 + /* This function copies ETH_GSTRINGS_LEN bytes, we will mangle 184 + * the output after to prepend our CPU port prefix we 185 + * constructed earlier 186 + */ 187 + ds->ops->get_strings(ds, port, stringset, ndata); 188 + count = ds->ops->get_sset_count(ds, port, stringset); 189 + if (count < 0) 190 + return count; 191 + 192 + for (i = 0; i < count; i++) { 193 + memmove(ndata + (i * len + sizeof(pfx)), 194 + ndata + i * len, len - sizeof(pfx)); 195 + memcpy(ndata + i * len, pfx, sizeof(pfx)); 196 + } 192 197 193 198 return count; 194 199 } ··· 234 163 static void dsa_conduit_get_strings(struct net_device *dev, u32 stringset, 235 164 u8 *data) 236 165 { 237 - struct dsa_port *cpu_dp = dev->dsa_ptr; 166 + struct dsa_port *dp, *cpu_dp = dev->dsa_ptr; 238 167 const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; 239 - struct dsa_switch *ds = cpu_dp->ds; 240 - int port = cpu_dp->index; 241 - int len = ETH_GSTRING_LEN; 242 - int mcount = 0, count, i; 243 - u8 pfx[4], *ndata; 244 - 245 - snprintf(pfx, sizeof(pfx), "p%.2d", port); 246 - /* We do not want to be NULL-terminated, since this is a prefix */ 247 - pfx[sizeof(pfx) - 1] = '_'; 168 + struct dsa_switch_tree *dst = cpu_dp->dst; 169 + int count, mcount = 0; 248 170 249 171 netdev_lock_ops(dev); 250 172 if (stringset == ETH_SS_PHY_STATS && dev->phydev && ··· 255 191 } 256 192 netdev_unlock_ops(dev); 257 193 258 - if (ds->ops->get_strings) { 259 - ndata = data + mcount * len; 260 - /* This function copies ETH_GSTRINGS_LEN bytes, we will mangle 261 - * the output after to prepend our CPU port prefix we 262 - * constructed earlier 263 - */ 264 - ds->ops->get_strings(ds, port, stringset, ndata); 265 - count = ds->ops->get_sset_count(ds, port, stringset); 194 + list_for_each_entry(dp, &dst->ports, list) { 195 + if (!dsa_port_is_dsa(dp) && !dsa_port_is_cpu(dp)) 196 + continue; 197 + 198 + count = dsa_conduit_append_port_strings(dp->ds, dp->index, 199 + stringset, data, 200 + mcount); 266 201 if (count < 0) 267 202 return; 268 - for (i = 0; i < count; i++) { 269 - memmove(ndata + (i * len + sizeof(pfx)), 270 - ndata + i * len, len - sizeof(pfx)); 271 - memcpy(ndata + i * len, pfx, sizeof(pfx)); 272 - } 203 + 204 + mcount += count; 273 205 } 274 206 } 275 207