|
@@ -235,8 +235,73 @@ static u32 vega10_ih_get_wptr(struct amdgpu_device *adev)
|
|
|
*/
|
|
|
static bool vega10_ih_prescreen_iv(struct amdgpu_device *adev)
|
|
|
{
|
|
|
- /* TODO: Filter known pending page faults */
|
|
|
+ u32 ring_index = adev->irq.ih.rptr >> 2;
|
|
|
+ u32 dw0, dw3, dw4, dw5;
|
|
|
+ u16 pasid;
|
|
|
+ u64 addr, key;
|
|
|
+ struct amdgpu_vm *vm;
|
|
|
+ int r;
|
|
|
+
|
|
|
+ dw0 = le32_to_cpu(adev->irq.ih.ring[ring_index + 0]);
|
|
|
+ dw3 = le32_to_cpu(adev->irq.ih.ring[ring_index + 3]);
|
|
|
+ dw4 = le32_to_cpu(adev->irq.ih.ring[ring_index + 4]);
|
|
|
+ dw5 = le32_to_cpu(adev->irq.ih.ring[ring_index + 5]);
|
|
|
+
|
|
|
+ /* Filter retry page faults, let only the first one pass. If
|
|
|
+ * there are too many outstanding faults, ignore them until
|
|
|
+ * some faults get cleared.
|
|
|
+ */
|
|
|
+ switch (dw0 & 0xff) {
|
|
|
+ case AMDGPU_IH_CLIENTID_VMC:
|
|
|
+ case AMDGPU_IH_CLIENTID_UTCL2:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* Not a VM fault */
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Not a retry fault */
|
|
|
+ if (!(dw5 & 0x80))
|
|
|
+ return true;
|
|
|
+
|
|
|
+ pasid = dw3 & 0xffff;
|
|
|
+ /* No PASID, can't identify faulting process */
|
|
|
+ if (!pasid)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ addr = ((u64)(dw5 & 0xf) << 44) | ((u64)dw4 << 12);
|
|
|
+ key = AMDGPU_VM_FAULT(pasid, addr);
|
|
|
+ r = amdgpu_ih_add_fault(adev, key);
|
|
|
+
|
|
|
+ /* Hash table is full or the fault is already being processed,
|
|
|
+ * ignore further page faults
|
|
|
+ */
|
|
|
+ if (r != 0)
|
|
|
+ goto ignore_iv;
|
|
|
+
|
|
|
+ /* Track retry faults in per-VM fault FIFO. */
|
|
|
+ spin_lock(&adev->vm_manager.pasid_lock);
|
|
|
+ vm = idr_find(&adev->vm_manager.pasid_idr, pasid);
|
|
|
+ spin_unlock(&adev->vm_manager.pasid_lock);
|
|
|
+ if (WARN_ON_ONCE(!vm)) {
|
|
|
+ /* VM not found, process it normally */
|
|
|
+ amdgpu_ih_clear_fault(adev, key);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ /* 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_ih_clear_fault(adev, key);
|
|
|
+ goto ignore_iv;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* It's the first fault for this address, process it normally */
|
|
|
return true;
|
|
|
+
|
|
|
+ignore_iv:
|
|
|
+ adev->irq.ih.rptr += 32;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -323,6 +388,14 @@ static int vega10_ih_sw_init(void *handle)
|
|
|
adev->irq.ih.use_doorbell = true;
|
|
|
adev->irq.ih.doorbell_index = AMDGPU_DOORBELL64_IH << 1;
|
|
|
|
|
|
+ adev->irq.ih.faults = kmalloc(sizeof(*adev->irq.ih.faults), GFP_KERNEL);
|
|
|
+ if (!adev->irq.ih.faults)
|
|
|
+ return -ENOMEM;
|
|
|
+ INIT_CHASH_TABLE(adev->irq.ih.faults->hash,
|
|
|
+ AMDGPU_PAGEFAULT_HASH_BITS, 8, 0);
|
|
|
+ spin_lock_init(&adev->irq.ih.faults->lock);
|
|
|
+ adev->irq.ih.faults->count = 0;
|
|
|
+
|
|
|
r = amdgpu_irq_init(adev);
|
|
|
|
|
|
return r;
|
|
@@ -335,6 +408,9 @@ static int vega10_ih_sw_fini(void *handle)
|
|
|
amdgpu_irq_fini(adev);
|
|
|
amdgpu_ih_ring_fini(adev);
|
|
|
|
|
|
+ kfree(adev->irq.ih.faults);
|
|
|
+ adev->irq.ih.faults = NULL;
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|