aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWeston Andros Adamson <dros@netapp.com>2011-03-24 16:48:21 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2011-03-24 17:01:41 -0400
commit35124a0994fc02545b14b9fa3aad000b3331f1c0 (patch)
tree5149267f387199fd9ca2718c74d86b6779013501
parentef31153786bc1e4304e6b9422cc8b9efef455611 (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.c53
-rw-r--r--fs/nfs/nfs4filelayoutdev.c178
-rw-r--r--fs/nfs/nfs4proc.c9
-rw-r--r--fs/nfs/nfs4xdr.c30
-rw-r--r--fs/nfs/pnfs.c39
-rw-r--r--fs/nfs/pnfs.h1
-rw-r--r--include/linux/nfs_xdr.h6
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
588out_err_free:
589 filelayout_free_fh_array(fl);
590out_err:
591 __free_page(scratch);
592 return -EIO;
558} 593}
559 594
560static void 595static 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 */
263static struct nfs4_pnfs_ds * 263static struct nfs4_pnfs_ds *
264decode_and_add_ds(__be32 **pp, struct inode *inode) 264decode_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:
336static struct nfs4_file_layout_dsaddr* 345static struct nfs4_file_layout_dsaddr*
337decode_device(struct inode *ino, struct pnfs_device *pdev) 346decode_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
421out_err_free: 488out_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;
492out_err_free_stripe_indices:
493 kfree(stripe_indices);
494out_err_free_scratch:
495 __free_page(scratch);
423out_err: 496out_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);
523out_free: 591out_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
528out_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
503bool pnfs_roc(struct inode *ino) 542bool 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
192struct nfs4_layoutdriver_data { 192struct nfs4_layoutdriver_data {
193 struct page **pages;
194 __u32 pglen;
193 __u32 len; 195 __u32 len;
194 void *buf;
195}; 196};
196 197
197struct pnfs_layout_range { 198struct 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
214struct nfs4_layoutget_res { 216struct 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
223struct nfs4_layoutget { 225struct nfs4_layoutget {