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

Commit b7dfde95 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 update from Rusty Russell:
 "Some nice cleanups, and even a patch my wife did as a "live" demo for
  Latinoware 2012.

  There's a slightly non-trivial merge in virtio-net, as we cleaned up
  the virtio add_buf interface while DaveM accepted the mq virtio-net
  patches."

* tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux: (27 commits)
  virtio_console: Add support for remoteproc serial
  virtio_console: Merge struct buffer_token into struct port_buffer
  virtio: add drv_to_virtio to make code clearly
  virtio: use dev_to_virtio wrapper in virtio
  virtio-mmio: Fix irq parsing in command line parameter
  virtio_console: Free buffers from out-queue upon close
  virtio: Convert dev_printk(KERN_<LEVEL> to dev_<level>(
  virtio_console: Use kmalloc instead of kzalloc
  virtio_console: Free buffer if splice fails
  virtio: tools: make it clear that virtqueue_add_buf() no longer returns > 0
  virtio: scsi: make it clear that virtqueue_add_buf() no longer returns > 0
  virtio: rpmsg: make it clear that virtqueue_add_buf() no longer returns > 0
  virtio: net: make it clear that virtqueue_add_buf() no longer returns > 0
  virtio: console: make it clear that virtqueue_add_buf() no longer returns > 0
  virtio: make virtqueue_add_buf() returning 0 on success, not capacity.
  virtio: console: don't rely on virtqueue_add_buf() returning capacity.
  virtio_net: don't rely on virtqueue_add_buf() returning capacity.
  virtio-net: remove unused skb_vnet_hdr->num_sg field
  virtio-net: correct capacity math on ring full
  virtio: move queue_index and num_free fields into core struct virtqueue.
  ...
parents 03c850ec 1b637046
Loading
Loading
Loading
Loading
+230 −99
Original line number Original line Diff line number Diff line
@@ -37,8 +37,12 @@
#include <linux/wait.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/workqueue.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/kconfig.h>
#include "../tty/hvc/hvc_console.h"
#include "../tty/hvc/hvc_console.h"


#define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC)

/*
/*
 * This is a global struct for storing common data for all the devices
 * This is a global struct for storing common data for all the devices
 * this driver handles.
 * this driver handles.
@@ -111,6 +115,21 @@ struct port_buffer {
	size_t len;
	size_t len;
	/* offset in the buf from which to consume data */
	/* offset in the buf from which to consume data */
	size_t offset;
	size_t offset;

	/* DMA address of buffer */
	dma_addr_t dma;

	/* Device we got DMA memory from */
	struct device *dev;

	/* List of pending dma buffers to free */
	struct list_head list;

	/* If sgpages == 0 then buf is used */
	unsigned int sgpages;

	/* sg is used if spages > 0. sg must be the last in is struct */
	struct scatterlist sg[0];
};
};


/*
/*
@@ -325,6 +344,11 @@ static bool is_console_port(struct port *port)
	return false;
	return false;
}
}


static bool is_rproc_serial(const struct virtio_device *vdev)
{
	return is_rproc_enabled && vdev->id.device == VIRTIO_ID_RPROC_SERIAL;
}

static inline bool use_multiport(struct ports_device *portdev)
static inline bool use_multiport(struct ports_device *portdev)
{
{
	/*
	/*
@@ -336,20 +360,110 @@ static inline bool use_multiport(struct ports_device *portdev)
	return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
	return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
}
}


static void free_buf(struct port_buffer *buf)
static DEFINE_SPINLOCK(dma_bufs_lock);
static LIST_HEAD(pending_free_dma_bufs);

static void free_buf(struct port_buffer *buf, bool can_sleep)
{
{
	unsigned int i;

	for (i = 0; i < buf->sgpages; i++) {
		struct page *page = sg_page(&buf->sg[i]);
		if (!page)
			break;
		put_page(page);
	}

	if (!buf->dev) {
		kfree(buf->buf);
		kfree(buf->buf);
	} else if (is_rproc_enabled) {
		unsigned long flags;

		/* dma_free_coherent requires interrupts to be enabled. */
		if (!can_sleep) {
			/* queue up dma-buffers to be freed later */
			spin_lock_irqsave(&dma_bufs_lock, flags);
			list_add_tail(&buf->list, &pending_free_dma_bufs);
			spin_unlock_irqrestore(&dma_bufs_lock, flags);
			return;
		}
		dma_free_coherent(buf->dev, buf->size, buf->buf, buf->dma);

		/* Release device refcnt and allow it to be freed */
		put_device(buf->dev);
	}

	kfree(buf);
	kfree(buf);
}
}


