aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/pnfs_nfs.c
diff options
context:
space:
mode:
authorTom Haynes <loghyr@primarydata.com>2014-12-11 15:34:59 -0500
committerTom Haynes <loghyr@primarydata.com>2015-02-03 14:06:31 -0500
commitf54bcf2ecee982da47c2baf8bd87fd9ad9984651 (patch)
treec813d2cc90424e847452cec824cbcdbf74470b39 /fs/nfs/pnfs_nfs.c
parentec6f34e5b552fb0a52e6aae1a5afbbb1605cc6cc (diff)
pnfs: Prepare for flexfiles by pulling out common code
The flexfilelayout driver will share some common code with the filelayout driver. This set of changes refactors that common code out to avoid any module depenencies. Signed-off-by: Tom Haynes <loghyr@primarydata.com>
Diffstat (limited to 'fs/nfs/pnfs_nfs.c')
-rw-r--r--fs/nfs/pnfs_nfs.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c
new file mode 100644
index 000000000000..e5f841cb6227
--- /dev/null
+++ b/fs/nfs/pnfs_nfs.c
@@ -0,0 +1,291 @@
1/*
2 * Common NFS I/O operations for the pnfs file based
3 * layout drivers.
4 *
5 * Copyright (c) 2014, Primary Data, Inc. All rights reserved.
6 *
7 * Tom Haynes <loghyr@primarydata.com>
8 */
9
10#include <linux/nfs_fs.h>
11#include <linux/nfs_page.h>
12
13#include "internal.h"
14#include "pnfs.h"
15
16static void pnfs_generic_fenceme(struct inode *inode,
17 struct pnfs_layout_hdr *lo)
18{
19 if (!test_and_clear_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
20 return;
21 pnfs_return_layout(inode);
22}
23
24void pnfs_generic_rw_release(void *data)
25{
26 struct nfs_pgio_header *hdr = data;
27 struct pnfs_layout_hdr *lo = hdr->lseg->pls_layout;
28
29 pnfs_generic_fenceme(lo->plh_inode, lo);
30 nfs_put_client(hdr->ds_clp);
31 hdr->mds_ops->rpc_release(data);
32}
33EXPORT_SYMBOL_GPL(pnfs_generic_rw_release);
34
35/* Fake up some data that will cause nfs_commit_release to retry the writes. */
36void pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data *data)
37{
38 struct nfs_page *first = nfs_list_entry(data->pages.next);
39
40 data->task.tk_status = 0;
41 memcpy(&data->verf.verifier, &first->wb_verf,
42 sizeof(data->verf.verifier));
43 data->verf.verifier.data[0]++; /* ensure verifier mismatch */
44}
45EXPORT_SYMBOL_GPL(pnfs_generic_prepare_to_resend_writes);
46
47void pnfs_generic_write_commit_done(struct rpc_task *task, void *data)
48{
49 struct nfs_commit_data *wdata = data;
50
51 /* Note this may cause RPC to be resent */
52 wdata->mds_ops->rpc_call_done(task, data);
53}
54EXPORT_SYMBOL_GPL(pnfs_generic_write_commit_done);
55
56void pnfs_generic_commit_release(void *calldata)
57{
58 struct nfs_commit_data *data = calldata;
59
60 data->completion_ops->completion(data);
61 pnfs_put_lseg(data->lseg);
62 nfs_put_client(data->ds_clp);
63 nfs_commitdata_release(data);
64}
65EXPORT_SYMBOL_GPL(pnfs_generic_commit_release);
66
67/* The generic layer is about to remove the req from the commit list.
68 * If this will make the bucket empty, it will need to put the lseg reference.
69 * Note this is must be called holding the inode (/cinfo) lock
70 */
71void
72pnfs_generic_clear_request_commit(struct nfs_page *req,
73 struct nfs_commit_info *cinfo)
74{
75 struct pnfs_layout_segment *freeme = NULL;
76
77 if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags))
78 goto out;
79 cinfo->ds->nwritten--;
80 if (list_is_singular(&req->wb_list)) {
81 struct pnfs_commit_bucket *bucket;
82
83 bucket = list_first_entry(&req->wb_list,
84 struct pnfs_commit_bucket,
85 written);
86 freeme = bucket->wlseg;
87 bucket->wlseg = NULL;
88 }
89out:
90 nfs_request_remove_commit_list(req, cinfo);
91 pnfs_put_lseg_locked(freeme);
92}
93EXPORT_SYMBOL_GPL(pnfs_generic_clear_request_commit);
94
95static int
96pnfs_generic_transfer_commit_list(struct list_head *src, struct list_head *dst,
97 struct nfs_commit_info *cinfo, int max)
98{
99 struct nfs_page *req, *tmp;
100 int ret = 0;
101
102 list_for_each_entry_safe(req, tmp, src, wb_list) {
103 if (!nfs_lock_request(req))
104 continue;
105 kref_get(&req->wb_kref);
106 if (cond_resched_lock(cinfo->lock))
107 list_safe_reset_next(req, tmp, wb_list);
108 nfs_request_remove_commit_list(req, cinfo);
109 clear_bit(PG_COMMIT_TO_DS, &req->wb_flags);
110 nfs_list_add_request(req, dst);
111 ret++;
112 if ((ret == max) && !cinfo->dreq)
113 break;
114 }
115 return ret;
116}
117
118/* Note called with cinfo->lock held. */
119static int
120pnfs_generic_scan_ds_commit_list(struct pnfs_commit_bucket *bucket,
121 struct nfs_commit_info *cinfo,
122 int max)
123{
124 struct list_head *src = &bucket->written;
125 struct list_head *dst = &bucket->committing;
126 int ret;
127
128 ret = pnfs_generic_transfer_commit_list(src, dst, cinfo, max);
129 if (ret) {
130 cinfo->ds->nwritten -= ret;
131 cinfo->ds->ncommitting += ret;
132 bucket->clseg = bucket->wlseg;
133 if (list_empty(src))
134 bucket->wlseg = NULL;
135 else
136 pnfs_get_lseg(bucket->clseg);
137 }
138 return ret;
139}
140
141/* Move reqs from written to committing lists, returning count of number moved.
142 * Note called with cinfo->lock held.
143 */
144int pnfs_generic_scan_commit_lists(struct nfs_commit_info *cinfo,
145 int max)
146{
147 int i, rv = 0, cnt;
148
149 for (i = 0; i < cinfo->ds->nbuckets && max != 0; i++) {
150 cnt = pnfs_generic_scan_ds_commit_list(&cinfo->ds->buckets[i],
151 cinfo, max);
152 max -= cnt;
153 rv += cnt;
154 }
155 return rv;
156}
157EXPORT_SYMBOL_GPL(pnfs_generic_scan_commit_lists);
158
159/* Pull everything off the committing lists and dump into @dst */
160void pnfs_generic_recover_commit_reqs(struct list_head *dst,
161 struct nfs_commit_info *cinfo)
162{
163 struct pnfs_commit_bucket *b;
164 struct pnfs_layout_segment *freeme;
165 int i;
166
167restart:
168 spin_lock(cinfo->lock);
169 for (i = 0, b = cinfo->ds->buckets; i < cinfo->ds->nbuckets; i++, b++) {
170 if (pnfs_generic_transfer_commit_list(&b->written, dst,
171 cinfo, 0)) {
172 freeme = b->wlseg;
173 b->wlseg = NULL;
174 spin_unlock(cinfo->lock);
175 pnfs_put_lseg(freeme);
176 goto restart;
177 }
178 }
179 cinfo->ds->nwritten = 0;
180 spin_unlock(cinfo->lock);
181}
182EXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs);
183
184static void pnfs_generic_retry_commit(struct nfs_commit_info *cinfo, int idx)
185{
186 struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
187 struct pnfs_commit_bucket *bucket;
188 struct pnfs_layout_segment *freeme;
189 int i;
190
191 for (i = idx; i < fl_cinfo->nbuckets; i++) {
192 bucket = &fl_cinfo->buckets[i];
193 if (list_empty(&bucket->committing))
194 continue;
195 nfs_retry_commit(&bucket->committing, bucket->clseg, cinfo);
196 spin_lock(cinfo->lock);
197 freeme = bucket->clseg;
198 bucket->clseg = NULL;
199 spin_unlock(cinfo->lock);
200 pnfs_put_lseg(freeme);
201 }
202}
203
204static unsigned int
205pnfs_generic_alloc_ds_commits(struct nfs_commit_info *cinfo,
206 struct list_head *list)
207{
208 struct pnfs_ds_commit_info *fl_cinfo;
209 struct pnfs_commit_bucket *bucket;
210 struct nfs_commit_data *data;
211 int i;
212 unsigned int nreq = 0;
213
214 fl_cinfo = cinfo->ds;
215 bucket = fl_cinfo->buckets;
216 for (i = 0; i < fl_cinfo->nbuckets; i++, bucket++) {
217 if (list_empty(&bucket->committing))
218 continue;
219 data = nfs_commitdata_alloc();
220 if (!data)
221 break;
222 data->ds_commit_index = i;
223 spin_lock(cinfo->lock);
224 data->lseg = bucket->clseg;
225 bucket->clseg = NULL;
226 spin_unlock(cinfo->lock);
227 list_add(&data->pages, list);
228 nreq++;
229 }
230
231 /* Clean up on error */
232 pnfs_generic_retry_commit(cinfo, i);
233 return nreq;
234}
235
236/* This follows nfs_commit_list pretty closely */
237int
238pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
239 int how, struct nfs_commit_info *cinfo,
240 int (*initiate_commit)(struct nfs_commit_data *data,
241 int how))
242{
243 struct nfs_commit_data *data, *tmp;
244 LIST_HEAD(list);
245 unsigned int nreq = 0;
246
247 if (!list_empty(mds_pages)) {
248 data = nfs_commitdata_alloc();
249 if (data != NULL) {
250 data->lseg = NULL;
251 list_add(&data->pages, &list);
252 nreq++;
253 } else {
254 nfs_retry_commit(mds_pages, NULL, cinfo);
255 pnfs_generic_retry_commit(cinfo, 0);
256 cinfo->completion_ops->error_cleanup(NFS_I(inode));
257 return -ENOMEM;
258 }
259 }
260
261 nreq += pnfs_generic_alloc_ds_commits(cinfo, &list);
262
263 if (nreq == 0) {
264 cinfo->completion_ops->error_cleanup(NFS_I(inode));
265 goto out;
266 }
267
268 atomic_add(nreq, &cinfo->mds->rpcs_out);
269
270 list_for_each_entry_safe(data, tmp, &list, pages) {
271 list_del_init(&data->pages);
272 if (!data->lseg) {
273 nfs_init_commit(data, mds_pages, NULL, cinfo);
274 nfs_initiate_commit(NFS_CLIENT(inode), data,
275 data->mds_ops, how, 0);
276 } else {
277 struct pnfs_commit_bucket *buckets;
278
279 buckets = cinfo->ds->buckets;
280 nfs_init_commit(data,
281 &buckets[data->ds_commit_index].committing,
282 data->lseg,
283 cinfo);
284 initiate_commit(data, how);
285 }
286 }
287out:
288 cinfo->ds->ncommitting = 0;
289 return PNFS_ATTEMPTED;
290}
291EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist);