virtio: acknowledge all features before access
commit 4fa59ede95195f267101a1b8916992cf3f245cdb upstream. The feature negotiation was designed in a way that makes it possible for devices to know which config fields will be accessed by drivers. This is broken since commit404123c2db
("virtio: allow drivers to validate features") with fallout in at least block and net. We have a partial work-around in commit 2f9a174f918e ("virtio: write back F_VERSION_1 before validate") which at least lets devices find out which format should config space have, but this is a partial fix: guests should not access config space without acknowledging features since otherwise we'll never be able to change the config space format. To fix, split finalize_features from virtio_finalize_features and call finalize_features with all feature bits before validation, and then - if validation changed any bits - once again after. Since virtio_finalize_features no longer writes out features rename it to virtio_features_ok - since that is what it does: checks that features are ok with the device. As a side effect, this also reduces the amount of hypervisor accesses - we now only acknowledge features once unless we are clearing any features when validating (which is uncommon). IRC I think that this was more or less always the intent in the spec but unfortunately the way the spec is worded does not say this explicitly, I plan to address this at the spec level, too. Acked-by: Jason Wang <jasowang@redhat.com> Cc: stable@vger.kernel.org Fixes:404123c2db
("virtio: allow drivers to validate features") Fixes: 2f9a174f918e ("virtio: write back F_VERSION_1 before validate") Cc: "Halil Pasic" <pasic@linux.ibm.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
ffeb42e05d
commit
635959a821
@ -167,14 +167,12 @@ void virtio_add_status(struct virtio_device *dev, unsigned int status)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(virtio_add_status);
|
||||
|
||||
static int virtio_finalize_features(struct virtio_device *dev)
|
||||
/* Do some validation, then set FEATURES_OK */
|
||||
static int virtio_features_ok(struct virtio_device *dev)
|
||||
{
|
||||
int ret = dev->config->finalize_features(dev);
|
||||
unsigned status;
|
||||
|
||||
might_sleep();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1))
|
||||
return 0;
|
||||
@ -224,17 +222,6 @@ static int virtio_dev_probe(struct device *_d)
|
||||
driver_features_legacy = driver_features;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some devices detect legacy solely via F_VERSION_1. Write
|
||||
* F_VERSION_1 to force LE config space accesses before FEATURES_OK for
|
||||
* these when needed.
|
||||
*/
|
||||
if (drv->validate && !virtio_legacy_is_little_endian()
|
||||
&& device_features & BIT_ULL(VIRTIO_F_VERSION_1)) {
|
||||
dev->features = BIT_ULL(VIRTIO_F_VERSION_1);
|
||||
dev->config->finalize_features(dev);
|
||||
}
|
||||
|
||||
if (device_features & (1ULL << VIRTIO_F_VERSION_1))
|
||||
dev->features = driver_features & device_features;
|
||||
else
|
||||
@ -245,13 +232,26 @@ static int virtio_dev_probe(struct device *_d)
|
||||
if (device_features & (1ULL << i))
|
||||
__virtio_set_bit(dev, i);
|
||||
|
||||
err = dev->config->finalize_features(dev);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
if (drv->validate) {
|
||||
u64 features = dev->features;
|
||||
|
||||
err = drv->validate(dev);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
/* Did validation change any features? Then write them again. */
|
||||
if (features != dev->features) {
|
||||
err = dev->config->finalize_features(dev);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
err = virtio_finalize_features(dev);
|
||||
err = virtio_features_ok(dev);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
@ -416,7 +416,11 @@ int virtio_device_restore(struct virtio_device *dev)
|
||||
/* We have a driver! */
|
||||
virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER);
|
||||
|
||||
ret = virtio_finalize_features(dev);
|
||||
ret = dev->config->finalize_features(dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = virtio_features_ok(dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
@ -56,8 +56,9 @@ struct irq_affinity;
|
||||
* Returns the first 64 feature bits (all we currently need).
|
||||
* @finalize_features: confirm what device features we'll be using.
|
||||
* vdev: the virtio_device
|
||||
* This gives the final feature bits for the device: it can change
|
||||
* This sends the driver feature bits to the device: it can change
|
||||
* the dev->feature bits if it wants.
|
||||
* Note: despite the name this can be called any number of times.
|
||||
* Returns 0 on success or error status
|
||||
* @bus_name: return the bus name associated with the device (optional)
|
||||
* vdev: the virtio_device
|
||||
|
Loading…
Reference in New Issue
Block a user