forked from Minki/linux
s390/qeth: Switchdev event handler
QETH HiperSockets devices with LEARNING_SYNC capability can be used to construct a linux bridge with: 2 isolated southbound interfaces: a) a default network interface b) a LEARNING-SYNC HiperSockets interface and 1 non-isolated northbound interface. This is called a 'HiperSockets Converged Interface' (HSCI). The existing LEARNING_SYNC functionality is used to update the bridge fdb with MAC addresses that should be sent-out via the HiperSockets interface, instead of the default network interface. Add handling of switchdev events SWITCHDEV_FDB_ADD_TO_DEVICE and SWITCHDEV_FDB_DEL_TO_DEVICE to the qeth LEARNING_SYNC functionality. Thus if the northbound bridgeport of an HSCI doesn't only have a single static MAC address, but instead is a learning bridgeport, work is enqueued, so the HiperSockets virtual switch (that is external to this Linux instance) can update its fdb. When BRIDGE is a loadable module, QETH_L2 mustn't be built-in: drivers/s390/net/qeth_l2_main.o: in function 'qeth_l2_switchdev_event': drivers/s390/net/qeth_l2_main.c:927: undefined reference to 'br_port_flag_is_set' Add Kconfig dependency to enforce usable configurations. Signed-off-by: Alexandra Winter <wintera@linux.ibm.com> Reviewed-by: Wenjia Zhang <wenjia@linux.ibm.com> Signed-off-by: Karsten Graul <kgraul@linux.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
60bb108946
commit
4e20e73e63
@ -74,6 +74,7 @@ config QETH_L2
|
||||
def_tristate y
|
||||
prompt "qeth layer 2 device support"
|
||||
depends on QETH
|
||||
depends on BRIDGE || BRIDGE=n
|
||||
help
|
||||
Select this option to be able to run qeth devices in layer 2 mode.
|
||||
To compile as a module, choose M. The module name is qeth_l2.
|
||||
|
@ -717,15 +717,79 @@ static int qeth_l2_dev2br_an_set(struct qeth_card *card, bool enable)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct net_device_ops qeth_l2_netdev_ops;
|
||||
|
||||
static bool qeth_l2_must_learn(struct net_device *netdev,
|
||||
struct net_device *dstdev)
|
||||
{
|
||||
struct qeth_priv *priv;
|
||||
|
||||
priv = netdev_priv(netdev);
|
||||
return (netdev != dstdev &&
|
||||
(priv->brport_features & BR_LEARNING_SYNC) &&
|
||||
!(br_port_flag_is_set(netdev, BR_ISOLATED) &&
|
||||
br_port_flag_is_set(dstdev, BR_ISOLATED)) &&
|
||||
netdev->netdev_ops == &qeth_l2_netdev_ops);
|
||||
}
|
||||
|
||||
/* Called under rtnl_lock */
|
||||
static int qeth_l2_switchdev_event(struct notifier_block *unused,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dstdev, *brdev, *lowerdev;
|
||||
struct switchdev_notifier_fdb_info *fdb_info;
|
||||
struct switchdev_notifier_info *info = ptr;
|
||||
struct list_head *iter;
|
||||
struct qeth_card *card;
|
||||
|
||||
if (!(event == SWITCHDEV_FDB_ADD_TO_DEVICE ||
|
||||
event == SWITCHDEV_FDB_DEL_TO_DEVICE))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
dstdev = switchdev_notifier_info_to_dev(info);
|
||||
brdev = netdev_master_upper_dev_get_rcu(dstdev);
|
||||
if (!brdev || !netif_is_bridge_master(brdev))
|
||||
return NOTIFY_DONE;
|
||||
fdb_info = container_of(info,
|
||||
struct switchdev_notifier_fdb_info,
|
||||
info);
|
||||
iter = &brdev->adj_list.lower;
|
||||
lowerdev = netdev_next_lower_dev_rcu(brdev, &iter);
|
||||
while (lowerdev) {
|
||||
if (qeth_l2_must_learn(lowerdev, dstdev)) {
|
||||
card = lowerdev->ml_priv;
|
||||
QETH_CARD_TEXT_(card, 4, "b2dqw%03x", event);
|
||||
/* tbd: rc = qeth_l2_br2dev_queue_work(brdev, lowerdev,
|
||||
* dstdev, event,
|
||||
* fdb_info->addr);
|
||||
*/
|
||||
}
|
||||
lowerdev = netdev_next_lower_dev_rcu(brdev, &iter);
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block qeth_l2_sw_notifier = {
|
||||
.notifier_call = qeth_l2_switchdev_event,
|
||||
};
|
||||
|
||||
static refcount_t qeth_l2_switchdev_notify_refcnt;
|
||||
|
||||
/* Called under rtnl_lock */
|
||||
static void qeth_l2_br2dev_get(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!refcount_inc_not_zero(&qeth_l2_switchdev_notify_refcnt)) {
|
||||
/* tbd: register_switchdev_notifier(&qeth_l2_sw_notifier); */
|
||||
refcount_set(&qeth_l2_switchdev_notify_refcnt, 1);
|
||||
QETH_DBF_MESSAGE(2, "qeth_l2_sw_notifier registered\n");
|
||||
rc = register_switchdev_notifier(&qeth_l2_sw_notifier);
|
||||
if (rc) {
|
||||
QETH_DBF_MESSAGE(2,
|
||||
"failed to register qeth_l2_sw_notifier: %d\n",
|
||||
rc);
|
||||
} else {
|
||||
refcount_set(&qeth_l2_switchdev_notify_refcnt, 1);
|
||||
QETH_DBF_MESSAGE(2, "qeth_l2_sw_notifier registered\n");
|
||||
}
|
||||
}
|
||||
QETH_DBF_TEXT_(SETUP, 2, "b2d+%04d",
|
||||
qeth_l2_switchdev_notify_refcnt.refs.counter);
|
||||
@ -734,9 +798,18 @@ static void qeth_l2_br2dev_get(void)
|
||||
/* Called under rtnl_lock */
|
||||
static void qeth_l2_br2dev_put(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (refcount_dec_and_test(&qeth_l2_switchdev_notify_refcnt)) {
|
||||
/* tbd: unregister_switchdev_notifier(&qeth_l2_sw_notifier); */
|
||||
QETH_DBF_MESSAGE(2, "qeth_l2_sw_notifier unregistered\n");
|
||||
rc = unregister_switchdev_notifier(&qeth_l2_sw_notifier);
|
||||
if (rc) {
|
||||
QETH_DBF_MESSAGE(2,
|
||||
"failed to unregister qeth_l2_sw_notifier: %d\n",
|
||||
rc);
|
||||
} else {
|
||||
QETH_DBF_MESSAGE(2,
|
||||
"qeth_l2_sw_notifier unregistered\n");
|
||||
}
|
||||
}
|
||||
QETH_DBF_TEXT_(SETUP, 2, "b2d-%04d",
|
||||
qeth_l2_switchdev_notify_refcnt.refs.counter);
|
||||
|
Loading…
Reference in New Issue
Block a user