ext2文件系统

来源:互联网 发布:天下粮仓知乎 编辑:程序博客网 时间:2024/05/29 08:50
http://www.crifan.com/files/doc/docbook/linux_nand_driver/release/html/linux_nand_driver.html
用户层到-文件系统-MTD驱动层-nand flash驱动层.

http://blog.csdn.net/wang_zheng_kai/article/details/18988521
flash层:rtk_nand.c nand_base_rtk.c
mtd层:mtdcore.c
flash 分为plane,block,page
BBM (BBM)Bad Block Management坏块管理
BBT (BBT)Bad Block Table坏块表.一个flash第0块一定是好块.如果bootcode不放再nand flash,就可以将放bbt来管理标记坏块.
ECC (ECC)Error Correction Code错误校验码.在oob区中.
EEPROM (EEPROM) Electrically Erasable Programmable Read-Only Memory电可擦只读存储器
MLC (MLC)Multi Level Cell多层单元
MOSFET (MOSFET)Metal-Oxide–Semiconductor Field Effect Transistor金属氧化物半导体场效应晶体管
MTD (MTD)Memory Technology Device内存技术设备
NVM (NVM)Non-Volatile Memory
NDA (NDA)None-Disclosure Agreement非公开协议,保密协议
OTP (OTP)One Time Programmable一次性可编程(存储器)
SLC (SLC)Single Level Cell单层单元,单层式存储
MTD(Memory Technology Device,内存技术设备)
EDC ( error detect code )
OOB(out of band)区,是每个Page都有的。Page大小是512字节的NAND每页分配16字节的OOB;如果NAND物理上是2K的Page,则每个Page分配64字节的OOB。

copy-back program在要写flush块同时要拷贝到另外一个块.读出一个页的时候,将内容放到nandflash内部缓存,用于拷贝到其他地方
Cache Read 同一个块操作,改进读取速度,读完一页自动跳到下一页.是否准备好,看状态寄存器.
Cache Program 同一个块操作,改进写入速度.写入一个页后,不结束继续写入地址写入页.
RANDOM DATA READ 随机数据读,是为了用户能够设定新的列地址,增加数据读出的灵活性
为了增加编程的速度,芯片有一个CACHE寄存器。在CACHE编程模式,数据先写入到CACHE寄存器,然后再写入到数据寄存器,一旦数据copy进数据寄存器后,编程就开始。在数据寄存器被装载及编程开始之后,CACHE寄存器变为空,可以继续装载下一个数据,这样内部的编程和数据的装载并行进行,提高了编程速度。
内部数据搬移命令(INTERNAL DATA MOVE)也使用内部CAHCE寄存器,通常搬移数据需要很长时间,通过使用内部CACHE寄存器和数据寄存器,数据的搬移速度大大增加,且不需要使用外部内存。
参考:
http://blog.csdn.net/shanghaiqianlun/article/details/6992915
====标注2====

BBT块设备在nand flash驱动层实现的,放在最后flash最后一页,初始化是5%的坏块率.

nandflash 横向坐标是block和plane以及page;纵向坐标是页内地址,包含oob

##################################################################################################################
文件系统:
ext2,ext3.ext3多个日志.其他修改都会添加到ext2和ext3.
ext2核心:该文件系统将磁盘分为很多块,再在这些块上划分:
超级快(1块)----块组描述(k块)----数据位图(1)----inode位图(1)----inode表(n)----数据块(m)    ====标注1====
====标注1====
块组描述:这个块会在ext2_fill_super-->ext2_iget-->ext2_get_inode-->ext2_get_group_desc可以看到s_group_desc是一个buffer_head数组.
ext2_fill_super中会先读出超级块数据;然后通过:sbi->s_group_desc[i] = sb_bread(sb, block);读取块组描述;ext2_iget->ext2_get_inode通过ext2_group_desc->bg_inode_table计算出inode表的地址读出inode表.


