Commit f05b985e authored by Thomas Hellström's avatar Thomas Hellström Committed by Matthew Auld

drm/i915/gem: Break out some shmem backend utils

Break out some shmem backend utils for future reuse by the TTM backend:
shmem_alloc_st(), shmem_free_st() and __shmem_writeback() which we can
use to provide a shmem-backed TTM page pool for cached-only TTM
buffer objects.

Main functional change here is that we now compute the page sizes using
the dma segments rather than using the physical page address segments.

v2(Reported-by: kernel test robot <lkp@intel.com>)
    - Make sure we initialise the mapping on the error path in
      shmem_get_pages()
Signed-off-by: default avatarThomas Hellström <thomas.hellstrom@linux.intel.com>
Reviewed-by: default avatarMatthew Auld <matthew.auld@intel.com>
Signed-off-by: default avatarMatthew Auld <matthew.auld@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20211018091055.1998191-1-matthew.auld@intel.com
parent ef3e6192
...@@ -25,46 +25,61 @@ static void check_release_pagevec(struct pagevec *pvec) ...@@ -25,46 +25,61 @@ static void check_release_pagevec(struct pagevec *pvec)
cond_resched(); cond_resched();
} }
static int shmem_get_pages(struct drm_i915_gem_object *obj) static void shmem_free_st(struct sg_table *st, struct address_space *mapping,
bool dirty, bool backup)
{ {
struct drm_i915_private *i915 = to_i915(obj->base.dev); struct sgt_iter sgt_iter;
struct intel_memory_region *mem = obj->mm.region; struct pagevec pvec;
const unsigned long page_count = obj->base.size / PAGE_SIZE; struct page *page;
mapping_clear_unevictable(mapping);
pagevec_init(&pvec);
for_each_sgt_page(page, sgt_iter, st) {
if (dirty)
set_page_dirty(page);
if (backup)
mark_page_accessed(page);
if (!pagevec_add(&pvec, page))
check_release_pagevec(&pvec);
}
if (pagevec_count(&pvec))
check_release_pagevec(&pvec);
sg_free_table(st);
kfree(st);
}
static struct sg_table *shmem_alloc_st(struct drm_i915_private *i915,
size_t size, struct intel_memory_region *mr,
struct address_space *mapping,
unsigned int max_segment)
{
const unsigned long page_count = size / PAGE_SIZE;
unsigned long i; unsigned long i;
struct address_space *mapping;
struct sg_table *st; struct sg_table *st;
struct scatterlist *sg; struct scatterlist *sg;
struct sgt_iter sgt_iter;
struct page *page; struct page *page;
unsigned long last_pfn = 0; /* suppress gcc warning */ unsigned long last_pfn = 0; /* suppress gcc warning */
unsigned int max_segment = i915_sg_segment_size();
unsigned int sg_page_sizes;
gfp_t noreclaim; gfp_t noreclaim;
int ret; int ret;
/*
* Assert that the object is not currently in any GPU domain. As it
* wasn't in the GTT, there shouldn't be any way it could have been in
* a GPU cache
*/
GEM_BUG_ON(obj->read_domains & I915_GEM_GPU_DOMAINS);
GEM_BUG_ON(obj->write_domain & I915_GEM_GPU_DOMAINS);
/* /*
* If there's no chance of allocating enough pages for the whole * If there's no chance of allocating enough pages for the whole
* object, bail early. * object, bail early.
*/ */
if (obj->base.size > resource_size(&mem->region)) if (size > resource_size(&mr->region))
return -ENOMEM; return ERR_PTR(-ENOMEM);
st = kmalloc(sizeof(*st), GFP_KERNEL); st = kmalloc(sizeof(*st), GFP_KERNEL);
if (!st) if (!st)
return -ENOMEM; return ERR_PTR(-ENOMEM);
rebuild_st:
if (sg_alloc_table(st, page_count, GFP_KERNEL)) { if (sg_alloc_table(st, page_count, GFP_KERNEL)) {
kfree(st); kfree(st);
return -ENOMEM; return ERR_PTR(-ENOMEM);
} }
/* /*
...@@ -73,14 +88,12 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj) ...@@ -73,14 +88,12 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj)
* *
* Fail silently without starting the shrinker * Fail silently without starting the shrinker
*/ */
mapping = obj->base.filp->f_mapping;
mapping_set_unevictable(mapping); mapping_set_unevictable(mapping);
noreclaim = mapping_gfp_constraint(mapping, ~__GFP_RECLAIM); noreclaim = mapping_gfp_constraint(mapping, ~__GFP_RECLAIM);
noreclaim |= __GFP_NORETRY | __GFP_NOWARN; noreclaim |= __GFP_NORETRY | __GFP_NOWARN;
sg = st->sgl; sg = st->sgl;
st->nents = 0; st->nents = 0;
sg_page_sizes = 0;
for (i = 0; i < page_count; i++) { for (i = 0; i < page_count; i++) {
const unsigned int shrink[] = { const unsigned int shrink[] = {
I915_SHRINK_BOUND | I915_SHRINK_UNBOUND, I915_SHRINK_BOUND | I915_SHRINK_UNBOUND,
...@@ -135,10 +148,9 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj) ...@@ -135,10 +148,9 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj)
if (!i || if (!i ||
sg->length >= max_segment || sg->length >= max_segment ||
page_to_pfn(page) != last_pfn + 1) { page_to_pfn(page) != last_pfn + 1) {
if (i) { if (i)
sg_page_sizes |= sg->length;
sg = sg_next(sg); sg = sg_next(sg);
}
st->nents++; st->nents++;
sg_set_page(sg, page, PAGE_SIZE, 0); sg_set_page(sg, page, PAGE_SIZE, 0);
} else { } else {
...@@ -149,14 +161,65 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj) ...@@ -149,14 +161,65 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj)
/* Check that the i965g/gm workaround works. */ /* Check that the i965g/gm workaround works. */
GEM_BUG_ON(gfp & __GFP_DMA32 && last_pfn >= 0x00100000UL); GEM_BUG_ON(gfp & __GFP_DMA32 && last_pfn >= 0x00100000UL);
} }
if (sg) { /* loop terminated early; short sg table */ if (sg) /* loop terminated early; short sg table */
sg_page_sizes |= sg->length;
sg_mark_end(sg); sg_mark_end(sg);
}
/* Trim unused sg entries to avoid wasting memory. */ /* Trim unused sg entries to avoid wasting memory. */
i915_sg_trim(st); i915_sg_trim(st);
return st;
err_sg:
sg_mark_end(sg);
if (sg != st->sgl) {
shmem_free_st(st, mapping, false, false);
} else {
mapping_clear_unevictable(mapping);
sg_free_table(st);
kfree(st);
}
/*
* shmemfs first checks if there is enough memory to allocate the page
* and reports ENOSPC should there be insufficient, along with the usual
* ENOMEM for a genuine allocation failure.
*
* We use ENOSPC in our driver to mean that we have run out of aperture
* space and so want to translate the error from shmemfs back to our
* usual understanding of ENOMEM.
*/
if (ret == -ENOSPC)
ret = -ENOMEM;
return ERR_PTR(ret);
}
static int shmem_get_pages(struct drm_i915_gem_object *obj)
{
struct drm_i915_private *i915 = to_i915(obj->base.dev);
struct intel_memory_region *mem = obj->mm.region;
struct address_space *mapping = obj->base.filp->f_mapping;
const unsigned long page_count = obj->base.size / PAGE_SIZE;
unsigned int max_segment = i915_sg_segment_size();
struct sg_table *st;
struct sgt_iter sgt_iter;
struct page *page;
int ret;
/*
* Assert that the object is not currently in any GPU domain. As it
* wasn't in the GTT, there shouldn't be any way it could have been in
* a GPU cache
*/
GEM_BUG_ON(obj->read_domains & I915_GEM_GPU_DOMAINS);
GEM_BUG_ON(obj->write_domain & I915_GEM_GPU_DOMAINS);
rebuild_st:
st = shmem_alloc_st(i915, obj->base.size, mem, mapping, max_segment);
if (IS_ERR(st)) {
ret = PTR_ERR(st);
goto err_st;
}
ret = i915_gem_gtt_prepare_pages(obj, st); ret = i915_gem_gtt_prepare_pages(obj, st);
if (ret) { if (ret) {
/* /*
...@@ -168,6 +231,7 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj) ...@@ -168,6 +231,7 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj)
for_each_sgt_page(page, sgt_iter, st) for_each_sgt_page(page, sgt_iter, st)
put_page(page); put_page(page);
sg_free_table(st); sg_free_table(st);
kfree(st);
max_segment = PAGE_SIZE; max_segment = PAGE_SIZE;
goto rebuild_st; goto rebuild_st;
...@@ -185,28 +249,12 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj) ...@@ -185,28 +249,12 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj)
if (i915_gem_object_can_bypass_llc(obj)) if (i915_gem_object_can_bypass_llc(obj))
obj->cache_dirty = true; obj->cache_dirty = true;
__i915_gem_object_set_pages(obj, st, sg_page_sizes); __i915_gem_object_set_pages(obj, st, i915_sg_dma_sizes(st->sgl));
return 0; return 0;
err_sg:
sg_mark_end(sg);
err_pages: err_pages:
mapping_clear_unevictable(mapping); shmem_free_st(st, mapping, false, false);
if (sg != st->sgl) {
struct pagevec pvec;
pagevec_init(&pvec);
for_each_sgt_page(page, sgt_iter, st) {
if (!pagevec_add(&pvec, page))
check_release_pagevec(&pvec);
}
if (pagevec_count(&pvec))
check_release_pagevec(&pvec);
}
sg_free_table(st);
kfree(st);
/* /*
* shmemfs first checks if there is enough memory to allocate the page * shmemfs first checks if there is enough memory to allocate the page
* and reports ENOSPC should there be insufficient, along with the usual * and reports ENOSPC should there be insufficient, along with the usual
...@@ -216,6 +264,7 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj) ...@@ -216,6 +264,7 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj)
* space and so want to translate the error from shmemfs back to our * space and so want to translate the error from shmemfs back to our
* usual understanding of ENOMEM. * usual understanding of ENOMEM.
*/ */
err_st:
if (ret == -ENOSPC) if (ret == -ENOSPC)
ret = -ENOMEM; ret = -ENOMEM;
...@@ -236,10 +285,8 @@ shmem_truncate(struct drm_i915_gem_object *obj) ...@@ -236,10 +285,8 @@ shmem_truncate(struct drm_i915_gem_object *obj)
obj->mm.pages = ERR_PTR(-EFAULT); obj->mm.pages = ERR_PTR(-EFAULT);
} }
static void static void __shmem_writeback(size_t size, struct address_space *mapping)
shmem_writeback(struct drm_i915_gem_object *obj)
{ {
struct address_space *mapping;
struct writeback_control wbc = { struct writeback_control wbc = {
.sync_mode = WB_SYNC_NONE, .sync_mode = WB_SYNC_NONE,
.nr_to_write = SWAP_CLUSTER_MAX, .nr_to_write = SWAP_CLUSTER_MAX,
...@@ -255,10 +302,9 @@ shmem_writeback(struct drm_i915_gem_object *obj) ...@@ -255,10 +302,9 @@ shmem_writeback(struct drm_i915_gem_object *obj)
* instead of invoking writeback so they are aged and paged out * instead of invoking writeback so they are aged and paged out
* as normal. * as normal.
*/ */
mapping = obj->base.filp->f_mapping;
/* Begin writeback on each dirty page */ /* Begin writeback on each dirty page */
for (i = 0; i < obj->base.size >> PAGE_SHIFT; i++) { for (i = 0; i < size >> PAGE_SHIFT; i++) {
struct page *page; struct page *page;
page = find_lock_page(mapping, i); page = find_lock_page(mapping, i);
...@@ -281,6 +327,12 @@ shmem_writeback(struct drm_i915_gem_object *obj) ...@@ -281,6 +327,12 @@ shmem_writeback(struct drm_i915_gem_object *obj)
} }
} }
static void
shmem_writeback(struct drm_i915_gem_object *obj)
{
__shmem_writeback(obj->base.size, obj->base.filp->f_mapping);
}
void void
__i915_gem_object_release_shmem(struct drm_i915_gem_object *obj, __i915_gem_object_release_shmem(struct drm_i915_gem_object *obj,
struct sg_table *pages, struct sg_table *pages,
...@@ -313,11 +365,6 @@ __i915_gem_object_release_shmem(struct drm_i915_gem_object *obj, ...@@ -313,11 +365,6 @@ __i915_gem_object_release_shmem(struct drm_i915_gem_object *obj,
void i915_gem_object_put_pages_shmem(struct drm_i915_gem_object *obj, struct sg_table *pages) void i915_gem_object_put_pages_shmem(struct drm_i915_gem_object *obj, struct sg_table *pages)
{ {
struct sgt_iter sgt_iter;
struct pagevec pvec;
struct page *page;
GEM_WARN_ON(IS_DGFX(to_i915(obj->base.dev)));
__i915_gem_object_release_shmem(obj, pages, true); __i915_gem_object_release_shmem(obj, pages, true);
i915_gem_gtt_finish_pages(obj, pages); i915_gem_gtt_finish_pages(obj, pages);
...@@ -325,25 +372,9 @@ void i915_gem_object_put_pages_shmem(struct drm_i915_gem_object *obj, struct sg_ ...@@ -325,25 +372,9 @@ void i915_gem_object_put_pages_shmem(struct drm_i915_gem_object *obj, struct sg_
if (i915_gem_object_needs_bit17_swizzle(obj)) if (i915_gem_object_needs_bit17_swizzle(obj))
i915_gem_object_save_bit_17_swizzle(obj, pages); i915_gem_object_save_bit_17_swizzle(obj, pages);
mapping_clear_unevictable(file_inode(obj->base.filp)->i_mapping); shmem_free_st(pages, file_inode(obj->base.filp)->i_mapping,
obj->mm.dirty, obj->mm.madv == I915_MADV_WILLNEED);
pagevec_init(&pvec);
for_each_sgt_page(page, sgt_iter, pages) {
if (obj->mm.dirty)
set_page_dirty(page);
if (obj->mm.madv == I915_MADV_WILLNEED)
mark_page_accessed(page);
if (!pagevec_add(&pvec, page))
check_release_pagevec(&pvec);
}
if (pagevec_count(&pvec))
check_release_pagevec(&pvec);
obj->mm.dirty = false; obj->mm.dirty = false;
sg_free_table(pages);
kfree(pages);
} }
static void static void
......
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