xref: /linux/net/dsa/switch.c (revision 04d3a4c6af52a58370795bc9f70dc15f51f8bb84)
1f515f192SVivien Didelot /*
2f515f192SVivien Didelot  * Handling of a single switch chip, part of a switch fabric
3f515f192SVivien Didelot  *
4f515f192SVivien Didelot  * Copyright (c) 2017 Vivien Didelot <vivien.didelot@savoirfairelinux.com>
5f515f192SVivien Didelot  *
6f515f192SVivien Didelot  * This program is free software; you can redistribute it and/or modify
7f515f192SVivien Didelot  * it under the terms of the GNU General Public License as published by
8f515f192SVivien Didelot  * the Free Software Foundation; either version 2 of the License, or
9f515f192SVivien Didelot  * (at your option) any later version.
10f515f192SVivien Didelot  */
11f515f192SVivien Didelot 
12f515f192SVivien Didelot #include <linux/netdevice.h>
13f515f192SVivien Didelot #include <linux/notifier.h>
14f515f192SVivien Didelot #include <net/dsa.h>
15f515f192SVivien Didelot 
16*04d3a4c6SVivien Didelot static int dsa_switch_bridge_join(struct dsa_switch *ds,
17*04d3a4c6SVivien Didelot 				  struct dsa_notifier_bridge_info *info)
18*04d3a4c6SVivien Didelot {
19*04d3a4c6SVivien Didelot 	if (ds->index == info->sw_index && ds->ops->port_bridge_join)
20*04d3a4c6SVivien Didelot 		return ds->ops->port_bridge_join(ds, info->port, info->br);
21*04d3a4c6SVivien Didelot 
22*04d3a4c6SVivien Didelot 	if (ds->index != info->sw_index)
23*04d3a4c6SVivien Didelot 		dev_dbg(ds->dev, "crosschip DSA port %d.%d bridged to %s\n",
24*04d3a4c6SVivien Didelot 			info->sw_index, info->port, netdev_name(info->br));
25*04d3a4c6SVivien Didelot 
26*04d3a4c6SVivien Didelot 	return 0;
27*04d3a4c6SVivien Didelot }
28*04d3a4c6SVivien Didelot 
29*04d3a4c6SVivien Didelot static int dsa_switch_bridge_leave(struct dsa_switch *ds,
30*04d3a4c6SVivien Didelot 				   struct dsa_notifier_bridge_info *info)
31*04d3a4c6SVivien Didelot {
32*04d3a4c6SVivien Didelot 	if (ds->index == info->sw_index && ds->ops->port_bridge_leave)
33*04d3a4c6SVivien Didelot 		ds->ops->port_bridge_leave(ds, info->port, info->br);
34*04d3a4c6SVivien Didelot 
35*04d3a4c6SVivien Didelot 	if (ds->index != info->sw_index)
36*04d3a4c6SVivien Didelot 		dev_dbg(ds->dev, "crosschip DSA port %d.%d unbridged from %s\n",
37*04d3a4c6SVivien Didelot 			info->sw_index, info->port, netdev_name(info->br));
38*04d3a4c6SVivien Didelot 
39*04d3a4c6SVivien Didelot 	return 0;
40*04d3a4c6SVivien Didelot }
41*04d3a4c6SVivien Didelot 
42f515f192SVivien Didelot static int dsa_switch_event(struct notifier_block *nb,
43f515f192SVivien Didelot 			    unsigned long event, void *info)
44f515f192SVivien Didelot {
45f515f192SVivien Didelot 	struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
46f515f192SVivien Didelot 	int err;
47f515f192SVivien Didelot 
48f515f192SVivien Didelot 	switch (event) {
49*04d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_JOIN:
50*04d3a4c6SVivien Didelot 		err = dsa_switch_bridge_join(ds, info);
51*04d3a4c6SVivien Didelot 		break;
52*04d3a4c6SVivien Didelot 	case DSA_NOTIFIER_BRIDGE_LEAVE:
53*04d3a4c6SVivien Didelot 		err = dsa_switch_bridge_leave(ds, info);
54*04d3a4c6SVivien Didelot 		break;
55f515f192SVivien Didelot 	default:
56f515f192SVivien Didelot 		err = -EOPNOTSUPP;
57f515f192SVivien Didelot 		break;
58f515f192SVivien Didelot 	}
59f515f192SVivien Didelot 
60f515f192SVivien Didelot 	/* Non-switchdev operations cannot be rolled back. If a DSA driver
61f515f192SVivien Didelot 	 * returns an error during the chained call, switch chips may be in an
62f515f192SVivien Didelot 	 * inconsistent state.
63f515f192SVivien Didelot 	 */
64f515f192SVivien Didelot 	if (err)
65f515f192SVivien Didelot 		dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
66f515f192SVivien Didelot 			event, err);
67f515f192SVivien Didelot 
68f515f192SVivien Didelot 	return notifier_from_errno(err);
69f515f192SVivien Didelot }
70f515f192SVivien Didelot 
71f515f192SVivien Didelot int dsa_switch_register_notifier(struct dsa_switch *ds)
72f515f192SVivien Didelot {
73f515f192SVivien Didelot 	ds->nb.notifier_call = dsa_switch_event;
74f515f192SVivien Didelot 
75f515f192SVivien Didelot 	return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
76f515f192SVivien Didelot }
77f515f192SVivien Didelot 
78f515f192SVivien Didelot void dsa_switch_unregister_notifier(struct dsa_switch *ds)
79f515f192SVivien Didelot {
80f515f192SVivien Didelot 	int err;
81f515f192SVivien Didelot 
82f515f192SVivien Didelot 	err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
83f515f192SVivien Didelot 	if (err)
84f515f192SVivien Didelot 		dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
85f515f192SVivien Didelot }
86