From: Richard Weinberger <rich...@nod.at>

Allow users to define weakpages/blocks while creating
a new nandsim instance.

Signed-off-by: Richard Weinberger <rich...@nod.at>
---
 drivers/mtd/nand/nandsim.c  | 338 ++++++++++++++++++++++++++++++++++++--------
 include/linux/mtd/nandsim.h |   7 +-
 2 files changed, 285 insertions(+), 60 deletions(-)

diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index d1402df..b58d1348 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -322,9 +322,9 @@ struct nandsim {
        struct ns_backend_ops *bops;
        void *backend_data;
 
-       struct list_head weak_blocks;
-       struct list_head weak_pages;
-       struct list_head grave_pages;
+       struct list_head *weak_blocks;
+       struct list_head *weak_pages;
+       struct list_head *grave_pages;
 
        unsigned long *erase_block_wear;
        unsigned int wear_eb_count;
@@ -387,6 +387,11 @@ static struct nandsim_operations {
                               STATE_DATAOUT, STATE_READY}},
 };
 
+struct bad_block {
+       struct list_head list;
+       unsigned int erase_block_no;
+};
+
 struct weak_block {
        struct list_head list;
        unsigned int erase_block_no;
@@ -957,16 +962,46 @@ static void free_nandsim(struct nandsim *ns)
        kfree(ns->buf.byte);
 }
 
-static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd,
+static int process_badblocks(struct nandsim_params *nsparam, struct nandsim 
*ns)
+{
+       loff_t offset;
+       struct bad_block *bb, *_bb;
+       struct mtd_info *nsmtd = ns_to_mtd(ns);
+
+       if (!nsparam->bad_blocks)
+               return 0;
+
+       list_for_each_entry_safe(bb, _bb, nsparam->bad_blocks, list) {
+               offset = (loff_t)bb->erase_block_no * ns->geom.secsz;
+               if (mtd_block_markbad(nsmtd, offset))
+                       pr_err("invalid badblocks: %i:\n", bb->erase_block_no);
+
+               list_del(&bb->list);
+               kfree(bb);
+       }
+
+       kfree(nsparam->bad_blocks);
+       nsparam->bad_blocks = NULL;
+
+       return 0;
+}
+
+static int parse_badblocks(struct nandsim_params *nsparam,
                           unsigned char *badblocks)
 {
        char *w;
        int zero_ok;
        unsigned int erase_block_no;
-       loff_t offset;
+       struct bad_block *bb;
+
+       nsparam->bad_blocks = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+       if (!nsparam->bad_blocks)
+               return -ENOMEM;
 
+       INIT_LIST_HEAD(nsparam->bad_blocks);
        if (!badblocks)
                return 0;
+
        w = badblocks;
        do {
                zero_ok = (*w == '0' ? 1 : 0);
@@ -975,18 +1010,21 @@ static int parse_badblocks(struct nandsim *ns, struct 
mtd_info *mtd,
                        pr_err("invalid badblocks.\n");
                        return -EINVAL;
                }
-               offset = (loff_t)erase_block_no * ns->geom.secsz;
-               if (mtd_block_markbad(mtd, offset)) {
-                       pr_err("invalid badblocks.\n");
-                       return -EINVAL;
-               }
+               bb = kzalloc(sizeof(*bb), GFP_KERNEL);
+               if (!bb)
+                       return -ENOMEM;
+
+               bb->erase_block_no = erase_block_no;
+               list_add(&bb->list, nsparam->bad_blocks);
+
                if (*w == ',')
                        w += 1;
        } while (*w);
        return 0;
 }
 
-static int parse_weakblocks(struct nandsim *ns, unsigned char *weakblocks)
+static int parse_weakblocks(struct nandsim_params *nsparam,
+                           unsigned char *weakblocks)
 {
        char *w;
        int zero_ok;
@@ -994,8 +1032,14 @@ static int parse_weakblocks(struct nandsim *ns, unsigned 
char *weakblocks)
        unsigned int max_erases;
        struct weak_block *wb;
 
+       nsparam->weak_blocks = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+       if (!nsparam->weak_blocks)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(nsparam->weak_blocks);
        if (!weakblocks)
                return 0;
+
        w = weakblocks;
        do {
                zero_ok = (*w == '0' ? 1 : 0);
@@ -1018,7 +1062,7 @@ static int parse_weakblocks(struct nandsim *ns, unsigned 
char *weakblocks)
                }
                wb->erase_block_no = erase_block_no;
                wb->max_erases = max_erases;
-               list_add(&wb->list, &ns->weak_blocks);
+               list_add(&wb->list, nsparam->weak_blocks);
        } while (*w);
        return 0;
 }
