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

Commit bb014db0 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull virtio fixes from Rusty Russell:
 "More virtio console fixes than I'm happy with, but all real issues,
  and all CC:stable.."

* tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux:
  virtio-scsi: Fix virtqueue affinity setup
  virtio: console: return -ENODEV on all read operations after unplug
  virtio: console: fix raising SIGIO after port unplug
  virtio: console: clean up port data immediately at time of unplug
  virtio: console: fix race in port_fops_open() and port unplug
  virtio: console: fix race with port unplug and open/close
  virtio/console: Add pipe_lock/unlock for splice_write
  virtio/console: Quit from splice_write if pipe->nrbufs is 0
parents 67ef6265 aa52aeea
Loading
Loading
Loading
Loading
+48 −22
Original line number Diff line number Diff line
@@ -272,9 +272,12 @@ static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev,
	unsigned long flags;

	spin_lock_irqsave(&portdev->ports_lock, flags);
	list_for_each_entry(port, &portdev->ports, list)
		if (port->cdev->dev == dev)
	list_for_each_entry(port, &portdev->ports, list) {
		if (port->cdev->dev == dev) {
			kref_get(&port->kref);
			goto out;
		}
	}
	port = NULL;
out:
	spin_unlock_irqrestore(&portdev->ports_lock, flags);
@@ -746,6 +749,10 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf,

	port = filp->private_data;

	/* Port is hot-unplugged. */
	if (!port->guest_connected)
		return -ENODEV;

	if (!port_has_data(port)) {
		/*
		 * If nothing's connected on the host just return 0 in
@@ -762,7 +769,7 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
		if (ret < 0)
			return ret;
	}
	/* Port got hot-unplugged. */
	/* Port got hot-unplugged while we were waiting above. */
	if (!port->guest_connected)
		return -ENODEV;
	/*
@@ -932,13 +939,25 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
	if (is_rproc_serial(port->out_vq->vdev))
		return -EINVAL;

	/*
	 * pipe->nrbufs == 0 means there are no data to transfer,
	 * so this returns just 0 for no data.
	 */
	pipe_lock(pipe);
	if (!pipe->nrbufs) {
		ret = 0;
		goto error_out;
	}

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

	buf = alloc_buf(port->out_vq, 0, pipe->nrbufs);
	if (!buf)
		return -ENOMEM;
	if (!buf) {
		ret = -ENOMEM;
		goto error_out;
	}

	sgl.n = 0;
	sgl.len = 0;
@@ -946,12 +965,17 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
	sgl.sg = buf->sg;
	sg_init_table(sgl.sg, sgl.size);
	ret = __splice_from_pipe(pipe, &sd, pipe_to_sg);
	pipe_unlock(pipe);
	if (likely(ret > 0))
		ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true);

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

error_out:
	pipe_unlock(pipe);
	return ret;
}

static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
@@ -1019,14 +1043,14 @@ static int port_fops_open(struct inode *inode, struct file *filp)
	struct port *port;
	int ret;

	/* We get the port with a kref here */
	port = find_port_by_devt(cdev->dev);
	if (!port) {
		/* Port was unplugged before we could proceed */
		return -ENXIO;
	}
	filp->private_data = port;

	/* Prevent against a port getting hot-unplugged at the same time */
	spin_lock_irq(&port->portdev->ports_lock);
	kref_get(&port->kref);
	spin_unlock_irq(&port->portdev->ports_lock);

	/*
	 * Don't allow opening of console port devices -- that's done
	 * via /dev/hvc
@@ -1498,14 +1522,6 @@ static void remove_port(struct kref *kref)

	port = container_of(kref, struct port, kref);

	sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
	device_destroy(pdrvdata.class, port->dev->devt);
	cdev_del(port->cdev);

	kfree(port->name);

	debugfs_remove(port->debugfs_file);

	kfree(port);
}

@@ -1539,12 +1555,14 @@ static void unplug_port(struct port *port)
	spin_unlock_irq(&port->portdev->ports_lock);

	if (port->guest_connected) {
		/* Let the app know the port is going down. */
		send_sigio_to_port(port);

		/* Do this after sigio is actually sent */
		port->guest_connected = false;
		port->host_connected = false;
		wake_up_interruptible(&port->waitqueue);

		/* Let the app know the port is going down. */
		send_sigio_to_port(port);
		wake_up_interruptible(&port->waitqueue);
	}

	if (is_console_port(port)) {
@@ -1563,6 +1581,14 @@ static void unplug_port(struct port *port)
	 */
	port->portdev = NULL;

	sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
	device_destroy(pdrvdata.class, port->dev->devt);
	cdev_del(port->cdev);

	kfree(port->name);

	debugfs_remove(port->debugfs_file);

	/*
	 * Locks around here are not necessary - a port can't be
	 * opened after we removed the port struct from ports_list
+1 −1
Original line number Diff line number Diff line
@@ -751,7 +751,7 @@ static void __virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity)

		vscsi->affinity_hint_set = true;
	} else {
		for (i = 0; i < vscsi->num_queues - VIRTIO_SCSI_VQ_BASE; i++)
		for (i = 0; i < vscsi->num_queues; i++)
			virtqueue_set_affinity(vscsi->req_vqs[i].vq, -1);

		vscsi->affinity_hint_set = false;