blob: 72c8dbd3d3f2d3cf0bd038960235ed46d00cfb27 [file] [log] [blame]
Vivien Didelota40c1752017-05-19 17:00:44 -04001/*
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>
Vivien Didelotcfbed322017-05-19 17:00:45 -040014#include <linux/notifier.h>
Vivien Didelota40c1752017-05-19 17:00:44 -040015
16#include "dsa_priv.h"
17
Vivien Didelotcfbed322017-05-19 17:00:45 -040018static 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
Vivien Didelota40c1752017-05-19 17:00:44 -040028int 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
Vivien Didelotfb8a6a22017-09-22 19:01:56 -040059static void dsa_port_set_state_now(struct dsa_port *dp, u8 state)
Vivien Didelota40c1752017-05-19 17:00:44 -040060{
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}
Vivien Didelotcfbed322017-05-19 17:00:45 -040067
Vivien Didelotfb8a6a22017-09-22 19:01:56 -040068int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy)
69{
70 u8 stp_state = dp->bridge_dev ? BR_STATE_BLOCKING : BR_STATE_FORWARDING;
71 struct dsa_switch *ds = dp->ds;
72 int port = dp->index;
73 int err;
74
75 if (ds->ops->port_enable) {
76 err = ds->ops->port_enable(ds, port, phy);
77 if (err)
78 return err;
79 }
80
81 dsa_port_set_state_now(dp, stp_state);
82
83 return 0;
84}
85
86void dsa_port_disable(struct dsa_port *dp, struct phy_device *phy)
87{
88 struct dsa_switch *ds = dp->ds;
89 int port = dp->index;
90
91 dsa_port_set_state_now(dp, BR_STATE_DISABLED);
92
93 if (ds->ops->port_disable)
94 ds->ops->port_disable(ds, port, phy);
95}
96
Vivien Didelotcfbed322017-05-19 17:00:45 -040097int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
98{
99 struct dsa_notifier_bridge_info info = {
100 .sw_index = dp->ds->index,
101 .port = dp->index,
102 .br = br,
103 };
104 int err;
105
106 /* Here the port is already bridged. Reflect the current configuration
107 * so that drivers can program their chips accordingly.
108 */
109 dp->bridge_dev = br;
110
111 err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info);
112
113 /* The bridging is rolled back on error */
114 if (err)
115 dp->bridge_dev = NULL;
116
117 return err;
118}
119
120void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
121{
122 struct dsa_notifier_bridge_info info = {
123 .sw_index = dp->ds->index,
124 .port = dp->index,
125 .br = br,
126 };
127 int err;
128
129 /* Here the port is already unbridged. Reflect the current configuration
130 * so that drivers can program their chips accordingly.
131 */
132 dp->bridge_dev = NULL;
133
134 err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info);
135 if (err)
136 pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
137
138 /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
139 * so allow it to be in BR_STATE_FORWARDING to be kept functional
140 */
141 dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
142}
Vivien Didelot4d61d302017-05-19 17:00:46 -0400143
144int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
145 struct switchdev_trans *trans)
146{
147 struct dsa_switch *ds = dp->ds;
148
149 /* bridge skips -EOPNOTSUPP, so skip the prepare phase */
150 if (switchdev_trans_ph_prepare(trans))
151 return 0;
152
153 if (ds->ops->port_vlan_filtering)
154 return ds->ops->port_vlan_filtering(ds, dp->index,
155 vlan_filtering);
156
157 return 0;
158}
Vivien Didelotd87bd942017-05-19 17:00:47 -0400159
Vivien Didelotd87bd942017-05-19 17:00:47 -0400160int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
161 struct switchdev_trans *trans)
162{
163 unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
164 unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
Vivien Didelot1faabf72017-05-19 17:00:52 -0400165 struct dsa_notifier_ageing_time_info info = {
166 .ageing_time = ageing_time,
Vivien Didelot1faabf72017-05-19 17:00:52 -0400167 .trans = trans,
168 };
Vivien Didelotd87bd942017-05-19 17:00:47 -0400169
Vivien Didelot1faabf72017-05-19 17:00:52 -0400170 if (switchdev_trans_ph_prepare(trans))
171 return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
Vivien Didelotd87bd942017-05-19 17:00:47 -0400172
Vivien Didelotd87bd942017-05-19 17:00:47 -0400173 dp->ageing_time = ageing_time;
Vivien Didelotd87bd942017-05-19 17:00:47 -0400174
Vivien Didelot1faabf72017-05-19 17:00:52 -0400175 return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
Vivien Didelotd87bd942017-05-19 17:00:47 -0400176}
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400177
Arkadi Sharshevsky2acf4e62017-08-06 16:15:41 +0300178int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
179 u16 vid)
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400180{
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400181 struct dsa_notifier_fdb_info info = {
182 .sw_index = dp->ds->index,
183 .port = dp->index,
Arkadi Sharshevsky2acf4e62017-08-06 16:15:41 +0300184 .addr = addr,
185 .vid = vid,
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400186 };
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400187
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400188 return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400189}
190
Arkadi Sharshevsky2acf4e62017-08-06 16:15:41 +0300191int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
192 u16 vid)
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400193{
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400194 struct dsa_notifier_fdb_info info = {
195 .sw_index = dp->ds->index,
196 .port = dp->index,
Arkadi Sharshevsky2acf4e62017-08-06 16:15:41 +0300197 .addr = addr,
198 .vid = vid,
199
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400200 };
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400201
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400202 return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
Vivien Didelotd1cffff2017-05-19 17:00:48 -0400203}
204
Vivien Didelotde40fc52017-09-20 19:32:14 -0400205int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data)
206{
207 struct dsa_switch *ds = dp->ds;
208 int port = dp->index;
209
210 if (!ds->ops->port_fdb_dump)
211 return -EOPNOTSUPP;
212
213 return ds->ops->port_fdb_dump(ds, port, cb, data);
214}
215
Vivien Didelot3a9afea2017-05-19 17:00:49 -0400216int dsa_port_mdb_add(struct dsa_port *dp,
217 const struct switchdev_obj_port_mdb *mdb,
218 struct switchdev_trans *trans)
219{
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400220 struct dsa_notifier_mdb_info info = {
221 .sw_index = dp->ds->index,
222 .port = dp->index,
223 .trans = trans,
224 .mdb = mdb,
225 };
Vivien Didelot3a9afea2017-05-19 17:00:49 -0400226
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400227 return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
Vivien Didelot3a9afea2017-05-19 17:00:49 -0400228}
229
230int dsa_port_mdb_del(struct dsa_port *dp,
231 const struct switchdev_obj_port_mdb *mdb)
232{
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400233 struct dsa_notifier_mdb_info info = {
234 .sw_index = dp->ds->index,
235 .port = dp->index,
236 .mdb = mdb,
237 };
Vivien Didelot3a9afea2017-05-19 17:00:49 -0400238
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400239 return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
Vivien Didelot3a9afea2017-05-19 17:00:49 -0400240}
241
Vivien Didelot076e7132017-05-19 17:00:50 -0400242int dsa_port_vlan_add(struct dsa_port *dp,
243 const struct switchdev_obj_port_vlan *vlan,
244 struct switchdev_trans *trans)
245{
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400246 struct dsa_notifier_vlan_info info = {
247 .sw_index = dp->ds->index,
248 .port = dp->index,
249 .trans = trans,
250 .vlan = vlan,
251 };
Vivien Didelot076e7132017-05-19 17:00:50 -0400252
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400253 return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
Vivien Didelot076e7132017-05-19 17:00:50 -0400254}
255
256int dsa_port_vlan_del(struct dsa_port *dp,
257 const struct switchdev_obj_port_vlan *vlan)
258{
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400259 struct dsa_notifier_vlan_info info = {
260 .sw_index = dp->ds->index,
261 .port = dp->index,
262 .vlan = vlan,
263 };
Vivien Didelot076e7132017-05-19 17:00:50 -0400264
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400265 return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
Vivien Didelot076e7132017-05-19 17:00:50 -0400266}