您的位置:首页 > 运维架构 > Linux

MTD系列 - linux中yaffs2文件系统挂载

2010-11-13 22:51 239 查看
/*

题记:

 上一篇文章《android平台上linux启动时init进程解析init.rc文件分析.txt》中跟踪了nand分区上的yaffs2文件系统在系统初始化时最上层的表现,调用到libc库函数mount()为止。对于我关心的几个分区可以将其罗列一下:

 mount(“/dev/block/mtdblock4”,  “/system”,   “yaffs2”, 0          , NULL);

 mount(“/dev/block/mtdblock7”,  “/opl”,      “yaffs2”, 0          , NULL);

 mount(“/dev/block/mtdblock13”, “/userdata”, “yaffs2”, MS_NOSUID | MS_NODEV, NULL);

 mount(“/dev/block/mtdblock5”,  “/local”,    “yaffs2”, MS_NOSUID | MS_NODEV, NULL);

 mount(“/dev/block/mtdblock9”,  “/cache”,    “yaffs2”, MS_NOSUID | MS_NODEV, NULL);

 这片文章主要描述主线,关于yaffs2的技术细节不会分析太多,主要跟踪它是如何建立在mtd原始设备层之上的。

 

 * linux2.6.29

 * 主要以yaffs2为例

 * 李枝果/lizgo  2010-11-5  lizhiguo0532@163.com

 * 文中不妥之处,烦请指正,谢谢!

*/

一、系统如何支持yaffs2类型的文件系统

/*

 在分析mount挂载过程之前呢,我们先来分析一下,在系统初始化阶段,也就是do_initcalls()的时候,怎么将yaffs类型

 的文件系统注册进系统的,只有在这之后,我们才可以使用mount命令来挂载yaffs类型的文件系统。

 

 话说在fs/yaffs2目录中存在这样的一个文件:yaffs_fs.c,该文件中存在如下两声明语句:

 module_init(init_yaffs_fs)

 module_exit(exit_yaffs_fs)

 是不是已经很熟悉了,对,init_yaffs_fs()就是yaffs文件系统注册的入口处!

*/

static struct file_system_type yaffs_fs_type = {

 .owner = THIS_MODULE,

 .name = "yaffs",

 .get_sb = yaffs_read_super,

 .kill_sb = kill_block_super,

 .fs_flags = FS_REQUIRES_DEV,

};

static struct file_system_type yaffs2_fs_type = {

 .owner = THIS_MODULE,

 .name = "yaffs2",

 .get_sb = yaffs2_read_super,

 .kill_sb = kill_block_super,

 .fs_flags = FS_REQUIRES_DEV,

};

/* Stuff to handle installation of file systems */

struct file_system_to_install {

 struct file_system_type *fst;

 int installed;

};

static struct file_system_to_install fs_to_install[] = {

 {&yaffs_fs_type, 0},

 {&yaffs2_fs_type, 0},

 {NULL, 0}

};

static int __init init_yaffs_fs(void)

{

 int error = 0;

 struct file_system_to_install *fsinst;

 T(YAFFS_TRACE_ALWAYS,

   ("yaffs " __DATE__ " " __TIME__ " Installing. /n"));

 /* Install the proc_fs entry */

 my_proc_entry = create_proc_entry("yaffs",

            S_IRUGO | S_IFREG,

            YPROC_ROOT);

 // 在proc顶层目录中创建yaffs文件,用来存放后期挂载的yaffs文件系统的信息

 if (my_proc_entry) {

  my_proc_entry->write_proc = yaffs_proc_write;

  my_proc_entry->read_proc = yaffs_proc_read;

  my_proc_entry->data = NULL; // 操作该文件的方法注册

 } else

  return -ENOMEM;

 /* Now add the file system entries */

 fsinst = fs_to_install;   // 将要注册进系统的yaffs类型列表

 while (fsinst->fst && !error) {

  error = register_filesystem(fsinst->fst); // note1

  if (!error)

   fsinst->installed = 1; // 注册成功标志

  fsinst++;

 }

 /* Any errors? uninstall  */

 if (error) {

  fsinst = fs_to_install;

  while (fsinst->fst) {

   if (fsinst->installed) {

    unregister_filesystem(fsinst->fst);

    fsinst->installed = 0;

   }

   fsinst++;

  }

 }

 return error;

}

/** note1   register_filesystem() **/

// fs/filesystems.c

static struct file_system_type *file_systems;

static DEFINE_RWLOCK(file_systems_lock);

int register_filesystem(struct file_system_type * fs)

