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是由用戶、系統網絡狀況、內存共同決定的,是不是呢?
?
轉載于:https://www.cnblogs.com/wajika/p/6424204.html
總結
以上是生活随笔為你收集整理的NFS客户端、服务器协商读写粒度(rsize、wsize)流程 【转】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何获取客户端的真实IP
- 下一篇: 算法笔记_044:表达式计算求值(Jav