From: Jan Kiszka <[email protected]>

Make sure that open/stop with their calls to ivshm_net_run and
ivshm_net_do_stop do not race with ivshm_net_state_change that runs in
workqueue context. Add a separate mutex with this so that state changes
can be completed independently of rtnl_lock.

Signed-off-by: Jan Kiszka <[email protected]>
---
 drivers/net/ivshmem-net.c | 35 ++++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c
index c4821b80aec8..1b22b1cc5ea3 100644
--- a/drivers/net/ivshmem-net.c
+++ b/drivers/net/ivshmem-net.c
@@ -30,6 +30,7 @@
 
 #define DRV_NAME "ivshmem-net"
 
+#define IVSHM_NET_STATE_UNKNOWN                (~0)
 #define IVSHM_NET_STATE_RESET          0
 #define IVSHM_NET_STATE_INIT           1
 #define IVSHM_NET_STATE_READY          2
@@ -88,6 +89,7 @@ struct ivshm_net {
 
        struct napi_struct napi;
 
+       struct mutex state_lock;
        u32 state;
        u32 last_peer_state;
        u32 *state_table;
@@ -575,6 +577,15 @@ static void ivshm_net_state_change(struct work_struct 
*work)
        struct net_device *ndev = in->napi.dev;
        u32 peer_state = READ_ONCE(in->state_table[in->peer_id]);
 
+       mutex_lock(&in->state_lock);
+
+       if (peer_state == in->last_peer_state) {
+               mutex_unlock(&in->state_lock);
+               return;
+       }
+
+       in->last_peer_state = peer_state;
+
        switch (in->state) {
        case IVSHM_NET_STATE_RESET:
                /*
@@ -594,9 +605,13 @@ static void ivshm_net_state_change(struct work_struct 
*work)
                        ivshm_net_init_queues(ndev);
                        ivshm_net_set_state(in, IVSHM_NET_STATE_READY);
 
+                       mutex_unlock(&in->state_lock);
+
                        rtnl_lock();
                        call_netdevice_notifiers(NETDEV_CHANGEADDR, ndev);
                        rtnl_unlock();
+
+                       return;
                }
                break;
 
@@ -622,15 +637,12 @@ static void ivshm_net_state_change(struct work_struct 
*work)
                break;
        }
 
-       virt_wmb();
-       WRITE_ONCE(in->last_peer_state, peer_state);
+       mutex_unlock(&in->state_lock);
 }
 
 static void ivshm_net_check_state(struct ivshm_net *in)
 {
-       if (in->state_table[in->peer_id] != in->last_peer_state ||
-           !test_bit(IVSHM_NET_FLAG_RUN, &in->flags))
-               queue_work(in->state_wq, &in->state_work);
+       queue_work(in->state_wq, &in->state_work);
 }
 
 static irqreturn_t ivshm_net_int_state(int irq, void *data)
@@ -663,17 +675,27 @@ static irqreturn_t ivshm_net_intx(int irq, void *data)
 
 static int ivshm_net_open(struct net_device *ndev)
 {
+       struct ivshm_net *in = netdev_priv(ndev);
+
        netdev_reset_queue(ndev);
        ndev->operstate = IF_OPER_UP;
+
+       mutex_lock(&in->state_lock);
        ivshm_net_run(ndev);
+       mutex_unlock(&in->state_lock);
 
        return 0;
 }
 
 static int ivshm_net_stop(struct net_device *ndev)
 {
+       struct ivshm_net *in = netdev_priv(ndev);
+
        ndev->operstate = IF_OPER_DOWN;
+
+       mutex_lock(&in->state_lock);
        ivshm_net_do_stop(ndev);
+       mutex_unlock(&in->state_lock);
 
        return 0;
 }
@@ -939,6 +961,9 @@ static int ivshm_net_probe(struct pci_dev *pdev,
 
        in->peer_id = !id;
        in->pdev = pdev;
+       in->last_peer_state = IVSHM_NET_STATE_UNKNOWN;
+
+       mutex_init(&in->state_lock);
 
        ret = ivshm_net_calc_qsize(ndev);
        if (ret)
-- 
2.26.2

-- 
You received this message because you are subscribed to the Google Groups 
"Jailhouse" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/jailhouse-dev/04d10f9e-9e04-ad4b-a3e6-399f84b2ecb7%40siemens.com.

Reply via email to