使用nandsim模拟超过64G的大容量MTD设备

Table of Contents

最近做毕设项目需要模拟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函数。kmallocvmalloc系列函数的区别主要在于它保证了虚拟地址到物理地址的映射连续,同时可以指定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问题,即使没有这个修改依然会出现,或许需要继续观察。

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to Top