Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions drivers/block/virtio_blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,12 @@ static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
blk_status_t status;
int err;

/* Fail fast if ERS frozen tore down VQs or the disk was marked dead. */
if (unlikely(!disk_live(vblk->disk) || !vblk->vqs || !vblk->vdev)) {
blk_mq_start_request(req);
return BLK_STS_IOERR;
}

status = virtblk_prep_rq(hctx, vblk, req, vbr);
if (unlikely(status))
return status;
Expand Down Expand Up @@ -1559,6 +1565,29 @@ static int virtblk_probe(struct virtio_device *vdev)
return err;
}

/* Stop I/O and mark the gendisk dead (ERS perm_failure or system shutdown). */
static void virtblk_shutdown(struct virtio_device *vdev)
{
struct virtio_blk *vblk = vdev->priv;
struct request_queue *q;
unsigned int memflags;

if (!vblk || !vblk->disk)
return;

flush_work(&vblk->config_work);
virtio_break_device(vdev);

q = vblk->disk->queue;
memflags = blk_mq_freeze_queue(q);
blk_mq_quiesce_queue_nowait(q);

blk_mark_disk_dead(vblk->disk);

blk_mq_unquiesce_queue(q);
blk_mq_unfreeze_queue(q, memflags);
}

static void virtblk_remove(struct virtio_device *vdev)
{
struct virtio_blk *vblk = vdev->priv;
Expand Down Expand Up @@ -1682,6 +1711,7 @@ static struct virtio_driver virtio_blk = {
.probe = virtblk_probe,
.remove = virtblk_remove,
.config_changed = virtblk_config_changed,
.shutdown = virtblk_shutdown,
#ifdef CONFIG_PM_SLEEP
.freeze = virtblk_freeze,
.restore = virtblk_restore,
Expand Down
38 changes: 38 additions & 0 deletions drivers/virtio/virtio_pci_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,45 @@ static void virtio_pci_reset_done(struct pci_dev *pci_dev)
dev_warn(&pci_dev->dev, "Reset done failure: %d", ret);
}

static pci_ers_result_t virtio_pci_error_detected(struct pci_dev *pci_dev,
pci_channel_state_t state)
{
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);

/*
* PCI ERS error_detected: quiesce on frozen before bus reset; on
* permanent failure ask the virtio driver to shut down (virtio-blk
* marks the disk dead in its .shutdown handler).
*/
switch (state) {
case pci_channel_io_normal:
return PCI_ERS_RESULT_CAN_RECOVER;
case pci_channel_io_frozen:
pci_info(pci_dev, "frozen error detected, quiesce device\n");
if (virtio_device_reset_prepare(&vp_dev->vdev))
dev_warn(&pci_dev->dev, "frozen: reset prepare failed\n");
return PCI_ERS_RESULT_NEED_RESET;
case pci_channel_io_perm_failure:
dev_warn(&pci_dev->dev,
"permanent failure, disconnecting device\n");
{
struct virtio_driver *drv =
drv_to_virtio(vp_dev->vdev.dev.driver);

if (drv && drv->shutdown)
drv->shutdown(&vp_dev->vdev);
else
virtio_break_device(&vp_dev->vdev);
}
return PCI_ERS_RESULT_DISCONNECT;
default:
break;
}
return PCI_ERS_RESULT_NEED_RESET;
}

static const struct pci_error_handlers virtio_pci_err_handler = {
.error_detected = virtio_pci_error_detected,
.reset_prepare = virtio_pci_reset_prepare,
.reset_done = virtio_pci_reset_done,
};
Expand Down
Loading