From d71de9ec6ba806104439d3a669befda84757b5af Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 14 Dec 2014 16:55:44 +0200 Subject: [PATCH] virtio: core support for config generation virtio 1.0 spec says: Drivers MUST NOT assume reads from fields greater than 32 bits wide are atomic, nor are reads from multiple fields: drivers SHOULD read device configuration space fields like so: u32 before, after; do { before = get_config_generation(device); // read config entry/entries. after = get_config_generation(device); } while (after != before); Do exactly this, for transports that support it. Signed-off-by: Michael S. Tsirkin --- include/linux/virtio_config.h | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index a61cd37f088c..ca3ed78e5ec7 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -19,6 +19,9 @@ * offset: the offset of the configuration field * buf: the buffer to read the field value from. * len: the length of the buffer + * @generation: config generation counter + * vdev: the virtio_device + * Returns the config generation counter * @get_status: read the status byte * vdev: the virtio_device * Returns the status byte @@ -60,6 +63,7 @@ struct virtio_config_ops { void *buf, unsigned len); void (*set)(struct virtio_device *vdev, unsigned offset, const void *buf, unsigned len); + u32 (*generation)(struct virtio_device *vdev); u8 (*get_status)(struct virtio_device *vdev); void (*set_status)(struct virtio_device *vdev, u8 status); void (*reset)(struct virtio_device *vdev); @@ -301,14 +305,33 @@ static inline u8 virtio_cread8(struct virtio_device *vdev, unsigned int offset) return ret; } +/* Read @count fields, @bytes each. */ +static inline void __virtio_cread_many(struct virtio_device *vdev, + unsigned int offset, + void *buf, size_t count, size_t bytes) +{ + u32 old, gen = vdev->config->generation ? + vdev->config->generation(vdev) : 0; + int i; + + do { + old = gen; + + for (i = 0; i < count; i++) + vdev->config->get(vdev, offset + bytes * i, + buf + i * bytes, bytes); + + gen = vdev->config->generation ? + vdev->config->generation(vdev) : 0; + } while (gen != old); +} + + static inline void virtio_cread_bytes(struct virtio_device *vdev, unsigned int offset, void *buf, size_t len) { - int i; - - for (i = 0; i < len; i++) - vdev->config->get(vdev, offset + i, buf + i, 1); + __virtio_cread_many(vdev, offset, buf, len, 1); } static inline void virtio_cwrite8(struct virtio_device *vdev, @@ -352,6 +375,7 @@ static inline u64 virtio_cread64(struct virtio_device *vdev, { u64 ret; vdev->config->get(vdev, offset, &ret, sizeof(ret)); + __virtio_cread_many(vdev, offset, &ret, 1, sizeof(ret)); return virtio64_to_cpu(vdev, (__force __virtio64)ret); } -- 2.11.0