#undef SHOW_BUFFERS
/* Kernel includes */
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>
#include <asm/page.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#ifdef SHOW_BUFFERS
#include <linux/fs.h>
#include <linux/pagemap.h>
#endif
#ifdef SHOW_BUFFERS
#define NUMFIELDS 7
#else
#define NUMFIELDS 5
#endif
/* 4K page size but our output routines use some slack for overruns */
#define PROC_BLOCK_SIZE (3*1024)
ssize_t memstat_read(struct file *, char *, size_t, loff_t *);
int memstat_get_info(char *);
/* The string labels for the contents of memstat */
char LABELS[NUMFIELDS][20] = {
"Total:",
"Used:",
"Free:",
"1Count:",
"Shared:"
#ifdef SHOW_BUFFERS
, "Buffers:",
"Cache:"
#endif
};
/* Structure for passing the values from si_memstat */
typedef struct {
unsigned int total;
unsigned int used;
unsigned int free;
unsigned int one_count;
unsigned int shared;
#ifdef SHOW_BUFFERS
unsigned int buffers;
unsigned int cache;
#endif
} memstat;
static struct file_operations proc_memstat_operations = {
NULL, /* mem_lseek */
memstat_read,
NULL, /* mem_write */
NULL, /* mem_readdir */
NULL, /* mem_poll */
NULL, /* mem_ioctl */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* can't fsync */
};
struct inode_operations proc_memstat_inode_operations = {
&proc_memstat_operations, /* default base directory file-ops */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
static struct proc_dir_entry memstat_proc_dir_entry = {
4000, 7, "memstat",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_memstat_inode_operations
};
/* Construct the values from global kernel variables */
void si_memstat(memstat *val)
{
int i;
unsigned int *tmp;
#ifdef SHOW_BUFFERS
val->cache = page_cache_size * PAGE_SIZE;
val->buffers = buffermem;
#endif
val->total = val->used = val->free = val->one_count
= val->shared = 0;
/* Loop through all pages tallying data */
i = max_mapnr;
while (i-- > 0) {
if (PageReserved(mem_map+i))
continue;
val->total++;
if (atomic_read(&mem_map[i].count)) {
val->used++;
if (atomic_read(&mem_map[i].count)
> (mem_map[i].inode ? 2 : 1))
val->shared++;
else
val->one_count++;
}
else
val->free++;
}
for (i = 0, tmp = (unsigned int *)val ; i < NUMFIELDS ; i++, tmp++) {
*tmp <<= PAGE_SHIFT;
*tmp /= 1024;
}
return;
}
/*
* put si_memstat into a char buffer in the way /proc wants it
* To be honest, this is copied almost verbatim from the network
* code.
*/
int memstat_get_info(char *page)
{
int len,size;
off_t pos, begin;
memstat Pages;
unsigned int *p, i;
len = pos = begin = 0;
size = 0; /* sprintf(buffer, "-- Memory information --\n"); */
si_memstat(&Pages);
p = (unsigned int *) &Pages;
pos += size;
len += size;
/* Add Pages to buffer */
/* Add only how much is needed (length), but if offset is 0 */
/* we have to start offset bytes into the output. This */
/* approach re-does everything, discarding (copying over) */
/* characters until it gets up to offset */
for (i=0; i < NUMFIELDS; i++) {
size = sprintf(page+len, "%s %d\n", LABELS[i], p[i]);
len += size;
}
return len;
}
/*
static long memstat_read(struct inode *inode, struct file *file,
char *buf, unsigned long count)
*/
ssize_t memstat_read(struct file * file, char * buf,
size_t count, loff_t *ppos_old)
{
unsigned long page;
char *start;
ssize_t length;
ssize_t end;
struct proc_dir_entry * dp;
loff_t *ppos = &file->f_pos;
struct inode * inode = file->f_dentry->d_inode;
if (count > PROC_BLOCK_SIZE)
count = PROC_BLOCK_SIZE;
if (count < 0)
return -EINVAL;
if (!(page = __get_free_page(GFP_KERNEL)))
return -ENOMEM;
start = NULL;
dp = (struct proc_dir_entry *) inode->u.generic_ip;
if (dp->get_info)
length = dp->get_info((char *)page, &start, *ppos,
count, 0);
else
length = memstat_get_info((char *) page);
if (length < 0) {
free_page(page);
return length;
}
if (start != NULL) {
/* We have had block-adjusting processing! */
copy_to_user(buf, start, length);
*ppos += length;
count = length;
} else {
/* Static 4kB (or whatever) block capacity */
if (*ppos >= length) {
free_page(page);
return 0;
}
if (count + *ppos > length)
count = length - *ppos;
end = count + *ppos;
copy_to_user(buf, (char *) page + *ppos, count);
*ppos = end;
}
free_page(page);
return count;
}
/* required procedure called upon module initialization */
/* Installs the /proc handler */
int init_module( void )
{
/* MOD_INC_USE_COUNT; */
proc_register(&proc_root, &memstat_proc_dir_entry);
return 0;
}
/* required procedure called upon module removal */
/* Removes the /proc handler */
void cleanup_module( void )
{
proc_unregister(&proc_root,4000);
/* MOD_DEC_USE_COUNT; */
}
syntax highlighted by Code2HTML, v. 0.9.1