diff options
author | Chuck Lever <cel@netapp.com> | 2005-11-30 18:09:02 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-01-06 14:58:49 -0500 |
commit | 40859d7ee64ed6bfad8a4e93f9bb5c1074afadff (patch) | |
tree | ed4069423c3d6551035d5b6116f50452cdac4103 /fs/nfs/write.c | |
parent | 325cfed9ae901320e9234b18c21434b783dbe342 (diff) |
NFS: support large reads and writes on the wire
Most NFS server implementations allow up to 64KB reads and writes on the
wire. The Solaris NFS server allows up to a megabyte, for instance.
Now the Linux NFS client supports transfer sizes up to 1MB, too. This will
help reduce protocol and context switch overhead on read/write intensive NFS
workloads, and support larger atomic read and write operations on servers
that support them.
Test-plan:
Connectathon and iozone on mount point with wsize=rsize>32768 over TCP.
Tests with NFS over UDP to verify the maximum RPC payload size cap.
Signed-off-by: Chuck Lever <cel@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r-- | fs/nfs/write.c | 29 |
1 files changed, 22 insertions, 7 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 80bc4ea1b824..1ce0c200df16 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -89,18 +89,33 @@ static mempool_t *nfs_commit_mempool; | |||
89 | 89 | ||
90 | static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion); | 90 | static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion); |
91 | 91 | ||
92 | static inline struct nfs_write_data *nfs_commit_alloc(void) | 92 | static inline struct nfs_write_data *nfs_commit_alloc(unsigned int pagecount) |
93 | { | 93 | { |
94 | struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, SLAB_NOFS); | 94 | struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, SLAB_NOFS); |
95 | |||
95 | if (p) { | 96 | if (p) { |
96 | memset(p, 0, sizeof(*p)); | 97 | memset(p, 0, sizeof(*p)); |
97 | INIT_LIST_HEAD(&p->pages); | 98 | INIT_LIST_HEAD(&p->pages); |
99 | if (pagecount < NFS_PAGEVEC_SIZE) | ||
100 | p->pagevec = &p->page_array[0]; | ||
101 | else { | ||
102 | size_t size = ++pagecount * sizeof(struct page *); | ||
103 | p->pagevec = kmalloc(size, GFP_NOFS); | ||
104 | if (p->pagevec) { | ||
105 | memset(p->pagevec, 0, size); | ||
106 | } else { | ||
107 | mempool_free(p, nfs_commit_mempool); | ||
108 | p = NULL; | ||
109 | } | ||
110 | } | ||
98 | } | 111 | } |
99 | return p; | 112 | return p; |
100 | } | 113 | } |
101 | 114 | ||
102 | static inline void nfs_commit_free(struct nfs_write_data *p) | 115 | static inline void nfs_commit_free(struct nfs_write_data *p) |
103 | { | 116 | { |
117 | if (p && (p->pagevec != &p->page_array[0])) | ||
118 | kfree(p->pagevec); | ||
104 | mempool_free(p, nfs_commit_mempool); | 119 | mempool_free(p, nfs_commit_mempool); |
105 | } | 120 | } |
106 | 121 | ||
@@ -167,7 +182,7 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode, | |||
167 | int result, written = 0; | 182 | int result, written = 0; |
168 | struct nfs_write_data *wdata; | 183 | struct nfs_write_data *wdata; |
169 | 184 | ||
170 | wdata = nfs_writedata_alloc(); | 185 | wdata = nfs_writedata_alloc(1); |
171 | if (!wdata) | 186 | if (!wdata) |
172 | return -ENOMEM; | 187 | return -ENOMEM; |
173 | 188 | ||
@@ -909,7 +924,7 @@ static int nfs_flush_multi(struct list_head *head, struct inode *inode, int how) | |||
909 | 924 | ||
910 | nbytes = req->wb_bytes; | 925 | nbytes = req->wb_bytes; |
911 | for (;;) { | 926 | for (;;) { |
912 | data = nfs_writedata_alloc(); | 927 | data = nfs_writedata_alloc(1); |
913 | if (!data) | 928 | if (!data) |
914 | goto out_bad; | 929 | goto out_bad; |
915 | list_add(&data->pages, &list); | 930 | list_add(&data->pages, &list); |
@@ -973,7 +988,7 @@ static int nfs_flush_one(struct list_head *head, struct inode *inode, int how) | |||
973 | if (NFS_SERVER(inode)->wsize < PAGE_CACHE_SIZE) | 988 | if (NFS_SERVER(inode)->wsize < PAGE_CACHE_SIZE) |
974 | return nfs_flush_multi(head, inode, how); | 989 | return nfs_flush_multi(head, inode, how); |
975 | 990 | ||
976 | data = nfs_writedata_alloc(); | 991 | data = nfs_writedata_alloc(NFS_SERVER(inode)->wpages); |
977 | if (!data) | 992 | if (!data) |
978 | goto out_bad; | 993 | goto out_bad; |
979 | 994 | ||
@@ -1241,12 +1256,12 @@ static void nfs_commit_rpcsetup(struct list_head *head, | |||
1241 | * Commit dirty pages | 1256 | * Commit dirty pages |
1242 | */ | 1257 | */ |
1243 | static int | 1258 | static int |
1244 | nfs_commit_list(struct list_head *head, int how) | 1259 | nfs_commit_list(struct inode *inode, struct list_head *head, int how) |
1245 | { | 1260 | { |
1246 | struct nfs_write_data *data; | 1261 | struct nfs_write_data *data; |
1247 | struct nfs_page *req; | 1262 | struct nfs_page *req; |
1248 | 1263 | ||
1249 | data = nfs_commit_alloc(); | 1264 | data = nfs_commit_alloc(NFS_SERVER(inode)->wpages); |
1250 | 1265 | ||
1251 | if (!data) | 1266 | if (!data) |
1252 | goto out_bad; | 1267 | goto out_bad; |
@@ -1351,7 +1366,7 @@ int nfs_commit_inode(struct inode *inode, int how) | |||
1351 | res = nfs_scan_commit(inode, &head, 0, 0); | 1366 | res = nfs_scan_commit(inode, &head, 0, 0); |
1352 | spin_unlock(&nfsi->req_lock); | 1367 | spin_unlock(&nfsi->req_lock); |
1353 | if (res) { | 1368 | if (res) { |
1354 | error = nfs_commit_list(&head, how); | 1369 | error = nfs_commit_list(inode, &head, how); |
1355 | if (error < 0) | 1370 | if (error < 0) |
1356 | return error; | 1371 | return error; |
1357 | } | 1372 | } |