-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dsa-distribute-switch-events'
Vivien Didelot says: ==================== net: dsa: distribute switch events DSA is by nature the support for a switch fabric, which can be composed of a single, or multiple interconnected Ethernet switch chips. The current DSA core behavior is to identify the slave port targeted by a request (e.g. adding a VLAN entry), and program the switch chip to which it belongs accordingly. This is problematic in a multi-chip environment, since all chips of a fabric must be aware of most configuration changes. Here are some concrete examples in a 3-chip environment: [CPU].................... (mdio) (eth0) | : : : _|_____ _______ _______ [__sw0__]--[__sw1__]--[__sw2__] | | | | | | | | | v v v v v v v v v p1 p2 p3 p4 p5 p6 p7 p8 p9 If you add a VLAN entry on p7, sw2 gets programmed, but frames won't reach the CPU interface in a VLAN filtered setup. sw0 and sw1 also need to be programmed. The same problem comes with MAC addresses (FDB, MDB), or ageing time changes for instance. This patch series uses the notification chain introduced for bridging, to notify not only bridge, but switchdev attributes and objects events to all switch chips of the fabric. An ugly debug message printing the ignored event and switch info in the code handling the switch VLAN events would give us: # bridge vlan add dev p7 vid 42 sw0: ignoring DSA_NOTIFIER_VLAN_ADD for sw2 (prepare phase) sw1: ignoring DSA_NOTIFIER_VLAN_ADD for sw2 (prepare phase) sw0: ignoring DSA_NOTIFIER_VLAN_ADD for sw2 (commit phase) sw1: ignoring DSA_NOTIFIER_VLAN_ADD for sw2 (commit phase) To achieve that, patches 1-8 change the scope of the bridge and switchdev callbacks from the DSA slave device to the generic DSA port, so that the port-wide API can be used later for switch ports not exposed to userspace, such as CPU and DSA links. Patches 9-15 move the DSA port specific functions in a new port.c file. Patches 16-20 introduce new events to notify the fabric about switchdev attributes and objects manipulation. This patch series only adds the plumbing to support a distributed configuration, but for the moment, each switch chip ignores events from other chips of the fabric, to keep the current behavior. The next patch series will add support for cross-chip configuration of bridge ageing time, VLAN and MAC address databases operations, etc. ==================== Tested-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
- Loading branch information
Showing
6 changed files
with
547 additions
and
337 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,260 @@ | ||
/* | ||
* Handling of a single switch port | ||
* | ||
* Copyright (c) 2017 Savoir-faire Linux Inc. | ||
* Vivien Didelot <vivien.didelot@savoirfairelinux.com> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
*/ | ||
|
||
#include <linux/if_bridge.h> | ||
#include <linux/notifier.h> | ||
|
||
#include "dsa_priv.h" | ||
|
||
static int dsa_port_notify(struct dsa_port *dp, unsigned long e, void *v) | ||
{ | ||
struct raw_notifier_head *nh = &dp->ds->dst->nh; | ||
int err; | ||
|
||
err = raw_notifier_call_chain(nh, e, v); | ||
|
||
return notifier_to_errno(err); | ||
} | ||
|
||
int dsa_port_set_state(struct dsa_port *dp, u8 state, | ||
struct switchdev_trans *trans) | ||
{ | ||
struct dsa_switch *ds = dp->ds; | ||
int port = dp->index; | ||
|
||
if (switchdev_trans_ph_prepare(trans)) | ||
return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP; | ||
|
||
if (ds->ops->port_stp_state_set) | ||
ds->ops->port_stp_state_set(ds, port, state); | ||
|
||
if (ds->ops->port_fast_age) { | ||
/* Fast age FDB entries or flush appropriate forwarding database | ||
* for the given port, if we are moving it from Learning or | ||
* Forwarding state, to Disabled or Blocking or Listening state. | ||
*/ | ||
|
||
if ((dp->stp_state == BR_STATE_LEARNING || | ||
dp->stp_state == BR_STATE_FORWARDING) && | ||
(state == BR_STATE_DISABLED || | ||
state == BR_STATE_BLOCKING || | ||
state == BR_STATE_LISTENING)) | ||
ds->ops->port_fast_age(ds, port); | ||
} | ||
|
||
dp->stp_state = state; | ||
|
||
return 0; | ||
} | ||
|
||
void dsa_port_set_state_now(struct dsa_port *dp, u8 state) | ||
{ | ||
int err; | ||
|
||
err = dsa_port_set_state(dp, state, NULL); | ||
if (err) | ||
pr_err("DSA: failed to set STP state %u (%d)\n", state, err); | ||
} | ||
|
||
int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) | ||
{ | ||
struct dsa_notifier_bridge_info info = { | ||
.sw_index = dp->ds->index, | ||
.port = dp->index, | ||
.br = br, | ||
}; | ||
int err; | ||
|
||
/* Here the port is already bridged. Reflect the current configuration | ||
* so that drivers can program their chips accordingly. | ||
*/ | ||
dp->bridge_dev = br; | ||
|
||
err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info); | ||
|
||
/* The bridging is rolled back on error */ | ||
if (err) | ||
dp->bridge_dev = NULL; | ||
|
||
return err; | ||
} | ||
|
||
void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) | ||
{ | ||
struct dsa_notifier_bridge_info info = { | ||
.sw_index = dp->ds->index, | ||
.port = dp->index, | ||
.br = br, | ||
}; | ||
int err; | ||
|
||
/* Here the port is already unbridged. Reflect the current configuration | ||
* so that drivers can program their chips accordingly. | ||
*/ | ||
dp->bridge_dev = NULL; | ||
|
||
err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info); | ||
if (err) | ||
pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); | ||
|
||
/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, | ||
* so allow it to be in BR_STATE_FORWARDING to be kept functional | ||
*/ | ||
dsa_port_set_state_now(dp, BR_STATE_FORWARDING); | ||
} | ||
|
||
int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, | ||
struct switchdev_trans *trans) | ||
{ | ||
struct dsa_switch *ds = dp->ds; | ||
|
||
/* bridge skips -EOPNOTSUPP, so skip the prepare phase */ | ||
if (switchdev_trans_ph_prepare(trans)) | ||
return 0; | ||
|
||
if (ds->ops->port_vlan_filtering) | ||
return ds->ops->port_vlan_filtering(ds, dp->index, | ||
vlan_filtering); | ||
|
||
return 0; | ||
} | ||
|
||
int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, | ||
struct switchdev_trans *trans) | ||
{ | ||
unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); | ||
unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); | ||
struct dsa_notifier_ageing_time_info info = { | ||
.ageing_time = ageing_time, | ||
.sw_index = dp->ds->index, | ||
.trans = trans, | ||
}; | ||
|
||
if (switchdev_trans_ph_prepare(trans)) | ||
return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); | ||
|
||
dp->ageing_time = ageing_time; | ||
|
||
return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); | ||
} | ||
|
||
int dsa_port_fdb_add(struct dsa_port *dp, | ||
const struct switchdev_obj_port_fdb *fdb, | ||
struct switchdev_trans *trans) | ||
{ | ||
struct dsa_notifier_fdb_info info = { | ||
.sw_index = dp->ds->index, | ||
.port = dp->index, | ||
.trans = trans, | ||
.fdb = fdb, | ||
}; | ||
|
||
return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); | ||
} | ||
|
||
int dsa_port_fdb_del(struct dsa_port *dp, | ||
const struct switchdev_obj_port_fdb *fdb) | ||
{ | ||
struct dsa_notifier_fdb_info info = { | ||
.sw_index = dp->ds->index, | ||
.port = dp->index, | ||
.fdb = fdb, | ||
}; | ||
|
||
return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); | ||
} | ||
|
||
int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb, | ||
switchdev_obj_dump_cb_t *cb) | ||
{ | ||
struct dsa_switch *ds = dp->ds; | ||
|
||
if (ds->ops->port_fdb_dump) | ||
return ds->ops->port_fdb_dump(ds, dp->index, fdb, cb); | ||
|
||
return -EOPNOTSUPP; | ||
} | ||
|
||
int dsa_port_mdb_add(struct dsa_port *dp, | ||
const struct switchdev_obj_port_mdb *mdb, | ||
struct switchdev_trans *trans) | ||
{ | ||
struct dsa_notifier_mdb_info info = { | ||
.sw_index = dp->ds->index, | ||
.port = dp->index, | ||
.trans = trans, | ||
.mdb = mdb, | ||
}; | ||
|
||
return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); | ||
} | ||
|
||
int dsa_port_mdb_del(struct dsa_port *dp, | ||
const struct switchdev_obj_port_mdb *mdb) | ||
{ | ||
struct dsa_notifier_mdb_info info = { | ||
.sw_index = dp->ds->index, | ||
.port = dp->index, | ||
.mdb = mdb, | ||
}; | ||
|
||
return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); | ||
} | ||
|
||
int dsa_port_mdb_dump(struct dsa_port *dp, struct switchdev_obj_port_mdb *mdb, | ||
switchdev_obj_dump_cb_t *cb) | ||
{ | ||
struct dsa_switch *ds = dp->ds; | ||
|
||
if (ds->ops->port_mdb_dump) | ||
return ds->ops->port_mdb_dump(ds, dp->index, mdb, cb); | ||
|
||
return -EOPNOTSUPP; | ||
} | ||
|
||
int dsa_port_vlan_add(struct dsa_port *dp, | ||
const struct switchdev_obj_port_vlan *vlan, | ||
struct switchdev_trans *trans) | ||
{ | ||
struct dsa_notifier_vlan_info info = { | ||
.sw_index = dp->ds->index, | ||
.port = dp->index, | ||
.trans = trans, | ||
.vlan = vlan, | ||
}; | ||
|
||
return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); | ||
} | ||
|
||
int dsa_port_vlan_del(struct dsa_port *dp, | ||
const struct switchdev_obj_port_vlan *vlan) | ||
{ | ||
struct dsa_notifier_vlan_info info = { | ||
.sw_index = dp->ds->index, | ||
.port = dp->index, | ||
.vlan = vlan, | ||
}; | ||
|
||
return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); | ||
} | ||
|
||
int dsa_port_vlan_dump(struct dsa_port *dp, | ||
struct switchdev_obj_port_vlan *vlan, | ||
switchdev_obj_dump_cb_t *cb) | ||
{ | ||
struct dsa_switch *ds = dp->ds; | ||
|
||
if (ds->ops->port_vlan_dump) | ||
return ds->ops->port_vlan_dump(ds, dp->index, vlan, cb); | ||
|
||
return -EOPNOTSUPP; | ||
} |
Oops, something went wrong.