static struct port_buffer *alloc_buf(size_t buf_size)
static void reclaim_dma_bufs(void)
{
	unsigned long flags;
	struct port_buffer *buf, *tmp;
	LIST_HEAD(tmp_list);

	if (list_empty(&pending_free_dma_bufs))
		return;

	/* Create a copy of the pending_free_dma_bufs while holding the lock */
	spin_lock_irqsave(&dma_bufs_lock, flags);
	list_cut_position(&tmp_list, &pending_free_dma_bufs,
			  pending_free_dma_bufs.prev);
	spin_unlock_irqrestore(&dma_bufs_lock, flags);

	/* Release the dma buffers, without irqs enabled */
	list_for_each_entry_safe(buf, tmp, &tmp_list, list) {
		list_del(&buf->list);
		free_buf(buf, true);
	}
}

static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size,
				     int pages)
{
{
	struct port_buffer *buf;
	struct port_buffer *buf;


	buf = kmalloc(sizeof(*buf), GFP_KERNEL);
	reclaim_dma_bufs();

	/*
	 * Allocate buffer and the sg list. The sg list array is allocated
	 * directly after the port_buffer struct.
	 */
	buf = kmalloc(sizeof(*buf) + sizeof(struct scatterlist) * pages,
		      GFP_KERNEL);
	if (!buf)
	if (!buf)
		goto fail;
		goto fail;
	buf->buf = kzalloc(buf_size, GFP_KERNEL);

	buf->sgpages = pages;
	if (pages > 0) {
		buf->dev = NULL;
		buf->buf = NULL;
		return buf;
	}

	if (is_rproc_serial(vq->vdev)) {
		/*
		 * Allocate DMA memory from ancestor. When a virtio
		 * device is created by remoteproc, the DMA memory is
		 * associated with the grandparent device:
		 * vdev => rproc => platform-dev.
		 * The code here would have been less quirky if
		 * DMA_MEMORY_INCLUDES_CHILDREN had been supported
		 * in dma-coherent.c
		 */
		if (!vq->vdev->dev.parent || !vq->vdev->dev.parent->parent)
			goto free_buf;
		buf->dev = vq->vdev->dev.parent->parent;

		/* Increase device refcnt to avoid freeing it */
		get_device(buf->dev);
		buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma,
					      GFP_KERNEL);
	} else {
		buf->dev = NULL;
		buf->buf = kmalloc(buf_size, GFP_KERNEL);
	}

	if (!buf->buf)
	if (!buf->buf)
		goto free_buf;
		goto free_buf;
	buf->len = 0;
	buf->len = 0;
@@ -396,6 +510,8 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)


	ret = virtqueue_add_buf(vq, sg, 0, 1, buf, GFP_ATOMIC);
	ret = virtqueue_add_buf(vq, sg, 0, 1, buf, GFP_ATOMIC);
	virtqueue_kick(vq);
	virtqueue_kick(vq);
	if (!ret)
		ret = vq->num_free;
	return ret;
	return ret;
}
}


@@ -416,7 +532,7 @@ static void discard_port_data(struct port *port)
		port->stats.bytes_discarded += buf->len - buf->offset;
		port->stats.bytes_discarded += buf->len - buf->offset;
		if (add_inbuf(port->in_vq, buf) < 0) {
		if (add_inbuf(port->in_vq, buf) < 0) {
			err++;
			err++;
			free_buf(buf);
			free_buf(buf, false);
		}
		}
		port->inbuf = NULL;
		port->inbuf = NULL;
		buf = get_inbuf(port);
		buf = get_inbuf(port);
@@ -459,7 +575,7 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id,
	vq = portdev->c_ovq;
	vq = portdev->c_ovq;


	sg_init_one(sg, &cpkt, sizeof(cpkt));
	sg_init_one(sg, &cpkt, sizeof(cpkt));
	if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt, GFP_ATOMIC) >= 0) {
	if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt, GFP_ATOMIC) == 0) {
		virtqueue_kick(vq);
		virtqueue_kick(vq);
		while (!virtqueue_get_buf(vq, &len))
		while (!virtqueue_get_buf(vq, &len))
			cpu_relax();
			cpu_relax();
@@ -476,55 +592,29 @@ static ssize_t send_control_msg(struct port *port, unsigned int event,
	return 0;
	return 0;
}
}


struct buffer_token {
	union {
		void *buf;
		struct scatterlist *sg;
	} u;
	/* If sgpages == 0 then buf is used, else sg is used */
	unsigned int sgpages;
};

static void reclaim_sg_pages(struct scatterlist *sg, unsigned int nrpages)
{
	int i;
	struct page *page;

	for (i = 0; i < nrpages; i++) {
		page = sg_page(&sg[i]);
		if (!page)
			break;
		put_page(page);
	}
	kfree(sg);
}


