PG_uptodate标志含义一直不是很明确。
很多地方会set次标志
1.从磁盘读取到页
2.从用户态拷贝的页缓存
3.把脏页刷到磁盘
4.初始化一个空页
清楚此标志只有读取或者下刷失败时候。
SetPageUptodate()
1.bl_done_with_rpage(struct page *page, const int ok)
{
if (ok) {
ClearPagePnfsErr(page);
SetPageUptodate(page);
} else {
ClearPageUptodate(page);
SetPageError(page);
SetPagePnfsErr(page);
}
/* Page is unlocked via rpc_release. Should really be done here. */
}
当页被正确读出时,设置叶已经被更新。
2bl_write_end(struct inode *inode, struct page *page, loff_t pos,
unsigned count, unsigned copied, struct pnfs_fsdata *fsdata)
{
dprintk("%s enter, %u@%lld, %i\n", __func__, count, pos,
fsdata ? fsdata->ok_to_use_pnfs : -1);
print_page(page);
if (fsdata) {
if (fsdata->ok_to_use_pnfs) {
dprintk("%s using pnfs\n", __func__);
SetPageUptodate(page);
}
}
return 0;
}
当页被正确刷到磁盘后,也会更新。
3. static void nfs_readpage_from_fscache_complete(struct page *page,
void *context,
int error)
{
dfprintk(FSCACHE,
"NFS: readpage_from_fscache_complete (0x%p/0x%p/%d)\n",
page, context, error);
/* if the read completes with an error, we just unlock the page and let
* the VM reissue the readpage */
if (!error) {
SetPageUptodate(page);
unlock_page(page);
} else {
error = nfs_readpage_async(context, page->mapping->host, page);
if (error)
unlock_page(page);
}
}
需要了解FScache是个什么东东
4.
static void nfs_readpage_release_partial(void *calldata)
{
struct nfs_read_data *data = calldata;
struct nfs_page *req = data->req;
struct page *page = req->wb_page;
int status = data->task.tk_status;
if (status < 0)
SetPageError(page);
if (atomic_dec_and_test(&req->wb_complete)) {
if (!PageError(page))
SetPageUptodate(page);
nfs_readpage_release(req);
}
nfs_readdata_release(calldata);
}
这个是当页从磁盘读出后,par的回调函数执行的,但在1中已经设置了,此处岂不重复设置了么?
同理,static void nfs_readpage_release_full(void *calldata)中也会更新页标志。
_______________________________________________________________________________
_______________________________________________________________________________
清除uptodate的标志只有一个地方:
bl_done_with_rpage(struct page *page, const int ok)
{
if (ok) {
ClearPagePnfsErr(page);
SetPageUptodate(page);
} else {
ClearPageUptodate(page);
SetPageError(page);
SetPagePnfsErr(page);
}
/* Page is unlocked via rpc_release. Should really be done here. */
}
这说明uptodate这个标志位包含的信息其实并不是很丰富。
_______________________________________________________________________________
_______________________________________________________________________________
检测uptodate
1.static int nfs_want_read_modify_write(struct file *file, struct page *page,
loff_t pos, unsigned len)
{
unsigned int pglen = nfs_page_length(page);
unsigned int offset = pos & (PAGE_CACHE_SIZE - 1);
unsigned int end = offset + len;
if ((file->f_mode & FMODE_READ) && /* open for read? */
!PageUptodate(page) && /* Uptodate? */
!PagePrivate(page) && /* i/o request already? */
pglen && /* valid bytes of file? */
(end < pglen || offset)) /* replace all valid bytes? */
return 1;
return 0;
}
这个函数用来判断:当需要写的时候,* Decide whether a read/modify/write cycle may be more efficient
* then a modify/write/read cycle when writing to a page in the
* page cache.
*
2.if (!PageUptodate(page)) {
unsigned pglen = nfs_page_length(page);
unsigned end = offset + len;
if (pglen == 0) {
zero_user_segments(page, 0, offset,
end, PAGE_CACHE_SIZE);
SetPageUptodate(page);
} else if (end >= pglen) {
zero_user_segment(page, end, PAGE_CACHE_SIZE);
if (offset == 0)
SetPageUptodate(page);
} else
zero_user_segment(page, pglen, PAGE_CACHE_SIZE);
}
这是在nfs_write_end中调用的,很可能的原因是:在bl driver层中,只修改了完整的页的标志,对于写了一半的页并没有处理。
PS:user_segment是什么意思i?
3. static void nfs_readpage_release(struct nfs_page *req)
{
struct inode *d_inode = req->wb_context->path.dentry->d_inode;
if (PageUptodate(req->wb_page))
nfs_readpage_to_fscache(d_inode, req->wb_page, 0);
unlock_page(req->wb_page);
dprintk("NFS: read done (%s/%Ld %d@%Ld)\n",
req->wb_context->path.dentry->d_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
req->wb_bytes,
(long long)req_offset(req));
nfs_clear_request(req);
nfs_release_request(req);
}
此处是当完成从磁盘到页的读操作后(完成bio后)的回调函数。
PS:fscache是什么?
{
struct nfs_open_context *ctx;
struct inode *inode = page->mapping->host;
int error;
dprintk("NFS: nfs_readpage (%p %ld@%lu)\n",
page, PAGE_CACHE_SIZE, page->index);
nfs_inc_stats(inode, NFSIOS_VFSREADPAGE);
nfs_add_stats(inode, NFSIOS_READPAGES, 1);
error = nfs_wb_page(inode, page);
if (PageUptodate(page)) goto out_unlock;
此处比较重要:在读一个页之前,先看是否有写请求在页上,若有则先刷到磁盘。若下刷成功,则uptodata标志会被置位(前面已经有代码),通过判断uptodate标志位则不需要再去读磁盘,直接返回即可。
5. /*
* If the page cache is marked as unsafe or invalid, then we can't rely on
* the PageUptodate() flag. In this case, we will need to turn off
* write optimisations that depend on the page contents being correct.
*/
static int nfs_write_pageuptodate(struct page *page, struct inode *inode)
{
return PageUptodate(page) &&
!(NFS_I(inode)->cache_validity & (NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA));
}