前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊lustre中的fid和fld

聊聊lustre中的fid和fld

作者头像
用户4700054
发布2022-08-17 12:57:46
9750
发布2022-08-17 12:57:46
举报
文章被收录于专栏:存储内核技术交流

IO访问流程概览

  • lustre 客户端提供linux kernel中的vfslustre server质检的接口层。lustre客户端是由不同的服务组成,每个服务层提供特定的lustre服务。比如MDC提供访问一个或者多个MDS的服务,OSC提供访问lustre集群中的OST.
  • MGC用来管理管理整个集群配置的客户端,每个MGC都会和MGS进行通信获取集群的最新的配置变更。MDC是lustre集群中MDS的客户端,每个MDC连接到MDS在客户端测提供目录或者文件的元数据管理操作服务。
  • LMVlogical metadata volume简称,它用来聚合多个MDC呈现单一元数据命名空间给客户端,提供透明方式访问所有的MDT。比如文件系统的目录树被分割为多个子树分别存储在多个MDT上,但是对于客户端看到的是统一一致的命名空间。
  • LOVlogical object volume简称聚合多个OSC提供透明访问集群内多个OST.比如数据设置的stripe_count=4,stripe_size=1M,,lustre 客户端内部在挂载的时候会初始化LOV,对应也会初始化4个OSC,每个OSC对应访问集群内的一个OST。集群内有4个OST,文件大小为5M,这时候数据写入的时候,文件stripe后,每个OSC请求OST写入这个文件中stripe_size=1m的分片数据。当访问读取数据的时候,每个OSC请求OST拿到数据,然后经过LOV层进行聚合,提供完整文件的数据。

数据查找和创建访问流程

  • 当在lustre客户端查找一个文件时候,lustre客户端打包rpc请求发送给MDS获取lock(目前有两种锁分别是read lock with lookup-intent锁和write lock with create intent锁).MDS响应客户端返回了三个非常核心重要的数据,分别是a lock、这个文件的所有的metadata attributes、这个文件的file layout extented attribute(EA)file layout是当前集群中包含该文件分片数据的OST列表和布局访问模式(用来描述数据分片对象如何在多个ost上分布),基于这个信息lustre客户端直接可以访问OST获取文件数据。
  • 当文件被创建的时候,lustre客户端请求MDS,MDS根据文件的layout发送rpc请求连接后端的OST,当OST完成了数据对象的创建后,返回FID(一个或者多个)MDS完成这个文件的对象创建。FID是一个128个字节的文件标识。FID是lustre客户端用来文件的唯一标识。每个OST上文件分片对象都是有唯一的FID于此同时每个数据分片在MDT上都会对应该这个文件的FID
  • 当执行挂载lustre客户端的时候,发起mount命令,lustre客户端内部的MGC服务连接到MGS获取整个文件系统的配置信息,挂载整个root目录树

MDT上扩展属性

  • lustre中MDT上对于文件的metadata是存储在mdt上文件的inode上的,而对于lustre 文件系统fid是作为文件或者数据对象的唯一标识。那么客户端请求创建一个文件,在MDT上会有创建fid,这个fid也会对应一个MDT上的inode.这inode上会存储客户端请求创建文件的inode,这个文件布局信息以及跨哪些OST都是以扩展属性保存在这个inode上的.MDT上扩展属性的layout eatrusted.link.定义的value是struct struct lov_mds_md_v3
代码语言:javascript
复制
// mds获取到ost上的fid信息的结构
struct ost_id {
	union {
		struct {
			__u64	oi_id;
			__u64	oi_seq;
		} oi;
		struct lu_fid oi_fid;
	};
} __attribute__((packed));

#define lov_ost_data lov_ost_data_v1

// 定义mds上文件stripe的数据分片的信息
struct lov_ost_data_v1 {          /* per-stripe data structure (little-endian)*/
	struct ost_id l_ost_oi;	  /* OST object ID */
	__u32 l_ost_gen;          /* generation of this l_ost_idx */
	__u32 l_ost_idx;          /* OST index in LOV (lov_tgt_desc->tgts) */
};

// mdt 上的文件元数据对应的fid,fid对应的inode存储在inode上的扩展属性的trusted.link的值
struct lov_mds_md_v3 {            /* LOV EA mds/wire data (little-endian) */
	__u32 lmm_magic;          /* magic number = LOV_MAGIC_V3 */
	__u32 lmm_pattern;        /* LOV_PATTERN_RAID0, LOV_PATTERN_RAID1 */
	struct ost_id	lmm_oi;	  /* LOV object ID */
	__u32 lmm_stripe_size;    /* size of stripe in bytes */
	/* lmm_stripe_count used to be __u32 */
	__u16 lmm_stripe_count;   /* num stripes in use for this object */
	__u16 lmm_layout_gen;     /* layout generation number */
	char  lmm_pool_name[LOV_MAXPOOLNAME + 1]; /* must be 32bit aligned */
	struct lov_ost_data_v1 lmm_objects[0]; /* per-stripe data */
};

FID文件唯一标识

  • lustre中的不仅仅是stripe文件对象,包括目录的entries、内部配置文件,这些数据都是以对象形式在lustre中存储的。为了标识整个lustre文件系统中每个对象的唯一性,lustre为每个对象提供一个FID作为唯一标识。FID是128位长度长度,包括了三个部分64位的序列号、32位的对象ID、32位的版本号FID的定义如下:
代码语言:javascript
复制
struct lu_fid {
	// 申请到的序列号
	__u64 f_seq;
	// 对象的ID
	__u32 f_oid;
	// 版本号
	__u32 f_ver;
} __attribute__((packed));

// 从fid中获取客户端查看的inode
static inline __u64 fid_flatten(const struct lu_fid *fid)
{
	__u64 ino;
	__u64 seq;

	if (fid_is_igif(fid)) {
		ino = lu_igif_ino(fid);
		return ino;
	}

	seq = fid_seq(fid);

	ino = (seq << 24) + ((seq >> 24) & 0xffffff0000ULL) + fid_oid(fid);

	return ino ?: fid_oid(fid);
}
  • Lustre文件系统控制序列号,客户端来申请。lustre中的sequence controller服务是在MDT0上运行,每个MDTOST运行一个sequence server服务。lustr整套服务启动后运行在MDT或者OST上的sequence serverMDT0上的sequence controller进行通信获取一个sequence的范围;每个FID客户端连接到MDT或者OST运行的sequence server服务获取唯一的sequence.
  • lustre客户端创建新的对象的时候,客户端会为这个对象申请在FID中的sequence,是由后端的storage targets中的sequence server授权给客户端的sequence range,客户端基于这个sequence range生成对象FID中的sequenceFID中的sequence number和object id是每个客户端维护的sequence number的计数器和object id的自增序列。如果客户端超出了有MDT或者OST授权sequence range,这时候会再次请求storage target(MDT或者OST)申请一个新的sequence range.
  • 新文件(文件大小> stripe_size)的创建,客户端会使用来自MDT授权的的sequence number来构造新文件的FID,此时的FID是用来标识MDT上这个文件的元数据对象。如果lustre集群有多个OST,这些OST存储文件的实际数据,这种情况下MDS充当FID申请客户端,向OST申请sequence number构造FID来标识每个OST上数据对象。lustre中定义sequence服务端和客户端类型。具体参照如下
代码语言:javascript
复制
/* seq client type */
// sequence 服务的客户端类型
enum lu_cli_type {
	// 负责申请mdt上的Sequence 客户端类型
	LUSTRE_SEQ_METADATA = 1,
	// 负责处理ost上数据对象的 sequence客户端类型
	LUSTRE_SEQ_DATA
};