@@ -1027,7 +1071,7 @@ static int erase_error(struct nandsim *ns, unsigned int 
erase_block_no)
 {
        struct weak_block *wb;
 
-       list_for_each_entry(wb, &ns->weak_blocks, list)
+       list_for_each_entry(wb, ns->weak_blocks, list)
                if (wb->erase_block_no == erase_block_no) {
                        if (wb->erases_done >= wb->max_erases)
                                return 1;
@@ -1037,7 +1081,8 @@ static int erase_error(struct nandsim *ns, unsigned int 
erase_block_no)
        return 0;
 }
 
-static int parse_weakpages(struct nandsim *ns, unsigned char *weakpages)
+static int parse_weakpages(struct nandsim_params *nsparam,
+                          unsigned char *weakpages)
 {
        char *w;
        int zero_ok;
@@ -1045,8 +1090,14 @@ static int parse_weakpages(struct nandsim *ns, unsigned 
char *weakpages)
        unsigned int max_writes;
        struct weak_page *wp;
 
+       nsparam->weak_pages = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+       if (!nsparam->weak_pages)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(nsparam->weak_pages);
        if (!weakpages)
                return 0;
+
        w = weakpages;
        do {
                zero_ok = (*w == '0' ? 1 : 0);
@@ -1069,7 +1120,7 @@ static int parse_weakpages(struct nandsim *ns, unsigned 
char *weakpages)
                }
                wp->page_no = page_no;
                wp->max_writes = max_writes;
-               list_add(&wp->list, &ns->weak_pages);
+               list_add(&wp->list, nsparam->weak_pages);
        } while (*w);
        return 0;
 }
@@ -1078,7 +1129,7 @@ static int write_error(struct nandsim *ns, unsigned int 
page_no)
 {
        struct weak_page *wp;
 
-       list_for_each_entry(wp, &ns->weak_pages, list)
+       list_for_each_entry(wp, ns->weak_pages, list)
                if (wp->page_no == page_no) {
                        if (wp->writes_done >= wp->max_writes)
                                return 1;
@@ -1088,7 +1139,7 @@ static int write_error(struct nandsim *ns, unsigned int 
page_no)
        return 0;
 }
 
-static int parse_gravepages(struct nandsim *ns, unsigned char *gravepages)
+static int parse_gravepages(struct nandsim_params *nsparam, unsigned char 
*gravepages)
 {
        char *g;
        int zero_ok;
@@ -1096,8 +1147,14 @@ static int parse_gravepages(struct nandsim *ns, unsigned 
char *gravepages)
        unsigned int max_reads;
        struct grave_page *gp;
 
+       nsparam->grave_pages = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+       if (!nsparam->grave_pages)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(nsparam->grave_pages);
        if (!gravepages)
                return 0;
+
        g = gravepages;
        do {
                zero_ok = (*g == '0' ? 1 : 0);
@@ -1120,7 +1177,7 @@ static int parse_gravepages(struct nandsim *ns, unsigned 
char *gravepages)
                }
                gp->page_no = page_no;
                gp->max_reads = max_reads;
-               list_add(&gp->list, &ns->grave_pages);
+               list_add(&gp->list, nsparam->grave_pages);
        } while (*g);
        return 0;
 }
