前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入浅出Lustre中mkdir实现

深入浅出Lustre中mkdir实现

作者头像
用户4700054
发布2023-02-26 14:47:59
6750
发布2023-02-26 14:47:59
举报
文章被收录于专栏:存储内核技术交流

概览

  • 基于linux文件系统无论是本地还是分布式文件系统都需要实现vfs层的posix层的语义,lustre定义内核的inode_operations中的inode操作的函数,整个mkdir过程分为两个部分,第一部分是lookup过程,第二个部分是mkdir过程。本篇文章着重分析lustre文件系统中的mkdir的实现过程。
代码语言:javascript
复制
// inode(目录)操作函数函数
const struct inode_operations ll_dir_inode_operations = {
	.lookup		= ll_lookup_nd,
	.mkdir		= ll_mkdir,
	/**...忽略其他的......**/
};
  • LMVLOVLustre分布式文件系统客户端的抽象层,这个后续在客户端的代码分析中会讲到,如下是LMVLOV`的在整个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
代码语言:javascript
复制
// 根据目录名称查找该目录是否存在
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中的inodefid,在申请fid过程需要在本地客户端(内置sequence服务在客户端初始化的时候已经连接到mdt0的sequence controller拿到sequence的范围)sequence,然后根据sequence实例化一个fid;第二个事情就是通过fid初始化一个客户端的inode并且设置inode的操作函数表
代码语言:javascript
复制
// 入口函数,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端
  • MDS端初始化挂载的时候定制了一些列的handler.这些handler根据不同类型的op操作码赋值不同的操作函数。在MDS后端的操作函数表mdt_tgt_handlers
代码语言:javascript
复制
// 定义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个函数实现中,下面是整个函数调用核心流程。
代码语言:javascript
复制
// 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(...)
                  }
                  }
                
              }
            }
          }
        }
      }
    }
  }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-01-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 存储内核技术交流 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概览
  • mkdir流程
    • 客户端
      • MDS端
      相关产品与服务
      云 HDFS
      云 HDFS(Cloud HDFS,CHDFS)为您提供标准 HDFS 访问协议,您无需更改现有代码,即可使用高可用、高可靠、多维度安全、分层命名空间的分布式文件系统。 只需几分钟,您就可以在云端创建和挂载 CHDFS,来实现您大数据存储需求。随着业务需求的变化,您可以实时扩展或缩减存储资源,CHDFS 存储空间无上限,满足您海量大数据存储与分析业务需求。此外,通过 CHDFS,您可以实现计算与存储分离,极大发挥计算资源灵活性,同时实现存储数据永久保存,降低您大数据分析资源成本。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档