Commit bb014db0 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux

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
...@@ -272,9 +272,12 @@ static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev, ...@@ -272,9 +272,12 @@ static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev,
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&portdev->ports_lock, flags); spin_lock_irqsave(&portdev->ports_lock, flags);
list_for_each_entry(port, &portdev->ports, list) list_for_each_entry(port, &portdev->ports, list) {
if (port->cdev->dev == dev) if (port->cdev->dev == dev) {
kref_get(&port->kref);
goto out; goto out;
}
}
port = NULL; port = NULL;
out: out:
spin_unlock_irqrestore(&portdev->ports_lock, flags); spin_unlock_irqrestore(&portdev->ports_lock, flags);
...@@ -746,6 +749,10 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf, ...@@ -746,6 +749,10 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
port = filp->private_data; port = filp->private_data;
/* Port is hot-unplugged. */
if (!port->guest_connected)
return -ENODEV;
if (!port_has_data(port)) { if (!port_has_data(port)) {
/* /*
* If nothing's connected on the host just return 0 in * 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, ...@@ -762,7 +769,7 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
/* Port got hot-unplugged. */ /* Port got hot-unplugged while we were waiting above. */
if (!port->guest_connected) if (!port->guest_connected)
return -ENODEV; return -ENODEV;
/* /*
...@@ -932,13 +939,25 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, ...@@ -932,13 +939,25 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
if (is_rproc_serial(port->out_vq->vdev)) if (is_rproc_serial(port->out_vq->vdev))
return -EINVAL; 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); ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK);
if (ret < 0) if (ret < 0)
return ret; goto error_out;
buf = alloc_buf(port->out_vq, 0, pipe->nrbufs); buf = alloc_buf(port->out_vq, 0, pipe->nrbufs);
if (!buf) if (!buf) {
return -ENOMEM; ret = -ENOMEM;
goto error_out;
}
sgl.n = 0; sgl.n = 0;
sgl.len = 0; sgl.len = 0;
...@@ -946,12 +965,17 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, ...@@ -946,12 +965,17 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
sgl.sg = buf->sg; sgl.sg = buf->sg;
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);
pipe_unlock(pipe);
if (likely(ret > 0)) if (likely(ret > 0))
ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true); ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true);
if (unlikely(ret <= 0)) if (unlikely(ret <= 0))
free_buf(buf, true); free_buf(buf, true);
return ret; return ret;
error_out:
pipe_unlock(pipe);
return ret;
} }
static unsigned int port_fops_poll(struct file *filp, poll_table *wait) 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) ...@@ -1019,14 +1043,14 @@ static int port_fops_open(struct inode *inode, struct file *filp)
struct port *port; struct port *port;
int ret; int ret;
/* We get the port with a kref here */
port = find_port_by_devt(cdev->dev); port = find_port_by_devt(cdev->dev);
if (!port) {
/* Port was unplugged before we could proceed */
return -ENXIO;
}
filp->private_data = port; 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 * Don't allow opening of console port devices -- that's done
* via /dev/hvc * via /dev/hvc
...@@ -1498,14 +1522,6 @@ static void remove_port(struct kref *kref) ...@@ -1498,14 +1522,6 @@ static void remove_port(struct kref *kref)
port = container_of(kref, struct port, 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); kfree(port);
} }
...@@ -1539,12 +1555,14 @@ static void unplug_port(struct port *port) ...@@ -1539,12 +1555,14 @@ static void unplug_port(struct port *port)
spin_unlock_irq(&port->portdev->ports_lock); spin_unlock_irq(&port->portdev->ports_lock);
if (port->guest_connected) { 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->guest_connected = false;
port->host_connected = false; port->host_connected = false;
wake_up_interruptible(&port->waitqueue);
/* Let the app know the port is going down. */ wake_up_interruptible(&port->waitqueue);
send_sigio_to_port(port);
} }
if (is_console_port(port)) { if (is_console_port(port)) {
...@@ -1563,6 +1581,14 @@ static void unplug_port(struct port *port) ...@@ -1563,6 +1581,14 @@ static void unplug_port(struct port *port)
*/ */
port->portdev = NULL; 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 * Locks around here are not necessary - a port can't be
* opened after we removed the port struct from ports_list * opened after we removed the port struct from ports_list
......
...@@ -751,7 +751,7 @@ static void __virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity) ...@@ -751,7 +751,7 @@ static void __virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity)
vscsi->affinity_hint_set = true; vscsi->affinity_hint_set = true;
} else { } 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); virtqueue_set_affinity(vscsi->req_vqs[i].vq, -1);
vscsi->affinity_hint_set = false; vscsi->affinity_hint_set = false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment