From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933108AbcIUJ47 (ORCPT ); Wed, 21 Sep 2016 05:56:59 -0400 Received: from mail.sigma-star.at ([95.130.255.111]:45997 "EHLO mail.sigma-star.at" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751501AbcIUJ4z (ORCPT ); Wed, 21 Sep 2016 05:56:55 -0400 From: Daniel Walter To: linux-mtd@lists.infradead.org Cc: linux-kernel@vger.kernel.org, computersforpeace@gmail.com, dwmw2@infradead.org, boris.brezillon@free-electrons.com, Richard Weinberger Subject: [PATCH v2 41/46] mtd: nandsim: Expose support for weakpages/blocks to userspace Date: Wed, 21 Sep 2016 11:56:51 +0200 Message-Id: X-Mailer: git-send-email 2.8.3 In-Reply-To: References: Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Richard Weinberger Allow users to define weakpages/blocks while creating a new nandsim instance. Signed-off-by: Richard Weinberger --- 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