diff options
Diffstat (limited to 'lib/scatterlist.c')
-rw-r--r-- | lib/scatterlist.c | 86 |
1 files changed, 61 insertions, 25 deletions
diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 7874b01e816e..b83c144d731f 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c | |||
@@ -394,6 +394,44 @@ int sg_alloc_table_from_pages(struct sg_table *sgt, | |||
394 | } | 394 | } |
395 | EXPORT_SYMBOL(sg_alloc_table_from_pages); | 395 | EXPORT_SYMBOL(sg_alloc_table_from_pages); |
396 | 396 | ||
397 | void __sg_page_iter_start(struct sg_page_iter *piter, | ||
398 | struct scatterlist *sglist, unsigned int nents, | ||
399 | unsigned long pgoffset) | ||
400 | { | ||
401 | piter->__pg_advance = 0; | ||
402 | piter->__nents = nents; | ||
403 | |||
404 | piter->page = NULL; | ||
405 | piter->sg = sglist; | ||
406 | piter->sg_pgoffset = pgoffset; | ||
407 | } | ||
408 | EXPORT_SYMBOL(__sg_page_iter_start); | ||
409 | |||
410 | static int sg_page_count(struct scatterlist *sg) | ||
411 | { | ||
412 | return PAGE_ALIGN(sg->offset + sg->length) >> PAGE_SHIFT; | ||
413 | } | ||
414 | |||
415 | bool __sg_page_iter_next(struct sg_page_iter *piter) | ||
416 | { | ||
417 | if (!piter->__nents || !piter->sg) | ||
418 | return false; | ||
419 | |||
420 | piter->sg_pgoffset += piter->__pg_advance; | ||
421 | piter->__pg_advance = 1; | ||
422 | |||
423 | while (piter->sg_pgoffset >= sg_page_count(piter->sg)) { | ||
424 | piter->sg_pgoffset -= sg_page_count(piter->sg); | ||
425 | piter->sg = sg_next(piter->sg); | ||
426 | if (!--piter->__nents || !piter->sg) | ||
427 | return false; | ||
428 | } | ||
429 | piter->page = nth_page(sg_page(piter->sg), piter->sg_pgoffset); | ||
430 | |||
431 | return true; | ||
432 | } | ||
433 | EXPORT_SYMBOL(__sg_page_iter_next); | ||
434 | |||
397 | /** | 435 | /** |
398 | * sg_miter_start - start mapping iteration over a sg list | 436 | * sg_miter_start - start mapping iteration over a sg list |
399 | * @miter: sg mapping iter to be started | 437 | * @miter: sg mapping iter to be started |
@@ -411,9 +449,7 @@ void sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl, | |||
411 | { | 449 | { |
412 | memset(miter, 0, sizeof(struct sg_mapping_iter)); | 450 | memset(miter, 0, sizeof(struct sg_mapping_iter)); |
413 | 451 | ||
414 | miter->__sg = sgl; | 452 | __sg_page_iter_start(&miter->piter, sgl, nents, 0); |
415 | miter->__nents = nents; | ||
416 | miter->__offset = 0; | ||
417 | WARN_ON(!(flags & (SG_MITER_TO_SG | SG_MITER_FROM_SG))); | 453 | WARN_ON(!(flags & (SG_MITER_TO_SG | SG_MITER_FROM_SG))); |
418 | miter->__flags = flags; | 454 | miter->__flags = flags; |
419 | } | 455 | } |
@@ -438,36 +474,35 @@ EXPORT_SYMBOL(sg_miter_start); | |||
438 | */ | 474 | */ |
439 | bool sg_miter_next(struct sg_mapping_iter *miter) | 475 | bool sg_miter_next(struct sg_mapping_iter *miter) |
440 | { | 476 | { |
441 | unsigned int off, len; | ||
442 | |||
443 | /* check for end and drop resources from the last iteration */ | ||
444 | if (!miter->__nents) | ||
445 | return false; | ||
446 | |||
447 | sg_miter_stop(miter); | 477 | sg_miter_stop(miter); |
448 | 478 | ||
449 | /* get to the next sg if necessary. __offset is adjusted by stop */ | 479 | /* |
450 | while (miter->__offset == miter->__sg->length) { | 480 | * Get to the next page if necessary. |
451 | if (--miter->__nents) { | 481 | * __remaining, __offset is adjusted by sg_miter_stop |
452 | miter->__sg = sg_next(miter->__sg); | 482 | */ |
453 | miter->__offset = 0; | 483 | if (!miter->__remaining) { |
454 | } else | 484 | struct scatterlist *sg; |
485 | unsigned long pgoffset; | ||
486 | |||
487 | if (!__sg_page_iter_next(&miter->piter)) | ||
455 | return false; | 488 | return false; |
456 | } | ||
457 | 489 | ||
458 | /* map the next page */ | 490 | sg = miter->piter.sg; |
459 | off = miter->__sg->offset + miter->__offset; | 491 | pgoffset = miter->piter.sg_pgoffset; |
460 | len = miter->__sg->length - miter->__offset; | ||
461 | 492 | ||
462 | miter->page = nth_page(sg_page(miter->__sg), off >> PAGE_SHIFT); | 493 | miter->__offset = pgoffset ? 0 : sg->offset; |
463 | off &= ~PAGE_MASK; | 494 | miter->__remaining = sg->offset + sg->length - |
464 | miter->length = min_t(unsigned int, len, PAGE_SIZE - off); | 495 | (pgoffset << PAGE_SHIFT) - miter->__offset; |
465 | miter->consumed = miter->length; | 496 | miter->__remaining = min_t(unsigned long, miter->__remaining, |
497 | PAGE_SIZE - miter->__offset); | ||
498 | } | ||
499 | miter->page = miter->piter.page; | ||
500 | miter->consumed = miter->length = miter->__remaining; | ||
466 | 501 | ||
467 | if (miter->__flags & SG_MITER_ATOMIC) | 502 | if (miter->__flags & SG_MITER_ATOMIC) |
468 | miter->addr = kmap_atomic(miter->page) + off; | 503 | miter->addr = kmap_atomic(miter->page) + miter->__offset; |
469 | else | 504 | else |
470 | miter->addr = kmap(miter->page) + off; | 505 | miter->addr = kmap(miter->page) + miter->__offset; |
471 | 506 | ||
472 | return true; | 507 | return true; |
473 | } | 508 | } |
@@ -494,6 +529,7 @@ void sg_miter_stop(struct sg_mapping_iter *miter) | |||
494 | /* drop resources from the last iteration */ | 529 | /* drop resources from the last iteration */ |
495 | if (miter->addr) { | 530 | if (miter->addr) { |
496 | miter->__offset += miter->consumed; | 531 | miter->__offset += miter->consumed; |
532 | miter->__remaining -= miter->consumed; | ||
497 | 533 | ||
498 | if (miter->__flags & SG_MITER_TO_SG) | 534 | if (miter->__flags & SG_MITER_TO_SG) |
499 | flush_kernel_dcache_page(miter->page); | 535 | flush_kernel_dcache_page(miter->page); |