{

 int res = 0;

 struct file_system_type ** p;  // 二级指针

 BUG_ON(strchr(fs->name, '.'));

 if (fs->next)

  return -EBUSY;

 INIT_LIST_HEAD(&fs->fs_supers);  // 初始化该类型文件系统的超级块列表指针

 // 每个类型的文件系统都可能有多个文件系统实例存在于整个系统中,那么fs_supers

 // 链表头就是用来挂接这些实例文件系统的超级块,以方便管理

 write_lock(&file_systems_lock);

 p = find_filesystem(fs->name, strlen(fs->name)); // note1-1

 if (*p)    // !NULL

  res = -EBUSY;

 else    // NULL

  *p = fs;

 write_unlock(&file_systems_lock);

 return res;

}

/**** note1-1   find_filesystem() ****/

static struct file_system_type **find_filesystem(const char *name, unsigned len)

{

 struct file_system_type **p;

 for (p=&file_systems; *p; p=&(*p)->next)

 // file_systems用来链接注册进系统的所有文件系统类型

 // 实际上file_systems不是链表头,而是指向了第一个注册进来的file_system_type的对象

  if (strlen((*p)->name) == len &&

      strncmp((*p)->name, name, len) == 0)

   break;

 return p;

 // *p == NULl , 那么就是没有找到该类型的文件系统注册过

 // *p != NULL , 那么就说明该类型的文件系统时注册过的

}

/**** note1-1   find_filesystem() ****/

/** note1   register_filesystem() **/

/*

 至此,系统在将来mount yaffs和yaffs2类型的文件系统的时候就会正常进行。

 接下来就分析mount的过程吧!!!

*/

二、yaffs2文件系统mount过程

/*

 libc库中的mount函数最终是通过软中断陷入内核来完成其工作的,在内核中向上提供的接口函数就是sys_mount()。

 linux内核文件include/linux/syscalls.h中有如下的函数声明:

 asmlinkage long sys_mount(char __user *dev_name, char __user *dir_name,

              char __user *type, unsigned long flags, void __user *data);

 该函数就是系统调用mount在内核中的实现函数了,但是我整个工程搜索都没有找到sys_mount()函数的实现代码,无奈上网

一搜,有高人指出do_mount()函数,呵呵,这下找到了,来一起看看吧!

 搜了半天,只有这里比较像那么回事儿,先看看再说。namespace.c中有如下函数的实现:

 SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,

  char __user *, type, unsigned long, flags, void __user *, data)

 {

 ... // 暂时先忽略不看

 }

 SYSCALL_DEFINE5定义于syscalls.h文件中:

 #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)

 

 #define SYSCALL_DEFINEx(x, name, ...)     /

     asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))

 

 #define __SC_DECL1(t1, a1) t1 a1

 #define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)

 #define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)

 #define __SC_DECL4(t4, a4, ...) t4 a4, __SC_DECL3(__VA_ARGS__)

 #define __SC_DECL5(t5, a5, ...) t5 a5, __SC_DECL4(__VA_ARGS__)

 #define __SC_DECL6(t6, a6, ...) t6 a6, __SC_DECL5(__VA_ARGS__)

 

 ---> asmlinkage long sys_mount(__SC_DECL5x(__VA_ARGS__))  // __SC_DECL5x(__VA_ARGS__)带有5个参数

 ---> asmlinkage long sys_mount(char __user *dev_name, char __user *dir_name,

              char __user *type, unsigned long flags, void __user *data)

 其余的系统调用函数的实现也是这么定义的。

 

 namespace.c文件中实现了sys_mount()函数的实现:

*/

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,

  char __user *, type, unsigned long, flags, void __user *, data)

{

 int retval;

 unsigned long data_page;

 unsigned long type_page;

 unsigned long dev_page;

 char *dir_page;

 // 将用户空间中的参数拷贝到内核空间中来,这里会分配一个物理页来存放数据

 retval = copy_mount_options(type, &type_page);

 if (retval < 0)

  return retval;

 dir_page = getname(dir_name);// 获取挂载目录路径字符串

 retval = PTR_ERR(dir_page);

 if (IS_ERR(dir_page))

  goto out1;

 retval = copy_mount_options(dev_name, &dev_page); // 同上

 if (retval < 0)

  goto out2;

 retval = copy_mount_options(data, &data_page);  // 同上

 if (retval < 0)

  goto out3;

 lock_kernel();

 retval = do_mount((char *)dev_page, dir_page, (char *)type_page,

     flags, (void *)data_page);  // 主体函数  note2

 unlock_kernel();

 free_page(data_page);

out3:

 free_page(dev_page);

out2:

 putname(dir_page);

out1:

 free_page(type_page);

 return retval;

}

