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

Commit 94ebd235 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'virtio' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-for-linus:
  virtio_blk: remove BKL leftovers
  virtio: console: Disable lseek(2) for port file operations
  virtio: console: Send SIGIO in case of port unplug
  virtio: console: Send SIGIO on new data arrival on ports
  virtio: console: Send SIGIO to processes that request it for host events
  virtio: console: Reference counting portdev structs is not needed
  virtio: console: Add reference counting for port struct
  virtio: console: Use cdev_alloc() instead of cdev_init()
  virtio: console: Add a find_port_by_devt() function
  virtio: console: Add a list of portdevs that are active
  virtio: console: open: Use a common path for error handling
  virtio: console: remove_port() should return void
  virtio: console: Make write() return -ENODEV on hot-unplug
  virtio: console: Make read() return -ENODEV on hot-unplug
  virtio: console: Unblock poll on port hot-unplug
  virtio: console: Un-block reads on chardev close
  virtio: console: Check if portdev is valid in send_control_msg()
  virtio: console: Remove control vq data only if using multiport support
  virtio: console: Reset vdev before removing device
parents f6f0a6d6 fe5a50a1
Loading
Loading
Loading
Loading
+2 −15
Original line number Diff line number Diff line
@@ -2,7 +2,6 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <linux/smp_lock.h>
#include <linux/hdreg.h>
#include <linux/virtio.h>
#include <linux/virtio_blk.h>
@@ -222,8 +221,8 @@ static int virtblk_get_id(struct gendisk *disk, char *id_str)
	return err;
}

static int virtblk_locked_ioctl(struct block_device *bdev, fmode_t mode,
			 unsigned cmd, unsigned long data)
static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
			     unsigned int cmd, unsigned long data)
{
	struct gendisk *disk = bdev->bd_disk;
	struct virtio_blk *vblk = disk->private_data;
@@ -238,18 +237,6 @@ static int virtblk_locked_ioctl(struct block_device *bdev, fmode_t mode,
			      (void __user *)data);
}

static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
			     unsigned int cmd, unsigned long param)
{
	int ret;

	lock_kernel();
	ret = virtblk_locked_ioctl(bdev, mode, cmd, param);
	unlock_kernel();

	return ret;
}

/* We provide getgeo only to please some old bootloader/partitioning tools */
static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
{
+207 −33
Original line number Diff line number Diff line
@@ -48,6 +48,9 @@ struct ports_driver_data {
	/* Used for exporting per-port information to debugfs */
	struct dentry *debugfs_dir;

	/* List of all the devices we're handling */
	struct list_head portdevs;

	/* Number of devices this driver is handling */
	unsigned int index;

@@ -108,6 +111,9 @@ struct port_buffer {
 * ports for that device (vdev->priv).
 */
struct ports_device {
	/* Next portdev in the list, head is in the pdrvdata struct */
	struct list_head list;

	/*
	 * Workqueue handlers where we process deferred work after
	 * notification
@@ -178,15 +184,21 @@ struct port {
	struct console cons;

	/* Each port associates with a separate char device */
	struct cdev cdev;
	struct cdev *cdev;
	struct device *dev;

	/* Reference-counting to handle port hot-unplugs and file operations */
	struct kref kref;

	/* A waitqueue for poll() or blocking read operations */
	wait_queue_head_t waitqueue;

	/* The 'name' of the port that we expose via sysfs properties */
	char *name;

	/* We can notify apps of host connect / disconnect events via SIGIO */
	struct fasync_struct *async_queue;

	/* The 'id' to identify the port with the Host */
	u32 id;

@@ -221,6 +233,41 @@ static struct port *find_port_by_vtermno(u32 vtermno)
	return port;
}

static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev,
						 dev_t dev)
{
	struct port *port;
	unsigned long flags;

	spin_lock_irqsave(&portdev->ports_lock, flags);
	list_for_each_entry(port, &portdev->ports, list)
		if (port->cdev->dev == dev)
			goto out;
	port = NULL;
out:
	spin_unlock_irqrestore(&portdev->ports_lock, flags);

	return port;
}

static struct port *find_port_by_devt(dev_t dev)
{
	struct ports_device *portdev;
	struct port *port;
	unsigned long flags;

	spin_lock_irqsave(&pdrvdata_lock, flags);
	list_for_each_entry(portdev, &pdrvdata.portdevs, list) {
		port = find_port_by_devt_in_portdev(portdev, dev);
		if (port)
			goto out;
	}
	port = NULL;
out:
	spin_unlock_irqrestore(&pdrvdata_lock, flags);
	return port;
}

static struct port *find_port_by_id(struct ports_device *portdev, u32 id)
{
	struct port *port;
@@ -410,7 +457,10 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id,
static ssize_t send_control_msg(struct port *port, unsigned int event,
				unsigned int value)
{
	/* Did the port get unplugged before userspace closed it? */
	if (port->portdev)
		return __send_control_msg(port->portdev, port->id, event, value);
	return 0;
}

/* Callers must take the port->outvq_lock */
@@ -525,6 +575,10 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count,
/* The condition that must be true for polling to end */
static bool will_read_block(struct port *port)
{
	if (!port->guest_connected) {
		/* Port got hot-unplugged. Let's exit. */
		return false;
	}
	return !port_has_data(port) && port->host_connected;
}

@@ -575,6 +629,9 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf,
		if (ret < 0)
			return ret;
	}
	/* Port got hot-unplugged. */
	if (!port->guest_connected)
		return -ENODEV;
	/*
	 * We could've received a disconnection message while we were
	 * waiting for more data.
@@ -616,6 +673,9 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
		if (ret < 0)
			return ret;
	}
	/* Port got hot-unplugged. */
	if (!port->guest_connected)
		return -ENODEV;

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

@@ -656,6 +716,10 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
	port = filp->private_data;
	poll_wait(filp, &port->waitqueue, wait);

	if (!port->guest_connected) {
		/* Port got unplugged */
		return POLLHUP;
	}
	ret = 0;
	if (!will_read_block(port))
		ret |= POLLIN | POLLRDNORM;
@@ -667,6 +731,8 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait)
	return ret;
}

static void remove_port(struct kref *kref);

static int port_fops_release(struct inode *inode, struct file *filp)
{
	struct port *port;
@@ -687,6 +753,16 @@ static int port_fops_release(struct inode *inode, struct file *filp)
	reclaim_consumed_buffers(port);
	spin_unlock_irq(&port->outvq_lock);

	/*
	 * 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
	 * exist for the port.  Plus, taking ports_lock here would
	 * create a dependency on other locks taken by functions
	 * inside remove_port if we're the last holder of the port,
	 * creating many problems.
	 */
	kref_put(&port->kref, remove_port);

	return 0;
}

@@ -694,22 +770,31 @@ static int port_fops_open(struct inode *inode, struct file *filp)
{
	struct cdev *cdev = inode->i_cdev;
	struct port *port;
	int ret;

	port = container_of(cdev, struct port, cdev);
	port = find_port_by_devt(cdev->dev);
	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
	 */
	if (is_console_port(port))
		return -ENXIO;
	if (is_console_port(port)) {
		ret = -ENXIO;
		goto out;
	}

	/* Allow only one process to open a particular port at a time */
	spin_lock_irq(&port->inbuf_lock);
	if (port->guest_connected) {
		spin_unlock_irq(&port->inbuf_lock);
		return -EMFILE;
		ret = -EMFILE;
		goto out;
	}

	port->guest_connected = true;
@@ -724,10 +809,23 @@ static int port_fops_open(struct inode *inode, struct file *filp)
	reclaim_consumed_buffers(port);
	spin_unlock_irq(&port->outvq_lock);

	nonseekable_open(inode, filp);

	/* Notify host of port being opened */
	send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1);

	return 0;
out:
	kref_put(&port->kref, remove_port);
	return ret;
}

static int port_fops_fasync(int fd, struct file *filp, int mode)
{
	struct port *port;

	port = filp->private_data;
	return fasync_helper(fd, filp, mode, &port->async_queue);
}

/*
@@ -743,6 +841,8 @@ static const struct file_operations port_fops = {
	.write = port_fops_write,
	.poll  = port_fops_poll,
	.release = port_fops_release,
	.fasync = port_fops_fasync,
	.llseek = no_llseek,
};

/*
@@ -1001,6 +1101,12 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)
	return nr_added_bufs;
}

static void send_sigio_to_port(struct port *port)
{
	if (port->async_queue && port->guest_connected)
		kill_fasync(&port->async_queue, SIGIO, POLL_OUT);
}

static int add_port(struct ports_device *portdev, u32 id)
{
	char debugfs_name[16];
@@ -1015,6 +1121,7 @@ static int add_port(struct ports_device *portdev, u32 id)
		err = -ENOMEM;
		goto fail;
	}
	kref_init(&port->kref);

	port->portdev = portdev;
	port->id = id;
@@ -1022,6 +1129,7 @@ static int add_port(struct ports_device *portdev, u32 id)
	port->name = NULL;
	port->inbuf = NULL;
	port->cons.hvc = NULL;
	port->async_queue = NULL;

	port->cons.ws.ws_row = port->cons.ws.ws_col = 0;

@@ -1032,14 +1140,20 @@ static int add_port(struct ports_device *portdev, u32 id)
	port->in_vq = portdev->in_vqs[port->id];
	port->out_vq = portdev->out_vqs[port->id];

	cdev_init(&port->cdev, &port_fops);
	port->cdev = cdev_alloc();
	if (!port->cdev) {
		dev_err(&port->portdev->vdev->dev, "Error allocating cdev\n");
		err = -ENOMEM;
		goto free_port;
	}
	port->cdev->ops = &port_fops;

	devt = MKDEV(portdev->chr_major, id);
	err = cdev_add(&port->cdev, devt, 1);
	err = cdev_add(port->cdev, devt, 1);
	if (err < 0) {
		dev_err(&port->portdev->vdev->dev,
			"Error %d adding cdev for port %u\n", err, id);
		goto free_port;
		goto free_cdev;
	}
	port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev,
				  devt, port, "vport%up%u",
@@ -1104,7 +1218,7 @@ static int add_port(struct ports_device *portdev, u32 id)
free_device:
	device_destroy(pdrvdata.class, port->dev->devt);
free_cdev:
	cdev_del(&port->cdev);
	cdev_del(port->cdev);
free_port:
	kfree(port);
fail:
@@ -1113,21 +1227,45 @@ static int add_port(struct ports_device *portdev, u32 id)
	return err;
}

/* Remove all port-specific data. */
static int remove_port(struct port *port)
/* No users remain, remove all port-specific data. */
static void remove_port(struct kref *kref)
{
	struct port *port;

	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);
}

