diff options
Diffstat (limited to 'drivers/mtd/mtdcore.c')
-rw-r--r-- | drivers/mtd/mtdcore.c | 285 |
1 files changed, 156 insertions, 129 deletions
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index b177e750efc..a1b8b70d2d0 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -19,7 +19,9 @@ #include <linux/init.h> #include <linux/mtd/compatmac.h> #include <linux/proc_fs.h> +#include <linux/idr.h> #include <linux/backing-dev.h> +#include <linux/gfp.h> #include <linux/mtd/mtd.h> @@ -63,13 +65,18 @@ static struct class mtd_class = { .resume = mtd_cls_resume, }; +static DEFINE_IDR(mtd_idr); + /* These are exported solely for the purpose of mtd_blkdevs.c. You should not use them for _anything_ else */ DEFINE_MUTEX(mtd_table_mutex); -struct mtd_info *mtd_table[MAX_MTD_DEVICES]; - EXPORT_SYMBOL_GPL(mtd_table_mutex); -EXPORT_SYMBOL_GPL(mtd_table); + +struct mtd_info *__mtd_next_device(int i) +{ + return idr_get_next(&mtd_idr, &i); +} +EXPORT_SYMBOL_GPL(__mtd_next_device); static LIST_HEAD(mtd_notifiers); @@ -265,13 +272,13 @@ static struct device_type mtd_devtype = { * Add a device to the list of MTD devices present in the system, and * notify each currently active MTD 'user' of its arrival. Returns * zero on success or 1 on failure, which currently will only happen - * if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16) - * or there's a sysfs error. + * if there is insufficient memory or a sysfs error. */ int add_mtd_device(struct mtd_info *mtd) { - int i; + struct mtd_notifier *not; + int i, error; if (!mtd->backing_dev_info) { switch (mtd->type) { @@ -290,70 +297,73 @@ int add_mtd_device(struct mtd_info *mtd) BUG_ON(mtd->writesize == 0); mutex_lock(&mtd_table_mutex); - for (i=0; i < MAX_MTD_DEVICES; i++) - if (!mtd_table[i]) { - struct mtd_notifier *not; - - mtd_table[i] = mtd; - mtd->index = i; - mtd->usecount = 0; - - if (is_power_of_2(mtd->erasesize)) - mtd->erasesize_shift = ffs(mtd->erasesize) - 1; - else - mtd->erasesize_shift = 0; - - if (is_power_of_2(mtd->writesize)) - mtd->writesize_shift = ffs(mtd->writesize) - 1; - else - mtd->writesize_shift = 0; - - mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; - mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; - - /* Some chips always power up locked. Unlock them now */ - if ((mtd->flags & MTD_WRITEABLE) - && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { - if (mtd->unlock(mtd, 0, mtd->size)) - printk(KERN_WARNING - "%s: unlock failed, " - "writes may not work\n", - mtd->name); - } + do { + if (!idr_pre_get(&mtd_idr, GFP_KERNEL)) + goto fail_locked; + error = idr_get_new(&mtd_idr, mtd, &i); + } while (error == -EAGAIN); - /* Caller should have set dev.parent to match the - * physical device. - */ - mtd->dev.type = &mtd_devtype; - mtd->dev.class = &mtd_class; - mtd->dev.devt = MTD_DEVT(i); - dev_set_name(&mtd->dev, "mtd%d", i); - dev_set_drvdata(&mtd->dev, mtd); - if (device_register(&mtd->dev) != 0) { - mtd_table[i] = NULL; - break; - } + if (error) + goto fail_locked; - if (MTD_DEVT(i)) - device_create(&mtd_class, mtd->dev.parent, - MTD_DEVT(i) + 1, - NULL, "mtd%dro", i); - - DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); - /* No need to get a refcount on the module containing - the notifier, since we hold the mtd_table_mutex */ - list_for_each_entry(not, &mtd_notifiers, list) - not->add(mtd); - - mutex_unlock(&mtd_table_mutex); - /* We _know_ we aren't being removed, because - our caller is still holding us here. So none - of this try_ nonsense, and no bitching about it - either. :) */ - __module_get(THIS_MODULE); - return 0; - } + mtd->index = i; + mtd->usecount = 0; + + if (is_power_of_2(mtd->erasesize)) + mtd->erasesize_shift = ffs(mtd->erasesize) - 1; + else + mtd->erasesize_shift = 0; + + if (is_power_of_2(mtd->writesize)) + mtd->writesize_shift = ffs(mtd->writesize) - 1; + else + mtd->writesize_shift = 0; + + mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; + mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; + + /* Some chips always power up locked. Unlock them now */ + if ((mtd->flags & MTD_WRITEABLE) + && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { + if (mtd->unlock(mtd, 0, mtd->size)) + printk(KERN_WARNING + "%s: unlock failed, writes may not work\n", + mtd->name); + } + + /* Caller should have set dev.parent to match the + * physical device. + */ + mtd->dev.type = &mtd_devtype; + mtd->dev.class = &mtd_class; + mtd->dev.devt = MTD_DEVT(i); + dev_set_name(&mtd->dev, "mtd%d", i); + dev_set_drvdata(&mtd->dev, mtd); + if (device_register(&mtd->dev) != 0) + goto fail_added; + + if (MTD_DEVT(i)) + device_create(&mtd_class, mtd->dev.parent, + MTD_DEVT(i) + 1, + NULL, "mtd%dro", i); + + DEBUG(0, "mtd: Giving out device %d to %s\n", i, mtd->name); + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each_entry(not, &mtd_notifiers, list) + not->add(mtd); + + mutex_unlock(&mtd_table_mutex); + /* We _know_ we aren't being removed, because + our caller is still holding us here. So none + of this try_ nonsense, and no bitching about it + either. :) */ + __module_get(THIS_MODULE); + return 0; +fail_added: + idr_remove(&mtd_idr, i); +fail_locked: mutex_unlock(&mtd_table_mutex); return 1; } @@ -371,31 +381,34 @@ int add_mtd_device(struct mtd_info *mtd) int del_mtd_device (struct mtd_info *mtd) { int ret; + struct mtd_notifier *not; mutex_lock(&mtd_table_mutex); - if (mtd_table[mtd->index] != mtd) { + if (idr_find(&mtd_idr, mtd->index) != mtd) { ret = -ENODEV; - } else if (mtd->usecount) { + goto out_error; + } + + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each_entry(not, &mtd_notifiers, list) + not->remove(mtd); + + if (mtd->usecount) { printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", mtd->index, mtd->name, mtd->usecount); ret = -EBUSY; } else { - struct mtd_notifier *not; - device_unregister(&mtd->dev); - /* No need to get a refcount on the module containing - the notifier, since we hold the mtd_table_mutex */ - list_for_each_entry(not, &mtd_notifiers, list) - not->remove(mtd); - - mtd_table[mtd->index] = NULL; + idr_remove(&mtd_idr, mtd->index); module_put(THIS_MODULE); ret = 0; } +out_error: mutex_unlock(&mtd_table_mutex); return ret; } @@ -411,7 +424,7 @@ int del_mtd_device (struct mtd_info *mtd) void register_mtd_user (struct mtd_notifier *new) { - int i; + struct mtd_info *mtd; mutex_lock(&mtd_table_mutex); @@ -419,9 +432,8 @@ void register_mtd_user (struct mtd_notifier *new) __module_get(THIS_MODULE); - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i]) - new->add(mtd_table[i]); + mtd_for_each_device(mtd) + new->add(mtd); mutex_unlock(&mtd_table_mutex); } @@ -438,15 +450,14 @@ void register_mtd_user (struct mtd_notifier *new) int unregister_mtd_user (struct mtd_notifier *old) { - int i; + struct mtd_info *mtd; mutex_lock(&mtd_table_mutex); module_put(THIS_MODULE); - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i]) - old->remove(mtd_table[i]); + mtd_for_each_device(mtd) + old->remove(mtd); list_del(&old->list); mutex_unlock(&mtd_table_mutex); @@ -468,42 +479,56 @@ int unregister_mtd_user (struct mtd_notifier *old) struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) { - struct mtd_info *ret = NULL; - int i, err = -ENODEV; + struct mtd_info *ret = NULL, *other; + int err = -ENODEV; mutex_lock(&mtd_table_mutex); if (num == -1) { - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i] == mtd) - ret = mtd_table[i]; - } else if (num >= 0 && num < MAX_MTD_DEVICES) { - ret = mtd_table[num]; + mtd_for_each_device(other) { + if (other == mtd) { + ret = mtd; + break; + } + } + } else if (num >= 0) { + ret = idr_find(&mtd_idr, num); if (mtd && mtd != ret) ret = NULL; } - if (!ret) - goto out_unlock; - - if (!try_module_get(ret->owner)) - goto out_unlock; - - if (ret->get_device) { - err = ret->get_device(ret); - if (err) - goto out_put; + if (!ret) { + ret = ERR_PTR(err); + goto out; } - ret->usecount++; + err = __get_mtd_device(ret); + if (err) + ret = ERR_PTR(err); +out: mutex_unlock(&mtd_table_mutex); return ret; +} -out_put: - module_put(ret->owner); -out_unlock: - mutex_unlock(&mtd_table_mutex); - return ERR_PTR(err); + +int __get_mtd_device(struct mtd_info *mtd) +{ + int err; + + if (!try_module_get(mtd->owner)) + return -ENODEV; + + if (mtd->get_device) { + + err = mtd->get_device(mtd); + + if (err) { + module_put(mtd->owner); + return err; + } + } + mtd->usecount++; + return 0; } /** @@ -517,14 +542,14 @@ out_unlock: struct mtd_info *get_mtd_device_nm(const char *name) { - int i, err = -ENODEV; - struct mtd_info *mtd = NULL; + int err = -ENODEV; + struct mtd_info *mtd = NULL, *other; mutex_lock(&mtd_table_mutex); - for (i = 0; i < MAX_MTD_DEVICES; i++) { - if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { - mtd = mtd_table[i]; + mtd_for_each_device(other) { + if (!strcmp(name, other->name)) { + mtd = other; break; } } @@ -554,14 +579,19 @@ out_unlock: void put_mtd_device(struct mtd_info *mtd) { - int c; - mutex_lock(&mtd_table_mutex); - c = --mtd->usecount; + __put_mtd_device(mtd); + mutex_unlock(&mtd_table_mutex); + +} + +void __put_mtd_device(struct mtd_info *mtd) +{ + --mtd->usecount; + BUG_ON(mtd->usecount < 0); + if (mtd->put_device) mtd->put_device(mtd); - mutex_unlock(&mtd_table_mutex); - BUG_ON(c < 0); module_put(mtd->owner); } @@ -599,7 +629,9 @@ EXPORT_SYMBOL_GPL(add_mtd_device); EXPORT_SYMBOL_GPL(del_mtd_device); EXPORT_SYMBOL_GPL(get_mtd_device); EXPORT_SYMBOL_GPL(get_mtd_device_nm); +EXPORT_SYMBOL_GPL(__get_mtd_device); EXPORT_SYMBOL_GPL(put_mtd_device); +EXPORT_SYMBOL_GPL(__put_mtd_device); EXPORT_SYMBOL_GPL(register_mtd_user); EXPORT_SYMBOL_GPL(unregister_mtd_user); EXPORT_SYMBOL_GPL(default_mtd_writev); @@ -611,14 +643,9 @@ EXPORT_SYMBOL_GPL(default_mtd_writev); static struct proc_dir_entry *proc_mtd; -static inline int mtd_proc_info (char *buf, int i) +static inline int mtd_proc_info(char *buf, struct mtd_info *this) { - struct mtd_info *this = mtd_table[i]; - - if (!this) - return 0; - - return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", i, + return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", this->index, (unsigned long long)this->size, this->erasesize, this->name); } @@ -626,15 +653,15 @@ static inline int mtd_proc_info (char *buf, int i) static int mtd_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data_unused) { - int len, l, i; + struct mtd_info *mtd; + int len, l; off_t begin = 0; mutex_lock(&mtd_table_mutex); len = sprintf(page, "dev: size erasesize name\n"); - for (i=0; i< MAX_MTD_DEVICES; i++) { - - l = mtd_proc_info(page + len, i); + mtd_for_each_device(mtd) { + l = mtd_proc_info(page + len, mtd); len += l; if (len+begin > off+count) goto done; |