]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - net/dsa/port.c
Merge branch 'locking-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[karo-tx-linux.git] / net / dsa / port.c
1 /*
2  * Handling of a single switch port
3  *
4  * Copyright (c) 2017 Savoir-faire Linux Inc.
5  *      Vivien Didelot <vivien.didelot@savoirfairelinux.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  */
12
13 #include <linux/if_bridge.h>
14 #include <linux/notifier.h>
15
16 #include "dsa_priv.h"
17
18 static int dsa_port_notify(struct dsa_port *dp, unsigned long e, void *v)
19 {
20         struct raw_notifier_head *nh = &dp->ds->dst->nh;
21         int err;
22
23         err = raw_notifier_call_chain(nh, e, v);
24
25         return notifier_to_errno(err);
26 }
27
28 int dsa_port_set_state(struct dsa_port *dp, u8 state,
29                        struct switchdev_trans *trans)
30 {
31         struct dsa_switch *ds = dp->ds;
32         int port = dp->index;
33
34         if (switchdev_trans_ph_prepare(trans))
35                 return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP;
36
37         if (ds->ops->port_stp_state_set)
38                 ds->ops->port_stp_state_set(ds, port, state);
39
40         if (ds->ops->port_fast_age) {
41                 /* Fast age FDB entries or flush appropriate forwarding database
42                  * for the given port, if we are moving it from Learning or
43                  * Forwarding state, to Disabled or Blocking or Listening state.
44                  */
45
46                 if ((dp->stp_state == BR_STATE_LEARNING ||
47                      dp->stp_state == BR_STATE_FORWARDING) &&
48                     (state == BR_STATE_DISABLED ||
49                      state == BR_STATE_BLOCKING ||
50                      state == BR_STATE_LISTENING))
51                         ds->ops->port_fast_age(ds, port);
52         }
53
54         dp->stp_state = state;
55
56         return 0;
57 }
58
59 void dsa_port_set_state_now(struct dsa_port *dp, u8 state)
60 {
61         int err;
62
63         err = dsa_port_set_state(dp, state, NULL);
64         if (err)
65                 pr_err("DSA: failed to set STP state %u (%d)\n", state, err);
66 }
67
68 int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
69 {
70         struct dsa_notifier_bridge_info info = {
71                 .sw_index = dp->ds->index,
72                 .port = dp->index,
73                 .br = br,
74         };
75         int err;
76
77         /* Here the port is already bridged. Reflect the current configuration
78          * so that drivers can program their chips accordingly.
79          */
80         dp->bridge_dev = br;
81
82         err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info);
83
84         /* The bridging is rolled back on error */
85         if (err)
86                 dp->bridge_dev = NULL;
87
88         return err;
89 }
90
91 void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
92 {
93         struct dsa_notifier_bridge_info info = {
94                 .sw_index = dp->ds->index,
95                 .port = dp->index,
96                 .br = br,
97         };
98         int err;
99
100         /* Here the port is already unbridged. Reflect the current configuration
101          * so that drivers can program their chips accordingly.
102          */
103         dp->bridge_dev = NULL;
104
105         err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info);
106         if (err)
107                 pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
108
109         /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
110          * so allow it to be in BR_STATE_FORWARDING to be kept functional
111          */
112         dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
113 }
114
115 int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
116                             struct switchdev_trans *trans)
117 {
118         struct dsa_switch *ds = dp->ds;
119
120         /* bridge skips -EOPNOTSUPP, so skip the prepare phase */
121         if (switchdev_trans_ph_prepare(trans))
122                 return 0;
123
124         if (ds->ops->port_vlan_filtering)
125                 return ds->ops->port_vlan_filtering(ds, dp->index,
126                                                     vlan_filtering);
127
128         return 0;
129 }
130
131 int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
132                          struct switchdev_trans *trans)
133 {
134         unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
135         unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
136         struct dsa_notifier_ageing_time_info info = {
137                 .ageing_time = ageing_time,
138                 .trans = trans,
139         };
140
141         if (switchdev_trans_ph_prepare(trans))
142                 return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
143
144         dp->ageing_time = ageing_time;
145
146         return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
147 }
148
149 int dsa_port_fdb_add(struct dsa_port *dp,
150                      const struct switchdev_obj_port_fdb *fdb,
151                      struct switchdev_trans *trans)
152 {
153         struct dsa_notifier_fdb_info info = {
154                 .sw_index = dp->ds->index,
155                 .port = dp->index,
156                 .trans = trans,
157                 .fdb = fdb,
158         };
159
160         return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
161 }
162
163 int dsa_port_fdb_del(struct dsa_port *dp,
164                      const struct switchdev_obj_port_fdb *fdb)
165 {
166         struct dsa_notifier_fdb_info info = {
167                 .sw_index = dp->ds->index,
168                 .port = dp->index,
169                 .fdb = fdb,
170         };
171
172         return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
173 }
174
175 int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb,
176                       switchdev_obj_dump_cb_t *cb)
177 {
178         struct dsa_switch *ds = dp->ds;
179
180         if (ds->ops->port_fdb_dump)
181                 return ds->ops->port_fdb_dump(ds, dp->index, fdb, cb);
182
183         return -EOPNOTSUPP;
184 }
185
186 int dsa_port_mdb_add(struct dsa_port *dp,
187                      const struct switchdev_obj_port_mdb *mdb,
188                      struct switchdev_trans *trans)
189 {
190         struct dsa_notifier_mdb_info info = {
191                 .sw_index = dp->ds->index,
192                 .port = dp->index,
193                 .trans = trans,
194                 .mdb = mdb,
195         };
196
197         return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
198 }
199
200 int dsa_port_mdb_del(struct dsa_port *dp,
201                      const struct switchdev_obj_port_mdb *mdb)
202 {
203         struct dsa_notifier_mdb_info info = {
204                 .sw_index = dp->ds->index,
205                 .port = dp->index,
206                 .mdb = mdb,
207         };
208
209         return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
210 }
211
212 int dsa_port_mdb_dump(struct dsa_port *dp, struct switchdev_obj_port_mdb *mdb,
213                       switchdev_obj_dump_cb_t *cb)
214 {
215         struct dsa_switch *ds = dp->ds;
216
217         if (ds->ops->port_mdb_dump)
218                 return ds->ops->port_mdb_dump(ds, dp->index, mdb, cb);
219
220         return -EOPNOTSUPP;
221 }
222
223 int dsa_port_vlan_add(struct dsa_port *dp,
224                       const struct switchdev_obj_port_vlan *vlan,
225                       struct switchdev_trans *trans)
226 {
227         struct dsa_notifier_vlan_info info = {
228                 .sw_index = dp->ds->index,
229                 .port = dp->index,
230                 .trans = trans,
231                 .vlan = vlan,
232         };
233
234         return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
235 }
236
237 int dsa_port_vlan_del(struct dsa_port *dp,
238                       const struct switchdev_obj_port_vlan *vlan)
239 {
240         struct dsa_notifier_vlan_info info = {
241                 .sw_index = dp->ds->index,
242                 .port = dp->index,
243                 .vlan = vlan,
244         };
245
246         return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
247 }
248
249 int dsa_port_vlan_dump(struct dsa_port *dp,
250                        struct switchdev_obj_port_vlan *vlan,
251                        switchdev_obj_dump_cb_t *cb)
252 {
253         struct dsa_switch *ds = dp->ds;
254
255         if (ds->ops->port_vlan_dump)
256                 return ds->ops->port_vlan_dump(ds, dp->index, vlan, cb);
257
258         return -EOPNOTSUPP;
259 }