// sequence 服务端类型
enum lu_mgr_type {
		// 
        LUSTRE_SEQ_SERVER,
        LUSTRE_SEQ_CONTROLLER
};

// 运行于mdt上的sequence 服务
static int mdt_seq_init(const struct lu_env *env, struct mdt_device *mdt)
{
	/* init sequence controller server(MDT0) */
	if (ss->ss_node_id == 0) {
		// 运行mdt0上的sequence controller,该controller负责分配seq的范围给sequence server
		rc = seq_server_init(env, ss->ss_control_seq, mdt->mdt_bottom,
				     mdt_obd_name(mdt), LUSTRE_SEQ_CONTROLLER,
				     ss);
		if (rc)
			GOTO(out_seq_fini, rc);
	}
	// 运行其他mdt(包括mdt0)上的sequence server服务,负责给fid客户端分配sequnce来构建fid
	rc = seq_server_init(env, ss->ss_server_seq, mdt->mdt_bottom,
			     mdt_obd_name(mdt), LUSTRE_SEQ_SERVER, ss);
	if (rc)
		GOTO(out_seq_fini, rc);

	// 初始化连接sequence controller的客户端,主要是和mdt0通信,获取sequence range
	rc = mdt_seq_init_cli(env, mdt);
	EXIT;
out_seq_fini:
	if (rc)
		mdt_seq_fini(env, mdt);

	return rc;
}

// 运行于ost上的sequnce服务
int ofd_fid_init(const struct lu_env *env, struct ofd_device *ofd)
{
	// OST上启动一个的Seqence server服务
	rc = seq_server_init(env, ss->ss_server_seq, ofd->ofd_osd, obd_name,
			     LUSTRE_SEQ_SERVER, ss);
	if (rc) {
		CERROR("%s: seq server init error: rc = %d\n", obd_name, rc);
		GOTO(out_server, rc);
	}

	// 客户端的初始化
	seq_client_init(ss->ss_client_seq, NULL, LUSTRE_SEQ_DATA,
			name, NULL);
	rc = seq_server_set_cli(env, ss->ss_server_seq, ss->ss_client_seq);
out_name:
	OBD_FREE(name, len);

	return rc;
}
  • 每个ost或者mdt上根据seq_server_handle请求的类型来判断是请求mdt0上的sequence controller还是 非mdt0和ost上的sequence server.
代码语言:javascript
复制
// sequence rpc请求的op type
enum seq_rpc_opc {
        SEQ_QUERY                       = 700,
        SEQ_LAST_OPC,
        SEQ_FIRST_OPC                   = SEQ_QUERY
};

// sequence服务的类型类型
enum seq_op {
        SEQ_ALLOC_SUPER = 0,
        SEQ_ALLOC_META = 1
};


// 请求的查询sequence rpc 的op_type
struct req_msg_field RMF_SEQ_OPC =
        DEFINE_MSGF("seq_query_opc", 0,
                    sizeof(__u32), lustre_swab_generic_32s, NULL);


// 请求sequence range范围
struct req_msg_field RMF_SEQ_RANGE =
        DEFINE_MSGF("seq_query_range", 0,
                    sizeof(struct lu_seq_range),
                    lustre_swab_lu_seq_range, NULL);
EXPORT_SYMBOL(RMF_SEQ_RANGE);

