brintos

brintos / linux-shallow public Read only

0
0
Text · 14.5 KiB · 7ac8ac9 Raw
580 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#define dev_fmt(fmt) "mtdoops-pstore: " fmt4 5#include <linux/kernel.h>6#include <linux/module.h>7#include <linux/pstore_blk.h>8#include <linux/mtd/mtd.h>9#include <linux/bitops.h>10#include <linux/slab.h>11 12static struct mtdpstore_context {13	int index;14	struct pstore_blk_config info;15	struct pstore_device_info dev;16	struct mtd_info *mtd;17	unsigned long *rmmap;		/* removed bit map */18	unsigned long *usedmap;		/* used bit map */19	/*20	 * used for panic write21	 * As there are no block_isbad for panic case, we should keep this22	 * status before panic to ensure panic_write not failed.23	 */24	unsigned long *badmap;		/* bad block bit map */25} oops_cxt;26 27static int mtdpstore_block_isbad(struct mtdpstore_context *cxt, loff_t off)28{29	int ret;30	struct mtd_info *mtd = cxt->mtd;31	u64 blknum;32 33	off = ALIGN_DOWN(off, mtd->erasesize);34	blknum = div_u64(off, mtd->erasesize);35 36	if (test_bit(blknum, cxt->badmap))37		return true;38	ret = mtd_block_isbad(mtd, off);39	if (ret < 0) {40		dev_err(&mtd->dev, "mtd_block_isbad failed, aborting\n");41		return ret;42	} else if (ret > 0) {43		set_bit(blknum, cxt->badmap);44		return true;45	}46	return false;47}48 49static inline int mtdpstore_panic_block_isbad(struct mtdpstore_context *cxt,50		loff_t off)51{52	struct mtd_info *mtd = cxt->mtd;53	u64 blknum;54 55	off = ALIGN_DOWN(off, mtd->erasesize);56	blknum = div_u64(off, mtd->erasesize);57	return test_bit(blknum, cxt->badmap);58}59 60static inline void mtdpstore_mark_used(struct mtdpstore_context *cxt,61		loff_t off)62{63	struct mtd_info *mtd = cxt->mtd;64	u64 zonenum = div_u64(off, cxt->info.kmsg_size);65 66	dev_dbg(&mtd->dev, "mark zone %llu used\n", zonenum);67	set_bit(zonenum, cxt->usedmap);68}69 70static inline void mtdpstore_mark_unused(struct mtdpstore_context *cxt,71		loff_t off)72{73	struct mtd_info *mtd = cxt->mtd;74	u64 zonenum = div_u64(off, cxt->info.kmsg_size);75 76	dev_dbg(&mtd->dev, "mark zone %llu unused\n", zonenum);77	clear_bit(zonenum, cxt->usedmap);78}79 80static inline void mtdpstore_block_mark_unused(struct mtdpstore_context *cxt,81		loff_t off)82{83	struct mtd_info *mtd = cxt->mtd;84	u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;85	u64 zonenum;86 87	off = ALIGN_DOWN(off, mtd->erasesize);88	zonenum = div_u64(off, cxt->info.kmsg_size);89	while (zonecnt > 0) {90		dev_dbg(&mtd->dev, "mark zone %llu unused\n", zonenum);91		clear_bit(zonenum, cxt->usedmap);92		zonenum++;93		zonecnt--;94	}95}96 97static inline int mtdpstore_is_used(struct mtdpstore_context *cxt, loff_t off)98{99	u64 zonenum = div_u64(off, cxt->info.kmsg_size);100	u64 blknum = div_u64(off, cxt->mtd->erasesize);101 102	if (test_bit(blknum, cxt->badmap))103		return true;104	return test_bit(zonenum, cxt->usedmap);105}106 107static int mtdpstore_block_is_used(struct mtdpstore_context *cxt,108		loff_t off)109{110	struct mtd_info *mtd = cxt->mtd;111	u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;112	u64 zonenum;113 114	off = ALIGN_DOWN(off, mtd->erasesize);115	zonenum = div_u64(off, cxt->info.kmsg_size);116	while (zonecnt > 0) {117		if (test_bit(zonenum, cxt->usedmap))118			return true;119		zonenum++;120		zonecnt--;121	}122	return false;123}124 125static int mtdpstore_is_empty(struct mtdpstore_context *cxt, char *buf,126		size_t size)127{128	struct mtd_info *mtd = cxt->mtd;129	size_t sz;130	int i;131 132	sz = min_t(uint32_t, size, mtd->writesize / 4);133	for (i = 0; i < sz; i++) {134		if (buf[i] != (char)0xFF)135			return false;136	}137	return true;138}139 140static void mtdpstore_mark_removed(struct mtdpstore_context *cxt, loff_t off)141{142	struct mtd_info *mtd = cxt->mtd;143	u64 zonenum = div_u64(off, cxt->info.kmsg_size);144 145	dev_dbg(&mtd->dev, "mark zone %llu removed\n", zonenum);146	set_bit(zonenum, cxt->rmmap);147}148 149static void mtdpstore_block_clear_removed(struct mtdpstore_context *cxt,150		loff_t off)151{152	struct mtd_info *mtd = cxt->mtd;153	u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;154	u64 zonenum;155 156	off = ALIGN_DOWN(off, mtd->erasesize);157	zonenum = div_u64(off, cxt->info.kmsg_size);158	while (zonecnt > 0) {159		clear_bit(zonenum, cxt->rmmap);160		zonenum++;161		zonecnt--;162	}163}164 165static int mtdpstore_block_is_removed(struct mtdpstore_context *cxt,166		loff_t off)167{168	struct mtd_info *mtd = cxt->mtd;169	u32 zonecnt = mtd->erasesize / cxt->info.kmsg_size;170	u64 zonenum;171 172	off = ALIGN_DOWN(off, mtd->erasesize);173	zonenum = div_u64(off, cxt->info.kmsg_size);174	while (zonecnt > 0) {175		if (test_bit(zonenum, cxt->rmmap))176			return true;177		zonenum++;178		zonecnt--;179	}180	return false;181}182 183static int mtdpstore_erase_do(struct mtdpstore_context *cxt, loff_t off)184{185	struct mtd_info *mtd = cxt->mtd;186	struct erase_info erase;187	int ret;188 189	off = ALIGN_DOWN(off, cxt->mtd->erasesize);190	dev_dbg(&mtd->dev, "try to erase off 0x%llx\n", off);191	erase.len = cxt->mtd->erasesize;192	erase.addr = off;193	ret = mtd_erase(cxt->mtd, &erase);194	if (!ret)195		mtdpstore_block_clear_removed(cxt, off);196	else197		dev_err(&mtd->dev, "erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",198		       (unsigned long long)erase.addr,199		       (unsigned long long)erase.len, cxt->info.device);200	return ret;201}202 203/*204 * called while removing file205 *206 * Avoiding over erasing, do erase block only when the whole block is unused.207 * If the block contains valid log, do erase lazily on flush_removed() when208 * unregister.209 */210static ssize_t mtdpstore_erase(size_t size, loff_t off)211{212	struct mtdpstore_context *cxt = &oops_cxt;213 214	if (mtdpstore_block_isbad(cxt, off))215		return -EIO;216 217	mtdpstore_mark_unused(cxt, off);218 219	/* If the block still has valid data, mtdpstore do erase lazily */220	if (likely(mtdpstore_block_is_used(cxt, off))) {221		mtdpstore_mark_removed(cxt, off);222		return 0;223	}224 225	/* all zones are unused, erase it */226	return mtdpstore_erase_do(cxt, off);227}228 229/*230 * What is security for mtdpstore?231 * As there is no erase for panic case, we should ensure at least one zone232 * is writable. Otherwise, panic write will fail.233 * If zone is used, write operation will return -ENOMSG, which means that234 * pstore/blk will try one by one until gets an empty zone. So, it is not235 * needed to ensure the next zone is empty, but at least one.236 */237static int mtdpstore_security(struct mtdpstore_context *cxt, loff_t off)238{239	int ret = 0, i;240	struct mtd_info *mtd = cxt->mtd;241	u32 zonenum = (u32)div_u64(off, cxt->info.kmsg_size);242	u32 zonecnt = (u32)div_u64(cxt->mtd->size, cxt->info.kmsg_size);243	u32 blkcnt = (u32)div_u64(cxt->mtd->size, cxt->mtd->erasesize);244	u32 erasesize = cxt->mtd->erasesize;245 246	for (i = 0; i < zonecnt; i++) {247		u32 num = (zonenum + i) % zonecnt;248 249		/* found empty zone */250		if (!test_bit(num, cxt->usedmap))251			return 0;252	}253 254	/* If there is no any empty zone, we have no way but to do erase */255	while (blkcnt--) {256		div64_u64_rem(off + erasesize, cxt->mtd->size, (u64 *)&off);257 258		if (mtdpstore_block_isbad(cxt, off))259			continue;260 261		ret = mtdpstore_erase_do(cxt, off);262		if (!ret) {263			mtdpstore_block_mark_unused(cxt, off);264			break;265		}266	}267 268	if (ret)269		dev_err(&mtd->dev, "all blocks bad!\n");270	dev_dbg(&mtd->dev, "end security\n");271	return ret;272}273 274static ssize_t mtdpstore_write(const char *buf, size_t size, loff_t off)275{276	struct mtdpstore_context *cxt = &oops_cxt;277	struct mtd_info *mtd = cxt->mtd;278	size_t retlen;279	int ret;280 281	if (mtdpstore_block_isbad(cxt, off))282		return -ENOMSG;283 284	/* zone is used, please try next one */285	if (mtdpstore_is_used(cxt, off))286		return -ENOMSG;287 288	dev_dbg(&mtd->dev, "try to write off 0x%llx size %zu\n", off, size);289	ret = mtd_write(cxt->mtd, off, size, &retlen, (u_char *)buf);290	if (ret < 0 || retlen != size) {291		dev_err(&mtd->dev, "write failure at %lld (%zu of %zu written), err %d\n",292				off, retlen, size, ret);293		return -EIO;294	}295	mtdpstore_mark_used(cxt, off);296 297	mtdpstore_security(cxt, off);298	return retlen;299}300 301static inline bool mtdpstore_is_io_error(int ret)302{303	return ret < 0 && !mtd_is_bitflip(ret) && !mtd_is_eccerr(ret);304}305 306/*307 * All zones will be read as pstore/blk will read zone one by one when do308 * recover.309 */310static ssize_t mtdpstore_read(char *buf, size_t size, loff_t off)311{312	struct mtdpstore_context *cxt = &oops_cxt;313	struct mtd_info *mtd = cxt->mtd;314	size_t retlen, done;315	int ret;316 317	if (mtdpstore_block_isbad(cxt, off))318		return -ENOMSG;319 320	dev_dbg(&mtd->dev, "try to read off 0x%llx size %zu\n", off, size);321	for (done = 0, retlen = 0; done < size; done += retlen) {322		retlen = 0;323 324		ret = mtd_read(cxt->mtd, off + done, size - done, &retlen,325				(u_char *)buf + done);326		if (mtdpstore_is_io_error(ret)) {327			dev_err(&mtd->dev, "read failure at %lld (%zu of %zu read), err %d\n",328					off + done, retlen, size - done, ret);329			/* the zone may be broken, try next one */330			return -ENOMSG;331		}332 333		/*334		 * ECC error. The impact on log data is so small. Maybe we can335		 * still read it and try to understand. So mtdpstore just hands336		 * over what it gets and user can judge whether the data is337		 * valid or not.338		 */339		if (mtd_is_eccerr(ret)) {340			dev_err(&mtd->dev, "ecc error at %lld (%zu of %zu read), err %d\n",341					off + done, retlen, size - done, ret);342			/* driver may not set retlen when ecc error */343			retlen = retlen == 0 ? size - done : retlen;344		}345	}346 347	if (mtdpstore_is_empty(cxt, buf, size))348		mtdpstore_mark_unused(cxt, off);349	else350		mtdpstore_mark_used(cxt, off);351 352	mtdpstore_security(cxt, off);353	return retlen;354}355 356static ssize_t mtdpstore_panic_write(const char *buf, size_t size, loff_t off)357{358	struct mtdpstore_context *cxt = &oops_cxt;359	struct mtd_info *mtd = cxt->mtd;360	size_t retlen;361	int ret;362 363	if (mtdpstore_panic_block_isbad(cxt, off))364		return -ENOMSG;365 366	/* zone is used, please try next one */367	if (mtdpstore_is_used(cxt, off))368		return -ENOMSG;369 370	ret = mtd_panic_write(cxt->mtd, off, size, &retlen, (u_char *)buf);371	if (ret < 0 || size != retlen) {372		dev_err(&mtd->dev, "panic write failure at %lld (%zu of %zu read), err %d\n",373				off, retlen, size, ret);374		return -EIO;375	}376	mtdpstore_mark_used(cxt, off);377 378	return retlen;379}380 381static void mtdpstore_notify_add(struct mtd_info *mtd)382{383	int ret;384	struct mtdpstore_context *cxt = &oops_cxt;385	struct pstore_blk_config *info = &cxt->info;386	unsigned long longcnt;387 388	if (!strcmp(mtd->name, info->device))389		cxt->index = mtd->index;390 391	if (mtd->index != cxt->index || cxt->index < 0)392		return;393 394	dev_dbg(&mtd->dev, "found matching MTD device %s\n", mtd->name);395 396	if (mtd->size < info->kmsg_size * 2) {397		dev_err(&mtd->dev, "MTD partition %d not big enough\n",398				mtd->index);399		return;400	}401	/*402	 * kmsg_size must be aligned to 4096 Bytes, which is limited by403	 * psblk. The default value of kmsg_size is 64KB. If kmsg_size404	 * is larger than erasesize, some errors will occur since mtdpstore405	 * is designed on it.406	 */407	if (mtd->erasesize < info->kmsg_size) {408		dev_err(&mtd->dev, "eraseblock size of MTD partition %d too small\n",409				mtd->index);410		return;411	}412	if (unlikely(info->kmsg_size % mtd->writesize)) {413		dev_err(&mtd->dev, "record size %lu KB must align to write size %d KB\n",414				info->kmsg_size / 1024,415				mtd->writesize / 1024);416		return;417	}418 419	longcnt = BITS_TO_LONGS(div_u64(mtd->size, info->kmsg_size));420	cxt->rmmap = kcalloc(longcnt, sizeof(long), GFP_KERNEL);421	cxt->usedmap = kcalloc(longcnt, sizeof(long), GFP_KERNEL);422 423	longcnt = BITS_TO_LONGS(div_u64(mtd->size, mtd->erasesize));424	cxt->badmap = kcalloc(longcnt, sizeof(long), GFP_KERNEL);425 426	/* just support dmesg right now */427	cxt->dev.flags = PSTORE_FLAGS_DMESG;428	cxt->dev.zone.read = mtdpstore_read;429	cxt->dev.zone.write = mtdpstore_write;430	cxt->dev.zone.erase = mtdpstore_erase;431	cxt->dev.zone.panic_write = mtdpstore_panic_write;432	cxt->dev.zone.total_size = mtd->size;433 434	ret = register_pstore_device(&cxt->dev);435	if (ret) {436		dev_err(&mtd->dev, "mtd%d register to psblk failed\n",437				mtd->index);438		return;439	}440	cxt->mtd = mtd;441	dev_info(&mtd->dev, "Attached to MTD device %d\n", mtd->index);442}443 444static int mtdpstore_flush_removed_do(struct mtdpstore_context *cxt,445		loff_t off, size_t size)446{447	struct mtd_info *mtd = cxt->mtd;448	u_char *buf;449	int ret;450	size_t retlen;451	struct erase_info erase;452 453	buf = kmalloc(mtd->erasesize, GFP_KERNEL);454	if (!buf)455		return -ENOMEM;456 457	/* 1st. read to cache */458	ret = mtd_read(mtd, off, mtd->erasesize, &retlen, buf);459	if (mtdpstore_is_io_error(ret))460		goto free;461 462	/* 2nd. erase block */463	erase.len = mtd->erasesize;464	erase.addr = off;465	ret = mtd_erase(mtd, &erase);466	if (ret)467		goto free;468 469	/* 3rd. write back */470	while (size) {471		unsigned int zonesize = cxt->info.kmsg_size;472 473		/* there is valid data on block, write back */474		if (mtdpstore_is_used(cxt, off)) {475			ret = mtd_write(mtd, off, zonesize, &retlen, buf);476			if (ret)477				dev_err(&mtd->dev, "write failure at %lld (%zu of %u written), err %d\n",478						off, retlen, zonesize, ret);479		}480 481		off += zonesize;482		size -= min_t(unsigned int, zonesize, size);483	}484 485free:486	kfree(buf);487	return ret;488}489 490/*491 * What does mtdpstore_flush_removed() do?492 * When user remove any log file on pstore filesystem, mtdpstore should do493 * something to ensure log file removed. If the whole block is no longer used,494 * it's nice to erase the block. However if the block still contains valid log,495 * what mtdpstore can do is to erase and write the valid log back.496 */497static int mtdpstore_flush_removed(struct mtdpstore_context *cxt)498{499	struct mtd_info *mtd = cxt->mtd;500	int ret;501	loff_t off;502	u32 blkcnt = (u32)div_u64(mtd->size, mtd->erasesize);503 504	for (off = 0; blkcnt > 0; blkcnt--, off += mtd->erasesize) {505		ret = mtdpstore_block_isbad(cxt, off);506		if (ret)507			continue;508 509		ret = mtdpstore_block_is_removed(cxt, off);510		if (!ret)511			continue;512 513		ret = mtdpstore_flush_removed_do(cxt, off, mtd->erasesize);514		if (ret)515			return ret;516	}517	return 0;518}519 520static void mtdpstore_notify_remove(struct mtd_info *mtd)521{522	struct mtdpstore_context *cxt = &oops_cxt;523 524	if (mtd->index != cxt->index || cxt->index < 0)525		return;526 527	mtdpstore_flush_removed(cxt);528 529	unregister_pstore_device(&cxt->dev);530	kfree(cxt->badmap);531	kfree(cxt->usedmap);532	kfree(cxt->rmmap);533	cxt->mtd = NULL;534	cxt->index = -1;535}536 537static struct mtd_notifier mtdpstore_notifier = {538	.add	= mtdpstore_notify_add,539	.remove	= mtdpstore_notify_remove,540};541 542static int __init mtdpstore_init(void)543{544	int ret;545	struct mtdpstore_context *cxt = &oops_cxt;546	struct pstore_blk_config *info = &cxt->info;547 548	ret = pstore_blk_get_config(info);549	if (unlikely(ret))550		return ret;551 552	if (strlen(info->device) == 0) {553		pr_err("mtd device must be supplied (device name is empty)\n");554		return -EINVAL;555	}556	if (!info->kmsg_size) {557		pr_err("no backend enabled (kmsg_size is 0)\n");558		return -EINVAL;559	}560 561	/* Setup the MTD device to use */562	ret = kstrtoint((char *)info->device, 0, &cxt->index);563	if (ret)564		cxt->index = -1;565 566	register_mtd_user(&mtdpstore_notifier);567	return 0;568}569module_init(mtdpstore_init);570 571static void __exit mtdpstore_exit(void)572{573	unregister_mtd_user(&mtdpstore_notifier);574}575module_exit(mtdpstore_exit);576 577MODULE_LICENSE("GPL");578MODULE_AUTHOR("WeiXiong Liao <liaoweixiong@allwinnertech.com>");579MODULE_DESCRIPTION("MTD backend for pstore/blk");580