Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <mst@redhat.com>

+28 -4
+28 -4
include/linux/virtio_config.h
··· 19 19 * offset: the offset of the configuration field 20 20 * buf: the buffer to read the field value from. 21 21 * len: the length of the buffer 22 + * @generation: config generation counter 23 + * vdev: the virtio_device 24 + * Returns the config generation counter 22 25 * @get_status: read the status byte 23 26 * vdev: the virtio_device 24 27 * Returns the status byte ··· 63 60 void *buf, unsigned len); 64 61 void (*set)(struct virtio_device *vdev, unsigned offset, 65 62 const void *buf, unsigned len); 63 + u32 (*generation)(struct virtio_device *vdev); 66 64 u8 (*get_status)(struct virtio_device *vdev); 67 65 void (*set_status)(struct virtio_device *vdev, u8 status); 68 66 void (*reset)(struct virtio_device *vdev); ··· 305 301 return ret; 306 302 } 307 303 304 + /* Read @count fields, @bytes each. */ 305 + static inline void __virtio_cread_many(struct virtio_device *vdev, 306 + unsigned int offset, 307 + void *buf, size_t count, size_t bytes) 308 + { 309 + u32 old, gen = vdev->config->generation ? 310 + vdev->config->generation(vdev) : 0; 311 + int i; 312 + 313 + do { 314 + old = gen; 315 + 316 + for (i = 0; i < count; i++) 317 + vdev->config->get(vdev, offset + bytes * i, 318 + buf + i * bytes, bytes); 319 + 320 + gen = vdev->config->generation ? 321 + vdev->config->generation(vdev) : 0; 322 + } while (gen != old); 323 + } 324 + 325 + 308 326 static inline void virtio_cread_bytes(struct virtio_device *vdev, 309 327 unsigned int offset, 310 328 void *buf, size_t len) 311 329 { 312 - int i; 313 - 314 - for (i = 0; i < len; i++) 315 - vdev->config->get(vdev, offset + i, buf + i, 1); 330 + __virtio_cread_many(vdev, offset, buf, len, 1); 316 331 } 317 332 318 333 static inline void virtio_cwrite8(struct virtio_device *vdev, ··· 375 352 { 376 353 u64 ret; 377 354 vdev->config->get(vdev, offset, &ret, sizeof(ret)); 355 + __virtio_cread_many(vdev, offset, &ret, 1, sizeof(ret)); 378 356 return virtio64_to_cpu(vdev, (__force __virtio64)ret); 379 357 } 380 358