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

Commit 80c28616 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://github.com/rustyrussell/linux:
  virtio-blk: use ida to allocate disk index
  virtio: Add platform bus driver for memory mapped virtio device
  virtio: Dont add "config" to list for !per_vq_vector
  virtio: console: wait for first console port for early console output
  virtio: console: add port stats for bytes received, sent and discarded
  virtio: console: make discard_port_data() use get_inbuf()
  virtio: console: rename variable
  virtio: console: make get_inbuf() return port->inbuf if present
  virtio: console: Fix return type for get_inbuf()
  virtio: console: Use wait_event_freezable instead of _interruptible
  virtio: console: Ignore port name update request if name already set
  virtio: console: Fix indentation
  virtio: modify vring_init and vring_size to take account of the layout containing *_event_idx
  virtio.h: correct comment for struct virtio_driver
  virtio-net: Use virtio_config_val() for retrieving config
  virtio_config: Add virtio_config_val_len()
  virtio-console: Use virtio_config_val() for retrieving config
parents d2118588 5087a50e
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
* virtio memory mapped device

See http://ozlabs.org/~rusty/virtio-spec/ for more details.

Required properties:

- compatible:	"virtio,mmio" compatibility string
- reg:		control registers base address and size including configuration space
- interrupts:	interrupt generated by the device

Example:

	virtio_block@3000 {
		compatible = "virtio,mmio";
		reg = <0x3000 0x100>;
		interrupts = <41>;
	}
+24 −6
Original line number Diff line number Diff line
@@ -8,10 +8,13 @@
#include <linux/scatterlist.h>
#include <linux/string_helpers.h>
#include <scsi/scsi_cmnd.h>
#include <linux/idr.h>

#define PART_BITS 4

static int major, index;
static int major;
static DEFINE_IDA(vd_index_ida);

struct workqueue_struct *virtblk_wq;

struct virtio_blk
@@ -35,6 +38,9 @@ struct virtio_blk
	/* What host tells us, plus 2 for header & tailer. */
	unsigned int sg_elems;

	/* Ida index - used to track minor number allocations. */
	int index;

	/* Scatterlist: can be too big for stack. */
	struct scatterlist sg[/*sg_elems*/];
};
@@ -276,6 +282,11 @@ static int index_to_minor(int index)
	return index << PART_BITS;
}

static int minor_to_index(int minor)
{
	return minor >> PART_BITS;
}

static ssize_t virtblk_serial_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
@@ -341,14 +352,17 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
{
	struct virtio_blk *vblk;
	struct request_queue *q;
	int err;
	int err, index;
	u64 cap;
	u32 v, blk_size, sg_elems, opt_io_size;
	u16 min_io_size;
	u8 physical_block_exp, alignment_offset;

	if (index_to_minor(index) >= 1 << MINORBITS)
		return -ENOSPC;
	err = ida_simple_get(&vd_index_ida, 0, minor_to_index(1 << MINORBITS),
			     GFP_KERNEL);
	if (err < 0)
		goto out;
	index = err;

	/* We need to know how many segments before we allocate. */
	err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX,
@@ -365,7 +379,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
				    sizeof(vblk->sg[0]) * sg_elems, GFP_KERNEL);
	if (!vblk) {
		err = -ENOMEM;
		goto out;
		goto out_free_index;
	}

	INIT_LIST_HEAD(&vblk->reqs);
@@ -421,7 +435,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
	vblk->disk->private_data = vblk;
	vblk->disk->fops = &virtblk_fops;
	vblk->disk->driverfs_dev = &vdev->dev;
	index++;
	vblk->index = index;

	/* configure queue flush support */
	if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH))
@@ -516,6 +530,8 @@ out_free_vq:
	vdev->config->del_vqs(vdev);
out_free_vblk:
	kfree(vblk);
out_free_index:
	ida_simple_remove(&vd_index_ida, index);
out:
	return err;
}
@@ -523,6 +539,7 @@ out:
static void __devexit virtblk_remove(struct virtio_device *vdev)
{
	struct virtio_blk *vblk = vdev->priv;
	int index = vblk->index;

	flush_work(&vblk->config_work);

@@ -538,6 +555,7 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
	mempool_destroy(vblk->pool);
	vdev->config->del_vqs(vdev);
	kfree(vblk);
	ida_simple_remove(&vd_index_ida, index);
}

