nfs write3

来源:互联网 发布:海康威视报警键盘编程 编辑:程序博客网 时间:2024/06/16 14:27

NFS文件系统中WRITE操作比READ操作要复杂一些。READ操作中只需要将数据从服务器中读取到客户端的缓存页中就可以了,但是WRITE操作中客户端可能将数据写入到服务器的缓存页中,也可能写入到服务器的磁盘中。如果数据写入到服务器的缓存页中了,客户端还需要在适当的时候发起COMMIT请求将数据写入到服务器的磁盘中。

1.WRITE请求的结构

RFC1818规定了WRITE请求报文和应答报文的格式,请求报文格式如下:

[cpp] view plain copy
 print?
  1. struct WRITE3args {  
  2.      nfs_fh3     file;    // 这是目标文件的文件句柄  
  3.      offset3     offset;  // 数据在文件中的偏移值  
  4.      count3      count;   // WRITE请求中数据长度  
  5.      stable_how  stable;  // 数据同步方式  
  6.      opaque      data<>;  // WRITE请求中的数据  
  7. };  
应答报文格式如下:

[cpp] view plain copy
 print?
  1. struct WRITE3resok {  
  2.      wcc_data    file_wcc;        // 文件的属性  
  3.      count3      count;           // 写入到服务器的数据量  
  4.      stable_how  committed;       // 数据在服务器端的同步方式  
  5.      writeverf3  verf;            // 这是一个验证信息  
  6. };  

stable_how是数据同步方式,表示数据写入到服务器的缓存页中还是磁盘中,包含三个取值:

[cpp] view plain copy
 print?
  1. enum stable_how {  
  2.      UNSTABLE  = 0,       // 不强求将数据和元数据写入磁盘中  
  3.      DATA_SYNC = 1,       // 数据必须写入磁盘中,元数据尽量写入磁盘中  
  4.      FILE_SYNC = 2        // 数据和元数据必须写入磁盘中  
  5. };  
请求报文中的stable_how表示客户端的请求方式,而应答报文中的stable_how表示服务器实际操作方式,对应关系如下:

请求报文应答报文UNSTABLEUNSTABLE、DATA_SYNC、FILE_SYNCDATA_SYNCDATA_SYNC、FILE_SYNCFILE_SYNCFILE_SYNC

    也就是说,如果客户端要求将数据/元数据写入到磁盘中,服务器就必须将数据/元数据写入到磁盘中。如果客户端不强制将数据/元数据写入磁盘中,服务器端可以将数据/元数据写入磁盘中,也可以只写入缓存中就返回。

应答消息中的verf是服务器传递给客户端的一个cookie信息,客户端可以根据这个信息判断服务器的状态是否发生了变化。


2.COMMIT相关的数据结构

    如果服务器只是将数据保存在了缓存页中,那么客户端需要在适当的时候发起COMMIT请求,将数据刷新到服务器磁盘中。这个过程需要使用几个数据结构。

struct nfs_commit_info:这个数据结构中保存了COMMIT请求的处理函数和一个nfs_page结构的链表,这个链表中的数据需要刷新到服务器磁盘中。pNFS中COMMIT请求可能提交到MDS中,也可能提交到DS中,nfs_mds_commit_info和pnfs_ds_commit_info的作用相同。COMMIT请求提交到MDS中时使用nfs_mds_commit_info,COMMIT请求提交到DS中时使用pnfs_ds_commit_info,在未使用pNFS的情况下使用的是nfs_mds_commit_info。

[cpp] view plain copy
 print?
  1. struct nfs_commit_info {  
  2.         spinlock_t                      *lock;  // 自旋锁  
  3.         // 当COMMIT请求提交到MDS时使用这个数据结构  
  4.         struct nfs_mds_commit_info      *mds;  
  5.         // 当COMMIT提交到DS时使用这个数据结构  
  6.         struct pnfs_ds_commit_info      *ds;  
  7.         // 这是直接IO使用的数据结构  
  8.         struct nfs_direct_req           *dreq;  /* O_DIRECT request */  
  9.         // 这是COMMIT请求的处理函数  
  10.         const struct nfs_commit_completion_ops *completion_ops;  
  11. };  
struct nfs_mds_commit_info:这就是一个链表结构,链表中的数据结构是nfs_page,保存了需要提交COMMIT请求的数据在文件中的范围。

[cpp] view plain copy
 print?
  1. struct nfs_mds_commit_info {  
  2.         atomic_t rpcs_out;              // 记录了提交请求的次数  
  3.         unsigned long           ncommit;    // 这个结构中nfs_page结构的数量  
  4.         struct list_head        list;       // 这是一个链表,保存了nfs_page结构  
  5. };  
