[PATCH v2 41/46] mtd: nandsim: Expose support for weakpages/blocks to userspace

Daniel Walter dwalter at sigma-star.at
Wed Sep 21 02:56:51 PDT 2016


From: Richard Weinberger <richard at nod.at>

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

Signed-off-by: Richard Weinberger <richard at 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




More information about the linux-mtd mailing list