1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Common NFS I/O operations for the pnfs file based
4*4882a593Smuzhiyun * layout drivers.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (c) 2014, Primary Data, Inc. All rights reserved.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Tom Haynes <loghyr@primarydata.com>
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/nfs_fs.h>
12*4882a593Smuzhiyun #include <linux/nfs_page.h>
13*4882a593Smuzhiyun #include <linux/sunrpc/addr.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include "nfs4session.h"
17*4882a593Smuzhiyun #include "internal.h"
18*4882a593Smuzhiyun #include "pnfs.h"
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #define NFSDBG_FACILITY NFSDBG_PNFS
21*4882a593Smuzhiyun
pnfs_generic_rw_release(void * data)22*4882a593Smuzhiyun void pnfs_generic_rw_release(void *data)
23*4882a593Smuzhiyun {
24*4882a593Smuzhiyun struct nfs_pgio_header *hdr = data;
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun nfs_put_client(hdr->ds_clp);
27*4882a593Smuzhiyun hdr->mds_ops->rpc_release(data);
28*4882a593Smuzhiyun }
29*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_generic_rw_release);
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun /* Fake up some data that will cause nfs_commit_release to retry the writes. */
pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data * data)32*4882a593Smuzhiyun void pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data *data)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun struct nfs_writeverf *verf = data->res.verf;
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun data->task.tk_status = 0;
37*4882a593Smuzhiyun memset(&verf->verifier, 0, sizeof(verf->verifier));
38*4882a593Smuzhiyun verf->committed = NFS_UNSTABLE;
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_generic_prepare_to_resend_writes);
41*4882a593Smuzhiyun
pnfs_generic_write_commit_done(struct rpc_task * task,void * data)42*4882a593Smuzhiyun void pnfs_generic_write_commit_done(struct rpc_task *task, void *data)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun struct nfs_commit_data *wdata = data;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun /* Note this may cause RPC to be resent */
47*4882a593Smuzhiyun wdata->mds_ops->rpc_call_done(task, data);
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_generic_write_commit_done);
50*4882a593Smuzhiyun
pnfs_generic_commit_release(void * calldata)51*4882a593Smuzhiyun void pnfs_generic_commit_release(void *calldata)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun struct nfs_commit_data *data = calldata;
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun data->completion_ops->completion(data);
56*4882a593Smuzhiyun pnfs_put_lseg(data->lseg);
57*4882a593Smuzhiyun nfs_put_client(data->ds_clp);
58*4882a593Smuzhiyun nfs_commitdata_release(data);
59*4882a593Smuzhiyun }
60*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_generic_commit_release);
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun static struct pnfs_layout_segment *
pnfs_free_bucket_lseg(struct pnfs_commit_bucket * bucket)63*4882a593Smuzhiyun pnfs_free_bucket_lseg(struct pnfs_commit_bucket *bucket)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun if (list_empty(&bucket->committing) && list_empty(&bucket->written)) {
66*4882a593Smuzhiyun struct pnfs_layout_segment *freeme = bucket->lseg;
67*4882a593Smuzhiyun bucket->lseg = NULL;
68*4882a593Smuzhiyun return freeme;
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun return NULL;
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun /* The generic layer is about to remove the req from the commit list.
74*4882a593Smuzhiyun * If this will make the bucket empty, it will need to put the lseg reference.
75*4882a593Smuzhiyun * Note this must be called holding nfsi->commit_mutex
76*4882a593Smuzhiyun */
77*4882a593Smuzhiyun void
pnfs_generic_clear_request_commit(struct nfs_page * req,struct nfs_commit_info * cinfo)78*4882a593Smuzhiyun pnfs_generic_clear_request_commit(struct nfs_page *req,
79*4882a593Smuzhiyun struct nfs_commit_info *cinfo)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun struct pnfs_commit_bucket *bucket = NULL;
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags))
84*4882a593Smuzhiyun goto out;
85*4882a593Smuzhiyun cinfo->ds->nwritten--;
86*4882a593Smuzhiyun if (list_is_singular(&req->wb_list))
87*4882a593Smuzhiyun bucket = list_first_entry(&req->wb_list,
88*4882a593Smuzhiyun struct pnfs_commit_bucket, written);
89*4882a593Smuzhiyun out:
90*4882a593Smuzhiyun nfs_request_remove_commit_list(req, cinfo);
91*4882a593Smuzhiyun if (bucket)
92*4882a593Smuzhiyun pnfs_put_lseg(pnfs_free_bucket_lseg(bucket));
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_generic_clear_request_commit);
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun struct pnfs_commit_array *
pnfs_alloc_commit_array(size_t n,gfp_t gfp_flags)97*4882a593Smuzhiyun pnfs_alloc_commit_array(size_t n, gfp_t gfp_flags)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun struct pnfs_commit_array *p;
100*4882a593Smuzhiyun struct pnfs_commit_bucket *b;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun p = kmalloc(struct_size(p, buckets, n), gfp_flags);
103*4882a593Smuzhiyun if (!p)
104*4882a593Smuzhiyun return NULL;
105*4882a593Smuzhiyun p->nbuckets = n;
106*4882a593Smuzhiyun INIT_LIST_HEAD(&p->cinfo_list);
107*4882a593Smuzhiyun INIT_LIST_HEAD(&p->lseg_list);
108*4882a593Smuzhiyun p->lseg = NULL;
109*4882a593Smuzhiyun for (b = &p->buckets[0]; n != 0; b++, n--) {
110*4882a593Smuzhiyun INIT_LIST_HEAD(&b->written);
111*4882a593Smuzhiyun INIT_LIST_HEAD(&b->committing);
112*4882a593Smuzhiyun b->lseg = NULL;
113*4882a593Smuzhiyun b->direct_verf.committed = NFS_INVALID_STABLE_HOW;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun return p;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_alloc_commit_array);
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun void
pnfs_free_commit_array(struct pnfs_commit_array * p)120*4882a593Smuzhiyun pnfs_free_commit_array(struct pnfs_commit_array *p)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun kfree_rcu(p, rcu);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_free_commit_array);
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun static struct pnfs_commit_array *
pnfs_find_commit_array_by_lseg(struct pnfs_ds_commit_info * fl_cinfo,struct pnfs_layout_segment * lseg)127*4882a593Smuzhiyun pnfs_find_commit_array_by_lseg(struct pnfs_ds_commit_info *fl_cinfo,
128*4882a593Smuzhiyun struct pnfs_layout_segment *lseg)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun struct pnfs_commit_array *array;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) {
133*4882a593Smuzhiyun if (array->lseg == lseg)
134*4882a593Smuzhiyun return array;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun return NULL;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun struct pnfs_commit_array *
pnfs_add_commit_array(struct pnfs_ds_commit_info * fl_cinfo,struct pnfs_commit_array * new,struct pnfs_layout_segment * lseg)140*4882a593Smuzhiyun pnfs_add_commit_array(struct pnfs_ds_commit_info *fl_cinfo,
141*4882a593Smuzhiyun struct pnfs_commit_array *new,
142*4882a593Smuzhiyun struct pnfs_layout_segment *lseg)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun struct pnfs_commit_array *array;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun array = pnfs_find_commit_array_by_lseg(fl_cinfo, lseg);
147*4882a593Smuzhiyun if (array)
148*4882a593Smuzhiyun return array;
149*4882a593Smuzhiyun new->lseg = lseg;
150*4882a593Smuzhiyun refcount_set(&new->refcount, 1);
151*4882a593Smuzhiyun list_add_rcu(&new->cinfo_list, &fl_cinfo->commits);
152*4882a593Smuzhiyun list_add(&new->lseg_list, &lseg->pls_commits);
153*4882a593Smuzhiyun return new;
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_add_commit_array);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun static struct pnfs_commit_array *
pnfs_lookup_commit_array(struct pnfs_ds_commit_info * fl_cinfo,struct pnfs_layout_segment * lseg)158*4882a593Smuzhiyun pnfs_lookup_commit_array(struct pnfs_ds_commit_info *fl_cinfo,
159*4882a593Smuzhiyun struct pnfs_layout_segment *lseg)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun struct pnfs_commit_array *array;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun rcu_read_lock();
164*4882a593Smuzhiyun array = pnfs_find_commit_array_by_lseg(fl_cinfo, lseg);
165*4882a593Smuzhiyun if (!array) {
166*4882a593Smuzhiyun rcu_read_unlock();
167*4882a593Smuzhiyun fl_cinfo->ops->setup_ds_info(fl_cinfo, lseg);
168*4882a593Smuzhiyun rcu_read_lock();
169*4882a593Smuzhiyun array = pnfs_find_commit_array_by_lseg(fl_cinfo, lseg);
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun rcu_read_unlock();
172*4882a593Smuzhiyun return array;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun static void
pnfs_release_commit_array_locked(struct pnfs_commit_array * array)176*4882a593Smuzhiyun pnfs_release_commit_array_locked(struct pnfs_commit_array *array)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun list_del_rcu(&array->cinfo_list);
179*4882a593Smuzhiyun list_del(&array->lseg_list);
180*4882a593Smuzhiyun pnfs_free_commit_array(array);
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun static void
pnfs_put_commit_array_locked(struct pnfs_commit_array * array)184*4882a593Smuzhiyun pnfs_put_commit_array_locked(struct pnfs_commit_array *array)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun if (refcount_dec_and_test(&array->refcount))
187*4882a593Smuzhiyun pnfs_release_commit_array_locked(array);
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun static void
pnfs_put_commit_array(struct pnfs_commit_array * array,struct inode * inode)191*4882a593Smuzhiyun pnfs_put_commit_array(struct pnfs_commit_array *array, struct inode *inode)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun if (refcount_dec_and_lock(&array->refcount, &inode->i_lock)) {
194*4882a593Smuzhiyun pnfs_release_commit_array_locked(array);
195*4882a593Smuzhiyun spin_unlock(&inode->i_lock);
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun static struct pnfs_commit_array *
pnfs_get_commit_array(struct pnfs_commit_array * array)200*4882a593Smuzhiyun pnfs_get_commit_array(struct pnfs_commit_array *array)
201*4882a593Smuzhiyun {
202*4882a593Smuzhiyun if (refcount_inc_not_zero(&array->refcount))
203*4882a593Smuzhiyun return array;
204*4882a593Smuzhiyun return NULL;
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun static void
pnfs_remove_and_free_commit_array(struct pnfs_commit_array * array)208*4882a593Smuzhiyun pnfs_remove_and_free_commit_array(struct pnfs_commit_array *array)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun array->lseg = NULL;
211*4882a593Smuzhiyun list_del_init(&array->lseg_list);
212*4882a593Smuzhiyun pnfs_put_commit_array_locked(array);
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun void
pnfs_generic_ds_cinfo_release_lseg(struct pnfs_ds_commit_info * fl_cinfo,struct pnfs_layout_segment * lseg)216*4882a593Smuzhiyun pnfs_generic_ds_cinfo_release_lseg(struct pnfs_ds_commit_info *fl_cinfo,
217*4882a593Smuzhiyun struct pnfs_layout_segment *lseg)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun struct pnfs_commit_array *array, *tmp;
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun list_for_each_entry_safe(array, tmp, &lseg->pls_commits, lseg_list)
222*4882a593Smuzhiyun pnfs_remove_and_free_commit_array(array);
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_generic_ds_cinfo_release_lseg);
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun void
pnfs_generic_ds_cinfo_destroy(struct pnfs_ds_commit_info * fl_cinfo)227*4882a593Smuzhiyun pnfs_generic_ds_cinfo_destroy(struct pnfs_ds_commit_info *fl_cinfo)
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun struct pnfs_commit_array *array, *tmp;
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun list_for_each_entry_safe(array, tmp, &fl_cinfo->commits, cinfo_list)
232*4882a593Smuzhiyun pnfs_remove_and_free_commit_array(array);
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_generic_ds_cinfo_destroy);
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun /*
237*4882a593Smuzhiyun * Locks the nfs_page requests for commit and moves them to
238*4882a593Smuzhiyun * @bucket->committing.
239*4882a593Smuzhiyun */
240*4882a593Smuzhiyun static int
pnfs_bucket_scan_ds_commit_list(struct pnfs_commit_bucket * bucket,struct nfs_commit_info * cinfo,int max)241*4882a593Smuzhiyun pnfs_bucket_scan_ds_commit_list(struct pnfs_commit_bucket *bucket,
242*4882a593Smuzhiyun struct nfs_commit_info *cinfo,
243*4882a593Smuzhiyun int max)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun struct list_head *src = &bucket->written;
246*4882a593Smuzhiyun struct list_head *dst = &bucket->committing;
247*4882a593Smuzhiyun int ret;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun lockdep_assert_held(&NFS_I(cinfo->inode)->commit_mutex);
250*4882a593Smuzhiyun ret = nfs_scan_commit_list(src, dst, cinfo, max);
251*4882a593Smuzhiyun if (ret) {
252*4882a593Smuzhiyun cinfo->ds->nwritten -= ret;
253*4882a593Smuzhiyun cinfo->ds->ncommitting += ret;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun return ret;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun
pnfs_bucket_scan_array(struct nfs_commit_info * cinfo,struct pnfs_commit_bucket * buckets,unsigned int nbuckets,int max)258*4882a593Smuzhiyun static int pnfs_bucket_scan_array(struct nfs_commit_info *cinfo,
259*4882a593Smuzhiyun struct pnfs_commit_bucket *buckets,
260*4882a593Smuzhiyun unsigned int nbuckets,
261*4882a593Smuzhiyun int max)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun unsigned int i;
264*4882a593Smuzhiyun int rv = 0, cnt;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun for (i = 0; i < nbuckets && max != 0; i++) {
267*4882a593Smuzhiyun cnt = pnfs_bucket_scan_ds_commit_list(&buckets[i], cinfo, max);
268*4882a593Smuzhiyun rv += cnt;
269*4882a593Smuzhiyun max -= cnt;
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun return rv;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun /* Move reqs from written to committing lists, returning count
275*4882a593Smuzhiyun * of number moved.
276*4882a593Smuzhiyun */
pnfs_generic_scan_commit_lists(struct nfs_commit_info * cinfo,int max)277*4882a593Smuzhiyun int pnfs_generic_scan_commit_lists(struct nfs_commit_info *cinfo, int max)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
280*4882a593Smuzhiyun struct pnfs_commit_array *array;
281*4882a593Smuzhiyun int rv = 0, cnt;
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun rcu_read_lock();
284*4882a593Smuzhiyun list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) {
285*4882a593Smuzhiyun if (!array->lseg || !pnfs_get_commit_array(array))
286*4882a593Smuzhiyun continue;
287*4882a593Smuzhiyun rcu_read_unlock();
288*4882a593Smuzhiyun cnt = pnfs_bucket_scan_array(cinfo, array->buckets,
289*4882a593Smuzhiyun array->nbuckets, max);
290*4882a593Smuzhiyun rcu_read_lock();
291*4882a593Smuzhiyun pnfs_put_commit_array(array, cinfo->inode);
292*4882a593Smuzhiyun rv += cnt;
293*4882a593Smuzhiyun max -= cnt;
294*4882a593Smuzhiyun if (!max)
295*4882a593Smuzhiyun break;
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun rcu_read_unlock();
298*4882a593Smuzhiyun return rv;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_generic_scan_commit_lists);
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun static unsigned int
pnfs_bucket_recover_commit_reqs(struct list_head * dst,struct pnfs_commit_bucket * buckets,unsigned int nbuckets,struct nfs_commit_info * cinfo)303*4882a593Smuzhiyun pnfs_bucket_recover_commit_reqs(struct list_head *dst,
304*4882a593Smuzhiyun struct pnfs_commit_bucket *buckets,
305*4882a593Smuzhiyun unsigned int nbuckets,
306*4882a593Smuzhiyun struct nfs_commit_info *cinfo)
307*4882a593Smuzhiyun {
308*4882a593Smuzhiyun struct pnfs_commit_bucket *b;
309*4882a593Smuzhiyun struct pnfs_layout_segment *freeme;
310*4882a593Smuzhiyun unsigned int nwritten, ret = 0;
311*4882a593Smuzhiyun unsigned int i;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun restart:
314*4882a593Smuzhiyun for (i = 0, b = buckets; i < nbuckets; i++, b++) {
315*4882a593Smuzhiyun nwritten = nfs_scan_commit_list(&b->written, dst, cinfo, 0);
316*4882a593Smuzhiyun if (!nwritten)
317*4882a593Smuzhiyun continue;
318*4882a593Smuzhiyun ret += nwritten;
319*4882a593Smuzhiyun freeme = pnfs_free_bucket_lseg(b);
320*4882a593Smuzhiyun if (freeme) {
321*4882a593Smuzhiyun pnfs_put_lseg(freeme);
322*4882a593Smuzhiyun goto restart;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun }
325*4882a593Smuzhiyun return ret;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun /* Pull everything off the committing lists and dump into @dst. */
pnfs_generic_recover_commit_reqs(struct list_head * dst,struct nfs_commit_info * cinfo)329*4882a593Smuzhiyun void pnfs_generic_recover_commit_reqs(struct list_head *dst,
330*4882a593Smuzhiyun struct nfs_commit_info *cinfo)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
333*4882a593Smuzhiyun struct pnfs_commit_array *array;
334*4882a593Smuzhiyun unsigned int nwritten;
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun lockdep_assert_held(&NFS_I(cinfo->inode)->commit_mutex);
337*4882a593Smuzhiyun rcu_read_lock();
338*4882a593Smuzhiyun list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) {
339*4882a593Smuzhiyun if (!array->lseg || !pnfs_get_commit_array(array))
340*4882a593Smuzhiyun continue;
341*4882a593Smuzhiyun rcu_read_unlock();
342*4882a593Smuzhiyun nwritten = pnfs_bucket_recover_commit_reqs(dst,
343*4882a593Smuzhiyun array->buckets,
344*4882a593Smuzhiyun array->nbuckets,
345*4882a593Smuzhiyun cinfo);
346*4882a593Smuzhiyun rcu_read_lock();
347*4882a593Smuzhiyun pnfs_put_commit_array(array, cinfo->inode);
348*4882a593Smuzhiyun fl_cinfo->nwritten -= nwritten;
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun rcu_read_unlock();
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs);
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun static struct nfs_page *
pnfs_bucket_search_commit_reqs(struct pnfs_commit_bucket * buckets,unsigned int nbuckets,struct page * page)355*4882a593Smuzhiyun pnfs_bucket_search_commit_reqs(struct pnfs_commit_bucket *buckets,
356*4882a593Smuzhiyun unsigned int nbuckets, struct page *page)
357*4882a593Smuzhiyun {
358*4882a593Smuzhiyun struct nfs_page *req;
359*4882a593Smuzhiyun struct pnfs_commit_bucket *b;
360*4882a593Smuzhiyun unsigned int i;
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun /* Linearly search the commit lists for each bucket until a matching
363*4882a593Smuzhiyun * request is found */
364*4882a593Smuzhiyun for (i = 0, b = buckets; i < nbuckets; i++, b++) {
365*4882a593Smuzhiyun list_for_each_entry(req, &b->written, wb_list) {
366*4882a593Smuzhiyun if (req->wb_page == page)
367*4882a593Smuzhiyun return req->wb_head;
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun list_for_each_entry(req, &b->committing, wb_list) {
370*4882a593Smuzhiyun if (req->wb_page == page)
371*4882a593Smuzhiyun return req->wb_head;
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun return NULL;
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun /* pnfs_generic_search_commit_reqs - Search lists in @cinfo for the head reqest
378*4882a593Smuzhiyun * for @page
379*4882a593Smuzhiyun * @cinfo - commit info for current inode
380*4882a593Smuzhiyun * @page - page to search for matching head request
381*4882a593Smuzhiyun *
382*4882a593Smuzhiyun * Returns a the head request if one is found, otherwise returns NULL.
383*4882a593Smuzhiyun */
384*4882a593Smuzhiyun struct nfs_page *
pnfs_generic_search_commit_reqs(struct nfs_commit_info * cinfo,struct page * page)385*4882a593Smuzhiyun pnfs_generic_search_commit_reqs(struct nfs_commit_info *cinfo, struct page *page)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
388*4882a593Smuzhiyun struct pnfs_commit_array *array;
389*4882a593Smuzhiyun struct nfs_page *req;
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun list_for_each_entry(array, &fl_cinfo->commits, cinfo_list) {
392*4882a593Smuzhiyun req = pnfs_bucket_search_commit_reqs(array->buckets,
393*4882a593Smuzhiyun array->nbuckets, page);
394*4882a593Smuzhiyun if (req)
395*4882a593Smuzhiyun return req;
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun return NULL;
398*4882a593Smuzhiyun }
399*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_generic_search_commit_reqs);
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun static struct pnfs_layout_segment *
pnfs_bucket_get_committing(struct list_head * head,struct pnfs_commit_bucket * bucket,struct nfs_commit_info * cinfo)402*4882a593Smuzhiyun pnfs_bucket_get_committing(struct list_head *head,
403*4882a593Smuzhiyun struct pnfs_commit_bucket *bucket,
404*4882a593Smuzhiyun struct nfs_commit_info *cinfo)
405*4882a593Smuzhiyun {
406*4882a593Smuzhiyun struct pnfs_layout_segment *lseg;
407*4882a593Smuzhiyun struct list_head *pos;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun list_for_each(pos, &bucket->committing)
410*4882a593Smuzhiyun cinfo->ds->ncommitting--;
411*4882a593Smuzhiyun list_splice_init(&bucket->committing, head);
412*4882a593Smuzhiyun lseg = pnfs_free_bucket_lseg(bucket);
413*4882a593Smuzhiyun if (!lseg)
414*4882a593Smuzhiyun lseg = pnfs_get_lseg(bucket->lseg);
415*4882a593Smuzhiyun return lseg;
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun static struct nfs_commit_data *
pnfs_bucket_fetch_commitdata(struct pnfs_commit_bucket * bucket,struct nfs_commit_info * cinfo)419*4882a593Smuzhiyun pnfs_bucket_fetch_commitdata(struct pnfs_commit_bucket *bucket,
420*4882a593Smuzhiyun struct nfs_commit_info *cinfo)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun struct nfs_commit_data *data = nfs_commitdata_alloc();
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun if (!data)
425*4882a593Smuzhiyun return NULL;
426*4882a593Smuzhiyun data->lseg = pnfs_bucket_get_committing(&data->pages, bucket, cinfo);
427*4882a593Smuzhiyun return data;
428*4882a593Smuzhiyun }
429*4882a593Smuzhiyun
pnfs_generic_retry_commit(struct pnfs_commit_bucket * buckets,unsigned int nbuckets,struct nfs_commit_info * cinfo,unsigned int idx)430*4882a593Smuzhiyun static void pnfs_generic_retry_commit(struct pnfs_commit_bucket *buckets,
431*4882a593Smuzhiyun unsigned int nbuckets,
432*4882a593Smuzhiyun struct nfs_commit_info *cinfo,
433*4882a593Smuzhiyun unsigned int idx)
434*4882a593Smuzhiyun {
435*4882a593Smuzhiyun struct pnfs_commit_bucket *bucket;
436*4882a593Smuzhiyun struct pnfs_layout_segment *freeme;
437*4882a593Smuzhiyun LIST_HEAD(pages);
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun for (bucket = buckets; idx < nbuckets; bucket++, idx++) {
440*4882a593Smuzhiyun if (list_empty(&bucket->committing))
441*4882a593Smuzhiyun continue;
442*4882a593Smuzhiyun mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
443*4882a593Smuzhiyun freeme = pnfs_bucket_get_committing(&pages, bucket, cinfo);
444*4882a593Smuzhiyun mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
445*4882a593Smuzhiyun nfs_retry_commit(&pages, freeme, cinfo, idx);
446*4882a593Smuzhiyun pnfs_put_lseg(freeme);
447*4882a593Smuzhiyun }
448*4882a593Smuzhiyun }
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun static unsigned int
pnfs_bucket_alloc_ds_commits(struct list_head * list,struct pnfs_commit_bucket * buckets,unsigned int nbuckets,struct nfs_commit_info * cinfo)451*4882a593Smuzhiyun pnfs_bucket_alloc_ds_commits(struct list_head *list,
452*4882a593Smuzhiyun struct pnfs_commit_bucket *buckets,
453*4882a593Smuzhiyun unsigned int nbuckets,
454*4882a593Smuzhiyun struct nfs_commit_info *cinfo)
455*4882a593Smuzhiyun {
456*4882a593Smuzhiyun struct pnfs_commit_bucket *bucket;
457*4882a593Smuzhiyun struct nfs_commit_data *data;
458*4882a593Smuzhiyun unsigned int i;
459*4882a593Smuzhiyun unsigned int nreq = 0;
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun for (i = 0, bucket = buckets; i < nbuckets; i++, bucket++) {
462*4882a593Smuzhiyun if (list_empty(&bucket->committing))
463*4882a593Smuzhiyun continue;
464*4882a593Smuzhiyun mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
465*4882a593Smuzhiyun if (!list_empty(&bucket->committing)) {
466*4882a593Smuzhiyun data = pnfs_bucket_fetch_commitdata(bucket, cinfo);
467*4882a593Smuzhiyun if (!data)
468*4882a593Smuzhiyun goto out_error;
469*4882a593Smuzhiyun data->ds_commit_index = i;
470*4882a593Smuzhiyun list_add_tail(&data->list, list);
471*4882a593Smuzhiyun nreq++;
472*4882a593Smuzhiyun }
473*4882a593Smuzhiyun mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
474*4882a593Smuzhiyun }
475*4882a593Smuzhiyun return nreq;
476*4882a593Smuzhiyun out_error:
477*4882a593Smuzhiyun mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
478*4882a593Smuzhiyun /* Clean up on error */
479*4882a593Smuzhiyun pnfs_generic_retry_commit(buckets, nbuckets, cinfo, i);
480*4882a593Smuzhiyun return nreq;
481*4882a593Smuzhiyun }
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun static unsigned int
pnfs_alloc_ds_commits_list(struct list_head * list,struct pnfs_ds_commit_info * fl_cinfo,struct nfs_commit_info * cinfo)484*4882a593Smuzhiyun pnfs_alloc_ds_commits_list(struct list_head *list,
485*4882a593Smuzhiyun struct pnfs_ds_commit_info *fl_cinfo,
486*4882a593Smuzhiyun struct nfs_commit_info *cinfo)
487*4882a593Smuzhiyun {
488*4882a593Smuzhiyun struct pnfs_commit_array *array;
489*4882a593Smuzhiyun unsigned int ret = 0;
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun rcu_read_lock();
492*4882a593Smuzhiyun list_for_each_entry_rcu(array, &fl_cinfo->commits, cinfo_list) {
493*4882a593Smuzhiyun if (!array->lseg || !pnfs_get_commit_array(array))
494*4882a593Smuzhiyun continue;
495*4882a593Smuzhiyun rcu_read_unlock();
496*4882a593Smuzhiyun ret += pnfs_bucket_alloc_ds_commits(list, array->buckets,
497*4882a593Smuzhiyun array->nbuckets, cinfo);
498*4882a593Smuzhiyun rcu_read_lock();
499*4882a593Smuzhiyun pnfs_put_commit_array(array, cinfo->inode);
500*4882a593Smuzhiyun }
501*4882a593Smuzhiyun rcu_read_unlock();
502*4882a593Smuzhiyun return ret;
503*4882a593Smuzhiyun }
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun /* This follows nfs_commit_list pretty closely */
506*4882a593Smuzhiyun int
pnfs_generic_commit_pagelist(struct inode * inode,struct list_head * mds_pages,int how,struct nfs_commit_info * cinfo,int (* initiate_commit)(struct nfs_commit_data * data,int how))507*4882a593Smuzhiyun pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
508*4882a593Smuzhiyun int how, struct nfs_commit_info *cinfo,
509*4882a593Smuzhiyun int (*initiate_commit)(struct nfs_commit_data *data,
510*4882a593Smuzhiyun int how))
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
513*4882a593Smuzhiyun struct nfs_commit_data *data, *tmp;
514*4882a593Smuzhiyun LIST_HEAD(list);
515*4882a593Smuzhiyun unsigned int nreq = 0;
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun if (!list_empty(mds_pages)) {
518*4882a593Smuzhiyun data = nfs_commitdata_alloc();
519*4882a593Smuzhiyun if (!data) {
520*4882a593Smuzhiyun nfs_retry_commit(mds_pages, NULL, cinfo, -1);
521*4882a593Smuzhiyun return -ENOMEM;
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun data->ds_commit_index = -1;
524*4882a593Smuzhiyun list_splice_init(mds_pages, &data->pages);
525*4882a593Smuzhiyun list_add_tail(&data->list, &list);
526*4882a593Smuzhiyun nreq++;
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun
529*4882a593Smuzhiyun nreq += pnfs_alloc_ds_commits_list(&list, fl_cinfo, cinfo);
530*4882a593Smuzhiyun if (nreq == 0)
531*4882a593Smuzhiyun goto out;
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun list_for_each_entry_safe(data, tmp, &list, list) {
534*4882a593Smuzhiyun list_del(&data->list);
535*4882a593Smuzhiyun if (data->ds_commit_index < 0) {
536*4882a593Smuzhiyun nfs_init_commit(data, NULL, NULL, cinfo);
537*4882a593Smuzhiyun nfs_initiate_commit(NFS_CLIENT(inode), data,
538*4882a593Smuzhiyun NFS_PROTO(data->inode),
539*4882a593Smuzhiyun data->mds_ops, how,
540*4882a593Smuzhiyun RPC_TASK_CRED_NOREF);
541*4882a593Smuzhiyun } else {
542*4882a593Smuzhiyun nfs_init_commit(data, NULL, data->lseg, cinfo);
543*4882a593Smuzhiyun initiate_commit(data, how);
544*4882a593Smuzhiyun }
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun out:
547*4882a593Smuzhiyun return PNFS_ATTEMPTED;
548*4882a593Smuzhiyun }
549*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist);
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun /*
552*4882a593Smuzhiyun * Data server cache
553*4882a593Smuzhiyun *
554*4882a593Smuzhiyun * Data servers can be mapped to different device ids.
555*4882a593Smuzhiyun * nfs4_pnfs_ds reference counting
556*4882a593Smuzhiyun * - set to 1 on allocation
557*4882a593Smuzhiyun * - incremented when a device id maps a data server already in the cache.
558*4882a593Smuzhiyun * - decremented when deviceid is removed from the cache.
559*4882a593Smuzhiyun */
560*4882a593Smuzhiyun static DEFINE_SPINLOCK(nfs4_ds_cache_lock);
561*4882a593Smuzhiyun static LIST_HEAD(nfs4_data_server_cache);
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun /* Debug routines */
564*4882a593Smuzhiyun static void
print_ds(struct nfs4_pnfs_ds * ds)565*4882a593Smuzhiyun print_ds(struct nfs4_pnfs_ds *ds)
566*4882a593Smuzhiyun {
567*4882a593Smuzhiyun if (ds == NULL) {
568*4882a593Smuzhiyun printk(KERN_WARNING "%s NULL device\n", __func__);
569*4882a593Smuzhiyun return;
570*4882a593Smuzhiyun }
571*4882a593Smuzhiyun printk(KERN_WARNING " ds %s\n"
572*4882a593Smuzhiyun " ref count %d\n"
573*4882a593Smuzhiyun " client %p\n"
574*4882a593Smuzhiyun " cl_exchange_flags %x\n",
575*4882a593Smuzhiyun ds->ds_remotestr,
576*4882a593Smuzhiyun refcount_read(&ds->ds_count), ds->ds_clp,
577*4882a593Smuzhiyun ds->ds_clp ? ds->ds_clp->cl_exchange_flags : 0);
578*4882a593Smuzhiyun }
579*4882a593Smuzhiyun
580*4882a593Smuzhiyun static bool
same_sockaddr(struct sockaddr * addr1,struct sockaddr * addr2)581*4882a593Smuzhiyun same_sockaddr(struct sockaddr *addr1, struct sockaddr *addr2)
582*4882a593Smuzhiyun {
583*4882a593Smuzhiyun struct sockaddr_in *a, *b;
584*4882a593Smuzhiyun struct sockaddr_in6 *a6, *b6;
585*4882a593Smuzhiyun
586*4882a593Smuzhiyun if (addr1->sa_family != addr2->sa_family)
587*4882a593Smuzhiyun return false;
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun switch (addr1->sa_family) {
590*4882a593Smuzhiyun case AF_INET:
591*4882a593Smuzhiyun a = (struct sockaddr_in *)addr1;
592*4882a593Smuzhiyun b = (struct sockaddr_in *)addr2;
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun if (a->sin_addr.s_addr == b->sin_addr.s_addr &&
595*4882a593Smuzhiyun a->sin_port == b->sin_port)
596*4882a593Smuzhiyun return true;
597*4882a593Smuzhiyun break;
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun case AF_INET6:
600*4882a593Smuzhiyun a6 = (struct sockaddr_in6 *)addr1;
601*4882a593Smuzhiyun b6 = (struct sockaddr_in6 *)addr2;
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun /* LINKLOCAL addresses must have matching scope_id */
604*4882a593Smuzhiyun if (ipv6_addr_src_scope(&a6->sin6_addr) ==
605*4882a593Smuzhiyun IPV6_ADDR_SCOPE_LINKLOCAL &&
606*4882a593Smuzhiyun a6->sin6_scope_id != b6->sin6_scope_id)
607*4882a593Smuzhiyun return false;
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun if (ipv6_addr_equal(&a6->sin6_addr, &b6->sin6_addr) &&
610*4882a593Smuzhiyun a6->sin6_port == b6->sin6_port)
611*4882a593Smuzhiyun return true;
612*4882a593Smuzhiyun break;
613*4882a593Smuzhiyun
614*4882a593Smuzhiyun default:
615*4882a593Smuzhiyun dprintk("%s: unhandled address family: %u\n",
616*4882a593Smuzhiyun __func__, addr1->sa_family);
617*4882a593Smuzhiyun return false;
618*4882a593Smuzhiyun }
619*4882a593Smuzhiyun
620*4882a593Smuzhiyun return false;
621*4882a593Smuzhiyun }
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun /*
624*4882a593Smuzhiyun * Checks if 'dsaddrs1' contains a subset of 'dsaddrs2'. If it does,
625*4882a593Smuzhiyun * declare a match.
626*4882a593Smuzhiyun */
627*4882a593Smuzhiyun static bool
_same_data_server_addrs_locked(const struct list_head * dsaddrs1,const struct list_head * dsaddrs2)628*4882a593Smuzhiyun _same_data_server_addrs_locked(const struct list_head *dsaddrs1,
629*4882a593Smuzhiyun const struct list_head *dsaddrs2)
630*4882a593Smuzhiyun {
631*4882a593Smuzhiyun struct nfs4_pnfs_ds_addr *da1, *da2;
632*4882a593Smuzhiyun struct sockaddr *sa1, *sa2;
633*4882a593Smuzhiyun bool match = false;
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun list_for_each_entry(da1, dsaddrs1, da_node) {
636*4882a593Smuzhiyun sa1 = (struct sockaddr *)&da1->da_addr;
637*4882a593Smuzhiyun match = false;
638*4882a593Smuzhiyun list_for_each_entry(da2, dsaddrs2, da_node) {
639*4882a593Smuzhiyun sa2 = (struct sockaddr *)&da2->da_addr;
640*4882a593Smuzhiyun match = same_sockaddr(sa1, sa2);
641*4882a593Smuzhiyun if (match)
642*4882a593Smuzhiyun break;
643*4882a593Smuzhiyun }
644*4882a593Smuzhiyun if (!match)
645*4882a593Smuzhiyun break;
646*4882a593Smuzhiyun }
647*4882a593Smuzhiyun return match;
648*4882a593Smuzhiyun }
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun /*
651*4882a593Smuzhiyun * Lookup DS by addresses. nfs4_ds_cache_lock is held
652*4882a593Smuzhiyun */
653*4882a593Smuzhiyun static struct nfs4_pnfs_ds *
_data_server_lookup_locked(const struct list_head * dsaddrs)654*4882a593Smuzhiyun _data_server_lookup_locked(const struct list_head *dsaddrs)
655*4882a593Smuzhiyun {
656*4882a593Smuzhiyun struct nfs4_pnfs_ds *ds;
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun list_for_each_entry(ds, &nfs4_data_server_cache, ds_node)
659*4882a593Smuzhiyun if (_same_data_server_addrs_locked(&ds->ds_addrs, dsaddrs))
660*4882a593Smuzhiyun return ds;
661*4882a593Smuzhiyun return NULL;
662*4882a593Smuzhiyun }
663*4882a593Smuzhiyun
destroy_ds(struct nfs4_pnfs_ds * ds)664*4882a593Smuzhiyun static void destroy_ds(struct nfs4_pnfs_ds *ds)
665*4882a593Smuzhiyun {
666*4882a593Smuzhiyun struct nfs4_pnfs_ds_addr *da;
667*4882a593Smuzhiyun
668*4882a593Smuzhiyun dprintk("--> %s\n", __func__);
669*4882a593Smuzhiyun ifdebug(FACILITY)
670*4882a593Smuzhiyun print_ds(ds);
671*4882a593Smuzhiyun
672*4882a593Smuzhiyun nfs_put_client(ds->ds_clp);
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun while (!list_empty(&ds->ds_addrs)) {
675*4882a593Smuzhiyun da = list_first_entry(&ds->ds_addrs,
676*4882a593Smuzhiyun struct nfs4_pnfs_ds_addr,
677*4882a593Smuzhiyun da_node);
678*4882a593Smuzhiyun list_del_init(&da->da_node);
679*4882a593Smuzhiyun kfree(da->da_remotestr);
680*4882a593Smuzhiyun kfree(da);
681*4882a593Smuzhiyun }
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun kfree(ds->ds_remotestr);
684*4882a593Smuzhiyun kfree(ds);
685*4882a593Smuzhiyun }
686*4882a593Smuzhiyun
nfs4_pnfs_ds_put(struct nfs4_pnfs_ds * ds)687*4882a593Smuzhiyun void nfs4_pnfs_ds_put(struct nfs4_pnfs_ds *ds)
688*4882a593Smuzhiyun {
689*4882a593Smuzhiyun if (refcount_dec_and_lock(&ds->ds_count,
690*4882a593Smuzhiyun &nfs4_ds_cache_lock)) {
691*4882a593Smuzhiyun list_del_init(&ds->ds_node);
692*4882a593Smuzhiyun spin_unlock(&nfs4_ds_cache_lock);
693*4882a593Smuzhiyun destroy_ds(ds);
694*4882a593Smuzhiyun }
695*4882a593Smuzhiyun }
696*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_put);
697*4882a593Smuzhiyun
698*4882a593Smuzhiyun /*
699*4882a593Smuzhiyun * Create a string with a human readable address and port to avoid
700*4882a593Smuzhiyun * complicated setup around many dprinks.
701*4882a593Smuzhiyun */
702*4882a593Smuzhiyun static char *
nfs4_pnfs_remotestr(struct list_head * dsaddrs,gfp_t gfp_flags)703*4882a593Smuzhiyun nfs4_pnfs_remotestr(struct list_head *dsaddrs, gfp_t gfp_flags)
704*4882a593Smuzhiyun {
705*4882a593Smuzhiyun struct nfs4_pnfs_ds_addr *da;
706*4882a593Smuzhiyun char *remotestr;
707*4882a593Smuzhiyun size_t len;
708*4882a593Smuzhiyun char *p;
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun len = 3; /* '{', '}' and eol */
711*4882a593Smuzhiyun list_for_each_entry(da, dsaddrs, da_node) {
712*4882a593Smuzhiyun len += strlen(da->da_remotestr) + 1; /* string plus comma */
713*4882a593Smuzhiyun }
714*4882a593Smuzhiyun
715*4882a593Smuzhiyun remotestr = kzalloc(len, gfp_flags);
716*4882a593Smuzhiyun if (!remotestr)
717*4882a593Smuzhiyun return NULL;
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun p = remotestr;
720*4882a593Smuzhiyun *(p++) = '{';
721*4882a593Smuzhiyun len--;
722*4882a593Smuzhiyun list_for_each_entry(da, dsaddrs, da_node) {
723*4882a593Smuzhiyun size_t ll = strlen(da->da_remotestr);
724*4882a593Smuzhiyun
725*4882a593Smuzhiyun if (ll > len)
726*4882a593Smuzhiyun goto out_err;
727*4882a593Smuzhiyun
728*4882a593Smuzhiyun memcpy(p, da->da_remotestr, ll);
729*4882a593Smuzhiyun p += ll;
730*4882a593Smuzhiyun len -= ll;
731*4882a593Smuzhiyun
732*4882a593Smuzhiyun if (len < 1)
733*4882a593Smuzhiyun goto out_err;
734*4882a593Smuzhiyun (*p++) = ',';
735*4882a593Smuzhiyun len--;
736*4882a593Smuzhiyun }
737*4882a593Smuzhiyun if (len < 2)
738*4882a593Smuzhiyun goto out_err;
739*4882a593Smuzhiyun *(p++) = '}';
740*4882a593Smuzhiyun *p = '\0';
741*4882a593Smuzhiyun return remotestr;
742*4882a593Smuzhiyun out_err:
743*4882a593Smuzhiyun kfree(remotestr);
744*4882a593Smuzhiyun return NULL;
745*4882a593Smuzhiyun }
746*4882a593Smuzhiyun
747*4882a593Smuzhiyun /*
748*4882a593Smuzhiyun * Given a list of multipath struct nfs4_pnfs_ds_addr, add it to ds cache if
749*4882a593Smuzhiyun * uncached and return cached struct nfs4_pnfs_ds.
750*4882a593Smuzhiyun */
751*4882a593Smuzhiyun struct nfs4_pnfs_ds *
nfs4_pnfs_ds_add(struct list_head * dsaddrs,gfp_t gfp_flags)752*4882a593Smuzhiyun nfs4_pnfs_ds_add(struct list_head *dsaddrs, gfp_t gfp_flags)
753*4882a593Smuzhiyun {
754*4882a593Smuzhiyun struct nfs4_pnfs_ds *tmp_ds, *ds = NULL;
755*4882a593Smuzhiyun char *remotestr;
756*4882a593Smuzhiyun
757*4882a593Smuzhiyun if (list_empty(dsaddrs)) {
758*4882a593Smuzhiyun dprintk("%s: no addresses defined\n", __func__);
759*4882a593Smuzhiyun goto out;
760*4882a593Smuzhiyun }
761*4882a593Smuzhiyun
762*4882a593Smuzhiyun ds = kzalloc(sizeof(*ds), gfp_flags);
763*4882a593Smuzhiyun if (!ds)
764*4882a593Smuzhiyun goto out;
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun /* this is only used for debugging, so it's ok if its NULL */
767*4882a593Smuzhiyun remotestr = nfs4_pnfs_remotestr(dsaddrs, gfp_flags);
768*4882a593Smuzhiyun
769*4882a593Smuzhiyun spin_lock(&nfs4_ds_cache_lock);
770*4882a593Smuzhiyun tmp_ds = _data_server_lookup_locked(dsaddrs);
771*4882a593Smuzhiyun if (tmp_ds == NULL) {
772*4882a593Smuzhiyun INIT_LIST_HEAD(&ds->ds_addrs);
773*4882a593Smuzhiyun list_splice_init(dsaddrs, &ds->ds_addrs);
774*4882a593Smuzhiyun ds->ds_remotestr = remotestr;
775*4882a593Smuzhiyun refcount_set(&ds->ds_count, 1);
776*4882a593Smuzhiyun INIT_LIST_HEAD(&ds->ds_node);
777*4882a593Smuzhiyun ds->ds_clp = NULL;
778*4882a593Smuzhiyun list_add(&ds->ds_node, &nfs4_data_server_cache);
779*4882a593Smuzhiyun dprintk("%s add new data server %s\n", __func__,
780*4882a593Smuzhiyun ds->ds_remotestr);
781*4882a593Smuzhiyun } else {
782*4882a593Smuzhiyun kfree(remotestr);
783*4882a593Smuzhiyun kfree(ds);
784*4882a593Smuzhiyun refcount_inc(&tmp_ds->ds_count);
785*4882a593Smuzhiyun dprintk("%s data server %s found, inc'ed ds_count to %d\n",
786*4882a593Smuzhiyun __func__, tmp_ds->ds_remotestr,
787*4882a593Smuzhiyun refcount_read(&tmp_ds->ds_count));
788*4882a593Smuzhiyun ds = tmp_ds;
789*4882a593Smuzhiyun }
790*4882a593Smuzhiyun spin_unlock(&nfs4_ds_cache_lock);
791*4882a593Smuzhiyun out:
792*4882a593Smuzhiyun return ds;
793*4882a593Smuzhiyun }
794*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_add);
795*4882a593Smuzhiyun
nfs4_wait_ds_connect(struct nfs4_pnfs_ds * ds)796*4882a593Smuzhiyun static int nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds)
797*4882a593Smuzhiyun {
798*4882a593Smuzhiyun might_sleep();
799*4882a593Smuzhiyun return wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING, TASK_KILLABLE);
800*4882a593Smuzhiyun }
801*4882a593Smuzhiyun
nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds * ds)802*4882a593Smuzhiyun static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds)
803*4882a593Smuzhiyun {
804*4882a593Smuzhiyun smp_mb__before_atomic();
805*4882a593Smuzhiyun clear_and_wake_up_bit(NFS4DS_CONNECTING, &ds->ds_state);
806*4882a593Smuzhiyun }
807*4882a593Smuzhiyun
808*4882a593Smuzhiyun static struct nfs_client *(*get_v3_ds_connect)(
809*4882a593Smuzhiyun struct nfs_server *mds_srv,
810*4882a593Smuzhiyun const struct sockaddr *ds_addr,
811*4882a593Smuzhiyun int ds_addrlen,
812*4882a593Smuzhiyun int ds_proto,
813*4882a593Smuzhiyun unsigned int ds_timeo,
814*4882a593Smuzhiyun unsigned int ds_retrans);
815*4882a593Smuzhiyun
load_v3_ds_connect(void)816*4882a593Smuzhiyun static bool load_v3_ds_connect(void)
817*4882a593Smuzhiyun {
818*4882a593Smuzhiyun if (!get_v3_ds_connect) {
819*4882a593Smuzhiyun get_v3_ds_connect = symbol_request(nfs3_set_ds_client);
820*4882a593Smuzhiyun WARN_ON_ONCE(!get_v3_ds_connect);
821*4882a593Smuzhiyun }
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun return(get_v3_ds_connect != NULL);
824*4882a593Smuzhiyun }
825*4882a593Smuzhiyun
nfs4_pnfs_v3_ds_connect_unload(void)826*4882a593Smuzhiyun void nfs4_pnfs_v3_ds_connect_unload(void)
827*4882a593Smuzhiyun {
828*4882a593Smuzhiyun if (get_v3_ds_connect) {
829*4882a593Smuzhiyun symbol_put(nfs3_set_ds_client);
830*4882a593Smuzhiyun get_v3_ds_connect = NULL;
831*4882a593Smuzhiyun }
832*4882a593Smuzhiyun }
833*4882a593Smuzhiyun
_nfs4_pnfs_v3_ds_connect(struct nfs_server * mds_srv,struct nfs4_pnfs_ds * ds,unsigned int timeo,unsigned int retrans)834*4882a593Smuzhiyun static int _nfs4_pnfs_v3_ds_connect(struct nfs_server *mds_srv,
835*4882a593Smuzhiyun struct nfs4_pnfs_ds *ds,
836*4882a593Smuzhiyun unsigned int timeo,
837*4882a593Smuzhiyun unsigned int retrans)
838*4882a593Smuzhiyun {
839*4882a593Smuzhiyun struct nfs_client *clp = ERR_PTR(-EIO);
840*4882a593Smuzhiyun struct nfs4_pnfs_ds_addr *da;
841*4882a593Smuzhiyun int status = 0;
842*4882a593Smuzhiyun
843*4882a593Smuzhiyun dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr);
844*4882a593Smuzhiyun
845*4882a593Smuzhiyun if (!load_v3_ds_connect())
846*4882a593Smuzhiyun goto out;
847*4882a593Smuzhiyun
848*4882a593Smuzhiyun list_for_each_entry(da, &ds->ds_addrs, da_node) {
849*4882a593Smuzhiyun dprintk("%s: DS %s: trying address %s\n",
850*4882a593Smuzhiyun __func__, ds->ds_remotestr, da->da_remotestr);
851*4882a593Smuzhiyun
852*4882a593Smuzhiyun if (!IS_ERR(clp)) {
853*4882a593Smuzhiyun struct xprt_create xprt_args = {
854*4882a593Smuzhiyun .ident = XPRT_TRANSPORT_TCP,
855*4882a593Smuzhiyun .net = clp->cl_net,
856*4882a593Smuzhiyun .dstaddr = (struct sockaddr *)&da->da_addr,
857*4882a593Smuzhiyun .addrlen = da->da_addrlen,
858*4882a593Smuzhiyun .servername = clp->cl_hostname,
859*4882a593Smuzhiyun };
860*4882a593Smuzhiyun /* Add this address as an alias */
861*4882a593Smuzhiyun rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
862*4882a593Smuzhiyun rpc_clnt_test_and_add_xprt, NULL);
863*4882a593Smuzhiyun continue;
864*4882a593Smuzhiyun }
865*4882a593Smuzhiyun clp = get_v3_ds_connect(mds_srv,
866*4882a593Smuzhiyun (struct sockaddr *)&da->da_addr,
867*4882a593Smuzhiyun da->da_addrlen, IPPROTO_TCP,
868*4882a593Smuzhiyun timeo, retrans);
869*4882a593Smuzhiyun if (IS_ERR(clp))
870*4882a593Smuzhiyun continue;
871*4882a593Smuzhiyun clp->cl_rpcclient->cl_softerr = 0;
872*4882a593Smuzhiyun clp->cl_rpcclient->cl_softrtry = 0;
873*4882a593Smuzhiyun }
874*4882a593Smuzhiyun
875*4882a593Smuzhiyun if (IS_ERR(clp)) {
876*4882a593Smuzhiyun status = PTR_ERR(clp);
877*4882a593Smuzhiyun goto out;
878*4882a593Smuzhiyun }
879*4882a593Smuzhiyun
880*4882a593Smuzhiyun smp_wmb();
881*4882a593Smuzhiyun WRITE_ONCE(ds->ds_clp, clp);
882*4882a593Smuzhiyun dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr);
883*4882a593Smuzhiyun out:
884*4882a593Smuzhiyun return status;
885*4882a593Smuzhiyun }
886*4882a593Smuzhiyun
_nfs4_pnfs_v4_ds_connect(struct nfs_server * mds_srv,struct nfs4_pnfs_ds * ds,unsigned int timeo,unsigned int retrans,u32 minor_version)887*4882a593Smuzhiyun static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
888*4882a593Smuzhiyun struct nfs4_pnfs_ds *ds,
889*4882a593Smuzhiyun unsigned int timeo,
890*4882a593Smuzhiyun unsigned int retrans,
891*4882a593Smuzhiyun u32 minor_version)
892*4882a593Smuzhiyun {
893*4882a593Smuzhiyun struct nfs_client *clp = ERR_PTR(-EIO);
894*4882a593Smuzhiyun struct nfs4_pnfs_ds_addr *da;
895*4882a593Smuzhiyun int status = 0;
896*4882a593Smuzhiyun
897*4882a593Smuzhiyun dprintk("--> %s DS %s\n", __func__, ds->ds_remotestr);
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun list_for_each_entry(da, &ds->ds_addrs, da_node) {
900*4882a593Smuzhiyun dprintk("%s: DS %s: trying address %s\n",
901*4882a593Smuzhiyun __func__, ds->ds_remotestr, da->da_remotestr);
902*4882a593Smuzhiyun
903*4882a593Smuzhiyun if (!IS_ERR(clp) && clp->cl_mvops->session_trunk) {
904*4882a593Smuzhiyun struct xprt_create xprt_args = {
905*4882a593Smuzhiyun .ident = XPRT_TRANSPORT_TCP,
906*4882a593Smuzhiyun .net = clp->cl_net,
907*4882a593Smuzhiyun .dstaddr = (struct sockaddr *)&da->da_addr,
908*4882a593Smuzhiyun .addrlen = da->da_addrlen,
909*4882a593Smuzhiyun .servername = clp->cl_hostname,
910*4882a593Smuzhiyun };
911*4882a593Smuzhiyun struct nfs4_add_xprt_data xprtdata = {
912*4882a593Smuzhiyun .clp = clp,
913*4882a593Smuzhiyun .cred = nfs4_get_clid_cred(clp),
914*4882a593Smuzhiyun };
915*4882a593Smuzhiyun struct rpc_add_xprt_test rpcdata = {
916*4882a593Smuzhiyun .add_xprt_test = clp->cl_mvops->session_trunk,
917*4882a593Smuzhiyun .data = &xprtdata,
918*4882a593Smuzhiyun };
919*4882a593Smuzhiyun
920*4882a593Smuzhiyun /**
921*4882a593Smuzhiyun * Test this address for session trunking and
922*4882a593Smuzhiyun * add as an alias
923*4882a593Smuzhiyun */
924*4882a593Smuzhiyun rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
925*4882a593Smuzhiyun rpc_clnt_setup_test_and_add_xprt,
926*4882a593Smuzhiyun &rpcdata);
927*4882a593Smuzhiyun if (xprtdata.cred)
928*4882a593Smuzhiyun put_cred(xprtdata.cred);
929*4882a593Smuzhiyun } else {
930*4882a593Smuzhiyun clp = nfs4_set_ds_client(mds_srv,
931*4882a593Smuzhiyun (struct sockaddr *)&da->da_addr,
932*4882a593Smuzhiyun da->da_addrlen, IPPROTO_TCP,
933*4882a593Smuzhiyun timeo, retrans, minor_version);
934*4882a593Smuzhiyun if (IS_ERR(clp))
935*4882a593Smuzhiyun continue;
936*4882a593Smuzhiyun
937*4882a593Smuzhiyun status = nfs4_init_ds_session(clp,
938*4882a593Smuzhiyun mds_srv->nfs_client->cl_lease_time);
939*4882a593Smuzhiyun if (status) {
940*4882a593Smuzhiyun nfs_put_client(clp);
941*4882a593Smuzhiyun clp = ERR_PTR(-EIO);
942*4882a593Smuzhiyun continue;
943*4882a593Smuzhiyun }
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun }
946*4882a593Smuzhiyun }
947*4882a593Smuzhiyun
948*4882a593Smuzhiyun if (IS_ERR(clp)) {
949*4882a593Smuzhiyun status = PTR_ERR(clp);
950*4882a593Smuzhiyun goto out;
951*4882a593Smuzhiyun }
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun smp_wmb();
954*4882a593Smuzhiyun WRITE_ONCE(ds->ds_clp, clp);
955*4882a593Smuzhiyun dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr);
956*4882a593Smuzhiyun out:
957*4882a593Smuzhiyun return status;
958*4882a593Smuzhiyun }
959*4882a593Smuzhiyun
960*4882a593Smuzhiyun /*
961*4882a593Smuzhiyun * Create an rpc connection to the nfs4_pnfs_ds data server.
962*4882a593Smuzhiyun * Currently only supports IPv4 and IPv6 addresses.
963*4882a593Smuzhiyun * If connection fails, make devid unavailable and return a -errno.
964*4882a593Smuzhiyun */
nfs4_pnfs_ds_connect(struct nfs_server * mds_srv,struct nfs4_pnfs_ds * ds,struct nfs4_deviceid_node * devid,unsigned int timeo,unsigned int retrans,u32 version,u32 minor_version)965*4882a593Smuzhiyun int nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
966*4882a593Smuzhiyun struct nfs4_deviceid_node *devid, unsigned int timeo,
967*4882a593Smuzhiyun unsigned int retrans, u32 version, u32 minor_version)
968*4882a593Smuzhiyun {
969*4882a593Smuzhiyun int err;
970*4882a593Smuzhiyun
971*4882a593Smuzhiyun do {
972*4882a593Smuzhiyun err = nfs4_wait_ds_connect(ds);
973*4882a593Smuzhiyun if (err || ds->ds_clp)
974*4882a593Smuzhiyun goto out;
975*4882a593Smuzhiyun if (nfs4_test_deviceid_unavailable(devid))
976*4882a593Smuzhiyun return -ENODEV;
977*4882a593Smuzhiyun } while (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) != 0);
978*4882a593Smuzhiyun
979*4882a593Smuzhiyun if (ds->ds_clp)
980*4882a593Smuzhiyun goto connect_done;
981*4882a593Smuzhiyun
982*4882a593Smuzhiyun switch (version) {
983*4882a593Smuzhiyun case 3:
984*4882a593Smuzhiyun err = _nfs4_pnfs_v3_ds_connect(mds_srv, ds, timeo, retrans);
985*4882a593Smuzhiyun break;
986*4882a593Smuzhiyun case 4:
987*4882a593Smuzhiyun err = _nfs4_pnfs_v4_ds_connect(mds_srv, ds, timeo, retrans,
988*4882a593Smuzhiyun minor_version);
989*4882a593Smuzhiyun break;
990*4882a593Smuzhiyun default:
991*4882a593Smuzhiyun dprintk("%s: unsupported DS version %d\n", __func__, version);
992*4882a593Smuzhiyun err = -EPROTONOSUPPORT;
993*4882a593Smuzhiyun }
994*4882a593Smuzhiyun
995*4882a593Smuzhiyun connect_done:
996*4882a593Smuzhiyun nfs4_clear_ds_conn_bit(ds);
997*4882a593Smuzhiyun out:
998*4882a593Smuzhiyun /*
999*4882a593Smuzhiyun * At this point the ds->ds_clp should be ready, but it might have
1000*4882a593Smuzhiyun * hit an error.
1001*4882a593Smuzhiyun */
1002*4882a593Smuzhiyun if (!err) {
1003*4882a593Smuzhiyun if (!ds->ds_clp || !nfs_client_init_is_complete(ds->ds_clp)) {
1004*4882a593Smuzhiyun WARN_ON_ONCE(ds->ds_clp ||
1005*4882a593Smuzhiyun !nfs4_test_deviceid_unavailable(devid));
1006*4882a593Smuzhiyun return -EINVAL;
1007*4882a593Smuzhiyun }
1008*4882a593Smuzhiyun err = nfs_client_init_status(ds->ds_clp);
1009*4882a593Smuzhiyun }
1010*4882a593Smuzhiyun
1011*4882a593Smuzhiyun return err;
1012*4882a593Smuzhiyun }
1013*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_connect);
1014*4882a593Smuzhiyun
1015*4882a593Smuzhiyun /*
1016*4882a593Smuzhiyun * Currently only supports ipv4, ipv6 and one multi-path address.
1017*4882a593Smuzhiyun */
1018*4882a593Smuzhiyun struct nfs4_pnfs_ds_addr *
nfs4_decode_mp_ds_addr(struct net * net,struct xdr_stream * xdr,gfp_t gfp_flags)1019*4882a593Smuzhiyun nfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags)
1020*4882a593Smuzhiyun {
1021*4882a593Smuzhiyun struct nfs4_pnfs_ds_addr *da = NULL;
1022*4882a593Smuzhiyun char *buf, *portstr;
1023*4882a593Smuzhiyun __be16 port;
1024*4882a593Smuzhiyun int nlen, rlen;
1025*4882a593Smuzhiyun int tmp[2];
1026*4882a593Smuzhiyun __be32 *p;
1027*4882a593Smuzhiyun char *netid, *match_netid;
1028*4882a593Smuzhiyun size_t len, match_netid_len;
1029*4882a593Smuzhiyun char *startsep = "";
1030*4882a593Smuzhiyun char *endsep = "";
1031*4882a593Smuzhiyun
1032*4882a593Smuzhiyun
1033*4882a593Smuzhiyun /* r_netid */
1034*4882a593Smuzhiyun p = xdr_inline_decode(xdr, 4);
1035*4882a593Smuzhiyun if (unlikely(!p))
1036*4882a593Smuzhiyun goto out_err;
1037*4882a593Smuzhiyun nlen = be32_to_cpup(p++);
1038*4882a593Smuzhiyun
1039*4882a593Smuzhiyun p = xdr_inline_decode(xdr, nlen);
1040*4882a593Smuzhiyun if (unlikely(!p))
1041*4882a593Smuzhiyun goto out_err;
1042*4882a593Smuzhiyun
1043*4882a593Smuzhiyun netid = kmalloc(nlen+1, gfp_flags);
1044*4882a593Smuzhiyun if (unlikely(!netid))
1045*4882a593Smuzhiyun goto out_err;
1046*4882a593Smuzhiyun
1047*4882a593Smuzhiyun netid[nlen] = '\0';
1048*4882a593Smuzhiyun memcpy(netid, p, nlen);
1049*4882a593Smuzhiyun
1050*4882a593Smuzhiyun /* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */
1051*4882a593Smuzhiyun p = xdr_inline_decode(xdr, 4);
1052*4882a593Smuzhiyun if (unlikely(!p))
1053*4882a593Smuzhiyun goto out_free_netid;
1054*4882a593Smuzhiyun rlen = be32_to_cpup(p);
1055*4882a593Smuzhiyun
1056*4882a593Smuzhiyun p = xdr_inline_decode(xdr, rlen);
1057*4882a593Smuzhiyun if (unlikely(!p))
1058*4882a593Smuzhiyun goto out_free_netid;
1059*4882a593Smuzhiyun
1060*4882a593Smuzhiyun /* port is ".ABC.DEF", 8 chars max */
1061*4882a593Smuzhiyun if (rlen > INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN + 8) {
1062*4882a593Smuzhiyun dprintk("%s: Invalid address, length %d\n", __func__,
1063*4882a593Smuzhiyun rlen);
1064*4882a593Smuzhiyun goto out_free_netid;
1065*4882a593Smuzhiyun }
1066*4882a593Smuzhiyun buf = kmalloc(rlen + 1, gfp_flags);
1067*4882a593Smuzhiyun if (!buf) {
1068*4882a593Smuzhiyun dprintk("%s: Not enough memory\n", __func__);
1069*4882a593Smuzhiyun goto out_free_netid;
1070*4882a593Smuzhiyun }
1071*4882a593Smuzhiyun buf[rlen] = '\0';
1072*4882a593Smuzhiyun memcpy(buf, p, rlen);
1073*4882a593Smuzhiyun
1074*4882a593Smuzhiyun /* replace port '.' with '-' */
1075*4882a593Smuzhiyun portstr = strrchr(buf, '.');
1076*4882a593Smuzhiyun if (!portstr) {
1077*4882a593Smuzhiyun dprintk("%s: Failed finding expected dot in port\n",
1078*4882a593Smuzhiyun __func__);
1079*4882a593Smuzhiyun goto out_free_buf;
1080*4882a593Smuzhiyun }
1081*4882a593Smuzhiyun *portstr = '-';
1082*4882a593Smuzhiyun
1083*4882a593Smuzhiyun /* find '.' between address and port */
1084*4882a593Smuzhiyun portstr = strrchr(buf, '.');
1085*4882a593Smuzhiyun if (!portstr) {
1086*4882a593Smuzhiyun dprintk("%s: Failed finding expected dot between address and "
1087*4882a593Smuzhiyun "port\n", __func__);
1088*4882a593Smuzhiyun goto out_free_buf;
1089*4882a593Smuzhiyun }
1090*4882a593Smuzhiyun *portstr = '\0';
1091*4882a593Smuzhiyun
1092*4882a593Smuzhiyun da = kzalloc(sizeof(*da), gfp_flags);
1093*4882a593Smuzhiyun if (unlikely(!da))
1094*4882a593Smuzhiyun goto out_free_buf;
1095*4882a593Smuzhiyun
1096*4882a593Smuzhiyun INIT_LIST_HEAD(&da->da_node);
1097*4882a593Smuzhiyun
1098*4882a593Smuzhiyun if (!rpc_pton(net, buf, portstr-buf, (struct sockaddr *)&da->da_addr,
1099*4882a593Smuzhiyun sizeof(da->da_addr))) {
1100*4882a593Smuzhiyun dprintk("%s: error parsing address %s\n", __func__, buf);
1101*4882a593Smuzhiyun goto out_free_da;
1102*4882a593Smuzhiyun }
1103*4882a593Smuzhiyun
1104*4882a593Smuzhiyun portstr++;
1105*4882a593Smuzhiyun sscanf(portstr, "%d-%d", &tmp[0], &tmp[1]);
1106*4882a593Smuzhiyun port = htons((tmp[0] << 8) | (tmp[1]));
1107*4882a593Smuzhiyun
1108*4882a593Smuzhiyun switch (da->da_addr.ss_family) {
1109*4882a593Smuzhiyun case AF_INET:
1110*4882a593Smuzhiyun ((struct sockaddr_in *)&da->da_addr)->sin_port = port;
1111*4882a593Smuzhiyun da->da_addrlen = sizeof(struct sockaddr_in);
1112*4882a593Smuzhiyun match_netid = "tcp";
1113*4882a593Smuzhiyun match_netid_len = 3;
1114*4882a593Smuzhiyun break;
1115*4882a593Smuzhiyun
1116*4882a593Smuzhiyun case AF_INET6:
1117*4882a593Smuzhiyun ((struct sockaddr_in6 *)&da->da_addr)->sin6_port = port;
1118*4882a593Smuzhiyun da->da_addrlen = sizeof(struct sockaddr_in6);
1119*4882a593Smuzhiyun match_netid = "tcp6";
1120*4882a593Smuzhiyun match_netid_len = 4;
1121*4882a593Smuzhiyun startsep = "[";
1122*4882a593Smuzhiyun endsep = "]";
1123*4882a593Smuzhiyun break;
1124*4882a593Smuzhiyun
1125*4882a593Smuzhiyun default:
1126*4882a593Smuzhiyun dprintk("%s: unsupported address family: %u\n",
1127*4882a593Smuzhiyun __func__, da->da_addr.ss_family);
1128*4882a593Smuzhiyun goto out_free_da;
1129*4882a593Smuzhiyun }
1130*4882a593Smuzhiyun
1131*4882a593Smuzhiyun if (nlen != match_netid_len || strncmp(netid, match_netid, nlen)) {
1132*4882a593Smuzhiyun dprintk("%s: ERROR: r_netid \"%s\" != \"%s\"\n",
1133*4882a593Smuzhiyun __func__, netid, match_netid);
1134*4882a593Smuzhiyun goto out_free_da;
1135*4882a593Smuzhiyun }
1136*4882a593Smuzhiyun
1137*4882a593Smuzhiyun /* save human readable address */
1138*4882a593Smuzhiyun len = strlen(startsep) + strlen(buf) + strlen(endsep) + 7;
1139*4882a593Smuzhiyun da->da_remotestr = kzalloc(len, gfp_flags);
1140*4882a593Smuzhiyun
1141*4882a593Smuzhiyun /* NULL is ok, only used for dprintk */
1142*4882a593Smuzhiyun if (da->da_remotestr)
1143*4882a593Smuzhiyun snprintf(da->da_remotestr, len, "%s%s%s:%u", startsep,
1144*4882a593Smuzhiyun buf, endsep, ntohs(port));
1145*4882a593Smuzhiyun
1146*4882a593Smuzhiyun dprintk("%s: Parsed DS addr %s\n", __func__, da->da_remotestr);
1147*4882a593Smuzhiyun kfree(buf);
1148*4882a593Smuzhiyun kfree(netid);
1149*4882a593Smuzhiyun return da;
1150*4882a593Smuzhiyun
1151*4882a593Smuzhiyun out_free_da:
1152*4882a593Smuzhiyun kfree(da);
1153*4882a593Smuzhiyun out_free_buf:
1154*4882a593Smuzhiyun dprintk("%s: Error parsing DS addr: %s\n", __func__, buf);
1155*4882a593Smuzhiyun kfree(buf);
1156*4882a593Smuzhiyun out_free_netid:
1157*4882a593Smuzhiyun kfree(netid);
1158*4882a593Smuzhiyun out_err:
1159*4882a593Smuzhiyun return NULL;
1160*4882a593Smuzhiyun }
1161*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(nfs4_decode_mp_ds_addr);
1162*4882a593Smuzhiyun
1163*4882a593Smuzhiyun void
pnfs_layout_mark_request_commit(struct nfs_page * req,struct pnfs_layout_segment * lseg,struct nfs_commit_info * cinfo,u32 ds_commit_idx)1164*4882a593Smuzhiyun pnfs_layout_mark_request_commit(struct nfs_page *req,
1165*4882a593Smuzhiyun struct pnfs_layout_segment *lseg,
1166*4882a593Smuzhiyun struct nfs_commit_info *cinfo,
1167*4882a593Smuzhiyun u32 ds_commit_idx)
1168*4882a593Smuzhiyun {
1169*4882a593Smuzhiyun struct list_head *list;
1170*4882a593Smuzhiyun struct pnfs_commit_array *array;
1171*4882a593Smuzhiyun struct pnfs_commit_bucket *bucket;
1172*4882a593Smuzhiyun
1173*4882a593Smuzhiyun mutex_lock(&NFS_I(cinfo->inode)->commit_mutex);
1174*4882a593Smuzhiyun array = pnfs_lookup_commit_array(cinfo->ds, lseg);
1175*4882a593Smuzhiyun if (!array || !pnfs_is_valid_lseg(lseg))
1176*4882a593Smuzhiyun goto out_resched;
1177*4882a593Smuzhiyun bucket = &array->buckets[ds_commit_idx];
1178*4882a593Smuzhiyun list = &bucket->written;
1179*4882a593Smuzhiyun /* Non-empty buckets hold a reference on the lseg. That ref
1180*4882a593Smuzhiyun * is normally transferred to the COMMIT call and released
1181*4882a593Smuzhiyun * there. It could also be released if the last req is pulled
1182*4882a593Smuzhiyun * off due to a rewrite, in which case it will be done in
1183*4882a593Smuzhiyun * pnfs_common_clear_request_commit
1184*4882a593Smuzhiyun */
1185*4882a593Smuzhiyun if (!bucket->lseg)
1186*4882a593Smuzhiyun bucket->lseg = pnfs_get_lseg(lseg);
1187*4882a593Smuzhiyun set_bit(PG_COMMIT_TO_DS, &req->wb_flags);
1188*4882a593Smuzhiyun cinfo->ds->nwritten++;
1189*4882a593Smuzhiyun
1190*4882a593Smuzhiyun nfs_request_add_commit_list_locked(req, list, cinfo);
1191*4882a593Smuzhiyun mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
1192*4882a593Smuzhiyun nfs_mark_page_unstable(req->wb_page, cinfo);
1193*4882a593Smuzhiyun return;
1194*4882a593Smuzhiyun out_resched:
1195*4882a593Smuzhiyun mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex);
1196*4882a593Smuzhiyun cinfo->completion_ops->resched_write(cinfo, req);
1197*4882a593Smuzhiyun }
1198*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_layout_mark_request_commit);
1199*4882a593Smuzhiyun
1200*4882a593Smuzhiyun int
pnfs_nfs_generic_sync(struct inode * inode,bool datasync)1201*4882a593Smuzhiyun pnfs_nfs_generic_sync(struct inode *inode, bool datasync)
1202*4882a593Smuzhiyun {
1203*4882a593Smuzhiyun int ret;
1204*4882a593Smuzhiyun
1205*4882a593Smuzhiyun if (!pnfs_layoutcommit_outstanding(inode))
1206*4882a593Smuzhiyun return 0;
1207*4882a593Smuzhiyun ret = nfs_commit_inode(inode, FLUSH_SYNC);
1208*4882a593Smuzhiyun if (ret < 0)
1209*4882a593Smuzhiyun return ret;
1210*4882a593Smuzhiyun if (datasync)
1211*4882a593Smuzhiyun return 0;
1212*4882a593Smuzhiyun return pnfs_layoutcommit_inode(inode, true);
1213*4882a593Smuzhiyun }
1214*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pnfs_nfs_generic_sync);
1215*4882a593Smuzhiyun
1216