Commit 62f7b27e authored by Trond Myklebust's avatar Trond Myklebust Committed by Linus Torvalds

[PATCH] slabify the sunrpc layer

In order to better cope with low memory conditions, add slabs for
struct rpc_task and 'small' RPC buffers of <= 2k. Protect these using
mempools.

The only case where we appear to use buffers of > 2k is when
symlinking, and is due to the fact that the path can be up to 4k in
length. For the moment, we just use kmalloc(), but it may be worth it
some time in the near future to convert nfs_symlink() to use pages.
parent 7c500ff1
...@@ -42,6 +42,7 @@ struct rpc_task { ...@@ -42,6 +42,7 @@ struct rpc_task {
*/ */
struct rpc_message tk_msg; /* RPC call info */ struct rpc_message tk_msg; /* RPC call info */
__u32 * tk_buffer; /* XDR buffer */ __u32 * tk_buffer; /* XDR buffer */
size_t tk_bufsize;
__u8 tk_garb_retry, __u8 tk_garb_retry,
tk_cred_retry, tk_cred_retry,
tk_suid_retry; tk_suid_retry;
...@@ -178,20 +179,16 @@ void rpc_wake_up(struct rpc_wait_queue *); ...@@ -178,20 +179,16 @@ void rpc_wake_up(struct rpc_wait_queue *);
struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *); struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *);
void rpc_wake_up_status(struct rpc_wait_queue *, int); void rpc_wake_up_status(struct rpc_wait_queue *, int);
void rpc_delay(struct rpc_task *, unsigned long); void rpc_delay(struct rpc_task *, unsigned long);
void * rpc_allocate(unsigned int flags, unsigned int); void * rpc_malloc(struct rpc_task *, size_t);
void rpc_free(void *); void rpc_free(struct rpc_task *);
int rpciod_up(void); int rpciod_up(void);
void rpciod_down(void); void rpciod_down(void);
void rpciod_wake_up(void); void rpciod_wake_up(void);
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
void rpc_show_tasks(void); void rpc_show_tasks(void);
#endif #endif
int rpc_init_mempool(void);
static __inline__ void * void rpc_destroy_mempool(void);
rpc_malloc(struct rpc_task *task, unsigned int size)
{
return rpc_allocate(task->tk_flags, size);
}
static __inline__ void static __inline__ void
rpc_exit(struct rpc_task *task, int status) rpc_exit(struct rpc_task *task, int status)
......
...@@ -25,7 +25,7 @@ nul_create(struct rpc_clnt *clnt) ...@@ -25,7 +25,7 @@ nul_create(struct rpc_clnt *clnt)
struct rpc_auth *auth; struct rpc_auth *auth;
dprintk("RPC: creating NULL authenticator for client %p\n", clnt); dprintk("RPC: creating NULL authenticator for client %p\n", clnt);
if (!(auth = (struct rpc_auth *) rpc_allocate(0, sizeof(*auth)))) if (!(auth = (struct rpc_auth *) kmalloc(sizeof(*auth),GFP_KERNEL)))
return NULL; return NULL;
auth->au_cslack = 4; auth->au_cslack = 4;
auth->au_rslack = 2; auth->au_rslack = 2;
...@@ -41,7 +41,7 @@ nul_destroy(struct rpc_auth *auth) ...@@ -41,7 +41,7 @@ nul_destroy(struct rpc_auth *auth)
{ {
dprintk("RPC: destroying NULL authenticator %p\n", auth); dprintk("RPC: destroying NULL authenticator %p\n", auth);
rpcauth_free_credcache(auth); rpcauth_free_credcache(auth);
rpc_free(auth); kfree(auth);
} }
/* /*
...@@ -52,7 +52,7 @@ nul_create_cred(int flags) ...@@ -52,7 +52,7 @@ nul_create_cred(int flags)
{ {
struct rpc_cred *cred; struct rpc_cred *cred;
if (!(cred = (struct rpc_cred *) rpc_allocate(flags, sizeof(*cred)))) if (!(cred = (struct rpc_cred *) kmalloc(sizeof(*cred),GFP_KERNEL)))
return NULL; return NULL;
atomic_set(&cred->cr_count, 0); atomic_set(&cred->cr_count, 0);
cred->cr_flags = RPCAUTH_CRED_UPTODATE; cred->cr_flags = RPCAUTH_CRED_UPTODATE;
...@@ -68,7 +68,7 @@ nul_create_cred(int flags) ...@@ -68,7 +68,7 @@ nul_create_cred(int flags)
static void static void
nul_destroy_cred(struct rpc_cred *cred) nul_destroy_cred(struct rpc_cred *cred)
{ {
rpc_free(cred); kfree(cred);
} }
/* /*
......
...@@ -41,7 +41,7 @@ unx_create(struct rpc_clnt *clnt) ...@@ -41,7 +41,7 @@ unx_create(struct rpc_clnt *clnt)
struct rpc_auth *auth; struct rpc_auth *auth;
dprintk("RPC: creating UNIX authenticator for client %p\n", clnt); dprintk("RPC: creating UNIX authenticator for client %p\n", clnt);
if (!(auth = (struct rpc_auth *) rpc_allocate(0, sizeof(*auth)))) if (!(auth = (struct rpc_auth *) kmalloc(sizeof(*auth), GFP_KERNEL)))
return NULL; return NULL;
auth->au_cslack = UNX_WRITESLACK; auth->au_cslack = UNX_WRITESLACK;
auth->au_rslack = 2; /* assume AUTH_NULL verf */ auth->au_rslack = 2; /* assume AUTH_NULL verf */
...@@ -58,7 +58,7 @@ unx_destroy(struct rpc_auth *auth) ...@@ -58,7 +58,7 @@ unx_destroy(struct rpc_auth *auth)
{ {
dprintk("RPC: destroying UNIX authenticator %p\n", auth); dprintk("RPC: destroying UNIX authenticator %p\n", auth);
rpcauth_free_credcache(auth); rpcauth_free_credcache(auth);
rpc_free(auth); kfree(auth);
} }
static struct rpc_cred * static struct rpc_cred *
...@@ -70,7 +70,7 @@ unx_create_cred(int flags) ...@@ -70,7 +70,7 @@ unx_create_cred(int flags)
dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", dprintk("RPC: allocating UNIX cred for uid %d gid %d\n",
current->uid, current->gid); current->uid, current->gid);
if (!(cred = (struct unx_cred *) rpc_allocate(flags, sizeof(*cred)))) if (!(cred = (struct unx_cred *) kmalloc(sizeof(*cred), GFP_KERNEL)))
return NULL; return NULL;
atomic_set(&cred->uc_count, 0); atomic_set(&cred->uc_count, 0);
...@@ -98,32 +98,10 @@ unx_create_cred(int flags) ...@@ -98,32 +98,10 @@ unx_create_cred(int flags)
return (struct rpc_cred *) cred; return (struct rpc_cred *) cred;
} }
struct rpc_cred *
authunix_fake_cred(struct rpc_task *task, uid_t uid, gid_t gid)
{
struct unx_cred *cred;
dprintk("RPC: allocating fake UNIX cred for uid %d gid %d\n",
uid, gid);
if (!(cred = (struct unx_cred *) rpc_malloc(task, sizeof(*cred))))
return NULL;
atomic_set(&cred->uc_count, 1);
cred->uc_flags = RPCAUTH_CRED_DEAD|RPCAUTH_CRED_UPTODATE;
cred->uc_uid = uid;
cred->uc_gid = gid;
cred->uc_fsuid = uid;
cred->uc_fsgid = gid;
cred->uc_gids[0] = (gid_t) NOGROUP;
return task->tk_msg.rpc_cred = (struct rpc_cred *) cred;
}
static void static void
unx_destroy_cred(struct rpc_cred *cred) unx_destroy_cred(struct rpc_cred *cred)
{ {
rpc_free(cred); kfree(cred);
} }
/* /*
......
...@@ -85,7 +85,7 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, ...@@ -85,7 +85,7 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname,
if (vers >= program->nrvers || !(version = program->version[vers])) if (vers >= program->nrvers || !(version = program->version[vers]))
goto out; goto out;
clnt = (struct rpc_clnt *) rpc_allocate(0, sizeof(*clnt)); clnt = (struct rpc_clnt *) kmalloc(sizeof(*clnt), GFP_KERNEL);
if (!clnt) if (!clnt)
goto out_no_clnt; goto out_no_clnt;
memset(clnt, 0, sizeof(*clnt)); memset(clnt, 0, sizeof(*clnt));
...@@ -125,7 +125,7 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, ...@@ -125,7 +125,7 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname,
out_no_auth: out_no_auth:
printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %u)\n", printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %u)\n",
flavor); flavor);
rpc_free(clnt); kfree(clnt);
clnt = NULL; clnt = NULL;
goto out; goto out;
} }
...@@ -180,7 +180,7 @@ rpc_destroy_client(struct rpc_clnt *clnt) ...@@ -180,7 +180,7 @@ rpc_destroy_client(struct rpc_clnt *clnt)
xprt_destroy(clnt->cl_xprt); xprt_destroy(clnt->cl_xprt);
clnt->cl_xprt = NULL; clnt->cl_xprt = NULL;
} }
rpc_free(clnt); kfree(clnt);
return 0; return 0;
} }
...@@ -487,7 +487,7 @@ call_allocate(struct rpc_task *task) ...@@ -487,7 +487,7 @@ call_allocate(struct rpc_task *task)
* auth->au_wslack */ * auth->au_wslack */
bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc) + RPC_SLACK_SPACE; bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc) + RPC_SLACK_SPACE;
if ((task->tk_buffer = rpc_malloc(task, bufsiz << 1)) != NULL) if (rpc_malloc(task, bufsiz << 1) != NULL)
return; return;
printk(KERN_INFO "RPC: buffer allocation failed for task %p\n", task); printk(KERN_INFO "RPC: buffer allocation failed for task %p\n", task);
...@@ -522,7 +522,7 @@ call_encode(struct rpc_task *task) ...@@ -522,7 +522,7 @@ call_encode(struct rpc_task *task)
task->tk_action = call_bind; task->tk_action = call_bind;
/* Default buffer setup */ /* Default buffer setup */
bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc)+RPC_SLACK_SPACE; bufsiz = task->tk_bufsize >> 1;
sndbuf->head[0].iov_base = (void *)task->tk_buffer; sndbuf->head[0].iov_base = (void *)task->tk_buffer;
sndbuf->head[0].iov_len = bufsiz; sndbuf->head[0].iov_len = bufsiz;
sndbuf->tail[0].iov_len = 0; sndbuf->tail[0].iov_len = 0;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mempool.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
...@@ -29,9 +30,15 @@ static int rpc_task_id; ...@@ -29,9 +30,15 @@ static int rpc_task_id;
#endif #endif
/* /*
* We give RPC the same get_free_pages priority as NFS * RPC slabs and memory pools
*/ */
#define GFP_RPC GFP_NOFS #define RPC_BUFFER_MAXSIZE (2048)
#define RPC_BUFFER_POOLSIZE (8)
#define RPC_TASK_POOLSIZE (8)
static kmem_cache_t *rpc_task_slabp;
static kmem_cache_t *rpc_buffer_slabp;
static mempool_t *rpc_task_mempool;
static mempool_t *rpc_buffer_mempool;
static void __rpc_default_timer(struct rpc_task *task); static void __rpc_default_timer(struct rpc_task *task);
static void rpciod_killall(void); static void rpciod_killall(void);
...@@ -79,24 +86,6 @@ static spinlock_t rpc_queue_lock = SPIN_LOCK_UNLOCKED; ...@@ -79,24 +86,6 @@ static spinlock_t rpc_queue_lock = SPIN_LOCK_UNLOCKED;
*/ */
static spinlock_t rpc_sched_lock = SPIN_LOCK_UNLOCKED; static spinlock_t rpc_sched_lock = SPIN_LOCK_UNLOCKED;
/*
* This is the last-ditch buffer for NFS swap requests
*/
static u32 swap_buffer[PAGE_SIZE >> 2];
static long swap_buffer_used;
/*
* Make allocation of the swap_buffer SMP-safe
*/
static __inline__ int rpc_lock_swapbuf(void)
{
return !test_and_set_bit(1, &swap_buffer_used);
}
static __inline__ void rpc_unlock_swapbuf(void)
{
clear_bit(1, &swap_buffer_used);
}
/* /*
* Disable the timer for a given RPC task. Should be called with * Disable the timer for a given RPC task. Should be called with
* rpc_queue_lock and bh_disabled in order to avoid races within * rpc_queue_lock and bh_disabled in order to avoid races within
...@@ -592,10 +581,7 @@ __rpc_execute(struct rpc_task *task) ...@@ -592,10 +581,7 @@ __rpc_execute(struct rpc_task *task)
/* Release RPC slot and buffer memory */ /* Release RPC slot and buffer memory */
if (task->tk_rqstp) if (task->tk_rqstp)
xprt_release(task); xprt_release(task);
if (task->tk_buffer) { rpc_free(task);
rpc_free(task->tk_buffer);
task->tk_buffer = NULL;
}
goto restarted; goto restarted;
} }
printk(KERN_ERR "RPC: dead task tries to walk away.\n"); printk(KERN_ERR "RPC: dead task tries to walk away.\n");
...@@ -676,63 +662,46 @@ __rpc_schedule(void) ...@@ -676,63 +662,46 @@ __rpc_schedule(void)
} }
/* /*
* Allocate memory for RPC purpose. * Allocate memory for RPC purposes.
* *
* This is yet another tricky issue: For sync requests issued by * We try to ensure that some NFS reads and writes can always proceed
* a user process, we want to make kmalloc sleep if there isn't * by using a mempool when allocating 'small' buffers.
* enough memory. Async requests should not sleep too excessively * In order to avoid memory starvation triggering more writebacks of
* because that will block rpciod (but that's not dramatic when * NFS requests, we use GFP_NOFS rather than GFP_KERNEL.
* it's starved of memory anyway). Finally, swapout requests should
* never sleep at all, and should not trigger another swap_out
* request through kmalloc which would just increase memory contention.
*
* I hope the following gets it right, which gives async requests
* a slight advantage over sync requests (good for writeback, debatable
* for readahead):
*
* sync user requests: GFP_KERNEL
* async requests: GFP_RPC (== GFP_NOFS)
* swap requests: GFP_ATOMIC (or new GFP_SWAPPER)
*/ */
void * void *
rpc_allocate(unsigned int flags, unsigned int size) rpc_malloc(struct rpc_task *task, size_t size)
{ {
u32 *buffer;
int gfp; int gfp;
if (flags & RPC_TASK_SWAPPER) if (task->tk_flags & RPC_TASK_SWAPPER)
gfp = GFP_ATOMIC; gfp = GFP_ATOMIC;
else if (flags & RPC_TASK_ASYNC)
gfp = GFP_RPC;
else else
gfp = GFP_KERNEL; gfp = GFP_NOFS;
do { if (size > RPC_BUFFER_MAXSIZE) {
if ((buffer = (u32 *) kmalloc(size, gfp)) != NULL) { task->tk_buffer = kmalloc(size, gfp);
dprintk("RPC: allocated buffer %p\n", buffer); if (task->tk_buffer)
return buffer; task->tk_bufsize = size;
} } else {
if ((flags & RPC_TASK_SWAPPER) && size <= sizeof(swap_buffer) task->tk_buffer = mempool_alloc(rpc_buffer_mempool, gfp);
&& rpc_lock_swapbuf()) { if (task->tk_buffer)
dprintk("RPC: used last-ditch swap buffer\n"); task->tk_bufsize = RPC_BUFFER_MAXSIZE;
return swap_buffer;
} }
if (flags & RPC_TASK_ASYNC) return task->tk_buffer;
return NULL;
yield();
} while (!signalled());
return NULL;
} }
void void
rpc_free(void *buffer) rpc_free(struct rpc_task *task)
{ {
if (buffer != swap_buffer) { if (task->tk_buffer) {
kfree(buffer); if (task->tk_bufsize == RPC_BUFFER_MAXSIZE)
return; mempool_free(task->tk_buffer, rpc_buffer_mempool);
else
kfree(task->tk_buffer);
task->tk_buffer = NULL;
task->tk_bufsize = 0;
} }
rpc_unlock_swapbuf();
} }
/* /*
...@@ -774,11 +743,17 @@ rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, ...@@ -774,11 +743,17 @@ rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt,
current->pid); current->pid);
} }
static struct rpc_task *
rpc_alloc_task(void)
{
return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOFS);
}
static void static void
rpc_default_free_task(struct rpc_task *task) rpc_default_free_task(struct rpc_task *task)
{ {
dprintk("RPC: %4d freeing task\n", task->tk_pid); dprintk("RPC: %4d freeing task\n", task->tk_pid);
rpc_free(task); mempool_free(task, rpc_task_mempool);
} }
/* /*
...@@ -791,7 +766,7 @@ rpc_new_task(struct rpc_clnt *clnt, rpc_action callback, int flags) ...@@ -791,7 +766,7 @@ rpc_new_task(struct rpc_clnt *clnt, rpc_action callback, int flags)
{ {
struct rpc_task *task; struct rpc_task *task;
task = (struct rpc_task *) rpc_allocate(flags, sizeof(*task)); task = rpc_alloc_task();
if (!task) if (!task)
goto cleanup; goto cleanup;
...@@ -856,10 +831,7 @@ rpc_release_task(struct rpc_task *task) ...@@ -856,10 +831,7 @@ rpc_release_task(struct rpc_task *task)
xprt_release(task); xprt_release(task);
if (task->tk_msg.rpc_cred) if (task->tk_msg.rpc_cred)
rpcauth_unbindcred(task); rpcauth_unbindcred(task);
if (task->tk_buffer) { rpc_free(task);
rpc_free(task->tk_buffer);
task->tk_buffer = NULL;
}
if (task->tk_client) { if (task->tk_client) {
rpc_release_client(task->tk_client); rpc_release_client(task->tk_client);
task->tk_client = NULL; task->tk_client = NULL;
...@@ -1159,3 +1131,49 @@ void rpc_show_tasks(void) ...@@ -1159,3 +1131,49 @@ void rpc_show_tasks(void)
spin_unlock(&rpc_sched_lock); spin_unlock(&rpc_sched_lock);
} }
#endif #endif
void
rpc_destroy_mempool(void)
{
if (rpc_buffer_mempool)
mempool_destroy(rpc_buffer_mempool);
if (rpc_task_mempool)
mempool_destroy(rpc_task_mempool);
if (rpc_task_slabp && kmem_cache_destroy(rpc_task_slabp))
printk(KERN_INFO "rpc_task: not all structures were freed\n");
if (rpc_buffer_slabp && kmem_cache_destroy(rpc_buffer_slabp))
printk(KERN_INFO "rpc_buffers: not all structures were freed\n");
}
int
rpc_init_mempool(void)
{
rpc_task_slabp = kmem_cache_create("rpc_tasks",
sizeof(struct rpc_task),
0, SLAB_HWCACHE_ALIGN,
NULL, NULL);
if (!rpc_task_slabp)
goto err_nomem;
rpc_buffer_slabp = kmem_cache_create("rpc_buffers",
RPC_BUFFER_MAXSIZE,
0, SLAB_HWCACHE_ALIGN,
NULL, NULL);
if (!rpc_buffer_slabp)
goto err_nomem;
rpc_task_mempool = mempool_create(RPC_TASK_POOLSIZE,
mempool_alloc_slab,
mempool_free_slab,
rpc_task_slabp);
if (!rpc_task_mempool)
goto err_nomem;
rpc_buffer_mempool = mempool_create(RPC_BUFFER_POOLSIZE,
mempool_alloc_slab,
mempool_free_slab,
rpc_buffer_slabp);
if (!rpc_buffer_mempool)
goto err_nomem;
return 0;
err_nomem:
rpc_destroy_mempool();
return -ENOMEM;
}
...@@ -25,8 +25,6 @@ ...@@ -25,8 +25,6 @@
/* RPC scheduler */ /* RPC scheduler */
EXPORT_SYMBOL(rpc_allocate);
EXPORT_SYMBOL(rpc_free);
EXPORT_SYMBOL(rpc_execute); EXPORT_SYMBOL(rpc_execute);
EXPORT_SYMBOL(rpc_init_task); EXPORT_SYMBOL(rpc_init_task);
EXPORT_SYMBOL(rpc_sleep_on); EXPORT_SYMBOL(rpc_sleep_on);
...@@ -134,6 +132,8 @@ EXPORT_SYMBOL(nlm_debug); ...@@ -134,6 +132,8 @@ EXPORT_SYMBOL(nlm_debug);
static int __init static int __init
init_sunrpc(void) init_sunrpc(void)
{ {
if (rpc_init_mempool() != 0)
return -ENOMEM;
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
rpc_register_sysctl(); rpc_register_sysctl();
#endif #endif
...@@ -148,6 +148,7 @@ init_sunrpc(void) ...@@ -148,6 +148,7 @@ init_sunrpc(void)
static void __exit static void __exit
cleanup_sunrpc(void) cleanup_sunrpc(void)
{ {
rpc_destroy_mempool();
cache_unregister(&auth_domain_cache); cache_unregister(&auth_domain_cache);
cache_unregister(&ip_map_cache); cache_unregister(&ip_map_cache);
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
......
...@@ -1543,7 +1543,7 @@ xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to) ...@@ -1543,7 +1543,7 @@ xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to)
out_bad: out_bad:
dprintk("RPC: xprt_create_proto failed\n"); dprintk("RPC: xprt_create_proto failed\n");
if (xprt) if (xprt)
rpc_free(xprt); kfree(xprt);
return NULL; return NULL;
} }
...@@ -1582,7 +1582,7 @@ xprt_destroy(struct rpc_xprt *xprt) ...@@ -1582,7 +1582,7 @@ xprt_destroy(struct rpc_xprt *xprt)
dprintk("RPC: destroying transport %p\n", xprt); dprintk("RPC: destroying transport %p\n", xprt);
xprt_shutdown(xprt); xprt_shutdown(xprt);
xprt_close(xprt); xprt_close(xprt);
rpc_free(xprt); kfree(xprt);
return 0; return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment