Memory leak
hinko.kocevar at cetrtapot.si
hinko.kocevar at cetrtapot.si
Wed Apr 9 09:54:03 EDT 2008
Hello,
I've ported 2.6.12 NAND module driver for our board to git tree version
(2.6.25-rc8). While testing the module, I've noticed that memory gets
eaten with each insmod/rmmod cycle. Eg. in ~12 minutes Slab consumption
rises from 1632 kB to 8584 kB, which is soon fatal for our embedded
system with 16megs of ram.
In my NAND driver only mtd_info and nand_chip structs are allocated in
module init, and accordingly released in module cleanup. Step by step
commenting lines in init/cleanup I came up with the conclusion that if
my drivers calls add_mtd_partition(), it leaks! If I comment out call to
add_mtd_partition() no more leaks are seen!?!?!
Quick inspectiion of /proc/slabinfo shows that 'sysfs_dir_cache' entry
'active_objs' rises from 3788 to 10800 - which is suspicious.
I've attached /proc/meminfo and /proc/slabinfo for my test, which does:
while c < 100
insmod nand driver
rmmod nand driver
save meminfo
save slabinfo
inc c
done
I've put the dumps on the public FTP server for you to retrieve (size
71940 b):
http://4thway.0catch.com/dumps.zip
Attached is out NAND driver ported to 2.6.25-rc8.
---
/*
* drivers/mtd/nand/carneol.c
*
* Copyright (C) 2005 Simon Posnjak (simon.posnjak at cetrtapot.si)
*
* Based on :
* drivers/mtd/nand/spia.c
* Copyright (C) 2000 Steven J. Hill (sjhill at realitydiluted.com)
*
* Pin fliping code was "borowed" from Hinko Kocevar's TCS2301 driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Overview:
* This is a device driver for the NAND flash device found on the
* Carneol board which utilizes the Toshiba TC58 part.
*/
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
#include <linux/platform_device.h>
#else
#include <linux/config.h>
#endif
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/cpot/carneol-platform.h>
static unsigned char mod_name[] = "carneol-nand";
static unsigned char mod_version[] = "08040801";
static struct mtd_info *tc58512_mtd = NULL;
/*
* All NAND flash signals are connected to cris PGx ports.
*/
/*
* Data signals are on PG1 port pins b0 - b7. Direction of data pins must be
* set before starting byte read/write operation on data pins.
*/
//#define TC58512_DATA CARNEOL_PG1 /* PORTG b8 - b15 */
//#define TC58512_DATA0 CARNEOL_PG1_IO0 /* PORTG b8 */
//#define TC58512_DATA1 CARNEOL_PG1_IO1 /* PORTG b9 */
//#define TC58512_DATA2 CARNEOL_PG1_IO2 /* PORTG b10 */
//#define TC58512_DATA3 CARNEOL_PG1_IO3 /* PORTG b11 */
//#define TC58512_DATA4 CARNEOL_PG1_IO4 /* PORTG b12 */
//#define TC58512_DATA5 CARNEOL_PG1_IO5 /* PORTG b13 */
//#define TC58512_DATA6 CARNEOL_PG1_IO6 /* PORTG b14 */
//#define TC58512_DATA7 CARNEOL_PG1_IO7 /* PORTG b15 */
/*
* Control signals are on PG2 port pins b1 - b6. Direction of control pins
* is set to output in init stage.
*
* NOTE: PG2 pins b0 and b7 are not used for NAND flash!
*/
//#define TC58512_CTRL CARNEOL_PG2 /* PORTG b16 - b23 */
//#define TC58512_RE CARNEOL_PG2_IO1 /* PORTG b17 */
//#define TC58512_CE CARNEOL_PG2_IO2 /* PORTG b18 */
//#define TC58512_CLE CARNEOL_PG2_IO3 /* PORTG b19 */
//#define TC58512_ALE CARNEOL_PG2_IO4 /* PORTG b20 */
//#define TC58512_WE CARNEOL_PG2_IO5 /* PORTG b21 */
//#define TC58512_WP CARNEOL_PG2_IO6 /* PORTG b22 */
/*
* Status signal (chip ready) is on PG3 pin b0. Direction of status pin
* is set to input in init stage.
*/
//#define TC58512_STATUS CARNEOL_PG3 /* PORTG b24 - b31 */
//#define TC58512_RYBY CARNEOL_PG3_IO0 /* PORTG b24 */
#if defined(CONFIG_CPOT_PLATFORM_AFC2)
const static struct mtd_partition partition_info[] =
{
{
.name = "Carneol NAND part1 (program)",
.offset = 0,
.size = 4 * 1024 * 1024
},
{
.name = "Carneol NAND part2 (dataout)",
.offset = 4 * 1024 * 1024,
.size = 4 * 1024 * 1024
},
{
.name = "Carneol NAND part3 (datain)",
.offset = 8 * 1024 * 1024,
.size = 4 * 1024 * 1024
},
{
.name = "Carneol NAND part4 (backup)",
.offset = 12 * 1024 * 1024,
.size = 8 * 1024 * 1024
},
{
.name = "Carneol NAND part5 (upgrade)",
.offset = 20 * 1024 * 1024,
.size = 8 * 1024 * 1024
}
};
#define NUM_PARTITIONS 5
#elif defined(CONFIG_CPOT_PLATFORM_CDU2)
const static struct mtd_partition partition_info[] =
{
{
.name = "Carneol NAND part1 (program)",
.offset = 0,
.size = 4 * 1024 * 1024
},
{
.name = "Carneol NAND part2 (dataout)",
.offset = 4 * 1024 * 1024,
.size = 4 * 1024 * 1024
},
{
.name = "Carneol NAND part3 (datain)",
.offset = 8 * 1024 * 1024,
.size = 4 * 1024 * 1024
},
{
.name = "Carneol NAND part4 (disk1)",
.offset = 12 * 1024 * 1024,
.size = 8 * 1024 * 1024
},
{
.name = "Carneol NAND part5 (disk2)",
.offset = 20 * 1024 * 1024,
.size = 8 * 1024 * 1024
},
{
.name = "Carneol NAND part4 (backup)",
.offset = 28 * 1024 * 1024,
.size = 4 * 1024 * 1024
},
};
#define NUM_PARTITIONS 6
#elif defined(CONFIG_CPOT_PLATFORM_TAA2)
const static struct mtd_partition partition_info[] =
{
{
.name = "Carneol NAND part1 (program)",
.offset = 0,
.size = 4 * 1024 * 1024,
},
{
.name = "Carneol NAND part2 (dataout)",
.offset = 4 * 1024 * 1024,
.size = 8 * 1024 * 1024,
},
{
.name = "Carneol NAND part3 (datain)",
.offset = 12 * 1024 * 1024,
.size = 8 * 1024 * 1024,
},
{
.name = "Carneol NAND part4 (lib)",
.offset = 20 * 1024 * 1024,
.size = 8 * 1024 * 1024,
},
{
.name = "Carneol NAND part5 (usr)",
.offset = 28 * 1024 * 1024,
.size = 4 * 1024 * 1024,
},
};
#define NUM_PARTITIONS 5
#endif /* CONFIG_CPOT_PLATFORM_AFC2, CDU2, TAA2 */
static u_char tc58512_read_byte (struct mtd_info *mtd);
static void tc58512_write_byte (struct mtd_info *mtd, u_char byte);
static void tc58512_write_buf (struct mtd_info *mtd, const u_char * buf,
int len);
static void tc58512_read_buf (struct mtd_info *mtd, u_char * buf, int len);
static int tc58512_verify_buf (struct mtd_info *mtd, const u_char * buf,
int len);
#if 0
57 /* Select the chip by setting nCE to low */
58 #define NAND_NCE 0x01
59 /* Select the command latch by setting CLE to high */
60 #define NAND_CLE 0x02
61 /* Select the address latch by setting ALE to high */
62 #define NAND_ALE 0x04
63
64 #define NAND_CTRL_CLE (NAND_NCE | NAND_CLE)
65 #define NAND_CTRL_ALE (NAND_NCE | NAND_ALE)
66 #define NAND_CTRL_CHANGE 0x80
#endif
static void tc58512_cmd_ctrl (struct mtd_info *mtd, int cmd, unsigned
int ctrl)
{
if (ctrl & NAND_CTRL_CHANGE)
{
if (ctrl & NAND_NCE)
//carneol_pin_low(TC58512_CTRL, TC58512_CE);
carneol_low_pg_18();
else
//carneol_pin_high(TC58512_CTRL, TC58512_CE);
carneol_high_pg_18();
if (ctrl & NAND_CLE)
//carneol_pin_high(TC58512_CTRL, TC58512_CLE);
carneol_high_pg_19();
else
//carneol_pin_low(TC58512_CTRL, TC58512_CLE);
carneol_low_pg_19();
if (ctrl & NAND_ALE)
//carneol_pin_high(TC58512_CTRL, TC58512_ALE);
carneol_high_pg_20();
else
//carneol_pin_low(TC58512_CTRL, TC58512_ALE);
carneol_low_pg_20();
}
if (cmd != NAND_CMD_NONE)
{
tc58512_write_byte(mtd, (unsigned char)cmd);
}
}
static int tc58512_device_ready (struct mtd_info *mtd)
{
/* 1 - chip is ready, 0 - chip is busy. */
//return (int) (carneol_pin_level(TC58512_STATUS, TC58512_RYBY));
return carneol_is_high_pg_24();
}
static u_char tc58512_read_byte (struct mtd_info *mtd)
{
u_int8_t val;
//carneol_port_input(TC58512_DATA);
carneol_input_pg_8();
//carneol_pin_low(TC58512_CTRL, TC58512_RE);
carneol_low_pg_17();
//val = (unsigned char) carneol_port_read(TC58512_DATA);
val = carneol_read_pg1();
//carneol_pin_high(TC58512_CTRL, TC58512_RE);
carneol_high_pg_17();
return val;
}
static void tc58512_write_byte (struct mtd_info *mtd, u_char byte)
{
//carneol_port_output(TC58512_DATA);
carneol_output_pg_8();
//carneol_port_write(TC58512_DATA, (unsigned char)byte);
carneol_write_pg1((u_int8_t) byte);
//carneol_pin_low(TC58512_CTRL, TC58512_WE);
carneol_low_pg_21();
//carneol_pin_high(TC58512_CTRL, TC58512_WE);
carneol_high_pg_21();
}
static void tc58512_write_buf (struct mtd_info *mtd, const u_char * buf,
int len)
{
int i;
//carneol_port_output(TC58512_DATA);
carneol_output_pg_8();
for (i = 0; i < len; i++)
{
//carneol_port_write(TC58512_DATA, (unsigned char)buf[i]);
carneol_write_pg1((u_int8_t) buf[i]);
//carneol_pin_low(TC58512_CTRL, TC58512_WE);
carneol_low_pg_21();
//carneol_pin_high(TC58512_CTRL, TC58512_WE);
carneol_high_pg_21();
}
}
static void tc58512_read_buf (struct mtd_info *mtd, u_char * buf, int len)
{
int i;
//carneol_port_input(TC58512_DATA);
carneol_input_pg_8();
for (i = 0; i < len; i++)
{
//carneol_pin_low(TC58512_CTRL, TC58512_RE);
carneol_low_pg_17();
//buf[i] = (unsigned char) carneol_port_read(TC58512_DATA);
buf[i] = carneol_read_pg1();
//carneol_pin_high(TC58512_CTRL, TC58512_RE);
carneol_high_pg_17();
}
}
static int tc58512_verify_buf (struct mtd_info *mtd, const u_char * buf,
int len)
{
int i;
//carneol_port_input(TC58512_DATA);
carneol_input_pg_8();
for (i = 0; i < len; i++)
{
//carneol_pin_low(TC58512_CTRL, TC58512_RE);
carneol_low_pg_17();
//if ((u_char) buf[i] != (unsigned char) carneol_port_read(TC58512_DATA))
if ((u_int8_t) buf[i] != (u_int8_t) carneol_read_pg1())
return -EFAULT;
//carneol_pin_high(TC58512_CTRL, TC58512_RE);
carneol_high_pg_17();
}
return 0;
}
static int __init tc58512_init (void)
{
struct nand_chip *this;
printk("Carneol %s module %s, (C) 2005 - 2008 Simon Posnjak, Hinko
Kocevar\n", mod_name, mod_version);
tc58512_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip),
GFP_KERNEL);
if (!tc58512_mtd)
{
printk(KERN_ERR "%s: Unable to allocate carneol NAND MTD device
structure.\n", __func__);
return -ENOMEM;
}
this = (struct nand_chip *) (&tc58512_mtd[1]);
memset((char *) tc58512_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
tc58512_mtd->priv = this;
tc58512_mtd->owner = THIS_MODULE;
//carneol_port_output(TC58512_CTRL);
carneol_output_pg_16();
//carneol_port_input(TC58512_DATA);
carneol_input_pg_8();
//carneol_port_input(TC58512_STATUS);
carneol_input_pg_24();
//carneol_pin_low(TC58512_CTRL, TC58512_CLE);
carneol_low_pg_19();
//carneol_pin_low(TC58512_CTRL, TC58512_ALE);
carneol_low_pg_20();
//carneol_pin_high(TC58512_CTRL, TC58512_CE);
carneol_high_pg_18();
//carneol_pin_high(TC58512_CTRL, TC58512_RE);
carneol_high_pg_17();
//carneol_pin_high(TC58512_CTRL, TC58512_WE);
carneol_high_pg_21();
//carneol_pin_high(TC58512_CTRL, TC58512_WP);
carneol_high_pg_22();
this->read_byte = tc58512_read_byte;
//this->write_byte = tc58512_write_byte;
this->write_buf = tc58512_write_buf;
this->read_buf = tc58512_read_buf;
this->verify_buf = tc58512_verify_buf;
this->dev_ready = tc58512_device_ready;
/* Set address of hardware control function */
this->cmd_ctrl = tc58512_cmd_ctrl;
/* 3 us command delay time */
this->chip_delay = 3;
this->ecc.mode = NAND_ECC_SOFT;
/* Scan to find existence of the device */
if (nand_scan(tc58512_mtd, 1))
{
kfree(tc58512_mtd);
return -ENXIO;
}
#ifdef CONFIG_MTD_PARTITIONS
/* Register the partitions */
add_mtd_partitions(tc58512_mtd, partition_info, NUM_PARTITIONS);
#endif
return 0;
}
static void __exit tc58512_cleanup (void)
{
#if 0
/* Release MTD partitions */
del_mtd_partitions(tc58512_mtd);
#endif
/* Release resources, unregister device */
nand_release(tc58512_mtd);
/* Free the MTD device structure */
kfree(tc58512_mtd);
printk("Carneol %s module version %s removed\n", mod_name, mod_version);
}
module_init(tc58512_init);
module_exit(tc58512_cleanup);
MODULE_AUTHOR("Simon Posnjak <simon.posnjak at cetrtapot.si>, Hinko Kocevar
<hinko.kocevar at cetrtapot.si>");
MODULE_DESCRIPTION("CPOT Carneol NAND flash module for TC58512
compatible flash");
MODULE_LICENSE("GPL");
---
Best regards,
Hinko
--
ČETRTA POT, d.o.o., Kranj
Planina 3
4000 Kranj
Slovenia, Europe
Tel. +386 (0) 4 280 66 03
E-mail: hinko.kocevar at cetrtapot.si
Http: www.cetrtapot.si
More information about the linux-mtd
mailing list