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/virtio | |
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/virtio')
-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 |
3 files changed, 56 insertions, 18 deletions
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 */ |