blob: ad211a4e1874fa58aebf0aecbb69a765d69fea31 [file] [log] [blame]
Tom Haynesf54bcf22014-12-11 15:34:59 -05001/*
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>
Peng Tao6b7f3cf2014-05-29 21:06:59 +080012#include <linux/sunrpc/addr.h>
Tom Haynesf54bcf22014-12-11 15:34:59 -050013
Peng Tao7405f9e2014-05-29 21:06:58 +080014#include "nfs4session.h"
Tom Haynesf54bcf22014-12-11 15:34:59 -050015#include "internal.h"
16#include "pnfs.h"
17
Peng Tao875ae062014-05-29 21:06:57 +080018#define NFSDBG_FACILITY NFSDBG_PNFS
19
Tom Haynesf54bcf22014-12-11 15:34:59 -050020static void pnfs_generic_fenceme(struct inode *inode,
21 struct pnfs_layout_hdr *lo)
22{
23 if (!test_and_clear_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
24 return;
25 pnfs_return_layout(inode);
26}
27
28void pnfs_generic_rw_release(void *data)
29{
30 struct nfs_pgio_header *hdr = data;
31 struct pnfs_layout_hdr *lo = hdr->lseg->pls_layout;
32
33 pnfs_generic_fenceme(lo->plh_inode, lo);
34 nfs_put_client(hdr->ds_clp);
35 hdr->mds_ops->rpc_release(data);
36}
37EXPORT_SYMBOL_GPL(pnfs_generic_rw_release);
38
39/* Fake up some data that will cause nfs_commit_release to retry the writes. */
40void pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data *data)
41{
42 struct nfs_page *first = nfs_list_entry(data->pages.next);
43
44 data->task.tk_status = 0;
45 memcpy(&data->verf.verifier, &first->wb_verf,
46 sizeof(data->verf.verifier));
47 data->verf.verifier.data[0]++; /* ensure verifier mismatch */
48}
49EXPORT_SYMBOL_GPL(pnfs_generic_prepare_to_resend_writes);
50
51void pnfs_generic_write_commit_done(struct rpc_task *task, void *data)
52{
53 struct nfs_commit_data *wdata = data;
54
55 /* Note this may cause RPC to be resent */
56 wdata->mds_ops->rpc_call_done(task, data);
57}
58EXPORT_SYMBOL_GPL(pnfs_generic_write_commit_done);
59
60void pnfs_generic_commit_release(void *calldata)
61{
62 struct nfs_commit_data *data = calldata;
63
64 data->completion_ops->completion(data);
65 pnfs_put_lseg(data->lseg);
66 nfs_put_client(data->ds_clp);
67 nfs_commitdata_release(data);
68}
69EXPORT_SYMBOL_GPL(pnfs_generic_commit_release);
70
71/* The generic layer is about to remove the req from the commit list.
72 * If this will make the bucket empty, it will need to put the lseg reference.
Tom Haynes085d1e32014-12-11 13:04:55 -050073 * Note this must be called holding the inode (/cinfo) lock
Tom Haynesf54bcf22014-12-11 15:34:59 -050074 */
75void
76pnfs_generic_clear_request_commit(struct nfs_page *req,
77 struct nfs_commit_info *cinfo)
78{
79 struct pnfs_layout_segment *freeme = NULL;
80
81 if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags))
82 goto out;
83 cinfo->ds->nwritten--;
84 if (list_is_singular(&req->wb_list)) {
85 struct pnfs_commit_bucket *bucket;
86
87 bucket = list_first_entry(&req->wb_list,
88 struct pnfs_commit_bucket,
89 written);
90 freeme = bucket->wlseg;
91 bucket->wlseg = NULL;
92 }
93out:
94 nfs_request_remove_commit_list(req, cinfo);
95 pnfs_put_lseg_locked(freeme);
96}
97EXPORT_SYMBOL_GPL(pnfs_generic_clear_request_commit);
98
99static int
100pnfs_generic_transfer_commit_list(struct list_head *src, struct list_head *dst,
101 struct nfs_commit_info *cinfo, int max)
102{
103 struct nfs_page *req, *tmp;
104 int ret = 0;
105
106 list_for_each_entry_safe(req, tmp, src, wb_list) {
107 if (!nfs_lock_request(req))
108 continue;
109 kref_get(&req->wb_kref);
110 if (cond_resched_lock(cinfo->lock))
111 list_safe_reset_next(req, tmp, wb_list);
112 nfs_request_remove_commit_list(req, cinfo);
113 clear_bit(PG_COMMIT_TO_DS, &req->wb_flags);
114 nfs_list_add_request(req, dst);
115 ret++;
116 if ((ret == max) && !cinfo->dreq)
117 break;
118 }
119 return ret;
120}
121
Tom Haynesf54bcf22014-12-11 15:34:59 -0500122static int
123pnfs_generic_scan_ds_commit_list(struct pnfs_commit_bucket *bucket,
124 struct nfs_commit_info *cinfo,
125 int max)
126{
127 struct list_head *src = &bucket->written;
128 struct list_head *dst = &bucket->committing;
129 int ret;
130
Tom Haynes085d1e32014-12-11 13:04:55 -0500131 lockdep_assert_held(cinfo->lock);
Tom Haynesf54bcf22014-12-11 15:34:59 -0500132 ret = pnfs_generic_transfer_commit_list(src, dst, cinfo, max);
133 if (ret) {
134 cinfo->ds->nwritten -= ret;
135 cinfo->ds->ncommitting += ret;
136 bucket->clseg = bucket->wlseg;
137 if (list_empty(src))
138 bucket->wlseg = NULL;
139 else
140 pnfs_get_lseg(bucket->clseg);
141 }
142 return ret;
143}
144
Tom Haynes085d1e32014-12-11 13:04:55 -0500145/* Move reqs from written to committing lists, returning count
146 * of number moved.
Tom Haynesf54bcf22014-12-11 15:34:59 -0500147 */
148int pnfs_generic_scan_commit_lists(struct nfs_commit_info *cinfo,
149 int max)
150{
151 int i, rv = 0, cnt;
152
Tom Haynes085d1e32014-12-11 13:04:55 -0500153 lockdep_assert_held(cinfo->lock);
Tom Haynesf54bcf22014-12-11 15:34:59 -0500154 for (i = 0; i < cinfo->ds->nbuckets && max != 0; i++) {
155 cnt = pnfs_generic_scan_ds_commit_list(&cinfo->ds->buckets[i],
156 cinfo, max);
157 max -= cnt;
158 rv += cnt;
159 }
160 return rv;
161}
162EXPORT_SYMBOL_GPL(pnfs_generic_scan_commit_lists);
163
Tom Haynes085d1e32014-12-11 13:04:55 -0500164/* Pull everything off the committing lists and dump into @dst. */
Tom Haynesf54bcf22014-12-11 15:34:59 -0500165void pnfs_generic_recover_commit_reqs(struct list_head *dst,
166 struct nfs_commit_info *cinfo)
167{
168 struct pnfs_commit_bucket *b;
169 struct pnfs_layout_segment *freeme;
170 int i;
171
Tom Haynes085d1e32014-12-11 13:04:55 -0500172 lockdep_assert_held(cinfo->lock);
Tom Haynesf54bcf22014-12-11 15:34:59 -0500173restart:
Tom Haynesf54bcf22014-12-11 15:34:59 -0500174 for (i = 0, b = cinfo->ds->buckets; i < cinfo->ds->nbuckets; i++, b++) {
175 if (pnfs_generic_transfer_commit_list(&b->written, dst,
176 cinfo, 0)) {
177 freeme = b->wlseg;
178 b->wlseg = NULL;
179 spin_unlock(cinfo->lock);
180 pnfs_put_lseg(freeme);
Tom Haynes085d1e32014-12-11 13:04:55 -0500181 spin_lock(cinfo->lock);
Tom Haynesf54bcf22014-12-11 15:34:59 -0500182 goto restart;
183 }
184 }
185 cinfo->ds->nwritten = 0;
Tom Haynesf54bcf22014-12-11 15:34:59 -0500186}
187EXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs);
188
189static void pnfs_generic_retry_commit(struct nfs_commit_info *cinfo, int idx)
190{
191 struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
192 struct pnfs_commit_bucket *bucket;
193 struct pnfs_layout_segment *freeme;
194 int i;
195
196 for (i = idx; i < fl_cinfo->nbuckets; i++) {
197 bucket = &fl_cinfo->buckets[i];
198 if (list_empty(&bucket->committing))
199 continue;
200 nfs_retry_commit(&bucket->committing, bucket->clseg, cinfo);
201 spin_lock(cinfo->lock);
202 freeme = bucket->clseg;
203 bucket->clseg = NULL;
204 spin_unlock(cinfo->lock);
205 pnfs_put_lseg(freeme);
206 }
207}
208
209static unsigned int
210pnfs_generic_alloc_ds_commits(struct nfs_commit_info *cinfo,
211 struct list_head *list)
212{
213 struct pnfs_ds_commit_info *fl_cinfo;
214 struct pnfs_commit_bucket *bucket;
215 struct nfs_commit_data *data;
216 int i;
217 unsigned int nreq = 0;
218
219 fl_cinfo = cinfo->ds;
220 bucket = fl_cinfo->buckets;
221 for (i = 0; i < fl_cinfo->nbuckets; i++, bucket++) {
222 if (list_empty(&bucket->committing))
223 continue;
224 data = nfs_commitdata_alloc();
225 if (!data)
226 break;
227 data->ds_commit_index = i;
228 spin_lock(cinfo->lock);
229 data->lseg = bucket->clseg;
230 bucket->clseg = NULL;
231 spin_unlock(cinfo->lock);
232 list_add(&data->pages, list);
233 nreq++;
234 }
235
236 /* Clean up on error */
237 pnfs_generic_retry_commit(cinfo, i);
238 return nreq;
239}
240
241/* This follows nfs_commit_list pretty closely */
242int
243pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
244 int how, struct nfs_commit_info *cinfo,
245 int (*initiate_commit)(struct nfs_commit_data *data,
246 int how))
247{
248 struct nfs_commit_data *data, *tmp;
249 LIST_HEAD(list);
250 unsigned int nreq = 0;
251
252 if (!list_empty(mds_pages)) {
253 data = nfs_commitdata_alloc();
254 if (data != NULL) {
255 data->lseg = NULL;
256 list_add(&data->pages, &list);
257 nreq++;
258 } else {
259 nfs_retry_commit(mds_pages, NULL, cinfo);
260 pnfs_generic_retry_commit(cinfo, 0);
261 cinfo->completion_ops->error_cleanup(NFS_I(inode));
262 return -ENOMEM;
263 }
264 }
265
266 nreq += pnfs_generic_alloc_ds_commits(cinfo, &list);
267
268 if (nreq == 0) {
269 cinfo->completion_ops->error_cleanup(NFS_I(inode));
270 goto out;
271 }
272
273 atomic_add(nreq, &cinfo->mds->rpcs_out);
274
275 list_for_each_entry_safe(data, tmp, &list, pages) {
276 list_del_init(&data->pages);
277 if (!data->lseg) {
278 nfs_init_commit(data, mds_pages, NULL, cinfo);
279 nfs_initiate_commit(NFS_CLIENT(inode), data,
280 data->mds_ops, how, 0);
281 } else {
282 struct pnfs_commit_bucket *buckets;
283
284 buckets = cinfo->ds->buckets;
285 nfs_init_commit(data,
286 &buckets[data->ds_commit_index].committing,
287 data->lseg,
288 cinfo);
289 initiate_commit(data, how);
290 }
291 }
292out:
293 cinfo->ds->ncommitting = 0;
294 return PNFS_ATTEMPTED;
295}
296EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist);
Peng Tao875ae062014-05-29 21:06:57 +0800297
298/*
299 * Data server cache
300 *
301 * Data servers can be mapped to different device ids.
302 * nfs4_pnfs_ds reference counting
303 * - set to 1 on allocation
304 * - incremented when a device id maps a data server already in the cache.
305 * - decremented when deviceid is removed from the cache.
306 */
307static DEFINE_SPINLOCK(nfs4_ds_cache_lock);
308static LIST_HEAD(nfs4_data_server_cache);
309
310/* Debug routines */
311static void
312print_ds(struct nfs4_pnfs_ds *ds)
313{
314 if (ds == NULL) {
315 printk(KERN_WARNING "%s NULL device\n", __func__);
316 return;
317 }
318 printk(KERN_WARNING " ds %s\n"
319 " ref count %d\n"
320 " client %p\n"
321 " cl_exchange_flags %x\n",
322 ds->ds_remotestr,
323 atomic_read(&ds->ds_count), ds->ds_clp,
324 ds->ds_clp ? ds->ds_clp->cl_exchange_flags : 0);
325}
326
327static bool
328same_sockaddr(struct sockaddr *addr1, struct sockaddr *addr2)
329{
330 struct sockaddr_in *a, *b;
331 struct sockaddr_in6 *a6, *b6;
332
333 if (addr1->sa_family != addr2->sa_family)
334 return false;
335
336 switch (addr1->sa_family) {
337 case AF_INET:
338 a = (struct sockaddr_in *)addr1;
339 b = (struct sockaddr_in *)addr2;
340
341 if (a->sin_addr.s_addr == b->sin_addr.s_addr &&
342 a->sin_port == b->sin_port)
343 return true;
344 break;
345
346 case AF_INET6:
347 a6 = (struct sockaddr_in6 *)addr1;
348 b6 = (struct sockaddr_in6 *)addr2;
349
350 /* LINKLOCAL addresses must have matching scope_id */
351 if (ipv6_addr_src_scope(&a6->sin6_addr) ==
352 IPV6_ADDR_SCOPE_LINKLOCAL &&
353 a6->sin6_scope_id != b6->sin6_scope_id)
354 return false;
355
356 if (ipv6_addr_equal(&a6->sin6_addr, &b6->sin6_addr) &&
357 a6->sin6_port == b6->sin6_port)
358 return true;
359 break;
360
361 default:
362 dprintk("%s: unhandled address family: %u\n",
363 __func__, addr1->sa_family);
364 return false;
365 }
366
367 return false;
368}
369
370static bool
371_same_data_server_addrs_locked(const struct list_head *dsaddrs1,
372 const struct list_head *dsaddrs2)
373{
374 struct nfs4_pnfs_ds_addr *da1, *da2;
375
376 /* step through both lists, comparing as we go */
377 for (da1 = list_first_entry(dsaddrs1, typeof(*da1), da_node),
378 da2 = list_first_entry(dsaddrs2, typeof(*da2), da_node);
379 da1 != NULL && da2 != NULL;
380 da1 = list_entry(da1->da_node.next, typeof(*da1), da_node),
381 da2 = list_entry(da2->da_node.next, typeof(*da2), da_node)) {
382 if (!same_sockaddr((struct sockaddr *)&da1->da_addr,
383 (struct sockaddr *)&da2->da_addr))
384 return false;
385 }
386 if (da1 == NULL && da2 == NULL)
387 return true;
388
389 return false;
390}
391
392/*
393 * Lookup DS by addresses. nfs4_ds_cache_lock is held
394 */
395static struct nfs4_pnfs_ds *
396_data_server_lookup_locked(const struct list_head *dsaddrs)
397{
398 struct nfs4_pnfs_ds *ds;
399
400 list_for_each_entry(ds, &nfs4_data_server_cache, ds_node)
401 if (_same_data_server_addrs_locked(&ds->ds_addrs, dsaddrs))
402 return ds;
403 return NULL;
404}
405
406static void destroy_ds(struct nfs4_pnfs_ds *ds)
407{
408 struct nfs4_pnfs_ds_addr *da;
409
410 dprintk("--> %s\n", __func__);
411 ifdebug(FACILITY)
412 print_ds(ds);
413
414 nfs_put_client(ds->ds_clp);
415
416 while (!list_empty(&ds->ds_addrs)) {
417 da = list_first_entry(&ds->ds_addrs,
418 struct nfs4_pnfs_ds_addr,
419 da_node);
420 list_del_init(&da->da_node);
421 kfree(da->da_remotestr);
422 kfree(da);
423 }
424
425 kfree(ds->ds_remotestr);
426 kfree(ds);
427}
428
429void nfs4_pnfs_ds_put(struct nfs4_pnfs_ds *ds)
430{
431 if (atomic_dec_and_lock(&ds->ds_count,
432 &nfs4_ds_cache_lock)) {
433 list_del_init(&ds->ds_node);
434 spin_unlock(&nfs4_ds_cache_lock);
435 destroy_ds(ds);
436 }
437}
438EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_put);
439
440/*
441 * Create a string with a human readable address and port to avoid
442 * complicated setup around many dprinks.
443 */
444static char *
445nfs4_pnfs_remotestr(struct list_head *dsaddrs, gfp_t gfp_flags)
446{
447 struct nfs4_pnfs_ds_addr *da;
448 char *remotestr;
449 size_t len;
450 char *p;
451
452 len = 3; /* '{', '}' and eol */
453 list_for_each_entry(da, dsaddrs, da_node) {
454 len += strlen(da->da_remotestr) + 1; /* string plus comma */
455 }
456
457 remotestr = kzalloc(len, gfp_flags);
458 if (!remotestr)
459 return NULL;
460
461 p = remotestr;
462 *(p++) = '{';
463 len--;
464 list_for_each_entry(da, dsaddrs, da_node) {
465 size_t ll = strlen(da->da_remotestr);
466
467 if (ll > len)
468 goto out_err;
469
470 memcpy(p, da->da_remotestr, ll);
471 p += ll;
472 len -= ll;
473
474 if (len < 1)
475 goto out_err;
476 (*p++) = ',';
477 len--;
478 }
479 if (len < 2)
480 goto out_err;
481 *(p++) = '}';
482 *p = '\0';
483 return remotestr;
484out_err:
485 kfree(remotestr);
486 return NULL;
487}
488
489/*
490 * Given a list of multipath struct nfs4_pnfs_ds_addr, add it to ds cache if
491 * uncached and return cached struct nfs4_pnfs_ds.
492 */
493struct nfs4_pnfs_ds *
494nfs4_pnfs_ds_add(struct list_head *dsaddrs, gfp_t gfp_flags)
495{
496 struct nfs4_pnfs_ds *tmp_ds, *ds = NULL;
497 char *remotestr;
498
499 if (list_empty(dsaddrs)) {
500 dprintk("%s: no addresses defined\n", __func__);
501 goto out;
502 }
503
504 ds = kzalloc(sizeof(*ds), gfp_flags);
505 if (!ds)
506 goto out;
507
508 /* this is only used for debugging, so it's ok if its NULL */
509 remotestr = nfs4_pnfs_remotestr(dsaddrs, gfp_flags);
510
511 spin_lock(&nfs4_ds_cache_lock);
512 tmp_ds = _data_server_lookup_locked(dsaddrs);
513 if (tmp_ds == NULL) {
514 INIT_LIST_HEAD(&ds->ds_addrs);
515 list_splice_init(dsaddrs, &ds->ds_addrs);
516 ds->ds_remotestr = remotestr;
517 atomic_set(&ds->ds_count, 1);
518 INIT_LIST_HEAD(&ds->ds_node);
519 ds->ds_clp = NULL;
520 list_add(&ds->ds_node, &nfs4_data_server_cache);
521 dprintk("%s add new data server %s\n", __func__,
522 ds->ds_remotestr);
523 } else {
524 kfree(remotestr);
525 kfree(ds);
526 atomic_inc(&tmp_ds->ds_count);
527 dprintk("%s data server %s found, inc'ed ds_count to %d\n",
528 __func__, tmp_ds->ds_remotestr,
529 atomic_read(&tmp_ds->ds_count));
530 ds = tmp_ds;
531 }
532 spin_unlock(&nfs4_ds_cache_lock);
533out:
534 return ds;
535}
536EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_add);
Peng Tao6b7f3cf2014-05-29 21:06:59 +0800537
Peng Tao7405f9e2014-05-29 21:06:58 +0800538static void nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds)
539{
540 might_sleep();
541 wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING,
542 TASK_KILLABLE);
543}
544
545static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds)
546{
547 smp_mb__before_atomic();
548 clear_bit(NFS4DS_CONNECTING, &ds->ds_state);
549 smp_mb__after_atomic();
550 wake_up_bit(&ds->ds_state, NFS4DS_CONNECTING);
551}
552
553static int _nfs4_pnfs_ds_connect(struct nfs_server *mds_srv,
554 struct nfs4_pnfs_ds *ds,
555 unsigned int timeo,
Peng Tao064172f2014-05-29 21:07:00 +0800556 unsigned int retrans,
Peng Tao30626f92014-05-30 18:15:58 +0800557 u32 minor_version,
Peng Tao064172f2014-05-29 21:07:00 +0800558 rpc_authflavor_t au_flavor)
Peng Tao7405f9e2014-05-29 21:06:58 +0800559{
560 struct nfs_client *clp = ERR_PTR(-EIO);
561 struct nfs4_pnfs_ds_addr *da;
562 int status = 0;
563
564 dprintk("--> %s DS %s au_flavor %d\n", __func__, ds->ds_remotestr,
565 mds_srv->nfs_client->cl_rpcclient->cl_auth->au_flavor);
566
567 list_for_each_entry(da, &ds->ds_addrs, da_node) {
568 dprintk("%s: DS %s: trying address %s\n",
569 __func__, ds->ds_remotestr, da->da_remotestr);
570
571 clp = nfs4_set_ds_client(mds_srv->nfs_client,
572 (struct sockaddr *)&da->da_addr,
573 da->da_addrlen, IPPROTO_TCP,
Peng Tao30626f92014-05-30 18:15:58 +0800574 timeo, retrans, minor_version,
575 au_flavor);
Peng Tao7405f9e2014-05-29 21:06:58 +0800576 if (!IS_ERR(clp))
577 break;
578 }
579
580 if (IS_ERR(clp)) {
581 status = PTR_ERR(clp);
582 goto out;
583 }
584
585 status = nfs4_init_ds_session(clp, mds_srv->nfs_client->cl_lease_time);
586 if (status)
587 goto out_put;
588
589 smp_wmb();
590 ds->ds_clp = clp;
591 dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr);
592out:
593 return status;
594out_put:
595 nfs_put_client(clp);
596 goto out;
597}
598
599/*
600 * Create an rpc connection to the nfs4_pnfs_ds data server.
601 * Currently only supports IPv4 and IPv6 addresses.
602 * If connection fails, make devid unavailable.
603 */
604void nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
605 struct nfs4_deviceid_node *devid, unsigned int timeo,
Peng Tao30626f92014-05-30 18:15:58 +0800606 unsigned int retrans, u32 version,
607 u32 minor_version, rpc_authflavor_t au_flavor)
Peng Tao7405f9e2014-05-29 21:06:58 +0800608{
609 if (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) == 0) {
610 int err = 0;
611
Peng Tao30626f92014-05-30 18:15:58 +0800612 err = _nfs4_pnfs_ds_connect(mds_srv, ds, timeo, retrans,
613 minor_version, au_flavor);
Peng Tao7405f9e2014-05-29 21:06:58 +0800614 if (err)
615 nfs4_mark_deviceid_unavailable(devid);
616 nfs4_clear_ds_conn_bit(ds);
617 } else {
618 nfs4_wait_ds_connect(ds);
619 }
620}
621EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_connect);
622
Peng Tao6b7f3cf2014-05-29 21:06:59 +0800623/*
624 * Currently only supports ipv4, ipv6 and one multi-path address.
625 */
626struct nfs4_pnfs_ds_addr *
627nfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags)
628{
629 struct nfs4_pnfs_ds_addr *da = NULL;
630 char *buf, *portstr;
631 __be16 port;
632 int nlen, rlen;
633 int tmp[2];
634 __be32 *p;
635 char *netid, *match_netid;
636 size_t len, match_netid_len;
637 char *startsep = "";
638 char *endsep = "";
639
640
641 /* r_netid */
642 p = xdr_inline_decode(xdr, 4);
643 if (unlikely(!p))
644 goto out_err;
645 nlen = be32_to_cpup(p++);
646
647 p = xdr_inline_decode(xdr, nlen);
648 if (unlikely(!p))
649 goto out_err;
650
651 netid = kmalloc(nlen+1, gfp_flags);
652 if (unlikely(!netid))
653 goto out_err;
654
655 netid[nlen] = '\0';
656 memcpy(netid, p, nlen);
657
658 /* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */
659 p = xdr_inline_decode(xdr, 4);
660 if (unlikely(!p))
661 goto out_free_netid;
662 rlen = be32_to_cpup(p);
663
664 p = xdr_inline_decode(xdr, rlen);
665 if (unlikely(!p))
666 goto out_free_netid;
667
668 /* port is ".ABC.DEF", 8 chars max */
669 if (rlen > INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN + 8) {
670 dprintk("%s: Invalid address, length %d\n", __func__,
671 rlen);
672 goto out_free_netid;
673 }
674 buf = kmalloc(rlen + 1, gfp_flags);
675 if (!buf) {
676 dprintk("%s: Not enough memory\n", __func__);
677 goto out_free_netid;
678 }
679 buf[rlen] = '\0';
680 memcpy(buf, p, rlen);
681
682 /* replace port '.' with '-' */
683 portstr = strrchr(buf, '.');
684 if (!portstr) {
685 dprintk("%s: Failed finding expected dot in port\n",
686 __func__);
687 goto out_free_buf;
688 }
689 *portstr = '-';
690
691 /* find '.' between address and port */
692 portstr = strrchr(buf, '.');
693 if (!portstr) {
694 dprintk("%s: Failed finding expected dot between address and "
695 "port\n", __func__);
696 goto out_free_buf;
697 }
698 *portstr = '\0';
699
700 da = kzalloc(sizeof(*da), gfp_flags);
701 if (unlikely(!da))
702 goto out_free_buf;
703
704 INIT_LIST_HEAD(&da->da_node);
705
706 if (!rpc_pton(net, buf, portstr-buf, (struct sockaddr *)&da->da_addr,
707 sizeof(da->da_addr))) {
708 dprintk("%s: error parsing address %s\n", __func__, buf);
709 goto out_free_da;
710 }
711
712 portstr++;
713 sscanf(portstr, "%d-%d", &tmp[0], &tmp[1]);
714 port = htons((tmp[0] << 8) | (tmp[1]));
715
716 switch (da->da_addr.ss_family) {
717 case AF_INET:
718 ((struct sockaddr_in *)&da->da_addr)->sin_port = port;
719 da->da_addrlen = sizeof(struct sockaddr_in);
720 match_netid = "tcp";
721 match_netid_len = 3;
722 break;
723
724 case AF_INET6:
725 ((struct sockaddr_in6 *)&da->da_addr)->sin6_port = port;
726 da->da_addrlen = sizeof(struct sockaddr_in6);
727 match_netid = "tcp6";
728 match_netid_len = 4;
729 startsep = "[";
730 endsep = "]";
731 break;
732
733 default:
734 dprintk("%s: unsupported address family: %u\n",
735 __func__, da->da_addr.ss_family);
736 goto out_free_da;
737 }
738
739 if (nlen != match_netid_len || strncmp(netid, match_netid, nlen)) {
740 dprintk("%s: ERROR: r_netid \"%s\" != \"%s\"\n",
741 __func__, netid, match_netid);
742 goto out_free_da;
743 }
744
745 /* save human readable address */
746 len = strlen(startsep) + strlen(buf) + strlen(endsep) + 7;
747 da->da_remotestr = kzalloc(len, gfp_flags);
748
749 /* NULL is ok, only used for dprintk */
750 if (da->da_remotestr)
751 snprintf(da->da_remotestr, len, "%s%s%s:%u", startsep,
752 buf, endsep, ntohs(port));
753
754 dprintk("%s: Parsed DS addr %s\n", __func__, da->da_remotestr);
755 kfree(buf);
756 kfree(netid);
757 return da;
758
759out_free_da:
760 kfree(da);
761out_free_buf:
762 dprintk("%s: Error parsing DS addr: %s\n", __func__, buf);
763 kfree(buf);
764out_free_netid:
765 kfree(netid);
766out_err:
767 return NULL;
768}
769EXPORT_SYMBOL_GPL(nfs4_decode_mp_ds_addr);