/* Callers must take the port->outvq_lock */
/* Callers must take the port->outvq_lock */
static void reclaim_consumed_buffers(struct port *port)
static void reclaim_consumed_buffers(struct port *port)
{
{
	struct buffer_token *tok;
	struct port_buffer *buf;
	unsigned int len;
	unsigned int len;


	if (!port->portdev) {
	if (!port->portdev) {
		/* Device has been unplugged.  vqs are already gone. */
		/* Device has been unplugged.  vqs are already gone. */
		return;
		return;
	}
	}
	while ((tok = virtqueue_get_buf(port->out_vq, &len))) {
	while ((buf = virtqueue_get_buf(port->out_vq, &len))) {
		if (tok->sgpages)
		free_buf(buf, false);
			reclaim_sg_pages(tok->u.sg, tok->sgpages);
		else
			kfree(tok->u.buf);
		kfree(tok);
		port->outvq_full = false;
		port->outvq_full = false;
	}
	}
}
}


static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
			      int nents, size_t in_count,
			      int nents, size_t in_count,
			      struct buffer_token *tok, bool nonblock)
			      void *data, bool nonblock)
{
{
	struct virtqueue *out_vq;
	struct virtqueue *out_vq;
	ssize_t ret;
	int err;
	unsigned long flags;
	unsigned long flags;
	unsigned int len;
	unsigned int len;


@@ -534,17 +624,17 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,


	reclaim_consumed_buffers(port);
	reclaim_consumed_buffers(port);


	ret = virtqueue_add_buf(out_vq, sg, nents, 0, tok, GFP_ATOMIC);
	err = virtqueue_add_buf(out_vq, sg, nents, 0, data, GFP_ATOMIC);


	/* Tell Host to go! */
	/* Tell Host to go! */
	virtqueue_kick(out_vq);
	virtqueue_kick(out_vq);


	if (ret < 0) {
	if (err) {
		in_count = 0;
		in_count = 0;
		goto done;
		goto done;
	}
	}


	if (ret == 0)
	if (out_vq->num_free == 0)
		port->outvq_full = true;
		port->outvq_full = true;


	if (nonblock)
	if (nonblock)
@@ -572,37 +662,6 @@ static ssize_t __send_to_port(struct port *port, struct scatterlist *sg,
	return in_count;
	return in_count;
}
}


static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count,
			bool nonblock)
{
	struct scatterlist sg[1];
	struct buffer_token *tok;

	tok = kmalloc(sizeof(*tok), GFP_ATOMIC);
	if (!tok)
		return -ENOMEM;
	tok->sgpages = 0;
	tok->u.buf = in_buf;

	sg_init_one(sg, in_buf, in_count);

	return __send_to_port(port, sg, 1, in_count, tok, nonblock);
}

static ssize_t send_pages(struct port *port, struct scatterlist *sg, int nents,
			  size_t in_count, bool nonblock)
{
	struct buffer_token *tok;

	tok = kmalloc(sizeof(*tok), GFP_ATOMIC);
	if (!tok)
		return -ENOMEM;
	tok->sgpages = nents;
	tok->u.sg = sg;

	return __send_to_port(port, sg, nents, in_count, tok, nonblock);
}

/*
/*
 * Give out the data that's requested from the buffer that we have
 * Give out the data that's requested from the buffer that we have
 * queued up.
 * queued up.
@@ -748,9 +807,10 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
			       size_t count, loff_t *offp)
			       size_t count, loff_t *offp)
{
{
	struct port *port;
	struct port *port;
	char *buf;
	struct port_buffer *buf;
	ssize_t ret;
	ssize_t ret;
	bool nonblock;
	bool nonblock;
	struct scatterlist sg[1];


	/* Userspace could be out to fool us */
	/* Userspace could be out to fool us */
	if (!count)
	if (!count)
