SUNRPC: Add svc_rqst_replace_page() API
[ Upstream commit 2f0f88f42f2eab0421ed37d7494de9124fdf0d34 ] Replacing a page in rq_pages[] requires a get_page(), which is a bus-locked operation, and a put_page(), which can be even more costly. To reduce the cost of replacing a page in rq_pages[], batch the put_page() operations by collecting "freed" pages in a pagevec, and then release those pages when the pagevec is full. This pagevec is also emptied when each RPC completes. [ cel: adjusted to apply without f6e70aab9dfe ] Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
9e5f2e0ae0
commit
a4f616afb4
@@ -19,6 +19,7 @@
|
|||||||
#include <linux/sunrpc/svcauth.h>
|
#include <linux/sunrpc/svcauth.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
|
#include <linux/pagevec.h>
|
||||||
|
|
||||||
/* statistics for svc_pool structures */
|
/* statistics for svc_pool structures */
|
||||||
struct svc_pool_stats {
|
struct svc_pool_stats {
|
||||||
@@ -256,6 +257,7 @@ struct svc_rqst {
|
|||||||
struct page * *rq_next_page; /* next reply page to use */
|
struct page * *rq_next_page; /* next reply page to use */
|
||||||
struct page * *rq_page_end; /* one past the last page */
|
struct page * *rq_page_end; /* one past the last page */
|
||||||
|
|
||||||
|
struct pagevec rq_pvec;
|
||||||
struct kvec rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */
|
struct kvec rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */
|
||||||
struct bio_vec rq_bvec[RPCSVC_MAXPAGES];
|
struct bio_vec rq_bvec[RPCSVC_MAXPAGES];
|
||||||
|
|
||||||
@@ -502,6 +504,8 @@ struct svc_rqst *svc_rqst_alloc(struct svc_serv *serv,
|
|||||||
struct svc_pool *pool, int node);
|
struct svc_pool *pool, int node);
|
||||||
struct svc_rqst *svc_prepare_thread(struct svc_serv *serv,
|
struct svc_rqst *svc_prepare_thread(struct svc_serv *serv,
|
||||||
struct svc_pool *pool, int node);
|
struct svc_pool *pool, int node);
|
||||||
|
void svc_rqst_replace_page(struct svc_rqst *rqstp,
|
||||||
|
struct page *page);
|
||||||
void svc_rqst_free(struct svc_rqst *);
|
void svc_rqst_free(struct svc_rqst *);
|
||||||
void svc_exit_thread(struct svc_rqst *);
|
void svc_exit_thread(struct svc_rqst *);
|
||||||
unsigned int svc_pool_map_get(void);
|
unsigned int svc_pool_map_get(void);
|
||||||
|
@@ -842,6 +842,27 @@ svc_set_num_threads_sync(struct svc_serv *serv, struct svc_pool *pool, int nrser
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(svc_set_num_threads_sync);
|
EXPORT_SYMBOL_GPL(svc_set_num_threads_sync);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* svc_rqst_replace_page - Replace one page in rq_pages[]
|
||||||
|
* @rqstp: svc_rqst with pages to replace
|
||||||
|
* @page: replacement page
|
||||||
|
*
|
||||||
|
* When replacing a page in rq_pages, batch the release of the
|
||||||
|
* replaced pages to avoid hammering the page allocator.
|
||||||
|
*/
|
||||||
|
void svc_rqst_replace_page(struct svc_rqst *rqstp, struct page *page)
|
||||||
|
{
|
||||||
|
if (*rqstp->rq_next_page) {
|
||||||
|
if (!pagevec_space(&rqstp->rq_pvec))
|
||||||
|
__pagevec_release(&rqstp->rq_pvec);
|
||||||
|
pagevec_add(&rqstp->rq_pvec, *rqstp->rq_next_page);
|
||||||
|
}
|
||||||
|
|
||||||
|
get_page(page);
|
||||||
|
*(rqstp->rq_next_page++) = page;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(svc_rqst_replace_page);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called from a server thread as it's exiting. Caller must hold the "service
|
* Called from a server thread as it's exiting. Caller must hold the "service
|
||||||
* mutex" for the service.
|
* mutex" for the service.
|
||||||
|
@@ -525,6 +525,7 @@ static void svc_xprt_release(struct svc_rqst *rqstp)
|
|||||||
kfree(rqstp->rq_deferred);
|
kfree(rqstp->rq_deferred);
|
||||||
rqstp->rq_deferred = NULL;
|
rqstp->rq_deferred = NULL;
|
||||||
|
|
||||||
|
pagevec_release(&rqstp->rq_pvec);
|
||||||
svc_free_res_pages(rqstp);
|
svc_free_res_pages(rqstp);
|
||||||
rqstp->rq_res.page_len = 0;
|
rqstp->rq_res.page_len = 0;
|
||||||
rqstp->rq_res.page_base = 0;
|
rqstp->rq_res.page_base = 0;
|
||||||
@@ -651,6 +652,8 @@ static int svc_alloc_arg(struct svc_rqst *rqstp)
|
|||||||
int pages;
|
int pages;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
pagevec_init(&rqstp->rq_pvec);
|
||||||
|
|
||||||
/* now allocate needed pages. If we get a failure, sleep briefly */
|
/* now allocate needed pages. If we get a failure, sleep briefly */
|
||||||
pages = (serv->sv_max_mesg + 2 * PAGE_SIZE) >> PAGE_SHIFT;
|
pages = (serv->sv_max_mesg + 2 * PAGE_SIZE) >> PAGE_SHIFT;
|
||||||
if (pages > RPCSVC_MAXPAGES) {
|
if (pages > RPCSVC_MAXPAGES) {
|
||||||
|
Reference in New Issue
Block a user