Subject: [PATCH] NFS: Direct read path allocates nfs_read_data on the stack Reduce stack utilization in the NFS direct read path by using a dynamically allocated nfs_read_data structure instead of allocating one on the stack. This reduces stack utilization of nfs_direct_read_seg from over 900 bytes to less than 100 bytes. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 65 ++++++++++++++++++++--------------------- fs/nfs/read.c | 19 +---------- include/linux/nfs_fs.h | 20 ++++++++++++ 3 files changed, 54 insertions(+), 50 deletions(-) diff -X /home/cel/src/linux/dont-diff -Naurp 12-nfs-direct-write-alloc/fs/nfs/direct.c 13-nfs-direct-read-alloc/fs/nfs/direct.c --- 12-nfs-direct-write-alloc/fs/nfs/direct.c 2004-10-21 10:41:53.934023000 -0400 +++ 13-nfs-direct-read-alloc/fs/nfs/direct.c 2004-10-21 10:42:40.753555000 -0400 @@ -124,34 +124,35 @@ nfs_direct_read_seg(struct inode *inode, const unsigned int rsize = NFS_SERVER(inode)->rsize; int tot_bytes = 0; int curpage = 0; - struct nfs_read_data rdata = { - .inode = inode, - .cred = ctx->cred, - .args = { - .fh = NFS_FH(inode), - .context = ctx, - }, - .res = { - .fattr = &rdata.fattr, - }, - }; + struct nfs_read_data *rdata; + + rdata = nfs_readdata_alloc(); + if (!rdata) + return -ENOMEM; + + memset(rdata, 0, sizeof(*rdata)); + rdata->inode = inode; + rdata->cred = ctx->cred; + rdata->args.fh = NFS_FH(inode); + rdata->args.context = ctx; + rdata->res.fattr = &rdata->fattr; - rdata.args.pgbase = user_addr & ~PAGE_MASK; - rdata.args.offset = file_offset; - do { + rdata->args.pgbase = user_addr & ~PAGE_MASK; + rdata->args.offset = file_offset; + do { int result; - rdata.args.count = count; - if (rdata.args.count > rsize) - rdata.args.count = rsize; - rdata.args.pages = &pages[curpage]; + rdata->args.count = count; + if (rdata->args.count > rsize) + rdata->args.count = rsize; + rdata->args.pages = &pages[curpage]; dprintk("NFS: direct read: c=%u o=%Ld ua=%lu, pb=%u, cp=%u\n", - rdata.args.count, (long long) rdata.args.offset, - user_addr + tot_bytes, rdata.args.pgbase, curpage); + rdata->args.count, (long long) rdata->args.offset, + user_addr + tot_bytes, rdata->args.pgbase, curpage); lock_kernel(); - result = NFS_PROTO(inode)->read(&rdata); + result = NFS_PROTO(inode)->read(rdata); unlock_kernel(); if (result <= 0) { @@ -159,23 +160,22 @@ nfs_direct_read_seg(struct inode *inode, break; if (result == -EISDIR) result = -EINVAL; + nfs_readdata_free(rdata); return result; } - tot_bytes += result; - if (rdata.res.eof) + tot_bytes += result; + if (rdata->res.eof) break; - rdata.args.offset += result; - rdata.args.pgbase += result; - curpage += rdata.args.pgbase >> PAGE_SHIFT; - rdata.args.pgbase &= ~PAGE_MASK; + rdata->args.offset += result; + rdata->args.pgbase += result; + curpage += rdata->args.pgbase >> PAGE_SHIFT; + rdata->args.pgbase &= ~PAGE_MASK; count -= result; } while (count != 0); - /* XXX: should we zero the rest of the user's buffer if we - * hit eof? */ - + nfs_readdata_free(rdata); return tot_bytes; } @@ -188,9 +188,8 @@ nfs_direct_read_seg(struct inode *inode, * file_offset: offset in file to begin the operation * nr_segs: size of iovec array * - * generic_file_direct_IO has already pushed out any non-direct - * writes so that this read will see them when we read from the - * server. + * We've already pushed out any non-direct writes so that this read + * will see them when we read from the server. */ static ssize_t nfs_direct_read(struct inode *inode, struct nfs_open_context *ctx, diff -X /home/cel/src/linux/dont-diff -Naurp 12-nfs-direct-write-alloc/fs/nfs/read.c 13-nfs-direct-read-alloc/fs/nfs/read.c --- 12-nfs-direct-write-alloc/fs/nfs/read.c 2004-10-18 17:53:05.000000000 -0400 +++ 13-nfs-direct-read-alloc/fs/nfs/read.c 2004-10-21 10:42:40.766543000 -0400 @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -39,25 +38,11 @@ static void nfs_readpage_result_partial( static void nfs_readpage_result_full(struct nfs_read_data *, int); static kmem_cache_t *nfs_rdata_cachep; -static mempool_t *nfs_rdata_mempool; +mempool_t *nfs_rdata_mempool; #define MIN_POOL_READ (32) -static struct nfs_read_data *nfs_readdata_alloc(void) -{ - struct nfs_read_data *p; - p = (struct nfs_read_data *)mempool_alloc(nfs_rdata_mempool, SLAB_NOFS); - if (p) - memset(p, 0, sizeof(*p)); - return p; -} - -static __inline__ void nfs_readdata_free(struct nfs_read_data *p) -{ - mempool_free(p, nfs_rdata_mempool); -} - -static void nfs_readdata_release(struct rpc_task *task) +void nfs_readdata_release(struct rpc_task *task) { struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; nfs_readdata_free(data); diff -X /home/cel/src/linux/dont-diff -Naurp 12-nfs-direct-write-alloc/include/linux/nfs_fs.h 13-nfs-direct-read-alloc/include/linux/nfs_fs.h --- 12-nfs-direct-write-alloc/include/linux/nfs_fs.h 2004-10-21 10:41:53.942011000 -0400 +++ 13-nfs-direct-read-alloc/include/linux/nfs_fs.h 2004-10-21 10:42:40.769540000 -0400 @@ -475,6 +475,26 @@ extern int nfs_pagein_list(struct list_ extern void nfs_readpage_result(struct rpc_task *); /* + * Allocate and free nfs_read_data structures + */ +extern mempool_t *nfs_rdata_mempool; + +static inline struct nfs_read_data *nfs_readdata_alloc(void) +{ + struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, SLAB_NOFS); + if (p) + memset(p, 0, sizeof(*p)); + return p; +} + +static inline void nfs_readdata_free(struct nfs_read_data *p) +{ + mempool_free(p, nfs_rdata_mempool); +} + +extern void nfs_readdata_release(struct rpc_task *task); + +/* * linux/fs/mount_clnt.c * (Used only by nfsroot module) */