XIP eXecute In Place,即芯片内执行,指应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。flash内执行是指nor flash 不需要初始化,可以直接在flash内执行代码。但往往只执行部分代码,比如初始化RAM.
/* Structure of a blocks group descriptor*/
struct ext2_group_desc
{
    __le32    bg_block_bitmap;        /* Blocks bitmap block */
    __le32    bg_inode_bitmap;        /* Inodes bitmap block */
    __le32    bg_inode_table;        /* Inodes table block */
    __le16    bg_free_blocks_count;    /* Free blocks count */
    __le16    bg_free_inodes_count;    /* Free inodes count */
    __le16    bg_used_dirs_count;    /* Directories count */
    __le16    bg_pad;
    __le32    bg_reserved[3];
};


数据位图:数据块使用标记

inode表:因为每个文件都会有一个inode.

小文件:一个块能装完inode和内容,就用一个块.
大文件:一个块不能装完所有信息,第一个块装inode,和指向一个间接块的地址.
间接块用来保存 指向内容的数据块地址的,可以装很多.间接块可以到3级.
比如:一个块为1024,能保存256个块地址,3层就可以装:256*256*256*1024=16GiB.这里多说点B=byte,b=bit.我们的计量单位内存,硬盘单位都:B

sys_open-->do_sys_open-->do_filp_open-->nameidata_to_filp-->__dentry_open-->f->f_op->open-->blkdev_open
sys_read-->vfs_read-->file->f_op->read-->do_generic_file_read-->do_generic_mapping_read-->mapping->a_ops->readpage(filp, page);
sys_write-->vfs_write-->blkdev_file_write-->generic_file_write_nolock-->generic_file_aio_write_nolock-->__generic_file_aio_write_nolock-->generic_file_buffered_write
##################################################################################################################
cat /proc/filesystems    支持的文件系统
df -T 可以查看挂载出来的设备的文件格式
mount 也能看到
cat /etc/fstab  也可以查看挂载type
<file system> <mount pt>     <type>    <options>         <dump> <pass>
/dev/root       /              ext2    ro,noauto         0      1
proc        /proc           proc     defaults      0     0
devpts        /dev/pts       devpts   defaults,gid=5,mode=620      0     0
tmpfs           /dev/shm       tmpfs    mode=0777         0      0
tmpfs           /tmp           tmpfs    mode=1777         0      0
sysfs        /sys           sysfs    defaults      0     0
tmpfs           /mnt           tmpfs    defaults         0      0
##################################################################################################################
文件系统的块和nandflash硬件上的块,不是一个意思.........
Linux内核还要求 Block_Size = Sector_Size  * (2的n次方),并且Block_Size <= 内存的Page_Size(页大小)
查看系统块大小:这个大小可能是可能是硬件上的,也可能是默认的512.
ext2_fill_super-->sb_min_blocksize中会确定块的大小,以硬件上的sector size为基础,用sector size,或者默认.但是默认要是sector size的2的n次方..nandflash我估计用的page为单位.
1.用tune2fs查看block size大小:
tune2fs -l /dev/sda1 |grep "Block size"      Block size: 1024
2.用stat查看block size大小:
stat /boot/|grep "IO Block"                  Size: 1024 Blocks: 2 IO Block: 1024 目录
3.用dumpe2fs查看block size大小:
dumpe2fs /dev/sda1 |grep "Block size"        Block size: 1024

struct address_space 这个结构,它是用于管理文件(struct inode)映射到内存的页面(struct page)的;与之对应,address_space_operations 就是用来操作该文件映射到内存的页面,比如把内存中的修改写回文件、从文件中读入数据到页面缓冲等。
也就是说address_space结构与文件的对应:一个具体的文件在打开后,内核会在内存中为之建立一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个 address_space与一个偏移量能够确定一个page cache 或swap cache中的一个页面。因此,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面


