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.