@@ -766,11 +826,11 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,


	count = min((size_t)(32 * 1024), count);
	count = min((size_t)(32 * 1024), count);


	buf = kmalloc(count, GFP_KERNEL);
	buf = alloc_buf(port->out_vq, count, 0);
	if (!buf)
	if (!buf)
		return -ENOMEM;
		return -ENOMEM;


	ret = copy_from_user(buf, ubuf, count);
	ret = copy_from_user(buf->buf, ubuf, count);
	if (ret) {
	if (ret) {
		ret = -EFAULT;
		ret = -EFAULT;
		goto free_buf;
		goto free_buf;
@@ -784,13 +844,14 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
	 * through to the host.
	 * through to the host.
	 */
	 */
	nonblock = true;
	nonblock = true;
	ret = send_buf(port, buf, count, nonblock);
	sg_init_one(sg, buf->buf, count);
	ret = __send_to_port(port, sg, 1, count, buf, nonblock);


	if (nonblock && ret > 0)
	if (nonblock && ret > 0)
		goto out;
		goto out;


free_buf:
free_buf:
	kfree(buf);
	free_buf(buf, true);
out:
out:
	return ret;
	return ret;
}
}
@@ -856,6 +917,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
	struct port *port = filp->private_data;
	struct port *port = filp->private_data;
	struct sg_list sgl;
	struct sg_list sgl;
	ssize_t ret;
	ssize_t ret;
	struct port_buffer *buf;
	struct splice_desc sd = {
	struct splice_desc sd = {
		.total_len = len,
		.total_len = len,
		.flags = flags,
		.flags = flags,
@@ -863,22 +925,34 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
		.u.data = &sgl,
		.u.data = &sgl,
	};
	};


	/*
	 * Rproc_serial does not yet support splice. To support splice
	 * pipe_to_sg() must allocate dma-buffers and copy content from
	 * regular pages to dma pages. And alloc_buf and free_buf must
	 * support allocating and freeing such a list of dma-buffers.
	 */
	if (is_rproc_serial(port->out_vq->vdev))
		return -EINVAL;

	ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK);
	ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK);
	if (ret < 0)
	if (ret < 0)
		return ret;
		return ret;


	buf = alloc_buf(port->out_vq, 0, pipe->nrbufs);
	if (!buf)
		return -ENOMEM;

	sgl.n = 0;
	sgl.n = 0;
	sgl.len = 0;
	sgl.len = 0;
	sgl.size = pipe->nrbufs;
	sgl.size = pipe->nrbufs;
	sgl.sg = kmalloc(sizeof(struct scatterlist) * sgl.size, GFP_KERNEL);
	sgl.sg = buf->sg;
	if (unlikely(!sgl.sg))
		return -ENOMEM;

	sg_init_table(sgl.sg, sgl.size);
	sg_init_table(sgl.sg, sgl.size);
	ret = __splice_from_pipe(pipe, &sd, pipe_to_sg);
	ret = __splice_from_pipe(pipe, &sd, pipe_to_sg);
	if (likely(ret > 0))
	if (likely(ret > 0))
		ret = send_pages(port, sgl.sg, sgl.n, sgl.len, true);
		ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true);


	if (unlikely(ret <= 0))
		free_buf(buf, true);
	return ret;
	return ret;
}
}


@@ -927,6 +1001,7 @@ static int port_fops_release(struct inode *inode, struct file *filp)
	reclaim_consumed_buffers(port);
	reclaim_consumed_buffers(port);
	spin_unlock_irq(&port->outvq_lock);
	spin_unlock_irq(&port->outvq_lock);


	reclaim_dma_bufs();
	/*
	/*
	 * Locks aren't necessary here as a port can't be opened after
	 * Locks aren't necessary here as a port can't be opened after
	 * unplug, and if a port isn't unplugged, a kref would already
	 * unplug, and if a port isn't unplugged, a kref would already
@@ -1031,6 +1106,7 @@ static const struct file_operations port_fops = {
static int put_chars(u32 vtermno, const char *buf, int count)
static int put_chars(u32 vtermno, const char *buf, int count)
{
{
	struct port *port;
	struct port *port;
	struct scatterlist sg[1];


	if (unlikely(early_put_chars))
	if (unlikely(early_put_chars))
		return early_put_chars(vtermno, buf, count);
		return early_put_chars(vtermno, buf, count);
@@ -1039,7 +1115,8 @@ static int put_chars(u32 vtermno, const char *buf, int count)
	if (!port)
	if (!port)
		return -EPIPE;
		return -EPIPE;


	return send_buf(port, (void *)buf, count, false);
	sg_init_one(sg, buf, count);
	return __send_to_port(port, sg, 1, count, (void *)buf, false);
}
}


/*
/*
@@ -1076,7 +1153,10 @@ static void resize_console(struct port *port)
		return;
		return;


	vdev = port->portdev->vdev;
	vdev = port->portdev->vdev;
	if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE))

	/* Don't test F_SIZE at all if we're rproc: not a valid feature! */
	if (!is_rproc_serial(vdev) &&
	    virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE))
		hvc_resize(port->cons.hvc, port->cons.ws);
		hvc_resize(port->cons.hvc, port->cons.ws);
}
}


