diff options
Diffstat (limited to 'include/linux/cma.h')
-rw-r--r-- | include/linux/cma.h | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/include/linux/cma.h b/include/linux/cma.h new file mode 100644 index 0000000..e1e70e93 --- /dev/null +++ b/include/linux/cma.h @@ -0,0 +1,499 @@ +#ifndef __LINUX_CMA_H +#define __LINUX_CMA_H + +/* linux/include/linux/cma.h + * + * Contiguous Memory Allocator framework + * Copyright (c) 2010 by Samsung Electronics. + * Written by Michal Nazarewicz (m.nazarewicz@samsung.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation + */ + +/* + * See Documentation/contiguous-memory.txt for details. + */ + +#include <linux/ioctl.h> +#include <linux/types.h> + + +#define CMA_MAGIC (('c' << 24) | ('M' << 16) | ('a' << 8) | 0x42) + +enum { + CMA_REQ_DEV_KIND, + CMA_REQ_FROM_REG +}; + +/** + * An information about area exportable to user space. + * @magic: must always be CMA_MAGIC. + * @type: type of the request. + * @spec: either "dev/kind\0" or "regions\0" depending on @type. + * In any case, the string must be NUL terminated. + * additionally, in the latter case scanning stops at + * semicolon (';'). + * @size: size of the chunk to allocate. + * @alignment: desired alignment of the chunk (must be power of two or zero). + * @start: when ioctl() finishes this stores physical address of the chunk. + */ +struct cma_alloc_request { + __u32 magic; + __u32 type; + + /* __u64 to be compatible accross 32 and 64 bit systems. */ + __u64 size; + __u64 alignment; + __u64 start; + + char spec[32]; +}; + +#define IOCTL_CMA_ALLOC _IOWR('p', 0, struct cma_alloc_request) + + +/***************************** Kernel level API *****************************/ + +#ifdef __KERNEL__ + +#include <linux/rbtree.h> +#include <linux/list.h> +#if defined CONFIG_CMA_SYSFS +# include <linux/kobject.h> +#endif + + +struct device; +struct cma_info; + +/* + * Don't call it directly, use cma_alloc(), cma_alloc_from() or + * cma_alloc_from_region(). + */ +dma_addr_t __must_check +__cma_alloc(const struct device *dev, const char *type, + size_t size, dma_addr_t alignment); + +/* Don't call it directly, use cma_info() or cma_info_about(). */ +int +__cma_info(struct cma_info *info, const struct device *dev, const char *type); + + +/** + * cma_alloc - allocates contiguous chunk of memory. + * @dev: The device to perform allocation for. + * @type: A type of memory to allocate. Platform may define + * several different types of memory and device drivers + * can then request chunks of different types. Usually it's + * safe to pass NULL here which is the same as passing + * "common". + * @size: Size of the memory to allocate in bytes. + * @alignment: Desired alignment in bytes. Must be a power of two or + * zero. If alignment is less then a page size it will be + * set to page size. If unsure, pass zero here. + * + * On error returns a negative error cast to dma_addr_t. Use + * IS_ERR_VALUE() to check if returned value is indeed an error. + * Otherwise bus address of the chunk is returned. + */ +static inline dma_addr_t __must_check +cma_alloc(const struct device *dev, const char *type, + size_t size, dma_addr_t alignment) +{ + return dev ? __cma_alloc(dev, type, size, alignment) : -EINVAL; +} + + +/** + * struct cma_info - information about regions returned by cma_info(). + * @lower_bound: The smallest address that is possible to be + * allocated for given (dev, type) pair. + * @upper_bound: The one byte after the biggest address that is + * possible to be allocated for given (dev, type) + * pair. + * @total_size: Total size of regions mapped to (dev, type) pair. + * @free_size: Total free size in all of the regions mapped to (dev, type) + * pair. Because of possible race conditions, it is not + * guaranteed that the value will be correct -- it gives only + * an approximation. + * @count: Number of regions mapped to (dev, type) pair. + */ +struct cma_info { + dma_addr_t lower_bound, upper_bound; + size_t total_size, free_size; + unsigned count; +}; + +/** + * cma_info - queries information about regions. + * @info: Pointer to a structure where to save the information. + * @dev: The device to query information for. + * @type: A type of memory to query information for. + * If unsure, pass NULL here which is equal to passing + * "common". + * + * On error returns a negative error, zero otherwise. + */ +static inline int +cma_info(struct cma_info *info, const struct device *dev, const char *type) +{ + return dev ? __cma_info(info, dev, type) : -EINVAL; +} + + +/** + * cma_free - frees a chunk of memory. + * @addr: Beginning of the chunk. + * + * Returns -ENOENT if there is no chunk at given location; otherwise + * zero. In the former case issues a warning. + */ +int cma_free(dma_addr_t addr); + +/** + * cma_get_virt - frees virtual address of cma memory. + * @phys: physical addrress + * @size: size of memory + * @noncached : 0 is cached, 1 is non-cached. + * + * Returns -ENOENT if there is no chunk at given location; otherwise + * zero. In the former case issues a warning. + */ +void *cma_get_virt(dma_addr_t phys, dma_addr_t size, int noncached); + + +/****************************** Lower lever API *****************************/ + +/** + * cma_alloc_from - allocates contiguous chunk of memory from named regions. + * @regions: Comma separated list of region names. Terminated by NUL + * byte or a semicolon. + * @size: Size of the memory to allocate in bytes. + * @alignment: Desired alignment in bytes. Must be a power of two or + * zero. If alignment is less then a page size it will be + * set to page size. If unsure, pass zero here. + * + * On error returns a negative error cast to dma_addr_t. Use + * IS_ERR_VALUE() to check if returned value is indeed an error. + * Otherwise bus address of the chunk is returned. + */ +static inline dma_addr_t __must_check +cma_alloc_from(const char *regions, size_t size, dma_addr_t alignment) +{ + return __cma_alloc(NULL, regions, size, alignment); +} + +/** + * cma_info_about - queries information about named regions. + * @info: Pointer to a structure where to save the information. + * @regions: Comma separated list of region names. Terminated by NUL + * byte or a semicolon. + * + * On error returns a negative error, zero otherwise. + */ +static inline int +cma_info_about(struct cma_info *info, const const char *regions) +{ + return __cma_info(info, NULL, regions); +} + + + +struct cma_allocator; + +/** + * struct cma_region - a region reserved for CMA allocations. + * @name: Unique name of the region. Read only. + * @start: Bus address of the region in bytes. Always aligned at + * least to a full page. Read only. + * @size: Size of the region in bytes. Multiply of a page size. + * Read only. + * @free_space: Free space in the region. Read only. + * @alignment: Desired alignment of the region in bytes. A power of two, + * always at least page size. Early. + * @alloc: Allocator used with this region. NULL means allocator is + * not attached. Private. + * @alloc_name: Allocator name read from cmdline. Private. This may be + * different from @alloc->name. + * @private_data: Allocator's private data. + * @users: Number of chunks allocated in this region. + * @list: Entry in list of regions. Private. + * @used: Whether region was already used, ie. there was at least + * one allocation request for. Private. + * @registered: Whether this region has been registered. Read only. + * @reserved: Whether this region has been reserved. Early. Read only. + * @copy_name: Whether @name and @alloc_name needs to be copied when + * this region is converted from early to normal. Early. + * Private. + * @free_alloc_name: Whether @alloc_name was kmalloced(). Private. + * + * Regions come in two types: an early region and normal region. The + * former can be reserved or not-reserved. Fields marked as "early" + * are only meaningful in early regions. + * + * Early regions are important only during initialisation. The list + * of early regions is built from the "cma" command line argument or + * platform defaults. Platform initialisation code is responsible for + * reserving space for unreserved regions that are placed on + * cma_early_regions list. + * + * Later, during CMA initialisation all reserved regions from the + * cma_early_regions list are registered as normal regions and can be + * used using standard mechanisms. + */ +struct cma_region { + const char *name; + dma_addr_t start; + size_t size; + union { + size_t free_space; /* Normal region */ + dma_addr_t alignment; /* Early region */ + }; + + struct cma_allocator *alloc; + const char *alloc_name; + void *private_data; + + unsigned users; + struct list_head list; + +#if defined CONFIG_CMA_SYSFS + struct kobject kobj; +#endif + + unsigned used:1; + unsigned registered:1; + unsigned reserved:1; + unsigned copy_name:1; + unsigned free_alloc_name:1; +}; + + +/** + * cma_region_register() - registers a region. + * @reg: Region to region. + * + * Region's start and size must be set. + * + * If name is set the region will be accessible using normal mechanism + * like mapping or cma_alloc_from() function otherwise it will be + * a private region and accessible only using the + * cma_alloc_from_region() function. + * + * If alloc is set function will try to initialise given allocator + * (and will return error if it failes). Otherwise alloc_name may + * point to a name of an allocator to use (if not set, the default + * will be used). + * + * All other fields are ignored and/or overwritten. + * + * Returns zero or negative error. In particular, -EADDRINUSE if + * region overlap with already existing region. + */ +int __must_check cma_region_register(struct cma_region *reg); + +/** + * cma_region_unregister() - unregisters a region. + * @reg: Region to unregister. + * + * Region is unregistered only if there are no chunks allocated for + * it. Otherwise, function returns -EBUSY. + * + * On success returs zero. + */ +int __must_check cma_region_unregister(struct cma_region *reg); + + +/** + * cma_alloc_from_region() - allocates contiguous chunk of memory from region. + * @reg: Region to allocate chunk from. + * @size: Size of the memory to allocate in bytes. + * @alignment: Desired alignment in bytes. Must be a power of two or + * zero. If alignment is less then a page size it will be + * set to page size. If unsure, pass zero here. + * + * On error returns a negative error cast to dma_addr_t. Use + * IS_ERR_VALUE() to check if returned value is indeed an error. + * Otherwise bus address of the chunk is returned. + */ +dma_addr_t __must_check +cma_alloc_from_region(struct cma_region *reg, + size_t size, dma_addr_t alignment); + + + +/****************************** Allocators API ******************************/ + +/** + * struct cma_chunk - an allocated contiguous chunk of memory. + * @start: Bus address in bytes. + * @size: Size in bytes. + * @free_space: Free space in region in bytes. Read only. + * @reg: Region this chunk belongs to. + * @by_start: A node in an red-black tree with all chunks sorted by + * start address. + * + * The cma_allocator::alloc() operation need to set only the @start + * and @size fields. The rest is handled by the caller (ie. CMA + * glue). + */ +struct cma_chunk { + dma_addr_t start; + size_t size; + + struct cma_region *reg; + struct rb_node by_start; +}; + + +/** + * struct cma_allocator - a CMA allocator. + * @name: Allocator's unique name + * @init: Initialises an allocator on given region. + * @cleanup: Cleans up after init. May assume that there are no chunks + * allocated in given region. + * @alloc: Allocates a chunk of memory of given size in bytes and + * with given alignment. Alignment is a power of + * two (thus non-zero) and callback does not need to check it. + * May also assume that it is the only call that uses given + * region (ie. access to the region is synchronised with + * a mutex). This has to allocate the chunk object (it may be + * contained in a bigger structure with allocator-specific data. + * Required. + * @free: Frees allocated chunk. May also assume that it is the only + * call that uses given region. This has to free() the chunk + * object as well. Required. + * @list: Entry in list of allocators. Private. + */ +struct cma_allocator { + const char *name; + + int (*init)(struct cma_region *reg); + void (*cleanup)(struct cma_region *reg); + struct cma_chunk *(*alloc)(struct cma_region *reg, size_t size, + dma_addr_t alignment); + void (*free)(struct cma_chunk *chunk); + + struct list_head list; +}; + + +/** + * cma_allocator_register() - Registers an allocator. + * @alloc: Allocator to register. + * + * Adds allocator to the list of allocators managed by CMA. + * + * All of the fields of cma_allocator structure must be set except for + * the optional name and the list's head which will be overriden + * anyway. + * + * Returns zero or negative error code. + */ +int cma_allocator_register(struct cma_allocator *alloc); + + +/**************************** Initialisation API ****************************/ + +/** + * cma_set_defaults() - specifies default command line parameters. + * @regions: A zero-sized entry terminated list of early regions. + * This array must not be placed in __initdata section. + * @map: Map attribute. + * + * This function should be called prior to cma_early_regions_reserve() + * and after early parameters have been parsed. + * + * Returns zero or negative error. + */ +int __init cma_set_defaults(struct cma_region *regions, const char *map); + + +/** + * cma_early_regions - a list of early regions. + * + * Platform needs to allocate space for each of the region before + * initcalls are executed. If space is reserved, the reserved flag + * must be set. Platform initialisation code may choose to use + * cma_early_regions_allocate(). + * + * Later, during CMA initialisation all reserved regions from the + * cma_early_regions list are registered as normal regions and can be + * used using standard mechanisms. + */ +extern struct list_head cma_early_regions __initdata; + + +/** + * cma_early_region_register() - registers an early region. + * @reg: Region to add. + * + * Region's size, start and alignment must be set (however the last + * two can be zero). If name is set the region will be accessible + * using normal mechanism like mapping or cma_alloc_from() function + * otherwise it will be a private region accessible only using the + * cma_alloc_from_region(). + * + * During platform initialisation, space is reserved for early + * regions. Later, when CMA initialises, the early regions are + * "converted" into normal regions. If cma_region::alloc is set, CMA + * will then try to setup given allocator on the region. Failure to + * do so will result in the region not being registered even though + * the space for it will still be reserved. If cma_region::alloc is + * not set, allocator will be attached to the region on first use and + * the value of cma_region::alloc_name will be taken into account if + * set. + * + * All other fields are ignored and/or overwritten. + * + * Returns zero or negative error. No checking if regions overlap is + * performed. + */ +int __init __must_check cma_early_region_register(struct cma_region *reg); + + +/** + * cma_early_region_reserve() - reserves a physically contiguous memory region. + * @reg: Early region to reserve memory for. + * + * If platform supports bootmem this is the first allocator this + * function tries to use. If that failes (or bootmem is not + * supported) function tries to use memblec if it is available. + * + * On success sets reg->reserved flag. + * + * Returns zero or negative error. + */ +int __init cma_early_region_reserve(struct cma_region *reg); + +/** + * cma_early_regions_reserve() - helper function for reserving early regions. + * @reserve: Callbac function used to reserve space for region. Needs + * to return non-negative if allocation succeeded, negative + * error otherwise. NULL means cma_early_region_alloc() will + * be used. + * + * This function traverses the %cma_early_regions list and tries to + * reserve memory for each early region. It uses the @reserve + * callback function for that purpose. The reserved flag of each + * region is updated accordingly. + */ +void __init cma_early_regions_reserve(int (*reserve)(struct cma_region *reg)); + +#else + +#define cma_set_defaults(regions, map) ((int)0) +#define cma_early_region_reserve(region) ((int)-EOPNOTSUPP) +#define cma_early_regions_reserve(reserve) do { } while (0) + +#endif + +#ifdef CONFIG_CMA +bool cma_is_registered_region(phys_addr_t start, size_t size); +#else +#define cma_is_registered_region(start, size) (false) +#endif + +#endif |