这篇文章详细讲解nfs_generic_pg_readpages()的流程。
1.创建一个nfs_read_header结构
rhdr = nfs_readhdr_alloc();
if (!rhdr) { // 创建rhdr失败了,不能处理desc中的缓存页了,需要释放这些缓存页
desc->pg_completion_ops->error_cleanup(&desc->pg_list);
return -ENOMEM;
}
创建nfs_read_header结构的函数是nfs_readhdr_alloc(),系统为nfs_read_header结构创建了slab缓存,名称是nfs_rdata_cachep,从slab缓存中分配内存。
struct nfs_read_header *nfs_readhdr_alloc(void)
{
struct nfs_read_header *rhdr;
// 从slab中分配内存
rhdr = kmem_cache_zalloc(nfs_rdata_cachep, GFP_KERNEL);
if (rhdr) {
struct nfs_pgio_header *hdr = &rhdr->header; // 这是nfs_pgio_header结构
// 对nfs_pgio_header进行一些初始化
INIT_LIST_HEAD(&hdr->pages); // 这是一个链表,链表中保存的数据结构是nfs_page
INIT_LIST_HEAD(&hdr->rpc_list); // 这是一个链表,链表中保存的数据结构是nfs_read_data
spin_lock_init(&hdr->lock); // 这是一个自旋锁
atomic_set(&hdr->refcnt, 0); // 初始化引用计数为0
}
return rhdr; // 返回新分配的nfs_read_header结构
}
当nfs_read_header创建失败时,就不能向服务器请求数据,填充缓存页了,因此需要进行错误处理,READ操作中,这个处理函数是nfs_async_read_error()。这个函数定义如下:
参数head:这是一个链表,链表中保存的是nfs_page结构,这是需要填充的缓存页。
// 当创建nfs_read_header结构失败,不能继续处理读请求了.
static void
nfs_async_read_error(struct list_head *head)
{
struct nfs_page *req;
while (!list_empty(head)) { // 依次删除链表中的每个缓存页
req = nfs_list_entry(head->next); // 取出链表中一个nfs_page结构
nfs_list_remove_request(req); // 从链表中摘除
nfs_readpage_release(req); // 释放这个缓存页
}
}
这个函数摘除链表中所有的缓存页,然后删除这些缓存页,删除缓存页的处理函数是nfs_readpage_release()。
static void nfs_readpage_release(struct nfs_page *req)
{
// 找到文件的索引节点
struct inode *d_inode = req->wb_context->dentry->d_inode;
if (PageUptodate(req->wb_page)) // 缓存中的数据是最新的
nfs_readpage_to_fscache(d_inode, req->wb_page, 0); // 加入到FS-Cache中
unlock_page(req->wb_page); // 解锁
dprintk("NFS: read done (%s/%Ld %d@%Ld)\n",
req->wb_context->dentry->d_inode->i_sb->s_id, // 文件系统名称
(long long)NFS_FILEID(req->wb_context->dentry->d_inode), // 文件编号
req->wb_bytes, // 缓存中有效数据量
(long long)req_offset(req)); // 偏移值
nfs_release_request(req); // 减少req的引用计数,当计数减为0时释放这个结构.
}
这里又涉及到了FS-Cache缓存。nfs_readpage_release()不仅在这里调用,当READ操作正常结束后也会调用这个函数,因此如果缓存页中的数据有效需要将数据写入FS-Cache缓存中。以后再讲解FS-Cache。
2.初始化nfs_pgio_header
hdr = &rhdr->header;
nfs_pgheader_init(desc, hdr, nfs_readhdr_free);
atomic_inc(&hdr->refcnt);
初始化工作是由nfs_pgheader_init()完成的。
参数desc:这个变量在前面已经初始化好了,包含了缓存页的信息以及各种操作函数。
参数hdr:这是待初始化的变量,nfs_pgheader_init()根据desc中的信息初始化hdr。
变量nfs_readhdr_free:这是释放hdr占用内存的函数,当需要使用hdr占用的内存时调用这个函数。