nfs-ganesha路径梳理
1. 结构及组成
1.1 FSAL
FSAL(File System Abstraction Layer),文件系统抽象层,将后端存储抽象为统一的、命名空间独立的API,供应用层来调用。
命名空间是什么?内核命名空间?
FSAL provides a namespace independent
API
1.2 ntirpc
一个nfs ganesha的组件,与GRPC相比?
为什么内核中喜欢使用RPC?(OpenHarmony内核中使用了大量的RPC,几乎每个组件都具有client与server)
Ganesha 的核心模块
1.3 Memory Manager
负责 Ganesha 的内存管理。
1.4 RPCSEC_GSS
负责使用 RPCSEC_GSS 的数据传输,通常使用 krb5, SPKM3 或 LIPKEY 来管理安全。
1.5 NFS 协议模块
负责 NFS 消息结构的管理Metadata(Inode)
1.6 Cache
负责元数据缓存管理
1.7 File Content Cache
负责数据缓存管理
1.8 Hash Tables
提供了基于红黑树的哈希表,这个模块在 Ganesha 里用到很多。
1.9 LTTNG
用于追踪应用程序路径,通过编译时-DUSE_LTTNG=ON激活
2. 调用链
2.1 启动
由src/MainNFSD/nfs_main.c或src/MainNFSD/nfs_lib.c中的main函数,启动后台服务进程(编译好的文件为/usr/bin/ganesha.nfsd,服务文件为src/scripts/systemd/nfs-ganesha.service.el7)。
Main函数中做参数解析操作后,转入nfs_prereq_init,初始化tirpc。
之后进入nfs_init_init,初始化Init的mutex和cond,之后进行初始化操作。
屏蔽SIGPIPE信号(由sig handler处理(sig handler里只是简单忽略并标记client已经关闭))
启动start_fsals,主要包括mdcache(CEPH)和pseudofs.
- start_fsals->load_config_from_parse->proc_block->do_block_init(载入FASL)
init_server_pkgs,初始化mdcache_pkginit
nfs_start_grace/nfs_wait_for_grace_enforcement,恢复
nfs_start,正式启动
nfs_Init,包括exports_pkginit,初始化clientid(通过hashtable存储)和state_id(通过hashtable存储),owner(hashtable),session_id(hash_table)以及session_pool(提前分配空间,用于memalloc时将数据存储在一起,避免碎片),dupreq2_pkginit,create_pseudofs,nfs_Init_svc,nfs_rpc_cb_pkginit
nfs_Start_threads,包括delayed executor(多个线程),sigmgr_thrid(信号处理),admin_thrid(主线程),reaper thread,fridgethr_init(类似idle)
delayed_get_work:通过avltree_container_of
nfs_Init_svc中创建Create_SVCXPRTs,即创建Create_udp和Create_tcp,绑定处理方式,为nfs_rpc_dispatch_udp_NFS和nfs_rpc_dispatch_tcp_NFS,其中均绑定为nfs_rpc_valid_NFS,并通过Register_program将handler注册到ntirpc中,在nfs_rpc_valid_NFS其中由nfs_rpc_process_request进行处理。其中通过svc_auth_authenticate鉴权(ntirpc中成员),最终移交给service_function,即nfs4_Compound,其中process_one_op进行处理(client发送可以发送多个请求,但是处理将一个一个处理),其中通过optabv4分发到具体操作,例如nfs4_op_access等。nfs4_op_setclientid等也是其中成员
2.2 操作
按照上述,当请求到来时会分发到nfs4_op_create等操作中。
以nfs4_op_create为例,先进行check_quota,然后移交给fsal_create
统一文件处理结构如下:
1 | struct compound_data { |
请求为一组有序的操作,第一个为getrootfh。getrootfh会设置全局的file handler和data中的current_obj。这些都是fsal定义的统一类型。后续操作中,会获取当前的current_obj,然后转换为自己的file handler(例如VFS使用container of和偏移量来计算得到自己的file handler),然后调用文件系统接口获得fd,进行实际操作。
NFS存在一系列的exports,这些exports例如文件路径/etc/nfs,首先通过getrootfh获得根的file handler(ganesha会使用pseudofs)来将路径映射到file handler(函数get_gsh_export_by_pseudo->_get_gsh_export_ref)通过比较每个export中记录的地址来映射到pseudofs中的路径。
3. client提供信息
1 | LogEvent(COMPONENT_DISPATCH, |
1 | Dec 23 02:47:59 openeuler-vm-2203 nfs-ganesha[53234]: [svc_5] nfs_rpc_process_request :DISP :EVENT :hello= of 44 bytes and str len 0 |
存在auth字段(opaque_auth已废弃,采用rq_cred_body),但是内容为空
rm_xid和rm_xdr:ntirpc中的标识?
rpcprog_t cb_prog; rpcvers_t cb_vers; rpcproc_t cb_proc; 程序,版本,函数
可以拿到ip
4. 并行
包括三种技术
pNFS(客户端首先发送请求获取数据分布信息,后直接与NFS存储节点进行交互,跳过NFS元数据节点,部分文件系统使用)
multipath(创建多个客户端,通过不同IP连接同一个server)
nconnect(创建多个客户端,连接同一个server)
4.1 pNFS
首先向server通过getlayout请求获取layout,服务端返回当前请求文件的对应地址的ds的id,接着客户端发送getdeviceinfo获取对应的ip,后直接与ds交流,通过is_hole来获取空地址,向空地址写入后,发送到ds,然后向服务器更新layout(layoutcommit),最后layout return(nfs-ganesha中规定必须return),当server没有人读时,尝试merge这些layout(ext4中实现或相应fsal中实现),完成COW操作。(通过bl_submit_bio和zero_user_segment初始化页)
lock:租期锁,超时释放,客户端重启后通过grace重新拿到同一把锁。并发写是被允许的,因为支持直接向ds通过COW写入,但是错误不会保证。
layoutdriver:使用set_pnfs_layoutdriver与set_layoutdriver设置驱动。加载内核模块之后,nfs简单地写入块设备即可,由块设备负责进行同步(例如使用iscsi)。当前同一filesystem的一种layouttype只支持一种driver。当bio callback需要drain时,调用do_callback_layoutrecall。如果是file,那么需要layoutcommit。
路径
nfs4_get_device_info->nfs4_proc_getdeviceinfo->nfs4_call_sync->nfs4_do_call_sync alloc_deviceid_node
nfs4_ff_layout_prepare_ds->nfs4_pnfs_ds_connect->_nfs4_pnfs_v4_ds_connect->nfs4_set_ds_client(nfs_v4)(nfs_v4_clientops)->nfs4_alloc_client->nfs_create_rpc_client->rpc_create
nfs4_do_open->>>bl_alloc_lseg->bl_alloc_extent->bl_find_get_deviceid->nfs4_find_get_deviceid->
nfs_readpage->nfs_readpage_async->nfs_pageio_do_add_request->(Layoutget)(pnfs_layout_segment)pg_init->pnfs_layout_process->bl_alloc_lseg->bl_alloc_extent->bl_find_get_deviceid->nfs4_find_get_deviceid->
nfs_readpage->(读取page,通过page_file_mapping获取到inode)>pnfs_generic_pg_readpages->pnfs_do_read->pnfs_try_to_read_data->read_pagelist->bl_read_pagelist->bl_submit_bio->submit_bio
nfs_page_async_flush->__nfs_pageio_add_request->pnfs_generic_pg_writepages…
vs_proc->svc_generic_init_request->
layoutget
原数据
1
2
3
4
5struct LAYOUTGET4resok {
bool logr_return_on_close;
stateid4 logr_stateid;
layout4 logr_layout<>;
};1
2
3
4
5
6
7struct layout4 {
offset4 lo_offset;
length4 lo_length;
layoutiomode4 lo_iomode;
layout_content4 lo_content;
};解析(通过nfsd4_decode_layoutget解析)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33static __be32
nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
{
argp->taglen = be32_to_cpup(p++);
argp->minorversion = be32_to_cpup(p++);
argp->opcnt = be32_to_cpup(p++);
op->opnum = be32_to_cpup(p++);
op->status = nfsd4_dec_ops[op->opnum](argp, &op->u);
}
static __be32
nfsd4_decode_layoutget(struct nfsd4_compoundargs *argp,
struct nfsd4_layoutget *lgp)
{
DECODE_HEAD;
READ_BUF(36);
lgp->lg_signal = be32_to_cpup(p++);
lgp->lg_layout_type = be32_to_cpup(p++);
lgp->lg_seg.iomode = be32_to_cpup(p++);
p = xdr_decode_hyper(p, &lgp->lg_seg.offset);
p = xdr_decode_hyper(p, &lgp->lg_seg.length);
p = xdr_decode_hyper(p, &lgp->lg_minlength);
status = nfsd4_decode_stateid(argp, &lgp->lg_sid);
if (status)
return status;
READ_BUF(4);
lgp->lg_maxcount = be32_to_cpup(p++);
DECODE_TAIL;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81def listen():
...
def unpack_COMPOUND4res(self):
data = types.COMPOUND4res()
data.status = self.unpack_nfsstat4()
data.tag = self.unpack_utf8str_cs()
data.resarray = self.unpack_array(self.unpack_nfs_resop4)
if hasattr(self, 'filter_COMPOUND4res'):
data = getattr(self, 'filter_COMPOUND4res')(data)
return data
def unpack_array(self, unpack_item):
n = self.unpack_uint()
return self.unpack_farray(n, unpack_item)
def unpack_nfs_resop4(self):
data = types.nfs_resop4()
data.resop = self.unpack_nfs_opnum4()
if data.resop == const.OP_ACCESS:
data.opaccess = self.unpack_ACCESS4res()
def unpack_LAYOUTGET4res(self):
data = types.LAYOUTGET4res()
data.logr_status = self.unpack_nfsstat4()
if data.logr_status == const.NFS4_OK:
data.logr_resok4 = self.unpack_LAYOUTGET4resok()
elif data.logr_status == const.NFS4ERR_LAYOUTTRYLATER:
data.logr_will_signal_layout_avail = self.unpack_bool()
else:
pass
if hasattr(self, 'filter_LAYOUTGET4res'):
data = getattr(self, 'filter_LAYOUTGET4res')(data)
return data
def unpack_LAYOUTGET4resok(self):
data = types.LAYOUTGET4resok()
data.logr_return_on_close = self.unpack_bool()
data.logr_stateid = self.unpack_stateid4()
data.logr_layout = self.unpack_array(self.unpack_layout4)
if hasattr(self, 'filter_LAYOUTGET4resok'):
data = getattr(self, 'filter_LAYOUTGET4resok')(data)
return data
def unpack_layout4(self):
data = types.layout4()
data.lo_offset = self.unpack_offset4()
data.lo_length = self.unpack_length4()
data.lo_iomode = self.unpack_layoutiomode4()
data.lo_content = self.unpack_layout_content4()
if hasattr(self, 'filter_layout4'):
data = getattr(self, 'filter_layout4')(data)
return data
def unpack_layout_content4(self):
data = types.layout_content4()
data.loc_type = self.unpack_layouttype4()
data.loc_body = self.unpack_opaque()
if hasattr(self, 'filter_layout_content4'):
data = getattr(self, 'filter_layout_content4')(data)
return data
if layout.loc_type == LAYOUT4_NFSV4_1_FILES:
def unpack_pnfs_block_layout4(self):
data = types.pnfs_block_layout4()
data.blo_extents = self.unpack_array(self.unpack_pnfs_block_extent4)
if hasattr(self, 'filter_pnfs_block_layout4'):
data = getattr(self, 'filter_pnfs_block_layout4')(data)
return data
def unpack_pnfs_block_extent4(self):
data = types.pnfs_block_extent4()
data.bex_vol_id = self.unpack_deviceid4()
data.bex_file_offset = self.unpack_offset4()
data.bex_length = self.unpack_length4()
data.bex_storage_offset = self.unpack_offset4()
data.bex_state = self.unpack_pnfs_block_extent_state4()
if hasattr(self, 'filter_pnfs_block_extent4'):
data = getattr(self, 'filter_pnfs_block_extent4')(data)
return data返回值
1
2
3
4
5
6
7
8
9struct nfs4_layoutget_res {
struct nfs4_sequence_res seq_res;
int status;
__u32 return_on_close;
struct pnfs_layout_range range;
__u32 type;
nfs4_stateid stateid;
struct nfs4_layoutdriver_data *layoutp;
};转换类型
1
2
3
4
5
6
7
8
9
10struct pnfs_layout_segment {
struct list_head pls_list;
struct list_head pls_lc_list;
struct list_head pls_commits;
struct pnfs_layout_range pls_range;
refcount_t pls_refcount;
u32 pls_seq;
unsigned long pls_flags;
struct pnfs_layout_hdr *pls_layout;
};
4.2 multipath
直接复用nconnect,只不过创建的server地址不同
4.3 nconnect
- 路径:
nfs4_set_client(nfs_v4)(nfs_v4_clientops)->nfs4_alloc_client->nfs_create_rpc_client->rpc_create
- 是
其他
- 打印日志(src/include/log.h)
1 | LogInfo(COMPONENT_DISPATCH, |
参考
【ceph】什么是NFS-Ganesha和fuse-CSDN博客
NFS-Ganesha 核心架构解读_文化 & 方法_陈涛_InfoQ精选文章
nfs-ganesha rados_ng恢复原理分析_grace period ganesha-CSDN博客
【nfs-ganesha】nfs4.1 rados_cluster数据恢复机制 - 苏格拉底的落泪 - 博客园
nfs-ganesha - thread model - fridgethr - 简书
使用glibc xdr对结构体进行编解码_a-xdr编码-CSDN博客
[LTTng学习之旅]——环境搭建_编译tlg需要依赖fst的环境,请参考文档安装fts相关环境-CSDN博客
nfs-ganesha cache代码分析,转mdcache readdir_nfs-ganesha 去mdcache-CSDN博客
Kerberos认证原理及基于Kerberos认证的NFS文件共享 - 佟晖 - 博客园
RPCSEC_GSS · nfs-ganesha/nfs-ganesha Wiki
How does nfs-ganesha handle kerberos authentication? · Issue #903 · nfs-ganesha/nfs-ganesha
Kerberos setup for NFS Ganesha in Ceph · nfs-ganesha/nfs-ganesha Wiki
Nfsv4 configuration - Linux NFS
FS#50663 : [nfs-utils] rpc-gssd.service: Inconsistency detected by ld.so: dl-close.c: 811
DBus - D-Bus模块-调试 - 《Linux》 - 极客文档
Fsalsupport · nfs-ganesha/nfs-ganesha Wiki
File Systems · nfs-ganesha/nfs-ganesha Wiki
nfs-ganesha: pseudofs Struct Reference
nfs-ganesha: nfs4_pseudo.c File Reference
支持NFS Multipathing的系统版本 - Kernel - openEuler 论坛
How to tune nconnect to improve NFS performance | FlamingBytes
Use nconnect to effortlessly increase NFS performance | by Emily Potyraj | Medium
nfs存储多路径 nfs 缓存_coolfengsy的技术博客_51CTO博客
认识NFS多路径 | openEuler文档 | openEuler社区 | v23.03
NFS 挂载选项说明:nconnect=、nosharetransport、sharetransport= |技术支持 |SUSE
nfs-ganesha I/O performance · Issue #681 · nfs-ganesha/nfs-ganesha
NFS Enters a Parallel Universe | Enterprise Storage Forum
Add nConnect Support to NFS v4.1 - vDan
NFS Features for Enhanced Performance
vmware.com/docs/nfs-iscsi-multipathing-in-vsphere
PNFS Development Road Map - Linux NFS
RFC5663 中文翻译 中文RFC RFC文档 RFC翻译 RFC中文版
RFC6688 中文翻译 中文RFC RFC文档 RFC翻译 RFC中文版
RFC 6688: Parallel NFS (pNFS) Block Disk Protection
linux-nfs.org PNFS Development
8.9. pNFS | Red Hat Product Documentation
详谈:pNFS增强文件系统架构 - 开源Linux - 博客园
8.10. 在 NFS 中启用 pNFS SCSI 布局 | Red Hat Product Documentation
理解filelayout中layout创建过程-CSDN博客
FILE LAYOUT_file layout(rfc5661)-CSDN博客
理解NFSv4.1布局类型及其在MDS中的查找过程-CSDN博客
从nfs_page结构看PNFS读写流程_nfs的pageio什么意思-CSDN博客
Reference counting in pnfs — The Linux Kernel documentation
Configuring and enabling the BLOCK service - IBM Documentation
《性能之巅》学习笔记之内存和文件系统 其三 vm_area_struct 与 虚拟内存 - 知乎
Linux 物理内存管理涉及的三大结构体之struct page-CSDN博客
- Title: nfs-ganesha路径梳理
- Author: Ethereal
- Created at: 2024-12-22 20:20:26
- Updated at: 2025-02-14 16:51:45
- Link: https://ethereal-o.github.io/2024/12/22/nfs-ganesha路径梳理/
- License: This work is licensed under CC BY-NC-SA 4.0.