aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/pnfs_nfs.c
diff options
context:
space:
mode:
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);