Kmalloc improvements.

Rogier Wolff (R.E.Wolff@bitwizard.nl)
Thu, 16 May 1996 00:36:14 +0200 (MET DST)


I've upgraded my kmalloc-improvement patch to the pre-2.0.4 kernel.

Linus: I think that this is ready to go into the mainstream kernel.
If you disagree, please tell me why.

This patch:
- gets rid of the 8 byte header in front of every piece of memory
that kmalloc handles.
- makes kmalloc about 80 ns faster on a 100MHz pentium. :-)
- Adds a /proc/malloc file that shows the kmalloc statistics
- Adds a /proc/dmalloc file that shows when kmalloc was called,
and from what file. (configurable)

A little more explanation can be found at:
http://www.BitWizard.nl/kmalloc.html

Roger.
Here is the patch:
------------------------------------------------------------------
diff -ur linux-orig/CREDITS linux/CREDITS
--- linux-orig/CREDITS Wed May 15 23:27:47 1996
+++ linux/CREDITS Tue May 14 20:29:30 1996
@@ -1350,7 +1350,8 @@
S: Finland

N: Roger E. Wolff
-E: wolff@dutecai.et.tudelft.nl
+E: R.E.Wolff@BitWizard.nl
+W: http://www.BitWizard.nl/wolff/
D: Written kmalloc/kfree
S: Oosterstraat 23
S: 2611 TT Delft
diff -ur linux-orig/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-orig/Documentation/Configure.help Wed May 15 23:27:47 1996
+++ linux/Documentation/Configure.help Tue May 14 20:29:30 1996
@@ -3301,6 +3301,12 @@
enabled "Kernel profiling support", you must be a kernel hacker and
hence you know what this is about :-)

+Debug kmalloc/kfree
+CONFIG_DEBUG_MALLOC
+ This allows you to view which kernel source files allocate memory.
+ The results can be viewed in /proc/dmalloc. It is mostly interesting
+ to kernel developers that want to find a memory leak.
+
ISDN subsystem
CONFIG_ISDN
This allows you to use an ISDN-card for networking connections and
diff -ur linux-orig/arch/i386/config.in linux/arch/i386/config.in
--- linux-orig/arch/i386/config.in Wed May 15 23:27:50 1996
+++ linux/arch/i386/config.in Tue May 14 20:29:30 1996
@@ -106,7 +106,7 @@
mainmenu_option next_comment
comment 'Kernel hacking'

-#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
+bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
bool 'Kernel profiling support' CONFIG_PROFILE
if [ "$CONFIG_PROFILE" = "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
diff -ur linux-orig/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c
--- linux-orig/drivers/scsi/53c7,8xx.c Wed May 15 23:26:27 1996
+++ linux/drivers/scsi/53c7,8xx.c Tue May 14 23:35:50 1996
@@ -273,6 +273,11 @@
};
#endif

+#ifdef CONFIG_DEBUG_MALLOC
+#undef kfree
+#define kfree deb_kfree
+#endif
+
static int check_address (unsigned long addr, int size);
static void dump_events (struct Scsi_Host *host, int count);
static Scsi_Cmnd * return_outstanding_commands (struct Scsi_Host *host,
@@ -6357,6 +6362,13 @@
}

#ifdef MODULE
+
+#ifdef CONFIG_DEBUG_MALLOC
+#undef kfree
+#define kfree(a) deb_kfree(__FILE__,__LINE__,a)
+#endif
+
+
int
NCR53c7x0_release(struct Scsi_Host *host) {
struct NCR53c7x0_hostdata *hostdata =
@@ -6387,7 +6399,8 @@
*/
cmd->next = NULL;
if (cmd->free)
- cmd->free ((void *) cmd->real, cmd->size);
+ /* Why is a pointer to kfree needed here???? */
+ kfree (cmd->real);
}
if (hostdata->num_cmds)
printk ("scsi%d : leaked %d NCR53c7x0_cmd structures\n",
diff -ur linux-orig/fs/proc/array.c linux/fs/proc/array.c
--- linux-orig/fs/proc/array.c Wed May 15 23:25:57 1996
+++ linux/fs/proc/array.c Tue May 14 20:30:25 1996
@@ -51,8 +51,10 @@
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)

