On Mon, 2023-11-27 at 10:55 +0800, Heng Qi wrote: > @@ -4738,11 +4881,14 @@ static void remove_vq_common(struct virtnet_info *vi) > static void virtnet_remove(struct virtio_device *vdev) > { > struct virtnet_info *vi = vdev->priv; > + int i; > > virtnet_cpu_notif_remove(vi); > > /* Make sure no work handler is accessing the device. */ > flush_work(&vi->config_work); > + for (i = 0; i < vi->max_queue_pairs; i++) > + cancel_work(&vi->rq[i].dim.work); If the dim work is still running here, what prevents it from completing after the following unregister/free netdev? It looks like you want need to call cancel_work_sync here? Additionally the later remove_vq_common() will needless call cancel_work() again; possibly is better to consolidate a single (sync) call there. Cheers, Paolo