Commit 5c02c392 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:
 "Main excitement is a virtio_scsi fix for alloc holding spinlock on the
  abort path, which I refuse to CC stable since (1) I discovered it
  myself, and (2) it's been there forever with no reports"

* tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux:
  virtio_scsi: don't call virtqueue_add_sgs(... GFP_NOIO) holding spinlock.
  virtio-rng: fixes for device registration/unregistration
  virtio-rng: fix boot with virtio-rng device
  virtio-rng: support multiple virtio-rng devices
  virtio_ccw: introduce device_lost in virtio_ccw_device
  virtio: virtio_break_device() to mark all virtqueues broken.
parents 3c81bdd9 c77fba9a
...@@ -25,88 +25,115 @@ ...@@ -25,88 +25,115 @@
#include <linux/virtio_rng.h> #include <linux/virtio_rng.h>
#include <linux/module.h> #include <linux/module.h>
static struct virtqueue *vq; static DEFINE_IDA(rng_index_ida);
static unsigned int data_avail;
static DECLARE_COMPLETION(have_data); struct virtrng_info {
static bool busy; struct virtio_device *vdev;
struct hwrng hwrng;
struct virtqueue *vq;
unsigned int data_avail;
struct completion have_data;
bool busy;
char name[25];
int index;
};
static void random_recv_done(struct virtqueue *vq) static void random_recv_done(struct virtqueue *vq)
{ {
struct virtrng_info *vi = vq->vdev->priv;
/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */ /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
if (!virtqueue_get_buf(vq, &data_avail)) if (!virtqueue_get_buf(vi->vq, &vi->data_avail))
return; return;
complete(&have_data); complete(&vi->have_data);
} }
/* The host will fill any buffer we give it with sweet, sweet randomness. */ /* The host will fill any buffer we give it with sweet, sweet randomness. */
static void register_buffer(u8 *buf, size_t size) static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size)
{ {
struct scatterlist sg; struct scatterlist sg;
sg_init_one(&sg, buf, size); sg_init_one(&sg, buf, size);
/* There should always be room for one buffer. */ /* There should always be room for one buffer. */
virtqueue_add_inbuf(vq, &sg, 1, buf, GFP_KERNEL); virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL);
virtqueue_kick(vq); virtqueue_kick(vi->vq);
} }
static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait) static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
{ {
int ret; int ret;
struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
if (!busy) { if (!vi->busy) {
busy = true; vi->busy = true;
init_completion(&have_data); init_completion(&vi->have_data);
register_buffer(buf, size); register_buffer(vi, buf, size);
} }
if (!wait) if (!wait)
return 0; return 0;
ret = wait_for_completion_killable(&have_data); ret = wait_for_completion_killable(&vi->have_data);
if (ret < 0) if (ret < 0)
return ret; return ret;
busy = false; vi->busy = false;
return data_avail; return vi->data_avail;
} }
static void virtio_cleanup(struct hwrng *rng) static void virtio_cleanup(struct hwrng *rng)
{ {
if (busy) struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
wait_for_completion(&have_data);
}
static struct hwrng virtio_hwrng = { if (vi->busy)
.name = "virtio", wait_for_completion(&vi->have_data);
.cleanup = virtio_cleanup, }
.read = virtio_read,
};
static int probe_common(struct virtio_device *vdev) static int probe_common(struct virtio_device *vdev)
{ {
int err; int err, index;
struct virtrng_info *vi = NULL;
vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL);
if (!vi)
return -ENOMEM;
if (vq) { vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
/* We only support one device for now */ if (index < 0) {
return -EBUSY; kfree(vi);
return index;
} }
sprintf(vi->name, "virtio_rng.%d", index);
init_completion(&vi->have_data);
vi->hwrng = (struct hwrng) {
.read = virtio_read,
.cleanup = virtio_cleanup,
.priv = (unsigned long)vi,
.name = vi->name,
};
vdev->priv = vi;
/* We expect a single virtqueue. */ /* We expect a single virtqueue. */
vq = virtio_find_single_vq(vdev, random_recv_done, "input"); vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");
if (IS_ERR(vq)) { if (IS_ERR(vi->vq)) {
err = PTR_ERR(vq); err = PTR_ERR(vi->vq);
vq = NULL; vi->vq = NULL;
kfree(vi);
ida_simple_remove(&rng_index_ida, index);
return err; return err;
} }
err = hwrng_register(&virtio_hwrng); err = hwrng_register(&vi->hwrng);
if (err) { if (err) {
vdev->config->del_vqs(vdev); vdev->config->del_vqs(vdev);
vq = NULL; vi->vq = NULL;
kfree(vi);
ida_simple_remove(&rng_index_ida, index);
return err; return err;
} }
...@@ -115,11 +142,13 @@ static int probe_common(struct virtio_device *vdev) ...@@ -115,11 +142,13 @@ static int probe_common(struct virtio_device *vdev)
static void remove_common(struct virtio_device *vdev) static void remove_common(struct virtio_device *vdev)
{ {
struct virtrng_info *vi = vdev->priv;
vdev->config->reset(vdev); vdev->config->reset(vdev);
busy = false; vi->busy = false;
hwrng_unregister(&virtio_hwrng); hwrng_unregister(&vi->hwrng);
vdev->config->del_vqs(vdev); vdev->config->del_vqs(vdev);
vq = NULL; ida_simple_remove(&rng_index_ida, vi->index);
kfree(vi);
} }
static int virtrng_probe(struct virtio_device *vdev) static int virtrng_probe(struct virtio_device *vdev)
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kvm_para.h> #include <linux/kvm_para.h>
#include <linux/notifier.h>
#include <asm/setup.h> #include <asm/setup.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/cio.h> #include <asm/cio.h>
...@@ -62,6 +63,7 @@ struct virtio_ccw_device { ...@@ -62,6 +63,7 @@ struct virtio_ccw_device {
struct vq_config_block *config_block; struct vq_config_block *config_block;
bool is_thinint; bool is_thinint;
bool going_away; bool going_away;
bool device_lost;
void *airq_info; void *airq_info;
}; };
...@@ -1010,11 +1012,14 @@ static void virtio_ccw_remove(struct ccw_device *cdev) ...@@ -1010,11 +1012,14 @@ static void virtio_ccw_remove(struct ccw_device *cdev)
unsigned long flags; unsigned long flags;
struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
if (vcdev && cdev->online) if (vcdev && cdev->online) {
if (vcdev->device_lost)
virtio_break_device(&vcdev->vdev);
unregister_virtio_device(&vcdev->vdev); unregister_virtio_device(&vcdev->vdev);
spin_lock_irqsave(get_ccwdev_lock(cdev), flags); spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
dev_set_drvdata(&cdev->dev, NULL); dev_set_drvdata(&cdev->dev, NULL);
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
}
cdev->handler = NULL; cdev->handler = NULL;
} }
...@@ -1023,12 +1028,14 @@ static int virtio_ccw_offline(struct ccw_device *cdev) ...@@ -1023,12 +1028,14 @@ static int virtio_ccw_offline(struct ccw_device *cdev)
unsigned long flags; unsigned long flags;
struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
if (vcdev) { if (!vcdev)
return 0;
if (vcdev->device_lost)
virtio_break_device(&vcdev->vdev);
unregister_virtio_device(&vcdev->vdev); unregister_virtio_device(&vcdev->vdev);
spin_lock_irqsave(get_ccwdev_lock(cdev), flags); spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
dev_set_drvdata(&cdev->dev, NULL); dev_set_drvdata(&cdev->dev, NULL);
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
}
return 0; return 0;
} }
...@@ -1096,8 +1103,26 @@ static int virtio_ccw_online(struct ccw_device *cdev) ...@@ -1096,8 +1103,26 @@ static int virtio_ccw_online(struct ccw_device *cdev)
static int virtio_ccw_cio_notify(struct ccw_device *cdev, int event) static int virtio_ccw_cio_notify(struct ccw_device *cdev, int event)
{ {
/* TODO: Check whether we need special handling here. */ int rc;
return 0; struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev);
/*
* Make sure vcdev is set
* i.e. set_offline/remove callback not already running
*/
if (!vcdev)
return NOTIFY_DONE;
switch (event) {
case CIO_GONE:
vcdev->device_lost = true;
rc = NOTIFY_DONE;
break;
default:
rc = NOTIFY_DONE;
break;
}
return rc;
} }
static struct ccw_device_id virtio_ids[] = { static struct ccw_device_id virtio_ids[] = {
......
...@@ -393,11 +393,10 @@ static void virtscsi_event_done(struct virtqueue *vq) ...@@ -393,11 +393,10 @@ static void virtscsi_event_done(struct virtqueue *vq)
* @cmd : command structure * @cmd : command structure
* @req_size : size of the request buffer * @req_size : size of the request buffer
* @resp_size : size of the response buffer * @resp_size : size of the response buffer
* @gfp : flags to use for memory allocations
*/ */
static int virtscsi_add_cmd(struct virtqueue *vq, static int virtscsi_add_cmd(struct virtqueue *vq,
struct virtio_scsi_cmd *cmd, struct virtio_scsi_cmd *cmd,
size_t req_size, size_t resp_size, gfp_t gfp) size_t req_size, size_t resp_size)
{ {
struct scsi_cmnd *sc = cmd->sc; struct scsi_cmnd *sc = cmd->sc;
struct scatterlist *sgs[4], req, resp; struct scatterlist *sgs[4], req, resp;
...@@ -429,19 +428,19 @@ static int virtscsi_add_cmd(struct virtqueue *vq, ...@@ -429,19 +428,19 @@ static int virtscsi_add_cmd(struct virtqueue *vq,
if (in) if (in)
sgs[out_num + in_num++] = in->sgl; sgs[out_num + in_num++] = in->sgl;
return virtqueue_add_sgs(vq, sgs, out_num, in_num, cmd, gfp); return virtqueue_add_sgs(vq, sgs, out_num, in_num, cmd, GFP_ATOMIC);
} }
static int virtscsi_kick_cmd(struct virtio_scsi_vq *vq, static int virtscsi_kick_cmd(struct virtio_scsi_vq *vq,
struct virtio_scsi_cmd *cmd, struct virtio_scsi_cmd *cmd,
size_t req_size, size_t resp_size, gfp_t gfp) size_t req_size, size_t resp_size)
{ {
unsigned long flags; unsigned long flags;
int err; int err;
bool needs_kick = false; bool needs_kick = false;
spin_lock_irqsave(&vq->vq_lock, flags); spin_lock_irqsave(&vq->vq_lock, flags);
err = virtscsi_add_cmd(vq->vq, cmd, req_size, resp_size, gfp); err = virtscsi_add_cmd(vq->vq, cmd, req_size, resp_size);
if (!err) if (!err)
needs_kick = virtqueue_kick_prepare(vq->vq); needs_kick = virtqueue_kick_prepare(vq->vq);
...@@ -484,8 +483,7 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi, ...@@ -484,8 +483,7 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len); memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
if (virtscsi_kick_cmd(req_vq, cmd, if (virtscsi_kick_cmd(req_vq, cmd,
sizeof cmd->req.cmd, sizeof cmd->resp.cmd, sizeof cmd->req.cmd, sizeof cmd->resp.cmd) != 0)
GFP_ATOMIC) != 0)
return SCSI_MLQUEUE_HOST_BUSY; return SCSI_MLQUEUE_HOST_BUSY;
return 0; return 0;
} }
...@@ -542,8 +540,7 @@ static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd) ...@@ -542,8 +540,7 @@ static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
cmd->comp = &comp; cmd->comp = &comp;
if (virtscsi_kick_cmd(&vscsi->ctrl_vq, cmd, if (virtscsi_kick_cmd(&vscsi->ctrl_vq, cmd,
sizeof cmd->req.tmf, sizeof cmd->resp.tmf, sizeof cmd->req.tmf, sizeof cmd->resp.tmf) < 0)
GFP_NOIO) < 0)
goto out; goto out;
wait_for_completion(&comp); wait_for_completion(&comp);
......
...@@ -865,4 +865,19 @@ bool virtqueue_is_broken(struct virtqueue *_vq) ...@@ -865,4 +865,19 @@ bool virtqueue_is_broken(struct virtqueue *_vq)
} }
EXPORT_SYMBOL_GPL(virtqueue_is_broken); EXPORT_SYMBOL_GPL(virtqueue_is_broken);
/*
* This should prevent the device from being used, allowing drivers to
* recover. You may need to grab appropriate locks to flush.
*/
void virtio_break_device(struct virtio_device *dev)
{
struct virtqueue *_vq;
list_for_each_entry(_vq, &dev->vqs, list) {
struct vring_virtqueue *vq = to_vvq(_vq);
vq->broken = true;
}
}
EXPORT_SYMBOL_GPL(virtio_break_device);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -106,6 +106,8 @@ static inline struct virtio_device *dev_to_virtio(struct device *_dev) ...@@ -106,6 +106,8 @@ static inline struct virtio_device *dev_to_virtio(struct device *_dev)
int register_virtio_device(struct virtio_device *dev); int register_virtio_device(struct virtio_device *dev);
void unregister_virtio_device(struct virtio_device *dev); void unregister_virtio_device(struct virtio_device *dev);
void virtio_break_device(struct virtio_device *dev);
/** /**
* 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).
......
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