]> git.karo-electronics.de Git - karo-tx-linux.git/blob - net/dsa/port.c
ed88d838164297a4c792359ba1827a001d67f984
[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                 .sw_index = dp->ds->index,
139                 .trans = trans,
140         };
141
142         if (switchdev_trans_ph_prepare(trans))
143                 return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
144
145         dp->ageing_time = ageing_time;
146
147         return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
148 }
149
150 int dsa_port_fdb_add(struct dsa_port *dp,
151                      const struct switchdev_obj_port_fdb *fdb,
152                      struct switchdev_trans *trans)
153 {
154         struct dsa_notifier_fdb_info info = {
155                 .sw_index = dp->ds->index,
156                 .port = dp->index,
157                 .trans = trans,
158                 .fdb = fdb,
159         };
160
161         return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
162 }
163
164 int dsa_port_fdb_del(struct dsa_port *dp,
165                      const struct switchdev_obj_port_fdb *fdb)
166 {
167         struct dsa_notifier_fdb_info info = {
168                 .sw_index = dp->ds->index,
169                 .port = dp->index,
170                 .fdb = fdb,
171         };
172
173         return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
174 }
175
176 int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb,
177                       switchdev_obj_dump_cb_t *cb)
178 {
179         struct dsa_switch *ds = dp->ds;
180
181         if (ds->ops->port_fdb_dump)
182                 return ds->ops->port_fdb_dump(ds, dp->index, fdb, cb);
183
184         return -EOPNOTSUPP;
185 }
186
187 int dsa_port_mdb_add(struct dsa_port *dp,
188                      const struct switchdev_obj_port_mdb *mdb,
189                      struct switchdev_trans *trans)
190 {
191         struct dsa_switch *ds = dp->ds;
192
193         if (switchdev_trans_ph_prepare(trans)) {
194                 if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add)
195                         return -EOPNOTSUPP;
196
197                 return ds->ops->port_mdb_prepare(ds, dp->index, mdb, trans);
198         }
199
200         ds->ops->port_mdb_add(ds, dp->index, mdb, trans);
201
202         return 0;
203 }
204
205 int dsa_port_mdb_del(struct dsa_port *dp,
206                      const struct switchdev_obj_port_mdb *mdb)
207 {
208         struct dsa_switch *ds = dp->ds;
209
210         if (ds->ops->port_mdb_del)
211                 return ds->ops->port_mdb_del(ds, dp->index, mdb);
212
213         return -EOPNOTSUPP;
214 }
215
216 int dsa_port_mdb_dump(struct dsa_port *dp, struct switchdev_obj_port_mdb *mdb,
217                       switchdev_obj_dump_cb_t *cb)
218 {
219         struct dsa_switch *ds = dp->ds;
220
221         if (ds->ops->port_mdb_dump)
222                 return ds->ops->port_mdb_dump(ds, dp->index, mdb, cb);
223
224         return -EOPNOTSUPP;
225 }
226
227 int dsa_port_vlan_add(struct dsa_port *dp,
228                       const struct switchdev_obj_port_vlan *vlan,
229                       struct switchdev_trans *trans)
230 {
231         struct dsa_switch *ds = dp->ds;
232
233         if (switchdev_trans_ph_prepare(trans)) {
234                 if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
235                         return -EOPNOTSUPP;
236
237                 return ds->ops->port_vlan_prepare(ds, dp->index, vlan, trans);
238         }
239
240         ds->ops->port_vlan_add(ds, dp->index, vlan, trans);
241
242         return 0;
243 }
244
245 int dsa_port_vlan_del(struct dsa_port *dp,
246                       const struct switchdev_obj_port_vlan *vlan)
247 {
248         struct dsa_switch *ds = dp->ds;
249
250         if (!ds->ops->port_vlan_del)
251                 return -EOPNOTSUPP;
252
253         return ds->ops->port_vlan_del(ds, dp->index, vlan);
254 }
255
256 int dsa_port_vlan_dump(struct dsa_port *dp,
257                        struct switchdev_obj_port_vlan *vlan,
258                        switchdev_obj_dump_cb_t *cb)
259 {
260         struct dsa_switch *ds = dp->ds;
261
262         if (ds->ops->port_vlan_dump)
263                 return ds->ops->port_vlan_dump(ds, dp->index, vlan, cb);
264
265         return -EOPNOTSUPP;
266 }