diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2008-05-02 22:50:50 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2008-05-02 07:50:50 -0400 |
commit | c45a6816c19dee67b8f725e6646d428901a6dc24 (patch) | |
tree | 096e3263fd14e140685bcc3082394ff15f5aeddb /drivers | |
parent | 72e61eb40b55dd57031ec5971e810649f82b0259 (diff) |
virtio: explicit advertisement of driver features
A recent proposed feature addition to the virtio block driver revealed
some flaws in the API: in particular, we assume that feature
negotiation is complete once a driver's probe function returns.
There is nothing in the API to require this, however, and even I
didn't notice when it was violated.
So instead, we require the driver to specify what features it supports
in a table, we can then move the feature negotiation into the virtio
core. The intersection of device and driver features are presented in
a new 'features' bitmap in the struct virtio_device.
Note that this highlights the difference between Linux unsigned-long
bitmaps where each unsigned long is in native endian, and a
straight-forward little-endian array of bytes.
Drivers can still remove feature bits in their probe routine if they
really have to.
API changes:
- dev->config->feature() no longer gets and acks a feature.
- drivers should advertise their features in the 'feature_table' field
- use virtio_has_feature() for extra sanity when checking feature bits
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/block/virtio_blk.c | 8 | ||||
-rw-r--r-- | drivers/lguest/lguest_device.c | 48 | ||||
-rw-r--r-- | drivers/net/virtio_net.c | 22 | ||||
-rw-r--r-- | drivers/virtio/virtio.c | 38 | ||||
-rw-r--r-- | drivers/virtio/virtio_balloon.c | 6 | ||||
-rw-r--r-- | drivers/virtio/virtio_pci.c | 30 |
6 files changed, 106 insertions, 46 deletions
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index cc6d39383a3f..78be6b8c89e0 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c | |||
@@ -242,7 +242,7 @@ static int virtblk_probe(struct virtio_device *vdev) | |||
242 | index++; | 242 | index++; |
243 | 243 | ||
244 | /* If barriers are supported, tell block layer that queue is ordered */ | 244 | /* If barriers are supported, tell block layer that queue is ordered */ |
245 | if (vdev->config->feature(vdev, VIRTIO_BLK_F_BARRIER)) | 245 | if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER)) |
246 | blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL); | 246 | blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL); |
247 | 247 | ||
248 | /* Host must always specify the capacity. */ | 248 | /* Host must always specify the capacity. */ |
@@ -308,7 +308,13 @@ static struct virtio_device_id id_table[] = { | |||
308 | { 0 }, | 308 | { 0 }, |
309 | }; | 309 | }; |
310 | 310 | ||
311 | static unsigned int features[] = { | ||
312 | VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, | ||
313 | }; | ||
314 | |||
311 | static struct virtio_driver virtio_blk = { | 315 | static struct virtio_driver virtio_blk = { |
316 | .feature_table = features, | ||
317 | .feature_table_size = ARRAY_SIZE(features), | ||
312 | .driver.name = KBUILD_MODNAME, | 318 | .driver.name = KBUILD_MODNAME, |
313 | .driver.owner = THIS_MODULE, | 319 | .driver.owner = THIS_MODULE, |
314 | .id_table = id_table, | 320 | .id_table = id_table, |
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index 2bc9bf7e88e5..7a643a6ee9a1 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c | |||
@@ -85,27 +85,34 @@ static unsigned desc_size(const struct lguest_device_desc *desc) | |||
85 | + desc->config_len; | 85 | + desc->config_len; |
86 | } | 86 | } |
87 | 87 | ||
88 | /* This tests (and acknowleges) a feature bit. */ | 88 | /* This gets the device's feature bits. */ |
89 | static bool lg_feature(struct virtio_device *vdev, unsigned fbit) | 89 | static u32 lg_get_features(struct virtio_device *vdev) |
90 | { | 90 | { |
91 | unsigned int i; | ||
92 | u32 features = 0; | ||
93 | struct lguest_device_desc *desc = to_lgdev(vdev)->desc; | ||
94 | u8 *in_features = lg_features(desc); | ||
95 | |||
96 | /* We do this the slow but generic way. */ | ||
97 | for (i = 0; i < min(desc->feature_len * 8, 32); i++) | ||
98 | if (in_features[i / 8] & (1 << (i % 8))) | ||
99 | features |= (1 << i); | ||
100 | |||
101 | return features; | ||
102 | } | ||
103 | |||
104 | static void lg_set_features(struct virtio_device *vdev, u32 features) | ||
105 | { | ||
106 | unsigned int i; | ||
91 | struct lguest_device_desc *desc = to_lgdev(vdev)->desc; | 107 | struct lguest_device_desc *desc = to_lgdev(vdev)->desc; |
92 | u8 *features; | 108 | /* Second half of bitmap is features we accept. */ |
93 | 109 | u8 *out_features = lg_features(desc) + desc->feature_len; | |
94 | /* Obviously if they ask for a feature off the end of our feature | 110 | |
95 | * bitmap, it's not set. */ | 111 | memset(out_features, 0, desc->feature_len); |
96 | if (fbit / 8 > desc->feature_len) | 112 | for (i = 0; i < min(desc->feature_len * 8, 32); i++) { |
97 | return false; | 113 | if (features & (1 << i)) |
98 | 114 | out_features[i / 8] |= (1 << (i % 8)); | |
99 | /* The feature bitmap comes after the virtqueues. */ | 115 | } |
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; | ||
109 | } | 116 | } |
110 | 117 | ||
111 | /* Once they've found a field, getting a copy of it is easy. */ | 118 | /* Once they've found a field, getting a copy of it is easy. */ |
@@ -286,7 +293,8 @@ static void lg_del_vq(struct virtqueue *vq) | |||
286 | 293 | ||
287 | /* The ops structure which hooks everything together. */ | 294 | /* The ops structure which hooks everything together. */ |
288 | static struct virtio_config_ops lguest_config_ops = { | 295 | static struct virtio_config_ops lguest_config_ops = { |
289 | .feature = lg_feature, | 296 | .get_features = lg_get_features, |
297 | .set_features = lg_set_features, | ||
290 | .get = lg_get, | 298 | .get = lg_get, |
291 | .set = lg_set, | 299 | .set = lg_set, |
292 | .get_status = lg_get_status, | 300 | .get_status = lg_get_status, |
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index ad43421ab6f1..f926b5ab3d09 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c | |||
@@ -378,26 +378,26 @@ static int virtnet_probe(struct virtio_device *vdev) | |||
378 | SET_NETDEV_DEV(dev, &vdev->dev); | 378 | SET_NETDEV_DEV(dev, &vdev->dev); |
379 | 379 | ||
380 | /* Do we support "hardware" checksums? */ | 380 | /* Do we support "hardware" checksums? */ |
381 | if (csum && vdev->config->feature(vdev, VIRTIO_NET_F_CSUM)) { | 381 | if (csum && virtio_has_feature(vdev, VIRTIO_NET_F_CSUM)) { |
382 | /* This opens up the world of extra features. */ | 382 | /* This opens up the world of extra features. */ |
383 | dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; | 383 | dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; |
384 | if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_GSO)) { | 384 | if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) { |
385 | dev->features |= NETIF_F_TSO | NETIF_F_UFO | 385 | dev->features |= NETIF_F_TSO | NETIF_F_UFO |
386 | | NETIF_F_TSO_ECN | NETIF_F_TSO6; | 386 | | NETIF_F_TSO_ECN | NETIF_F_TSO6; |
387 | } | 387 | } |
388 | /* Individual feature bits: what can host handle? */ | 388 | /* Individual feature bits: what can host handle? */ |
389 | if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_HOST_TSO4)) | 389 | if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO4)) |
390 | dev->features |= NETIF_F_TSO; | 390 | dev->features |= NETIF_F_TSO; |
391 | if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_HOST_TSO6)) | 391 | if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO6)) |
392 | dev->features |= NETIF_F_TSO6; | 392 | dev->features |= NETIF_F_TSO6; |
393 | if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_HOST_ECN)) | 393 | if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN)) |
394 | dev->features |= NETIF_F_TSO_ECN; | 394 | dev->features |= NETIF_F_TSO_ECN; |
395 | if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_HOST_UFO)) | 395 | if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UFO)) |
396 | dev->features |= NETIF_F_UFO; | 396 | dev->features |= NETIF_F_UFO; |
397 | } | 397 | } |
398 | 398 | ||
399 | /* Configuration may specify what MAC to use. Otherwise random. */ | 399 | /* Configuration may specify what MAC to use. Otherwise random. */ |
400 | if (vdev->config->feature(vdev, VIRTIO_NET_F_MAC)) { | 400 | if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) { |
401 | vdev->config->get(vdev, | 401 | vdev->config->get(vdev, |
402 | offsetof(struct virtio_net_config, mac), | 402 | offsetof(struct virtio_net_config, mac), |
403 | dev->dev_addr, dev->addr_len); | 403 | dev->dev_addr, dev->addr_len); |
@@ -486,7 +486,15 @@ static struct virtio_device_id id_table[] = { | |||
486 | { 0 }, | 486 | { 0 }, |
487 | }; | 487 | }; |
488 | 488 | ||
489 | static unsigned int features[] = { | ||
490 | VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC, | ||
491 | VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, | ||
492 | VIRTIO_NET_F_HOST_ECN, | ||
493 | }; | ||
494 | |||
489 | static struct virtio_driver virtio_net = { | 495 | static struct virtio_driver virtio_net = { |
496 | .feature_table = features, | ||
497 | .feature_table_size = ARRAY_SIZE(features), | ||
490 | .driver.name = KBUILD_MODNAME, | 498 | .driver.name = KBUILD_MODNAME, |
491 | .driver.owner = THIS_MODULE, | 499 | .driver.owner = THIS_MODULE, |
492 | .id_table = id_table, | 500 | .id_table = id_table, |
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index b535483bc556..13866789b356 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c | |||
@@ -80,19 +80,51 @@ static void add_status(struct virtio_device *dev, unsigned status) | |||
80 | dev->config->set_status(dev, dev->config->get_status(dev) | status); | 80 | dev->config->set_status(dev, dev->config->get_status(dev) | status); |
81 | } | 81 | } |
82 | 82 | ||
83 | void virtio_check_driver_offered_feature(const struct virtio_device *vdev, | ||
84 | unsigned int fbit) | ||
85 | { | ||
86 | unsigned int i; | ||
87 | struct virtio_driver *drv = container_of(vdev->dev.driver, | ||
88 | struct virtio_driver, driver); | ||
89 | |||
90 | for (i = 0; i < drv->feature_table_size; i++) | ||
91 | if (drv->feature_table[i] == fbit) | ||
92 | return; | ||
93 | BUG(); | ||
94 | } | ||
95 | EXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature); | ||
96 | |||
83 | static int virtio_dev_probe(struct device *_d) | 97 | static int virtio_dev_probe(struct device *_d) |
84 | { | 98 | { |
85 | int err; | 99 | int err, i; |
86 | struct virtio_device *dev = container_of(_d,struct virtio_device,dev); | 100 | struct virtio_device *dev = container_of(_d,struct virtio_device,dev); |
87 | struct virtio_driver *drv = container_of(dev->dev.driver, | 101 | struct virtio_driver *drv = container_of(dev->dev.driver, |
88 | struct virtio_driver, driver); | 102 | struct virtio_driver, driver); |
103 | u32 device_features; | ||
89 | 104 | ||
105 | /* We have a driver! */ | ||
90 | add_status(dev, VIRTIO_CONFIG_S_DRIVER); | 106 | add_status(dev, VIRTIO_CONFIG_S_DRIVER); |
107 | |||
108 | /* Figure out what features the device supports. */ | ||
109 | device_features = dev->config->get_features(dev); | ||
110 | |||
111 | /* Features supported by both device and driver into dev->features. */ | ||
112 | memset(dev->features, 0, sizeof(dev->features)); | ||
113 | for (i = 0; i < drv->feature_table_size; i++) { | ||
114 | unsigned int f = drv->feature_table[i]; | ||
115 | BUG_ON(f >= 32); | ||
116 | if (device_features & (1 << f)) | ||
117 | set_bit(f, dev->features); | ||
118 | } | ||
119 | |||
91 | err = drv->probe(dev); | 120 | err = drv->probe(dev); |
92 | if (err) | 121 | if (err) |
93 | add_status(dev, VIRTIO_CONFIG_S_FAILED); | 122 | add_status(dev, VIRTIO_CONFIG_S_FAILED); |
94 | else | 123 | else { |
95 | add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); | 124 | add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); |
125 | /* They should never have set feature bits beyond 32 */ | ||
126 | dev->config->set_features(dev, dev->features[0]); | ||
127 | } | ||
96 | return err; | 128 | return err; |
97 | } | 129 | } |
98 | 130 | ||
@@ -114,6 +146,8 @@ static int virtio_dev_remove(struct device *_d) | |||
114 | 146 | ||
115 | int register_virtio_driver(struct virtio_driver *driver) | 147 | int register_virtio_driver(struct virtio_driver *driver) |
116 | { | 148 | { |
149 | /* Catch this early. */ | ||
150 | BUG_ON(driver->feature_table_size && !driver->feature_table); | ||
117 | driver->driver.bus = &virtio_bus; | 151 | driver->driver.bus = &virtio_bus; |
118 | driver->driver.probe = virtio_dev_probe; | 152 | driver->driver.probe = virtio_dev_probe; |
119 | driver->driver.remove = virtio_dev_remove; | 153 | driver->driver.remove = virtio_dev_remove; |
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index fef88d84cef6..bfef604160d1 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c | |||
@@ -227,7 +227,7 @@ static int virtballoon_probe(struct virtio_device *vdev) | |||
227 | } | 227 | } |
228 | 228 | ||
229 | vb->tell_host_first | 229 | vb->tell_host_first |
230 | = vdev->config->feature(vdev, VIRTIO_BALLOON_F_MUST_TELL_HOST); | 230 | = virtio_has_feature(vdev, VIRTIO_BALLOON_F_MUST_TELL_HOST); |
231 | 231 | ||
232 | return 0; | 232 | return 0; |
233 | 233 | ||
@@ -259,7 +259,11 @@ static void virtballoon_remove(struct virtio_device *vdev) | |||
259 | kfree(vb); | 259 | kfree(vb); |
260 | } | 260 | } |
261 | 261 | ||
262 | static unsigned int features[] = { VIRTIO_BALLOON_F_MUST_TELL_HOST }; | ||
263 | |||
262 | static struct virtio_driver virtio_balloon = { | 264 | static struct virtio_driver virtio_balloon = { |
265 | .feature_table = features, | ||
266 | .feature_table_size = ARRAY_SIZE(features), | ||
263 | .driver.name = KBUILD_MODNAME, | 267 | .driver.name = KBUILD_MODNAME, |
264 | .driver.owner = THIS_MODULE, | 268 | .driver.owner = THIS_MODULE, |
265 | .id_table = id_table, | 269 | .id_table = id_table, |
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index de102a614e97..27e9fc9117cd 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c | |||
@@ -87,23 +87,22 @@ static struct virtio_pci_device *to_vp_device(struct virtio_device *vdev) | |||
87 | return container_of(vdev, struct virtio_pci_device, vdev); | 87 | return container_of(vdev, struct virtio_pci_device, vdev); |
88 | } | 88 | } |
89 | 89 | ||
90 | /* virtio config->feature() implementation */ | 90 | /* virtio config->get_features() implementation */ |
91 | static bool vp_feature(struct virtio_device *vdev, unsigned bit) | 91 | static u32 vp_get_features(struct virtio_device *vdev) |
92 | { | ||
93 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | ||
94 | |||
95 | /* When someone needs more than 32 feature bits, we'll need to | ||
96 | * steal a bit to indicate that the rest are somewhere else. */ | ||
97 | return ioread32(vp_dev->ioaddr + VIRTIO_PCI_HOST_FEATURES); | ||
98 | } | ||
99 | |||
100 | /* virtio config->set_features() implementation */ | ||
101 | static void vp_set_features(struct virtio_device *vdev, u32 features) | ||
92 | { | 102 | { |
93 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); | 103 | struct virtio_pci_device *vp_dev = to_vp_device(vdev); |
94 | u32 mask; | ||
95 | |||
96 | /* Since this function is supposed to have the side effect of | ||
97 | * enabling a queried feature, we simulate that by doing a read | ||
98 | * from the host feature bitmask and then writing to the guest | ||
99 | * feature bitmask */ | ||
100 | mask = ioread32(vp_dev->ioaddr + VIRTIO_PCI_HOST_FEATURES); | ||
101 | if (mask & (1 << bit)) { | ||
102 | mask |= (1 << bit); | ||
103 | iowrite32(mask, vp_dev->ioaddr + VIRTIO_PCI_GUEST_FEATURES); | ||
104 | } | ||
105 | 104 | ||
106 | return !!(mask & (1 << bit)); | 105 | iowrite32(features, vp_dev->ioaddr + VIRTIO_PCI_GUEST_FEATURES); |
107 | } | 106 | } |
108 | 107 | ||
109 | /* virtio config->get() implementation */ | 108 | /* virtio config->get() implementation */ |
@@ -293,7 +292,6 @@ static void vp_del_vq(struct virtqueue *vq) | |||
293 | } | 292 | } |
294 | 293 | ||
295 | static struct virtio_config_ops virtio_pci_config_ops = { | 294 | static struct virtio_config_ops virtio_pci_config_ops = { |
296 | .feature = vp_feature, | ||
297 | .get = vp_get, | 295 | .get = vp_get, |
298 | .set = vp_set, | 296 | .set = vp_set, |
299 | .get_status = vp_get_status, | 297 | .get_status = vp_get_status, |
@@ -301,6 +299,8 @@ static struct virtio_config_ops virtio_pci_config_ops = { | |||
301 | .reset = vp_reset, | 299 | .reset = vp_reset, |
302 | .find_vq = vp_find_vq, | 300 | .find_vq = vp_find_vq, |
303 | .del_vq = vp_del_vq, | 301 | .del_vq = vp_del_vq, |
302 | .get_features = vp_get_features, | ||
303 | .set_features = vp_set_features, | ||
304 | }; | 304 | }; |
305 | 305 | ||
306 | /* the PCI probing function */ | 306 | /* the PCI probing function */ |