/*
题记:
 上一篇文章《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.

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