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

drm/amdgpu: use ring/hash for fault handling on GMC9 v3

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
v3: check the timeout to make sure all entries age

Signed-off-by: Christian König <christian.koenig@amd.com>
Reviewed-by: Felix Kuehling <Felix.Kuehling@amd.com> (v2)
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>

authored by

Christian König and committed by
Alex Deucher
c1a8abd9 8c65fe5f

+92 -57
+55
drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
··· 240 240 dev_info(adev->dev, "AGP: %lluM 0x%016llX - 0x%016llX\n", 241 241 mc->agp_size >> 20, mc->agp_start, mc->agp_end); 242 242 } 243 + 244 + /** 245 + * amdgpu_gmc_filter_faults - filter VM faults 246 + * 247 + * @adev: amdgpu device structure 248 + * @addr: address of the VM fault 249 + * @pasid: PASID of the process causing the fault 250 + * @timestamp: timestamp of the fault 251 + * 252 + * Returns: 253 + * True if the fault was filtered and should not be processed further. 254 + * False if the fault is a new one and needs to be handled. 255 + */ 256 + bool amdgpu_gmc_filter_faults(struct amdgpu_device *adev, uint64_t addr, 257 + uint16_t pasid, uint64_t timestamp) 258 + { 259 + struct amdgpu_gmc *gmc = &adev->gmc; 260 + 261 + uint64_t stamp, key = addr << 4 | pasid; 262 + struct amdgpu_gmc_fault *fault; 263 + uint32_t hash; 264 + 265 + /* If we don't have space left in the ring buffer return immediately */ 266 + stamp = max(timestamp, AMDGPU_GMC_FAULT_TIMEOUT + 1) - 267 + AMDGPU_GMC_FAULT_TIMEOUT; 268 + if (gmc->fault_ring[gmc->last_fault].timestamp >= stamp) 269 + return true; 270 + 271 + /* Try to find the fault in the hash */ 272 + hash = hash_64(key, AMDGPU_GMC_FAULT_HASH_ORDER); 273 + fault = &gmc->fault_ring[gmc->fault_hash[hash].idx]; 274 + while (fault->timestamp >= stamp) { 275 + uint64_t tmp; 276 + 277 + if (fault->key == key) 278 + return true; 279 + 280 + tmp = fault->timestamp; 281 + fault = &gmc->fault_ring[fault->next]; 282 + 283 + /* Check if the entry was reused */ 284 + if (fault->timestamp >= tmp) 285 + break; 286 + } 287 + 288 + /* Add the fault to the ring */ 289 + fault = &gmc->fault_ring[gmc->last_fault]; 290 + fault->key = key; 291 + fault->timestamp = timestamp; 292 + 293 + /* And update the hash */ 294 + fault->next = gmc->fault_hash[hash].idx; 295 + gmc->fault_hash[hash].idx = gmc->last_fault++; 296 + return false; 297 + }
+34
drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
··· 43 43 */ 44 44 #define AMDGPU_GMC_HOLE_MASK 0x0000ffffffffffffULL 45 45 46 + /* 47 + * Ring size as power of two for the log of recent faults. 48 + */ 49 + #define AMDGPU_GMC_FAULT_RING_ORDER 8 50 + #define AMDGPU_GMC_FAULT_RING_SIZE (1 << AMDGPU_GMC_FAULT_RING_ORDER) 51 + 52 + /* 53 + * Hash size as power of two for the log of recent faults 54 + */ 55 + #define AMDGPU_GMC_FAULT_HASH_ORDER 8 56 + #define AMDGPU_GMC_FAULT_HASH_SIZE (1 << AMDGPU_GMC_FAULT_HASH_ORDER) 57 + 58 + /* 59 + * Number of IH timestamp ticks until a fault is considered handled 60 + */ 61 + #define AMDGPU_GMC_FAULT_TIMEOUT 5000ULL 62 + 46 63 struct firmware; 64 + 65 + /* 66 + * GMC page fault information 67 + */ 68 + struct amdgpu_gmc_fault { 69 + uint64_t timestamp; 70 + uint64_t next:AMDGPU_GMC_FAULT_RING_ORDER; 71 + uint64_t key:52; 72 + }; 47 73 48 74 /* 49 75 * VMHUB structures, functions & helpers ··· 167 141 struct kfd_vm_fault_info *vm_fault_info; 168 142 atomic_t vm_fault_info_updated; 169 143 144 + struct amdgpu_gmc_fault fault_ring[AMDGPU_GMC_FAULT_RING_SIZE]; 145 + struct { 146 + uint64_t idx:AMDGPU_GMC_FAULT_RING_ORDER; 147 + } fault_hash[AMDGPU_GMC_FAULT_HASH_SIZE]; 148 + uint64_t last_fault:AMDGPU_GMC_FAULT_RING_ORDER; 149 + 170 150 const struct amdgpu_gmc_funcs *gmc_funcs; 171 151 172 152 struct amdgpu_xgmi xgmi; ··· 227 195 struct amdgpu_gmc *mc); 228 196 void amdgpu_gmc_agp_location(struct amdgpu_device *adev, 229 197 struct amdgpu_gmc *mc); 198 + bool amdgpu_gmc_filter_faults(struct amdgpu_device *adev, uint64_t addr, 199 + uint16_t pasid, uint64_t timestamp); 230 200 231 201 #endif
+3 -57
drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
··· 301 301 return 0; 302 302 } 303 303 304 - /** 305 - * vega10_ih_prescreen_iv - prescreen an interrupt vector 306 - * 307 - * @adev: amdgpu_device pointer 308 - * 309 - * Returns true if the interrupt vector should be further processed. 310 - */ 311 - static bool gmc_v9_0_prescreen_iv(struct amdgpu_device *adev, 312 - struct amdgpu_iv_entry *entry, 313 - uint64_t addr) 314 - { 315 - struct amdgpu_vm *vm; 316 - u64 key; 317 - int r; 318 - 319 - /* No PASID, can't identify faulting process */ 320 - if (!entry->pasid) 321 - return true; 322 - 323 - /* Not a retry fault */ 324 - if (!(entry->src_data[1] & 0x80)) 325 - return true; 326 - 327 - /* Track retry faults in per-VM fault FIFO. */ 328 - spin_lock(&adev->vm_manager.pasid_lock); 329 - vm = idr_find(&adev->vm_manager.pasid_idr, entry->pasid); 330 - if (!vm) { 331 - /* VM not found, process it normally */ 332 - spin_unlock(&adev->vm_manager.pasid_lock); 333 - return true; 334 - } 335 - 336 - key = AMDGPU_VM_FAULT(entry->pasid, addr); 337 - r = amdgpu_vm_add_fault(vm->fault_hash, key); 338 - 339 - /* Hash table is full or the fault is already being processed, 340 - * ignore further page faults 341 - */ 342 - if (r != 0) { 343 - spin_unlock(&adev->vm_manager.pasid_lock); 344 - return false; 345 - } 346 - /* No locking required with single writer and single reader */ 347 - r = kfifo_put(&vm->faults, key); 348 - if (!r) { 349 - /* FIFO is full. Ignore it until there is space */ 350 - amdgpu_vm_clear_fault(vm->fault_hash, key); 351 - spin_unlock(&adev->vm_manager.pasid_lock); 352 - return false; 353 - } 354 - 355 - spin_unlock(&adev->vm_manager.pasid_lock); 356 - /* It's the first fault for this address, process it normally */ 357 - return true; 358 - } 359 - 360 304 static int gmc_v9_0_process_interrupt(struct amdgpu_device *adev, 361 305 struct amdgpu_irq_src *source, 362 306 struct amdgpu_iv_entry *entry) ··· 313 369 addr = (u64)entry->src_data[0] << 12; 314 370 addr |= ((u64)entry->src_data[1] & 0xf) << 44; 315 371 316 - if (!gmc_v9_0_prescreen_iv(adev, entry, addr)) 372 + if (retry_fault && amdgpu_gmc_filter_faults(adev, addr, entry->pasid, 373 + entry->timestamp)) 317 374 return 1; /* This also prevents sending it to KFD */ 318 375 376 + /* If it's the first fault for this address, process it normally */ 319 377 if (!amdgpu_sriov_vf(adev)) { 320 378 status = RREG32(hub->vm_l2_pro_fault_status); 321 379 WREG32_P(hub->vm_l2_pro_fault_cntl, 1, ~1);