Skip to content

Commit 1b63704

Browse files
Sjur Brændelandrustyrussell
authored andcommitted
virtio_console: Add support for remoteproc serial
Add a simple serial connection driver called VIRTIO_ID_RPROC_SERIAL (11) for communicating with a remote processor in an asymmetric multi-processing configuration. This implementation reuses the existing virtio_console implementation, and adds support for DMA allocation of data buffers and disables use of tty console and the virtio control queue. Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com> Acked-by: Amit Shah <amit.shah@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent 276a3e9 commit 1b63704

2 files changed

Lines changed: 170 additions & 23 deletions

File tree

drivers/char/virtio_console.c

Lines changed: 169 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,12 @@
3737
#include <linux/wait.h>
3838
#include <linux/workqueue.h>
3939
#include <linux/module.h>
40+
#include <linux/dma-mapping.h>
41+
#include <linux/kconfig.h>
4042
#include "../tty/hvc/hvc_console.h"
4143

44+
#define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC)
45+
4246
/*
4347
* This is a global struct for storing common data for all the devices
4448
* this driver handles.
@@ -112,6 +116,15 @@ struct port_buffer {
112116
/* offset in the buf from which to consume data */
113117
size_t offset;
114118

119+
/* DMA address of buffer */
120+
dma_addr_t dma;
121+
122+
/* Device we got DMA memory from */
123+
struct device *dev;
124+
125+
/* List of pending dma buffers to free */
126+
struct list_head list;
127+
115128
/* If sgpages == 0 then buf is used */
116129
unsigned int sgpages;
117130

@@ -331,6 +344,11 @@ static bool is_console_port(struct port *port)
331344
return false;
332345
}
333346

347+
static bool is_rproc_serial(const struct virtio_device *vdev)
348+
{
349+
return is_rproc_enabled && vdev->id.device == VIRTIO_ID_RPROC_SERIAL;
350+
}
351+
334352
static inline bool use_multiport(struct ports_device *portdev)
335353
{
336354
/*
@@ -342,26 +360,71 @@ static inline bool use_multiport(struct ports_device *portdev)
342360
return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
343361
}
344362

345-
static void free_buf(struct port_buffer *buf)
363+
static DEFINE_SPINLOCK(dma_bufs_lock);
364+
static LIST_HEAD(pending_free_dma_bufs);
365+
366+
static void free_buf(struct port_buffer *buf, bool can_sleep)
346367
{
347368
unsigned int i;
348369

349-
kfree(buf->buf);
350370
for (i = 0; i < buf->sgpages; i++) {
351371
struct page *page = sg_page(&buf->sg[i]);
352372
if (!page)
353373
break;
354374
put_page(page);
355375
}
356376

377+
if (!buf->dev) {
378+
kfree(buf->buf);
379+
} else if (is_rproc_enabled) {
380+
unsigned long flags;
381+
382+
/* dma_free_coherent requires interrupts to be enabled. */
383+
if (!can_sleep) {
384+
/* queue up dma-buffers to be freed later */
385+
spin_lock_irqsave(&dma_bufs_lock, flags);
386+
list_add_tail(&buf->list, &pending_free_dma_bufs);
387+
spin_unlock_irqrestore(&dma_bufs_lock, flags);
388+
return;
389+
}
390+
dma_free_coherent(buf->dev, buf->size, buf->buf, buf->dma);
391+
392+
/* Release device refcnt and allow it to be freed */
393+
put_device(buf->dev);
394+
}
395+
357396
kfree(buf);
358397
}
359398

