NFS客户端、服务器协商读写粒度(rsize、wsize)流程 【转】
rsize和wsize决定了网络文件系统(NFS)一次网络交互所能够读写的数据块的大小,rsize和wsize的大小对网络文件系统(NFS)的性能有重要影响。rsize和wsize的大小是在用户配置的基础上客户端和服务器端共同协商的结果。
本文面向NFS的开发者和维护者,主要介绍rsize和wsize在客户端和服务器之间协商的流程,同时介绍rsize和wsize的设置的方法。文章以V3的实现为主线,顺便提及V4,代码基于Linux-pnfs-2.6.32。
(1) 用户挂载
用户在挂载NFS文件系统的时候,可以用挂载选项指定rsize和wsize的大小,如下:
mount -t nfs -o vers=3,xxxx,rsize=????,wsize=????
Linux mount 命令将由系统调用下发到内核的vfs层,由vfs_kern_mount执行挂载操作。Vfs_kern_mount将调用具体文件系统的get_sb方法创建并初始化super block。对于NFS文件系统来说,这个方法便是Nfs_get_sb/Nfs4_get_sb,这两个函数定义在nfs模块代码的super.c文件当中。
(2) Nfs_get_sb/nfs4_get_sb
这里主要介绍nfs_get_sb,nfs4_get_sb可以以此为参照。Nfs_get_sb的部分函数代码如下:
2142 static int nfs_get_sb(structfile_system_type *fs_type,
2143 int flags, const char *dev_name, void*raw_data, struct vfsmount *mnt)
2144 {//其中raw_data包含了用户挂载时的挂载参数
2145 struct nfs_server *server = NULL;
2146 struct super_block *s;
2147 struct nfs_parsed_mount_data *data;//内核挂载参数
2148 struct nfs_fh *mntfh;
2149 struct dentry *mntroot;
2150 int (*compare_super)(structsuper_block *, void *) = nfs_compare_super;
2151 struct nfs_sb_mountdata sb_mntdata = {
2152 .mntflags = flags,
2153 };
2154 int error = -ENOMEM;
2155
2156 data = nfs_alloc_parsed_mount_data(3);//分配并且初始化内核挂载参数,此时data中的rsize和wsize分别被初始化为NFS_MAX_FILE_IO_SIZE(4U * 1048576U = 4MB);
2157 mntfh = kzalloc(sizeof(*mntfh),GFP_KERNEL);
2158 if (data == NULL || mntfh == NULL)
2159 goto out_free_fh;
2160
2161 security_init_mnt_opts(&data->lsm_opts);
2162
2163 /* Validate the mount data */
2164 error = nfs_validate_mount_data(raw_data,data, mntfh, dev_name);//利用raw_data和dev_name设置内核挂载参数,这时data当中rsize和wsize被设置为用户挂载时指定的大小。
2165 if (error < 0)
2166 goto out;
2167
2168 #ifdef CONFIG_NFS_V4
2169 if (data->version == 4) {
2170 error = nfs4_try_mount(flags,dev_name, data, mnt);
2171 kfree(data->client_address);
2172 goto out;
2173 }
2174 #endif /* CONFIG_NFS_V4 */
2175
2176 /* Get a volume representation */
2177 server = nfs_create_server(data,mntfh);//创建server,server中保存了和服务器交互的参数,在这个函数中客户端将和服务器协商rsize和wsize的大小。
2178 if (IS_ERR(server)) {
2179 error = PTR_ERR(server);
2180 goto out;
2181 }
}
(3) 客户端协商过程
在nfs_create_server/nfs4_create_server中,首先会调用nfs_init_server对server进行初始化,其中会利用用户设置的data中的rsize和wsize对server中的rsize和wsize进行赋值;然后会调用nfs_probe_fsinfo过程,nfs_probe_fsinfo过程会通过远程过程调用获取服务器所允许的读写粒度的相关信息,最终决定系统所允许的rsize和wsize的大小。nfs_probe_fsinfo函数的部分代码如下:
927 static int nfs_probe_fsinfo(struct nfs_server*server, struct nfs_fh *mntfh, struct nfs_fattr *fatt r)
928 {
929 struct nfs_fsinfo fsinfo;
930 struct nfs_client *clp = server->nfs_client;
931 int error;
932
933 dprintk("-->nfs_probe_fsinfo()\n");
934
935 if(clp->rpc_ops->set_capabilities != NULL) {
936 error =clp->rpc_ops->set_capabilities(server, mntfh);
937 if (error < 0)
938 goto out_error;
939 }
940
941 fsinfo.fattr = fattr;
942 nfs_fattr_init(fattr);
943 error =clp->rpc_ops->fsinfo(server, mntfh, &fsinfo);//通过远程过程调用获取服务器导出文件系统信息,其中包括所允许的读写粒度的相关信息。
944 if (error < 0)
945 goto out_error;
946
947 nfs_server_set_fsinfo(server,&fsinfo);//协商决定最终的rsize和wsize
}
其中nfs_server_set_fsinfo函数的部分代码如下:
869 staticvoid nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo*fsinfo)
870 {
871 unsigned long max_rpc_payload;
872
873 /* Work out a lot of parameters */
874 if (server->rsize == 0)//如果用户没有对rsize进行设置
875 server->rsize =nfs_block_size(fsinfo->rtpref, NULL);//rtpref->(perfer.优先)读操作的传输粒度
876 if (server->wsize == 0) 如果用户没有对wsize进行设置
877 server->wsize =nfs_block_size(fsinfo->wtpref, NULL);//wtpref->(perfer.优先)写操作的传输粒度
878
879 if (fsinfo->rtmax >= 512 && server->rsize >fsinfo->rtmax)//rtmax->最大读操作的传输粒度
880 server->rsize =nfs_block_size(fsinfo->rtmax, NULL);
881 if (fsinfo->wtmax >= 512 && server->wsize >fsinfo->wtmax) //wtmax->最大读操作的传输粒度
882 server->wsize =nfs_block_size(fsinfo->wtmax, NULL);
883
884 max_rpc_payload = nfs_block_size(rpc_max_payload(server->client),NULL); //Rpc_max_payload 对于传输协议tcp和udp来说不一样。1、对于tcp来说rpc_max_payload就是一个分片大小,#define RPC_MAX_FRAGMENT_SIZE ((1U << 31) -1);2、对于udp来说rpc_max_payload是一个IP包减去IP、UDP、RPC头之后的大小,(1U <<16) - (MAX_HEADER << 3)。
885 if (server->rsize > max_rpc_payload)
886 server->rsize =max_rpc_payload;
887 if (server->rsize > NFS_MAX_FILE_IO_SIZE)// NFS_MAX_FILE_IO_SIZE(4U * 1048576U = 4MB)
888 server->rsize =NFS_MAX_FILE_IO_SIZE;
889 server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >>PAGE_CACHE_SHIFT;
890
891 server->backing_dev_info.name = "nfs";
892 server->backing_dev_info.ra_pages = server->rpages *NFS_MAX_READAHEAD;
893
894 if (server->wsize > max_rpc_payload)
895 server->wsize =max_rpc_payload;
896 if (server->wsize > NFS_MAX_FILE_IO_SIZE)
897 server->wsize =NFS_MAX_FILE_IO_SIZE;
}
(4) 服务器传递的参数
这里不过多阐述远程过程调用的问题,仅关注对于远程过程调用fsinfo,服务器给客户端传输了什么参数。首先看v3服务器端的处理函数nfsd3_proc_fsinfo。
545 nfsd3_proc_fsinfo(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
546 struct nfsd3_fsinfores *resp)
547 {
548 __be32 nfserr;
549 u32 max_blocksize = svc_max_payload(rqstp);
550
551 dprintk("nfsd: FSINFO(3) %s\n",
552 SVCFH_fmt(&argp->fh));
553
554 resp->f_rtmax = max_blocksize;
555 resp->f_rtpref = max_blocksize;
556 resp->f_rtmult = PAGE_SIZE;
557 resp->f_wtmax = max_blocksize;
558 resp->f_wtpref = max_blocksize;
559 resp->f_wtmult = PAGE_SIZE;
560 resp->f_dtpref = PAGE_SIZE;
}
V4的代码中fsinfo通过getattr实现,通过服务器端的编码函数nfsd4_encode_fattr来看一下服务器给客户端传回了什么参数。
2146 __be32
2147 nfsd4_encode_fattr(struct svc_fh*fhp, struct svc_export *exp,
2148 struct dentry *dentry, __be32*buffer, int *countp, u32 *bmval,
2149 struct svc_rqst *rqstp, intignore_crossmnt)
{
2516 if (bmval0 & FATTR4_WORD0_MAXREAD){
2517 if ((buflen -= 8) < 0)
2518 goto out_resource;
2519 WRITE64((u64)svc_max_payload(rqstp));//客户端解码出来的数值,将决定了客户端rtpref和rtmax的值,详细参见解码函数
2520 }
2521 if (bmval0 &FATTR4_WORD0_MAXWRITE) {
2522 if ((buflen -= 8) < 0)
2523 goto out_resource;
2524 WRITE64((u64)svc_max_payload(rqstp)); //客户端解码出来的数值,将决定了客户端rtpref和rtmax的值,详细参见解码函数
}
既然参见了解码函数,那么把客户端解码函数相关部分代码也贴一贴吧。
Decode_fsinfo{
4366 fsinfo->rtmult = fsinfo->wtmult= 512; /* ??? */
4367
4368 if ((status = decode_attr_lease_time(xdr,bitmap, &fsinfo->lease_time)) != 0)
4369 goto xdr_error;
4370 if ((status =decode_attr_maxfilesize(xdr, bitmap, &fsinfo->maxfilesize)) != 0)
4371 goto xdr_error;
4372 if ((status =decode_attr_maxread(xdr, bitmap, &fsinfo->rtmax)) != 0)
4373 goto xdr_error;
4374 fsinfo->rtpref = fsinfo->dtpref =fsinfo->rtmax;
4375 if ((status =decode_attr_maxwrite(xdr, bitmap, &fsinfo->wtmax)) != 0)
4376 goto xdr_error;
4377 fsinfo->wtpref = fsinfo->wtmax;
}
综上可以看出服务器端rtperf、rtmax、wtperf、wtmax,都是由函数svc_max_payload决定的,函数代码如下:
1299 u32 svc_max_payload(const structsvc_rqst *rqstp)
1300 {// xcl_max_payload 和sv_max_payload的较小者决定了函数的返回值,其中sv_max_payload是rpc服务所允许的最大传输粒度,也就是nfsd服务所允许的最大粒度。
1301 u32 max =rqstp->rq_xprt->xpt_class->xcl_max_payload;
1302
1303 if(rqstp->rq_server->sv_max_payload < max)
1304 max =rqstp->rq_server->sv_max_payload;
1305 return max;
1306 }
1307EXPORT_SYMBOL_GPL(svc_max_payload);
其中xcl_max_payload在tcp和udp当中的定义如下。
1212 static struct svc_xprt_classsvc_tcp_class = {
1213 .xcl_name = "tcp",
1214 .xcl_owner = THIS_MODULE,
1215 .xcl_ops = &svc_tcp_ops,
1216 .xcl_max_payload =RPCSVC_MAXPAYLOAD_TCP,//(1 *1024 *1024U) =1M
1217 };
685 static struct svc_xprt_classsvc_udp_class = {
686 .xcl_name = "udp",
687 .xcl_owner = THIS_MODULE,
688 .xcl_ops = &svc_udp_ops,
689 .xcl_max_payload = RPCSVC_MAXPAYLOAD_UDP,// (32*1024u) =32k
690 };
(5) Nfsd服务所允许的最大传输粒度
Nfsd模块加载之后,用户就可以通过proc虚拟文件系统接口对nfsd服务所允许的最大传输粒度进行设置了,命令为echo xxxx > /proc/fs/nfsd/max_block_size ,这个虚拟接口在nfsd模块中影响到服务启动时的一个参数nfsd_max_blksize,这个参数最终会影响到Nfsd服务所允许的最大传输粒度。
Nfsd服务启动函数的相关部分代码如下:
Nfsd_create_serv {
321 if (nfsd_max_blksize == 0) {//如果用户没有通过proc接口设置这个参数
322 /* choose a suitable default*/
323 struct sysinfo i;
324 si_meminfo(&i);
325 /* Aim for 1/4096 of memoryper thread
326 * This gives 1MB on 4Gigmachines
327 * But only uses 32K on 128Mmachines.
328 * Bottom out at 8K on 32M andsmaller.
329 * Of course, this is only adefault.
330 */
331 nfsd_max_blksize =NFSSVC_MAXBLKSIZE;// (1*1024*1024u) =1M
332 i.totalram <<=PAGE_SHIFT - 12;//i.totalram是系统总共的内存大小,所以如果用户不加以设置,服务器系统的内存的大小也会影响到rsize/wsize
333 while (nfsd_max_blksize >i.totalram &&
334 nfsd_max_blksize >=8*1024*2)//最小是8k哦
335 nfsd_max_blksize /= 2;
336 }
337
338 nfsd_serv =svc_create_pooled(&nfsd_program, nfsd_max_blksize,
339 nfsd_last_thread, nfsd, THIS_MODULE); //nfsd_max_blksize作为参数传给了服务创建函数
}
435 struct svc_serv *
436 svc_create_pooled(struct svc_program*prog, unsigned int bufsize,
437 void (*shutdown)(structsvc_serv *serv),
438 svc_thread_fn func, structmodule *mod)
439 {//nfsd_max_blksize 被传递给了bufsize
440 struct svc_serv *serv;
441 unsigned int npools = svc_pool_map_get();
442
443 serv = __svc_create(prog, bufsize,npools, shutdown);
444
445 if (serv != NULL) {
446 serv->sv_function = func;
447 serv->sv_module = mod;
448 }
449
450 return serv;
451 }
360 static struct svc_serv *
361 __svc_create(struct svc_program *prog,unsigned int bufsize, int npools,
362 void (*shutdown)(struct svc_serv*serv))
363 {
364 struct svc_serv *serv;
365 unsigned int vers;
366 unsigned int xdrsize;
367 unsigned int i;
368
369 if (!(serv = kzalloc(sizeof(*serv), GFP_KERNEL)))
370 return NULL;
371 serv->sv_name =prog->pg_name;
372 serv->sv_program = prog;
373 serv->sv_nrthreads = 1;
374 serv->sv_stats =prog->pg_stats;
375 if (bufsize > RPCSVC_MAXPAYLOAD)
376 bufsize = RPCSVC_MAXPAYLOAD;
377 serv->sv_max_payload =bufsize? bufsize : 4096;
378 serv->sv_max_mesg = roundup(serv->sv_max_payload +PAGE_SIZE, PAGE_SIZE);
379 serv->sv_shutdown = shutdown;
380 xdrsize = 0;
}
流程介绍结束,最后总结一下,rsize/wsize是由用户、系统网络状况、内存共同决定的,是不是呢?
NFS客户端、服务器协商读写粒度(rsize、wsize)流程 【转】的更多相关文章
- NFS客户端挂载目录后无写入权限的解决方案
转载至:https://blog.csdn.net/younger_china/article/details/52089337 在客户机通过 mount -o rw -t nfs 192.168.1 ...
- NFS文件共享服务器搭建
环境准备 centos 7.x+ 两台 192.168.40.128(用作NFS服务端) 192.168.40.129(用作NFS客户端) NFS服务端部署(192.168.40.128 机器上面执行 ...
- nfs客户端的一次处理
为什么要说这个呢,由于节点环境不一致,导致在重建pod时,我们暂且叫该pod为 cxpod,cxpod所在宿主机出现了问题现象如下:一.cxpod始终处于创建中 ContainerCreating [ ...
- NFS客户端阻塞睡眠问题与配置调研
Linux NFS客户端需要很小心地配置,否则在NFS服务器崩溃时,访问NFS的程序会被挂起,用ps查看,进程状态(STAT)处于D,意为(由于IO阻塞而进入)不可中断睡眠(如果是D+,+号表示程序运 ...
- exportfs命令 NFS客户端问题 FTP介绍 使用vsftpd搭建ftp
exportfs命令 • 常用选项 • -a 全部挂载或者全部卸载 • -r 重新挂载 • -u 卸载某一个目录 • -v 显示共享目录 • 以下操作在服务端上 • vim /etc/exports ...
- exportfs命令、NFS客户端问题、FTP介绍、使用vsftpd搭建ftp
6月22日任务 14.4 exportfs命令14.5 NFS客户端问题15.1 FTP介绍15.2/15.3 使用vsftpd搭建ftp 14.4 exportfs命令 当我们修改nfs的配置文件e ...
- NFS客户端挂载及永久生效
1.NFS客户端挂载的命令格式: 挂载命令 挂载的格式类型 NFS服务器提供的共享目录 NFS客户端要挂载的目录mount -t nfs 服务器IP:/共享目录 /本地的挂载点(必须存在) 重启失效 ...
- nfs下的exportfs命令和nfs客户端重新挂载
工作中,如果使用了nfs服务器,会遇到修改nfs服务器配置的情况,如果想重新让客户端加载上修改后的配置,但是又不能重启rpcbind服务,我们需要使用export命令了 exportfs命令 常用选项 ...
- Linux centosVMware NFS exportfs命令、NFS客户端问题、FTP介绍、使用vsftpd搭建ftp
一.exportfs命令 常用选项 -a 全部挂载或者全部卸载 -r 重新挂载 -u 卸载某一个目录 -v 显示共享目录 以下操作在服务端上 vim /etc/exports //增加 /tmp/ 1 ...
随机推荐
- MYSQL数据删除数据,物理空间没释放
当您的库中删除了大量的数据后,您可能会发现数据文件尺寸并没有减小.这是因为删除操作后在数据文件中留下碎片所致.OPTIMIZE TABLE 是指对表进行优化.如果已经删除了表的一大部分数据,或者如果已 ...
- 【Eclipse】Eclipse-Build-缓慢-卡住
Eclipse-Build-缓慢-卡住 eclipse building workspace 卡主_百度搜索 解决building workplace 导致的卡死,使得eclipse加速 - CSDN ...
- WebApi&MVC对比
使用上区分,mvc主要用于建站,web api主要用于构建http服务,当然你非要用mvc来构建Uri式的Api也行,不过显然是没有这个必要的,一个不恰当的比喻就像是你也可以玩破解版的单机游戏,也可以 ...
- Android 7.1.1 之实现 3D Touch
转载请注明出处:http://blog.csdn.net/yyh352091626/article/details/68962736 Shortcut概念 详细实现 BuildConfig 配置 静态 ...
- Using Timers in MFC Applications
Timer Events in MFC Applications Event timers are always handy to have around and useful in nearly e ...
- Jquery Ajax 返回数据类型变成document
下面是我写的一段Jquery Ajax的代码,在chrome下没有问题,在firefox下就算是返回success也提示"系统正忙"; $.ajax({ url: "fa ...
- Dictionary应用
using System; using System.Collections.Generic; using System.Data; using System.Web; using System.We ...
- WinForm 之 应用程序开机自启动设置方法
一.原理 需要开机自启动的程序,需要将其启动程序的路径写到注册表中指定的文件夹下. 二.实现方式 方法1:在生成安装程序时配置: 方法2:在程序运行时动态配置. 三.在生成安装程序时配置 1.右击安装 ...
- linux2.6.30.4内核移植(1)
内核源码:linux2.6.30.4 交叉编译工具:3.4.5 移植linux内核至:TQ2440 1.进入内核顶层目录,修改顶层Makefile,大概在193和194行,将ARCH和CROSS_CO ...
- reStructuredText - 一个比MarkDown更好用的标记语言
文档和教程 http://docutils.sourceforge.net/rst.html http://zh-sphinx-doc.readthedocs.io/en/latest/rest.ht ...