diff mbox series

[v4,01/12] drm/ttm: Allow TTM LRU list nodes of different types

Message ID 20240614102548.4364-2-thomas.hellstrom@linux.intel.com (mailing list archive)
State New, archived
Headers show
Series TTM shrinker helpers and xe buffer object shrinker | expand

Commit Message

Thomas Hellstrom June 14, 2024, 10:25 a.m. UTC
To be able to handle list unlocking while traversing the LRU
list, we want the iterators not only to point to the next
position of the list traversal, but to insert themselves as
list nodes at that point to work around the fact that the
next node might otherwise disappear from the list while
the iterator is pointing to it.

These list nodes need to be easily distinguishable from other
list nodes so that others traversing the list can skip
over them.

So declare a struct ttm_lru_item, with a struct list_head member
and a type enum. This will slightly increase the size of a
struct ttm_resource.

Changes in previous series:
- Update enum ttm_lru_item_type documentation.
v3:
- Introduce ttm_lru_first_res_or_null()
  (Christian König, Thomas Hellström)

Cc: Christian König <christian.koenig@amd.com>
Cc: Somalapuram Amaranath <Amaranath.Somalapuram@amd.com>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: <dri-devel@lists.freedesktop.org>
Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Reviewed-by: Matthew Brost <matthew.brost@intel.com>
Reviewed-by: Christian König <christian.koenig@amd.com>
---
 drivers/gpu/drm/ttm/ttm_device.c   |  4 +-
 drivers/gpu/drm/ttm/ttm_resource.c | 89 +++++++++++++++++++++++-------
 include/drm/ttm/ttm_resource.h     | 54 +++++++++++++++++-
 3 files changed, 125 insertions(+), 22 deletions(-)

Comments

kernel test robot June 15, 2024, 7:45 a.m. UTC | #1
Hi Thomas,

kernel test robot noticed the following build errors:

