Commit 0e6e58f9 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux

Pull virtio updates from Rusty Russell:
 "One cc: stable commit, the rest are a series of minor cleanups which
  have been sitting in MST's tree during my vacation.  I changed a
  function name and made one trivial change, then they spent two days in
  linux-next"

* tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux: (25 commits)
  virtio-rng: refactor probe error handling
  virtio_scsi: drop scan callback
  virtio_balloon: enable VQs early on restore
  virtio_scsi: fix race on device removal
  virito_scsi: use freezable WQ for events
  virtio_net: enable VQs early on restore
  virtio_console: enable VQs early on restore
  virtio_scsi: enable VQs early on restore
  virtio_blk: enable VQs early on restore
  virtio_scsi: move kick event out from virtscsi_init
  virtio_net: fix use after free on allocation failure
  9p/trans_virtio: enable VQs early
  virtio_console: enable VQs early
  virtio_blk: enable VQs early
  virtio_net: enable VQs early
  virtio: add API to enable VQs early
  virtio_net: minor cleanup
  virtio-net: drop config_mutex
  virtio_net: drop config_enable
  virtio-blk: drop config_mutex
  ...
parents 50edb5cc 1bbc2606
...@@ -41,12 +41,6 @@ struct virtio_blk ...@@ -41,12 +41,6 @@ struct virtio_blk
/* Process context for config space updates */ /* Process context for config space updates */
struct work_struct config_work; struct work_struct config_work;
/* Lock for config space updates */
struct mutex config_lock;
/* enable config space updates */
bool config_enable;
/* What host tells us, plus 2 for header & tailer. */ /* What host tells us, plus 2 for header & tailer. */
unsigned int sg_elems; unsigned int sg_elems;
...@@ -347,10 +341,6 @@ static void virtblk_config_changed_work(struct work_struct *work) ...@@ -347,10 +341,6 @@ static void virtblk_config_changed_work(struct work_struct *work)
char *envp[] = { "RESIZE=1", NULL }; char *envp[] = { "RESIZE=1", NULL };
u64 capacity, size; u64 capacity, size;
mutex_lock(&vblk->config_lock);
if (!vblk->config_enable)
goto done;
/* Host must always specify the capacity. */ /* Host must always specify the capacity. */
virtio_cread(vdev, struct virtio_blk_config, capacity, &capacity); virtio_cread(vdev, struct virtio_blk_config, capacity, &capacity);
...@@ -374,8 +364,6 @@ static void virtblk_config_changed_work(struct work_struct *work) ...@@ -374,8 +364,6 @@ static void virtblk_config_changed_work(struct work_struct *work)
set_capacity(vblk->disk, capacity); set_capacity(vblk->disk, capacity);
revalidate_disk(vblk->disk); revalidate_disk(vblk->disk);
kobject_uevent_env(&disk_to_dev(vblk->disk)->kobj, KOBJ_CHANGE, envp); kobject_uevent_env(&disk_to_dev(vblk->disk)->kobj, KOBJ_CHANGE, envp);
done:
mutex_unlock(&vblk->config_lock);
} }
static void virtblk_config_changed(struct virtio_device *vdev) static void virtblk_config_changed(struct virtio_device *vdev)
...@@ -606,10 +594,8 @@ static int virtblk_probe(struct virtio_device *vdev) ...@@ -606,10 +594,8 @@ static int virtblk_probe(struct virtio_device *vdev)
vblk->vdev = vdev; vblk->vdev = vdev;
vblk->sg_elems = sg_elems; vblk->sg_elems = sg_elems;
mutex_init(&vblk->config_lock);
INIT_WORK(&vblk->config_work, virtblk_config_changed_work); INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
vblk->config_enable = true;
err = init_vq(vblk); err = init_vq(vblk);
if (err) if (err)
...@@ -733,6 +719,8 @@ static int virtblk_probe(struct virtio_device *vdev) ...@@ -733,6 +719,8 @@ static int virtblk_probe(struct virtio_device *vdev)
if (!err && opt_io_size) if (!err && opt_io_size)
blk_queue_io_opt(q, blk_size * opt_io_size); blk_queue_io_opt(q, blk_size * opt_io_size);
virtio_device_ready(vdev);
add_disk(vblk->disk); add_disk(vblk->disk);
err = device_create_file(disk_to_dev(vblk->disk), &dev_attr_serial); err = device_create_file(disk_to_dev(vblk->disk), &dev_attr_serial);
if (err) if (err)
...@@ -771,10 +759,8 @@ static void virtblk_remove(struct virtio_device *vdev) ...@@ -771,10 +759,8 @@ static void virtblk_remove(struct virtio_device *vdev)
int index = vblk->index; int index = vblk->index;
int refc; int refc;
/* Prevent config work handler from accessing the device. */ /* Make sure no work handler is accessing the device. */
mutex_lock(&vblk->config_lock); flush_work(&vblk->config_work);
vblk->config_enable = false;
mutex_unlock(&vblk->config_lock);
del_gendisk(vblk->disk); del_gendisk(vblk->disk);
blk_cleanup_queue(vblk->disk->queue); blk_cleanup_queue(vblk->disk->queue);
...@@ -784,8 +770,6 @@ static void virtblk_remove(struct virtio_device *vdev) ...@@ -784,8 +770,6 @@ static void virtblk_remove(struct virtio_device *vdev)
/* Stop all the virtqueues. */ /* Stop all the virtqueues. */
vdev->config->reset(vdev); vdev->config->reset(vdev);
flush_work(&vblk->config_work);
refc = atomic_read(&disk_to_dev(vblk->disk)->kobj.kref.refcount); refc = atomic_read(&disk_to_dev(vblk->disk)->kobj.kref.refcount);
put_disk(vblk->disk); put_disk(vblk->disk);
vdev->config->del_vqs(vdev); vdev->config->del_vqs(vdev);
...@@ -805,11 +789,7 @@ static int virtblk_freeze(struct virtio_device *vdev) ...@@ -805,11 +789,7 @@ static int virtblk_freeze(struct virtio_device *vdev)
/* Ensure we don't receive any more interrupts */ /* Ensure we don't receive any more interrupts */
vdev->config->reset(vdev); vdev->config->reset(vdev);
/* Prevent config work handler from accessing the device. */ /* Make sure no work handler is accessing the device. */
mutex_lock(&vblk->config_lock);
vblk->config_enable = false;
mutex_unlock(&vblk->config_lock);
flush_work(&vblk->config_work); flush_work(&vblk->config_work);
blk_mq_stop_hw_queues(vblk->disk->queue); blk_mq_stop_hw_queues(vblk->disk->queue);
...@@ -823,12 +803,14 @@ static int virtblk_restore(struct virtio_device *vdev) ...@@ -823,12 +803,14 @@ static int virtblk_restore(struct virtio_device *vdev)
struct virtio_blk *vblk = vdev->priv; struct virtio_blk *vblk = vdev->priv;
int ret; int ret;
vblk->config_enable = true;
ret = init_vq(vdev->priv); ret = init_vq(vdev->priv);
if (!ret) if (ret)
blk_mq_start_stopped_hw_queues(vblk->disk->queue, true);
return ret; return ret;
virtio_device_ready(vdev);
blk_mq_start_stopped_hw_queues(vblk->disk->queue, true);
return 0;
} }
#endif #endif
......
...@@ -109,8 +109,8 @@ static int probe_common(struct virtio_device *vdev) ...@@ -109,8 +109,8 @@ static int probe_common(struct virtio_device *vdev)
vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL); vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
if (index < 0) { if (index < 0) {
kfree(vi); err = index;
return index; goto err_ida;
} }
sprintf(vi->name, "virtio_rng.%d", index); sprintf(vi->name, "virtio_rng.%d", index);
init_completion(&vi->have_data); init_completion(&vi->have_data);
...@@ -128,13 +128,16 @@ static int probe_common(struct virtio_device *vdev) ...@@ -128,13 +128,16 @@ static int probe_common(struct virtio_device *vdev)
vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input"); vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");
if (IS_ERR(vi->vq)) { if (IS_ERR(vi->vq)) {
err = PTR_ERR(vi->vq); err = PTR_ERR(vi->vq);
vi->vq = NULL; goto err_find;
kfree(vi);
ida_simple_remove(&rng_index_ida, index);
return err;
} }
return 0; return 0;
err_find:
ida_simple_remove(&rng_index_ida, index);
err_ida:
kfree(vi);
return err;
} }
static void remove_common(struct virtio_device *vdev) static void remove_common(struct virtio_device *vdev)
......
...@@ -1449,6 +1449,8 @@ static int add_port(struct ports_device *portdev, u32 id) ...@@ -1449,6 +1449,8 @@ static int add_port(struct ports_device *portdev, u32 id)
spin_lock_init(&port->outvq_lock); spin_lock_init(&port->outvq_lock);
init_waitqueue_head(&port->waitqueue); init_waitqueue_head(&port->waitqueue);
virtio_device_ready(portdev->vdev);
/* Fill the in_vq with buffers so the host can send us data. */ /* Fill the in_vq with buffers so the host can send us data. */
nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock);
if (!nr_added_bufs) { if (!nr_added_bufs) {
...@@ -2182,6 +2184,8 @@ static int virtcons_restore(struct virtio_device *vdev) ...@@ -2182,6 +2184,8 @@ static int virtcons_restore(struct virtio_device *vdev)
if (ret) if (ret)
return ret; return ret;
virtio_device_ready(portdev->vdev);
if (use_multiport(portdev)) if (use_multiport(portdev))
fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
......
...@@ -462,16 +462,12 @@ static void mic_handle_config_change(struct mic_device_desc __iomem *d, ...@@ -462,16 +462,12 @@ static void mic_handle_config_change(struct mic_device_desc __iomem *d,
struct mic_device_ctrl __iomem *dc struct mic_device_ctrl __iomem *dc
= (void __iomem *)d + mic_aligned_desc_size(d); = (void __iomem *)d + mic_aligned_desc_size(d);
struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev); struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev);
struct virtio_driver *drv;
if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED) if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED)
return; return;
dev_dbg(mdrv->dev, "%s %d\n", __func__, __LINE__); dev_dbg(mdrv->dev, "%s %d\n", __func__, __LINE__);
drv = container_of(mvdev->vdev.dev.driver, virtio_config_changed(&mvdev->vdev);
struct virtio_driver, driver);
if (drv->config_changed)
drv->config_changed(&mvdev->vdev);
iowrite8(1, &dc->guest_ack); iowrite8(1, &dc->guest_ack);
} }
......
...@@ -123,9 +123,6 @@ struct virtnet_info { ...@@ -123,9 +123,6 @@ struct virtnet_info {
/* Host can handle any s/g split between our header and packet data */ /* Host can handle any s/g split between our header and packet data */
bool any_header_sg; bool any_header_sg;
/* enable config space updates */
bool config_enable;
/* Active statistics */ /* Active statistics */
struct virtnet_stats __percpu *stats; struct virtnet_stats __percpu *stats;
...@@ -135,9 +132,6 @@ struct virtnet_info { ...@@ -135,9 +132,6 @@ struct virtnet_info {
/* Work struct for config space updates */ /* Work struct for config space updates */
struct work_struct config_work; struct work_struct config_work;
/* Lock for config space updates */
struct mutex config_lock;
/* Does the affinity hint is set for virtqueues? */ /* Does the affinity hint is set for virtqueues? */
bool affinity_hint_set; bool affinity_hint_set;
...@@ -1414,13 +1408,9 @@ static void virtnet_config_changed_work(struct work_struct *work) ...@@ -1414,13 +1408,9 @@ static void virtnet_config_changed_work(struct work_struct *work)
container_of(work, struct virtnet_info, config_work); container_of(work, struct virtnet_info, config_work);
u16 v; u16 v;
mutex_lock(&vi->config_lock);
if (!vi->config_enable)
goto done;
if (virtio_cread_feature(vi->vdev, VIRTIO_NET_F_STATUS, if (virtio_cread_feature(vi->vdev, VIRTIO_NET_F_STATUS,
struct virtio_net_config, status, &v) < 0) struct virtio_net_config, status, &v) < 0)
goto done; return;
if (v & VIRTIO_NET_S_ANNOUNCE) { if (v & VIRTIO_NET_S_ANNOUNCE) {
netdev_notify_peers(vi->dev); netdev_notify_peers(vi->dev);
...@@ -1431,7 +1421,7 @@ static void virtnet_config_changed_work(struct work_struct *work) ...@@ -1431,7 +1421,7 @@ static void virtnet_config_changed_work(struct work_struct *work)
v &= VIRTIO_NET_S_LINK_UP; v &= VIRTIO_NET_S_LINK_UP;
if (vi->status == v) if (vi->status == v)
goto done; return;
vi->status = v; vi->status = v;
...@@ -1442,8 +1432,6 @@ static void virtnet_config_changed_work(struct work_struct *work) ...@@ -1442,8 +1432,6 @@ static void virtnet_config_changed_work(struct work_struct *work)
netif_carrier_off(vi->dev); netif_carrier_off(vi->dev);
netif_tx_stop_all_queues(vi->dev); netif_tx_stop_all_queues(vi->dev);
} }
done:
mutex_unlock(&vi->config_lock);
} }
static void virtnet_config_changed(struct virtio_device *vdev) static void virtnet_config_changed(struct virtio_device *vdev)
...@@ -1764,8 +1752,6 @@ static int virtnet_probe(struct virtio_device *vdev) ...@@ -1764,8 +1752,6 @@ static int virtnet_probe(struct virtio_device *vdev)
u64_stats_init(&virtnet_stats->rx_syncp); u64_stats_init(&virtnet_stats->rx_syncp);
} }
mutex_init(&vi->config_lock);
vi->config_enable = true;
INIT_WORK(&vi->config_work, virtnet_config_changed_work); INIT_WORK(&vi->config_work, virtnet_config_changed_work);
/* If we can receive ANY GSO packets, we must allocate large ones. */ /* If we can receive ANY GSO packets, we must allocate large ones. */
...@@ -1813,6 +1799,8 @@ static int virtnet_probe(struct virtio_device *vdev) ...@@ -1813,6 +1799,8 @@ static int virtnet_probe(struct virtio_device *vdev)
goto free_vqs; goto free_vqs;
} }
virtio_device_ready(vdev);
/* Last of all, set up some receive buffers. */ /* Last of all, set up some receive buffers. */
for (i = 0; i < vi->curr_queue_pairs; i++) { for (i = 0; i < vi->curr_queue_pairs; i++) {
try_fill_recv(&vi->rq[i], GFP_KERNEL); try_fill_recv(&vi->rq[i], GFP_KERNEL);
...@@ -1849,6 +1837,8 @@ static int virtnet_probe(struct virtio_device *vdev) ...@@ -1849,6 +1837,8 @@ static int virtnet_probe(struct virtio_device *vdev)
return 0; return 0;
free_recv_bufs: free_recv_bufs:
vi->vdev->config->reset(vdev);
free_receive_bufs(vi); free_receive_bufs(vi);
unregister_netdev(dev); unregister_netdev(dev);
free_vqs: free_vqs:
...@@ -1882,17 +1872,13 @@ static void virtnet_remove(struct virtio_device *vdev) ...@@ -1882,17 +1872,13 @@ static void virtnet_remove(struct virtio_device *vdev)
unregister_hotcpu_notifier(&vi->nb); unregister_hotcpu_notifier(&vi->nb);
/* Prevent config work handler from accessing the device. */ /* Make sure no work handler is accessing the device. */
mutex_lock(&vi->config_lock); flush_work(&vi->config_work);
vi->config_enable = false;
mutex_unlock(&vi->config_lock);
unregister_netdev(vi->dev); unregister_netdev(vi->dev);
remove_vq_common(vi); remove_vq_common(vi);
flush_work(&vi->config_work);
free_percpu(vi->stats); free_percpu(vi->stats);
free_netdev(vi->dev); free_netdev(vi->dev);
} }
...@@ -1905,10 +1891,8 @@ static int virtnet_freeze(struct virtio_device *vdev) ...@@ -1905,10 +1891,8 @@ static int virtnet_freeze(struct virtio_device *vdev)
unregister_hotcpu_notifier(&vi->nb); unregister_hotcpu_notifier(&vi->nb);
/* Prevent config work handler from accessing the device */ /* Make sure no work handler is accessing the device */
mutex_lock(&vi->config_lock); flush_work(&vi->config_work);
vi->config_enable = false;
mutex_unlock(&vi->config_lock);
netif_device_detach(vi->dev); netif_device_detach(vi->dev);
cancel_delayed_work_sync(&vi->refill); cancel_delayed_work_sync(&vi->refill);
...@@ -1923,8 +1907,6 @@ static int virtnet_freeze(struct virtio_device *vdev) ...@@ -1923,8 +1907,6 @@ static int virtnet_freeze(struct virtio_device *vdev)
remove_vq_common(vi); remove_vq_common(vi);
flush_work(&vi->config_work);
return 0; return 0;
} }
...@@ -1937,6 +1919,8 @@ static int virtnet_restore(struct virtio_device *vdev) ...@@ -1937,6 +1919,8 @@ static int virtnet_restore(struct virtio_device *vdev)
if (err) if (err)
return err; return err;
virtio_device_ready(vdev);
if (netif_running(vi->dev)) { if (netif_running(vi->dev)) {
for (i = 0; i < vi->curr_queue_pairs; i++) for (i = 0; i < vi->curr_queue_pairs; i++)
if (!try_fill_recv(&vi->rq[i], GFP_KERNEL)) if (!try_fill_recv(&vi->rq[i], GFP_KERNEL))
...@@ -1948,10 +1932,6 @@ static int virtnet_restore(struct virtio_device *vdev) ...@@ -1948,10 +1932,6 @@ static int virtnet_restore(struct virtio_device *vdev)
netif_device_attach(vi->dev); netif_device_attach(vi->dev);
mutex_lock(&vi->config_lock);
vi->config_enable = true;
mutex_unlock(&vi->config_lock);
rtnl_lock(); rtnl_lock();
virtnet_set_queues(vi, vi->curr_queue_pairs); virtnet_set_queues(vi, vi->curr_queue_pairs);
rtnl_unlock(); rtnl_unlock();
......
...@@ -406,15 +406,8 @@ static void kvm_extint_handler(struct ext_code ext_code, ...@@ -406,15 +406,8 @@ static void kvm_extint_handler(struct ext_code ext_code,
switch (param) { switch (param) {
case VIRTIO_PARAM_CONFIG_CHANGED: case VIRTIO_PARAM_CONFIG_CHANGED:
{ virtio_config_changed(vq->vdev);
struct virtio_driver *drv;
drv = container_of(vq->vdev->dev.driver,
struct virtio_driver, driver);
if (drv->config_changed)
drv->config_changed(vq->vdev);
break; break;
}
case VIRTIO_PARAM_DEV_ADD: case VIRTIO_PARAM_DEV_ADD:
schedule_work(&hotplug_work); schedule_work(&hotplug_work);
break; break;
......
...@@ -940,11 +940,7 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev, ...@@ -940,11 +940,7 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
vring_interrupt(0, vq); vring_interrupt(0, vq);
} }
if (test_bit(0, &vcdev->indicators2)) { if (test_bit(0, &vcdev->indicators2)) {
drv = container_of(vcdev->vdev.dev.driver, virtio_config_changed(&vcdev->vdev);
struct virtio_driver, driver);
if (drv && drv->config_changed)
drv->config_changed(&vcdev->vdev);
clear_bit(0, &vcdev->indicators2); clear_bit(0, &vcdev->indicators2);
} }
} }
......
...@@ -110,6 +110,9 @@ struct virtio_scsi { ...@@ -110,6 +110,9 @@ struct virtio_scsi {
/* CPU hotplug notifier */ /* CPU hotplug notifier */
struct notifier_block nb; struct notifier_block nb;
/* Protected by event_vq lock */
bool stop_events;
struct virtio_scsi_vq ctrl_vq; struct virtio_scsi_vq ctrl_vq;
struct virtio_scsi_vq event_vq; struct virtio_scsi_vq event_vq;
struct virtio_scsi_vq req_vqs[]; struct virtio_scsi_vq req_vqs[];
...@@ -303,6 +306,11 @@ static void virtscsi_cancel_event_work(struct virtio_scsi *vscsi) ...@@ -303,6 +306,11 @@ static void virtscsi_cancel_event_work(struct virtio_scsi *vscsi)
{ {
int i; int i;
/* Stop scheduling work before calling cancel_work_sync. */
spin_lock_irq(&vscsi->event_vq.vq_lock);
vscsi->stop_events = true;
spin_unlock_irq(&vscsi->event_vq.vq_lock);
for (i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++) for (i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++)
cancel_work_sync(&vscsi->event_list[i].work); cancel_work_sync(&vscsi->event_list[i].work);
} }
...@@ -390,7 +398,8 @@ static void virtscsi_complete_event(struct virtio_scsi *vscsi, void *buf) ...@@ -390,7 +398,8 @@ static void virtscsi_complete_event(struct virtio_scsi *vscsi, void *buf)
{ {
struct virtio_scsi_event_node *event_node = buf; struct virtio_scsi_event_node *event_node = buf;
schedule_work(&event_node->work); if (!vscsi->stop_events)
queue_work(system_freezable_wq, &event_node->work);
} }
static void virtscsi_event_done(struct virtqueue *vq) static void virtscsi_event_done(struct virtqueue *vq)
...@@ -851,13 +860,6 @@ static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq, ...@@ -851,13 +860,6 @@ static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq,
virtscsi_vq->vq = vq; virtscsi_vq->vq = vq;
} }
static void virtscsi_scan(struct virtio_device *vdev)
{
struct Scsi_Host *shost = (struct Scsi_Host *)vdev->priv;
scsi_scan_host(shost);
}
static void virtscsi_remove_vqs(struct virtio_device *vdev) static void virtscsi_remove_vqs(struct virtio_device *vdev)
{ {
struct Scsi_Host *sh = virtio_scsi_host(vdev); struct Scsi_Host *sh = virtio_scsi_host(vdev);
...@@ -916,9 +918,6 @@ static int virtscsi_init(struct virtio_device *vdev, ...@@ -916,9 +918,6 @@ static int virtscsi_init(struct virtio_device *vdev,
virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE); virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE);
virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE); virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE);
if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG))
virtscsi_kick_event_all(vscsi);
err = 0; err = 0;
out: out:
...@@ -997,10 +996,13 @@ static int virtscsi_probe(struct virtio_device *vdev) ...@@ -997,10 +996,13 @@ static int virtscsi_probe(struct virtio_device *vdev)
err = scsi_add_host(shost, &vdev->dev); err = scsi_add_host(shost, &vdev->dev);
if (err) if (err)
goto scsi_add_host_failed; goto scsi_add_host_failed;
/*
* scsi_scan_host() happens in virtscsi_scan() via virtio_driver->scan() virtio_device_ready(vdev);
* after VIRTIO_CONFIG_S_DRIVER_OK has been set..
*/ if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG))
virtscsi_kick_event_all(vscsi);
scsi_scan_host(shost);
return 0; return 0;
scsi_add_host_failed: scsi_add_host_failed:
...@@ -1048,8 +1050,15 @@ static int virtscsi_restore(struct virtio_device *vdev) ...@@ -1048,8 +1050,15 @@ static int virtscsi_restore(struct virtio_device *vdev)
return err; return err;
err = register_hotcpu_notifier(&vscsi->nb); err = register_hotcpu_notifier(&vscsi->nb);
if (err) if (err) {
vdev->config->del_vqs(vdev); vdev->config->del_vqs(vdev);
return err;
}
virtio_device_ready(vdev);
if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG))
virtscsi_kick_event_all(vscsi);
return err; return err;
} }
...@@ -1073,7 +1082,6 @@ static struct virtio_driver virtio_scsi_driver = { ...@@ -1073,7 +1082,6 @@ static struct virtio_driver virtio_scsi_driver = {
.driver.owner = THIS_MODULE, .driver.owner = THIS_MODULE,
.id_table = id_table, .id_table = id_table,
.probe = virtscsi_probe, .probe = virtscsi_probe,
.scan = virtscsi_scan,
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
.freeze = virtscsi_freeze, .freeze = virtscsi_freeze,
.restore = virtscsi_restore, .restore = virtscsi_restore,
......
...@@ -117,6 +117,43 @@ void virtio_check_driver_offered_feature(const struct virtio_device *vdev, ...@@ -117,6 +117,43 @@ void virtio_check_driver_offered_feature(const struct virtio_device *vdev,
} }
EXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature); EXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature);
static void __virtio_config_changed(struct virtio_device *dev)
{
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
if (!dev->config_enabled)
dev->config_change_pending = true;
else if (drv && drv->config_changed)
drv->config_changed(dev);
}
void virtio_config_changed(struct virtio_device *dev)
{
unsigned long flags;
spin_lock_irqsave(&dev->config_lock, flags);
__virtio_config_changed(dev);
spin_unlock_irqrestore(&dev->config_lock, flags);
}
EXPORT_SYMBOL_GPL(virtio_config_changed);
static void virtio_config_disable(struct virtio_device *dev)
{
spin_lock_irq(&dev->config_lock);
dev->config_enabled = false;
spin_unlock_irq(&dev->config_lock);
}
static void virtio_config_enable(struct virtio_device *dev)
{
spin_lock_irq(&dev->config_lock);
dev->config_enabled = true;
if (dev->config_change_pending)
__virtio_config_changed(dev);
dev->config_change_pending = false;
spin_unlock_irq(&dev->config_lock);
}
static int virtio_dev_probe(struct device *_d) static int virtio_dev_probe(struct device *_d)
{ {
int err, i; int err, i;
...@@ -153,6 +190,8 @@ static int virtio_dev_probe(struct device *_d) ...@@ -153,6 +190,8 @@ static int virtio_dev_probe(struct device *_d)
add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
if (drv->scan) if (drv->scan)
drv->scan(dev); drv->scan(dev);
virtio_config_enable(dev);
} }
return err; return err;
...@@ -163,6 +202,8 @@ static int virtio_dev_remove(struct device *_d) ...@@ -163,6 +202,8 @@ static int virtio_dev_remove(struct device *_d)
struct virtio_device *dev = dev_to_virtio(_d); struct virtio_device *dev = dev_to_virtio(_d);
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
virtio_config_disable(dev);
drv->remove(dev); drv->remove(dev);
/* Driver should have reset device. */ /* Driver should have reset device. */
...@@ -211,6 +252,10 @@ int register_virtio_device(struct virtio_device *dev) ...@@ -211,6 +252,10 @@ int register_virtio_device(struct virtio_device *dev)
dev->index = err; dev->index = err;
dev_set_name(&dev->dev, "virtio%u", dev->index); dev_set_name(&dev->dev, "virtio%u", dev->index);
spin_lock_init(&dev->config_lock);
dev->config_enabled = false;
dev->config_change_pending = false;
/* We always start by resetting the device, in case a previous /* We always start by resetting the device, in case a previous
* driver messed it up. This also tests that code path a little. */ * driver messed it up. This also tests that code path a little. */
dev->config->reset(dev); dev->config->reset(dev);
...@@ -239,6 +284,64 @@ void unregister_virtio_device(struct virtio_device *dev) ...@@ -239,6 +284,64 @@ void unregister_virtio_device(struct virtio_device *dev)
} }
EXPORT_SYMBOL_GPL(unregister_virtio_device); EXPORT_SYMBOL_GPL(unregister_virtio_device);
#ifdef CONFIG_PM_SLEEP
int virtio_device_freeze(struct virtio_device *dev)
{
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
virtio_config_disable(dev);
dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED;
if (drv && drv->freeze)
return drv->freeze(dev);
return 0;
}
EXPORT_SYMBOL_GPL(virtio_device_freeze);
int virtio_device_restore(struct virtio_device *dev)
{
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
/* We always start by resetting the device, in case a previous
* driver messed it up. */
dev->config->reset(dev);
/* Acknowledge that we've seen the device. */
add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
/* Maybe driver failed before freeze.
* Restore the failed status, for debugging. */
if (dev->failed)
add_status(dev, VIRTIO_CONFIG_S_FAILED);
if (!drv)
return 0;
/* We have a driver! */
add_status(dev, VIRTIO_CONFIG_S_DRIVER);
dev->config->finalize_features(dev);
if (drv->restore) {
int ret = drv->restore(dev);
if (ret) {
add_status(dev, VIRTIO_CONFIG_S_FAILED);
return ret;
}
}
/* Finally, tell the device we're all set */
add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
virtio_config_enable(dev);
return 0;
}
EXPORT_SYMBOL_GPL(virtio_device_restore);
#endif
static int virtio_init(void) static int virtio_init(void)
{ {
if (bus_register(&virtio_bus) != 0) if (bus_register(&virtio_bus) != 0)
......
...@@ -504,6 +504,8 @@ static int virtballoon_restore(struct virtio_device *vdev) ...@@ -504,6 +504,8 @@ static int virtballoon_restore(struct virtio_device *vdev)
if (ret) if (ret)
return ret; return ret;
virtio_device_ready(vdev);
fill_balloon(vb, towards_target(vb)); fill_balloon(vb, towards_target(vb));
update_balloon_size(vb); update_balloon_size(vb);
return 0; return 0;
......
...@@ -234,8 +234,6 @@ static irqreturn_t vm_interrupt(int irq, void *opaque) ...@@ -234,8 +234,6 @@ static irqreturn_t vm_interrupt(int irq, void *opaque)
{ {
struct virtio_mmio_device *vm_dev = opaque; struct virtio_mmio_device *vm_dev = opaque;
struct virtio_mmio_vq_info *info; struct virtio_mmio_vq_info *info;
struct virtio_driver *vdrv = container_of(vm_dev->vdev.dev.driver,
struct virtio_driver, driver);
unsigned long status; unsigned long status;
unsigned long flags; unsigned long flags;
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
...@@ -244,9 +242,8 @@ static irqreturn_t vm_interrupt(int irq, void *opaque) ...@@ -244,9 +242,8 @@ static irqreturn_t vm_interrupt(int irq, void *opaque)
status = readl(vm_dev->base + VIRTIO_MMIO_INTERRUPT_STATUS); status = readl(vm_dev->base + VIRTIO_MMIO_INTERRUPT_STATUS);
writel(status, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK); writel(status, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK);
if (unlikely(status & VIRTIO_MMIO_INT_CONFIG) if (unlikely(status & VIRTIO_MMIO_INT_CONFIG)) {
&& vdrv && vdrv->config_changed) { virtio_config_changed(&vm_dev->vdev);
vdrv->config_changed(&vm_dev->vdev);
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
} }
......
...@@ -57,9 +57,6 @@ struct virtio_pci_device ...@@ -57,9 +57,6 @@ struct virtio_pci_device
/* Vectors allocated, excluding per-vq vectors if any */ /* Vectors allocated, excluding per-vq vectors if any */
unsigned msix_used_vectors; unsigned msix_used_vectors;
/* Status saved during hibernate/restore */
u8 saved_status;
/* Whether we have vector per vq */ /* Whether we have vector per vq */
bool per_vq_vectors; bool per_vq_vectors;
}; };
...@@ -211,12 +208,8 @@ static bool vp_notify(struct virtqueue *vq) ...@@ -211,12 +208,8 @@ static bool vp_notify(struct virtqueue *vq)
static irqreturn_t vp_config_changed(int irq, void *opaque) static irqreturn_t vp_config_changed(int irq, void *opaque)
{ {
struct virtio_pci_device *vp_dev = opaque; struct virtio_pci_device *vp_dev = opaque;
struct virtio_driver *drv;
drv = container_of(vp_dev->vdev.dev.driver,
struct virtio_driver, driver);
if (drv && drv->config_changed) virtio_config_changed(&vp_dev->vdev);
drv->config_changed(&vp_dev->vdev);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -768,16 +761,9 @@ static int virtio_pci_freeze(struct device *dev) ...@@ -768,16 +761,9 @@ static int virtio_pci_freeze(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev); struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
struct virtio_driver *drv;
int ret; int ret;
drv = container_of(vp_dev->vdev.dev.driver, ret = virtio_device_freeze(&vp_dev->vdev);
struct virtio_driver, driver);
ret = 0;
vp_dev->saved_status = vp_get_status(&vp_dev->vdev);
if (drv && drv->freeze)
ret = drv->freeze(&vp_dev->vdev);
if (!ret) if (!ret)
pci_disable_device(pci_dev); pci_disable_device(pci_dev);
...@@ -788,27 +774,14 @@ static int virtio_pci_restore(struct device *dev) ...@@ -788,27 +774,14 @@ static int virtio_pci_restore(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev); struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
struct virtio_driver *drv;
int ret; int ret;
drv = container_of(vp_dev->vdev.dev.driver,
struct virtio_driver, driver);
ret = pci_enable_device(pci_dev); ret = pci_enable_device(pci_dev);
if (ret) if (ret)
return ret; return ret;
pci_set_master(pci_dev); pci_set_master(pci_dev);
vp_finalize_features(&vp_dev->vdev); return virtio_device_restore(&vp_dev->vdev);
if (drv && drv->restore)
ret = drv->restore(&vp_dev->vdev);
/* Finally, tell the device we're all set */
if (!ret)
vp_set_status(&vp_dev->vdev, vp_dev->saved_status);
return ret;
} }
static const struct dev_pm_ops virtio_pci_pm_ops = { static const struct dev_pm_ops virtio_pci_pm_ops = {
......
...@@ -78,6 +78,10 @@ bool virtqueue_is_broken(struct virtqueue *vq); ...@@ -78,6 +78,10 @@ bool virtqueue_is_broken(struct virtqueue *vq);
/** /**
* virtio_device - representation of a device using virtio * virtio_device - representation of a device using virtio
* @index: unique position on the virtio bus * @index: unique position on the virtio bus
* @failed: saved value for CONFIG_S_FAILED bit (for restore)
* @config_enabled: configuration change reporting enabled
* @config_change_pending: configuration change reported while disabled
* @config_lock: protects configuration change reporting
* @dev: underlying device. * @dev: underlying device.
* @id: the device type identification (used to match it with a driver). * @id: the device type identification (used to match it with a driver).
* @config: the configuration ops for this device. * @config: the configuration ops for this device.
...@@ -88,6 +92,10 @@ bool virtqueue_is_broken(struct virtqueue *vq); ...@@ -88,6 +92,10 @@ bool virtqueue_is_broken(struct virtqueue *vq);
*/ */
struct virtio_device { struct virtio_device {
int index; int index;
bool failed;
bool config_enabled;
bool config_change_pending;
spinlock_t config_lock;
struct device dev; struct device dev;
struct virtio_device_id id; struct virtio_device_id id;
const struct virtio_config_ops *config; const struct virtio_config_ops *config;
...@@ -108,6 +116,12 @@ void unregister_virtio_device(struct virtio_device *dev); ...@@ -108,6 +116,12 @@ void unregister_virtio_device(struct virtio_device *dev);
void virtio_break_device(struct virtio_device *dev); void virtio_break_device(struct virtio_device *dev);
void virtio_config_changed(struct virtio_device *dev);
#ifdef CONFIG_PM_SLEEP
int virtio_device_freeze(struct virtio_device *dev);
int virtio_device_restore(struct virtio_device *dev);
#endif
/** /**
* virtio_driver - operations for a virtio I/O driver * virtio_driver - operations for a virtio I/O driver
* @driver: underlying device driver (populate name and owner). * @driver: underlying device driver (populate name and owner).
......
...@@ -109,6 +109,23 @@ struct virtqueue *virtio_find_single_vq(struct virtio_device *vdev, ...@@ -109,6 +109,23 @@ struct virtqueue *virtio_find_single_vq(struct virtio_device *vdev,
return vq; return vq;
} }
/**
* virtio_device_ready - enable vq use in probe function
* @vdev: the device
*
* Driver must call this to use vqs in the probe function.
*
* Note: vqs are enabled automatically after probe returns.
*/
static inline
void virtio_device_ready(struct virtio_device *dev)
{
unsigned status = dev->config->get_status(dev);
BUG_ON(status & VIRTIO_CONFIG_S_DRIVER_OK);
dev->config->set_status(dev, status | VIRTIO_CONFIG_S_DRIVER_OK);
}
static inline static inline
const char *virtio_bus_name(struct virtio_device *vdev) const char *virtio_bus_name(struct virtio_device *vdev)
{ {
......
...@@ -575,6 +575,8 @@ static int p9_virtio_probe(struct virtio_device *vdev) ...@@ -575,6 +575,8 @@ static int p9_virtio_probe(struct virtio_device *vdev)
/* Ceiling limit to avoid denial of service attacks */ /* Ceiling limit to avoid denial of service attacks */
chan->p9_max_pages = nr_free_buffer_pages()/4; chan->p9_max_pages = nr_free_buffer_pages()/4;
virtio_device_ready(vdev);
mutex_lock(&virtio_9p_lock); mutex_lock(&virtio_9p_lock);
list_add_tail(&chan->chan_list, &virtio_chan_list); list_add_tail(&chan->chan_list, &virtio_chan_list);
mutex_unlock(&virtio_9p_lock); mutex_unlock(&virtio_9p_lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment