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

virtio: Implement get_shm_region for PCI transport

On PCI the shm regions are found using capability entries;
find a region by searching for the capability.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: kbuild test robot <lkp@intel.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Cc: kvm@vger.kernel.org
Cc: virtualization@lists.linux-foundation.org
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>

authored by

Sebastien Boeuf and committed by
Miklos Szeredi
0dd4ff93 5bfe37ca

+105 -1
+95
drivers/virtio/virtio_pci_modern.c
··· 444 444 vring_del_virtqueue(vq); 445 445 } 446 446 447 + static int virtio_pci_find_shm_cap(struct pci_dev *dev, u8 required_id, 448 + u8 *bar, u64 *offset, u64 *len) 449 + { 450 + int pos; 451 + 452 + for (pos = pci_find_capability(dev, PCI_CAP_ID_VNDR); pos > 0; 453 + pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_VNDR)) { 454 + u8 type, cap_len, id; 455 + u32 tmp32; 456 + u64 res_offset, res_length; 457 + 458 + pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 459 + cfg_type), &type); 460 + if (type != VIRTIO_PCI_CAP_SHARED_MEMORY_CFG) 461 + continue; 462 + 463 + pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 464 + cap_len), &cap_len); 465 + if (cap_len != sizeof(struct virtio_pci_cap64)) { 466 + dev_err(&dev->dev, "%s: shm cap with bad size offset:" 467 + " %d size: %d\n", __func__, pos, cap_len); 468 + continue; 469 + } 470 + 471 + pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 472 + id), &id); 473 + if (id != required_id) 474 + continue; 475 + 476 + /* Type, and ID match, looks good */ 477 + pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 478 + bar), bar); 479 + 480 + /* Read the lower 32bit of length and offset */ 481 + pci_read_config_dword(dev, pos + offsetof(struct virtio_pci_cap, 482 + offset), &tmp32); 483 + res_offset = tmp32; 484 + pci_read_config_dword(dev, pos + offsetof(struct virtio_pci_cap, 485 + length), &tmp32); 486 + res_length = tmp32; 487 + 488 + /* and now the top half */ 489 + pci_read_config_dword(dev, 490 + pos + offsetof(struct virtio_pci_cap64, 491 + offset_hi), &tmp32); 492 + res_offset |= ((u64)tmp32) << 32; 493 + pci_read_config_dword(dev, 494 + pos + offsetof(struct virtio_pci_cap64, 495 + length_hi), &tmp32); 496 + res_length |= ((u64)tmp32) << 32; 497 + 498 + *offset = res_offset; 499 + *len = res_length; 500 + 501 + return pos; 502 + } 503 + return 0; 504 + } 505 + 506 + static bool vp_get_shm_region(struct virtio_device *vdev, 507 + struct virtio_shm_region *region, u8 id) 508 + { 509 + struct virtio_pci_device *vp_dev = to_vp_device(vdev); 510 + struct pci_dev *pci_dev = vp_dev->pci_dev; 511 + u8 bar; 512 + u64 offset, len; 513 + phys_addr_t phys_addr; 514 + size_t bar_len; 515 + 516 + if (!virtio_pci_find_shm_cap(pci_dev, id, &bar, &offset, &len)) 517 + return false; 518 + 519 + phys_addr = pci_resource_start(pci_dev, bar); 520 + bar_len = pci_resource_len(pci_dev, bar); 521 + 522 + if ((offset + len) < offset) { 523 + dev_err(&pci_dev->dev, "%s: cap offset+len overflow detected\n", 524 + __func__); 525 + return false; 526 + } 527 + 528 + if (offset + len > bar_len) { 529 + dev_err(&pci_dev->dev, "%s: bar shorter than cap offset+len\n", 530 + __func__); 531 + return false; 532 + } 533 + 534 + region->len = len; 535 + region->addr = (u64) phys_addr + offset; 536 + 537 + return true; 538 + } 539 + 447 540 static const struct virtio_config_ops virtio_pci_config_nodev_ops = { 448 541 .get = NULL, 449 542 .set = NULL, ··· 551 458 .bus_name = vp_bus_name, 552 459 .set_vq_affinity = vp_set_vq_affinity, 553 460 .get_vq_affinity = vp_get_vq_affinity, 461 + .get_shm_region = vp_get_shm_region, 554 462 }; 555 463 556 464 static const struct virtio_config_ops virtio_pci_config_ops = { ··· 568 474 .bus_name = vp_bus_name, 569 475 .set_vq_affinity = vp_set_vq_affinity, 570 476 .get_vq_affinity = vp_get_vq_affinity, 477 + .get_shm_region = vp_get_shm_region, 571 478 }; 572 479 573 480 /**
+10 -1
include/uapi/linux/virtio_pci.h
··· 113 113 #define VIRTIO_PCI_CAP_DEVICE_CFG 4 114 114 /* PCI configuration access */ 115 115 #define VIRTIO_PCI_CAP_PCI_CFG 5 116 + /* Additional shared memory capability */ 117 + #define VIRTIO_PCI_CAP_SHARED_MEMORY_CFG 8 116 118 117 119 /* This is the PCI capability header: */ 118 120 struct virtio_pci_cap { ··· 123 121 __u8 cap_len; /* Generic PCI field: capability length */ 124 122 __u8 cfg_type; /* Identifies the structure. */ 125 123 __u8 bar; /* Where to find it. */ 126 - __u8 padding[3]; /* Pad to full dword. */ 124 + __u8 id; /* Multiple capabilities of the same type */ 125 + __u8 padding[2]; /* Pad to full dword. */ 127 126 __le32 offset; /* Offset within bar. */ 128 127 __le32 length; /* Length of the structure, in bytes. */ 128 + }; 129 + 130 + struct virtio_pci_cap64 { 131 + struct virtio_pci_cap cap; 132 + __le32 offset_hi; /* Most sig 32 bits of offset */ 133 + __le32 length_hi; /* Most sig 32 bits of length */ 129 134 }; 130 135 131 136 struct virtio_pci_notify_cap {