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 @@
#include <linux/virtio_rng.h>
#include <linux/module.h>
static struct virtqueue *vq;
static unsigned int data_avail;
static DECLARE_COMPLETION(have_data);
static bool busy;
static DEFINE_IDA(rng_index_ida);
struct virtrng_info {
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)
{
struct virtrng_info *vi = vq->vdev->priv;
/* 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;
complete(&have_data);
complete(&vi->have_data);
}
/* 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;
sg_init_one(&sg, buf, size);
/* 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)
{
int ret;
struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
if (!busy) {
busy = true;
init_completion(&have_data);
register_buffer(buf, size);
if (!vi->busy) {
vi->busy = true;
init_completion(&vi->have_data);
register_buffer(vi, buf, size);
}
if (!wait)
return 0;
ret = wait_for_completion_killable(&have_data);
ret = wait_for_completion_killable(&vi->have_data);
if (ret < 0)
return ret;
busy = false;
vi->busy = false;
return data_avail;
return vi->data_avail;
}
static void virtio_cleanup(struct hwrng *rng)
{
if (busy)
wait_for_completion(&have_data);
}
struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
static struct hwrng virtio_hwrng = {
.name = "virtio",
.cleanup = virtio_cleanup,
.read = virtio_read,
};
if (vi->busy)
wait_for_completion(&vi->have_data);
}
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) {
/* We only support one device for now */
return -EBUSY;
vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
if (index < 0) {
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. */
vq = virtio_find_single_vq(vdev, random_recv_done, "input");
if (IS_ERR(vq)) {
err = PTR_ERR(vq);
vq = NULL;
vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");
if (IS_ERR(vi->vq)) {
err = PTR_ERR(vi->vq);
vi->vq = NULL;
kfree(vi);
ida_simple_remove(&rng_index_ida, index);
return err;
}
err = hwrng_register(&virtio_hwrng);
err = hwrng_register(&vi->hwrng);
if (err) {
vdev->config->del_vqs(vdev);
vq = NULL;
vi->vq = NULL;
kfree(vi);
ida_simple_remove(&rng_index_ida, index);
return err;
}
......@@ -115,11 +142,13 @@ static int probe_common(struct virtio_device *vdev)
static void remove_common(struct virtio_device *vdev)
{
struct virtrng_info *vi = vdev->priv;
vdev->config->reset(vdev);
busy = false;
hwrng_unregister(&virtio_hwrng);
vi->busy = false;
hwrng_unregister(&vi->hwrng);
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)
......
......@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/io.h>
#include <linux/kvm_para.h>
#include <linux/notifier.h>
#include <asm/setup.h>
#include <asm/irq.h>
#include <asm/cio.h>
......@@ -62,6 +63,7 @@ struct virtio_ccw_device {
struct vq_config_block *config_block;
bool is_thinint;
bool going_away;
bool device_lost;
void *airq_info;
};
......@@ -1010,11 +1012,14 @@ static void virtio_ccw_remove(struct ccw_device *cdev)
unsigned long flags;
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);
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
dev_set_drvdata(&cdev->dev, NULL);
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
}
cdev->handler = NULL;
}
......@@ -1023,12 +1028,14 @@ static int virtio_ccw_offline(struct ccw_device *cdev)
unsigned long flags;
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);
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
dev_set_drvdata(&cdev->dev, NULL);
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
}
return 0;
}
......@@ -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)
{
/* TODO: Check whether we need special handling here. */
return 0;
int rc;
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[] = {
......
......@@ -393,11 +393,10 @@ static void virtscsi_event_done(struct virtqueue *vq)
* @cmd : command structure
* @req_size : size of the request buffer
* @resp_size : size of the response buffer
* @gfp : flags to use for memory allocations
*/
static int virtscsi_add_cmd(struct virtqueue *vq,
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 scatterlist *sgs[4], req, resp;
......@@ -429,19 +428,19 @@ static int virtscsi_add_cmd(struct virtqueue *vq,
if (in)
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,
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;
int err;
bool needs_kick = false;
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)
needs_kick = virtqueue_kick_prepare(vq->vq);
......@@ -484,8 +483,7 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
if (virtscsi_kick_cmd(req_vq, cmd,
sizeof cmd->req.cmd, sizeof cmd->resp.cmd,
GFP_ATOMIC) != 0)
sizeof cmd->req.cmd, sizeof cmd->resp.cmd) != 0)
return SCSI_MLQUEUE_HOST_BUSY;
return 0;
}
......@@ -542,8 +540,7 @@ static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
cmd->comp = &comp;
if (virtscsi_kick_cmd(&vscsi->ctrl_vq, cmd,
sizeof cmd->req.tmf, sizeof cmd->resp.tmf,
GFP_NOIO) < 0)
sizeof cmd->req.tmf, sizeof cmd->resp.tmf) < 0)
goto out;
wait_for_completion(&comp);
......
......@@ -865,4 +865,19 @@ bool virtqueue_is_broken(struct virtqueue *_vq)
}
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");
......@@ -106,6 +106,8 @@ static inline struct virtio_device *dev_to_virtio(struct device *_dev)
int register_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
* @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