blob: ac2d0097fc5cc73b997b8124300512b189bd73df [file] [log] [blame]
#include <linux/fs.h>
#include <linux/hugetlb.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/mmzone.h>
#include <linux/proc_fs.h>
#include <linux/quicklist.h>
#include <linux/seq_file.h>
#include <linux/swap.h>
#include <linux/vmstat.h>
#include <linux/atomic.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include "internal.h"
/// M: LCA @{
#include <linux/export.h>
/// @}
void __attribute__((weak)) arch_report_meminfo(struct seq_file *m)
{
}
/// M: LCA @{
int extra_log_meminfo_lite(void)
{
#ifdef CONFIG_ZRAM
struct sysinfo i;
unsigned long committed;
struct vmalloc_info vmi;
long cached;
unsigned long pages[NR_LRU_LISTS];
int lru;
/*
* display in kilobytes.
*/
#define K(x) ((x) << (PAGE_SHIFT - 10))
si_meminfo(&i);
si_swapinfo(&i);
committed = percpu_counter_read_positive(&vm_committed_as);
cached = global_page_state(NR_FILE_PAGES) -
total_swapcache_pages - i.bufferram;
if (cached < 0)
cached = 0;
get_vmalloc_info(&vmi);
for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++)
pages[lru] = global_page_state(NR_LRU_BASE + lru);
/*
* Tagged format, for easy grepping and expansion.
*/
printk(KERN_INFO "[MTK_MSR]meminfo"\
"(%lu)" // (MemFree)
"(%lu" // (Buffers, Cache, SwapCached)
",%lu"
",%lu)"
"(%lu" // (Active, Inactive, Unevictable, MLocked)
",%lu"
",%lu"
",%lu"
",%lu"
",%lu"
",%lu"
",%lu)"
"(%lu" // (Swap total, Swap Free)
",%lu)"
"(%lu" // (Anon, Mapped)
",%lu)"
"(%lu" // (Slab KStack, PageTable)
",%lu"
",%lu)\n"
,
K(i.freeram),
K(i.bufferram),
K(cached),
K(total_swapcache_pages),
K(pages[LRU_ACTIVE_ANON] + pages[LRU_ACTIVE_FILE]),
K(pages[LRU_INACTIVE_ANON] + pages[LRU_INACTIVE_FILE]),
K(pages[LRU_ACTIVE_ANON]),
K(pages[LRU_INACTIVE_ANON]),
K(pages[LRU_ACTIVE_FILE]),
K(pages[LRU_INACTIVE_FILE]),
K(pages[LRU_UNEVICTABLE]),
K(global_page_state(NR_MLOCK)),
K(i.totalswap),
K(i.freeswap),
K(global_page_state(NR_ANON_PAGES)),
K(global_page_state(NR_FILE_MAPPED)),
K(global_page_state(NR_SLAB_RECLAIMABLE) + global_page_state(NR_SLAB_UNRECLAIMABLE)),
global_page_state(NR_KERNEL_STACK) * THREAD_SIZE >> 10,
K(global_page_state(NR_PAGETABLE))
);
#undef K
#endif /* CONFIG_ZRAM */
return 0;
}
EXPORT_SYMBOL(extra_log_meminfo_lite);
/// @}
static int meminfo_proc_show(struct seq_file *m, void *v)
{
struct sysinfo i;
unsigned long committed;
unsigned long allowed;
struct vmalloc_info vmi;
long cached;
unsigned long pages[NR_LRU_LISTS];
int lru;
/*
* display in kilobytes.
*/
#define K(x) ((x) << (PAGE_SHIFT - 10))
si_meminfo(&i);
si_swapinfo(&i);
committed = percpu_counter_read_positive(&vm_committed_as);
allowed = ((totalram_pages - hugetlb_total_pages())
* sysctl_overcommit_ratio / 100) + total_swap_pages;
cached = global_page_state(NR_FILE_PAGES) -
total_swapcache_pages - i.bufferram;
if (cached < 0)
cached = 0;
get_vmalloc_info(&vmi);
for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++)
pages[lru] = global_page_state(NR_LRU_BASE + lru);
/*
* Tagged format, for easy grepping and expansion.
*/
seq_printf(m,
"MemTotal: %8lu kB\n"
"MemFree: %8lu kB\n"
"Buffers: %8lu kB\n"
"Cached: %8lu kB\n"
"SwapCached: %8lu kB\n"
"Active: %8lu kB\n"
"Inactive: %8lu kB\n"
"Active(anon): %8lu kB\n"
"Inactive(anon): %8lu kB\n"
"Active(file): %8lu kB\n"
"Inactive(file): %8lu kB\n"
"Unevictable: %8lu kB\n"
"Mlocked: %8lu kB\n"
#ifdef CONFIG_HIGHMEM
"HighTotal: %8lu kB\n"
"HighFree: %8lu kB\n"
"LowTotal: %8lu kB\n"
"LowFree: %8lu kB\n"
#endif
#ifndef CONFIG_MMU
"MmapCopy: %8lu kB\n"
#endif
"SwapTotal: %8lu kB\n"
"SwapFree: %8lu kB\n"
"Dirty: %8lu kB\n"
"Writeback: %8lu kB\n"
"AnonPages: %8lu kB\n"
"Mapped: %8lu kB\n"
"Shmem: %8lu kB\n"
"Slab: %8lu kB\n"
"SReclaimable: %8lu kB\n"
"SUnreclaim: %8lu kB\n"
"KernelStack: %8lu kB\n"
"PageTables: %8lu kB\n"
#ifdef CONFIG_QUICKLIST
"Quicklists: %8lu kB\n"
#endif
"NFS_Unstable: %8lu kB\n"
"Bounce: %8lu kB\n"
"WritebackTmp: %8lu kB\n"
"CommitLimit: %8lu kB\n"
"Committed_AS: %8lu kB\n"
"VmallocTotal: %8lu kB\n"
"VmallocUsed: %8lu kB\n"
"VmallocChunk: %8lu kB\n"
#ifdef CONFIG_MEMORY_FAILURE
"HardwareCorrupted: %5lu kB\n"
#endif
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
"AnonHugePages: %8lu kB\n"
#endif
,
K(i.totalram),
K(i.freeram),
K(i.bufferram),
K(cached),
K(total_swapcache_pages),
K(pages[LRU_ACTIVE_ANON] + pages[LRU_ACTIVE_FILE]),
K(pages[LRU_INACTIVE_ANON] + pages[LRU_INACTIVE_FILE]),
K(pages[LRU_ACTIVE_ANON]),
K(pages[LRU_INACTIVE_ANON]),
K(pages[LRU_ACTIVE_FILE]),
K(pages[LRU_INACTIVE_FILE]),
K(pages[LRU_UNEVICTABLE]),
K(global_page_state(NR_MLOCK)),
#ifdef CONFIG_HIGHMEM
K(i.totalhigh),
K(i.freehigh),
K(i.totalram-i.totalhigh),
K(i.freeram-i.freehigh),
#endif
#ifndef CONFIG_MMU
K((unsigned long) atomic_long_read(&mmap_pages_allocated)),
#endif
K(i.totalswap),
K(i.freeswap),
K(global_page_state(NR_FILE_DIRTY)),
K(global_page_state(NR_WRITEBACK)),
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
K(global_page_state(NR_ANON_PAGES)
+ global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) *
HPAGE_PMD_NR),
#else
K(global_page_state(NR_ANON_PAGES)),
#endif
K(global_page_state(NR_FILE_MAPPED)),
K(global_page_state(NR_SHMEM)),
K(global_page_state(NR_SLAB_RECLAIMABLE) +
global_page_state(NR_SLAB_UNRECLAIMABLE)),
K(global_page_state(NR_SLAB_RECLAIMABLE)),
K(global_page_state(NR_SLAB_UNRECLAIMABLE)),
global_page_state(NR_KERNEL_STACK) * THREAD_SIZE / 1024,
K(global_page_state(NR_PAGETABLE)),
#ifdef CONFIG_QUICKLIST
K(quicklist_total_size()),
#endif
K(global_page_state(NR_UNSTABLE_NFS)),
K(global_page_state(NR_BOUNCE)),
K(global_page_state(NR_WRITEBACK_TEMP)),
K(allowed),
K(committed),
(unsigned long)VMALLOC_TOTAL >> 10,
vmi.used >> 10,
vmi.largest_chunk >> 10
#ifdef CONFIG_MEMORY_FAILURE
,atomic_long_read(&mce_bad_pages) << (PAGE_SHIFT - 10)
#endif
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
,K(global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) *
HPAGE_PMD_NR)
#endif
);
hugetlb_report_meminfo(m);
arch_report_meminfo(m);
return 0;
#undef K
}
static int meminfo_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, meminfo_proc_show, NULL);
}
static const struct file_operations meminfo_proc_fops = {
.open = meminfo_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
////////////////////////////////////////////////////
//ifdef CONFIG_MT_ENG_BUILD true == under Eng.Build
//Otherwise, it should be under User-Build. (So, we would disable Kernel Memory Status Report Mechanism.)
//
//////////////////
#ifdef CONFIG_MT_ENG_BUILD
#include <asm/kmap_types.h>
#include <asm/uaccess.h>
//Kmem Log Buffer Depth
#define Log_Buffer_Depth 120 //would keep Log_Buffer_Depth lines in Buffer
#define Log_Max_Length 1024 //Per-Log would occupied Log_Max_Length bytes
static char gKMemLogBuffer[Log_Buffer_Depth][Log_Max_Length];
static int gKMemLogBufferIndex=0;
static long gKMemLogBufferCounter=0;
//Kmem Status Basic Parameters
static unsigned long kmem_status_timer_interval=1;
static unsigned long kmem_status_timer_interval_HZ=1;
//for interval
static unsigned long kmem_status_page_reclaim_counter=0;
static unsigned long kmem_status_lmk_counter=0;
static unsigned long kmem_status_oom_counter=0;
static unsigned long kmem_status_filemap_fault_counter=0;
static unsigned long kmem_status_writeback_counter=0;
//for total counter (from kernel boot-up to now.)
static unsigned long kmem_status_page_reclaim_total=0;
static unsigned long kmem_status_lmk_total=0;
static unsigned long kmem_status_oom_total=0;
static unsigned long kmem_status_filemap_fault_total=0;
static unsigned long kmem_status_writeback_total=0;
//
struct timer_list kmem_status_timer;
static int gkmem_status_timer=0;
static void kmem_status_callback(unsigned long);
struct kmem_status_data {
int count;
};
static struct kmem_status_data gkmem_status_data;
///////////
#define MAX_USER_INPUT 256
static char kmem_user_input[MAX_USER_INPUT]={0x00};
#define MAX_STATUS_MSG_SIZE 4096
static char kmem_status_msg[MAX_STATUS_MSG_SIZE]={0x00};
/////
#define MAX_READ_OUT_MSG_SIZE (MAX_STATUS_MSG_SIZE + Log_Max_Length*Log_Buffer_Depth)
static char READ_OUT_BUFFER[MAX_READ_OUT_MSG_SIZE];
static int gREAD_OUT_BUFFER_LEN=0;
//
static int kmem_user_input_len=0;
static int kmem_status_msg_len=0;
//embedded in __alloc_pages_direct_reclaim of mm/page_alloc.c
void add_kmem_status_page_reclaim_counter(void)
{kmem_status_page_reclaim_counter++;}
//embedded in lowmem_shrink of drivers/staging/android/lowmemorykiller.c
void add_kmem_status_lmk_counter(void)
{kmem_status_lmk_counter++;}
//embedded in out_of_memory of mm/oom_kill.c
void add_kmem_status_oom_counter(void)
{kmem_status_oom_counter++;}
//embedded in filemap_fault of mm/filemap.c
void add_kmem_status_filemap_fault_counter(void)
{kmem_status_filemap_fault_counter++;}
//embedded in balance_dirty_pages of mm/page-writeback.c
void add_kmem_status_writeback_counter(void)
{kmem_status_writeback_counter++;}
//
static void kmem_status_prepare_msg(void)
{
//add to total
kmem_status_page_reclaim_total+=kmem_status_page_reclaim_counter;
kmem_status_lmk_total+=kmem_status_lmk_counter;
kmem_status_oom_total+=kmem_status_oom_counter;
kmem_status_filemap_fault_total+=kmem_status_filemap_fault_counter;
kmem_status_writeback_total+=kmem_status_writeback_counter;
//prepare the user space print-out log.
snprintf(kmem_status_msg,MAX_STATUS_MSG_SIZE,
"Kernel Memory Status Report (/proc/kmem_interval)\nTimer Interval:%ld ms (%ld HZ)\nPage Reclaim Counter:%ld(%ld)\nLMK Counter:%ld(%ld)\nOOM Counter:%ld(%ld)\nFileMap Fault Counter:%ld(%ld)\nWriteBack Counter:%ld(%ld)\n==============================\n",kmem_status_timer_interval,kmem_status_timer_interval_HZ,kmem_status_page_reclaim_counter,kmem_status_page_reclaim_total,kmem_status_lmk_counter,kmem_status_lmk_total,kmem_status_oom_counter,kmem_status_oom_total,kmem_status_filemap_fault_counter,kmem_status_filemap_fault_total,kmem_status_writeback_counter,kmem_status_writeback_total);
kmem_status_msg_len=strlen(kmem_status_msg);
//printk in console log. by default.
//Chnage prink to internal buffer by Julius' request.
snprintf(gKMemLogBuffer[gKMemLogBufferIndex],Log_Max_Length-2,"(%ld) : Jiffies:%lu,PRC:%ld(%ld),LMKC:%ld(%ld),OOMC:%ld(%ld),FMFC:%ld(%ld),WBC:%ld(%ld)\n",gKMemLogBufferCounter,jiffies,kmem_status_page_reclaim_counter,kmem_status_page_reclaim_total,kmem_status_lmk_counter,kmem_status_lmk_total,kmem_status_oom_counter,kmem_status_oom_total,kmem_status_filemap_fault_counter,kmem_status_filemap_fault_total,kmem_status_writeback_counter,kmem_status_writeback_total);
gKMemLogBufferIndex++;
if(gKMemLogBufferIndex>=Log_Buffer_Depth)
gKMemLogBufferIndex=0;
//
gKMemLogBufferCounter++;
if(gKMemLogBufferCounter > 0x7fff0000)
gKMemLogBufferCounter=0;
//reset interval to zero.
kmem_status_page_reclaim_counter=0;
kmem_status_lmk_counter=0;
kmem_status_oom_counter=0;
kmem_status_filemap_fault_counter=0;
kmem_status_writeback_counter=0;
}
static void prepare_read_out_buffer(void)
{
int i,index;
memset(READ_OUT_BUFFER,0x00,MAX_READ_OUT_MSG_SIZE);
gREAD_OUT_BUFFER_LEN=0;
strcat(READ_OUT_BUFFER,kmem_status_msg);
index=gKMemLogBufferIndex;
if(index>=(Log_Buffer_Depth-1))
index=0;
else
index++;
for(i=0;i<(Log_Buffer_Depth-1);i++)
{
if(gKMemLogBuffer[index][0]==0)
{
//break;
}
else
{
strcat(READ_OUT_BUFFER,gKMemLogBuffer[index]);
}
if(index>=(Log_Buffer_Depth-1))
index=0;
else
index++;
}
////
//
gREAD_OUT_BUFFER_LEN=strlen(READ_OUT_BUFFER);
}
//
static void kmem_status_callback(unsigned long data)
{
//printk("kmem_status_callback %s(): %d\n", __FUNCTION__, dp->count);
kmem_status_prepare_msg();
//
if(kmem_status_timer_interval_HZ>0)
mod_timer(&kmem_status_timer, jiffies + kmem_status_timer_interval_HZ);//kmem_status_timer_interval * HZ);
}
static void kmem_status_init_timer(void)
{
if(gkmem_status_timer!=0)
{
del_timer(&kmem_status_timer);
//return;
}
if(kmem_status_timer_interval_HZ<=0)
return;
gkmem_status_timer=1;
init_timer(&kmem_status_timer);
//
kmem_status_timer.expires = jiffies + kmem_status_timer_interval_HZ;//kmem_status_timer_interval * HZ;
kmem_status_timer.function = &kmem_status_callback;
kmem_status_timer.data = (unsigned long) &gkmem_status_data;
add_timer(&kmem_status_timer);
}
static void kmem_status_fini_timer(void)
{
if(gkmem_status_timer==0)
return;
gkmem_status_timer=0;
del_timer(&kmem_status_timer);
}
static int kmem_status_proc_show(struct seq_file *m, void *v)
{
seq_printf(m,
"kmem status\n");
return 0;
}
static int kmem_status_proc_open(struct inode *inode, struct file *file)
{
return single_open(file,kmem_status_proc_show, NULL);
}
//
static ssize_t kmem_status_proc_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int vSize;
int offset=*ppos + filp->f_pos;
if(offset==0)
prepare_read_out_buffer();
//printk("kmem_status_proc_read:filp->f_pos:%ld_%xh offset:%ld",filp->f_pos,filp->f_pos,offset);
if(offset >=gREAD_OUT_BUFFER_LEN)//kmem_user_input_len)
{
printk("kmem_status_proc_read offset:%d_gREAD_OUT_BUFFER_LEN:%d",offset,gREAD_OUT_BUFFER_LEN);
return 0;
}
//
if(count > gREAD_OUT_BUFFER_LEN-offset)
vSize=gREAD_OUT_BUFFER_LEN-offset;
else
vSize=count;
if(copy_to_user(buf,READ_OUT_BUFFER+offset,vSize))
{
printk("kmem_status_proc_read error#1\n");
return 0;
}
//
*ppos+=vSize;// offset;
return vSize;
}
static void prepare_init_msg(void)
{
snprintf(kmem_status_msg,MAX_STATUS_MSG_SIZE,"Kernel memory status report daemon is disable now. \nPlease echo interval (in ms) to /proc/kmem_interval to enable it.\n eq. echo \"1000\" > /proc/kmem_interval \n\n");
kmem_status_msg_len=strlen(kmem_status_msg);
}
static ssize_t kmem_status_proc_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
int interval;
int vSize;
int offset=*ppos;
if(count > MAX_USER_INPUT-1)
vSize=MAX_USER_INPUT-1;
else
vSize=count;
memset(kmem_user_input,0,MAX_USER_INPUT);
memcpy(kmem_user_input,buf,vSize);
kmem_user_input_len=vSize;
offset=vSize;
*ppos=offset;
///
interval =simple_strtol(kmem_user_input, NULL, 0);//atoi(kmem_user_input);
if(interval > 0)
{
//printk("kmem interval:%ld init timer",interval);
kmem_status_init_timer();
}
else
{
interval=0;
//printk("kmem interval:%ld fini timer",interval);
kmem_status_fini_timer();
//
prepare_init_msg();
}
kmem_status_timer_interval=interval;
if(interval>0)
{
kmem_status_timer_interval_HZ = interval/10;
if(kmem_status_timer_interval_HZ==0)
kmem_status_timer_interval_HZ=1;
}
else
{
kmem_status_timer_interval_HZ=0;
}
return vSize;
}
static const struct file_operations kmem_status_proc_fops = {
.open = kmem_status_proc_open, //single_open, //kmem_status_proc_open,
.write = kmem_status_proc_write,
.read = kmem_status_proc_read,
.llseek = seq_lseek, //kmem_status_proc_write,
.release = single_release,
};
static int __init proc_kmem_status(void)
{
prepare_init_msg();
memset(gKMemLogBuffer,0x00,Log_Buffer_Depth*Log_Max_Length);
gKMemLogBufferIndex=0;
gKMemLogBufferCounter=0;
memset(READ_OUT_BUFFER,0x00,MAX_READ_OUT_MSG_SIZE);
gREAD_OUT_BUFFER_LEN=0;
proc_create("kmem_interval", 0, NULL, &kmem_status_proc_fops);
return 0;
}
#endif
//////////////////////////////////////////////////////
static int __init proc_meminfo_init(void)
{
proc_create("meminfo", 0, NULL, &meminfo_proc_fops);
#ifdef CONFIG_MT_ENG_BUILD
proc_kmem_status();
#endif
return 0;
}
module_init(proc_meminfo_init);