LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

问题定位:mount: mounting /dev/mtdblock23 on /rootfs failed: Invalid argument

原有使用ubifs文件系统的分区,计划切换到squashfs。在镜像制作好之后,mount出现如下错误:

mount: mounting /dev/mtdblock23 on /rootfs failed: Invalid argument

1 定位前思考

操作流程如下:

  • Buildroot生成rootfs.squashfs。
  • 通过工具烧录到NAND分区。
  • 在Linux环境下,挂载:mount -t squashfs /dev/mtdblock23 /rootfs。

预想定位流程:

  • strace查看是哪个系统调用返回的Invalid argument。
  • 内核的话:就需要往内核加调试代码。
  • 导出镜像文件和原始rootfs.squashfs对比,查看读写是否有问题。

2 定位流程

2.1 查看返回Invalid argument执行流程

通过strace mount -t squashfs /dev/mtdblock23 /rootfs,确定是mount系统调用返回Invalid argument。

通过ftrace查看mount调用定位:

echo *sys_mount > /sys/kernel/debug/tracing/set_graph_function
echo function_graph > /sys/kernel/debug/tracing/current_trace

执行mount命令,然后查看函数调用关系:

cat /sys/kernel/debug/tracing/trace

Kernel的mount系统调用流程:

sys_mount
  ksys_mount
    copy_mount_string--拷贝mount的文件系统类型和设备名称到内核中。
    copy_mount_options--拷贝mount选项到内核中。
    do_mount
      user_path_at
      security_sb_mount
      do_reconfigure_mnt
      do_remount
      do_loopback
      do_change_type
      do_move_mount_old
      do_new_mount
        get_fs_type
        fs_context_for_mount
        put_filesystem
        vfs_parse_fs_string
        parse_monolithic_mount_data
        mount_capable
        vfs_get_tree
          squashfs_get_tree
            squashfs_get_tree
              get_tree_bdev
                blkdev_get_by_path
                sget_fc
                squashfs_fill_super
                  kmem_cache_alloc_trace
                  sb_min_blocksize
                  squashfs_read_table
                    squashfs_read_data
                      __getblk_gfp
                      ll_rw_block
                        submit_bh_wbc
                          bio_alloc_bioset
                          bio_associate_blkg
                          bio_add_page
                          guard_bio_eod
                          submit_bio
                            generic_make_request
                              generic_make_request_checks
                              blk_queue_enter
                              blk_mq_make_request
                                __blk_mq_sched_bio_merge
                                blk_mq_get_request
                                blk_account_io_start
                                blk_mq_sched_insert_request
                                  dd_insert_requests
                                  blk_mq_run_hw_queue--大概在这里返回错误,怀疑是独到的block数据错误。
                deactivate_locked_super

 PS:通过strace粗略定位,然后通过function_graph达到函数级别定位,这个流程还是挺迅速的。

2.2 检查写入NAND数据正确性

首先可以在Ubuntu对rootfs.ubifs进行验证,验证原始数据正确性:

mount: mounting /dev/mtdblock23 on /rootfs failed: Invalid argument

进入rootfs查看,挂在是否正确。如果正确说明原始数据没有问题。

在启动的Linux环境中,检查分区内容是否正确:

  • 通过dd读出/dev/mtdblock23内容,使用md5sum对比hash值:
dd if=/dev/mtdblock24 of=test.squashfs bs=1605632 count=1
  •  通多hexedit打开/dev/mtdblock23,查看具体数据是否一致。

对比后发现通过工具写入的数据不正确,基本确定是工具问题。

然后通过如下命令写入,确认镜像是否正确:

dd if=rootfs.squashfs of=/dev/mtdblock24

 进一步确定是工具问题。

3 定位后反思

  • strace+function_graph是一个通用高效的定位内核问题的手段。
  • 在定位前,要找准方向,不要在错误的方向上努力。对整个流程,疑问点有个全局的认知,再进行定位。
  • 如果是mtdblock层的问题,定位就困难了,这块还需要加强。
  • 回顾一下数据经过不同存储介质,应该优先检查数据在过程中的完整和正确。

4 /dev/mtdblock相关

4.1 /dev/mtd和/dev/mtdblock区别