-#ifdef CONFIG_DEBUG_MALLOC
+/* Malloc externals.... */
int get_malloc(char * buffer);
+#ifdef CONFIG_DEBUG_MALLOC
+int read_dmalloc (struct file * file, char * buf, int count);
#endif


@@ -995,10 +997,8 @@
case PROC_VERSION:
return get_version(page);

-#ifdef CONFIG_DEBUG_MALLOC
case PROC_MALLOC:
return get_malloc(page);
-#endif

#ifdef CONFIG_MODULES
case PROC_MODULES:
@@ -1167,10 +1167,18 @@
if (count < 0)
return -EINVAL;

- switch (type) {
- case PROC_PID_MAPS:
- return read_maps(pid, file, buf, count);
- }
+ if (pid)
+ switch (type) {
+ case PROC_PID_MAPS:
+ return read_maps(pid, file, buf, count);
+ }
+ else
+ switch (type) {
+#ifdef CONFIG_DEBUG_MALLOC
+ case PROC_DMALLOC: /* Not PID */
+ return read_dmalloc(file, buf, count);
+#endif
+ }
return -EINVAL;
}

diff -ur linux-orig/fs/proc/inode.c linux/fs/proc/inode.c
--- linux-orig/fs/proc/inode.c Wed May 15 23:25:25 1996
+++ linux/fs/proc/inode.c Tue May 14 20:30:25 1996
@@ -180,6 +180,12 @@
inode->i_op = &proc_kcore_inode_operations;
inode->i_size = (MAP_NR(high_memory) << PAGE_SHIFT) + PAGE_SIZE;
break;
+#ifdef CONFIG_DEBUG_MALLOC
+ case PROC_DMALLOC:
+ inode->i_mode = S_IFREG | S_IRUGO;
+ inode->i_op = &proc_arraylong_inode_operations;
+ break;
+#endif
case PROC_PROFILE:
inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
inode->i_op = &proc_profile_inode_operations;
diff -ur linux-orig/fs/proc/root.c linux/fs/proc/root.c
--- linux-orig/fs/proc/root.c Wed May 15 23:25:57 1996
+++ linux/fs/proc/root.c Tue May 14 20:30:25 1996
@@ -297,9 +297,14 @@
proc_register(&proc_root, &proc_scsi);
proc_register(&proc_root, &proc_sys_root);

-#ifdef CONFIG_DEBUG_MALLOC
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_MALLOC, 6, "malloc",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ });
+
+#ifdef CONFIG_DEBUG_MALLOC
+ proc_register(&proc_root, &(struct proc_dir_entry) {
+ PROC_DMALLOC, 7, "dmalloc",
S_IFREG | S_IRUGO, 1, 0, 0,
});
#endif
diff -ur linux-orig/include/linux/malloc.h linux/include/linux/malloc.h
--- linux-orig/include/linux/malloc.h Fri Mar 1 09:37:20 1996
+++ linux/include/linux/malloc.h Tue May 14 23:03:46 1996
@@ -1,11 +1,24 @@
#ifndef _LINUX_MALLOC_H
#define _LINUX_MALLOC_H

+#include <linux/config.h>
#include <linux/mm.h>

-void * kmalloc(unsigned int size, int priority);
+#ifdef CONFIG_DEBUG_MALLOC
+#define kmalloc(a,b) deb_kmalloc(__FILE__,__LINE__,a,b)
+#define kfree(a) deb_kfree(__FILE__,__LINE__,a)
+
+void *deb_kmalloc (const char *file, unsigned short line, size_t size, int priority);
+void deb_kfree (const char *file, unsigned short line,void *obj);
+
+
+#else /* !debug */
+
+void *kmalloc(unsigned int size, int priority);
void kfree(void * obj);

