.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * linux/net/sunrpc/xdr.c |
---|
3 | 4 | * |
---|
.. | .. |
---|
15 | 16 | #include <linux/errno.h> |
---|
16 | 17 | #include <linux/sunrpc/xdr.h> |
---|
17 | 18 | #include <linux/sunrpc/msg_prot.h> |
---|
| 19 | +#include <linux/bvec.h> |
---|
| 20 | +#include <trace/events/sunrpc.h> |
---|
| 21 | + |
---|
| 22 | +static void _copy_to_pages(struct page **, size_t, const char *, size_t); |
---|
| 23 | + |
---|
18 | 24 | |
---|
19 | 25 | /* |
---|
20 | 26 | * XDR functions for basic NFS types |
---|
.. | .. |
---|
128 | 134 | } |
---|
129 | 135 | EXPORT_SYMBOL_GPL(xdr_terminate_string); |
---|
130 | 136 | |
---|
| 137 | +size_t |
---|
| 138 | +xdr_buf_pagecount(struct xdr_buf *buf) |
---|
| 139 | +{ |
---|
| 140 | + if (!buf->page_len) |
---|
| 141 | + return 0; |
---|
| 142 | + return (buf->page_base + buf->page_len + PAGE_SIZE - 1) >> PAGE_SHIFT; |
---|
| 143 | +} |
---|
| 144 | + |
---|
| 145 | +int |
---|
| 146 | +xdr_alloc_bvec(struct xdr_buf *buf, gfp_t gfp) |
---|
| 147 | +{ |
---|
| 148 | + size_t i, n = xdr_buf_pagecount(buf); |
---|
| 149 | + |
---|
| 150 | + if (n != 0 && buf->bvec == NULL) { |
---|
| 151 | + buf->bvec = kmalloc_array(n, sizeof(buf->bvec[0]), gfp); |
---|
| 152 | + if (!buf->bvec) |
---|
| 153 | + return -ENOMEM; |
---|
| 154 | + for (i = 0; i < n; i++) { |
---|
| 155 | + buf->bvec[i].bv_page = buf->pages[i]; |
---|
| 156 | + buf->bvec[i].bv_len = PAGE_SIZE; |
---|
| 157 | + buf->bvec[i].bv_offset = 0; |
---|
| 158 | + } |
---|
| 159 | + } |
---|
| 160 | + return 0; |
---|
| 161 | +} |
---|
| 162 | + |
---|
| 163 | +void |
---|
| 164 | +xdr_free_bvec(struct xdr_buf *buf) |
---|
| 165 | +{ |
---|
| 166 | + kfree(buf->bvec); |
---|
| 167 | + buf->bvec = NULL; |
---|
| 168 | +} |
---|
| 169 | + |
---|
| 170 | +/** |
---|
| 171 | + * xdr_inline_pages - Prepare receive buffer for a large reply |
---|
| 172 | + * @xdr: xdr_buf into which reply will be placed |
---|
| 173 | + * @offset: expected offset where data payload will start, in bytes |
---|
| 174 | + * @pages: vector of struct page pointers |
---|
| 175 | + * @base: offset in first page where receive should start, in bytes |
---|
| 176 | + * @len: expected size of the upper layer data payload, in bytes |
---|
| 177 | + * |
---|
| 178 | + */ |
---|
131 | 179 | void |
---|
132 | 180 | xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, |
---|
133 | 181 | struct page **pages, unsigned int base, unsigned int len) |
---|
.. | .. |
---|
145 | 193 | |
---|
146 | 194 | tail->iov_base = buf + offset; |
---|
147 | 195 | tail->iov_len = buflen - offset; |
---|
| 196 | + if ((xdr->page_len & 3) == 0) |
---|
| 197 | + tail->iov_len -= sizeof(__be32); |
---|
148 | 198 | |
---|
149 | 199 | xdr->buflen += len; |
---|
150 | 200 | } |
---|
.. | .. |
---|
153 | 203 | /* |
---|
154 | 204 | * Helper routines for doing 'memmove' like operations on a struct xdr_buf |
---|
155 | 205 | */ |
---|
| 206 | + |
---|
| 207 | +/** |
---|
| 208 | + * _shift_data_left_pages |
---|
| 209 | + * @pages: vector of pages containing both the source and dest memory area. |
---|
| 210 | + * @pgto_base: page vector address of destination |
---|
| 211 | + * @pgfrom_base: page vector address of source |
---|
| 212 | + * @len: number of bytes to copy |
---|
| 213 | + * |
---|
| 214 | + * Note: the addresses pgto_base and pgfrom_base are both calculated in |
---|
| 215 | + * the same way: |
---|
| 216 | + * if a memory area starts at byte 'base' in page 'pages[i]', |
---|
| 217 | + * then its address is given as (i << PAGE_CACHE_SHIFT) + base |
---|
| 218 | + * Alse note: pgto_base must be < pgfrom_base, but the memory areas |
---|
| 219 | + * they point to may overlap. |
---|
| 220 | + */ |
---|
| 221 | +static void |
---|
| 222 | +_shift_data_left_pages(struct page **pages, size_t pgto_base, |
---|
| 223 | + size_t pgfrom_base, size_t len) |
---|
| 224 | +{ |
---|
| 225 | + struct page **pgfrom, **pgto; |
---|
| 226 | + char *vfrom, *vto; |
---|
| 227 | + size_t copy; |
---|
| 228 | + |
---|
| 229 | + BUG_ON(pgfrom_base <= pgto_base); |
---|
| 230 | + |
---|
| 231 | + pgto = pages + (pgto_base >> PAGE_SHIFT); |
---|
| 232 | + pgfrom = pages + (pgfrom_base >> PAGE_SHIFT); |
---|
| 233 | + |
---|
| 234 | + pgto_base &= ~PAGE_MASK; |
---|
| 235 | + pgfrom_base &= ~PAGE_MASK; |
---|
| 236 | + |
---|
| 237 | + do { |
---|
| 238 | + if (pgto_base >= PAGE_SIZE) { |
---|
| 239 | + pgto_base = 0; |
---|
| 240 | + pgto++; |
---|
| 241 | + } |
---|
| 242 | + if (pgfrom_base >= PAGE_SIZE){ |
---|
| 243 | + pgfrom_base = 0; |
---|
| 244 | + pgfrom++; |
---|
| 245 | + } |
---|
| 246 | + |
---|
| 247 | + copy = len; |
---|
| 248 | + if (copy > (PAGE_SIZE - pgto_base)) |
---|
| 249 | + copy = PAGE_SIZE - pgto_base; |
---|
| 250 | + if (copy > (PAGE_SIZE - pgfrom_base)) |
---|
| 251 | + copy = PAGE_SIZE - pgfrom_base; |
---|
| 252 | + |
---|
| 253 | + vto = kmap_atomic(*pgto); |
---|
| 254 | + if (*pgto != *pgfrom) { |
---|
| 255 | + vfrom = kmap_atomic(*pgfrom); |
---|
| 256 | + memcpy(vto + pgto_base, vfrom + pgfrom_base, copy); |
---|
| 257 | + kunmap_atomic(vfrom); |
---|
| 258 | + } else |
---|
| 259 | + memmove(vto + pgto_base, vto + pgfrom_base, copy); |
---|
| 260 | + flush_dcache_page(*pgto); |
---|
| 261 | + kunmap_atomic(vto); |
---|
| 262 | + |
---|
| 263 | + pgto_base += copy; |
---|
| 264 | + pgfrom_base += copy; |
---|
| 265 | + |
---|
| 266 | + } while ((len -= copy) != 0); |
---|
| 267 | +} |
---|
| 268 | + |
---|
| 269 | +static void |
---|
| 270 | +_shift_data_left_tail(struct xdr_buf *buf, unsigned int pgto, size_t len) |
---|
| 271 | +{ |
---|
| 272 | + struct kvec *tail = buf->tail; |
---|
| 273 | + |
---|
| 274 | + if (len > tail->iov_len) |
---|
| 275 | + len = tail->iov_len; |
---|
| 276 | + |
---|
| 277 | + _copy_to_pages(buf->pages, |
---|
| 278 | + buf->page_base + pgto, |
---|
| 279 | + (char *)tail->iov_base, |
---|
| 280 | + len); |
---|
| 281 | + tail->iov_len -= len; |
---|
| 282 | + |
---|
| 283 | + if (tail->iov_len > 0) |
---|
| 284 | + memmove((char *)tail->iov_base, |
---|
| 285 | + tail->iov_base + len, |
---|
| 286 | + tail->iov_len); |
---|
| 287 | +} |
---|
156 | 288 | |
---|
157 | 289 | /** |
---|
158 | 290 | * _shift_data_right_pages |
---|
.. | .. |
---|
217 | 349 | kunmap_atomic(vto); |
---|
218 | 350 | |
---|
219 | 351 | } while ((len -= copy) != 0); |
---|
| 352 | +} |
---|
| 353 | + |
---|
| 354 | +static unsigned int |
---|
| 355 | +_shift_data_right_tail(struct xdr_buf *buf, unsigned int pgfrom, size_t len) |
---|
| 356 | +{ |
---|
| 357 | + struct kvec *tail = buf->tail; |
---|
| 358 | + unsigned int tailbuf_len; |
---|
| 359 | + unsigned int result = 0; |
---|
| 360 | + size_t copy; |
---|
| 361 | + |
---|
| 362 | + tailbuf_len = buf->buflen - buf->head->iov_len - buf->page_len; |
---|
| 363 | + |
---|
| 364 | + /* Shift the tail first */ |
---|
| 365 | + if (tailbuf_len != 0) { |
---|
| 366 | + unsigned int free_space = tailbuf_len - tail->iov_len; |
---|
| 367 | + |
---|
| 368 | + if (len < free_space) |
---|
| 369 | + free_space = len; |
---|
| 370 | + if (len > free_space) |
---|
| 371 | + len = free_space; |
---|
| 372 | + |
---|
| 373 | + tail->iov_len += free_space; |
---|
| 374 | + copy = len; |
---|
| 375 | + |
---|
| 376 | + if (tail->iov_len > len) { |
---|
| 377 | + char *p = (char *)tail->iov_base + len; |
---|
| 378 | + memmove(p, tail->iov_base, tail->iov_len - free_space); |
---|
| 379 | + result += tail->iov_len - free_space; |
---|
| 380 | + } else |
---|
| 381 | + copy = tail->iov_len; |
---|
| 382 | + |
---|
| 383 | + /* Copy from the inlined pages into the tail */ |
---|
| 384 | + _copy_from_pages((char *)tail->iov_base, |
---|
| 385 | + buf->pages, |
---|
| 386 | + buf->page_base + pgfrom, |
---|
| 387 | + copy); |
---|
| 388 | + result += copy; |
---|
| 389 | + } |
---|
| 390 | + |
---|
| 391 | + return result; |
---|
220 | 392 | } |
---|
221 | 393 | |
---|
222 | 394 | /** |
---|
.. | .. |
---|
304 | 476 | EXPORT_SYMBOL_GPL(_copy_from_pages); |
---|
305 | 477 | |
---|
306 | 478 | /** |
---|
| 479 | + * _zero_pages |
---|
| 480 | + * @pages: array of pages |
---|
| 481 | + * @pgbase: beginning page vector address |
---|
| 482 | + * @len: length |
---|
| 483 | + */ |
---|
| 484 | +static void |
---|
| 485 | +_zero_pages(struct page **pages, size_t pgbase, size_t len) |
---|
| 486 | +{ |
---|
| 487 | + struct page **page; |
---|
| 488 | + char *vpage; |
---|
| 489 | + size_t zero; |
---|
| 490 | + |
---|
| 491 | + page = pages + (pgbase >> PAGE_SHIFT); |
---|
| 492 | + pgbase &= ~PAGE_MASK; |
---|
| 493 | + |
---|
| 494 | + do { |
---|
| 495 | + zero = PAGE_SIZE - pgbase; |
---|
| 496 | + if (zero > len) |
---|
| 497 | + zero = len; |
---|
| 498 | + |
---|
| 499 | + vpage = kmap_atomic(*page); |
---|
| 500 | + memset(vpage + pgbase, 0, zero); |
---|
| 501 | + kunmap_atomic(vpage); |
---|
| 502 | + |
---|
| 503 | + flush_dcache_page(*page); |
---|
| 504 | + pgbase = 0; |
---|
| 505 | + page++; |
---|
| 506 | + |
---|
| 507 | + } while ((len -= zero) != 0); |
---|
| 508 | +} |
---|
| 509 | + |
---|
| 510 | +/** |
---|
307 | 511 | * xdr_shrink_bufhead |
---|
308 | 512 | * @buf: xdr_buf |
---|
309 | 513 | * @len: bytes to remove from buf->head[0] |
---|
.. | .. |
---|
312 | 516 | * 'len' bytes. The extra data is not lost, but is instead |
---|
313 | 517 | * moved into the inlined pages and/or the tail. |
---|
314 | 518 | */ |
---|
315 | | -static void |
---|
| 519 | +static unsigned int |
---|
316 | 520 | xdr_shrink_bufhead(struct xdr_buf *buf, size_t len) |
---|
317 | 521 | { |
---|
318 | 522 | struct kvec *head, *tail; |
---|
319 | 523 | size_t copy, offs; |
---|
320 | 524 | unsigned int pglen = buf->page_len; |
---|
| 525 | + unsigned int result; |
---|
321 | 526 | |
---|
| 527 | + result = 0; |
---|
322 | 528 | tail = buf->tail; |
---|
323 | 529 | head = buf->head; |
---|
324 | 530 | |
---|
.. | .. |
---|
332 | 538 | copy = tail->iov_len - len; |
---|
333 | 539 | memmove((char *)tail->iov_base + len, |
---|
334 | 540 | tail->iov_base, copy); |
---|
| 541 | + result += copy; |
---|
335 | 542 | } |
---|
336 | 543 | /* Copy from the inlined pages into the tail */ |
---|
337 | 544 | copy = len; |
---|
.. | .. |
---|
342 | 549 | copy = 0; |
---|
343 | 550 | else if (copy > tail->iov_len - offs) |
---|
344 | 551 | copy = tail->iov_len - offs; |
---|
345 | | - if (copy != 0) |
---|
| 552 | + if (copy != 0) { |
---|
346 | 553 | _copy_from_pages((char *)tail->iov_base + offs, |
---|
347 | 554 | buf->pages, |
---|
348 | 555 | buf->page_base + pglen + offs - len, |
---|
349 | 556 | copy); |
---|
| 557 | + result += copy; |
---|
| 558 | + } |
---|
350 | 559 | /* Do we also need to copy data from the head into the tail ? */ |
---|
351 | 560 | if (len > pglen) { |
---|
352 | 561 | offs = copy = len - pglen; |
---|
.. | .. |
---|
356 | 565 | (char *)head->iov_base + |
---|
357 | 566 | head->iov_len - offs, |
---|
358 | 567 | copy); |
---|
| 568 | + result += copy; |
---|
359 | 569 | } |
---|
360 | 570 | } |
---|
361 | 571 | /* Now handle pages */ |
---|
.. | .. |
---|
371 | 581 | _copy_to_pages(buf->pages, buf->page_base, |
---|
372 | 582 | (char *)head->iov_base + head->iov_len - len, |
---|
373 | 583 | copy); |
---|
| 584 | + result += copy; |
---|
374 | 585 | } |
---|
375 | 586 | head->iov_len -= len; |
---|
376 | 587 | buf->buflen -= len; |
---|
377 | 588 | /* Have we truncated the message? */ |
---|
378 | 589 | if (buf->len > buf->buflen) |
---|
379 | 590 | buf->len = buf->buflen; |
---|
| 591 | + |
---|
| 592 | + return result; |
---|
380 | 593 | } |
---|
381 | 594 | |
---|
382 | 595 | /** |
---|
383 | | - * xdr_shrink_pagelen |
---|
| 596 | + * xdr_shrink_pagelen - shrinks buf->pages by up to @len bytes |
---|
384 | 597 | * @buf: xdr_buf |
---|
385 | 598 | * @len: bytes to remove from buf->pages |
---|
386 | 599 | * |
---|
387 | | - * Shrinks XDR buffer's page array buf->pages by |
---|
388 | | - * 'len' bytes. The extra data is not lost, but is instead |
---|
389 | | - * moved into the tail. |
---|
| 600 | + * The extra data is not lost, but is instead moved into buf->tail. |
---|
| 601 | + * Returns the actual number of bytes moved. |
---|
390 | 602 | */ |
---|
391 | | -static void |
---|
| 603 | +static unsigned int |
---|
392 | 604 | xdr_shrink_pagelen(struct xdr_buf *buf, size_t len) |
---|
393 | 605 | { |
---|
394 | | - struct kvec *tail; |
---|
395 | | - size_t copy; |
---|
396 | 606 | unsigned int pglen = buf->page_len; |
---|
397 | | - unsigned int tailbuf_len; |
---|
| 607 | + unsigned int result; |
---|
398 | 608 | |
---|
399 | | - tail = buf->tail; |
---|
400 | | - BUG_ON (len > pglen); |
---|
| 609 | + if (len > buf->page_len) |
---|
| 610 | + len = buf-> page_len; |
---|
401 | 611 | |
---|
402 | | - tailbuf_len = buf->buflen - buf->head->iov_len - buf->page_len; |
---|
403 | | - |
---|
404 | | - /* Shift the tail first */ |
---|
405 | | - if (tailbuf_len != 0) { |
---|
406 | | - unsigned int free_space = tailbuf_len - tail->iov_len; |
---|
407 | | - |
---|
408 | | - if (len < free_space) |
---|
409 | | - free_space = len; |
---|
410 | | - tail->iov_len += free_space; |
---|
411 | | - |
---|
412 | | - copy = len; |
---|
413 | | - if (tail->iov_len > len) { |
---|
414 | | - char *p = (char *)tail->iov_base + len; |
---|
415 | | - memmove(p, tail->iov_base, tail->iov_len - len); |
---|
416 | | - } else |
---|
417 | | - copy = tail->iov_len; |
---|
418 | | - /* Copy from the inlined pages into the tail */ |
---|
419 | | - _copy_from_pages((char *)tail->iov_base, |
---|
420 | | - buf->pages, buf->page_base + pglen - len, |
---|
421 | | - copy); |
---|
422 | | - } |
---|
| 612 | + result = _shift_data_right_tail(buf, pglen - len, len); |
---|
423 | 613 | buf->page_len -= len; |
---|
424 | 614 | buf->buflen -= len; |
---|
425 | 615 | /* Have we truncated the message? */ |
---|
426 | 616 | if (buf->len > buf->buflen) |
---|
427 | 617 | buf->len = buf->buflen; |
---|
| 618 | + |
---|
| 619 | + return result; |
---|
428 | 620 | } |
---|
429 | 621 | |
---|
430 | 622 | void |
---|
.. | .. |
---|
445 | 637 | EXPORT_SYMBOL_GPL(xdr_stream_pos); |
---|
446 | 638 | |
---|
447 | 639 | /** |
---|
| 640 | + * xdr_page_pos - Return the current offset from the start of the xdr pages |
---|
| 641 | + * @xdr: pointer to struct xdr_stream |
---|
| 642 | + */ |
---|
| 643 | +unsigned int xdr_page_pos(const struct xdr_stream *xdr) |
---|
| 644 | +{ |
---|
| 645 | + unsigned int pos = xdr_stream_pos(xdr); |
---|
| 646 | + |
---|
| 647 | + WARN_ON(pos < xdr->buf->head[0].iov_len); |
---|
| 648 | + return pos - xdr->buf->head[0].iov_len; |
---|
| 649 | +} |
---|
| 650 | +EXPORT_SYMBOL_GPL(xdr_page_pos); |
---|
| 651 | + |
---|
| 652 | +/** |
---|
448 | 653 | * xdr_init_encode - Initialize a struct xdr_stream for sending data. |
---|
449 | 654 | * @xdr: pointer to xdr_stream struct |
---|
450 | 655 | * @buf: pointer to XDR buffer in which to encode data |
---|
451 | 656 | * @p: current pointer inside XDR buffer |
---|
| 657 | + * @rqst: pointer to controlling rpc_rqst, for debugging |
---|
452 | 658 | * |
---|
453 | 659 | * Note: at the moment the RPC client only passes the length of our |
---|
454 | 660 | * scratch buffer in the xdr_buf's header kvec. Previously this |
---|
.. | .. |
---|
457 | 663 | * of the buffer length, and takes care of adjusting the kvec |
---|
458 | 664 | * length for us. |
---|
459 | 665 | */ |
---|
460 | | -void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) |
---|
| 666 | +void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p, |
---|
| 667 | + struct rpc_rqst *rqst) |
---|
461 | 668 | { |
---|
462 | 669 | struct kvec *iov = buf->head; |
---|
463 | 670 | int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len; |
---|
.. | .. |
---|
479 | 686 | buf->len += len; |
---|
480 | 687 | iov->iov_len += len; |
---|
481 | 688 | } |
---|
| 689 | + xdr->rqst = rqst; |
---|
482 | 690 | } |
---|
483 | 691 | EXPORT_SYMBOL_GPL(xdr_init_encode); |
---|
484 | 692 | |
---|
.. | .. |
---|
495 | 703 | * required at the end of encoding, or any other time when the xdr_buf |
---|
496 | 704 | * data might be read. |
---|
497 | 705 | */ |
---|
498 | | -void xdr_commit_encode(struct xdr_stream *xdr) |
---|
| 706 | +inline void xdr_commit_encode(struct xdr_stream *xdr) |
---|
499 | 707 | { |
---|
500 | 708 | int shift = xdr->scratch.iov_len; |
---|
501 | 709 | void *page; |
---|
.. | .. |
---|
517 | 725 | int frag1bytes, frag2bytes; |
---|
518 | 726 | |
---|
519 | 727 | if (nbytes > PAGE_SIZE) |
---|
520 | | - return NULL; /* Bigger buffers require special handling */ |
---|
| 728 | + goto out_overflow; /* Bigger buffers require special handling */ |
---|
521 | 729 | if (xdr->buf->len + nbytes > xdr->buf->buflen) |
---|
522 | | - return NULL; /* Sorry, we're totally out of space */ |
---|
| 730 | + goto out_overflow; /* Sorry, we're totally out of space */ |
---|
523 | 731 | frag1bytes = (xdr->end - xdr->p) << 2; |
---|
524 | 732 | frag2bytes = nbytes - frag1bytes; |
---|
525 | 733 | if (xdr->iov) |
---|
.. | .. |
---|
544 | 752 | */ |
---|
545 | 753 | xdr->p = (void *)p + frag2bytes; |
---|
546 | 754 | space_left = xdr->buf->buflen - xdr->buf->len; |
---|
547 | | - xdr->end = (void *)p + min_t(int, space_left, PAGE_SIZE); |
---|
| 755 | + if (space_left - frag1bytes >= PAGE_SIZE) |
---|
| 756 | + xdr->end = (void *)p + PAGE_SIZE; |
---|
| 757 | + else |
---|
| 758 | + xdr->end = (void *)p + space_left - frag1bytes; |
---|
| 759 | + |
---|
548 | 760 | xdr->buf->page_len += frag2bytes; |
---|
549 | 761 | xdr->buf->len += nbytes; |
---|
550 | 762 | return p; |
---|
| 763 | +out_overflow: |
---|
| 764 | + trace_rpc_xdr_overflow(xdr, nbytes); |
---|
| 765 | + return NULL; |
---|
551 | 766 | } |
---|
552 | 767 | |
---|
553 | 768 | /** |
---|
.. | .. |
---|
581 | 796 | } |
---|
582 | 797 | EXPORT_SYMBOL_GPL(xdr_reserve_space); |
---|
583 | 798 | |
---|
| 799 | + |
---|
| 800 | +/** |
---|
| 801 | + * xdr_reserve_space_vec - Reserves a large amount of buffer space for sending |
---|
| 802 | + * @xdr: pointer to xdr_stream |
---|
| 803 | + * @vec: pointer to a kvec array |
---|
| 804 | + * @nbytes: number of bytes to reserve |
---|
| 805 | + * |
---|
| 806 | + * Reserves enough buffer space to encode 'nbytes' of data and stores the |
---|
| 807 | + * pointers in 'vec'. The size argument passed to xdr_reserve_space() is |
---|
| 808 | + * determined based on the number of bytes remaining in the current page to |
---|
| 809 | + * avoid invalidating iov_base pointers when xdr_commit_encode() is called. |
---|
| 810 | + */ |
---|
| 811 | +int xdr_reserve_space_vec(struct xdr_stream *xdr, struct kvec *vec, size_t nbytes) |
---|
| 812 | +{ |
---|
| 813 | + int thislen; |
---|
| 814 | + int v = 0; |
---|
| 815 | + __be32 *p; |
---|
| 816 | + |
---|
| 817 | + /* |
---|
| 818 | + * svcrdma requires every READ payload to start somewhere |
---|
| 819 | + * in xdr->pages. |
---|
| 820 | + */ |
---|
| 821 | + if (xdr->iov == xdr->buf->head) { |
---|
| 822 | + xdr->iov = NULL; |
---|
| 823 | + xdr->end = xdr->p; |
---|
| 824 | + } |
---|
| 825 | + |
---|
| 826 | + while (nbytes) { |
---|
| 827 | + thislen = xdr->buf->page_len % PAGE_SIZE; |
---|
| 828 | + thislen = min_t(size_t, nbytes, PAGE_SIZE - thislen); |
---|
| 829 | + |
---|
| 830 | + p = xdr_reserve_space(xdr, thislen); |
---|
| 831 | + if (!p) |
---|
| 832 | + return -EIO; |
---|
| 833 | + |
---|
| 834 | + vec[v].iov_base = p; |
---|
| 835 | + vec[v].iov_len = thislen; |
---|
| 836 | + v++; |
---|
| 837 | + nbytes -= thislen; |
---|
| 838 | + } |
---|
| 839 | + |
---|
| 840 | + return v; |
---|
| 841 | +} |
---|
| 842 | +EXPORT_SYMBOL_GPL(xdr_reserve_space_vec); |
---|
| 843 | + |
---|
584 | 844 | /** |
---|
585 | 845 | * xdr_truncate_encode - truncate an encode buffer |
---|
586 | 846 | * @xdr: pointer to xdr_stream |
---|
.. | .. |
---|
591 | 851 | * head, tail, and page lengths are adjusted to correspond. |
---|
592 | 852 | * |
---|
593 | 853 | * If this means moving xdr->p to a different buffer, we assume that |
---|
594 | | - * that the end pointer should be set to the end of the current page, |
---|
| 854 | + * the end pointer should be set to the end of the current page, |
---|
595 | 855 | * except in the case of the head buffer when we assume the head |
---|
596 | 856 | * buffer's current length represents the end of the available buffer. |
---|
597 | 857 | * |
---|
.. | .. |
---|
758 | 1018 | return 0; |
---|
759 | 1019 | } |
---|
760 | 1020 | |
---|
| 1021 | +static void xdr_set_page(struct xdr_stream *xdr, unsigned int base, |
---|
| 1022 | + unsigned int len) |
---|
| 1023 | +{ |
---|
| 1024 | + if (xdr_set_page_base(xdr, base, len) < 0) |
---|
| 1025 | + xdr_set_iov(xdr, xdr->buf->tail, xdr->nwords << 2); |
---|
| 1026 | +} |
---|
| 1027 | + |
---|
761 | 1028 | static void xdr_set_next_page(struct xdr_stream *xdr) |
---|
762 | 1029 | { |
---|
763 | 1030 | unsigned int newbase; |
---|
.. | .. |
---|
765 | 1032 | newbase = (1 + xdr->page_ptr - xdr->buf->pages) << PAGE_SHIFT; |
---|
766 | 1033 | newbase -= xdr->buf->page_base; |
---|
767 | 1034 | |
---|
768 | | - if (xdr_set_page_base(xdr, newbase, PAGE_SIZE) < 0) |
---|
769 | | - xdr_set_iov(xdr, xdr->buf->tail, xdr->nwords << 2); |
---|
| 1035 | + xdr_set_page(xdr, newbase, PAGE_SIZE); |
---|
770 | 1036 | } |
---|
771 | 1037 | |
---|
772 | 1038 | static bool xdr_set_next_buffer(struct xdr_stream *xdr) |
---|
.. | .. |
---|
774 | 1040 | if (xdr->page_ptr != NULL) |
---|
775 | 1041 | xdr_set_next_page(xdr); |
---|
776 | 1042 | else if (xdr->iov == xdr->buf->head) { |
---|
777 | | - if (xdr_set_page_base(xdr, 0, PAGE_SIZE) < 0) |
---|
778 | | - xdr_set_iov(xdr, xdr->buf->tail, xdr->nwords << 2); |
---|
| 1043 | + xdr_set_page(xdr, 0, PAGE_SIZE); |
---|
779 | 1044 | } |
---|
780 | 1045 | return xdr->p != xdr->end; |
---|
781 | 1046 | } |
---|
.. | .. |
---|
785 | 1050 | * @xdr: pointer to xdr_stream struct |
---|
786 | 1051 | * @buf: pointer to XDR buffer from which to decode data |
---|
787 | 1052 | * @p: current pointer inside XDR buffer |
---|
| 1053 | + * @rqst: pointer to controlling rpc_rqst, for debugging |
---|
788 | 1054 | */ |
---|
789 | | -void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) |
---|
| 1055 | +void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p, |
---|
| 1056 | + struct rpc_rqst *rqst) |
---|
790 | 1057 | { |
---|
791 | 1058 | xdr->buf = buf; |
---|
792 | 1059 | xdr->scratch.iov_base = NULL; |
---|
.. | .. |
---|
802 | 1069 | xdr->nwords -= p - xdr->p; |
---|
803 | 1070 | xdr->p = p; |
---|
804 | 1071 | } |
---|
| 1072 | + xdr->rqst = rqst; |
---|
805 | 1073 | } |
---|
806 | 1074 | EXPORT_SYMBOL_GPL(xdr_init_decode); |
---|
807 | 1075 | |
---|
.. | .. |
---|
820 | 1088 | buf->page_len = len; |
---|
821 | 1089 | buf->buflen = len; |
---|
822 | 1090 | buf->len = len; |
---|
823 | | - xdr_init_decode(xdr, buf, NULL); |
---|
| 1091 | + xdr_init_decode(xdr, buf, NULL, NULL); |
---|
824 | 1092 | } |
---|
825 | 1093 | EXPORT_SYMBOL_GPL(xdr_init_decode_pages); |
---|
826 | 1094 | |
---|
.. | .. |
---|
862 | 1130 | size_t cplen = (char *)xdr->end - (char *)xdr->p; |
---|
863 | 1131 | |
---|
864 | 1132 | if (nbytes > xdr->scratch.iov_len) |
---|
865 | | - return NULL; |
---|
| 1133 | + goto out_overflow; |
---|
866 | 1134 | p = __xdr_inline_decode(xdr, cplen); |
---|
867 | 1135 | if (p == NULL) |
---|
868 | 1136 | return NULL; |
---|
869 | 1137 | memcpy(cpdest, p, cplen); |
---|
| 1138 | + if (!xdr_set_next_buffer(xdr)) |
---|
| 1139 | + goto out_overflow; |
---|
870 | 1140 | cpdest += cplen; |
---|
871 | 1141 | nbytes -= cplen; |
---|
872 | | - if (!xdr_set_next_buffer(xdr)) |
---|
873 | | - return NULL; |
---|
874 | 1142 | p = __xdr_inline_decode(xdr, nbytes); |
---|
875 | 1143 | if (p == NULL) |
---|
876 | 1144 | return NULL; |
---|
877 | 1145 | memcpy(cpdest, p, nbytes); |
---|
878 | 1146 | return xdr->scratch.iov_base; |
---|
| 1147 | +out_overflow: |
---|
| 1148 | + trace_rpc_xdr_overflow(xdr, nbytes); |
---|
| 1149 | + return NULL; |
---|
879 | 1150 | } |
---|
880 | 1151 | |
---|
881 | 1152 | /** |
---|
.. | .. |
---|
892 | 1163 | { |
---|
893 | 1164 | __be32 *p; |
---|
894 | 1165 | |
---|
895 | | - if (nbytes == 0) |
---|
| 1166 | + if (unlikely(nbytes == 0)) |
---|
896 | 1167 | return xdr->p; |
---|
897 | 1168 | if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr)) |
---|
898 | | - return NULL; |
---|
| 1169 | + goto out_overflow; |
---|
899 | 1170 | p = __xdr_inline_decode(xdr, nbytes); |
---|
900 | 1171 | if (p != NULL) |
---|
901 | 1172 | return p; |
---|
902 | 1173 | return xdr_copy_to_scratch(xdr, nbytes); |
---|
| 1174 | +out_overflow: |
---|
| 1175 | + trace_rpc_xdr_overflow(xdr, nbytes); |
---|
| 1176 | + return NULL; |
---|
903 | 1177 | } |
---|
904 | 1178 | EXPORT_SYMBOL_GPL(xdr_inline_decode); |
---|
| 1179 | + |
---|
| 1180 | +static void xdr_realign_pages(struct xdr_stream *xdr) |
---|
| 1181 | +{ |
---|
| 1182 | + struct xdr_buf *buf = xdr->buf; |
---|
| 1183 | + struct kvec *iov = buf->head; |
---|
| 1184 | + unsigned int cur = xdr_stream_pos(xdr); |
---|
| 1185 | + unsigned int copied, offset; |
---|
| 1186 | + |
---|
| 1187 | + /* Realign pages to current pointer position */ |
---|
| 1188 | + if (iov->iov_len > cur) { |
---|
| 1189 | + offset = iov->iov_len - cur; |
---|
| 1190 | + copied = xdr_shrink_bufhead(buf, offset); |
---|
| 1191 | + trace_rpc_xdr_alignment(xdr, offset, copied); |
---|
| 1192 | + xdr->nwords = XDR_QUADLEN(buf->len - cur); |
---|
| 1193 | + } |
---|
| 1194 | +} |
---|
905 | 1195 | |
---|
906 | 1196 | static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len) |
---|
907 | 1197 | { |
---|
908 | 1198 | struct xdr_buf *buf = xdr->buf; |
---|
909 | | - struct kvec *iov; |
---|
910 | 1199 | unsigned int nwords = XDR_QUADLEN(len); |
---|
911 | 1200 | unsigned int cur = xdr_stream_pos(xdr); |
---|
| 1201 | + unsigned int copied, offset; |
---|
912 | 1202 | |
---|
913 | 1203 | if (xdr->nwords == 0) |
---|
914 | 1204 | return 0; |
---|
915 | | - /* Realign pages to current pointer position */ |
---|
916 | | - iov = buf->head; |
---|
917 | | - if (iov->iov_len > cur) { |
---|
918 | | - xdr_shrink_bufhead(buf, iov->iov_len - cur); |
---|
919 | | - xdr->nwords = XDR_QUADLEN(buf->len - cur); |
---|
920 | | - } |
---|
921 | 1205 | |
---|
| 1206 | + xdr_realign_pages(xdr); |
---|
922 | 1207 | if (nwords > xdr->nwords) { |
---|
923 | 1208 | nwords = xdr->nwords; |
---|
924 | 1209 | len = nwords << 2; |
---|
.. | .. |
---|
927 | 1212 | len = buf->page_len; |
---|
928 | 1213 | else if (nwords < xdr->nwords) { |
---|
929 | 1214 | /* Truncate page data and move it into the tail */ |
---|
930 | | - xdr_shrink_pagelen(buf, buf->page_len - len); |
---|
| 1215 | + offset = buf->page_len - len; |
---|
| 1216 | + copied = xdr_shrink_pagelen(buf, offset); |
---|
| 1217 | + trace_rpc_xdr_alignment(xdr, offset, copied); |
---|
931 | 1218 | xdr->nwords = XDR_QUADLEN(buf->len - cur); |
---|
932 | 1219 | } |
---|
933 | 1220 | return len; |
---|
.. | .. |
---|
975 | 1262 | } |
---|
976 | 1263 | EXPORT_SYMBOL_GPL(xdr_read_pages); |
---|
977 | 1264 | |
---|
| 1265 | +uint64_t xdr_align_data(struct xdr_stream *xdr, uint64_t offset, uint32_t length) |
---|
| 1266 | +{ |
---|
| 1267 | + struct xdr_buf *buf = xdr->buf; |
---|
| 1268 | + unsigned int from, bytes; |
---|
| 1269 | + unsigned int shift = 0; |
---|
| 1270 | + |
---|
| 1271 | + if ((offset + length) < offset || |
---|
| 1272 | + (offset + length) > buf->page_len) |
---|
| 1273 | + length = buf->page_len - offset; |
---|
| 1274 | + |
---|
| 1275 | + xdr_realign_pages(xdr); |
---|
| 1276 | + from = xdr_page_pos(xdr); |
---|
| 1277 | + bytes = xdr->nwords << 2; |
---|
| 1278 | + if (length < bytes) |
---|
| 1279 | + bytes = length; |
---|
| 1280 | + |
---|
| 1281 | + /* Move page data to the left */ |
---|
| 1282 | + if (from > offset) { |
---|
| 1283 | + shift = min_t(unsigned int, bytes, buf->page_len - from); |
---|
| 1284 | + _shift_data_left_pages(buf->pages, |
---|
| 1285 | + buf->page_base + offset, |
---|
| 1286 | + buf->page_base + from, |
---|
| 1287 | + shift); |
---|
| 1288 | + bytes -= shift; |
---|
| 1289 | + |
---|
| 1290 | + /* Move tail data into the pages, if necessary */ |
---|
| 1291 | + if (bytes > 0) |
---|
| 1292 | + _shift_data_left_tail(buf, offset + shift, bytes); |
---|
| 1293 | + } |
---|
| 1294 | + |
---|
| 1295 | + xdr->nwords -= XDR_QUADLEN(length); |
---|
| 1296 | + xdr_set_page(xdr, from + length, PAGE_SIZE); |
---|
| 1297 | + return length; |
---|
| 1298 | +} |
---|
| 1299 | +EXPORT_SYMBOL_GPL(xdr_align_data); |
---|
| 1300 | + |
---|
| 1301 | +uint64_t xdr_expand_hole(struct xdr_stream *xdr, uint64_t offset, uint64_t length) |
---|
| 1302 | +{ |
---|
| 1303 | + struct xdr_buf *buf = xdr->buf; |
---|
| 1304 | + unsigned int bytes; |
---|
| 1305 | + unsigned int from; |
---|
| 1306 | + unsigned int truncated = 0; |
---|
| 1307 | + |
---|
| 1308 | + if ((offset + length) < offset || |
---|
| 1309 | + (offset + length) > buf->page_len) |
---|
| 1310 | + length = buf->page_len - offset; |
---|
| 1311 | + |
---|
| 1312 | + xdr_realign_pages(xdr); |
---|
| 1313 | + from = xdr_page_pos(xdr); |
---|
| 1314 | + bytes = xdr->nwords << 2; |
---|
| 1315 | + |
---|
| 1316 | + if (offset + length + bytes > buf->page_len) { |
---|
| 1317 | + unsigned int shift = (offset + length + bytes) - buf->page_len; |
---|
| 1318 | + unsigned int res = _shift_data_right_tail(buf, from + bytes - shift, shift); |
---|
| 1319 | + truncated = shift - res; |
---|
| 1320 | + xdr->nwords -= XDR_QUADLEN(truncated); |
---|
| 1321 | + bytes -= shift; |
---|
| 1322 | + } |
---|
| 1323 | + |
---|
| 1324 | + /* Now move the page data over and zero pages */ |
---|
| 1325 | + if (bytes > 0) |
---|
| 1326 | + _shift_data_right_pages(buf->pages, |
---|
| 1327 | + buf->page_base + offset + length, |
---|
| 1328 | + buf->page_base + from, |
---|
| 1329 | + bytes); |
---|
| 1330 | + _zero_pages(buf->pages, buf->page_base + offset, length); |
---|
| 1331 | + |
---|
| 1332 | + buf->len += length - (from - offset) - truncated; |
---|
| 1333 | + xdr_set_page(xdr, offset + length, PAGE_SIZE); |
---|
| 1334 | + return length; |
---|
| 1335 | +} |
---|
| 1336 | +EXPORT_SYMBOL_GPL(xdr_expand_hole); |
---|
| 1337 | + |
---|
978 | 1338 | /** |
---|
979 | 1339 | * xdr_enter_page - decode data from the XDR page |
---|
980 | 1340 | * @xdr: pointer to xdr_stream struct |
---|
.. | .. |
---|
997 | 1357 | } |
---|
998 | 1358 | EXPORT_SYMBOL_GPL(xdr_enter_page); |
---|
999 | 1359 | |
---|
1000 | | -static struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0}; |
---|
| 1360 | +static const struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0}; |
---|
1001 | 1361 | |
---|
1002 | 1362 | void |
---|
1003 | 1363 | xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf) |
---|
.. | .. |
---|
1197 | 1557 | return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj)); |
---|
1198 | 1558 | } |
---|
1199 | 1559 | EXPORT_SYMBOL_GPL(xdr_encode_word); |
---|
1200 | | - |
---|
1201 | | -/* If the netobj starting offset bytes from the start of xdr_buf is contained |
---|
1202 | | - * entirely in the head or the tail, set object to point to it; otherwise |
---|
1203 | | - * try to find space for it at the end of the tail, copy it there, and |
---|
1204 | | - * set obj to point to it. */ |
---|
1205 | | -int xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, unsigned int offset) |
---|
1206 | | -{ |
---|
1207 | | - struct xdr_buf subbuf; |
---|
1208 | | - |
---|
1209 | | - if (xdr_decode_word(buf, offset, &obj->len)) |
---|
1210 | | - return -EFAULT; |
---|
1211 | | - if (xdr_buf_subsegment(buf, &subbuf, offset + 4, obj->len)) |
---|
1212 | | - return -EFAULT; |
---|
1213 | | - |
---|
1214 | | - /* Is the obj contained entirely in the head? */ |
---|
1215 | | - obj->data = subbuf.head[0].iov_base; |
---|
1216 | | - if (subbuf.head[0].iov_len == obj->len) |
---|
1217 | | - return 0; |
---|
1218 | | - /* ..or is the obj contained entirely in the tail? */ |
---|
1219 | | - obj->data = subbuf.tail[0].iov_base; |
---|
1220 | | - if (subbuf.tail[0].iov_len == obj->len) |
---|
1221 | | - return 0; |
---|
1222 | | - |
---|
1223 | | - /* use end of tail as storage for obj: |
---|
1224 | | - * (We don't copy to the beginning because then we'd have |
---|
1225 | | - * to worry about doing a potentially overlapping copy. |
---|
1226 | | - * This assumes the object is at most half the length of the |
---|
1227 | | - * tail.) */ |
---|
1228 | | - if (obj->len > buf->buflen - buf->len) |
---|
1229 | | - return -ENOMEM; |
---|
1230 | | - if (buf->tail[0].iov_len != 0) |
---|
1231 | | - obj->data = buf->tail[0].iov_base + buf->tail[0].iov_len; |
---|
1232 | | - else |
---|
1233 | | - obj->data = buf->head[0].iov_base + buf->head[0].iov_len; |
---|
1234 | | - __read_bytes_from_xdr_buf(&subbuf, obj->data, obj->len); |
---|
1235 | | - return 0; |
---|
1236 | | -} |
---|
1237 | | -EXPORT_SYMBOL_GPL(xdr_buf_read_netobj); |
---|
1238 | 1560 | |
---|
1239 | 1561 | /* Returns 0 on success, or else a negative error code. */ |
---|
1240 | 1562 | static int |
---|