/** note2   do_mount() **/

long do_mount(char *dev_name, char *dir_name, char *type_page,

    unsigned long flags, void *data_page)

{

 struct path path;

 int retval = 0;

 int mnt_flags = 0;

 /* Discard magic */

 if ((flags & MS_MGC_MSK) == MS_MGC_VAL)

  flags &= ~MS_MGC_MSK;

 /* Basic sanity checks */

 if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))

  return -EINVAL;

 if (dev_name && !memchr(dev_name, 0, PAGE_SIZE))

  return -EINVAL;

 if (data_page)

  ((char *)data_page)[PAGE_SIZE - 1] = 0;

 /* Separate the per-mountpoint flags */

 if (flags & MS_NOSUID)

  mnt_flags |= MNT_NOSUID;

 if (flags & MS_NODEV)

  mnt_flags |= MNT_NODEV;

 if (flags & MS_NOEXEC)

  mnt_flags |= MNT_NOEXEC;

 if (flags & MS_NOATIME)

  mnt_flags |= MNT_NOATIME;

 if (flags & MS_NODIRATIME)

  mnt_flags |= MNT_NODIRATIME;

 if (flags & MS_RELATIME)

  mnt_flags |= MNT_RELATIME;

 if (flags & MS_RDONLY)

  mnt_flags |= MNT_READONLY;

 flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |

     MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT);

 /* ... and get the mountpoint */

 retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);    // note2-1

 // 取得当前任务的fs_struct结构体中的root域,直接复制给path结构体

 // 验证dir_name给出的路径是否有效,ok返回0

 if (retval)

  return retval;

 retval = security_sb_mount(dev_name, &path,

       type_page, flags, data_page);          // 实际上是一个空函数security.h

 if (retval)

  goto dput_out;

 if (flags & MS_REMOUNT)

  retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,

        data_page);

 else if (flags & MS_BIND)

  retval = do_loopback(&path, dev_name, flags & MS_REC);

 else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))

  retval = do_change_type(&path, flags);

 else if (flags & MS_MOVE)

  retval = do_move_mount(&path, dev_name);

 else

  retval = do_new_mount(&path, type_page, flags, mnt_flags,

          dev_name, data_page);// 完成mount工作的主要函数  note 2-2

dput_out:

 path_put(&path);

 return retval;

}

/**** note2-1 kern_path() ****/

/** // mount.h

struct vfsmount {

 struct list_head mnt_hash;   /* 哈希表 */

 struct vfsmount *mnt_parent;  /* fs we are mounted on 父文件系统 */

 struct dentry *mnt_mountpoint; /* dentry of mountpoint 安装点的目录项对象*/

 struct dentry *mnt_root;    /* root of the mounted tree

                    该文件系统的根目录项对象*/

 struct super_block *mnt_sb;   /* pointer to superblock 该文件系统的超级块*/

 struct list_head mnt_mounts;  /* list of children, anchored here

                    子文件系统列表*/

 struct list_head mnt_child;   /* and going through their mnt_child

                    子文件系统列表*/

 int mnt_flags;         /* 安装标记 */

 // MNT_NOSUID - 禁止该文件系统的可执行文件设置setuid和setgid标志

 // MNT_NODEV  - 禁止访问该文件系统上的设备文件

 // MNT_NOEXEC - 禁止执行该文件系统上的可执行文件

                 /* 4 bytes hole on 64bits arches */

 const char *mnt_devname;    /* Name of device e.g. /dev/dsk/hda1 设备文件名*/

 struct list_head mnt_list;   /* 描述符链表 */

 struct list_head mnt_expire;  /* link in fs-specific expiry list */

 struct list_head mnt_share;   /* circular list of shared mounts */

 struct list_head mnt_slave_list;/* list of slave mounts */

 struct list_head mnt_slave;   /* slave list entry */

 struct vfsmount *mnt_master;  /* slave is on master->mnt_slave_list */

 struct mnt_namespace *mnt_ns;  /* containing namespace */

 int mnt_id;           /* mount identifier */

 int mnt_group_id;        /* peer group identifier */

 /*

  * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount

  * to let these frequently modified fields in a separate cache line

  * (so that reads of mnt_flags wont ping-pong on SMP machines)

  */

 atomic_t mnt_count;       /* vfsmount结构体引用计数 */

 int mnt_expiry_mark;      /* true if marked for expiry */

 int mnt_pinned;

 int mnt_ghosts;

 /*

  * This value is not stable unless all of the mnt_writers[] spinlocks

  * are held, and all mnt_writer[]s on this mount have 0 as their ->count

  */

 atomic_t __mnt_writers;  

};

