vfs
层文件创建链路vfs
层是在客户端执行创建创建,首先是经过内核的syscall
的open
调用,最后调用的是具体文件系统实现的
的dir->i_op->atomic_open
函数,这个函数是具体文件系统定义的。如下是vfs层
的简要函数的定制和执行路径。// 定义了系统调用open
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
// open函数的具体执行syscall函数
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
do_sys_openat2(dfd, filename, &how);
}
static long do_sys_openat2(int dfd, const char __user *filename,
struct open_how *how)
{
struct file *f = do_filp_open(dfd, tmp, &op);
}
// 返回内核返回的struct file,这个是每个进程的fd所指向的结构
struct file *do_filp_open(int dfd, struct filename *pathname,
const struct open_flags *op)
{
filp = path_openat(&nd, op, flags | LOOKUP_RCU);
}
// 文件查找
static struct file *path_openat(struct nameidata *nd,
const struct open_flags *op, unsigned flags)
{
const char *s = path_init(nd, flags);
while (!(error = link_path_walk(s, nd)) &&
(s = open_last_lookups(nd, file, op)) != NULL)
;
}
// 快速查找
static const char *open_last_lookups(struct nameidata *nd,
struct file *file, const struct open_flags *op)
{
lookup_open(nd, file, op, got_write);
}
// dentry的快速打开
static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
const struct open_flags *op,
bool got_write)
{
dentry = atomic_open(nd, dentry, file, open_flag, mode);
}
// 在这里即使执行具体文件系统定义的atomic_open函数
static struct dentry *atomic_open(struct nameidata *nd, struct dentry *dentry,
struct file *file,
int open_flag, umode_t mode)
{
// ll_atomic_open
error = dir->i_op->atomic_open(dir, dentry, file,
open_to_namei_flags(open_flag), mode);
}
lustre
客户端ll_atomic_open
入口函数inode
的数据块添加一个dentry
.在lustre中文件创建首先执行的是atomic_open
函数,lustre
文件系统和内核的vfs
衔接是通过inode_operations ll_dir_inode_operations
结构,这个是元数据的操作的函数结构体表其定义如下
const struct inode_operations ll_dir_inode_operations = {
.mknod = ll_mknod,
.atomic_open = ll_atomic_open,
.lookup = ll_lookup_nd,
.create = ll_create_nd,
/* We need all these non-raw things for NFSD, to not patch it. */
.unlink = ll_unlink,
.mkdir = ll_mkdir,
.rmdir = ll_rmdir,
.symlink = ll_symlink,
.link = ll_link,
.rename = ll_rename,
.setattr = ll_setattr,
.getattr = ll_getattr,
.permission = ll_inode_permission,
#ifdef HAVE_IOP_XATTR
.setxattr = ll_setxattr,
.getxattr = ll_getxattr,
.removexattr = ll_removexattr,
#endif
.listxattr = ll_listxattr,
.get_acl = ll_get_acl,
#ifdef HAVE_IOP_SET_ACL
.set_acl = ll_set_acl,
#endif
};
lustre
创建文件首先执行的是ll_atomic_open
,这个函数核心逻辑分为两部分的核心逻辑,一个是查找函数ll_lookup_it
,另外一个是创建文件函数ll_create_it
.ll_atomic_open
将lookup/create/open操作在一个接口里完成主要用于性能优化,如下把最核心的函数列举出来。static int ll_atomic_open(struct inode *dir, struct dentry *dentry,
struct file *file, unsigned open_flags,
umode_t mode ll_last_arg)
{
struct lookup_intent *it;
struct dentry *de;
// 创建文件时候的查找过程,带着需要文件的父目录和文件名称,请求mds创建文件的元数据信息
de = ll_lookup_it(dir, dentry, it, &secctx, &secctxlen, &pca, encrypt,&encctx, &encctxlen);
// 客户端测文件的创建过程,需要设置client的obd层的初始化和设置
rc=ll_create_it(dir, dentry, it, secctx, secctxlen,
encrypt, encctx, encctxlen,
open_flags);
}
lustre
创建文件的时候仅仅是客户端的mdc
和mds
进行交互,所以这里涉及的obd stack
仅仅是mdc
ll_lookup_it
函数ll_lookup_it
函数的核心逻辑负责去创建文件的父目录查找当前目标文件,其中顺着链路调用:ll_lookup_it->ll_prep_md_op_data->md_intent_lock->req_capsule_server_get->ll_lookup_it_finish
。这个链路带着意向锁请求到MDS,MDS负责创建对应文件的元数据,通过网络在回给这个函数
static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
struct lookup_intent *it,
void **secctx, __u32 *secctxlen,
struct pcc_create_attach *pca,
bool encrypt,
void **encctx, __u32 *encctxlen)
{
struct lookup_intent lookup_it = { .it_op = IT_LOOKUP };
struct dentry *save = dentry, *retval;
struct ptlrpc_request *req = NULL;
struct md_op_data *op_data = NULL;
struct lov_user_md *lum = NULL;
__u32 opc;
int rc;
// 准备请求mgs的文件的父目录和文件名称
op_data = ll_prep_md_op_data(NULL, parent, NULL, fname.disk_name.name,fname.disk_name.len, 0, opc, NULL);
// 添加意向锁,同时设置lustre的锁的回调函数请求mds来为新文件创建元数据信息
rc = md_intent_lock(ll_i2mdexp(parent), op_data, it, &req,
&ll_md_blocking_ast, 0)
{
// 在lmv层添加意向锁
lmv_intent_lock() {
// 带有意向锁的查找,需要检查是否有锁冲突或者失效
lmv_intent_lookup() {
// 通过mdc请求意向锁
mdc_intent_lock() {
//网络层的请求发送
}
}
}
}
// 完成查找后的逻辑处理,到这里MDS端文件的元数据inode已经在mds端创建
rc = ll_lookup_it_finish(req,....)
{
// 初始化inode的dentry
ll_splice_alias(inode, *de)
}
return retval;
}
ll_create_it
函数ll_create_it
函数是文件请求到MDS后返回结构,根据这些信息去初始化客户端的OBD DEVICE STACK
.这个函数最核心的逻辑是ll_create_node
,基本做了从mds获取meta,设置客户端的lov和lmv
信息,同时设置文件在vfs
层的操作函数表,最后把dentry
和inode
进行关联起来
// name=demo1.txt, dir=[0x200000007:0x1:0x0](00000000c5a5737f), intent=open|creat
static int ll_create_it(struct inode *dir, struct dentry *dentry,
struct lookup_intent *it,
void *secctx, __u32 secctxlen, bool encrypt,
void *encctx, __u32 encctxlen, unsigned int open_flags)
{
struct inode *inode;
__u64 bits = 0;
int rc = 0;
ENTRY;
inode = ll_create_node(dir, it)
{
// inode准备工作
ll_prep_inode(&inode, &request->rq_pill, dir->i_sb, it)
{
// 从服务端拿到meta信息
md_get_lustre_md(){
mdc_get_lustre_md() {
// 从mds获取新文件的meta
body=req_capsule_server_get(pill, &RMF_MDT_BODY);
// 设置新文件的lmv信息
lmv = req_capsule_server_sized_get(pill, &RMF_MDT_MD, lmv_size);
}
}
//
*inode = ll_iget(sb, cl_fid_build_ino(fid1, api32), &md)
{
// 设置lustre文件的inode->i_op/inode->i_fop/inode->i_mapping 操作函数表
rc = ll_read_inode2(inode, md)
cl_object_find(...)
{
// 通过多层的调用进入如下两个函数
top = lu_object_find(env, dev, f, conf)
{
// cache中有则返回,没有则创建
lu_object_find_at(...)
{
lu_object_alloc(env, dev, f)
{
// 这里是客户端的操作stack,在外层代码中详细展示出来
vvp_object_alloc()
}
}
}
// 把对象添加到客户端的stack操作列表
obj = lu_object_locate(top->lo_header, dev->ld_type);
}
}
}
}
// 新文件的dentry和新创建的inode进行关联
d_instantiate(dentry, inode);
RETURN(0);
}
lu_object_alloc
开始就开始初始化文件对象的OBD DEVICE STACK
.这里可以看到一个对象的函数操作stack会经历:vvp->lov->lovsub->osc
层,这个是文件IO读写的必须经历的函数操作栈
// vvp层对象初始化
vvp_object_alloc(){
// lov对象申请
lov_object_alloc()
// lov对象初始化
lov_object_init()
{
// lov初始化布局
lov_init_composite()
{
// raid0的布局初始化
lov_init_raid0()
{
//
lu_object_find_at()
{
// lovsub对象初始化,这是涉及到多个OSC
lovsub_object_alloc()
lu_object_start()
{
lovsub_object_init()
{
// osc初始化
osc_object_alloc()
}
}
}
// lov子对象初始化
lov_init_sub()
}
}
}
}