参考:
http://blog.csdn.net/sanwenyublog/article/details/50938280
static int ext2_fill_super(struct super_block *sb, void *data, int silent)
{
    struct buffer_head * bh;
    struct ext2_sb_info * sbi;
    struct ext2_super_block * es;
    struct inode *root;
    unsigned long block;
    unsigned long sb_block = get_sb_block(&data);//获取super block 块号.
    unsigned long logic_sb_block;
    unsigned long offset = 0;
    unsigned long def_mount_opts;
    long ret = -EINVAL;
    int blocksize = BLOCK_SIZE;
    int db_count;
    int i, j;
    __le32 features;
    int err;

    err = -ENOMEM;
    sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
    if (!sbi)
        goto failed;

    sbi->s_blockgroup_lock =
        kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
    if (!sbi->s_blockgroup_lock) {
        kfree(sbi);
        goto failed;
    }
    sb->s_fs_info = sbi;//放入super block info
    sbi->s_sb_block = sb_block;

    spin_lock_init(&sbi->s_lock);

    /*
     * See what the current blocksize for the device is, and
     * use that as the blocksize.  Otherwise (or if the blocksize
     * is smaller than the default) use the default.
     * This is important for devices that have a hardware
     * sectorsize that is larger than the default.
     看设备的块大小(mtd_blktrans_ops mtdblock_tr.blksize = 4096,),并且作为块大小.如果硬件block size小于default,则用default*/
    blocksize = sb_min_blocksize(sb, BLOCK_SIZE);
    if (!blocksize) {
        ext2_msg(sb, KERN_ERR, "error: unable to set blocksize");
        goto failed_sbi;
    }

    /*
     * If the superblock doesn't start on a hardware sector boundary,
     * calculate the offset.  调整超级块的偏移地址.
     */
    if (blocksize != BLOCK_SIZE) {
        logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
        offset = (sb_block*BLOCK_SIZE) % blocksize;
    } else {
        logic_sb_block = sb_block;
    }

    if (!(bh = sb_bread(sb, logic_sb_block))) {//读取超级块的数据.先去cpu-lru找,失败去page/swap cache找,失败发信号去硬盘找.
        ext2_msg(sb, KERN_ERR, "error: unable to read superblock");
        goto failed_sbi;
    }
    /*
     * Note: s_es must be initialized as soon as possible because
     *       some ext2 macro-instructions depend on its value
     */
    es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);//读出超级快了,就赋值.
    sbi->s_es = es;
    sb->s_magic = le16_to_cpu(es->s_magic);//超级快签名

    if (sb->s_magic != EXT2_SUPER_MAGIC)//签名不是ext2格式的.
        goto cantfind_ext2;

    /* Set defaults before we parse the mount options */
    def_mount_opts = le32_to_cpu(es->s_default_mount_opts);//读取挂载属性,并配置到superblock info --> s_mount_opt中.
    if (def_mount_opts & EXT2_DEFM_DEBUG)
        set_opt(sbi->s_mount_opt, DEBUG);
    if (def_mount_opts & EXT2_DEFM_BSDGROUPS)
        set_opt(sbi->s_mount_opt, GRPID);
    if (def_mount_opts & EXT2_DEFM_UID16)
        set_opt(sbi->s_mount_opt, NO_UID32);
#ifdef CONFIG_EXT2_FS_XATTR
    if (def_mount_opts & EXT2_DEFM_XATTR_USER)
        set_opt(sbi->s_mount_opt, XATTR_USER);
#endif
#ifdef CONFIG_EXT2_FS_POSIX_ACL
    if (def_mount_opts & EXT2_DEFM_ACL)
        set_opt(sbi->s_mount_opt, POSIX_ACL);
