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. Cheers, Rusty. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTmRNwAAoJENkgDmzRrbjxfkQP/25Xjr1T6d9wR3ZRbJ2LRDX1 hwwwuFeYJMe5KZBqsA2gNeRqbrbW8S9t4ClyjXj2AZsC1XPi5zQbzXfm77HqRpKO KCQ7YoIyLsrtHfKtdKrOK5qiwuns3AsKn988Yy6HkZ94/D6tp8urINdEZg5xtw6z zbgTmv5kSEoY/+D6SmSIN9CT0gJNmIRG5bkDDijhxIHUi9oTFvkG4Rvhtgsdfivm 3vOOnyzD+oXEj7Jzpz4j2D1m8C134uRE67psmAp5zADxDKr66df62YKGBrZJFs45 1Tjr0KancMDXDr8ZWNsmShFnzfailK87KycQbxLoNBvY0wAZZ2H7iS+2Xmid9ee+ feBF6FxBZgmkLnWxlybNy5hJmXKWmM3Hz4p4QZ59N4cEFL6vRGdXiZLCzNFxHyaj p5VggFyhB/fjYfYtmlT8GS4K8M5wfySgfMxDPLYrASzSnx7xFxS3LZPBSPEEgM2q +ivoRBCM5cXdRJUSsS/MdbixAGl0seHR3+KzOGE1ZbU1YQoKA1c9Ci9dTs1REEhS KSL9I2rb0AcnHwhOC3wUOEi1Y7fi0rf4KywWuT6kkA5OrDZIhb0ZrH6CPnBBWabK 7bEq782tF6tIJP9rpMAeNwztRt2GcFhdc54ZLesw9xFoJdf2TPTC0XF+jG1iji5L Nboz+428hzrGarIilHBH =YCNa -----END PGP SIGNATURE----- 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.
This commit is contained in:
commit
5c02c392cd
@ -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;
|
||||
|
||||
if (vi->busy)
|
||||
wait_for_completion(&vi->have_data);
|
||||
}
|
||||
|
||||
|
||||
static struct hwrng virtio_hwrng = {
|
||||
.name = "virtio",
|
||||
.cleanup = virtio_cleanup,
|
||||
.read = virtio_read,
|
||||
};
|
||||
|
||||
static int probe_common(struct virtio_device *vdev)
|
||||
{
|
||||
int err;
|
||||
int err, index;
|
||||
struct virtrng_info *vi = NULL;
|
||||
|
||||
if (vq) {
|
||||
/* We only support one device for now */
|
||||
return -EBUSY;
|
||||
vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL);
|
||||
if (!vi)
|
||||
return -ENOMEM;
|
||||
|
||||
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);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
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 @@ out_free:
|
||||
|
||||
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 = ∁
|
||||
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).
|
||||
|
Loading…
Reference in New Issue
Block a user