GNU Linux-libre 4.14.266-gnu1
[releases.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, const unsigned char *addr,
150                      u16 vid)
151 {
152         struct dsa_notifier_fdb_info info = {
153                 .sw_index = dp->ds->index,
154                 .port = dp->index,
155                 .addr = addr,
156                 .vid = vid,
157         };
158
159         return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
160 }
161
162 int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
163                      u16 vid)
164 {
165         struct dsa_notifier_fdb_info info = {
166                 .sw_index = dp->ds->index,
167                 .port = dp->index,
168                 .addr = addr,
169                 .vid = vid,
170
171         };
172
173         return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
174 }
175
176 int dsa_port_mdb_add(struct dsa_port *dp,
177                      const struct switchdev_obj_port_mdb *mdb,
178                      struct switchdev_trans *trans)
179 {
180         struct dsa_notifier_mdb_info info = {
181                 .sw_index = dp->ds->index,
182                 .port = dp->index,
183                 .trans = trans,
184                 .mdb = mdb,
185         };
186
187         return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
188 }
189
190 int dsa_port_mdb_del(struct dsa_port *dp,
191                      const struct switchdev_obj_port_mdb *mdb)
192 {
193         struct dsa_notifier_mdb_info info = {
194                 .sw_index = dp->ds->index,
195                 .port = dp->index,
196                 .mdb = mdb,
197         };
198
199         return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
200 }
201
202 int dsa_port_vlan_add(struct dsa_port *dp,
203                       const struct switchdev_obj_port_vlan *vlan,
204                       struct switchdev_trans *trans)
205 {
206         struct dsa_notifier_vlan_info info = {
207                 .sw_index = dp->ds->index,
208                 .port = dp->index,
209                 .trans = trans,
210                 .vlan = vlan,
211         };
212
213         return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
214 }
215
216 int dsa_port_vlan_del(struct dsa_port *dp,
217                       const struct switchdev_obj_port_vlan *vlan)
218 {
219         struct dsa_notifier_vlan_info info = {
220                 .sw_index = dp->ds->index,
221                 .port = dp->index,
222                 .vlan = vlan,
223         };
224
225         return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
226 }