[auto build test ERROR on drm-xe/drm-xe-next]
[also build test ERROR on linus/master v6.10-rc3 next-20240613]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Thomas-Hellstr-m/drm-ttm-Allow-TTM-LRU-list-nodes-of-different-types/20240614-182911
base:   https://gitlab.freedesktop.org/drm/xe/kernel.git drm-xe-next
patch link:    https://lore.kernel.org/r/20240614102548.4364-2-thomas.hellstrom%40linux.intel.com
patch subject: [PATCH v4 01/12] drm/ttm: Allow TTM LRU list nodes of different types
config: loongarch-randconfig-001-20240615 (https://download.01.org/0day-ci/archive/20240615/202406151512.Smo5bzLx-lkp@intel.com/config)
compiler: loongarch64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240615/202406151512.Smo5bzLx-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202406151512.Smo5bzLx-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from include/linux/build_bug.h:5,
                    from include/linux/container_of.h:5,
                    from include/linux/list.h:5,
                    from include/drm/ttm/ttm_resource.h:29,
                    from drivers/gpu/drm/ttm/tests/ttm_resource_test.c:5:
   drivers/gpu/drm/ttm/tests/ttm_resource_test.c: In function 'ttm_resource_fini_basic':
>> drivers/gpu/drm/ttm/tests/ttm_resource_test.c:201:44: error: passing argument 1 of 'list_empty' from incompatible pointer type [-Werror=incompatible-pointer-types]
     201 |         KUNIT_ASSERT_TRUE(test, list_empty(&res->lru));
         |                                            ^~~~~~~~~
         |                                            |
         |                                            struct ttm_lru_item *
   include/linux/compiler.h:76:45: note: in definition of macro 'likely'
      76 | # define likely(x)      __builtin_expect(!!(x), 1)
         |                                             ^
   include/kunit/test.h:668:9: note: in expansion of macro 'KUNIT_UNARY_ASSERTION'
     668 |         KUNIT_UNARY_ASSERTION(test,                                            \
         |         ^~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:1232:9: note: in expansion of macro 'KUNIT_TRUE_MSG_ASSERTION'
    1232 |         KUNIT_TRUE_MSG_ASSERTION(test,                                         \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:1229:9: note: in expansion of macro 'KUNIT_ASSERT_TRUE_MSG'
    1229 |         KUNIT_ASSERT_TRUE_MSG(test, condition, NULL)
         |         ^~~~~~~~~~~~~~~~~~~~~
   drivers/gpu/drm/ttm/tests/ttm_resource_test.c:201:9: note: in expansion of macro 'KUNIT_ASSERT_TRUE'
     201 |         KUNIT_ASSERT_TRUE(test, list_empty(&res->lru));
         |         ^~~~~~~~~~~~~~~~~
   include/linux/list.h:371:54: note: expected 'const struct list_head *' but argument is of type 'struct ttm_lru_item *'
     371 | static inline int list_empty(const struct list_head *head)
         |                              ~~~~~~~~~~~~~~~~~~~~~~~~^~~~
   cc1: some warnings being treated as errors
--
   In file included from include/drm/drm_kunit_helpers.h:10,
                    from drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.h:13,
                    from drivers/gpu/drm/ttm/tests/ttm_bo_test.c:17:
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c: In function 'ttm_bo_unreserve_basic':
>> drivers/gpu/drm/ttm/tests/ttm_bo_test.c:268:38: error: passing argument 1 of 'list_is_last' from incompatible pointer type [-Werror=incompatible-pointer-types]
     268 |                         list_is_last(&res1->lru, &man->lru[bo->priority]), 1);
         |                                      ^~~~~~~~~~
         |                                      |
         |                                      struct ttm_lru_item *
   include/kunit/test.h:707:22: note: in definition of macro 'KUNIT_BASE_BINARY_ASSERTION'
     707 |         const typeof(left) __left = (left);                                    \
         |                      ^~~~
   include/kunit/test.h:1271:9: note: in expansion of macro 'KUNIT_BINARY_INT_ASSERTION'
    1271 |         KUNIT_BINARY_INT_ASSERTION(test,                                       \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:1268:9: note: in expansion of macro 'KUNIT_ASSERT_EQ_MSG'
    1268 |         KUNIT_ASSERT_EQ_MSG(test, left, right, NULL)
         |         ^~~~~~~~~~~~~~~~~~~
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c:267:9: note: in expansion of macro 'KUNIT_ASSERT_EQ'
     267 |         KUNIT_ASSERT_EQ(test,
         |         ^~~~~~~~~~~~~~~
   In file included from include/linux/mutex.h:15,
                    from include/linux/ww_mutex.h:20,
                    from include/linux/dma-resv.h:42,
                    from drivers/gpu/drm/ttm/tests/ttm_bo_test.c:5:
   include/linux/list.h:352:56: note: expected 'const struct list_head *' but argument is of type 'struct ttm_lru_item *'
     352 | static inline int list_is_last(const struct list_head *list, const struct list_head *head)
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~^~~~
>> drivers/gpu/drm/ttm/tests/ttm_bo_test.c:268:38: error: passing argument 1 of 'list_is_last' from incompatible pointer type [-Werror=incompatible-pointer-types]
     268 |                         list_is_last(&res1->lru, &man->lru[bo->priority]), 1);
         |                                      ^~~~~~~~~~
         |                                      |
         |                                      struct ttm_lru_item *
   include/kunit/test.h:707:38: note: in definition of macro 'KUNIT_BASE_BINARY_ASSERTION'
     707 |         const typeof(left) __left = (left);                                    \
         |                                      ^~~~
   include/kunit/test.h:1271:9: note: in expansion of macro 'KUNIT_BINARY_INT_ASSERTION'
    1271 |         KUNIT_BINARY_INT_ASSERTION(test,                                       \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:1268:9: note: in expansion of macro 'KUNIT_ASSERT_EQ_MSG'
    1268 |         KUNIT_ASSERT_EQ_MSG(test, left, right, NULL)
         |         ^~~~~~~~~~~~~~~~~~~
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c:267:9: note: in expansion of macro 'KUNIT_ASSERT_EQ'
     267 |         KUNIT_ASSERT_EQ(test,
         |         ^~~~~~~~~~~~~~~
   include/linux/list.h:352:56: note: expected 'const struct list_head *' but argument is of type 'struct ttm_lru_item *'
     352 | static inline int list_is_last(const struct list_head *list, const struct list_head *head)
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~^~~~
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c: In function 'ttm_bo_unreserve_pinned':
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c:305:38: error: passing argument 1 of 'list_is_last' from incompatible pointer type [-Werror=incompatible-pointer-types]
     305 |                         list_is_last(&res2->lru, &priv->ttm_dev->pinned), 1);
         |                                      ^~~~~~~~~~
         |                                      |
         |                                      struct ttm_lru_item *
   include/kunit/test.h:707:22: note: in definition of macro 'KUNIT_BASE_BINARY_ASSERTION'
     707 |         const typeof(left) __left = (left);                                    \
         |                      ^~~~
   include/kunit/test.h:1271:9: note: in expansion of macro 'KUNIT_BINARY_INT_ASSERTION'
    1271 |         KUNIT_BINARY_INT_ASSERTION(test,                                       \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:1268:9: note: in expansion of macro 'KUNIT_ASSERT_EQ_MSG'
    1268 |         KUNIT_ASSERT_EQ_MSG(test, left, right, NULL)
         |         ^~~~~~~~~~~~~~~~~~~
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c:304:9: note: in expansion of macro 'KUNIT_ASSERT_EQ'
     304 |         KUNIT_ASSERT_EQ(test,
         |         ^~~~~~~~~~~~~~~
   include/linux/list.h:352:56: note: expected 'const struct list_head *' but argument is of type 'struct ttm_lru_item *'
     352 | static inline int list_is_last(const struct list_head *list, const struct list_head *head)
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~^~~~
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c:305:38: error: passing argument 1 of 'list_is_last' from incompatible pointer type [-Werror=incompatible-pointer-types]
     305 |                         list_is_last(&res2->lru, &priv->ttm_dev->pinned), 1);
         |                                      ^~~~~~~~~~
         |                                      |
         |                                      struct ttm_lru_item *
   include/kunit/test.h:707:38: note: in definition of macro 'KUNIT_BASE_BINARY_ASSERTION'
     707 |         const typeof(left) __left = (left);                                    \
         |                                      ^~~~
   include/kunit/test.h:1271:9: note: in expansion of macro 'KUNIT_BINARY_INT_ASSERTION'
    1271 |         KUNIT_BINARY_INT_ASSERTION(test,                                       \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:1268:9: note: in expansion of macro 'KUNIT_ASSERT_EQ_MSG'
    1268 |         KUNIT_ASSERT_EQ_MSG(test, left, right, NULL)
         |         ^~~~~~~~~~~~~~~~~~~
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c:304:9: note: in expansion of macro 'KUNIT_ASSERT_EQ'
     304 |         KUNIT_ASSERT_EQ(test,
         |         ^~~~~~~~~~~~~~~
   include/linux/list.h:352:56: note: expected 'const struct list_head *' but argument is of type 'struct ttm_lru_item *'
     352 | static inline int list_is_last(const struct list_head *list, const struct list_head *head)
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~^~~~
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c:309:38: error: passing argument 1 of 'list_is_last' from incompatible pointer type [-Werror=incompatible-pointer-types]
     309 |                         list_is_last(&res1->lru, &priv->ttm_dev->pinned), 1);
         |                                      ^~~~~~~~~~
         |                                      |
         |                                      struct ttm_lru_item *
   include/kunit/test.h:707:22: note: in definition of macro 'KUNIT_BASE_BINARY_ASSERTION'
     707 |         const typeof(left) __left = (left);                                    \
         |                      ^~~~
   include/kunit/test.h:1271:9: note: in expansion of macro 'KUNIT_BINARY_INT_ASSERTION'
    1271 |         KUNIT_BINARY_INT_ASSERTION(test,                                       \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:1268:9: note: in expansion of macro 'KUNIT_ASSERT_EQ_MSG'
    1268 |         KUNIT_ASSERT_EQ_MSG(test, left, right, NULL)
         |         ^~~~~~~~~~~~~~~~~~~
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c:308:9: note: in expansion of macro 'KUNIT_ASSERT_EQ'
     308 |         KUNIT_ASSERT_EQ(test,
         |         ^~~~~~~~~~~~~~~
   include/linux/list.h:352:56: note: expected 'const struct list_head *' but argument is of type 'struct ttm_lru_item *'
     352 | static inline int list_is_last(const struct list_head *list, const struct list_head *head)
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~^~~~
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c:309:38: error: passing argument 1 of 'list_is_last' from incompatible pointer type [-Werror=incompatible-pointer-types]
     309 |                         list_is_last(&res1->lru, &priv->ttm_dev->pinned), 1);
         |                                      ^~~~~~~~~~
         |                                      |
         |                                      struct ttm_lru_item *
   include/kunit/test.h:707:38: note: in definition of macro 'KUNIT_BASE_BINARY_ASSERTION'
     707 |         const typeof(left) __left = (left);                                    \
         |                                      ^~~~
   include/kunit/test.h:1271:9: note: in expansion of macro 'KUNIT_BINARY_INT_ASSERTION'
    1271 |         KUNIT_BINARY_INT_ASSERTION(test,                                       \
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:1268:9: note: in expansion of macro 'KUNIT_ASSERT_EQ_MSG'
    1268 |         KUNIT_ASSERT_EQ_MSG(test, left, right, NULL)
         |         ^~~~~~~~~~~~~~~~~~~
   drivers/gpu/drm/ttm/tests/ttm_bo_test.c:308:9: note: in expansion of macro 'KUNIT_ASSERT_EQ'
     308 |         KUNIT_ASSERT_EQ(test,
         |         ^~~~~~~~~~~~~~~
   include/linux/list.h:352:56: note: expected 'const struct list_head *' but argument is of type 'struct ttm_lru_item *'
     352 | static inline int list_is_last(const struct list_head *list, const struct list_head *head)
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~^~~~


vim +/list_empty +201 drivers/gpu/drm/ttm/tests/ttm_resource_test.c

9afc1e0aa4851e Karolina Stolarek 2023-11-29  180  
9afc1e0aa4851e Karolina Stolarek 2023-11-29  181  static void ttm_resource_fini_basic(struct kunit *test)
9afc1e0aa4851e Karolina Stolarek 2023-11-29  182  {
9afc1e0aa4851e Karolina Stolarek 2023-11-29  183  	struct ttm_resource_test_priv *priv = test->priv;
9afc1e0aa4851e Karolina Stolarek 2023-11-29  184  	struct ttm_resource *res;
9afc1e0aa4851e Karolina Stolarek 2023-11-29  185  	struct ttm_buffer_object *bo;
9afc1e0aa4851e Karolina Stolarek 2023-11-29  186  	struct ttm_place *place;
9afc1e0aa4851e Karolina Stolarek 2023-11-29  187  	struct ttm_resource_manager *man;
9afc1e0aa4851e Karolina Stolarek 2023-11-29  188  
9afc1e0aa4851e Karolina Stolarek 2023-11-29  189  	ttm_init_test_mocks(test, priv, TTM_PL_SYSTEM, 0);
9afc1e0aa4851e Karolina Stolarek 2023-11-29  190  	bo = priv->bo;
9afc1e0aa4851e Karolina Stolarek 2023-11-29  191  	place = priv->place;
9afc1e0aa4851e Karolina Stolarek 2023-11-29  192  
9afc1e0aa4851e Karolina Stolarek 2023-11-29  193  	man = ttm_manager_type(priv->devs->ttm_dev, place->mem_type);
9afc1e0aa4851e Karolina Stolarek 2023-11-29  194  
9afc1e0aa4851e Karolina Stolarek 2023-11-29  195  	res = kunit_kzalloc(test, sizeof(*res), GFP_KERNEL);
9afc1e0aa4851e Karolina Stolarek 2023-11-29  196  	KUNIT_ASSERT_NOT_NULL(test, res);
9afc1e0aa4851e Karolina Stolarek 2023-11-29  197  
9afc1e0aa4851e Karolina Stolarek 2023-11-29  198  	ttm_resource_init(bo, place, res);
9afc1e0aa4851e Karolina Stolarek 2023-11-29  199  	ttm_resource_fini(man, res);
9afc1e0aa4851e Karolina Stolarek 2023-11-29  200  
9afc1e0aa4851e Karolina Stolarek 2023-11-29 @201  	KUNIT_ASSERT_TRUE(test, list_empty(&res->lru));
9afc1e0aa4851e Karolina Stolarek 2023-11-29  202  	KUNIT_ASSERT_EQ(test, man->usage, 0);
9afc1e0aa4851e Karolina Stolarek 2023-11-29  203  }
9afc1e0aa4851e Karolina Stolarek 2023-11-29  204
diff mbox series

Patch

diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c
index 434cf0258000..09411978a13a 100644
--- a/drivers/gpu/drm/ttm/ttm_device.c
+++ b/drivers/gpu/drm/ttm/ttm_device.c
@@ -274,14 +274,14 @@  static void ttm_device_clear_lru_dma_mappings(struct ttm_device *bdev,
 	struct ttm_resource *res;
 
 	spin_lock(&bdev->lru_lock);
-	while ((res = list_first_entry_or_null(list, typeof(*res), lru))) {
+	while ((res = ttm_lru_first_res_or_null(list))) {
 		struct ttm_buffer_object *bo = res->bo;
 
 		/* Take ref against racing releases once lru_lock is unlocked */
 		if (!ttm_bo_get_unless_zero(bo))
 			continue;
 
-		list_del_init(&res->lru);
+		list_del_init(&bo->resource->lru.link);
 		spin_unlock(&bdev->lru_lock);
 
 		if (bo->ttm)
diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c
index 4a66b851b67d..db9a7a3717c4 100644
--- a/drivers/gpu/drm/ttm/ttm_resource.c
+++ b/drivers/gpu/drm/ttm/ttm_resource.c
@@ -70,8 +70,8 @@  void ttm_lru_bulk_move_tail(struct ttm_lru_bulk_move *bulk)
 			dma_resv_assert_held(pos->last->bo->base.resv);
 
 			man = ttm_manager_type(pos->first->bo->bdev, i);
-			list_bulk_move_tail(&man->lru[j], &pos->first->lru,
-					    &pos->last->lru);
+			list_bulk_move_tail(&man->lru[j], &pos->first->lru.link,
+					    &pos->last->lru.link);
 		}
 	}
 }
@@ -84,14 +84,38 @@  ttm_lru_bulk_move_pos(struct ttm_lru_bulk_move *bulk, struct ttm_resource *res)
 	return &bulk->pos[res->mem_type][res->bo->priority];
 }
 
+/* Return the previous resource on the list (skip over non-resource list items) */
+static struct ttm_resource *ttm_lru_prev_res(struct ttm_resource *cur)
+{
+	struct ttm_lru_item *lru = &cur->lru;
+
+	do {
+		lru = list_prev_entry(lru, link);
+	} while (!ttm_lru_item_is_res(lru));
+
+	return ttm_lru_item_to_res(lru);
+}
+
+/* Return the next resource on the list (skip over non-resource list items) */
+static struct ttm_resource *ttm_lru_next_res(struct ttm_resource *cur)
+{
+	struct ttm_lru_item *lru = &cur->lru;
+
+	do {
+		lru = list_next_entry(lru, link);
+	} while (!ttm_lru_item_is_res(lru));
+
+	return ttm_lru_item_to_res(lru);
+}
+
 /* Move the resource to the tail of the bulk move range */
 static void ttm_lru_bulk_move_pos_tail(struct ttm_lru_bulk_move_pos *pos,
 				       struct ttm_resource *res)
 {
 	if (pos->last != res) {
 		if (pos->first == res)
-			pos->first = list_next_entry(res, lru);
-		list_move(&res->lru, &pos->last->lru);
+			pos->first = ttm_lru_next_res(res);
+		list_move(&res->lru.link, &pos->last->lru.link);
 		pos->last = res;
 	}
 }
@@ -122,11 +146,11 @@  static void ttm_lru_bulk_move_del(struct ttm_lru_bulk_move *bulk,
 		pos->first = NULL;
 		pos->last = NULL;
 	} else if (pos->first == res) {
-		pos->first = list_next_entry(res, lru);
+		pos->first = ttm_lru_next_res(res);
 	} else if (pos->last == res) {
-		pos->last = list_prev_entry(res, lru);
+		pos->last = ttm_lru_prev_res(res);
 	} else {
-		list_move(&res->lru, &pos->last->lru);
+		list_move(&res->lru.link, &pos->last->lru.link);
 	}
 }
 
@@ -155,7 +179,7 @@  void ttm_resource_move_to_lru_tail(struct ttm_resource *res)
 	lockdep_assert_held(&bo->bdev->lru_lock);
 
 	if (bo->pin_count) {
-		list_move_tail(&res->lru, &bdev->pinned);
+		list_move_tail(&res->lru.link, &bdev->pinned);
 
 	} else	if (bo->bulk_move) {
 		struct ttm_lru_bulk_move_pos *pos =
@@ -166,7 +190,7 @@  void ttm_resource_move_to_lru_tail(struct ttm_resource *res)
 		struct ttm_resource_manager *man;
 
 		man = ttm_manager_type(bdev, res->mem_type);
-		list_move_tail(&res->lru, &man->lru[bo->priority]);
+		list_move_tail(&res->lru.link, &man->lru[bo->priority]);
 	}
 }
 
@@ -197,9 +221,9 @@  void ttm_resource_init(struct ttm_buffer_object *bo,
 	man = ttm_manager_type(bo->bdev, place->mem_type);
 	spin_lock(&bo->bdev->lru_lock);
 	if (bo->pin_count)
-		list_add_tail(&res->lru, &bo->bdev->pinned);
+		list_add_tail(&res->lru.link, &bo->bdev->pinned);
 	else
-		list_add_tail(&res->lru, &man->lru[bo->priority]);
+		list_add_tail(&res->lru.link, &man->lru[bo->priority]);
 	man->usage += res->size;
 	spin_unlock(&bo->bdev->lru_lock);
 }
@@ -221,7 +245,7 @@  void ttm_resource_fini(struct ttm_resource_manager *man,
 	struct ttm_device *bdev = man->bdev;
 
 	spin_lock(&bdev->lru_lock);
-	list_del_init(&res->lru);
+	list_del_init(&res->lru.link);
 	man->usage -= res->size;
 	spin_unlock(&bdev->lru_lock);
 }
@@ -472,14 +496,16 @@  struct ttm_resource *
 ttm_resource_manager_first(struct ttm_resource_manager *man,
 			   struct ttm_resource_cursor *cursor)
 {
-	struct ttm_resource *res;
+	struct ttm_lru_item *lru;
 
 	lockdep_assert_held(&man->bdev->lru_lock);
 
 	for (cursor->priority = 0; cursor->priority < TTM_MAX_BO_PRIORITY;
 	     ++cursor->priority)
-		list_for_each_entry(res, &man->lru[cursor->priority], lru)
-			return res;
+		list_for_each_entry(lru, &man->lru[cursor->priority], link) {
+			if (ttm_lru_item_is_res(lru))
+				return ttm_lru_item_to_res(lru);
+		}
 
 	return NULL;
 }
@@ -498,15 +524,40 @@  ttm_resource_manager_next(struct ttm_resource_manager *man,
 			  struct ttm_resource_cursor *cursor,
 			  struct ttm_resource *res)
 {
+	struct ttm_lru_item *lru = &res->lru;
+
 	lockdep_assert_held(&man->bdev->lru_lock);
 
-	list_for_each_entry_continue(res, &man->lru[cursor->priority], lru)
-		return res;
+	list_for_each_entry_continue(lru, &man->lru[cursor->priority], link) {
+		if (ttm_lru_item_is_res(lru))
+			return ttm_lru_item_to_res(lru);
+	}
 
 	for (++cursor->priority; cursor->priority < TTM_MAX_BO_PRIORITY;
 	     ++cursor->priority)
-		list_for_each_entry(res, &man->lru[cursor->priority], lru)
-			return res;
+		list_for_each_entry(lru, &man->lru[cursor->priority], link) {
+			if (ttm_lru_item_is_res(lru))
+				ttm_lru_item_to_res(lru);
+		}
+
+	return NULL;
+}
+
+/**
+ * ttm_lru_first_res_or_null() - Return the first resource on an lru list
+ * @head: The list head of the lru list.
+ *
+ * Return: Pointer to the first resource on the lru list or NULL if
+ * there is none.
+ */
+struct ttm_resource *ttm_lru_first_res_or_null(struct list_head *head)
+{
+	struct ttm_lru_item *lru;
+
+	list_for_each_entry(lru, head, link) {
+		if (ttm_lru_item_is_res(lru))
+			return ttm_lru_item_to_res(lru);
+	}
 
 	return NULL;
 }
diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h
index 69769355139f..1511d91e290d 100644
--- a/include/drm/ttm/ttm_resource.h
+++ b/include/drm/ttm/ttm_resource.h
@@ -49,6 +49,43 @@  struct io_mapping;
 struct sg_table;
 struct scatterlist;
 
+/**
+ * enum ttm_lru_item_type - enumerate ttm_lru_item subclasses
+ */
+enum ttm_lru_item_type {
+	/** @TTM_LRU_RESOURCE: The resource subclass */
+	TTM_LRU_RESOURCE,
+	/** @TTM_LRU_HITCH: The iterator hitch subclass */
+	TTM_LRU_HITCH
+};
+
+/**
+ * struct ttm_lru_item - The TTM lru list node base class
+ * @link: The list link
+ * @type: The subclass type
+ */
+struct ttm_lru_item {
+	struct list_head link;
+	enum ttm_lru_item_type type;
+};
+
+/**
+ * ttm_lru_item_init() - initialize a struct ttm_lru_item
+ * @item: The item to initialize
+ * @type: The subclass type
+ */
+static inline void ttm_lru_item_init(struct ttm_lru_item *item,
+				     enum ttm_lru_item_type type)
+{
+	item->type = type;
+	INIT_LIST_HEAD(&item->link);
+}
+
+static inline bool ttm_lru_item_is_res(const struct ttm_lru_item *item)
+{
+	return item->type == TTM_LRU_RESOURCE;
+}
+
 struct ttm_resource_manager_func {
 	/**
 	 * struct ttm_resource_manager_func member alloc
@@ -217,9 +254,21 @@  struct ttm_resource {
 	/**
 	 * @lru: Least recently used list, see &ttm_resource_manager.lru
 	 */
-	struct list_head lru;
+	struct ttm_lru_item lru;
 };
 
+/**
+ * ttm_lru_item_to_res() - Downcast a struct ttm_lru_item to a struct ttm_resource
+ * @item: The struct ttm_lru_item to downcast
+ *
+ * Return: Pointer to the embedding struct ttm_resource
+ */
+static inline struct ttm_resource *
+ttm_lru_item_to_res(struct ttm_lru_item *item)
+{
+	return container_of(item, struct ttm_resource, lru);
+}
+
 /**
  * struct ttm_resource_cursor
  *
@@ -393,6 +442,9 @@  ttm_resource_manager_next(struct ttm_resource_manager *man,
 			  struct ttm_resource_cursor *cursor,
 			  struct ttm_resource *res);
 
+struct ttm_resource *
+ttm_lru_first_res_or_null(struct list_head *head);
+
 /**
  * ttm_resource_manager_for_each_res - iterate over all resources
  * @man: the resource manager