[PATCH 1/2] drm/amdgpu: use ring/hash for fault handling on GMC9 v2
Kuehling, Felix
Felix.Kuehling at amd.com
Fri Mar 8 01:37:53 UTC 2019
Hmm, that's a clever (and elegant) little data structure. The series is
Reviewed-by: Felix Kuehling <Felix.Kuehling at amd.com>
Regards,
Felix
On 3/7/2019 8:28 AM, Christian König wrote:
> Further testing showed that the idea with the chash doesn't work as expected.
> Especially we can't predict when we can remove the entries from the hash again.
>
> So replace the chash with a ring buffer/hash mix where entries in the container
> age automatically based on their timestamp.
>
> v2: use ring buffer / hash mix
>
> Signed-off-by: Christian König <christian.koenig at amd.com>
> ---
> drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c | 49 ++++++++++++++++++++
> drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h | 34 ++++++++++++++
> drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c | 60 ++-----------------------
> 3 files changed, 86 insertions(+), 57 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
> index 5a32a0d2ad31..579cadd16886 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
> @@ -240,3 +240,52 @@ void amdgpu_gmc_agp_location(struct amdgpu_device *adev, struct amdgpu_gmc *mc)
> dev_info(adev->dev, "AGP: %lluM 0x%016llX - 0x%016llX\n",
> mc->agp_size >> 20, mc->agp_start, mc->agp_end);
> }
> +
> +/**
> + * amdgpu_gmc_filter_faults - filter VM faults
> + *
> + * @adev: amdgpu device structure
> + * @addr: address of the VM fault
> + * @pasid: PASID of the process causing the fault
> + * @timestamp: timestamp of the fault
> + *
> + * Returns:
> + * True if the fault was filtered and should not be processed further.
> + * False if the fault is a new one and needs to be handled.
> + */
> +bool amdgpu_gmc_filter_faults(struct amdgpu_device *adev, uint64_t addr,
> + uint16_t pasid, uint64_t timestamp)
> +{
> + struct amdgpu_gmc *gmc = &adev->gmc;
> +
> + uint64_t stamp, key = addr << 4 | pasid;
> + struct amdgpu_gmc_fault *fault;
> + uint32_t hash;
> +
> + /* If we don't have space left in the ring buffer return immediately */
> + stamp = max(timestamp, AMDGPU_GMC_FAULT_TIMEOUT + 1) -
> + AMDGPU_GMC_FAULT_TIMEOUT;
> + if (gmc->fault_ring[gmc->last_fault].timestamp >= stamp)
> + return true;
> +
> + /* Try to find the fault in the hash */
> + hash = hash_64(key, AMDGPU_GMC_FAULT_HASH_ORDER);
> + fault = &gmc->fault_ring[gmc->fault_hash[hash].idx];
> + do {
> + if (fault->key == key)
> + return true;
> +
> + stamp = fault->timestamp;
> + fault = &gmc->fault_ring[fault->next];
> + } while (fault->timestamp < stamp);
> +
> + /* Add the fault to the ring */
> + fault = &gmc->fault_ring[gmc->last_fault];
> + fault->key = key;
> + fault->timestamp = timestamp;
> +
> + /* And update the hash */
> + fault->next = gmc->fault_hash[hash].idx;
> + gmc->fault_hash[hash].idx = gmc->last_fault++;
> + return false;
> +}
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
> index 6ce45664ff87..071145ac67b5 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
> @@ -43,8 +43,34 @@
> */
> #define AMDGPU_GMC_HOLE_MASK 0x0000ffffffffffffULL
>
> +/*
> + * Ring size as power of two for the log of recent faults.
> + */
> +#define AMDGPU_GMC_FAULT_RING_ORDER 8
> +#define AMDGPU_GMC_FAULT_RING_SIZE (1 << AMDGPU_GMC_FAULT_RING_ORDER)
> +
> +/*
> + * Hash size as power of two for the log of recent faults
> + */
> +#define AMDGPU_GMC_FAULT_HASH_ORDER 8
> +#define AMDGPU_GMC_FAULT_HASH_SIZE (1 << AMDGPU_GMC_FAULT_HASH_ORDER)
> +
> +/*
> + * Number of IH timestamp ticks until a fault is considered handled
> + */
> +#define AMDGPU_GMC_FAULT_TIMEOUT 5000ULL
> +
> struct firmware;
>
> +/*
> + * GMC page fault information
> + */
> +struct amdgpu_gmc_fault {
> + uint64_t timestamp;
> + uint64_t next:AMDGPU_GMC_FAULT_RING_ORDER;
> + uint64_t key:52;
> +};
> +
> /*
> * VMHUB structures, functions & helpers
> */
> @@ -141,6 +167,12 @@ struct amdgpu_gmc {
> struct kfd_vm_fault_info *vm_fault_info;
> atomic_t vm_fault_info_updated;
>
> + struct amdgpu_gmc_fault fault_ring[AMDGPU_GMC_FAULT_RING_SIZE];
> + struct {
> + uint64_t idx:AMDGPU_GMC_FAULT_RING_ORDER;
> + } fault_hash[AMDGPU_GMC_FAULT_HASH_SIZE];
> + uint64_t last_fault:AMDGPU_GMC_FAULT_RING_ORDER;
> +
> const struct amdgpu_gmc_funcs *gmc_funcs;
>
> struct amdgpu_xgmi xgmi;
> @@ -195,5 +227,7 @@ void amdgpu_gmc_gart_location(struct amdgpu_device *adev,
> struct amdgpu_gmc *mc);
> void amdgpu_gmc_agp_location(struct amdgpu_device *adev,
> struct amdgpu_gmc *mc);
> +bool amdgpu_gmc_filter_faults(struct amdgpu_device *adev, uint64_t addr,
> + uint16_t pasid, uint64_t timestamp);
>
> #endif
> diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
> index 84904bd680df..0c37c0afb1bd 100644
> --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
> +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
> @@ -416,62 +416,6 @@ static int gmc_v9_0_vm_fault_interrupt_state(struct amdgpu_device *adev,
> return 0;
> }
>
> -/**
> - * vega10_ih_prescreen_iv - prescreen an interrupt vector
> - *
> - * @adev: amdgpu_device pointer
> - *
> - * Returns true if the interrupt vector should be further processed.
> - */
> -static bool gmc_v9_0_prescreen_iv(struct amdgpu_device *adev,
> - struct amdgpu_iv_entry *entry,
> - uint64_t addr)
> -{
> - struct amdgpu_vm *vm;
> - u64 key;
> - int r;
> -
> - /* No PASID, can't identify faulting process */
> - if (!entry->pasid)
> - return true;
> -
> - /* Not a retry fault */
> - if (!(entry->src_data[1] & 0x80))
> - return true;
> -
> - /* Track retry faults in per-VM fault FIFO. */
> - spin_lock(&adev->vm_manager.pasid_lock);
> - vm = idr_find(&adev->vm_manager.pasid_idr, entry->pasid);
> - if (!vm) {
> - /* VM not found, process it normally */
> - spin_unlock(&adev->vm_manager.pasid_lock);
> - return true;
> - }
> -
> - key = AMDGPU_VM_FAULT(entry->pasid, addr);
> - r = amdgpu_vm_add_fault(vm->fault_hash, key);
> -
> - /* Hash table is full or the fault is already being processed,
> - * ignore further page faults
> - */
> - if (r != 0) {
> - spin_unlock(&adev->vm_manager.pasid_lock);
> - return false;
> - }
> - /* No locking required with single writer and single reader */
> - r = kfifo_put(&vm->faults, key);
> - if (!r) {
> - /* FIFO is full. Ignore it until there is space */
> - amdgpu_vm_clear_fault(vm->fault_hash, key);
> - spin_unlock(&adev->vm_manager.pasid_lock);
> - return false;
> - }
> -
> - spin_unlock(&adev->vm_manager.pasid_lock);
> - /* It's the first fault for this address, process it normally */
> - return true;
> -}
> -
> static int gmc_v9_0_process_interrupt(struct amdgpu_device *adev,
> struct amdgpu_irq_src *source,
> struct amdgpu_iv_entry *entry)
> @@ -484,9 +428,11 @@ static int gmc_v9_0_process_interrupt(struct amdgpu_device *adev,
> addr = (u64)entry->src_data[0] << 12;
> addr |= ((u64)entry->src_data[1] & 0xf) << 44;
>
> - if (!gmc_v9_0_prescreen_iv(adev, entry, addr))
> + if (retry_fault && amdgpu_gmc_filter_faults(adev, addr, entry->pasid,
> + entry->timestamp))
> return 1; /* This also prevents sending it to KFD */
>
> + /* If it's the first fault for this address, process it normally */
> if (!amdgpu_sriov_vf(adev)) {
> status = RREG32(hub->vm_l2_pro_fault_status);
> WREG32_P(hub->vm_l2_pro_fault_cntl, 1, ~1);
More information about the amd-gfx
mailing list