从nfs_page结构看PNFS读写流程
来源:互联网 发布:java的封装性 编辑:程序博客网 时间:2024/04/30 17:58
在pnfs中,每个radix tree 中page的private指针都会指向一个nfs_page结构,在代码中的变量名通常是req,即为读写请求,事实上,其存在的意义也是读写请求。
创建函数:
/**
* nfs_create_request - Create an NFS read/write request.
* @file: file descriptor to use
* @inode: inode to which the request is attached
* @page: page to write
* @offset: starting offset within the page for the write
* @count: number of bytes to read/write
*
* The page must be locked by the caller. This makes sure we never
* create two different requests for the same page.
* User should ensure it is safe to sleep in this function.
*/
struct nfs_page *
nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
struct page *page,
unsigned int offset, unsigned int count, void *fsdata)
{
struct nfs_page *req;
for (;;) {
/* try to allocate the request struct */
req = nfs_page_alloc();
if (req != NULL)
break;
if (fatal_signal_pending(current))
return ERR_PTR(-ERESTARTSYS);
yield();
}
/* Initialize the request struct. Initially, we assume a
* long write-back delay. This will be adjusted in
* update_nfs_request below if the region is not locked. */
req->wb_page = page;
atomic_set(&req->wb_complete, 0);
req->wb_index = page->index;
page_cache_get(page);
BUG_ON(PagePrivate(page));
BUG_ON(!PageLocked(page));
BUG_ON(page->mapping->host != inode);
req->wb_offset = offset;
req->wb_pgbase = offset;
req->wb_bytes = count;
req->wb_context = get_nfs_open_context(ctx);
kref_init(&req->wb_kref);
pnfs_modify_new_request(req, fsdata);
return req;
}
此函数应在两个地方,要求持有page_lock锁,这是因为要修改page->private指针,保证没有其他req挂在此指针上。
1.nfs_write_end;
当把数据写到也缓存中时候,需要为脏页设置好一个req,当下刷的时候就可以直接用了。具体函数如下
static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,
struct page *page, unsigned int offset, unsigned int bytes,
void *fsdata)
{
struct inode *inode = page->mapping->host;
struct nfs_page *req;
int error;
req = nfs_try_to_update_request(inode, page, offset, bytes, fsdata);
if (req != NULL)
goto out;
req = nfs_create_request(ctx, inode, page, offset, bytes, fsdata);
if (IS_ERR(req))
goto out;
error = nfs_inode_add_request(inode, req);
if (error != 0) {
nfs_release_request(req);
req = ERR_PTR(error);
}
out:
return req;
}
从代码来看,先去尝试更新一个已有的req,若没有,则尝试创建一个新的,并将其添加到nfs_inode结构中的radix tree中。
接着看更新一个已有的request的函数
tatic struct nfs_page *nfs_try_to_update_request(struct inode *inode,
struct page *page,
unsigned int offset,
unsigned int bytes,
void *fsdata)
{
struct nfs_page *req;
unsigned int rqend;
unsigned int end;
int error;
if (!PagePrivate(page))
return NULL;
end = offset + bytes;
spin_lock(&inode->i_lock);
for (;;) {
req = nfs_page_find_request_locked(page);
if (req == NULL)
goto out_unlock;
rqend = req->wb_offset + req->wb_bytes;
/*
* Tell the caller to flush out the request if
* the offsets are non-contiguous.
* Note: nfs_flush_incompatible() will already
* have flushed out requests having wrong owners.
*/
if (offset > rqend
|| end < req->wb_offset
|| pnfs_do_flush(req, fsdata))
goto out_flushme;
if (nfs_set_page_tag_locked(req))
break;
/* The request is locked, so wait and then retry */
spin_unlock(&inode->i_lock);
error = nfs_wait_on_request(req);
nfs_release_request(req);
if (error != 0)
goto out_err;
spin_lock(&inode->i_lock);
}
if (nfs_clear_request_commit(req))
radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree,
req->wb_index, NFS_PAGE_TAG_COMMIT);
/* Okay, the request matches. Update the region */
if (offset < req->wb_offset) {
req->wb_offset = offset;
req->wb_pgbase = offset;
}
if (end > rqend)
req->wb_bytes = end - req->wb_offset;
else
req->wb_bytes = rqend - req->wb_offset;
out_unlock:
spin_unlock(&inode->i_lock);
return req;
out_flushme:
spin_unlock(&inode->i_lock);
nfs_release_request(req);
error = nfs_wb_page(inode, page);
out_err:
return ERR_PTR(error);
}
此函数的for循环中,先去尝试找到一个req,若存在,其对应区段与当前的不连续,则下刷之。然后修改对应的offset和length.
此函数返回后,在 nfs_setup_write_request()中,将其添加到error = nfs_inode_add_request(inode, req);中。
对应的函数是static void nfs_inode_remove_request(struct nfs_page *req)
此函数在nfs_write_release()中被调用,即为把脏页写回磁盘的回调函数;或者commit结束后调用。
×××××××××××××××××读写分割线×××××××××××××××××××××××××××××××××××××
PNFS从磁盘读数据时,调用流程是nfs_readpages ->read_cache_pages(mapping, pages, readpage_async_filler, &desc);
注意传进去的参数 readpage_async_filler,此函数代码如下:
static int
readpage_async_filler(void *data, struct page *page)
{
struct nfs_readdesc *desc = (struct nfs_readdesc *)data;
struct inode *inode = page->mapping->host;
struct nfs_page *new;
unsigned int len;
int error;
len = nfs_page_length(page);
if (len == 0)
return nfs_return_empty_page(page);
new = nfs_create_request(desc->ctx, inode, page, 0, len, NULL);
if (IS_ERR(new))
goto out_error;
if (len < PAGE_CACHE_SIZE)
zero_user_segment(page, len, PAGE_CACHE_SIZE);
if (!nfs_pageio_add_request(desc->pgio, new)) {
error = desc->pgio->pg_error;
goto out_unlock;
}
return 0;
out_error:
error = PTR_ERR(new);
SetPageError(page);
out_unlock:
unlock_page(page);
return error;
}
创建了一个req,并将其添加到desc->pgio这个链表中。此函数返回到nfs_readpages中后,nfs_pageio_compliete最终会根据此链表,发送到layoutdriver层。
在bio的回调函数中,释放req.
对nfs_page这个结构,读与写有什么区别?
1.下刷时候,脏页脱离了radix tree,而是挂到了nfs_inode的tree上,而从磁盘读时,页还在原来的radix tree.
其实这是个页缓存的问题,对于脏页,将其刷到磁盘后就应该将其释放,也就是从inode->i_mapping中删除。
但是在layout commit时候需要这棵树来表征标志值,所以在nfs inode中新建了一颗新树。
而读磁盘时候,是不同的,直接挂到inode->i_maping中。
2.在layout driver层,要求传递进来的页是逻辑连续的。
下刷时候,有相关判断,如果不连续就先刷一部分。
读取时候则没有,直接把一串页传递给layout层,这是因为由上层接口决定。
nfs_readpages()指定了一个区段,最终目标就是填满这个区段对应的radix tree,读取过程中,如果有脏页,则要先刷再读,这样就不存在不连续的情况。
而nfs_wb_all,则是要下刷所有脏页,脏页不连续很正常。
××××××××××××××××××××××××
今天终于比较透彻的理解了PNFS的读写代码。
花了两天的时间,还是很值得的。
不仅是对PNFS的理解,还有页缓存,更加宝贵的是,提高了阅读代码的信心和经验。
- 从nfs_page结构看PNFS读写流程
- pNFS
- pNFS
- 三副本的PNFS重构流程
- 从冯诺伊曼结构看AI
- 从内核文件系统看文件读写过程
- 从内核文件系统看文件读写过程
- 从内核文件系统看文件读写过程
- 从内核文件系统看文件读写过程
- 从内核文件系统看文件读写过程
- 从内核文件系统看文件读写过程
- 从内核文件系统看文件读写过程
- 从内核文件系统看文件读写过程
- 从内核文件系统看文件读写过程
- 通过setattr()看PNFS中客户端操作调用路径
- 从消费流程看图书站点设计
- 从 startActivity 看activity启动流程
- 从WordCount看MapReduce框架执行流程
- pthread
- Qt安装—图文并茂搭建VS2008+QT开发环境
- kmp
- php截取中文字符,去html标记,count用法
- 面试(2)
- 从nfs_page结构看PNFS读写流程
- java.io.IOException: No space left on device
- C#如何获得屏幕宽度和高度
- oracle替换文本中的换行符、回车符、制表符
- 用Live555 中的openRTSP 保存H264文件
- 大型网站系统架构分析(转载自博客园)
- 应用程序安装和获取版本号
- jQuery技巧大放送
- 压力测试