-#define kfree_s(a,b) kfree(a)
+#endif

#endif /* _LINUX_MALLOC_H */
+
+#define kfree_s(a,b) kfree(a)
diff -ur linux-orig/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- linux-orig/include/linux/proc_fs.h Wed May 15 23:25:58 1996
+++ linux/include/linux/proc_fs.h Tue May 14 23:03:47 1996
@@ -25,6 +25,7 @@
PROC_NET,
PROC_SCSI,
PROC_MALLOC,
+ PROC_DMALLOC,
PROC_KCORE,
PROC_MODULES,
PROC_STAT,
diff -ur linux-orig/kernel/ksyms.c linux/kernel/ksyms.c
--- linux-orig/kernel/ksyms.c Wed May 15 23:27:53 1996
+++ linux/kernel/ksyms.c Tue May 14 20:30:25 1996
@@ -127,8 +127,13 @@
/* internal kernel memory management */
X(__get_free_pages),
X(free_pages),
+#ifdef CONFIG_DEBUG_MALLOC
+ X(deb_kmalloc),
+ X(deb_kfree),
+#else
X(kmalloc),
X(kfree),
+#endif
X(vmalloc),
X(vremap),
X(vfree),
diff -ur linux-orig/mm/kmalloc.c linux/mm/kmalloc.c
--- linux-orig/mm/kmalloc.c Sun Apr 14 13:53:04 1996
+++ linux/mm/kmalloc.c Tue May 14 20:30:25 1996
@@ -4,6 +4,7 @@
* Copyright (C) 1991, 1992 Linus Torvalds & Roger Wolff.
*
* Written by R.E. Wolff Sept/Oct '93.
+ * R.E.Wolff@BitWizard.nl
*
*/

@@ -22,49 +23,79 @@
/* Define this if you want slow routines that try to trip errors */
#undef SADISTIC_KMALLOC

-/* Private flags. */

-#define MF_USED 0xffaa0055
-#define MF_DMA 0xff00aa55
-#define MF_FREE 0x0055ffaa
+#ifdef TEST
+#include <stdio.h>
+#include <stdlib.h>
+
+#define printk printf
+#define panic printf
+
+#undef cli
+#define cli()
+
+#undef restore_flags
+#define restore_flags(flags)
+
+#ifdef PAGE_SIZE_OVERRIDE
+#undef PAGE_SIZE
+#define PAGE_SIZE PAGE_SIZE_OVERRIDE
+#endif
+
+
+unsigned long jiffies = 0;
+unsigned long intr_count =0;
+

+unsigned long __get_free_pages(int pri,unsigned long order,int dma)
+{
+ return (unsigned long) malloc (PAGE_SIZE << order);
+}
+
+void free_pages (unsigned long addr,unsigned long order)
+{
+ free ((void *)addr);
+}
+
+#endif

/*
* Much care has gone into making these routines in this file reentrant.
*
- * The fancy bookkeeping of nbytesmalloced and the like are only used to
- * report them to the user (oooohhhhh, aaaaahhhhh....) are not
- * protected by cli(). (If that goes wrong. So what?)
- *
* These routines restore the interrupt status to allow calling with ints
* off.
*/

/*
- * A block header. This is in front of every malloc-block, whether free or not.
+ * A block header. This is in front of every malloc-block,
+ * when debugging kmalloc is compiled in....
*/
struct block_header {
- unsigned long bh_flags;
- union {
- unsigned long ubh_length;
- struct block_header *fbh_next;
- } vp;
+#ifdef CONFIG_DEBUG_MALLOC
+ struct block_header *dbh_next, *dbh_prev;
+ long dbh_time;
+ const char *dbh_file;
+ short dbh_line;
+ int dbh_length;
+#endif
};


