/*
* Copyright 2011 (c) Oracle Corp.
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sub license,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Author: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
*/
/*
* A simple DMA pool losely based on dmapool.c. It has certain advantages
* over the DMA pools:
* - Pool collects resently freed pages for reuse (and hooks up to
* the shrinker).
* - Tracks currently in use pages
* - Tracks whether the page is UC, WB or cached (and reverts to WB
* when freed).
*/
#define pr_fmt(fmt) "[TTM] " fmt
#include <linux/dma-mapping.h>
#include <linux/list.h>
#include <linux/seq_file.h> /* for seq_printf */
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/highmem.h>
#include <linux/mm_types.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/kthread.h>
#include <drm/ttm/ttm_bo_driver.h>
#include <drm/ttm/ttm_page_alloc.h>
#include <drm/ttm/ttm_set_memory.h>
#define NUM_PAGES_TO_ALLOC (PAGE_SIZE/sizeof(struct page *))
#define SMALL_ALLOCATION 4
#define FREE_ALL_PAGES (~0U)
#define VADDR_FLAG_HUGE_POOL 1UL
#define VADDR_FLAG_UPDATED_COUNT 2UL
enum pool_type {
IS_UNDEFINED = 0,
IS_WC = 1 << 1,
IS_UC = 1 << 2,
IS_CACHED = 1 << 3,
IS_DMA32 = 1 << 4,
IS_HUGE = 1 << 5
};
/*
* The pool structure. There are up to nine pools:
* - generic (not restricted to DMA32):
* - write combined, uncached, cached.
* - dma32 (up to 2^32 - so up 4GB):
* - write combined, uncached, cached.
* - huge (not restricted to DMA32):
* - write combined, uncached, cached.
* for each 'struct device'. The 'cached' is for pages that are actively used.
* The other ones can be shrunk by the shrinker API if neccessary.
* @pools: The 'struct device->dma_pools' link.
* @type: Type of the pool
* @lock: Protects the free_list from concurrnet access. Must be
* used with irqsave/irqrestore variants because pool allocator maybe called
* from delayed work.
* @free_list: Pool of pages that are free to be used. No order requirements.
* @dev: The device that is associated with these pools.
* @size: Size used during DMA allocation.
* @npages_free: Count of available pages for re-use.
* @npages_in_use: Count of pages that are in use.
* @nfrees: Stats when pool is shrinking.
* @nrefills: Stats when the pool is grown.
* @gfp_flags: Flags to pass for alloc_page.
* @name: Name of the pool.
* @dev_name: Name derieved from dev - similar to how dev_info works.
* Used during shutdown as the dev_info during release is unavailable.
*/
struct dma_pool {
struct list_head pools; /* The 'struct device->dma_pools link */
enum pool_type type;
spinlock_t lock;
struct list_head free_list;
struct device *dev;
unsigned size;
unsigned npages_free;
unsigned npages_in_use;
unsigned long nfrees; /* Stats when shrunk. */
unsigned long nrefills; /* Stats when grown. */
gfp_t gfp_flags;
char name[13]; /* "cached dma32" */
char dev_name[64]; /* Constructed from dev */
};
/*
* The accounting page keeping track of the allocated page along with
* the DMA address.
* @page_list: The link to the 'page_list' in 'struct dma_pool'.
* @vaddr: The virtual address of the page and a flag if the page belongs to a
* huge pool
* @dma: The bus address of the page. If the page is not allocated
* via the DMA API, it will be -1.
*/
struct dma_page {
struct list_head page_list;
unsigned long vaddr;
struct page *p;
dma_addr_t dma;
};
/*
* Limits for the pool. They are handled without locks because only place where
* they may change is in sysfs store. They won't have immediate effect anyway
* so forcing serialization to access them is pointless.
*/
struct ttm_pool_opts {
unsigned alloc_size;
unsigned max_size;
unsigned small;
};
/*
* Contains the list of all of the 'struct device' and their corresponding
* DMA pools. Guarded by _mutex->lock.
* @pools: The link to 'struct ttm_pool_manager->pools'
* @dev: The 'struct device' associated with the 'pool'
* @pool: The 'struct dma_pool' associated with the 'dev'
*/
struct device_pools {
struct list_head pools;
struct device *dev;
struct dma_pool *pool;
};
/*
* struct ttm_pool_manager - Holds memory pools for fast allocation
*
* @lock: Lock used when adding/removing from pools
* @pools: List of 'struct device' and 'struct dma_pool' tuples.
* @options: Limits for the pool.
* @npools: Total amount of pools in existence.
* @shrinker: The structure used by [un|]register_shrinker
*/
struct ttm_pool_manager {
struct mutex lock;
struct list_head pools;
struct ttm_pool_opts options;
unsigned npools;
struct shrinker mm_shrink;
struct kobject kobj;
};
static struct ttm_pool_manager *_manager;
static struct attribute ttm_page_pool_max = {
.name = &