/* * drivers/video/tegra/nvmap/nvmap_alloc.c * * Handle allocation and freeing routines for nvmap * * Copyright (c) 2011-2018, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include #include #include #include #include "nvmap_priv.h" bool nvmap_convert_carveout_to_iovmm; bool nvmap_convert_iovmm_to_carveout; u32 nvmap_max_handle_count; u64 nvmap_big_page_allocs; u64 nvmap_total_page_allocs; /* handles may be arbitrarily large (16+MiB), and any handle allocated from * the kernel (i.e., not a carveout handle) includes its array of pages. to * preserve kmalloc space, if the array of pages exceeds PAGELIST_VMALLOC_MIN, * the array is allocated using vmalloc. */ #define PAGELIST_VMALLOC_MIN (PAGE_SIZE) void *nvmap_altalloc(size_t len) { if (len > PAGELIST_VMALLOC_MIN) return vmalloc(len); else return kmalloc(len, GFP_KERNEL); } void nvmap_altfree(void *ptr, size_t len) { if (!ptr) return; if (len > PAGELIST_VMALLOC_MIN) vfree(ptr); else kfree(ptr); } static struct page *nvmap_alloc_pages_exact(gfp_t gfp, size_t size) { struct page *page, *p, *e; unsigned int order; order = get_order(size); page = alloc_pages(gfp, order); if (!page) return NULL; split_page(page, order); e = nth_page(page, (1 << order)); for (p = nth_page(page, (size >> PAGE_SHIFT)); p < e; p++) __free_page(p); return page; } static uint s_nr_colors = 1; module_param_named(nr_colors, s_nr_colors, uint, 0644); #define NVMAP_MAX_COLORS 16 struct color_list { u32 *counts; u32 *heads; u32 *list; struct page **pages; u32 page_count; u32 length; }; static struct color_list *alloc_color_list(u32 nr_pages, u32 nr_colors) { struct color_list *list; u32 *temp = NULL; u32 nr_u32; list = kzalloc(sizeof(struct color_list), GFP_KERNEL); if (!list) return NULL; list->pages = vmalloc(nr_pages * sizeof(struct page *)); if (!list->pages) { kfree(list); return NULL; } /* Allocate counts, heads, and list with a single allocation */ nr_u32 = nr_pages + 2 * nr_colors; temp = vmalloc(nr_u32 * sizeof(u32)); if (!temp) goto fail; memset(&temp[0], 0, 2 * nr_colors * sizeof(u32)); list->counts = &temp[0]; list->heads = &temp[nr_colors]; list->list = &temp[2 * nr_colors]; list->page_count = nr_pages; return list; fail: if (list->pages) vfree(list->pages); kfree(list); return NULL; } static void free_color_list(struct color_list *list) { vfree(list->pages); vfree(list->counts); /* Frees counts, heads, and list */ kfree(list); } static struct page *list_pop_page(struct color_list *list, u32 color, char *who) { u32 i; /* Debug check */ if ((list->counts[color] == 0) || (list->counts[color] > 1 << 31)) { pr_err("list_pop_page: OVER FREE!\n"); pr_err(" called from: %s\n", who); for (i = 0; i < s_nr_colors; i++) pr_err(" color = %d: %d\n", i, list->counts[i]); BUG(); } i = list->heads[color]; list->heads[color] = list->list[i]; list->counts[color]--; return list->pages[i]; } struct nvmap_alloc_state { u32 nr_colors; u32 (*addr_to_color)(uintptr_t phys); u32 tile; u32 output_count; u32 nr_pages; u32 max_color_per_tile; struct color_list *list; }; #define CHANNEL_MASK_0 0x27af5200 #define CHANNEL_MASK_1 0x563ca400 #define CHANNEL_MASK_2 0x3f264800 #define CHANNEL_MASK_3 0xe2443000 #define BANK_MASK_0 0x5ca78400 #define BANK_MASK_1 0xe5724800 #define BANK_MASK_2 0x973bb000 #define BIT_N(a, n) \ (((a) >> (n)) & 1) #define BITS_XOR_9_TO_31(a) \ (BIT_N((a), 9) ^ BIT_N((a), 10) ^ BIT_N((a), 11) ^ BIT_N((a), 12) ^ \ BIT_N((a), 13) ^ BIT_N((a), 14) ^ BIT_N((a), 15) ^ BIT_N((a), 16) ^ \ BIT_N((a), 17) ^ BIT_N((a), 18) ^ BIT_N((a), 19) ^ BIT_N((a), 20) ^ \ BIT_N((a), 21) ^ BIT_N((a), 22) ^ BIT_N((a), 23) ^ BIT_N((a), 24) ^ \ BIT_N((a), 25) ^ BIT_N((a), 26) ^ BIT_N((a), 27) ^ BIT_N((a), 28) ^ \ BIT_N((a), 29) ^ BIT_N((a), 30) ^ BIT_N((a), 31)) #define BITS_XOR_10_TO_31(a) \ (BIT_N((a), 10) ^ BIT_N((a), 11) ^ BIT_N((a), 12) ^ \ BIT_N((a), 13) ^ BIT_N((a), 14) ^ BIT_N((a), 15) ^ BIT_N((a), 16) ^ \ BIT_N((a), 17) ^ BIT_N((a), 18) ^ BIT_N((a), 19) ^ BIT_N((a), 20) ^ \ BIT_N((a), 21) ^ BIT_N((a), 22) ^ BIT_N((a), 23) ^ BIT_N((a), 24) ^ \ BIT_N((a), 25) ^ BIT_N((a), 26) ^ BIT_N((a), 27) ^ BIT_N((a), 28) ^ \ BIT_N((a), 29) ^ BIT_N((a), 30) ^ BIT_N((a), 31)) static u32 addr_to_color_t19x(uintptr_t phys) { int color, chan, bank; u32 addr = (u32)phys; u32 xaddr = (u32)(phys >> 4); chan = (BITS_XOR_9_TO_31(addr & CHANNEL_MASK_0) << 0); chan |= (BITS_XOR_9_TO_31(addr & CHANNEL_MASK_1) << 1); chan |= (BITS_XOR_9_TO_31(addr & CHANNEL_MASK_2) << 2); chan |= (BITS_XOR_9_TO_31(addr & CHANNEL_MASK_3) << 3); bank = (BITS_XOR_10_TO_31(xaddr & BANK_MASK_0) << 0); bank |= (BITS_XOR_10_TO_31(xaddr & BANK_MASK_1) << 1); bank |= (BITS_XOR_10_TO_31(xaddr & BANK_MASK_2) << 2); WARN_ON(chan > 15); WARN_ON(bank > 7); /* It is preferable to color pages based on even/odd banks * as well. To limit the number of colors to 16, bank info * is not used in page coloring. */ color = chan; return color; } static struct color_list *init_color_list(struct nvmap_page_pool *pool, struct nvmap_alloc_state *state, u32 nr_pages) { struct color_list *list; u32 color, i, page_index = 0; gfp_t gfp = GFP_NVMAP | __GFP_ZERO; list = alloc_color_list(nr_pages, state->nr_colors); if (!list) return NULL; #ifdef CONFIG_NVMAP_PAGE_POOLS /* Allocated page from nvmap page pool if possible */ page_index = nvmap_page_pool_alloc_lots(pool, list->pages, nr_pages); #endif /* Fall back to general page allocator */ for (i = page_index; i < nr_pages; i++) { list->pages[i] = nvmap_alloc_pages_exact(gfp, PAGE_SIZE); if (!list->pages[i]) goto fail; } /* Clean the cache for any page that didn't come from the page pool */ if (page_index < nr_pages) nvmap_clean_cache(&list->pages[page_index], nr_pages - page_index); /* Create linked list of colors and compute the histogram */ for (i = 0; i < nr_pages; i++) { color = state->addr_to_color((uintptr_t) page_to_phys(list->pages[i])); list->list[i] = list->heads[color]; list->heads[color] = i; list->counts[color]++; } return list; fail: while (i--) __free_page(list->pages[i]); free_color_list(list); return NULL; } static void smooth_pages(struct color_list *list, u32 nr_extra, u32 nr_colors) { u32 i, j, color, max; u32 counts[NVMAP_MAX_COLORS] = {0}; if (nr_extra == 0) return; /* Determine which colors need to be freed */ for (i = 0; i < nr_extra; i++) { /* Find the max */ max = 0; color = 0; for (j = 0; j < nr_colors; j++) { if (list->counts[j] - counts[j] > max) { color = j; max = list->counts[j] - counts[j]; } } counts[color]++; } /* Iterate through 0...nr_extra-1 in psuedorandom order */ do { /* Pop the max off and free it */ for (color = 0; color < nr_colors; color++) { while (counts[color]) { __free_page(list_pop_page(list, color, "smooth_pages")); counts[color]--; nr_extra--; } } } while (nr_extra > 0); } static void add_perfect(struct nvmap_alloc_state *state, u32 nr_pages, struct page **out_pages) { u32 i; u32 color; struct page *page; uintptr_t virt_addr; /* create a perfect tile */ for (i = 0; i < state->nr_colors && state->output_count < nr_pages; i++) { virt_addr = (i + (state->tile * state->nr_colors)) * PAGE_SIZE; color = state->addr_to_color(virt_addr); page = list_pop_page(state->list, color, "perfect"); out_pages[state->output_count++] = page; } } static void add_imperfect(struct nvmap_alloc_state *state, u32 nr_pages, struct page **out_pages) { u32 i, j; u32 max_count; u32 color; struct page *page; uintptr_t virt_addr; u32 counts[NVMAP_MAX_COLORS] = {0}; /* Determine which colors will go into the tile */ for (i = 0; i < state->nr_colors; i++) { max_count = 0; color = 0; for (j = 0; j < state->nr_colors; j++) { u32 left = state->list->counts[j] - counts[j]; if (left > max_count && counts[j] < state->max_color_per_tile) { max_count = left; color = j; } } counts[color]++; } /* Arrange the colors into the tile */ for (i = 0; i < state->nr_colors && state->output_count < nr_pages; i++) { virt_addr = (i + (i * state->nr_colors)) * PAGE_SIZE; color = state->addr_to_color(virt_addr); /* Find a substitute color */ if (counts[color] == 0) { /* Find the color used the most in the tile */ max_count = 0; for (j = 0; j < state->nr_colors; j++) { if (counts[j] > max_count) { max_count = counts[j]; color = j; } } } page = list_pop_page(state->list, color, "imperfect"); out_pages[state->output_count++] = page; counts[color]--; } } static int alloc_colored(struct nvmap_page_pool *pool, u32 nr_pages, struct page **out_pages, u32 chipid) { struct nvmap_alloc_state state; u32 nr_alloc, max_count, min_count; u32 nr_tiles, nr_perfect, nr_imperfect; int dither_state; u32 i; state.nr_colors = s_nr_colors; state.addr_to_color = addr_to_color_t19x; /* Allocate pages for full 32-page tiles */ nr_tiles = (nr_pages + state.nr_colors - 1) / state.nr_colors; /* Overallocate pages by 1/16th */ nr_alloc = state.nr_colors * nr_tiles; nr_alloc += nr_alloc >> 4; /* Create lists of each page color */ state.list = init_color_list(pool, &state, nr_alloc); if (!state.list) return -ENOMEM; /* Smooth out the histogram by freeing over allocated pages */ smooth_pages(state.list, nr_alloc - state.nr_colors * nr_tiles, state.nr_colors); max_count = 0; min_count = state.list->counts[0]; for (i = 0; i < state.nr_colors; i++) { if (state.list->counts[i] > max_count) max_count = state.list->counts[i]; if (state.list->counts[i] < min_count) min_count = state.list->counts[i]; } /* Compute the number of perfect / imperfect tiles and the maximum * number of pages with the same color can be in a tile */ if (max_count / nr_tiles >= 3) { /* It is not possible to create perfect tiles with * max_color_per_tile <= 3 */ nr_perfect = 0; state.max_color_per_tile = (max_count + nr_tiles - 1) / nr_tiles; } else if (nr_tiles * 2 == max_count) { /* All of the tiles can be perfect */ nr_perfect = nr_tiles; state.max_color_per_tile = 2; } else { /* Some of the tiles can be perfect */ nr_perfect = nr_tiles - (max_count % nr_tiles); state.max_color_per_tile = 3; } /* Check if the number of perfect tiles is bound by the color with the * minimum count */ if (nr_perfect * 2 > min_count) nr_perfect = min_count / 2; nr_imperfect = nr_tiles - nr_perfect; /* Output tiles */ dither_state = nr_perfect - nr_imperfect; state.output_count = 0; for (state.tile = 0; state.tile < nr_tiles; state.tile++) { if (dither_state > 0) { add_perfect(&state, nr_pages, out_pages); dither_state -= nr_imperfect; } else { add_imperfect(&state, nr_pages, out_pages); dither_state += nr_perfect; } } /* Free extra pages created when the buffer does not * fill the last tile */ for (i = 0; i < state.nr_colors; i++) while (state.list->counts[i] > 0) __free_page(list_pop_page(state.list, i, "free")); free_color_list(state.list); return 0; } static int handle_page_alloc(struct nvmap_client *client, struct nvmap_handle *h, bool contiguous) { size_t size = h->size; int nr_page = size >> PAGE_SHIFT; int i = 0, page_index = 0; struct page **pages; gfp_t gfp = GFP_NVMAP | __GFP_ZERO; int pages_per_big_pg = NVMAP_PP_BIG_PAGE_SIZE >> PAGE_SHIFT; static u32 chipid; if (!chipid) { #ifdef CONFIG_NVMAP_COLOR_PAGES chipid = tegra_hidrev_get_chipid(tegra_read_chipid()); if (chipid == TEGRA194) s_nr_colors = 16; #endif } pages = nvmap_altalloc(nr_page * sizeof(*pages)); if (!pages) return -ENOMEM; if (contiguous) { struct page *page; page = nvmap_alloc_pages_exact(gfp, size); if (!page) goto fail; for (i = 0; i < nr_page; i++) pages[i] = nth_page(page, i); } else { #ifdef CONFIG_NVMAP_PAGE_POOLS /* Get as many big pages from the pool as possible. */ page_index = nvmap_page_pool_alloc_lots_bp(&nvmap_dev->pool, pages, nr_page); pages_per_big_pg = nvmap_dev->pool.pages_per_big_pg; #endif /* Try to allocate big pages from page allocator */ for (i = page_index; i < nr_page && pages_per_big_pg > 1 && (nr_page - i) >= pages_per_big_pg; i += pages_per_big_pg, page_index += pages_per_big_pg) { struct page *page; int idx; /* * set the gfp not to trigger direct/kswapd reclaims and * not to use emergency reserves. */ gfp_t gfp_no_reclaim = (gfp | __GFP_NOMEMALLOC) & ~__GFP_RECLAIM; page = nvmap_alloc_pages_exact(gfp_no_reclaim, pages_per_big_pg << PAGE_SHIFT); if (!page) break; for (idx = 0; idx < pages_per_big_pg; idx++) pages[i + idx] = nth_page(page, idx); nvmap_clean_cache(&pages[i], pages_per_big_pg); } nvmap_big_page_allocs += page_index; if (s_nr_colors <= 1) { #ifdef CONFIG_NVMAP_PAGE_POOLS /* Get as many 4K pages from the pool as possible. */ page_index += nvmap_page_pool_alloc_lots( &nvmap_dev->pool, &pages[page_index], nr_page - page_index); #endif for (i = page_index; i < nr_page; i++) { pages[i] = nvmap_alloc_pages_exact(gfp, PAGE_SIZE); if (!pages[i]) goto fail; } } else if (page_index < nr_page) { if (alloc_colored(&nvmap_dev->pool, nr_page - page_index, &pages[page_index], chipid)) goto fail; page_index = nr_page; } nvmap_total_page_allocs += nr_page; } /* * Make sure any data in the caches is cleaned out before * passing these pages to userspace. Many nvmap clients assume that * the buffers are clean as soon as they are allocated. nvmap * clients can pass the buffer to hardware as it is without any * explicit cache maintenance. */ if (page_index < nr_page) nvmap_clean_cache(&pages[page_index], nr_page - page_index); h->pgalloc.pages = pages; h->pgalloc.contig = contiguous; atomic_set(&h->pgalloc.ndirty, 0); return 0; fail: while (i--) __free_page(pages[i]); nvmap_altfree(pages, nr_page * sizeof(*pages)); wmb(); return -ENOMEM; } static struct device *nvmap_heap_pgalloc_dev(unsigned long type) { int ret = -EINVAL; struct device *dma_dev; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) ret = 0; #endif if (ret || (type != NVMAP_HEAP_CARVEOUT_VPR)) return ERR_PTR(-EINVAL); dma_dev = dma_dev_from_handle(type); if (IS_ERR(dma_dev)) return dma_dev; ret = dma_set_resizable_heap_floor_size(dma_dev, 0); if (ret) return ERR_PTR(ret); return dma_dev; } static int nvmap_heap_pgalloc(struct nvmap_client *client, struct nvmap_handle *h, unsigned long type) { size_t size = h->size; struct page **pages; struct device *dma_dev; DEFINE_DMA_ATTRS(attrs); dma_addr_t pa; dma_dev = nvmap_heap_pgalloc_dev(type); if (IS_ERR(dma_dev)) return PTR_ERR(dma_dev); dma_set_attr(DMA_ATTR_ALLOC_EXACT_SIZE, __DMA_ATTR(attrs)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) dma_set_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, __DMA_ATTR(attrs)); #endif pages = dma_alloc_attrs(dma_dev, size, &pa, GFP_KERNEL, __DMA_ATTR(attrs)); if (dma_mapping_error(dma_dev, pa)) return -ENOMEM; h->pgalloc.pages = pages; h->pgalloc.contig = 0; atomic_set(&h->pgalloc.ndirty, 0); return 0; } static int nvmap_heap_pgfree(struct nvmap_handle *h) { size_t size = h->size; struct device *dma_dev; DEFINE_DMA_ATTRS(attrs); dma_addr_t pa = ~(dma_addr_t)0; dma_dev = nvmap_heap_pgalloc_dev(h->heap_type); if (IS_ERR(dma_dev)) return PTR_ERR(dma_dev); dma_set_attr(DMA_ATTR_ALLOC_EXACT_SIZE, __DMA_ATTR(attrs)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) dma_set_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, __DMA_ATTR(attrs)); #endif dma_free_attrs(dma_dev, size, h->pgalloc.pages, pa, __DMA_ATTR(attrs)); h->pgalloc.pages = NULL; return 0; } static void alloc_handle(struct nvmap_client *client, struct nvmap_handle *h, unsigned int type) { unsigned int carveout_mask = NVMAP_HEAP_CARVEOUT_MASK; unsigned int iovmm_mask = NVMAP_HEAP_IOVMM; int ret; BUG_ON(type & (type - 1)); if (nvmap_convert_carveout_to_iovmm) { carveout_mask &= ~NVMAP_HEAP_CARVEOUT_GENERIC; iovmm_mask |= NVMAP_HEAP_CARVEOUT_GENERIC; } else if (nvmap_convert_iovmm_to_carveout) { if (type & NVMAP_HEAP_IOVMM) { type &= ~NVMAP_HEAP_IOVMM; type |= NVMAP_HEAP_CARVEOUT_GENERIC; } } if (type & carveout_mask) { struct nvmap_heap_block *b; b = nvmap_carveout_alloc(client, h, type, NULL); if (b) { h->heap_type = type; h->heap_pgalloc = false; /* barrier to ensure all handle alloc data * is visible before alloc is seen by other * processors. */ mb(); h->alloc = true; return; } ret = nvmap_heap_pgalloc(client, h, type); if (ret) return; h->heap_type = NVMAP_HEAP_CARVEOUT_VPR; h->heap_pgalloc = true; mb(); h->alloc = true; } else if (type & iovmm_mask) { ret = handle_page_alloc(client, h, h->userflags & NVMAP_HANDLE_PHYS_CONTIG); if (ret) return; h->heap_type = NVMAP_HEAP_IOVMM; h->heap_pgalloc = true; mb(); h->alloc = true; } } static int alloc_handle_from_va(struct nvmap_client *client, struct nvmap_handle *h, ulong vaddr, u32 flags) { int nr_page = h->size >> PAGE_SHIFT; struct page **pages; int ret = 0; pages = nvmap_altalloc(nr_page * sizeof(*pages)); if (IS_ERR_OR_NULL(pages)) return PTR_ERR(pages); ret = nvmap_get_user_pages(vaddr & PAGE_MASK, nr_page, pages, true, (flags & NVMAP_HANDLE_RO) ? 0 : FOLL_WRITE); if (ret) { nvmap_altfree(pages, nr_page * sizeof(*pages)); return ret; } if (flags & NVMAP_HANDLE_RO) h->is_ro = true; nvmap_clean_cache(&pages[0], nr_page); h->pgalloc.pages = pages; atomic_set(&h->pgalloc.ndirty, 0); h->heap_type = NVMAP_HEAP_IOVMM; h->heap_pgalloc = true; h->from_va = true; mb(); h->alloc = true; return ret; } /* small allocations will try to allocate from generic OS memory before * any of the limited heaps, to increase the effective memory for graphics * allocations, and to reduce fragmentation of the graphics heaps with * sub-page splinters */ static const unsigned int heap_policy_small[] = { NVMAP_HEAP_CARVEOUT_VPR, NVMAP_HEAP_CARVEOUT_IRAM, NVMAP_HEAP_CARVEOUT_MASK, NVMAP_HEAP_IOVMM, 0, }; static const unsigned int heap_policy_large[] = { NVMAP_HEAP_CARVEOUT_VPR, NVMAP_HEAP_CARVEOUT_IRAM, NVMAP_HEAP_IOVMM, NVMAP_HEAP_CARVEOUT_MASK, 0, }; static const unsigned int heap_policy_excl[] = { NVMAP_HEAP_CARVEOUT_IVM, NVMAP_HEAP_CARVEOUT_VIDMEM, 0, }; int nvmap_alloc_handle(struct nvmap_client *client, struct nvmap_handle *h, unsigned int heap_mask, size_t align, u8 kind, unsigned int flags, int peer) { const unsigned int *alloc_policy; int nr_page; int err = -ENOMEM; int tag, i; bool alloc_from_excl = false; h = nvmap_handle_get(h); if (!h) return -EINVAL; if (h->alloc) { nvmap_handle_put(h); return -EEXIST; } nvmap_stats_inc(NS_TOTAL, h->size); nvmap_stats_inc(NS_ALLOC, h->size); trace_nvmap_alloc_handle(client, h, h->size, heap_mask, align, flags, nvmap_stats_read(NS_TOTAL), nvmap_stats_read(NS_ALLOC)); h->userflags = flags; nr_page = ((h->size + PAGE_SIZE - 1) >> PAGE_SHIFT); /* Force mapping to uncached for VPR memory. */ if (heap_mask & (NVMAP_HEAP_CARVEOUT_VPR | ~nvmap_dev->cpu_access_mask)) h->flags = NVMAP_HANDLE_UNCACHEABLE; else h->flags = (flags & NVMAP_HANDLE_CACHE_FLAG); h->align = max_t(size_t, align, L1_CACHE_BYTES); h->peer = peer; tag = flags >> 16; if (!tag && client && !client->tag_warned) { char task_comm[TASK_COMM_LEN]; client->tag_warned = 1; get_task_comm(task_comm, client->task); pr_err("PID %d: %s: WARNING: " "All NvMap Allocations must have a tag " "to identify the subsystem allocating memory." "Please pass the tag to the API call" " NvRmMemHanldeAllocAttr() or relevant. \n", client->task->pid, task_comm); } /* * If user specifies one of the exclusive carveouts, allocation * from no other heap should be allowed. */ for (i = 0; i < ARRAY_SIZE(heap_policy_excl); i++) { if (!(heap_mask & heap_policy_excl[i])) continue; if (heap_mask & ~(heap_policy_excl[i])) { pr_err("%s alloc mixes exclusive heap %d and other heaps\n", current->group_leader->comm, heap_policy_excl[i]); err = -EINVAL; goto out; } alloc_from_excl = true; } if (!heap_mask) { err = -EINVAL; goto out; } alloc_policy = alloc_from_excl ? heap_policy_excl : (nr_page == 1) ? heap_policy_small : heap_policy_large; while (!h->alloc && *alloc_policy) { unsigned int heap_type; heap_type = *alloc_policy++; heap_type &= heap_mask; if (!heap_type) continue; heap_mask &= ~heap_type; while (heap_type && !h->alloc) { unsigned int heap; /* iterate possible heaps MSB-to-LSB, since higher- * priority carveouts will have higher usage masks */ heap = 1 << __fls(heap_type); alloc_handle(client, h, heap); heap_type &= ~heap; } } out: if (h->alloc) { if (client->kernel_client) nvmap_stats_inc(NS_KALLOC, h->size); else nvmap_stats_inc(NS_UALLOC, h->size); NVMAP_TAG_TRACE(trace_nvmap_alloc_handle_done, NVMAP_TP_ARGS_CHR(client, h, NULL)); err = 0; } else { nvmap_stats_dec(NS_TOTAL, h->size); nvmap_stats_dec(NS_ALLOC, h->size); } nvmap_handle_put(h); return err; } int nvmap_alloc_handle_from_va(struct nvmap_client *client, struct nvmap_handle *h, ulong addr, unsigned int flags) { int err = -ENOMEM; int tag; h = nvmap_handle_get(h); if (!h) return -EINVAL; if (h->alloc) { nvmap_handle_put(h); return -EEXIST; } h->userflags = flags; h->flags = (flags & NVMAP_HANDLE_CACHE_FLAG); h->align = PAGE_SIZE; tag = flags >> 16; if (!tag && client && !client->tag_warned) { char task_comm[TASK_COMM_LEN]; client->tag_warned = 1; get_task_comm(task_comm, client->task); pr_err("PID %d: %s: WARNING: " "All NvMap Allocations must have a tag " "to identify the subsystem allocating memory." "Please pass the tag to the API call" " NvRmMemHanldeAllocAttr() or relevant. \n", client->task->pid, task_comm); } err = alloc_handle_from_va(client, h, addr, flags); if (err) { pr_err("alloc_handle_from_va failed %d", err); nvmap_handle_put(h); return -EINVAL; } if (h->alloc) { NVMAP_TAG_TRACE(trace_nvmap_alloc_handle_done, NVMAP_TP_ARGS_CHR(client, h, NULL)); err = 0; } nvmap_handle_put(h); return err; } void _nvmap_handle_free(struct nvmap_handle *h) { unsigned int i, nr_page, page_index = 0; struct nvmap_handle_dmabuf_priv *curr, *next; list_for_each_entry_safe(curr, next, &h->dmabuf_priv, list) { curr->priv_release(curr->priv); list_del(&curr->list); kzfree(curr); } if (nvmap_handle_remove(nvmap_dev, h) != 0) return; if (!h->alloc) goto out; nvmap_stats_inc(NS_RELEASE, h->size); nvmap_stats_dec(NS_TOTAL, h->size); if (!h->heap_pgalloc) { if (h->vaddr) { struct vm_struct *vm; void *addr = h->vaddr; addr -= (h->carveout->base & ~PAGE_MASK); vm = find_vm_area(addr); BUG_ON(!vm); free_vm_area(vm); } nvmap_heap_free(h->carveout); nvmap_kmaps_dec(h); h->vaddr = NULL; goto out; } else { int ret = nvmap_heap_pgfree(h); if (!ret) goto out; } nr_page = DIV_ROUND_UP(h->size, PAGE_SIZE); BUG_ON(h->size & ~PAGE_MASK); BUG_ON(!h->pgalloc.pages); if (h->vaddr) { nvmap_kmaps_dec(h); vm_unmap_ram(h->vaddr, h->size >> PAGE_SHIFT); h->vaddr = NULL; } for (i = 0; i < nr_page; i++) h->pgalloc.pages[i] = nvmap_to_page(h->pgalloc.pages[i]); #ifdef CONFIG_NVMAP_PAGE_POOLS if (!h->from_va) page_index = nvmap_page_pool_fill_lots(&nvmap_dev->pool, h->pgalloc.pages, nr_page); #endif for (i = page_index; i < nr_page; i++) { if (h->from_va) put_page(h->pgalloc.pages[i]); else __free_page(h->pgalloc.pages[i]); } nvmap_altfree(h->pgalloc.pages, nr_page * sizeof(struct page *)); out: NVMAP_TAG_TRACE(trace_nvmap_destroy_handle, NULL, get_current()->pid, 0, NVMAP_TP_ARGS_H(h)); kfree(h); } void nvmap_free_handle(struct nvmap_client *client, struct nvmap_handle *handle) { struct nvmap_handle_ref *ref; struct nvmap_handle *h; nvmap_ref_lock(client); ref = __nvmap_validate_locked(client, handle); if (!ref) { nvmap_ref_unlock(client); return; } BUG_ON(!ref->handle); h = ref->handle; if (atomic_dec_return(&ref->dupes)) { NVMAP_TAG_TRACE(trace_nvmap_free_handle, NVMAP_TP_ARGS_CHR(client, h, ref)); nvmap_ref_unlock(client); goto out; } smp_rmb(); rb_erase(&ref->node, &client->handle_refs); client->handle_count--; atomic_dec(&ref->handle->share_count); nvmap_ref_unlock(client); if (h->owner == client) h->owner = NULL; dma_buf_put(ref->handle->dmabuf); NVMAP_TAG_TRACE(trace_nvmap_free_handle, NVMAP_TP_ARGS_CHR(client, h, ref)); kfree(ref); out: BUG_ON(!atomic_read(&h->ref)); nvmap_handle_put(h); } EXPORT_SYMBOL(nvmap_free_handle); void nvmap_free_handle_fd(struct nvmap_client *client, int fd) { struct nvmap_handle *handle = nvmap_handle_get_from_fd(fd); if (handle) { nvmap_free_handle(client, handle); nvmap_handle_put(handle); } }