Skip to content

Commit 2b624a2

Browse files
Maarten Lankhorstmripard
authored andcommitted
drm/ttm: Handle cgroup based eviction in TTM
cgroup resource allocation has to be handled in TTM, so -EAGAIN from cgroups can be converted into -ENOSPC, and the limitcg can be properly evicted in ttm code. When hitting a resource limit through -EAGAIN, the cgroup for which the limit is hit is also returned. This allows eviction to delete only from cgroups which are a subgroup of the current cgroup. The returned CSS is used to determine if eviction is valuable for a given resource, and allows TTM to only target specific resources to lower memory usage. Co-developed-by: Friedrich Vock <[email protected]> Signed-off-by: Friedrich Vock <[email protected]> Co-developed-by: Maxime Ripard <[email protected]> Signed-off-by: Maarten Lankhorst <[email protected]> Acked-by: Tejun Heo <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Maxime Ripard <[email protected]>
1 parent 7b0af16 commit 2b624a2

File tree

6 files changed

+89
-22
lines changed

6 files changed

+89
-22
lines changed

drivers/gpu/drm/ttm/tests/ttm_bo_test.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -258,13 +258,13 @@ static void ttm_bo_unreserve_basic(struct kunit *test)
258258
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE, NULL);
259259
bo->priority = bo_prio;
260260

261-
err = ttm_resource_alloc(bo, place, &res1);
261+
err = ttm_resource_alloc(bo, place, &res1, NULL);
262262
KUNIT_ASSERT_EQ(test, err, 0);
263263

264264
bo->resource = res1;
265265

266266
/* Add a dummy resource to populate LRU */
267-
ttm_resource_alloc(bo, place, &res2);
267+
ttm_resource_alloc(bo, place, &res2, NULL);
268268

269269
dma_resv_lock(bo->base.resv, NULL);
270270
ttm_bo_unreserve(bo);
@@ -300,12 +300,12 @@ static void ttm_bo_unreserve_pinned(struct kunit *test)
300300
dma_resv_lock(bo->base.resv, NULL);
301301
ttm_bo_pin(bo);
302302

303-
err = ttm_resource_alloc(bo, place, &res1);
303+
err = ttm_resource_alloc(bo, place, &res1, NULL);
304304
KUNIT_ASSERT_EQ(test, err, 0);
305305
bo->resource = res1;
306306

307307
/* Add a dummy resource to the pinned list */
308-
err = ttm_resource_alloc(bo, place, &res2);
308+
err = ttm_resource_alloc(bo, place, &res2, NULL);
309309
KUNIT_ASSERT_EQ(test, err, 0);
310310
KUNIT_ASSERT_EQ(test,
311311
list_is_last(&res2->lru.link, &priv->ttm_dev->unevictable), 1);
@@ -355,15 +355,15 @@ static void ttm_bo_unreserve_bulk(struct kunit *test)
355355
ttm_bo_set_bulk_move(bo1, &lru_bulk_move);
356356
dma_resv_unlock(bo1->base.resv);
357357

358-
err = ttm_resource_alloc(bo1, place, &res1);
358+
err = ttm_resource_alloc(bo1, place, &res1, NULL);
359359
KUNIT_ASSERT_EQ(test, err, 0);
360360
bo1->resource = res1;
361361

362362
dma_resv_lock(bo2->base.resv, NULL);
363363
ttm_bo_set_bulk_move(bo2, &lru_bulk_move);
364364
dma_resv_unlock(bo2->base.resv);
365365

366-
err = ttm_resource_alloc(bo2, place, &res2);
366+
err = ttm_resource_alloc(bo2, place, &res2, NULL);
367367
KUNIT_ASSERT_EQ(test, err, 0);
368368
bo2->resource = res2;
369369

@@ -401,7 +401,7 @@ static void ttm_bo_put_basic(struct kunit *test)
401401
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE, NULL);
402402
bo->type = ttm_bo_type_device;
403403

404-
err = ttm_resource_alloc(bo, place, &res);
404+
err = ttm_resource_alloc(bo, place, &res, NULL);
405405
KUNIT_ASSERT_EQ(test, err, 0);
406406
bo->resource = res;
407407

