diff options
Diffstat (limited to 'drivers/char/virtio_console.c')
| -rw-r--r-- | drivers/char/virtio_console.c | 178 |
1 files changed, 58 insertions, 120 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 6a821118d553..088182e54deb 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -26,6 +26,7 @@ #include <linux/workqueue.h> #include <linux/module.h> #include <linux/dma-mapping.h> +#include <linux/string_choices.h> #include "../tty/hvc/hvc_console.h" #define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC) @@ -40,9 +41,6 @@ * across multiple devices and multiple ports per device. */ struct ports_driver_data { - /* Used for registering chardevs */ - struct class *class; - /* Used for exporting per-port information to debugfs */ struct dentry *debugfs_dir; @@ -55,6 +53,10 @@ struct ports_driver_data { static struct ports_driver_data pdrvdata; +static const struct class port_class = { + .name = "virtio-ports", +}; + static DEFINE_SPINLOCK(pdrvdata_lock); static DECLARE_COMPLETION(early_console_added); @@ -105,7 +107,7 @@ struct port_buffer { unsigned int sgpages; /* sg is used if spages > 0. sg must be the last in is struct */ - struct scatterlist sg[]; + struct scatterlist sg[] __counted_by(sgpages); }; /* @@ -229,9 +231,6 @@ struct port { bool guest_connected; }; -/* This is the very early arch-specified put chars function. */ -static int (*early_put_chars)(u32, const char *, int); - static struct port *find_port_by_vtermno(u32 vtermno) { struct port *port; @@ -652,7 +651,7 @@ done: * Give out the data that's requested from the buffer that we have * queued up. */ -static ssize_t fill_readbuf(struct port *port, char __user *out_buf, +static ssize_t fill_readbuf(struct port *port, u8 __user *out_buf, size_t out_count, bool to_user) { struct port_buffer *buf; @@ -671,7 +670,7 @@ static ssize_t fill_readbuf(struct port *port, char __user *out_buf, if (ret) return -EFAULT; } else { - memcpy((__force char *)out_buf, buf->buf + buf->offset, + memcpy((__force u8 *)out_buf, buf->buf + buf->offset, out_count); } @@ -885,9 +884,9 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf, if (len + offset > PAGE_SIZE) len = PAGE_SIZE - offset; - src = kmap_atomic(buf->page); + src = kmap_local_page(buf->page); memcpy(page_address(page) + offset, src + buf->offset, len); - kunmap_atomic(src); + kunmap_local(src); sg_set_page(&(sgl->sg[sgl->n]), page, len, offset); } @@ -925,14 +924,14 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, pipe_lock(pipe); ret = 0; - if (pipe_empty(pipe->head, pipe->tail)) + if (pipe_is_empty(pipe)) goto error_out; ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK); if (ret < 0) goto error_out; - occupancy = pipe_occupancy(pipe->head, pipe->tail); + occupancy = pipe_buf_usage(pipe); buf = alloc_buf(port->portdev->vdev, 0, occupancy); if (!buf) { @@ -1095,7 +1094,6 @@ static const struct file_operations port_fops = { .poll = port_fops_poll, .release = port_fops_release, .fasync = port_fops_fasync, - .llseek = no_llseek, }; /* @@ -1106,16 +1104,13 @@ static const struct file_operations port_fops = { * it to finish: inefficient in theory, but in practice * implementations will do it immediately. */ -static int put_chars(u32 vtermno, const char *buf, int count) +static ssize_t put_chars(u32 vtermno, const u8 *buf, size_t count) { struct port *port; struct scatterlist sg[1]; void *data; int ret; - if (unlikely(early_put_chars)) - return early_put_chars(vtermno, buf, count); - port = find_port_by_vtermno(vtermno); if (!port) return -EPIPE; @@ -1137,14 +1132,10 @@ static int put_chars(u32 vtermno, const char *buf, int count) * We call out to fill_readbuf that gets us the required data from the * buffers that are queued up. */ -static int get_chars(u32 vtermno, char *buf, int count) +static ssize_t get_chars(u32 vtermno, u8 *buf, size_t count) { struct port *port; - /* If we've not set up the port yet, we have no input to give. */ - if (unlikely(early_put_chars)) - return 0; - port = find_port_by_vtermno(vtermno); if (!port) return -EPIPE; @@ -1152,7 +1143,7 @@ static int get_chars(u32 vtermno, char *buf, int count) /* If we don't have an input queue yet, we can't get input. */ BUG_ON(!port->in_vq); - return fill_readbuf(port, (__force char __user *)buf, count, false); + return fill_readbuf(port, (__force u8 __user *)buf, count, false); } static void resize_console(struct port *port) @@ -1200,21 +1191,6 @@ static const struct hv_ops hv_ops = { .notifier_hangup = notifier_del_vio, }; -/* - * Console drivers are initialized very early so boot messages can go - * out, so we do things slightly differently from the generic virtio - * initialization of the net and block drivers. - * - * At this stage, the console is output-only. It's too early to set - * up a virtqueue, so we let the drivers do some boutique early-output - * thing. - */ -int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) -{ - early_put_chars = put_chars; - return hvc_instantiate(0, 0, &hv_ops); -} - static int init_port_console(struct port *port) { int ret; @@ -1255,13 +1231,6 @@ static int init_port_console(struct port *port) spin_unlock_irq(&pdrvdata_lock); port->guest_connected = true; - /* - * Start using the new console output if this is the first - * console to come up. - */ - if (early_put_chars) - early_put_chars = NULL; - /* Notify host of port being opened */ send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); @@ -1301,8 +1270,7 @@ static int port_debugfs_show(struct seq_file *s, void *data) seq_printf(s, "bytes_sent: %lu\n", port->stats.bytes_sent); seq_printf(s, "bytes_received: %lu\n", port->stats.bytes_received); seq_printf(s, "bytes_discarded: %lu\n", port->stats.bytes_discarded); - seq_printf(s, "is_console: %s\n", - is_console_port(port) ? "yes" : "no"); + seq_printf(s, "is_console: %s\n", str_yes_no(is_console_port(port))); seq_printf(s, "console_vtermno: %u\n", port->cons.vtermno); return 0; @@ -1353,7 +1321,6 @@ static void send_sigio_to_port(struct port *port) static int add_port(struct ports_device *portdev, u32 id) { - char debugfs_name[16]; struct port *port; dev_t devt; int err; @@ -1399,7 +1366,7 @@ static int add_port(struct ports_device *portdev, u32 id) "Error %d adding cdev for port %u\n", err, id); goto free_cdev; } - port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, + port->dev = device_create(&port_class, &port->portdev->vdev->dev, devt, port, "vport%up%u", port->portdev->vdev->index, id); if (IS_ERR(port->dev)) { @@ -1456,16 +1423,14 @@ static int add_port(struct ports_device *portdev, u32 id) * Finally, create the debugfs file that we can use to * inspect a port's state at any time */ - snprintf(debugfs_name, sizeof(debugfs_name), "vport%up%u", - port->portdev->vdev->index, id); - port->debugfs_file = debugfs_create_file(debugfs_name, 0444, + port->debugfs_file = debugfs_create_file(dev_name(port->dev), 0444, pdrvdata.debugfs_dir, port, &port_debugfs_fops); return 0; free_inbufs: free_device: - device_destroy(pdrvdata.class, port->dev->devt); + device_destroy(&port_class, port->dev->devt); free_cdev: cdev_del(port->cdev); free_port: @@ -1540,7 +1505,7 @@ 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); + device_destroy(&port_class, port->dev->devt); cdev_del(port->cdev); debugfs_remove(port->debugfs_file); @@ -1611,8 +1576,8 @@ static void handle_control_message(struct virtio_device *vdev, break; case VIRTIO_CONSOLE_RESIZE: { struct { - __u16 rows; - __u16 cols; + __virtio16 cols; + __virtio16 rows; } size; if (!is_console_port(port)) @@ -1620,7 +1585,8 @@ static void handle_control_message(struct virtio_device *vdev, memcpy(&size, buf->buf + buf->offset + sizeof(*cpkt), sizeof(size)); - set_console_size(port, size.rows, size.cols); + set_console_size(port, virtio16_to_cpu(vdev, size.rows), + virtio16_to_cpu(vdev, size.cols)); port->cons.hvc->irq_requested = 1; resize_console(port); @@ -1666,9 +1632,8 @@ static void handle_control_message(struct virtio_device *vdev, "Not enough space to store port name\n"); break; } - strncpy(port->name, buf->buf + buf->offset + sizeof(*cpkt), - name_size - 1); - port->name[name_size - 1] = 0; + strscpy(port->name, buf->buf + buf->offset + sizeof(*cpkt), + name_size); /* * Since we only have one sysfs attribute, 'name', @@ -1836,8 +1801,7 @@ static void config_work_handler(struct work_struct *work) static int init_vqs(struct ports_device *portdev) { - vq_callback_t **io_callbacks; - char **io_names; + struct virtqueue_info *vqs_info; struct virtqueue **vqs; u32 i, j, nr_ports, nr_queues; int err; @@ -1846,15 +1810,12 @@ static int init_vqs(struct ports_device *portdev) nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2; vqs = kmalloc_array(nr_queues, sizeof(struct virtqueue *), GFP_KERNEL); - io_callbacks = kmalloc_array(nr_queues, sizeof(vq_callback_t *), - GFP_KERNEL); - io_names = kmalloc_array(nr_queues, sizeof(char *), GFP_KERNEL); + vqs_info = kcalloc(nr_queues, sizeof(*vqs_info), GFP_KERNEL); portdev->in_vqs = kmalloc_array(nr_ports, sizeof(struct virtqueue *), GFP_KERNEL); portdev->out_vqs = kmalloc_array(nr_ports, sizeof(struct virtqueue *), GFP_KERNEL); - if (!vqs || !io_callbacks || !io_names || !portdev->in_vqs || - !portdev->out_vqs) { + if (!vqs || !vqs_info || !portdev->in_vqs || !portdev->out_vqs) { err = -ENOMEM; goto free; } @@ -1865,30 +1826,27 @@ static int init_vqs(struct ports_device *portdev) * 0 before others. */ j = 0; - io_callbacks[j] = in_intr; - io_callbacks[j + 1] = out_intr; - io_names[j] = "input"; - io_names[j + 1] = "output"; + vqs_info[j].callback = in_intr; + vqs_info[j + 1].callback = out_intr; + vqs_info[j].name = "input"; + vqs_info[j + 1].name = "output"; j += 2; if (use_multiport(portdev)) { - io_callbacks[j] = control_intr; - io_callbacks[j + 1] = NULL; - io_names[j] = "control-i"; - io_names[j + 1] = "control-o"; + vqs_info[j].callback = control_intr; + vqs_info[j].name = "control-i"; + vqs_info[j + 1].name = "control-o"; for (i = 1; i < nr_ports; i++) { j += 2; - io_callbacks[j] = in_intr; - io_callbacks[j + 1] = out_intr; - io_names[j] = "input"; - io_names[j + 1] = "output"; + vqs_info[j].callback = in_intr; + vqs_info[j + 1].callback = out_intr; + vqs_info[j].name = "input"; + vqs_info[j + 1].name = "output"; } } /* Find the queues. */ - err = virtio_find_vqs(portdev->vdev, nr_queues, vqs, - io_callbacks, - (const char **)io_names, NULL); + err = virtio_find_vqs(portdev->vdev, nr_queues, vqs, vqs_info, NULL); if (err) goto free; @@ -1906,8 +1864,7 @@ static int init_vqs(struct ports_device *portdev) portdev->out_vqs[i] = vqs[j + 1]; } } - kfree(io_names); - kfree(io_callbacks); + kfree(vqs_info); kfree(vqs); return 0; @@ -1915,8 +1872,7 @@ static int init_vqs(struct ports_device *portdev) free: kfree(portdev->out_vqs); kfree(portdev->in_vqs); - kfree(io_names); - kfree(io_callbacks); + kfree(vqs_info); kfree(vqs); return err; @@ -1936,6 +1892,7 @@ static void remove_vqs(struct ports_device *portdev) flush_bufs(vq, true); while ((buf = virtqueue_detach_unused_buf(vq))) free_buf(buf, true); + cond_resched(); } portdev->vdev->config->del_vqs(portdev->vdev); kfree(portdev->in_vqs); @@ -1998,7 +1955,6 @@ static int virtcons_probe(struct virtio_device *vdev) struct ports_device *portdev; int err; bool multiport; - bool early = early_put_chars != NULL; /* We only need a config space if features are offered */ if (!vdev->config->get && @@ -2009,9 +1965,6 @@ static int virtcons_probe(struct virtio_device *vdev) return -EINVAL; } - /* Ensure to read early_put_chars now */ - barrier(); - portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); if (!portdev) { err = -ENOMEM; @@ -2051,25 +2004,27 @@ static int virtcons_probe(struct virtio_device *vdev) multiport = true; } - err = init_vqs(portdev); - if (err < 0) { - dev_err(&vdev->dev, "Error %d initializing vqs\n", err); - goto free_chrdev; - } - spin_lock_init(&portdev->ports_lock); INIT_LIST_HEAD(&portdev->ports); INIT_LIST_HEAD(&portdev->list); - virtio_device_ready(portdev->vdev); - INIT_WORK(&portdev->config_work, &config_work_handler); INIT_WORK(&portdev->control_work, &control_work_handler); if (multiport) { spin_lock_init(&portdev->c_ivq_lock); spin_lock_init(&portdev->c_ovq_lock); + } + + err = init_vqs(portdev); + if (err < 0) { + dev_err(&vdev->dev, "Error %d initializing vqs\n", err); + goto free_chrdev; + } + virtio_device_ready(portdev->vdev); + + if (multiport) { err = fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); if (err < 0) { dev_err(&vdev->dev, @@ -2099,18 +2054,6 @@ static int virtcons_probe(struct virtio_device *vdev) __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, VIRTIO_CONSOLE_DEVICE_READY, 1); - /* - * If there was an early virtio console, assume that there are no - * other consoles. We need to wait until the hvc_alloc matches the - * hvc_instantiate, otherwise tty_open will complain, resulting in - * a "Warning: unable to open an initial console" boot failure. - * Without multiport this is done in add_port above. With multiport - * this might take some host<->guest communication - thus we have to - * wait. - */ - if (multiport && early) - wait_for_completion(&early_console_added); - return 0; free_chrdev: @@ -2220,7 +2163,6 @@ static struct virtio_driver virtio_console = { .feature_table = features, .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, .id_table = id_table, .probe = virtcons_probe, .remove = virtcons_remove, @@ -2235,7 +2177,6 @@ static struct virtio_driver 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, @@ -2245,12 +2186,9 @@ static int __init virtio_console_init(void) { int err; - pdrvdata.class = class_create(THIS_MODULE, "virtio-ports"); - if (IS_ERR(pdrvdata.class)) { - err = PTR_ERR(pdrvdata.class); - pr_err("Error %d creating virtio-ports class\n", err); + err = class_register(&port_class); + if (err) return err; - } pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL); INIT_LIST_HEAD(&pdrvdata.consoles); @@ -2272,7 +2210,7 @@ unregister: unregister_virtio_driver(&virtio_console); free: debugfs_remove_recursive(pdrvdata.debugfs_dir); - class_destroy(pdrvdata.class); + class_unregister(&port_class); return err; } @@ -2283,7 +2221,7 @@ static void __exit virtio_console_fini(void) unregister_virtio_driver(&virtio_console); unregister_virtio_driver(&virtio_rproc_serial); - class_destroy(pdrvdata.class); + class_unregister(&port_class); debugfs_remove_recursive(pdrvdata.debugfs_dir); } module_init(virtio_console_init); |