#endif
    
    if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC)/*记录发生的错误*/  
        set_opt(sbi->s_mount_opt, ERRORS_PANIC);
    else if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_CONTINUE)
        set_opt(sbi->s_mount_opt, ERRORS_CONT);
    else
        set_opt(sbi->s_mount_opt, ERRORS_RO);

    sbi->s_resuid = make_kuid(&init_user_ns, le16_to_cpu(es->s_def_resuid));
    sbi->s_resgid = make_kgid(&init_user_ns, le16_to_cpu(es->s_def_resgid));
    
    set_opt(sbi->s_mount_opt, RESERVATION);

    if (!parse_options((char *) data, sb))//解析参数,写入superblock info --> s_mount_opt中.
        goto failed_mount;

    sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
        ((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ?
         MS_POSIXACL : 0);

    ext2_xip_verify_sb(sb); /* see if bdev supports xip, unset
                    EXT2_MOUNT_XIP if not */

    if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV &&
        (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) ||
         EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
         EXT2_HAS_INCOMPAT_FEATURE(sb, ~0U)))
        ext2_msg(sb, KERN_WARNING,
            "warning: feature flags set on rev 0 fs, "
            "running e2fsck is recommended");
    /*
     * Check feature flags regardless of the revision level, since we
     * previously didn't change the revision level when setting the flags,
     * so there is a chance incompat flags are set on a rev 0 filesystem.
     */
    features = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP);
    if (features) {
        ext2_msg(sb, KERN_ERR,    "error: couldn't mount because of "
               "unsupported optional features (%x)",
            le32_to_cpu(features));
        goto failed_mount;
    }
    if (!(sb->s_flags & MS_RDONLY) &&
        (features = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){
        ext2_msg(sb, KERN_ERR, "error: couldn't mount RDWR because of "
               "unsupported optional features (%x)",
               le32_to_cpu(features));
        goto failed_mount;
    }

    blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);

    if (ext2_use_xip(sb) && blocksize != PAGE_SIZE) {
        if (!silent)
            ext2_msg(sb, KERN_ERR,
                "error: unsupported blocksize for xip");
        goto failed_mount;
    }

    /* If the blocksize doesn't match, re-read the thing.. */
    if (sb->s_blocksize != blocksize) {
        brelse(bh);

        if (!sb_set_blocksize(sb, blocksize)) {
            ext2_msg(sb, KERN_ERR,
                "error: bad blocksize %d", blocksize);
            goto failed_sbi;
        }

        logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
        offset = (sb_block*BLOCK_SIZE) % blocksize;
        bh = sb_bread(sb, logic_sb_block);
        if(!bh) {
            ext2_msg(sb, KERN_ERR, "error: couldn't read"
                "superblock on 2nd try");
            goto failed_sbi;
        }
        es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
        sbi->s_es = es;
        if (es->s_magic != cpu_to_le16(EXT2_SUPER_MAGIC)) {
            ext2_msg(sb, KERN_ERR, "error: magic mismatch");
            goto failed_mount;
        }
    }

    sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits);
    sb->s_max_links = EXT2_LINK_MAX;

    if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {
        sbi->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
        sbi->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
    } else {
        sbi->s_inode_size = le16_to_cpu(es->s_inode_size);
        sbi->s_first_ino = le32_to_cpu(es->s_first_ino);
        if ((sbi->s_inode_size < EXT2_GOOD_OLD_INODE_SIZE) ||
            !is_power_of_2(sbi->s_inode_size) ||
            (sbi->s_inode_size > blocksize)) {
            ext2_msg(sb, KERN_ERR,
                "error: unsupported inode size: %d",
                sbi->s_inode_size);
            goto failed_mount;
        }
    }

    sbi->s_frag_size = EXT2_MIN_FRAG_SIZE <<
                   le32_to_cpu(es->s_log_frag_size);
    if (sbi->s_frag_size == 0)
        goto cantfind_ext2;
    sbi->s_frags_per_block = sb->s_blocksize / sbi->s_frag_size;

    sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
    sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
    sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);

    if (EXT2_INODE_SIZE(sb) == 0)
        goto cantfind_ext2;
    sbi->s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb);
    if (sbi->s_inodes_per_block == 0 || sbi->s_inodes_per_group == 0)
        goto cantfind_ext2;
    sbi->s_itb_per_group = sbi->s_inodes_per_group /
                    sbi->s_inodes_per_block;
    sbi->s_desc_per_block = sb->s_blocksize /
                    sizeof (struct ext2_group_desc);
    sbi->s_sbh = bh;
    sbi->s_mount_state = le16_to_cpu(es->s_state);
    sbi->s_addr_per_block_bits =
        ilog2 (EXT2_ADDR_PER_BLOCK(sb));
    sbi->s_desc_per_block_bits =
        ilog2 (EXT2_DESC_PER_BLOCK(sb));

    if (sb->s_magic != EXT2_SUPER_MAGIC)
        goto cantfind_ext2;

    if (sb->s_blocksize != bh->b_size) {
        if (!silent)
            ext2_msg(sb, KERN_ERR, "error: unsupported blocksize");
        goto failed_mount;
    }

    if (sb->s_blocksize != sbi->s_frag_size) {
        ext2_msg(sb, KERN_ERR,
            "error: fragsize %lu != blocksize %lu"
            "(not supported yet)",
            sbi->s_frag_size, sb->s_blocksize);
        goto failed_mount;
    }

    if (sbi->s_blocks_per_group > sb->s_blocksize * 8) {
        ext2_msg(sb, KERN_ERR,
            "error: #blocks per group too big: %lu",
            sbi->s_blocks_per_group);
        goto failed_mount;
    }
    if (sbi->s_frags_per_group > sb->s_blocksize * 8) {
        ext2_msg(sb, KERN_ERR,
            "error: #fragments per group too big: %lu",
            sbi->s_frags_per_group);
        goto failed_mount;
    }
    if (sbi->s_inodes_per_group > sb->s_blocksize * 8) {
        ext2_msg(sb, KERN_ERR,
            "error: #inodes per group too big: %lu",
            sbi->s_inodes_per_group);
        goto failed_mount;
    }

    if (EXT2_BLOCKS_PER_GROUP(sb) == 0)
        goto cantfind_ext2;
     sbi->s_groups_count = ((le32_to_cpu(es->s_blocks_count) -
                 le32_to_cpu(es->s_first_data_block) - 1)
                     / EXT2_BLOCKS_PER_GROUP(sb)) + 1;
    db_count = (sbi->s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
           EXT2_DESC_PER_BLOCK(sb);
    sbi->s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
    if (sbi->s_group_desc == NULL) {
        ext2_msg(sb, KERN_ERR, "error: not enough memory");
        goto failed_mount;
    }
    bgl_lock_init(sbi->s_blockgroup_lock);
    sbi->s_debts = kcalloc(sbi->s_groups_count, sizeof(*sbi->s_debts), GFP_KERNEL);
    if (!sbi->s_debts) {
        ext2_msg(sb, KERN_ERR, "error: not enough memory");
        goto failed_mount_group_desc;
    }
    for (i = 0; i < db_count; i++) {
        block = descriptor_loc(sb, logic_sb_block, i);
        sbi->s_group_desc[i] = sb_bread(sb, block);
        if (!sbi->s_group_desc[i]) {
            for (j = 0; j < i; j++)
                brelse (sbi->s_group_desc[j]);
            ext2_msg(sb, KERN_ERR,
                "error: unable to read group descriptors");
            goto failed_mount_group_desc;
        }
    }
    if (!ext2_check_descriptors (sb)) {
        ext2_msg(sb, KERN_ERR, "group descriptors corrupted");
        goto failed_mount2;
    }
    sbi->s_gdb_count = db_count;
    get_random_bytes(&sbi->s_next_generation, sizeof(u32));
    spin_lock_init(&sbi->s_next_gen_lock);

    /* per fileystem reservation list head & lock */
    spin_lock_init(&sbi->s_rsv_window_lock);
    sbi->s_rsv_window_root = RB_ROOT;
    /*
     * Add a single, static dummy reservation to the start of the
     * reservation window list --- it gives us a placeholder for
     * append-at-start-of-list which makes the allocation logic
     * _much_ simpler.
     */
    sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
    sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
    sbi->s_rsv_window_head.rsv_alloc_hit = 0;
    sbi->s_rsv_window_head.rsv_goal_size = 0;
    ext2_rsv_window_add(sb, &sbi->s_rsv_window_head);

    err = percpu_counter_init(&sbi->s_freeblocks_counter,
                ext2_count_free_blocks(sb));
    if (!err) {
        err = percpu_counter_init(&sbi->s_freeinodes_counter,
                ext2_count_free_inodes(sb));
    }
    if (!err) {
        err = percpu_counter_init(&sbi->s_dirs_counter,
                ext2_count_dirs(sb));
    }
    if (err) {
        ext2_msg(sb, KERN_ERR, "error: insufficient memory");
        goto failed_mount3;
    }
    /*
     * set up enough so that it can read an inode
     */
    sb->s_op = &ext2_sops;
    sb->s_export_op = &ext2_export_ops;
    sb->s_xattr = ext2_xattr_handlers;

#ifdef CONFIG_QUOTA
    sb->dq_op = &dquot_operations;
    sb->s_qcop = &dquot_quotactl_ops;
#endif

    root = ext2_iget(sb, EXT2_ROOT_INO);/*开始读入根节点的inode,并初始化这个inode的方法.*/
    if (IS_ERR(root)) {
        ret = PTR_ERR(root);
        goto failed_mount3;
    }
    if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {
        iput(root);
        ext2_msg(sb, KERN_ERR, "error: corrupt root inode, run e2fsck");
        goto failed_mount3;
    }

    sb->s_root = d_make_root(root);
    if (!sb->s_root) {
        ext2_msg(sb, KERN_ERR, "error: get root inode failed");
        ret = -ENOMEM;
        goto failed_mount3;
    }
    if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL))
        ext2_msg(sb, KERN_WARNING,
            "warning: mounting ext3 filesystem as ext2");
    if (ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY))
        sb->s_flags |= MS_RDONLY;
    ext2_write_super(sb);
    return 0;

