From: Sjur Brændeland <sjur.brandel...@stericsson.com> Add a virtio remoteproc serial driver: VIRTIO_ID_RPROC_SERIAL (0xB) for communicating with a remote processor in an asymmetric multi-processing configuration.
The virtio remoteproc serial driver reuses the existing virtio_console implementation, and adds support for DMA allocation of data buffers but disables support for tty console, mutiple ports, and the control queue. Signed-off-by: Sjur Brændeland <sjur.brandel...@stericsson.com> cc: Rusty Russell <ru...@rustcorp.com.au> cc: Michael S. Tsirkin <m...@redhat.com> cc: Amit Shah <amit.s...@redhat.com> cc: Ohad Ben-Cohen <o...@wizery.com> cc: Linus Walleij <linus.wall...@linaro.org> cc: virtualization@lists.linux-foundation.org cc: linux-ker...@vger.kernel.org --- Hi Rusty, ... >Sorry for the back and forth, I've been pondering MST's points. >If we make a new dma-multiport device (eg. ID 11), how ugly is the code? I don't think it's too bad. It's a few more code lines - as I have added a new virtio_driver definition and feature table. The rest is just replacing the check on feature bits with driver type. I no longer need to check on feature bits in the probe function, as the match on device type is done automatically by the driver framework. I actually like this new approach better. It solves the issues Michael has pointed out, and we don't have to think through side effects of weired combination of feature bits. >It would be a virtio console with DMA buffers and no console, just the >multiport stuff. This would have no impact on the current spec for >virtio console. Yes, and this way it will also be easier to explain what this new feature does. Regards, Sjur drivers/char/virtio_console.c | 133 +++++++++++++++++++++++++++++++++-------- include/linux/virtio_ids.h | 1 + 2 files changed, 109 insertions(+), 25 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index cdf2f54..08593aa 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -35,6 +35,7 @@ #include <linux/wait.h> #include <linux/workqueue.h> #include <linux/module.h> +#include <linux/dma-mapping.h> #include "../tty/hvc/hvc_console.h" /* @@ -323,6 +324,52 @@ static bool is_console_port(struct port *port) return false; } +#ifdef CONFIG_HAS_DMA +static inline bool is_rproc_serial(struct virtio_device *vdev) +{ + return vdev->id.device == VIRTIO_ID_RPROC_SERIAL; +} +#else +static inline bool is_rproc_serial(struct virtio_device *vdev) +{ + return false; +} +#endif + +/* Allocate data buffer from DMA memory if requested */ +static inline void * +alloc_databuf(struct virtio_device *vdev, size_t size, gfp_t flag) +{ + if (is_rproc_serial(vdev)) { + dma_addr_t dma; + struct device *dev = &vdev->dev; + /* + * Allocate DMA memory from ancestors. Finding the ancestor + * is a bit quirky when DMA_MEMORY_INCLUDES_CHILDREN is not + * implemented. + */ + dev = dev->parent ? dev->parent : dev; + dev = dev->parent ? dev->parent : dev; + return dma_alloc_coherent(dev, size, &dma, flag); + } + return kzalloc(size, flag); +} + +static inline void +free_databuf(struct virtio_device *vdev, size_t size, void *cpu_addr) +{ + + if (is_rproc_serial(vdev)) { + struct device *dev = &vdev->dev; + dma_addr_t dma_handle = virt_to_bus(cpu_addr); + dev = dev->parent ? dev->parent : dev; + dev = dev->parent ? dev->parent : dev; + dma_free_coherent(dev, size, cpu_addr, dma_handle); + return; + } + kfree(cpu_addr); +} + static inline bool use_multiport(struct ports_device *portdev) { /* @@ -334,20 +381,21 @@ static inline bool use_multiport(struct ports_device *portdev) return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT); } -static void free_buf(struct port_buffer *buf) +static void +free_buf(struct virtqueue *vq, struct port_buffer *buf, size_t buf_size) { - kfree(buf->buf); + free_databuf(vq->vdev, buf_size, buf->buf); kfree(buf); } -static struct port_buffer *alloc_buf(size_t buf_size) +static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size) { struct port_buffer *buf; buf = kmalloc(sizeof(*buf), GFP_KERNEL); if (!buf) goto fail; - buf->buf = kzalloc(buf_size, GFP_KERNEL); + buf->buf = alloc_databuf(vq->vdev, buf_size, GFP_KERNEL); if (!buf->buf) goto free_buf; buf->len = 0; @@ -414,7 +462,7 @@ static void discard_port_data(struct port *port) port->stats.bytes_discarded += buf->len - buf->offset; if (add_inbuf(port->in_vq, buf) < 0) { err++; - free_buf(buf); + free_buf(port->in_vq, buf, PAGE_SIZE); } port->inbuf = NULL; buf = get_inbuf(port); @@ -485,7 +533,7 @@ static void reclaim_consumed_buffers(struct port *port) return; } while ((buf = virtqueue_get_buf(port->out_vq, &len))) { - kfree(buf); + free_databuf(port->portdev->vdev, len, buf); port->outvq_full = false; } } @@ -672,6 +720,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, char *buf; ssize_t ret; bool nonblock; + struct virtio_device *vdev; /* Userspace could be out to fool us */ if (!count) @@ -694,9 +743,10 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, if (!port->guest_connected) return -ENODEV; + vdev = port->portdev->vdev; count = min((size_t)(32 * 1024), count); - buf = kmalloc(count, GFP_KERNEL); + buf = alloc_databuf(vdev, count, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -720,7 +770,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, goto out; free_buf: - kfree(buf); + free_databuf(vdev, count, buf); out: return ret; } @@ -918,7 +968,8 @@ static void resize_console(struct port *port) return; vdev = port->portdev->vdev; - if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) + if (!is_rproc_serial(vdev) && + virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) hvc_resize(port->cons.hvc, port->cons.ws); } @@ -1102,7 +1153,7 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) nr_added_bufs = 0; do { - buf = alloc_buf(PAGE_SIZE); + buf = alloc_buf(vq, PAGE_SIZE); if (!buf) break; @@ -1110,7 +1161,7 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) ret = add_inbuf(vq, buf); if (ret < 0) { spin_unlock_irq(lock); - free_buf(buf); + free_buf(vq, buf, PAGE_SIZE); break; } nr_added_bufs++; @@ -1198,10 +1249,18 @@ static int add_port(struct ports_device *portdev, u32 id) goto free_device; } - /* - * If we're not using multiport support, this has to be a console port - */ - if (!use_multiport(port->portdev)) { + if (is_rproc_serial(port->portdev->vdev)) + /* + * For rproc_serial assume remote processor is connected. + * rproc_serial does not want the console port, but + * the generic port implementation. + */ + port->host_connected = true; + else if (!use_multiport(port->portdev)) { + /* + * If we're not using multiport support, + * this has to be a console port. + */ err = init_port_console(port); if (err) goto free_inbufs; @@ -1234,7 +1293,7 @@ static int add_port(struct ports_device *portdev, u32 id) free_inbufs: while ((buf = virtqueue_detach_unused_buf(port->in_vq))) - free_buf(buf); + free_buf(port->in_vq, buf, PAGE_SIZE); free_device: device_destroy(pdrvdata.class, port->dev->devt); free_cdev: @@ -1276,7 +1335,7 @@ static void remove_port_data(struct port *port) /* 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); + free_buf(port->in_vq, buf, PAGE_SIZE); } /* @@ -1478,7 +1537,7 @@ static void control_work_handler(struct work_struct *work) if (add_inbuf(portdev->c_ivq, buf) < 0) { dev_warn(&portdev->vdev->dev, "Error adding buffer to queue\n"); - free_buf(buf); + free_buf(portdev->c_ivq, buf, PAGE_SIZE); } } spin_unlock(&portdev->cvq_lock); @@ -1674,10 +1733,10 @@ static void remove_controlq_data(struct ports_device *portdev) return; while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) - free_buf(buf); + free_buf(portdev->c_ivq, buf, PAGE_SIZE); while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) - free_buf(buf); + free_buf(portdev->c_ivq, buf, PAGE_SIZE); } /* @@ -1724,10 +1783,12 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) multiport = false; portdev->config.max_nr_ports = 1; - if (virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT, - offsetof(struct virtio_console_config, - max_nr_ports), - &portdev->config.max_nr_ports) == 0) + if (is_rproc_serial(vdev)) + multiport = false; + else if (virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT, + offsetof(struct virtio_console_config, + max_nr_ports), + &portdev->config.max_nr_ports) == 0) multiport = true; err = init_vqs(portdev); @@ -1838,6 +1899,16 @@ static unsigned int features[] = { VIRTIO_CONSOLE_F_MULTIPORT, }; +static struct virtio_device_id rproc_serial_id_table[] = { +#ifdef CONFIG_HAS_DMA + { VIRTIO_ID_RPROC_SERIAL, VIRTIO_DEV_ANY_ID }, +#endif + { 0 }, +}; + +static unsigned int rproc_serial_features[] = { +}; + #ifdef CONFIG_PM static int virtcons_freeze(struct virtio_device *vdev) { @@ -1922,6 +1993,16 @@ static struct virtio_driver virtio_console = { #endif }; +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, +}; + static int __init init(void) { int err; @@ -1941,12 +2022,14 @@ static int __init init(void) INIT_LIST_HEAD(&pdrvdata.consoles); INIT_LIST_HEAD(&pdrvdata.portdevs); - return register_virtio_driver(&virtio_console); + return register_virtio_driver(&virtio_console) && + register_virtio_driver(&virtio_rproc_serial); } static void __exit fini(void) { unregister_virtio_driver(&virtio_console); + unregister_virtio_driver(&virtio_rproc_serial); class_destroy(pdrvdata.class); if (pdrvdata.debugfs_dir) diff --git a/include/linux/virtio_ids.h b/include/linux/virtio_ids.h index 270fb22..07cf6f7 100644 --- a/include/linux/virtio_ids.h +++ b/include/linux/virtio_ids.h @@ -37,5 +37,6 @@ #define VIRTIO_ID_RPMSG 7 /* virtio remote processor messaging */ #define VIRTIO_ID_SCSI 8 /* virtio scsi */ #define VIRTIO_ID_9P 9 /* 9p virtio console */ +#define VIRTIO_ID_RPROC_SERIAL 0xB /* virtio remoteproc serial link */ #endif /* _LINUX_VIRTIO_IDS_H */ -- 1.7.5.4 _______________________________________________ Virtualization mailing list Virtualization@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/virtualization