diff --git a/drivers/nvdimm/nd_virtio.c b/drivers/nvdimm/nd_virtio.c index a6820300cbe8..3b8be79a20a0 100644 --- a/drivers/nvdimm/nd_virtio.c +++ b/drivers/nvdimm/nd_virtio.c @@ -99,6 +99,9 @@ void virtio_pmem_drain(struct virtio_pmem *vpmem) struct virtio_pmem_request *req; unsigned int len; + if (!vpmem->req_vq) + return; + while ((req = virtqueue_get_buf(vpmem->req_vq, &len)) != NULL) { virtio_pmem_clear_inflight(vpmem, req); virtio_pmem_complete_err(req); @@ -218,6 +221,8 @@ static int virtio_pmem_flush(struct nd_region *nd_region) break; } + if (READ_ONCE(vpmem->broken)) + err = -EIO; if (err == -EIO || virtqueue_is_broken(vpmem->req_vq)) virtio_pmem_mark_broken(vpmem); diff --git a/drivers/nvdimm/virtio_pmem.c b/drivers/nvdimm/virtio_pmem.c index 36664a5ea25e..7ee3fb1779f7 100644 --- a/drivers/nvdimm/virtio_pmem.c +++ b/drivers/nvdimm/virtio_pmem.c @@ -17,11 +17,16 @@ static struct virtio_device_id id_table[] = { /* Initialize virt queue */ static int init_vq(struct virtio_pmem *vpmem) { + int err; + /* single vq */ vpmem->req_vq = virtio_find_single_vq(vpmem->vdev, virtio_pmem_host_ack, "flush_queue"); - if (IS_ERR(vpmem->req_vq)) - return PTR_ERR(vpmem->req_vq); + if (IS_ERR(vpmem->req_vq)) { + err = PTR_ERR(vpmem->req_vq); + vpmem->req_vq = NULL; + return err; + } spin_lock_init(&vpmem->pmem_lock); INIT_LIST_HEAD(&vpmem->req_list); @@ -31,6 +36,15 @@ static int init_vq(struct virtio_pmem *vpmem) return 0; }; +static void virtio_pmem_del_vqs(struct virtio_pmem *vpmem) +{ + if (!vpmem->req_vq) + return; + + vpmem->vdev->config->del_vqs(vpmem->vdev); + vpmem->req_vq = NULL; +} + static int virtio_pmem_validate(struct virtio_device *vdev) { struct virtio_shm_region shm_reg; @@ -139,7 +153,7 @@ static int virtio_pmem_probe(struct virtio_device *vdev) virtio_reset_device(vdev); nvdimm_bus_unregister(vpmem->nvdimm_bus); out_vq: - vdev->config->del_vqs(vdev); + virtio_pmem_del_vqs(vpmem); out_wq: destroy_workqueue(vpmem->flush_wq); out_err: @@ -164,18 +178,28 @@ static void virtio_pmem_remove(struct virtio_device *vdev) spin_unlock_irqrestore(&vpmem->pmem_lock, flags); nvdimm_bus_unregister(nvdimm_bus); - vdev->config->del_vqs(vdev); + virtio_pmem_del_vqs(vpmem); destroy_workqueue(vpmem->flush_wq); } static int virtio_pmem_freeze(struct virtio_device *vdev) { struct virtio_pmem *vpmem = vdev->priv; + unsigned long flags; + + spin_lock_irqsave(&vpmem->pmem_lock, flags); + virtio_pmem_mark_broken(vpmem); + spin_unlock_irqrestore(&vpmem->pmem_lock, flags); drain_workqueue(vpmem->flush_wq); - vdev->config->del_vqs(vdev); virtio_reset_device(vdev); + spin_lock_irqsave(&vpmem->pmem_lock, flags); + virtio_pmem_drain(vpmem); + spin_unlock_irqrestore(&vpmem->pmem_lock, flags); + + virtio_pmem_del_vqs(vpmem); + return 0; }