aboutsummaryrefslogtreecommitdiffstats
path: root/lib/scatterlist.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/scatterlist.c')
-rw-r--r--lib/scatterlist.c133
1 files changed, 111 insertions, 22 deletions
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index a1cf8cae60e7..a685c8a79578 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -247,13 +247,15 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents,
247 struct scatterlist *sg, *prv; 247 struct scatterlist *sg, *prv;
248 unsigned int left; 248 unsigned int left;
249 249
250 memset(table, 0, sizeof(*table));
251
252 if (nents == 0)
253 return -EINVAL;
250#ifndef ARCH_HAS_SG_CHAIN 254#ifndef ARCH_HAS_SG_CHAIN
251 if (WARN_ON_ONCE(nents > max_ents)) 255 if (WARN_ON_ONCE(nents > max_ents))
252 return -EINVAL; 256 return -EINVAL;
253#endif 257#endif
254 258
255 memset(table, 0, sizeof(*table));
256
257 left = nents; 259 left = nents;
258 prv = NULL; 260 prv = NULL;
259 do { 261 do {
@@ -453,6 +455,65 @@ void sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl,
453} 455}
454EXPORT_SYMBOL(sg_miter_start); 456EXPORT_SYMBOL(sg_miter_start);
455 457
458static bool sg_miter_get_next_page(struct sg_mapping_iter *miter)
459{
460 if (!miter->__remaining) {
461 struct scatterlist *sg;
462 unsigned long pgoffset;
463
464 if (!__sg_page_iter_next(&miter->piter))
465 return false;
466
467 sg = miter->piter.sg;
468 pgoffset = miter->piter.sg_pgoffset;
469
470 miter->__offset = pgoffset ? 0 : sg->offset;
471 miter->__remaining = sg->offset + sg->length -
472 (pgoffset << PAGE_SHIFT) - miter->__offset;
473 miter->__remaining = min_t(unsigned long, miter->__remaining,
474 PAGE_SIZE - miter->__offset);
475 }
476
477 return true;
478}
479
480/**
481 * sg_miter_skip - reposition mapping iterator
482 * @miter: sg mapping iter to be skipped
483 * @offset: number of bytes to plus the current location
484 *
485 * Description:
486 * Sets the offset of @miter to its current location plus @offset bytes.
487 * If mapping iterator @miter has been proceeded by sg_miter_next(), this
488 * stops @miter.
489 *
490 * Context:
491 * Don't care if @miter is stopped, or not proceeded yet.
492 * Otherwise, preemption disabled if the SG_MITER_ATOMIC is set.
493 *
494 * Returns:
495 * true if @miter contains the valid mapping. false if end of sg
496 * list is reached.
497 */
498static bool sg_miter_skip(struct sg_mapping_iter *miter, off_t offset)
499{
500 sg_miter_stop(miter);
501
502 while (offset) {
503 off_t consumed;
504
505 if (!sg_miter_get_next_page(miter))
506 return false;
507
508 consumed = min_t(off_t, offset, miter->__remaining);
509 miter->__offset += consumed;
510 miter->__remaining -= consumed;
511 offset -= consumed;
512 }
513
514 return true;
515}
516
456/** 517/**
457 * sg_miter_next - proceed mapping iterator to the next mapping 518 * sg_miter_next - proceed mapping iterator to the next mapping
458 * @miter: sg mapping iter to proceed 519 * @miter: sg mapping iter to proceed
@@ -478,22 +539,9 @@ bool sg_miter_next(struct sg_mapping_iter *miter)
478 * Get to the next page if necessary. 539 * Get to the next page if necessary.
479 * __remaining, __offset is adjusted by sg_miter_stop 540 * __remaining, __offset is adjusted by sg_miter_stop
480 */ 541 */
481 if (!miter->__remaining) { 542 if (!sg_miter_get_next_page(miter))
482 struct scatterlist *sg; 543 return false;
483 unsigned long pgoffset;
484
485 if (!__sg_page_iter_next(&miter->piter))
486 return false;
487
488 sg = miter->piter.sg;
489 pgoffset = miter->piter.sg_pgoffset;
490 544
491 miter->__offset = pgoffset ? 0 : sg->offset;
492 miter->__remaining = sg->offset + sg->length -
493 (pgoffset << PAGE_SHIFT) - miter->__offset;
494 miter->__remaining = min_t(unsigned long, miter->__remaining,
495 PAGE_SIZE - miter->__offset);
496 }
497 miter->page = sg_page_iter_page(&miter->piter); 545 miter->page = sg_page_iter_page(&miter->piter);
498 miter->consumed = miter->length = miter->__remaining; 546 miter->consumed = miter->length = miter->__remaining;
499 547
@@ -552,14 +600,16 @@ EXPORT_SYMBOL(sg_miter_stop);
552 * @nents: Number of SG entries 600 * @nents: Number of SG entries
553 * @buf: Where to copy from 601 * @buf: Where to copy from
554 * @buflen: The number of bytes to copy 602 * @buflen: The number of bytes to copy
555 * @to_buffer: transfer direction (non zero == from an sg list to a 603 * @skip: Number of bytes to skip before copying
556 * buffer, 0 == from a buffer to an sg list 604 * @to_buffer: transfer direction (true == from an sg list to a
605 * buffer, false == from a buffer to an sg list
557 * 606 *
558 * Returns the number of copied bytes. 607 * Returns the number of copied bytes.
559 * 608 *
560 **/ 609 **/
561static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, 610static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
562 void *buf, size_t buflen, int to_buffer) 611 void *buf, size_t buflen, off_t skip,
612 bool to_buffer)
563{ 613{
564 unsigned int offset = 0; 614 unsigned int offset = 0;
565 struct sg_mapping_iter miter; 615 struct sg_mapping_iter miter;
@@ -573,6 +623,9 @@ static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
573 623
574 sg_miter_start(&miter, sgl, nents, sg_flags); 624 sg_miter_start(&miter, sgl, nents, sg_flags);
575 625
626 if (!sg_miter_skip(&miter, skip))
627 return false;
628
576 local_irq_save(flags); 629 local_irq_save(flags);
577 630
578 while (sg_miter_next(&miter) && offset < buflen) { 631 while (sg_miter_next(&miter) && offset < buflen) {
@@ -607,7 +660,7 @@ static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
607size_t sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents, 660size_t sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents,
608 void *buf, size_t buflen) 661 void *buf, size_t buflen)
609{ 662{
610 return sg_copy_buffer(sgl, nents, buf, buflen, 0); 663 return sg_copy_buffer(sgl, nents, buf, buflen, 0, false);
611} 664}
612EXPORT_SYMBOL(sg_copy_from_buffer); 665EXPORT_SYMBOL(sg_copy_from_buffer);
613 666
@@ -624,6 +677,42 @@ EXPORT_SYMBOL(sg_copy_from_buffer);
624size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents, 677size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
625 void *buf, size_t buflen) 678 void *buf, size_t buflen)
626{ 679{
627 return sg_copy_buffer(sgl, nents, buf, buflen, 1); 680 return sg_copy_buffer(sgl, nents, buf, buflen, 0, true);
628} 681}
629EXPORT_SYMBOL(sg_copy_to_buffer); 682EXPORT_SYMBOL(sg_copy_to_buffer);
683
684/**
685 * sg_pcopy_from_buffer - Copy from a linear buffer to an SG list
686 * @sgl: The SG list
687 * @nents: Number of SG entries
688 * @buf: Where to copy from
689 * @skip: Number of bytes to skip before copying
690 * @buflen: The number of bytes to copy
691 *
692 * Returns the number of copied bytes.
693 *
694 **/
695size_t sg_pcopy_from_buffer(struct scatterlist *sgl, unsigned int nents,
696 void *buf, size_t buflen, off_t skip)
697{
698 return sg_copy_buffer(sgl, nents, buf, buflen, skip, false);
699}
700EXPORT_SYMBOL(sg_pcopy_from_buffer);
701
702/**
703 * sg_pcopy_to_buffer - Copy from an SG list to a linear buffer
704 * @sgl: The SG list
705 * @nents: Number of SG entries
706 * @buf: Where to copy to
707 * @skip: Number of bytes to skip before copying
708 * @buflen: The number of bytes to copy
709 *
710 * Returns the number of copied bytes.
711 *
712 **/
713size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
714 void *buf, size_t buflen, off_t skip)
715{
716 return sg_copy_buffer(sgl, nents, buf, buflen, skip, true);
717}
718EXPORT_SYMBOL(sg_pcopy_to_buffer);