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);
 | 
					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)
 | 
					static int virtio_dev_probe(struct device *_d)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int err, i;
 | 
						int err, i;
 | 
				
			||||||
| 
						 | 
					@ -153,6 +190,8 @@ static int virtio_dev_probe(struct device *_d)
 | 
				
			||||||
		add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
 | 
							add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
 | 
				
			||||||
		if (drv->scan)
 | 
							if (drv->scan)
 | 
				
			||||||
			drv->scan(dev);
 | 
								drv->scan(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							virtio_config_enable(dev);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
| 
						 | 
					@ -163,6 +202,8 @@ static int virtio_dev_remove(struct device *_d)
 | 
				
			||||||
	struct virtio_device *dev = dev_to_virtio(_d);
 | 
						struct virtio_device *dev = dev_to_virtio(_d);
 | 
				
			||||||
	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
 | 
						struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtio_config_disable(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	drv->remove(dev);
 | 
						drv->remove(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Driver should have reset device. */
 | 
						/* Driver should have reset device. */
 | 
				
			||||||
| 
						 | 
					@ -211,6 +252,10 @@ int register_virtio_device(struct virtio_device *dev)
 | 
				
			||||||
	dev->index = err;
 | 
						dev->index = err;
 | 
				
			||||||
	dev_set_name(&dev->dev, "virtio%u", dev->index);
 | 
						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
 | 
						/* We always start by resetting the device, in case a previous
 | 
				
			||||||
	 * driver messed it up.  This also tests that code path a little. */
 | 
						 * driver messed it up.  This also tests that code path a little. */
 | 
				
			||||||
	dev->config->reset(dev);
 | 
						dev->config->reset(dev);
 | 
				
			||||||
| 
						 | 
					@ -239,20 +284,13 @@ void unregister_virtio_device(struct virtio_device *dev)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(unregister_virtio_device);
 | 
					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
 | 
					#ifdef CONFIG_PM_SLEEP
 | 
				
			||||||
int virtio_device_freeze(struct virtio_device *dev)
 | 
					int virtio_device_freeze(struct virtio_device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
 | 
						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;
 | 
						dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (drv && drv->freeze)
 | 
						if (drv && drv->freeze)
 | 
				
			||||||
| 
						 | 
					@ -297,6 +335,8 @@ int virtio_device_restore(struct virtio_device *dev)
 | 
				
			||||||
	/* Finally, tell the device we're all set */
 | 
						/* Finally, tell the device we're all set */
 | 
				
			||||||
	add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
 | 
						add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtio_config_enable(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(virtio_device_restore);
 | 
					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
 | 
					 * virtio_device - representation of a device using virtio
 | 
				
			||||||
 * @index: unique position on the virtio bus
 | 
					 * @index: unique position on the virtio bus
 | 
				
			||||||
 * @failed: saved value for CONFIG_S_FAILED bit (for restore)
 | 
					 * @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.
 | 
					 * @dev: underlying device.
 | 
				
			||||||
 * @id: the device type identification (used to match it with a driver).
 | 
					 * @id: the device type identification (used to match it with a driver).
 | 
				
			||||||
 * @config: the configuration ops for this device.
 | 
					 * @config: the configuration ops for this device.
 | 
				
			||||||
| 
						 | 
					@ -90,6 +93,9 @@ bool virtqueue_is_broken(struct virtqueue *vq);
 | 
				
			||||||
struct virtio_device {
 | 
					struct virtio_device {
 | 
				
			||||||
	int index;
 | 
						int index;
 | 
				
			||||||
	bool failed;
 | 
						bool failed;
 | 
				
			||||||
 | 
						bool config_enabled;
 | 
				
			||||||
 | 
						bool config_change_pending;
 | 
				
			||||||
 | 
						spinlock_t config_lock;
 | 
				
			||||||
	struct device dev;
 | 
						struct device dev;
 | 
				
			||||||
	struct virtio_device_id id;
 | 
						struct virtio_device_id id;
 | 
				
			||||||
	const struct virtio_config_ops *config;
 | 
						const struct virtio_config_ops *config;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue