This message generated a parse failure. Raw output follows here. Please use 'back' to navigate. From devnull@lkml.org Sat Apr 20 03:09:53 2024 Received: from orchard.washtenaw.cc.mi.us (orchard.washtenaw.cc.mi.us [198.111.176.4]) by herbie.ucs.indiana.edu (8.6.12/8.6.12) with ESMTP id UAA19447 for ; Wed, 15 May 1996 20:19:15 -0500 Received: from vger.rutgers.edu by orchard.washtenaw.cc.mi.us (8.6.10/2.3) with ESMTP id VAA01839; Wed, 15 May 1996 21:19:21 -0400 Received: by vger.rutgers.edu id <108279-146>; Wed, 15 May 1996 20:58:02 -0500 Message-Id: <199605152236.AAA02094@cave.et.tudelft.nl> Subject: Kmalloc improvements. To: Linus.Torvalds@helsinki.fi, linux-kernel@vger.rutgers.edu Date: Thu, 16 May 1996 00:36:14 +0200 (MET DST) From: R.E.Wolff@bitwizard.nl (Rogier Wolff) X-Mailer: ELM [version 2.4 PL24] Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Content-Length: 24715 Sender: owner-linux-kernel@vger.rutgers.edu Precedence: bulk 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 #include -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 +#include + +#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 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 |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 + + +/* + * 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