162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2020 Advanced Micro Devices, Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
662306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
762306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
862306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
962306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
1062306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
1362306a36Sopenharmony_ci * all copies or substantial portions of the Software.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1862306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1962306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2062306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2162306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Authors: Christian König
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* Pooling of allocated pages is necessary because changing the caching
2762306a36Sopenharmony_ci * attributes on x86 of the linear mapping requires a costly cross CPU TLB
2862306a36Sopenharmony_ci * invalidate for those addresses.
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Additional to that allocations from the DMA coherent API are pooled as well
3162306a36Sopenharmony_ci * cause they are rather slow compared to alloc_pages+map.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <linux/module.h>
3562306a36Sopenharmony_ci#include <linux/dma-mapping.h>
3662306a36Sopenharmony_ci#include <linux/debugfs.h>
3762306a36Sopenharmony_ci#include <linux/highmem.h>
3862306a36Sopenharmony_ci#include <linux/sched/mm.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#ifdef CONFIG_X86
4162306a36Sopenharmony_ci#include <asm/set_memory.h>
4262306a36Sopenharmony_ci#endif
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#include <drm/ttm/ttm_pool.h>
4562306a36Sopenharmony_ci#include <drm/ttm/ttm_tt.h>
4662306a36Sopenharmony_ci#include <drm/ttm/ttm_bo.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#include "ttm_module.h"
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/**
5162306a36Sopenharmony_ci * struct ttm_pool_dma - Helper object for coherent DMA mappings
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci * @addr: original DMA address returned for the mapping
5462306a36Sopenharmony_ci * @vaddr: original vaddr return for the mapping and order in the lower bits
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_cistruct ttm_pool_dma {
5762306a36Sopenharmony_ci	dma_addr_t addr;
5862306a36Sopenharmony_ci	unsigned long vaddr;
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic unsigned long page_pool_size;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciMODULE_PARM_DESC(page_pool_size, "Number of pages in the WC/UC/DMA pool");
6462306a36Sopenharmony_cimodule_param(page_pool_size, ulong, 0644);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic atomic_long_t allocated_pages;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic struct ttm_pool_type global_write_combined[MAX_ORDER + 1];
6962306a36Sopenharmony_cistatic struct ttm_pool_type global_uncached[MAX_ORDER + 1];
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic struct ttm_pool_type global_dma32_write_combined[MAX_ORDER + 1];
7262306a36Sopenharmony_cistatic struct ttm_pool_type global_dma32_uncached[MAX_ORDER + 1];
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic spinlock_t shrinker_lock;
7562306a36Sopenharmony_cistatic struct list_head shrinker_list;
7662306a36Sopenharmony_cistatic struct shrinker mm_shrinker;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* Allocate pages of size 1 << order with the given gfp_flags */
7962306a36Sopenharmony_cistatic struct page *ttm_pool_alloc_page(struct ttm_pool *pool, gfp_t gfp_flags,
8062306a36Sopenharmony_ci					unsigned int order)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	unsigned long attr = DMA_ATTR_FORCE_CONTIGUOUS;
8362306a36Sopenharmony_ci	struct ttm_pool_dma *dma;
8462306a36Sopenharmony_ci	struct page *p;
8562306a36Sopenharmony_ci	void *vaddr;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* Don't set the __GFP_COMP flag for higher order allocations.
8862306a36Sopenharmony_ci	 * Mapping pages directly into an userspace process and calling
8962306a36Sopenharmony_ci	 * put_page() on a TTM allocated page is illegal.
9062306a36Sopenharmony_ci	 */
9162306a36Sopenharmony_ci	if (order)
9262306a36Sopenharmony_ci		gfp_flags |= __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN |
9362306a36Sopenharmony_ci			__GFP_KSWAPD_RECLAIM;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (!pool->use_dma_alloc) {
9662306a36Sopenharmony_ci		p = alloc_pages_node(pool->nid, gfp_flags, order);
9762306a36Sopenharmony_ci		if (p)
9862306a36Sopenharmony_ci			p->private = order;
9962306a36Sopenharmony_ci		return p;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	dma = kmalloc(sizeof(*dma), GFP_KERNEL);
10362306a36Sopenharmony_ci	if (!dma)
10462306a36Sopenharmony_ci		return NULL;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (order)
10762306a36Sopenharmony_ci		attr |= DMA_ATTR_NO_WARN;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	vaddr = dma_alloc_attrs(pool->dev, (1ULL << order) * PAGE_SIZE,
11062306a36Sopenharmony_ci				&dma->addr, gfp_flags, attr);
11162306a36Sopenharmony_ci	if (!vaddr)
11262306a36Sopenharmony_ci		goto error_free;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* TODO: This is an illegal abuse of the DMA API, but we need to rework
11562306a36Sopenharmony_ci	 * TTM page fault handling and extend the DMA API to clean this up.
11662306a36Sopenharmony_ci	 */
11762306a36Sopenharmony_ci	if (is_vmalloc_addr(vaddr))
11862306a36Sopenharmony_ci		p = vmalloc_to_page(vaddr);
11962306a36Sopenharmony_ci	else
12062306a36Sopenharmony_ci		p = virt_to_page(vaddr);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	dma->vaddr = (unsigned long)vaddr | order;
12362306a36Sopenharmony_ci	p->private = (unsigned long)dma;
12462306a36Sopenharmony_ci	return p;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cierror_free:
12762306a36Sopenharmony_ci	kfree(dma);
12862306a36Sopenharmony_ci	return NULL;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/* Reset the caching and pages of size 1 << order */
13262306a36Sopenharmony_cistatic void ttm_pool_free_page(struct ttm_pool *pool, enum ttm_caching caching,
13362306a36Sopenharmony_ci			       unsigned int order, struct page *p)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	unsigned long attr = DMA_ATTR_FORCE_CONTIGUOUS;
13662306a36Sopenharmony_ci	struct ttm_pool_dma *dma;
13762306a36Sopenharmony_ci	void *vaddr;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#ifdef CONFIG_X86
14062306a36Sopenharmony_ci	/* We don't care that set_pages_wb is inefficient here. This is only
14162306a36Sopenharmony_ci	 * used when we have to shrink and CPU overhead is irrelevant then.
14262306a36Sopenharmony_ci	 */
14362306a36Sopenharmony_ci	if (caching != ttm_cached && !PageHighMem(p))
14462306a36Sopenharmony_ci		set_pages_wb(p, 1 << order);
14562306a36Sopenharmony_ci#endif
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (!pool || !pool->use_dma_alloc) {
14862306a36Sopenharmony_ci		__free_pages(p, order);
14962306a36Sopenharmony_ci		return;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (order)
15362306a36Sopenharmony_ci		attr |= DMA_ATTR_NO_WARN;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	dma = (void *)p->private;
15662306a36Sopenharmony_ci	vaddr = (void *)(dma->vaddr & PAGE_MASK);
15762306a36Sopenharmony_ci	dma_free_attrs(pool->dev, (1UL << order) * PAGE_SIZE, vaddr, dma->addr,
15862306a36Sopenharmony_ci		       attr);
15962306a36Sopenharmony_ci	kfree(dma);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/* Apply a new caching to an array of pages */
16362306a36Sopenharmony_cistatic int ttm_pool_apply_caching(struct page **first, struct page **last,
16462306a36Sopenharmony_ci				  enum ttm_caching caching)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci#ifdef CONFIG_X86
16762306a36Sopenharmony_ci	unsigned int num_pages = last - first;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (!num_pages)
17062306a36Sopenharmony_ci		return 0;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	switch (caching) {
17362306a36Sopenharmony_ci	case ttm_cached:
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	case ttm_write_combined:
17662306a36Sopenharmony_ci		return set_pages_array_wc(first, num_pages);
17762306a36Sopenharmony_ci	case ttm_uncached:
17862306a36Sopenharmony_ci		return set_pages_array_uc(first, num_pages);
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci#endif
18162306a36Sopenharmony_ci	return 0;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci/* Map pages of 1 << order size and fill the DMA address array  */
18562306a36Sopenharmony_cistatic int ttm_pool_map(struct ttm_pool *pool, unsigned int order,
18662306a36Sopenharmony_ci			struct page *p, dma_addr_t **dma_addr)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	dma_addr_t addr;
18962306a36Sopenharmony_ci	unsigned int i;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (pool->use_dma_alloc) {
19262306a36Sopenharmony_ci		struct ttm_pool_dma *dma = (void *)p->private;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		addr = dma->addr;
19562306a36Sopenharmony_ci	} else {
19662306a36Sopenharmony_ci		size_t size = (1ULL << order) * PAGE_SIZE;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		addr = dma_map_page(pool->dev, p, 0, size, DMA_BIDIRECTIONAL);
19962306a36Sopenharmony_ci		if (dma_mapping_error(pool->dev, addr))
20062306a36Sopenharmony_ci			return -EFAULT;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	for (i = 1 << order; i ; --i) {
20462306a36Sopenharmony_ci		*(*dma_addr)++ = addr;
20562306a36Sopenharmony_ci		addr += PAGE_SIZE;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return 0;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/* Unmap pages of 1 << order size */
21262306a36Sopenharmony_cistatic void ttm_pool_unmap(struct ttm_pool *pool, dma_addr_t dma_addr,
21362306a36Sopenharmony_ci			   unsigned int num_pages)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	/* Unmapped while freeing the page */
21662306a36Sopenharmony_ci	if (pool->use_dma_alloc)
21762306a36Sopenharmony_ci		return;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	dma_unmap_page(pool->dev, dma_addr, (long)num_pages << PAGE_SHIFT,
22062306a36Sopenharmony_ci		       DMA_BIDIRECTIONAL);
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/* Give pages into a specific pool_type */
22462306a36Sopenharmony_cistatic void ttm_pool_type_give(struct ttm_pool_type *pt, struct page *p)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	unsigned int i, num_pages = 1 << pt->order;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	for (i = 0; i < num_pages; ++i) {
22962306a36Sopenharmony_ci		if (PageHighMem(p))
23062306a36Sopenharmony_ci			clear_highpage(p + i);
23162306a36Sopenharmony_ci		else
23262306a36Sopenharmony_ci			clear_page(page_address(p + i));
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	spin_lock(&pt->lock);
23662306a36Sopenharmony_ci	list_add(&p->lru, &pt->pages);
23762306a36Sopenharmony_ci	spin_unlock(&pt->lock);
23862306a36Sopenharmony_ci	atomic_long_add(1 << pt->order, &allocated_pages);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/* Take pages from a specific pool_type, return NULL when nothing available */
24262306a36Sopenharmony_cistatic struct page *ttm_pool_type_take(struct ttm_pool_type *pt)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct page *p;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	spin_lock(&pt->lock);
24762306a36Sopenharmony_ci	p = list_first_entry_or_null(&pt->pages, typeof(*p), lru);
24862306a36Sopenharmony_ci	if (p) {
24962306a36Sopenharmony_ci		atomic_long_sub(1 << pt->order, &allocated_pages);
25062306a36Sopenharmony_ci		list_del(&p->lru);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci	spin_unlock(&pt->lock);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return p;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/* Initialize and add a pool type to the global shrinker list */
25862306a36Sopenharmony_cistatic void ttm_pool_type_init(struct ttm_pool_type *pt, struct ttm_pool *pool,
25962306a36Sopenharmony_ci			       enum ttm_caching caching, unsigned int order)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	pt->pool = pool;
26262306a36Sopenharmony_ci	pt->caching = caching;
26362306a36Sopenharmony_ci	pt->order = order;
26462306a36Sopenharmony_ci	spin_lock_init(&pt->lock);
26562306a36Sopenharmony_ci	INIT_LIST_HEAD(&pt->pages);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	spin_lock(&shrinker_lock);
26862306a36Sopenharmony_ci	list_add_tail(&pt->shrinker_list, &shrinker_list);
26962306a36Sopenharmony_ci	spin_unlock(&shrinker_lock);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/* Remove a pool_type from the global shrinker list and free all pages */
27362306a36Sopenharmony_cistatic void ttm_pool_type_fini(struct ttm_pool_type *pt)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct page *p;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	spin_lock(&shrinker_lock);
27862306a36Sopenharmony_ci	list_del(&pt->shrinker_list);
27962306a36Sopenharmony_ci	spin_unlock(&shrinker_lock);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	while ((p = ttm_pool_type_take(pt)))
28262306a36Sopenharmony_ci		ttm_pool_free_page(pt->pool, pt->caching, pt->order, p);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/* Return the pool_type to use for the given caching and order */
28662306a36Sopenharmony_cistatic struct ttm_pool_type *ttm_pool_select_type(struct ttm_pool *pool,
28762306a36Sopenharmony_ci						  enum ttm_caching caching,
28862306a36Sopenharmony_ci						  unsigned int order)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	if (pool->use_dma_alloc || pool->nid != NUMA_NO_NODE)
29162306a36Sopenharmony_ci		return &pool->caching[caching].orders[order];
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci#ifdef CONFIG_X86
29462306a36Sopenharmony_ci	switch (caching) {
29562306a36Sopenharmony_ci	case ttm_write_combined:
29662306a36Sopenharmony_ci		if (pool->use_dma32)
29762306a36Sopenharmony_ci			return &global_dma32_write_combined[order];
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		return &global_write_combined[order];
30062306a36Sopenharmony_ci	case ttm_uncached:
30162306a36Sopenharmony_ci		if (pool->use_dma32)
30262306a36Sopenharmony_ci			return &global_dma32_uncached[order];
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		return &global_uncached[order];
30562306a36Sopenharmony_ci	default:
30662306a36Sopenharmony_ci		break;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci#endif
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return NULL;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/* Free pages using the global shrinker list */
31462306a36Sopenharmony_cistatic unsigned int ttm_pool_shrink(void)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct ttm_pool_type *pt;
31762306a36Sopenharmony_ci	unsigned int num_pages;
31862306a36Sopenharmony_ci	struct page *p;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	spin_lock(&shrinker_lock);
32162306a36Sopenharmony_ci	pt = list_first_entry(&shrinker_list, typeof(*pt), shrinker_list);
32262306a36Sopenharmony_ci	list_move_tail(&pt->shrinker_list, &shrinker_list);
32362306a36Sopenharmony_ci	spin_unlock(&shrinker_lock);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	p = ttm_pool_type_take(pt);
32662306a36Sopenharmony_ci	if (p) {
32762306a36Sopenharmony_ci		ttm_pool_free_page(pt->pool, pt->caching, pt->order, p);
32862306a36Sopenharmony_ci		num_pages = 1 << pt->order;
32962306a36Sopenharmony_ci	} else {
33062306a36Sopenharmony_ci		num_pages = 0;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return num_pages;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci/* Return the allocation order based for a page */
33762306a36Sopenharmony_cistatic unsigned int ttm_pool_page_order(struct ttm_pool *pool, struct page *p)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	if (pool->use_dma_alloc) {
34062306a36Sopenharmony_ci		struct ttm_pool_dma *dma = (void *)p->private;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci		return dma->vaddr & ~PAGE_MASK;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return p->private;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci/* Called when we got a page, either from a pool or newly allocated */
34962306a36Sopenharmony_cistatic int ttm_pool_page_allocated(struct ttm_pool *pool, unsigned int order,
35062306a36Sopenharmony_ci				   struct page *p, dma_addr_t **dma_addr,
35162306a36Sopenharmony_ci				   unsigned long *num_pages,
35262306a36Sopenharmony_ci				   struct page ***pages)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	unsigned int i;
35562306a36Sopenharmony_ci	int r;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (*dma_addr) {
35862306a36Sopenharmony_ci		r = ttm_pool_map(pool, order, p, dma_addr);
35962306a36Sopenharmony_ci		if (r)
36062306a36Sopenharmony_ci			return r;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	*num_pages -= 1 << order;
36462306a36Sopenharmony_ci	for (i = 1 << order; i; --i, ++(*pages), ++p)
36562306a36Sopenharmony_ci		**pages = p;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return 0;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci/**
37162306a36Sopenharmony_ci * ttm_pool_free_range() - Free a range of TTM pages
37262306a36Sopenharmony_ci * @pool: The pool used for allocating.
37362306a36Sopenharmony_ci * @tt: The struct ttm_tt holding the page pointers.
37462306a36Sopenharmony_ci * @caching: The page caching mode used by the range.
37562306a36Sopenharmony_ci * @start_page: index for first page to free.
37662306a36Sopenharmony_ci * @end_page: index for last page to free + 1.
37762306a36Sopenharmony_ci *
37862306a36Sopenharmony_ci * During allocation the ttm_tt page-vector may be populated with ranges of
37962306a36Sopenharmony_ci * pages with different attributes if allocation hit an error without being
38062306a36Sopenharmony_ci * able to completely fulfill the allocation. This function can be used
38162306a36Sopenharmony_ci * to free these individual ranges.
38262306a36Sopenharmony_ci */
38362306a36Sopenharmony_cistatic void ttm_pool_free_range(struct ttm_pool *pool, struct ttm_tt *tt,
38462306a36Sopenharmony_ci				enum ttm_caching caching,
38562306a36Sopenharmony_ci				pgoff_t start_page, pgoff_t end_page)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct page **pages = &tt->pages[start_page];
38862306a36Sopenharmony_ci	unsigned int order;
38962306a36Sopenharmony_ci	pgoff_t i, nr;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	for (i = start_page; i < end_page; i += nr, pages += nr) {
39262306a36Sopenharmony_ci		struct ttm_pool_type *pt = NULL;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		order = ttm_pool_page_order(pool, *pages);
39562306a36Sopenharmony_ci		nr = (1UL << order);
39662306a36Sopenharmony_ci		if (tt->dma_address)
39762306a36Sopenharmony_ci			ttm_pool_unmap(pool, tt->dma_address[i], nr);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		pt = ttm_pool_select_type(pool, caching, order);
40062306a36Sopenharmony_ci		if (pt)
40162306a36Sopenharmony_ci			ttm_pool_type_give(pt, *pages);
40262306a36Sopenharmony_ci		else
40362306a36Sopenharmony_ci			ttm_pool_free_page(pool, caching, order, *pages);
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci/**
40862306a36Sopenharmony_ci * ttm_pool_alloc - Fill a ttm_tt object
40962306a36Sopenharmony_ci *
41062306a36Sopenharmony_ci * @pool: ttm_pool to use
41162306a36Sopenharmony_ci * @tt: ttm_tt object to fill
41262306a36Sopenharmony_ci * @ctx: operation context
41362306a36Sopenharmony_ci *
41462306a36Sopenharmony_ci * Fill the ttm_tt object with pages and also make sure to DMA map them when
41562306a36Sopenharmony_ci * necessary.
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * Returns: 0 on successe, negative error code otherwise.
41862306a36Sopenharmony_ci */
41962306a36Sopenharmony_ciint ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
42062306a36Sopenharmony_ci		   struct ttm_operation_ctx *ctx)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	pgoff_t num_pages = tt->num_pages;
42362306a36Sopenharmony_ci	dma_addr_t *dma_addr = tt->dma_address;
42462306a36Sopenharmony_ci	struct page **caching = tt->pages;
42562306a36Sopenharmony_ci	struct page **pages = tt->pages;
42662306a36Sopenharmony_ci	enum ttm_caching page_caching;
42762306a36Sopenharmony_ci	gfp_t gfp_flags = GFP_USER;
42862306a36Sopenharmony_ci	pgoff_t caching_divide;
42962306a36Sopenharmony_ci	unsigned int order;
43062306a36Sopenharmony_ci	struct page *p;
43162306a36Sopenharmony_ci	int r;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	WARN_ON(!num_pages || ttm_tt_is_populated(tt));
43462306a36Sopenharmony_ci	WARN_ON(dma_addr && !pool->dev);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (tt->page_flags & TTM_TT_FLAG_ZERO_ALLOC)
43762306a36Sopenharmony_ci		gfp_flags |= __GFP_ZERO;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (ctx->gfp_retry_mayfail)
44062306a36Sopenharmony_ci		gfp_flags |= __GFP_RETRY_MAYFAIL;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (pool->use_dma32)
44362306a36Sopenharmony_ci		gfp_flags |= GFP_DMA32;
44462306a36Sopenharmony_ci	else
44562306a36Sopenharmony_ci		gfp_flags |= GFP_HIGHUSER;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	for (order = min_t(unsigned int, MAX_ORDER, __fls(num_pages));
44862306a36Sopenharmony_ci	     num_pages;
44962306a36Sopenharmony_ci	     order = min_t(unsigned int, order, __fls(num_pages))) {
45062306a36Sopenharmony_ci		struct ttm_pool_type *pt;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		page_caching = tt->caching;
45362306a36Sopenharmony_ci		pt = ttm_pool_select_type(pool, tt->caching, order);
45462306a36Sopenharmony_ci		p = pt ? ttm_pool_type_take(pt) : NULL;
45562306a36Sopenharmony_ci		if (p) {
45662306a36Sopenharmony_ci			r = ttm_pool_apply_caching(caching, pages,
45762306a36Sopenharmony_ci						   tt->caching);
45862306a36Sopenharmony_ci			if (r)
45962306a36Sopenharmony_ci				goto error_free_page;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci			caching = pages;
46262306a36Sopenharmony_ci			do {
46362306a36Sopenharmony_ci				r = ttm_pool_page_allocated(pool, order, p,
46462306a36Sopenharmony_ci							    &dma_addr,
46562306a36Sopenharmony_ci							    &num_pages,
46662306a36Sopenharmony_ci							    &pages);
46762306a36Sopenharmony_ci				if (r)
46862306a36Sopenharmony_ci					goto error_free_page;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci				caching = pages;
47162306a36Sopenharmony_ci				if (num_pages < (1 << order))
47262306a36Sopenharmony_ci					break;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci				p = ttm_pool_type_take(pt);
47562306a36Sopenharmony_ci			} while (p);
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		page_caching = ttm_cached;
47962306a36Sopenharmony_ci		while (num_pages >= (1 << order) &&
48062306a36Sopenharmony_ci		       (p = ttm_pool_alloc_page(pool, gfp_flags, order))) {
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci			if (PageHighMem(p)) {
48362306a36Sopenharmony_ci				r = ttm_pool_apply_caching(caching, pages,
48462306a36Sopenharmony_ci							   tt->caching);
48562306a36Sopenharmony_ci				if (r)
48662306a36Sopenharmony_ci					goto error_free_page;
48762306a36Sopenharmony_ci				caching = pages;
48862306a36Sopenharmony_ci			}
48962306a36Sopenharmony_ci			r = ttm_pool_page_allocated(pool, order, p, &dma_addr,
49062306a36Sopenharmony_ci						    &num_pages, &pages);
49162306a36Sopenharmony_ci			if (r)
49262306a36Sopenharmony_ci				goto error_free_page;
49362306a36Sopenharmony_ci			if (PageHighMem(p))
49462306a36Sopenharmony_ci				caching = pages;
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		if (!p) {
49862306a36Sopenharmony_ci			if (order) {
49962306a36Sopenharmony_ci				--order;
50062306a36Sopenharmony_ci				continue;
50162306a36Sopenharmony_ci			}
50262306a36Sopenharmony_ci			r = -ENOMEM;
50362306a36Sopenharmony_ci			goto error_free_all;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	r = ttm_pool_apply_caching(caching, pages, tt->caching);
50862306a36Sopenharmony_ci	if (r)
50962306a36Sopenharmony_ci		goto error_free_all;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return 0;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cierror_free_page:
51462306a36Sopenharmony_ci	ttm_pool_free_page(pool, page_caching, order, p);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cierror_free_all:
51762306a36Sopenharmony_ci	num_pages = tt->num_pages - num_pages;
51862306a36Sopenharmony_ci	caching_divide = caching - tt->pages;
51962306a36Sopenharmony_ci	ttm_pool_free_range(pool, tt, tt->caching, 0, caching_divide);
52062306a36Sopenharmony_ci	ttm_pool_free_range(pool, tt, ttm_cached, caching_divide, num_pages);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	return r;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_pool_alloc);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci/**
52762306a36Sopenharmony_ci * ttm_pool_free - Free the backing pages from a ttm_tt object
52862306a36Sopenharmony_ci *
52962306a36Sopenharmony_ci * @pool: Pool to give pages back to.
53062306a36Sopenharmony_ci * @tt: ttm_tt object to unpopulate
53162306a36Sopenharmony_ci *
53262306a36Sopenharmony_ci * Give the packing pages back to a pool or free them
53362306a36Sopenharmony_ci */
53462306a36Sopenharmony_civoid ttm_pool_free(struct ttm_pool *pool, struct ttm_tt *tt)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	ttm_pool_free_range(pool, tt, tt->caching, 0, tt->num_pages);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	while (atomic_long_read(&allocated_pages) > page_pool_size)
53962306a36Sopenharmony_ci		ttm_pool_shrink();
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_pool_free);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci/**
54462306a36Sopenharmony_ci * ttm_pool_init - Initialize a pool
54562306a36Sopenharmony_ci *
54662306a36Sopenharmony_ci * @pool: the pool to initialize
54762306a36Sopenharmony_ci * @dev: device for DMA allocations and mappings
54862306a36Sopenharmony_ci * @nid: NUMA node to use for allocations
54962306a36Sopenharmony_ci * @use_dma_alloc: true if coherent DMA alloc should be used
55062306a36Sopenharmony_ci * @use_dma32: true if GFP_DMA32 should be used
55162306a36Sopenharmony_ci *
55262306a36Sopenharmony_ci * Initialize the pool and its pool types.
55362306a36Sopenharmony_ci */
55462306a36Sopenharmony_civoid ttm_pool_init(struct ttm_pool *pool, struct device *dev,
55562306a36Sopenharmony_ci		   int nid, bool use_dma_alloc, bool use_dma32)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	unsigned int i, j;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	WARN_ON(!dev && use_dma_alloc);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	pool->dev = dev;
56262306a36Sopenharmony_ci	pool->nid = nid;
56362306a36Sopenharmony_ci	pool->use_dma_alloc = use_dma_alloc;
56462306a36Sopenharmony_ci	pool->use_dma32 = use_dma32;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (use_dma_alloc || nid != NUMA_NO_NODE) {
56762306a36Sopenharmony_ci		for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i)
56862306a36Sopenharmony_ci			for (j = 0; j <= MAX_ORDER; ++j)
56962306a36Sopenharmony_ci				ttm_pool_type_init(&pool->caching[i].orders[j],
57062306a36Sopenharmony_ci						   pool, i, j);
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_pool_init);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci/**
57662306a36Sopenharmony_ci * ttm_pool_fini - Cleanup a pool
57762306a36Sopenharmony_ci *
57862306a36Sopenharmony_ci * @pool: the pool to clean up
57962306a36Sopenharmony_ci *
58062306a36Sopenharmony_ci * Free all pages in the pool and unregister the types from the global
58162306a36Sopenharmony_ci * shrinker.
58262306a36Sopenharmony_ci */
58362306a36Sopenharmony_civoid ttm_pool_fini(struct ttm_pool *pool)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	unsigned int i, j;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (pool->use_dma_alloc || pool->nid != NUMA_NO_NODE) {
58862306a36Sopenharmony_ci		for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i)
58962306a36Sopenharmony_ci			for (j = 0; j <= MAX_ORDER; ++j)
59062306a36Sopenharmony_ci				ttm_pool_type_fini(&pool->caching[i].orders[j]);
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	/* We removed the pool types from the LRU, but we need to also make sure
59462306a36Sopenharmony_ci	 * that no shrinker is concurrently freeing pages from the pool.
59562306a36Sopenharmony_ci	 */
59662306a36Sopenharmony_ci	synchronize_shrinkers();
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_pool_fini);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci/* As long as pages are available make sure to release at least one */
60162306a36Sopenharmony_cistatic unsigned long ttm_pool_shrinker_scan(struct shrinker *shrink,
60262306a36Sopenharmony_ci					    struct shrink_control *sc)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	unsigned long num_freed = 0;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	do
60762306a36Sopenharmony_ci		num_freed += ttm_pool_shrink();
60862306a36Sopenharmony_ci	while (!num_freed && atomic_long_read(&allocated_pages));
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return num_freed;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci/* Return the number of pages available or SHRINK_EMPTY if we have none */
61462306a36Sopenharmony_cistatic unsigned long ttm_pool_shrinker_count(struct shrinker *shrink,
61562306a36Sopenharmony_ci					     struct shrink_control *sc)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	unsigned long num_pages = atomic_long_read(&allocated_pages);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	return num_pages ? num_pages : SHRINK_EMPTY;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
62362306a36Sopenharmony_ci/* Count the number of pages available in a pool_type */
62462306a36Sopenharmony_cistatic unsigned int ttm_pool_type_count(struct ttm_pool_type *pt)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	unsigned int count = 0;
62762306a36Sopenharmony_ci	struct page *p;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	spin_lock(&pt->lock);
63062306a36Sopenharmony_ci	/* Only used for debugfs, the overhead doesn't matter */
63162306a36Sopenharmony_ci	list_for_each_entry(p, &pt->pages, lru)
63262306a36Sopenharmony_ci		++count;
63362306a36Sopenharmony_ci	spin_unlock(&pt->lock);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return count;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/* Print a nice header for the order */
63962306a36Sopenharmony_cistatic void ttm_pool_debugfs_header(struct seq_file *m)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	unsigned int i;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	seq_puts(m, "\t ");
64462306a36Sopenharmony_ci	for (i = 0; i <= MAX_ORDER; ++i)
64562306a36Sopenharmony_ci		seq_printf(m, " ---%2u---", i);
64662306a36Sopenharmony_ci	seq_puts(m, "\n");
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci/* Dump information about the different pool types */
65062306a36Sopenharmony_cistatic void ttm_pool_debugfs_orders(struct ttm_pool_type *pt,
65162306a36Sopenharmony_ci				    struct seq_file *m)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	unsigned int i;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	for (i = 0; i <= MAX_ORDER; ++i)
65662306a36Sopenharmony_ci		seq_printf(m, " %8u", ttm_pool_type_count(&pt[i]));
65762306a36Sopenharmony_ci	seq_puts(m, "\n");
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci/* Dump the total amount of allocated pages */
66162306a36Sopenharmony_cistatic void ttm_pool_debugfs_footer(struct seq_file *m)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	seq_printf(m, "\ntotal\t: %8lu of %8lu\n",
66462306a36Sopenharmony_ci		   atomic_long_read(&allocated_pages), page_pool_size);
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/* Dump the information for the global pools */
66862306a36Sopenharmony_cistatic int ttm_pool_debugfs_globals_show(struct seq_file *m, void *data)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	ttm_pool_debugfs_header(m);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	spin_lock(&shrinker_lock);
67362306a36Sopenharmony_ci	seq_puts(m, "wc\t:");
67462306a36Sopenharmony_ci	ttm_pool_debugfs_orders(global_write_combined, m);
67562306a36Sopenharmony_ci	seq_puts(m, "uc\t:");
67662306a36Sopenharmony_ci	ttm_pool_debugfs_orders(global_uncached, m);
67762306a36Sopenharmony_ci	seq_puts(m, "wc 32\t:");
67862306a36Sopenharmony_ci	ttm_pool_debugfs_orders(global_dma32_write_combined, m);
67962306a36Sopenharmony_ci	seq_puts(m, "uc 32\t:");
68062306a36Sopenharmony_ci	ttm_pool_debugfs_orders(global_dma32_uncached, m);
68162306a36Sopenharmony_ci	spin_unlock(&shrinker_lock);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	ttm_pool_debugfs_footer(m);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return 0;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ttm_pool_debugfs_globals);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci/**
69062306a36Sopenharmony_ci * ttm_pool_debugfs - Debugfs dump function for a pool
69162306a36Sopenharmony_ci *
69262306a36Sopenharmony_ci * @pool: the pool to dump the information for
69362306a36Sopenharmony_ci * @m: seq_file to dump to
69462306a36Sopenharmony_ci *
69562306a36Sopenharmony_ci * Make a debugfs dump with the per pool and global information.
69662306a36Sopenharmony_ci */
69762306a36Sopenharmony_ciint ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	unsigned int i;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	if (!pool->use_dma_alloc) {
70262306a36Sopenharmony_ci		seq_puts(m, "unused\n");
70362306a36Sopenharmony_ci		return 0;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	ttm_pool_debugfs_header(m);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	spin_lock(&shrinker_lock);
70962306a36Sopenharmony_ci	for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) {
71062306a36Sopenharmony_ci		seq_puts(m, "DMA ");
71162306a36Sopenharmony_ci		switch (i) {
71262306a36Sopenharmony_ci		case ttm_cached:
71362306a36Sopenharmony_ci			seq_puts(m, "\t:");
71462306a36Sopenharmony_ci			break;
71562306a36Sopenharmony_ci		case ttm_write_combined:
71662306a36Sopenharmony_ci			seq_puts(m, "wc\t:");
71762306a36Sopenharmony_ci			break;
71862306a36Sopenharmony_ci		case ttm_uncached:
71962306a36Sopenharmony_ci			seq_puts(m, "uc\t:");
72062306a36Sopenharmony_ci			break;
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci		ttm_pool_debugfs_orders(pool->caching[i].orders, m);
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci	spin_unlock(&shrinker_lock);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	ttm_pool_debugfs_footer(m);
72762306a36Sopenharmony_ci	return 0;
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_pool_debugfs);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci/* Test the shrinker functions and dump the result */
73262306a36Sopenharmony_cistatic int ttm_pool_debugfs_shrink_show(struct seq_file *m, void *data)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct shrink_control sc = { .gfp_mask = GFP_NOFS };
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	fs_reclaim_acquire(GFP_KERNEL);
73762306a36Sopenharmony_ci	seq_printf(m, "%lu/%lu\n", ttm_pool_shrinker_count(&mm_shrinker, &sc),
73862306a36Sopenharmony_ci		   ttm_pool_shrinker_scan(&mm_shrinker, &sc));
73962306a36Sopenharmony_ci	fs_reclaim_release(GFP_KERNEL);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	return 0;
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(ttm_pool_debugfs_shrink);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci#endif
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci/**
74862306a36Sopenharmony_ci * ttm_pool_mgr_init - Initialize globals
74962306a36Sopenharmony_ci *
75062306a36Sopenharmony_ci * @num_pages: default number of pages
75162306a36Sopenharmony_ci *
75262306a36Sopenharmony_ci * Initialize the global locks and lists for the MM shrinker.
75362306a36Sopenharmony_ci */
75462306a36Sopenharmony_ciint ttm_pool_mgr_init(unsigned long num_pages)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	unsigned int i;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (!page_pool_size)
75962306a36Sopenharmony_ci		page_pool_size = num_pages;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	spin_lock_init(&shrinker_lock);
76262306a36Sopenharmony_ci	INIT_LIST_HEAD(&shrinker_list);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	for (i = 0; i <= MAX_ORDER; ++i) {
76562306a36Sopenharmony_ci		ttm_pool_type_init(&global_write_combined[i], NULL,
76662306a36Sopenharmony_ci				   ttm_write_combined, i);
76762306a36Sopenharmony_ci		ttm_pool_type_init(&global_uncached[i], NULL, ttm_uncached, i);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		ttm_pool_type_init(&global_dma32_write_combined[i], NULL,
77062306a36Sopenharmony_ci				   ttm_write_combined, i);
77162306a36Sopenharmony_ci		ttm_pool_type_init(&global_dma32_uncached[i], NULL,
77262306a36Sopenharmony_ci				   ttm_uncached, i);
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
77662306a36Sopenharmony_ci	debugfs_create_file("page_pool", 0444, ttm_debugfs_root, NULL,
77762306a36Sopenharmony_ci			    &ttm_pool_debugfs_globals_fops);
77862306a36Sopenharmony_ci	debugfs_create_file("page_pool_shrink", 0400, ttm_debugfs_root, NULL,
77962306a36Sopenharmony_ci			    &ttm_pool_debugfs_shrink_fops);
78062306a36Sopenharmony_ci#endif
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	mm_shrinker.count_objects = ttm_pool_shrinker_count;
78362306a36Sopenharmony_ci	mm_shrinker.scan_objects = ttm_pool_shrinker_scan;
78462306a36Sopenharmony_ci	mm_shrinker.seeks = 1;
78562306a36Sopenharmony_ci	return register_shrinker(&mm_shrinker, "drm-ttm_pool");
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci/**
78962306a36Sopenharmony_ci * ttm_pool_mgr_fini - Finalize globals
79062306a36Sopenharmony_ci *
79162306a36Sopenharmony_ci * Cleanup the global pools and unregister the MM shrinker.
79262306a36Sopenharmony_ci */
79362306a36Sopenharmony_civoid ttm_pool_mgr_fini(void)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	unsigned int i;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	for (i = 0; i <= MAX_ORDER; ++i) {
79862306a36Sopenharmony_ci		ttm_pool_type_fini(&global_write_combined[i]);
79962306a36Sopenharmony_ci		ttm_pool_type_fini(&global_uncached[i]);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci		ttm_pool_type_fini(&global_dma32_write_combined[i]);
80262306a36Sopenharmony_ci		ttm_pool_type_fini(&global_dma32_uncached[i]);
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	unregister_shrinker(&mm_shrinker);
80662306a36Sopenharmony_ci	WARN_ON(!list_empty(&shrinker_list));
80762306a36Sopenharmony_ci}
808