399+
static void reclaim_dma_bufs(void)
400+
{
401+
unsigned long flags;
402+
struct port_buffer *buf, *tmp;
403+
LIST_HEAD(tmp_list);
404+
405+
if (list_empty(&pending_free_dma_bufs))
406+
return;
407+
408+
/* Create a copy of the pending_free_dma_bufs while holding the lock */
409+
spin_lock_irqsave(&dma_bufs_lock, flags);
410+
list_cut_position(&tmp_list, &pending_free_dma_bufs,
411+
pending_free_dma_bufs.prev);
412+
spin_unlock_irqrestore(&dma_bufs_lock, flags);
413+
414+
/* Release the dma buffers, without irqs enabled */
415+
list_for_each_entry_safe(buf, tmp, &tmp_list, list) {
416+
list_del(&buf->list);
417+
free_buf(buf, true);
418+
}
419+
}
420+
360421
static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size,
361422
int pages)
362423
{
363424
struct port_buffer *buf;
364425

426+
reclaim_dma_bufs();
427+
365428
/*
366429
* Allocate buffer and the sg list. The sg list array is allocated
367430
* directly after the port_buffer struct.
@@ -373,11 +436,34 @@ static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size,
373436

374437
buf->sgpages = pages;
375438
if (pages > 0) {
439+
buf->dev = NULL;
376440
buf->buf = NULL;
377441
return buf;
378442
}
379443

380-
buf->buf = kmalloc(buf_size, GFP_KERNEL);
444+
if (is_rproc_serial(vq->vdev)) {
445+
/*
446+
* Allocate DMA memory from ancestor. When a virtio
447+
* device is created by remoteproc, the DMA memory is
448+
* associated with the grandparent device:
449+
* vdev => rproc => platform-dev.
450+
* The code here would have been less quirky if
451+
* DMA_MEMORY_INCLUDES_CHILDREN had been supported
452+
* in dma-coherent.c
453+
*/
454+
if (!vq->vdev->dev.parent || !vq->vdev->dev.parent->parent)
455+
goto free_buf;
456+
buf->dev = vq->vdev->dev.parent->parent;
457+
458+
/* Increase device refcnt to avoid freeing it */
459+
get_device(buf->dev);
460+
buf->buf = dma_alloc_coherent(buf->dev, buf_size, &buf->dma,
461+
GFP_KERNEL);
462+
} else {
463+
buf->dev = NULL;
464+
buf->buf = kmalloc(buf_size, GFP_KERNEL);
465+
}
466+
381467
if (!buf->buf)
382468
goto free_buf;
383469
buf->len = 0;
@@ -446,7 +532,7 @@ static void discard_port_data(struct port *port)
446532
port->stats.bytes_discarded += buf->len - buf->offset;
447533
if (add_inbuf(port->in_vq, buf) < 0) {
448534
err++;
449-
free_buf(buf);
535+
free_buf(buf, false);
450536
}
451537
port->inbuf = NULL;
452538
buf = get_inbuf(port);
@@ -518,7 +604,7 @@ static void reclaim_consumed_buffers(struct port *port)
518604
return;
519605
}
520606
while ((buf = virtqueue_get_buf(port->out_vq, &len))) {
521-
free_buf(buf);
607+
free_buf(buf, false);
522608
port->outvq_full = false;
523609
}
524610
}
@@ -765,7 +851,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
765851
goto out;
766852

767853
free_buf:
768-
free_buf(buf);
854+
free_buf(buf, true);
769855
out:
770856
return ret;
771857
}
@@ -839,6 +925,15 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
839925
.u.data = &sgl,
840926
};
841927

928+
/*
929+
* Rproc_serial does not yet support splice. To support splice
930+
* pipe_to_sg() must allocate dma-buffers and copy content from
931+
* regular pages to dma pages. And alloc_buf and free_buf must
932+
* support allocating and freeing such a list of dma-buffers.
933+
*/
934+
if (is_rproc_serial(port->out_vq->vdev))
935+
return -EINVAL;
936+
842937
ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK);
843938
if (ret < 0)
844939
return ret;
@@ -857,7 +952,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
857952
ret = __send_to_port(port, buf->sg, sgl.n, sgl.len, buf, true);
858953

