diff options
Diffstat (limited to 'fs/nfs/read.c')
| -rw-r--r-- | fs/nfs/read.c | 174 | 
1 files changed, 98 insertions, 76 deletions
diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 20a7f952e244..2171c043ab08 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -30,8 +30,7 @@  #define NFSDBG_FACILITY		NFSDBG_PAGECACHE -static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc); -static int nfs_pagein_one(struct nfs_pageio_descriptor *desc); +static const struct nfs_pageio_ops nfs_pageio_read_ops;  static const struct rpc_call_ops nfs_read_partial_ops;  static const struct rpc_call_ops nfs_read_full_ops; @@ -68,7 +67,7 @@ void nfs_readdata_free(struct nfs_read_data *p)  	mempool_free(p, nfs_rdata_mempool);  } -static void nfs_readdata_release(struct nfs_read_data *rdata) +void nfs_readdata_release(struct nfs_read_data *rdata)  {  	put_lseg(rdata->lseg);  	put_nfs_open_context(rdata->args.context); @@ -113,6 +112,27 @@ static void nfs_readpage_truncate_uninitialised_page(struct nfs_read_data *data)  	}  } +static void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio, +		struct inode *inode) +{ +	nfs_pageio_init(pgio, inode, &nfs_pageio_read_ops, +			NFS_SERVER(inode)->rsize, 0); +} + +void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio) +{ +	pgio->pg_ops = &nfs_pageio_read_ops; +	pgio->pg_bsize = NFS_SERVER(pgio->pg_inode)->rsize; +} +EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds); + +static void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, +		struct inode *inode) +{ +	if (!pnfs_pageio_init_read(pgio, inode)) +		nfs_pageio_init_read_mds(pgio, inode); +} +  int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,  		       struct page *page)  { @@ -131,20 +151,15 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,  	if (len < PAGE_CACHE_SIZE)  		zero_user_segment(page, len, PAGE_CACHE_SIZE); -	nfs_pageio_init(&pgio, inode, NULL, 0, 0); -	nfs_list_add_request(new, &pgio.pg_list); -	pgio.pg_count = len; - -	if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE) -		nfs_pagein_multi(&pgio); -	else -		nfs_pagein_one(&pgio); +	nfs_pageio_init_read(&pgio, inode); +	nfs_pageio_add_request(&pgio, new); +	nfs_pageio_complete(&pgio);  	return 0;  }  static void nfs_readpage_release(struct nfs_page *req)  { -	struct inode *d_inode = req->wb_context->path.dentry->d_inode; +	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); @@ -152,8 +167,8 @@ static void nfs_readpage_release(struct nfs_page *req)  	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_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); @@ -202,17 +217,14 @@ EXPORT_SYMBOL_GPL(nfs_initiate_read);  /*   * Set up the NFS read request struct   */ -static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, -		const struct rpc_call_ops *call_ops, -		unsigned int count, unsigned int offset, -		struct pnfs_layout_segment *lseg) +static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, +		unsigned int count, unsigned int offset)  { -	struct inode *inode = req->wb_context->path.dentry->d_inode; +	struct inode *inode = req->wb_context->dentry->d_inode;  	data->req	  = req;  	data->inode	  = inode;  	data->cred	  = req->wb_context->cred; -	data->lseg	  = get_lseg(lseg);  	data->args.fh     = NFS_FH(inode);  	data->args.offset = req_offset(req) + offset; @@ -226,14 +238,36 @@ static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,  	data->res.count   = count;  	data->res.eof     = 0;  	nfs_fattr_init(&data->fattr); +} -	if (data->lseg && -	    (pnfs_try_to_read_data(data, call_ops) == PNFS_ATTEMPTED)) -		return 0; +static int nfs_do_read(struct nfs_read_data *data, +		const struct rpc_call_ops *call_ops) +{ +	struct inode *inode = data->args.context->dentry->d_inode;  	return nfs_initiate_read(data, NFS_CLIENT(inode), call_ops);  } +static int +nfs_do_multiple_reads(struct list_head *head, +		const struct rpc_call_ops *call_ops) +{ +	struct nfs_read_data *data; +	int ret = 0; + +	while (!list_empty(head)) { +		int ret2; + +		data = list_entry(head->next, struct nfs_read_data, list); +		list_del_init(&data->list); + +		ret2 = nfs_do_read(data, call_ops); +		if (ret == 0) +			ret = ret2; +	} +	return ret; +} +  static void  nfs_async_read_error(struct list_head *head)  { @@ -260,20 +294,19 @@ nfs_async_read_error(struct list_head *head)   * won't see the new data until our attribute cache is updated.  This is more   * or less conventional NFS client behavior.   */ -static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc) +static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc, struct list_head *res)  {  	struct nfs_page *req = nfs_list_entry(desc->pg_list.next);  	struct page *page = req->wb_page;  	struct nfs_read_data *data; -	size_t rsize = NFS_SERVER(desc->pg_inode)->rsize, nbytes; +	size_t rsize = desc->pg_bsize, nbytes;  	unsigned int offset;  	int requests = 0;  	int ret = 0; -	struct pnfs_layout_segment *lseg; -	LIST_HEAD(list);  	nfs_list_remove_request(req); +	offset = 0;  	nbytes = desc->pg_count;  	do {  		size_t len = min(nbytes,rsize); @@ -281,45 +314,21 @@ static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc)  		data = nfs_readdata_alloc(1);  		if (!data)  			goto out_bad; -		list_add(&data->pages, &list); +		data->pagevec[0] = page; +		nfs_read_rpcsetup(req, data, len, offset); +		list_add(&data->list, res);  		requests++;  		nbytes -= len; +		offset += len;  	} while(nbytes != 0);  	atomic_set(&req->wb_complete, requests); - -	BUG_ON(desc->pg_lseg != NULL); -	lseg = pnfs_update_layout(desc->pg_inode, req->wb_context, -				  req_offset(req), desc->pg_count, -				  IOMODE_READ, GFP_KERNEL);  	ClearPageError(page); -	offset = 0; -	nbytes = desc->pg_count; -	do { -		int ret2; - -		data = list_entry(list.next, struct nfs_read_data, pages); -		list_del_init(&data->pages); - -		data->pagevec[0] = page; - -		if (nbytes < rsize) -			rsize = nbytes; -		ret2 = nfs_read_rpcsetup(req, data, &nfs_read_partial_ops, -					 rsize, offset, lseg); -		if (ret == 0) -			ret = ret2; -		offset += rsize; -		nbytes -= rsize; -	} while (nbytes != 0); -	put_lseg(lseg); -	desc->pg_lseg = NULL; - +	desc->pg_rpc_callops = &nfs_read_partial_ops;  	return ret; -  out_bad: -	while (!list_empty(&list)) { -		data = list_entry(list.next, struct nfs_read_data, pages); -		list_del(&data->pages); +	while (!list_empty(res)) { +		data = list_entry(res->next, struct nfs_read_data, list); +		list_del(&data->list);  		nfs_readdata_free(data);  	}  	SetPageError(page); @@ -327,19 +336,19 @@ out_bad:  	return -ENOMEM;  } -static int nfs_pagein_one(struct nfs_pageio_descriptor *desc) +static int nfs_pagein_one(struct nfs_pageio_descriptor *desc, struct list_head *res)  {  	struct nfs_page		*req;  	struct page		**pages;  	struct nfs_read_data	*data;  	struct list_head *head = &desc->pg_list; -	struct pnfs_layout_segment *lseg = desc->pg_lseg; -	int ret = -ENOMEM; +	int ret = 0;  	data = nfs_readdata_alloc(nfs_page_array_len(desc->pg_base,  						     desc->pg_count));  	if (!data) {  		nfs_async_read_error(head); +		ret = -ENOMEM;  		goto out;  	} @@ -352,19 +361,37 @@ static int nfs_pagein_one(struct nfs_pageio_descriptor *desc)  		*pages++ = req->wb_page;  	}  	req = nfs_list_entry(data->pages.next); -	if ((!lseg) && list_is_singular(&data->pages)) -		lseg = pnfs_update_layout(desc->pg_inode, req->wb_context, -					  req_offset(req), desc->pg_count, -					  IOMODE_READ, GFP_KERNEL); -	ret = nfs_read_rpcsetup(req, data, &nfs_read_full_ops, desc->pg_count, -				0, lseg); +	nfs_read_rpcsetup(req, data, desc->pg_count, 0); +	list_add(&data->list, res); +	desc->pg_rpc_callops = &nfs_read_full_ops;  out: -	put_lseg(lseg); -	desc->pg_lseg = NULL;  	return ret;  } +int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, struct list_head *head) +{ +	if (desc->pg_bsize < PAGE_CACHE_SIZE) +		return nfs_pagein_multi(desc, head); +	return nfs_pagein_one(desc, head); +} + +static int nfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc) +{ +	LIST_HEAD(head); +	int ret; + +	ret = nfs_generic_pagein(desc, &head); +	if (ret == 0) +		ret = nfs_do_multiple_reads(&head, desc->pg_rpc_callops); +	return ret; +} + +static const struct nfs_pageio_ops nfs_pageio_read_ops = { +	.pg_test = nfs_generic_pg_test, +	.pg_doio = nfs_generic_pg_readpages, +}; +  /*   * This is the callback from RPC telling us whether a reply was   * received or some error occurred (timeout or socket shutdown). @@ -635,8 +662,6 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,  		.pgio = &pgio,  	};  	struct inode *inode = mapping->host; -	struct nfs_server *server = NFS_SERVER(inode); -	size_t rsize = server->rsize;  	unsigned long npages;  	int ret = -ESTALE; @@ -664,10 +689,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,  	if (ret == 0)  		goto read_complete; /* all pages were read */ -	if (rsize < PAGE_CACHE_SIZE) -		nfs_pageio_init(&pgio, inode, nfs_pagein_multi, rsize, 0); -	else -		nfs_pageio_init(&pgio, inode, nfs_pagein_one, rsize, 0); +	nfs_pageio_init_read(&pgio, inode);  	ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);  |