| From 4fa59ede95195f267101a1b8916992cf3f245cdb Mon Sep 17 00:00:00 2001 |
| From: "Michael S. Tsirkin" <mst@redhat.com> |
| Date: Fri, 14 Jan 2022 14:58:41 -0500 |
| Subject: virtio: acknowledge all features before access |
| |
| From: Michael S. Tsirkin <mst@redhat.com> |
| |
| 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 commit 404123c2db79 ("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: 404123c2db79 ("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> |
| --- |
| drivers/virtio/virtio.c | 39 ++++++++++++++++++++++----------------- |
| include/linux/virtio_config.h | 3 ++- |
| 2 files changed, 24 insertions(+), 18 deletions(-) |
| |
| --- a/drivers/virtio/virtio.c |
| +++ b/drivers/virtio/virtio.c |
| @@ -166,14 +166,13 @@ void virtio_add_status(struct virtio_dev |
| } |
| 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; |
| + int ret; |
| |
| might_sleep(); |
| - if (ret) |
| - return ret; |
| |
| ret = arch_has_restricted_virtio_memory_access(); |
| if (ret) { |
| @@ -238,17 +237,6 @@ static int virtio_dev_probe(struct devic |
| 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 |
| @@ -259,13 +247,26 @@ static int virtio_dev_probe(struct devic |
| 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; |
| |
| @@ -489,7 +490,11 @@ int virtio_device_restore(struct virtio_ |
| /* 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; |
| |
| --- a/include/linux/virtio_config.h |
| +++ b/include/linux/virtio_config.h |
| @@ -62,8 +62,9 @@ struct virtio_shm_region { |
| * 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 |