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 } 260