static const struct virtio_device_id id_table[] = {
+79 −41
Original line number Diff line number Diff line
@@ -19,8 +19,10 @@
 */
#include <linux/cdev.h>
#include <linux/debugfs.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/freezer.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/list.h>
@@ -73,6 +75,7 @@ struct ports_driver_data {
static struct ports_driver_data pdrvdata;

DEFINE_SPINLOCK(pdrvdata_lock);
DECLARE_COMPLETION(early_console_added);

/* This struct holds information that's relevant only for console ports */
struct console {
@@ -151,6 +154,10 @@ struct ports_device {
	int chr_major;
};

struct port_stats {
	unsigned long bytes_sent, bytes_received, bytes_discarded;
};

/* This struct holds the per-port data */
struct port {
	/* Next port in the list, head is in the ports_device */
@@ -178,6 +185,13 @@ struct port {
	/* File in the debugfs directory that exposes this port's information */
	struct dentry *debugfs_file;

	/*
	 * Keep count of the bytes sent, received and discarded for
	 * this port for accounting and debugging purposes.  These
	 * counts are not reset across port open / close events.
	 */
	struct port_stats stats;

	/*
	 * The entries in this struct will be valid if this port is
	 * hooked up to an hvc console
@@ -347,17 +361,19 @@ fail:
}

/* Callers should take appropriate locks */
static void *get_inbuf(struct port *port)
static struct port_buffer *get_inbuf(struct port *port)
{
	struct port_buffer *buf;
	struct virtqueue *vq;
	unsigned int len;

	vq = port->in_vq;
	buf = virtqueue_get_buf(vq, &len);
	if (port->inbuf)
		return port->inbuf;

	buf = virtqueue_get_buf(port->in_vq, &len);
	if (buf) {
		buf->len = len;
		buf->offset = 0;
		port->stats.bytes_received += len;
	}
	return buf;
}
@@ -384,32 +400,27 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
static void discard_port_data(struct port *port)
{
	struct port_buffer *buf;
	struct virtqueue *vq;
	unsigned int len;
	int ret;
	unsigned int err;

	if (!port->portdev) {
		/* Device has been unplugged.  vqs are already gone. */
		return;
	}
	vq = port->in_vq;
	if (port->inbuf)
		buf = port->inbuf;
	else
		buf = virtqueue_get_buf(vq, &len);
	buf = get_inbuf(port);

	ret = 0;
	err = 0;
	while (buf) {
		if (add_inbuf(vq, buf) < 0) {
			ret++;
		port->stats.bytes_discarded += buf->len - buf->offset;
		if (add_inbuf(port->in_vq, buf) < 0) {
			err++;
			free_buf(buf);
		}
		buf = virtqueue_get_buf(vq, &len);
	}
		port->inbuf = NULL;
	if (ret)
		buf = get_inbuf(port);
	}
	if (err)
		dev_warn(port->dev, "Errors adding %d buffers back to vq\n",
			 ret);
			 err);
}

static bool port_has_data(struct port *port)
@@ -417,18 +428,12 @@ static bool port_has_data(struct port *port)
	unsigned long flags;
	bool ret;

	ret = false;
	spin_lock_irqsave(&port->inbuf_lock, flags);
	if (port->inbuf) {
		ret = true;
		goto out;
	}
	port->inbuf = get_inbuf(port);
	if (port->inbuf) {
	if (port->inbuf)
		ret = true;
		goto out;
	}
	ret = false;
out:

	spin_unlock_irqrestore(&port->inbuf_lock, flags);
	return ret;
}
@@ -529,6 +534,8 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count,
		cpu_relax();
done:
	spin_unlock_irqrestore(&port->outvq_lock, flags);

	port->stats.bytes_sent += in_count;
	/*
	 * We're expected to return the amount of data we wrote -- all
	 * of it
@@ -633,7 +640,7 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
		if (filp->f_flags & O_NONBLOCK)
			return -EAGAIN;

		ret = wait_event_interruptible(port->waitqueue,
		ret = wait_event_freezable(port->waitqueue,
					   !will_read_block(port));
		if (ret < 0)
			return ret;
@@ -677,7 +684,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
		if (nonblock)
			return -EAGAIN;

		ret = wait_event_interruptible(port->waitqueue,
		ret = wait_event_freezable(port->waitqueue,
					   !will_write_block(port));
		if (ret < 0)
			return ret;
@@ -1058,6 +1065,14 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf,
			       "host_connected: %d\n", port->host_connected);
	out_offset += snprintf(buf + out_offset, out_count - out_offset,
			       "outvq_full: %d\n", port->outvq_full);
	out_offset += snprintf(buf + out_offset, out_count - out_offset,
			       "bytes_sent: %lu\n", port->stats.bytes_sent);
	out_offset += snprintf(buf + out_offset, out_count - out_offset,
			       "bytes_received: %lu\n",
			       port->stats.bytes_received);
	out_offset += snprintf(buf + out_offset, out_count - out_offset,
			       "bytes_discarded: %lu\n",
			       port->stats.bytes_discarded);
	out_offset += snprintf(buf + out_offset, out_count - out_offset,
			       "is_console: %s\n",
			       is_console_port(port) ? "yes" : "no");
@@ -1143,6 +1158,7 @@ static int add_port(struct ports_device *portdev, u32 id)
	port->cons.ws.ws_row = port->cons.ws.ws_col = 0;

	port->host_connected = port->guest_connected = false;
	port->stats = (struct port_stats) { 0 };

	port->outvq_full = false;

@@ -1352,6 +1368,7 @@ static void handle_control_message(struct ports_device *portdev,
			break;

		init_port_console(port);
		complete(&early_console_added);
		/*
		 * Could remove the port here in case init fails - but
		 * have to notify the host first.
@@ -1393,6 +1410,13 @@ static void handle_control_message(struct ports_device *portdev,
		send_sigio_to_port(port);
		break;
	case VIRTIO_CONSOLE_PORT_NAME:
		/*
		 * If we woke up after hibernation, we can get this
		 * again.  Skip it in that case.
		 */
		if (port->name)
			break;

		/*
		 * Skip the size of the header and the cpkt to get the size
		 * of the name that was sent
@@ -1481,7 +1505,6 @@ static void in_intr(struct virtqueue *vq)
		return;

	spin_lock_irqsave(&port->inbuf_lock, flags);
	if (!port->inbuf)
	port->inbuf = get_inbuf(port);

	/*
@@ -1648,6 +1671,10 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
	struct ports_device *portdev;
	int err;
	bool multiport;
	bool early = early_put_chars != NULL;

	/* Ensure to read early_put_chars now */
	barrier();

	portdev = kmalloc(sizeof(*portdev), GFP_KERNEL);
	if (!portdev) {
@@ -1675,13 +1702,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)

	multiport = false;
	portdev->config.max_nr_ports = 1;
	if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) {
		multiport = true;
		vdev->config->get(vdev, offsetof(struct virtio_console_config,
	if (virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
			      offsetof(struct virtio_console_config,
				       max_nr_ports),
				  &portdev->config.max_nr_ports,
				  sizeof(portdev->config.max_nr_ports));
	}
			      &portdev->config.max_nr_ports) == 0)
		multiport = true;

	err = init_vqs(portdev);
	if (err < 0) {
@@ -1719,6 +1744,19 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)

	__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
			   VIRTIO_CONSOLE_DEVICE_READY, 1);

	/*
	 * If there was an early virtio console, assume that there are no
	 * other consoles. We need to wait until the hvc_alloc matches the
	 * hvc_instantiate, otherwise tty_open will complain, resulting in
	 * a "Warning: unable to open an initial console" boot failure.
	 * Without multiport this is done in add_port above. With multiport
	 * this might take some host<->guest communication - thus we have to
	 * wait.
	 */
	if (multiport && early)
		wait_for_completion(&early_console_added);

	return 0;

free_vqs:
+5 −9
Original line number Diff line number Diff line
@@ -925,12 +925,10 @@ static void virtnet_update_status(struct virtnet_info *vi)
{
	u16 v;

	if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS))
		return;

	vi->vdev->config->get(vi->vdev,
	if (virtio_config_val(vi->vdev, VIRTIO_NET_F_STATUS,
			      offsetof(struct virtio_net_config, status),
			      &v, sizeof(v));
			      &v) < 0)
		return;

	/* Ignore unknown (future) status bits */
	v &= VIRTIO_NET_S_LINK_UP;
@@ -1006,11 +1004,9 @@ static int virtnet_probe(struct virtio_device *vdev)
	}

	/* Configuration may specify what MAC to use.  Otherwise random. */
	if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) {
		vdev->config->get(vdev,
	if (virtio_config_val_len(vdev, VIRTIO_NET_F_MAC,
				  offsetof(struct virtio_net_config, mac),
				  dev->dev_addr, dev->addr_len);
	} else
				  dev->dev_addr, dev->addr_len) < 0)
		random_ether_addr(dev->dev_addr);

	/* Set up our device-specific information */
+11 −0
Original line number Diff line number Diff line
@@ -35,4 +35,15 @@ config VIRTIO_BALLOON

	 If unsure, say M.

 config VIRTIO_MMIO
 	tristate "Platform bus driver for memory mapped virtio devices (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
 	select VIRTIO
 	select VIRTIO_RING
 	---help---
 	 This drivers provides support for memory mapped virtio
	 platform device driver.

 	 If unsure, say N.

endmenu
Loading