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:
Коммит
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)
|
||||
{
|
||||
struct port_buffer *buf;
|
||||
|
@ -445,16 +445,16 @@ static struct port_buffer *alloc_buf(struct virtqueue *vq, size_t buf_size,
|
|||
return buf;
|
||||
}
|
||||
|
||||
if (is_rproc_serial(vq->vdev)) {
|
||||
if (is_rproc_serial(vdev)) {
|
||||
/*
|
||||
* Allocate DMA memory from ancestor. When a virtio
|
||||
* device is created by remoteproc, the DMA memory is
|
||||
* associated with the grandparent device:
|
||||
* 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;
|
||||
buf->dev = vq->vdev->dev.parent->parent;
|
||||
buf->dev = vdev->dev.parent->parent;
|
||||
|
||||
/* Increase device refcnt to avoid freeing it */
|
||||
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);
|
||||
|
||||
buf = alloc_buf(port->out_vq, count, 0);
|
||||
buf = alloc_buf(port->portdev->vdev, count, 0);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -957,7 +957,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe,
|
|||
if (ret < 0)
|
||||
goto error_out;
|
||||
|
||||
buf = alloc_buf(port->out_vq, 0, pipe->nrbufs);
|
||||
buf = alloc_buf(port->portdev->vdev, 0, pipe->nrbufs);
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto error_out;
|
||||
|
@ -1374,7 +1374,7 @@ static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock)
|
|||
|
||||
nr_added_bufs = 0;
|
||||
do {
|
||||
buf = alloc_buf(vq, PAGE_SIZE, 0);
|
||||
buf = alloc_buf(vq->vdev, PAGE_SIZE, 0);
|
||||
if (!buf)
|
||||
break;
|
||||
|
||||
|
@ -1402,7 +1402,6 @@ static int add_port(struct ports_device *portdev, u32 id)
|
|||
{
|
||||
char debugfs_name[16];
|
||||
struct port *port;
|
||||
struct port_buffer *buf;
|
||||
dev_t devt;
|
||||
unsigned int nr_added_bufs;
|
||||
int err;
|
||||
|
@ -1513,8 +1512,6 @@ static int add_port(struct ports_device *portdev, u32 id)
|
|||
return 0;
|
||||
|
||||
free_inbufs:
|
||||
while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
|
||||
free_buf(buf, true);
|
||||
free_device:
|
||||
device_destroy(pdrvdata.class, port->dev->devt);
|
||||
free_cdev:
|
||||
|
@ -1539,34 +1536,14 @@ static void remove_port(struct kref *kref)
|
|||
|
||||
static void remove_port_data(struct port *port)
|
||||
{
|
||||
struct port_buffer *buf;
|
||||
|
||||
spin_lock_irq(&port->inbuf_lock);
|
||||
/* Remove unused data this port might have received. */
|
||||
discard_port_data(port);
|
||||
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);
|
||||
reclaim_consumed_buffers(port);
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct port *port;
|
||||
|
||||
port = find_port_by_vq(vq->vdev->priv, vq);
|
||||
if (!port)
|
||||
if (!port) {
|
||||
flush_bufs(vq, false);
|
||||
return;
|
||||
}
|
||||
|
||||
wake_up_interruptible(&port->waitqueue);
|
||||
}
|
||||
|
@ -1808,8 +1796,10 @@ static void in_intr(struct virtqueue *vq)
|
|||
unsigned long flags;
|
||||
|
||||
port = find_port_by_vq(vq->vdev->priv, vq);
|
||||
if (!port)
|
||||
if (!port) {
|
||||
flush_bufs(vq, false);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&port->inbuf_lock, flags);
|
||||
port->inbuf = get_inbuf(port);
|
||||
|
@ -1984,24 +1974,54 @@ static const struct file_operations portdev_fops = {
|
|||
|
||||
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);
|
||||
kfree(portdev->in_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;
|
||||
unsigned int len;
|
||||
struct ports_device *portdev;
|
||||
struct port *port, *port2;
|
||||
|
||||
if (!use_multiport(portdev))
|
||||
return;
|
||||
portdev = vdev->priv;
|
||||
|
||||
while ((buf = virtqueue_get_buf(portdev->c_ivq, &len)))
|
||||
free_buf(buf, true);
|
||||
spin_lock_irq(&pdrvdata_lock);
|
||||
list_del(&portdev->list);
|
||||
spin_unlock_irq(&pdrvdata_lock);
|
||||
|
||||
while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq)))
|
||||
free_buf(buf, true);
|
||||
/* 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_vqs(portdev);
|
||||
kfree(portdev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2070,6 +2090,7 @@ static int virtcons_probe(struct virtio_device *vdev)
|
|||
|
||||
spin_lock_init(&portdev->ports_lock);
|
||||
INIT_LIST_HEAD(&portdev->ports);
|
||||
INIT_LIST_HEAD(&portdev->list);
|
||||
|
||||
virtio_device_ready(portdev->vdev);
|
||||
|
||||
|
@ -2087,8 +2108,15 @@ static int virtcons_probe(struct virtio_device *vdev)
|
|||
if (!nr_added_bufs) {
|
||||
dev_err(&vdev->dev,
|
||||
"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 {
|
||||
/*
|
||||
|
@ -2119,11 +2147,6 @@ static int virtcons_probe(struct virtio_device *vdev)
|
|||
|
||||
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:
|
||||
unregister_chrdev(portdev->chr_major, "virtio-portsdev");
|
||||
free:
|
||||
|
@ -2132,43 +2155,6 @@ fail:
|
|||
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[] = {
|
||||
{ VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID },
|
||||
{ 0 },
|
||||
|
@ -2209,7 +2195,6 @@ static int virtcons_freeze(struct virtio_device *vdev)
|
|||
*/
|
||||
if (use_multiport(portdev))
|
||||
virtqueue_disable_cb(portdev->c_ivq);
|
||||
remove_controlq_data(portdev);
|
||||
|
||||
list_for_each_entry(port, &portdev->ports, list) {
|
||||
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);
|
||||
#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
|
||||
* @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_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.
|
||||
* Driver fills an array of these structures and passes to device.
|
||||
|
|
Загрузка…
Ссылка в новой задаче