cantfind_ext2:
    if (!silent)
        ext2_msg(sb, KERN_ERR,
            "error: can't find an ext2 filesystem on dev %s.",
            sb->s_id);
    goto failed_mount;
failed_mount3:
    percpu_counter_destroy(&sbi->s_freeblocks_counter);
    percpu_counter_destroy(&sbi->s_freeinodes_counter);
    percpu_counter_destroy(&sbi->s_dirs_counter);
failed_mount2:
    for (i = 0; i < db_count; i++)
        brelse(sbi->s_group_desc[i]);
failed_mount_group_desc:
    kfree(sbi->s_group_desc);
    kfree(sbi->s_debts);
failed_mount:
    brelse(bh);
failed_sbi:
    sb->s_fs_info = NULL;
    kfree(sbi->s_blockgroup_lock);
    kfree(sbi);
failed:
    return ret;
}
sb_read-->

mtdblock_tr-->mtdblock_add_mtd-->add_mtd_blktrans_dev-->blk_init_queue-->blk_init_queue_node-->blk_init_allocated_queue-->blk_queue_make_request(q, blk_queue_bio)-->q->make_request_fn = mfn;
在mtd块设备注册的时候会注册inode和block_device的关联.以便在mount_bdev的时候获取到对应的block_device.
ext2_fill_super-->sb_bread-->__bread-->__bread_slow-->submit_bh-->_submit_bh-->submit_bio-->generic_make_request-->q->make_request_fn(q, bio);-->blk_queue_bio-->__blk_run_queue-->__blk_run_queue_uncond-->q->request_fn(q);-->mtd_blktrans_request{----new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock);----}任务队列来做此事.-->mtd_blktrans_work{----INIT_WORK(&new->work, mtd_blktrans_work);----}


                      <--register_disk<--gendisk(request_queue)<---         <---add_mtd_device(mtd_info)<----------
