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

Commit bcf8a3df authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'to-linus' of git://github.com/rustyrussell/linux

* tag 'to-linus' of git://github.com/rustyrussell/linux: (24 commits)
  lguest: Make sure interrupt is allocated ok by lguest_setup_irq
  lguest: move the lguest tool to the tools directory
  lguest: switch segment-voodoo-numbers to readable symbols
  virtio: balloon: Add freeze, restore handlers to support S4
  virtio: balloon: Move vq initialization into separate function
  virtio: net: Add freeze, restore handlers to support S4
  virtio: net: Move vq and vq buf removal into separate function
  virtio: net: Move vq initialization into separate function
  virtio: blk: Add freeze, restore handlers to support S4
  virtio: blk: Move vq initialization to separate function
  virtio: console: Disable callbacks for virtqueues at start of S4 freeze
  virtio: console: Add freeze and restore handlers to support S4
  virtio: console: Move vq and vq buf removal into separate functions
  virtio: pci: add PM notification handlers for restore, freeze, thaw, poweroff
  virtio: pci: switch to new PM API
  virtio_blk: fix config handler race
  virtio: add debugging if driver doesn't kick.
  virtio: expose added descriptors immediately.
  virtio: avoid modulus operation.
  virtio: support unlocked queue kick
  ...
parents 61bd5e56 b6c96c02
Loading
Loading
Loading
Loading
+13 −8
Original line number Diff line number Diff line
@@ -856,18 +856,23 @@ static void __init lguest_init_IRQ(void)
}

/*
 * With CONFIG_SPARSE_IRQ, interrupt descriptors are allocated as-needed, so
 * rather than set them in lguest_init_IRQ we are called here every time an
 * lguest device needs an interrupt.
 *
 * FIXME: irq_alloc_desc_at() can fail due to lack of memory, we should
 * pass that up!
 * Interrupt descriptors are allocated as-needed, but low-numbered ones are
 * reserved by the generic x86 code.  So we ignore irq_alloc_desc_at if it
 * tells us the irq is already used: other errors (ie. ENOMEM) we take
 * seriously.
 */
void lguest_setup_irq(unsigned int irq)
int lguest_setup_irq(unsigned int irq)
{
	irq_alloc_desc_at(irq, 0);
	int err;

	/* Returns -ve error or vector number. */
	err = irq_alloc_desc_at(irq, 0);
	if (err < 0 && err != -EEXIST)
		return err;

	irq_set_chip_and_handler_name(irq, &lguest_irq_controller,
				      handle_level_irq, "level");
	return 0;
}

/*
+80 −7
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/virtio.h>
#include <linux/virtio_blk.h>
#include <linux/scatterlist.h>
@@ -36,6 +37,12 @@ struct virtio_blk
	/* Process context for config space updates */
	struct work_struct config_work;

	/* Lock for config space updates */
	struct mutex config_lock;

	/* enable config space updates */
	bool config_enable;

	/* What host tells us, plus 2 for header & tailer. */
	unsigned int sg_elems;

@@ -172,7 +179,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
		}
	}

	if (virtqueue_add_buf(vblk->vq, vblk->sg, out, in, vbr) < 0) {
	if (virtqueue_add_buf(vblk->vq, vblk->sg, out, in, vbr, GFP_ATOMIC)<0) {
		mempool_free(vbr, vblk->pool);
		return false;
	}
@@ -318,6 +325,10 @@ static void virtblk_config_changed_work(struct work_struct *work)
	char cap_str_2[10], cap_str_10[10];
	u64 capacity, size;

	mutex_lock(&vblk->config_lock);
	if (!vblk->config_enable)
		goto done;

	/* Host must always specify the capacity. */
	vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity),
			  &capacity, sizeof(capacity));
@@ -340,6 +351,8 @@ static void virtblk_config_changed_work(struct work_struct *work)
		  cap_str_10, cap_str_2);

	set_capacity(vblk->disk, capacity);
done:
	mutex_unlock(&vblk->config_lock);
}

static void virtblk_config_changed(struct virtio_device *vdev)
@@ -349,6 +362,18 @@ static void virtblk_config_changed(struct virtio_device *vdev)
	queue_work(virtblk_wq, &vblk->config_work);
}

static int init_vq(struct virtio_blk *vblk)
{
	int err = 0;

	/* We expect one virtqueue, for output. */
	vblk->vq = virtio_find_single_vq(vblk->vdev, blk_done, "requests");
	if (IS_ERR(vblk->vq))
		err = PTR_ERR(vblk->vq);

	return err;
}

static int __devinit virtblk_probe(struct virtio_device *vdev)
{
	struct virtio_blk *vblk;
@@ -388,14 +413,13 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
	vblk->vdev = vdev;
	vblk->sg_elems = sg_elems;
	sg_init_table(vblk->sg, vblk->sg_elems);
	mutex_init(&vblk->config_lock);
	INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
	vblk->config_enable = true;

	/* We expect one virtqueue, for output. */
	vblk->vq = virtio_find_single_vq(vdev, blk_done, "requests");
	if (IS_ERR(vblk->vq)) {
		err = PTR_ERR(vblk->vq);
	err = init_vq(vblk);
	if (err)
		goto out_free_vblk;
	}

	vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req));
	if (!vblk->pool) {
@@ -542,7 +566,10 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
	struct virtio_blk *vblk = vdev->priv;
	int index = vblk->index;

	flush_work(&vblk->config_work);
	/* Prevent config work handler from accessing the device. */
	mutex_lock(&vblk->config_lock);
	vblk->config_enable = false;
	mutex_unlock(&vblk->config_lock);

	/* Nothing should be pending. */
	BUG_ON(!list_empty(&vblk->reqs));
@@ -550,6 +577,8 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
	/* Stop all the virtqueues. */
	vdev->config->reset(vdev);

	flush_work(&vblk->config_work);

	del_gendisk(vblk->disk);
	blk_cleanup_queue(vblk->disk->queue);
	put_disk(vblk->disk);
@@ -559,6 +588,46 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
	ida_simple_remove(&vd_index_ida, index);
}

#ifdef CONFIG_PM
static int virtblk_freeze(struct virtio_device *vdev)
{
	struct virtio_blk *vblk = vdev->priv;

	/* Ensure we don't receive any more interrupts */
	vdev->config->reset(vdev);

	/* Prevent config work handler from accessing the device. */
	mutex_lock(&vblk->config_lock);
	vblk->config_enable = false;
	mutex_unlock(&vblk->config_lock);

	flush_work(&vblk->config_work);

	spin_lock_irq(vblk->disk->queue->queue_lock);
	blk_stop_queue(vblk->disk->queue);
	spin_unlock_irq(vblk->disk->queue->queue_lock);
	blk_sync_queue(vblk->disk->queue);

	vdev->config->del_vqs(vdev);
	return 0;
}

static int virtblk_restore(struct virtio_device *vdev)
{
	struct virtio_blk *vblk = vdev->priv;
	int ret;

	vblk->config_enable = true;
	ret = init_vq(vdev->priv);
	if (!ret) {
		spin_lock_irq(vblk->disk->queue->queue_lock);
		blk_start_queue(vblk->disk->queue);
		spin_unlock_irq(vblk->disk->queue->queue_lock);
	}
	return ret;
}
#endif

static const struct virtio_device_id id_table[] = {
	{ VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
	{ 0 },
@@ -584,6 +653,10 @@ static struct virtio_driver __refdata virtio_blk = {
	.probe			= virtblk_probe,
	.remove			= __devexit_p(virtblk_remove),
	.config_changed		= virtblk_config_changed,
#ifdef CONFIG_PM
	.freeze			= virtblk_freeze,
	.restore		= virtblk_restore,
#endif
};

static int __init init(void)
+1 −1
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@ static void register_buffer(u8 *buf, size_t size)
	sg_init_one(&sg, buf, size);

	/* There should always be room for one buffer. */
	if (virtqueue_add_buf(vq, &sg, 0, 1, buf) < 0)
	if (virtqueue_add_buf(vq, &sg, 0, 1, buf, GFP_KERNEL) < 0)
		BUG();

	virtqueue_kick(vq);
+109 −31
Original line number Diff line number Diff line
@@ -392,7 +392,7 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)

	sg_init_one(sg, buf->buf, buf->size);

	ret = virtqueue_add_buf(vq, sg, 0, 1, buf);
	ret = virtqueue_add_buf(vq, sg, 0, 1, buf, GFP_ATOMIC);
	virtqueue_kick(vq);
	return ret;
}
@@ -457,7 +457,7 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id,
	vq = portdev->c_ovq;

	sg_init_one(sg, &cpkt, sizeof(cpkt));
	if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt) >= 0) {
	if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt, GFP_ATOMIC) >= 0) {
		virtqueue_kick(vq);
		while (!virtqueue_get_buf(vq, &len))
			cpu_relax();
@@ -506,7 +506,7 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count,
	reclaim_consumed_buffers(port);

	sg_init_one(sg, in_buf, in_count);
	ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf);
	ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf, GFP_ATOMIC);

	/* Tell Host to go! */
	virtqueue_kick(out_vq);
@@ -1271,6 +1271,20 @@ static void remove_port(struct kref *kref)
	kfree(port);
}

static void remove_port_data(struct port *port)
{
	struct port_buffer *buf;

	/* Remove unused data this port might have received. */
	discard_port_data(port);

	reclaim_consumed_buffers(port);

	/* Remove buffers we queued up for the Host to send us data in. */
	while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
		free_buf(buf);
}

/*
 * Port got unplugged.  Remove port from portdev's list and drop the
 * kref reference.  If no userspace has this port opened, it will
@@ -1278,8 +1292,6 @@ static void remove_port(struct kref *kref)
 */