859954
if (unlikely(ret <= 0))
860-
kfree(sgl.sg);
955+
free_buf(buf, true);
861956
return ret;
862957
}
863958

@@ -906,6 +1001,7 @@ static int port_fops_release(struct inode *inode, struct file *filp)
9061001
reclaim_consumed_buffers(port);
9071002
spin_unlock_irq(&port->outvq_lock);
9081003

1004+
reclaim_dma_bufs();
9091005
/*
9101006
* Locks aren't necessary here as a port can't be opened after
9111007
* unplug, and if a port isn't unplugged, a kref would already
@@ -1057,7 +1153,10 @@ static void resize_console(struct port *port)
10571153
return;
10581154

10591155
vdev = port->portdev->vdev;
1060-
if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE))
1156+
1157+
/* Don't test F_SIZE at all if we're rproc: not a valid feature! */
1158+
if (!is_rproc_serial(vdev) &&
1159+
virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE))
10611160
hvc_resize(port->cons.hvc, port->cons.ws);
10621161
}
10631162

@@ -1249,7 +1348,7 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)
12491348
ret = add_inbuf(vq, buf);
12501349
if (ret < 0) {
12511350
spin_unlock_irq(lock);
1252-
free_buf(buf);
1351+
free_buf(buf, true);
12531352
break;
12541353
}
12551354
nr_added_bufs++;
@@ -1337,10 +1436,18 @@ static int add_port(struct ports_device *portdev, u32 id)
13371436
goto free_device;
13381437
}
13391438

1340-
/*
1341-
* If we're not using multiport support, this has to be a console port
1342-
*/
1343-
if (!use_multiport(port->portdev)) {
1439+
if (is_rproc_serial(port->portdev->vdev))
1440+
/*
1441+
* For rproc_serial assume remote processor is connected.
1442+
* rproc_serial does not want the console port, only
1443+
* the generic port implementation.
1444+
*/
1445+
port->host_connected = true;
1446+
else if (!use_multiport(port->portdev)) {
1447+
/*
1448+
* If we're not using multiport support,
1449+
* this has to be a console port.
1450+
*/
13441451
err = init_port_console(port);
13451452
if (err)
13461453
goto free_inbufs;
@@ -1373,7 +1480,7 @@ static int add_port(struct ports_device *portdev, u32 id)
13731480

13741481
free_inbufs:
13751482
while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
1376-
free_buf(buf);
1483+
free_buf(buf, true);
13771484
free_device:
13781485
device_destroy(pdrvdata.class, port->dev->devt);
13791486
free_cdev:
@@ -1415,11 +1522,11 @@ static void remove_port_data(struct port *port)
14151522

14161523
/* Remove buffers we queued up for the Host to send us data in. */
14171524
while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
1418-
free_buf(buf);
1525+
free_buf(buf, true);
14191526

14201527
/* Free pending buffers from the out-queue. */
14211528
while ((buf = virtqueue_detach_unused_buf(port->out_vq)))
1422-
free_buf(buf);
1529+
free_buf(buf, true);
14231530
}
14241531

14251532
/*
@@ -1621,7 +1728,7 @@ static void control_work_handler(struct work_struct *work)
16211728
if (add_inbuf(portdev->c_ivq, buf) < 0) {
16221729
dev_warn(&portdev->vdev->dev,
16231730
"Error adding buffer to queue\n");
1624-
free_buf(buf);
1731+
free_buf(buf, false);
16251732
}
16261733
}
16271734
spin_unlock(&portdev->cvq_lock);
@@ -1817,10 +1924,10 @@ static void remove_controlq_data(struct ports_device *portdev)
18171924
return;
18181925

18191926
while ((buf = virtqueue_get_buf(portdev->c_ivq, &len)))
1820-
free_buf(buf);
1927+
free_buf(buf, true);
18211928

18221929
while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq)))
1823-
free_buf(buf);
1930+
free_buf(buf, true);
18241931
}
18251932

18261933
/*
@@ -1867,11 +1974,15 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
18671974

18681975
multiport = false;
18691976
portdev->config.max_nr_ports = 1;
1870-
if (virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
1871-
offsetof(struct virtio_console_config,
1872-
max_nr_ports),
1873-
&portdev->config.max_nr_ports) == 0)
1977+
1978+
/* Don't test MULTIPORT at all if we're rproc: not a valid feature! */
1979+
if (!is_rproc_serial(vdev) &&
1980+
virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT,
1981+
offsetof(struct virtio_console_config,
1982+
max_nr_ports),
1983+
&portdev->config.max_nr_ports) == 0) {
18741984
multiport = true;
1985+
}
18751986

18761987
err = init_vqs(portdev);
18771988
if (err < 0) {
@@ -1981,6 +2092,16 @@ static unsigned int features[] = {
19812092
VIRTIO_CONSOLE_F_MULTIPORT,
19822093
};
19832094

2095+
static struct virtio_device_id rproc_serial_id_table[] = {
2096+
#if IS_ENABLED(CONFIG_REMOTEPROC)
2097+
{ VIRTIO_ID_RPROC_SERIAL, VIRTIO_DEV_ANY_ID },
2098+
#endif
2099+
{ 0 },
2100+
};
2101+
2102+
static unsigned int rproc_serial_features[] = {
2103+
};
2104+
19842105
#ifdef CONFIG_PM
19852106
static int virtcons_freeze(struct virtio_device *vdev)
19862107
{
@@ -2065,6 +2186,20 @@ static struct virtio_driver virtio_console = {
20652186
#endif
20662187
};
20672188

2189+
/*
2190+
* virtio_rproc_serial refers to __devinit function which causes
2191+
* section mismatch warnings. So use __refdata to silence warnings.
2192+
*/
2193+
static struct virtio_driver __refdata virtio_rproc_serial = {
2194+
.feature_table = rproc_serial_features,
2195+
.feature_table_size = ARRAY_SIZE(rproc_serial_features),
2196+
.driver.name = "virtio_rproc_serial",
2197+
.driver.owner = THIS_MODULE,
2198+
.id_table = rproc_serial_id_table,
2199+
.probe = virtcons_probe,
2200+
.remove = virtcons_remove,
2201+
};
2202+
20682203
static int __init init(void)
20692204
{
20702205
int err;
@@ -2089,7 +2224,15 @@ static int __init init(void)
20892224
pr_err("Error %d registering virtio driver\n", err);
20902225
goto free;
20912226
}
2227+
err = register_virtio_driver(&virtio_rproc_serial);
2228+
if (err < 0) {
2229+
pr_err("Error %d registering virtio rproc serial driver\n",
2230+
err);
2231+
goto unregister;
2232+
}
20922233
return 0;
2234+
unregister:
2235+
unregister_virtio_driver(&virtio_console);
20932236
free:
20942237
if (pdrvdata.debugfs_dir)
20952238
debugfs_remove_recursive(pdrvdata.debugfs_dir);
@@ -2099,7 +2242,10 @@ static int __init init(void)
20992242

21002243
static void __exit fini(void)
21012244
{
2245+
reclaim_dma_bufs();
2246+
21022247
unregister_virtio_driver(&virtio_console);
2248+
unregister_virtio_driver(&virtio_rproc_serial);
21032249

21042250
class_destroy(pdrvdata.class);
21052251
if (pdrvdata.debugfs_dir)

include/uapi/linux/virtio_ids.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@
3737
#define VIRTIO_ID_RPMSG 7 /* virtio remote processor messaging */
3838
#define VIRTIO_ID_SCSI 8 /* virtio scsi */
3939
#define VIRTIO_ID_9P 9 /* 9p virtio console */
40+
#define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
4041

4142
#endif /* _LINUX_VIRTIO_IDS_H */

0 commit comments

Comments
 (0)