mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 11:41:29 -04:00
nvdimm: virtio_pmem: drain requests in freeze
virtio_pmem_freeze() currently deletes virtqueues and resets the device without waking threads waiting for a virtqueue descriptor or a host completion. Mark the request virtqueue broken before reset. This makes new submissions fail fast and lets -ENOSPC waiters leave the wait list. Reset the device before draining used and unused request tokens, then delete the virtqueues. This wakes waiters with -EIO. It also keeps the detach call on a quiesced device. Clear req_vq after del_vqs(). Make drain tolerate a NULL queue so remove after freeze does not dereference a stale virtqueue pointer. Also make virtio_pmem_flush() stop checking req_vq once the broken state is visible. A waiter woken by freeze/remove can resume after del_vqs() has cleared req_vq. Signed-off-by: Li Chen <me@linux.beauty> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-ID: <20260630092338.2094628-13-me@linux.beauty>
This commit is contained in:
committed by
Michael S. Tsirkin
parent
36e33955ae
commit
74d7d137f4
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user