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