linux kernel中的vfs
和lustre server
质检的接口层。lustre
客户端是由不同的服务组成,每个服务层提供特定的lustre服务。比如MDC
提供访问一个或者多个MDS
的服务,OSC
提供访问lustre集群中的OST
.MGC
用来管理管理整个集群配置的客户端,每个MGC
都会和MGS
进行通信获取集群的最新的配置变更。MDC
是lustre集群中MDS
的客户端,每个MDC
连接到MDS
在客户端测提供目录或者文件的元数据管理操作服务。LMV
是logical metadata volume
简称,它用来聚合多个MDC
呈现单一元数据命名空间给客户端,提供透明方式访问所有的MDT。比如文件系统的目录树被分割为多个子树分别存储在多个MDT上,但是对于客户端看到的是统一一致的命名空间。LOV
是logical 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
层进行聚合,提供完整文件的数据。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
获取文件数据。MDS
,MDS
根据文件的layout
发送rpc请求连接后端的OST
,当OST
完成了数据对象的创建后,返回FID(一个或者多个)
给MDS
完成这个文件的对象创建。FID
是一个128个字节的文件标识。FID
是lustre客户端用来文件的唯一标识。每个OST
上文件分片对象都是有唯一的FID
于此同时每个数据分片在MDT
上都会对应该这个文件的FID
mount
命令,lustre客户端内部的MGC
服务连接到MGS
获取整个文件系统的配置信息,挂载整个root
目录树metadata
是存储在mdt上文件的inode上的,而对于lustre 文件系统fid
是作为文件或者数据对象的唯一标识。那么客户端请求创建一个文件,在MDT
上会有创建fid
,这个fid
也会对应一个MDT
上的inode.这inode上会存储客户端请求创建文件的inode
,这个文件布局信息以及跨哪些OST
都是以扩展属性保存在这个inode
上的.MDT上扩展属性的layout ea
是trusted.link
.定义的value是struct struct lov_mds_md_v3
// 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
文件唯一标识stripe
文件对象,包括目录的entries
、内部配置文件,这些数据都是以对象形式在lustre中存储的。为了标识整个lustre文件系统中每个对象的唯一性,lustre为每个对象提供一个FID
作为唯一标识。FID
是128位长度长度,包括了三个部分64位的序列号、32位的对象ID、32位的版本号
。FID
的定义如下: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);
}
sequence controller
服务是在MDT0
上运行,每个MDT
和OST
运行一个sequence server
服务。lustr整套服务启动后运行在MDT
或者OST
上的sequence server
和MDT0
上的sequence controller
进行通信获取一个sequence
的范围;每个FID
客户端连接到MDT
或者OST
运行的sequence server
服务获取唯一的sequence
.FID
中的sequence
,是由后端的storage targets
中的sequence server
授权给客户端的sequence range
,客户端基于这个sequence range
生成对象FID
中的sequence
。FID
中的sequence number和object id
是每个客户端维护的sequence number
的计数器和object id
的自增序列。如果客户端超出了有MDT
或者OST
授权sequence range
,这时候会再次请求storage target(MDT或者OST)
申请一个新的sequence range
.MDT
授权的的sequence number
来构造新文件的FID
,此时的FID
是用来标识MDT
上这个文件的元数据对象。如果lustre集群有多个OST
,这些OST
存储文件的实际数据,这种情况下MDS
充当FID
申请客户端,向OST
申请sequence number
构造FID
来标识每个OST
上数据对象。lustre中定义sequence
服务端和客户端类型。具体参照如下/* 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;
}
seq_server_handle
请求的类型来判断是请求mdt0上的sequence controller
还是 非mdt0和ost上的sequence server
.
// 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 server
,fld server
响应fid
给客户端,同时把fid
添加到fld client
的缓存中来加速访问。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);
}
fid
申请请求到了mdt
或者ost
,是由sequence server
的fld_server_lookup
函数处理.其逻辑先查找fld cache
,如果没有找到再去查找fld
数据库,比如lustre客户端创建一个文件,首先是在mdt
上会有一个元数据inode对应的fid,存储在mdt
端的fld
,如果这个文件设置的stripe
,那么mdt
端会作为fid client
请求ost
的sequence server
,返回fid
后,mdt
持久化到本地的fld
中。接下来看看具体的实现// 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);
}