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 | |
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>
-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 | ||||
-rw-r--r-- | include/linux/virtio.h | 7 | ||||
-rw-r--r-- | include/linux/virtio_config.h | 36 |
8 files changed, 142 insertions, 53 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 */ |
diff --git a/include/linux/virtio.h b/include/linux/virtio.h index e7d10845b3c1..06005fa9e982 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h | |||
@@ -76,6 +76,7 @@ struct virtqueue_ops { | |||
76 | * @dev: underlying device. | 76 | * @dev: underlying device. |
77 | * @id: the device type identification (used to match it with a driver). | 77 | * @id: the device type identification (used to match it with a driver). |
78 | * @config: the configuration ops for this device. | 78 | * @config: the configuration ops for this device. |
79 | * @features: the features supported by both driver and device. | ||
79 | * @priv: private pointer for the driver's use. | 80 | * @priv: private pointer for the driver's use. |
80 | */ | 81 | */ |
81 | struct virtio_device | 82 | struct virtio_device |
@@ -84,6 +85,8 @@ struct virtio_device | |||
84 | struct device dev; | 85 | struct device dev; |
85 | struct virtio_device_id id; | 86 | struct virtio_device_id id; |
86 | struct virtio_config_ops *config; | 87 | struct virtio_config_ops *config; |
88 | /* Note that this is a Linux set_bit-style bitmap. */ | ||
89 | unsigned long features[1]; | ||
87 | void *priv; | 90 | void *priv; |
88 | }; | 91 | }; |
89 | 92 | ||
@@ -94,6 +97,8 @@ void unregister_virtio_device(struct virtio_device *dev); | |||
94 | * virtio_driver - operations for a virtio I/O driver | 97 | * virtio_driver - operations for a virtio I/O driver |
95 | * @driver: underlying device driver (populate name and owner). | 98 | * @driver: underlying device driver (populate name and owner). |
96 | * @id_table: the ids serviced by this driver. | 99 | * @id_table: the ids serviced by this driver. |
100 | * @feature_table: an array of feature numbers supported by this device. | ||
101 | * @feature_table_size: number of entries in the feature table array. | ||
97 | * @probe: the function to call when a device is found. Returns a token for | 102 | * @probe: the function to call when a device is found. Returns a token for |
98 | * remove, or PTR_ERR(). | 103 | * remove, or PTR_ERR(). |
99 | * @remove: the function when a device is removed. | 104 | * @remove: the function when a device is removed. |
@@ -103,6 +108,8 @@ void unregister_virtio_device(struct virtio_device *dev); | |||
103 | struct virtio_driver { | 108 | struct virtio_driver { |
104 | struct device_driver driver; | 109 | struct device_driver driver; |
105 | const struct virtio_device_id *id_table; | 110 | const struct virtio_device_id *id_table; |
111 | const unsigned int *feature_table; | ||
112 | unsigned int feature_table_size; | ||
106 | int (*probe)(struct virtio_device *dev); | 113 | int (*probe)(struct virtio_device *dev); |
107 | void (*remove)(struct virtio_device *dev); | 114 | void (*remove)(struct virtio_device *dev); |
108 | void (*config_changed)(struct virtio_device *dev); | 115 | void (*config_changed)(struct virtio_device *dev); |
diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index 475572e976fe..50db245c81ad 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h | |||
@@ -20,11 +20,6 @@ | |||
20 | 20 | ||
21 | /** | 21 | /** |
22 | * virtio_config_ops - operations for configuring a virtio device | 22 | * virtio_config_ops - operations for configuring a virtio device |
23 | * @feature: search for a feature in this config | ||
24 | * vdev: the virtio_device | ||
25 | * bit: the feature bit | ||
26 | * Returns true if the feature is supported. Acknowledges the feature | ||
27 | * so the host can see it. | ||
28 | * @get: read the value of a configuration field | 23 | * @get: read the value of a configuration field |
29 | * vdev: the virtio_device | 24 | * vdev: the virtio_device |
30 | * offset: the offset of the configuration field | 25 | * offset: the offset of the configuration field |
@@ -50,10 +45,15 @@ | |||
50 | * callback: the virqtueue callback | 45 | * callback: the virqtueue callback |
51 | * Returns the new virtqueue or ERR_PTR() (eg. -ENOENT). | 46 | * Returns the new virtqueue or ERR_PTR() (eg. -ENOENT). |
52 | * @del_vq: free a virtqueue found by find_vq(). | 47 | * @del_vq: free a virtqueue found by find_vq(). |
48 | * @get_features: get the array of feature bits for this device. | ||
49 | * vdev: the virtio_device | ||
50 | * Returns the first 32 feature bits (all we currently need). | ||
51 | * @set_features: confirm what device features we'll be using. | ||
52 | * vdev: the virtio_device | ||
53 | * feature: the first 32 feature bits | ||
53 | */ | 54 | */ |
54 | struct virtio_config_ops | 55 | struct virtio_config_ops |
55 | { | 56 | { |
56 | bool (*feature)(struct virtio_device *vdev, unsigned bit); | ||
57 | void (*get)(struct virtio_device *vdev, unsigned offset, | 57 | void (*get)(struct virtio_device *vdev, unsigned offset, |
58 | void *buf, unsigned len); | 58 | void *buf, unsigned len); |
59 | void (*set)(struct virtio_device *vdev, unsigned offset, | 59 | void (*set)(struct virtio_device *vdev, unsigned offset, |
@@ -65,8 +65,30 @@ struct virtio_config_ops | |||
65 | unsigned index, | 65 | unsigned index, |
66 | void (*callback)(struct virtqueue *)); | 66 | void (*callback)(struct virtqueue *)); |
67 | void (*del_vq)(struct virtqueue *vq); | 67 | void (*del_vq)(struct virtqueue *vq); |
68 | u32 (*get_features)(struct virtio_device *vdev); | ||
69 | void (*set_features)(struct virtio_device *vdev, u32 features); | ||
68 | }; | 70 | }; |
69 | 71 | ||
72 | /* If driver didn't advertise the feature, it will never appear. */ | ||
73 | void virtio_check_driver_offered_feature(const struct virtio_device *vdev, | ||
74 | unsigned int fbit); | ||
75 | |||
76 | /** | ||
77 | * virtio_has_feature - helper to determine if this device has this feature. | ||
78 | * @vdev: the device | ||
79 | * @fbit: the feature bit | ||
80 | */ | ||
81 | static inline bool virtio_has_feature(const struct virtio_device *vdev, | ||
82 | unsigned int fbit) | ||
83 | { | ||
84 | /* Did you forget to fix assumptions on max features? */ | ||
85 | if (__builtin_constant_p(fbit)) | ||
86 | BUILD_BUG_ON(fbit >= 32); | ||
87 | |||
88 | virtio_check_driver_offered_feature(vdev, fbit); | ||
89 | return test_bit(fbit, vdev->features); | ||
90 | } | ||
91 | |||
70 | /** | 92 | /** |
71 | * virtio_config_val - look for a feature and get a virtio config entry. | 93 | * virtio_config_val - look for a feature and get a virtio config entry. |
72 | * @vdev: the virtio device | 94 | * @vdev: the virtio device |
@@ -84,7 +106,7 @@ static inline int virtio_config_buf(struct virtio_device *vdev, | |||
84 | unsigned int offset, | 106 | unsigned int offset, |
85 | void *buf, unsigned len) | 107 | void *buf, unsigned len) |
86 | { | 108 | { |
87 | if (!vdev->config->feature(vdev, fbit)) | 109 | if (!virtio_has_feature(vdev, fbit)) |
88 | return -ENOENT; | 110 | return -ENOENT; |
89 | 111 | ||
90 | vdev->config->get(vdev, offset, buf, len); | 112 | vdev->config->get(vdev, offset, buf, len); |