// seq_handler 是sequence处理函数;sequence的处理函数,对于lustre客户端,mdt是自身申请文件数据的fid;于此mdt需要作为fid的客户端rpc连接到其他的ost的sequence server申请sequence,返回fid给mdt
static int seq_handler(struct tgt_session_info *tsi)
{
	// 获取fid客户端网络 rpc请求
	opc = req_capsule_client_get(tsi->tsi_pill, &RMF_SEQ_OPC);
	if (opc) {
		out = req_capsule_server_get(tsi->tsi_pill, &RMF_SEQ_RANGE);
		if (!out)
			RETURN(err_serious(-EPROTO));
		// 获取fid客户端网络 rpc请求
		tmp = req_capsule_client_get(tsi->tsi_pill, &RMF_SEQ_RANGE);

		out->lsr_index = tmp->lsr_index;
		out->lsr_flags = tmp->lsr_flags;
		// 在mdt0上的sequence controller或者非mdt0的和ost上的Sequence server处理sequence请求
		rc = seq_server_handle(site, tsi->tsi_env, *opc, out);
	} else {
		rc = err_serious(-EPROTO);
	}

	RETURN(rc);
}
// 运行在mdt和ost上申请sequence的函数,根据请求类型来决策是申请controller上 sequence范围还server上的sequence 
static int seq_server_handle(struct lu_site *site,
			     const struct lu_env *env,
			     __u32 opc, struct lu_seq_range *out)
{
	int rc;
	struct seq_server_site *ss_site;
	struct dt_device *dev;
	ENTRY;

	ss_site = lu_site2seq(site);

	switch (opc) {

	// 在sequence server上申请sequence 范围
	case SEQ_ALLOC_META:
		rc = seq_server_alloc_meta(ss_site->ss_server_seq, out, env);
		break;

	// 在controller上申请一个sequence 范围
	case SEQ_ALLOC_SUPER:
		rc = seq_server_alloc_super(ss_site->ss_control_seq, out, env);
		break;
	default:
		rc = -EINVAL;
		break;
	}
	RETURN(rc);
}

FID Location Database(FLD)

  • 后端的mdt或者ost永远不会拥有相同的sequence,fld client根据数据分片的fid决定数据分片的位置。数据分片位置的查找是基于fid存储表fld.在lustre中所有的fld都存储在mdt0上,其他的mdt或者ost存储了一个子集。fld client发送查询请求给fld serverfld server响应fid给客户端,同时把fid添加到fld client的缓存中来加速访问。
代码语言:javascript
复制
enum fld_op {
	FLD_CREATE = 0,
	FLD_DELETE = 1,
	FLD_LOOKUP = 2,
};


static int seq_server_handle(struct lu_site *site,
			     const struct lu_env *env,
			     __u32 opc, struct lu_seq_range *out)
{

	switch (opc) {
	// 在sequence server上申请sequence 范围
	case SEQ_ALLOC_META:
		rc = seq_server_alloc_meta(ss_site->ss_server_seq, out, env);
		break;

	// 在controller上申请一个sequence 范围
	case SEQ_ALLOC_SUPER:
		rc = seq_server_alloc_super(ss_site->ss_control_seq, out, env);
		break;
	default:
		rc = -EINVAL;
		break;
	}
}

// sequence server申请Sequence
int seq_server_alloc_meta(struct lu_server_seq *seq,
			  struct lu_seq_range *out,
			  const struct lu_env *env)
{
	int rc;
	// 实际的处理函数
	rc = __seq_server_alloc_meta(seq, out, env);
	RETURN(rc);
}

// sequence server申请Sequence范围
static int __seq_server_alloc_meta(struct lu_server_seq *seq,
				   struct lu_seq_range *out,
				   const struct lu_env *env)
{
	struct lu_seq_range *space = &seq->lss_space;
	int rc = 0;
	// 
	rc = seq_server_check_and_alloc_super(env, seq){
		// 向mdt0申请新的可用的sequence range
		rc = seq_client_alloc_super(seq->lss_cli, env);
		// 插入到本地的sequence server的fld
		rc = fld_insert_entry(env, fld, space);
	}

	// 设置sequence server中的sequence range,并且更新seqence server的fld
	rc = range_alloc_set(env, out, seq);

	RETURN(rc);
}


// mdt0上sequnece请求处理
int seq_server_alloc_super(struct lu_server_seq *seq,
			   struct lu_seq_range *out,
			   const struct lu_env *env)
{

	rc = __seq_server_alloc_super(seq, out, env);

	RETURN(rc);
}