block_device(文件系统)<--------------------------------------->MTD<---------------------------------------->nandflash
                           ------->inode------->gendisk------>          -->mtdblk_dev--->mtd_blktrans(_dev/_ops)-->

##################################################################################################################
mount入口函数在:fs/namespace.c,SYSCALL_DEFINE5
mount -t deanfs /mnt /mnt 以指定的格式=deanfs进行挂载,并且会调用file_system_type->mount函数.
在内存中, 每个文件都有一个dentry(目录项)和inode(索引节点)结构,dentry记录着文件名,上级目录等信息,正是它形成了我们所看到的树状结构;而有关该文件的组织和管理的信息主要存放inode里面,它记录着文件在存储介质上的位置与分布。同时dentry->d_inode指向相应的inode结构。dentry与inode是多对一的关系,因为有可能一个文件有好几个文件名

##################################################################################################################
mtd:

##################################################################################################################
seqlock的特点:临界区只允许一个writer thread进入,在没有writer thread的情况下,reader thread可以随意进入,也就是说reader不会阻挡reader。在临界区只有有reader thread的情况下,writer thread可以立刻执行,不会等待。 对reader而言不是很公平,特别是如果writer thread负荷比较重的时候,reader thread可能会retry多次,从而导致reader thread这一侧性能的下降。
参考:
读取,会有retry
    u64 get_jiffies_64(void)
    {

        do {
            seq = read_seqbegin(&jiffies_lock);
            ret = jiffies_64;
        } while (read_seqretry(&jiffies_lock, seq));
    }

