diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2008-02-04 23:49:56 -0500 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2008-02-04 07:49:57 -0500 |
commit | a586d4f6016f7139d8c26df0e6927131168d3b5b (patch) | |
tree | 1c47e1a6b6b8fb18baa42f32980f29c4ae9cbbdc /drivers/lguest/lguest_device.c | |
parent | f35d9d8aae08940b7fdd1bb8110619da2ece6b28 (diff) |
virtio: simplify config mechanism.
Previously we used a type/len pair within the config space, but this
seems overkill. We now simply define a structure which represents the
layout in the config space: the config space can now only be extended
at the end.
The main driver-visible changes:
1) We indicate what fields are present with an explicit feature bit.
2) Virtqueues are explicitly numbered, and not in the config space.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/lguest/lguest_device.c')
-rw-r--r-- | drivers/lguest/lguest_device.c | 132 |
1 files changed, 74 insertions, 58 deletions
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index e2eec38c83c2..07f57a53658b 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c | |||
@@ -52,57 +52,82 @@ struct lguest_device { | |||
52 | /*D:130 | 52 | /*D:130 |
53 | * Device configurations | 53 | * Device configurations |
54 | * | 54 | * |
55 | * The configuration information for a device consists of a series of fields. | 55 | * The configuration information for a device consists of one or more |
56 | * We don't really care what they are: the Launcher set them up, and the driver | 56 | * virtqueues, a feature bitmaks, and some configuration bytes. The |
57 | * will look at them during setup. | 57 | * configuration bytes don't really matter to us: the Launcher set them up, and |
58 | * the driver will look at them during setup. | ||
58 | * | 59 | * |
59 | * For us these fields come immediately after that device's descriptor in the | 60 | * A convenient routine to return the device's virtqueue config array: |
60 | * lguest_devices page. | 61 | * immediately after the descriptor. */ |
61 | * | 62 | static struct lguest_vqconfig *lg_vq(const struct lguest_device_desc *desc) |
62 | * Each field starts with a "type" byte, a "length" byte, then that number of | 63 | { |
63 | * bytes of configuration information. The device descriptor tells us the | 64 | return (void *)(desc + 1); |
64 | * total configuration length so we know when we've reached the last field. */ | 65 | } |
65 | 66 | ||
66 | /* type + length bytes */ | 67 | /* The features come immediately after the virtqueues. */ |
67 | #define FHDR_LEN 2 | 68 | static u8 *lg_features(const struct lguest_device_desc *desc) |
69 | { | ||
70 | return (void *)(lg_vq(desc) + desc->num_vq); | ||
71 | } | ||
68 | 72 | ||
69 | /* This finds the first field of a given type for a device's configuration. */ | 73 | /* The config space comes after the two feature bitmasks. */ |
70 | static void *lg_find(struct virtio_device *vdev, u8 type, unsigned int *len) | 74 | static u8 *lg_config(const struct lguest_device_desc *desc) |
71 | { | 75 | { |
72 | struct lguest_device_desc *desc = to_lgdev(vdev)->desc; | 76 | return lg_features(desc) + desc->feature_len * 2; |
73 | int i; | 77 | } |
74 | |||
75 | for (i = 0; i < desc->config_len; i += FHDR_LEN + desc->config[i+1]) { | ||
76 | if (desc->config[i] == type) { | ||
77 | /* Mark it used, so Host can know we looked at it, and | ||
78 | * also so we won't find the same one twice. */ | ||
79 | desc->config[i] |= 0x80; | ||
80 | /* Remember, the second byte is the length. */ | ||
81 | *len = desc->config[i+1]; | ||
82 | /* We return a pointer to the field header. */ | ||
83 | return desc->config + i; | ||
84 | } | ||
85 | } | ||
86 | 78 | ||
87 | /* Not found: return NULL for failure. */ | 79 | /* The total size of the config page used by this device (incl. desc) */ |
88 | return NULL; | 80 | static unsigned desc_size(const struct lguest_device_desc *desc) |
81 | { | ||
82 | return sizeof(*desc) | ||
83 | + desc->num_vq * sizeof(struct lguest_vqconfig) | ||
84 | + desc->feature_len * 2 | ||
85 | + desc->config_len; | ||
86 | } | ||
87 | |||
88 | /* This tests (and acknowleges) a feature bit. */ | ||
89 | static bool lg_feature(struct virtio_device *vdev, unsigned fbit) | ||
90 | { | ||
91 | struct lguest_device_desc *desc = to_lgdev(vdev)->desc; | ||
92 | u8 *features; | ||
93 | |||
94 | /* Obviously if they ask for a feature off the end of our feature | ||
95 | * bitmap, it's not set. */ | ||
96 | if (fbit / 8 > desc->feature_len) | ||
97 | return false; | ||
98 | |||
99 | /* The feature bitmap comes after the virtqueues. */ | ||
100 | features = lg_features(desc); | ||
101 | if (!(features[fbit / 8] & (1 << (fbit % 8)))) | ||
102 | return false; | ||
103 | |||
104 | /* We set the matching bit in the other half of the bitmap to tell the | ||
105 | * Host we want to use this feature. We don't use this yet, but we | ||
106 | * could in future. */ | ||
107 | features[desc->feature_len + fbit / 8] |= (1 << (fbit % 8)); | ||
108 | return true; | ||
89 | } | 109 | } |
90 | 110 | ||
91 | /* Once they've found a field, getting a copy of it is easy. */ | 111 | /* Once they've found a field, getting a copy of it is easy. */ |
92 | static void lg_get(struct virtio_device *vdev, void *token, | 112 | static void lg_get(struct virtio_device *vdev, unsigned int offset, |
93 | void *buf, unsigned len) | 113 | void *buf, unsigned len) |
94 | { | 114 | { |
95 | /* Check they didn't ask for more than the length of the field! */ | 115 | struct lguest_device_desc *desc = to_lgdev(vdev)->desc; |
96 | BUG_ON(len > ((u8 *)token)[1]); | 116 | |
97 | memcpy(buf, token + FHDR_LEN, len); | 117 | /* Check they didn't ask for more than the length of the config! */ |
118 | BUG_ON(offset + len > desc->config_len); | ||
119 | memcpy(buf, lg_config(desc) + offset, len); | ||
98 | } | 120 | } |
99 | 121 | ||
100 | /* Setting the contents is also trivial. */ | 122 | /* Setting the contents is also trivial. */ |
101 | static void lg_set(struct virtio_device *vdev, void *token, | 123 | static void lg_set(struct virtio_device *vdev, unsigned int offset, |
102 | const void *buf, unsigned len) | 124 | const void *buf, unsigned len) |
103 | { | 125 | { |
104 | BUG_ON(len > ((u8 *)token)[1]); | 126 | struct lguest_device_desc *desc = to_lgdev(vdev)->desc; |
105 | memcpy(token + FHDR_LEN, buf, len); | 127 | |
128 | /* Check they didn't ask for more than the length of the config! */ | ||
129 | BUG_ON(offset + len > desc->config_len); | ||
130 | memcpy(lg_config(desc) + offset, buf, len); | ||
106 | } | 131 | } |
107 | 132 | ||
108 | /* The operations to get and set the status word just access the status field | 133 | /* The operations to get and set the status word just access the status field |
@@ -165,39 +190,29 @@ static void lg_notify(struct virtqueue *vq) | |||
165 | * | 190 | * |
166 | * So we provide devices with a "find virtqueue and set it up" function. */ | 191 | * So we provide devices with a "find virtqueue and set it up" function. */ |
167 | static struct virtqueue *lg_find_vq(struct virtio_device *vdev, | 192 | static struct virtqueue *lg_find_vq(struct virtio_device *vdev, |
193 | unsigned index, | ||
168 | bool (*callback)(struct virtqueue *vq)) | 194 | bool (*callback)(struct virtqueue *vq)) |
169 | { | 195 | { |
196 | struct lguest_device *ldev = to_lgdev(vdev); | ||
170 | struct lguest_vq_info *lvq; | 197 | struct lguest_vq_info *lvq; |
171 | struct virtqueue *vq; | 198 | struct virtqueue *vq; |
172 | unsigned int len; | ||
173 | void *token; | ||
174 | int err; | 199 | int err; |
175 | 200 | ||
176 | /* Look for a field of the correct type to mark a virtqueue. Note that | 201 | /* We must have this many virtqueues. */ |
177 | * if this succeeds, then the type will be changed so it won't be found | 202 | if (index >= ldev->desc->num_vq) |
178 | * again, and future lg_find_vq() calls will find the next | ||
179 | * virtqueue (if any). */ | ||
180 | token = vdev->config->find(vdev, VIRTIO_CONFIG_F_VIRTQUEUE, &len); | ||
181 | if (!token) | ||
182 | return ERR_PTR(-ENOENT); | 203 | return ERR_PTR(-ENOENT); |
183 | 204 | ||
184 | lvq = kmalloc(sizeof(*lvq), GFP_KERNEL); | 205 | lvq = kmalloc(sizeof(*lvq), GFP_KERNEL); |
185 | if (!lvq) | 206 | if (!lvq) |
186 | return ERR_PTR(-ENOMEM); | 207 | return ERR_PTR(-ENOMEM); |
187 | 208 | ||
188 | /* Note: we could use a configuration space inside here, just like we | 209 | /* Make a copy of the "struct lguest_vqconfig" entry, which sits after |
189 | * do for the device. This would allow expansion in future, because | 210 | * the descriptor. We need a copy because the config space might not |
190 | * our configuration system is designed to be expansible. But this is | 211 | * be aligned correctly. */ |
191 | * way easier. */ | 212 | memcpy(&lvq->config, lg_vq(ldev->desc)+index, sizeof(lvq->config)); |
192 | if (len != sizeof(lvq->config)) { | ||
193 | dev_err(&vdev->dev, "Unexpected virtio config len %u\n", len); | ||
194 | err = -EIO; | ||
195 | goto free_lvq; | ||
196 | } | ||
197 | /* Make a copy of the "struct lguest_vqconfig" field. We need a copy | ||
198 | * because the config space might not be aligned correctly. */ | ||
199 | vdev->config->get(vdev, token, &lvq->config, sizeof(lvq->config)); | ||
200 | 213 | ||
214 | printk("Mapping virtqueue %i addr %lx\n", index, | ||
215 | (unsigned long)lvq->config.pfn << PAGE_SHIFT); | ||
201 | /* Figure out how many pages the ring will take, and map that memory */ | 216 | /* Figure out how many pages the ring will take, and map that memory */ |
202 | lvq->pages = lguest_map((unsigned long)lvq->config.pfn << PAGE_SHIFT, | 217 | lvq->pages = lguest_map((unsigned long)lvq->config.pfn << PAGE_SHIFT, |
203 | DIV_ROUND_UP(vring_size(lvq->config.num, | 218 | DIV_ROUND_UP(vring_size(lvq->config.num, |
@@ -259,7 +274,7 @@ static void lg_del_vq(struct virtqueue *vq) | |||
259 | 274 | ||
260 | /* The ops structure which hooks everything together. */ | 275 | /* The ops structure which hooks everything together. */ |
261 | static struct virtio_config_ops lguest_config_ops = { | 276 | static struct virtio_config_ops lguest_config_ops = { |
262 | .find = lg_find, | 277 | .feature = lg_feature, |
263 | .get = lg_get, | 278 | .get = lg_get, |
264 | .set = lg_set, | 279 | .set = lg_set, |
265 | .get_status = lg_get_status, | 280 | .get_status = lg_get_status, |
@@ -329,13 +344,14 @@ static void scan_devices(void) | |||
329 | struct lguest_device_desc *d; | 344 | struct lguest_device_desc *d; |
330 | 345 | ||
331 | /* We start at the page beginning, and skip over each entry. */ | 346 | /* We start at the page beginning, and skip over each entry. */ |
332 | for (i = 0; i < PAGE_SIZE; i += sizeof(*d) + d->config_len) { | 347 | for (i = 0; i < PAGE_SIZE; i += desc_size(d)) { |
333 | d = lguest_devices + i; | 348 | d = lguest_devices + i; |
334 | 349 | ||
335 | /* Once we hit a zero, stop. */ | 350 | /* Once we hit a zero, stop. */ |
336 | if (d->type == 0) | 351 | if (d->type == 0) |
337 | break; | 352 | break; |
338 | 353 | ||
354 | printk("Device at %i has size %u\n", i, desc_size(d)); | ||
339 | add_lguest_device(d); | 355 | add_lguest_device(d); |
340 | } | 356 | } |
341 | } | 357 | } |