diff options
Diffstat (limited to 'drivers/mtd/devices/block2mtd.c')
| -rw-r--r-- | drivers/mtd/devices/block2mtd.c | 339 |
1 files changed, 159 insertions, 180 deletions
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 4a7a805e756..66f0405f7e5 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -1,14 +1,14 @@ /* - * $Id: block2mtd.c,v 1.28 2005/03/19 22:40:44 gleixner Exp $ - * * block2mtd.c - create an mtd from a block device * * Copyright (C) 2001,2002 Simon Evans <spse@secret.org.uk> - * Copyright (C) 2004,2005 Jörn Engel <joern@wh.fh-wedel.de> + * Copyright (C) 2004-2006 Joern Engel <joern@wh.fh-wedel.de> * * Licence: GPL */ -#include <linux/config.h> + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/fs.h> #include <linux/blkdev.h> @@ -17,21 +17,17 @@ #include <linux/list.h> #include <linux/init.h> #include <linux/mtd/mtd.h> -#include <linux/buffer_head.h> - -#define VERSION "$Revision: 1.28 $" - - -#define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) -#define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args) - +#include <linux/mutex.h> +#include <linux/mount.h> +#include <linux/slab.h> +#include <linux/major.h> /* Info for the block device */ struct block2mtd_dev { struct list_head list; struct block_device *blkdev; struct mtd_info mtd; - struct semaphore write_mutex; + struct mutex write_mutex; }; @@ -39,60 +35,11 @@ struct block2mtd_dev { static LIST_HEAD(blkmtd_device_list); -#define PAGE_READAHEAD 64 -void cache_readahead(struct address_space *mapping, int index) -{ - filler_t *filler = (filler_t*)mapping->a_ops->readpage; - int i, pagei; - unsigned ret = 0; - unsigned long end_index; - struct page *page; - LIST_HEAD(page_pool); - struct inode *inode = mapping->host; - loff_t isize = i_size_read(inode); - - if (!isize) { - INFO("iSize=0 in cache_readahead\n"); - return; - } - - end_index = ((isize - 1) >> PAGE_CACHE_SHIFT); - - read_lock_irq(&mapping->tree_lock); - for (i = 0; i < PAGE_READAHEAD; i++) { - pagei = index + i; - if (pagei > end_index) { - INFO("Overrun end of disk in cache readahead\n"); - break; - } - page = radix_tree_lookup(&mapping->page_tree, pagei); - if (page && (!i)) - break; - if (page) - continue; - read_unlock_irq(&mapping->tree_lock); - page = page_cache_alloc_cold(mapping); - read_lock_irq(&mapping->tree_lock); - if (!page) - break; - page->index = pagei; - list_add(&page->lru, &page_pool); - ret++; - } - read_unlock_irq(&mapping->tree_lock); - if (ret) - read_cache_pages(mapping, &page_pool, filler, NULL); -} - - -static struct page* page_readahead(struct address_space *mapping, int index) +static struct page *page_read(struct address_space *mapping, int index) { - filler_t *filler = (filler_t*)mapping->a_ops->readpage; - cache_readahead(mapping, index); - return read_cache_page(mapping, index, filler, NULL); + return read_mapping_page(mapping, index, NULL); } - /* erase a specified part of the device */ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len) { @@ -104,19 +51,18 @@ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len) u_long *max; while (pages) { - page = page_readahead(mapping, index); - if (!page) - return -ENOMEM; + page = page_read(mapping, index); if (IS_ERR(page)) return PTR_ERR(page); - max = (u_long*)page_address(page) + PAGE_SIZE; - for (p=(u_long*)page_address(page); p<max; p++) + max = page_address(page) + PAGE_SIZE; + for (p=page_address(page); p<max; p++) if (*p != -1UL) { lock_page(page); memset(page_address(page), 0xff, PAGE_SIZE); set_page_dirty(page); unlock_page(page); + balance_dirty_pages_ratelimited(mapping); break; } @@ -134,16 +80,15 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr) int err; instr->state = MTD_ERASING; - down(&dev->write_mutex); + mutex_lock(&dev->write_mutex); err = _block2mtd_erase(dev, from, len); - up(&dev->write_mutex); + mutex_unlock(&dev->write_mutex); if (err) { - ERROR("erase failed err = %d", err); + pr_err("erase failed err = %d\n", err); instr->state = MTD_ERASE_FAILED; } else instr->state = MTD_ERASE_DONE; - instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); return err; } @@ -158,14 +103,6 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len, int offset = from & (PAGE_SIZE-1); int cpylen; - if (from > mtd->size) - return -EINVAL; - if (from + len > mtd->size) - len = mtd->size - from; - - if (retlen) - *retlen = 0; - while (len) { if ((offset + len) > PAGE_SIZE) cpylen = PAGE_SIZE - offset; // multiple pages @@ -173,10 +110,7 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len, cpylen = len; // this page len = len - cpylen; - // Get page - page = page_readahead(dev->blkdev->bd_inode->i_mapping, index); - if (!page) - return -ENOMEM; + page = page_read(dev->blkdev->bd_inode->i_mapping, index); if (IS_ERR(page)) return PTR_ERR(page); @@ -203,19 +137,14 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf, int offset = to & ~PAGE_MASK; // page offset int cpylen; - if (retlen) - *retlen = 0; while (len) { - if ((offset+len) > PAGE_SIZE) + if ((offset+len) > PAGE_SIZE) cpylen = PAGE_SIZE - offset; // multiple pages else cpylen = len; // this page len = len - cpylen; - // Get page - page = page_readahead(mapping, index); - if (!page) - return -ENOMEM; + page = page_read(mapping, index); if (IS_ERR(page)) return PTR_ERR(page); @@ -224,6 +153,7 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf, memcpy(page_address(page) + offset, buf, cpylen); set_page_dirty(page); unlock_page(page); + balance_dirty_pages_ratelimited(mapping); } page_cache_release(page); @@ -236,22 +166,17 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf, } return 0; } + + static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { struct block2mtd_dev *dev = mtd->priv; int err; - if (!len) - return 0; - if (to >= mtd->size) - return -ENOSPC; - if (to + len > mtd->size) - len = mtd->size - to; - - down(&dev->write_mutex); + mutex_lock(&dev->write_mutex); err = _block2mtd_write(dev, buf, to, len, retlen); - up(&dev->write_mutex); + mutex_unlock(&dev->write_mutex); if (err > 0) err = 0; return err; @@ -275,81 +200,107 @@ static void block2mtd_free_device(struct block2mtd_dev *dev) kfree(dev->mtd.name); if (dev->blkdev) { - invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); - close_bdev_excl(dev->blkdev); + invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping, + 0, -1); + blkdev_put(dev->blkdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL); } kfree(dev); } -/* FIXME: ensure that mtd->size % erase_size == 0 */ static struct block2mtd_dev *add_device(char *devname, int erase_size) { + const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL; struct block_device *bdev; struct block2mtd_dev *dev; + char *name; if (!devname) return NULL; - dev = kmalloc(sizeof(struct block2mtd_dev), GFP_KERNEL); + dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL); if (!dev) return NULL; - memset(dev, 0, sizeof(*dev)); /* Get a handle on the device */ - bdev = open_bdev_excl(devname, O_RDWR, NULL); + bdev = blkdev_get_by_path(devname, mode, dev); +#ifndef MODULE + if (IS_ERR(bdev)) { + + /* We might not have rootfs mounted at this point. Try + to resolve the device name by other means. */ + + dev_t devt = name_to_dev_t(devname); + if (devt) + bdev = blkdev_get_by_dev(devt, mode, dev); + } +#endif + if (IS_ERR(bdev)) { - ERROR("error: cannot open device %s", devname); - goto devinit_err; + pr_err("error: cannot open device %s\n", devname); + goto err_free_block2mtd; } dev->blkdev = bdev; if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { - ERROR("attempting to use an MTD device as a block device"); - goto devinit_err; + pr_err("attempting to use an MTD device as a block device\n"); + goto err_free_block2mtd; + } + + if ((long)dev->blkdev->bd_inode->i_size % erase_size) { + pr_err("erasesize must be a divisor of device size\n"); + goto err_free_block2mtd; } - init_MUTEX(&dev->write_mutex); + mutex_init(&dev->write_mutex); /* Setup the MTD structure */ /* make the name contain the block device in */ - dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname), - GFP_KERNEL); - if (!dev->mtd.name) - goto devinit_err; + name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname); + if (!name) + goto err_destroy_mutex; - sprintf(dev->mtd.name, "block2mtd: %s", devname); + dev->mtd.name = name; dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; dev->mtd.erasesize = erase_size; + dev->mtd.writesize = 1; + dev->mtd.writebufsize = PAGE_SIZE; dev->mtd.type = MTD_RAM; dev->mtd.flags = MTD_CAP_RAM; - dev->mtd.erase = block2mtd_erase; - dev->mtd.write = block2mtd_write; - dev->mtd.writev = default_mtd_writev; - dev->mtd.sync = block2mtd_sync; - dev->mtd.read = block2mtd_read; - dev->mtd.readv = default_mtd_readv; + dev->mtd._erase = block2mtd_erase; + dev->mtd._write = block2mtd_write; + dev->mtd._sync = block2mtd_sync; + dev->mtd._read = block2mtd_read; dev->mtd.priv = dev; dev->mtd.owner = THIS_MODULE; - if (add_mtd_device(&dev->mtd)) { - /* Device didnt get added, so free the entry */ - goto devinit_err; + if (mtd_device_register(&dev->mtd, NULL, 0)) { + /* Device didn't get added, so free the entry */ + goto err_destroy_mutex; } list_add(&dev->list, &blkmtd_device_list); - INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, - dev->mtd.name + strlen("blkmtd: "), - dev->mtd.erasesize >> 10, dev->mtd.erasesize); + pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n", + dev->mtd.index, + dev->mtd.name + strlen("block2mtd: "), + dev->mtd.erasesize >> 10, dev->mtd.erasesize); return dev; -devinit_err: +err_destroy_mutex: + mutex_destroy(&dev->write_mutex); +err_free_block2mtd: block2mtd_free_device(dev); return NULL; } +/* This function works similar to reguler strtoul. In addition, it + * allows some suffixes for a more human-readable number format: + * ki, Ki, kiB, KiB - multiply result with 1024 + * Mi, MiB - multiply result with 1024^2 + * Gi, GiB - multiply result with 1024^3 + */ static int ustrtoul(const char *cp, char **endp, unsigned int base) { unsigned long result = simple_strtoul(cp, endp, base); @@ -358,11 +309,16 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base) result *= 1024; case 'M': result *= 1024; + case 'K': case 'k': result *= 1024; /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ - if ((*endp)[1] == 'i') - (*endp) += 2; + if ((*endp)[1] == 'i') { + if ((*endp)[2] == 'B') + (*endp) += 3; + else + (*endp) += 2; + } } return result; } @@ -382,26 +338,6 @@ static int parse_num(size_t *num, const char *token) } -static int parse_name(char **pname, const char *token, size_t limit) -{ - size_t len; - char *name; - - len = strlen(token) + 1; - if (len > limit) - return -ENOSPC; - - name = kmalloc(len, GFP_KERNEL); - if (!name) - return -ENOMEM; - - strcpy(name, token); - - *pname = name; - return 0; -} - - static inline void kill_final_newline(char *str) { char *newline = strrchr(str, '\n'); @@ -410,46 +346,53 @@ static inline void kill_final_newline(char *str) } -#define parse_err(fmt, args...) do { \ - ERROR("block2mtd: " fmt "\n", ## args); \ - return 0; \ -} while (0) +#ifndef MODULE +static int block2mtd_init_called = 0; +static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */ +#endif -static int block2mtd_setup(const char *val, struct kernel_param *kp) +static int block2mtd_setup2(const char *val) { - char buf[80+12], *str=buf; /* 80 for device, 12 for erase size */ + char buf[80 + 12]; /* 80 for device, 12 for erase size */ + char *str = buf; char *token[2]; char *name; size_t erase_size = PAGE_SIZE; int i, ret; - if (strnlen(val, sizeof(buf)) >= sizeof(buf)) - parse_err("parameter too long"); + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) { + pr_err("parameter too long\n"); + return 0; + } strcpy(str, val); kill_final_newline(str); - for (i=0; i<2; i++) + for (i = 0; i < 2; i++) token[i] = strsep(&str, ","); - if (str) - parse_err("too many arguments"); + if (str) { + pr_err("too many arguments\n"); + return 0; + } - if (!token[0]) - parse_err("no argument"); + if (!token[0]) { + pr_err("no argument\n"); + return 0; + } - ret = parse_name(&name, token[0], 80); - if (ret == -ENOMEM) - parse_err("out of memory"); - if (ret == -ENOSPC) - parse_err("name too long"); - if (ret) + name = token[0]; + if (strlen(name) + 1 > 80) { + pr_err("device name too long\n"); return 0; + } if (token[1]) { ret = parse_num(&erase_size, token[1]); - if (ret) - parse_err("illegal erase size"); + if (ret) { + pr_err("illegal erase size\n"); + return 0; + } } add_device(name, erase_size); @@ -458,17 +401,51 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp) } +static int block2mtd_setup(const char *val, struct kernel_param *kp) +{ +#ifdef MODULE + return block2mtd_setup2(val); +#else + /* If more parameters are later passed in via + /sys/module/block2mtd/parameters/block2mtd + and block2mtd_init() has already been called, + we can parse the argument now. */ + + if (block2mtd_init_called) + return block2mtd_setup2(val); + + /* During early boot stage, we only save the parameters + here. We must parse them later: if the param passed + from kernel boot command line, block2mtd_setup() is + called so early that it is not possible to resolve + the device (even kmalloc() fails). Deter that work to + block2mtd_setup2(). */ + + strlcpy(block2mtd_paramline, val, sizeof(block2mtd_paramline)); + + return 0; +#endif +} + + module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200); MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>]\""); static int __init block2mtd_init(void) { - INFO("version " VERSION); - return 0; + int ret = 0; + +#ifndef MODULE + if (strlen(block2mtd_paramline)) + ret = block2mtd_setup2(block2mtd_paramline); + block2mtd_init_called = 1; +#endif + + return ret; } -static void __devexit block2mtd_exit(void) +static void block2mtd_exit(void) { struct list_head *pos, *next; @@ -476,9 +453,11 @@ static void __devexit block2mtd_exit(void) list_for_each_safe(pos, next, &blkmtd_device_list) { struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list); block2mtd_sync(&dev->mtd); - del_mtd_device(&dev->mtd); - INFO("mtd%d: [%s] removed", dev->mtd.index, - dev->mtd.name + strlen("blkmtd: ")); + mtd_device_unregister(&dev->mtd); + mutex_destroy(&dev->write_mutex); + pr_info("mtd%d: [%s] removed\n", + dev->mtd.index, + dev->mtd.name + strlen("block2mtd: ")); list_del(&dev->list); block2mtd_free_device(dev); } @@ -489,5 +468,5 @@ module_init(block2mtd_init); module_exit(block2mtd_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Simon Evans <spse@secret.org.uk> and others"); +MODULE_AUTHOR("Joern Engel <joern@lazybastard.org>"); MODULE_DESCRIPTION("Emulate an MTD using a block device"); |