**/

/** // dcache.h

struct dentry {

 atomic_t d_count;   // dentry结构体引用计数

 unsigned int d_flags;  /* protected by d_lock 目录项缓存标志*/

 spinlock_t d_lock;   /* per dentry lock 单目录项锁*/

 int d_mounted;    /* 是登陆点的目录项吗? */

 struct inode *d_inode;  /* Where the name belongs to - NULL is

            negative  相关索引节点*/

 /*

  * The next three fields are touched by __d_lookup.  Place them here

  * so they all fit in a cache line.

  */

 struct hlist_node d_hash; /* lookup hash list */

 struct dentry *d_parent; /* parent directory */

 struct qstr d_name;

 struct list_head d_lru;  /* LRU list */

 /*

  * d_child and d_rcu can share memory

  */

 union {

  struct list_head d_child; /* child of parent list */

   struct rcu_head d_rcu;

 } d_u;

 struct list_head d_subdirs; /* our children */

 struct list_head d_alias; /* inode alias list */

 unsigned long d_time;  /* used by d_revalidate */

 struct dentry_operations *d_op;

 struct super_block *d_sb; /* The root of the dentry tree */

 void *d_fsdata;   /* fs-specific data */

 unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */

};

**/

/** // path.h

struct dentry;

struct vfsmount;

struct path {

 struct vfsmount *mnt;   // 描述一个已安装文件系统的结构体指针, 代表某类型文件系统的安装点

 struct dentry *dentry;  // 目录项对象指针

};

extern void path_get(struct path *);

extern void path_put(struct path *);

**/

/**

 目录可以层层嵌套,形成文件路径,路径中的每一部分称作目录条目,也叫目录项。如:/dev/block/mtdblock4

 其中根目录是/, 目录dev,block和设备文件mtdblock4都是目录条目,即目录项,使用结构体struct dentry来描述

**/

// 根据给出的name路径名,返回该路径所描述的文件或目录的struct path对象给上级函数。path中既有该文件或目录所属

// 的文件系统,也有该文件和目录对应的目录项对象。

int kern_path(const char *name, unsigned int flags, struct path *path)

{

 struct nameidata nd;

 int res = do_path_lookup(AT_FDCWD, name, flags, &nd);  // note 2-1-1

 if (!res)

  *path = nd.path;// 将函数do_path_lookup函数最终得到的path结构体返回给上级函数

 return res;

}

/****** note2-1-1 do_path_lookup() ******/

// 路径分解函数

static int do_path_lookup(int dfd, const char *name,

    unsigned int flags, struct nameidata *nd)

{

 int retval = 0;

 int fput_needed;

 struct file *file;

 struct fs_struct *fs = current->fs;

 nd->last_type = LAST_ROOT; /* if there are only slashes... */

 nd->flags = flags;

 nd->depth = 0;

 if (*name=='/') {    // 检查挂载的路径是否处于根目录下

  read_lock(&fs->lock);

  nd->path = fs->root;

  path_get(&fs->root); // 增加当前任务fs_struct结构体中root域引用计数

  read_unlock(&fs->lock);

 } else if (dfd == AT_FDCWD) {

  read_lock(&fs->lock);

  nd->path = fs->pwd;

  path_get(&fs->pwd);

  read_unlock(&fs->lock);

 } else {

  struct dentry *dentry;

  file = fget_light(dfd, &fput_needed);

  retval = -EBADF;

  if (!file)

   goto out_fail;

  dentry = file->f_path.dentry;

  retval = -ENOTDIR;

  if (!S_ISDIR(dentry->d_inode->i_mode))

   goto fput_fail;

  retval = file_permission(file, MAY_EXEC);

  if (retval)

   goto fput_fail;

  nd->path = file->f_path;

  path_get(&file->f_path);

  fput_light(file, fput_needed);

 }

 retval = path_walk(name, nd);  // 真正的路径分解函数,返回最终的nd.path,这里就不往下分析了。

 // path_walk()-->link_path_walk()-->__link_path_walk()-->do_lookup()

 if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&

    nd->path.dentry->d_inode))

  audit_inode(name, nd->path.dentry);

out_fail:

 return retval;

fput_fail:

 fput_light(file, fput_needed);

 goto out_fail;

}

/****** note2-1-1 do_path_lookup() ******/

/**** note2-1 kern_path() ****/

/**** note2-2 do_new_mount() ****/

static int do_new_mount(struct path *path, char *type, int flags,

   int mnt_flags, char *name, void *data)

{

 struct vfsmount *mnt; // 用来指向描述一个已安装文件系统的vfsmount结构体实例

 if (!type || !memchr(type, 0, PAGE_SIZE))

  return -EINVAL;

 /* we need capabilities... */

 if (!capable(CAP_SYS_ADMIN))

  return -EPERM;

 mnt = do_kern_mount(type, flags, name, data);  // 呵呵,很眼熟的一个函数  note2-2-1

                         // 曾经分析rootfs初始化的时候详细分析过,这里再来走一次

 // eg: do_kern_mount("yaffs2", 0, "/dev/block/mtdblock4", NULL);

 // eg: do_kern_mount("yaffs2", MS_NOSUID | MS_NODEV, "/dev/block/mtdblock13", NULL);

 if (IS_ERR(mnt))

  return PTR_ERR(mnt);

 return do_add_mount(mnt, path, mnt_flags, NULL);

}

/****** note2-2-1 do_kern_mount() ******/

struct vfsmount *

do_kern_mount(const char *fstype, int flags, const char *name, void *data)

{

 struct file_system_type *type = get_fs_type(fstype);

 // 获取全局链表file_system中名为fstype(yaffs2)的文件系统结构体指针(&yaffs2_fs_type)

 struct vfsmount *mnt;

 if (!type)

  return ERR_PTR(-ENODEV);

 mnt = vfs_kern_mount(type, flags, name, data);    // note2-2-1-1

 if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&

     !mnt->mnt_sb->s_subtype)

  mnt = fs_set_subtype(mnt, fstype);

 put_filesystem(type);

 return mnt;

}

/******** note2-2-1-1 vfs_kern_mount() ********/

struct vfsmount *

vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)

{

 struct vfsmount *mnt;

 char *secdata = NULL;

 int error;

 if (!type)

  return ERR_PTR(-ENODEV);  // 参数验证

 error = -ENOMEM;

 mnt = alloc_vfsmnt(name);   // note2-2-1-1-1

 /*在slab高速缓存组mnt_cache中分配一个vfsmount对象,并对其进行初始化*/

 if (!mnt)

  goto out;

 if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) { // 我们这里没有data传入,所以暂不研究

  secdata = alloc_secdata();

  if (!secdata)

   goto out_mnt;

  error = security_sb_copy_data(data, secdata);

  if (error)

   goto out_free_secdata;

 }

 error = type->get_sb(type, flags, name, data, mnt); // note2-2-1-1-2  文件系统超级块回调函数

 // 对yaffs2类型的文件系统,该函数是: yaffs2_read_super()

 if (error < 0)

  goto out_free_secdata;

 BUG_ON(!mnt->mnt_sb);

  error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);

  if (error)

   goto out_sb;

 mnt->mnt_mountpoint = mnt->mnt_root; // vfsmount结构体相关域设置

 mnt->mnt_parent = mnt;

 up_write(&mnt->mnt_sb->s_umount);

 free_secdata(secdata);

 return mnt;              // 执行到这里,整个mount过程基本就完成了,看的出来,

                    // 主要是block_device结构体查找和超级块的填充。

out_sb:

 dput(mnt->mnt_root);

 up_write(&mnt->mnt_sb->s_umount);

 deactivate_super(mnt->mnt_sb);

out_free_secdata:

 free_secdata(secdata);

out_mnt:

 free_vfsmnt(mnt);

out:

 return ERR_PTR(error);

}

/********** note2-2-1-1-1 alloc_vfsmnt() **********/

/* 该函数初始化内容

1. 分配一个vfsmount的结构体

2. mnt->mnt_id分配一个随机值

3. mnt->mnt_devname设置,eg: mnt->mnt_devname = “/dev/block/mtdblock13”

4. mnt->mnt_count和mnt->__mnt_writers 设置为1

5. mnt->mnt_hash、mnt->mnt_child、mnt->mnt_mounts、mnt->mnt_list、mnt->mnt_expire、mnt->mnt_share、

   mnt->mnt_slave_list、mnt->mnt_slave初始化

*/

struct vfsmount *alloc_vfsmnt(const char *name) // name是欲挂载的设备

{

 struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);

 // mnt_cache高速缓存已经在mnt_init()中创建,可以直接去获取空间

 if (mnt) {

  int err;

  err = mnt_alloc_id(mnt);

  // Allocate new ID, id returns a value in the range 0 ... 0x7fffffff.

  if (err)

   goto out_free_cache;

  if (name) {

   mnt->mnt_devname = kstrdup(name, GFP_KERNEL); // copy name

   if (!mnt->mnt_devname)

    goto out_free_id;

  }

  atomic_set(&mnt->mnt_count, 1);

  INIT_LIST_HEAD(&mnt->mnt_hash);

  INIT_LIST_HEAD(&mnt->mnt_child);

  INIT_LIST_HEAD(&mnt->mnt_mounts);

  INIT_LIST_HEAD(&mnt->mnt_list);

  INIT_LIST_HEAD(&mnt->mnt_expire);

  INIT_LIST_HEAD(&mnt->mnt_share);

  INIT_LIST_HEAD(&mnt->mnt_slave_list);

  INIT_LIST_HEAD(&mnt->mnt_slave);

  atomic_set(&mnt->__mnt_writers, 0);

 }

 return mnt;

out_free_id:

 mnt_free_id(mnt);

out_free_cache:

 kmem_cache_free(mnt_cache, mnt);

 return NULL;

}

/********** note2-2-1-1-1 alloc_vfsmnt() **********/

/********** note2-2-1-1-2 type->get_sb() // yaffs2_read_super() **********/

static int yaffs2_read_super(struct file_system_type *fs,

             int flags, const char *dev_name, void *data, struct vfsmount *mnt)

{

 return get_sb_bdev(fs, flags, dev_name, data,

           yaffs2_internal_read_super_mtd, mnt);  // note2-2-1-1-2-1

 // eg: get_sb_bdev(&yaffs2_fs_type, MS_NOSUID | MS_NODEV, "/dev/block/mtdblock13", NULL,

 //                 yaffs2_internal_read_super_mtd, mnt);

}

/************ note2-2-1-1-2-1 get_sb_bdev() ************/

int get_sb_bdev(struct file_system_type *fs_type,

 int flags, const char *dev_name, void *data,

 int (*fill_super)(struct super_block *, void *, int),

 struct vfsmount *mnt)

{

 struct block_device *bdev;   // block device pointer

 struct super_block *s;    // 超级块指针

 fmode_t mode = FMODE_READ;

 int error = 0;

 if (!(flags & MS_RDONLY))

  mode |= FMODE_WRITE;   // 可写

 bdev = open_bdev_exclusive(dev_name, mode, fs_type);// note2-2-1-1-2-1-1

 // 打开dev_name对应的块设备节点, 获得对应的block_device结构体

 if (IS_ERR(bdev))

  return PTR_ERR(bdev);

 /*

  * once the super is inserted into the list by sget, s_umount

  * will protect the lockfs code from trying to start a snapshot

  * while we are mounting

  */

 down(&bdev->bd_mount_sem);

 s = sget(fs_type, test_bdev_super, set_bdev_super, bdev); // note2-2-1-1-2-1-2

 up(&bdev->bd_mount_sem);

 if (IS_ERR(s))

  goto error_s;

 if (s->s_root) {

  if ((flags ^ s->s_flags) & MS_RDONLY) {

   up_write(&s->s_umount);

   deactivate_super(s);

   error = -EBUSY;

   goto error_bdev;

  }

  close_bdev_exclusive(bdev, mode);

 } else {

  char b[BDEVNAME_SIZE];

  s->s_flags = flags;  // 超级块的标志

  s->s_mode = mode;   // 访问权限

  strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));

  sb_set_blocksize(s, block_size(bdev));

  error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);

  // yaffs_internal_read_super_mtd()  note2-2-1-1-2-1-3 

  if (error) {

   up_write(&s->s_umount);

   deactivate_super(s);

   goto error;

  }

  s->s_flags |= MS_ACTIVE;

  bdev->bd_super = s;

 }

 return simple_set_mnt(mnt, s);

error_s:

 error = PTR_ERR(s);

error_bdev:

 close_bdev_exclusive(bdev, mode);

error:

 return error;

}

/************** note2-2-1-1-2-1-1 open_bdev_exclusive() **************/

struct block_device *open_bdev_exclusive(const char *path, fmode_t mode, void *holder)

{

 struct block_device *bdev;

 int error = 0;

 bdev = lookup_bdev(path);  // note2-2-1-1-2-1-1-1

 // 根据路径先找到对应的path结构体,然后通过 path->dentry->d_inode->i_bdev 取得该块设备

 // 节点对应的block_device结构体。

 if (IS_ERR(bdev))

  return bdev;

 error = blkdev_get(bdev, mode);// 找到对应的gendisk,放在bdev->bd_disk中

 if (error)

  return ERR_PTR(error);

 error = -EACCES;

 if ((mode & FMODE_WRITE) && bdev_read_only(bdev))

  goto blkdev_put;

 error = bd_claim(bdev, holder);// 检查该块设备属于哪一类文件系统