@@ -1260,7 +1340,7 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)


	nr_added_bufs = 0;
	nr_added_bufs = 0;
	do {
	do {
		buf = alloc_buf(PAGE_SIZE);
		buf = alloc_buf(vq, PAGE_SIZE, 0);
		if (!buf)
		if (!buf)
			break;
			break;


@@ -1268,7 +1348,7 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)
		ret = add_inbuf(vq, buf);
		ret = add_inbuf(vq, buf);
		if (ret < 0) {
		if (ret < 0) {
			spin_unlock_irq(lock);
			spin_unlock_irq(lock);
			free_buf(buf);
			free_buf(buf, true);
			break;
			break;
		}
		}
		nr_added_bufs++;
		nr_added_bufs++;
@@ -1356,10 +1436,18 @@ static int add_port(struct ports_device *portdev, u32 id)
		goto free_device;
		goto free_device;
	}
	}


	if (is_rproc_serial(port->portdev->vdev))
		/*
		/*
	 * If we're not using multiport support, this has to be a console port
		 * For rproc_serial assume remote processor is connected.
		 * rproc_serial does not want the console port, only
		 * the generic port implementation.
		 */
		port->host_connected = true;
	else if (!use_multiport(port->portdev)) {
		/*
		 * If we're not using multiport support,
		 * this has to be a console port.
		 */
		 */
	if (!use_multiport(port->portdev)) {
		err = init_port_console(port);
		err = init_port_console(port);
		if (err)
		if (err)
			goto free_inbufs;
			goto free_inbufs;
@@ -1392,7 +1480,7 @@ static int add_port(struct ports_device *portdev, u32 id)


free_inbufs:
free_inbufs:
	while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
	while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
		free_buf(buf);
		free_buf(buf, true);
free_device:
free_device:
	device_destroy(pdrvdata.class, port->dev->devt);
	device_destroy(pdrvdata.class, port->dev->devt);
free_cdev:
free_cdev:
@@ -1434,7 +1522,11 @@ static void remove_port_data(struct port *port)


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

	/* Free pending buffers from the out-queue. */
	while ((buf = virtqueue_detach_unused_buf(port->out_vq)))
		free_buf(buf, true);
}
}


