virtio: fixups
Latest header update will break QEMU (if it's rebuilt with the new header) - and it seems that the code there is so fragile that any change in this header will break it. Add a better interface so users do not need to change their code every time that header changes. Fix virtio console for spec compliance. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> -----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJa4L3jAAoJECgfDbjSjVRpJiAIAMLVjPeMTsES6BX4duG/jhhc QmAflHg73Qmgvanbpqit/B1TRRsOsVnUGQ/4SubfQdEFZld8u/1ZNur9LKDika7h qhCM1HN9KN3O7E4IIF45i8jmsXoqBWOIb3BqBdAyeqNDWH4q48524IvYizPMgkDd ZnEZ/2pRi2HRstlwBD/JTcsfWRp/nUjarxnj8ZhUEUDFbJfjr7sPTeDwPSDShuIQ PrC9U8gliNRuxuq1v5Afn9F6mQptgvMxMLmtUqvYydlYgwu7cJUQ+Qxp8i7rNfM8 kCKkn/24UdUYHft4596bEEgDWR6nriMFCQAYKWlsCtwIvbZnURURl5TKT5ceI7Y= =N0il -----END PGP SIGNATURE----- Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost Pull virtio fixups from Michael Tsirkin: - Latest header update will break QEMU (if it's rebuilt with the new header) - and it seems that the code there is so fragile that any change in this header will break it. Add a better interface so users do not need to change their code every time that header changes. - Fix virtio console for spec compliance. * tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost: virtio_console: reset on out of memory virtio_console: move removal code virtio_console: drop custom control queue cleanup virtio_console: free buffers after reset virtio: add ability to iterate over vqs virtio_console: don't tie bufs to a vq virtio_balloon: add array of stat names
This commit is contained in:
commit
0644f186fc
|
@ -422,7 +422,7 @@ static void reclaim_dma_bufs(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size,
|
static struct port_buffer *alloc_buf(struct virtio_device *vdev, size_t buf_size,
|
||||||
int pages)
|
int pages)
|
||||||
{
|
{
|
||||||
struct port_buffer *buf;
|
struct port_buffer *buf;
|
||||||
|
@ -445,16 +445,16 @@ static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size,
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_rproc_serial(vq->vdev)) {
|
if (is_rproc_serial(vdev)) {
|
||||||
/*
|
/*
|
||||||
* Allocate DMA memory from ancestor. When a virtio
|
* Allocate DMA memory from ancestor. When a virtio
|
||||||
* device is created by remoteproc, the DMA memory is
|
* device is created by remoteproc, the DMA memory is
|
||||||
* associated with the grandparent device:
|
* associated with the grandparent device:
|
||||||
* vdev => rproc => platform-dev.
|
* vdev => rproc => platform-dev.
|
||||||
*/
|
*/
|
||||||
if (!vq->vdev->dev.parent || !vq->vdev->dev.parent->parent)
|
if (!vdev->dev.parent || !vdev->dev.parent->parent)
|
||||||
goto free_buf;
|
goto free_buf;
|
||||||
buf->dev = vq->vdev->dev.parent->parent;
|
buf->dev = vdev->dev.parent->parent;
|
||||||
|
|
||||||
/* Increase device refcnt to avoid freeing it */
|
/* Increase device refcnt to avoid freeing it */
|
||||||
get_device(buf->dev);
|
get_device(buf->dev);
|
||||||
|
@ -838,7 +838,7 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf,
|
||||||
|
|
||||||
count = min((size_t)(32 * 1024), count);
|
count = min((size_t)(32 * 1024), count);
|
||||||
|
|
||||||
buf = alloc_buf(port->out_vq, count, 0);
|
buf = alloc_buf(port->portdev->vdev, count, 0);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -957,7 +957,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error_out;
|
goto error_out;
|
||||||
|
|
||||||
buf = alloc_buf(port->out_vq, 0, pipe->nrbufs);
|
buf = alloc_buf(port->portdev->vdev, 0, pipe->nrbufs);
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto error_out;
|
goto error_out;
|
||||||
|
@ -1374,7 +1374,7 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)
|
||||||
|
|
||||||
nr_added_bufs = 0;
|
nr_added_bufs = 0;
|
||||||
do {
|
do {
|
||||||
buf = alloc_buf(vq, PAGE_SIZE, 0);
|
buf = alloc_buf(vq->vdev, PAGE_SIZE, 0);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1402,7 +1402,6 @@ static int add_port(struct ports_device *portdev, u32 id)
|
||||||
{
|
{
|
||||||
char debugfs_name[16];
|
char debugfs_name[16];
|
||||||
struct port *port;
|
struct port *port;
|
||||||
struct port_buffer *buf;
|
|
||||||
dev_t devt;
|
dev_t devt;
|
||||||
unsigned int nr_added_bufs;
|
unsigned int nr_added_bufs;
|
||||||
int err;
|
int err;
|
||||||
|
@ -1513,8 +1512,6 @@ static int add_port(struct ports_device *portdev, u32 id)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free_inbufs:
|
free_inbufs:
|
||||||
while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
|
|
||||||
free_buf(buf, true);
|
|
||||||
free_device:
|
free_device:
|
||||||
device_destroy(pdrvdata.class, port->dev->devt);
|
device_destroy(pdrvdata.class, port->dev->devt);
|
||||||
free_cdev:
|
free_cdev:
|
||||||
|
@ -1539,34 +1536,14 @@ static void remove_port(struct kref *kref)
|
||||||
|
|
||||||
static void remove_port_data(struct port *port)
|
static void remove_port_data(struct port *port)
|
||||||
{
|
{
|
||||||
struct port_buffer *buf;
|
|
||||||
|
|
||||||
spin_lock_irq(&port->inbuf_lock);
|
spin_lock_irq(&port->inbuf_lock);
|
||||||
/* Remove unused data this port might have received. */
|
/* Remove unused data this port might have received. */
|
||||||
discard_port_data(port);
|
discard_port_data(port);
|
||||||
spin_unlock_irq(&port->inbuf_lock);
|
spin_unlock_irq(&port->inbuf_lock);
|
||||||
|
|
||||||
/* Remove buffers we queued up for the Host to send us data in. */
|
|
||||||
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);
|
spin_lock_irq(&port->outvq_lock);
|
||||||
reclaim_consumed_buffers(port);
|
reclaim_consumed_buffers(port);
|
||||||
spin_unlock_irq(&port->outvq_lock);
|
spin_unlock_irq(&port->outvq_lock);
|
||||||
|
|
||||||
/* Free pending buffers from the out-queue. */
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1791,13 +1768,24 @@ static void control_work_handler(struct work_struct *work)
|
||||||
spin_unlock(&portdev->c_ivq_lock);
|
spin_unlock(&portdev->c_ivq_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void flush_bufs(struct virtqueue *vq, bool can_sleep)
|
||||||
|
{
|
||||||
|
struct port_buffer *buf;
|
||||||
|
unsigned int len;
|
||||||
|
|
||||||
|
while ((buf = virtqueue_get_buf(vq, &len)))
|
||||||
|
free_buf(buf, can_sleep);
|
||||||
|
}
|
||||||
|
|
||||||
static void out_intr(struct virtqueue *vq)
|
static void out_intr(struct virtqueue *vq)
|
||||||
{
|
{
|
||||||
struct port *port;
|
struct port *port;
|
||||||
|
|
||||||
port = find_port_by_vq(vq->vdev->priv, vq);
|
port = find_port_by_vq(vq->vdev->priv, vq);
|
||||||
if (!port)
|
if (!port) {
|
||||||
|
flush_bufs(vq, false);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
wake_up_interruptible(&port->waitqueue);
|
wake_up_interruptible(&port->waitqueue);
|
||||||
}
|
}
|
||||||
|
@ -1808,8 +1796,10 @@ static void in_intr(struct virtqueue *vq)
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
port = find_port_by_vq(vq->vdev->priv, vq);
|
port = find_port_by_vq(vq->vdev->priv, vq);
|
||||||
if (!port)
|
if (!port) {
|
||||||
|
flush_bufs(vq, false);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&port->inbuf_lock, flags);
|
spin_lock_irqsave(&port->inbuf_lock, flags);
|
||||||
port->inbuf = get_inbuf(port);
|
port->inbuf = get_inbuf(port);
|
||||||
|
@ -1984,24 +1974,54 @@ static const struct file_operations portdev_fops = {
|
||||||
|
|
||||||
static void remove_vqs(struct ports_device *portdev)
|
static void remove_vqs(struct ports_device *portdev)
|
||||||
{
|
{
|
||||||
|
struct virtqueue *vq;
|
||||||
|
|
||||||
|
virtio_device_for_each_vq(portdev->vdev, vq) {
|
||||||
|
struct port_buffer *buf;
|
||||||
|
|
||||||
|
flush_bufs(vq, true);
|
||||||
|
while ((buf = virtqueue_detach_unused_buf(vq)))
|
||||||
|
free_buf(buf, true);
|
||||||
|
}
|
||||||
portdev->vdev->config->del_vqs(portdev->vdev);
|
portdev->vdev->config->del_vqs(portdev->vdev);
|
||||||
kfree(portdev->in_vqs);
|
kfree(portdev->in_vqs);
|
||||||
kfree(portdev->out_vqs);
|
kfree(portdev->out_vqs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove_controlq_data(struct ports_device *portdev)
|
static void virtcons_remove(struct virtio_device *vdev)
|
||||||
{
|
{
|
||||||
struct port_buffer *buf;
|
struct ports_device *portdev;
|
||||||
unsigned int len;
|
struct port *port, *port2;
|
||||||
|
|
||||||
if (!use_multiport(portdev))
|
portdev = vdev->priv;
|
||||||
return;
|
|
||||||
|
|
||||||
while ((buf = virtqueue_get_buf(portdev->c_ivq, &len)))
|
spin_lock_irq(&pdrvdata_lock);
|
||||||
free_buf(buf, true);
|
list_del(&portdev->list);
|
||||||
|
spin_unlock_irq(&pdrvdata_lock);
|
||||||
|
|
||||||
while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq)))
|
/* Disable interrupts for vqs */
|
||||||
free_buf(buf, true);
|
vdev->config->reset(vdev);
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
unregister_chrdev(portdev->chr_major, "virtio-portsdev");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When yanking out a device, we immediately lose the
|
||||||
|
* (device-side) queues. So there's no point in keeping the
|
||||||
|
* guest side around till we drop our final reference. This
|
||||||
|
* also means that any ports which are in an open state will
|
||||||
|
* have to just stop using the port, as the vqs are going
|
||||||
|
* away.
|
||||||
|
*/
|
||||||
|
remove_vqs(portdev);
|
||||||
|
kfree(portdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2070,6 +2090,7 @@ static int virtcons_probe(struct virtio_device *vdev)
|
||||||
|
|
||||||
spin_lock_init(&portdev->ports_lock);
|
spin_lock_init(&portdev->ports_lock);
|
||||||
INIT_LIST_HEAD(&portdev->ports);
|
INIT_LIST_HEAD(&portdev->ports);
|
||||||
|
INIT_LIST_HEAD(&portdev->list);
|
||||||
|
|
||||||
virtio_device_ready(portdev->vdev);
|
virtio_device_ready(portdev->vdev);
|
||||||
|
|
||||||
|
@ -2087,8 +2108,15 @@ static int virtcons_probe(struct virtio_device *vdev)
|
||||||
if (!nr_added_bufs) {
|
if (!nr_added_bufs) {
|
||||||
dev_err(&vdev->dev,
|
dev_err(&vdev->dev,
|
||||||
"Error allocating buffers for control queue\n");
|
"Error allocating buffers for control queue\n");
|
||||||
err = -ENOMEM;
|
/*
|
||||||
goto free_vqs;
|
* The host might want to notify mgmt sw about device
|
||||||
|
* add failure.
|
||||||
|
*/
|
||||||
|
__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
|
||||||
|
VIRTIO_CONSOLE_DEVICE_READY, 0);
|
||||||
|
/* Device was functional: we need full cleanup. */
|
||||||
|
virtcons_remove(vdev);
|
||||||
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
|
@ -2119,11 +2147,6 @@ static int virtcons_probe(struct virtio_device *vdev)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free_vqs:
|
|
||||||
/* The host might want to notify mgmt sw about device add failure */
|
|
||||||
__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
|
|
||||||
VIRTIO_CONSOLE_DEVICE_READY, 0);
|
|
||||||
remove_vqs(portdev);
|
|
||||||
free_chrdev:
|
free_chrdev:
|
||||||
unregister_chrdev(portdev->chr_major, "virtio-portsdev");
|
unregister_chrdev(portdev->chr_major, "virtio-portsdev");
|
||||||
free:
|
free:
|
||||||
|
@ -2132,43 +2155,6 @@ fail:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtcons_remove(struct virtio_device *vdev)
|
|
||||||
{
|
|
||||||
struct ports_device *portdev;
|
|
||||||
struct port *port, *port2;
|
|
||||||
|
|
||||||
portdev = vdev->priv;
|
|
||||||
|
|
||||||
spin_lock_irq(&pdrvdata_lock);
|
|
||||||
list_del(&portdev->list);
|
|
||||||
spin_unlock_irq(&pdrvdata_lock);
|
|
||||||
|
|
||||||
/* Disable interrupts for vqs */
|
|
||||||
vdev->config->reset(vdev);
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
unregister_chrdev(portdev->chr_major, "virtio-portsdev");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When yanking out a device, we immediately lose the
|
|
||||||
* (device-side) queues. So there's no point in keeping the
|
|
||||||
* guest side around till we drop our final reference. This
|
|
||||||
* also means that any ports which are in an open state will
|
|
||||||
* have to just stop using the port, as the vqs are going
|
|
||||||
* away.
|
|
||||||
*/
|
|
||||||
remove_controlq_data(portdev);
|
|
||||||
remove_vqs(portdev);
|
|
||||||
kfree(portdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct virtio_device_id id_table[] = {
|
static struct virtio_device_id id_table[] = {
|
||||||
{ VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID },
|
{ VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID },
|
||||||
{ 0 },
|
{ 0 },
|
||||||
|
@ -2209,7 +2195,6 @@ static int virtcons_freeze(struct virtio_device *vdev)
|
||||||
*/
|
*/
|
||||||
if (use_multiport(portdev))
|
if (use_multiport(portdev))
|
||||||
virtqueue_disable_cb(portdev->c_ivq);
|
virtqueue_disable_cb(portdev->c_ivq);
|
||||||
remove_controlq_data(portdev);
|
|
||||||
|
|
||||||
list_for_each_entry(port, &portdev->ports, list) {
|
list_for_each_entry(port, &portdev->ports, list) {
|
||||||
virtqueue_disable_cb(port->in_vq);
|
virtqueue_disable_cb(port->in_vq);
|
||||||
|
|
|
@ -157,6 +157,9 @@ int virtio_device_freeze(struct virtio_device *dev);
|
||||||
int virtio_device_restore(struct virtio_device *dev);
|
int virtio_device_restore(struct virtio_device *dev);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define virtio_device_for_each_vq(vdev, vq) \
|
||||||
|
list_for_each_entry(vq, &vdev->vqs, list)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* virtio_driver - operations for a virtio I/O driver
|
* virtio_driver - operations for a virtio I/O driver
|
||||||
* @driver: underlying device driver (populate name and owner).
|
* @driver: underlying device driver (populate name and owner).
|
||||||
|
|
|
@ -57,6 +57,21 @@ struct virtio_balloon_config {
|
||||||
#define VIRTIO_BALLOON_S_HTLB_PGFAIL 9 /* Hugetlb page allocation failures */
|
#define VIRTIO_BALLOON_S_HTLB_PGFAIL 9 /* Hugetlb page allocation failures */
|
||||||
#define VIRTIO_BALLOON_S_NR 10
|
#define VIRTIO_BALLOON_S_NR 10
|
||||||
|
|
||||||
|
#define VIRTIO_BALLOON_S_NAMES_WITH_PREFIX(VIRTIO_BALLOON_S_NAMES_prefix) { \
|
||||||
|
VIRTIO_BALLOON_S_NAMES_prefix "swap-in", \
|
||||||
|
VIRTIO_BALLOON_S_NAMES_prefix "swap-out", \
|
||||||
|
VIRTIO_BALLOON_S_NAMES_prefix "major-faults", \
|
||||||
|
VIRTIO_BALLOON_S_NAMES_prefix "minor-faults", \
|
||||||
|
VIRTIO_BALLOON_S_NAMES_prefix "free-memory", \
|
||||||
|
VIRTIO_BALLOON_S_NAMES_prefix "total-memory", \
|
||||||
|
VIRTIO_BALLOON_S_NAMES_prefix "available-memory", \
|
||||||
|
VIRTIO_BALLOON_S_NAMES_prefix "disk-caches", \
|
||||||
|
VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-allocations", \
|
||||||
|
VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-failures" \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VIRTIO_BALLOON_S_NAMES VIRTIO_BALLOON_S_NAMES_WITH_PREFIX("")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Memory statistics structure.
|
* Memory statistics structure.
|
||||||
* Driver fills an array of these structures and passes to device.
|
* Driver fills an array of these structures and passes to device.
|
||||||
|
|
Loading…
Reference in New Issue