Blame view
net/dsa/master.c
9.15 KB
2874c5fd2 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
f2f235668 net: dsa: move ma... |
2 3 4 5 6 |
/* * Handling of a master device, switching frames via its switch fabric CPU port * * Copyright (c) 2017 Savoir-faire Linux Inc. * Vivien Didelot <vivien.didelot@savoirfairelinux.com> |
f2f235668 net: dsa: move ma... |
7 8 9 |
*/ #include "dsa_priv.h" |
48e233119 net: dsa: dump CP... |
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
static int dsa_master_get_regs_len(struct net_device *dev) { struct dsa_port *cpu_dp = dev->dsa_ptr; const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; int port = cpu_dp->index; int ret = 0; int len; if (ops->get_regs_len) { len = ops->get_regs_len(dev); if (len < 0) return len; ret += len; } ret += sizeof(struct ethtool_drvinfo); ret += sizeof(struct ethtool_regs); if (ds->ops->get_regs_len) { len = ds->ops->get_regs_len(ds, port); if (len < 0) return len; ret += len; } return ret; } static void dsa_master_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *data) { struct dsa_port *cpu_dp = dev->dsa_ptr; const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; struct ethtool_drvinfo *cpu_info; struct ethtool_regs *cpu_regs; int port = cpu_dp->index; int len; if (ops->get_regs_len && ops->get_regs) { len = ops->get_regs_len(dev); if (len < 0) return; regs->len = len; ops->get_regs(dev, regs, data); data += regs->len; } cpu_info = (struct ethtool_drvinfo *)data; strlcpy(cpu_info->driver, "dsa", sizeof(cpu_info->driver)); data += sizeof(*cpu_info); cpu_regs = (struct ethtool_regs *)data; data += sizeof(*cpu_regs); if (ds->ops->get_regs_len && ds->ops->get_regs) { len = ds->ops->get_regs_len(ds, port); if (len < 0) return; cpu_regs->len = len; ds->ops->get_regs(ds, port, cpu_regs, data); } } |
f2f235668 net: dsa: move ma... |
73 74 75 76 |
static void dsa_master_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, uint64_t *data) { |
2f657a600 net: dsa: change ... |
77 |
struct dsa_port *cpu_dp = dev->dsa_ptr; |
7ec764eef net: dsa: use cpu... |
78 79 80 |
const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; int port = cpu_dp->index; |
f2f235668 net: dsa: move ma... |
81 |
int count = 0; |
1d1e79f1c net: dsa: Do not ... |
82 |
if (ops->get_sset_count && ops->get_ethtool_stats) { |
f2f235668 net: dsa: move ma... |
83 84 85 86 87 |
count = ops->get_sset_count(dev, ETH_SS_STATS); ops->get_ethtool_stats(dev, stats, data); } if (ds->ops->get_ethtool_stats) |
7ec764eef net: dsa: use cpu... |
88 |
ds->ops->get_ethtool_stats(ds, port, data + count); |
f2f235668 net: dsa: move ma... |
89 |
} |
cf9635730 net: dsa: Allow p... |
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
static void dsa_master_get_ethtool_phy_stats(struct net_device *dev, struct ethtool_stats *stats, uint64_t *data) { struct dsa_port *cpu_dp = dev->dsa_ptr; const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; int port = cpu_dp->index; int count = 0; if (dev->phydev && !ops->get_ethtool_phy_stats) { count = phy_ethtool_get_sset_count(dev->phydev); if (count >= 0) phy_ethtool_get_stats(dev->phydev, stats, data); } else if (ops->get_sset_count && ops->get_ethtool_phy_stats) { count = ops->get_sset_count(dev, ETH_SS_PHY_STATS); ops->get_ethtool_phy_stats(dev, stats, data); } if (count < 0) count = 0; if (ds->ops->get_ethtool_phy_stats) ds->ops->get_ethtool_phy_stats(ds, port, data + count); } |
f2f235668 net: dsa: move ma... |
115 116 |
static int dsa_master_get_sset_count(struct net_device *dev, int sset) { |
2f657a600 net: dsa: change ... |
117 |
struct dsa_port *cpu_dp = dev->dsa_ptr; |
7ec764eef net: dsa: use cpu... |
118 119 |
const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; |
f2f235668 net: dsa: move ma... |
120 |
int count = 0; |
cf9635730 net: dsa: Allow p... |
121 122 123 124 |
if (sset == ETH_SS_PHY_STATS && dev->phydev && !ops->get_ethtool_phy_stats) count = phy_ethtool_get_sset_count(dev->phydev); else if (ops->get_sset_count) |
89f090483 net: dsa: Pass st... |
125 |
count = ops->get_sset_count(dev, sset); |
cf9635730 net: dsa: Allow p... |
126 127 128 |
if (count < 0) count = 0; |
f2f235668 net: dsa: move ma... |
129 |
|
89f090483 net: dsa: Pass st... |
130 131 |
if (ds->ops->get_sset_count) count += ds->ops->get_sset_count(ds, cpu_dp->index, sset); |
f2f235668 net: dsa: move ma... |
132 133 134 135 136 137 138 |
return count; } static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, uint8_t *data) { |
2f657a600 net: dsa: change ... |
139 |
struct dsa_port *cpu_dp = dev->dsa_ptr; |
7ec764eef net: dsa: use cpu... |
140 141 142 |
const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; int port = cpu_dp->index; |
f2f235668 net: dsa: move ma... |
143 144 145 146 147 |
int len = ETH_GSTRING_LEN; int mcount = 0, count; unsigned int i; uint8_t pfx[4]; uint8_t *ndata; |
7ec764eef net: dsa: use cpu... |
148 |
snprintf(pfx, sizeof(pfx), "p%.2d", port); |
f2f235668 net: dsa: move ma... |
149 150 |
/* We do not want to be NULL-terminated, since this is a prefix */ pfx[sizeof(pfx) - 1] = '_'; |
cf9635730 net: dsa: Allow p... |
151 152 153 154 155 156 157 158 |
if (stringset == ETH_SS_PHY_STATS && dev->phydev && !ops->get_ethtool_phy_stats) { mcount = phy_ethtool_get_sset_count(dev->phydev); if (mcount < 0) mcount = 0; else phy_ethtool_get_strings(dev->phydev, data); } else if (ops->get_sset_count && ops->get_strings) { |
89f090483 net: dsa: Pass st... |
159 160 161 |
mcount = ops->get_sset_count(dev, stringset); if (mcount < 0) mcount = 0; |
f2f235668 net: dsa: move ma... |
162 163 |
ops->get_strings(dev, stringset, data); } |
89f090483 net: dsa: Pass st... |
164 |
if (ds->ops->get_strings) { |
f2f235668 net: dsa: move ma... |
165 166 167 168 169 |
ndata = data + mcount * len; /* This function copies ETH_GSTRINGS_LEN bytes, we will mangle * the output after to prepend our CPU port prefix we * constructed earlier */ |
89f090483 net: dsa: Pass st... |
170 171 |
ds->ops->get_strings(ds, port, stringset, ndata); count = ds->ops->get_sset_count(ds, port, stringset); |
f2f235668 net: dsa: move ma... |
172 173 174 175 176 177 178 |
for (i = 0; i < count; i++) { memmove(ndata + (i * len + sizeof(pfx)), ndata + i * len, len - sizeof(pfx)); memcpy(ndata + i * len, pfx, sizeof(pfx)); } } } |
f685e609a net: dsa: Deny PT... |
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
static int dsa_master_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct dsa_port *cpu_dp = dev->dsa_ptr; struct dsa_switch *ds = cpu_dp->ds; struct dsa_switch_tree *dst; int err = -EOPNOTSUPP; struct dsa_port *dp; dst = ds->dst; switch (cmd) { case SIOCGHWTSTAMP: case SIOCSHWTSTAMP: /* Deny PTP operations on master if there is at least one * switch in the tree that is PTP capable. */ list_for_each_entry(dp, &dst->ports, list) if (dp->ds->ops->port_hwtstamp_get || dp->ds->ops->port_hwtstamp_set) return -EBUSY; break; } |
9c0c7014f net: dsa: Setup d... |
201 202 |
if (dev->netdev_ops->ndo_do_ioctl) err = dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd); |
f685e609a net: dsa: Deny PT... |
203 204 205 |
return err; } |
9c0c7014f net: dsa: Setup d... |
206 207 |
static const struct dsa_netdevice_ops dsa_netdev_ops = { .ndo_do_ioctl = dsa_master_ioctl, |
9c0c7014f net: dsa: Setup d... |
208 |
}; |
17a22fcfc net: dsa: setup a... |
209 |
static int dsa_master_ethtool_setup(struct net_device *dev) |
f2f235668 net: dsa: move ma... |
210 |
{ |
2f657a600 net: dsa: change ... |
211 |
struct dsa_port *cpu_dp = dev->dsa_ptr; |
7ec764eef net: dsa: use cpu... |
212 |
struct dsa_switch *ds = cpu_dp->ds; |
f2f235668 net: dsa: move ma... |
213 214 215 216 217 |
struct ethtool_ops *ops; ops = devm_kzalloc(ds->dev, sizeof(*ops), GFP_KERNEL); if (!ops) return -ENOMEM; |
7ec764eef net: dsa: use cpu... |
218 219 220 |
cpu_dp->orig_ethtool_ops = dev->ethtool_ops; if (cpu_dp->orig_ethtool_ops) memcpy(ops, cpu_dp->orig_ethtool_ops, sizeof(*ops)); |
f2f235668 net: dsa: move ma... |
221 |
|
48e233119 net: dsa: dump CP... |
222 223 |
ops->get_regs_len = dsa_master_get_regs_len; ops->get_regs = dsa_master_get_regs; |
f2f235668 net: dsa: move ma... |
224 225 226 |
ops->get_sset_count = dsa_master_get_sset_count; ops->get_ethtool_stats = dsa_master_get_ethtool_stats; ops->get_strings = dsa_master_get_strings; |
cf9635730 net: dsa: Allow p... |
227 |
ops->get_ethtool_phy_stats = dsa_master_get_ethtool_phy_stats; |
f2f235668 net: dsa: move ma... |
228 229 230 231 232 |
dev->ethtool_ops = ops; return 0; } |
17a22fcfc net: dsa: setup a... |
233 |
static void dsa_master_ethtool_teardown(struct net_device *dev) |
f2f235668 net: dsa: move ma... |
234 |
{ |
2f657a600 net: dsa: change ... |
235 |
struct dsa_port *cpu_dp = dev->dsa_ptr; |
f2f235668 net: dsa: move ma... |
236 |
|
7ec764eef net: dsa: use cpu... |
237 238 |
dev->ethtool_ops = cpu_dp->orig_ethtool_ops; cpu_dp->orig_ethtool_ops = NULL; |
f2f235668 net: dsa: move ma... |
239 |
} |
17a22fcfc net: dsa: setup a... |
240 |
|
9c0c7014f net: dsa: Setup d... |
241 242 |
static void dsa_netdev_ops_set(struct net_device *dev, const struct dsa_netdevice_ops *ops) |
da7b9e9b0 net: dsa: Add ndo... |
243 |
{ |
9c0c7014f net: dsa: Setup d... |
244 |
dev->dsa_ptr->netdev_ops = ops; |
da7b9e9b0 net: dsa: Add ndo... |
245 |
} |
c3975400c net: dsa: allow d... |
246 247 248 249 250 251 252 253 254 255 256 |
static void dsa_master_set_promiscuity(struct net_device *dev, int inc) { const struct dsa_device_ops *ops = dev->dsa_ptr->tag_ops; if (!ops->promisc_on_master) return; rtnl_lock(); dev_set_promiscuity(dev, inc); rtnl_unlock(); } |
a3d7e01da net: dsa: Fix tag... |
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
static ssize_t tagging_show(struct device *d, struct device_attribute *attr, char *buf) { struct net_device *dev = to_net_dev(d); struct dsa_port *cpu_dp = dev->dsa_ptr; return sprintf(buf, "%s ", dsa_tag_protocol_to_str(cpu_dp->tag_ops)); } static DEVICE_ATTR_RO(tagging); static struct attribute *dsa_slave_attrs[] = { &dev_attr_tagging.attr, NULL }; static const struct attribute_group dsa_group = { .name = "dsa", .attrs = dsa_slave_attrs, }; |
91ba47957 net: dsa: Restore... |
278 279 280 281 282 283 284 285 286 287 288 289 |
static void dsa_master_reset_mtu(struct net_device *dev) { int err; rtnl_lock(); err = dev_set_mtu(dev, ETH_DATA_LEN); if (err) netdev_dbg(dev, "Unable to reset MTU to exclude DSA overheads "); rtnl_unlock(); } |
845e0ebb4 net: change addr_... |
290 |
static struct lock_class_key dsa_master_addr_list_lock_key; |
17a22fcfc net: dsa: setup a... |
291 292 |
int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp) { |
a3d7e01da net: dsa: Fix tag... |
293 |
int ret; |
bfcb81320 net: dsa: configu... |
294 295 296 297 298 299 300 |
rtnl_lock(); ret = dev_set_mtu(dev, ETH_DATA_LEN + cpu_dp->tag_ops->overhead); rtnl_unlock(); if (ret) netdev_warn(dev, "error %d setting MTU to include DSA overhead ", ret); |
dc0fe7d47 net: dsa: Set the... |
301 |
|
17a22fcfc net: dsa: setup a... |
302 303 304 305 306 307 308 |
/* If we use a tagging format that doesn't have an ethertype * field, make sure that all packets from this point on get * sent to the tag format's receive function. */ wmb(); dev->dsa_ptr = cpu_dp; |
845e0ebb4 net: change addr_... |
309 310 |
lockdep_set_class(&dev->addr_list_lock, &dsa_master_addr_list_lock_key); |
c3975400c net: dsa: allow d... |
311 312 |
dsa_master_set_promiscuity(dev, 1); |
a3d7e01da net: dsa: Fix tag... |
313 314 |
ret = dsa_master_ethtool_setup(dev); if (ret) |
c3975400c net: dsa: allow d... |
315 |
goto out_err_reset_promisc; |
a3d7e01da net: dsa: Fix tag... |
316 |
|
9c0c7014f net: dsa: Setup d... |
317 |
dsa_netdev_ops_set(dev, &dsa_netdev_ops); |
da7b9e9b0 net: dsa: Add ndo... |
318 |
|
a3d7e01da net: dsa: Fix tag... |
319 320 |
ret = sysfs_create_group(&dev->dev.kobj, &dsa_group); if (ret) |
da7b9e9b0 net: dsa: Add ndo... |
321 322 323 |
goto out_err_ndo_teardown; return ret; |
a3d7e01da net: dsa: Fix tag... |
324 |
|
da7b9e9b0 net: dsa: Add ndo... |
325 |
out_err_ndo_teardown: |
9c0c7014f net: dsa: Setup d... |
326 |
dsa_netdev_ops_set(dev, NULL); |
da7b9e9b0 net: dsa: Add ndo... |
327 |
dsa_master_ethtool_teardown(dev); |
c3975400c net: dsa: allow d... |
328 329 |
out_err_reset_promisc: dsa_master_set_promiscuity(dev, -1); |
a3d7e01da net: dsa: Fix tag... |
330 |
return ret; |
17a22fcfc net: dsa: setup a... |
331 332 333 334 |
} void dsa_master_teardown(struct net_device *dev) { |
a3d7e01da net: dsa: Fix tag... |
335 |
sysfs_remove_group(&dev->dev.kobj, &dsa_group); |
9c0c7014f net: dsa: Setup d... |
336 |
dsa_netdev_ops_set(dev, NULL); |
17a22fcfc net: dsa: setup a... |
337 |
dsa_master_ethtool_teardown(dev); |
91ba47957 net: dsa: Restore... |
338 |
dsa_master_reset_mtu(dev); |
c3975400c net: dsa: allow d... |
339 |
dsa_master_set_promiscuity(dev, -1); |
17a22fcfc net: dsa: setup a... |
340 341 342 343 344 345 346 347 348 |
dev->dsa_ptr = NULL; /* If we used a tagging format that doesn't have an ethertype * field, make sure that all packets from this point get sent * without the tag and go through the regular receive path. */ wmb(); } |