diff options
Diffstat (limited to 'drivers/virtio/virtio.c')
-rw-r--r-- | drivers/virtio/virtio.c | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index fed0ce198ae3..df598dd8c5c8 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c | |||
@@ -117,6 +117,43 @@ void virtio_check_driver_offered_feature(const struct virtio_device *vdev, | |||
117 | } | 117 | } |
118 | EXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature); | 118 | EXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature); |
119 | 119 | ||
120 | static void __virtio_config_changed(struct virtio_device *dev) | ||
121 | { | ||
122 | struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); | ||
123 | |||
124 | if (!dev->config_enabled) | ||
125 | dev->config_change_pending = true; | ||
126 | else if (drv && drv->config_changed) | ||
127 | drv->config_changed(dev); | ||
128 | } | ||
129 | |||
130 | void virtio_config_changed(struct virtio_device *dev) | ||
131 | { | ||
132 | unsigned long flags; | ||
133 | |||
134 | spin_lock_irqsave(&dev->config_lock, flags); | ||
135 | __virtio_config_changed(dev); | ||
136 | spin_unlock_irqrestore(&dev->config_lock, flags); | ||
137 | } | ||
138 | EXPORT_SYMBOL_GPL(virtio_config_changed); | ||
139 | |||
140 | static void virtio_config_disable(struct virtio_device *dev) | ||
141 | { | ||
142 | spin_lock_irq(&dev->config_lock); | ||
143 | dev->config_enabled = false; | ||
144 | spin_unlock_irq(&dev->config_lock); | ||
145 | } | ||
146 | |||
147 | static void virtio_config_enable(struct virtio_device *dev) | ||
148 | { | ||
149 | spin_lock_irq(&dev->config_lock); | ||
150 | dev->config_enabled = true; | ||
151 | if (dev->config_change_pending) | ||
152 | __virtio_config_changed(dev); | ||
153 | dev->config_change_pending = false; | ||
154 | spin_unlock_irq(&dev->config_lock); | ||
155 | } | ||
156 | |||
120 | static int virtio_dev_probe(struct device *_d) | 157 | static int virtio_dev_probe(struct device *_d) |
121 | { | 158 | { |
122 | int err, i; | 159 | int err, i; |
@@ -153,6 +190,8 @@ static int virtio_dev_probe(struct device *_d) | |||
153 | add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); | 190 | add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); |
154 | if (drv->scan) | 191 | if (drv->scan) |
155 | drv->scan(dev); | 192 | drv->scan(dev); |
193 | |||
194 | virtio_config_enable(dev); | ||
156 | } | 195 | } |
157 | 196 | ||
158 | return err; | 197 | return err; |
@@ -163,6 +202,8 @@ static int virtio_dev_remove(struct device *_d) | |||
163 | struct virtio_device *dev = dev_to_virtio(_d); | 202 | struct virtio_device *dev = dev_to_virtio(_d); |
164 | struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); | 203 | struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); |
165 | 204 | ||
205 | virtio_config_disable(dev); | ||
206 | |||
166 | drv->remove(dev); | 207 | drv->remove(dev); |
167 | 208 | ||
168 | /* Driver should have reset device. */ | 209 | /* Driver should have reset device. */ |
@@ -211,6 +252,10 @@ int register_virtio_device(struct virtio_device *dev) | |||
211 | dev->index = err; | 252 | dev->index = err; |
212 | dev_set_name(&dev->dev, "virtio%u", dev->index); | 253 | dev_set_name(&dev->dev, "virtio%u", dev->index); |
213 | 254 | ||
255 | spin_lock_init(&dev->config_lock); | ||
256 | dev->config_enabled = false; | ||
257 | dev->config_change_pending = false; | ||
258 | |||
214 | /* We always start by resetting the device, in case a previous | 259 | /* We always start by resetting the device, in case a previous |
215 | * driver messed it up. This also tests that code path a little. */ | 260 | * driver messed it up. This also tests that code path a little. */ |
216 | dev->config->reset(dev); | 261 | dev->config->reset(dev); |
@@ -239,6 +284,64 @@ void unregister_virtio_device(struct virtio_device *dev) | |||
239 | } | 284 | } |
240 | EXPORT_SYMBOL_GPL(unregister_virtio_device); | 285 | EXPORT_SYMBOL_GPL(unregister_virtio_device); |
241 | 286 | ||
287 | #ifdef CONFIG_PM_SLEEP | ||
288 | int virtio_device_freeze(struct virtio_device *dev) | ||
289 | { | ||
290 | struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); | ||
291 | |||
292 | virtio_config_disable(dev); | ||
293 | |||
294 | dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED; | ||
295 | |||
296 | if (drv && drv->freeze) | ||
297 | return drv->freeze(dev); | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | EXPORT_SYMBOL_GPL(virtio_device_freeze); | ||
302 | |||
303 | int virtio_device_restore(struct virtio_device *dev) | ||
304 | { | ||
305 | struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); | ||
306 | |||
307 | /* We always start by resetting the device, in case a previous | ||
308 | * driver messed it up. */ | ||
309 | dev->config->reset(dev); | ||
310 | |||
311 | /* Acknowledge that we've seen the device. */ | ||
312 | add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE); | ||
313 | |||
314 | /* Maybe driver failed before freeze. | ||
315 | * Restore the failed status, for debugging. */ | ||
316 | if (dev->failed) | ||
317 | add_status(dev, VIRTIO_CONFIG_S_FAILED); | ||
318 | |||
319 | if (!drv) | ||
320 | return 0; | ||
321 | |||
322 | /* We have a driver! */ | ||
323 | add_status(dev, VIRTIO_CONFIG_S_DRIVER); | ||
324 | |||
325 | dev->config->finalize_features(dev); | ||
326 | |||
327 | if (drv->restore) { | ||
328 | int ret = drv->restore(dev); | ||
329 | if (ret) { | ||
330 | add_status(dev, VIRTIO_CONFIG_S_FAILED); | ||
331 | return ret; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | /* Finally, tell the device we're all set */ | ||
336 | add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); | ||
337 | |||
338 | virtio_config_enable(dev); | ||
339 | |||
340 | return 0; | ||
341 | } | ||
342 | EXPORT_SYMBOL_GPL(virtio_device_restore); | ||
343 | #endif | ||
344 | |||
242 | static int virtio_init(void) | 345 | static int virtio_init(void) |
243 | { | 346 | { |
244 | if (bus_register(&virtio_bus) != 0) | 347 | if (bus_register(&virtio_bus) != 0) |