使用nandsim模拟超过64G的大容量MTD设备
最近做毕设项目需要模拟mtd设备,并对yaffs进行相关测试。而由于要求模拟1TB容量的mtd设备,而若使用block2mtd,由于它的page size为1B大小,无法进行yaffs的挂载。
后来同学发现了Linux Kernel自带了nandsim模块,我们可以模拟一个块设备。且这个模块启动的时候可以通过overridesize参数设置整个mtd的大小为所选模拟设备的erase_size为2的整数幂。
但我们实测发现,如果overridesize设置过大,会报出以下错误:
➜ sudo modprobe nandsim first_id_byte=0x98 second_id_byte=0xd7 third_id_byte=0x94 fourth_id_byte=0x32 overridesize=20
insmod: ERROR: could not insert module nandsim: Cannot allocate memory
而这个时候查看dmesg,可以看到以下错误信息:
[nandsim] error: Too many erase blocks for wear reporting
然后,我们在内核的drivers/mtd/nand/nandsim.c
搜索这个错误信息,可以看到它所处的函数如下:
static int setup_wear_reporting(struct mtd_info *mtd)
{
size_t mem;
wear_eb_count = div_u64(mtd->size, mtd->erasesize);
mem = wear_eb_count * sizeof(unsigned long);
if (mem / sizeof(unsigned long) != wear_eb_count) {
NS_ERR("Too many erase blocks for wear reporting\n");
return -ENOMEM;
}
erase_block_wear = kzalloc(mem, GFP_KERNEL);
if (!erase_block_wear) {
NS_ERR("Too many erase blocks for wear reporting\n");
return -ENOMEM;
}
return 0;
}
这里我们可以注意到,它使用了kzalloc
函数。kmalloc
与vmalloc
系列函数的区别主要在于它保证了虚拟地址到物理地址的映射连续,同时可以指定GFP,便于进行DMA一类的操作,但同时它有一个很大的缺点是限制了最大的分配大小是128KB,因此不够灵活。
因此,我们找到整个文件中用到了erase_block_wear指针的地方,发现涉及到分配和释放的就两处,我们只需要将kzalloc
替换成vzalloc
即可,并在它释放的位置由kfree
改为vfree
,最后打造出了以下patch(基于 Kernel 4.4.289):
1011c1011
< kfree(erase_block_wear);
---
> vfree(erase_block_wear);
1024c1024
< erase_block_wear = kzalloc(mem, GFP_KERNEL);
---
> erase_block_wear = vzalloc(mem);
(用这么老的内核是因为毕设项目还用了Open Channel SSD,需要替换out of tree的nvme驱动,以及厂商修改了已经被Kernel deprecated的pblk,厂商只做了Kernel 4.4的版本,其它版本移植非常复杂。)
应用这个patch重新编译后,就可以正常模拟大容量的MTD设备了,且挂载yaffs一切正常。
➜ linux-4.4.289 patch drivers/mtd/nand/nandsim.c ~/large_nand_sim.patch
patching file drivers/mtd/nand/nandsim.c
➜ linux-4.4.289 make -j 64
CHK include/config/kernel.release
CHK include/generated/uapi/linux/version.h
CHK include/generated/utsrelease.h
CHK scripts/mod/devicetable-offsets.h
CHK include/generated/timeconst.h
CHK include/generated/bounds.h
CHK include/generated/asm-offsets.h
CALL scripts/checksyscalls.sh
CHK include/generated/compile.h
CC [M] drivers/mtd/nand/nandsim.o
Building modules, stage 2.
Kernel: arch/x86/boot/bzImage is ready (#2)
MODPOST 3949 modules
CC drivers/mtd/nand/nandsim.mod.o
LD [M] drivers/mtd/nand/nandsim.ko
➜ linux-4.4.289 sudo insmod ./drivers/mtd/nand/nandsim.ko first_id_byte=0x98 second_id_byte=0xd7 third_id_byte=0x94 fourth_id_byte=0x32 overridesize=21
➜ linux-4.4.289 lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 40G 0 disk
├─sda1 8:1 0 1M 0 part
└─sda2 8:2 0 40G 0 part /
sdb 8:16 0 1T 0 disk
sr0 11:0 1 1024M 0 rom
mtdblock0 31:0 0 1T 0 disk
➜ linux-4.4.289 sudo mtdinfo /dev/mtd0
mtd0
Name: NAND simulator partition 0
Type: mlc-nand
Eraseblock size: 524288 bytes, 512.0 KiB
Amount of eraseblocks: 2097152 (1099511627776 bytes, 1024.0 GiB)
Minimum input/output unit size: 4096 bytes
Sub-page size: 4096 bytes
OOB size: 64 bytes
Character device major/minor: 90:0
Bad blocks are allowed: true
Device is writable: true
注:后续我还发现模拟大容量mtd设备时nandsim本身会出现ecc问题,即使没有这个修改依然会出现,或许需要继续观察。