forked from mirrors/linux
		
	virtio: defer config changed notifications
Defer config changed notifications that arrive during probe/scan/freeze/restore. This will allow drivers to set DRIVER_OK earlier, without worrying about racing with config change interrupts. This change will also benefit old hypervisors (before 2009) that send interrupts without checking DRIVER_OK: previously, the callback could race with driver-specific initialization. This will also help simplify drivers. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (cosmetic changes)
This commit is contained in:
		
							parent
							
								
									c6716bae52
								
							
						
					
					
						commit
						22b7050a02
					
				
					 2 changed files with 55 additions and 9 deletions
				
			
		|  | @ -117,6 +117,43 @@ void virtio_check_driver_offered_feature(const struct virtio_device *vdev, | |||
| } | ||||
| EXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature); | ||||
| 
 | ||||
| static void __virtio_config_changed(struct virtio_device *dev) | ||||
| { | ||||
| 	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); | ||||
| 
 | ||||
| 	if (!dev->config_enabled) | ||||
| 		dev->config_change_pending = true; | ||||
| 	else if (drv && drv->config_changed) | ||||
| 		drv->config_changed(dev); | ||||
| } | ||||
| 
 | ||||
| void virtio_config_changed(struct virtio_device *dev) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&dev->config_lock, flags); | ||||
| 	__virtio_config_changed(dev); | ||||
| 	spin_unlock_irqrestore(&dev->config_lock, flags); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(virtio_config_changed); | ||||
| 
 | ||||
| static void virtio_config_disable(struct virtio_device *dev) | ||||
| { | ||||
| 	spin_lock_irq(&dev->config_lock); | ||||
| 	dev->config_enabled = false; | ||||
| 	spin_unlock_irq(&dev->config_lock); | ||||
| } | ||||
| 
 | ||||
| static void virtio_config_enable(struct virtio_device *dev) | ||||
| { | ||||
| 	spin_lock_irq(&dev->config_lock); | ||||
| 	dev->config_enabled = true; | ||||
| 	if (dev->config_change_pending) | ||||
| 		__virtio_config_changed(dev); | ||||
| 	dev->config_change_pending = false; | ||||
| 	spin_unlock_irq(&dev->config_lock); | ||||
| } | ||||
| 
 | ||||
| static int virtio_dev_probe(struct device *_d) | ||||
| { | ||||
| 	int err, i; | ||||
|  | @ -153,6 +190,8 @@ static int virtio_dev_probe(struct device *_d) | |||
| 		add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); | ||||
| 		if (drv->scan) | ||||
| 			drv->scan(dev); | ||||
| 
 | ||||
| 		virtio_config_enable(dev); | ||||
| 	} | ||||
| 
 | ||||
| 	return err; | ||||
|  | @ -163,6 +202,8 @@ static int virtio_dev_remove(struct device *_d) | |||
| 	struct virtio_device *dev = dev_to_virtio(_d); | ||||
| 	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); | ||||
| 
 | ||||
| 	virtio_config_disable(dev); | ||||
| 
 | ||||
| 	drv->remove(dev); | ||||
| 
 | ||||
| 	/* Driver should have reset device. */ | ||||
|  | @ -211,6 +252,10 @@ int register_virtio_device(struct virtio_device *dev) | |||
| 	dev->index = err; | ||||
| 	dev_set_name(&dev->dev, "virtio%u", dev->index); | ||||
| 
 | ||||
| 	spin_lock_init(&dev->config_lock); | ||||
| 	dev->config_enabled = false; | ||||
| 	dev->config_change_pending = false; | ||||
| 
 | ||||
| 	/* We always start by resetting the device, in case a previous
 | ||||
| 	 * driver messed it up.  This also tests that code path a little. */ | ||||
| 	dev->config->reset(dev); | ||||
|  | @ -239,20 +284,13 @@ void unregister_virtio_device(struct virtio_device *dev) | |||
| } | ||||
| EXPORT_SYMBOL_GPL(unregister_virtio_device); | ||||
| 
 | ||||
| void virtio_config_changed(struct virtio_device *dev) | ||||
| { | ||||
| 	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); | ||||
| 
 | ||||
| 	if (drv && drv->config_changed) | ||||
| 		drv->config_changed(dev); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(virtio_config_changed); | ||||
| 
 | ||||
| #ifdef CONFIG_PM_SLEEP | ||||
| int virtio_device_freeze(struct virtio_device *dev) | ||||
| { | ||||
| 	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); | ||||
| 
 | ||||
| 	virtio_config_disable(dev); | ||||
| 
 | ||||
| 	dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED; | ||||
| 
 | ||||
| 	if (drv && drv->freeze) | ||||
|  | @ -297,6 +335,8 @@ int virtio_device_restore(struct virtio_device *dev) | |||
| 	/* Finally, tell the device we're all set */ | ||||
| 	add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); | ||||
| 
 | ||||
| 	virtio_config_enable(dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(virtio_device_restore); | ||||
|  |  | |||
|  | @ -79,6 +79,9 @@ bool virtqueue_is_broken(struct virtqueue *vq); | |||
|  * virtio_device - representation of a device using virtio | ||||
|  * @index: unique position on the virtio bus | ||||
|  * @failed: saved value for CONFIG_S_FAILED bit (for restore) | ||||
|  * @config_enabled: configuration change reporting enabled | ||||
|  * @config_change_pending: configuration change reported while disabled | ||||
|  * @config_lock: protects configuration change reporting | ||||
|  * @dev: underlying device. | ||||
|  * @id: the device type identification (used to match it with a driver). | ||||
|  * @config: the configuration ops for this device. | ||||
|  | @ -90,6 +93,9 @@ bool virtqueue_is_broken(struct virtqueue *vq); | |||
| struct virtio_device { | ||||
| 	int index; | ||||
| 	bool failed; | ||||
| 	bool config_enabled; | ||||
| 	bool config_change_pending; | ||||
| 	spinlock_t config_lock; | ||||
| 	struct device dev; | ||||
| 	struct virtio_device_id id; | ||||
| 	const struct virtio_config_ops *config; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Michael S. Tsirkin
						Michael S. Tsirkin