aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/scatterlist.h10
-rw-r--r--lib/Kconfig4
-rw-r--r--lib/scatterlist.c105
3 files changed, 119 insertions, 0 deletions
diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
index b7c83254c566..b8a7c1d1dbe3 100644
--- a/include/linux/scatterlist.h
+++ b/include/linux/scatterlist.h
@@ -276,6 +276,16 @@ int sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages,
276 unsigned int n_pages, unsigned int offset, 276 unsigned int n_pages, unsigned int offset,
277 unsigned long size, gfp_t gfp_mask); 277 unsigned long size, gfp_t gfp_mask);
278 278
279#ifdef CONFIG_SGL_ALLOC
280struct scatterlist *sgl_alloc_order(unsigned long long length,
281 unsigned int order, bool chainable,
282 gfp_t gfp, unsigned int *nent_p);
283struct scatterlist *sgl_alloc(unsigned long long length, gfp_t gfp,
284 unsigned int *nent_p);
285void sgl_free_order(struct scatterlist *sgl, int order);
286void sgl_free(struct scatterlist *sgl);
287#endif /* CONFIG_SGL_ALLOC */
288
279size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf, 289size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf,
280 size_t buflen, off_t skip, bool to_buffer); 290 size_t buflen, off_t skip, bool to_buffer);
281 291
diff --git a/lib/Kconfig b/lib/Kconfig
index c5e84fbcb30b..4dd5c11366f9 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -409,6 +409,10 @@ config HAS_DMA
409 depends on !NO_DMA 409 depends on !NO_DMA
410 default y 410 default y
411 411
412config SGL_ALLOC
413 bool
414 default n
415
412config DMA_NOOP_OPS 416config DMA_NOOP_OPS
413 bool 417 bool
414 depends on HAS_DMA && (!64BIT || ARCH_DMA_ADDR_T_64BIT) 418 depends on HAS_DMA && (!64BIT || ARCH_DMA_ADDR_T_64BIT)
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index 7c1c55f7daaa..9afc9b432083 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -474,6 +474,111 @@ int sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages,
474} 474}
475EXPORT_SYMBOL(sg_alloc_table_from_pages); 475EXPORT_SYMBOL(sg_alloc_table_from_pages);
476 476
477#ifdef CONFIG_SGL_ALLOC
478
479/**
480 * sgl_alloc_order - allocate a scatterlist and its pages
481 * @length: Length in bytes of the scatterlist. Must be at least one
482 * @order: Second argument for alloc_pages()
483 * @chainable: Whether or not to allocate an extra element in the scatterlist
484 * for scatterlist chaining purposes
485 * @gfp: Memory allocation flags
486 * @nent_p: [out] Number of entries in the scatterlist that have pages
487 *
488 * Returns: A pointer to an initialized scatterlist or %NULL upon failure.
489 */
490struct scatterlist *sgl_alloc_order(unsigned long long length,
491 unsigned int order, bool chainable,
492 gfp_t gfp, unsigned int *nent_p)
493{
494 struct scatterlist *sgl, *sg;
495 struct page *page;
496 unsigned int nent, nalloc;
497 u32 elem_len;
498
499 nent = round_up(length, PAGE_SIZE << order) >> (PAGE_SHIFT + order);
500 /* Check for integer overflow */
501 if (length > (nent << (PAGE_SHIFT + order)))
502 return NULL;
503 nalloc = nent;
504 if (chainable) {
505 /* Check for integer overflow */
506 if (nalloc + 1 < nalloc)
507 return NULL;
508 nalloc++;
509 }
510 sgl = kmalloc_array(nalloc, sizeof(struct scatterlist),
511 (gfp & ~GFP_DMA) | __GFP_ZERO);
512 if (!sgl)
513 return NULL;
514
515 sg_init_table(sgl, nent);
516 sg = sgl;
517 while (length) {
518 elem_len = min_t(u64, length, PAGE_SIZE << order);
519 page = alloc_pages(gfp, order);
520 if (!page) {
521 sgl_free(sgl);
522 return NULL;
523 }
524
525 sg_set_page(sg, page, elem_len, 0);
526 length -= elem_len;
527 sg = sg_next(sg);
528 }
529 WARN_ON_ONCE(sg);
530 if (nent_p)
531 *nent_p = nent;
532 return sgl;
533}
534EXPORT_SYMBOL(sgl_alloc_order);
535
536/**
537 * sgl_alloc - allocate a scatterlist and its pages
538 * @length: Length in bytes of the scatterlist
539 * @gfp: Memory allocation flags
540 * @nent_p: [out] Number of entries in the scatterlist
541 *
542 * Returns: A pointer to an initialized scatterlist or %NULL upon failure.
543 */
544struct scatterlist *sgl_alloc(unsigned long long length, gfp_t gfp,
545 unsigned int *nent_p)
546{
547 return sgl_alloc_order(length, 0, false, gfp, nent_p);
548}
549EXPORT_SYMBOL(sgl_alloc);
550
551/**
552 * sgl_free_order - free a scatterlist and its pages
553 * @sgl: Scatterlist with one or more elements
554 * @order: Second argument for __free_pages()
555 */
556void sgl_free_order(struct scatterlist *sgl, int order)
557{
558 struct scatterlist *sg;
559 struct page *page;
560
561 for (sg = sgl; sg; sg = sg_next(sg)) {
562 page = sg_page(sg);
563 if (page)
564 __free_pages(page, order);
565 }
566 kfree(sgl);
567}
568EXPORT_SYMBOL(sgl_free_order);
569
570/**
571 * sgl_free - free a scatterlist and its pages
572 * @sgl: Scatterlist with one or more elements
573 */
574void sgl_free(struct scatterlist *sgl)
575{
576 sgl_free_order(sgl, 0);
577}
578EXPORT_SYMBOL(sgl_free);
579
580#endif /* CONFIG_SGL_ALLOC */
581
477void __sg_page_iter_start(struct sg_page_iter *piter, 582void __sg_page_iter_start(struct sg_page_iter *piter,
478 struct scatterlist *sglist, unsigned int nents, 583 struct scatterlist *sglist, unsigned int nents,
479 unsigned long pgoffset) 584 unsigned long pgoffset)