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