现在可以讲解LAYOUTGET请求了,首先我们需要回忆一下NFS文件系统中读数据的过程,看看LAYOUT请求是什么时候触发的。NFS文件系统中,每个缓存页关联了一个数据结构struct nfs_page。同时客户端还定义了一个数据结构struct nfs_pageio_descriptor,这个结构中包含了一个链表。当客户端向服务器请求数据时,会把缓存页挂载到这个数据结构的链表中,添加到这个链表中的缓存页中的数据必须是连续的。如果一个新的缓存页和链表中的数据不连续,那么就需要先向服务器发起READ请求,更新链表中的缓存页。当获取数据后这些缓存页从链表中摘除,这时就可以添加新的缓存页了。将一个缓存页添加到链表中的函数是nfs_pageio_do_add_request(),这个函数的代码如下:
static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
struct nfs_page *req)
{
if (desc->pg_count != 0) {
struct nfs_page *prev;
prev = nfs_list_entry(desc->pg_list.prev);
if (!nfs_can_coalesce_requests(prev, req, desc))
return 0;
} else {
if (desc->pg_ops->pg_init)
desc->pg_ops->pg_init(desc, req);
desc->pg_base = req->wb_pgbase;
}
nfs_list_remove_request(req);
nfs_list_add_request(req, &desc->pg_list);
desc->pg_count += req->wb_bytes;
return 1;
}
前面的文章中已经分析过这个函数了,可以参考这篇文章:
http://blog.csdn.net/ycnian/article/details/8526734。当desc->pg_count==0时,也就是链表中没有缓存页时会执行函数desc->pg_ops->pg_init(desc, req)。前面的文章中没有分析这个函数,因为在不使用pNFS的情况下这个函数为空,所以当时直接跳过了。那么在使用pNFS的情况下这个函数是什么呢?如果采用的是file layout,desc->pg_ops的定义如下:
static const struct nfs_pageio_ops filelayout_pg_read_ops = {
.pg_init = filelayout_pg_init_read,
.pg_test = filelayout_pg_test,
.pg_doio = pnfs_generic_pg_readpages,
};
这时就会执行到函数filelayout_pg_init_read(),这个函数就发起了LAYOUTGET请求。不过现在还不能讲解layout的获取过程,我们首先需要弄清楚layout在客户端的存放方式。客户端,每个layout用数据结构struct pnfs_layout_segment表示,每个layout表示文件中的一段数据。用户可能申请了文件中多个数据段的layout,因此同一个文件中所有的layout构成了一个链表,这个链表保存在数据结构struct pnfs_layout_hdr中。用户还可能访问了多个文件,每个文件对应一个struct pnfs_layout_hdr结构,因此客户端就有多个struct pnfs_layout_hdr结构,这些pnfs_layout_hdr构成了一个链表,保存在struct nfs_server中。
struct nfs_server {
......