/* * Support for Medifield PNW Camera Imaging ISP subsystem. * * Copyright (c) 2010 Intel Corporation. All Rights Reserved. * * Copyright (c) 2010 Silicon Hive www.siliconhive.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. * * This program is distributed in the hope that 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. * * */ /* * This file contains functions for dynamic memory pool management */ #include #include #include #include #include "atomisp_internal.h" #include "hmm/hmm_pool.h" /* * dynamic memory pool ops. */ static unsigned int get_pages_from_dynamic_pool(void *pool, struct hmm_page_object *page_obj, unsigned int size, bool cached) { struct hmm_page *hmm_page; unsigned long flags; unsigned int i = 0; struct hmm_dynamic_pool_info *dypool_info = pool; if (!dypool_info) return 0; spin_lock_irqsave(&dypool_info->list_lock, flags); if (dypool_info->initialized) { while (!list_empty(&dypool_info->pages_list)) { hmm_page = list_entry(dypool_info->pages_list.next, struct hmm_page, list); list_del(&hmm_page->list); dypool_info->pgnr--; spin_unlock_irqrestore(&dypool_info->list_lock, flags); page_obj[i].page = hmm_page->page; page_obj[i++].type = HMM_PAGE_TYPE_DYNAMIC; kmem_cache_free(dypool_info->pgptr_cache, hmm_page); if (i == size) return i; spin_lock_irqsave(&dypool_info->list_lock, flags); } } spin_unlock_irqrestore(&dypool_info->list_lock, flags); return i; } static void free_pages_to_dynamic_pool(void *pool, struct hmm_page_object *page_obj) { struct hmm_page *hmm_page; unsigned long flags; int ret; struct hmm_dynamic_pool_info *dypool_info = pool; if (!dypool_info) return; spin_lock_irqsave(&dypool_info->list_lock, flags); if (!dypool_info->initialized) { spin_unlock_irqrestore(&dypool_info->list_lock, flags); return; } spin_unlock_irqrestore(&dypool_info->list_lock, flags); if (page_obj->type == HMM_PAGE_TYPE_RESERVED) return; if (dypool_info->pgnr >= dypool_info->pool_size) { /* free page directly back to system */ ret = set_pages_wb(page_obj->page, 1); if (ret) dev_err(atomisp_dev, "set page to WB err ...ret=%d\n", ret); /* W/A: set_pages_wb seldom return value = -EFAULT indicate that address of page is not in valid range(0xffff880000000000~0xffffc7ffffffffff) then, _free_pages would panic; Do not know why page address be valid, it maybe memory corruption by lowmemory */ if (!ret) { __free_pages(page_obj->page, 0); hmm_mem_stat.sys_size--; } return; } hmm_page = kmem_cache_zalloc(dypool_info->pgptr_cache, GFP_KERNEL); if (!hmm_page) { /* free page directly */ ret = set_pages_wb(page_obj->page, 1); if (ret) dev_err(atomisp_dev, "set page to WB err ...ret=%d\n", ret); if (!ret) { __free_pages(page_obj->page, 0); hmm_mem_stat.sys_size--; } return; } hmm_page->page = page_obj->page; /* * add to pages_list of pages_pool */ spin_lock_irqsave(&dypool_info->list_lock, flags); list_add_tail(&hmm_page->list, &dypool_info->pages_list); dypool_info->pgnr++; spin_unlock_irqrestore(&dypool_info->list_lock, flags); hmm_mem_stat.dyc_size++; } static int hmm_dynamic_pool_init(void **pool, unsigned int pool_size) { struct hmm_dynamic_pool_info *dypool_info; if (pool_size == 0) return 0; dypool_info = kmalloc(sizeof(struct hmm_dynamic_pool_info), GFP_KERNEL); if (unlikely(!dypool_info)) return -ENOMEM; dypool_info->pgptr_cache = kmem_cache_create("pgptr_cache", sizeof(struct hmm_page), 0, SLAB_HWCACHE_ALIGN, NULL); if (!dypool_info->pgptr_cache) { kfree(dypool_info); return -ENOMEM; } INIT_LIST_HEAD(&dypool_info->pages_list); spin_lock_init(&dypool_info->list_lock); dypool_info->initialized = true; dypool_info->pool_size = pool_size; dypool_info->pgnr = 0; *pool = dypool_info; return 0; } static void hmm_dynamic_pool_exit(void **pool) { struct hmm_dynamic_pool_info *dypool_info = *pool; struct hmm_page *hmm_page; unsigned long flags; int ret; if (!dypool_info) return; spin_lock_irqsave(&dypool_info->list_lock, flags); if (!dypool_info->initialized) { spin_unlock_irqrestore(&dypool_info->list_lock, flags); return; } dypool_info->initialized = false; while (!list_empty(&dypool_info->pages_list)) { hmm_page = list_entry(dypool_info->pages_list.next, struct hmm_page, list); list_del(&hmm_page->list); spin_unlock_irqrestore(&dypool_info->list_lock, flags); /* can cause thread sleep, so cannot be put into spin_lock */ ret = set_pages_wb(hmm_page->page, 1); if (ret) dev_err(atomisp_dev, "set page to WB err...ret=%d\n", ret); if (!ret) { __free_pages(hmm_page->page, 0); hmm_mem_stat.dyc_size--; hmm_mem_stat.sys_size--; } kmem_cache_free(dypool_info->pgptr_cache, hmm_page); spin_lock_irqsave(&dypool_info->list_lock, flags); } spin_unlock_irqrestore(&dypool_info->list_lock, flags); kmem_cache_destroy(dypool_info->pgptr_cache); kfree(dypool_info); *pool = NULL; } static int hmm_dynamic_pool_inited(void *pool) { struct hmm_dynamic_pool_info *dypool_info = pool; if (!dypool_info) return 0; return dypool_info->initialized; } struct hmm_pool_ops dynamic_pops = { .pool_init = hmm_dynamic_pool_init, .pool_exit = hmm_dynamic_pool_exit, .pool_alloc_pages = get_pages_from_dynamic_pool, .pool_free_pages = free_pages_to_dynamic_pool, .pool_inited = hmm_dynamic_pool_inited, };