@@ -518,7 +518,7 @@ static void ttm_bo_pin_unpin_resource(struct kunit *test)
518518

519519
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE, NULL);
520520

521-
err = ttm_resource_alloc(bo, place, &res);
521+
err = ttm_resource_alloc(bo, place, &res, NULL);
522522
KUNIT_ASSERT_EQ(test, err, 0);
523523
bo->resource = res;
524524

@@ -569,7 +569,7 @@ static void ttm_bo_multiple_pin_one_unpin(struct kunit *test)
569569

570570
bo = ttm_bo_kunit_init(test, test->priv, BO_SIZE, NULL);
571571

572-
err = ttm_resource_alloc(bo, place, &res);
572+
err = ttm_resource_alloc(bo, place, &res, NULL);
573573
KUNIT_ASSERT_EQ(test, err, 0);
574574
bo->resource = res;
575575

drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ static void ttm_bo_validate_no_placement_signaled(struct kunit *test)
542542
bo->ttm = old_tt;
543543
}
544544

545-
err = ttm_resource_alloc(bo, place, &bo->resource);
545+
err = ttm_resource_alloc(bo, place, &bo->resource, NULL);
546546
KUNIT_EXPECT_EQ(test, err, 0);
547547
KUNIT_ASSERT_EQ(test, man->usage, size);
548548

@@ -603,7 +603,7 @@ static void ttm_bo_validate_no_placement_not_signaled(struct kunit *test)
603603
bo = ttm_bo_kunit_init(test, test->priv, size, NULL);
604604
bo->type = params->bo_type;
605605

606-
err = ttm_resource_alloc(bo, place, &bo->resource);
606+
err = ttm_resource_alloc(bo, place, &bo->resource, NULL);
607607
KUNIT_EXPECT_EQ(test, err, 0);
608608

609609
placement = kunit_kzalloc(test, sizeof(*placement), GFP_KERNEL);

drivers/gpu/drm/ttm/tests/ttm_resource_test.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ static void ttm_sys_man_free_basic(struct kunit *test)
302302
res = kunit_kzalloc(test, sizeof(*res), GFP_KERNEL);
303303
KUNIT_ASSERT_NOT_NULL(test, res);
304304

305-
ttm_resource_alloc(bo, place, &res);
305+
ttm_resource_alloc(bo, place, &res, NULL);
306306

307307
man = ttm_manager_type(priv->devs->ttm_dev, mem_type);
308308
man->func->free(man, res);

drivers/gpu/drm/ttm/ttm_bo.c

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <linux/file.h>
4343
#include <linux/module.h>
4444
#include <linux/atomic.h>
45+
#include <linux/cgroup_dmem.h>
4546
#include <linux/dma-resv.h>
4647

4748
#include "ttm_module.h"
@@ -499,6 +500,13 @@ struct ttm_bo_evict_walk {
499500
struct ttm_resource **res;
500501
/** @evicted: Number of successful evictions. */
501502
unsigned long evicted;
503+
504+
/** @limit_pool: Which pool limit we should test against */
505+
struct dmem_cgroup_pool_state *limit_pool;
506+
/** @try_low: Whether we should attempt to evict BO's with low watermark threshold */
507+
bool try_low;
508+
/** @hit_low: If we cannot evict a bo when @try_low is false (first pass) */
509+
bool hit_low;
502510
};
503511

504512
static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
@@ -507,6 +515,10 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *
507515
container_of(walk, typeof(*evict_walk), walk);
508516
s64 lret;
509517

518+
if (!dmem_cgroup_state_evict_valuable(evict_walk->limit_pool, bo->resource->css,
519+
evict_walk->try_low, &evict_walk->hit_low))
520+
return 0;
521+
510522
if (bo->pin_count || !bo->bdev->funcs->eviction_valuable(bo, evict_walk->place))
511523
return 0;
512524

