drm_pagemap_util.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. // SPDX-License-Identifier: GPL-2.0-only OR MIT
  2. /*
  3. * Copyright © 2025 Intel Corporation
  4. */
  5. #include <linux/slab.h>
  6. #include <drm/drm_drv.h>
  7. #include <drm/drm_managed.h>
  8. #include <drm/drm_pagemap.h>
  9. #include <drm/drm_pagemap_util.h>
  10. #include <drm/drm_print.h>
  11. /**
  12. * struct drm_pagemap_cache - Lookup structure for pagemaps
  13. *
  14. * Structure to keep track of active (refcount > 1) and inactive
  15. * (refcount == 0) pagemaps. Inactive pagemaps can be made active
  16. * again by waiting for the @queued completion (indicating that the
  17. * pagemap has been put on the @shrinker's list of shrinkable
  18. * pagemaps, and then successfully removing it from @shrinker's
  19. * list. The latter may fail if the shrinker is already in the
  20. * process of freeing the pagemap. A struct drm_pagemap_cache can
  21. * hold a single struct drm_pagemap.
  22. */
  23. struct drm_pagemap_cache {
  24. /** @lookup_mutex: Mutex making the lookup process atomic */
  25. struct mutex lookup_mutex;
  26. /** @lock: Lock protecting the @dpagemap pointer */
  27. spinlock_t lock;
  28. /** @shrinker: Pointer to the shrinker used for this cache. Immutable. */
  29. struct drm_pagemap_shrinker *shrinker;
  30. /** @dpagemap: Non-refcounted pointer to the drm_pagemap */
  31. struct drm_pagemap *dpagemap;
  32. /**
  33. * @queued: Signals when an inactive drm_pagemap has been put on
  34. * @shrinker's list.
  35. */
  36. struct completion queued;
  37. };
  38. /**
  39. * struct drm_pagemap_shrinker - Shrinker to remove unused pagemaps
  40. */
  41. struct drm_pagemap_shrinker {
  42. /** @drm: Pointer to the drm device. */
  43. struct drm_device *drm;
  44. /** @lock: Spinlock to protect the @dpagemaps list. */
  45. spinlock_t lock;
  46. /** @dpagemaps: List of unused dpagemaps. */
  47. struct list_head dpagemaps;
  48. /** @num_dpagemaps: Number of unused dpagemaps in @dpagemaps. */
  49. atomic_t num_dpagemaps;
  50. /** @shrink: Pointer to the struct shrinker. */
  51. struct shrinker *shrink;
  52. };
  53. static bool drm_pagemap_shrinker_cancel(struct drm_pagemap *dpagemap);
  54. static void drm_pagemap_cache_fini(void *arg)
  55. {
  56. struct drm_pagemap_cache *cache = arg;
  57. struct drm_pagemap *dpagemap;
  58. drm_dbg(cache->shrinker->drm, "Destroying dpagemap cache.\n");
  59. spin_lock(&cache->lock);
  60. dpagemap = cache->dpagemap;
  61. cache->dpagemap = NULL;
  62. if (dpagemap && !drm_pagemap_shrinker_cancel(dpagemap))
  63. dpagemap = NULL;
  64. spin_unlock(&cache->lock);
  65. if (dpagemap)
  66. drm_pagemap_destroy(dpagemap, false);
  67. mutex_destroy(&cache->lookup_mutex);
  68. kfree(cache);
  69. }
  70. /**
  71. * drm_pagemap_cache_create_devm() - Create a drm_pagemap_cache
  72. * @shrinker: Pointer to a struct drm_pagemap_shrinker.
  73. *
  74. * Create a device-managed drm_pagemap cache. The cache is automatically
  75. * destroyed on struct device removal, at which point any *inactive*
  76. * drm_pagemap's are destroyed.
  77. *
  78. * Return: Pointer to a struct drm_pagemap_cache on success. Error pointer
  79. * on failure.
  80. */
  81. struct drm_pagemap_cache *drm_pagemap_cache_create_devm(struct drm_pagemap_shrinker *shrinker)
  82. {
  83. struct drm_pagemap_cache *cache = kzalloc_obj(*cache);
  84. int err;
  85. if (!cache)
  86. return ERR_PTR(-ENOMEM);
  87. mutex_init(&cache->lookup_mutex);
  88. spin_lock_init(&cache->lock);
  89. cache->shrinker = shrinker;
  90. init_completion(&cache->queued);
  91. err = devm_add_action_or_reset(shrinker->drm->dev, drm_pagemap_cache_fini, cache);
  92. if (err)
  93. return ERR_PTR(err);
  94. return cache;
  95. }
  96. EXPORT_SYMBOL(drm_pagemap_cache_create_devm);
  97. /**
  98. * DOC: Cache lookup
  99. *
  100. * Cache lookup should be done under a locked mutex, so that a
  101. * failed drm_pagemap_get_from_cache() and a following
  102. * drm_pagemap_cache_setpagemap() are carried out as an atomic
  103. * operation WRT other lookups. Otherwise, racing lookups may
  104. * unnecessarily concurrently create pagemaps to fulfill a
  105. * failed lookup. The API provides two functions to perform this lock,
  106. * drm_pagemap_lock_lookup() and drm_pagemap_unlock_lookup() and they
  107. * should be used in the following way:
  108. *
  109. * .. code-block:: c
  110. *
  111. * drm_pagemap_lock_lookup(cache);
  112. * dpagemap = drm_pagemap_get_from_cache(cache);
  113. * if (dpagemap)
  114. * goto out_unlock;
  115. *
  116. * dpagemap = driver_create_new_dpagemap();
  117. * if (!IS_ERR(dpagemap))
  118. * drm_pagemap_cache_set_pagemap(cache, dpagemap);
  119. *
  120. * out_unlock:
  121. * drm_pagemap_unlock_lookup(cache);
  122. */
  123. /**
  124. * drm_pagemap_cache_lock_lookup() - Lock a drm_pagemap_cache for lookup.
  125. * @cache: The drm_pagemap_cache to lock.
  126. *
  127. * Return: %-EINTR if interrupted while blocking. %0 otherwise.
  128. */
  129. int drm_pagemap_cache_lock_lookup(struct drm_pagemap_cache *cache)
  130. {
  131. return mutex_lock_interruptible(&cache->lookup_mutex);
  132. }
  133. EXPORT_SYMBOL(drm_pagemap_cache_lock_lookup);
  134. /**
  135. * drm_pagemap_cache_unlock_lookup() - Unlock a drm_pagemap_cache after lookup.
  136. * @cache: The drm_pagemap_cache to unlock.
  137. */
  138. void drm_pagemap_cache_unlock_lookup(struct drm_pagemap_cache *cache)
  139. {
  140. mutex_unlock(&cache->lookup_mutex);
  141. }
  142. EXPORT_SYMBOL(drm_pagemap_cache_unlock_lookup);
  143. /**
  144. * drm_pagemap_get_from_cache() - Lookup of drm_pagemaps.
  145. * @cache: The cache used for lookup.
  146. *
  147. * If an active pagemap is present in the cache, it is immediately returned.
  148. * If an inactive pagemap is present, it's removed from the shrinker list and
  149. * an attempt is made to make it active.
  150. * If no pagemap present or the attempt to make it active failed, %NULL is returned
  151. * to indicate to the caller to create a new drm_pagemap and insert it into
  152. * the cache.
  153. *
  154. * Return: A reference-counted pointer to a drm_pagemap if successful. An error
  155. * pointer if an error occurred, or %NULL if no drm_pagemap was found and
  156. * the caller should insert a new one.
  157. */
  158. struct drm_pagemap *drm_pagemap_get_from_cache(struct drm_pagemap_cache *cache)
  159. {
  160. struct drm_pagemap *dpagemap;
  161. int err;
  162. lockdep_assert_held(&cache->lookup_mutex);
  163. retry:
  164. spin_lock(&cache->lock);
  165. dpagemap = cache->dpagemap;
  166. if (drm_pagemap_get_unless_zero(dpagemap)) {
  167. spin_unlock(&cache->lock);
  168. return dpagemap;
  169. }
  170. if (!dpagemap) {
  171. spin_unlock(&cache->lock);
  172. return NULL;
  173. }
  174. if (!try_wait_for_completion(&cache->queued)) {
  175. spin_unlock(&cache->lock);
  176. err = wait_for_completion_interruptible(&cache->queued);
  177. if (err)
  178. return ERR_PTR(err);
  179. goto retry;
  180. }
  181. if (drm_pagemap_shrinker_cancel(dpagemap)) {
  182. cache->dpagemap = NULL;
  183. spin_unlock(&cache->lock);
  184. err = drm_pagemap_reinit(dpagemap);
  185. if (err) {
  186. drm_pagemap_destroy(dpagemap, false);
  187. return ERR_PTR(err);
  188. }
  189. drm_pagemap_cache_set_pagemap(cache, dpagemap);
  190. } else {
  191. cache->dpagemap = NULL;
  192. spin_unlock(&cache->lock);
  193. dpagemap = NULL;
  194. }
  195. return dpagemap;
  196. }
  197. EXPORT_SYMBOL(drm_pagemap_get_from_cache);
  198. /**
  199. * drm_pagemap_cache_set_pagemap() - Assign a drm_pagemap to a drm_pagemap_cache
  200. * @cache: The cache to assign the drm_pagemap to.
  201. * @dpagemap: The drm_pagemap to assign.
  202. *
  203. * The function must be called to populate a drm_pagemap_cache only
  204. * after a call to drm_pagemap_get_from_cache() returns NULL.
  205. */
  206. void drm_pagemap_cache_set_pagemap(struct drm_pagemap_cache *cache, struct drm_pagemap *dpagemap)
  207. {
  208. struct drm_device *drm = dpagemap->drm;
  209. lockdep_assert_held(&cache->lookup_mutex);
  210. spin_lock(&cache->lock);
  211. dpagemap->cache = cache;
  212. swap(cache->dpagemap, dpagemap);
  213. reinit_completion(&cache->queued);
  214. spin_unlock(&cache->lock);
  215. drm_WARN_ON(drm, !!dpagemap);
  216. }
  217. EXPORT_SYMBOL(drm_pagemap_cache_set_pagemap);
  218. /**
  219. * drm_pagemap_get_from_cache_if_active() - Quick lookup of active drm_pagemaps
  220. * @cache: The cache to lookup from.
  221. *
  222. * Function that should be used to lookup a drm_pagemap that is already active.
  223. * (refcount > 0).
  224. *
  225. * Return: A pointer to the cache's drm_pagemap if it's active; %NULL otherwise.
  226. */
  227. struct drm_pagemap *drm_pagemap_get_from_cache_if_active(struct drm_pagemap_cache *cache)
  228. {
  229. struct drm_pagemap *dpagemap;
  230. spin_lock(&cache->lock);
  231. dpagemap = drm_pagemap_get_unless_zero(cache->dpagemap);
  232. spin_unlock(&cache->lock);
  233. return dpagemap;
  234. }
  235. EXPORT_SYMBOL(drm_pagemap_get_from_cache_if_active);
  236. static bool drm_pagemap_shrinker_cancel(struct drm_pagemap *dpagemap)
  237. {
  238. struct drm_pagemap_cache *cache = dpagemap->cache;
  239. struct drm_pagemap_shrinker *shrinker = cache->shrinker;
  240. spin_lock(&shrinker->lock);
  241. if (list_empty(&dpagemap->shrink_link)) {
  242. spin_unlock(&shrinker->lock);
  243. return false;
  244. }
  245. list_del_init(&dpagemap->shrink_link);
  246. atomic_dec(&shrinker->num_dpagemaps);
  247. spin_unlock(&shrinker->lock);
  248. return true;
  249. }
  250. #ifdef CONFIG_PROVE_LOCKING
  251. /**
  252. * drm_pagemap_shrinker_might_lock() - lockdep test for drm_pagemap_shrinker_add()
  253. * @dpagemap: The drm pagemap.
  254. *
  255. * The drm_pagemap_shrinker_add() function performs some locking.
  256. * This function can be called in code-paths that might
  257. * call drm_pagemap_shrinker_add() to detect any lockdep problems early.
  258. */
  259. void drm_pagemap_shrinker_might_lock(struct drm_pagemap *dpagemap)
  260. {
  261. int idx;
  262. if (drm_dev_enter(dpagemap->drm, &idx)) {
  263. struct drm_pagemap_cache *cache = dpagemap->cache;
  264. if (cache)
  265. might_lock(&cache->shrinker->lock);
  266. drm_dev_exit(idx);
  267. }
  268. }
  269. #endif
  270. /**
  271. * drm_pagemap_shrinker_add() - Add a drm_pagemap to the shrinker list or destroy
  272. * @dpagemap: The drm_pagemap.
  273. *
  274. * If @dpagemap is associated with a &struct drm_pagemap_cache AND the
  275. * struct device backing the drm device is still alive, add @dpagemap to
  276. * the &struct drm_pagemap_shrinker list of shrinkable drm_pagemaps.
  277. *
  278. * Otherwise destroy the pagemap directly using drm_pagemap_destroy().
  279. *
  280. * This is an internal function which is not intended to be exposed to drivers.
  281. */
  282. void drm_pagemap_shrinker_add(struct drm_pagemap *dpagemap)
  283. {
  284. struct drm_pagemap_cache *cache;
  285. struct drm_pagemap_shrinker *shrinker;
  286. int idx;
  287. /*
  288. * The pagemap cache and shrinker are disabled at
  289. * pci device remove time. After that, dpagemaps
  290. * are freed directly.
  291. */
  292. if (!drm_dev_enter(dpagemap->drm, &idx))
  293. goto out_no_cache;
  294. cache = dpagemap->cache;
  295. if (!cache) {
  296. drm_dev_exit(idx);
  297. goto out_no_cache;
  298. }
  299. shrinker = cache->shrinker;
  300. spin_lock(&shrinker->lock);
  301. list_add_tail(&dpagemap->shrink_link, &shrinker->dpagemaps);
  302. atomic_inc(&shrinker->num_dpagemaps);
  303. spin_unlock(&shrinker->lock);
  304. complete_all(&cache->queued);
  305. drm_dev_exit(idx);
  306. return;
  307. out_no_cache:
  308. drm_pagemap_destroy(dpagemap, true);
  309. }
  310. static unsigned long
  311. drm_pagemap_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
  312. {
  313. struct drm_pagemap_shrinker *shrinker = shrink->private_data;
  314. unsigned long count = atomic_read(&shrinker->num_dpagemaps);
  315. return count ? : SHRINK_EMPTY;
  316. }
  317. static unsigned long
  318. drm_pagemap_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
  319. {
  320. struct drm_pagemap_shrinker *shrinker = shrink->private_data;
  321. struct drm_pagemap *dpagemap;
  322. struct drm_pagemap_cache *cache;
  323. unsigned long nr_freed = 0;
  324. sc->nr_scanned = 0;
  325. spin_lock(&shrinker->lock);
  326. do {
  327. dpagemap = list_first_entry_or_null(&shrinker->dpagemaps, typeof(*dpagemap),
  328. shrink_link);
  329. if (!dpagemap)
  330. break;
  331. atomic_dec(&shrinker->num_dpagemaps);
  332. list_del_init(&dpagemap->shrink_link);
  333. spin_unlock(&shrinker->lock);
  334. sc->nr_scanned++;
  335. nr_freed++;
  336. cache = dpagemap->cache;
  337. spin_lock(&cache->lock);
  338. cache->dpagemap = NULL;
  339. spin_unlock(&cache->lock);
  340. drm_dbg(dpagemap->drm, "Shrinking dpagemap %p.\n", dpagemap);
  341. drm_pagemap_destroy(dpagemap, true);
  342. spin_lock(&shrinker->lock);
  343. } while (sc->nr_scanned < sc->nr_to_scan);
  344. spin_unlock(&shrinker->lock);
  345. return sc->nr_scanned ? nr_freed : SHRINK_STOP;
  346. }
  347. static void drm_pagemap_shrinker_fini(void *arg)
  348. {
  349. struct drm_pagemap_shrinker *shrinker = arg;
  350. drm_dbg(shrinker->drm, "Destroying dpagemap shrinker.\n");
  351. drm_WARN_ON(shrinker->drm, !!atomic_read(&shrinker->num_dpagemaps));
  352. shrinker_free(shrinker->shrink);
  353. kfree(shrinker);
  354. }
  355. /**
  356. * drm_pagemap_shrinker_create_devm() - Create and register a pagemap shrinker
  357. * @drm: The drm device
  358. *
  359. * Create and register a pagemap shrinker that shrinks unused pagemaps
  360. * and thereby reduces memory footprint.
  361. * The shrinker is drm_device managed and unregisters itself when
  362. * the drm device is removed.
  363. *
  364. * Return: %0 on success, negative error code on failure.
  365. */
  366. struct drm_pagemap_shrinker *drm_pagemap_shrinker_create_devm(struct drm_device *drm)
  367. {
  368. struct drm_pagemap_shrinker *shrinker;
  369. struct shrinker *shrink;
  370. int err;
  371. shrinker = kzalloc_obj(*shrinker);
  372. if (!shrinker)
  373. return ERR_PTR(-ENOMEM);
  374. shrink = shrinker_alloc(0, "drm-drm_pagemap:%s", drm->unique);
  375. if (!shrink) {
  376. kfree(shrinker);
  377. return ERR_PTR(-ENOMEM);
  378. }
  379. spin_lock_init(&shrinker->lock);
  380. INIT_LIST_HEAD(&shrinker->dpagemaps);
  381. shrinker->drm = drm;
  382. shrinker->shrink = shrink;
  383. shrink->count_objects = drm_pagemap_shrinker_count;
  384. shrink->scan_objects = drm_pagemap_shrinker_scan;
  385. shrink->private_data = shrinker;
  386. shrinker_register(shrink);
  387. err = devm_add_action_or_reset(drm->dev, drm_pagemap_shrinker_fini, shrinker);
  388. if (err)
  389. return ERR_PTR(err);
  390. return shrinker;
  391. }
  392. EXPORT_SYMBOL(drm_pagemap_shrinker_create_devm);
  393. /**
  394. * struct drm_pagemap_owner - Device interconnect group
  395. * @kref: Reference count.
  396. *
  397. * A struct drm_pagemap_owner identifies a device interconnect group.
  398. */
  399. struct drm_pagemap_owner {
  400. struct kref kref;
  401. };
  402. static void drm_pagemap_owner_release(struct kref *kref)
  403. {
  404. kfree(container_of(kref, struct drm_pagemap_owner, kref));
  405. }
  406. /**
  407. * drm_pagemap_release_owner() - Stop participating in an interconnect group
  408. * @peer: Pointer to the struct drm_pagemap_peer used when joining the group
  409. *
  410. * Stop participating in an interconnect group. This function is typically
  411. * called when a pagemap is removed to indicate that it doesn't need to
  412. * be taken into account.
  413. */
  414. void drm_pagemap_release_owner(struct drm_pagemap_peer *peer)
  415. {
  416. struct drm_pagemap_owner_list *owner_list = peer->list;
  417. if (!owner_list)
  418. return;
  419. mutex_lock(&owner_list->lock);
  420. list_del(&peer->link);
  421. kref_put(&peer->owner->kref, drm_pagemap_owner_release);
  422. peer->owner = NULL;
  423. mutex_unlock(&owner_list->lock);
  424. }
  425. EXPORT_SYMBOL(drm_pagemap_release_owner);
  426. /**
  427. * typedef interconnect_fn - Callback function to identify fast interconnects
  428. * @peer1: First endpoint.
  429. * @peer2: Second endpont.
  430. *
  431. * The function returns %true iff @peer1 and @peer2 have a fast interconnect.
  432. * Note that this is symmetrical. The function has no notion of client and provider,
  433. * which may not be sufficient in some cases. However, since the callback is intended
  434. * to guide in providing common pagemap owners, the notion of a common owner to
  435. * indicate fast interconnects would then have to change as well.
  436. *
  437. * Return: %true iff @peer1 and @peer2 have a fast interconnect. Otherwise @false.
  438. */
  439. typedef bool (*interconnect_fn)(struct drm_pagemap_peer *peer1, struct drm_pagemap_peer *peer2);
  440. /**
  441. * drm_pagemap_acquire_owner() - Join an interconnect group
  442. * @peer: A struct drm_pagemap_peer keeping track of the device interconnect
  443. * @owner_list: Pointer to the owner_list, keeping track of all interconnects
  444. * @has_interconnect: Callback function to determine whether two peers have a
  445. * fast local interconnect.
  446. *
  447. * Repeatedly calls @has_interconnect for @peer and other peers on @owner_list to
  448. * determine a set of peers for which @peer has a fast interconnect. That set will
  449. * have common &struct drm_pagemap_owner, and upon successful return, @peer::owner
  450. * will point to that struct, holding a reference, and @peer will be registered in
  451. * @owner_list. If @peer doesn't have any fast interconnects to other @peers, a
  452. * new unique &struct drm_pagemap_owner will be allocated for it, and that
  453. * may be shared with other peers that, at a later point, are determined to have
  454. * a fast interconnect with @peer.
  455. *
  456. * When @peer no longer participates in an interconnect group,
  457. * drm_pagemap_release_owner() should be called to drop the reference on the
  458. * struct drm_pagemap_owner.
  459. *
  460. * Return: %0 on success, negative error code on failure.
  461. */
  462. int drm_pagemap_acquire_owner(struct drm_pagemap_peer *peer,
  463. struct drm_pagemap_owner_list *owner_list,
  464. interconnect_fn has_interconnect)
  465. {
  466. struct drm_pagemap_peer *cur_peer;
  467. struct drm_pagemap_owner *owner = NULL;
  468. bool interconnect = false;
  469. mutex_lock(&owner_list->lock);
  470. might_alloc(GFP_KERNEL);
  471. list_for_each_entry(cur_peer, &owner_list->peers, link) {
  472. if (cur_peer->owner != owner) {
  473. if (owner && interconnect)
  474. break;
  475. owner = cur_peer->owner;
  476. interconnect = true;
  477. }
  478. if (interconnect && !has_interconnect(peer, cur_peer))
  479. interconnect = false;
  480. }
  481. if (!interconnect) {
  482. owner = kmalloc_obj(*owner);
  483. if (!owner) {
  484. mutex_unlock(&owner_list->lock);
  485. return -ENOMEM;
  486. }
  487. kref_init(&owner->kref);
  488. list_add_tail(&peer->link, &owner_list->peers);
  489. } else {
  490. kref_get(&owner->kref);
  491. list_add_tail(&peer->link, &cur_peer->link);
  492. }
  493. peer->owner = owner;
  494. peer->list = owner_list;
  495. mutex_unlock(&owner_list->lock);
  496. return 0;
  497. }
  498. EXPORT_SYMBOL(drm_pagemap_acquire_owner);