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
|
def_tristate y
|
||||||
prompt "qeth layer 2 device support"
|
prompt "qeth layer 2 device support"
|
||||||
depends on QETH
|
depends on QETH
|
||||||
|
depends on BRIDGE || BRIDGE=n
|
||||||
help
|
help
|
||||||
Select this option to be able to run qeth devices in layer 2 mode.
|
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.
|
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;
|
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;
|
static refcount_t qeth_l2_switchdev_notify_refcnt;
|
||||||
|
|
||||||
/* Called under rtnl_lock */
|
/* Called under rtnl_lock */
|
||||||
static void qeth_l2_br2dev_get(void)
|
static void qeth_l2_br2dev_get(void)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (!refcount_inc_not_zero(&qeth_l2_switchdev_notify_refcnt)) {
|
if (!refcount_inc_not_zero(&qeth_l2_switchdev_notify_refcnt)) {
|
||||||
/* tbd: register_switchdev_notifier(&qeth_l2_sw_notifier); */
|
rc = register_switchdev_notifier(&qeth_l2_sw_notifier);
|
||||||
refcount_set(&qeth_l2_switchdev_notify_refcnt, 1);
|
if (rc) {
|
||||||
QETH_DBF_MESSAGE(2, "qeth_l2_sw_notifier registered\n");
|
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_DBF_TEXT_(SETUP, 2, "b2d+%04d",
|
||||||
qeth_l2_switchdev_notify_refcnt.refs.counter);
|
qeth_l2_switchdev_notify_refcnt.refs.counter);
|
||||||
@ -734,9 +798,18 @@ static void qeth_l2_br2dev_get(void)
|
|||||||
/* Called under rtnl_lock */
|
/* Called under rtnl_lock */
|
||||||
static void qeth_l2_br2dev_put(void)
|
static void qeth_l2_br2dev_put(void)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (refcount_dec_and_test(&qeth_l2_switchdev_notify_refcnt)) {
|
if (refcount_dec_and_test(&qeth_l2_switchdev_notify_refcnt)) {
|
||||||
/* tbd: unregister_switchdev_notifier(&qeth_l2_sw_notifier); */
|
rc = unregister_switchdev_notifier(&qeth_l2_sw_notifier);
|
||||||
QETH_DBF_MESSAGE(2, "qeth_l2_sw_notifier unregistered\n");
|
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_DBF_TEXT_(SETUP, 2, "b2d-%04d",
|
||||||
qeth_l2_switchdev_notify_refcnt.refs.counter);
|
qeth_l2_switchdev_notify_refcnt.refs.counter);
|
||||||
|
Loading…
Reference in New Issue
Block a user