diff options
author | Weston Andros Adamson <dros@netapp.com> | 2011-05-31 18:48:57 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-07-12 13:40:27 -0400 |
commit | 14f9a6076f5388f3fd6341ad4b841337b28fc825 (patch) | |
tree | d5c318c3956560b8b46346b494f8fa6e0724cbcc /fs | |
parent | c9895cb69b07a4b17d8fdae26667f9a9fba5183b (diff) |
NFS: Parse and store all multipath DS addresses
This parses and stores all addresses associated with each data server,
laying the groundwork for supporting multipath to data servers.
- Skips over addresses that cannot be parsed (ie IPv6 addrs if v6 is not
enabled). Only fails if none of the addresses are recognizable
- Currently only uses the first address that parsed cleanly
- Tested against pynfs server (modified to support multipath)
Signed-off-by: Weston Andros Adamson <dros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/nfs4filelayout.h | 12 | ||||
-rw-r--r-- | fs/nfs/nfs4filelayoutdev.c | 363 |
2 files changed, 243 insertions, 132 deletions
diff --git a/fs/nfs/nfs4filelayout.h b/fs/nfs/nfs4filelayout.h index 6c6a817710e5..68cce730b800 100644 --- a/fs/nfs/nfs4filelayout.h +++ b/fs/nfs/nfs4filelayout.h | |||
@@ -47,11 +47,17 @@ enum stripetype4 { | |||
47 | }; | 47 | }; |
48 | 48 | ||
49 | /* Individual ip address */ | 49 | /* Individual ip address */ |
50 | struct nfs4_pnfs_ds_addr { | ||
51 | struct sockaddr_storage da_addr; | ||
52 | size_t da_addrlen; | ||
53 | struct list_head da_node; /* nfs4_pnfs_dev_hlist dev_dslist */ | ||
54 | char *da_remotestr; /* human readable addr+port */ | ||
55 | }; | ||
56 | |||
50 | struct nfs4_pnfs_ds { | 57 | struct nfs4_pnfs_ds { |
51 | struct list_head ds_node; /* nfs4_pnfs_dev_hlist dev_dslist */ | 58 | struct list_head ds_node; /* nfs4_pnfs_dev_hlist dev_dslist */ |
52 | struct sockaddr_storage ds_addr; | 59 | char *ds_remotestr; /* comma sep list of addrs */ |
53 | size_t ds_addrlen; | 60 | struct list_head ds_addrs; |
54 | char *ds_remotestr; /* human readable addr+port */ | ||
55 | struct nfs_client *ds_clp; | 61 | struct nfs_client *ds_clp; |
56 | atomic_t ds_count; | 62 | atomic_t ds_count; |
57 | }; | 63 | }; |
diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index 1a8308b90108..610808a96f25 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c | |||
@@ -65,53 +65,104 @@ print_ds(struct nfs4_pnfs_ds *ds) | |||
65 | ds->ds_clp ? ds->ds_clp->cl_exchange_flags : 0); | 65 | ds->ds_clp ? ds->ds_clp->cl_exchange_flags : 0); |
66 | } | 66 | } |
67 | 67 | ||
68 | /* nfs4_ds_cache_lock is held */ | 68 | static bool |
69 | static struct nfs4_pnfs_ds * | 69 | same_sockaddr(struct sockaddr *addr1, struct sockaddr *addr2) |
70 | _data_server_lookup_locked(struct sockaddr *addr, size_t addrlen) | ||
71 | { | 70 | { |
72 | struct nfs4_pnfs_ds *ds; | ||
73 | struct sockaddr_in *a, *b; | 71 | struct sockaddr_in *a, *b; |
74 | struct sockaddr_in6 *a6, *b6; | 72 | struct sockaddr_in6 *a6, *b6; |
75 | 73 | ||
76 | list_for_each_entry(ds, &nfs4_data_server_cache, ds_node) { | 74 | if (addr1->sa_family != addr2->sa_family) |
77 | if (addr->sa_family != ds->ds_addr.ss_family) | 75 | return false; |
78 | continue; | 76 | |
79 | 77 | switch (addr1->sa_family) { | |
80 | switch (addr->sa_family) { | 78 | case AF_INET: |
81 | case AF_INET: | 79 | a = (struct sockaddr_in *)addr1; |
82 | a = (struct sockaddr_in *)addr; | 80 | b = (struct sockaddr_in *)addr2; |
83 | b = (struct sockaddr_in *)&ds->ds_addr; | 81 | |
84 | 82 | if (a->sin_addr.s_addr == b->sin_addr.s_addr && | |
85 | if (a->sin_addr.s_addr == b->sin_addr.s_addr && | 83 | a->sin_port == b->sin_port) |
86 | a->sin_port == b->sin_port) | 84 | return true; |
87 | return ds; | 85 | break; |
88 | break; | 86 | |
89 | 87 | case AF_INET6: | |
90 | case AF_INET6: | 88 | a6 = (struct sockaddr_in6 *)addr1; |
91 | a6 = (struct sockaddr_in6 *)addr; | 89 | b6 = (struct sockaddr_in6 *)addr2; |
92 | b6 = (struct sockaddr_in6 *)&ds->ds_addr; | 90 | |
93 | 91 | /* LINKLOCAL addresses must have matching scope_id */ | |
94 | /* LINKLOCAL addresses must have matching scope_id */ | 92 | if (ipv6_addr_scope(&a6->sin6_addr) == |
95 | if (ipv6_addr_scope(&a6->sin6_addr) == | 93 | IPV6_ADDR_SCOPE_LINKLOCAL && |
96 | IPV6_ADDR_SCOPE_LINKLOCAL && | 94 | a6->sin6_scope_id != b6->sin6_scope_id) |
97 | a6->sin6_scope_id != b6->sin6_scope_id) | 95 | return false; |
98 | continue; | 96 | |
99 | 97 | if (ipv6_addr_equal(&a6->sin6_addr, &b6->sin6_addr) && | |
100 | if (ipv6_addr_equal(&a6->sin6_addr, &b6->sin6_addr) && | 98 | a6->sin6_port == b6->sin6_port) |
101 | a6->sin6_port == b6->sin6_port) | 99 | return true; |
102 | return ds; | 100 | break; |
103 | break; | 101 | |
104 | 102 | default: | |
105 | default: | 103 | dprintk("%s: unhandled address family: %u\n", |
106 | dprintk("%s: unhandled address family: %u\n", | 104 | __func__, addr1->sa_family); |
107 | __func__, addr->sa_family); | 105 | return false; |
108 | return NULL; | 106 | } |
107 | |||
108 | return false; | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * Lookup DS by addresses. The first matching address returns true. | ||
113 | * nfs4_ds_cache_lock is held | ||
114 | */ | ||
115 | static struct nfs4_pnfs_ds * | ||
116 | _data_server_lookup_locked(struct list_head *dsaddrs) | ||
117 | { | ||
118 | struct nfs4_pnfs_ds *ds; | ||
119 | struct nfs4_pnfs_ds_addr *da1, *da2; | ||
120 | |||
121 | list_for_each_entry(da1, dsaddrs, da_node) { | ||
122 | list_for_each_entry(ds, &nfs4_data_server_cache, ds_node) { | ||
123 | list_for_each_entry(da2, &ds->ds_addrs, da_node) { | ||
124 | if (same_sockaddr( | ||
125 | (struct sockaddr *)&da1->da_addr, | ||
126 | (struct sockaddr *)&da2->da_addr)) | ||
127 | return ds; | ||
128 | } | ||
109 | } | 129 | } |
110 | } | 130 | } |
111 | return NULL; | 131 | return NULL; |
112 | } | 132 | } |
113 | 133 | ||
114 | /* | 134 | /* |
135 | * Compare two lists of addresses. | ||
136 | */ | ||
137 | static bool | ||
138 | _data_server_match_all_addrs_locked(struct list_head *dsaddrs1, | ||
139 | struct list_head *dsaddrs2) | ||
140 | { | ||
141 | struct nfs4_pnfs_ds_addr *da1, *da2; | ||
142 | size_t count1 = 0, | ||
143 | count2 = 0; | ||
144 | |||
145 | list_for_each_entry(da1, dsaddrs1, da_node) | ||
146 | count1++; | ||
147 | |||
148 | list_for_each_entry(da2, dsaddrs2, da_node) { | ||
149 | bool found = false; | ||
150 | count2++; | ||
151 | list_for_each_entry(da1, dsaddrs1, da_node) { | ||
152 | if (same_sockaddr((struct sockaddr *)&da1->da_addr, | ||
153 | (struct sockaddr *)&da2->da_addr)) { | ||
154 | found = true; | ||
155 | break; | ||
156 | } | ||
157 | } | ||
158 | if (!found) | ||
159 | return false; | ||
160 | } | ||
161 | |||
162 | return (count1 == count2); | ||
163 | } | ||
164 | |||
165 | /* | ||
115 | * Create an rpc connection to the nfs4_pnfs_ds data server | 166 | * Create an rpc connection to the nfs4_pnfs_ds data server |
116 | * Currently only support IPv4 | 167 | * Currently only support IPv4 |
117 | */ | 168 | */ |
@@ -119,14 +170,21 @@ static int | |||
119 | nfs4_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds) | 170 | nfs4_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds) |
120 | { | 171 | { |
121 | struct nfs_client *clp; | 172 | struct nfs_client *clp; |
173 | struct nfs4_pnfs_ds_addr *da; | ||
122 | int status = 0; | 174 | int status = 0; |
123 | 175 | ||
124 | dprintk("--> %s addr %s au_flavor %d\n", __func__, ds->ds_remotestr, | 176 | dprintk("--> %s DS %s au_flavor %d\n", __func__, ds->ds_remotestr, |
125 | mds_srv->nfs_client->cl_rpcclient->cl_auth->au_flavor); | 177 | mds_srv->nfs_client->cl_rpcclient->cl_auth->au_flavor); |
126 | 178 | ||
179 | BUG_ON(list_empty(&ds->ds_addrs)); | ||
180 | |||
181 | da = list_first_entry(&ds->ds_addrs, struct nfs4_pnfs_ds_addr, da_node); | ||
182 | dprintk("%s: using the first address for DS %s: %s\n", | ||
183 | __func__, ds->ds_remotestr, da->da_remotestr); | ||
184 | |||
127 | clp = nfs4_set_ds_client(mds_srv->nfs_client, | 185 | clp = nfs4_set_ds_client(mds_srv->nfs_client, |
128 | (struct sockaddr *)&ds->ds_addr, | 186 | (struct sockaddr *)&da->da_addr, |
129 | ds->ds_addrlen, IPPROTO_TCP); | 187 | da->da_addrlen, IPPROTO_TCP); |
130 | if (IS_ERR(clp)) { | 188 | if (IS_ERR(clp)) { |
131 | status = PTR_ERR(clp); | 189 | status = PTR_ERR(clp); |
132 | goto out; | 190 | goto out; |
@@ -169,12 +227,24 @@ out_put: | |||
169 | static void | 227 | static void |
170 | destroy_ds(struct nfs4_pnfs_ds *ds) | 228 | destroy_ds(struct nfs4_pnfs_ds *ds) |
171 | { | 229 | { |
230 | struct nfs4_pnfs_ds_addr *da; | ||
231 | |||
172 | dprintk("--> %s\n", __func__); | 232 | dprintk("--> %s\n", __func__); |
173 | ifdebug(FACILITY) | 233 | ifdebug(FACILITY) |
174 | print_ds(ds); | 234 | print_ds(ds); |
175 | 235 | ||
176 | if (ds->ds_clp) | 236 | if (ds->ds_clp) |
177 | nfs_put_client(ds->ds_clp); | 237 | nfs_put_client(ds->ds_clp); |
238 | |||
239 | while (!list_empty(&ds->ds_addrs)) { | ||
240 | da = list_first_entry(&ds->ds_addrs, | ||
241 | struct nfs4_pnfs_ds_addr, | ||
242 | da_node); | ||
243 | list_del_init(&da->da_node); | ||
244 | kfree(da->da_remotestr); | ||
245 | kfree(da); | ||
246 | } | ||
247 | |||
178 | kfree(ds->ds_remotestr); | 248 | kfree(ds->ds_remotestr); |
179 | kfree(ds); | 249 | kfree(ds); |
180 | } | 250 | } |
@@ -207,67 +277,73 @@ nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr) | |||
207 | * complicated setup around many dprinks. | 277 | * complicated setup around many dprinks. |
208 | */ | 278 | */ |
209 | static char * | 279 | static char * |
210 | nfs4_pnfs_remotestr(struct sockaddr *ds_addr, gfp_t gfp_flags) | 280 | nfs4_pnfs_remotestr(struct list_head *dsaddrs, gfp_t gfp_flags) |
211 | { | 281 | { |
212 | char buf[INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN]; | 282 | struct nfs4_pnfs_ds_addr *da; |
213 | char *remotestr; | 283 | char *remotestr; |
214 | char *startsep = ""; | ||
215 | char *endsep = ""; | ||
216 | size_t len; | 284 | size_t len; |
217 | uint16_t port; | 285 | char *p; |
218 | 286 | ||
219 | switch (ds_addr->sa_family) { | 287 | len = 3; /* '{', '}' and eol */ |
220 | case AF_INET: | 288 | list_for_each_entry(da, dsaddrs, da_node) { |
221 | port = ((struct sockaddr_in *)ds_addr)->sin_port; | 289 | len += strlen(da->da_remotestr) + 1; /* string plus comma */ |
222 | break; | ||
223 | case AF_INET6: | ||
224 | startsep = "["; | ||
225 | endsep = "]"; | ||
226 | port = ((struct sockaddr_in6 *)ds_addr)->sin6_port; | ||
227 | break; | ||
228 | default: | ||
229 | dprintk("%s: Unknown address family %u\n", | ||
230 | __func__, ds_addr->sa_family); | ||
231 | return NULL; | ||
232 | } | 290 | } |
233 | 291 | ||
234 | if (!rpc_ntop((struct sockaddr *)ds_addr, buf, sizeof(buf))) { | 292 | remotestr = kzalloc(len, gfp_flags); |
235 | dprintk("%s: error printing addr\n", __func__); | 293 | if (!remotestr) |
236 | return NULL; | 294 | return NULL; |
237 | } | ||
238 | 295 | ||
239 | len = strlen(buf) + strlen(startsep) + strlen(endsep) + 1 + 5 + 1; | 296 | p = remotestr; |
240 | remotestr = kzalloc(len, gfp_flags); | 297 | *(p++) = '{'; |
298 | len--; | ||
299 | list_for_each_entry(da, dsaddrs, da_node) { | ||
300 | size_t ll = strlen(da->da_remotestr); | ||
241 | 301 | ||
242 | if (unlikely(!remotestr)) { | 302 | if (ll > len) |
243 | dprintk("%s: couldn't alloc remotestr\n", __func__); | 303 | goto out_err; |
244 | return NULL; | ||
245 | } | ||
246 | 304 | ||
247 | snprintf(remotestr, len, "%s%s%s:%u", | 305 | memcpy(p, da->da_remotestr, ll); |
248 | startsep, buf, endsep, ntohs(port)); | 306 | p += ll; |
307 | len -= ll; | ||
249 | 308 | ||
309 | if (len < 1) | ||
310 | goto out_err; | ||
311 | (*p++) = ','; | ||
312 | len--; | ||
313 | } | ||
314 | if (len < 2) | ||
315 | goto out_err; | ||
316 | *(p++) = '}'; | ||
317 | *p = '\0'; | ||
250 | return remotestr; | 318 | return remotestr; |
319 | out_err: | ||
320 | kfree(remotestr); | ||
321 | return NULL; | ||
251 | } | 322 | } |
252 | 323 | ||
253 | static struct nfs4_pnfs_ds * | 324 | static struct nfs4_pnfs_ds * |
254 | nfs4_pnfs_ds_add(struct sockaddr *addr, size_t addrlen, gfp_t gfp_flags) | 325 | nfs4_pnfs_ds_add(struct list_head *dsaddrs, gfp_t gfp_flags) |
255 | { | 326 | { |
256 | struct nfs4_pnfs_ds *tmp_ds, *ds = NULL; | 327 | struct nfs4_pnfs_ds *tmp_ds, *ds = NULL; |
257 | char *remotestr; | 328 | char *remotestr; |
258 | 329 | ||
259 | ds = kzalloc(sizeof(*tmp_ds), gfp_flags); | 330 | if (list_empty(dsaddrs)) { |
331 | dprintk("%s: no addresses defined\n", __func__); | ||
332 | goto out; | ||
333 | } | ||
334 | |||
335 | ds = kzalloc(sizeof(*ds), gfp_flags); | ||
260 | if (!ds) | 336 | if (!ds) |
261 | goto out; | 337 | goto out; |
262 | 338 | ||
263 | /* this is only used for debugging, so it's ok if its NULL */ | 339 | /* this is only used for debugging, so it's ok if its NULL */ |
264 | remotestr = nfs4_pnfs_remotestr(addr, gfp_flags); | 340 | remotestr = nfs4_pnfs_remotestr(dsaddrs, gfp_flags); |
265 | 341 | ||
266 | spin_lock(&nfs4_ds_cache_lock); | 342 | spin_lock(&nfs4_ds_cache_lock); |
267 | tmp_ds = _data_server_lookup_locked(addr, addrlen); | 343 | tmp_ds = _data_server_lookup_locked(dsaddrs); |
268 | if (tmp_ds == NULL) { | 344 | if (tmp_ds == NULL) { |
269 | memcpy(&ds->ds_addr, addr, addrlen); | 345 | INIT_LIST_HEAD(&ds->ds_addrs); |
270 | ds->ds_addrlen = addrlen; | 346 | list_splice_init(dsaddrs, &ds->ds_addrs); |
271 | ds->ds_remotestr = remotestr; | 347 | ds->ds_remotestr = remotestr; |
272 | atomic_set(&ds->ds_count, 1); | 348 | atomic_set(&ds->ds_count, 1); |
273 | INIT_LIST_HEAD(&ds->ds_node); | 349 | INIT_LIST_HEAD(&ds->ds_node); |
@@ -276,6 +352,11 @@ nfs4_pnfs_ds_add(struct sockaddr *addr, size_t addrlen, gfp_t gfp_flags) | |||
276 | dprintk("%s add new data server %s\n", __func__, | 352 | dprintk("%s add new data server %s\n", __func__, |
277 | ds->ds_remotestr); | 353 | ds->ds_remotestr); |
278 | } else { | 354 | } else { |
355 | if (!_data_server_match_all_addrs_locked(&tmp_ds->ds_addrs, | ||
356 | dsaddrs)) { | ||
357 | dprintk("%s: multipath address mismatch: %s != %s", | ||
358 | __func__, tmp_ds->ds_remotestr, remotestr); | ||
359 | } | ||
279 | kfree(remotestr); | 360 | kfree(remotestr); |
280 | kfree(ds); | 361 | kfree(ds); |
281 | atomic_inc(&tmp_ds->ds_count); | 362 | atomic_inc(&tmp_ds->ds_count); |
@@ -292,19 +373,20 @@ out: | |||
292 | /* | 373 | /* |
293 | * Currently only supports ipv4, ipv6 and one multi-path address. | 374 | * Currently only supports ipv4, ipv6 and one multi-path address. |
294 | */ | 375 | */ |
295 | static struct nfs4_pnfs_ds * | 376 | static struct nfs4_pnfs_ds_addr * |
296 | decode_and_add_ds(struct xdr_stream *streamp, struct inode *inode, gfp_t gfp_flags) | 377 | decode_ds_addr(struct xdr_stream *streamp, gfp_t gfp_flags) |
297 | { | 378 | { |
298 | struct nfs4_pnfs_ds *ds = NULL; | 379 | struct nfs4_pnfs_ds_addr *da = NULL; |
299 | char *buf, *portstr; | 380 | char *buf, *portstr; |
300 | struct sockaddr_storage ss; | ||
301 | size_t sslen; | ||
302 | u32 port; | 381 | u32 port; |
303 | int nlen, rlen; | 382 | int nlen, rlen; |
304 | int tmp[2]; | 383 | int tmp[2]; |
305 | __be32 *p; | 384 | __be32 *p; |
306 | char *netid, *match_netid; | 385 | char *netid, *match_netid; |
307 | size_t match_netid_len; | 386 | size_t len, match_netid_len; |
387 | char *startsep = ""; | ||
388 | char *endsep = ""; | ||
389 | |||
308 | 390 | ||
309 | /* r_netid */ | 391 | /* r_netid */ |
310 | p = xdr_inline_decode(streamp, 4); | 392 | p = xdr_inline_decode(streamp, 4); |
@@ -365,50 +447,74 @@ decode_and_add_ds(struct xdr_stream *streamp, struct inode *inode, gfp_t gfp_fla | |||
365 | } | 447 | } |
366 | *portstr = '\0'; | 448 | *portstr = '\0'; |
367 | 449 | ||
368 | if (!rpc_pton(buf, portstr-buf, (struct sockaddr *)&ss, sizeof(ss))) { | 450 | da = kzalloc(sizeof(*da), gfp_flags); |
369 | dprintk("%s: Error parsing address %s\n", __func__, buf); | 451 | if (unlikely(!da)) |
370 | goto out_free_buf; | 452 | goto out_free_buf; |
453 | |||
454 | INIT_LIST_HEAD(&da->da_node); | ||
455 | |||
456 | if (!rpc_pton(buf, portstr-buf, (struct sockaddr *)&da->da_addr, | ||
457 | sizeof(da->da_addr))) { | ||
458 | dprintk("%s: error parsing address %s\n", __func__, buf); | ||
459 | goto out_free_da; | ||
371 | } | 460 | } |
372 | 461 | ||
373 | portstr++; | 462 | portstr++; |
374 | sscanf(portstr, "%d-%d", &tmp[0], &tmp[1]); | 463 | sscanf(portstr, "%d-%d", &tmp[0], &tmp[1]); |
375 | port = htons((tmp[0] << 8) | (tmp[1])); | 464 | port = htons((tmp[0] << 8) | (tmp[1])); |
376 | 465 | ||
377 | switch (ss.ss_family) { | 466 | switch (da->da_addr.ss_family) { |
378 | case AF_INET: | 467 | case AF_INET: |
379 | ((struct sockaddr_in *)&ss)->sin_port = port; | 468 | ((struct sockaddr_in *)&da->da_addr)->sin_port = port; |
380 | sslen = sizeof(struct sockaddr_in); | 469 | da->da_addrlen = sizeof(struct sockaddr_in); |
381 | match_netid = "tcp"; | 470 | match_netid = "tcp"; |
382 | match_netid_len = 3; | 471 | match_netid_len = 3; |
383 | break; | 472 | break; |
384 | 473 | ||
385 | case AF_INET6: | 474 | case AF_INET6: |
386 | ((struct sockaddr_in6 *)&ss)->sin6_port = port; | 475 | ((struct sockaddr_in6 *)&da->da_addr)->sin6_port = port; |
387 | sslen = sizeof(struct sockaddr_in6); | 476 | da->da_addrlen = sizeof(struct sockaddr_in6); |
388 | match_netid = "tcp6"; | 477 | match_netid = "tcp6"; |
389 | match_netid_len = 4; | 478 | match_netid_len = 4; |
479 | startsep = "["; | ||
480 | endsep = "]"; | ||
390 | break; | 481 | break; |
391 | 482 | ||
392 | default: | 483 | default: |
393 | dprintk("%s: unsupported address family: %u\n", | 484 | dprintk("%s: unsupported address family: %u\n", |
394 | __func__, ss.ss_family); | 485 | __func__, da->da_addr.ss_family); |
395 | goto out_free_buf; | 486 | goto out_free_da; |
396 | } | 487 | } |
397 | 488 | ||
398 | if (nlen != match_netid_len || strncmp(netid, match_netid, nlen)) { | 489 | if (nlen != match_netid_len || strncmp(netid, match_netid, nlen)) { |
399 | dprintk("%s: ERROR: r_netid \"%s\" != \"%s\"\n", | 490 | dprintk("%s: ERROR: r_netid \"%s\" != \"%s\"\n", |
400 | __func__, netid, match_netid); | 491 | __func__, netid, match_netid); |
401 | goto out_free_buf; | 492 | goto out_free_da; |
402 | } | 493 | } |
403 | 494 | ||
404 | ds = nfs4_pnfs_ds_add((struct sockaddr *)&ss, sslen, gfp_flags); | 495 | /* save human readable address */ |
405 | dprintk("%s: Added DS %s\n", __func__, ds->ds_remotestr); | 496 | len = strlen(startsep) + strlen(buf) + strlen(endsep) + 7; |
497 | da->da_remotestr = kzalloc(len, gfp_flags); | ||
498 | |||
499 | /* NULL is ok, only used for dprintk */ | ||
500 | if (da->da_remotestr) | ||
501 | snprintf(da->da_remotestr, len, "%s%s%s:%u", startsep, | ||
502 | buf, endsep, ntohs(port)); | ||
503 | |||
504 | dprintk("%s: Parsed DS addr %s\n", __func__, da->da_remotestr); | ||
505 | kfree(buf); | ||
506 | kfree(netid); | ||
507 | return da; | ||
508 | |||
509 | out_free_da: | ||
510 | kfree(da); | ||
406 | out_free_buf: | 511 | out_free_buf: |
512 | dprintk("%s: Error parsing DS addr: %s\n", __func__, buf); | ||
407 | kfree(buf); | 513 | kfree(buf); |
408 | out_free_netid: | 514 | out_free_netid: |
409 | kfree(netid); | 515 | kfree(netid); |
410 | out_err: | 516 | out_err: |
411 | return ds; | 517 | return NULL; |
412 | } | 518 | } |
413 | 519 | ||
414 | /* Decode opaque device data and return the result */ | 520 | /* Decode opaque device data and return the result */ |
@@ -425,6 +531,8 @@ decode_device(struct inode *ino, struct pnfs_device *pdev, gfp_t gfp_flags) | |||
425 | struct xdr_stream stream; | 531 | struct xdr_stream stream; |
426 | struct xdr_buf buf; | 532 | struct xdr_buf buf; |
427 | struct page *scratch; | 533 | struct page *scratch; |
534 | struct list_head dsaddrs; | ||
535 | struct nfs4_pnfs_ds_addr *da; | ||
428 | 536 | ||
429 | /* set up xdr stream */ | 537 | /* set up xdr stream */ |
430 | scratch = alloc_page(gfp_flags); | 538 | scratch = alloc_page(gfp_flags); |
@@ -501,6 +609,8 @@ decode_device(struct inode *ino, struct pnfs_device *pdev, gfp_t gfp_flags) | |||
501 | NFS_SERVER(ino)->nfs_client, | 609 | NFS_SERVER(ino)->nfs_client, |
502 | &pdev->dev_id); | 610 | &pdev->dev_id); |
503 | 611 | ||
612 | INIT_LIST_HEAD(&dsaddrs); | ||
613 | |||
504 | for (i = 0; i < dsaddr->ds_num; i++) { | 614 | for (i = 0; i < dsaddr->ds_num; i++) { |
505 | int j; | 615 | int j; |
506 | u32 mp_count; | 616 | u32 mp_count; |
@@ -510,48 +620,43 @@ decode_device(struct inode *ino, struct pnfs_device *pdev, gfp_t gfp_flags) | |||
510 | goto out_err_free_deviceid; | 620 | goto out_err_free_deviceid; |
511 | 621 | ||
512 | mp_count = be32_to_cpup(p); /* multipath count */ | 622 | mp_count = be32_to_cpup(p); /* multipath count */ |
513 | if (mp_count > 1) { | ||
514 | printk(KERN_WARNING | ||
515 | "%s: Multipath count %d not supported, " | ||
516 | "skipping all greater than 1\n", __func__, | ||
517 | mp_count); | ||
518 | } | ||
519 | for (j = 0; j < mp_count; j++) { | 623 | for (j = 0; j < mp_count; j++) { |
520 | if (j == 0) { | 624 | da = decode_ds_addr(&stream, gfp_flags); |
521 | dsaddr->ds_list[i] = decode_and_add_ds(&stream, | 625 | if (da) |
522 | ino, gfp_flags); | 626 | list_add_tail(&da->da_node, &dsaddrs); |
523 | if (dsaddr->ds_list[i] == NULL) | 627 | } |
524 | goto out_err_free_deviceid; | 628 | if (list_empty(&dsaddrs)) { |
525 | } else { | 629 | dprintk("%s: no suitable DS addresses found\n", |
526 | u32 len; | 630 | __func__); |
527 | /* skip extra multipath */ | 631 | goto out_err_free_deviceid; |
528 | 632 | } | |
529 | /* read len, skip */ | 633 | |
530 | p = xdr_inline_decode(&stream, 4); | 634 | dsaddr->ds_list[i] = nfs4_pnfs_ds_add(&dsaddrs, gfp_flags); |
531 | if (unlikely(!p)) | 635 | if (!dsaddr->ds_list[i]) |
532 | goto out_err_free_deviceid; | 636 | goto out_err_drain_dsaddrs; |
533 | len = be32_to_cpup(p); | 637 | |
534 | 638 | /* If DS was already in cache, free ds addrs */ | |
535 | p = xdr_inline_decode(&stream, len); | 639 | while (!list_empty(&dsaddrs)) { |
536 | if (unlikely(!p)) | 640 | da = list_first_entry(&dsaddrs, |
537 | goto out_err_free_deviceid; | 641 | struct nfs4_pnfs_ds_addr, |
538 | 642 | da_node); | |
539 | /* read len, skip */ | 643 | list_del_init(&da->da_node); |
540 | p = xdr_inline_decode(&stream, 4); | 644 | kfree(da->da_remotestr); |
541 | if (unlikely(!p)) | 645 | kfree(da); |
542 | goto out_err_free_deviceid; | ||
543 | len = be32_to_cpup(p); | ||
544 | |||
545 | p = xdr_inline_decode(&stream, len); | ||
546 | if (unlikely(!p)) | ||
547 | goto out_err_free_deviceid; | ||
548 | } | ||
549 | } | 646 | } |
550 | } | 647 | } |
551 | 648 | ||
552 | __free_page(scratch); | 649 | __free_page(scratch); |
553 | return dsaddr; | 650 | return dsaddr; |
554 | 651 | ||
652 | out_err_drain_dsaddrs: | ||
653 | while (!list_empty(&dsaddrs)) { | ||
654 | da = list_first_entry(&dsaddrs, struct nfs4_pnfs_ds_addr, | ||
655 | da_node); | ||
656 | list_del_init(&da->da_node); | ||
657 | kfree(da->da_remotestr); | ||
658 | kfree(da); | ||
659 | } | ||
555 | out_err_free_deviceid: | 660 | out_err_free_deviceid: |
556 | nfs4_fl_free_deviceid(dsaddr); | 661 | nfs4_fl_free_deviceid(dsaddr); |
557 | /* stripe_indicies was part of dsaddr */ | 662 | /* stripe_indicies was part of dsaddr */ |