/*
/*
@@ -1636,7 +1728,7 @@ static void control_work_handler(struct work_struct *work)
		if (add_inbuf(portdev->c_ivq, buf) < 0) {
		if (add_inbuf(portdev->c_ivq, buf) < 0) {
			dev_warn(&portdev->vdev->dev,
			dev_warn(&portdev->vdev->dev,
				 "Error adding buffer to queue\n");
				 "Error adding buffer to queue\n");
			free_buf(buf);
			free_buf(buf, false);
		}
		}
	}
	}
	spin_unlock(&portdev->cvq_lock);
	spin_unlock(&portdev->cvq_lock);
@@ -1832,10 +1924,10 @@ static void remove_controlq_data(struct ports_device *portdev)
		return;
		return;


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


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


/*
/*
@@ -1882,11 +1974,15 @@ static int virtcons_probe(struct virtio_device *vdev)


	multiport = false;
	multiport = false;
	portdev->config.max_nr_ports = 1;
	portdev->config.max_nr_ports = 1;
	if (virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT,

	/* Don't test MULTIPORT at all if we're rproc: not a valid feature! */
	if (!is_rproc_serial(vdev) &&
	    virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
				  offsetof(struct virtio_console_config,
				  offsetof(struct virtio_console_config,
					   max_nr_ports),
					   max_nr_ports),
			      &portdev->config.max_nr_ports) == 0)
				  &portdev->config.max_nr_ports) == 0) {
		multiport = true;
		multiport = true;
	}


	err = init_vqs(portdev);
	err = init_vqs(portdev);
	if (err < 0) {
	if (err < 0) {
@@ -1996,6 +2092,16 @@ static unsigned int features[] = {
	VIRTIO_CONSOLE_F_MULTIPORT,
	VIRTIO_CONSOLE_F_MULTIPORT,
};
};


static struct virtio_device_id rproc_serial_id_table[] = {
#if IS_ENABLED(CONFIG_REMOTEPROC)
	{ VIRTIO_ID_RPROC_SERIAL, VIRTIO_DEV_ANY_ID },
#endif
	{ 0 },
};

static unsigned int rproc_serial_features[] = {
};

#ifdef CONFIG_PM
#ifdef CONFIG_PM
static int virtcons_freeze(struct virtio_device *vdev)
static int virtcons_freeze(struct virtio_device *vdev)
{
{
@@ -2080,6 +2186,20 @@ static struct virtio_driver virtio_console = {
#endif
#endif
};
};


/*
 * virtio_rproc_serial refers to __devinit function which causes
 * section mismatch warnings. So use __refdata to silence warnings.
 */
static struct virtio_driver __refdata virtio_rproc_serial = {
	.feature_table = rproc_serial_features,
	.feature_table_size = ARRAY_SIZE(rproc_serial_features),
	.driver.name =	"virtio_rproc_serial",
	.driver.owner =	THIS_MODULE,
	.id_table =	rproc_serial_id_table,
	.probe =	virtcons_probe,
	.remove =	virtcons_remove,
};

static int __init init(void)
static int __init init(void)
{
{
	int err;
	int err;
@@ -2104,7 +2224,15 @@ static int __init init(void)
		pr_err("Error %d registering virtio driver\n", err);
		pr_err("Error %d registering virtio driver\n", err);
		goto free;
		goto free;
	}
	}
	err = register_virtio_driver(&virtio_rproc_serial);
	if (err < 0) {
		pr_err("Error %d registering virtio rproc serial driver\n",
		       err);
		goto unregister;
	}
	return 0;
	return 0;
unregister:
	unregister_virtio_driver(&virtio_console);
free:
free:
	if (pdrvdata.debugfs_dir)
	if (pdrvdata.debugfs_dir)
		debugfs_remove_recursive(pdrvdata.debugfs_dir);
		debugfs_remove_recursive(pdrvdata.debugfs_dir);
@@ -2114,7 +2242,10 @@ static int __init init(void)


static void __exit fini(void)
static void __exit fini(void)
{
{
	reclaim_dma_bufs();

	unregister_virtio_driver(&virtio_console);
	unregister_virtio_driver(&virtio_console);
	unregister_virtio_driver(&virtio_rproc_serial);


	class_destroy(pdrvdata.class);
	class_destroy(pdrvdata.class);
	if (pdrvdata.debugfs_dir)
	if (pdrvdata.debugfs_dir)
+1 −1
Original line number Original line Diff line number Diff line
@@ -225,7 +225,7 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
			 * eventfd (ie. the appropriate virtqueue thread)?
			 * eventfd (ie. the appropriate virtqueue thread)?
			 */
			 */
			if (!send_notify_to_eventfd(cpu)) {
			if (!send_notify_to_eventfd(cpu)) {
				/* OK, we tell the main Laucher. */
				/* OK, we tell the main Launcher. */
				if (put_user(cpu->pending_notify, user))
				if (put_user(cpu->pending_notify, user))
					return -EFAULT;
					return -EFAULT;
				return sizeof(cpu->pending_notify);
				return sizeof(cpu->pending_notify);
+19 −29
Original line number Original line Diff line number Diff line
@@ -130,7 +130,6 @@ struct skb_vnet_hdr {
		struct virtio_net_hdr hdr;
		struct virtio_net_hdr hdr;
		struct virtio_net_hdr_mrg_rxbuf mhdr;
		struct virtio_net_hdr_mrg_rxbuf mhdr;
	};
	};
	unsigned int num_sg;
};
};


struct padded_vnet_hdr {
struct padded_vnet_hdr {
@@ -530,10 +529,10 @@ static bool try_fill_recv(struct receive_queue *rq, gfp_t gfp)
			err = add_recvbuf_small(rq, gfp);
			err = add_recvbuf_small(rq, gfp);


		oom = err == -ENOMEM;
		oom = err == -ENOMEM;
		if (err < 0)
		if (err)
			break;
			break;
		++rq->num;
		++rq->num;
	} while (err > 0);
	} while (rq->vq->num_free);
	if (unlikely(rq->num > rq->max))
	if (unlikely(rq->num > rq->max))
		rq->max = rq->num;
		rq->max = rq->num;
	virtqueue_kick(rq->vq);
	virtqueue_kick(rq->vq);
@@ -640,10 +639,10 @@ static int virtnet_open(struct net_device *dev)
	return 0;
	return 0;
}
}


static unsigned int free_old_xmit_skbs(struct send_queue *sq)
static void free_old_xmit_skbs(struct send_queue *sq)
{
{
	struct sk_buff *skb;
	struct sk_buff *skb;
	unsigned int len, tot_sgs = 0;
	unsigned int len;
	struct virtnet_info *vi = sq->vq->vdev->priv;
	struct virtnet_info *vi = sq->vq->vdev->priv;
	struct virtnet_stats *stats = this_cpu_ptr(vi->stats);
	struct virtnet_stats *stats = this_cpu_ptr(vi->stats);


@@ -655,10 +654,8 @@ static unsigned int free_old_xmit_skbs(struct send_queue *sq)
		stats->tx_packets++;
		stats->tx_packets++;
		u64_stats_update_end(&stats->tx_syncp);
		u64_stats_update_end(&stats->tx_syncp);


		tot_sgs += skb_vnet_hdr(skb)->num_sg;
		dev_kfree_skb_any(skb);
		dev_kfree_skb_any(skb);
	}
	}
	return tot_sgs;
}
}


