diff options
Diffstat (limited to 'fs/nfs/pnfs_nfs.c')
-rw-r--r-- | fs/nfs/pnfs_nfs.c | 291 |
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 | |||
16 | static 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 | |||
24 | void 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 | } | ||
33 | EXPORT_SYMBOL_GPL(pnfs_generic_rw_release); | ||
34 | |||
35 | /* Fake up some data that will cause nfs_commit_release to retry the writes. */ | ||
36 | void 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 | } | ||
45 | EXPORT_SYMBOL_GPL(pnfs_generic_prepare_to_resend_writes); | ||
46 | |||
47 | void 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 | } | ||
54 | EXPORT_SYMBOL_GPL(pnfs_generic_write_commit_done); | ||
55 | |||
56 | void 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 | } | ||
65 | EXPORT_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 | */ | ||
71 | void | ||
72 | pnfs_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 | } | ||
89 | out: | ||
90 | nfs_request_remove_commit_list(req, cinfo); | ||
91 | pnfs_put_lseg_locked(freeme); | ||
92 | } | ||
93 | EXPORT_SYMBOL_GPL(pnfs_generic_clear_request_commit); | ||
94 | |||
95 | static int | ||
96 | pnfs_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. */ | ||
119 | static int | ||
120 | pnfs_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 | */ | ||
144 | int 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 | } | ||
157 | EXPORT_SYMBOL_GPL(pnfs_generic_scan_commit_lists); | ||
158 | |||
159 | /* Pull everything off the committing lists and dump into @dst */ | ||
160 | void 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 | |||
167 | restart: | ||
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 | } | ||
182 | EXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs); | ||
183 | |||
184 | static 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 | |||
204 | static unsigned int | ||
205 | pnfs_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 */ | ||
237 | int | ||
238 | pnfs_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 | } | ||
287 | out: | ||
288 | cinfo->ds->ncommitting = 0; | ||
289 | return PNFS_ATTEMPTED; | ||
290 | } | ||
291 | EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist); | ||