@@ -524,7 +536,7 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *
524536
evict_walk->evicted++;
525537
if (evict_walk->res)
526538
lret = ttm_resource_alloc(evict_walk->evictor, evict_walk->place,
527-
evict_walk->res);
539+
evict_walk->res, NULL);
528540
if (lret == 0)
529541
return 1;
530542
out:
@@ -545,7 +557,8 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
545557
struct ttm_buffer_object *evictor,
546558
struct ttm_operation_ctx *ctx,
547559
struct ww_acquire_ctx *ticket,
548-
struct ttm_resource **res)
560+
struct ttm_resource **res,
561+
struct dmem_cgroup_pool_state *limit_pool)
549562
{
550563
struct ttm_bo_evict_walk evict_walk = {
551564
.walk = {
@@ -556,22 +569,39 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev,
556569
.place = place,
557570
.evictor = evictor,
558571
.res = res,
572+
.limit_pool = limit_pool,
559573
};
560574
s64 lret;
561575

562576
evict_walk.walk.trylock_only = true;
563577
lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1);
578+
579+
/* One more attempt if we hit low limit? */
580+
if (!lret && evict_walk.hit_low) {
581+
evict_walk.try_low = true;
582+
lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1);
583+
}
564584
if (lret || !ticket)
565585
goto out;
566586

587+
/* Reset low limit */
588+
evict_walk.try_low = evict_walk.hit_low = false;
567589
/* If ticket-locking, repeat while making progress. */
568590
evict_walk.walk.trylock_only = false;
591+
592+
retry:
569593
do {
570594
/* The walk may clear the evict_walk.walk.ticket field */
571595
evict_walk.walk.ticket = ticket;
572596
evict_walk.evicted = 0;
573597
lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1);
574598
} while (!lret && evict_walk.evicted);
599+
600+
/* We hit the low limit? Try once more */
601+
if (!lret && evict_walk.hit_low && !evict_walk.try_low) {
602+
evict_walk.try_low = true;
603+
goto retry;
604+
}
575605
out:
576606
if (lret < 0)
577607
return lret;
@@ -689,6 +719,7 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo,
689719

690720
for (i = 0; i < placement->num_placement; ++i) {
691721
const struct ttm_place *place = &placement->placement[i];
722+
struct dmem_cgroup_pool_state *limit_pool = NULL;
692723
struct ttm_resource_manager *man;
693724
bool may_evict;
694725

@@ -701,15 +732,20 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo,
701732
continue;
702733

703734
may_evict = (force_space && place->mem_type != TTM_PL_SYSTEM);
704-
ret = ttm_resource_alloc(bo, place, res);
735+
ret = ttm_resource_alloc(bo, place, res, force_space ? &limit_pool : NULL);
705736
if (ret) {
706-
if (ret != -ENOSPC)
737+
if (ret != -ENOSPC && ret != -EAGAIN) {
738+
dmem_cgroup_pool_state_put(limit_pool);
707739
return ret;
708-
if (!may_evict)
740+
}
741+
if (!may_evict) {
742+
dmem_cgroup_pool_state_put(limit_pool);
709743
continue;
744+
}
710745

711746
ret = ttm_bo_evict_alloc(bdev, man, place, bo, ctx,
712-
ticket, res);
747+
ticket, res, limit_pool);
748+
dmem_cgroup_pool_state_put(limit_pool);
713749
if (ret == -EBUSY)
714750
continue;
715751
if (ret)
@@ -1056,6 +1092,8 @@ struct ttm_bo_swapout_walk {
10561092
struct ttm_lru_walk walk;
10571093
/** @gfp_flags: The gfp flags to use for ttm_tt_swapout() */
10581094
gfp_t gfp_flags;
1095+
1096+
bool hit_low, evict_low;
10591097
};
10601098

10611099
static s64
@@ -1106,7 +1144,7 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
11061144

11071145
memset(&hop, 0, sizeof(hop));
11081146
place.mem_type = TTM_PL_SYSTEM;
1109-
ret = ttm_resource_alloc(bo, &place, &evict_mem);
1147+
ret = ttm_resource_alloc(bo, &place, &evict_mem, NULL);
11101148
if (ret)
11111149
goto out;
11121150

drivers/gpu/drm/ttm/ttm_resource.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <linux/io-mapping.h>
2727
#include <linux/iosys-map.h>
2828
#include <linux/scatterlist.h>
29+
#include <linux/cgroup_dmem.h>
2930

3031
#include <drm/ttm/ttm_bo.h>
3132
#include <drm/ttm/ttm_placement.h>
@@ -350,15 +351,28 @@ EXPORT_SYMBOL(ttm_resource_fini);
350351

351352
int ttm_resource_alloc(struct ttm_buffer_object *bo,
352353
const struct ttm_place *place,
353-
struct ttm_resource **res_ptr)
354+
struct ttm_resource **res_ptr,
355+
struct dmem_cgroup_pool_state **ret_limit_pool)
354356
{
355357
struct ttm_resource_manager *man =
356358
ttm_manager_type(bo->bdev, place->mem_type);
359+
struct dmem_cgroup_pool_state *pool = NULL;
357360
int ret;
358361

362+
if (man->cg) {
363+
ret = dmem_cgroup_try_charge(man->cg, bo->base.size, &pool, ret_limit_pool);
364+
if (ret)
365+
return ret;
366+
}
367+
359368
ret = man->func->alloc(man, bo, place, res_ptr);
360-
if (ret)
369+
if (ret) {
370+
if (pool)
371+
dmem_cgroup_uncharge(pool, bo->base.size);
361372
return ret;
373+
}
374+
375+
(*res_ptr)->css = pool;
362376

363377
spin_lock(&bo->bdev->lru_lock);
364378
ttm_resource_add_bulk_move(*res_ptr, bo);
@@ -370,16 +384,21 @@ EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_resource_alloc);
370384
void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res)
371385
{
372386
struct ttm_resource_manager *man;
387+
struct dmem_cgroup_pool_state *pool;
373388

374389
if (!*res)
375390
return;
376391

377392
spin_lock(&bo->bdev->lru_lock);
378393
ttm_resource_del_bulk_move(*res, bo);
379394
spin_unlock(&bo->bdev->lru_lock);
395+
396+
pool = (*res)->css;
380397
man = ttm_manager_type(bo->bdev, (*res)->mem_type);
381398
man->func->free(man, *res);
382399
*res = NULL;
400+
if (man->cg)
401+
dmem_cgroup_uncharge(pool, bo->base.size);
383402
}
384403
EXPORT_SYMBOL(ttm_resource_free);
385404

