On 9/23/19 9:25 AM, Tiwei Bie wrote:
> On Tue, Sep 17, 2019 at 04:49:00PM +0200, Adrian Moreno wrote:
>> When IOMMU is enabled the incoming log address is in IOVA space. In that
>> case, look in IOTLB table and translate the resulting HVA to GPA.
>>
>> If IOMMU is not enabled, the incoming log address is already a GPA so no
>> transformation is needed.
>>
>> This change makes page logging work when IOVA_VA is selected in the guest.
> 
> Besides the log address of the ring, when IOMMU is enabled,
> the addresses in descriptors are also IOVAs and should be
> translated to GPAs before doing the dirty page logging.
> 
Thanks Tiwei. You're right. In fact, it's not the only place where IOVAs are
assumed to be GPAs, for example in vhost.h:gpa_to_hpa:
/* Convert guest physical address to host physical address */
static __rte_always_inline rte_iova_t
gpa_to_hpa(struct virtio_net *dev, uint64_t gpa, uint64_t size)
{
        uint32_t i;
        struct guest_page *page;

        for (i = 0; i < dev->nr_guest_pages; i++) {
                page = &dev->guest_pages[i];

                if (gpa >= page->guest_phys_addr &&
                    gpa + size < page->guest_phys_addr + page->size) {
                        return gpa - page->guest_phys_addr +
                               page->host_phys_addr;
                }
        }

        return 0;
}

used in ZERO-COPY mode to check if the IOVA range is continuous in host physical
memory and called buffer IOVAs. If I'm not mistaken this should also be
translated as:

IOVA --> HVA (iotlb lookup)
HVA --> GPA (mem_region lookup)
GPA --> HIOVA (guest_page lookup)

Right?

Thanks.
Adrián

>> Further information: https://bugs.dpdk.org/show_bug.cgi?id=337
> 
> As this is fixing a real bug, we also need a "Fixes: " line and Cc stable.
> 
> Thanks!
> Tiwei
> 
>>
>> Cc: Maxime Coquelin <maxime.coque...@redhat.com>
>> Reported-by: Pei Zhang <pezh...@redhat.com>
>>
>> Signed-off-by: Adrian Moreno <amore...@redhat.com>
>> ---
>>  lib/librte_vhost/vhost.c      |  1 +
>>  lib/librte_vhost/vhost_user.c | 78 ++++++++++++++++++++++++++++++++++-
>>  2 files changed, 78 insertions(+), 1 deletion(-)
>>
>> diff --git a/lib/librte_vhost/vhost.c b/lib/librte_vhost/vhost.c
>> index 981837b5d..e57dda22f 100644
>> --- a/lib/librte_vhost/vhost.c
>> +++ b/lib/librte_vhost/vhost.c
>> @@ -383,6 +383,7 @@ vring_invalidate(struct virtio_net *dev, struct 
>> vhost_virtqueue *vq)
>>      vq->desc = NULL;
>>      vq->avail = NULL;
>>      vq->used = NULL;
>> +    vq->log_guest_addr = 0;
>>  
>>      if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
>>              vhost_user_iotlb_wr_unlock(vq);
>> diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
>> index 0b72648a5..35fca00fe 100644
>> --- a/lib/librte_vhost/vhost_user.c
>> +++ b/lib/librte_vhost/vhost_user.c
>> @@ -570,6 +570,74 @@ ring_addr_to_vva(struct virtio_net *dev, struct 
>> vhost_virtqueue *vq,
>>      return qva_to_vva(dev, ra, size);
>>  }
>>  
>> +/*
>> + * Converts Vhost Virtual Address to Guest Physical Address
>> + */
>> +static uint64_t
>> +vva_to_gpa(struct virtio_net *dev, uint64_t vva, uint64_t *len)
>> +{
>> +    struct rte_vhost_mem_region *r;
>> +    uint32_t i;
>> +
>> +    if (unlikely(!dev || !dev->mem))
>> +            goto out_error;
>> +
>> +    /* Find the region where the address lives. */
>> +    for (i = 0; i < dev->mem->nregions; i++) {
>> +            r = &dev->mem->regions[i];
>> +
>> +            if (vva >= r->host_user_addr &&
>> +                vva <  r->host_user_addr + r->size) {
>> +
>> +                    if (unlikely(vva + *len > r->host_user_addr + r->size))
>> +                            *len = r->guest_user_addr + r->size - vva;
>> +
>> +                    return r->guest_phys_addr + vva - r->host_user_addr;
>> +            }
>> +    }
>> +out_error:
>> +    *len = 0;
>> +
>> +    return 0;
>> +}
>> +
>> +/*
>> + * Converts vring log address to GPA
>> + * If IOMMU is enabled, the log address is IOVA
>> + * If IOMMU not enabled, the log address is already GPA
>> + */
>> +static uint64_t
>> +translate_log_addr(struct virtio_net *dev, struct vhost_virtqueue *vq,
>> +            uint64_t log_addr)
>> +{
>> +    if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM)) {
>> +            const uint64_t exp_size = sizeof(struct vring_used) +
>> +                    sizeof(struct vring_used_elem) * vq->size;
>> +            uint64_t vva, gpa;
>> +            uint64_t size = exp_size;
>> +
>> +            vva = vhost_user_iotlb_cache_find(vq, log_addr,
>> +                                    &size, VHOST_ACCESS_RW);
>> +            if (size != exp_size) {
>> +                    vhost_user_iotlb_miss(dev, log_addr + size,
>> +                                          VHOST_ACCESS_RW);
>> +                    return 0;
>> +            }
>> +
>> +            gpa = vva_to_gpa(dev, vva, &size);
>> +            if (size != exp_size) {
>> +                    RTE_LOG(ERR, VHOST_CONFIG,
>> +                            "VQ: Failed to find GPA mapping for log_addr."
>> +                            "log_addr: 0x%0lx vva: 0x%0lx\n",
>> +                            log_addr, vva);
>> +                    return 0;
>> +            }
>> +            return gpa;
>> +
>> +    } else
>> +            return log_addr;
>> +}
>> +
>>  static struct virtio_net *
>>  translate_ring_addresses(struct virtio_net *dev, int vq_index)
>>  {
>> @@ -676,7 +744,15 @@ translate_ring_addresses(struct virtio_net *dev, int 
>> vq_index)
>>              vq->last_avail_idx = vq->used->idx;
>>      }
>>  
>> -    vq->log_guest_addr = addr->log_guest_addr;
>> +    vq->log_guest_addr =
>> +            translate_log_addr(dev, vq, addr->log_guest_addr);
>> +    if (vq->log_guest_addr == 0) {
>> +            RTE_LOG(DEBUG, VHOST_CONFIG,
>> +                    "(%d) failed to map log_guest_addr .\n",
>> +                    dev->vid);
>> +            return dev;
>> +    }
>> +
>>  
>>      VHOST_LOG_DEBUG(VHOST_CONFIG, "(%d) mapped address desc: %p\n",
>>                      dev->vid, vq->desc);
>> -- 
>> 2.21.0
>>

Reply via email to