On Wed, 11 Jul 2007 11:25:11 +0200 Attila Kinali <attila@xxxxxxxxx> wrote: > Before i start poking around in mtdram.c and replacing vmalloc > by get_free_page() calls i wanted to ask whether anyone has done > something similar already or has a better idea what to do. Scratch that, doing it myself was faster. If anyone cares, the patch is attached. Attila Kinali -- Praised are the Fountains of Shelieth, the silver harp of the waters, But blest in my name forever this stream that stanched my thirst! -- Deed of Morred
--- drivers/mtd/devices/mtdram.c.orig 2007-07-11 11:46:39.000000000 +0200 +++ drivers/mtd/devices/mtdram.c 2007-07-11 15:53:16.000000000 +0200 @@ -35,62 +35,118 @@ static int ram_erase(struct mtd_info *mtd, struct erase_info *instr) { + size_t erased = 0; + int page; + void ** table = mtd->priv; + if (instr->addr + instr->len > mtd->size) return -EINVAL; - memset((char *)mtd->priv + instr->addr, 0xff, instr->len); + page = instr->addr / PAGE_SIZE; - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); + while(erased < instr->len) { + loff_t ofs = (instr->addr + erased) % PAGE_SIZE; + int to_erase = instr->len - erased; + + if(to_erase > PAGE_SIZE - ofs) + to_erase = PAGE_SIZE - ofs; - return 0; -} + memset(table[page] + ofs, 0xff, to_erase); -static int ram_point(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char **mtdbuf) -{ - if (from + len > mtd->size) - return -EINVAL; + erased += to_erase; + page++; + } + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); - *mtdbuf = mtd->priv + from; - *retlen = len; return 0; } -static void ram_unpoint(struct mtd_info *mtd, u_char * addr, loff_t from, - size_t len) -{ -} static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + size_t copied = 0; + int page; + void ** table = mtd->priv; + if (from + len > mtd->size) return -EINVAL; - memcpy(buf, mtd->priv + from, len); + page = from / PAGE_SIZE; + + /* mtdblock relies on receiving all data + * at once. so we have to take care of + * copying everything + */ + while(copied < len) { + loff_t ofs = (from + copied) % PAGE_SIZE; + int to_copy = len - copied; + + if(to_copy > PAGE_SIZE - ofs) + to_copy = PAGE_SIZE - ofs; + + memcpy(buf + copied, table[page] + ofs, to_copy); - *retlen = len; + copied += to_copy; + page++; + } + + *retlen = copied; return 0; } static int ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { + size_t copied = 0; + int page; + void ** table = mtd->priv; + if (to + len > mtd->size) return -EINVAL; - memcpy((char *)mtd->priv + to, buf, len); + page = to / PAGE_SIZE; + + /* mtdblock relies on sending all data + * at once. so we have to take care of + * copying everything + */ + while(copied < len) { + loff_t ofs = (to + copied) % PAGE_SIZE; + int to_copy = len - copied; + + if(to_copy > PAGE_SIZE - ofs) + to_copy = PAGE_SIZE - ofs; - *retlen = len; + memcpy(table[page] + ofs, buf + copied, to_copy); + + copied += to_copy; + page++; + } + + *retlen = copied; return 0; } static void __exit cleanup_mtdram(void) { + void ** table; + int i, table_size; + if (mtd_info) { + table = mtd_info->priv; + if(table) { + table_size = mtd_info->size / PAGE_SIZE; + if(mtd_info->size % PAGE_SIZE) + table_size++; + for(i=0; i < table_size; i++) + free_page((unsigned long)table[i]); + kfree(table); + table = NULL; + } del_mtd_device(mtd_info); - vfree(mtd_info->priv); kfree(mtd_info); } } @@ -111,8 +167,8 @@ mtd->owner = THIS_MODULE; mtd->erase = ram_erase; - mtd->point = ram_point; - mtd->unpoint = ram_unpoint; + mtd->point = NULL; + mtd->unpoint = NULL; mtd->read = ram_read; mtd->write = ram_write; @@ -125,8 +181,10 @@ static int __init init_mtdram(void) { - void *addr; - int err; + void **addr=NULL; + int err = 0; + int i = 0; + int table_size; if (!total_size) return -EINVAL; @@ -136,20 +194,45 @@ if (!mtd_info) return -ENOMEM; - addr = vmalloc(MTDRAM_TOTAL_SIZE); + table_size = MTDRAM_TOTAL_SIZE / PAGE_SIZE; + if(MTDRAM_TOTAL_SIZE % PAGE_SIZE) + table_size++; + + addr = kmalloc(table_size * sizeof(void*), GFP_KERNEL); if (!addr) { kfree(mtd_info); mtd_info = NULL; return -ENOMEM; } + + memset(addr, 0, table_size * sizeof(void*)); + + for(i = 0; i < table_size; i++) { + addr[i] = (void*) __get_free_page(GFP_KERNEL); + if(!addr[i]) { + err = -ENOMEM; + break; + } + memset(addr[i], 0xff, PAGE_SIZE); + } + + err = mtdram_init_device(mtd_info, addr, MTDRAM_TOTAL_SIZE, "mtdram test device"); + if (err) { - vfree(addr); + if(addr) { + /* use i as a counter how many pages + * have been allocated */ + for(--i; i >= 0; i--) + free_page((unsigned long) addr[i]); + kfree(addr); + addr = NULL; + } kfree(mtd_info); mtd_info = NULL; return err; } - memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE); + return err; }