Commit 66c45500 authored by Philip Yang's avatar Philip Yang Committed by Alex Deucher

drm/amdgpu: use new HMM APIs and helpers

HMM provides new APIs and helps in kernel 5.2-rc1 to simplify driver
path. The old hmm APIs are deprecated and will be removed in future.

Below are changes in driver:

1. Change hmm_vma_fault to hmm_range_register and hmm_range_fault which
supports range with multiple vmas, remove the multiple vmas handle path
and data structure.
2. Change hmm_vma_range_done to hmm_range_unregister.
3. Use default flags to avoid pre-fill pfn arrays.
4. Use new hmm_device_ helpers.
Signed-off-by: default avatarPhilip Yang <Philip.Yang@amd.com>
Reviewed-by: default avatarFelix Kuehling <Felix.Kuehling@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 8252562d
...@@ -519,7 +519,6 @@ void amdgpu_hmm_init_range(struct hmm_range *range) ...@@ -519,7 +519,6 @@ void amdgpu_hmm_init_range(struct hmm_range *range)
range->flags = hmm_range_flags; range->flags = hmm_range_flags;
range->values = hmm_range_values; range->values = hmm_range_values;
range->pfn_shift = PAGE_SHIFT; range->pfn_shift = PAGE_SHIFT;
range->pfns = NULL;
INIT_LIST_HEAD(&range->list); INIT_LIST_HEAD(&range->list);
} }
} }
...@@ -711,8 +711,7 @@ struct amdgpu_ttm_tt { ...@@ -711,8 +711,7 @@ struct amdgpu_ttm_tt {
struct task_struct *usertask; struct task_struct *usertask;
uint32_t userflags; uint32_t userflags;
#if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR) #if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR)
struct hmm_range *ranges; struct hmm_range *range;
int nr_ranges;
#endif #endif
}; };
...@@ -725,57 +724,36 @@ struct amdgpu_ttm_tt { ...@@ -725,57 +724,36 @@ struct amdgpu_ttm_tt {
*/ */
#if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR) #if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR)
/* Support Userptr pages cross max 16 vmas */ #define MAX_RETRY_HMM_RANGE_FAULT 16
#define MAX_NR_VMAS (16)
int amdgpu_ttm_tt_get_user_pages(struct ttm_tt *ttm, struct page **pages) int amdgpu_ttm_tt_get_user_pages(struct ttm_tt *ttm, struct page **pages)
{ {
struct amdgpu_ttm_tt *gtt = (void *)ttm; struct amdgpu_ttm_tt *gtt = (void *)ttm;
struct mm_struct *mm = gtt->usertask->mm; struct mm_struct *mm = gtt->usertask->mm;
unsigned long start = gtt->userptr; unsigned long start = gtt->userptr;
unsigned long end = start + ttm->num_pages * PAGE_SIZE; struct vm_area_struct *vma;
struct vm_area_struct *vma = NULL, *vmas[MAX_NR_VMAS]; struct hmm_range *range;
struct hmm_range *ranges; unsigned long i;
unsigned long nr_pages, i; uint64_t *pfns;
uint64_t *pfns, f; int retry = 0;
int r = 0; int r = 0;
if (!mm) /* Happens during process shutdown */ if (!mm) /* Happens during process shutdown */
return -ESRCH; return -ESRCH;
down_read(&mm->mmap_sem); vma = find_vma(mm, start);
if (unlikely(!vma || start < vma->vm_start)) {
/* user pages may cross multiple VMAs */
gtt->nr_ranges = 0;
do {
unsigned long vm_start;
if (gtt->nr_ranges >= MAX_NR_VMAS) {
DRM_ERROR("Too many VMAs in userptr range\n");
r = -EFAULT;
goto out;
}
vm_start = vma ? vma->vm_end : start;
vma = find_vma(mm, vm_start);
if (unlikely(!vma || vm_start < vma->vm_start)) {
r = -EFAULT; r = -EFAULT;
goto out; goto out;
} }
vmas[gtt->nr_ranges++] = vma;
} while (end > vma->vm_end);
DRM_DEBUG_DRIVER("0x%lx nr_ranges %d pages 0x%lx\n",
start, gtt->nr_ranges, ttm->num_pages);
if (unlikely((gtt->userflags & AMDGPU_GEM_USERPTR_ANONONLY) && if (unlikely((gtt->userflags & AMDGPU_GEM_USERPTR_ANONONLY) &&
vmas[0]->vm_file)) { vma->vm_file)) {
r = -EPERM; r = -EPERM;
goto out; goto out;
} }
ranges = kvmalloc_array(gtt->nr_ranges, sizeof(*ranges), GFP_KERNEL); range = kzalloc(sizeof(*range), GFP_KERNEL);
if (unlikely(!ranges)) { if (unlikely(!range)) {
r = -ENOMEM; r = -ENOMEM;
goto out; goto out;
} }
...@@ -786,61 +764,67 @@ int amdgpu_ttm_tt_get_user_pages(struct ttm_tt *ttm, struct page **pages) ...@@ -786,61 +764,67 @@ int amdgpu_ttm_tt_get_user_pages(struct ttm_tt *ttm, struct page **pages)
goto out_free_ranges; goto out_free_ranges;
} }
for (i = 0; i < gtt->nr_ranges; i++) amdgpu_hmm_init_range(range);
amdgpu_hmm_init_range(&ranges[i]); range->default_flags = range->flags[HMM_PFN_VALID];
range->default_flags |= amdgpu_ttm_tt_is_readonly(ttm) ?
0 : range->flags[HMM_PFN_WRITE];
range->pfn_flags_mask = 0;
range->pfns = pfns;
hmm_range_register(range, mm, start,
start + ttm->num_pages * PAGE_SIZE, PAGE_SHIFT);
f = ranges[0].flags[HMM_PFN_VALID]; retry:
f |= amdgpu_ttm_tt_is_readonly(ttm) ? /*
0 : ranges[0].flags[HMM_PFN_WRITE]; * Just wait for range to be valid, safe to ignore return value as we
memset64(pfns, f, ttm->num_pages); * will use the return value of hmm_range_fault() below under the
* mmap_sem to ascertain the validity of the range.
*/
hmm_range_wait_until_valid(range, HMM_RANGE_DEFAULT_TIMEOUT);
for (nr_pages = 0, i = 0; i < gtt->nr_ranges; i++) { down_read(&mm->mmap_sem);
ranges[i].vma = vmas[i];
ranges[i].start = max(start, vmas[i]->vm_start);
ranges[i].end = min(end, vmas[i]->vm_end);
ranges[i].pfns = pfns + nr_pages;
nr_pages += (ranges[i].end - ranges[i].start) / PAGE_SIZE;
r = hmm_vma_fault(&ranges[i], true); r = hmm_range_fault(range, true);
if (unlikely(r)) if (unlikely(r < 0)) {
break; if (likely(r == -EAGAIN)) {
/*
* return -EAGAIN, mmap_sem is dropped
*/
if (retry++ < MAX_RETRY_HMM_RANGE_FAULT)
goto retry;
else
pr_err("Retry hmm fault too many times\n");
} }
if (unlikely(r)) {
while (i--)
hmm_vma_range_done(&ranges[i]);
goto out_free_pfns; goto out_up_read;
} }
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
for (i = 0; i < ttm->num_pages; i++) { for (i = 0; i < ttm->num_pages; i++) {
pages[i] = hmm_pfn_to_page(&ranges[0], pfns[i]); pages[i] = hmm_device_entry_to_page(range, pfns[i]);
if (!pages[i]) { if (unlikely(!pages[i])) {
pr_err("Page fault failed for pfn[%lu] = 0x%llx\n", pr_err("Page fault failed for pfn[%lu] = 0x%llx\n",
i, pfns[i]); i, pfns[i]);
goto out_invalid_pfn; r = -ENOMEM;
goto out_free_pfns;
} }
} }
gtt->ranges = ranges;
gtt->range = range;
return 0; return 0;
out_up_read:
if (likely(r != -EAGAIN))
up_read(&mm->mmap_sem);
out_free_pfns: out_free_pfns:
hmm_range_unregister(range);
kvfree(pfns); kvfree(pfns);
out_free_ranges: out_free_ranges:
kvfree(ranges); kfree(range);
out: out:
up_read(&mm->mmap_sem);
return r; return r;
out_invalid_pfn:
for (i = 0; i < gtt->nr_ranges; i++)
hmm_vma_range_done(&ranges[i]);
kvfree(pfns);
kvfree(ranges);
return -ENOMEM;
} }
/** /**
...@@ -853,23 +837,23 @@ bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm) ...@@ -853,23 +837,23 @@ bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm)
{ {
struct amdgpu_ttm_tt *gtt = (void *)ttm; struct amdgpu_ttm_tt *gtt = (void *)ttm;
bool r = false; bool r = false;
int i;
if (!gtt || !gtt->userptr) if (!gtt || !gtt->userptr)
return false; return false;
DRM_DEBUG_DRIVER("user_pages_done 0x%llx nr_ranges %d pages 0x%lx\n", DRM_DEBUG_DRIVER("user_pages_done 0x%llx pages 0x%lx\n",
gtt->userptr, gtt->nr_ranges, ttm->num_pages); gtt->userptr, ttm->num_pages);
WARN_ONCE(!gtt->ranges || !gtt->ranges[0].pfns, WARN_ONCE(!gtt->range || !gtt->range->pfns,
"No user pages to check\n"); "No user pages to check\n");
if (gtt->ranges) { if (gtt->range) {
for (i = 0; i < gtt->nr_ranges; i++) r = hmm_range_valid(gtt->range);
r |= hmm_vma_range_done(&gtt->ranges[i]); hmm_range_unregister(gtt->range);
kvfree(gtt->ranges[0].pfns);
kvfree(gtt->ranges); kvfree(gtt->range->pfns);
gtt->ranges = NULL; kfree(gtt->range);
gtt->range = NULL;
} }
return r; return r;
...@@ -953,9 +937,9 @@ static void amdgpu_ttm_tt_unpin_userptr(struct ttm_tt *ttm) ...@@ -953,9 +937,9 @@ static void amdgpu_ttm_tt_unpin_userptr(struct ttm_tt *ttm)
sg_free_table(ttm->sg); sg_free_table(ttm->sg);
#if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR) #if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR)
if (gtt->ranges && if (gtt->range &&
ttm->pages[0] == hmm_pfn_to_page(&gtt->ranges[0], ttm->pages[0] == hmm_device_entry_to_page(gtt->range,
gtt->ranges[0].pfns[0])) gtt->range->pfns[0]))
WARN_ONCE(1, "Missing get_user_page_done\n"); WARN_ONCE(1, "Missing get_user_page_done\n");
#endif #endif
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment