diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2008-02-04 23:50:03 -0500 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2008-02-04 07:50:03 -0500 |
commit | 6e5aa7efb27aec7e55b6463fa2c8db594c4226fa (patch) | |
tree | 060a955e711ac224136157a5410e88dcdab965af /drivers | |
parent | b3369c1fb410fddeb38a404316c861395f6d6ae8 (diff) |
virtio: reset function
A reset function solves three problems:
1) It allows us to renegotiate features, eg. if we want to upgrade a
guest driver without rebooting the guest.
2) It gives us a clean way of shutting down virtqueues: after a reset,
we know that the buffers won't be used by the host, and
3) It helps the guest recover from messed-up drivers.
So we remove the ->shutdown hook, and the only way we now remove
feature bits is via reset.
We leave it to the driver to do the reset before it deletes queues:
the balloon driver, for example, needs to chat to the host in its
remove function.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/block/virtio_blk.c | 6 | ||||
-rw-r--r-- | drivers/lguest/lguest_device.c | 14 | ||||
-rw-r--r-- | drivers/net/virtio_net.c | 5 | ||||
-rw-r--r-- | drivers/virtio/virtio.c | 12 | ||||
-rw-r--r-- | drivers/virtio/virtio_ring.c | 11 |
5 files changed, 31 insertions, 17 deletions
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 54a8017ad487..6143337527e7 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c | |||
@@ -264,12 +264,16 @@ static void virtblk_remove(struct virtio_device *vdev) | |||
264 | struct virtio_blk *vblk = vdev->priv; | 264 | struct virtio_blk *vblk = vdev->priv; |
265 | int major = vblk->disk->major; | 265 | int major = vblk->disk->major; |
266 | 266 | ||
267 | /* Nothing should be pending. */ | ||
267 | BUG_ON(!list_empty(&vblk->reqs)); | 268 | BUG_ON(!list_empty(&vblk->reqs)); |
269 | |||
270 | /* Stop all the virtqueues. */ | ||
271 | vdev->config->reset(vdev); | ||
272 | |||
268 | blk_cleanup_queue(vblk->disk->queue); | 273 | blk_cleanup_queue(vblk->disk->queue); |
269 | put_disk(vblk->disk); | 274 | put_disk(vblk->disk); |
270 | unregister_blkdev(major, "virtblk"); | 275 | unregister_blkdev(major, "virtblk"); |
271 | mempool_destroy(vblk->pool); | 276 | mempool_destroy(vblk->pool); |
272 | /* There should be nothing in the queue now, so no need to shutdown */ | ||
273 | vdev->config->del_vq(vblk->vq); | 277 | vdev->config->del_vq(vblk->vq); |
274 | kfree(vblk); | 278 | kfree(vblk); |
275 | } | 279 | } |
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index ced5b44cebce..84f85e23cca7 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c | |||
@@ -54,7 +54,7 @@ struct lguest_device { | |||
54 | * | 54 | * |
55 | * The configuration information for a device consists of one or more | 55 | * The configuration information for a device consists of one or more |
56 | * virtqueues, a feature bitmaks, and some configuration bytes. The | 56 | * virtqueues, a feature bitmaks, and some configuration bytes. The |
57 | * configuration bytes don't really matter to us: the Launcher set them up, and | 57 | * configuration bytes don't really matter to us: the Launcher sets them up, and |
58 | * the driver will look at them during setup. | 58 | * the driver will look at them during setup. |
59 | * | 59 | * |
60 | * A convenient routine to return the device's virtqueue config array: | 60 | * A convenient routine to return the device's virtqueue config array: |
@@ -139,9 +139,20 @@ static u8 lg_get_status(struct virtio_device *vdev) | |||
139 | 139 | ||
140 | static void lg_set_status(struct virtio_device *vdev, u8 status) | 140 | static void lg_set_status(struct virtio_device *vdev, u8 status) |
141 | { | 141 | { |
142 | BUG_ON(!status); | ||
142 | to_lgdev(vdev)->desc->status = status; | 143 | to_lgdev(vdev)->desc->status = status; |
143 | } | 144 | } |
144 | 145 | ||
146 | /* To reset the device, we (ab)use the NOTIFY hypercall, with the descriptor | ||
147 | * address of the device. The Host will zero the status and all the | ||
148 | * features. */ | ||
149 | static void lg_reset(struct virtio_device *vdev) | ||
150 | { | ||
151 | unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices; | ||
152 | |||
153 | hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0); | ||
154 | } | ||
155 | |||
145 | /* | 156 | /* |
146 | * Virtqueues | 157 | * Virtqueues |
147 | * | 158 | * |
@@ -279,6 +290,7 @@ static struct virtio_config_ops lguest_config_ops = { | |||
279 | .set = lg_set, | 290 | .set = lg_set, |
280 | .get_status = lg_get_status, | 291 | .get_status = lg_get_status, |
281 | .set_status = lg_set_status, | 292 | .set_status = lg_set_status, |
293 | .reset = lg_reset, | ||
282 | .find_vq = lg_find_vq, | 294 | .find_vq = lg_find_vq, |
283 | .del_vq = lg_del_vq, | 295 | .del_vq = lg_del_vq, |
284 | }; | 296 | }; |
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index ec43284ffd13..6e0a9fefe6cb 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c | |||
@@ -390,13 +390,14 @@ static void virtnet_remove(struct virtio_device *vdev) | |||
390 | struct virtnet_info *vi = vdev->priv; | 390 | struct virtnet_info *vi = vdev->priv; |
391 | struct sk_buff *skb; | 391 | struct sk_buff *skb; |
392 | 392 | ||
393 | /* Stop all the virtqueues. */ | ||
394 | vdev->config->reset(vdev); | ||
395 | |||
393 | /* Free our skbs in send and recv queues, if any. */ | 396 | /* Free our skbs in send and recv queues, if any. */ |
394 | vi->rvq->vq_ops->shutdown(vi->rvq); | ||
395 | while ((skb = __skb_dequeue(&vi->recv)) != NULL) { | 397 | while ((skb = __skb_dequeue(&vi->recv)) != NULL) { |
396 | kfree_skb(skb); | 398 | kfree_skb(skb); |
397 | vi->num--; | 399 | vi->num--; |
398 | } | 400 | } |
399 | vi->svq->vq_ops->shutdown(vi->svq); | ||
400 | while ((skb = __skb_dequeue(&vi->send)) != NULL) | 401 | while ((skb = __skb_dequeue(&vi->send)) != NULL) |
401 | kfree_skb(skb); | 402 | kfree_skb(skb); |
402 | 403 | ||
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 303cb6f90108..7dddb1860936 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c | |||
@@ -102,9 +102,13 @@ static int virtio_dev_remove(struct device *_d) | |||
102 | struct virtio_driver *drv = container_of(dev->dev.driver, | 102 | struct virtio_driver *drv = container_of(dev->dev.driver, |
103 | struct virtio_driver, driver); | 103 | struct virtio_driver, driver); |
104 | 104 | ||
105 | dev->config->set_status(dev, dev->config->get_status(dev) | ||
106 | & ~VIRTIO_CONFIG_S_DRIVER); | ||
107 | drv->remove(dev); | 105 | drv->remove(dev); |
106 | |||
107 | /* Driver should have reset device. */ | ||
108 | BUG_ON(dev->config->get_status(dev)); | ||
109 | |||
110 | /* Acknowledge the device's existence again. */ | ||
111 | add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE); | ||
108 | return 0; | 112 | return 0; |
109 | } | 113 | } |
110 | 114 | ||
@@ -130,6 +134,10 @@ int register_virtio_device(struct virtio_device *dev) | |||
130 | dev->dev.bus = &virtio_bus; | 134 | dev->dev.bus = &virtio_bus; |
131 | sprintf(dev->dev.bus_id, "%u", dev->index); | 135 | sprintf(dev->dev.bus_id, "%u", dev->index); |
132 | 136 | ||
137 | /* We always start by resetting the device, in case a previous | ||
138 | * driver messed it up. This also tests that code path a little. */ | ||
139 | dev->config->reset(dev); | ||
140 | |||
133 | /* Acknowledge that we've seen the device. */ | 141 | /* Acknowledge that we've seen the device. */ |
134 | add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE); | 142 | add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE); |
135 | 143 | ||
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index dbe1d35db32a..9849babd6b37 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c | |||
@@ -173,16 +173,6 @@ static void detach_buf(struct vring_virtqueue *vq, unsigned int head) | |||
173 | vq->num_free++; | 173 | vq->num_free++; |
174 | } | 174 | } |
175 | 175 | ||
176 | /* FIXME: We need to tell other side about removal, to synchronize. */ | ||
177 | static void vring_shutdown(struct virtqueue *_vq) | ||
178 | { | ||
179 | struct vring_virtqueue *vq = to_vvq(_vq); | ||
180 | unsigned int i; | ||
181 | |||
182 | for (i = 0; i < vq->vring.num; i++) | ||
183 | detach_buf(vq, i); | ||
184 | } | ||
185 | |||
186 | static inline bool more_used(const struct vring_virtqueue *vq) | 176 | static inline bool more_used(const struct vring_virtqueue *vq) |
187 | { | 177 | { |
188 | return vq->last_used_idx != vq->vring.used->idx; | 178 | return vq->last_used_idx != vq->vring.used->idx; |
@@ -278,7 +268,6 @@ static struct virtqueue_ops vring_vq_ops = { | |||
278 | .kick = vring_kick, | 268 | .kick = vring_kick, |
279 | .disable_cb = vring_disable_cb, | 269 | .disable_cb = vring_disable_cb, |
280 | .enable_cb = vring_enable_cb, | 270 | .enable_cb = vring_enable_cb, |
281 | .shutdown = vring_shutdown, | ||
282 | }; | 271 | }; |
283 | 272 | ||
284 | struct virtqueue *vring_new_virtqueue(unsigned int num, | 273 | struct virtqueue *vring_new_virtqueue(unsigned int num, |