diff options
author | Weston Andros Adamson <dros@netapp.com> | 2011-03-24 16:48:21 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-03-24 17:01:41 -0400 |
commit | 35124a0994fc02545b14b9fa3aad000b3331f1c0 (patch) | |
tree | 5149267f387199fd9ca2718c74d86b6779013501 | |
parent | ef31153786bc1e4304e6b9422cc8b9efef455611 (diff) |
Cleanup XDR parsing for LAYOUTGET, GETDEVICEINFO
changes LAYOUTGET and GETDEVICEINFO XDR parsing to:
- not use vmap, which doesn't work on incoherent archs
- use xdr_stream parsing for all xdr
Signed-off-by: Weston Andros Adamson <dros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/nfs4filelayout.c | 53 | ||||
-rw-r--r-- | fs/nfs/nfs4filelayoutdev.c | 178 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 9 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 30 | ||||
-rw-r--r-- | fs/nfs/pnfs.c | 39 | ||||
-rw-r--r-- | fs/nfs/pnfs.h | 1 | ||||
-rw-r--r-- | include/linux/nfs_xdr.h | 6 |
7 files changed, 230 insertions, 86 deletions
diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index ffb54a082f3a..6f8192f4cfc7 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c | |||
@@ -502,12 +502,33 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo, | |||
502 | struct nfs4_layoutget_res *lgr, | 502 | struct nfs4_layoutget_res *lgr, |
503 | struct nfs4_deviceid *id) | 503 | struct nfs4_deviceid *id) |
504 | { | 504 | { |
505 | uint32_t *p = (uint32_t *)lgr->layout.buf; | 505 | struct xdr_stream stream; |
506 | struct xdr_buf buf = { | ||
507 | .pages = lgr->layoutp->pages, | ||
508 | .page_len = lgr->layoutp->len, | ||
509 | .buflen = lgr->layoutp->len, | ||
510 | .len = lgr->layoutp->len, | ||
511 | }; | ||
512 | struct page *scratch; | ||
513 | __be32 *p; | ||
506 | uint32_t nfl_util; | 514 | uint32_t nfl_util; |
507 | int i; | 515 | int i; |
508 | 516 | ||
509 | dprintk("%s: set_layout_map Begin\n", __func__); | 517 | dprintk("%s: set_layout_map Begin\n", __func__); |
510 | 518 | ||
519 | scratch = alloc_page(GFP_KERNEL); | ||
520 | if (!scratch) | ||
521 | return -ENOMEM; | ||
522 | |||
523 | xdr_init_decode(&stream, &buf, NULL); | ||
524 | xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE); | ||
525 | |||
526 | /* 20 = ufl_util (4), first_stripe_index (4), pattern_offset (8), | ||
527 | * num_fh (4) */ | ||
528 | p = xdr_inline_decode(&stream, NFS4_DEVICEID4_SIZE + 20); | ||
529 | if (unlikely(!p)) | ||
530 | goto out_err; | ||
531 | |||
511 | memcpy(id, p, sizeof(*id)); | 532 | memcpy(id, p, sizeof(*id)); |
512 | p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); | 533 | p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); |
513 | print_deviceid(id); | 534 | print_deviceid(id); |
@@ -529,32 +550,46 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo, | |||
529 | __func__, nfl_util, fl->num_fh, fl->first_stripe_index, | 550 | __func__, nfl_util, fl->num_fh, fl->first_stripe_index, |
530 | fl->pattern_offset); | 551 | fl->pattern_offset); |
531 | 552 | ||
553 | if (!fl->num_fh) | ||
554 | goto out_err; | ||
555 | |||
532 | fl->fh_array = kzalloc(fl->num_fh * sizeof(struct nfs_fh *), | 556 | fl->fh_array = kzalloc(fl->num_fh * sizeof(struct nfs_fh *), |
533 | GFP_KERNEL); | 557 | GFP_KERNEL); |
534 | if (!fl->fh_array) | 558 | if (!fl->fh_array) |
535 | return -ENOMEM; | 559 | goto out_err; |
536 | 560 | ||
537 | for (i = 0; i < fl->num_fh; i++) { | 561 | for (i = 0; i < fl->num_fh; i++) { |
538 | /* Do we want to use a mempool here? */ | 562 | /* Do we want to use a mempool here? */ |
539 | fl->fh_array[i] = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL); | 563 | fl->fh_array[i] = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL); |
540 | if (!fl->fh_array[i]) { | 564 | if (!fl->fh_array[i]) |
541 | filelayout_free_fh_array(fl); | 565 | goto out_err_free; |
542 | return -ENOMEM; | 566 | |
543 | } | 567 | p = xdr_inline_decode(&stream, 4); |
568 | if (unlikely(!p)) | ||
569 | goto out_err_free; | ||
544 | fl->fh_array[i]->size = be32_to_cpup(p++); | 570 | fl->fh_array[i]->size = be32_to_cpup(p++); |
545 | if (sizeof(struct nfs_fh) < fl->fh_array[i]->size) { | 571 | if (sizeof(struct nfs_fh) < fl->fh_array[i]->size) { |
546 | printk(KERN_ERR "Too big fh %d received %d\n", | 572 | printk(KERN_ERR "Too big fh %d received %d\n", |
547 | i, fl->fh_array[i]->size); | 573 | i, fl->fh_array[i]->size); |
548 | filelayout_free_fh_array(fl); | 574 | goto out_err_free; |
549 | return -EIO; | ||
550 | } | 575 | } |
576 | |||
577 | p = xdr_inline_decode(&stream, fl->fh_array[i]->size); | ||
578 | if (unlikely(!p)) | ||
579 | goto out_err_free; | ||
551 | memcpy(fl->fh_array[i]->data, p, fl->fh_array[i]->size); | 580 | memcpy(fl->fh_array[i]->data, p, fl->fh_array[i]->size); |
552 | p += XDR_QUADLEN(fl->fh_array[i]->size); | ||
553 | dprintk("DEBUG: %s: fh len %d\n", __func__, | 581 | dprintk("DEBUG: %s: fh len %d\n", __func__, |
554 | fl->fh_array[i]->size); | 582 | fl->fh_array[i]->size); |
555 | } | 583 | } |
556 | 584 | ||
585 | __free_page(scratch); | ||
557 | return 0; | 586 | return 0; |
587 | |||
588 | out_err_free: | ||
589 | filelayout_free_fh_array(fl); | ||
590 | out_err: | ||
591 | __free_page(scratch); | ||
592 | return -EIO; | ||
558 | } | 593 | } |
559 | 594 | ||
560 | static void | 595 | static void |
diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index 68143c162e3b..de5350f2b249 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c | |||
@@ -261,7 +261,7 @@ out: | |||
261 | * Currently only support ipv4, and one multi-path address. | 261 | * Currently only support ipv4, and one multi-path address. |
262 | */ | 262 | */ |
263 | static struct nfs4_pnfs_ds * | 263 | static struct nfs4_pnfs_ds * |
264 | decode_and_add_ds(__be32 **pp, struct inode *inode) | 264 | decode_and_add_ds(struct xdr_stream *streamp, struct inode *inode) |
265 | { | 265 | { |
266 | struct nfs4_pnfs_ds *ds = NULL; | 266 | struct nfs4_pnfs_ds *ds = NULL; |
267 | char *buf; | 267 | char *buf; |
@@ -269,25 +269,34 @@ decode_and_add_ds(__be32 **pp, struct inode *inode) | |||
269 | u32 ip_addr, port; | 269 | u32 ip_addr, port; |
270 | int nlen, rlen, i; | 270 | int nlen, rlen, i; |
271 | int tmp[2]; | 271 | int tmp[2]; |
272 | __be32 *r_netid, *r_addr, *p = *pp; | 272 | __be32 *p; |
273 | 273 | ||
274 | /* r_netid */ | 274 | /* r_netid */ |
275 | p = xdr_inline_decode(streamp, 4); | ||
276 | if (unlikely(!p)) | ||
277 | goto out_err; | ||
275 | nlen = be32_to_cpup(p++); | 278 | nlen = be32_to_cpup(p++); |
276 | r_netid = p; | ||
277 | p += XDR_QUADLEN(nlen); | ||
278 | 279 | ||
279 | /* r_addr */ | 280 | p = xdr_inline_decode(streamp, nlen); |
280 | rlen = be32_to_cpup(p++); | 281 | if (unlikely(!p)) |
281 | r_addr = p; | 282 | goto out_err; |
282 | p += XDR_QUADLEN(rlen); | ||
283 | *pp = p; | ||
284 | 283 | ||
285 | /* Check that netid is "tcp" */ | 284 | /* Check that netid is "tcp" */ |
286 | if (nlen != 3 || memcmp((char *)r_netid, "tcp", 3)) { | 285 | if (nlen != 3 || memcmp((char *)p, "tcp", 3)) { |
287 | dprintk("%s: ERROR: non ipv4 TCP r_netid\n", __func__); | 286 | dprintk("%s: ERROR: non ipv4 TCP r_netid\n", __func__); |
288 | goto out_err; | 287 | goto out_err; |
289 | } | 288 | } |
290 | 289 | ||
290 | /* r_addr */ | ||
291 | p = xdr_inline_decode(streamp, 4); | ||
292 | if (unlikely(!p)) | ||
293 | goto out_err; | ||
294 | rlen = be32_to_cpup(p); | ||
295 | |||
296 | p = xdr_inline_decode(streamp, rlen); | ||
297 | if (unlikely(!p)) | ||
298 | goto out_err; | ||
299 | |||
291 | /* ipv6 length plus port is legal */ | 300 | /* ipv6 length plus port is legal */ |
292 | if (rlen > INET6_ADDRSTRLEN + 8) { | 301 | if (rlen > INET6_ADDRSTRLEN + 8) { |
293 | dprintk("%s: Invalid address, length %d\n", __func__, | 302 | dprintk("%s: Invalid address, length %d\n", __func__, |
@@ -300,7 +309,7 @@ decode_and_add_ds(__be32 **pp, struct inode *inode) | |||
300 | goto out_err; | 309 | goto out_err; |
301 | } | 310 | } |
302 | buf[rlen] = '\0'; | 311 | buf[rlen] = '\0'; |
303 | memcpy(buf, r_addr, rlen); | 312 | memcpy(buf, p, rlen); |
304 | 313 | ||
305 | /* replace the port dots with dashes for the in4_pton() delimiter*/ | 314 | /* replace the port dots with dashes for the in4_pton() delimiter*/ |
306 | for (i = 0; i < 2; i++) { | 315 | for (i = 0; i < 2; i++) { |
@@ -336,90 +345,154 @@ out_err: | |||
336 | static struct nfs4_file_layout_dsaddr* | 345 | static struct nfs4_file_layout_dsaddr* |
337 | decode_device(struct inode *ino, struct pnfs_device *pdev) | 346 | decode_device(struct inode *ino, struct pnfs_device *pdev) |
338 | { | 347 | { |
339 | int i, dummy; | 348 | int i; |
340 | u32 cnt, num; | 349 | u32 cnt, num; |
341 | u8 *indexp; | 350 | u8 *indexp; |
342 | __be32 *p = (__be32 *)pdev->area, *indicesp; | 351 | __be32 *p; |
343 | struct nfs4_file_layout_dsaddr *dsaddr; | 352 | u8 *stripe_indices; |
353 | u8 max_stripe_index; | ||
354 | struct nfs4_file_layout_dsaddr *dsaddr = NULL; | ||
355 | struct xdr_stream stream; | ||
356 | struct xdr_buf buf = { | ||
357 | .pages = pdev->pages, | ||
358 | .page_len = pdev->pglen, | ||
359 | .buflen = pdev->pglen, | ||
360 | .len = pdev->pglen, | ||
361 | }; | ||
362 | struct page *scratch; | ||
363 | |||
364 | /* set up xdr stream */ | ||
365 | scratch = alloc_page(GFP_KERNEL); | ||
366 | if (!scratch) | ||
367 | goto out_err; | ||
368 | |||
369 | xdr_init_decode(&stream, &buf, NULL); | ||
370 | xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE); | ||
344 | 371 | ||
345 | /* Get the stripe count (number of stripe index) */ | 372 | /* Get the stripe count (number of stripe index) */ |
346 | cnt = be32_to_cpup(p++); | 373 | p = xdr_inline_decode(&stream, 4); |
374 | if (unlikely(!p)) | ||
375 | goto out_err_free_scratch; | ||
376 | |||
377 | cnt = be32_to_cpup(p); | ||
347 | dprintk("%s stripe count %d\n", __func__, cnt); | 378 | dprintk("%s stripe count %d\n", __func__, cnt); |
348 | if (cnt > NFS4_PNFS_MAX_STRIPE_CNT) { | 379 | if (cnt > NFS4_PNFS_MAX_STRIPE_CNT) { |
349 | printk(KERN_WARNING "%s: stripe count %d greater than " | 380 | printk(KERN_WARNING "%s: stripe count %d greater than " |
350 | "supported maximum %d\n", __func__, | 381 | "supported maximum %d\n", __func__, |
351 | cnt, NFS4_PNFS_MAX_STRIPE_CNT); | 382 | cnt, NFS4_PNFS_MAX_STRIPE_CNT); |
352 | goto out_err; | 383 | goto out_err_free_scratch; |
384 | } | ||
385 | |||
386 | /* read stripe indices */ | ||
387 | stripe_indices = kcalloc(cnt, sizeof(u8), GFP_KERNEL); | ||
388 | if (!stripe_indices) | ||
389 | goto out_err_free_scratch; | ||
390 | |||
391 | p = xdr_inline_decode(&stream, cnt << 2); | ||
392 | if (unlikely(!p)) | ||
393 | goto out_err_free_stripe_indices; | ||
394 | |||
395 | indexp = &stripe_indices[0]; | ||
396 | max_stripe_index = 0; | ||
397 | for (i = 0; i < cnt; i++) { | ||
398 | *indexp = be32_to_cpup(p++); | ||
399 | max_stripe_index = max(max_stripe_index, *indexp); | ||
400 | indexp++; | ||
353 | } | 401 | } |
354 | 402 | ||
355 | /* Check the multipath list count */ | 403 | /* Check the multipath list count */ |
356 | indicesp = p; | 404 | p = xdr_inline_decode(&stream, 4); |
357 | p += XDR_QUADLEN(cnt << 2); | 405 | if (unlikely(!p)) |
358 | num = be32_to_cpup(p++); | 406 | goto out_err_free_stripe_indices; |
407 | |||
408 | num = be32_to_cpup(p); | ||
359 | dprintk("%s ds_num %u\n", __func__, num); | 409 | dprintk("%s ds_num %u\n", __func__, num); |
360 | if (num > NFS4_PNFS_MAX_MULTI_CNT) { | 410 | if (num > NFS4_PNFS_MAX_MULTI_CNT) { |
361 | printk(KERN_WARNING "%s: multipath count %d greater than " | 411 | printk(KERN_WARNING "%s: multipath count %d greater than " |
362 | "supported maximum %d\n", __func__, | 412 | "supported maximum %d\n", __func__, |
363 | num, NFS4_PNFS_MAX_MULTI_CNT); | 413 | num, NFS4_PNFS_MAX_MULTI_CNT); |
364 | goto out_err; | 414 | goto out_err_free_stripe_indices; |
365 | } | 415 | } |
416 | |||
417 | /* validate stripe indices are all < num */ | ||
418 | if (max_stripe_index >= num) { | ||
419 | printk(KERN_WARNING "%s: stripe index %u >= num ds %u\n", | ||
420 | __func__, max_stripe_index, num); | ||
421 | goto out_err_free_stripe_indices; | ||
422 | } | ||
423 | |||
366 | dsaddr = kzalloc(sizeof(*dsaddr) + | 424 | dsaddr = kzalloc(sizeof(*dsaddr) + |
367 | (sizeof(struct nfs4_pnfs_ds *) * (num - 1)), | 425 | (sizeof(struct nfs4_pnfs_ds *) * (num - 1)), |
368 | GFP_KERNEL); | 426 | GFP_KERNEL); |
369 | if (!dsaddr) | 427 | if (!dsaddr) |
370 | goto out_err; | 428 | goto out_err_free_stripe_indices; |
371 | |||
372 | dsaddr->stripe_indices = kzalloc(sizeof(u8) * cnt, GFP_KERNEL); | ||
373 | if (!dsaddr->stripe_indices) | ||
374 | goto out_err_free; | ||
375 | 429 | ||
376 | dsaddr->stripe_count = cnt; | 430 | dsaddr->stripe_count = cnt; |
431 | dsaddr->stripe_indices = stripe_indices; | ||
432 | stripe_indices = NULL; | ||
377 | dsaddr->ds_num = num; | 433 | dsaddr->ds_num = num; |
378 | 434 | ||
379 | memcpy(&dsaddr->deviceid, &pdev->dev_id, sizeof(pdev->dev_id)); | 435 | memcpy(&dsaddr->deviceid, &pdev->dev_id, sizeof(pdev->dev_id)); |
380 | 436 | ||
381 | /* Go back an read stripe indices */ | ||
382 | p = indicesp; | ||
383 | indexp = &dsaddr->stripe_indices[0]; | ||
384 | for (i = 0; i < dsaddr->stripe_count; i++) { | ||
385 | *indexp = be32_to_cpup(p++); | ||
386 | if (*indexp >= num) | ||
387 | goto out_err_free; | ||
388 | indexp++; | ||
389 | } | ||
390 | /* Skip already read multipath list count */ | ||
391 | p++; | ||
392 | |||
393 | for (i = 0; i < dsaddr->ds_num; i++) { | 437 | for (i = 0; i < dsaddr->ds_num; i++) { |
394 | int j; | 438 | int j; |
439 | u32 mp_count; | ||
440 | |||
441 | p = xdr_inline_decode(&stream, 4); | ||
442 | if (unlikely(!p)) | ||
443 | goto out_err_free_deviceid; | ||
395 | 444 | ||
396 | dummy = be32_to_cpup(p++); /* multipath count */ | 445 | mp_count = be32_to_cpup(p); /* multipath count */ |
397 | if (dummy > 1) { | 446 | if (mp_count > 1) { |
398 | printk(KERN_WARNING | 447 | printk(KERN_WARNING |
399 | "%s: Multipath count %d not supported, " | 448 | "%s: Multipath count %d not supported, " |
400 | "skipping all greater than 1\n", __func__, | 449 | "skipping all greater than 1\n", __func__, |
401 | dummy); | 450 | mp_count); |
402 | } | 451 | } |
403 | for (j = 0; j < dummy; j++) { | 452 | for (j = 0; j < mp_count; j++) { |
404 | if (j == 0) { | 453 | if (j == 0) { |
405 | dsaddr->ds_list[i] = decode_and_add_ds(&p, ino); | 454 | dsaddr->ds_list[i] = decode_and_add_ds(&stream, |
455 | ino); | ||
406 | if (dsaddr->ds_list[i] == NULL) | 456 | if (dsaddr->ds_list[i] == NULL) |
407 | goto out_err_free; | 457 | goto out_err_free_deviceid; |
408 | } else { | 458 | } else { |
409 | u32 len; | 459 | u32 len; |
410 | /* skip extra multipath */ | 460 | /* skip extra multipath */ |
411 | len = be32_to_cpup(p++); | 461 | |
412 | p += XDR_QUADLEN(len); | 462 | /* read len, skip */ |
413 | len = be32_to_cpup(p++); | 463 | p = xdr_inline_decode(&stream, 4); |
414 | p += XDR_QUADLEN(len); | 464 | if (unlikely(!p)) |
415 | continue; | 465 | goto out_err_free_deviceid; |
466 | len = be32_to_cpup(p); | ||
467 | |||
468 | p = xdr_inline_decode(&stream, len); | ||
469 | if (unlikely(!p)) | ||
470 | goto out_err_free_deviceid; | ||
471 | |||
472 | /* read len, skip */ | ||
473 | p = xdr_inline_decode(&stream, 4); | ||
474 | if (unlikely(!p)) | ||
475 | goto out_err_free_deviceid; | ||
476 | len = be32_to_cpup(p); | ||
477 | |||
478 | p = xdr_inline_decode(&stream, len); | ||
479 | if (unlikely(!p)) | ||
480 | goto out_err_free_deviceid; | ||
416 | } | 481 | } |
417 | } | 482 | } |
418 | } | 483 | } |
484 | |||
485 | __free_page(scratch); | ||
419 | return dsaddr; | 486 | return dsaddr; |
420 | 487 | ||
421 | out_err_free: | 488 | out_err_free_deviceid: |
422 | nfs4_fl_free_deviceid(dsaddr); | 489 | nfs4_fl_free_deviceid(dsaddr); |
490 | /* stripe_indicies was part of dsaddr */ | ||
491 | goto out_err_free_scratch; | ||
492 | out_err_free_stripe_indices: | ||
493 | kfree(stripe_indices); | ||
494 | out_err_free_scratch: | ||
495 | __free_page(scratch); | ||
423 | out_err: | 496 | out_err: |
424 | dprintk("%s ERROR: returning NULL\n", __func__); | 497 | dprintk("%s ERROR: returning NULL\n", __func__); |
425 | return NULL; | 498 | return NULL; |
@@ -498,11 +571,6 @@ get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id) | |||
498 | goto out_free; | 571 | goto out_free; |
499 | } | 572 | } |
500 | 573 | ||
501 | /* set pdev->area */ | ||
502 | pdev->area = vmap(pages, max_pages, VM_MAP, PAGE_KERNEL); | ||
503 | if (!pdev->area) | ||
504 | goto out_free; | ||
505 | |||
506 | memcpy(&pdev->dev_id, dev_id, sizeof(*dev_id)); | 574 | memcpy(&pdev->dev_id, dev_id, sizeof(*dev_id)); |
507 | pdev->layout_type = LAYOUT_NFSV4_1_FILES; | 575 | pdev->layout_type = LAYOUT_NFSV4_1_FILES; |
508 | pdev->pages = pages; | 576 | pdev->pages = pages; |
@@ -521,8 +589,6 @@ get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id) | |||
521 | */ | 589 | */ |
522 | dsaddr = decode_and_add_device(inode, pdev); | 590 | dsaddr = decode_and_add_device(inode, pdev); |
523 | out_free: | 591 | out_free: |
524 | if (pdev->area != NULL) | ||
525 | vunmap(pdev->area); | ||
526 | for (i = 0; i < max_pages; i++) | 592 | for (i = 0; i < max_pages; i++) |
527 | __free_page(pages[i]); | 593 | __free_page(pages[i]); |
528 | kfree(pages); | 594 | kfree(pages); |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 43045fa44710..8f071314e94b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -5526,8 +5526,6 @@ static void nfs4_layoutget_release(void *calldata) | |||
5526 | struct nfs4_layoutget *lgp = calldata; | 5526 | struct nfs4_layoutget *lgp = calldata; |
5527 | 5527 | ||
5528 | dprintk("--> %s\n", __func__); | 5528 | dprintk("--> %s\n", __func__); |
5529 | if (lgp->res.layout.buf != NULL) | ||
5530 | free_page((unsigned long) lgp->res.layout.buf); | ||
5531 | put_nfs_open_context(lgp->args.ctx); | 5529 | put_nfs_open_context(lgp->args.ctx); |
5532 | kfree(calldata); | 5530 | kfree(calldata); |
5533 | dprintk("<-- %s\n", __func__); | 5531 | dprintk("<-- %s\n", __func__); |
@@ -5559,12 +5557,7 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp) | |||
5559 | 5557 | ||
5560 | dprintk("--> %s\n", __func__); | 5558 | dprintk("--> %s\n", __func__); |
5561 | 5559 | ||
5562 | lgp->res.layout.buf = (void *)__get_free_page(GFP_NOFS); | 5560 | lgp->res.layoutp = &lgp->args.layout; |
5563 | if (lgp->res.layout.buf == NULL) { | ||
5564 | nfs4_layoutget_release(lgp); | ||
5565 | return -ENOMEM; | ||
5566 | } | ||
5567 | |||
5568 | lgp->res.seq_res.sr_slot = NULL; | 5561 | lgp->res.seq_res.sr_slot = NULL; |
5569 | task = rpc_run_task(&task_setup_data); | 5562 | task = rpc_run_task(&task_setup_data); |
5570 | if (IS_ERR(task)) | 5563 | if (IS_ERR(task)) |
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 207d399c8dee..40da65e8fa2a 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -2656,6 +2656,10 @@ static void nfs4_xdr_enc_layoutget(struct rpc_rqst *req, | |||
2656 | encode_sequence(xdr, &args->seq_args, &hdr); | 2656 | encode_sequence(xdr, &args->seq_args, &hdr); |
2657 | encode_putfh(xdr, NFS_FH(args->inode), &hdr); | 2657 | encode_putfh(xdr, NFS_FH(args->inode), &hdr); |
2658 | encode_layoutget(xdr, args, &hdr); | 2658 | encode_layoutget(xdr, args, &hdr); |
2659 | |||
2660 | xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, | ||
2661 | args->layout.pages, 0, args->layout.pglen); | ||
2662 | |||
2659 | encode_nops(&hdr); | 2663 | encode_nops(&hdr); |
2660 | } | 2664 | } |
2661 | 2665 | ||
@@ -5022,6 +5026,9 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5022 | __be32 *p; | 5026 | __be32 *p; |
5023 | int status; | 5027 | int status; |
5024 | u32 layout_count; | 5028 | u32 layout_count; |
5029 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | ||
5030 | struct kvec *iov = rcvbuf->head; | ||
5031 | u32 hdrlen, recvd; | ||
5025 | 5032 | ||
5026 | status = decode_op_hdr(xdr, OP_LAYOUTGET); | 5033 | status = decode_op_hdr(xdr, OP_LAYOUTGET); |
5027 | if (status) | 5034 | if (status) |
@@ -5038,17 +5045,14 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5038 | return -EINVAL; | 5045 | return -EINVAL; |
5039 | } | 5046 | } |
5040 | 5047 | ||
5041 | p = xdr_inline_decode(xdr, 24); | 5048 | p = xdr_inline_decode(xdr, 28); |
5042 | if (unlikely(!p)) | 5049 | if (unlikely(!p)) |
5043 | goto out_overflow; | 5050 | goto out_overflow; |
5044 | p = xdr_decode_hyper(p, &res->range.offset); | 5051 | p = xdr_decode_hyper(p, &res->range.offset); |
5045 | p = xdr_decode_hyper(p, &res->range.length); | 5052 | p = xdr_decode_hyper(p, &res->range.length); |
5046 | res->range.iomode = be32_to_cpup(p++); | 5053 | res->range.iomode = be32_to_cpup(p++); |
5047 | res->type = be32_to_cpup(p++); | 5054 | res->type = be32_to_cpup(p++); |
5048 | 5055 | res->layoutp->len = be32_to_cpup(p); | |
5049 | status = decode_opaque_inline(xdr, &res->layout.len, (char **)&p); | ||
5050 | if (unlikely(status)) | ||
5051 | return status; | ||
5052 | 5056 | ||
5053 | dprintk("%s roff:%lu rlen:%lu riomode:%d, lo_type:0x%x, lo.len:%d\n", | 5057 | dprintk("%s roff:%lu rlen:%lu riomode:%d, lo_type:0x%x, lo.len:%d\n", |
5054 | __func__, | 5058 | __func__, |
@@ -5056,12 +5060,18 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5056 | (unsigned long)res->range.length, | 5060 | (unsigned long)res->range.length, |
5057 | res->range.iomode, | 5061 | res->range.iomode, |
5058 | res->type, | 5062 | res->type, |
5059 | res->layout.len); | 5063 | res->layoutp->len); |
5064 | |||
5065 | hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base; | ||
5066 | recvd = req->rq_rcv_buf.len - hdrlen; | ||
5067 | if (res->layoutp->len > recvd) { | ||
5068 | dprintk("NFS: server cheating in layoutget reply: " | ||
5069 | "layout len %u > recvd %u\n", | ||
5070 | res->layoutp->len, recvd); | ||
5071 | return -EINVAL; | ||
5072 | } | ||
5060 | 5073 | ||
5061 | /* nfs4_proc_layoutget allocated a single page */ | 5074 | xdr_read_pages(xdr, res->layoutp->len); |
5062 | if (res->layout.len > PAGE_SIZE) | ||
5063 | return -ENOMEM; | ||
5064 | memcpy(res->layout.buf, p, res->layout.len); | ||
5065 | 5075 | ||
5066 | if (layout_count > 1) { | 5076 | if (layout_count > 1) { |
5067 | /* We only handle a length one array at the moment. Any | 5077 | /* We only handle a length one array at the moment. Any |
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 22c2ddbef420..d9ab97269ce6 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c | |||
@@ -472,6 +472,9 @@ send_layoutget(struct pnfs_layout_hdr *lo, | |||
472 | struct nfs_server *server = NFS_SERVER(ino); | 472 | struct nfs_server *server = NFS_SERVER(ino); |
473 | struct nfs4_layoutget *lgp; | 473 | struct nfs4_layoutget *lgp; |
474 | struct pnfs_layout_segment *lseg = NULL; | 474 | struct pnfs_layout_segment *lseg = NULL; |
475 | struct page **pages = NULL; | ||
476 | int i; | ||
477 | u32 max_resp_sz, max_pages; | ||
475 | 478 | ||
476 | dprintk("--> %s\n", __func__); | 479 | dprintk("--> %s\n", __func__); |
477 | 480 | ||
@@ -479,6 +482,21 @@ send_layoutget(struct pnfs_layout_hdr *lo, | |||
479 | lgp = kzalloc(sizeof(*lgp), GFP_KERNEL); | 482 | lgp = kzalloc(sizeof(*lgp), GFP_KERNEL); |
480 | if (lgp == NULL) | 483 | if (lgp == NULL) |
481 | return NULL; | 484 | return NULL; |
485 | |||
486 | /* allocate pages for xdr post processing */ | ||
487 | max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; | ||
488 | max_pages = max_resp_sz >> PAGE_SHIFT; | ||
489 | |||
490 | pages = kzalloc(max_pages * sizeof(struct page *), GFP_KERNEL); | ||
491 | if (!pages) | ||
492 | goto out_err_free; | ||
493 | |||
494 | for (i = 0; i < max_pages; i++) { | ||
495 | pages[i] = alloc_page(GFP_KERNEL); | ||
496 | if (!pages[i]) | ||
497 | goto out_err_free; | ||
498 | } | ||
499 | |||
482 | lgp->args.minlength = NFS4_MAX_UINT64; | 500 | lgp->args.minlength = NFS4_MAX_UINT64; |
483 | lgp->args.maxcount = PNFS_LAYOUT_MAXSIZE; | 501 | lgp->args.maxcount = PNFS_LAYOUT_MAXSIZE; |
484 | lgp->args.range.iomode = iomode; | 502 | lgp->args.range.iomode = iomode; |
@@ -487,6 +505,8 @@ send_layoutget(struct pnfs_layout_hdr *lo, | |||
487 | lgp->args.type = server->pnfs_curr_ld->id; | 505 | lgp->args.type = server->pnfs_curr_ld->id; |
488 | lgp->args.inode = ino; | 506 | lgp->args.inode = ino; |
489 | lgp->args.ctx = get_nfs_open_context(ctx); | 507 | lgp->args.ctx = get_nfs_open_context(ctx); |
508 | lgp->args.layout.pages = pages; | ||
509 | lgp->args.layout.pglen = max_pages * PAGE_SIZE; | ||
490 | lgp->lsegpp = &lseg; | 510 | lgp->lsegpp = &lseg; |
491 | 511 | ||
492 | /* Synchronously retrieve layout information from server and | 512 | /* Synchronously retrieve layout information from server and |
@@ -497,7 +517,26 @@ send_layoutget(struct pnfs_layout_hdr *lo, | |||
497 | /* remember that LAYOUTGET failed and suspend trying */ | 517 | /* remember that LAYOUTGET failed and suspend trying */ |
498 | set_bit(lo_fail_bit(iomode), &lo->plh_flags); | 518 | set_bit(lo_fail_bit(iomode), &lo->plh_flags); |
499 | } | 519 | } |
520 | |||
521 | /* free xdr pages */ | ||
522 | for (i = 0; i < max_pages; i++) | ||
523 | __free_page(pages[i]); | ||
524 | kfree(pages); | ||
525 | |||
500 | return lseg; | 526 | return lseg; |
527 | |||
528 | out_err_free: | ||
529 | /* free any allocated xdr pages, lgp as it's not used */ | ||
530 | if (pages) { | ||
531 | for (i = 0; i < max_pages; i++) { | ||
532 | if (!pages[i]) | ||
533 | break; | ||
534 | __free_page(pages[i]); | ||
535 | } | ||
536 | kfree(pages); | ||
537 | } | ||
538 | kfree(lgp); | ||
539 | return NULL; | ||
501 | } | 540 | } |
502 | 541 | ||
503 | bool pnfs_roc(struct inode *ino) | 542 | bool pnfs_roc(struct inode *ino) |
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 33b9ae90c6f7..bc4827202e7a 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h | |||
@@ -109,7 +109,6 @@ struct pnfs_device { | |||
109 | unsigned int layout_type; | 109 | unsigned int layout_type; |
110 | unsigned int mincount; | 110 | unsigned int mincount; |
111 | struct page **pages; | 111 | struct page **pages; |
112 | void *area; | ||
113 | unsigned int pgbase; | 112 | unsigned int pgbase; |
114 | unsigned int pglen; | 113 | unsigned int pglen; |
115 | }; | 114 | }; |
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 84f3585c5728..a6e21b10f43d 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h | |||
@@ -190,8 +190,9 @@ struct nfs4_get_lease_time_res { | |||
190 | #define PNFS_LAYOUT_MAXSIZE 4096 | 190 | #define PNFS_LAYOUT_MAXSIZE 4096 |
191 | 191 | ||
192 | struct nfs4_layoutdriver_data { | 192 | struct nfs4_layoutdriver_data { |
193 | struct page **pages; | ||
194 | __u32 pglen; | ||
193 | __u32 len; | 195 | __u32 len; |
194 | void *buf; | ||
195 | }; | 196 | }; |
196 | 197 | ||
197 | struct pnfs_layout_range { | 198 | struct pnfs_layout_range { |
@@ -209,6 +210,7 @@ struct nfs4_layoutget_args { | |||
209 | struct nfs_open_context *ctx; | 210 | struct nfs_open_context *ctx; |
210 | struct nfs4_sequence_args seq_args; | 211 | struct nfs4_sequence_args seq_args; |
211 | nfs4_stateid stateid; | 212 | nfs4_stateid stateid; |
213 | struct nfs4_layoutdriver_data layout; | ||
212 | }; | 214 | }; |
213 | 215 | ||
214 | struct nfs4_layoutget_res { | 216 | struct nfs4_layoutget_res { |
@@ -216,8 +218,8 @@ struct nfs4_layoutget_res { | |||
216 | struct pnfs_layout_range range; | 218 | struct pnfs_layout_range range; |
217 | __u32 type; | 219 | __u32 type; |
218 | nfs4_stateid stateid; | 220 | nfs4_stateid stateid; |
219 | struct nfs4_layoutdriver_data layout; | ||
220 | struct nfs4_sequence_res seq_res; | 221 | struct nfs4_sequence_res seq_res; |
222 | struct nfs4_layoutdriver_data *layoutp; | ||
221 | }; | 223 | }; |
222 | 224 | ||
223 | struct nfs4_layoutget { | 225 | struct nfs4_layoutget { |