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