123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- From 295ab96f478d0fa56393e85406f19a867e26ce22 Mon Sep 17 00:00:00 2001
- From: Vladimir Oltean <[email protected]>
- Date: Wed, 2 Feb 2022 01:03:20 +0100
- Subject: [PATCH 01/16] net: dsa: provide switch operations for tracking the
- master state
- Certain drivers may need to send management traffic to the switch for
- things like register access, FDB dump, etc, to accelerate what their
- slow bus (SPI, I2C, MDIO) can already do.
- Ethernet is faster (especially in bulk transactions) but is also more
- unreliable, since the user may decide to bring the DSA master down (or
- not bring it up), therefore severing the link between the host and the
- attached switch.
- Drivers needing Ethernet-based register access already should have
- fallback logic to the slow bus if the Ethernet method fails, but that
- fallback may be based on a timeout, and the I/O to the switch may slow
- down to a halt if the master is down, because every Ethernet packet will
- have to time out. The driver also doesn't have the option to turn off
- Ethernet-based I/O momentarily, because it wouldn't know when to turn it
- back on.
- Which is where this change comes in. By tracking NETDEV_CHANGE,
- NETDEV_UP and NETDEV_GOING_DOWN events on the DSA master, we should know
- the exact interval of time during which this interface is reliably
- available for traffic. Provide this information to switches so they can
- use it as they wish.
- An helper is added dsa_port_master_is_operational() to check if a master
- port is operational.
- Signed-off-by: Vladimir Oltean <[email protected]>
- Signed-off-by: Ansuel Smith <[email protected]>
- Reviewed-by: Florian Fainelli <[email protected]>
- Signed-off-by: David S. Miller <[email protected]>
- ---
- include/net/dsa.h | 17 +++++++++++++++++
- net/dsa/dsa2.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
- net/dsa/dsa_priv.h | 13 +++++++++++++
- net/dsa/slave.c | 32 ++++++++++++++++++++++++++++++++
- net/dsa/switch.c | 15 +++++++++++++++
- 5 files changed, 123 insertions(+)
- --- a/include/net/dsa.h
- +++ b/include/net/dsa.h
- @@ -291,6 +291,10 @@ struct dsa_port {
- struct list_head mdbs;
-
- bool setup;
- + /* Master state bits, valid only on CPU ports */
- + u8 master_admin_up:1;
- + u8 master_oper_up:1;
- +
- };
-
- /* TODO: ideally DSA ports would have a single dp->link_dp member,
- @@ -456,6 +460,12 @@ static inline bool dsa_port_is_unused(st
- return dp->type == DSA_PORT_TYPE_UNUSED;
- }
-
- +static inline bool dsa_port_master_is_operational(struct dsa_port *dp)
- +{
- + return dsa_port_is_cpu(dp) && dp->master_admin_up &&
- + dp->master_oper_up;
- +}
- +
- static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p)
- {
- return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_UNUSED;
- @@ -949,6 +959,13 @@ struct dsa_switch_ops {
- int (*tag_8021q_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
- u16 flags);
- int (*tag_8021q_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
- +
- + /*
- + * DSA master tracking operations
- + */
- + void (*master_state_change)(struct dsa_switch *ds,
- + const struct net_device *master,
- + bool operational);
- };
-
- #define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \
- --- a/net/dsa/dsa2.c
- +++ b/net/dsa/dsa2.c
- @@ -1275,6 +1275,52 @@ out_unlock:
- return err;
- }
-
- +static void dsa_tree_master_state_change(struct dsa_switch_tree *dst,
- + struct net_device *master)
- +{
- + struct dsa_notifier_master_state_info info;
- + struct dsa_port *cpu_dp = master->dsa_ptr;
- +
- + info.master = master;
- + info.operational = dsa_port_master_is_operational(cpu_dp);
- +
- + dsa_tree_notify(dst, DSA_NOTIFIER_MASTER_STATE_CHANGE, &info);
- +}
- +
- +void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst,
- + struct net_device *master,
- + bool up)
- +{
- + struct dsa_port *cpu_dp = master->dsa_ptr;
- + bool notify = false;
- +
- + if ((dsa_port_master_is_operational(cpu_dp)) !=
- + (up && cpu_dp->master_oper_up))
- + notify = true;
- +
- + cpu_dp->master_admin_up = up;
- +
- + if (notify)
- + dsa_tree_master_state_change(dst, master);
- +}
- +
- +void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst,
- + struct net_device *master,
- + bool up)
- +{
- + struct dsa_port *cpu_dp = master->dsa_ptr;
- + bool notify = false;
- +
- + if ((dsa_port_master_is_operational(cpu_dp)) !=
- + (cpu_dp->master_admin_up && up))
- + notify = true;
- +
- + cpu_dp->master_oper_up = up;
- +
- + if (notify)
- + dsa_tree_master_state_change(dst, master);
- +}
- +
- static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
- {
- struct dsa_switch_tree *dst = ds->dst;
- --- a/net/dsa/dsa_priv.h
- +++ b/net/dsa/dsa_priv.h
- @@ -45,6 +45,7 @@ enum {
- DSA_NOTIFIER_MRP_DEL_RING_ROLE,
- DSA_NOTIFIER_TAG_8021Q_VLAN_ADD,
- DSA_NOTIFIER_TAG_8021Q_VLAN_DEL,
- + DSA_NOTIFIER_MASTER_STATE_CHANGE,
- };
-
- /* DSA_NOTIFIER_AGEING_TIME */
- @@ -127,6 +128,12 @@ struct dsa_notifier_tag_8021q_vlan_info
- u16 vid;
- };
-
- +/* DSA_NOTIFIER_MASTER_STATE_CHANGE */
- +struct dsa_notifier_master_state_info {
- + const struct net_device *master;
- + bool operational;
- +};
- +
- struct dsa_switchdev_event_work {
- struct dsa_switch *ds;
- int port;
- @@ -548,6 +555,12 @@ int dsa_tree_change_tag_proto(struct dsa
- struct net_device *master,
- const struct dsa_device_ops *tag_ops,
- const struct dsa_device_ops *old_tag_ops);
- +void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst,
- + struct net_device *master,
- + bool up);
- +void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst,
- + struct net_device *master,
- + bool up);
- int dsa_bridge_num_get(const struct net_device *bridge_dev, int max);
- void dsa_bridge_num_put(const struct net_device *bridge_dev, int bridge_num);
-
- --- a/net/dsa/slave.c
- +++ b/net/dsa/slave.c
- @@ -2311,6 +2311,36 @@ static int dsa_slave_netdevice_event(str
- err = dsa_port_lag_change(dp, info->lower_state_info);
- return notifier_from_errno(err);
- }
- + case NETDEV_CHANGE:
- + case NETDEV_UP: {
- + /* Track state of master port.
- + * DSA driver may require the master port (and indirectly
- + * the tagger) to be available for some special operation.
- + */
- + if (netdev_uses_dsa(dev)) {
- + struct dsa_port *cpu_dp = dev->dsa_ptr;
- + struct dsa_switch_tree *dst = cpu_dp->ds->dst;
- +
- + /* Track when the master port is UP */
- + dsa_tree_master_oper_state_change(dst, dev,
- + netif_oper_up(dev));
- +
- + /* Track when the master port is ready and can accept
- + * packet.
- + * NETDEV_UP event is not enough to flag a port as ready.
- + * We also have to wait for linkwatch_do_dev to dev_activate
- + * and emit a NETDEV_CHANGE event.
- + * We check if a master port is ready by checking if the dev
- + * have a qdisc assigned and is not noop.
- + */
- + dsa_tree_master_admin_state_change(dst, dev,
- + !qdisc_tx_is_noop(dev));
- +
- + return NOTIFY_OK;
- + }
- +
- + return NOTIFY_DONE;
- + }
- case NETDEV_GOING_DOWN: {
- struct dsa_port *dp, *cpu_dp;
- struct dsa_switch_tree *dst;
- @@ -2322,6 +2352,8 @@ static int dsa_slave_netdevice_event(str
- cpu_dp = dev->dsa_ptr;
- dst = cpu_dp->ds->dst;
-
- + dsa_tree_master_admin_state_change(dst, dev, false);
- +
- list_for_each_entry(dp, &dst->ports, list) {
- if (!dsa_is_user_port(dp->ds, dp->index))
- continue;
- --- a/net/dsa/switch.c
- +++ b/net/dsa/switch.c
- @@ -722,6 +722,18 @@ dsa_switch_mrp_del_ring_role(struct dsa_
- return 0;
- }
-
- +static int
- +dsa_switch_master_state_change(struct dsa_switch *ds,
- + struct dsa_notifier_master_state_info *info)
- +{
- + if (!ds->ops->master_state_change)
- + return 0;
- +
- + ds->ops->master_state_change(ds, info->master, info->operational);
- +
- + return 0;
- +}
- +
- static int dsa_switch_event(struct notifier_block *nb,
- unsigned long event, void *info)
- {
- @@ -813,6 +825,9 @@ static int dsa_switch_event(struct notif
- case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL:
- err = dsa_switch_tag_8021q_vlan_del(ds, info);
- break;
- + case DSA_NOTIFIER_MASTER_STATE_CHANGE:
- + err = dsa_switch_master_state_change(ds, info);
- + break;
- default:
- err = -EOPNOTSUPP;
- break;
|