vdpa: make sure set_features is invoked for legacy
authorMichael S. Tsirkin <mst@redhat.com>
Mon, 27 Jul 2020 14:51:55 +0000 (10:51 -0400)
committerMichael S. Tsirkin <mst@redhat.com>
Wed, 5 Aug 2020 15:08:40 +0000 (11:08 -0400)
Some legacy guests just assume features are 0 after reset.
We detect that config space is accessed before features are
set and set features to 0 automatically.
Note: some legacy guests might not even access config space, if this is
reported in the field we might need to catch a kick to handle these.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
drivers/vdpa/vdpa.c
include/linux/vdpa.h

index de211ef..7105265 100644 (file)
@@ -96,6 +96,7 @@ struct vdpa_device *__vdpa_alloc_device(struct device *parent,
        vdev->dev.release = vdpa_release_dev;
        vdev->index = err;
        vdev->config = config;
+       vdev->features_valid = false;
 
        err = dev_set_name(&vdev->dev, "vdpa%u", vdev->index);
        if (err)
index 239db79..29b8296 100644 (file)
@@ -33,12 +33,14 @@ struct vdpa_notification_area {
  * @dma_dev: the actual device that is performing DMA
  * @config: the configuration ops for this device.
  * @index: device index
+ * @features_valid: were features initialized? for legacy guests
  */
 struct vdpa_device {
        struct device dev;
        struct device *dma_dev;
        const struct vdpa_config_ops *config;
        unsigned int index;
+       bool features_valid;
 };
 
 /**
@@ -266,4 +268,36 @@ static inline struct device *vdpa_get_dma_dev(struct vdpa_device *vdev)
 {
        return vdev->dma_dev;
 }
+
+static inline void vdpa_reset(struct vdpa_device *vdev)
+{
+        const struct vdpa_config_ops *ops = vdev->config;
+
+       vdev->features_valid = false;
+        ops->set_status(vdev, 0);
+}
+
+static inline int vdpa_set_features(struct vdpa_device *vdev, u64 features)
+{
+        const struct vdpa_config_ops *ops = vdev->config;
+
+       vdev->features_valid = true;
+        return ops->set_features(vdev, features);
+}
+
+
+static inline void vdpa_get_config(struct vdpa_device *vdev, unsigned offset,
+                                  void *buf, unsigned int len)
+{
+        const struct vdpa_config_ops *ops = vdev->config;
+
+       /*
+        * Config accesses aren't supposed to trigger before features are set.
+        * If it does happen we assume a legacy guest.
+        */
+       if (!vdev->features_valid)
+               vdpa_set_features(vdev, 0);
+       ops->get_config(vdev, offset, buf, len);
+}
+
 #endif /* _LINUX_VDPA_H */