Skip to content

Commit d791cfc

Browse files
viviermstsirkin
authored andcommitted
virtio_console: allocate inbufs in add_port() only if it is needed
When we hot unplug a virtserialport and then try to hot plug again, it fails: (qemu) chardev-add socket,id=serial0,path=/tmp/serial0,server,nowait (qemu) device_add virtserialport,bus=virtio-serial0.0,nr=2,\ chardev=serial0,id=serial0,name=serial0 (qemu) device_del serial0 (qemu) device_add virtserialport,bus=virtio-serial0.0,nr=2,\ chardev=serial0,id=serial0,name=serial0 kernel error: virtio-ports vport2p2: Error allocating inbufs qemu error: virtio-serial-bus: Guest failure in adding port 2 for device \ virtio-serial0.0 This happens because buffers for the in_vq are allocated when the port is added but are not released when the port is unplugged. They are only released when virtconsole is removed (see a7a69ec) To avoid the problem and to be symmetric, we could allocate all the buffers in init_vqs() as they are released in remove_vqs(), but it sounds like a waste of memory. Rather than that, this patch changes add_port() logic to ignore ENOSPC error in fill_queue(), which means queue has already been filled. Fixes: a7a69ec ("virtio_console: free buffers after reset") Cc: [email protected] Cc: [email protected] Signed-off-by: Laurent Vivier <[email protected]> Signed-off-by: Michael S. Tsirkin <[email protected]>
1 parent f772800 commit d791cfc

File tree

1 file changed

+13
-15
lines changed

1 file changed

+13
-15
lines changed

drivers/char/virtio_console.c

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,24 +1325,24 @@ static void set_console_size(struct port *port, u16 rows, u16 cols)
13251325
port->cons.ws.ws_col = cols;
13261326
}
13271327

1328-
static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)
1328+
static int fill_queue(struct virtqueue *vq, spinlock_t *lock)
13291329
{
13301330
struct port_buffer *buf;
1331-
unsigned int nr_added_bufs;
1331+
int nr_added_bufs;
13321332
int ret;
13331333

13341334
nr_added_bufs = 0;
13351335
do {
13361336
buf = alloc_buf(vq->vdev, PAGE_SIZE, 0);
13371337
if (!buf)
1338-
break;
1338+
return -ENOMEM;
13391339

13401340
spin_lock_irq(lock);
13411341
ret = add_inbuf(vq, buf);
13421342
if (ret < 0) {
13431343
spin_unlock_irq(lock);
13441344
free_buf(buf, true);
1345-
break;
1345+
return ret;
13461346
}
13471347
nr_added_bufs++;
13481348
spin_unlock_irq(lock);
@@ -1362,7 +1362,6 @@ static int add_port(struct ports_device *portdev, u32 id)
13621362
char debugfs_name[16];
13631363
struct port *port;
13641364
dev_t devt;
1365-
unsigned int nr_added_bufs;
13661365
int err;
13671366

13681367
port = kmalloc(sizeof(*port), GFP_KERNEL);
@@ -1421,11 +1420,13 @@ static int add_port(struct ports_device *portdev, u32 id)
14211420
spin_lock_init(&port->outvq_lock);
14221421
init_waitqueue_head(&port->waitqueue);
14231422

1424-
/* Fill the in_vq with buffers so the host can send us data. */
1425-
nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock);
1426-
if (!nr_added_bufs) {
1423+
/* We can safely ignore ENOSPC because it means
1424+
* the queue already has buffers. Buffers are removed
1425+
* only by virtcons_remove(), not by unplug_port()
1426+
*/
1427+
err = fill_queue(port->in_vq, &port->inbuf_lock);
1428+
if (err < 0 && err != -ENOSPC) {
14271429
dev_err(port->dev, "Error allocating inbufs\n");
1428-
err = -ENOMEM;
14291430
goto free_device;
14301431
}
14311432

@@ -2059,14 +2060,11 @@ static int virtcons_probe(struct virtio_device *vdev)
20592060
INIT_WORK(&portdev->control_work, &control_work_handler);
20602061

20612062
if (multiport) {
2062-
unsigned int nr_added_bufs;
2063-
20642063
spin_lock_init(&portdev->c_ivq_lock);
20652064
spin_lock_init(&portdev->c_ovq_lock);
20662065

2067-
nr_added_bufs = fill_queue(portdev->c_ivq,
2068-
&portdev->c_ivq_lock);
2069-
if (!nr_added_bufs) {
2066+
err = fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
2067+
if (err < 0) {
20702068
dev_err(&vdev->dev,
20712069
"Error allocating buffers for control queue\n");
20722070
/*
@@ -2077,7 +2075,7 @@ static int virtcons_probe(struct virtio_device *vdev)
20772075
VIRTIO_CONSOLE_DEVICE_READY, 0);
20782076
/* Device was functional: we need full cleanup. */
20792077
virtcons_remove(vdev);
2080-
return -ENOMEM;
2078+
return err;
20812079
}
20822080
} else {
20832081
/*

0 commit comments

Comments
 (0)