diff options
Diffstat (limited to 'drivers/char')
| -rw-r--r-- | drivers/char/hw_random/core.c | 28 | ||||
| -rw-r--r-- | drivers/char/hw_random/virtio-rng.c | 13 | ||||
| -rw-r--r-- | drivers/char/random.c | 12 | ||||
| -rw-r--r-- | drivers/char/virtio_console.c | 44 | 
4 files changed, 74 insertions, 23 deletions
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 1bafb40ec8a..a0f7724852e 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -40,6 +40,7 @@  #include <linux/init.h>  #include <linux/miscdevice.h>  #include <linux/delay.h> +#include <linux/slab.h>  #include <asm/uaccess.h> @@ -52,8 +53,12 @@ static struct hwrng *current_rng;  static LIST_HEAD(rng_list);  static DEFINE_MUTEX(rng_mutex);  static int data_avail; -static u8 rng_buffer[SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES] -	__cacheline_aligned; +static u8 *rng_buffer; + +static size_t rng_buffer_size(void) +{ +	return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES; +}  static inline int hwrng_init(struct hwrng *rng)  { @@ -116,7 +121,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,  		if (!data_avail) {  			bytes_read = rng_get_data(current_rng, rng_buffer, -				sizeof(rng_buffer), +				rng_buffer_size(),  				!(filp->f_flags & O_NONBLOCK));  			if (bytes_read < 0) {  				err = bytes_read; @@ -307,6 +312,14 @@ int hwrng_register(struct hwrng *rng)  	mutex_lock(&rng_mutex); +	/* kmalloc makes this safe for virt_to_page() in virtio_rng.c */ +	err = -ENOMEM; +	if (!rng_buffer) { +		rng_buffer = kmalloc(rng_buffer_size(), GFP_KERNEL); +		if (!rng_buffer) +			goto out_unlock; +	} +  	/* Must not register two RNGs with the same name. */  	err = -EEXIST;  	list_for_each_entry(tmp, &rng_list, list) { @@ -367,6 +380,15 @@ void hwrng_unregister(struct hwrng *rng)  }  EXPORT_SYMBOL_GPL(hwrng_unregister); +static void __exit hwrng_exit(void) +{ +	mutex_lock(&rng_mutex); +	BUG_ON(current_rng); +	kfree(rng_buffer); +	mutex_unlock(&rng_mutex); +} + +module_exit(hwrng_exit);  MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");  MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 10fd71ccf58..6bf4d47324e 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -92,14 +92,22 @@ static int probe_common(struct virtio_device *vdev)  {  	int err; +	if (vq) { +		/* We only support one device for now */ +		return -EBUSY; +	}  	/* We expect a single virtqueue. */  	vq = virtio_find_single_vq(vdev, random_recv_done, "input"); -	if (IS_ERR(vq)) -		return PTR_ERR(vq); +	if (IS_ERR(vq)) { +		err = PTR_ERR(vq); +		vq = NULL; +		return err; +	}  	err = hwrng_register(&virtio_hwrng);  	if (err) {  		vdev->config->del_vqs(vdev); +		vq = NULL;  		return err;  	} @@ -112,6 +120,7 @@ static void remove_common(struct virtio_device *vdev)  	busy = false;  	hwrng_unregister(&virtio_hwrng);  	vdev->config->del_vqs(vdev); +	vq = NULL;  }  static int virtrng_probe(struct virtio_device *vdev) diff --git a/drivers/char/random.c b/drivers/char/random.c index 594bda9dcfc..32a6c576495 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -852,6 +852,7 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min,  		      int reserved)  {  	unsigned long flags; +	int wakeup_write = 0;  	/* Hold lock while accounting */  	spin_lock_irqsave(&r->lock, flags); @@ -873,10 +874,8 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min,  		else  			r->entropy_count = reserved; -		if (r->entropy_count < random_write_wakeup_thresh) { -			wake_up_interruptible(&random_write_wait); -			kill_fasync(&fasync, SIGIO, POLL_OUT); -		} +		if (r->entropy_count < random_write_wakeup_thresh) +			wakeup_write = 1;  	}  	DEBUG_ENT("debiting %zu entropy credits from %s%s\n", @@ -884,6 +883,11 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min,  	spin_unlock_irqrestore(&r->lock, flags); +	if (wakeup_write) { +		wake_up_interruptible(&random_write_wait); +		kill_fasync(&fasync, SIGIO, POLL_OUT); +	} +  	return nbytes;  } diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index e905d5f5305..ce5f3fc25d6 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -149,7 +149,8 @@ struct ports_device {  	spinlock_t ports_lock;  	/* To protect the vq operations for the control channel */ -	spinlock_t cvq_lock; +	spinlock_t c_ivq_lock; +	spinlock_t c_ovq_lock;  	/* The current config space is stored here */  	struct virtio_console_config config; @@ -569,11 +570,14 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id,  	vq = portdev->c_ovq;  	sg_init_one(sg, &cpkt, sizeof(cpkt)); + +	spin_lock(&portdev->c_ovq_lock);  	if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt, GFP_ATOMIC) == 0) {  		virtqueue_kick(vq);  		while (!virtqueue_get_buf(vq, &len))  			cpu_relax();  	} +	spin_unlock(&portdev->c_ovq_lock);  	return 0;  } @@ -1436,7 +1440,7 @@ static int add_port(struct ports_device *portdev, u32 id)  		 * rproc_serial does not want the console port, only  		 * the generic port implementation.  		 */ -		port->host_connected = port->guest_connected = true; +		port->host_connected = true;  	else if (!use_multiport(port->portdev)) {  		/*  		 * If we're not using multiport support, @@ -1709,23 +1713,23 @@ static void control_work_handler(struct work_struct *work)  	portdev = container_of(work, struct ports_device, control_work);  	vq = portdev->c_ivq; -	spin_lock(&portdev->cvq_lock); +	spin_lock(&portdev->c_ivq_lock);  	while ((buf = virtqueue_get_buf(vq, &len))) { -		spin_unlock(&portdev->cvq_lock); +		spin_unlock(&portdev->c_ivq_lock);  		buf->len = len;  		buf->offset = 0;  		handle_control_message(portdev, buf); -		spin_lock(&portdev->cvq_lock); +		spin_lock(&portdev->c_ivq_lock);  		if (add_inbuf(portdev->c_ivq, buf) < 0) {  			dev_warn(&portdev->vdev->dev,  				 "Error adding buffer to queue\n");  			free_buf(buf, false);  		}  	} -	spin_unlock(&portdev->cvq_lock); +	spin_unlock(&portdev->c_ivq_lock);  }  static void out_intr(struct virtqueue *vq) @@ -1752,13 +1756,23 @@ static void in_intr(struct virtqueue *vq)  	port->inbuf = get_inbuf(port);  	/* -	 * Don't queue up data when port is closed.  This condition +	 * Normally the port should not accept data when the port is +	 * closed. For generic serial ports, the host won't (shouldn't) +	 * send data till the guest is connected. But this condition  	 * can be reached when a console port is not yet connected (no -	 * tty is spawned) and the host sends out data to console -	 * ports.  For generic serial ports, the host won't -	 * (shouldn't) send data till the guest is connected. +	 * tty is spawned) and the other side sends out data over the +	 * vring, or when a remote devices start sending data before +	 * the ports are opened. +	 * +	 * A generic serial port will discard data if not connected, +	 * while console ports and rproc-serial ports accepts data at +	 * any time. rproc-serial is initiated with guest_connected to +	 * false because port_fops_open expects this. Console ports are +	 * hooked up with an HVC console and is initialized with +	 * guest_connected to true.  	 */ -	if (!port->guest_connected) + +	if (!port->guest_connected && !is_rproc_serial(port->portdev->vdev))  		discard_port_data(port);  	spin_unlock_irqrestore(&port->inbuf_lock, flags); @@ -1986,10 +2000,12 @@ static int virtcons_probe(struct virtio_device *vdev)  	if (multiport) {  		unsigned int nr_added_bufs; -		spin_lock_init(&portdev->cvq_lock); +		spin_lock_init(&portdev->c_ivq_lock); +		spin_lock_init(&portdev->c_ovq_lock);  		INIT_WORK(&portdev->control_work, &control_work_handler); -		nr_added_bufs = fill_queue(portdev->c_ivq, &portdev->cvq_lock); +		nr_added_bufs = fill_queue(portdev->c_ivq, +					   &portdev->c_ivq_lock);  		if (!nr_added_bufs) {  			dev_err(&vdev->dev,  				"Error allocating buffers for control queue\n"); @@ -2140,7 +2156,7 @@ static int virtcons_restore(struct virtio_device *vdev)  		return ret;  	if (use_multiport(portdev)) -		fill_queue(portdev->c_ivq, &portdev->cvq_lock); +		fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);  	list_for_each_entry(port, &portdev->ports, list) {  		port->in_vq = portdev->in_vqs[port->id];  |