Message ID | 20210324134845.2338-1-christian.koenig@amd.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | drm/ttm: switch back to static allocation limits for now | expand |
On Wed, Mar 24, 2021 at 02:48:45PM +0100, Christian König wrote: > The shrinker based approach still has some flaws. Especially that we need > temporary pages to free up the pages allocated to the driver is problematic > in a shrinker. > > Signed-off-by: Christian König <christian.koenig@amd.com> > --- > drivers/gpu/drm/ttm/ttm_device.c | 14 ++-- > drivers/gpu/drm/ttm/ttm_tt.c | 112 ++++++++++++------------------- > include/drm/ttm/ttm_tt.h | 3 +- > 3 files changed, 53 insertions(+), 76 deletions(-) > > diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c > index 95e1b7b1f2e6..388da2a7f0bb 100644 > --- a/drivers/gpu/drm/ttm/ttm_device.c > +++ b/drivers/gpu/drm/ttm/ttm_device.c > @@ -53,7 +53,6 @@ static void ttm_global_release(void) > goto out; > > ttm_pool_mgr_fini(); > - ttm_tt_mgr_fini(); > > __free_page(glob->dummy_read_page); > memset(glob, 0, sizeof(*glob)); > @@ -64,7 +63,7 @@ static void ttm_global_release(void) > static int ttm_global_init(void) > { > struct ttm_global *glob = &ttm_glob; > - unsigned long num_pages; > + unsigned long num_pages, num_dma32; > struct sysinfo si; > int ret = 0; > unsigned i; > @@ -79,8 +78,15 @@ static int ttm_global_init(void) > * system memory. > */ > num_pages = ((u64)si.totalram * si.mem_unit) >> PAGE_SHIFT; > - ttm_pool_mgr_init(num_pages * 50 / 100); > - ttm_tt_mgr_init(); > + num_pages /= 2; > + > + /* But for DMA32 we limit ourself to only use 2GiB maximum. */ > + num_dma32 = (u64)(si.totalram - si.totalhigh) * si.mem_unit > + >> PAGE_SHIFT; > + num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT)); > + > + ttm_pool_mgr_init(num_pages); > + ttm_tt_mgr_init(num_pages, num_dma32); > > spin_lock_init(&glob->lru_lock); > glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32); > diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c > index 2f0833c98d2c..5d8820725b75 100644 > --- a/drivers/gpu/drm/ttm/ttm_tt.c > +++ b/drivers/gpu/drm/ttm/ttm_tt.c > @@ -40,8 +40,18 @@ > > #include "ttm_module.h" > > -static struct shrinker mm_shrinker; > -static atomic_long_t swapable_pages; > +static unsigned long ttm_pages_limit; > + > +MODULE_PARM_DESC(pages_limit, "Limit for the allocated pages"); > +module_param_named(pages_limit, ttm_pages_limit, ulong, 0644); > + > +static unsigned long ttm_dma32_pages_limit; > + > +MODULE_PARM_DESC(dma32_pages_limit, "Limit for the allocated DMA32 pages"); > +module_param_named(dma32_pages_limit, ttm_dma32_pages_limit, ulong, 0644); > + > +static atomic_long_t ttm_pages_allocated; > +static atomic_long_t ttm_dma32_pages_allocated; Making this configurable looks an awful lot like "job done, move on". Just the revert to hardcoded 50% (or I guess just revert the shrinker patch at that point) for -fixes is imo better. Then I guess retry again for 5.14 or so. -Daniel > > /* > * Allocates a ttm structure for the given BO. > @@ -294,8 +304,6 @@ static void ttm_tt_add_mapping(struct ttm_device *bdev, struct ttm_tt *ttm) > > for (i = 0; i < ttm->num_pages; ++i) > ttm->pages[i]->mapping = bdev->dev_mapping; > - > - atomic_long_add(ttm->num_pages, &swapable_pages); > } > > int ttm_tt_populate(struct ttm_device *bdev, > @@ -309,12 +317,25 @@ int ttm_tt_populate(struct ttm_device *bdev, > if (ttm_tt_is_populated(ttm)) > return 0; > > + atomic_long_add(ttm->num_pages, &ttm_pages_allocated); > + if (bdev->pool.use_dma32) > + atomic_long_add(ttm->num_pages, &ttm_dma32_pages_allocated); > + > + while (atomic_long_read(&ttm_pages_allocated) > ttm_pages_limit || > + atomic_long_read(&ttm_dma32_pages_allocated) > > + ttm_dma32_pages_limit) { > + > + ret = ttm_bo_swapout(ctx, GFP_KERNEL); > + if (ret) > + goto error; > + } > + > if (bdev->funcs->ttm_tt_populate) > ret = bdev->funcs->ttm_tt_populate(bdev, ttm, ctx); > else > ret = ttm_pool_alloc(&bdev->pool, ttm, ctx); > if (ret) > - return ret; > + goto error; > > ttm_tt_add_mapping(bdev, ttm); > ttm->page_flags |= TTM_PAGE_FLAG_PRIV_POPULATED; > @@ -327,6 +348,12 @@ int ttm_tt_populate(struct ttm_device *bdev, > } > > return 0; > + > +error: > + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); > + if (bdev->pool.use_dma32) > + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); > + return ret; > } > EXPORT_SYMBOL(ttm_tt_populate); > > @@ -342,12 +369,9 @@ static void ttm_tt_clear_mapping(struct ttm_tt *ttm) > (*page)->mapping = NULL; > (*page++)->index = 0; > } > - > - atomic_long_sub(ttm->num_pages, &swapable_pages); > } > > -void ttm_tt_unpopulate(struct ttm_device *bdev, > - struct ttm_tt *ttm) > +void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm) > { > if (!ttm_tt_is_populated(ttm)) > return; > @@ -357,76 +381,24 @@ void ttm_tt_unpopulate(struct ttm_device *bdev, > bdev->funcs->ttm_tt_unpopulate(bdev, ttm); > else > ttm_pool_free(&bdev->pool, ttm); > - ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; > -} > - > -/* As long as pages are available make sure to release at least one */ > -static unsigned long ttm_tt_shrinker_scan(struct shrinker *shrink, > - struct shrink_control *sc) > -{ > - struct ttm_operation_ctx ctx = { > - .no_wait_gpu = false > - }; > - int ret; > - > - ret = ttm_bo_swapout(&ctx, GFP_NOFS); > - return ret < 0 ? SHRINK_EMPTY : ret; > -} > - > -/* Return the number of pages available or SHRINK_EMPTY if we have none */ > -static unsigned long ttm_tt_shrinker_count(struct shrinker *shrink, > - struct shrink_control *sc) > -{ > - unsigned long num_pages; > - > - num_pages = atomic_long_read(&swapable_pages); > - return num_pages ? num_pages : SHRINK_EMPTY; > -} > > -#ifdef CONFIG_DEBUG_FS > + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); > + if (bdev->pool.use_dma32) > + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); > > -/* Test the shrinker functions and dump the result */ > -static int ttm_tt_debugfs_shrink_show(struct seq_file *m, void *data) > -{ > - struct shrink_control sc = { .gfp_mask = GFP_KERNEL }; > - > - fs_reclaim_acquire(GFP_KERNEL); > - seq_printf(m, "%lu/%lu\n", ttm_tt_shrinker_count(&mm_shrinker, &sc), > - ttm_tt_shrinker_scan(&mm_shrinker, &sc)); > - fs_reclaim_release(GFP_KERNEL); > - > - return 0; > + ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; > } > -DEFINE_SHOW_ATTRIBUTE(ttm_tt_debugfs_shrink); > - > -#endif > - > - > > /** > * ttm_tt_mgr_init - register with the MM shrinker > * > * Register with the MM shrinker for swapping out BOs. > */ > -int ttm_tt_mgr_init(void) > +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages) > { > -#ifdef CONFIG_DEBUG_FS > - debugfs_create_file("tt_shrink", 0400, ttm_debugfs_root, NULL, > - &ttm_tt_debugfs_shrink_fops); > -#endif > - > - mm_shrinker.count_objects = ttm_tt_shrinker_count; > - mm_shrinker.scan_objects = ttm_tt_shrinker_scan; > - mm_shrinker.seeks = 1; > - return register_shrinker(&mm_shrinker); > -} > + if (!ttm_pages_limit) > + ttm_pages_limit = num_pages; > > -/** > - * ttm_tt_mgr_fini - unregister our MM shrinker > - * > - * Unregisters the MM shrinker. > - */ > -void ttm_tt_mgr_fini(void) > -{ > - unregister_shrinker(&mm_shrinker); > + if (!ttm_dma32_pages_limit) > + ttm_dma32_pages_limit = num_dma32_pages; > } > diff --git a/include/drm/ttm/ttm_tt.h b/include/drm/ttm/ttm_tt.h > index 069f8130241a..134d09ef7766 100644 > --- a/include/drm/ttm/ttm_tt.h > +++ b/include/drm/ttm/ttm_tt.h > @@ -157,8 +157,7 @@ int ttm_tt_populate(struct ttm_device *bdev, struct ttm_tt *ttm, struct ttm_oper > */ > void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm); > > -int ttm_tt_mgr_init(void); > -void ttm_tt_mgr_fini(void); > +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages); > > #if IS_ENABLED(CONFIG_AGP) > #include <linux/agp_backend.h> > -- > 2.25.1 >
Am 24.03.21 um 20:29 schrieb Daniel Vetter: > On Wed, Mar 24, 2021 at 02:48:45PM +0100, Christian König wrote: >> The shrinker based approach still has some flaws. Especially that we need >> temporary pages to free up the pages allocated to the driver is problematic >> in a shrinker. >> >> Signed-off-by: Christian König <christian.koenig@amd.com> >> --- >> drivers/gpu/drm/ttm/ttm_device.c | 14 ++-- >> drivers/gpu/drm/ttm/ttm_tt.c | 112 ++++++++++++------------------- >> include/drm/ttm/ttm_tt.h | 3 +- >> 3 files changed, 53 insertions(+), 76 deletions(-) >> >> diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c >> index 95e1b7b1f2e6..388da2a7f0bb 100644 >> --- a/drivers/gpu/drm/ttm/ttm_device.c >> +++ b/drivers/gpu/drm/ttm/ttm_device.c >> @@ -53,7 +53,6 @@ static void ttm_global_release(void) >> goto out; >> >> ttm_pool_mgr_fini(); >> - ttm_tt_mgr_fini(); >> >> __free_page(glob->dummy_read_page); >> memset(glob, 0, sizeof(*glob)); >> @@ -64,7 +63,7 @@ static void ttm_global_release(void) >> static int ttm_global_init(void) >> { >> struct ttm_global *glob = &ttm_glob; >> - unsigned long num_pages; >> + unsigned long num_pages, num_dma32; >> struct sysinfo si; >> int ret = 0; >> unsigned i; >> @@ -79,8 +78,15 @@ static int ttm_global_init(void) >> * system memory. >> */ >> num_pages = ((u64)si.totalram * si.mem_unit) >> PAGE_SHIFT; >> - ttm_pool_mgr_init(num_pages * 50 / 100); >> - ttm_tt_mgr_init(); >> + num_pages /= 2; >> + >> + /* But for DMA32 we limit ourself to only use 2GiB maximum. */ >> + num_dma32 = (u64)(si.totalram - si.totalhigh) * si.mem_unit >> + >> PAGE_SHIFT; >> + num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT)); >> + >> + ttm_pool_mgr_init(num_pages); >> + ttm_tt_mgr_init(num_pages, num_dma32); >> >> spin_lock_init(&glob->lru_lock); >> glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32); >> diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c >> index 2f0833c98d2c..5d8820725b75 100644 >> --- a/drivers/gpu/drm/ttm/ttm_tt.c >> +++ b/drivers/gpu/drm/ttm/ttm_tt.c >> @@ -40,8 +40,18 @@ >> >> #include "ttm_module.h" >> >> -static struct shrinker mm_shrinker; >> -static atomic_long_t swapable_pages; >> +static unsigned long ttm_pages_limit; >> + >> +MODULE_PARM_DESC(pages_limit, "Limit for the allocated pages"); >> +module_param_named(pages_limit, ttm_pages_limit, ulong, 0644); >> + >> +static unsigned long ttm_dma32_pages_limit; >> + >> +MODULE_PARM_DESC(dma32_pages_limit, "Limit for the allocated DMA32 pages"); >> +module_param_named(dma32_pages_limit, ttm_dma32_pages_limit, ulong, 0644); >> + >> +static atomic_long_t ttm_pages_allocated; >> +static atomic_long_t ttm_dma32_pages_allocated; > Making this configurable looks an awful lot like "job done, move on". Just > the revert to hardcoded 50% (or I guess just revert the shrinker patch at > that point) for -fixes is imo better. Well this is the revert to hardcoded 50%. We had that configurable before and have it configurable now. Reverting everything back would mean that we also need to revert the sysfs changes and move all the memory accounting code from VMWGFX back into TTM. Christian. > > Then I guess retry again for 5.14 or so. > -Daniel > >> >> /* >> * Allocates a ttm structure for the given BO. >> @@ -294,8 +304,6 @@ static void ttm_tt_add_mapping(struct ttm_device *bdev, struct ttm_tt *ttm) >> >> for (i = 0; i < ttm->num_pages; ++i) >> ttm->pages[i]->mapping = bdev->dev_mapping; >> - >> - atomic_long_add(ttm->num_pages, &swapable_pages); >> } >> >> int ttm_tt_populate(struct ttm_device *bdev, >> @@ -309,12 +317,25 @@ int ttm_tt_populate(struct ttm_device *bdev, >> if (ttm_tt_is_populated(ttm)) >> return 0; >> >> + atomic_long_add(ttm->num_pages, &ttm_pages_allocated); >> + if (bdev->pool.use_dma32) >> + atomic_long_add(ttm->num_pages, &ttm_dma32_pages_allocated); >> + >> + while (atomic_long_read(&ttm_pages_allocated) > ttm_pages_limit || >> + atomic_long_read(&ttm_dma32_pages_allocated) > >> + ttm_dma32_pages_limit) { >> + >> + ret = ttm_bo_swapout(ctx, GFP_KERNEL); >> + if (ret) >> + goto error; >> + } >> + >> if (bdev->funcs->ttm_tt_populate) >> ret = bdev->funcs->ttm_tt_populate(bdev, ttm, ctx); >> else >> ret = ttm_pool_alloc(&bdev->pool, ttm, ctx); >> if (ret) >> - return ret; >> + goto error; >> >> ttm_tt_add_mapping(bdev, ttm); >> ttm->page_flags |= TTM_PAGE_FLAG_PRIV_POPULATED; >> @@ -327,6 +348,12 @@ int ttm_tt_populate(struct ttm_device *bdev, >> } >> >> return 0; >> + >> +error: >> + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); >> + if (bdev->pool.use_dma32) >> + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); >> + return ret; >> } >> EXPORT_SYMBOL(ttm_tt_populate); >> >> @@ -342,12 +369,9 @@ static void ttm_tt_clear_mapping(struct ttm_tt *ttm) >> (*page)->mapping = NULL; >> (*page++)->index = 0; >> } >> - >> - atomic_long_sub(ttm->num_pages, &swapable_pages); >> } >> >> -void ttm_tt_unpopulate(struct ttm_device *bdev, >> - struct ttm_tt *ttm) >> +void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm) >> { >> if (!ttm_tt_is_populated(ttm)) >> return; >> @@ -357,76 +381,24 @@ void ttm_tt_unpopulate(struct ttm_device *bdev, >> bdev->funcs->ttm_tt_unpopulate(bdev, ttm); >> else >> ttm_pool_free(&bdev->pool, ttm); >> - ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; >> -} >> - >> -/* As long as pages are available make sure to release at least one */ >> -static unsigned long ttm_tt_shrinker_scan(struct shrinker *shrink, >> - struct shrink_control *sc) >> -{ >> - struct ttm_operation_ctx ctx = { >> - .no_wait_gpu = false >> - }; >> - int ret; >> - >> - ret = ttm_bo_swapout(&ctx, GFP_NOFS); >> - return ret < 0 ? SHRINK_EMPTY : ret; >> -} >> - >> -/* Return the number of pages available or SHRINK_EMPTY if we have none */ >> -static unsigned long ttm_tt_shrinker_count(struct shrinker *shrink, >> - struct shrink_control *sc) >> -{ >> - unsigned long num_pages; >> - >> - num_pages = atomic_long_read(&swapable_pages); >> - return num_pages ? num_pages : SHRINK_EMPTY; >> -} >> >> -#ifdef CONFIG_DEBUG_FS >> + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); >> + if (bdev->pool.use_dma32) >> + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); >> >> -/* Test the shrinker functions and dump the result */ >> -static int ttm_tt_debugfs_shrink_show(struct seq_file *m, void *data) >> -{ >> - struct shrink_control sc = { .gfp_mask = GFP_KERNEL }; >> - >> - fs_reclaim_acquire(GFP_KERNEL); >> - seq_printf(m, "%lu/%lu\n", ttm_tt_shrinker_count(&mm_shrinker, &sc), >> - ttm_tt_shrinker_scan(&mm_shrinker, &sc)); >> - fs_reclaim_release(GFP_KERNEL); >> - >> - return 0; >> + ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; >> } >> -DEFINE_SHOW_ATTRIBUTE(ttm_tt_debugfs_shrink); >> - >> -#endif >> - >> - >> >> /** >> * ttm_tt_mgr_init - register with the MM shrinker >> * >> * Register with the MM shrinker for swapping out BOs. >> */ >> -int ttm_tt_mgr_init(void) >> +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages) >> { >> -#ifdef CONFIG_DEBUG_FS >> - debugfs_create_file("tt_shrink", 0400, ttm_debugfs_root, NULL, >> - &ttm_tt_debugfs_shrink_fops); >> -#endif >> - >> - mm_shrinker.count_objects = ttm_tt_shrinker_count; >> - mm_shrinker.scan_objects = ttm_tt_shrinker_scan; >> - mm_shrinker.seeks = 1; >> - return register_shrinker(&mm_shrinker); >> -} >> + if (!ttm_pages_limit) >> + ttm_pages_limit = num_pages; >> >> -/** >> - * ttm_tt_mgr_fini - unregister our MM shrinker >> - * >> - * Unregisters the MM shrinker. >> - */ >> -void ttm_tt_mgr_fini(void) >> -{ >> - unregister_shrinker(&mm_shrinker); >> + if (!ttm_dma32_pages_limit) >> + ttm_dma32_pages_limit = num_dma32_pages; >> } >> diff --git a/include/drm/ttm/ttm_tt.h b/include/drm/ttm/ttm_tt.h >> index 069f8130241a..134d09ef7766 100644 >> --- a/include/drm/ttm/ttm_tt.h >> +++ b/include/drm/ttm/ttm_tt.h >> @@ -157,8 +157,7 @@ int ttm_tt_populate(struct ttm_device *bdev, struct ttm_tt *ttm, struct ttm_oper >> */ >> void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm); >> >> -int ttm_tt_mgr_init(void); >> -void ttm_tt_mgr_fini(void); >> +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages); >> >> #if IS_ENABLED(CONFIG_AGP) >> #include <linux/agp_backend.h> >> -- >> 2.25.1 >>
On Thu, Mar 25, 2021 at 9:07 AM Christian König <ckoenig.leichtzumerken@gmail.com> wrote: > > Am 24.03.21 um 20:29 schrieb Daniel Vetter: > > On Wed, Mar 24, 2021 at 02:48:45PM +0100, Christian König wrote: > >> The shrinker based approach still has some flaws. Especially that we need > >> temporary pages to free up the pages allocated to the driver is problematic > >> in a shrinker. > >> > >> Signed-off-by: Christian König <christian.koenig@amd.com> > >> --- > >> drivers/gpu/drm/ttm/ttm_device.c | 14 ++-- > >> drivers/gpu/drm/ttm/ttm_tt.c | 112 ++++++++++++------------------- > >> include/drm/ttm/ttm_tt.h | 3 +- > >> 3 files changed, 53 insertions(+), 76 deletions(-) > >> > >> diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c > >> index 95e1b7b1f2e6..388da2a7f0bb 100644 > >> --- a/drivers/gpu/drm/ttm/ttm_device.c > >> +++ b/drivers/gpu/drm/ttm/ttm_device.c > >> @@ -53,7 +53,6 @@ static void ttm_global_release(void) > >> goto out; > >> > >> ttm_pool_mgr_fini(); > >> - ttm_tt_mgr_fini(); > >> > >> __free_page(glob->dummy_read_page); > >> memset(glob, 0, sizeof(*glob)); > >> @@ -64,7 +63,7 @@ static void ttm_global_release(void) > >> static int ttm_global_init(void) > >> { > >> struct ttm_global *glob = &ttm_glob; > >> - unsigned long num_pages; > >> + unsigned long num_pages, num_dma32; > >> struct sysinfo si; > >> int ret = 0; > >> unsigned i; > >> @@ -79,8 +78,15 @@ static int ttm_global_init(void) > >> * system memory. > >> */ > >> num_pages = ((u64)si.totalram * si.mem_unit) >> PAGE_SHIFT; > >> - ttm_pool_mgr_init(num_pages * 50 / 100); > >> - ttm_tt_mgr_init(); > >> + num_pages /= 2; > >> + > >> + /* But for DMA32 we limit ourself to only use 2GiB maximum. */ > >> + num_dma32 = (u64)(si.totalram - si.totalhigh) * si.mem_unit > >> + >> PAGE_SHIFT; > >> + num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT)); > >> + > >> + ttm_pool_mgr_init(num_pages); > >> + ttm_tt_mgr_init(num_pages, num_dma32); > >> > >> spin_lock_init(&glob->lru_lock); > >> glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32); > >> diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c > >> index 2f0833c98d2c..5d8820725b75 100644 > >> --- a/drivers/gpu/drm/ttm/ttm_tt.c > >> +++ b/drivers/gpu/drm/ttm/ttm_tt.c > >> @@ -40,8 +40,18 @@ > >> > >> #include "ttm_module.h" > >> > >> -static struct shrinker mm_shrinker; > >> -static atomic_long_t swapable_pages; > >> +static unsigned long ttm_pages_limit; > >> + > >> +MODULE_PARM_DESC(pages_limit, "Limit for the allocated pages"); > >> +module_param_named(pages_limit, ttm_pages_limit, ulong, 0644); > >> + > >> +static unsigned long ttm_dma32_pages_limit; > >> + > >> +MODULE_PARM_DESC(dma32_pages_limit, "Limit for the allocated DMA32 pages"); > >> +module_param_named(dma32_pages_limit, ttm_dma32_pages_limit, ulong, 0644); > >> + > >> +static atomic_long_t ttm_pages_allocated; > >> +static atomic_long_t ttm_dma32_pages_allocated; > > Making this configurable looks an awful lot like "job done, move on". Just > > the revert to hardcoded 50% (or I guess just revert the shrinker patch at > > that point) for -fixes is imo better. > > Well this is the revert to hardcoded 50%. We had that configurable > before and have it configurable now. Hm I looked, but I missed it I guess. Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> I just really want to make sure we don't walk down the path of first reinventing kswapd, then reinventing direct reclaim (i915-gem has done that because of the single dev->struct_mutex, despite our shrinker, it's real ugly and fragile) in selective calls, then realizing that's still not enough because especially compute loves userptr, and then we're back to having to reinvent the GFP hierarchy. Been there with i915-gem for dev->struct_mutex reasons, but the effect is kinda similar: In some cases the shrinker is defacto not there, and everything then keels over. btw one thing that cross my mind a bunch times is to add a GFP_NOGPU. That fixes things for everything else except our own gpu memory usage, and since the goal is to let that approach 100% it's not really a gain. > Reverting everything back would mean that we also need to revert the > sysfs changes and move all the memory accounting code from VMWGFX back > into TTM. Hm yeah ... -Daniel > Christian. > > > > > Then I guess retry again for 5.14 or so. > > -Daniel > > > >> > >> /* > >> * Allocates a ttm structure for the given BO. > >> @@ -294,8 +304,6 @@ static void ttm_tt_add_mapping(struct ttm_device *bdev, struct ttm_tt *ttm) > >> > >> for (i = 0; i < ttm->num_pages; ++i) > >> ttm->pages[i]->mapping = bdev->dev_mapping; > >> - > >> - atomic_long_add(ttm->num_pages, &swapable_pages); > >> } > >> > >> int ttm_tt_populate(struct ttm_device *bdev, > >> @@ -309,12 +317,25 @@ int ttm_tt_populate(struct ttm_device *bdev, > >> if (ttm_tt_is_populated(ttm)) > >> return 0; > >> > >> + atomic_long_add(ttm->num_pages, &ttm_pages_allocated); > >> + if (bdev->pool.use_dma32) > >> + atomic_long_add(ttm->num_pages, &ttm_dma32_pages_allocated); > >> + > >> + while (atomic_long_read(&ttm_pages_allocated) > ttm_pages_limit || > >> + atomic_long_read(&ttm_dma32_pages_allocated) > > >> + ttm_dma32_pages_limit) { > >> + > >> + ret = ttm_bo_swapout(ctx, GFP_KERNEL); > >> + if (ret) > >> + goto error; > >> + } > >> + > >> if (bdev->funcs->ttm_tt_populate) > >> ret = bdev->funcs->ttm_tt_populate(bdev, ttm, ctx); > >> else > >> ret = ttm_pool_alloc(&bdev->pool, ttm, ctx); > >> if (ret) > >> - return ret; > >> + goto error; > >> > >> ttm_tt_add_mapping(bdev, ttm); > >> ttm->page_flags |= TTM_PAGE_FLAG_PRIV_POPULATED; > >> @@ -327,6 +348,12 @@ int ttm_tt_populate(struct ttm_device *bdev, > >> } > >> > >> return 0; > >> + > >> +error: > >> + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); > >> + if (bdev->pool.use_dma32) > >> + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); > >> + return ret; > >> } > >> EXPORT_SYMBOL(ttm_tt_populate); > >> > >> @@ -342,12 +369,9 @@ static void ttm_tt_clear_mapping(struct ttm_tt *ttm) > >> (*page)->mapping = NULL; > >> (*page++)->index = 0; > >> } > >> - > >> - atomic_long_sub(ttm->num_pages, &swapable_pages); > >> } > >> > >> -void ttm_tt_unpopulate(struct ttm_device *bdev, > >> - struct ttm_tt *ttm) > >> +void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm) > >> { > >> if (!ttm_tt_is_populated(ttm)) > >> return; > >> @@ -357,76 +381,24 @@ void ttm_tt_unpopulate(struct ttm_device *bdev, > >> bdev->funcs->ttm_tt_unpopulate(bdev, ttm); > >> else > >> ttm_pool_free(&bdev->pool, ttm); > >> - ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; > >> -} > >> - > >> -/* As long as pages are available make sure to release at least one */ > >> -static unsigned long ttm_tt_shrinker_scan(struct shrinker *shrink, > >> - struct shrink_control *sc) > >> -{ > >> - struct ttm_operation_ctx ctx = { > >> - .no_wait_gpu = false > >> - }; > >> - int ret; > >> - > >> - ret = ttm_bo_swapout(&ctx, GFP_NOFS); > >> - return ret < 0 ? SHRINK_EMPTY : ret; > >> -} > >> - > >> -/* Return the number of pages available or SHRINK_EMPTY if we have none */ > >> -static unsigned long ttm_tt_shrinker_count(struct shrinker *shrink, > >> - struct shrink_control *sc) > >> -{ > >> - unsigned long num_pages; > >> - > >> - num_pages = atomic_long_read(&swapable_pages); > >> - return num_pages ? num_pages : SHRINK_EMPTY; > >> -} > >> > >> -#ifdef CONFIG_DEBUG_FS > >> + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); > >> + if (bdev->pool.use_dma32) > >> + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); > >> > >> -/* Test the shrinker functions and dump the result */ > >> -static int ttm_tt_debugfs_shrink_show(struct seq_file *m, void *data) > >> -{ > >> - struct shrink_control sc = { .gfp_mask = GFP_KERNEL }; > >> - > >> - fs_reclaim_acquire(GFP_KERNEL); > >> - seq_printf(m, "%lu/%lu\n", ttm_tt_shrinker_count(&mm_shrinker, &sc), > >> - ttm_tt_shrinker_scan(&mm_shrinker, &sc)); > >> - fs_reclaim_release(GFP_KERNEL); > >> - > >> - return 0; > >> + ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; > >> } > >> -DEFINE_SHOW_ATTRIBUTE(ttm_tt_debugfs_shrink); > >> - > >> -#endif > >> - > >> - > >> > >> /** > >> * ttm_tt_mgr_init - register with the MM shrinker > >> * > >> * Register with the MM shrinker for swapping out BOs. > >> */ > >> -int ttm_tt_mgr_init(void) > >> +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages) > >> { > >> -#ifdef CONFIG_DEBUG_FS > >> - debugfs_create_file("tt_shrink", 0400, ttm_debugfs_root, NULL, > >> - &ttm_tt_debugfs_shrink_fops); > >> -#endif > >> - > >> - mm_shrinker.count_objects = ttm_tt_shrinker_count; > >> - mm_shrinker.scan_objects = ttm_tt_shrinker_scan; > >> - mm_shrinker.seeks = 1; > >> - return register_shrinker(&mm_shrinker); > >> -} > >> + if (!ttm_pages_limit) > >> + ttm_pages_limit = num_pages; > >> > >> -/** > >> - * ttm_tt_mgr_fini - unregister our MM shrinker > >> - * > >> - * Unregisters the MM shrinker. > >> - */ > >> -void ttm_tt_mgr_fini(void) > >> -{ > >> - unregister_shrinker(&mm_shrinker); > >> + if (!ttm_dma32_pages_limit) > >> + ttm_dma32_pages_limit = num_dma32_pages; > >> } > >> diff --git a/include/drm/ttm/ttm_tt.h b/include/drm/ttm/ttm_tt.h > >> index 069f8130241a..134d09ef7766 100644 > >> --- a/include/drm/ttm/ttm_tt.h > >> +++ b/include/drm/ttm/ttm_tt.h > >> @@ -157,8 +157,7 @@ int ttm_tt_populate(struct ttm_device *bdev, struct ttm_tt *ttm, struct ttm_oper > >> */ > >> void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm); > >> > >> -int ttm_tt_mgr_init(void); > >> -void ttm_tt_mgr_fini(void); > >> +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages); > >> > >> #if IS_ENABLED(CONFIG_AGP) > >> #include <linux/agp_backend.h> > >> -- > >> 2.25.1 > >> > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel
Am 25.03.21 um 09:31 schrieb Daniel Vetter: > On Thu, Mar 25, 2021 at 9:07 AM Christian König > <ckoenig.leichtzumerken@gmail.com> wrote: >> Am 24.03.21 um 20:29 schrieb Daniel Vetter: >>> On Wed, Mar 24, 2021 at 02:48:45PM +0100, Christian König wrote: >>>> The shrinker based approach still has some flaws. Especially that we need >>>> temporary pages to free up the pages allocated to the driver is problematic >>>> in a shrinker. >>>> >>>> Signed-off-by: Christian König <christian.koenig@amd.com> >>>> --- >>>> drivers/gpu/drm/ttm/ttm_device.c | 14 ++-- >>>> drivers/gpu/drm/ttm/ttm_tt.c | 112 ++++++++++++------------------- >>>> include/drm/ttm/ttm_tt.h | 3 +- >>>> 3 files changed, 53 insertions(+), 76 deletions(-) >>>> >>>> diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c >>>> index 95e1b7b1f2e6..388da2a7f0bb 100644 >>>> --- a/drivers/gpu/drm/ttm/ttm_device.c >>>> +++ b/drivers/gpu/drm/ttm/ttm_device.c >>>> @@ -53,7 +53,6 @@ static void ttm_global_release(void) >>>> goto out; >>>> >>>> ttm_pool_mgr_fini(); >>>> - ttm_tt_mgr_fini(); >>>> >>>> __free_page(glob->dummy_read_page); >>>> memset(glob, 0, sizeof(*glob)); >>>> @@ -64,7 +63,7 @@ static void ttm_global_release(void) >>>> static int ttm_global_init(void) >>>> { >>>> struct ttm_global *glob = &ttm_glob; >>>> - unsigned long num_pages; >>>> + unsigned long num_pages, num_dma32; >>>> struct sysinfo si; >>>> int ret = 0; >>>> unsigned i; >>>> @@ -79,8 +78,15 @@ static int ttm_global_init(void) >>>> * system memory. >>>> */ >>>> num_pages = ((u64)si.totalram * si.mem_unit) >> PAGE_SHIFT; >>>> - ttm_pool_mgr_init(num_pages * 50 / 100); >>>> - ttm_tt_mgr_init(); >>>> + num_pages /= 2; >>>> + >>>> + /* But for DMA32 we limit ourself to only use 2GiB maximum. */ >>>> + num_dma32 = (u64)(si.totalram - si.totalhigh) * si.mem_unit >>>> + >> PAGE_SHIFT; >>>> + num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT)); >>>> + >>>> + ttm_pool_mgr_init(num_pages); >>>> + ttm_tt_mgr_init(num_pages, num_dma32); >>>> >>>> spin_lock_init(&glob->lru_lock); >>>> glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32); >>>> diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c >>>> index 2f0833c98d2c..5d8820725b75 100644 >>>> --- a/drivers/gpu/drm/ttm/ttm_tt.c >>>> +++ b/drivers/gpu/drm/ttm/ttm_tt.c >>>> @@ -40,8 +40,18 @@ >>>> >>>> #include "ttm_module.h" >>>> >>>> -static struct shrinker mm_shrinker; >>>> -static atomic_long_t swapable_pages; >>>> +static unsigned long ttm_pages_limit; >>>> + >>>> +MODULE_PARM_DESC(pages_limit, "Limit for the allocated pages"); >>>> +module_param_named(pages_limit, ttm_pages_limit, ulong, 0644); >>>> + >>>> +static unsigned long ttm_dma32_pages_limit; >>>> + >>>> +MODULE_PARM_DESC(dma32_pages_limit, "Limit for the allocated DMA32 pages"); >>>> +module_param_named(dma32_pages_limit, ttm_dma32_pages_limit, ulong, 0644); >>>> + >>>> +static atomic_long_t ttm_pages_allocated; >>>> +static atomic_long_t ttm_dma32_pages_allocated; >>> Making this configurable looks an awful lot like "job done, move on". Just >>> the revert to hardcoded 50% (or I guess just revert the shrinker patch at >>> that point) for -fixes is imo better. >> Well this is the revert to hardcoded 50%. We had that configurable >> before and have it configurable now. > Hm I looked, but I missed it I guess. > > Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> > > I just really want to make sure we don't walk down the path of first > reinventing kswapd, then reinventing direct reclaim (i915-gem has done > that because of the single dev->struct_mutex, despite our shrinker, > it's real ugly and fragile) in selective calls, then realizing that's > still not enough because especially compute loves userptr, and then > we're back to having to reinvent the GFP hierarchy. Been there with > i915-gem for dev->struct_mutex reasons, but the effect is kinda > similar: In some cases the shrinker is defacto not there, and > everything then keels over. Yeah, I mean we really (REALLY) want to get away from this 50% hack. So no worries about that. Doing the shrinker is likely the right approach for this, but we need to get it bulled prove or otherwise we will run into issues. > btw one thing that cross my mind a bunch times is to add a GFP_NOGPU. > That fixes things for everything else except our own gpu memory usage, > and since the goal is to let that approach 100% it's not really a > gain. Hui what? How exactly should that help? Regards, Christian. > >> Reverting everything back would mean that we also need to revert the >> sysfs changes and move all the memory accounting code from VMWGFX back >> into TTM. > Hm yeah ... > -Daniel > >> Christian. >> >>> Then I guess retry again for 5.14 or so. >>> -Daniel >>> >>>> /* >>>> * Allocates a ttm structure for the given BO. >>>> @@ -294,8 +304,6 @@ static void ttm_tt_add_mapping(struct ttm_device *bdev, struct ttm_tt *ttm) >>>> >>>> for (i = 0; i < ttm->num_pages; ++i) >>>> ttm->pages[i]->mapping = bdev->dev_mapping; >>>> - >>>> - atomic_long_add(ttm->num_pages, &swapable_pages); >>>> } >>>> >>>> int ttm_tt_populate(struct ttm_device *bdev, >>>> @@ -309,12 +317,25 @@ int ttm_tt_populate(struct ttm_device *bdev, >>>> if (ttm_tt_is_populated(ttm)) >>>> return 0; >>>> >>>> + atomic_long_add(ttm->num_pages, &ttm_pages_allocated); >>>> + if (bdev->pool.use_dma32) >>>> + atomic_long_add(ttm->num_pages, &ttm_dma32_pages_allocated); >>>> + >>>> + while (atomic_long_read(&ttm_pages_allocated) > ttm_pages_limit || >>>> + atomic_long_read(&ttm_dma32_pages_allocated) > >>>> + ttm_dma32_pages_limit) { >>>> + >>>> + ret = ttm_bo_swapout(ctx, GFP_KERNEL); >>>> + if (ret) >>>> + goto error; >>>> + } >>>> + >>>> if (bdev->funcs->ttm_tt_populate) >>>> ret = bdev->funcs->ttm_tt_populate(bdev, ttm, ctx); >>>> else >>>> ret = ttm_pool_alloc(&bdev->pool, ttm, ctx); >>>> if (ret) >>>> - return ret; >>>> + goto error; >>>> >>>> ttm_tt_add_mapping(bdev, ttm); >>>> ttm->page_flags |= TTM_PAGE_FLAG_PRIV_POPULATED; >>>> @@ -327,6 +348,12 @@ int ttm_tt_populate(struct ttm_device *bdev, >>>> } >>>> >>>> return 0; >>>> + >>>> +error: >>>> + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); >>>> + if (bdev->pool.use_dma32) >>>> + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); >>>> + return ret; >>>> } >>>> EXPORT_SYMBOL(ttm_tt_populate); >>>> >>>> @@ -342,12 +369,9 @@ static void ttm_tt_clear_mapping(struct ttm_tt *ttm) >>>> (*page)->mapping = NULL; >>>> (*page++)->index = 0; >>>> } >>>> - >>>> - atomic_long_sub(ttm->num_pages, &swapable_pages); >>>> } >>>> >>>> -void ttm_tt_unpopulate(struct ttm_device *bdev, >>>> - struct ttm_tt *ttm) >>>> +void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm) >>>> { >>>> if (!ttm_tt_is_populated(ttm)) >>>> return; >>>> @@ -357,76 +381,24 @@ void ttm_tt_unpopulate(struct ttm_device *bdev, >>>> bdev->funcs->ttm_tt_unpopulate(bdev, ttm); >>>> else >>>> ttm_pool_free(&bdev->pool, ttm); >>>> - ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; >>>> -} >>>> - >>>> -/* As long as pages are available make sure to release at least one */ >>>> -static unsigned long ttm_tt_shrinker_scan(struct shrinker *shrink, >>>> - struct shrink_control *sc) >>>> -{ >>>> - struct ttm_operation_ctx ctx = { >>>> - .no_wait_gpu = false >>>> - }; >>>> - int ret; >>>> - >>>> - ret = ttm_bo_swapout(&ctx, GFP_NOFS); >>>> - return ret < 0 ? SHRINK_EMPTY : ret; >>>> -} >>>> - >>>> -/* Return the number of pages available or SHRINK_EMPTY if we have none */ >>>> -static unsigned long ttm_tt_shrinker_count(struct shrinker *shrink, >>>> - struct shrink_control *sc) >>>> -{ >>>> - unsigned long num_pages; >>>> - >>>> - num_pages = atomic_long_read(&swapable_pages); >>>> - return num_pages ? num_pages : SHRINK_EMPTY; >>>> -} >>>> >>>> -#ifdef CONFIG_DEBUG_FS >>>> + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); >>>> + if (bdev->pool.use_dma32) >>>> + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); >>>> >>>> -/* Test the shrinker functions and dump the result */ >>>> -static int ttm_tt_debugfs_shrink_show(struct seq_file *m, void *data) >>>> -{ >>>> - struct shrink_control sc = { .gfp_mask = GFP_KERNEL }; >>>> - >>>> - fs_reclaim_acquire(GFP_KERNEL); >>>> - seq_printf(m, "%lu/%lu\n", ttm_tt_shrinker_count(&mm_shrinker, &sc), >>>> - ttm_tt_shrinker_scan(&mm_shrinker, &sc)); >>>> - fs_reclaim_release(GFP_KERNEL); >>>> - >>>> - return 0; >>>> + ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; >>>> } >>>> -DEFINE_SHOW_ATTRIBUTE(ttm_tt_debugfs_shrink); >>>> - >>>> -#endif >>>> - >>>> - >>>> >>>> /** >>>> * ttm_tt_mgr_init - register with the MM shrinker >>>> * >>>> * Register with the MM shrinker for swapping out BOs. >>>> */ >>>> -int ttm_tt_mgr_init(void) >>>> +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages) >>>> { >>>> -#ifdef CONFIG_DEBUG_FS >>>> - debugfs_create_file("tt_shrink", 0400, ttm_debugfs_root, NULL, >>>> - &ttm_tt_debugfs_shrink_fops); >>>> -#endif >>>> - >>>> - mm_shrinker.count_objects = ttm_tt_shrinker_count; >>>> - mm_shrinker.scan_objects = ttm_tt_shrinker_scan; >>>> - mm_shrinker.seeks = 1; >>>> - return register_shrinker(&mm_shrinker); >>>> -} >>>> + if (!ttm_pages_limit) >>>> + ttm_pages_limit = num_pages; >>>> >>>> -/** >>>> - * ttm_tt_mgr_fini - unregister our MM shrinker >>>> - * >>>> - * Unregisters the MM shrinker. >>>> - */ >>>> -void ttm_tt_mgr_fini(void) >>>> -{ >>>> - unregister_shrinker(&mm_shrinker); >>>> + if (!ttm_dma32_pages_limit) >>>> + ttm_dma32_pages_limit = num_dma32_pages; >>>> } >>>> diff --git a/include/drm/ttm/ttm_tt.h b/include/drm/ttm/ttm_tt.h >>>> index 069f8130241a..134d09ef7766 100644 >>>> --- a/include/drm/ttm/ttm_tt.h >>>> +++ b/include/drm/ttm/ttm_tt.h >>>> @@ -157,8 +157,7 @@ int ttm_tt_populate(struct ttm_device *bdev, struct ttm_tt *ttm, struct ttm_oper >>>> */ >>>> void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm); >>>> >>>> -int ttm_tt_mgr_init(void); >>>> -void ttm_tt_mgr_fini(void); >>>> +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages); >>>> >>>> #if IS_ENABLED(CONFIG_AGP) >>>> #include <linux/agp_backend.h> >>>> -- >>>> 2.25.1 >>>> >> _______________________________________________ >> dri-devel mailing list >> dri-devel@lists.freedesktop.org >> https://lists.freedesktop.org/mailman/listinfo/dri-devel > >
On Thu, Mar 25, 2021 at 10:27 AM Christian König <christian.koenig@amd.com> wrote: > > Am 25.03.21 um 09:31 schrieb Daniel Vetter: > > On Thu, Mar 25, 2021 at 9:07 AM Christian König > > <ckoenig.leichtzumerken@gmail.com> wrote: > >> Am 24.03.21 um 20:29 schrieb Daniel Vetter: > >>> On Wed, Mar 24, 2021 at 02:48:45PM +0100, Christian König wrote: > >>>> The shrinker based approach still has some flaws. Especially that we need > >>>> temporary pages to free up the pages allocated to the driver is problematic > >>>> in a shrinker. > >>>> > >>>> Signed-off-by: Christian König <christian.koenig@amd.com> > >>>> --- > >>>> drivers/gpu/drm/ttm/ttm_device.c | 14 ++-- > >>>> drivers/gpu/drm/ttm/ttm_tt.c | 112 ++++++++++++------------------- > >>>> include/drm/ttm/ttm_tt.h | 3 +- > >>>> 3 files changed, 53 insertions(+), 76 deletions(-) > >>>> > >>>> diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c > >>>> index 95e1b7b1f2e6..388da2a7f0bb 100644 > >>>> --- a/drivers/gpu/drm/ttm/ttm_device.c > >>>> +++ b/drivers/gpu/drm/ttm/ttm_device.c > >>>> @@ -53,7 +53,6 @@ static void ttm_global_release(void) > >>>> goto out; > >>>> > >>>> ttm_pool_mgr_fini(); > >>>> - ttm_tt_mgr_fini(); > >>>> > >>>> __free_page(glob->dummy_read_page); > >>>> memset(glob, 0, sizeof(*glob)); > >>>> @@ -64,7 +63,7 @@ static void ttm_global_release(void) > >>>> static int ttm_global_init(void) > >>>> { > >>>> struct ttm_global *glob = &ttm_glob; > >>>> - unsigned long num_pages; > >>>> + unsigned long num_pages, num_dma32; > >>>> struct sysinfo si; > >>>> int ret = 0; > >>>> unsigned i; > >>>> @@ -79,8 +78,15 @@ static int ttm_global_init(void) > >>>> * system memory. > >>>> */ > >>>> num_pages = ((u64)si.totalram * si.mem_unit) >> PAGE_SHIFT; > >>>> - ttm_pool_mgr_init(num_pages * 50 / 100); > >>>> - ttm_tt_mgr_init(); > >>>> + num_pages /= 2; > >>>> + > >>>> + /* But for DMA32 we limit ourself to only use 2GiB maximum. */ > >>>> + num_dma32 = (u64)(si.totalram - si.totalhigh) * si.mem_unit > >>>> + >> PAGE_SHIFT; > >>>> + num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT)); > >>>> + > >>>> + ttm_pool_mgr_init(num_pages); > >>>> + ttm_tt_mgr_init(num_pages, num_dma32); > >>>> > >>>> spin_lock_init(&glob->lru_lock); > >>>> glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32); > >>>> diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c > >>>> index 2f0833c98d2c..5d8820725b75 100644 > >>>> --- a/drivers/gpu/drm/ttm/ttm_tt.c > >>>> +++ b/drivers/gpu/drm/ttm/ttm_tt.c > >>>> @@ -40,8 +40,18 @@ > >>>> > >>>> #include "ttm_module.h" > >>>> > >>>> -static struct shrinker mm_shrinker; > >>>> -static atomic_long_t swapable_pages; > >>>> +static unsigned long ttm_pages_limit; > >>>> + > >>>> +MODULE_PARM_DESC(pages_limit, "Limit for the allocated pages"); > >>>> +module_param_named(pages_limit, ttm_pages_limit, ulong, 0644); > >>>> + > >>>> +static unsigned long ttm_dma32_pages_limit; > >>>> + > >>>> +MODULE_PARM_DESC(dma32_pages_limit, "Limit for the allocated DMA32 pages"); > >>>> +module_param_named(dma32_pages_limit, ttm_dma32_pages_limit, ulong, 0644); > >>>> + > >>>> +static atomic_long_t ttm_pages_allocated; > >>>> +static atomic_long_t ttm_dma32_pages_allocated; > >>> Making this configurable looks an awful lot like "job done, move on". Just > >>> the revert to hardcoded 50% (or I guess just revert the shrinker patch at > >>> that point) for -fixes is imo better. > >> Well this is the revert to hardcoded 50%. We had that configurable > >> before and have it configurable now. > > Hm I looked, but I missed it I guess. > > > > Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> > > > > I just really want to make sure we don't walk down the path of first > > reinventing kswapd, then reinventing direct reclaim (i915-gem has done > > that because of the single dev->struct_mutex, despite our shrinker, > > it's real ugly and fragile) in selective calls, then realizing that's > > still not enough because especially compute loves userptr, and then > > we're back to having to reinvent the GFP hierarchy. Been there with > > i915-gem for dev->struct_mutex reasons, but the effect is kinda > > similar: In some cases the shrinker is defacto not there, and > > everything then keels over. > > Yeah, I mean we really (REALLY) want to get away from this 50% hack. So > no worries about that. > > Doing the shrinker is likely the right approach for this, but we need to > get it bulled prove or otherwise we will run into issues. > > > btw one thing that cross my mind a bunch times is to add a GFP_NOGPU. > > That fixes things for everything else except our own gpu memory usage, > > and since the goal is to let that approach 100% it's not really a > > gain. > > Hui what? How exactly should that help? You can still do GFP_KERNEL in the shrinker in that case, it would simply be GFP_KERNEL | GFP_NOGPU to make sure we don't get back into our own shrinker when we allocate for shmem. But it would probably break all kinds of assumptions about how this works in core mm, plus since we aim to have most system memory used by gpus it doesn't really help. That kinda of special GFP flag for gpus has come up in the past though. -Daniel > Regards, > Christian. > > > > >> Reverting everything back would mean that we also need to revert the > >> sysfs changes and move all the memory accounting code from VMWGFX back > >> into TTM. > > Hm yeah ... > > -Daniel > > > >> Christian. > >> > >>> Then I guess retry again for 5.14 or so. > >>> -Daniel > >>> > >>>> /* > >>>> * Allocates a ttm structure for the given BO. > >>>> @@ -294,8 +304,6 @@ static void ttm_tt_add_mapping(struct ttm_device *bdev, struct ttm_tt *ttm) > >>>> > >>>> for (i = 0; i < ttm->num_pages; ++i) > >>>> ttm->pages[i]->mapping = bdev->dev_mapping; > >>>> - > >>>> - atomic_long_add(ttm->num_pages, &swapable_pages); > >>>> } > >>>> > >>>> int ttm_tt_populate(struct ttm_device *bdev, > >>>> @@ -309,12 +317,25 @@ int ttm_tt_populate(struct ttm_device *bdev, > >>>> if (ttm_tt_is_populated(ttm)) > >>>> return 0; > >>>> > >>>> + atomic_long_add(ttm->num_pages, &ttm_pages_allocated); > >>>> + if (bdev->pool.use_dma32) > >>>> + atomic_long_add(ttm->num_pages, &ttm_dma32_pages_allocated); > >>>> + > >>>> + while (atomic_long_read(&ttm_pages_allocated) > ttm_pages_limit || > >>>> + atomic_long_read(&ttm_dma32_pages_allocated) > > >>>> + ttm_dma32_pages_limit) { > >>>> + > >>>> + ret = ttm_bo_swapout(ctx, GFP_KERNEL); > >>>> + if (ret) > >>>> + goto error; > >>>> + } > >>>> + > >>>> if (bdev->funcs->ttm_tt_populate) > >>>> ret = bdev->funcs->ttm_tt_populate(bdev, ttm, ctx); > >>>> else > >>>> ret = ttm_pool_alloc(&bdev->pool, ttm, ctx); > >>>> if (ret) > >>>> - return ret; > >>>> + goto error; > >>>> > >>>> ttm_tt_add_mapping(bdev, ttm); > >>>> ttm->page_flags |= TTM_PAGE_FLAG_PRIV_POPULATED; > >>>> @@ -327,6 +348,12 @@ int ttm_tt_populate(struct ttm_device *bdev, > >>>> } > >>>> > >>>> return 0; > >>>> + > >>>> +error: > >>>> + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); > >>>> + if (bdev->pool.use_dma32) > >>>> + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); > >>>> + return ret; > >>>> } > >>>> EXPORT_SYMBOL(ttm_tt_populate); > >>>> > >>>> @@ -342,12 +369,9 @@ static void ttm_tt_clear_mapping(struct ttm_tt *ttm) > >>>> (*page)->mapping = NULL; > >>>> (*page++)->index = 0; > >>>> } > >>>> - > >>>> - atomic_long_sub(ttm->num_pages, &swapable_pages); > >>>> } > >>>> > >>>> -void ttm_tt_unpopulate(struct ttm_device *bdev, > >>>> - struct ttm_tt *ttm) > >>>> +void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm) > >>>> { > >>>> if (!ttm_tt_is_populated(ttm)) > >>>> return; > >>>> @@ -357,76 +381,24 @@ void ttm_tt_unpopulate(struct ttm_device *bdev, > >>>> bdev->funcs->ttm_tt_unpopulate(bdev, ttm); > >>>> else > >>>> ttm_pool_free(&bdev->pool, ttm); > >>>> - ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; > >>>> -} > >>>> - > >>>> -/* As long as pages are available make sure to release at least one */ > >>>> -static unsigned long ttm_tt_shrinker_scan(struct shrinker *shrink, > >>>> - struct shrink_control *sc) > >>>> -{ > >>>> - struct ttm_operation_ctx ctx = { > >>>> - .no_wait_gpu = false > >>>> - }; > >>>> - int ret; > >>>> - > >>>> - ret = ttm_bo_swapout(&ctx, GFP_NOFS); > >>>> - return ret < 0 ? SHRINK_EMPTY : ret; > >>>> -} > >>>> - > >>>> -/* Return the number of pages available or SHRINK_EMPTY if we have none */ > >>>> -static unsigned long ttm_tt_shrinker_count(struct shrinker *shrink, > >>>> - struct shrink_control *sc) > >>>> -{ > >>>> - unsigned long num_pages; > >>>> - > >>>> - num_pages = atomic_long_read(&swapable_pages); > >>>> - return num_pages ? num_pages : SHRINK_EMPTY; > >>>> -} > >>>> > >>>> -#ifdef CONFIG_DEBUG_FS > >>>> + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); > >>>> + if (bdev->pool.use_dma32) > >>>> + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); > >>>> > >>>> -/* Test the shrinker functions and dump the result */ > >>>> -static int ttm_tt_debugfs_shrink_show(struct seq_file *m, void *data) > >>>> -{ > >>>> - struct shrink_control sc = { .gfp_mask = GFP_KERNEL }; > >>>> - > >>>> - fs_reclaim_acquire(GFP_KERNEL); > >>>> - seq_printf(m, "%lu/%lu\n", ttm_tt_shrinker_count(&mm_shrinker, &sc), > >>>> - ttm_tt_shrinker_scan(&mm_shrinker, &sc)); > >>>> - fs_reclaim_release(GFP_KERNEL); > >>>> - > >>>> - return 0; > >>>> + ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; > >>>> } > >>>> -DEFINE_SHOW_ATTRIBUTE(ttm_tt_debugfs_shrink); > >>>> - > >>>> -#endif > >>>> - > >>>> - > >>>> > >>>> /** > >>>> * ttm_tt_mgr_init - register with the MM shrinker > >>>> * > >>>> * Register with the MM shrinker for swapping out BOs. > >>>> */ > >>>> -int ttm_tt_mgr_init(void) > >>>> +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages) > >>>> { > >>>> -#ifdef CONFIG_DEBUG_FS > >>>> - debugfs_create_file("tt_shrink", 0400, ttm_debugfs_root, NULL, > >>>> - &ttm_tt_debugfs_shrink_fops); > >>>> -#endif > >>>> - > >>>> - mm_shrinker.count_objects = ttm_tt_shrinker_count; > >>>> - mm_shrinker.scan_objects = ttm_tt_shrinker_scan; > >>>> - mm_shrinker.seeks = 1; > >>>> - return register_shrinker(&mm_shrinker); > >>>> -} > >>>> + if (!ttm_pages_limit) > >>>> + ttm_pages_limit = num_pages; > >>>> > >>>> -/** > >>>> - * ttm_tt_mgr_fini - unregister our MM shrinker > >>>> - * > >>>> - * Unregisters the MM shrinker. > >>>> - */ > >>>> -void ttm_tt_mgr_fini(void) > >>>> -{ > >>>> - unregister_shrinker(&mm_shrinker); > >>>> + if (!ttm_dma32_pages_limit) > >>>> + ttm_dma32_pages_limit = num_dma32_pages; > >>>> } > >>>> diff --git a/include/drm/ttm/ttm_tt.h b/include/drm/ttm/ttm_tt.h > >>>> index 069f8130241a..134d09ef7766 100644 > >>>> --- a/include/drm/ttm/ttm_tt.h > >>>> +++ b/include/drm/ttm/ttm_tt.h > >>>> @@ -157,8 +157,7 @@ int ttm_tt_populate(struct ttm_device *bdev, struct ttm_tt *ttm, struct ttm_oper > >>>> */ > >>>> void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm); > >>>> > >>>> -int ttm_tt_mgr_init(void); > >>>> -void ttm_tt_mgr_fini(void); > >>>> +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages); > >>>> > >>>> #if IS_ENABLED(CONFIG_AGP) > >>>> #include <linux/agp_backend.h> > >>>> -- > >>>> 2.25.1 > >>>> > >> _______________________________________________ > >> dri-devel mailing list > >> dri-devel@lists.freedesktop.org > >> https://lists.freedesktop.org/mailman/listinfo/dri-devel > > > > >
diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c index 95e1b7b1f2e6..388da2a7f0bb 100644 --- a/drivers/gpu/drm/ttm/ttm_device.c +++ b/drivers/gpu/drm/ttm/ttm_device.c @@ -53,7 +53,6 @@ static void ttm_global_release(void) goto out; ttm_pool_mgr_fini(); - ttm_tt_mgr_fini(); __free_page(glob->dummy_read_page); memset(glob, 0, sizeof(*glob)); @@ -64,7 +63,7 @@ static void ttm_global_release(void) static int ttm_global_init(void) { struct ttm_global *glob = &ttm_glob; - unsigned long num_pages; + unsigned long num_pages, num_dma32; struct sysinfo si; int ret = 0; unsigned i; @@ -79,8 +78,15 @@ static int ttm_global_init(void) * system memory. */ num_pages = ((u64)si.totalram * si.mem_unit) >> PAGE_SHIFT; - ttm_pool_mgr_init(num_pages * 50 / 100); - ttm_tt_mgr_init(); + num_pages /= 2; + + /* But for DMA32 we limit ourself to only use 2GiB maximum. */ + num_dma32 = (u64)(si.totalram - si.totalhigh) * si.mem_unit + >> PAGE_SHIFT; + num_dma32 = min(num_dma32, 2UL << (30 - PAGE_SHIFT)); + + ttm_pool_mgr_init(num_pages); + ttm_tt_mgr_init(num_pages, num_dma32); spin_lock_init(&glob->lru_lock); glob->dummy_read_page = alloc_page(__GFP_ZERO | GFP_DMA32); diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index 2f0833c98d2c..5d8820725b75 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -40,8 +40,18 @@ #include "ttm_module.h" -static struct shrinker mm_shrinker; -static atomic_long_t swapable_pages; +static unsigned long ttm_pages_limit; + +MODULE_PARM_DESC(pages_limit, "Limit for the allocated pages"); +module_param_named(pages_limit, ttm_pages_limit, ulong, 0644); + +static unsigned long ttm_dma32_pages_limit; + +MODULE_PARM_DESC(dma32_pages_limit, "Limit for the allocated DMA32 pages"); +module_param_named(dma32_pages_limit, ttm_dma32_pages_limit, ulong, 0644); + +static atomic_long_t ttm_pages_allocated; +static atomic_long_t ttm_dma32_pages_allocated; /* * Allocates a ttm structure for the given BO. @@ -294,8 +304,6 @@ static void ttm_tt_add_mapping(struct ttm_device *bdev, struct ttm_tt *ttm) for (i = 0; i < ttm->num_pages; ++i) ttm->pages[i]->mapping = bdev->dev_mapping; - - atomic_long_add(ttm->num_pages, &swapable_pages); } int ttm_tt_populate(struct ttm_device *bdev, @@ -309,12 +317,25 @@ int ttm_tt_populate(struct ttm_device *bdev, if (ttm_tt_is_populated(ttm)) return 0; + atomic_long_add(ttm->num_pages, &ttm_pages_allocated); + if (bdev->pool.use_dma32) + atomic_long_add(ttm->num_pages, &ttm_dma32_pages_allocated); + + while (atomic_long_read(&ttm_pages_allocated) > ttm_pages_limit || + atomic_long_read(&ttm_dma32_pages_allocated) > + ttm_dma32_pages_limit) { + + ret = ttm_bo_swapout(ctx, GFP_KERNEL); + if (ret) + goto error; + } + if (bdev->funcs->ttm_tt_populate) ret = bdev->funcs->ttm_tt_populate(bdev, ttm, ctx); else ret = ttm_pool_alloc(&bdev->pool, ttm, ctx); if (ret) - return ret; + goto error; ttm_tt_add_mapping(bdev, ttm); ttm->page_flags |= TTM_PAGE_FLAG_PRIV_POPULATED; @@ -327,6 +348,12 @@ int ttm_tt_populate(struct ttm_device *bdev, } return 0; + +error: + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); + if (bdev->pool.use_dma32) + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); + return ret; } EXPORT_SYMBOL(ttm_tt_populate); @@ -342,12 +369,9 @@ static void ttm_tt_clear_mapping(struct ttm_tt *ttm) (*page)->mapping = NULL; (*page++)->index = 0; } - - atomic_long_sub(ttm->num_pages, &swapable_pages); } -void ttm_tt_unpopulate(struct ttm_device *bdev, - struct ttm_tt *ttm) +void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm) { if (!ttm_tt_is_populated(ttm)) return; @@ -357,76 +381,24 @@ void ttm_tt_unpopulate(struct ttm_device *bdev, bdev->funcs->ttm_tt_unpopulate(bdev, ttm); else ttm_pool_free(&bdev->pool, ttm); - ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; -} - -/* As long as pages are available make sure to release at least one */ -static unsigned long ttm_tt_shrinker_scan(struct shrinker *shrink, - struct shrink_control *sc) -{ - struct ttm_operation_ctx ctx = { - .no_wait_gpu = false - }; - int ret; - - ret = ttm_bo_swapout(&ctx, GFP_NOFS); - return ret < 0 ? SHRINK_EMPTY : ret; -} - -/* Return the number of pages available or SHRINK_EMPTY if we have none */ -static unsigned long ttm_tt_shrinker_count(struct shrinker *shrink, - struct shrink_control *sc) -{ - unsigned long num_pages; - - num_pages = atomic_long_read(&swapable_pages); - return num_pages ? num_pages : SHRINK_EMPTY; -} -#ifdef CONFIG_DEBUG_FS + atomic_long_sub(ttm->num_pages, &ttm_pages_allocated); + if (bdev->pool.use_dma32) + atomic_long_sub(ttm->num_pages, &ttm_dma32_pages_allocated); -/* Test the shrinker functions and dump the result */ -static int ttm_tt_debugfs_shrink_show(struct seq_file *m, void *data) -{ - struct shrink_control sc = { .gfp_mask = GFP_KERNEL }; - - fs_reclaim_acquire(GFP_KERNEL); - seq_printf(m, "%lu/%lu\n", ttm_tt_shrinker_count(&mm_shrinker, &sc), - ttm_tt_shrinker_scan(&mm_shrinker, &sc)); - fs_reclaim_release(GFP_KERNEL); - - return 0; + ttm->page_flags &= ~TTM_PAGE_FLAG_PRIV_POPULATED; } -DEFINE_SHOW_ATTRIBUTE(ttm_tt_debugfs_shrink); - -#endif - - /** * ttm_tt_mgr_init - register with the MM shrinker * * Register with the MM shrinker for swapping out BOs. */ -int ttm_tt_mgr_init(void) +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages) { -#ifdef CONFIG_DEBUG_FS - debugfs_create_file("tt_shrink", 0400, ttm_debugfs_root, NULL, - &ttm_tt_debugfs_shrink_fops); -#endif - - mm_shrinker.count_objects = ttm_tt_shrinker_count; - mm_shrinker.scan_objects = ttm_tt_shrinker_scan; - mm_shrinker.seeks = 1; - return register_shrinker(&mm_shrinker); -} + if (!ttm_pages_limit) + ttm_pages_limit = num_pages; -/** - * ttm_tt_mgr_fini - unregister our MM shrinker - * - * Unregisters the MM shrinker. - */ -void ttm_tt_mgr_fini(void) -{ - unregister_shrinker(&mm_shrinker); + if (!ttm_dma32_pages_limit) + ttm_dma32_pages_limit = num_dma32_pages; } diff --git a/include/drm/ttm/ttm_tt.h b/include/drm/ttm/ttm_tt.h index 069f8130241a..134d09ef7766 100644 --- a/include/drm/ttm/ttm_tt.h +++ b/include/drm/ttm/ttm_tt.h @@ -157,8 +157,7 @@ int ttm_tt_populate(struct ttm_device *bdev, struct ttm_tt *ttm, struct ttm_oper */ void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm); -int ttm_tt_mgr_init(void); -void ttm_tt_mgr_fini(void); +void ttm_tt_mgr_init(unsigned long num_pages, unsigned long num_dma32_pages); #if IS_ENABLED(CONFIG_AGP) #include <linux/agp_backend.h>
The shrinker based approach still has some flaws. Especially that we need temporary pages to free up the pages allocated to the driver is problematic in a shrinker. Signed-off-by: Christian König <christian.koenig@amd.com> --- drivers/gpu/drm/ttm/ttm_device.c | 14 ++-- drivers/gpu/drm/ttm/ttm_tt.c | 112 ++++++++++++------------------- include/drm/ttm/ttm_tt.h | 3 +- 3 files changed, 53 insertions(+), 76 deletions(-)