// mdt0上同时运行sequence controller和sequence server,这时候只需要在本地申请sequence range即可,不需要网络请求
static int __seq_server_alloc_super(struct lu_server_seq *seq,
				    struct lu_seq_range *out,
				    const struct lu_env *env)
{
	struct lu_seq_range *space = &seq->lss_space;
	// 本mdt0内申请新的sequence 范围
	range_alloc(out, space, seq->lss_width);
	// 内存对应Sequence的范围,同时更新本地的fld
	rc = seq_store_update(env, seq, out, 1 /* sync */)
	{
		rc = fld_server_create(env, seq->lss_site->ss_server_fld, out,th)
		{
			rc = fld_index_create(env, fld, range, th);
		}
	}

	RETURN(rc);
}
  • lustre中的fid申请请求到了mdt或者ost,是由sequence serverfld_server_lookup函数处理.其逻辑先查找fld cache,如果没有找到再去查找fld数据库,比如lustre客户端创建一个文件,首先是在mdt上会有一个元数据inode对应的fid,存储在mdt端的fld,如果这个文件设置的stripe,那么mdt端会作为fid client请求ostsequence server,返回fid后,mdt持久化到本地的fld中。接下来看看具体的实现
代码语言:javascript
复制
// lustre sequence定义了2种类型的操作,一个是查询,一个是读取Sequence
struct tgt_handler fld_handlers[] = {
	TGT_FLD_HDL_VAR(0,	FLD_QUERY,	fld_handle_query),
	TGT_FLD_HDL_VAR(0,	FLD_READ,	fld_handle_read),
};

// sequence查询的请求处理函数
static int fld_handle_query(struct tgt_session_info *tsi)
{
	int	rc;

	ENTRY;

	req_capsule_set(tsi->tsi_pill, &RQF_FLD_QUERY);
	// 处理Sequence的查找工作
	rc = fld_handle_lookup(tsi)
	{
		rc = fld_server_lookup(tsi->tsi_env, fld, in->lsr_start, out)
		{
			// 优先查找fld cache是否存在
			rc = fld_local_lookup(env, fld, seq, range);

			// 如果不存在,则请求mdt0申请一个sequence range
			rc = fld_client_rpc(fld->lsf_control_exp,
				    range, FLD_QUERY, NULL);
			if (rc == 0)
				// 插入到本地fld Cache
				fld_cache_insert(fld->lsf_cache, range);
			}
	}

	RETURN(rc);
}


// sequence读取的请求处理函数
static int fld_handle_read(struct tgt_session_info *tsi)
{
	struct obd_export *exp = tsi->tsi_exp;
	struct lu_site *site = exp->exp_obd->obd_lu_dev->ld_site;
	struct lu_seq_range *in;
	void *data;
	int rc;

	ENTRY;

	// 初始化请求包
	req_capsule_set(tsi->tsi_pill, &RQF_FLD_READ);

	// 获取来自`fld client`的请求
	in = req_capsule_client_get(tsi->tsi_pill, &RMF_FLD_MDFLD);
	if (!in)
		RETURN(err_serious(-EPROTO));

	req_capsule_set_size(tsi->tsi_pill, &RMF_GENERIC_DATA, RCL_SERVER,
			     PAGE_SIZE);
	// 服务端需要解包
	rc = req_capsule_server_pack(tsi->tsi_pill);
	if (unlikely(rc != 0))
		RETURN(err_serious(rc));

	data = req_capsule_server_get(tsi->tsi_pill, &RMF_GENERIC_DATA);

	// 本地从本地fld数据库读取seqeunce
	rc = fld_server_read(tsi->tsi_env, lu_site2seq(site)->ss_server_fld,
			     in, data, PAGE_SIZE);
	RETURN(rc);
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-26,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • IO访问流程概览
  • 数据查找和创建访问流程
  • MDT上扩展属性
  • FID文件唯一标识
  • FID Location Database(FLD)
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档