static void unplug_port(struct port *port)
{
	struct port_buffer *buf;

	spin_lock_irq(&port->portdev->ports_lock);
	list_del(&port->list);
	spin_unlock_irq(&port->portdev->ports_lock);
@@ -1300,14 +1312,7 @@ static void unplug_port(struct port *port)
		hvc_remove(port->cons.hvc);
	}

	/* Remove unused data this port might have received. */
	discard_port_data(port);

	reclaim_consumed_buffers(port);

	/* Remove buffers we queued up for the Host to send us data in. */
	while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
		free_buf(buf);
	remove_port_data(port);

	/*
	 * We should just assume the device itself has gone off --
@@ -1659,6 +1664,28 @@ static const struct file_operations portdev_fops = {
	.owner = THIS_MODULE,
};

static void remove_vqs(struct ports_device *portdev)
{
	portdev->vdev->config->del_vqs(portdev->vdev);
	kfree(portdev->in_vqs);
	kfree(portdev->out_vqs);
}

static void remove_controlq_data(struct ports_device *portdev)
{
	struct port_buffer *buf;
	unsigned int len;

	if (!use_multiport(portdev))
		return;

	while ((buf = virtqueue_get_buf(portdev->c_ivq, &len)))
		free_buf(buf);

	while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq)))
		free_buf(buf);
}

/*
 * Once we're further in boot, we get probed like any other virtio
 * device.
@@ -1764,9 +1791,7 @@ free_vqs:
	/* The host might want to notify mgmt sw about device add failure */
	__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
			   VIRTIO_CONSOLE_DEVICE_READY, 0);
	vdev->config->del_vqs(vdev);
	kfree(portdev->in_vqs);
	kfree(portdev->out_vqs);
	remove_vqs(portdev);
free_chrdev:
	unregister_chrdev(portdev->chr_major, "virtio-portsdev");
free:
@@ -1804,21 +1829,8 @@ static void virtcons_remove(struct virtio_device *vdev)
	 * have to just stop using the port, as the vqs are going
	 * away.
	 */
	if (use_multiport(portdev)) {
		struct port_buffer *buf;
		unsigned int len;

		while ((buf = virtqueue_get_buf(portdev->c_ivq, &len)))
			free_buf(buf);

		while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq)))
			free_buf(buf);
	}

	vdev->config->del_vqs(vdev);
	kfree(portdev->in_vqs);
	kfree(portdev->out_vqs);

	remove_controlq_data(portdev);
	remove_vqs(portdev);
	kfree(portdev);
}

@@ -1832,6 +1844,68 @@ static unsigned int features[] = {
	VIRTIO_CONSOLE_F_MULTIPORT,
};

#ifdef CONFIG_PM
static int virtcons_freeze(struct virtio_device *vdev)
{
	struct ports_device *portdev;
	struct port *port;

	portdev = vdev->priv;

	vdev->config->reset(vdev);

	virtqueue_disable_cb(portdev->c_ivq);
	cancel_work_sync(&portdev->control_work);
	/*
	 * Once more: if control_work_handler() was running, it would
	 * enable the cb as the last step.
	 */
	virtqueue_disable_cb(portdev->c_ivq);
	remove_controlq_data(portdev);

	list_for_each_entry(port, &portdev->ports, list) {
		virtqueue_disable_cb(port->in_vq);
		virtqueue_disable_cb(port->out_vq);
		/*
		 * We'll ask the host later if the new invocation has
		 * the port opened or closed.
		 */
		port->host_connected = false;
		remove_port_data(port);
	}
	remove_vqs(portdev);

	return 0;
}

static int virtcons_restore(struct virtio_device *vdev)
{
	struct ports_device *portdev;
	struct port *port;
	int ret;

	portdev = vdev->priv;

	ret = init_vqs(portdev);
	if (ret)
		return ret;

	if (use_multiport(portdev))
		fill_queue(portdev->c_ivq, &portdev->cvq_lock);

	list_for_each_entry(port, &portdev->ports, list) {
		port->in_vq = portdev->in_vqs[port->id];
		port->out_vq = portdev->out_vqs[port->id];

		fill_queue(port->in_vq, &port->inbuf_lock);

		/* Get port open/close status on the host */
		send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
	}
	return 0;
}
#endif

static struct virtio_driver virtio_console = {
	.feature_table = features,
	.feature_table_size = ARRAY_SIZE(features),
@@ -1841,6 +1915,10 @@ static struct virtio_driver virtio_console = {
	.probe =	virtcons_probe,
	.remove =	virtcons_remove,
	.config_changed = config_intr,
#ifdef CONFIG_PM
	.freeze =	virtcons_freeze,
	.restore =	virtcons_restore,
#endif
};

static int __init init(void)
+1 −1
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ Mastery: PREFIX=M
Beer:
	@for f in Preparation Guest Drivers Launcher Host Switcher Mastery; do echo "{==- $$f -==}"; make -s $$f; done; echo "{==-==}"
Preparation Preparation! Guest Drivers Launcher Host Switcher Mastery:
	@sh ../../Documentation/virtual/lguest/extract $(PREFIX) `find ../../* -name '*.[chS]' -wholename '*lguest*'`
	@sh ../../tools/lguest/extract $(PREFIX) `find ../../* -name '*.[chS]' -wholename '*lguest*'`
Puppy:
	@clear
	@printf "      __  \n (___()'\`;\n /,    /\`\n \\\\\\\"--\\\\\\   \n"
Loading