--- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -47,6 +47,16 @@ devices. Partitioning on NFTL 'devices' is a different - that's the 'normal' form of partitioning used on a block device. +config MTD_ROOTFS_ROOT_DEV + bool "Automatically set 'rootfs' partition to be root filesystem" + depends on MTD_PARTITIONS + default y + +config MTD_ROOTFS_SPLIT + bool "Automatically split 'rootfs' partition for squashfs" + depends on MTD_PARTITIONS + default y + config MTD_REDBOOT_PARTS tristate "RedBoot partition table parsing" depends on MTD_PARTITIONS --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include /* Our partition linked list */ static LIST_HEAD(mtd_partitions); @@ -39,7 +41,7 @@ * the pointer to that structure with this macro. */ #define PART(x) ((struct mtd_part *)(x)) - +#define IS_PART(mtd) (mtd->read == part_read) /* * MTD methods which simply translate the effective address and pass through @@ -322,6 +324,316 @@ return 0; } +static u_int32_t cur_offset = 0; +static int add_one_partition(struct mtd_info *master, const struct mtd_partition *part, + int i, struct mtd_part **slp) +{ + struct mtd_part *slave; + + /* allocate the partition structure */ + slave = kzalloc (sizeof(*slave), GFP_KERNEL); + if (!slave) { + printk ("memory allocation error while creating partitions for \"%s\"\n", + master->name); + del_mtd_partitions(master); + return -ENOMEM; + } + list_add(&slave->list, &mtd_partitions); + + /* set up the MTD object for this partition */ + slave->mtd.type = master->type; + slave->mtd.flags = master->flags & ~part->mask_flags; + slave->mtd.size = part->size; + slave->mtd.writesize = master->writesize; + slave->mtd.oobsize = master->oobsize; + slave->mtd.oobavail = master->oobavail; + slave->mtd.subpage_sft = master->subpage_sft; + + slave->mtd.name = part->name; + slave->mtd.owner = master->owner; + + slave->mtd.read = part_read; + slave->mtd.write = part_write; + + if (master->panic_write) + slave->mtd.panic_write = part_panic_write; + + slave->mtd.refresh_device = part->refresh_partition; + + if(master->point && master->unpoint){ + slave->mtd.point = part_point; + slave->mtd.unpoint = part_unpoint; + } + + if (master->read_oob) + slave->mtd.read_oob = part_read_oob; + if (master->write_oob) + slave->mtd.write_oob = part_write_oob; + if(master->read_user_prot_reg) + slave->mtd.read_user_prot_reg = part_read_user_prot_reg; + if(master->read_fact_prot_reg) + slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; + if(master->write_user_prot_reg) + slave->mtd.write_user_prot_reg = part_write_user_prot_reg; + if(master->lock_user_prot_reg) + slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; + if(master->get_user_prot_info) + slave->mtd.get_user_prot_info = part_get_user_prot_info; + if(master->get_fact_prot_info) + slave->mtd.get_fact_prot_info = part_get_fact_prot_info; + if (master->sync) + slave->mtd.sync = part_sync; + if (!i && master->suspend && master->resume) { + slave->mtd.suspend = part_suspend; + slave->mtd.resume = part_resume; + } + if (master->writev) + slave->mtd.writev = part_writev; + if (master->lock) + slave->mtd.lock = part_lock; + if (master->unlock) + slave->mtd.unlock = part_unlock; + if (master->block_isbad) + slave->mtd.block_isbad = part_block_isbad; + if (master->block_markbad) + slave->mtd.block_markbad = part_block_markbad; + slave->mtd.erase = part_erase; + slave->master = master; + slave->offset = part->offset; + slave->index = i; + + if (slave->offset == MTDPART_OFS_APPEND) + slave->offset = cur_offset; + if (slave->offset == MTDPART_OFS_NXTBLK) { + slave->offset = cur_offset; + if ((cur_offset % master->erasesize) != 0) { + /* Round up to next erasesize */ + slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; + printk(KERN_NOTICE "Moving partition %d: " + "0x%08x -> 0x%08x\n", i, + cur_offset, slave->offset); + } + } + if (slave->mtd.size == MTDPART_SIZ_FULL) + slave->mtd.size = master->size - slave->offset; + cur_offset = slave->offset + slave->mtd.size; + + printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, + slave->offset + slave->mtd.size, slave->mtd.name); + + /* let's do some sanity checks */ + if (slave->offset >= master->size) { + /* let's register it anyway to preserve ordering */ + slave->offset = 0; + slave->mtd.size = 0; + printk ("mtd: partition \"%s\" is out of reach -- disabled\n", + part->name); + } + if (slave->offset + slave->mtd.size > master->size) { + slave->mtd.size = master->size - slave->offset; + printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", + part->name, master->name, slave->mtd.size); + } + if (master->numeraseregions>1) { + /* Deal with variable erase size stuff */ + int i; + struct mtd_erase_region_info *regions = master->eraseregions; + + /* Find the first erase regions which is part of this partition. */ + for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) + ; + + for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { + if (slave->mtd.erasesize < regions[i].erasesize) { + slave->mtd.erasesize = regions[i].erasesize; + } + } + } else { + /* Single erase size */ + slave->mtd.erasesize = master->erasesize; + } + + if ((slave->mtd.flags & MTD_WRITEABLE) && + (slave->offset % slave->mtd.erasesize)) { + /* Doesn't start on a boundary of major erase size */ + /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ + slave->mtd.flags &= ~MTD_WRITEABLE; + printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", + part->name); + } + if ((slave->mtd.flags & MTD_WRITEABLE) && + (slave->mtd.size % slave->mtd.erasesize)) { + slave->mtd.flags &= ~MTD_WRITEABLE; + printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", + part->name); + } + + slave->mtd.ecclayout = master->ecclayout; + if (master->block_isbad) { + uint32_t offs = 0; + + while(offs < slave->mtd.size) { + if (master->block_isbad(master, + offs + slave->offset)) + slave->mtd.ecc_stats.badblocks++; + offs += slave->mtd.erasesize; + } + } + + if(part->mtdp) + { /* store the object pointer (caller may or may not register it */ + *part->mtdp = &slave->mtd; + slave->registered = 0; + } + else + { + /* register our partition */ + add_mtd_device(&slave->mtd); + slave->registered = 1; + } + + if (slp) + *slp = slave; + + return 0; +} + +#ifdef CONFIG_MTD_ROOTFS_SPLIT +#define ROOTFS_SPLIT_NAME "rootfs_data" +#define ROOTFS_REMOVED_NAME "" +static int split_squashfs(struct mtd_info *master, int offset, int *split_offset) +{ + char buf[512]; + struct squashfs_super_block *sb = (struct squashfs_super_block *) buf; + int len, ret; + + ret = master->read(master, offset, sizeof(*sb), &len, buf); + if (ret || (len != sizeof(*sb))) { + printk(KERN_ALERT "split_squashfs: error occured while reading " + "from \"%s\"\n", master->name); + return -EINVAL; + } + + if (*((u32 *) buf) != SQUASHFS_MAGIC) { + printk(KERN_ALERT "split_squashfs: no squashfs found in \"%s\"\n", + master->name); + *split_offset = 0; + return 0; + } + + if (sb->bytes_used <= 0) { + printk(KERN_ALERT "split_squashfs: squashfs is empty in \"%s\"\n", + master->name); + *split_offset = 0; + return 0; + } + + len = (u32) sb->bytes_used; + len += (offset & 0x000fffff); + len += (master->erasesize - 1); + len &= ~(master->erasesize - 1); + len -= (offset & 0x000fffff); + *split_offset = offset + len; + + return 0; +} + +static int split_rootfs_data(struct mtd_info *master, struct mtd_info *rpart, struct mtd_partition *part, + int index) +{ + struct mtd_partition *dpart; + struct mtd_part *slave = NULL; + int split_offset = 0; + int ret; + + ret = split_squashfs(master, part->offset, &split_offset); + if (ret) + return ret; + + if (split_offset <= 0) + return 0; + + dpart = kmalloc(sizeof(*part)+sizeof(ROOTFS_SPLIT_NAME)+1, GFP_KERNEL); + if (dpart == NULL) { + printk(KERN_INFO "split_squashfs: no memory for partition \"%s\"\n", + ROOTFS_SPLIT_NAME); + return -ENOMEM; + } + + memcpy(dpart, part, sizeof(*part)); + dpart->name = (unsigned char *)&dpart[1]; + strcpy(dpart->name, ROOTFS_SPLIT_NAME); + + dpart->size -= split_offset - dpart->offset; + dpart->offset = split_offset; + + if (dpart == NULL) + return 1; + + printk(KERN_INFO "mtd: partition \"%s\" created automatically, ofs=%X, len=%X \n", + ROOTFS_SPLIT_NAME, dpart->offset, dpart->size); + + ret = add_one_partition(master, dpart, index, &slave); + if (ret) + kfree(dpart); + else if (slave) + rpart->split = &slave->mtd; + + return ret; +} + +static int refresh_rootfs_split(struct mtd_info *mtd) +{ + struct mtd_partition tpart; + struct mtd_part *part; + int index = 0; + int offset, size; + int ret; + + part = PART(mtd); + + /* check for the new squashfs offset first */ + ret = split_squashfs(part->master, part->offset, &offset); + if (ret) + return ret; + + if ((offset > 0) && !mtd->split) { + printk(KERN_INFO "%s: creating new split partition for \"%s\"\n", __func__, mtd->name); + /* if we don't have a rootfs split partition, create a new one */ + tpart.name = mtd->name; + tpart.size = mtd->size; + tpart.offset = part->offset; + + /* find the index of the last partition */ + if (!list_empty(&mtd_partitions)) + index = list_first_entry(&mtd_partitions, struct mtd_part, list)->index + 1; + + return split_rootfs_data(part->master, &part->mtd, &tpart, index); + } else if ((offset > 0) && mtd->split) { + /* update the offsets of the existing partition */ + size = mtd->size + part->offset - offset; + + part = PART(mtd->split); + part->offset = offset; + part->mtd.size = size; + printk(KERN_INFO "%s: %s partition \"" ROOTFS_SPLIT_NAME "\", offset: 0x%06x (0x%06x)\n", + __func__, (!strcmp(part->mtd.name, ROOTFS_SPLIT_NAME) ? "updating" : "creating"), + part->offset, part->mtd.size); + strcpy(part->mtd.name, ROOTFS_SPLIT_NAME); + } else if ((offset <= 0) && mtd->split) { + printk(KERN_INFO "%s: removing partition \"%s\"\n", __func__, mtd->split->name); + + /* mark existing partition as removed */ + part = PART(mtd->split); + strcpy(part->mtd.name, ROOTFS_REMOVED_NAME); + part->offset = 0; + part->mtd.size = 0; + } + + return 0; +} +#endif /* CONFIG_MTD_ROOTFS_SPLIT */ + /* * This function, given a master MTD object and a partition table, creates * and registers slave MTD objects which are bound to the master according to @@ -334,171 +646,31 @@ int nbparts) { struct mtd_part *slave; - u_int32_t cur_offset = 0; - int i; + struct mtd_partition *part; + int i, j, ret = 0; printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); - for (i = 0; i < nbparts; i++) { - - /* allocate the partition structure */ - slave = kzalloc (sizeof(*slave), GFP_KERNEL); - if (!slave) { - printk ("memory allocation error while creating partitions for \"%s\"\n", - master->name); - del_mtd_partitions(master); - return -ENOMEM; - } - list_add(&slave->list, &mtd_partitions); - - /* set up the MTD object for this partition */ - slave->mtd.type = master->type; - slave->mtd.flags = master->flags & ~parts[i].mask_flags; - slave->mtd.size = parts[i].size; - slave->mtd.writesize = master->writesize; - slave->mtd.oobsize = master->oobsize; - slave->mtd.oobavail = master->oobavail; - slave->mtd.subpage_sft = master->subpage_sft; - - slave->mtd.name = parts[i].name; - slave->mtd.owner = master->owner; - - slave->mtd.read = part_read; - slave->mtd.write = part_write; - - if (master->panic_write) - slave->mtd.panic_write = part_panic_write; - - if(master->point && master->unpoint){ - slave->mtd.point = part_point; - slave->mtd.unpoint = part_unpoint; - } - - if (master->read_oob) - slave->mtd.read_oob = part_read_oob; - if (master->write_oob) - slave->mtd.write_oob = part_write_oob; - if(master->read_user_prot_reg) - slave->mtd.read_user_prot_reg = part_read_user_prot_reg; - if(master->read_fact_prot_reg) - slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; - if(master->write_user_prot_reg) - slave->mtd.write_user_prot_reg = part_write_user_prot_reg; - if(master->lock_user_prot_reg) - slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; - if(master->get_user_prot_info) - slave->mtd.get_user_prot_info = part_get_user_prot_info; - if(master->get_fact_prot_info) - slave->mtd.get_fact_prot_info = part_get_fact_prot_info; - if (master->sync) - slave->mtd.sync = part_sync; - if (!i && master->suspend && master->resume) { - slave->mtd.suspend = part_suspend; - slave->mtd.resume = part_resume; - } - if (master->writev) - slave->mtd.writev = part_writev; - if (master->lock) - slave->mtd.lock = part_lock; - if (master->unlock) - slave->mtd.unlock = part_unlock; - if (master->block_isbad) - slave->mtd.block_isbad = part_block_isbad; - if (master->block_markbad) - slave->mtd.block_markbad = part_block_markbad; - slave->mtd.erase = part_erase; - slave->master = master; - slave->offset = parts[i].offset; - slave->index = i; - - if (slave->offset == MTDPART_OFS_APPEND) - slave->offset = cur_offset; - if (slave->offset == MTDPART_OFS_NXTBLK) { - slave->offset = cur_offset; - if ((cur_offset % master->erasesize) != 0) { - /* Round up to next erasesize */ - slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; - printk(KERN_NOTICE "Moving partition %d: " - "0x%08x -> 0x%08x\n", i, - cur_offset, slave->offset); - } - } - if (slave->mtd.size == MTDPART_SIZ_FULL) - slave->mtd.size = master->size - slave->offset; - cur_offset = slave->offset + slave->mtd.size; - - printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, - slave->offset + slave->mtd.size, slave->mtd.name); - - /* let's do some sanity checks */ - if (slave->offset >= master->size) { - /* let's register it anyway to preserve ordering */ - slave->offset = 0; - slave->mtd.size = 0; - printk ("mtd: partition \"%s\" is out of reach -- disabled\n", - parts[i].name); - } - if (slave->offset + slave->mtd.size > master->size) { - slave->mtd.size = master->size - slave->offset; - printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", - parts[i].name, master->name, slave->mtd.size); - } - if (master->numeraseregions>1) { - /* Deal with variable erase size stuff */ - int i; - struct mtd_erase_region_info *regions = master->eraseregions; - - /* Find the first erase regions which is part of this partition. */ - for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) - ; - - for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { - if (slave->mtd.erasesize < regions[i].erasesize) { - slave->mtd.erasesize = regions[i].erasesize; - } + for (i = 0, j = 0; i < nbparts; i++) { + part = (struct mtd_partition *) &parts[i]; + ret = add_one_partition(master, part, j, &slave); + if (ret) + return ret; + j++; + + if (strcmp(part->name, "rootfs") == 0 && slave->registered) { +#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV + if (ROOT_DEV == 0) { + printk(KERN_NOTICE "mtd: partition \"rootfs\" " + "set to be root filesystem\n"); + ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, slave->mtd.index); } - } else { - /* Single erase size */ - slave->mtd.erasesize = master->erasesize; - } - - if ((slave->mtd.flags & MTD_WRITEABLE) && - (slave->offset % slave->mtd.erasesize)) { - /* Doesn't start on a boundary of major erase size */ - /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ - slave->mtd.flags &= ~MTD_WRITEABLE; - printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", - parts[i].name); - } - if ((slave->mtd.flags & MTD_WRITEABLE) && - (slave->mtd.size % slave->mtd.erasesize)) { - slave->mtd.flags &= ~MTD_WRITEABLE; - printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", - parts[i].name); - } - - slave->mtd.ecclayout = master->ecclayout; - if (master->block_isbad) { - uint32_t offs = 0; - - while(offs < slave->mtd.size) { - if (master->block_isbad(master, - offs + slave->offset)) - slave->mtd.ecc_stats.badblocks++; - offs += slave->mtd.erasesize; - } - } - - if(parts[i].mtdp) - { /* store the object pointer (caller may or may not register it */ - *parts[i].mtdp = &slave->mtd; - slave->registered = 0; - } - else - { - /* register our partition */ - add_mtd_device(&slave->mtd); - slave->registered = 1; +#endif +#ifdef CONFIG_MTD_ROOTFS_SPLIT + ret = split_rootfs_data(master, &slave->mtd, part, j); + if (ret == 0) + j++; +#endif } } @@ -574,6 +746,32 @@ return ret; } +int refresh_mtd_partitions(struct mtd_info *mtd) +{ + int ret = 0; + + if (IS_PART(mtd)) { + struct mtd_part *part; + struct mtd_info *master; + + part = PART(mtd); + master = part->master; + if (master->refresh_device) + ret = master->refresh_device(master); + } + + if (!ret && mtd->refresh_device) + ret = mtd->refresh_device(mtd); + +#ifdef CONFIG_MTD_ROOTFS_SPLIT + if (!ret && IS_PART(mtd) && !strcmp(mtd->name, "rootfs")) + refresh_rootfs_split(mtd); +#endif + + return 0; +} + EXPORT_SYMBOL_GPL(parse_mtd_partitions); +EXPORT_SYMBOL_GPL(refresh_mtd_partitions); EXPORT_SYMBOL_GPL(register_mtd_parser); EXPORT_SYMBOL_GPL(deregister_mtd_parser); --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -34,6 +34,8 @@ struct block_device *blkdev; struct mtd_info mtd; struct mutex write_mutex; + rwlock_t bdev_mutex; + char devname[0]; }; @@ -86,6 +88,12 @@ size_t len = instr->len; int err; + read_lock(&dev->bdev_mutex); + if (!dev->blkdev) { + err = -EINVAL; + goto done; + } + instr->state = MTD_ERASING; mutex_lock(&dev->write_mutex); err = _block2mtd_erase(dev, from, len); @@ -98,6 +106,10 @@ instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); + +done: + read_unlock(&dev->bdev_mutex); + return err; } @@ -109,10 +121,14 @@ struct page *page; int index = from >> PAGE_SHIFT; int offset = from & (PAGE_SIZE-1); - int cpylen; + int cpylen, err = 0; + + read_lock(&dev->bdev_mutex); + if (!dev->blkdev || (from > mtd->size)) { + err = -EINVAL; + goto done; + } - if (from > mtd->size) - return -EINVAL; if (from + len > mtd->size) len = mtd->size - from; @@ -127,10 +143,14 @@ len = len - cpylen; page = page_read(dev->blkdev->bd_inode->i_mapping, index); - if (!page) - return -ENOMEM; - if (IS_ERR(page)) - return PTR_ERR(page); + if (!page) { + err = -ENOMEM; + goto done; + } + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto done; + } memcpy(buf, page_address(page) + offset, cpylen); page_cache_release(page); @@ -141,7 +161,10 @@ offset = 0; index++; } - return 0; + +done: + read_unlock(&dev->bdev_mutex); + return err; } @@ -193,12 +216,22 @@ size_t *retlen, const u_char *buf) { struct block2mtd_dev *dev = mtd->priv; - int err; + int err = 0; + + read_lock(&dev->bdev_mutex); + if (!dev->blkdev) { + err = -EINVAL; + goto done; + } if (!len) - return 0; - if (to >= mtd->size) - return -ENOSPC; + goto done; + + if (to >= mtd->size) { + err = -ENOSPC; + goto done; + } + if (to + len > mtd->size) len = mtd->size - to; @@ -207,6 +240,9 @@ mutex_unlock(&dev->write_mutex); if (err > 0) err = 0; + +done: + read_unlock(&dev->bdev_mutex); return err; } @@ -215,51 +251,29 @@ static void block2mtd_sync(struct mtd_info *mtd) { struct block2mtd_dev *dev = mtd->priv; - sync_blockdev(dev->blkdev); - return; -} - - -static void block2mtd_free_device(struct block2mtd_dev *dev) -{ - if (!dev) - return; - - kfree(dev->mtd.name); - if (dev->blkdev) { - invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping, - 0, -1); - close_bdev_excl(dev->blkdev); - } + read_lock(&dev->bdev_mutex); + if (dev->blkdev) + sync_blockdev(dev->blkdev); + read_unlock(&dev->bdev_mutex); - kfree(dev); + return; } -/* FIXME: ensure that mtd->size % erase_size == 0 */ -static struct block2mtd_dev *add_device(char *devname, int erase_size, char *mtdname) +static int _open_bdev(struct block2mtd_dev *dev) { struct block_device *bdev; - struct block2mtd_dev *dev; - struct mtd_partition *part; - - if (!devname) - return NULL; - - dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL); - if (!dev) - return NULL; /* Get a handle on the device */ - bdev = open_bdev_excl(devname, O_RDWR, NULL); + bdev = open_bdev_excl(dev->devname, O_RDWR, NULL); #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); + dev_t devt = name_to_dev_t(dev->devname); if (devt) { bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ); } @@ -267,17 +281,96 @@ #endif if (IS_ERR(bdev)) { - ERROR("error: cannot open device %s", devname); - goto devinit_err; + ERROR("error: cannot open device %s", dev->devname); + return 1; } 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; + return 1; } + return 0; +} + +static void _close_bdev(struct block2mtd_dev *dev) +{ + struct block_device *bdev; + + if (!dev->blkdev) + return; + + bdev = dev->blkdev; + invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping, 0, -1); + close_bdev_excl(dev->blkdev); + dev->blkdev = NULL; +} + +static void block2mtd_free_device(struct block2mtd_dev *dev) +{ + if (!dev) + return; + + kfree(dev->mtd.name); + _close_bdev(dev); + kfree(dev); +} + + +static int block2mtd_refresh(struct mtd_info *mtd) +{ + struct block2mtd_dev *dev = mtd->priv; + struct block_device *bdev; + dev_t devt; + int err = 0; + + /* no other mtd function can run at this point */ + write_lock(&dev->bdev_mutex); + + /* get the device number for the whole disk */ + devt = MKDEV(MAJOR(dev->blkdev->bd_dev), 0); + + /* close the old block device */ + _close_bdev(dev); + + /* open the whole disk, issue a partition rescan, then */ + bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ); + if (!bdev || !bdev->bd_disk) + err = -EINVAL; + else { + err = rescan_partitions(bdev->bd_disk, bdev); + } + if (bdev) + close_bdev_excl(bdev); + + /* try to open the partition block device again */ + _open_bdev(dev); + write_unlock(&dev->bdev_mutex); + + return err; +} + +/* FIXME: ensure that mtd->size % erase_size == 0 */ +static struct block2mtd_dev *add_device(char *devname, int erase_size, char *mtdname) +{ + struct block2mtd_dev *dev; + struct mtd_partition *part; + + if (!devname) + return NULL; + + dev = kzalloc(sizeof(struct block2mtd_dev) + strlen(devname) + 1, GFP_KERNEL); + if (!dev) + return NULL; + + strcpy(dev->devname, devname); + + if (_open_bdev(dev)) + goto devinit_err; + mutex_init(&dev->write_mutex); + rwlock_init(&dev->bdev_mutex); /* Setup the MTD structure */ /* make the name contain the block device in */ @@ -304,6 +397,7 @@ dev->mtd.read = block2mtd_read; dev->mtd.priv = dev; dev->mtd.owner = THIS_MODULE; + dev->mtd.refresh_device = block2mtd_refresh; part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL); part->name = dev->mtd.name; --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -17,6 +17,7 @@ #include #include +#include #include @@ -756,6 +757,13 @@ file->f_pos = 0; break; } +#ifdef CONFIG_MTD_PARTITIONS + case MTDREFRESH: + { + ret = refresh_mtd_partitions(mtd); + break; + } +#endif default: ret = -ENOTTY; --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -98,6 +98,7 @@ uint8_t *oobbuf; }; +struct mtd_info; struct mtd_info { u_char type; u_int32_t flags; @@ -213,6 +214,9 @@ struct module *owner; int usecount; + int (*refresh_device)(struct mtd_info *mtd); + struct mtd_info *split; + /* If the driver is something smart, like UBI, it may need to maintain * its own reference counting. The below functions are only for driver. * The driver may register its callbacks. These callbacks are not --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -36,6 +36,7 @@ * erasesize aligned (e.g. use MTDPART_OFS_NEXTBLK). */ +struct mtd_partition; struct mtd_partition { char *name; /* identifier string */ u_int32_t size; /* partition size */ @@ -43,6 +44,7 @@ u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ struct mtd_info **mtdp; /* pointer to store the MTD object */ + int (*refresh_partition)(struct mtd_info *); }; #define MTDPART_OFS_NXTBLK (-2) @@ -52,6 +54,7 @@ int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); int del_mtd_partitions(struct mtd_info *); +int refresh_mtd_partitions(struct mtd_info *); /* * Functions dealing with the various ways of partitioning the space --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -95,6 +95,7 @@ #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) #define MTDFILEMODE _IO('M', 19) +#define MTDREFRESH _IO('M', 23) /* * Obsolete legacy interface. Keep it in order not to break userspace