From 4460bbca4731a3521e30b742b2cc1c3252ab0be5 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 19 Aug 2007 21:53:17 +0000 Subject: [PATCH] add refresh ioctl to mtd (for rescanning partitions after reflash) SVN-Revision: 8438 --- ...td_init.patch => 060-block2mtd_init.patch} | 0 ...tfs_split.patch => 065-rootfs_split.patch} | 605 ++++++++++++++++-- 2 files changed, 537 insertions(+), 68 deletions(-) rename target/linux/generic-2.6/patches-2.6.22/{065-block2mtd_init.patch => 060-block2mtd_init.patch} (100%) rename target/linux/generic-2.6/patches-2.6.22/{060-rootfs_split.patch => 065-rootfs_split.patch} (50%) diff --git a/target/linux/generic-2.6/patches-2.6.22/065-block2mtd_init.patch b/target/linux/generic-2.6/patches-2.6.22/060-block2mtd_init.patch similarity index 100% rename from target/linux/generic-2.6/patches-2.6.22/065-block2mtd_init.patch rename to target/linux/generic-2.6/patches-2.6.22/060-block2mtd_init.patch diff --git a/target/linux/generic-2.6/patches-2.6.22/060-rootfs_split.patch b/target/linux/generic-2.6/patches-2.6.22/065-rootfs_split.patch similarity index 50% rename from target/linux/generic-2.6/patches-2.6.22/060-rootfs_split.patch rename to target/linux/generic-2.6/patches-2.6.22/065-rootfs_split.patch index 37d645eb09..7d9d4ca118 100644 --- a/target/linux/generic-2.6/patches-2.6.22/060-rootfs_split.patch +++ b/target/linux/generic-2.6/patches-2.6.22/065-rootfs_split.patch @@ -1,8 +1,8 @@ -Index: linux/drivers/mtd/Kconfig +Index: linux-2.6.22.1/drivers/mtd/Kconfig =================================================================== ---- linux.orig/drivers/mtd/Kconfig -+++ linux/drivers/mtd/Kconfig -@@ -47,6 +47,16 @@ config MTD_PARTITIONS +--- linux-2.6.22.1.orig/drivers/mtd/Kconfig 2007-08-19 23:30:07.068115074 +0200 ++++ linux-2.6.22.1/drivers/mtd/Kconfig 2007-08-19 23:31:43.961636725 +0200 +@@ -47,6 +47,16 @@ devices. Partitioning on NFTL 'devices' is a different - that's the 'normal' form of partitioning used on a block device. @@ -19,10 +19,10 @@ Index: linux/drivers/mtd/Kconfig config MTD_REDBOOT_PARTS tristate "RedBoot partition table parsing" depends on MTD_PARTITIONS -Index: linux/drivers/mtd/mtdpart.c +Index: linux-2.6.22.1/drivers/mtd/mtdpart.c =================================================================== ---- linux.orig/drivers/mtd/mtdpart.c -+++ linux/drivers/mtd/mtdpart.c +--- linux-2.6.22.1.orig/drivers/mtd/mtdpart.c 2007-08-19 23:30:07.076115531 +0200 ++++ linux-2.6.22.1/drivers/mtd/mtdpart.c 2007-08-19 23:31:44.065642653 +0200 @@ -20,6 +20,8 @@ #include #include @@ -32,7 +32,16 @@ Index: linux/drivers/mtd/mtdpart.c /* Our partition linked list */ static LIST_HEAD(mtd_partitions); -@@ -308,6 +310,265 @@ int del_mtd_partitions(struct mtd_info * +@@ -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 +@@ -308,6 +310,312 @@ return 0; } @@ -66,6 +75,7 @@ Index: linux/drivers/mtd/mtdpart.c + + slave->mtd.read = part_read; + slave->mtd.write = part_write; ++ slave->mtd.refresh_device = part->refresh_partition; + + if(master->point && master->unpoint){ + slave->mtd.point = part_point; @@ -207,98 +217,144 @@ Index: linux/drivers/mtd/mtdpart.c + +#ifdef CONFIG_MTD_ROOTFS_SPLIT +#define ROOTFS_SPLIT_NAME "rootfs_data" -+static int split_squashfs(struct mtd_info *master, struct mtd_partition *old, -+ struct mtd_partition **new) ++#define ROOTFS_REMOVED_NAME "" ++static int split_squashfs(struct mtd_info *master, int offset, int *split_offset) +{ -+ struct mtd_partition *part = NULL; -+ int len; + char buf[512]; + struct squashfs_super_block *sb = (struct squashfs_super_block *) buf; -+ int ret; ++ int len, ret; + -+ ret = master->read(master, old->offset, sizeof(*sb), &len, buf); -+ if (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); -+ goto out; -+ } -+ -+ if (len != sizeof(*sb)) { -+ printk(KERN_ALERT "split_squashfs: unable to read superblock " -+ "from \"%s\"\n", master->name); -+ ret=-1; -+ goto out; ++ return -EINVAL; + } + + if (*((u32 *) buf) != SQUASHFS_MAGIC) { + printk(KERN_ALERT "split_squasfs: no squashfs found in \"%s\"\n", + master->name); -+ ret=0; -+ goto out; ++ *split_offset = 0; ++ return 0; + } + + if (sb->bytes_used <= 0) { + printk(KERN_ALERT "split_squashfs: squashfs is empty in \"%s\"\n", + master->name); -+ ret=0; -+ goto out; -+ } -+ -+ part = kmalloc(sizeof(*part)+sizeof(ROOTFS_SPLIT_NAME)+1, GFP_KERNEL); -+ if (part == NULL) { -+ printk(KERN_INFO "split_squashfs: no memory for partition \"%s\"\n", -+ ROOTFS_SPLIT_NAME); -+ ret = -ENOMEM; -+ goto out; ++ *split_offset = 0; ++ return 0; + } + -+ memcpy(part, old, sizeof(*part)); -+ part->name = (unsigned char *)&part[1]; -+ strcpy(part->name, ROOTFS_SPLIT_NAME); -+ + len = (u32) sb->bytes_used; -+ len += (part->offset & 0x000fffff); ++ len += (offset & 0x000fffff); + len += (master->erasesize - 1); + len &= ~(master->erasesize - 1); -+ len -= (part->offset & 0x000fffff); -+ part->offset += len; -+ part->size -= len; ++ len -= (offset & 0x000fffff); ++ *split_offset = offset + len; + -+ ret = 0; -+ -+out: -+ *new = part; -+ return ret; ++ return 0; +} + -+static int split_rootfs_data(struct mtd_info *master, struct mtd_partition *part, ++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, &dpart); ++ 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, NULL); ++ 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 -@@ -320,168 +581,31 @@ int add_mtd_partitions(struct mtd_info * +@@ -320,168 +628,31 @@ int nbparts) { struct mtd_part *slave; @@ -423,7 +479,20 @@ Index: linux/drivers/mtd/mtdpart.c - 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; @@ -453,20 +522,7 @@ Index: linux/drivers/mtd/mtdpart.c - offs + slave->offset)) - slave->mtd.ecc_stats.badblocks++; - offs += slave->mtd.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); - } +- } - } - - if(parts[i].mtdp) @@ -481,10 +537,423 @@ Index: linux/drivers/mtd/mtdpart.c - slave->registered = 1; +#endif +#ifdef CONFIG_MTD_ROOTFS_SPLIT -+ ret = split_rootfs_data(master, part, j); ++ ret = split_rootfs_data(master, &slave->mtd, part, j); + if (ret == 0) + j++; +#endif } } +@@ -557,7 +728,33 @@ + 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); + +Index: linux-2.6.22.1/drivers/mtd/devices/block2mtd.c +=================================================================== +--- linux-2.6.22.1.orig/drivers/mtd/devices/block2mtd.c 2007-08-19 23:30:49.794549917 +0200 ++++ linux-2.6.22.1/drivers/mtd/devices/block2mtd.c 2007-08-19 23:31:44.025640373 +0200 +@@ -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; +Index: linux-2.6.22.1/drivers/mtd/mtdchar.c +=================================================================== +--- linux-2.6.22.1.orig/drivers/mtd/mtdchar.c 2007-08-19 23:30:07.096116671 +0200 ++++ linux-2.6.22.1/drivers/mtd/mtdchar.c 2007-08-19 23:31:44.041641284 +0200 +@@ -16,6 +16,7 @@ + + #include + #include ++#include + + #include + +@@ -752,6 +753,13 @@ + file->f_pos = 0; + break; + } ++#ifdef CONFIG_MTD_PARTITIONS ++ case MTDREFRESH: ++ { ++ ret = refresh_mtd_partitions(mtd); ++ break; ++ } ++#endif + + default: + ret = -ENOTTY; +Index: linux-2.6.22.1/include/linux/mtd/mtd.h +=================================================================== +--- linux-2.6.22.1.orig/include/linux/mtd/mtd.h 2007-08-19 23:30:07.100116893 +0200 ++++ linux-2.6.22.1/include/linux/mtd/mtd.h 2007-08-19 23:31:44.105644933 +0200 +@@ -98,6 +98,7 @@ + uint8_t *oobbuf; + }; + ++struct mtd_info; + struct mtd_info { + u_char type; + u_int32_t flags; +@@ -195,6 +196,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 +Index: linux-2.6.22.1/include/linux/mtd/partitions.h +=================================================================== +--- linux-2.6.22.1.orig/include/linux/mtd/partitions.h 2007-08-19 23:30:07.112117591 +0200 ++++ linux-2.6.22.1/include/linux/mtd/partitions.h 2007-08-19 23:31:44.133646528 +0200 +@@ -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 +Index: linux-2.6.22.1/include/mtd/mtd-abi.h +=================================================================== +--- linux-2.6.22.1.orig/include/mtd/mtd-abi.h 2007-08-19 23:30:07.124118264 +0200 ++++ linux-2.6.22.1/include/mtd/mtd-abi.h 2007-08-19 23:31:44.157647897 +0200 +@@ -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 -- 2.30.2