写:
    static void tick_do_update_jiffies64(ktime_t now)
    {
        write_seqlock(&jiffies_lock);

    临界区会修改jiffies_64等相关变量,具体代码略
        write_sequnlock(&jiffies_lock);
    }

##################################################################################################################
获得文件过程:namei.c
http://blog.chinaunix.net/uid-20522771-id-4419703.html
//获取name的根路径,当前路径.
static int path_init(int dfd, const char *name, unsigned int flags,
             struct nameidata *nd, struct file **fp)
int inode_permission(struct inode *inode, int mask)//判断inode的权限,比如目录,需要有可执行权限.
sys_open > do_sys_open > do_filp_open > path_openat > link_path_walk > walk_component
walk_component --> lookup_fast 快速查找,已经加载到高速缓存的时候,用他.
walk_component --> lookup_slow 也是到dcache找,没找到就调用 (inode)dir->i_op->lookup去磁盘找.
walk_component --> should_follow_link 如果是是个连接符,用rcu找不到就返回1,到父函数nested_symlink处理.
static int link_path_walk(const char *name, struct nameidata *nd)
{
    struct path next;
    int err;
    
    while (*name=='/')
        name++;
    if (!*name)
        return 0;

    /* At this point we know we have a real path component. */
    for(;;) {
...
        err = may_lookup(nd);//可以不可以找
...
        len = hash_name(name, &this.hash);//提取一个component,就是提取../.././../../xxx/xxxx/xxx中"/""/"之间的组件.意思就是要换目录了
...
        err = walk_component(nd, &next, LOOKUP_FOLLOW);//切换目录,因为父目录和子目录的file system type可能不同,需要换path结构体也就是dentry和inode.
        if (err < 0)
            return err;
        if (err) {
            err = nested_symlink(&next, nd);//如果是连接符,这里处理
        }
    }
    terminate_walk(nd);//停止walk
    return err;
}
static inline int walk_component(struct nameidata *nd, struct path *path,
        int follow)
{
    struct inode *inode;
    int err;
    /*
     * "." and ".." are special - ".." especially so because it has
     * to be able to know about the current root directory and
     * parent relationships.
     */
    if (unlikely(nd->last_type != LAST_NORM))
        return handle_dots(nd, nd->last_type);
    err = lookup_fast(nd, path, &inode);//先高速缓存找,
    if (unlikely(err)) {
        if (err < 0)
            goto out_err;
        /*lookup_slow-->__lookup_hash-->lookup_real-->dir->i_op->lookup-->ext2_lookup(假设用的是ext2文件系统,这个函数的ext2_iget回去磁盘找.)*/
        err = lookup_slow(nd, path);//高速缓存没找到,就慢找,可能回去磁盘找.
        if (err < 0)
            goto out_err;

        inode = path->dentry->d_inode;
    }
    err = -ENOENT;
    if (!inode)
        goto out_path_put;

    if (should_follow_link(inode, follow)) {//这个是link文件返回1,给上面处理
        if (nd->flags & LOOKUP_RCU) {
            if (unlikely(unlazy_walk(nd, path->dentry))) {
                err = -ECHILD;
                goto out_err;
            }
        }
        BUG_ON(inode != path->dentry->d_inode);
        return 1;
    }
    path_to_nameidata(path, nd);//目录文件type替换掉.
    nd->inode = inode;
    return 0;

out_path_put:
    path_to_nameidata(path, nd);
out_err:
    terminate_walk(nd);
    return err;
}
##################################################################################################################

##################################################################################################################

##################################################################################################################
##################################################################################################################
##################################################################################################################
DMA:
文件arch/arm/include/asm/dma-mapping.h
获取dma操作方法.我看到nand flash用的默认:arm_dma_ops
static inline struct dma_map_ops *get_dma_ops(struct device *dev)
{
    if (dev && dev->archdata.dma_ops)
        return dev->archdata.dma_ops;
    return &arm_dma_ops;
}

0 0