Commit bcf8a3df authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'to-linus' of git://github.com/rustyrussell/linux

* tag 'to-linus' of git://github.com/rustyrussell/linux: (24 commits)
  lguest: Make sure interrupt is allocated ok by lguest_setup_irq
  lguest: move the lguest tool to the tools directory
  lguest: switch segment-voodoo-numbers to readable symbols
  virtio: balloon: Add freeze, restore handlers to support S4
  virtio: balloon: Move vq initialization into separate function
  virtio: net: Add freeze, restore handlers to support S4
  virtio: net: Move vq and vq buf removal into separate function
  virtio: net: Move vq initialization into separate function
  virtio: blk: Add freeze, restore handlers to support S4
  virtio: blk: Move vq initialization to separate function
  virtio: console: Disable callbacks for virtqueues at start of S4 freeze
  virtio: console: Add freeze and restore handlers to support S4
  virtio: console: Move vq and vq buf removal into separate functions
  virtio: pci: add PM notification handlers for restore, freeze, thaw, poweroff
  virtio: pci: switch to new PM API
  virtio_blk: fix config handler race
  virtio: add debugging if driver doesn't kick.
  virtio: expose added descriptors immediately.
  virtio: avoid modulus operation.
  virtio: support unlocked queue kick
  ...
parents 61bd5e56 b6c96c02
...@@ -856,18 +856,23 @@ static void __init lguest_init_IRQ(void) ...@@ -856,18 +856,23 @@ static void __init lguest_init_IRQ(void)
} }
/* /*
* With CONFIG_SPARSE_IRQ, interrupt descriptors are allocated as-needed, so * Interrupt descriptors are allocated as-needed, but low-numbered ones are
* rather than set them in lguest_init_IRQ we are called here every time an * reserved by the generic x86 code. So we ignore irq_alloc_desc_at if it
* lguest device needs an interrupt. * tells us the irq is already used: other errors (ie. ENOMEM) we take
* * seriously.
* FIXME: irq_alloc_desc_at() can fail due to lack of memory, we should
* pass that up!
*/ */
void lguest_setup_irq(unsigned int irq) int lguest_setup_irq(unsigned int irq)
{ {
irq_alloc_desc_at(irq, 0); int err;
/* Returns -ve error or vector number. */
err = irq_alloc_desc_at(irq, 0);
if (err < 0 && err != -EEXIST)
return err;
irq_set_chip_and_handler_name(irq, &lguest_irq_controller, irq_set_chip_and_handler_name(irq, &lguest_irq_controller,
handle_level_irq, "level"); handle_level_irq, "level");
return 0;
} }
/* /*
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/hdreg.h> #include <linux/hdreg.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/virtio.h> #include <linux/virtio.h>
#include <linux/virtio_blk.h> #include <linux/virtio_blk.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
...@@ -36,6 +37,12 @@ struct virtio_blk ...@@ -36,6 +37,12 @@ struct virtio_blk
/* Process context for config space updates */ /* Process context for config space updates */
struct work_struct config_work; struct work_struct config_work;
/* Lock for config space updates */
struct mutex config_lock;
/* enable config space updates */
bool config_enable;
/* What host tells us, plus 2 for header & tailer. */ /* What host tells us, plus 2 for header & tailer. */
unsigned int sg_elems; unsigned int sg_elems;
...@@ -172,7 +179,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, ...@@ -172,7 +179,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
} }
} }
if (virtqueue_add_buf(vblk->vq, vblk->sg, out, in, vbr) < 0) { if (virtqueue_add_buf(vblk->vq, vblk->sg, out, in, vbr, GFP_ATOMIC)<0) {
mempool_free(vbr, vblk->pool); mempool_free(vbr, vblk->pool);
return false; return false;
} }
...@@ -318,6 +325,10 @@ static void virtblk_config_changed_work(struct work_struct *work) ...@@ -318,6 +325,10 @@ static void virtblk_config_changed_work(struct work_struct *work)
char cap_str_2[10], cap_str_10[10]; char cap_str_2[10], cap_str_10[10];
u64 capacity, size; u64 capacity, size;
mutex_lock(&vblk->config_lock);
if (!vblk->config_enable)
goto done;
/* Host must always specify the capacity. */ /* Host must always specify the capacity. */
vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity), vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity),
&capacity, sizeof(capacity)); &capacity, sizeof(capacity));
...@@ -340,6 +351,8 @@ static void virtblk_config_changed_work(struct work_struct *work) ...@@ -340,6 +351,8 @@ static void virtblk_config_changed_work(struct work_struct *work)
cap_str_10, cap_str_2); cap_str_10, cap_str_2);
set_capacity(vblk->disk, capacity); set_capacity(vblk->disk, capacity);
done:
mutex_unlock(&vblk->config_lock);
} }
static void virtblk_config_changed(struct virtio_device *vdev) static void virtblk_config_changed(struct virtio_device *vdev)
...@@ -349,6 +362,18 @@ static void virtblk_config_changed(struct virtio_device *vdev) ...@@ -349,6 +362,18 @@ static void virtblk_config_changed(struct virtio_device *vdev)
queue_work(virtblk_wq, &vblk->config_work); queue_work(virtblk_wq, &vblk->config_work);
} }
static int init_vq(struct virtio_blk *vblk)
{
int err = 0;
/* We expect one virtqueue, for output. */
vblk->vq = virtio_find_single_vq(vblk->vdev, blk_done, "requests");
if (IS_ERR(vblk->vq))
err = PTR_ERR(vblk->vq);
return err;
}
static int __devinit virtblk_probe(struct virtio_device *vdev) static int __devinit virtblk_probe(struct virtio_device *vdev)
{ {
struct virtio_blk *vblk; struct virtio_blk *vblk;
...@@ -388,14 +413,13 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) ...@@ -388,14 +413,13 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
vblk->vdev = vdev; vblk->vdev = vdev;
vblk->sg_elems = sg_elems; vblk->sg_elems = sg_elems;
sg_init_table(vblk->sg, vblk->sg_elems); sg_init_table(vblk->sg, vblk->sg_elems);
mutex_init(&vblk->config_lock);
INIT_WORK(&vblk->config_work, virtblk_config_changed_work); INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
vblk->config_enable = true;
/* We expect one virtqueue, for output. */ err = init_vq(vblk);
vblk->vq = virtio_find_single_vq(vdev, blk_done, "requests"); if (err)
if (IS_ERR(vblk->vq)) {
err = PTR_ERR(vblk->vq);
goto out_free_vblk; goto out_free_vblk;
}
vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req)); vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req));
if (!vblk->pool) { if (!vblk->pool) {
...@@ -542,7 +566,10 @@ static void __devexit virtblk_remove(struct virtio_device *vdev) ...@@ -542,7 +566,10 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
struct virtio_blk *vblk = vdev->priv; struct virtio_blk *vblk = vdev->priv;
int index = vblk->index; int index = vblk->index;
flush_work(&vblk->config_work); /* Prevent config work handler from accessing the device. */
mutex_lock(&vblk->config_lock);
vblk->config_enable = false;
mutex_unlock(&vblk->config_lock);
/* Nothing should be pending. */ /* Nothing should be pending. */
BUG_ON(!list_empty(&vblk->reqs)); BUG_ON(!list_empty(&vblk->reqs));
...@@ -550,6 +577,8 @@ static void __devexit virtblk_remove(struct virtio_device *vdev) ...@@ -550,6 +577,8 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
/* Stop all the virtqueues. */ /* Stop all the virtqueues. */
vdev->config->reset(vdev); vdev->config->reset(vdev);
flush_work(&vblk->config_work);
del_gendisk(vblk->disk); del_gendisk(vblk->disk);
blk_cleanup_queue(vblk->disk->queue); blk_cleanup_queue(vblk->disk->queue);
put_disk(vblk->disk); put_disk(vblk->disk);
...@@ -559,6 +588,46 @@ static void __devexit virtblk_remove(struct virtio_device *vdev) ...@@ -559,6 +588,46 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
ida_simple_remove(&vd_index_ida, index); ida_simple_remove(&vd_index_ida, index);
} }
#ifdef CONFIG_PM
static int virtblk_freeze(struct virtio_device *vdev)
{
struct virtio_blk *vblk = vdev->priv;
/* Ensure we don't receive any more interrupts */
vdev->config->reset(vdev);
/* Prevent config work handler from accessing the device. */
mutex_lock(&vblk->config_lock);
vblk->config_enable = false;
mutex_unlock(&vblk->config_lock);
flush_work(&vblk->config_work);
spin_lock_irq(vblk->disk->queue->queue_lock);
blk_stop_queue(vblk->disk->queue);
spin_unlock_irq(vblk->disk->queue->queue_lock);
blk_sync_queue(vblk->disk->queue);
vdev->config->del_vqs(vdev);
return 0;
}
static int virtblk_restore(struct virtio_device *vdev)
{
struct virtio_blk *vblk = vdev->priv;
int ret;
vblk->config_enable = true;
ret = init_vq(vdev->priv);
if (!ret) {
spin_lock_irq(vblk->disk->queue->queue_lock);
blk_start_queue(vblk->disk->queue);
spin_unlock_irq(vblk->disk->queue->queue_lock);
}
return ret;
}
#endif
static const struct virtio_device_id id_table[] = { static const struct virtio_device_id id_table[] = {
{ VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID }, { VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
{ 0 }, { 0 },
...@@ -584,6 +653,10 @@ static struct virtio_driver __refdata virtio_blk = { ...@@ -584,6 +653,10 @@ static struct virtio_driver __refdata virtio_blk = {
.probe = virtblk_probe, .probe = virtblk_probe,
.remove = __devexit_p(virtblk_remove), .remove = __devexit_p(virtblk_remove),
.config_changed = virtblk_config_changed, .config_changed = virtblk_config_changed,
#ifdef CONFIG_PM
.freeze = virtblk_freeze,
.restore = virtblk_restore,
#endif
}; };
static int __init init(void) static int __init init(void)
......
...@@ -47,7 +47,7 @@ static void register_buffer(u8 *buf, size_t size) ...@@ -47,7 +47,7 @@ static void register_buffer(u8 *buf, size_t size)
sg_init_one(&sg, buf, size); sg_init_one(&sg, buf, size);
/* There should always be room for one buffer. */ /* There should always be room for one buffer. */
if (virtqueue_add_buf(vq, &sg, 0, 1, buf) < 0) if (virtqueue_add_buf(vq, &sg, 0, 1, buf, GFP_KERNEL) < 0)
BUG(); BUG();
virtqueue_kick(vq); virtqueue_kick(vq);
......
...@@ -392,7 +392,7 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) ...@@ -392,7 +392,7 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf)
sg_init_one(sg, buf->buf, buf->size); sg_init_one(sg, buf->buf, buf->size);
ret = virtqueue_add_buf(vq, sg, 0, 1, buf); ret = virtqueue_add_buf(vq, sg, 0, 1, buf, GFP_ATOMIC);
virtqueue_kick(vq); virtqueue_kick(vq);
return ret; return ret;
} }
...@@ -457,7 +457,7 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id, ...@@ -457,7 +457,7 @@ static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id,
vq = portdev->c_ovq; vq = portdev->c_ovq;
sg_init_one(sg, &cpkt, sizeof(cpkt)); sg_init_one(sg, &cpkt, sizeof(cpkt));
if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt) >= 0) { if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt, GFP_ATOMIC) >= 0) {
virtqueue_kick(vq); virtqueue_kick(vq);
while (!virtqueue_get_buf(vq, &len)) while (!virtqueue_get_buf(vq, &len))
cpu_relax(); cpu_relax();
...@@ -506,7 +506,7 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, ...@@ -506,7 +506,7 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count,
reclaim_consumed_buffers(port); reclaim_consumed_buffers(port);
sg_init_one(sg, in_buf, in_count); sg_init_one(sg, in_buf, in_count);
ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf); ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf, GFP_ATOMIC);
/* Tell Host to go! */ /* Tell Host to go! */
virtqueue_kick(out_vq); virtqueue_kick(out_vq);
...@@ -1271,6 +1271,20 @@ static void remove_port(struct kref *kref) ...@@ -1271,6 +1271,20 @@ static void remove_port(struct kref *kref)
kfree(port); kfree(port);
} }
static void remove_port_data(struct port *port)
{
struct port_buffer *buf;
/* Remove unused data this port might have received. */
discard_port_data(port);
reclaim_consumed_buffers(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);
}
/* /*
* Port got unplugged. Remove port from portdev's list and drop the * Port got unplugged. Remove port from portdev's list and drop the
* kref reference. If no userspace has this port opened, it will * kref reference. If no userspace has this port opened, it will
...@@ -1278,8 +1292,6 @@ static void remove_port(struct kref *kref) ...@@ -1278,8 +1292,6 @@ static void remove_port(struct kref *kref)
*/ */
static void unplug_port(struct port *port) static void unplug_port(struct port *port)
{ {
struct port_buffer *buf;
spin_lock_irq(&port->portdev->ports_lock); spin_lock_irq(&port->portdev->ports_lock);
list_del(&port->list); list_del(&port->list);
spin_unlock_irq(&port->portdev->ports_lock); spin_unlock_irq(&port->portdev->ports_lock);
...@@ -1300,14 +1312,7 @@ static void unplug_port(struct port *port) ...@@ -1300,14 +1312,7 @@ static void unplug_port(struct port *port)
hvc_remove(port->cons.hvc); hvc_remove(port->cons.hvc);
} }
/* Remove unused data this port might have received. */ remove_port_data(port);
discard_port_data(port);
reclaim_consumed_buffers(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);
/* /*
* We should just assume the device itself has gone off -- * We should just assume the device itself has gone off --
...@@ -1659,6 +1664,28 @@ static const struct file_operations portdev_fops = { ...@@ -1659,6 +1664,28 @@ static const struct file_operations portdev_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static void remove_vqs(struct ports_device *portdev)
{
portdev->vdev->config->del_vqs(portdev->vdev);
kfree(portdev->in_vqs);
kfree(portdev->out_vqs);
}
static void remove_controlq_data(struct ports_device *portdev)
{
struct port_buffer *buf;
unsigned int len;
if (!use_multiport(portdev))
return;
while ((buf = virtqueue_get_buf(portdev->c_ivq, &len)))
free_buf(buf);
while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq)))
free_buf(buf);
}
/* /*
* Once we're further in boot, we get probed like any other virtio * Once we're further in boot, we get probed like any other virtio
* device. * device.
...@@ -1764,9 +1791,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) ...@@ -1764,9 +1791,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev)
/* The host might want to notify mgmt sw about device add failure */ /* The host might want to notify mgmt sw about device add failure */
__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
VIRTIO_CONSOLE_DEVICE_READY, 0); VIRTIO_CONSOLE_DEVICE_READY, 0);
vdev->config->del_vqs(vdev); remove_vqs(portdev);
kfree(portdev->in_vqs);
kfree(portdev->out_vqs);
free_chrdev: free_chrdev:
unregister_chrdev(portdev->chr_major, "virtio-portsdev"); unregister_chrdev(portdev->chr_major, "virtio-portsdev");
free: free:
...@@ -1804,21 +1829,8 @@ static void virtcons_remove(struct virtio_device *vdev) ...@@ -1804,21 +1829,8 @@ static void virtcons_remove(struct virtio_device *vdev)
* have to just stop using the port, as the vqs are going * have to just stop using the port, as the vqs are going
* away. * away.
*/ */
if (use_multiport(portdev)) { remove_controlq_data(portdev);
struct port_buffer *buf; remove_vqs(portdev);
unsigned int len;
while ((buf = virtqueue_get_buf(portdev->c_ivq, &len)))
free_buf(buf);
while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq)))
free_buf(buf);
}
vdev->config->del_vqs(vdev);
kfree(portdev->in_vqs);
kfree(portdev->out_vqs);
kfree(portdev); kfree(portdev);
} }
...@@ -1832,6 +1844,68 @@ static unsigned int features[] = { ...@@ -1832,6 +1844,68 @@ static unsigned int features[] = {
VIRTIO_CONSOLE_F_MULTIPORT, VIRTIO_CONSOLE_F_MULTIPORT,
}; };
#ifdef CONFIG_PM
static int virtcons_freeze(struct virtio_device *vdev)
{
struct ports_device *portdev;
struct port *port;
portdev = vdev->priv;
vdev->config->reset(vdev);
virtqueue_disable_cb(portdev->c_ivq);
cancel_work_sync(&portdev->control_work);
/*
* Once more: if control_work_handler() was running, it would
* enable the cb as the last step.
*/
virtqueue_disable_cb(portdev->c_ivq);
remove_controlq_data(portdev);
list_for_each_entry(port, &portdev->ports, list) {
virtqueue_disable_cb(port->in_vq);
virtqueue_disable_cb(port->out_vq);
/*
* We'll ask the host later if the new invocation has
* the port opened or closed.
*/
port->host_connected = false;
remove_port_data(port);
}
remove_vqs(portdev);
return 0;
}
static int virtcons_restore(struct virtio_device *vdev)
{
struct ports_device *portdev;
struct port *port;
int ret;
portdev = vdev->priv;
ret = init_vqs(portdev);
if (ret)
return ret;
if (use_multiport(portdev))
fill_queue(portdev->c_ivq, &portdev->cvq_lock);
list_for_each_entry(port, &portdev->ports, list) {
port->in_vq = portdev->in_vqs[port->id];
port->out_vq = portdev->out_vqs[port->id];
fill_queue(port->in_vq, &port->inbuf_lock);
/* Get port open/close status on the host */
send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
}
return 0;
}
#endif
static struct virtio_driver virtio_console = { static struct virtio_driver virtio_console = {
.feature_table = features, .feature_table = features,
.feature_table_size = ARRAY_SIZE(features), .feature_table_size = ARRAY_SIZE(features),
...@@ -1841,6 +1915,10 @@ static struct virtio_driver virtio_console = { ...@@ -1841,6 +1915,10 @@ static struct virtio_driver virtio_console = {
.probe = virtcons_probe, .probe = virtcons_probe,
.remove = virtcons_remove, .remove = virtcons_remove,
.config_changed = config_intr, .config_changed = config_intr,
#ifdef CONFIG_PM
.freeze = virtcons_freeze,
.restore = virtcons_restore,
#endif
}; };
static int __init init(void) static int __init init(void)
......
...@@ -18,7 +18,7 @@ Mastery: PREFIX=M ...@@ -18,7 +18,7 @@ Mastery: PREFIX=M
Beer: Beer:
@for f in Preparation Guest Drivers Launcher Host Switcher Mastery; do echo "{==- $$f -==}"; make -s $$f; done; echo "{==-==}" @for f in Preparation Guest Drivers Launcher Host Switcher Mastery; do echo "{==- $$f -==}"; make -s $$f; done; echo "{==-==}"
Preparation Preparation! Guest Drivers Launcher Host Switcher Mastery: Preparation Preparation! Guest Drivers Launcher Host Switcher Mastery:
@sh ../../Documentation/virtual/lguest/extract $(PREFIX) `find ../../* -name '*.[chS]' -wholename '*lguest*'` @sh ../../tools/lguest/extract $(PREFIX) `find ../../* -name '*.[chS]' -wholename '*lguest*'`
Puppy: Puppy:
@clear @clear
@printf " __ \n (___()'\`;\n /, /\`\n \\\\\\\"--\\\\\\ \n" @printf " __ \n (___()'\`;\n /, /\`\n \\\\\\\"--\\\\\\ \n"
......
...@@ -241,7 +241,7 @@ static void lg_notify(struct virtqueue *vq) ...@@ -241,7 +241,7 @@ static void lg_notify(struct virtqueue *vq)
} }
/* An extern declaration inside a C file is bad form. Don't do it. */ /* An extern declaration inside a C file is bad form. Don't do it. */
extern void lguest_setup_irq(unsigned int irq); extern int lguest_setup_irq(unsigned int irq);
/* /*
* This routine finds the Nth virtqueue described in the configuration of * This routine finds the Nth virtqueue described in the configuration of
...@@ -292,17 +292,21 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev, ...@@ -292,17 +292,21 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
/* /*
* OK, tell virtio_ring.c to set up a virtqueue now we know its size * OK, tell virtio_ring.c to set up a virtqueue now we know its size
* and we've got a pointer to its pages. * and we've got a pointer to its pages. Note that we set weak_barriers
* to 'true': the host just a(nother) SMP CPU, so we only need inter-cpu
* barriers.
*/ */
vq = vring_new_virtqueue(lvq->config.num, LGUEST_VRING_ALIGN, vq = vring_new_virtqueue(lvq->config.num, LGUEST_VRING_ALIGN, vdev,
vdev, lvq->pages, lg_notify, callback, name); true, lvq->pages, lg_notify, callback, name);
if (!vq) { if (!vq) {
err = -ENOMEM; err = -ENOMEM;
goto unmap; goto unmap;
} }
/* Make sure the interrupt is allocated. */ /* Make sure the interrupt is allocated. */
lguest_setup_irq(lvq->config.irq); err = lguest_setup_irq(lvq->config.irq);
if (err)
goto destroy_vring;
/* /*
* Tell the interrupt for this virtqueue to go to the virtio_ring * Tell the interrupt for this virtqueue to go to the virtio_ring
...@@ -315,7 +319,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev, ...@@ -315,7 +319,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
err = request_irq(lvq->config.irq, vring_interrupt, IRQF_SHARED, err = request_irq(lvq->config.irq, vring_interrupt, IRQF_SHARED,
dev_name(&vdev->dev), vq); dev_name(&vdev->dev), vq);
if (err) if (err)
goto destroy_vring; goto free_desc;
/* /*
* Last of all we hook up our 'struct lguest_vq_info" to the * Last of all we hook up our 'struct lguest_vq_info" to the
...@@ -324,6 +328,8 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev, ...@@ -324,6 +328,8 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
vq->priv = lvq; vq->priv = lvq;
return vq; return vq;
free_desc:
irq_free_desc(lvq->config.irq);
destroy_vring: destroy_vring:
vring_del_virtqueue(vq); vring_del_virtqueue(vq);
unmap: unmap:
......
...@@ -81,8 +81,8 @@ static void fixup_gdt_table(struct lg_cpu *cpu, unsigned start, unsigned end) ...@@ -81,8 +81,8 @@ static void fixup_gdt_table(struct lg_cpu *cpu, unsigned start, unsigned end)
* sometimes careless and leaves this as 0, even though it's * sometimes careless and leaves this as 0, even though it's
* running at privilege level 1. If so, we fix it here. * running at privilege level 1. If so, we fix it here.
*/ */
if ((cpu->arch.gdt[i].b & 0x00006000) == 0) if (cpu->arch.gdt[i].dpl == 0)
cpu->arch.gdt[i].b |= (GUEST_PL << 13); cpu->arch.gdt[i].dpl |= GUEST_PL;
/* /*
* Each descriptor has an "accessed" bit. If we don't set it * Each descriptor has an "accessed" bit. If we don't set it
...@@ -90,7 +90,7 @@ static void fixup_gdt_table(struct lg_cpu *cpu, unsigned start, unsigned end) ...@@ -90,7 +90,7 @@ static void fixup_gdt_table(struct lg_cpu *cpu, unsigned start, unsigned end)
* that entry into a segment register. But the GDT isn't * that entry into a segment register. But the GDT isn't
* writable by the Guest, so bad things can happen. * writable by the Guest, so bad things can happen.
*/ */
cpu->arch.gdt[i].b |= 0x00000100; cpu->arch.gdt[i].type |= 0x1;
} }
} }
...@@ -114,13 +114,19 @@ void setup_default_gdt_entries(struct lguest_ro_state *state) ...@@ -114,13 +114,19 @@ void setup_default_gdt_entries(struct lguest_ro_state *state)
/* /*
* The TSS segment refers to the TSS entry for this particular CPU. * The TSS segment refers to the TSS entry for this particular CPU.
* Forgive the magic flags: the 0x8900 means the entry is Present, it's
* privilege level 0 Available 386 TSS system segment, and the 0x67
* means Saturn is eclipsed by Mercury in the twelfth house.
*/ */
gdt[GDT_ENTRY_TSS].a = 0x00000067 | (tss << 16); gdt[GDT_ENTRY_TSS].a = 0;
gdt[GDT_ENTRY_TSS].b = 0x00008900 | (tss & 0xFF000000) gdt[GDT_ENTRY_TSS].b = 0;
| ((tss >> 16) & 0x000000FF);
gdt[GDT_ENTRY_TSS].limit0 = 0x67;
gdt[GDT_ENTRY_TSS].base0 = tss & 0xFFFF;
gdt[GDT_ENTRY_TSS].base1 = (tss >> 16) & 0xFF;
gdt[GDT_ENTRY_TSS].base2 = tss >> 24;
gdt[GDT_ENTRY_TSS].type = 0x9; /* 32-bit TSS (available) */
gdt[GDT_ENTRY_TSS].p = 0x1; /* Entry is present */
gdt[GDT_ENTRY_TSS].dpl = 0x0; /* Privilege level 0 */
gdt[GDT_ENTRY_TSS].s = 0x0; /* system segment */
} }
/* /*
...@@ -135,8 +141,8 @@ void setup_guest_gdt(struct lg_cpu *cpu) ...@@ -135,8 +141,8 @@ void setup_guest_gdt(struct lg_cpu *cpu)
*/ */
cpu->arch.gdt[GDT_ENTRY_KERNEL_CS] = FULL_EXEC_SEGMENT; cpu->arch.gdt[GDT_ENTRY_KERNEL_CS] = FULL_EXEC_SEGMENT;
cpu->arch.gdt[GDT_ENTRY_KERNEL_DS] = FULL_SEGMENT; cpu->arch.gdt[GDT_ENTRY_KERNEL_DS] = FULL_SEGMENT;
cpu->arch.gdt[GDT_ENTRY_KERNEL_CS].b |= (GUEST_PL << 13); cpu->arch.gdt[GDT_ENTRY_KERNEL_CS].dpl |= GUEST_PL;
cpu->arch.gdt[GDT_ENTRY_KERNEL_DS].b |= (GUEST_PL << 13); cpu->arch.gdt[GDT_ENTRY_KERNEL_DS].dpl |= GUEST_PL;
} }
/*H:650 /*H:650
......
...@@ -370,7 +370,7 @@ static int add_recvbuf_small(struct virtnet_info *vi, gfp_t gfp) ...@@ -370,7 +370,7 @@ static int add_recvbuf_small(struct virtnet_info *vi, gfp_t gfp)
skb_to_sgvec(skb, vi->rx_sg + 1, 0, skb->len); skb_to_sgvec(skb, vi->rx_sg + 1, 0, skb->len);
err = virtqueue_add_buf_gfp(vi->rvq, vi->rx_sg, 0, 2, skb, gfp); err = virtqueue_add_buf(vi->rvq, vi->rx_sg, 0, 2, skb, gfp);
if (err < 0) if (err < 0)
dev_kfree_skb(skb); dev_kfree_skb(skb);
...@@ -415,7 +415,7 @@ static int add_recvbuf_big(struct virtnet_info *vi, gfp_t gfp) ...@@ -415,7 +415,7 @@ static int add_recvbuf_big(struct virtnet_info *vi, gfp_t gfp)
/* chain first in list head */ /* chain first in list head */
first->private = (unsigned long)list; first->private = (unsigned long)list;
err = virtqueue_add_buf_gfp(vi->rvq, vi->rx_sg, 0, MAX_SKB_FRAGS + 2, err = virtqueue_add_buf(vi->rvq, vi->rx_sg, 0, MAX_SKB_FRAGS + 2,
first, gfp); first, gfp);
if (err < 0) if (err < 0)
give_pages(vi, first); give_pages(vi, first);
...@@ -434,7 +434,7 @@ static int add_recvbuf_mergeable(struct virtnet_info *vi, gfp_t gfp) ...@@ -434,7 +434,7 @@ static int add_recvbuf_mergeable(struct virtnet_info *vi, gfp_t gfp)
sg_init_one(vi->rx_sg, page_address(page), PAGE_SIZE); sg_init_one(vi->rx_sg, page_address(page), PAGE_SIZE);
err = virtqueue_add_buf_gfp(vi->rvq, vi->rx_sg, 0, 1, page, gfp); err = virtqueue_add_buf(vi->rvq, vi->rx_sg, 0, 1, page, gfp);
if (err < 0) if (err < 0)
give_pages(vi, page); give_pages(vi, page);
...@@ -609,7 +609,7 @@ static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb) ...@@ -609,7 +609,7 @@ static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb)
hdr->num_sg = skb_to_sgvec(skb, vi->tx_sg + 1, 0, skb->len) + 1; hdr->num_sg = skb_to_sgvec(skb, vi->tx_sg + 1, 0, skb->len) + 1;
return virtqueue_add_buf(vi->svq, vi->tx_sg, hdr->num_sg, return virtqueue_add_buf(vi->svq, vi->tx_sg, hdr->num_sg,
0, skb); 0, skb, GFP_ATOMIC);
} }
static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
...@@ -767,7 +767,7 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, ...@@ -767,7 +767,7 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd,
sg_set_buf(&sg[i + 1], sg_virt(s), s->length); sg_set_buf(&sg[i + 1], sg_virt(s), s->length);
sg_set_buf(&sg[out + in - 1], &status, sizeof(status)); sg_set_buf(&sg[out + in - 1], &status, sizeof(status));
BUG_ON(virtqueue_add_buf(vi->cvq, sg, out, in, vi) < 0); BUG_ON(virtqueue_add_buf(vi->cvq, sg, out, in, vi, GFP_ATOMIC) < 0);
virtqueue_kick(vi->cvq); virtqueue_kick(vi->cvq);
...@@ -985,15 +985,38 @@ static void virtnet_config_changed(struct virtio_device *vdev) ...@@ -985,15 +985,38 @@ static void virtnet_config_changed(struct virtio_device *vdev)
virtnet_update_status(vi); virtnet_update_status(vi);
} }
static int init_vqs(struct virtnet_info *vi)
{
struct virtqueue *vqs[3];
vq_callback_t *callbacks[] = { skb_recv_done, skb_xmit_done, NULL};
const char *names[] = { "input", "output", "control" };
int nvqs, err;
/* We expect two virtqueues, receive then send,
* and optionally control. */
nvqs = virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ) ? 3 : 2;
err = vi->vdev->config->find_vqs(vi->vdev, nvqs, vqs, callbacks, names);
if (err)
return err;
vi->rvq = vqs[0];
vi->svq = vqs[1];
if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) {
vi->cvq = vqs[2];
if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VLAN))
vi->dev->features |= NETIF_F_HW_VLAN_FILTER;
}
return 0;
}
static int virtnet_probe(struct virtio_device *vdev) static int virtnet_probe(struct virtio_device *vdev)
{ {
int err; int err;
struct net_device *dev; struct net_device *dev;
struct virtnet_info *vi; struct virtnet_info *vi;
struct virtqueue *vqs[3];
vq_callback_t *callbacks[] = { skb_recv_done, skb_xmit_done, NULL};
const char *names[] = { "input", "output", "control" };
int nvqs;
/* Allocate ourselves a network device with room for our info */ /* Allocate ourselves a network device with room for our info */
dev = alloc_etherdev(sizeof(struct virtnet_info)); dev = alloc_etherdev(sizeof(struct virtnet_info));
...@@ -1065,24 +1088,10 @@ static int virtnet_probe(struct virtio_device *vdev) ...@@ -1065,24 +1088,10 @@ static int virtnet_probe(struct virtio_device *vdev)
if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
vi->mergeable_rx_bufs = true; vi->mergeable_rx_bufs = true;
/* We expect two virtqueues, receive then send, err = init_vqs(vi);
* and optionally control. */
nvqs = virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ) ? 3 : 2;
err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names);
if (err) if (err)
goto free_stats; goto free_stats;
vi->rvq = vqs[0];
vi->svq = vqs[1];
if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) {
vi->cvq = vqs[2];
if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VLAN))
dev->features |= NETIF_F_HW_VLAN_FILTER;
}
err = register_netdev(dev); err = register_netdev(dev);
if (err) { if (err) {
pr_debug("virtio_net: registering device failed\n"); pr_debug("virtio_net: registering device failed\n");
...@@ -1144,27 +1153,73 @@ static void free_unused_bufs(struct virtnet_info *vi) ...@@ -1144,27 +1153,73 @@ static void free_unused_bufs(struct virtnet_info *vi)
BUG_ON(vi->num != 0); BUG_ON(vi->num != 0);
} }
static void __devexit virtnet_remove(struct virtio_device *vdev) static void remove_vq_common(struct virtnet_info *vi)
{ {
struct virtnet_info *vi = vdev->priv; vi->vdev->config->reset(vi->vdev);
/* Stop all the virtqueues. */
vdev->config->reset(vdev);
unregister_netdev(vi->dev);
/* Free unused buffers in both send and recv, if any. */ /* Free unused buffers in both send and recv, if any. */
free_unused_bufs(vi); free_unused_bufs(vi);
vdev->config->del_vqs(vi->vdev); vi->vdev->config->del_vqs(vi->vdev);
while (vi->pages) while (vi->pages)
__free_pages(get_a_page(vi, GFP_KERNEL), 0); __free_pages(get_a_page(vi, GFP_KERNEL), 0);
}
static void __devexit virtnet_remove(struct virtio_device *vdev)
{
struct virtnet_info *vi = vdev->priv;
unregister_netdev(vi->dev);
remove_vq_common(vi);
free_percpu(vi->stats); free_percpu(vi->stats);
free_netdev(vi->dev); free_netdev(vi->dev);
} }
#ifdef CONFIG_PM
static int virtnet_freeze(struct virtio_device *vdev)
{
struct virtnet_info *vi = vdev->priv;
virtqueue_disable_cb(vi->rvq);
virtqueue_disable_cb(vi->svq);
if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ))
virtqueue_disable_cb(vi->cvq);
netif_device_detach(vi->dev);
cancel_delayed_work_sync(&vi->refill);
if (netif_running(vi->dev))
napi_disable(&vi->napi);
remove_vq_common(vi);
return 0;
}
static int virtnet_restore(struct virtio_device *vdev)
{
struct virtnet_info *vi = vdev->priv;
int err;
err = init_vqs(vi);
if (err)
return err;
if (netif_running(vi->dev))
virtnet_napi_enable(vi);
netif_device_attach(vi->dev);
if (!try_fill_recv(vi, GFP_KERNEL))
queue_delayed_work(system_nrt_wq, &vi->refill, 0);
return 0;
}
#endif
static struct virtio_device_id id_table[] = { static struct virtio_device_id id_table[] = {
{ VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID }, { VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID },
{ 0 }, { 0 },
...@@ -1189,6 +1244,10 @@ static struct virtio_driver virtio_net_driver = { ...@@ -1189,6 +1244,10 @@ static struct virtio_driver virtio_net_driver = {
.probe = virtnet_probe, .probe = virtnet_probe,
.remove = __devexit_p(virtnet_remove), .remove = __devexit_p(virtnet_remove),
.config_changed = virtnet_config_changed, .config_changed = virtnet_config_changed,
#ifdef CONFIG_PM
.freeze = virtnet_freeze,
.restore = virtnet_restore,
#endif
}; };
static int __init init(void) static int __init init(void)
......
...@@ -198,7 +198,7 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev, ...@@ -198,7 +198,7 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev,
goto out; goto out;
vq = vring_new_virtqueue(config->num, KVM_S390_VIRTIO_RING_ALIGN, vq = vring_new_virtqueue(config->num, KVM_S390_VIRTIO_RING_ALIGN,
vdev, (void *) config->address, vdev, true, (void *) config->address,
kvm_notify, callback, name); kvm_notify, callback, name);
if (!vq) { if (!vq) {
err = -ENOMEM; err = -ENOMEM;
......
/* Virtio balloon implementation, inspired by Dor Loar and Marcelo /*
* Virtio balloon implementation, inspired by Dor Laor and Marcelo
* Tosatti's implementations. * Tosatti's implementations.
* *
* Copyright 2008 Rusty Russell IBM Corporation * Copyright 2008 Rusty Russell IBM Corporation
...@@ -17,7 +18,7 @@ ...@@ -17,7 +18,7 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
//#define DEBUG
#include <linux/virtio.h> #include <linux/virtio.h>
#include <linux/virtio_balloon.h> #include <linux/virtio_balloon.h>
#include <linux/swap.h> #include <linux/swap.h>
...@@ -87,7 +88,7 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq) ...@@ -87,7 +88,7 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq)
init_completion(&vb->acked); init_completion(&vb->acked);
/* We should always be able to add one buffer to an empty queue. */ /* We should always be able to add one buffer to an empty queue. */
if (virtqueue_add_buf(vq, &sg, 1, 0, vb) < 0) if (virtqueue_add_buf(vq, &sg, 1, 0, vb, GFP_KERNEL) < 0)
BUG(); BUG();
virtqueue_kick(vq); virtqueue_kick(vq);
...@@ -149,7 +150,6 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num) ...@@ -149,7 +150,6 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num)
vb->num_pages--; vb->num_pages--;
} }
/* /*
* Note that if * Note that if
* virtio_has_feature(vdev, VIRTIO_BALLOON_F_MUST_TELL_HOST); * virtio_has_feature(vdev, VIRTIO_BALLOON_F_MUST_TELL_HOST);
...@@ -220,7 +220,7 @@ static void stats_handle_request(struct virtio_balloon *vb) ...@@ -220,7 +220,7 @@ static void stats_handle_request(struct virtio_balloon *vb)
vq = vb->stats_vq; vq = vb->stats_vq;
sg_init_one(&sg, vb->stats, sizeof(vb->stats)); sg_init_one(&sg, vb->stats, sizeof(vb->stats));
if (virtqueue_add_buf(vq, &sg, 1, 0, vb) < 0) if (virtqueue_add_buf(vq, &sg, 1, 0, vb, GFP_KERNEL) < 0)
BUG(); BUG();
virtqueue_kick(vq); virtqueue_kick(vq);
} }
...@@ -275,32 +275,21 @@ static int balloon(void *_vballoon) ...@@ -275,32 +275,21 @@ static int balloon(void *_vballoon)
return 0; return 0;
} }
static int virtballoon_probe(struct virtio_device *vdev) static int init_vqs(struct virtio_balloon *vb)
{ {
struct virtio_balloon *vb;
struct virtqueue *vqs[3]; struct virtqueue *vqs[3];
vq_callback_t *callbacks[] = { balloon_ack, balloon_ack, stats_request }; vq_callback_t *callbacks[] = { balloon_ack, balloon_ack, stats_request };
const char *names[] = { "inflate", "deflate", "stats" }; const char *names[] = { "inflate", "deflate", "stats" };
int err, nvqs; int err, nvqs;
vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL); /*
if (!vb) { * We expect two virtqueues: inflate and deflate, and
err = -ENOMEM; * optionally stat.
goto out; */
}
INIT_LIST_HEAD(&vb->pages);
vb->num_pages = 0;
init_waitqueue_head(&vb->config_change);
vb->vdev = vdev;
vb->need_stats_update = 0;
/* We expect two virtqueues: inflate and deflate,
* and optionally stat. */
nvqs = virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ) ? 3 : 2; nvqs = virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ) ? 3 : 2;
err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names); err = vb->vdev->config->find_vqs(vb->vdev, nvqs, vqs, callbacks, names);
if (err) if (err)
goto out_free_vb; return err;
vb->inflate_vq = vqs[0]; vb->inflate_vq = vqs[0];
vb->deflate_vq = vqs[1]; vb->deflate_vq = vqs[1];
...@@ -313,10 +302,34 @@ static int virtballoon_probe(struct virtio_device *vdev) ...@@ -313,10 +302,34 @@ static int virtballoon_probe(struct virtio_device *vdev)
* use it to signal us later. * use it to signal us later.
*/ */
sg_init_one(&sg, vb->stats, sizeof vb->stats); sg_init_one(&sg, vb->stats, sizeof vb->stats);
if (virtqueue_add_buf(vb->stats_vq, &sg, 1, 0, vb) < 0) if (virtqueue_add_buf(vb->stats_vq, &sg, 1, 0, vb, GFP_KERNEL)
< 0)
BUG(); BUG();
virtqueue_kick(vb->stats_vq); virtqueue_kick(vb->stats_vq);
} }
return 0;
}
static int virtballoon_probe(struct virtio_device *vdev)
{
struct virtio_balloon *vb;
int err;
vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL);
if (!vb) {
err = -ENOMEM;
goto out;
}
INIT_LIST_HEAD(&vb->pages);
vb->num_pages = 0;
init_waitqueue_head(&vb->config_change);
vb->vdev = vdev;
vb->need_stats_update = 0;
err = init_vqs(vb);
if (err)
goto out_free_vb;
vb->thread = kthread_run(balloon, vb, "vballoon"); vb->thread = kthread_run(balloon, vb, "vballoon");
if (IS_ERR(vb->thread)) { if (IS_ERR(vb->thread)) {
...@@ -351,6 +364,48 @@ static void __devexit virtballoon_remove(struct virtio_device *vdev) ...@@ -351,6 +364,48 @@ static void __devexit virtballoon_remove(struct virtio_device *vdev)
kfree(vb); kfree(vb);
} }
#ifdef CONFIG_PM
static int virtballoon_freeze(struct virtio_device *vdev)
{
/*
* The kthread is already frozen by the PM core before this
* function is called.
*/
/* Ensure we don't get any more requests from the host */
vdev->config->reset(vdev);
vdev->config->del_vqs(vdev);
return 0;
}
static int virtballoon_thaw(struct virtio_device *vdev)
{
return init_vqs(vdev->priv);
}
static int virtballoon_restore(struct virtio_device *vdev)
{
struct virtio_balloon *vb = vdev->priv;
struct page *page, *page2;
/* We're starting from a clean slate */
vb->num_pages = 0;
/*
* If a request wasn't complete at the time of freezing, this
* could have been set.
*/
vb->need_stats_update = 0;
/* We don't have these pages in the balloon anymore! */
list_for_each_entry_safe(page, page2, &vb->pages, lru) {
list_del(&page->lru);
totalram_pages++;
}
return init_vqs(vdev->priv);
}
#endif
static unsigned int features[] = { static unsigned int features[] = {
VIRTIO_BALLOON_F_MUST_TELL_HOST, VIRTIO_BALLOON_F_MUST_TELL_HOST,
VIRTIO_BALLOON_F_STATS_VQ, VIRTIO_BALLOON_F_STATS_VQ,
...@@ -365,6 +420,11 @@ static struct virtio_driver virtio_balloon_driver = { ...@@ -365,6 +420,11 @@ static struct virtio_driver virtio_balloon_driver = {
.probe = virtballoon_probe, .probe = virtballoon_probe,
.remove = __devexit_p(virtballoon_remove), .remove = __devexit_p(virtballoon_remove),
.config_changed = virtballoon_changed, .config_changed = virtballoon_changed,
#ifdef CONFIG_PM
.freeze = virtballoon_freeze,
.restore = virtballoon_restore,
.thaw = virtballoon_thaw,
#endif
}; };
static int __init init(void) static int __init init(void)
......
...@@ -310,8 +310,8 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index, ...@@ -310,8 +310,8 @@ static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index,
vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
/* Create the vring */ /* Create the vring */
vq = vring_new_virtqueue(info->num, VIRTIO_MMIO_VRING_ALIGN, vq = vring_new_virtqueue(info->num, VIRTIO_MMIO_VRING_ALIGN, vdev,
vdev, info->queue, vm_notify, callback, name); true, info->queue, vm_notify, callback, name);
if (!vq) { if (!vq) {
err = -ENOMEM; err = -ENOMEM;
goto error_new_virtqueue; goto error_new_virtqueue;
......
...@@ -55,6 +55,10 @@ struct virtio_pci_device ...@@ -55,6 +55,10 @@ struct virtio_pci_device
unsigned msix_vectors; unsigned msix_vectors;
/* Vectors allocated, excluding per-vq vectors if any */ /* Vectors allocated, excluding per-vq vectors if any */
unsigned msix_used_vectors; unsigned msix_used_vectors;
/* Status saved during hibernate/restore */
u8 saved_status;
/* Whether we have vector per vq */ /* Whether we have vector per vq */
bool per_vq_vectors; bool per_vq_vectors;
}; };
...@@ -414,8 +418,8 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index, ...@@ -414,8 +418,8 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN); vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
/* create the vring */ /* create the vring */
vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN, vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN, vdev,
vdev, info->queue, vp_notify, callback, name); true, info->queue, vp_notify, callback, name);
if (!vq) { if (!vq) {
err = -ENOMEM; err = -ENOMEM;
goto out_activate_queue; goto out_activate_queue;
...@@ -716,19 +720,114 @@ static void __devexit virtio_pci_remove(struct pci_dev *pci_dev) ...@@ -716,19 +720,114 @@ static void __devexit virtio_pci_remove(struct pci_dev *pci_dev)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int virtio_pci_suspend(struct pci_dev *pci_dev, pm_message_t state) static int virtio_pci_suspend(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev);
pci_save_state(pci_dev); pci_save_state(pci_dev);
pci_set_power_state(pci_dev, PCI_D3hot); pci_set_power_state(pci_dev, PCI_D3hot);
return 0; return 0;
} }
static int virtio_pci_resume(struct pci_dev *pci_dev) static int virtio_pci_resume(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev);
pci_restore_state(pci_dev); pci_restore_state(pci_dev);
pci_set_power_state(pci_dev, PCI_D0); pci_set_power_state(pci_dev, PCI_D0);
return 0; return 0;
} }
static int virtio_pci_freeze(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
struct virtio_driver *drv;
int ret;
drv = container_of(vp_dev->vdev.dev.driver,
struct virtio_driver, driver);
ret = 0;
vp_dev->saved_status = vp_get_status(&vp_dev->vdev);
if (drv && drv->freeze)
ret = drv->freeze(&vp_dev->vdev);
if (!ret)
pci_disable_device(pci_dev);
return ret;
}
static int restore_common(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
int ret;
ret = pci_enable_device(pci_dev);
if (ret)
return ret;
pci_set_master(pci_dev);
vp_finalize_features(&vp_dev->vdev);
return ret;
}
static int virtio_pci_thaw(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
struct virtio_driver *drv;
int ret;
ret = restore_common(dev);
if (ret)
return ret;
drv = container_of(vp_dev->vdev.dev.driver,
struct virtio_driver, driver);
if (drv && drv->thaw)
ret = drv->thaw(&vp_dev->vdev);
else if (drv && drv->restore)
ret = drv->restore(&vp_dev->vdev);
/* Finally, tell the device we're all set */
if (!ret)
vp_set_status(&vp_dev->vdev, vp_dev->saved_status);
return ret;
}
static int virtio_pci_restore(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
struct virtio_driver *drv;
int ret;
drv = container_of(vp_dev->vdev.dev.driver,
struct virtio_driver, driver);
ret = restore_common(dev);
if (!ret && drv && drv->restore)
ret = drv->restore(&vp_dev->vdev);
/* Finally, tell the device we're all set */
if (!ret)
vp_set_status(&vp_dev->vdev, vp_dev->saved_status);
return ret;
}
static const struct dev_pm_ops virtio_pci_pm_ops = {
.suspend = virtio_pci_suspend,
.resume = virtio_pci_resume,
.freeze = virtio_pci_freeze,
.thaw = virtio_pci_thaw,
.restore = virtio_pci_restore,
.poweroff = virtio_pci_suspend,
};
#endif #endif
static struct pci_driver virtio_pci_driver = { static struct pci_driver virtio_pci_driver = {
...@@ -737,8 +836,7 @@ static struct pci_driver virtio_pci_driver = { ...@@ -737,8 +836,7 @@ static struct pci_driver virtio_pci_driver = {
.probe = virtio_pci_probe, .probe = virtio_pci_probe,
.remove = __devexit_p(virtio_pci_remove), .remove = __devexit_p(virtio_pci_remove),
#ifdef CONFIG_PM #ifdef CONFIG_PM
.suspend = virtio_pci_suspend, .driver.pm = &virtio_pci_pm_ops,
.resume = virtio_pci_resume,
#endif #endif
}; };
......
This diff is collapsed.
...@@ -25,71 +25,19 @@ struct virtqueue { ...@@ -25,71 +25,19 @@ struct virtqueue {
void *priv; void *priv;
}; };
/** int virtqueue_add_buf(struct virtqueue *vq,
* operations for virtqueue
* virtqueue_add_buf: expose buffer to other end
* vq: the struct virtqueue we're talking about.
* sg: the description of the buffer(s).
* out_num: the number of sg readable by other side
* in_num: the number of sg which are writable (after readable ones)
* data: the token identifying the buffer.
* gfp: how to do memory allocations (if necessary).
* Returns remaining capacity of queue (sg segments) or a negative error.
* virtqueue_kick: update after add_buf
* vq: the struct virtqueue
* After one or more add_buf calls, invoke this to kick the other side.
* virtqueue_get_buf: get the next used buffer
* vq: the struct virtqueue we're talking about.
* len: the length written into the buffer
* Returns NULL or the "data" token handed to add_buf.
* virtqueue_disable_cb: disable callbacks
* vq: the struct virtqueue we're talking about.
* Note that this is not necessarily synchronous, hence unreliable and only
* useful as an optimization.
* virtqueue_enable_cb: restart callbacks after disable_cb.
* vq: the struct virtqueue we're talking about.
* This re-enables callbacks; it returns "false" if there are pending
* buffers in the queue, to detect a possible race between the driver
* checking for more work, and enabling callbacks.
* virtqueue_enable_cb_delayed: restart callbacks after disable_cb.
* vq: the struct virtqueue we're talking about.
* This re-enables callbacks but hints to the other side to delay
* interrupts until most of the available buffers have been processed;
* it returns "false" if there are many pending buffers in the queue,
* to detect a possible race between the driver checking for more work,
* and enabling callbacks.
* virtqueue_detach_unused_buf: detach first unused buffer
* vq: the struct virtqueue we're talking about.
* Returns NULL or the "data" token handed to add_buf
* virtqueue_get_vring_size: return the size of the virtqueue's vring
* vq: the struct virtqueue containing the vring of interest.
* Returns the size of the vring.
*
* Locking rules are straightforward: the driver is responsible for
* locking. No two operations may be invoked simultaneously, with the exception
* of virtqueue_disable_cb.
*
* All operations can be called in any context.
*/
int virtqueue_add_buf_gfp(struct virtqueue *vq,
struct scatterlist sg[], struct scatterlist sg[],
unsigned int out_num, unsigned int out_num,
unsigned int in_num, unsigned int in_num,
void *data, void *data,
gfp_t gfp); gfp_t gfp);
static inline int virtqueue_add_buf(struct virtqueue *vq,
struct scatterlist sg[],
unsigned int out_num,
unsigned int in_num,
void *data)
{
return virtqueue_add_buf_gfp(vq, sg, out_num, in_num, data, GFP_ATOMIC);
}
void virtqueue_kick(struct virtqueue *vq); void virtqueue_kick(struct virtqueue *vq);
bool virtqueue_kick_prepare(struct virtqueue *vq);
void virtqueue_notify(struct virtqueue *vq);
void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len); void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len);
void virtqueue_disable_cb(struct virtqueue *vq); void virtqueue_disable_cb(struct virtqueue *vq);
...@@ -146,6 +94,11 @@ struct virtio_driver { ...@@ -146,6 +94,11 @@ struct virtio_driver {
int (*probe)(struct virtio_device *dev); int (*probe)(struct virtio_device *dev);
void (*remove)(struct virtio_device *dev); void (*remove)(struct virtio_device *dev);
void (*config_changed)(struct virtio_device *dev); void (*config_changed)(struct virtio_device *dev);
#ifdef CONFIG_PM
int (*freeze)(struct virtio_device *dev);
int (*thaw)(struct virtio_device *dev);
int (*restore)(struct virtio_device *dev);
#endif
}; };
int register_virtio_driver(struct virtio_driver *drv); int register_virtio_driver(struct virtio_driver *drv);
......
...@@ -168,6 +168,7 @@ struct virtqueue; ...@@ -168,6 +168,7 @@ struct virtqueue;
struct virtqueue *vring_new_virtqueue(unsigned int num, struct virtqueue *vring_new_virtqueue(unsigned int num,
unsigned int vring_align, unsigned int vring_align,
struct virtio_device *vdev, struct virtio_device *vdev,
bool weak_barriers,
void *pages, void *pages,
void (*notify)(struct virtqueue *vq), void (*notify)(struct virtqueue *vq),
void (*callback)(struct virtqueue *vq), void (*callback)(struct virtqueue *vq),
......
...@@ -272,7 +272,8 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req) ...@@ -272,7 +272,8 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
in = pack_sg_list(chan->sg, out, in = pack_sg_list(chan->sg, out,
VIRTQUEUE_NUM, req->rc->sdata, req->rc->capacity); VIRTQUEUE_NUM, req->rc->sdata, req->rc->capacity);
err = virtqueue_add_buf(chan->vq, chan->sg, out, in, req->tc); err = virtqueue_add_buf(chan->vq, chan->sg, out, in, req->tc,
GFP_ATOMIC);
if (err < 0) { if (err < 0) {
if (err == -ENOSPC) { if (err == -ENOSPC) {
chan->ring_bufs_avail = 0; chan->ring_bufs_avail = 0;
...@@ -414,7 +415,8 @@ p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req, ...@@ -414,7 +415,8 @@ p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req,
in += pack_sg_list_p(chan->sg, out + in, VIRTQUEUE_NUM, in += pack_sg_list_p(chan->sg, out + in, VIRTQUEUE_NUM,
in_pages, in_nr_pages, uidata, inlen); in_pages, in_nr_pages, uidata, inlen);
err = virtqueue_add_buf(chan->vq, chan->sg, out, in, req->tc); err = virtqueue_add_buf(chan->vq, chan->sg, out, in, req->tc,
GFP_ATOMIC);
if (err < 0) { if (err < 0) {
if (err == -ENOSPC) { if (err == -ENOSPC) {
chan->ring_bufs_avail = 0; chan->ring_bufs_avail = 0;
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
#include <linux/virtio_rng.h> #include <linux/virtio_rng.h>
#include <linux/virtio_ring.h> #include <linux/virtio_ring.h>
#include <asm/bootparam.h> #include <asm/bootparam.h>
#include "../../../include/linux/lguest_launcher.h" #include "../../include/linux/lguest_launcher.h"
/*L:110 /*L:110
* We can ignore the 43 include files we need for this program, but I do want * We can ignore the 43 include files we need for this program, but I do want
* to draw attention to the use of kernel-style types. * to draw attention to the use of kernel-style types.
......
...@@ -186,22 +186,13 @@ struct virtqueue { ...@@ -186,22 +186,13 @@ struct virtqueue {
#endif #endif
/* Interfaces exported by virtio_ring. */ /* Interfaces exported by virtio_ring. */
int virtqueue_add_buf_gfp(struct virtqueue *vq, int virtqueue_add_buf(struct virtqueue *vq,
struct scatterlist sg[], struct scatterlist sg[],
unsigned int out_num, unsigned int out_num,
unsigned int in_num, unsigned int in_num,
void *data, void *data,
gfp_t gfp); gfp_t gfp);
static inline int virtqueue_add_buf(struct virtqueue *vq,
struct scatterlist sg[],
unsigned int out_num,
unsigned int in_num,
void *data)
{
return virtqueue_add_buf_gfp(vq, sg, out_num, in_num, data, GFP_ATOMIC);
}
void virtqueue_kick(struct virtqueue *vq); void virtqueue_kick(struct virtqueue *vq);
void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len); void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len);
...@@ -214,6 +205,7 @@ void *virtqueue_detach_unused_buf(struct virtqueue *vq); ...@@ -214,6 +205,7 @@ void *virtqueue_detach_unused_buf(struct virtqueue *vq);
struct virtqueue *vring_new_virtqueue(unsigned int num, struct virtqueue *vring_new_virtqueue(unsigned int num,
unsigned int vring_align, unsigned int vring_align,
struct virtio_device *vdev, struct virtio_device *vdev,
bool weak_barriers,
void *pages, void *pages,
void (*notify)(struct virtqueue *vq), void (*notify)(struct virtqueue *vq),
void (*callback)(struct virtqueue *vq), void (*callback)(struct virtqueue *vq),
......
...@@ -92,7 +92,8 @@ static void vq_info_add(struct vdev_info *dev, int num) ...@@ -92,7 +92,8 @@ static void vq_info_add(struct vdev_info *dev, int num)
assert(r >= 0); assert(r >= 0);
memset(info->ring, 0, vring_size(num, 4096)); memset(info->ring, 0, vring_size(num, 4096));
vring_init(&info->vring, num, info->ring, 4096); vring_init(&info->vring, num, info->ring, 4096);
info->vq = vring_new_virtqueue(info->vring.num, 4096, &dev->vdev, info->ring, info->vq = vring_new_virtqueue(info->vring.num, 4096, &dev->vdev,
true, info->ring,
vq_notify, vq_callback, "test"); vq_notify, vq_callback, "test");
assert(info->vq); assert(info->vq);
info->vq->priv = info; info->vq->priv = info;
...@@ -160,7 +161,8 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq, int bufs) ...@@ -160,7 +161,8 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq, int bufs)
if (started < bufs) { if (started < bufs) {
sg_init_one(&sl, dev->buf, dev->buf_size); sg_init_one(&sl, dev->buf, dev->buf_size);
r = virtqueue_add_buf(vq->vq, &sl, 1, 0, r = virtqueue_add_buf(vq->vq, &sl, 1, 0,
dev->buf + started); dev->buf + started,
GFP_ATOMIC);
if (likely(r >= 0)) { if (likely(r >= 0)) {
++started; ++started;
virtqueue_kick(vq->vq); virtqueue_kick(vq->vq);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment