/* * removes a virtio device if a hot remove event has been * requested by the host. */ static int _vop_remove_device(struct mic_device_desc __iomem *d, unsigned int offset, struct vop_device *vpdev) { struct mic_device_ctrl __iomem *dc = (void __iomem *)d + _vop_aligned_desc_size(d); struct _vop_vdev *vdev = (struct _vop_vdev *)readq(&dc->vdev); u8 status; int ret = -1; if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) { dev_dbg(&vpdev->dev, "%s %d config_change %d type %d vdev %p\n", __func__, __LINE__, ioread8(&dc->config_change), ioread8(&d->type), vdev); status = ioread8(&d->status); reinit_completion(&vdev->reset_done); unregister_virtio_device(&vdev->vdev); vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); iowrite8(-1, &dc->h2c_vdev_db); if (status & VIRTIO_CONFIG_S_DRIVER_OK) wait_for_completion(&vdev->reset_done); kfree(vdev); iowrite8(1, &dc->guest_ack); dev_dbg(&vpdev->dev, "%s %d guest_ack %d\n", __func__, __LINE__, ioread8(&dc->guest_ack)); iowrite8(-1, &d->type); ret = 0; } return ret; }
/* * adds a new device and register it with virtio * appropriate drivers are loaded by the device model */ static int _vop_add_device(struct mic_device_desc __iomem *d, unsigned int offset, struct vop_device *vpdev, int dnode) { struct _vop_vdev *vdev, *reg_dev = NULL; int ret; u8 type = ioread8(&d->type); vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); if (!vdev) return -ENOMEM; vdev->vpdev = vpdev; vdev->vdev.dev.parent = &vpdev->dev; vdev->vdev.dev.release = vop_virtio_release_dev; vdev->vdev.id.device = type; vdev->vdev.config = &vop_vq_config_ops; vdev->desc = d; vdev->dc = (void __iomem *)d + _vop_aligned_desc_size(d); vdev->dnode = dnode; vdev->vdev.priv = (void *)(unsigned long)dnode; init_completion(&vdev->reset_done); vdev->h2c_vdev_db = vpdev->hw_ops->next_db(vpdev); vdev->virtio_cookie = vpdev->hw_ops->request_irq(vpdev, vop_virtio_intr_handler, "virtio intr", vdev, vdev->h2c_vdev_db); if (IS_ERR(vdev->virtio_cookie)) { ret = PTR_ERR(vdev->virtio_cookie); goto kfree; } iowrite8((u8)vdev->h2c_vdev_db, &vdev->dc->h2c_vdev_db); vdev->c2h_vdev_db = ioread8(&vdev->dc->c2h_vdev_db); ret = register_virtio_device(&vdev->vdev); reg_dev = vdev; if (ret) { dev_err(_vop_dev(vdev), "Failed to register vop device %u type %u\n", offset, type); goto free_irq; } writeq((unsigned long)vdev, &vdev->dc->vdev); dev_dbg(_vop_dev(vdev), "%s: registered vop device %u type %u vdev %p\n", __func__, offset, type, vdev); return 0; free_irq: vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); kfree: if (reg_dev) put_device(&vdev->vdev.dev); else kfree(vdev); return ret; }
static void _vop_scan_devices(void __iomem *dp, struct vop_device *vpdev, bool remove, int dnode) { s8 type; unsigned int i; struct mic_device_desc __iomem *d; struct mic_device_ctrl __iomem *dc; struct device *dev; int ret; for (i = sizeof(struct mic_bootparam); i < MIC_DP_SIZE; i += _vop_total_desc_size(d)) { d = dp + i; dc = (void __iomem *)d + _vop_aligned_desc_size(d); /* * This read barrier is paired with the corresponding write * barrier on the host which is inserted before adding or * removing a virtio device descriptor, by updating the type. */ rmb(); type = ioread8(&d->type); /* end of list */ if (type == 0) break; if (type == -1) continue; /* device already exists */ dev = device_find_child(&vpdev->dev, (void __force *)d, vop_match_desc); if (dev) { if (remove) iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE, &dc->config_change); put_device(dev); _vop_handle_config_change(d, i, vpdev); ret = _vop_remove_device(d, i, vpdev); if (remove) { iowrite8(0, &dc->config_change); iowrite8(0, &dc->guest_ack); } continue; } /* new device */ dev_dbg(&vpdev->dev, "%s %d Adding new virtio device %p\n", __func__, __LINE__, d); if (!remove) _vop_add_device(d, i, vpdev, dnode); } }
static void _vop_handle_config_change(struct mic_device_desc __iomem *d, unsigned int offset, struct vop_device *vpdev) { struct mic_device_ctrl __iomem *dc = (void __iomem *)d + _vop_aligned_desc_size(d); struct _vop_vdev *vdev = (struct _vop_vdev *)readq(&dc->vdev); if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED) return; dev_dbg(&vpdev->dev, "%s %d\n", __func__, __LINE__); virtio_config_changed(&vdev->vdev); iowrite8(1, &dc->guest_ack); }
static inline unsigned _vop_total_desc_size(struct mic_device_desc __iomem *desc) { return _vop_aligned_desc_size(desc) + sizeof(struct mic_device_ctrl); }