nfsv3: introduce nfs3_set_ds_client
[deliverable/linux.git] / fs / nfs / pnfs_nfs.c
CommitLineData
f54bcf2e
TH
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>
6b7f3cf9 12#include <linux/sunrpc/addr.h>
f54bcf2e 13
7405f9e1 14#include "nfs4session.h"
f54bcf2e
TH
15#include "internal.h"
16#include "pnfs.h"
17
875ae069
PT
18#define NFSDBG_FACILITY NFSDBG_PNFS
19
f54bcf2e
TH
20static 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.
085d1e33 73 * Note this must be called holding the inode (/cinfo) lock
f54bcf2e
TH
74 */
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
f54bcf2e
TH
122static 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
085d1e33 131 lockdep_assert_held(cinfo->lock);
f54bcf2e
TH
132 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
085d1e33
TH
145/* Move reqs from written to committing lists, returning count
146 * of number moved.
f54bcf2e
TH
147 */
148int pnfs_generic_scan_commit_lists(struct nfs_commit_info *cinfo,
149 int max)
150{
151 int i, rv = 0, cnt;
152
085d1e33 153 lockdep_assert_held(cinfo->lock);
f54bcf2e
TH
154 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
085d1e33 164/* Pull everything off the committing lists and dump into @dst. */
f54bcf2e
TH
165void 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
085d1e33 172 lockdep_assert_held(cinfo->lock);
f54bcf2e 173restart:
f54bcf2e
TH
174 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);
085d1e33 181 spin_lock(cinfo->lock);
f54bcf2e
TH
182 goto restart;
183 }
184 }
185 cinfo->ds->nwritten = 0;
f54bcf2e
TH
186}
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);
875ae069
PT
297
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);
6b7f3cf9 537
7405f9e1
PT
538static 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,
064172f3
PT
556 unsigned int retrans,
557 rpc_authflavor_t au_flavor)
7405f9e1
PT
558{
559 struct nfs_client *clp = ERR_PTR(-EIO);
560 struct nfs4_pnfs_ds_addr *da;
561 int status = 0;
562
563 dprintk("--> %s DS %s au_flavor %d\n", __func__, ds->ds_remotestr,
564 mds_srv->nfs_client->cl_rpcclient->cl_auth->au_flavor);
565
566 list_for_each_entry(da, &ds->ds_addrs, da_node) {
567 dprintk("%s: DS %s: trying address %s\n",
568 __func__, ds->ds_remotestr, da->da_remotestr);
569
570 clp = nfs4_set_ds_client(mds_srv->nfs_client,
571 (struct sockaddr *)&da->da_addr,
572 da->da_addrlen, IPPROTO_TCP,
064172f3 573 timeo, retrans, au_flavor);
7405f9e1
PT
574 if (!IS_ERR(clp))
575 break;
576 }
577
578 if (IS_ERR(clp)) {
579 status = PTR_ERR(clp);
580 goto out;
581 }
582
583 status = nfs4_init_ds_session(clp, mds_srv->nfs_client->cl_lease_time);
584 if (status)
585 goto out_put;
586
587 smp_wmb();
588 ds->ds_clp = clp;
589 dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr);
590out:
591 return status;
592out_put:
593 nfs_put_client(clp);
594 goto out;
595}
596
597/*
598 * Create an rpc connection to the nfs4_pnfs_ds data server.
599 * Currently only supports IPv4 and IPv6 addresses.
600 * If connection fails, make devid unavailable.
601 */
602void nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
603 struct nfs4_deviceid_node *devid, unsigned int timeo,
064172f3 604 unsigned int retrans, rpc_authflavor_t au_flavor)
7405f9e1
PT
605{
606 if (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) == 0) {
607 int err = 0;
608
064172f3
PT
609 err = _nfs4_pnfs_ds_connect(mds_srv, ds, timeo,
610 retrans, au_flavor);
7405f9e1
PT
611 if (err)
612 nfs4_mark_deviceid_unavailable(devid);
613 nfs4_clear_ds_conn_bit(ds);
614 } else {
615 nfs4_wait_ds_connect(ds);
616 }
617}
618EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_connect);
619
6b7f3cf9
PT
620/*
621 * Currently only supports ipv4, ipv6 and one multi-path address.
622 */
623struct nfs4_pnfs_ds_addr *
624nfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags)
625{
626 struct nfs4_pnfs_ds_addr *da = NULL;
627 char *buf, *portstr;
628 __be16 port;
629 int nlen, rlen;
630 int tmp[2];
631 __be32 *p;
632 char *netid, *match_netid;
633 size_t len, match_netid_len;
634 char *startsep = "";
635 char *endsep = "";
636
637
638 /* r_netid */
639 p = xdr_inline_decode(xdr, 4);
640 if (unlikely(!p))
641 goto out_err;
642 nlen = be32_to_cpup(p++);
643
644 p = xdr_inline_decode(xdr, nlen);
645 if (unlikely(!p))
646 goto out_err;
647
648 netid = kmalloc(nlen+1, gfp_flags);
649 if (unlikely(!netid))
650 goto out_err;
651
652 netid[nlen] = '\0';
653 memcpy(netid, p, nlen);
654
655 /* r_addr: ip/ip6addr with port in dec octets - see RFC 5665 */
656 p = xdr_inline_decode(xdr, 4);
657 if (unlikely(!p))
658 goto out_free_netid;
659 rlen = be32_to_cpup(p);
660
661 p = xdr_inline_decode(xdr, rlen);
662 if (unlikely(!p))
663 goto out_free_netid;
664
665 /* port is ".ABC.DEF", 8 chars max */
666 if (rlen > INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN + 8) {
667 dprintk("%s: Invalid address, length %d\n", __func__,
668 rlen);
669 goto out_free_netid;
670 }
671 buf = kmalloc(rlen + 1, gfp_flags);
672 if (!buf) {
673 dprintk("%s: Not enough memory\n", __func__);
674 goto out_free_netid;
675 }
676 buf[rlen] = '\0';
677 memcpy(buf, p, rlen);
678
679 /* replace port '.' with '-' */
680 portstr = strrchr(buf, '.');
681 if (!portstr) {
682 dprintk("%s: Failed finding expected dot in port\n",
683 __func__);
684 goto out_free_buf;
685 }
686 *portstr = '-';
687
688 /* find '.' between address and port */
689 portstr = strrchr(buf, '.');
690 if (!portstr) {
691 dprintk("%s: Failed finding expected dot between address and "
692 "port\n", __func__);
693 goto out_free_buf;
694 }
695 *portstr = '\0';
696
697 da = kzalloc(sizeof(*da), gfp_flags);
698 if (unlikely(!da))
699 goto out_free_buf;
700
701 INIT_LIST_HEAD(&da->da_node);
702
703 if (!rpc_pton(net, buf, portstr-buf, (struct sockaddr *)&da->da_addr,
704 sizeof(da->da_addr))) {
705 dprintk("%s: error parsing address %s\n", __func__, buf);
706 goto out_free_da;
707 }
708
709 portstr++;
710 sscanf(portstr, "%d-%d", &tmp[0], &tmp[1]);
711 port = htons((tmp[0] << 8) | (tmp[1]));
712
713 switch (da->da_addr.ss_family) {
714 case AF_INET:
715 ((struct sockaddr_in *)&da->da_addr)->sin_port = port;
716 da->da_addrlen = sizeof(struct sockaddr_in);
717 match_netid = "tcp";
718 match_netid_len = 3;
719 break;
720
721 case AF_INET6:
722 ((struct sockaddr_in6 *)&da->da_addr)->sin6_port = port;
723 da->da_addrlen = sizeof(struct sockaddr_in6);
724 match_netid = "tcp6";
725 match_netid_len = 4;
726 startsep = "[";
727 endsep = "]";
728 break;
729
730 default:
731 dprintk("%s: unsupported address family: %u\n",
732 __func__, da->da_addr.ss_family);
733 goto out_free_da;
734 }
735
736 if (nlen != match_netid_len || strncmp(netid, match_netid, nlen)) {
737 dprintk("%s: ERROR: r_netid \"%s\" != \"%s\"\n",
738 __func__, netid, match_netid);
739 goto out_free_da;
740 }
741
742 /* save human readable address */
743 len = strlen(startsep) + strlen(buf) + strlen(endsep) + 7;
744 da->da_remotestr = kzalloc(len, gfp_flags);
745
746 /* NULL is ok, only used for dprintk */
747 if (da->da_remotestr)
748 snprintf(da->da_remotestr, len, "%s%s%s:%u", startsep,
749 buf, endsep, ntohs(port));
750
751 dprintk("%s: Parsed DS addr %s\n", __func__, da->da_remotestr);
752 kfree(buf);
753 kfree(netid);
754 return da;
755
756out_free_da:
757 kfree(da);
758out_free_buf:
759 dprintk("%s: Error parsing DS addr: %s\n", __func__, buf);
760 kfree(buf);
761out_free_netid:
762 kfree(netid);
763out_err:
764 return NULL;
765}
766EXPORT_SYMBOL_GPL(nfs4_decode_mp_ds_addr);
This page took 0.052409 seconds and 5 git commands to generate.