-#define bh_length vp.ubh_length
-#define bh_next vp.fbh_next
#define BH(p) ((struct block_header *)(p))

+#ifdef CONFIG_DEBUG_MALLOC
+static struct block_header first_bh = {&first_bh,&first_bh,0,NULL,0};
+#endif

/*
* The page descriptor is at the front of every page that malloc has in use.
*/
struct page_descriptor {
struct page_descriptor *next;
- struct block_header *firstfree;
- int order;
int nfree;
+ short order;
+ short flags;
+ unsigned long bitmap[1];
};


@@ -78,131 +109,104 @@
struct size_descriptor {
struct page_descriptor *firstfree;
struct page_descriptor *dmafree; /* DMA-able memory */
+ int offset;
int nblocks;
+ unsigned long gfporder; /* number of pages in the area required */

int nmallocs;
int nfrees;
int nbytesmalloced;
int npages;
- unsigned long gfporder; /* number of pages in the area required */
};

/*
* For now it is unsafe to allocate bucket sizes between n and
* n-sizeof(page_descriptor) where n is PAGE_SIZE * any power of two
*
- * The blocksize and sizes arrays _must_ match!
+ * The current blocksizes array wastes memory if you want to allocate
+ * between 4080 and 4088 bytes when PAGE_SIZE == 8192. I don't care enough
+ * to add a different array for that case.
*/
-#if PAGE_SIZE == 4096
-static const unsigned int blocksize[] = {
- 32,
- 64,
- 128,
- 252,
- 508,
- 1020,
- 2040,
- 4096 - 16,
- 8192 - 16,
- 16384 - 16,
- 32768 - 16,
- 65536 - 16,
+
+#define NSIZES 14
+static const unsigned int blocksize[NSIZES] = {
+ 32,
+ 64,
+ 128,
+ 256 - 4,
+ 512 - 4,
+ 1024 - 4,
+ 2048 - 8,
+ 4096 - 16,
+ 8192 - 16,
+ 16384 - 16,
+ 32768 - 16,
+ 65536 - 16,
131072 - 16,
0
};

-static struct size_descriptor sizes[] =
+static struct size_descriptor sizes[NSIZES] =
{
- {NULL, NULL, 127, 0, 0, 0, 0, 0},
- {NULL, NULL, 63, 0, 0, 0, 0, 0},
- {NULL, NULL, 31, 0, 0, 0, 0, 0},
- {NULL, NULL, 16, 0, 0, 0, 0, 0},
- {NULL, NULL, 8, 0, 0, 0, 0, 0},
- {NULL, NULL, 4, 0, 0, 0, 0, 0},
- {NULL, NULL, 2, 0, 0, 0, 0, 0},
- {NULL, NULL, 1, 0, 0, 0, 0, 0},
- {NULL, NULL, 1, 0, 0, 0, 0, 1},
- {NULL, NULL, 1, 0, 0, 0, 0, 2},
- {NULL, NULL, 1, 0, 0, 0, 0, 3},
- {NULL, NULL, 1, 0, 0, 0, 0, 4},
- {NULL, NULL, 1, 0, 0, 0, 0, 5},
- {NULL, NULL, 0, 0, 0, 0, 0, 0}
-};
-#elif PAGE_SIZE == 8192
-static const unsigned int blocksize[] = {
- 64,
- 128,
- 248,
- 504,
- 1016,
- 2040,
- 4080,
- 8192 - 32,
- 16384 - 32,
- 32768 - 32,
- 65536 - 32,
- 131072 - 32,
- 262144 - 32,
- 0
+ {NULL, NULL, 0, 0, 0, 0, 0, 0, 0},
};

-struct size_descriptor sizes[] =
-{
- {NULL, NULL, 127, 0, 0, 0, 0, 0},
- {NULL, NULL, 63, 0, 0, 0, 0, 0},
- {NULL, NULL, 31, 0, 0, 0, 0, 0},
- {NULL, NULL, 16, 0, 0, 0, 0, 0},
- {NULL, NULL, 8, 0, 0, 0, 0, 0},
- {NULL, NULL, 4, 0, 0, 0, 0, 0},
- {NULL, NULL, 2, 0, 0, 0, 0, 0},
- {NULL, NULL, 1, 0, 0, 0, 0, 0},
- {NULL, NULL, 1, 0, 0, 0, 0, 1},
- {NULL, NULL, 1, 0, 0, 0, 0, 2},
- {NULL, NULL, 1, 0, 0, 0, 0, 3},
- {NULL, NULL, 1, 0, 0, 0, 0, 4},
- {NULL, NULL, 1, 0, 0, 0, 0, 5},
- {NULL, NULL, 0, 0, 0, 0, 0, 0}
-};
-#else
-#error you need to make a version for your pagesize
-#endif

-#define NBLOCKS(order) (sizes[order].nblocks)
-#define BLOCKSIZE(order) (blocksize[order])
+#define NBLOCKS(order) (sizes[order].nblocks)
+#define OFFSET(order) (sizes[order].offset)
+#define BLOCKSIZE(order) (blocksize[order])
#define AREASIZE(order) (PAGE_SIZE<<(sizes[order].gfporder))


long kmalloc_init(long start_mem, long end_mem)
{
int order;
+ int gfp_order;
+ int area_size;

-/*
- * Check the static info array. Things will blow up terribly if it's
- * incorrect. This is a late "compile time" check.....
- */
- for (order = 0; BLOCKSIZE(order); order++) {
- if ((NBLOCKS(order) * BLOCKSIZE(order) + sizeof(struct page_descriptor)) >
- AREASIZE(order)) {
- printk("Cannot use %d bytes out of %d in order = %d block mallocs\n",
- (int) (NBLOCKS(order) * BLOCKSIZE(order) +
- sizeof(struct page_descriptor)),
- (int) AREASIZE(order),
- BLOCKSIZE(order));
- panic("This only happens if someone messes with kmalloc");
+ gfp_order = 0;
+ area_size = PAGE_SIZE;
+ for (order=0;order<NSIZES-1;order++) {
+ if (BLOCKSIZE(order) > area_size) {
+ area_size <<= 1;
+ gfp_order ++;
}
+ NBLOCKS(order) = (area_size - sizeof (struct page_descriptor)) /
+ BLOCKSIZE(order);
+ OFFSET(order) = area_size - NBLOCKS(order) * BLOCKSIZE(order);
+ sizes[order].gfporder = gfp_order;
}
+
return start_mem;
}


+#ifndef CONFIG_DEBUG_MALLOC
void *kmalloc(size_t size, int priority)
+#else
+void *deb_kmalloc (const char *file, unsigned short line, size_t size, int priority)
+#endif
{
unsigned long flags;
- unsigned long type;
- int order, dma;
+ int order, dma, index;
struct block_header *p;
struct page_descriptor *page, **pg;

+/* Sanity check... */
+ if (intr_count && priority != GFP_ATOMIC) {
+ static int count = 0;
+ if (++count < 5) {
+ if (__builtin_return_address(0) < (void *) 0x1000)
+ printk ("kmalloc called nonatomically.\n"
+ "recompile kmalloc.c without -fno-frame-pointer "
+ "for valid return address.\n");
+ else
+ printk("kmalloc called nonatomically from interrupt %p\n",
+ __builtin_return_address(0));
+ priority = GFP_ATOMIC;
+ }
+ }
+
/* Get order */
order = 0;
{
@@ -219,46 +223,18 @@
}
}

- dma = 0;
- type = MF_USED;
- pg = &sizes[order].firstfree;
- if (priority & GFP_DMA) {
- dma = 1;
- type = MF_DMA;
- pg = &sizes[order].dmafree;
- }
-
+ dma = priority & GFP_DMA;
priority &= GFP_LEVEL_MASK;
+ pg = dma? &(sizes[order].dmafree) : &(sizes[order].firstfree);

-/* Sanity check... */
- if (intr_count && priority != GFP_ATOMIC) {
- static int count = 0;
- if (++count < 5) {
- printk("kmalloc called nonatomically from interrupt %p\n",
- __builtin_return_address(0));
- priority = GFP_ATOMIC;
- }
- }

save_flags(flags);
cli();
page = *pg;
- if (page) {
- p = page->firstfree;
- if (p->bh_flags != MF_FREE)
- goto not_free_on_freelist;
- goto found_it;
- }
-
- /* We need to get a new free page..... */
- /* This can be done with ints on: This is private to this invocation */
- restore_flags(flags);
-
- {
- int i, sz;
-
- /* sz is the size of the blocks we're dealing with */
- sz = BLOCKSIZE(order);
+ if (!page) {
+ /* We need to get a new free page..... */
+ /* This can be done with ints on: This is private to this invocation */
+ restore_flags(flags);

page = (struct page_descriptor *) __get_free_pages(priority,
sizes[order].gfporder, dma);
@@ -267,38 +243,44 @@
goto no_free_page;
sizes[order].npages++;

- /* Loop for all but last block: */
- for (i = NBLOCKS(order), p = BH(page + 1); i > 1; i--, p = p->bh_next) {
- p->bh_flags = MF_FREE;
- p->bh_next = BH(((long) p) + sz);
- }
- /* Last block: */
- p->bh_flags = MF_FREE;
- p->bh_next = NULL;
-
page->order = order;
page->nfree = NBLOCKS(order);
- p = BH(page+1);
+ page->flags = dma;
+ memset (page->bitmap,0,(sizes[order].nblocks+7) / 8);
+
+ /*
+ * Now we're going to muck with the "global" freelist
+ * for this size: this should be uninterruptible
+ */
+ cli();
+ page->next = *pg;
+ *pg = page;
}

- /*
- * Now we're going to muck with the "global" freelist
- * for this size: this should be uninterruptible
- */
- cli();
- page->next = *pg;
- *pg = page;

-found_it:
- page->firstfree = p->bh_next;
+ index = find_first_zero_bit (page->bitmap,sizes[order].nblocks);
+
+ p = BH(((char *)page) + sizes[order].offset + index * BLOCKSIZE (order));
+ if (set_bit (index,page->bitmap)) {
+ printk ("kmalloc: interal error. Bit already set.\n");
+ return NULL;
+ }
page->nfree--;
if (!page->nfree)
*pg = page->next;
- restore_flags(flags);
+#ifdef CONFIG_DEBUG_MALLOC
+ p->dbh_file = file;
+ p->dbh_line = line;
+ p->dbh_time = jiffies;
+ p->dbh_next = &first_bh;
+ p->dbh_prev = first_bh.dbh_prev;
+ p->dbh_length = size;
+ first_bh.dbh_prev->dbh_next = p;
+ first_bh.dbh_prev = p;
+#endif
sizes[order].nmallocs++;
sizes[order].nbytesmalloced += size;
- p->bh_flags = type; /* As of now this block is officially in use */
- p->bh_length = size;
+ restore_flags(flags);
#ifdef SADISTIC_KMALLOC
memset(p+1, 0xf0, size);
#endif
@@ -309,22 +291,21 @@
static unsigned long last = 0;
if (priority != GFP_BUFFER && (last + 10 * HZ < jiffies)) {
last = jiffies;
- printk("Couldn't get a free page.....\n");
+ printk("kmalloc: couldn't get a free page.....\n");
}
return NULL;
}
-
-not_free_on_freelist:
- restore_flags(flags);
- printk("Problem: block on freelist at %08lx isn't free.\n", (long) p);
- return NULL;
}

+
+#ifndef CONFIG_DEBUG_MALLOC
void kfree(void *ptr)
+#else
+void deb_kfree (const char *file, unsigned short line, void *ptr)
+#endif
{
- int size;
unsigned long flags;
- int order;
+ int order, dma, index;
register struct block_header *p;
struct page_descriptor *page, **pg;

@@ -333,31 +314,33 @@
p = ((struct block_header *) ptr) - 1;
page = PAGE_DESC(p);
order = page->order;
- pg = &sizes[order].firstfree;
- if (p->bh_flags == MF_DMA) {
- p->bh_flags = MF_USED;
- pg = &sizes[order].dmafree;
- }

if ((order < 0) ||
(order >= sizeof(sizes) / sizeof(sizes[0])) ||
- (((long) (page->next)) & ~PAGE_MASK) ||
- (p->bh_flags != MF_USED)) {
- printk("kfree of non-kmalloced memory: %p, next= %p, order=%d\n",
+ (((long) (page->next)) & ~PAGE_MASK)) {
+ printk("kfree: free of non-kmalloced memory: %p, next= %p, order=%d\n",
p, page->next, page->order);
return;
}
- size = p->bh_length;
- p->bh_flags = MF_FREE; /* As of now this block is officially free */
+
+ dma = page->flags & GFP_DMA;
+ pg = dma?&sizes[order].dmafree : &sizes[order].firstfree;
+ index = ((char *)ptr - ((char *)page + sizes[order].offset)) /
+ BLOCKSIZE(order);
+
#ifdef SADISTIC_KMALLOC
memset(p+1, 0xe0, size);
#endif
save_flags(flags);
cli();
- p->bh_next = page->firstfree;
- page->firstfree = p;
+ clear_bit (index,page->bitmap);
page->nfree++;

+#ifdef CONFIG_DEBUG_MALLOC
+ p->dbh_next->dbh_prev = p->dbh_prev;
+ p->dbh_prev->dbh_next = p->dbh_next;
+#endif
+
if (page->nfree == 1) {
/* Page went from full to one free block: put it on the freelist. */
page->next = *pg;
@@ -368,8 +351,8 @@
for (;;) {
struct page_descriptor *tmp = *pg;
if (!tmp) {
- printk("Ooops. page %p doesn't show on freelist.\n", page);
- break;
+ printk("kfree: page %p doesn't show on freelist.\n", page);
+ goto aargh; /* Better waste a page than crash the system */
}
if (tmp == page) {
*pg = page->next;
@@ -380,7 +363,176 @@
sizes[order].npages--;
free_pages((long) page, sizes[order].gfporder);
}
+aargh:
sizes[order].nfrees++;
- sizes[order].nbytesmalloced -= size;
restore_flags(flags);
}
+
+
+
+int get_malloc (char *buffer)
+{
+ int i;
+ int len=0;
+
+ len += sprintf (buffer+len,"order size nblk nmalocs nfrees totbyt totpgs\n");
+ for (i = 0;(len < 4000) && BLOCKSIZE(i);i++)
+ len += sprintf (buffer+len," %2d %-6d %4d %6d %6d %9d %5d\n",
+ i,
+ BLOCKSIZE(i),
+ sizes[i].nblocks,
+ sizes[i].nmallocs,
+ sizes[i].nfrees,
+ sizes[i].nbytesmalloced,
+ sizes[i].npages);
+ return len;
+}
+
+
+
+#ifdef CONFIG_DEBUG_MALLOC
+
+#define LINE_SHIFT 12
+
+int read_dmalloc (struct file * file, char * buf, int count)
+{
+ struct block_header *bh;
+ int lineno;
+ int i,len;
+ char line[100]; /* 52 chars misc stuff + 48 for the dump */
+ unsigned char *chp;
+ char *destptr;
+
+ if (count == 0)
+ return 0;
+
+ lineno = file->f_pos >> LINE_SHIFT;
+
+ /* Quickly go to the right part of the file... */
+ for (bh = first_bh.dbh_next,i=0;
+ (bh != &first_bh) && (i < lineno);
+ bh=bh->dbh_next,i++)
+ /* Nothing */;
+
+ destptr = buf;
+
+ /*
+ * I object to using "column" around there. Anything may have
+ * happened in between, which might have altered whatever we were
+ * looking at. So if you are for example at line 5 column 34 a task
+ * switch may change whatever would be displayed on line 5. You
+ * would then see the first half of one part, and the second half
+ * of another. I consider this VERY bad. You should realize that
+ * you are always seeing a snapshot. If "top" would occasionally
+ * show a "sleep" using 50% cpu and lots of memory you would have a
+ * reason to be upset. A line is a piece of data that is internally
+ * consistent.
+ *
+ * Although the "maps" (which was used as an example) are more
+ * stable than kmalloced memory areas, I'd rather not risk
+ * this confusion.....
+ */
+
+ /*
+ * Solution used:
+ * If the next line would give more than your buffer can hold,
+ * the next line is not returned.
+ * Reading 4096 bytes is likely to give you around 4050 bytes.
+ *
+ * Gnu fileutils like dd, copy and cat behave correctly under these
+ * conditions. "less" doesn't like /proc files (cat <file>|less)
+ */
+
+ /*
+ * All the link-following here works under the condition that
+ * not the last malloced block of a page is freed. This could then
+ * free a page for reuse, which may overwrite the dbh_next pointer.
+ *
+ * This means is that it might be possible for this routine
+ * to follow a bad link. This would (VERY occasionally) result in
+ * a kenel oops, which would kill the process reading /proc/dmalloc .
+ * I think this is preferable to doing this all with interrupts
+ * off. The code for the "maps" file can assume that only the
+ * current process can modify its own maps. Here we have to assume
+ * any interrupt can cause a kfree.....
+ *
+ * In short: Problem signalled, consequences evaluated,
+ * and decided that the cure would be worse than the illness.
+ * Try to convince me otherwise.....
+ */
+ while (bh && (bh != &first_bh)) {
+ len = sprintf (line,"%ld %s %d %d",
+ bh->dbh_time,
+ bh->dbh_file,
+ bh->dbh_line,
+ bh->dbh_length);
+ chp=(unsigned char *)(bh+1);
+ for (i=0;i<16;i++)
+ len +=sprintf (line+len, " %02x", chp[i]);
+ len += sprintf (line+len, "\n");
+
+ if (len > count) break;
+
+ memcpy_tofs (destptr,line,len);
+ destptr += len;
+ count -= len;
+ lineno ++;
+ bh = bh->dbh_next;
+ }
+ file->f_pos = lineno << LINE_SHIFT;
+
+ return destptr-buf;
+}
+#endif
+
+
+
+#ifdef TEST
+#include <linux/malloc.h>
+
+
+/*
+ * This is a quick-and-dirty way to be able to run kmalloc in usermode
+ * under a debugger.
+ */
+
+int main (void)
+{
+ char buf[100],com;
+ int p,i;
+
+ kmalloc_init (0,0);
+ printf ("order size nblocks offset gfporder\n");
+ for (i=0;i<NSIZES;i++) {
+ printf ("%5d %6d %7d %6d %8ld\n",
+ i,
+ BLOCKSIZE(i),
+ sizes[i].nblocks,
+ sizes[i].offset,
+ sizes[i].gfporder);
+ }
+ while (gets (buf)) {
+ sscanf (buf,"%c %x",&com,&p);
+ switch (com) {
+ case 'm':printf ("%p\n",kmalloc (p,0));break;
+ case 'f':printf ("ok.\n");kfree ((void *)p);break;
+ case 'q':printf ("bye.\n");exit (0);break;
+ default:printf ("Unrecognized command.\n");
+ }
+ }
+ exit (0);
+}
+
+#endif
+
+
+
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -g -D__KERNEL__ -DTEST -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -pipe -m486 -o kmalloc kmalloc.c"
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */