[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20210806152603.375642-3-kgraul@linux.ibm.com>
Date: Fri, 6 Aug 2021 17:26:02 +0200
From: Karsten Graul <kgraul@...ux.ibm.com>
To: David Miller <davem@...emloft.net>,
Jakub Kicinski <kuba@...nel.org>
Cc: netdev@...r.kernel.org, linux-s390@...r.kernel.org,
Heiko Carstens <hca@...ux.ibm.com>,
Guvenc Gulce <guvenc@...ux.ibm.com>,
Julian Wiedmann <jwi@...ux.ibm.com>,
Wenjia Zhang <wenjia@...ux.ibm.com>,
Alexandra Winter <wintera@...ux.ibm.com>
Subject: [PATCH net-next 2/3] s390/qeth: Switchdev event handler
From: Alexandra Winter <wintera@...ux.ibm.com>
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@...ux.ibm.com>
Reviewed-by: Wenjia Zhang <wenjia@...ux.ibm.com>
Signed-off-by: Karsten Graul <kgraul@...ux.ibm.com>
---
drivers/s390/net/Kconfig | 1 +
drivers/s390/net/qeth_l2_main.c | 83 +++++++++++++++++++++++++++++++--
2 files changed, 79 insertions(+), 5 deletions(-)
diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig
index cff91b4f1a76..9c67b97faba2 100644
--- a/drivers/s390/net/Kconfig
+++ b/drivers/s390/net/Kconfig
@@ -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.
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 3d02d35df5d3..e38a1befce3f 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -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);
--
2.25.1
Powered by blists - more mailing lists