在Linux系统中,/dev/mtd 和 /dev/mtdblock 是与MTD(Memory Technology Device)相关的设备文件,如NAND。以下是 /dev/mtd 和 /dev/mtdblock 的主要区别:

  1. 设备抽象级别:
    • /dev/mtd:这些设备文件提供了对MTD设备的原始访问,通常用于直接与MTD设备交互,如nandwrite/nandump、flashcp/flash_crase等命令。参考《NAND/MTD/UBI/UBIFS概念及使用方法》。
    • /dev/mtdblock:这些设备文件提供了对MTD设备的块设备抽象,允许将MTD设备作为块设备来访问,这使得它们可以被挂载为文件系统。
  2. 使用场景:
    • /dev/mtd:通常用于底层的设备驱动程序和需要直接访问MTD设备的场景,如嵌入式系统或特定的应用程序。
    • /dev/mtdblock:适用于需要将MTD设备作为文件系统存储设备的场景,如用于存储操作系统、应用程序或用户数据。
  3. 分区和文件系统:
    • /dev/mtd:不支持分区或文件系统,它们是按字节寻址的,通常不用于存储文件系统。
    • /dev/mtdblock:可以被分区并挂载文件系统,如JFFS2、YAFFS2、SQUASHFS等,适用于需要文件系统功能的场景。
  4. 设备节点:
    • /dev/mtd:设备节点通常以 /dev/mtdX 的形式存在,其中 X 是设备编号。
    • /dev/mtdblock:设备节点通常以 /dev/mtdblockX 的形式存在,其中 X 是设备编号。
  5. 驱动程序:
    • /dev/mtd:由MTD子系统的驱动程序管理,如 mtdchar 驱动
    • /dev/mtdblock:由 mtdblock 驱动管理,该驱动在MTD设备之上模拟块设备
  6. 访问方式:
    • /dev/mtd:通常需要使用特定的MTD工具或API来访问,如 mtdinfo、mtdpart 等。
    • /dev/mtdblock:可以使用标准的块设备操作来访问,如 mount、umount、dd 等。
  7. 性能:
    • /dev/mtd:由于是直接访问,可能提供更好的性能,尤其是在需要频繁擦除和写入的场景。
    • /dev/mtdblock:性能可能受到文件系统和块设备抽象的影响,但对于文件系统操作来说,这种方式更直观和方便。

总的来说,/dev/mtd 和 /dev/mtdblock 提供了不同层次的抽象,适用于不同的使用场景。开发者应根据具体需求选择合适的访问方式。

4.2 /dev/mtdblock框架

/dev/mtdx对应的驱动为drivers/mtd/mtdchar.c:

init_mtdchar
  __register_chrdev--字符设备操作函数集为mtd_fops。对/dev/mtdx的操作,对应如下函数。
    mtdchar_open
      get_mtd_device--获取struct mtd_info结构体。
    mtdchar_unlocked_ioctl
      mtdchar_ioctl
    mtdchar_read
      mtd_read_oob/mtd_read
    mtdchar_write
      mtd_write_oob/mtd_write
    mtdchar_mmap
      vm_iomap_memory
    mtdchar_close
      put_mtd_device

/dev/mtdblockx的驱动在drivers/mtd/mtdblock.c中:

init_mtdblock
  register_mtd_blktrans--注册mtdblock_tr操作函数集作为block层到mtd层的转换层。
    register_blkdev--注册block设备。
    mtd_for_each_device--遍历mtd设备调用struct mtd_blktrans_ops的add_mtd函数,创建block到mtd的转换设备。

 mtdblock_tr是block到mtd转换操作函数集:

static struct mtd_blktrans_ops mtdblock_tr = {
    .name        = "mtdblock",
    .major        = MTD_BLOCK_MAJOR,
    .part_bits    = 0,
    .blksize     = 512,
    .open        = mtdblock_open,
    .flush        = mtdblock_flush,
    .release    = mtdblock_release,
    .readsect    = mtdblock_readsect,
    do_cached_read
      mtd_read .writesect
= mtdblock_writesect,
    do_cached_write
      mtd_write .add_mtd
= mtdblock_add_mtd, .remove_dev = mtdblock_remove_dev, .owner = THIS_MODULE, };

mtdblock_add_mtd建立了block和mtd层之间的连接:

mtdblock_add_mtd
  ->创建struct mtdblk_dev设备,其中mbd建立了mtd和tr之间的联系。tr中的major和name关联到mtdblock类型block。
  ->add_mtd_blktrans_dev
    ->list_add_tail--将新建的struct mtd_blktrans_dev加入到struct mtdblktrans_ops的devs列表中。
    ->alloc_disk--分配一个struct gendisk。然后对其初始化,并且操作函数集为mtd_block_ops。
    ->blk_mq_init_sq_queue--创建gendisk的struct request_queue,操作函数集为mtd_mq_ops。
    ->blk_queue_logical_block_size
    ->blk_queue_flag_set
    ->blk_queue_flag_clear
    ->device_add_disk--创建gendisk设备。
    ->sysfs_create_group

大致框架结构如下:

posted on 2024-07-16 23:59  ArnoldLu  阅读(282)  评论(0编辑  收藏  举报

导航