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 /Documentation/lguest/lguest.c | |
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 'Documentation/lguest/lguest.c')
-rw-r--r-- | Documentation/lguest/lguest.c | 62 |
1 files changed, 52 insertions, 10 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 | ||