linux
文件系统无论是本地还是分布式文件系统都需要实现vfs
层的posix
层的语义,lustre
定义内核的inode_operations
中的inode
操作的函数,整个mkdir
过程分为两个部分,第一部分是lookup
过程,第二个部分是mkdir
过程。本篇文章着重分析lustre文件系统中的mkdir
的实现过程。// inode(目录)操作函数函数
const struct inode_operations ll_dir_inode_operations = {
.lookup = ll_lookup_nd,
.mkdir = ll_mkdir,
/**...忽略其他的......**/
};
和
LOV是
Lustre分布式文件系统客户端的抽象层,这个后续在客户端的代码分析中会讲到,如下是
LMV和
LOV`的在整个Lustre文件系统的呈现形式如下mkdir
流程kernel vfs
的posix层的接口(lookup
),其次调用mkdir
系统调用.lookup
接口被lustre重新赋值为ll_lookup_nd
函数,mkdir
函数被lustre重新赋值ll_mkdir
函数。客户端流程中进入vfs
层后就直接进入ll_lookup_nd
函数(这个函数核心的职责是根据父目录查找目标目录的父目录加锁)然后调用ll_mkdir
创建目录ll_lookup_nd
函数是mkdir
的前奏,这个过程中核心是在lmv
层(mdc
结合抽象层)中加LOOKUP_INTENT
锁// 根据目录名称查找该目录是否存在
static struct dentry *ll_lookup_nd(struct inode *parent, struct dentry *dentry,...)
{
struct dentry *de;
de=ll_lookup_it(parent, dentry, itp...)
{
// 添加LOOKUP_INTENT意向锁
md_intent_lock(...)
{
// 这里实际用宏定义MDP(exp->exp_obd, intent_lock)(exp, op_data, it, reqp..),指向的是lmv_intent_lookup
lmv_intent_lookup()
{
md_intent_lock(...)
{
// 宏定义MDP(exp->exp_obd, intent_lock)(exp, op_data, it, reqp,cb_blocking, extra_lock_flags);
mdc_intent_lock(...)
{
mdc_finish_intent_lock(...)
{
// 根据RMF_MDT_BODY获取缓冲区
req_capsule_server_get(&request->rq_pill,&RMF_MDT_BODY);
}
}
}
}
}
}
}
ll_mkdir
函数正式进入mkdir
的流程,这里做了几个核心的事情,第一是创建lustre中的inode
即fid
,在申请fid过程需要在本地客户端(内置sequence服务在客户端初始化的时候已经连接到mdt0的sequence controller拿到sequence的范围)sequence,然后根据sequence实例化一个fid;第二个事情就是通过fid初始化一个客户端的inode并且设置inode的操作函数表// 入口函数,posix的mkdir具体实现函数,这里是需要创建一个lustre "inode"
static int ll_mkdir(...)
{
// lustre文件唯一标识的fid的申请流程
ll_new_node(dir, dchild, NULL, mode, 0, LUSTRE_OPC_MKDIR)
{
md_create(sbi->ll_md_exp, op_data...)
{
// MDP(exp->exp_obd, create)宏定义的函数指针,实际指向lmv_create
lmv_create(...)
{
lmv_fid_alloc(NULL, exp, &op_data->op_fid2, op_data)
{
obd_fid_alloc(...)
{
// OBP(exp->exp_obd, fid_alloc)所定义的函数
mdc_fid_alloc(NULL, tgt->ltd_exp, fid, NULL)
{
// 为新目录申请fid
seq_client_alloc_fid(...)
{
// fid的申请需要seq,这个seq是客户端挂在时候会启动一个sequence服务同时会请求seq manager一个seq的范围
seq_client_alloc_seq(...)
{
// 客户端本地申请seq
*seqnr = seq->lcs_space.lsr_start;
seq->lcs_space.lsr_start += 1;
// 如果这个客户端申请的Seq用完了需要请求sequence manager在申请一个范围的seq
seq_client_alloc_meta(...)
{
seq_client_rpc(...)
}
}
}
}
}
}
md_create(tgt->ltd_exp, op_data, ...)
{
mdc_create(struct obd_export *exp...)
}
}
}
// 第二部分 inode的初始化
ll_prep_inode(&inode, &request->rq_pill...)
{
// 根据fid初始化inode
*inode = ll_iget(sb, cl_fid_build_ino(fid1, api32), &md)
{
// 这里设置inode操作函数表
ll_read_inode2(inode, md)
}
}
}
}
MDS
端初始化挂载的时候定制了一些列的handler
.这些handler
根据不同类型的op操作码
赋值不同的操作函数。在MDS
后端的操作函数表mdt_tgt_handlers
// 定义OPC、请求体对应的处理函数
#define TGT_RPC_HANDLER_HP(base, flags, opc, fn, hp, fmt, version) \
[opc - base] = { \
.th_name = #opc, \
.th_fail_id = OBD_FAIL_ ## opc ## _NET, \
.th_opc = opc, \
.th_flags = flags, \
.th_act = fn, \
.th_fmt = fmt, \
.th_version = version, \
.th_hp = hp, \
}
#define TGT_RPC_HANDLER(base, flags, opc, fn, fmt, version) \
#define TGT_MDT_HDL(flags, name, fn) \
TGT_RPC_HANDLER(MDS_FIRST_OPC, flags, name, fn, &RQF_ ## name, \
LUSTRE_MDS_VERSION)
// 在这里已经给th_act 赋值到mdt_tgt_handlers中对应的函数了
static struct tgt_handler mdt_tgt_handlers[] = {
TGT_MDT_HDL(IS_MUTABLE, MDS_REINT, mdt_reint)
// 忽略其他的
};
// 请求处理的时候根据请求opc检查handler,这里在tgt_init(env, &m->mdt_lut, obd, m->mdt_bottom, mdt_common_slice)中进行初始化。
static struct tgt_opc_slice mdt_common_slice[] = {
{
.tos_opc_start = MDS_FIRST_OPC,
.tos_opc_end = MDS_LAST_OPC,
.tos_hs = mdt_tgt_handlers
}
// 忽略其他的 //
MDS
端的入口函数ptlrpc_server_handle_request
用来接受来自客户端的请求。这里会涉及一系列的处理,第一是从request中获取opc(操作码)
然后获取请求处理的操作函数struct tgt_handler
.MDS
目录创建的整体流程如下MDS
目录创建的核心链路,在mdt_reint_create
函数里核心是在mdt_object_new、mdo_create
这2个函数实现中,下面是整个函数调用核心流程。// mdt_reinters定义,这里是请求到了MDS,MDS需要针对请求的意向设置处理的函数指针
static const struct mdt_reinter mdt_reinters[] = {
[REINT_CREATE] = {
.mr_handler = &mdt_reint_create,
.mr_extra_opc = MDS_REINT_CREATE,
}
}
static int ptlrpc_server_handle_request(...)
{
//ptlrpc_server_handle_request函数中通过 svc->srv_ops.so_req_handler(request)来处理请求,这个函数指针实际指向是tgt_request_handle
svc->srv_ops.so_req_handler(request){
tgt_request_handle(struct ptlrpc_request *req)
{
// 这里定义的tgt_handler的函数指针,根据请求中的操作OPC,根据OPC获取请求处理的实际函数
struct tgt_handler *h = tgt_handler_find_check(req)
{
// 这里面会根据mdt_common_slice来检查handler
}
// 根据情况处理崩溃,如果mds在请求之前有崩溃过,当请求来的时候还是需要崩溃恢复,处理完成后再处理请求
tgt_handle_recovery(req, tsi->tsi_reply_fail_id)
// 请求处理的核心函数
tgt_handle_request0(tsi, h, req)
{
// 这里是函数指针,实际指向的是 mdt_tgt_handlers函数表中的mdt_reint
rc = h->th_act(tsi)
{
// 进入mdt_reint的函数进行处理
mdt_reint(...)
{
// 内部方法调用
mdt_reint_internal(...)
{
// 这里核心执行的函数操作mdt_reinters表的mdt_reinters[opcode],opcode=1,REINT_CREATE.
mdt_reint_rec(...)
{
mr = &mdt_reinters[info->mti_rr.rr_opcode];
rc = (*mr->mr_handler)(info, lhc)
{
// mdt_reint_create在父目录里创建目录的核心函数
mdt_reint_create(...)
{
// 在缓存中查找目标目录的父目录;如果不存在则创建一个并插入到缓存中
parent = mdt_object_find(...)
{
lu_object_find(...)
{
lu_object_find_at(...)
{
htable_lookup(...)
}
}
}
// 根据名字在目标目录的父目录查找
mdt_lookup_version_check(...)
{
// mdo_lookup查找dentry,读取inode给fid
//mdo_lookup指向的mdd_lookup函数,这里会一次经历mdd_lookup->__mdd_lookup->osd_index_ea_lookup->osd_ea_lookup_rec
mdo_lookup(...)
}
// 检查父目录是否存在
mdt_object_exists(parent)
mdt_restripe(...)
{
// 获取父目录的stripe信息
mdt_stripe_get(...)
// 设置目标创建目录的stripe信息
mdt_restripe_internal(...)
}
// mdt_object_new是在mdt上创建一个mdt的内存对象
// mdt_object_new的执行路径依次是mdt_object_new->lu_object_find_at->mdt_object_alloc->mdt_object_init->lod_object_alloc->lod_object_init->lod_fld_lookup->fld_server_lookup->fld_local_lookup->fld_cache_lookup
child = mdt_object_new(...)
// mdo_create实际指向的是mdd_create,这个函数是在底层的ldiskfs中持久化一个对象
rc = mdo_create(...)
}
}
}
}
}
}
}
}
}
}