struct nfs_commit_completion_ops:这是一个操作函数集合,当客户端提交COMMIT请求时使用这个集合中的函数。
[cpp] view plain copy
 print?
  1. struct nfs_commit_completion_ops {  
  2.         void (*error_cleanup) (struct nfs_inode *nfsi);  
  3.         void (*completion) (struct nfs_commit_data *data);  
  4. };  

COMMIT请求中这两个函数如下:

[cpp] view plain copy
 print?
  1. static const struct nfs_commit_completion_ops nfs_commit_completion_ops = {  
  2.         .completion = nfs_commit_release_pages,  
  3.         .error_cleanup = nfs_commit_clear_lock,  
  4. };  


3.nfs_write_completion

    nfs_write_completion()是WRITE请求的收尾函数,当所有的WRITE请求结束后会执行nfs_write_completion()。如果WRITE应答报文中返回的不是FILE_SYNC,则需要提交COMMIT请求,函数的完整定义如下:

[cpp] view plain copy
 print?
  1. static void nfs_write_completion(struct nfs_pgio_header *hdr)  
  2. {  
  3.     struct nfs_commit_info cinfo;  
  4.     unsigned long bytes = 0;  
  5.   
  6.     // 当创建nfs_write_data结构出错时就设置标志位NFS_IOHDR_REDO,  
  7.     // 这种情况下根本没有向服务器传输数据,直接退出就可以了。  
  8.     if (test_bit(NFS_IOHDR_REDO, &hdr->flags))  
  9.         goto out;  
  10.     // 根据inode初始化cinfo,主要是下面两个操作.  
  11.     // cinfo->mds = &NFS_I(inode)->commit_info;  
  12.     // cinfo->completion_ops = &nfs_commit_completion_ops;  
  13.     nfs_init_cinfo_from_inode(&cinfo, hdr->inode);   // 初始化cinfo  
  14.     while (!list_empty(&hdr->pages)) {   // 处理每一个nfs_page  
  15.         struct nfs_page *req = nfs_list_entry(hdr->pages.next);  // 取出一个nfs_page结构  
  16.   
  17.         bytes += req->wb_bytes;  // req->wb_bytes是WRITE请求中写到服务器中的数据量  
  18.         nfs_list_remove_request(req);   // 从链表hdr->pages中删除  
  19.         if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) &&  
  20.             (hdr->good_bytes < bytes)) {  
  21.             // 数据传输过程发生I/O错误,设置标志位PG_error以及NFS_INO_INVALID_DATA  
  22.             // 本地缓存中的数据标记为无效  
  23.             nfs_set_pageerror(req->wb_page);  
  24.             // WRITE请求过程出错了  
  25.             nfs_context_set_write_error(req->wb_context, hdr->error);  
  26.             goto remove_req;  
  27.         }  
  28.         if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags)) { // 需要重新调度这个WRITE请求  
  29.             // 将缓存页面标记为脏(PG_dirty),将radix树中的路径标记为脏(PAGECACHE_TAG_DIRTY)  
  30.             // 将文件索引节点标记为脏(I_DIRTY_PAGES)  
  31.             nfs_mark_request_dirty(req);  
  32.             goto next;  
  33.         }  
  34.         // 如果WRITE操作中缓存页的数据没有刷新到磁盘中,就会设置这个标志位.  
  35.         if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) {  // 数据需要刷新到磁盘中.  
  36.             // 将提交头部的verifier拷贝到nfs_page结构中.   拷贝verifier  8字节  
  37.             memcpy(&req->wb_verf, &hdr->verf->verifier, sizeof(req->wb_verf));  
  38.             // 将一个请求添加到inode的提交链表中(cinfo->mds)  
  39.             nfs_mark_request_commit(req, hdr->lseg, &cinfo); // 这个请求需要提交到cinfo中,然后呢  
  40.             goto next;  // 继续检查下一个缓存页.  
  41.         }  
  42. remove_req:  
  43.         // 从文件中移除一个写请求,当写操作完成或者出错后执行这个函数.  
  44.         // 撤销了nfs_page与page的关联,删除了nfs_page结构  
  45.         nfs_inode_remove_request(req);  // 这个函数中已经执行过nfs_release_request()了,再次执行会不会出错呢zzzz   bugbug  
  46. next:  
  47.         nfs_unlock_request(req);    // 清除了req中的标志位PG_BUSY.  
  48.         // 主要是取消标志位PG_writeback,减少nfs_server->writeback中缓存页面的数量.  
  49.         nfs_end_page_writeback(req->wb_page);  
  50.   
  51.             if (nfs_write_pageuptodate(req->wb_page, hdr->inode))  
  52.                     dprintk("nfs_updatepage: PG_uptodate");  
  53.             else  
  54.                     dprintk("nfs_updatepage: PG_notuptodate");  
  55.   
  56.         nfs_release_request(req);  
  57.     }  
  58. out:  
  59.     hdr->release(hdr);   // 释放hdr占用的缓存  
  60. }