--- zzzz-none-000/linux-3.10.107/drivers/char/virtio_console.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/char/virtio_console.c 2021-02-04 17:41:59.000000000 +0000 @@ -142,6 +142,7 @@ * notification */ struct work_struct control_work; + struct work_struct config_work; struct list_head ports; @@ -354,8 +355,8 @@ * early_init */ if (!portdev->vdev) - return 0; - return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT); + return false; + return __virtio_test_bit(portdev->vdev, VIRTIO_CONSOLE_F_MULTIPORT); } static DEFINE_SPINLOCK(dma_bufs_lock); @@ -566,9 +567,9 @@ if (!use_multiport(portdev)) return 0; - cpkt.id = port_id; - cpkt.event = event; - cpkt.value = value; + cpkt.id = cpu_to_virtio32(portdev->vdev, port_id); + cpkt.event = cpu_to_virtio16(portdev->vdev, event); + cpkt.value = cpu_to_virtio16(portdev->vdev, value); vq = portdev->c_ovq; @@ -577,7 +578,8 @@ spin_lock(&portdev->c_ovq_lock); if (virtqueue_add_outbuf(vq, sg, 1, &cpkt, GFP_ATOMIC) == 0) { virtqueue_kick(vq); - while (!virtqueue_get_buf(vq, &len)) + while (!virtqueue_get_buf(vq, &len) + && !virtqueue_is_broken(vq)) cpu_relax(); } spin_unlock(&portdev->c_ovq_lock); @@ -650,7 +652,8 @@ * we need to kmalloc a GFP_ATOMIC buffer each time the * console driver writes something out. */ - while (!virtqueue_get_buf(out_vq, &len)) + while (!virtqueue_get_buf(out_vq, &len) + && !virtqueue_is_broken(out_vq)) cpu_relax(); done: spin_unlock_irqrestore(&port->outvq_lock, flags); @@ -667,8 +670,8 @@ * Give out the data that's requested from the buffer that we have * queued up. */ -static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, - bool to_user) +static ssize_t fill_readbuf(struct port *port, char __user *out_buf, + size_t out_count, bool to_user) { struct port_buffer *buf; unsigned long flags; @@ -686,7 +689,8 @@ if (ret) return -EFAULT; } else { - memcpy(out_buf, buf->buf + buf->offset, out_count); + memcpy((__force char *)out_buf, buf->buf + buf->offset, + out_count); } buf->offset += out_count; @@ -888,12 +892,10 @@ } else { /* Failback to copying a page */ struct page *page = alloc_page(GFP_KERNEL); - char *src = buf->ops->map(pipe, buf, 1); - char *dst; + char *src; if (!page) return -ENOMEM; - dst = kmap(page); offset = sd->pos & ~PAGE_MASK; @@ -901,10 +903,9 @@ if (len + offset > PAGE_SIZE) len = PAGE_SIZE - offset; - memcpy(dst + offset, src + buf->offset, len); - - kunmap(page); - buf->ops->unmap(pipe, buf, src); + src = kmap_atomic(buf->page); + memcpy(page_address(page) + offset, src + buf->offset, len); + kunmap_atomic(src); sg_set_page(&(sgl->sg[sgl->n]), page, len, offset); } @@ -1129,8 +1130,6 @@ { struct port *port; struct scatterlist sg[1]; - void *data; - int ret; if (unlikely(early_put_chars)) return early_put_chars(vtermno, buf, count); @@ -1139,14 +1138,8 @@ if (!port) return -EPIPE; - data = kmemdup(buf, count, GFP_ATOMIC); - if (!data) - return -ENOMEM; - - sg_init_one(sg, data, count); - ret = __send_to_port(port, sg, 1, count, data, false); - kfree(data); - return ret; + sg_init_one(sg, buf, count); + return __send_to_port(port, sg, 1, count, (void *)buf, false); } /* @@ -1171,7 +1164,7 @@ /* If we don't have an input queue yet, we can't get input. */ BUG_ON(!port->in_vq); - return fill_readbuf(port, buf, count, false); + return fill_readbuf(port, (__force char __user *)buf, count, false); } static void resize_console(struct port *port) @@ -1499,8 +1492,8 @@ * Finally, create the debugfs file that we can use to * inspect a port's state at any time */ - sprintf(debugfs_name, "vport%up%u", - port->portdev->vdev->index, id); + snprintf(debugfs_name, sizeof(debugfs_name), "vport%up%u", + port->portdev->vdev->index, id); port->debugfs_file = debugfs_create_file(debugfs_name, 0444, pdrvdata.debugfs_dir, port, @@ -1537,18 +1530,32 @@ { struct port_buffer *buf; + spin_lock_irq(&port->inbuf_lock); /* Remove unused data this port might have received. */ discard_port_data(port); - - reclaim_consumed_buffers(port); + spin_unlock_irq(&port->inbuf_lock); /* Remove buffers we queued up for the Host to send us data in. */ - while ((buf = virtqueue_detach_unused_buf(port->in_vq))) - free_buf(buf, true); + do { + spin_lock_irq(&port->inbuf_lock); + buf = virtqueue_detach_unused_buf(port->in_vq); + spin_unlock_irq(&port->inbuf_lock); + if (buf) + free_buf(buf, true); + } while (buf); + + spin_lock_irq(&port->outvq_lock); + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); /* Free pending buffers from the out-queue. */ - while ((buf = virtqueue_detach_unused_buf(port->out_vq))) - free_buf(buf, true); + do { + spin_lock_irq(&port->outvq_lock); + buf = virtqueue_detach_unused_buf(port->out_vq); + spin_unlock_irq(&port->outvq_lock); + if (buf) + free_buf(buf, true); + } while (buf); } /* @@ -1562,6 +1569,7 @@ list_del(&port->list); spin_unlock_irq(&port->portdev->ports_lock); + spin_lock_irq(&port->inbuf_lock); if (port->guest_connected) { /* Let the app know the port is going down. */ send_sigio_to_port(port); @@ -1572,6 +1580,7 @@ wake_up_interruptible(&port->waitqueue); } + spin_unlock_irq(&port->inbuf_lock); if (is_console_port(port)) { spin_lock_irq(&pdrvdata_lock); @@ -1593,9 +1602,8 @@ device_destroy(pdrvdata.class, port->dev->devt); cdev_del(port->cdev); - kfree(port->name); - debugfs_remove(port->debugfs_file); + kfree(port->name); /* * Locks around here are not necessary - a port can't be @@ -1606,7 +1614,8 @@ } /* Any private messages that the Host and Guest want to share */ -static void handle_control_message(struct ports_device *portdev, +static void handle_control_message(struct virtio_device *vdev, + struct ports_device *portdev, struct port_buffer *buf) { struct virtio_console_control *cpkt; @@ -1616,15 +1625,16 @@ cpkt = (struct virtio_console_control *)(buf->buf + buf->offset); - port = find_port_by_id(portdev, cpkt->id); - if (!port && cpkt->event != VIRTIO_CONSOLE_PORT_ADD) { + port = find_port_by_id(portdev, virtio32_to_cpu(vdev, cpkt->id)); + if (!port && + cpkt->event != cpu_to_virtio16(vdev, VIRTIO_CONSOLE_PORT_ADD)) { /* No valid header at start of buffer. Drop it. */ dev_dbg(&portdev->vdev->dev, "Invalid index %u in control packet\n", cpkt->id); return; } - switch (cpkt->event) { + switch (virtio16_to_cpu(vdev, cpkt->event)) { case VIRTIO_CONSOLE_PORT_ADD: if (port) { dev_dbg(&portdev->vdev->dev, @@ -1632,13 +1642,15 @@ send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); break; } - if (cpkt->id >= portdev->config.max_nr_ports) { + if (virtio32_to_cpu(vdev, cpkt->id) >= + portdev->config.max_nr_ports) { dev_warn(&portdev->vdev->dev, - "Request for adding port with out-of-bound id %u, max. supported id: %u\n", + "Request for adding port with " + "out-of-bound id %u, max. supported id: %u\n", cpkt->id, portdev->config.max_nr_ports - 1); break; } - add_port(portdev, cpkt->id); + add_port(portdev, virtio32_to_cpu(vdev, cpkt->id)); break; case VIRTIO_CONSOLE_PORT_REMOVE: unplug_port(port); @@ -1674,7 +1686,7 @@ break; } case VIRTIO_CONSOLE_PORT_OPEN: - port->host_connected = cpkt->value; + port->host_connected = virtio16_to_cpu(vdev, cpkt->value); wake_up_interruptible(&port->waitqueue); /* * If the host port got closed and the host had any @@ -1689,7 +1701,9 @@ * If the guest is connected, it'll be interested in * knowing the host connection state changed. */ + spin_lock_irq(&port->inbuf_lock); send_sigio_to_port(port); + spin_unlock_irq(&port->inbuf_lock); break; case VIRTIO_CONSOLE_PORT_NAME: /* @@ -1754,7 +1768,7 @@ buf->len = len; buf->offset = 0; - handle_control_message(portdev, buf); + handle_control_message(vq->vdev, portdev, buf); spin_lock(&portdev->c_ivq_lock); if (add_inbuf(portdev->c_ivq, buf) < 0) { @@ -1809,13 +1823,13 @@ if (!port->guest_connected && !is_rproc_serial(port->portdev->vdev)) discard_port_data(port); + /* Send a SIGIO indicating new data in case the process asked for it */ + send_sigio_to_port(port); + spin_unlock_irqrestore(&port->inbuf_lock, flags); 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(); } @@ -1834,16 +1848,23 @@ portdev = vdev->priv; + if (!use_multiport(portdev)) + schedule_work(&portdev->config_work); +} + +static void config_work_handler(struct work_struct *work) +{ + struct ports_device *portdev; + + portdev = container_of(work, struct ports_device, control_work); if (!use_multiport(portdev)) { + struct virtio_device *vdev; struct port *port; u16 rows, cols; - vdev->config->get(vdev, - offsetof(struct virtio_console_config, cols), - &cols, sizeof(u16)); - vdev->config->get(vdev, - offsetof(struct virtio_console_config, rows), - &rows, sizeof(u16)); + vdev = portdev->vdev; + virtio_cread(vdev, struct virtio_console_config, cols, &cols); + virtio_cread(vdev, struct virtio_console_config, rows, &rows); port = find_port_by_id(portdev, 0); set_console_size(port, rows, cols); @@ -1987,6 +2008,15 @@ bool multiport; bool early = early_put_chars != NULL; + /* We only need a config space if features are offered */ + if (!vdev->config->get && + (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE) + || virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT))) { + dev_err(&vdev->dev, "%s failure: config access disabled\n", + __func__); + return -EINVAL; + } + /* Ensure to read early_put_chars now */ barrier(); @@ -2015,10 +2045,9 @@ /* Don't test MULTIPORT at all if we're rproc: not a valid feature! */ if (!is_rproc_serial(vdev) && - virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT, - offsetof(struct virtio_console_config, - max_nr_ports), - &portdev->config.max_nr_ports) == 0) { + virtio_cread_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT, + struct virtio_console_config, max_nr_ports, + &portdev->config.max_nr_ports) == 0) { multiport = true; } @@ -2031,6 +2060,9 @@ spin_lock_init(&portdev->ports_lock); INIT_LIST_HEAD(&portdev->ports); + virtio_device_ready(portdev->vdev); + + INIT_WORK(&portdev->config_work, &config_work_handler); INIT_WORK(&portdev->control_work, &control_work_handler); if (multiport) { @@ -2105,6 +2137,8 @@ /* Finish up work that's lined up */ if (use_multiport(portdev)) cancel_work_sync(&portdev->control_work); + else + cancel_work_sync(&portdev->config_work); list_for_each_entry_safe(port, port2, &portdev->ports, list) unplug_port(port); @@ -2144,7 +2178,7 @@ static unsigned int rproc_serial_features[] = { }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int virtcons_freeze(struct virtio_device *vdev) { struct ports_device *portdev; @@ -2156,6 +2190,7 @@ virtqueue_disable_cb(portdev->c_ivq); cancel_work_sync(&portdev->control_work); + cancel_work_sync(&portdev->config_work); /* * Once more: if control_work_handler() was running, it would * enable the cb as the last step. @@ -2190,6 +2225,8 @@ if (ret) return ret; + virtio_device_ready(portdev->vdev); + if (use_multiport(portdev)) fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); @@ -2222,7 +2259,7 @@ .probe = virtcons_probe, .remove = virtcons_remove, .config_changed = config_intr, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .freeze = virtcons_freeze, .restore = virtcons_restore, #endif @@ -2250,10 +2287,8 @@ } pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL); - if (!pdrvdata.debugfs_dir) { - pr_warning("Error %ld creating debugfs dir for virtio-ports\n", - PTR_ERR(pdrvdata.debugfs_dir)); - } + if (!pdrvdata.debugfs_dir) + pr_warning("Error creating debugfs dir for virtio-ports\n"); INIT_LIST_HEAD(&pdrvdata.consoles); INIT_LIST_HEAD(&pdrvdata.portdevs); @@ -2272,8 +2307,7 @@ unregister: unregister_virtio_driver(&virtio_console); free: - if (pdrvdata.debugfs_dir) - debugfs_remove_recursive(pdrvdata.debugfs_dir); + debugfs_remove_recursive(pdrvdata.debugfs_dir); class_destroy(pdrvdata.class); return err; } @@ -2286,8 +2320,7 @@ unregister_virtio_driver(&virtio_rproc_serial); class_destroy(pdrvdata.class); - if (pdrvdata.debugfs_dir) - debugfs_remove_recursive(pdrvdata.debugfs_dir); + debugfs_remove_recursive(pdrvdata.debugfs_dir); } module_init(init); module_exit(fini);