qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

hw/arm/smmuv3: Cache/invalidate config data

Let's cache config data to avoid fetching and parsing STE/CD
structures on each translation. We invalidate them on data structure
invalidation commands.

We put in place a per-smmu mutex to protect the config cache. This
will be useful too to protect the IOTLB cache. The caches can be
accessed without BQL, ie. in IO dataplane. The same kind of mutex was
put in place in the intel viommu.

Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1529653501-15358-3-git-send-email-eric.auger@redhat.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

authored by

Eric Auger and committed by
Peter Maydell
32cfd7f3 9122bea9

+164 -7
+22 -2
hw/arm/smmu-common.c
··· 310 310 return &sdev->as; 311 311 } 312 312 313 + IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) 314 + { 315 + uint8_t bus_n, devfn; 316 + SMMUPciBus *smmu_bus; 317 + SMMUDevice *smmu; 318 + 319 + bus_n = PCI_BUS_NUM(sid); 320 + smmu_bus = smmu_find_smmu_pcibus(s, bus_n); 321 + if (smmu_bus) { 322 + devfn = sid & 0x7; 323 + smmu = smmu_bus->pbdev[devfn]; 324 + if (smmu) { 325 + return &smmu->iommu; 326 + } 327 + } 328 + return NULL; 329 + } 330 + 313 331 static void smmu_base_realize(DeviceState *dev, Error **errp) 314 332 { 315 333 SMMUState *s = ARM_SMMU(dev); ··· 321 339 error_propagate(errp, local_err); 322 340 return; 323 341 } 324 - 342 + s->configs = g_hash_table_new_full(NULL, NULL, NULL, g_free); 325 343 s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL); 326 344 327 345 if (s->primary_bus) { ··· 333 351 334 352 static void smmu_base_reset(DeviceState *dev) 335 353 { 336 - /* will be filled later on */ 354 + SMMUState *s = ARM_SMMU(dev); 355 + 356 + g_hash_table_remove_all(s->configs); 337 357 } 338 358 339 359 static Property smmu_dev_properties[] = {
+130 -5
hw/arm/smmuv3.c
··· 544 544 return decode_cd(cfg, &cd, event); 545 545 } 546 546 547 + /** 548 + * smmuv3_get_config - Look up for a cached copy of configuration data for 549 + * @sdev and on cache miss performs a configuration structure decoding from 550 + * guest RAM. 551 + * 552 + * @sdev: SMMUDevice handle 553 + * @event: output event info 554 + * 555 + * The configuration cache contains data resulting from both STE and CD 556 + * decoding under the form of an SMMUTransCfg struct. The hash table is indexed 557 + * by the SMMUDevice handle. 558 + */ 559 + static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev, SMMUEventInfo *event) 560 + { 561 + SMMUv3State *s = sdev->smmu; 562 + SMMUState *bc = &s->smmu_state; 563 + SMMUTransCfg *cfg; 564 + 565 + cfg = g_hash_table_lookup(bc->configs, sdev); 566 + if (cfg) { 567 + sdev->cfg_cache_hits++; 568 + trace_smmuv3_config_cache_hit(smmu_get_sid(sdev), 569 + sdev->cfg_cache_hits, sdev->cfg_cache_misses, 570 + 100 * sdev->cfg_cache_hits / 571 + (sdev->cfg_cache_hits + sdev->cfg_cache_misses)); 572 + } else { 573 + sdev->cfg_cache_misses++; 574 + trace_smmuv3_config_cache_miss(smmu_get_sid(sdev), 575 + sdev->cfg_cache_hits, sdev->cfg_cache_misses, 576 + 100 * sdev->cfg_cache_hits / 577 + (sdev->cfg_cache_hits + sdev->cfg_cache_misses)); 578 + cfg = g_new0(SMMUTransCfg, 1); 579 + 580 + if (!smmuv3_decode_config(&sdev->iommu, cfg, event)) { 581 + g_hash_table_insert(bc->configs, sdev, cfg); 582 + } else { 583 + g_free(cfg); 584 + cfg = NULL; 585 + } 586 + } 587 + return cfg; 588 + } 589 + 590 + static void smmuv3_flush_config(SMMUDevice *sdev) 591 + { 592 + SMMUv3State *s = sdev->smmu; 593 + SMMUState *bc = &s->smmu_state; 594 + 595 + trace_smmuv3_config_cache_inv(smmu_get_sid(sdev)); 596 + g_hash_table_remove(bc->configs, sdev); 597 + } 598 + 547 599 static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, 548 600 IOMMUAccessFlags flag, int iommu_idx) 549 601 { ··· 553 605 SMMUEventInfo event = {.type = SMMU_EVT_NONE, .sid = sid}; 554 606 SMMUPTWEventInfo ptw_info = {}; 555 607 SMMUTranslationStatus status; 556 - SMMUTransCfg cfg = {}; 608 + SMMUTransCfg *cfg = NULL; 557 609 IOMMUTLBEntry entry = { 558 610 .target_as = &address_space_memory, 559 611 .iova = addr, ··· 562 614 .perm = IOMMU_NONE, 563 615 }; 564 616 617 + qemu_mutex_lock(&s->mutex); 618 + 565 619 if (!smmu_enabled(s)) { 566 620 status = SMMU_TRANS_DISABLE; 567 621 goto epilogue; 568 622 } 569 623 570 - if (smmuv3_decode_config(mr, &cfg, &event)) { 624 + cfg = smmuv3_get_config(sdev, &event); 625 + if (!cfg) { 571 626 status = SMMU_TRANS_ERROR; 572 627 goto epilogue; 573 628 } 574 629 575 - if (cfg.aborted) { 630 + if (cfg->aborted) { 576 631 status = SMMU_TRANS_ABORT; 577 632 goto epilogue; 578 633 } 579 634 580 - if (cfg.bypassed) { 635 + if (cfg->bypassed) { 581 636 status = SMMU_TRANS_BYPASS; 582 637 goto epilogue; 583 638 } 584 639 585 - if (smmu_ptw(&cfg, addr, flag, &entry, &ptw_info)) { 640 + if (smmu_ptw(cfg, addr, flag, &entry, &ptw_info)) { 586 641 switch (ptw_info.type) { 587 642 case SMMU_PTW_ERR_WALK_EABT: 588 643 event.type = SMMU_EVT_F_WALK_EABT; ··· 628 683 } 629 684 630 685 epilogue: 686 + qemu_mutex_unlock(&s->mutex); 631 687 switch (status) { 632 688 case SMMU_TRANS_SUCCESS: 633 689 entry.perm = flag; ··· 664 720 665 721 static int smmuv3_cmdq_consume(SMMUv3State *s) 666 722 { 723 + SMMUState *bs = ARM_SMMU(s); 667 724 SMMUCmdError cmd_error = SMMU_CERROR_NONE; 668 725 SMMUQueue *q = &s->cmdq; 669 726 SMMUCommandType type = 0; ··· 698 755 699 756 trace_smmuv3_cmdq_opcode(smmu_cmd_string(type)); 700 757 758 + qemu_mutex_lock(&s->mutex); 701 759 switch (type) { 702 760 case SMMU_CMD_SYNC: 703 761 if (CMD_SYNC_CS(&cmd) & CMD_SYNC_SIG_IRQ) { ··· 706 764 break; 707 765 case SMMU_CMD_PREFETCH_CONFIG: 708 766 case SMMU_CMD_PREFETCH_ADDR: 767 + break; 709 768 case SMMU_CMD_CFGI_STE: 769 + { 770 + uint32_t sid = CMD_SID(&cmd); 771 + IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); 772 + SMMUDevice *sdev; 773 + 774 + if (CMD_SSEC(&cmd)) { 775 + cmd_error = SMMU_CERROR_ILL; 776 + break; 777 + } 778 + 779 + if (!mr) { 780 + break; 781 + } 782 + 783 + trace_smmuv3_cmdq_cfgi_ste(sid); 784 + sdev = container_of(mr, SMMUDevice, iommu); 785 + smmuv3_flush_config(sdev); 786 + 787 + break; 788 + } 710 789 case SMMU_CMD_CFGI_STE_RANGE: /* same as SMMU_CMD_CFGI_ALL */ 790 + { 791 + uint32_t start = CMD_SID(&cmd), end, i; 792 + uint8_t range = CMD_STE_RANGE(&cmd); 793 + 794 + if (CMD_SSEC(&cmd)) { 795 + cmd_error = SMMU_CERROR_ILL; 796 + break; 797 + } 798 + 799 + end = start + (1 << (range + 1)) - 1; 800 + trace_smmuv3_cmdq_cfgi_ste_range(start, end); 801 + 802 + for (i = start; i <= end; i++) { 803 + IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, i); 804 + SMMUDevice *sdev; 805 + 806 + if (!mr) { 807 + continue; 808 + } 809 + sdev = container_of(mr, SMMUDevice, iommu); 810 + smmuv3_flush_config(sdev); 811 + } 812 + break; 813 + } 711 814 case SMMU_CMD_CFGI_CD: 712 815 case SMMU_CMD_CFGI_CD_ALL: 816 + { 817 + uint32_t sid = CMD_SID(&cmd); 818 + IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); 819 + SMMUDevice *sdev; 820 + 821 + if (CMD_SSEC(&cmd)) { 822 + cmd_error = SMMU_CERROR_ILL; 823 + break; 824 + } 825 + 826 + if (!mr) { 827 + break; 828 + } 829 + 830 + trace_smmuv3_cmdq_cfgi_cd(sid); 831 + sdev = container_of(mr, SMMUDevice, iommu); 832 + smmuv3_flush_config(sdev); 833 + break; 834 + } 713 835 case SMMU_CMD_TLBI_NH_ALL: 714 836 case SMMU_CMD_TLBI_NH_ASID: 715 837 case SMMU_CMD_TLBI_NH_VA: ··· 735 857 "Illegal command type: %d\n", CMD_TYPE(&cmd)); 736 858 break; 737 859 } 860 + qemu_mutex_unlock(&s->mutex); 738 861 if (cmd_error) { 739 862 break; 740 863 } ··· 1113 1236 error_propagate(errp, local_err); 1114 1237 return; 1115 1238 } 1239 + 1240 + qemu_mutex_init(&s->mutex); 1116 1241 1117 1242 memory_region_init_io(&sys->iomem, OBJECT(s), 1118 1243 &smmu_mem_ops, sys, TYPE_ARM_SMMUV3, 0x20000);
+6
hw/arm/trace-events
··· 40 40 smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64 41 41 smmuv3_decode_cd(uint32_t oas) "oas=%d" 42 42 smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d" 43 + smmuv3_cmdq_cfgi_ste(int streamid) "streamid =%d" 44 + smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%d - end=0x%d" 45 + smmuv3_cmdq_cfgi_cd(uint32_t sid) "streamid = %d" 46 + smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid %d (hits=%d, misses=%d, hit rate=%d)" 47 + smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid %d (hits=%d, misses=%d, hit rate=%d)" 48 + smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid %d"
+5
include/hw/arm/smmu-common.h
··· 75 75 int devfn; 76 76 IOMMUMemoryRegion iommu; 77 77 AddressSpace as; 78 + uint32_t cfg_cache_hits; 79 + uint32_t cfg_cache_misses; 78 80 } SMMUDevice; 79 81 80 82 typedef struct SMMUNotifierNode { ··· 141 143 * the input iova and translation config and return the TT specific info 142 144 */ 143 145 SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova); 146 + 147 + /* Return the iommu mr associated to @sid, or NULL if none */ 148 + IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid); 144 149 145 150 #endif /* HW_ARM_SMMU_COMMON */
+1
include/hw/arm/smmuv3.h
··· 59 59 SMMUQueue eventq, cmdq; 60 60 61 61 qemu_irq irq[4]; 62 + QemuMutex mutex; 62 63 } SMMUv3State; 63 64 64 65 typedef enum {