static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)
static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)
@@ -666,6 +663,7 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)
	struct skb_vnet_hdr *hdr = skb_vnet_hdr(skb);
	struct skb_vnet_hdr *hdr = skb_vnet_hdr(skb);
	const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;
	const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;
	struct virtnet_info *vi = sq->vq->vdev->priv;
	struct virtnet_info *vi = sq->vq->vdev->priv;
	unsigned num_sg;


	pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest);
	pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest);


@@ -704,8 +702,8 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)
	else
	else
		sg_set_buf(sq->sg, &hdr->hdr, sizeof hdr->hdr);
		sg_set_buf(sq->sg, &hdr->hdr, sizeof hdr->hdr);


	hdr->num_sg = skb_to_sgvec(skb, sq->sg + 1, 0, skb->len) + 1;
	num_sg = skb_to_sgvec(skb, sq->sg + 1, 0, skb->len) + 1;
	return virtqueue_add_buf(sq->vq, sq->sg, hdr->num_sg,
	return virtqueue_add_buf(sq->vq, sq->sg, num_sg,
				 0, skb, GFP_ATOMIC);
				 0, skb, GFP_ATOMIC);
}
}


@@ -714,28 +712,20 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
	struct virtnet_info *vi = netdev_priv(dev);
	struct virtnet_info *vi = netdev_priv(dev);
	int qnum = skb_get_queue_mapping(skb);
	int qnum = skb_get_queue_mapping(skb);
	struct send_queue *sq = &vi->sq[qnum];
	struct send_queue *sq = &vi->sq[qnum];
	int capacity;
	int err;


	/* Free up any pending old buffers before queueing new ones. */
	/* Free up any pending old buffers before queueing new ones. */
	free_old_xmit_skbs(sq);
	free_old_xmit_skbs(sq);


	/* Try to transmit */
	/* Try to transmit */
	capacity = xmit_skb(sq, skb);
	err = xmit_skb(sq, skb);


	/* This can happen with OOM and indirect buffers. */
	/* This should not happen! */
	if (unlikely(capacity < 0)) {
	if (unlikely(err)) {
		if (likely(capacity == -ENOMEM)) {
			if (net_ratelimit())
				dev_warn(&dev->dev,
					 "TXQ (%d) failure: out of memory\n",
					 qnum);
		} else {
		dev->stats.tx_fifo_errors++;
		dev->stats.tx_fifo_errors++;
		if (net_ratelimit())
		if (net_ratelimit())
			dev_warn(&dev->dev,
			dev_warn(&dev->dev,
					 "Unexpected TXQ (%d) failure: %d\n",
				 "Unexpected TXQ (%d) queue failure: %d\n", qnum, err);
					 qnum, capacity);
		}
		dev->stats.tx_dropped++;
		dev->stats.tx_dropped++;
		kfree_skb(skb);
		kfree_skb(skb);
		return NETDEV_TX_OK;
		return NETDEV_TX_OK;
@@ -748,12 +738,12 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)


	/* Apparently nice girls don't return TX_BUSY; stop the queue
	/* Apparently nice girls don't return TX_BUSY; stop the queue
	 * before it gets out of hand.  Naturally, this wastes entries. */
	 * before it gets out of hand.  Naturally, this wastes entries. */
	if (capacity < 2+MAX_SKB_FRAGS) {
	if (sq->vq->num_free < 2+MAX_SKB_FRAGS) {
		netif_stop_subqueue(dev, qnum);
		netif_stop_subqueue(dev, qnum);
		if (unlikely(!virtqueue_enable_cb_delayed(sq->vq))) {
		if (unlikely(!virtqueue_enable_cb_delayed(sq->vq))) {
			/* More just got used, free them then recheck. */
			/* More just got used, free them then recheck. */
			capacity += free_old_xmit_skbs(sq);
			free_old_xmit_skbs(sq);
			if (capacity >= 2+MAX_SKB_FRAGS) {
			if (sq->vq->num_free >= 2+MAX_SKB_FRAGS) {
				netif_start_subqueue(dev, qnum);
				netif_start_subqueue(dev, qnum);
				virtqueue_disable_cb(sq->vq);
				virtqueue_disable_cb(sq->vq);
			}
			}
+2 −4
Original line number Original line Diff line number Diff line
@@ -764,7 +764,7 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst,


	/* add message to the remote processor's virtqueue */
	/* add message to the remote processor's virtqueue */
	err = virtqueue_add_buf(vrp->svq, &sg, 1, 0, msg, GFP_KERNEL);
	err = virtqueue_add_buf(vrp->svq, &sg, 1, 0, msg, GFP_KERNEL);
	if (err < 0) {
	if (err) {
		/*
		/*
		 * need to reclaim the buffer here, otherwise it's lost
		 * need to reclaim the buffer here, otherwise it's lost
		 * (memory won't leak, but rpmsg won't use it again for TX).
		 * (memory won't leak, but rpmsg won't use it again for TX).
@@ -776,8 +776,6 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst,


	/* tell the remote processor it has a pending message to read */
	/* tell the remote processor it has a pending message to read */
	virtqueue_kick(vrp->svq);
	virtqueue_kick(vrp->svq);

	err = 0;
out:
out:
	mutex_unlock(&vrp->tx_lock);
	mutex_unlock(&vrp->tx_lock);
	return err;
	return err;
@@ -980,7 +978,7 @@ static int rpmsg_probe(struct virtio_device *vdev)


		err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, cpu_addr,
		err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, cpu_addr,
								GFP_KERNEL);
								GFP_KERNEL);
		WARN_ON(err < 0); /* sanity check; this can't really happen */
		WARN_ON(err); /* sanity check; this can't really happen */
	}
	}


	/* suppress "tx-complete" interrupts */
	/* suppress "tx-complete" interrupts */
+13 −11
Original line number Original line Diff line number Diff line
@@ -215,7 +215,7 @@ static void virtscsi_ctrl_done(struct virtqueue *vq)
static int virtscsi_kick_event(struct virtio_scsi *vscsi,
static int virtscsi_kick_event(struct virtio_scsi *vscsi,
			       struct virtio_scsi_event_node *event_node)
			       struct virtio_scsi_event_node *event_node)
{
{
	int ret;
	int err;
	struct scatterlist sg;
	struct scatterlist sg;
	unsigned long flags;
	unsigned long flags;


@@ -223,13 +223,14 @@ static int virtscsi_kick_event(struct virtio_scsi *vscsi,


	spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags);
	spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags);


	ret = virtqueue_add_buf(vscsi->event_vq.vq, &sg, 0, 1, event_node, GFP_ATOMIC);
	err = virtqueue_add_buf(vscsi->event_vq.vq, &sg, 0, 1, event_node,
	if (ret >= 0)
				GFP_ATOMIC);
	if (!err)
		virtqueue_kick(vscsi->event_vq.vq);
		virtqueue_kick(vscsi->event_vq.vq);


	spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags);
	spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags);


	return ret;
	return err;
}
}


