MTD系列 - linux中yaffs2文件系统挂载
/*
题记:
上一篇文章《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.
更多推荐
所有评论(0)