@@ -1129,7 +1186,7 @@ static int read_error(struct nandsim *ns, unsigned int 
page_no)
 {
        struct grave_page *gp;
 
-       list_for_each_entry(gp, &ns->grave_pages, list)
+       list_for_each_entry(gp, ns->grave_pages, list)
                if (gp->page_no == page_no) {
                        if (gp->reads_done >= gp->max_reads)
                                return 1;
@@ -1142,17 +1199,31 @@ static int read_error(struct nandsim *ns, unsigned int 
page_no)
 static void free_lists(struct nandsim *ns)
 {
        struct list_head *pos, *n;
-       list_for_each_safe(pos, n, &ns->weak_blocks) {
-               list_del(pos);
-               kfree(list_entry(pos, struct weak_block, list));
+
+       if (ns->weak_blocks) {
+               list_for_each_safe(pos, n, ns->weak_blocks) {
+                       list_del(pos);
+                       kfree(list_entry(pos, struct weak_block, list));
+               }
+
+               kfree(ns->weak_blocks);
+               ns->weak_blocks = NULL;
        }
-       list_for_each_safe(pos, n, &ns->weak_pages) {
-               list_del(pos);
-               kfree(list_entry(pos, struct weak_page, list));
+       if (ns->weak_pages) {
+               list_for_each_safe(pos, n, ns->weak_pages) {
+                       list_del(pos);
+                       kfree(list_entry(pos, struct weak_page, list));
+               }
+               kfree(ns->weak_pages);
+               ns->weak_pages = NULL;
        }
-       list_for_each_safe(pos, n, &ns->grave_pages) {
-               list_del(pos);
-               kfree(list_entry(pos, struct grave_page, list));
+       if (ns->grave_pages) {
+               list_for_each_safe(pos, n, ns->grave_pages) {
+                       list_del(pos);
+                       kfree(list_entry(pos, struct grave_page, list));
+               }
+               kfree(ns->grave_pages);
+               ns->grave_pages = NULL;
        }
        kfree(ns->erase_block_wear);
 }
@@ -2579,8 +2650,122 @@ static void ns_nand_read_buf(struct mtd_info *mtd, 
u_char *buf, int len)
        return;
 }
 
-static int ns_ctrl_new_instance(struct ns_new_instance_req *req)
+static void destroy_nsparam_lists(struct nandsim_params *nsparam)
+{
+       struct list_head *pos, *n;
+
+       if (nsparam->bad_blocks) {
+               list_for_each_safe(pos, n, nsparam->bad_blocks) {
+                       list_del(pos);
+                       kfree(list_entry(pos, struct bad_block, list));
+               }
+       }
+
+       if (nsparam->weak_blocks) {
+               list_for_each_safe(pos, n, nsparam->weak_blocks) {
+                       list_del(pos);
+                       kfree(list_entry(pos, struct weak_block, list));
+               }
+       }
+
+       if (nsparam->weak_pages) {
+               list_for_each_safe(pos, n, nsparam->weak_pages) {
+                       list_del(pos);
+                       kfree(list_entry(pos, struct weak_page, list));
+               }
+       }
+
+       if (nsparam->grave_pages) {
+               list_for_each_safe(pos, n, nsparam->grave_pages) {
+                       list_del(pos);
+                       kfree(list_entry(pos, struct grave_page, list));
+               }
+       }
+
+       kfree(nsparam->bad_blocks);
+       kfree(nsparam->weak_blocks);
+       kfree(nsparam->weak_pages);
+       kfree(nsparam->grave_pages);
+}
+
+static int process_element_params(struct nandsim_params *nsparam, int num,
+                                 void __user *elem_argp)
+{
+       int i, err;
+       struct ns_simelement_prop sep;
+
+       for (i = 0; i < num; i++) {
+               err = copy_from_user(&sep, elem_argp + (i * sizeof(sep)),
+                                    sizeof(sep));
+               if (err)
+                       goto out_err;
+
+               err = -ENOMEM;
+
+               switch (sep.elem_type) {
+               case NANDSIM_SIMELEM_BADBLOCK:
+               {
+                       struct bad_block *bb = kzalloc(sizeof(*bb), GFP_KERNEL);
+
+                       if (!bb)
+                               goto out_err;
+
+                       bb->erase_block_no = sep.elem_id;
+                       list_add(&bb->list, nsparam->bad_blocks);
+               }
+               break;
+               case NANDSIM_SIMELEM_WEAKBLOCK:
+               {
+                       struct weak_block *wb = kzalloc(sizeof(*wb), 
GFP_KERNEL);
+
+                       if (!wb)
+                               goto out_err;
+
+                       wb->erase_block_no = sep.elem_id;
+                       wb->max_erases = sep.elem_attr;
+                       list_add(&wb->list, nsparam->weak_blocks);
+               }
+               break;
+               case NANDSIM_SIMELEM_WEAKPAGE:
+               {
+                       struct weak_page *wp = kzalloc(sizeof(*wp), GFP_KERNEL);
+
+                       if (!wp)
+                               goto out_err;
+
+                       wp->page_no = sep.elem_id;
+                       wp->max_writes = sep.elem_attr;
+                       list_add(&wp->list, nsparam->weak_pages);
+               }
+               break;
+               case NANDSIM_SIMELEM_GRAVEPAGE:
+               {
+                       struct grave_page *gp = kzalloc(sizeof(*gp), 
GFP_KERNEL);
+
+                       if (!gp)
+                               goto out_err;
+
+                       gp->page_no = sep.elem_id;
+                       gp->max_reads = sep.elem_attr;
+                       list_add(&gp->list, nsparam->grave_pages);
+               }
+               break;
+               default:
+                       err = -EINVAL;
+                       goto out_err;
+               }
+       }
+
+       return 0;
+
+out_err:
+       destroy_nsparam_lists(nsparam);
+       return err;
+}
+
+static int ns_ctrl_new_instance(struct ns_new_instance_req *req, void __user 
*elem_argp)
 {
+       int ret = -EINVAL;
        struct mtd_info *nsmtd;
        struct nand_chip *chip;
        struct nandsim *ns;
@@ -2599,20 +2784,20 @@ static int ns_ctrl_new_instance(struct 
ns_new_instance_req *req)
        nsparam->overridesize = req->overridesize;
 
        if (req->bch_strength && req->no_oob)
-               goto err_inval;
+               goto err;
 
        if (req->access_delay && req->program_delay && req->erase_delay &&
            req->output_cycle && req->input_cycle) {
                if (req->access_delay > MAX_UDELAY_MS * 1000)
-                       goto err_inval;
+                       goto err;
                if (req->program_delay > MAX_UDELAY_MS * 1000)
-                       goto err_inval;
+                       goto err;
                if (req->erase_delay > 1000)
-                       goto err_inval;
+                       goto err;
                if (req->output_cycle > MAX_UDELAY_MS * 1000)
-                       goto err_inval;
+                       goto err;
                if (req->input_cycle > MAX_UDELAY_MS * 1000)
-                       goto err_inval;
+                       goto err;
 
                nsparam->access_delay = req->access_delay;
                nsparam->program_delay = req->program_delay;
@@ -2623,7 +2808,7 @@ static int ns_ctrl_new_instance(struct 
ns_new_instance_req *req)
        }
 
        if (req->parts_num > NANDSIM_MAX_PARTS || req->parts_num < 0)
-               goto err_inval;
+               goto err;
 
        if (req->parts_num > 0) {
                nsparam->parts_num = req->parts_num;
@@ -2644,7 +2829,37 @@ static int ns_ctrl_new_instance(struct 
ns_new_instance_req *req)
                break;
 
                default:
-                       goto err_inval;
+                       goto err;
+       }
+
+       nsparam->bad_blocks = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+       nsparam->weak_blocks = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+       nsparam->weak_pages = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+       nsparam->grave_pages = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+
+       if (!nsparam->bad_blocks || !nsparam->weak_blocks ||
+           !nsparam->weak_pages || !nsparam->grave_pages) {
+               kfree(nsparam->bad_blocks);
+               kfree(nsparam->weak_blocks);
+               kfree(nsparam->weak_pages);
+               kfree(nsparam->grave_pages);
+
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       INIT_LIST_HEAD(nsparam->bad_blocks);
+       INIT_LIST_HEAD(nsparam->weak_blocks);
+       INIT_LIST_HEAD(nsparam->weak_pages);
+       INIT_LIST_HEAD(nsparam->grave_pages);
+
+       if (req->simelem_num > 0) {
+               if (req->simelem_num > 1000)
+                       goto err;
+
+               ret = process_element_params(nsparam, req->simelem_num, 
elem_argp);
+               if (ret)
+                       goto err;
        }
 
        nsmtd = ns_new_instance(nsparam);
@@ -2658,9 +2873,10 @@ static int ns_ctrl_new_instance(struct 
ns_new_instance_req *req)
 
        return ns->index;
 
-err_inval:
+err:
+       destroy_nsparam_lists(nsparam);
        kfree(nsparam);
-       return -EINVAL;
+       return ret;
 }
 
 static int ns_ctrl_destroy_instance(struct ns_destroy_instance_req *req)
@@ -2798,10 +3014,6 @@ struct mtd_info *ns_new_instance(struct nandsim_params 
*nsparam)
        nand->index = i;
        mutex_unlock(&ns_mtd_mutex);
 
-       INIT_LIST_HEAD(&nand->weak_blocks);
-       INIT_LIST_HEAD(&nand->grave_pages);
-       INIT_LIST_HEAD(&nand->weak_pages);
-       INIT_LIST_HEAD(&nand->weak_blocks);
        spin_lock_init(&nand->refcnt_lock);
 
        /*
@@ -2854,14 +3066,9 @@ struct mtd_info *ns_new_instance(struct nandsim_params 
*nsparam)
        nsmtd->_get_device = ns_get_device;
        nsmtd->_put_device = ns_put_device;
 
-       if ((retval = parse_weakblocks(nand, nsparam->weakblocks)) != 0)
-               goto err_lists;
-
-       if ((retval = parse_weakpages(nand, nsparam->weakpages)) != 0)
-               goto err_lists;
-
-       if ((retval = parse_gravepages(nand, nsparam->gravepages)) != 0)
-               goto err_lists;
+       nand->weak_blocks = nsparam->weak_blocks;
+       nand->weak_pages = nsparam->weak_pages;
+       nand->grave_pages = nsparam->grave_pages;
 
        nand->do_delays = nsparam->do_delays;
        nand->access_delay = nsparam->access_delay;
@@ -2960,7 +3167,7 @@ struct mtd_info *ns_new_instance(struct nandsim_params 
*nsparam)
        if ((retval = chip->scan_bbt(nsmtd)) != 0)
                goto err_nandsim;
 
-       if ((retval = parse_badblocks(nand, nsmtd, nsparam->badblocks)) != 0)
+       if ((retval = process_badblocks(nsparam, nand)) != 0)
                goto err_nandsim;
 
        /* Register NAND partitions */
@@ -3022,6 +3229,7 @@ static void ns_destroy_all(void)
 
 static int __init ns_init_default(void)
 {
+       int ret;
        struct mtd_info *nsmtd;
        struct nandsim_params *nsparam = kzalloc(sizeof(*nsparam), GFP_KERNEL);
 
@@ -3037,11 +3245,6 @@ static int __init ns_init_default(void)
        nsparam->do_delays = do_delays;
        memcpy(nsparam->parts, parts, sizeof(nsparam->parts));
        nsparam->parts_num = parts_num;
-       nsparam->badblocks = badblocks;
-       nsparam->weakblocks = weakblocks;
-       nsparam->weakpages = weakpages;
-       nsparam->bitflips = bitflips;
-       nsparam->gravepages = gravepages;
        nsparam->overridesize = overridesize;
        nsparam->cache_file = cache_file;
        nsparam->bbt = bbt;
@@ -3053,6 +3256,22 @@ static int __init ns_init_default(void)
        else
                nsparam->bops = &ns_cachefile_bops;
 
+       ret = parse_badblocks(nsparam, badblocks);
+       if (ret)
+               goto err_lists;
+
+       ret = parse_weakblocks(nsparam, weakblocks);
+       if (ret)
+               goto err_lists;
+
+       ret = parse_weakpages(nsparam, weakpages);
+       if (ret)
+               goto err_lists;
+
+       ret = parse_gravepages(nsparam, gravepages);
+       if (ret)
+               goto err_lists;
+
        nsmtd = ns_new_instance(nsparam);
        kfree(nsparam);
 
@@ -3060,6 +3279,11 @@ static int __init ns_init_default(void)
                return PTR_ERR(nsmtd);
 
        return 0;
+
+err_lists:
+       destroy_nsparam_lists(nsparam);
+       kfree(nsparam);
+       return ret;
 }
 
 static int __init ns_init_module(void)
diff --git a/include/linux/mtd/nandsim.h b/include/linux/mtd/nandsim.h
index 880c0b1..702e49d 100644
--- a/include/linux/mtd/nandsim.h
+++ b/include/linux/mtd/nandsim.h
@@ -14,9 +14,10 @@ struct nandsim_params {
        bool do_delays;
        unsigned int parts[NANDSIM_MAX_PARTS];
        unsigned int parts_num;
-       char *badblocks;
-       char *weakblocks;
-       char *weakpages;
+       struct list_head *bad_blocks;
+       struct list_head *weak_blocks;
+       struct list_head *weak_pages;
+       struct list_head *grave_pages;
        unsigned int bitflips;
        char *gravepages;
        unsigned int overridesize;
-- 
2.8.3

Reply via email to