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/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);
|
|
||||||
|
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)
|
static int probe_common(struct virtio_device *vdev)
|
||||||
{
|
{
|
||||||
int err;
|
int err, index;
|
||||||
|
struct virtrng_info *vi = NULL;
|
||||||
|
|
||||||
if (vq) {
|
vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL);
|
||||||
/* We only support one device for now */
|
if (!vi)
|
||||||
return -EBUSY;
|
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. */
|
/* 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)
|
|||||||
|
|
||||||
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 @@
|
|||||||
#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 {
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
||||||
unregister_virtio_device(&vcdev->vdev);
|
return 0;
|
||||||
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
|
if (vcdev->device_lost)
|
||||||
dev_set_drvdata(&cdev->dev, NULL);
|
virtio_break_device(&vcdev->vdev);
|
||||||
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1096,8 +1103,26 @@ out_free:
|
|||||||
|
|
||||||
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)
|
|||||||
* @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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
|
|
||||||
cmd->comp = ∁
|
cmd->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)
|
|||||||
}
|
}
|
||||||
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)
|
|||||||
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).
|
||||||
|
Loading…
Reference in New Issue
Block a user