Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c02c392 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
Loading
Loading
Loading
Loading
+67 −38
Original line number Diff line number Diff line
@@ -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)
+37 −12
Original line number Diff line number Diff line
@@ -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 @@ 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[] = {
+6 −9
Original line number Diff line number Diff line
@@ -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);
+15 −0
Original line number Diff line number Diff line
@@ -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");
+2 −0
Original line number Diff line number Diff line
@@ -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).