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 | |
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>
-rw-r--r-- | Documentation/lguest/lguest.c | 62 | ||||
-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 | ||||
-rw-r--r-- | include/linux/virtio.h | 5 | ||||
-rw-r--r-- | include/linux/virtio_config.h | 4 |
8 files changed, 87 insertions, 32 deletions
diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index 8ff2d8bc690a..0f23d67f958f 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c | |||
@@ -193,6 +193,13 @@ static void *_convert(struct iovec *iov, size_t size, size_t align, | |||
193 | #define le32_to_cpu(v32) (v32) | 193 | #define le32_to_cpu(v32) (v32) |
194 | #define le64_to_cpu(v64) (v64) | 194 | #define le64_to_cpu(v64) (v64) |
195 | 195 | ||
196 | /* The device virtqueue descriptors are followed by feature bitmasks. */ | ||
197 | static u8 *get_feature_bits(struct device *dev) | ||
198 | { | ||
199 | return (u8 *)(dev->desc + 1) | ||
200 | + dev->desc->num_vq * sizeof(struct lguest_vqconfig); | ||
201 | } | ||
202 | |||
196 | /*L:100 The Launcher code itself takes us out into userspace, that scary place | 203 | /*L:100 The Launcher code itself takes us out into userspace, that scary place |
197 | * where pointers run wild and free! Unfortunately, like most userspace | 204 | * where pointers run wild and free! Unfortunately, like most userspace |
198 | * programs, it's quite boring (which is why everyone likes to hack on the | 205 | * programs, it's quite boring (which is why everyone likes to hack on the |
@@ -914,21 +921,58 @@ static void enable_fd(int fd, struct virtqueue *vq) | |||
914 | write(waker_fd, &vq->dev->fd, sizeof(vq->dev->fd)); | 921 | write(waker_fd, &vq->dev->fd, sizeof(vq->dev->fd)); |
915 | } | 922 | } |
916 | 923 | ||
924 | /* Resetting a device is fairly easy. */ | ||
925 | static void reset_device(struct device *dev) | ||
926 | { | ||
927 | struct virtqueue *vq; | ||
928 | |||
929 | verbose("Resetting device %s\n", dev->name); | ||
930 | /* Clear the status. */ | ||
931 | dev->desc->status = 0; | ||
932 | |||
933 | /* Clear any features they've acked. */ | ||
934 | memset(get_feature_bits(dev) + dev->desc->feature_len, 0, | ||
935 | dev->desc->feature_len); | ||
936 | |||
937 | /* Zero out the virtqueues. */ | ||
938 | for (vq = dev->vq; vq; vq = vq->next) { | ||
939 | memset(vq->vring.desc, 0, | ||
940 | vring_size(vq->config.num, getpagesize())); | ||
941 | vq->last_avail_idx = 0; | ||
942 | } | ||
943 | } | ||
944 | |||
917 | /* This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */ | 945 | /* This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */ |
918 | static void handle_output(int fd, unsigned long addr) | 946 | static void handle_output(int fd, unsigned long addr) |
919 | { | 947 | { |
920 | struct device *i; | 948 | struct device *i; |
921 | struct virtqueue *vq; | 949 | struct virtqueue *vq; |
922 | 950 | ||
923 | /* Check each virtqueue. */ | 951 | /* Check each device and virtqueue. */ |
924 | for (i = devices.dev; i; i = i->next) { | 952 | for (i = devices.dev; i; i = i->next) { |
953 | /* Notifications to device descriptors reset the device. */ | ||
954 | if (from_guest_phys(addr) == i->desc) { | ||
955 | reset_device(i); | ||
956 | return; | ||
957 | } | ||
958 | |||
959 | /* Notifications to virtqueues mean output has occurred. */ | ||
925 | for (vq = i->vq; vq; vq = vq->next) { | 960 | for (vq = i->vq; vq; vq = vq->next) { |
926 | if (vq->config.pfn == addr/getpagesize()) { | 961 | if (vq->config.pfn != addr/getpagesize()) |
927 | verbose("Output to %s\n", vq->dev->name); | 962 | continue; |
928 | if (vq->handle_output) | 963 | |
929 | vq->handle_output(fd, vq); | 964 | /* Guest should acknowledge (and set features!) before |
965 | * using the device. */ | ||
966 | if (i->desc->status == 0) { | ||
967 | warnx("%s gave early output", i->name); | ||
930 | return; | 968 | return; |
931 | } | 969 | } |
970 | |||
971 | if (strcmp(vq->dev->name, "console") != 0) | ||
972 | verbose("Output to %s\n", vq->dev->name); | ||
973 | if (vq->handle_output) | ||
974 | vq->handle_output(fd, vq); | ||
975 | return; | ||
932 | } | 976 | } |
933 | } | 977 | } |
934 | 978 | ||
@@ -1074,10 +1118,11 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs, | |||
1074 | vq->vring.used->flags = VRING_USED_F_NO_NOTIFY; | 1118 | vq->vring.used->flags = VRING_USED_F_NO_NOTIFY; |
1075 | } | 1119 | } |
1076 | 1120 | ||
1077 | /* The virtqueue descriptors are followed by feature bytes. */ | 1121 | /* The first half of the feature bitmask is for us to advertise features. The |
1122 | * second half if for the Guest to accept features. */ | ||
1078 | static void add_feature(struct device *dev, unsigned bit) | 1123 | static void add_feature(struct device *dev, unsigned bit) |
1079 | { | 1124 | { |
1080 | u8 *features; | 1125 | u8 *features = get_feature_bits(dev); |
1081 | 1126 | ||
1082 | /* We can't extend the feature bits once we've added config bytes */ | 1127 | /* We can't extend the feature bits once we've added config bytes */ |
1083 | if (dev->desc->feature_len <= bit / CHAR_BIT) { | 1128 | if (dev->desc->feature_len <= bit / CHAR_BIT) { |
@@ -1085,9 +1130,6 @@ static void add_feature(struct device *dev, unsigned bit) | |||
1085 | dev->desc->feature_len = (bit / CHAR_BIT) + 1; | 1130 | dev->desc->feature_len = (bit / CHAR_BIT) + 1; |
1086 | } | 1131 | } |
1087 | 1132 | ||
1088 | features = (u8 *)(dev->desc + 1) | ||
1089 | + dev->desc->num_vq * sizeof(struct lguest_vqconfig); | ||
1090 | |||
1091 | features[bit / CHAR_BIT] |= (1 << (bit % CHAR_BIT)); | 1133 | features[bit / CHAR_BIT] |= (1 << (bit % CHAR_BIT)); |
1092 | } | 1134 | } |
1093 | 1135 | ||
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, |
diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 78408d5237c1..260d1fcf29a4 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h | |||
@@ -45,9 +45,6 @@ struct virtqueue | |||
45 | * vq: the struct virtqueue we're talking about. | 45 | * vq: the struct virtqueue we're talking about. |
46 | * This returns "false" (and doesn't re-enable) if there are pending | 46 | * This returns "false" (and doesn't re-enable) if there are pending |
47 | * buffers in the queue, to avoid a race. | 47 | * buffers in the queue, to avoid a race. |
48 | * @shutdown: "unadd" all buffers. | ||
49 | * vq: the struct virtqueue we're talking about. | ||
50 | * Remove everything from the queue. | ||
51 | * | 48 | * |
52 | * Locking rules are straightforward: the driver is responsible for | 49 | * Locking rules are straightforward: the driver is responsible for |
53 | * locking. No two operations may be invoked simultaneously. | 50 | * locking. No two operations may be invoked simultaneously. |
@@ -67,8 +64,6 @@ struct virtqueue_ops { | |||
67 | 64 | ||
68 | void (*disable_cb)(struct virtqueue *vq); | 65 | void (*disable_cb)(struct virtqueue *vq); |
69 | bool (*enable_cb)(struct virtqueue *vq); | 66 | bool (*enable_cb)(struct virtqueue *vq); |
70 | |||
71 | void (*shutdown)(struct virtqueue *vq); | ||
72 | }; | 67 | }; |
73 | 68 | ||
74 | /** | 69 | /** |
diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index 81f828ac8f47..d581b2914b34 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h | |||
@@ -43,6 +43,9 @@ struct virtio_device; | |||
43 | * @set_status: write the status byte | 43 | * @set_status: write the status byte |
44 | * vdev: the virtio_device | 44 | * vdev: the virtio_device |
45 | * status: the new status byte | 45 | * status: the new status byte |
46 | * @reset: reset the device | ||
47 | * vdev: the virtio device | ||
48 | * After this, status and feature negotiation must be done again | ||
46 | * @find_vq: find a virtqueue and instantiate it. | 49 | * @find_vq: find a virtqueue and instantiate it. |
47 | * vdev: the virtio_device | 50 | * vdev: the virtio_device |
48 | * index: the 0-based virtqueue number in case there's more than one. | 51 | * index: the 0-based virtqueue number in case there's more than one. |
@@ -59,6 +62,7 @@ struct virtio_config_ops | |||
59 | const void *buf, unsigned len); | 62 | const void *buf, unsigned len); |
60 | u8 (*get_status)(struct virtio_device *vdev); | 63 | u8 (*get_status)(struct virtio_device *vdev); |
61 | void (*set_status)(struct virtio_device *vdev, u8 status); | 64 | void (*set_status)(struct virtio_device *vdev, u8 status); |
65 | void (*reset)(struct virtio_device *vdev); | ||
62 | struct virtqueue *(*find_vq)(struct virtio_device *vdev, | 66 | struct virtqueue *(*find_vq)(struct virtio_device *vdev, |
63 | unsigned index, | 67 | unsigned index, |
64 | void (*callback)(struct virtqueue *)); | 68 | void (*callback)(struct virtqueue *)); |