include/drm/ttm/ttm_resource.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#define TTM_MAX_BO_PRIORITY 4U
3939
#define TTM_NUM_MEM_TYPES 8
4040

41+
struct dmem_cgroup_device;
4142
struct ttm_device;
4243
struct ttm_resource_manager;
4344
struct ttm_resource;
@@ -211,6 +212,11 @@ struct ttm_resource_manager {
211212
* bdev->lru_lock.
212213
*/
213214
uint64_t usage;
215+
216+
/**
217+
* @cg: &dmem_cgroup_region used for memory accounting, if not NULL.
218+
*/
219+
struct dmem_cgroup_region *cg;
214220
};
215221

216222
/**
@@ -239,6 +245,7 @@ struct ttm_bus_placement {
239245
* @placement: Placement flags.
240246
* @bus: Placement on io bus accessible to the CPU
241247
* @bo: weak reference to the BO, protected by ttm_device::lru_lock
248+
* @css: cgroup state this resource is charged to
242249
*
243250
* Structure indicating the placement and space resources used by a
244251
* buffer object.
@@ -251,6 +258,8 @@ struct ttm_resource {
251258
struct ttm_bus_placement bus;
252259
struct ttm_buffer_object *bo;
253260

261+
struct dmem_cgroup_pool_state *css;
262+
254263
/**
255264
* @lru: Least recently used list, see &ttm_resource_manager.lru
256265
*/
@@ -432,7 +441,8 @@ void ttm_resource_fini(struct ttm_resource_manager *man,
432441

433442
int ttm_resource_alloc(struct ttm_buffer_object *bo,
434443
const struct ttm_place *place,
435-
struct ttm_resource **res);
444+
struct ttm_resource **res,
445+
struct dmem_cgroup_pool_state **ret_limit_pool);
436446
void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res);
437447
bool ttm_resource_intersects(struct ttm_device *bdev,
438448
struct ttm_resource *res,

0 commit comments

Comments
 (0)