 if (error)

  goto blkdev_put;

 return bdev;

 

blkdev_put:

 blkdev_put(bdev, mode);

 return ERR_PTR(error);

}

/**************** note2-2-1-1-2-1-1-1 lookup_bdev() ****************/

struct block_device *lookup_bdev(const char *pathname)

{

 struct block_device *bdev;

 struct inode *inode;

 struct path path;

 int error;

 if (!pathname || !*pathname)

  return ERR_PTR(-EINVAL);

 error = kern_path(pathname, LOOKUP_FOLLOW, &path); // 参考前文note2-1

 // 路径分解,得到最后块设备文件的path结构体

 if (error)

  return ERR_PTR(error);

 inode = path.dentry->d_inode;  // 取出对应目录项的inode

 error = -ENOTBLK;

 if (!S_ISBLK(inode->i_mode))  // 检查其访问权限,是否是块设备

  goto fail;

 error = -EACCES;

 if (path.mnt->mnt_flags & MNT_NODEV)

  goto fail;

 error = -ENOMEM;

 bdev = bd_acquire(inode);    // note2-2-1-1-2-1-1-1-1

 // 取得inode->i_bdev,这是一个描述块设备结构体指针

 if (!bdev)

  goto fail;

out:

 path_put(&path);

 return bdev;

fail:

 bdev = ERR_PTR(error);

 goto out;

}

/****************** note2-2-1-1-2-1-1-1-1 bd_acquire() ******************/

static struct block_device *bd_acquire(struct inode *inode)

{

 struct block_device *bdev;

 spin_lock(&bdev_lock);

 bdev = inode->i_bdev; // 如果是块设备文件,那么这个指向其块设备结构体

 if (bdev) {

  atomic_inc(&bdev->bd_inode->i_count);

  spin_unlock(&bdev_lock);

  return bdev;  // 返回块设备结构体指针

 }

 spin_unlock(&bdev_lock);

 bdev = bdget(inode->i_rdev); // 根据主次设备号获得block_device结构体

 if (bdev) {

  spin_lock(&bdev_lock);

  if (!inode->i_bdev) {

   /*

    * We take an additional bd_inode->i_count for inode,

    * and it's released in clear_inode() of inode.

    * So, we can access it via ->i_mapping always

    * without igrab().

    */

   atomic_inc(&bdev->bd_inode->i_count);

   inode->i_bdev = bdev;

   inode->i_mapping = bdev->bd_inode->i_mapping;

   list_add(&inode->i_devices, &bdev->bd_inodes);

  }

  spin_unlock(&bdev_lock);

 }

 return bdev;

}

/****************** note2-2-1-1-2-1-1-1-1 bd_acquire() ******************/

/**************** note2-2-1-1-2-1-1-1 lookup_bdev() ****************/

/************** note2-2-1-1-2-1-1 open_bdev_exclusive() **************/

/************** note2-2-1-1-2-1-2 sget() **************/

static int set_bdev_super(struct super_block *s, void *data)

{

 s->s_bdev = data;        // block_device

 s->s_dev = s->s_bdev->bd_dev;  // 主次设备号 

 return 0;

}

static int test_bdev_super(struct super_block *s, void *data)

{

 return (void *)s->s_bdev == data;

}

struct super_block *sget(struct file_system_type *type,

   int (*test)(struct super_block *,void *),

   int (*set)(struct super_block *,void *),

   void *data)

{

 struct super_block *s = NULL;

 struct super_block *old;

 int err;

retry:

 spin_lock(&sb_lock);

 if (test) {

  list_for_each_entry(old, &type->fs_supers, s_instances) {

   if (!test(old, data))

    // 检查上层传递下来的block_device是否已经和存在的超级块联系上了,是则返回1

    continue;

   if (!grab_super(old))

    goto retry;

   if (s) {

    up_write(&s->s_umount);

    destroy_super(s);

   }

   return old;

  }

 }

 if (!s) {

  spin_unlock(&sb_lock);

  s = alloc_super(type);  // 分配超级块空间

  if (!s)

   return ERR_PTR(-ENOMEM);

  goto retry;

 }

 

 err = set(s, data);

 // 用上层传下来的block_device指针来初始化超级块相关域

 if (err) {

  spin_unlock(&sb_lock);

  up_write(&s->s_umount);

  destroy_super(s);

  return ERR_PTR(err);

 }

 s->s_type = type;   // 该超级块对应的文件系统的类型

 strlcpy(s->s_id, type->name, sizeof(s->s_id)); // 文件系统类型名字

 list_add_tail(&s->s_list, &super_blocks);

 // 将该超级块加入到全局的超级块列表中

 list_add(&s->s_instances, &type->fs_supers);

 // 将该超级块加入到对应文件系统的超级块列表中

 spin_unlock(&sb_lock);

 get_filesystem(type);

 return s;

}

