diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/platform/sprd/sprd_mem_pool.c | |
Diffstat (limited to 'drivers/platform/sprd/sprd_mem_pool.c')
| -rw-r--r-- | drivers/platform/sprd/sprd_mem_pool.c | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/drivers/platform/sprd/sprd_mem_pool.c b/drivers/platform/sprd/sprd_mem_pool.c new file mode 100644 index 00000000..dcfa658d --- /dev/null +++ b/drivers/platform/sprd/sprd_mem_pool.c @@ -0,0 +1,470 @@ +/* linux/arch/arm/mach-sc8810/sprd_mem_pool.c + * + * Copyright (C) 2010 Spreadtrum + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/mm.h> +#include <linux/mmzone.h> +#include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/pid.h> +#include <linux/mutex.h> +#include <asm/current.h> +#include <asm/uaccess.h> +#include <linux/pagemap.h> +#include <linux/pfn.h> +#include <linux/memory.h> +#include <linux/slab.h> +#include <linux/kthread.h> +#include <linux/sched.h> + +/** + * add another order pool, plz modify: + * 1.PAGE_POOL_MAX_COUNT: +1 + * 2.page_pool struct: +node + * */ + +/*Magic Numbers*/ +#define DEBUG_PRINT 1 /*used for debug*/ +#define UNSIGNED_LONG_MAX 0xffffffff +#define PAGE_POOL_MAX_COUNT 5 + +/*status record struct declare*/ +struct status { + unsigned long used; + unsigned long free; + unsigned long peak; + unsigned long total; +}; + +/*page pool struct definition*/ +static struct { + unsigned long order; + unsigned long threshold; + unsigned long node_count; + unsigned long *buffer; + struct mutex lock; + struct status stat; + struct workqueue_struct *sprd_alloc_wq; + struct work_struct sprd_alloc_work; +} page_pool[PAGE_POOL_MAX_COUNT] = { + { + .order = 0, + .buffer = NULL, + .sprd_alloc_wq = NULL, + }, { + .order = 1, + .threshold = 2, + .node_count = 15, + .buffer = NULL, + .lock = __MUTEX_INITIALIZER(page_pool[1].lock), + .stat.used = 0, + .stat.free = 0, + .stat.peak = 0, + .stat.total = 0, + .sprd_alloc_wq = NULL, + }, { + .order = 2, + .threshold = 2, + .node_count = 10, + .buffer = NULL, + .lock = __MUTEX_INITIALIZER(page_pool[2].lock), + .stat.used = 0, + .stat.free = 0, + .stat.peak = 0, + .stat.total = 0, + .sprd_alloc_wq = NULL, + }, { + .order = 3, + .threshold = 1, + .node_count = 3, + .buffer = NULL, + .lock = __MUTEX_INITIALIZER(page_pool[3].lock), + .stat.used = 0, + .stat.free = 0, + .stat.peak = 0, + .stat.total = 0, + .sprd_alloc_wq = NULL, + }, { + .order = 4, + .threshold = 1, + .node_count = 2, + .buffer = NULL, + .lock = __MUTEX_INITIALIZER(page_pool[4].lock), + .stat.used = 0, + .stat.free = 0, + .stat.peak = 0, + .stat.total = 0, + .sprd_alloc_wq = NULL, + } +}; + +/*global variables*/ +static unsigned long sys_buddy_info[MAX_ORDER]; +static unsigned long alloc_flag = 1; +static int sprd_alloc_workqueue_pid = -1; + +/*extern functions*/ +extern void msleep(unsigned int msecs); +extern void dump_stack(void); +struct mutex sprd_mem_stat_lock; +static int sprd_mem_pool_stat = 0; +static unsigned long long sprd_mem_alloc_count = 0; + +/*internal functions*/ +static unsigned long pow2(unsigned long x, unsigned long y) +{ + unsigned long result = 0; + + /*x == 0*/ + if(!x) return 0; + + /*y == 0*/ + if(!y) return 1; + + /*x > 0, y > 0*/ + result = x * pow2(x, (y - 1)); + + if(result > UNSIGNED_LONG_MAX) { + result = UNSIGNED_LONG_MAX; + printk("__SPRD__INIT__: call pow2(%lu, %lu) error!!!\n", x, y); + } + + return result; +} + +static void dumpstack(void) +{ + printk("Process Name: %s, Process Pid: %d, Parent Name: %s, Parent Pid: %d\n", + current->comm, current->pid, current->parent->comm, current->parent->pid); + dump_stack(); +} + +static struct page *address_to_pages(unsigned long address) +{ + if(!address) return NULL; + +#if defined(WANT_PAGE_VIRTUAL) + return container_of((void *)(address), struct page, virtual); +#else + return virt_to_page(address); +#endif +} + +static void get_buddy_info(void) +{ + struct zone *zone; + unsigned int order = 0; + + /*normal zone only*/ + for_each_zone(zone) { + if(is_normal(zone)) break; + } + + /*global buddyinfo*/ + for(order = 0; order < MAX_ORDER; order++) { + sys_buddy_info[order] = zone->free_area[order].nr_free; + } +} + +static unsigned long get_page_count(unsigned int entry_order) +{ + struct zone *zone; + unsigned int order = 0; + unsigned long page_count = 0; + + /*normal zone only*/ + for_each_zone(zone) { + if(is_normal(zone)) break; + } + + /*forall buddy pages*/ + for(order = 0; order < MAX_ORDER; order++) { + /*get page counts*/ + if(order < entry_order) continue; + + page_count += (zone->free_area[order].nr_free << (order - entry_order)); + } + + return page_count; +} + +static unsigned long sprd_alloc_one(unsigned long order) +{ + unsigned long sub = 0; + unsigned long address = 0; + + /*lock*/ + mutex_lock(&(page_pool[order].lock)); + + /*for order page pool*/ + for(sub = 0; sub < page_pool[order].node_count; sub++) { + + /*jump over null page*/ + if(!page_pool[order].buffer[sub]) continue; + + /*get first !null page*/ + address = page_pool[order].buffer[sub]; + page_pool[order].buffer[sub] = 0; + + /*status record*/ + page_pool[order].stat.free--; + page_pool[order].stat.used++; + + mutex_lock(&sprd_mem_stat_lock); + sprd_mem_alloc_count++; + sprd_mem_pool_stat = 1; + mutex_unlock(&sprd_mem_stat_lock); + + if(strncmp(current->comm, "sprd-page-alloc", 13)) { + if(page_pool[order].stat.peak < page_pool[order].stat.used) + page_pool[order].stat.peak = page_pool[order].stat.used; + if(page_pool[order].stat.total < UNSIGNED_LONG_MAX) + page_pool[order].stat.total++; + } + goto end; + } + +end: + /*unlock*/ + mutex_unlock(&(page_pool[order].lock)); + +#if DEBUG_PRINT + printk("__SPRD__ALLOC__ONE__: buffer for order = %lu empty; free = %lu, used = %lu, sys = %lu; peak = %lu, total = %lu; Call Stack:\n", + order, page_pool[order].stat.free, page_pool[order].stat.used, + get_page_count(order), page_pool[order].stat.peak, page_pool[order].stat.total); + dumpstack(); +#endif + return address; +} + +int sprd_page_mask_check(int pid) +{ + if(pid == sprd_alloc_workqueue_pid) return -1; + + return 0; +} + +struct page *sprd_page_alloc(gfp_t gfp_mask, unsigned int order, unsigned long zoneidx) +{ + unsigned long address = 0; + struct page *page = NULL; + + /*check some flags*/ + if((!alloc_flag) || (GFP_KERNEL != gfp_mask) || (ZONE_NORMAL != zoneidx) || (order > PAGE_POOL_MAX_COUNT - 1) || (order < 1)) + goto Failed; + + /*alloc*/ + address = sprd_alloc_one((unsigned long)order); + if(!address) goto Failed; + + /*convert address to page*/ + page = address_to_pages(address); + + return page; + +Failed: + return NULL; +} + +static int sprd_show_pages_info(struct seq_file *m, void *v) +{ + unsigned long i = 0; + unsigned long start = 0; + + seq_printf(m, + "sprd pool summery:\n" + " status: %s\n", + (alloc_flag ? "open" : "closed")); + for(start = 1; start < PAGE_POOL_MAX_COUNT; start++) { + seq_printf(m, + " free node (order=%lu) = %lu, used node (order=%lu) = %lu\n", + start, page_pool[start].stat.free, start, page_pool[start].stat.used); + } + for(start = 1; start < PAGE_POOL_MAX_COUNT; start++) { + seq_printf(m, + " system memory count (order=%lu) = %lu\n", + start, get_page_count(start)); + } + for(start = 1; start < PAGE_POOL_MAX_COUNT; start++) { + seq_printf(m, + " alloc peak (order=%lu) = %lu, alloc total (order=%lu) = %lu\n", + start, page_pool[start].stat.peak, start, page_pool[start].stat.total); + } + + seq_printf(m, "buddy info:\n"); + get_buddy_info(); + for(i = 0; i < MAX_ORDER; i++) { + seq_printf(m, " orders = %lu, number = %lu\n", i, sys_buddy_info[i]); + } + + for(start = 1; start < PAGE_POOL_MAX_COUNT; start++) { + seq_printf(m, "pool node (order=%lu) details:\n", start); + for(i = 0; i < page_pool[start].node_count; i++) { + struct page *page = NULL; + page = address_to_pages(page_pool[start].buffer[i]); + seq_printf(m, " subscript = %lu, value = %p, page = %p, page->flags %lx\n", + i, (void *)(page_pool[start].buffer[i]), page, (page ? page->flags : 0x00)); + } + } + + seq_printf(m, "sprd_mem_alloc_count %d\n", sprd_mem_alloc_count); + seq_printf(m, "sprd_mem_pool_stat %d\n", sprd_mem_pool_stat); + sprd_alloc_one(3); + return 0; +} + +static int sprd_pages_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, sprd_show_pages_info, NULL); +} + +static int sprd_pages_info_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) +{ + char flag; + + if (copy_from_user(&flag, buf, 1)) return -EFAULT; + + switch (flag) { + case '0': alloc_flag = 0; break; + case '1': alloc_flag = 1; break; + default: break; + } + + return 1; +} + +static const struct file_operations sprd_page_info_fops = { + .open = sprd_pages_info_open, + .read = seq_read, + .write = sprd_pages_info_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static int sprd_mem_pool_daemon(void *arg) +{ + while (1) { + int start = 0; + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(1000)); + set_current_state(TASK_RUNNING); + + if (!sprd_mem_pool_stat) + continue; + + mutex_lock(&sprd_mem_stat_lock); + sprd_mem_pool_stat = 0; + mutex_unlock(&sprd_mem_stat_lock); + + for(start = PAGE_POOL_MAX_COUNT - 1; start > 0; start--) { + unsigned long sub = 0; + + if (page_pool[start].node_count == page_pool[start].stat.free) + continue; + else { + mutex_lock(&sprd_mem_stat_lock); + if (!sprd_mem_pool_stat) + sprd_mem_pool_stat = 1; + mutex_unlock(&sprd_mem_stat_lock); + } + + /*for single page buffer*/ + for(sub = 0; sub < page_pool[start].node_count; sub++) { + mutex_lock(&(page_pool[start].lock)); + gfp_t gfp_mask = GFP_KERNEL; + + /*jump !null node*/ + if(page_pool[start].buffer[sub]) { + mutex_unlock(&(page_pool[start].lock)); + continue; + } + + /*check if sys pages enough*/ + if(page_pool[start].threshold > get_page_count(page_pool[start].order)) { + mutex_unlock(&(page_pool[start].lock)); + break; + } + + /*alloc page from sys for null node*/ + page_pool[start].buffer[sub] = __get_free_pages(gfp_mask, page_pool[start].order); + if(!page_pool[start].buffer[sub]) { + mutex_unlock(&(page_pool[start].lock)); + break; + } + + /*stats record*/ + page_pool[start].stat.free++; + page_pool[start].stat.used--; + mutex_unlock(&(page_pool[start].lock)); + } + } + + mutex_lock(&sprd_mem_stat_lock); + if(!sprd_mem_pool_stat) + sprd_mem_alloc_count = 0; + mutex_unlock(&sprd_mem_stat_lock); + } +} + +static int __init sprd_pages_init(void) +{ + int start = 0, mem_sum = 0; + gfp_t gfp_mask = GFP_KERNEL; + struct workqueue_struct *sprd_alloc_wq = NULL; + struct task_struct *p = NULL; + + /*init global*/ + memset(sys_buddy_info, 0, sizeof(sys_buddy_info)); + mutex_init(&sprd_mem_stat_lock); + + /*init page pool*/ + for(start = 1; start < PAGE_POOL_MAX_COUNT; start++) { + + /*node buffer*/ + unsigned long sub = 0; + page_pool[start].buffer = kmalloc((page_pool[start].node_count * sizeof(unsigned long)), gfp_mask); + memset(page_pool[start].buffer, 0, (page_pool[start].node_count * sizeof(unsigned long))); + + for(sub = 0; sub < page_pool[start].node_count; sub++) { + /*alloc page from sys*/ + page_pool[start].buffer[sub] = __get_free_pages(gfp_mask, page_pool[start].order); + /*stats record*/ + page_pool[start].buffer[sub] ? page_pool[start].stat.free++ : page_pool[start].stat.used++; + } + + /*alloc total size*/ + mem_sum += page_pool[start].node_count * 4 * pow2(2, page_pool[start].order); + +#if DEBUG_PRINT + printk("__SPRD__INIT__: page_pool[%d].node_count = %lu, page_pool[%d].order = %lu, mem_sum = %d KiB\n", + start, page_pool[start].node_count, start, page_pool[start].order, mem_sum); +#endif + } + p = kthread_create(sprd_mem_pool_daemon, NULL, "%s", "sprdmempool"); + wake_up_process(p); + sprd_alloc_workqueue_pid = p->pid; + + printk("__SPRD__INIT__: Sprd memory pool initialized, %d KiB memory allocated.\n", mem_sum); + + /*create proc file*/ + proc_create("sprd_pages", 0, NULL, &sprd_page_info_fops); + return 0; +} + +module_init(sprd_pages_init); + |