/*
 * Port got unplugged.  Remove port from portdev's list and drop the
 * kref reference.  If no userspace has this port opened, it will
 * result in immediate removal the port.
 */
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);

	if (port->guest_connected) {
		port->guest_connected = false;
		port->host_connected = false;
		wake_up_interruptible(&port->waitqueue);
		send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
	}

	spin_lock_irq(&port->portdev->ports_lock);
	list_del(&port->list);
	spin_unlock_irq(&port->portdev->ports_lock);
		/* Let the app know the port is going down. */
		send_sigio_to_port(port);
	}

	if (is_console_port(port)) {
		spin_lock_irq(&pdrvdata_lock);
@@ -1146,9 +1284,6 @@ static int remove_port(struct port *port)
		hvc_remove(port->cons.hvc);
#endif
	}
	sysfs_remove_group(&port->dev->kobj, &port_attribute_group);
	device_destroy(pdrvdata.class, port->dev->devt);
	cdev_del(&port->cdev);

	/* Remove unused data this port might have received. */
	discard_port_data(port);
@@ -1159,12 +1294,19 @@ static int remove_port(struct port *port)
	while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
		free_buf(buf);

	kfree(port->name);

	debugfs_remove(port->debugfs_file);
	/*
	 * We should just assume the device itself has gone off --
	 * else a close on an open port later will try to send out a
	 * control message.
	 */
	port->portdev = NULL;

	kfree(port);
	return 0;
	/*
	 * Locks around here are not necessary - a port can't be
	 * opened after we removed the port struct from ports_list
	 * above.
	 */
	kref_put(&port->kref, remove_port);
}

/* Any private messages that the Host and Guest want to share */
@@ -1203,7 +1345,7 @@ static void handle_control_message(struct ports_device *portdev,
		add_port(portdev, cpkt->id);
		break;
	case VIRTIO_CONSOLE_PORT_REMOVE:
		remove_port(port);
		unplug_port(port);
		break;
	case VIRTIO_CONSOLE_CONSOLE_PORT:
		if (!cpkt->value)
@@ -1245,6 +1387,12 @@ static void handle_control_message(struct ports_device *portdev,
		spin_lock_irq(&port->outvq_lock);
		reclaim_consumed_buffers(port);
		spin_unlock_irq(&port->outvq_lock);

		/*
		 * If the guest is connected, it'll be interested in
		 * knowing the host connection state changed.
		 */
		send_sigio_to_port(port);
		break;
	case VIRTIO_CONSOLE_PORT_NAME:
		/*
@@ -1341,6 +1489,9 @@ static void in_intr(struct virtqueue *vq)

	wake_up_interruptible(&port->waitqueue);

	/* Send a SIGIO indicating new data in case the process asked for it */
	send_sigio_to_port(port);

	if (is_console_port(port) && hvc_poll(port->cons.hvc))
		hvc_kick();
}
@@ -1577,6 +1728,10 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
		add_port(portdev, 0);
	}

	spin_lock_irq(&pdrvdata_lock);
	list_add_tail(&portdev->list, &pdrvdata.portdevs);
	spin_unlock_irq(&pdrvdata_lock);

	__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
			   VIRTIO_CONSOLE_DEVICE_READY, 1);
	return 0;
@@ -1600,23 +1755,41 @@ static void virtcons_remove(struct virtio_device *vdev)
{
	struct ports_device *portdev;
	struct port *port, *port2;
	struct port_buffer *buf;
	unsigned int len;

	portdev = vdev->priv;

	spin_lock_irq(&pdrvdata_lock);
	list_del(&portdev->list);
	spin_unlock_irq(&pdrvdata_lock);

	/* Disable interrupts for vqs */
	vdev->config->reset(vdev);
	/* Finish up work that's lined up */
	cancel_work_sync(&portdev->control_work);

	list_for_each_entry_safe(port, port2, &portdev->ports, list)
		remove_port(port);
		unplug_port(port);

	unregister_chrdev(portdev->chr_major, "virtio-portsdev");

	/*
	 * When yanking out a device, we immediately lose the
	 * (device-side) queues.  So there's no point in keeping the
	 * guest side around till we drop our final reference.  This
	 * also means that any ports which are in an open state will
	 * 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);
@@ -1663,6 +1836,7 @@ static int __init init(void)
			   PTR_ERR(pdrvdata.debugfs_dir));
	}
	INIT_LIST_HEAD(&pdrvdata.consoles);
	INIT_LIST_HEAD(&pdrvdata.portdevs);

	return register_virtio_driver(&virtio_console);
}