OSDN Git Service

net: dsa: Remove prepare phase for FDB
[uclinux-h8/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 {
152         struct dsa_notifier_fdb_info info = {
153                 .sw_index = dp->ds->index,
154                 .port = dp->index,
155                 .fdb = fdb,
156         };
157
158         return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
159 }
160
161 int dsa_port_fdb_del(struct dsa_port *dp,
162                      const struct switchdev_obj_port_fdb *fdb)
163 {
164         struct dsa_notifier_fdb_info info = {
165                 .sw_index = dp->ds->index,
166                 .port = dp->index,
167                 .fdb = fdb,
168         };
169
170         return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
171 }
172
173 int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb,
174                       switchdev_obj_dump_cb_t *cb)
175 {
176         struct dsa_switch *ds = dp->ds;
177
178         if (ds->ops->port_fdb_dump)
179                 return ds->ops->port_fdb_dump(ds, dp->index, fdb, cb);
180
181         return -EOPNOTSUPP;
182 }
183
184 int dsa_port_mdb_add(struct dsa_port *dp,
185                      const struct switchdev_obj_port_mdb *mdb,
186                      struct switchdev_trans *trans)
187 {
188         struct dsa_notifier_mdb_info info = {
189                 .sw_index = dp->ds->index,
190                 .port = dp->index,
191                 .trans = trans,
192                 .mdb = mdb,
193         };
194
195         return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
196 }
197
198 int dsa_port_mdb_del(struct dsa_port *dp,
199                      const struct switchdev_obj_port_mdb *mdb)
200 {
201         struct dsa_notifier_mdb_info info = {
202                 .sw_index = dp->ds->index,
203                 .port = dp->index,
204                 .mdb = mdb,
205         };
206
207         return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
208 }
209
210 int dsa_port_mdb_dump(struct dsa_port *dp, struct switchdev_obj_port_mdb *mdb,
211                       switchdev_obj_dump_cb_t *cb)
212 {
213         struct dsa_switch *ds = dp->ds;
214
215         if (ds->ops->port_mdb_dump)
216                 return ds->ops->port_mdb_dump(ds, dp->index, mdb, cb);
217
218         return -EOPNOTSUPP;
219 }
220
221 int dsa_port_vlan_add(struct dsa_port *dp,
222                       const struct switchdev_obj_port_vlan *vlan,
223                       struct switchdev_trans *trans)
224 {
225         struct dsa_notifier_vlan_info info = {
226                 .sw_index = dp->ds->index,
227                 .port = dp->index,
228                 .trans = trans,
229                 .vlan = vlan,
230         };
231
232         return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
233 }
234
235 int dsa_port_vlan_del(struct dsa_port *dp,
236                       const struct switchdev_obj_port_vlan *vlan)
237 {
238         struct dsa_notifier_vlan_info info = {
239                 .sw_index = dp->ds->index,
240                 .port = dp->index,
241                 .vlan = vlan,
242         };
243
244         return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
245 }
246
247 int dsa_port_vlan_dump(struct dsa_port *dp,
248                        struct switchdev_obj_port_vlan *vlan,
249                        switchdev_obj_dump_cb_t *cb)
250 {
251         struct dsa_switch *ds = dp->ds;
252
253         if (ds->ops->port_vlan_dump)
254                 return ds->ops->port_vlan_dump(ds, dp->index, vlan, cb);
255
256         return -EOPNOTSUPP;
257 }