static int virtscsi_kick_event_all(struct virtio_scsi *vscsi)
static int virtscsi_kick_event_all(struct virtio_scsi *vscsi)
@@ -410,22 +411,23 @@ static int virtscsi_kick_cmd(struct virtio_scsi_target_state *tgt,
{
{
	unsigned int out_num, in_num;
	unsigned int out_num, in_num;
	unsigned long flags;
	unsigned long flags;
	int ret;
	int err;
	bool needs_kick = false;


	spin_lock_irqsave(&tgt->tgt_lock, flags);
	spin_lock_irqsave(&tgt->tgt_lock, flags);
	virtscsi_map_cmd(tgt, cmd, &out_num, &in_num, req_size, resp_size);
	virtscsi_map_cmd(tgt, cmd, &out_num, &in_num, req_size, resp_size);


	spin_lock(&vq->vq_lock);
	spin_lock(&vq->vq_lock);
	ret = virtqueue_add_buf(vq->vq, tgt->sg, out_num, in_num, cmd, gfp);
	err = virtqueue_add_buf(vq->vq, tgt->sg, out_num, in_num, cmd, gfp);
	spin_unlock(&tgt->tgt_lock);
	spin_unlock(&tgt->tgt_lock);
	if (ret >= 0)
	if (!err)
		ret = virtqueue_kick_prepare(vq->vq);
		needs_kick = virtqueue_kick_prepare(vq->vq);


	spin_unlock_irqrestore(&vq->vq_lock, flags);
	spin_unlock_irqrestore(&vq->vq_lock, flags);


	if (ret > 0)
	if (needs_kick)
		virtqueue_notify(vq->vq);
		virtqueue_notify(vq->vq);
	return ret;
	return err;
}
}


static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
@@ -467,7 +469,7 @@ static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)


	if (virtscsi_kick_cmd(tgt, &vscsi->req_vq, cmd,
	if (virtscsi_kick_cmd(tgt, &vscsi->req_vq, cmd,
			      sizeof cmd->req.cmd, sizeof cmd->resp.cmd,
			      sizeof cmd->req.cmd, sizeof cmd->resp.cmd,
			      GFP_ATOMIC) >= 0)
			      GFP_ATOMIC) == 0)
		ret = 0;
		ret = 0;
	else
	else
		mempool_free(cmd, virtscsi_cmd_pool);
		mempool_free(cmd, virtscsi_cmd_pool);
Loading