net/netvsc: process link change messages in alarm
authorStephen Hemminger <stephen@networkplumber.org>
Tue, 19 May 2020 16:52:29 +0000 (09:52 -0700)
committerFerruh Yigit <ferruh.yigit@intel.com>
Thu, 28 May 2020 15:57:07 +0000 (17:57 +0200)
The original code would deadlock itself if a link change event
happened with link state interrupt enabled. The problem is that
the link state changed message would be seen while reading
the host to guest ring (under lock) and then the driver would
send a query to the host to see the new link state. The response
would never be seen (stuck in a while loop) waiting for the
response.

The solution is to use the link change indication to trigger
a DPDK alarm. The alarm will happen in a different thread and
in that context it can send request for new link state and
also do interrupt callback. This is similar to how the bonding
driver is handling the same thing.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
drivers/net/netvsc/hn_rndis.c

index 7947ca2..6a850ce 100644 (file)
@@ -17,6 +17,7 @@
 #include <rte_memzone.h>
 #include <rte_malloc.h>
 #include <rte_atomic.h>
+#include <rte_alarm.h>
 #include <rte_branch_prediction.h>
 #include <rte_ether.h>
 #include <rte_common.h>
@@ -281,6 +282,15 @@ static int hn_nvs_send_rndis_ctrl(struct vmbus_channel *chan,
                                  &nvs_rndis, sizeof(nvs_rndis), 0U, NULL);
 }
 
+/*
+ * Alarm callback to process link changed notifications.
+ * Can not directly since link_status is discovered while reading ring
+ */
+static void hn_rndis_link_alarm(void *arg)
+{
+       _rte_eth_dev_callback_process(arg, RTE_ETH_EVENT_INTR_LSC, NULL);
+}
+
 void hn_rndis_link_status(struct rte_eth_dev *dev, const void *msg)
 {
        const struct rndis_status_msg *indicate = msg;
@@ -298,11 +308,8 @@ void hn_rndis_link_status(struct rte_eth_dev *dev, const void *msg)
        case RNDIS_STATUS_LINK_SPEED_CHANGE:
        case RNDIS_STATUS_MEDIA_CONNECT:
        case RNDIS_STATUS_MEDIA_DISCONNECT:
-               if (dev->data->dev_conf.intr_conf.lsc &&
-                   hn_dev_link_update(dev, 0) == 0)
-                       _rte_eth_dev_callback_process(dev,
-                                                     RTE_ETH_EVENT_INTR_LSC,
-                                                     NULL);
+               if (dev->data->dev_conf.intr_conf.lsc)
+                       rte_eal_alarm_set(10, hn_rndis_link_alarm, dev);
                break;
        default:
                PMD_DRV_LOG(NOTICE, "unknown RNDIS indication: %#x",
@@ -1101,6 +1108,10 @@ hn_rndis_attach(struct hn_data *hv)
 void
 hn_rndis_detach(struct hn_data *hv)
 {
+       struct rte_eth_dev *dev = &rte_eth_devices[hv->port_id];
+
+       rte_eal_alarm_cancel(hn_rndis_link_alarm, dev);
+
        /* Halt the RNDIS. */
        hn_rndis_halt(hv);
 }