/*
題記:
上一篇文章《android平臺上linux啟動時init進(jìn)程解析init.rc文件分析.txt》中跟蹤了nand分區(qū)上的yaffs2文件系統(tǒng)在系統(tǒng)初始化時最上層的表現(xiàn),,調(diào)用到libc庫函數(shù)mount()為止。對于我關(guān)心的幾個分區(qū)可以將其羅列一下:
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);
這片文章主要描述主線,,關(guān)于yaffs2的技術(shù)細(xì)節(jié)不會分析太多,,主要跟蹤它是如何建立在mtd原始設(shè)備層之上的。
* linux2.6.29
* 主要以yaffs2為例
* 李枝果/lizgo 2010-11-5 [email protected]
* 文中不妥之處,,煩請指正,,謝謝!
*/
一,、系統(tǒng)如何支持yaffs2類型的文件系統(tǒng)
/*
在分析mount掛載過程之前呢,我們先來分析一下,,在系統(tǒng)初始化階段,,也就是do_initcalls()的時候,怎么將yaffs類型
的文件系統(tǒng)注冊進(jìn)系統(tǒng)的,,只有在這之后,,我們才可以使用mount命令來掛載yaffs類型的文件系統(tǒng)。
話說在fs/yaffs2目錄中存在這樣的一個文件:yaffs_fs.c,,該文件中存在如下兩聲明語句:
module_init(init_yaffs_fs)
module_exit(exit_yaffs_fs)
是不是已經(jīng)很熟悉了,,對,init_yaffs_fs()就是yaffs文件系統(tǒng)注冊的入口處,!
*/
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頂層目錄中創(chuàng)建yaffs文件,,用來存放后期掛載的yaffs文件系統(tǒng)的信息
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; // 將要注冊進(jìn)系統(tǒng)的yaffs類型列表
while (fsinst->fst && !error) {
error = register_filesystem(fsinst->fst); // note1
if (!error)
fsinst->installed = 1; // 注冊成功標(biāo)志
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); // 初始化該類型文件系統(tǒng)的超級塊列表指針
// 每個類型的文件系統(tǒng)都可能有多個文件系統(tǒng)實例存在于整個系統(tǒng)中,那么fs_supers
// 鏈表頭就是用來掛接這些實例文件系統(tǒng)的超級塊,,以方便管理
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用來鏈接注冊進(jìn)系統(tǒng)的所有文件系統(tǒng)類型
// 實際上file_systems不是鏈表頭,,而是指向了第一個注冊進(jìn)來的file_system_type的對象
if (strlen((*p)->name) == len &&
strncmp((*p)->name, name, len) == 0)
break;
return p;
// *p == NULl , 那么就是沒有找到該類型的文件系統(tǒng)注冊過
// *p != NULL , 那么就說明該類型的文件系統(tǒng)時注冊過的
}
/**** note1-1 find_filesystem() ****/
/** note1 register_filesystem() **/
/*
至此,,系統(tǒng)在將來mount yaffs和yaffs2類型的文件系統(tǒng)的時候就會正常進(jìn)行。
接下來就分析mount的過程吧?。,。?br>
*/
二,、yaffs2文件系統(tǒng)mount過程
/*
libc庫中的mount函數(shù)最終是通過軟中斷陷入內(nèi)核來完成其工作的,,在內(nèi)核中向上提供的接口函數(shù)就是sys_mount()。
linux內(nèi)核文件include/linux/syscalls.h中有如下的函數(shù)聲明:
asmlinkage long sys_mount(char __user *dev_name, char __user *dir_name,
char __user *type, unsigned long flags, void __user *data);
該函數(shù)就是系統(tǒng)調(diào)用mount在內(nèi)核中的實現(xiàn)函數(shù)了,,但是我整個工程搜索都沒有找到sys_mount()函數(shù)的實現(xiàn)代碼,,無奈上網(wǎng)
一搜,有高人指出do_mount()函數(shù),,呵呵,,這下找到了,來一起看看吧,!
搜了半天,,只有這里比較像那么回事兒,先看看再說,。namespace.c中有如下函數(shù)的實現(xiàn):
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個參數(shù)
---> asmlinkage long sys_mount(char __user *dev_name, char __user *dir_name,
char __user *type, unsigned long flags, void __user *data)
其余的系統(tǒng)調(diào)用函數(shù)的實現(xiàn)也是這么定義的,。
namespace.c文件中實現(xiàn)了sys_mount()函數(shù)的實現(xiàn):
*/
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;
// 將用戶空間中的參數(shù)拷貝到內(nèi)核空間中來,這里會分配一個物理頁來存放數(shù)據(jù)
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); // 主體函數(shù) 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
// 取得當(dāng)前任務(wù)的fs_struct結(jié)構(gòu)體中的root域,,直接復(fù)制給path結(jié)構(gòu)體
// 驗證dir_name給出的路徑是否有效,,ok返回0
if (retval)
return retval;
retval = security_sb_mount(dev_name, &path,
type_page, flags, data_page); // 實際上是一個空函數(shù)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工作的主要函數(shù) 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 父文件系統(tǒng) */
struct dentry *mnt_mountpoint; /* dentry of mountpoint 安裝點的目錄項對象*/
struct dentry *mnt_root; /* root of the mounted tree
該文件系統(tǒng)的根目錄項對象*/
struct super_block *mnt_sb; /* pointer to superblock 該文件系統(tǒng)的超級塊*/
struct list_head mnt_mounts; /* list of children, anchored here
子文件系統(tǒng)列表*/
struct list_head mnt_child; /* and going through their mnt_child
子文件系統(tǒng)列表*/
int mnt_flags; /* 安裝標(biāo)記 */
// MNT_NOSUID - 禁止該文件系統(tǒng)的可執(zhí)行文件設(shè)置setuid和setgid標(biāo)志
// MNT_NODEV - 禁止訪問該文件系統(tǒng)上的設(shè)備文件
// MNT_NOEXEC - 禁止執(zhí)行該文件系統(tǒng)上的可執(zhí)行文件
/* 4 bytes hole on 64bits arches */
const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 設(shè)備文件名*/
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結(jié)構(gòu)體引用計數(shù) */
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結(jié)構(gòu)體引用計數(shù)
unsigned int d_flags; /* protected by d_lock 目錄項緩存標(biāo)志*/
spinlock_t d_lock; /* per dentry lock 單目錄項鎖*/
int d_mounted; /* 是登陸點的目錄項嗎? */
struct inode *d_inode; /* Where the name belongs to - NULL is
negative 相關(guān)索引節(jié)點*/
/*
* 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; // 描述一個已安裝文件系統(tǒng)的結(jié)構(gòu)體指針, 代表某類型文件系統(tǒng)的安裝點
struct dentry *dentry; // 目錄項對象指針
};
extern void path_get(struct path *);
extern void path_put(struct path *);
**/
/**
目錄可以層層嵌套,形成文件路徑,,路徑中的每一部分稱作目錄條目,,也叫目錄項。如:/dev/block/mtdblock4
其中根目錄是/, 目錄dev,,block和設(shè)備文件mtdblock4都是目錄條目,,即目錄項,使用結(jié)構(gòu)體struct dentry來描述
**/
// 根據(jù)給出的name路徑名,,返回該路徑所描述的文件或目錄的struct path對象給上級函數(shù),。path中既有該文件或目錄所屬
// 的文件系統(tǒng),也有該文件和目錄對應(yīng)的目錄項對象,。
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;// 將函數(shù)do_path_lookup函數(shù)最終得到的path結(jié)構(gòu)體返回給上級函數(shù)
return res;
}
/****** note2-1-1 do_path_lookup() ******/
// 路徑分解函數(shù)
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); // 增加當(dāng)前任務(wù)fs_struct結(jié)構(gòu)體中root域引用計數(shù)
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); // 真正的路徑分解函數(shù),,返回最終的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; // 用來指向描述一個已安裝文件系統(tǒng)的vfsmount結(jié)構(gòu)體實例
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); // 呵呵,,很眼熟的一個函數(shù) note2-2-1
// 曾經(jīng)分析rootfs初始化的時候詳細(xì)分析過,這里再來走一次
// 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)的文件系統(tǒng)結(jié)構(gòu)體指針(&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); // 參數(shù)驗證
error = -ENOMEM;
mnt = alloc_vfsmnt(name); // note2-2-1-1-1
/*在slab高速緩存組mnt_cache中分配一個vfsmount對象,并對其進(jìn)行初始化*/
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 文件系統(tǒng)超級塊回調(diào)函數(shù)
// 對yaffs2類型的文件系統(tǒng),,該函數(shù)是: 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結(jié)構(gòu)體相關(guān)域設(shè)置
mnt->mnt_parent = mnt;
up_write(&mnt->mnt_sb->s_umount);
free_secdata(secdata);
return mnt; // 執(zhí)行到這里,,整個mount過程基本就完成了,看的出來,,
// 主要是block_device結(jié)構(gòu)體查找和超級塊的填充,。
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() **********/
/* 該函數(shù)初始化內(nèi)容
1. 分配一個vfsmount的結(jié)構(gòu)體
2. mnt->mnt_id分配一個隨機(jī)值
3. mnt->mnt_devname設(shè)置,eg: mnt->mnt_devname = “/dev/block/mtdblock13”
4. mnt->mnt_count和mnt->__mnt_writers 設(shè)置為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是欲掛載的設(shè)備
{
struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
// mnt_cache高速緩存已經(jīng)在mnt_init()中創(chuàng)建,,可以直接去獲取空間
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對應(yīng)的塊設(shè)備節(jié)點, 獲得對應(yīng)的block_device結(jié)構(gòu)體
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; // 超級塊的標(biāo)志
s->s_mode = mode; // 訪問權(quán)限
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
// 根據(jù)路徑先找到對應(yīng)的path結(jié)構(gòu)體,然后通過 path->dentry->d_inode->i_bdev 取得該塊設(shè)備
// 節(jié)點對應(yīng)的block_device結(jié)構(gòu)體,。
if (IS_ERR(bdev))
return bdev;
error = blkdev_get(bdev, mode);// 找到對應(yīng)的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);// 檢查該塊設(shè)備屬于哪一類文件系統(tǒng)
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
// 路徑分解,得到最后塊設(shè)備文件的path結(jié)構(gòu)體
if (error)
return ERR_PTR(error);
inode = path.dentry->d_inode; // 取出對應(yīng)目錄項的inode
error = -ENOTBLK;
if (!S_ISBLK(inode->i_mode)) // 檢查其訪問權(quán)限,,是否是塊設(shè)備
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,這是一個描述塊設(shè)備結(jié)構(gòu)體指針
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; // 如果是塊設(shè)備文件,,那么這個指向其塊設(shè)備結(jié)構(gòu)體
if (bdev) {
atomic_inc(&bdev->bd_inode->i_count);
spin_unlock(&bdev_lock);
return bdev; // 返回塊設(shè)備結(jié)構(gòu)體指針
}
spin_unlock(&bdev_lock);
bdev = bdget(inode->i_rdev); // 根據(jù)主次設(shè)備號獲得block_device結(jié)構(gòu)體
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; // 主次設(shè)備號
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是否已經(jīng)和存在的超級塊聯(lián)系上了,是則返回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指針來初始化超級塊相關(guān)域
if (err) {
spin_unlock(&sb_lock);
up_write(&s->s_umount);
destroy_super(s);
return ERR_PTR(err);
}
s->s_type = type; // 該超級塊對應(yīng)的文件系統(tǒng)的類型
strlcpy(s->s_id, type->name, sizeof(s->s_id)); // 文件系統(tǒng)類型名字
list_add_tail(&s->s_list, &super_blocks);
// 將該超級塊加入到全局的超級塊列表中
list_add(&s->s_instances, &type->fs_supers);
// 將該超級塊加入到對應(yīng)文件系統(tǒng)的超級塊列表中
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)); // 取得對應(yīng)的mtd_info結(jié)構(gòu)體
...
/* 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的過程主要繁雜在函數(shù)vfs_kern_mount()中,,其中完成的工作主要是block_device結(jié)構(gòu)體的查找和超級塊的填充,。
/// 接下來在yaffs2的操作函數(shù)中,比如:nandmtd2_ReadChunkWithTagsFromNAND中,,實際上是調(diào)用了對應(yīng)的
/// mtd->read函數(shù),。但是在什么時候調(diào)用這些函數(shù)和怎么調(diào)用,那就是IO調(diào)度層的工作了,。
/// 總的來說,,通過設(shè)備節(jié)點的inode結(jié)構(gòu)體找到了對應(yīng)得struct block_device結(jié)構(gòu)體,即inode->i_bdev.