/************** note2-2-1-1-2-1-2 sget() **************/

/************** note2-2-1-1-2-1-3 fill_super() // yaffs_internal_read_super_mtd() **************/

static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data,

      int silent)

{

 return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL;

}

/**************** note2-2-1-1-2-1-3-1 yaffs_internal_read_super() ****************/

static struct super_block *yaffs_internal_read_super(int yaffsVersion,

      struct super_block *sb,

      void *data, int silent)

{

 int nBlocks;

 struct inode *inode = NULL;

 struct dentry *root;

 yaffs_Device *dev = 0;

 char devname_buf[BDEVNAME_SIZE + 1];

 struct mtd_info *mtd;

 int err;

 char *data_str = (char *)data;

 yaffs_options options;

 sb->s_magic = YAFFS_MAGIC;

 sb->s_op = &yaffs_super_ops;

 sb->s_flags |= MS_NOATIME;

 ...

 

 sb->s_blocksize = PAGE_CACHE_SIZE;

 sb->s_blocksize_bits = PAGE_CACHE_SHIFT;

 ...

 

 /* Check it's an mtd device..... */

 if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR)

  return NULL; /* This isn't an mtd device */

 

 ...

 

 /* Get the device */

 mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); // 取得对应的mtd_info结构体

 ...

 

 /* Check it's NAND */

 if (mtd->type != MTD_NANDFLASH) {

  T(YAFFS_TRACE_ALWAYS,

    ("yaffs: MTD device is not NAND it's type %d/n", mtd->type));

  return NULL;

 }

 ...

 

 if (yaffsVersion == 1 && WRITE_SIZE(mtd) >= 2048) {

  T(YAFFS_TRACE_ALWAYS, ("yaffs: auto selecting yaffs2/n"));

  yaffsVersion = 2;

 }

 ...

 

 memset(dev, 0, sizeof(yaffs_Device));

 dev->genericDevice = mtd;

 dev->name = mtd->name;

 ...

 

 /* ... and the functions. */

 if (yaffsVersion == 2) {

  dev->writeChunkWithTagsToNAND =

      nandmtd2_WriteChunkWithTagsToNAND;

  dev->readChunkWithTagsFromNAND =

      nandmtd2_ReadChunkWithTagsFromNAND;

  dev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad;

  dev->queryNANDBlock = nandmtd2_QueryNANDBlock;

  dev->spareBuffer = YMALLOC(mtd->oobsize);

  dev->isYaffs2 = 1;

#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17))

  dev->totalBytesPerChunk = mtd->writesize;

  dev->nChunksPerBlock = mtd->erasesize / mtd->writesize;

#else

  dev->totalBytesPerChunk = mtd->oobblock;

  dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock;

#endif

  nBlocks = YCALCBLOCKS(mtd->size, mtd->erasesize);

  dev->startBlock = 0;

  dev->endBlock = nBlocks - 1;

 }

 ...

 

 inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0,

     yaffs_Root(dev));

 inode->i_op = &yaffs_dir_inode_operations;

 inode->i_fop = &yaffs_dir_operations;

 

 ...

 root = d_alloc_root(inode);

 ...

 sb->s_root = root;

 sb->s_dirt = !dev->isCheckpointed;

 

 ...

 return sb;

}

/**************** note2-2-1-1-2-1-3-1 yaffs_internal_read_super() ****************/

/************** note2-2-1-1-2-1-3 fill_super() // yaffs_internal_read_super_mtd() **************/

/************ note2-2-1-1-2-1 get_sb_bdev() ************/

/********** note2-2-1-1-2 type->get_sb() // yaffs2_read_super() **********/

/******** note2-2-1-1 vfs_kern_mount() ********/

/****** note2-2-1 do_kern_mount() ******/

/**** note2-2 do_new_mount() ****/

/** note2   do_mount() **/

/// mount的过程主要繁杂在函数vfs_kern_mount()中,其中完成的工作主要是block_device结构体的查找和超级块的填充。

/// 接下来在yaffs2的操作函数中,比如:nandmtd2_ReadChunkWithTagsFromNAND中,实际上是调用了对应的

/// mtd->read函数。但是在什么时候调用这些函数和怎么调用,那就是IO调度层的工作了。

/// 总的来说,通过设备节点的inode结构体找到了对应得struct block_device结构体,即inode->i_bdev.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息