18d59ecb2SHans Petter Selasky /*- 28d59ecb2SHans Petter Selasky * Copyright (c) 2010 Isilon Systems, Inc. 38d59ecb2SHans Petter Selasky * Copyright (c) 2010 iX Systems, Inc. 48d59ecb2SHans Petter Selasky * Copyright (c) 2010 Panasas, Inc. 58306998fSHans Petter Selasky * Copyright (c) 2013-2017 Mellanox Technologies, Ltd. 68d59ecb2SHans Petter Selasky * Copyright (c) 2015 Matthew Dillon <dillon@backplane.com> 7c072f6e8SVladimir Kondratyev * Copyright (c) 2016 Matthew Macy 88d59ecb2SHans Petter Selasky * All rights reserved. 98d59ecb2SHans Petter Selasky * 108d59ecb2SHans Petter Selasky * Redistribution and use in source and binary forms, with or without 118d59ecb2SHans Petter Selasky * modification, are permitted provided that the following conditions 128d59ecb2SHans Petter Selasky * are met: 138d59ecb2SHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 148d59ecb2SHans Petter Selasky * notice unmodified, this list of conditions, and the following 158d59ecb2SHans Petter Selasky * disclaimer. 168d59ecb2SHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 178d59ecb2SHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 188d59ecb2SHans Petter Selasky * documentation and/or other materials provided with the distribution. 198d59ecb2SHans Petter Selasky * 208d59ecb2SHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 218d59ecb2SHans Petter Selasky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 228d59ecb2SHans Petter Selasky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 238d59ecb2SHans Petter Selasky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 248d59ecb2SHans Petter Selasky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 258d59ecb2SHans Petter Selasky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 268d59ecb2SHans Petter Selasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 278d59ecb2SHans Petter Selasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 288d59ecb2SHans Petter Selasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 298d59ecb2SHans Petter Selasky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 308d59ecb2SHans Petter Selasky */ 31307f78f3SVladimir Kondratyev #ifndef _LINUXKPI_LINUX_SCATTERLIST_H_ 32307f78f3SVladimir Kondratyev #define _LINUXKPI_LINUX_SCATTERLIST_H_ 338d59ecb2SHans Petter Selasky 344abbf816SBjoern A. Zeeb #include <sys/types.h> 35*256eb8d5SVladimir Kondratyev #include <sys/proc.h> 36*256eb8d5SVladimir Kondratyev #include <sys/sched.h> 374abbf816SBjoern A. Zeeb #include <sys/sf_buf.h> 384abbf816SBjoern A. Zeeb 39*256eb8d5SVladimir Kondratyev #include <linux/err.h> 408d59ecb2SHans Petter Selasky #include <linux/page.h> 418d59ecb2SHans Petter Selasky #include <linux/slab.h> 428306998fSHans Petter Selasky #include <linux/mm.h> 438d59ecb2SHans Petter Selasky 44442d12d8SHans Petter Selasky struct bus_dmamap; 458d59ecb2SHans Petter Selasky struct scatterlist { 468306998fSHans Petter Selasky unsigned long page_link; 4772ebbe00SHans Petter Selasky #define SG_PAGE_LINK_CHAIN 0x1UL 4872ebbe00SHans Petter Selasky #define SG_PAGE_LINK_LAST 0x2UL 4972ebbe00SHans Petter Selasky #define SG_PAGE_LINK_MASK 0x3UL 508306998fSHans Petter Selasky unsigned int offset; 518306998fSHans Petter Selasky unsigned int length; 52f211d536STycho Nightingale dma_addr_t dma_address; 53442d12d8SHans Petter Selasky struct bus_dmamap *dma_map; /* FreeBSD specific */ 548d59ecb2SHans Petter Selasky }; 558d59ecb2SHans Petter Selasky 5672ebbe00SHans Petter Selasky CTASSERT((sizeof(struct scatterlist) & SG_PAGE_LINK_MASK) == 0); 578306998fSHans Petter Selasky 588d59ecb2SHans Petter Selasky struct sg_table { 598d59ecb2SHans Petter Selasky struct scatterlist *sgl; 608d59ecb2SHans Petter Selasky unsigned int nents; 618d59ecb2SHans Petter Selasky unsigned int orig_nents; 628d59ecb2SHans Petter Selasky }; 638d59ecb2SHans Petter Selasky 648d59ecb2SHans Petter Selasky struct sg_page_iter { 658d59ecb2SHans Petter Selasky struct scatterlist *sg; 668d59ecb2SHans Petter Selasky unsigned int sg_pgoffset; 678d59ecb2SHans Petter Selasky unsigned int maxents; 688306998fSHans Petter Selasky struct { 698306998fSHans Petter Selasky unsigned int nents; 708306998fSHans Petter Selasky int pg_advance; 718306998fSHans Petter Selasky } internal; 728d59ecb2SHans Petter Selasky }; 738d59ecb2SHans Petter Selasky 74fe4b041aSHans Petter Selasky struct sg_dma_page_iter { 75fe4b041aSHans Petter Selasky struct sg_page_iter base; 76fe4b041aSHans Petter Selasky }; 77fe4b041aSHans Petter Selasky 7857a865f8SHans Petter Selasky #define SCATTERLIST_MAX_SEGMENT (-1U & ~(PAGE_SIZE - 1)) 7957a865f8SHans Petter Selasky 808d59ecb2SHans Petter Selasky #define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist)) 818d59ecb2SHans Petter Selasky 828306998fSHans Petter Selasky #define SG_MAGIC 0x87654321UL 8356b16627SHans Petter Selasky #define SG_CHAIN SG_PAGE_LINK_CHAIN 8456b16627SHans Petter Selasky #define SG_END SG_PAGE_LINK_LAST 858306998fSHans Petter Selasky 8672ebbe00SHans Petter Selasky #define sg_is_chain(sg) ((sg)->page_link & SG_PAGE_LINK_CHAIN) 8772ebbe00SHans Petter Selasky #define sg_is_last(sg) ((sg)->page_link & SG_PAGE_LINK_LAST) 888306998fSHans Petter Selasky #define sg_chain_ptr(sg) \ 8972ebbe00SHans Petter Selasky ((struct scatterlist *) ((sg)->page_link & ~SG_PAGE_LINK_MASK)) 908306998fSHans Petter Selasky 91f211d536STycho Nightingale #define sg_dma_address(sg) (sg)->dma_address 92442d12d8SHans Petter Selasky #define sg_dma_len(sg) (sg)->length 938d59ecb2SHans Petter Selasky 948306998fSHans Petter Selasky #define for_each_sg_page(sgl, iter, nents, pgoffset) \ 958306998fSHans Petter Selasky for (_sg_iter_init(sgl, iter, nents, pgoffset); \ 968306998fSHans Petter Selasky (iter)->sg; _sg_iter_next(iter)) 97fe4b041aSHans Petter Selasky #define for_each_sg_dma_page(sgl, iter, nents, pgoffset) \ 98fe4b041aSHans Petter Selasky for_each_sg_page(sgl, &(iter)->base, nents, pgoffset) 998306998fSHans Petter Selasky 1008306998fSHans Petter Selasky #define for_each_sg(sglist, sg, sgmax, iter) \ 1018306998fSHans Petter Selasky for (iter = 0, sg = (sglist); iter < (sgmax); iter++, sg = sg_next(sg)) 1028306998fSHans Petter Selasky 1034370e9f1SEmmanuel Vadot #define for_each_sgtable_sg(sgt, sg, i) \ 1044370e9f1SEmmanuel Vadot for_each_sg((sgt)->sgl, sg, (sgt)->orig_nents, i) 1054370e9f1SEmmanuel Vadot 1064370e9f1SEmmanuel Vadot #define for_each_sgtable_page(sgt, iter, pgoffset) \ 1074370e9f1SEmmanuel Vadot for_each_sg_page((sgt)->sgl, iter, (sgt)->orig_nents, pgoffset) 1084370e9f1SEmmanuel Vadot 1091aca8a6eSEmmanuel Vadot #define for_each_sgtable_dma_sg(sgt, sg, iter) \ 1101aca8a6eSEmmanuel Vadot for_each_sg((sgt)->sgl, sg, (sgt)->nents, iter) 1111aca8a6eSEmmanuel Vadot 1121aca8a6eSEmmanuel Vadot #define for_each_sgtable_dma_page(sgt, iter, pgoffset) \ 1131aca8a6eSEmmanuel Vadot for_each_sg_dma_page((sgt)->sgl, iter, (sgt)->nents, pgoffset) 1141aca8a6eSEmmanuel Vadot 1158306998fSHans Petter Selasky typedef struct scatterlist *(sg_alloc_fn) (unsigned int, gfp_t); 1168306998fSHans Petter Selasky typedef void (sg_free_fn) (struct scatterlist *, unsigned int); 1178306998fSHans Petter Selasky 1188306998fSHans Petter Selasky static inline void 1198306998fSHans Petter Selasky sg_assign_page(struct scatterlist *sg, struct page *page) 1208306998fSHans Petter Selasky { 12172ebbe00SHans Petter Selasky unsigned long page_link = sg->page_link & SG_PAGE_LINK_MASK; 1228306998fSHans Petter Selasky 1238306998fSHans Petter Selasky sg->page_link = page_link | (unsigned long)page; 1248306998fSHans Petter Selasky } 1258d59ecb2SHans Petter Selasky 1268d59ecb2SHans Petter Selasky static inline void 1278d59ecb2SHans Petter Selasky sg_set_page(struct scatterlist *sg, struct page *page, unsigned int len, 1288d59ecb2SHans Petter Selasky unsigned int offset) 1298d59ecb2SHans Petter Selasky { 1308306998fSHans Petter Selasky sg_assign_page(sg, page); 1318d59ecb2SHans Petter Selasky sg->offset = offset; 1328306998fSHans Petter Selasky sg->length = len; 1338306998fSHans Petter Selasky } 1348306998fSHans Petter Selasky 1358306998fSHans Petter Selasky static inline struct page * 1368306998fSHans Petter Selasky sg_page(struct scatterlist *sg) 1378306998fSHans Petter Selasky { 13872ebbe00SHans Petter Selasky return ((struct page *)((sg)->page_link & ~SG_PAGE_LINK_MASK)); 1398d59ecb2SHans Petter Selasky } 1408d59ecb2SHans Petter Selasky 1418d59ecb2SHans Petter Selasky static inline void 1428d59ecb2SHans Petter Selasky sg_set_buf(struct scatterlist *sg, const void *buf, unsigned int buflen) 1438d59ecb2SHans Petter Selasky { 1448d59ecb2SHans Petter Selasky sg_set_page(sg, virt_to_page(buf), buflen, 1458d59ecb2SHans Petter Selasky ((uintptr_t)buf) & (PAGE_SIZE - 1)); 1468d59ecb2SHans Petter Selasky } 1478d59ecb2SHans Petter Selasky 1488d59ecb2SHans Petter Selasky static inline struct scatterlist * 1498d59ecb2SHans Petter Selasky sg_next(struct scatterlist *sg) 1508d59ecb2SHans Petter Selasky { 1518306998fSHans Petter Selasky if (sg_is_last(sg)) 1528d59ecb2SHans Petter Selasky return (NULL); 1538d59ecb2SHans Petter Selasky sg++; 1548306998fSHans Petter Selasky if (sg_is_chain(sg)) 1558306998fSHans Petter Selasky sg = sg_chain_ptr(sg); 1568d59ecb2SHans Petter Selasky return (sg); 1578d59ecb2SHans Petter Selasky } 1588d59ecb2SHans Petter Selasky 1598d59ecb2SHans Petter Selasky static inline vm_paddr_t 1608d59ecb2SHans Petter Selasky sg_phys(struct scatterlist *sg) 1618d59ecb2SHans Petter Selasky { 162d1ea0764SBjoern A. Zeeb return (page_to_phys(sg_page(sg)) + sg->offset); 1638d59ecb2SHans Petter Selasky } 1648d59ecb2SHans Petter Selasky 165998f22ebSHans Petter Selasky static inline void * 166998f22ebSHans Petter Selasky sg_virt(struct scatterlist *sg) 167998f22ebSHans Petter Selasky { 168998f22ebSHans Petter Selasky 169998f22ebSHans Petter Selasky return ((void *)((unsigned long)page_address(sg_page(sg)) + sg->offset)); 170998f22ebSHans Petter Selasky } 171998f22ebSHans Petter Selasky 1728d59ecb2SHans Petter Selasky static inline void 1738d59ecb2SHans Petter Selasky sg_chain(struct scatterlist *prv, unsigned int prv_nents, 1748d59ecb2SHans Petter Selasky struct scatterlist *sgl) 1758d59ecb2SHans Petter Selasky { 1768d59ecb2SHans Petter Selasky struct scatterlist *sg = &prv[prv_nents - 1]; 1778d59ecb2SHans Petter Selasky 1788d59ecb2SHans Petter Selasky sg->offset = 0; 1798d59ecb2SHans Petter Selasky sg->length = 0; 18072ebbe00SHans Petter Selasky sg->page_link = ((unsigned long)sgl | 18172ebbe00SHans Petter Selasky SG_PAGE_LINK_CHAIN) & ~SG_PAGE_LINK_LAST; 1828d59ecb2SHans Petter Selasky } 1838d59ecb2SHans Petter Selasky 1848d59ecb2SHans Petter Selasky static inline void 1858d59ecb2SHans Petter Selasky sg_mark_end(struct scatterlist *sg) 1868d59ecb2SHans Petter Selasky { 18772ebbe00SHans Petter Selasky sg->page_link |= SG_PAGE_LINK_LAST; 18872ebbe00SHans Petter Selasky sg->page_link &= ~SG_PAGE_LINK_CHAIN; 1898d59ecb2SHans Petter Selasky } 1908d59ecb2SHans Petter Selasky 1918d59ecb2SHans Petter Selasky static inline void 1928306998fSHans Petter Selasky sg_init_table(struct scatterlist *sg, unsigned int nents) 1938306998fSHans Petter Selasky { 1948306998fSHans Petter Selasky bzero(sg, sizeof(*sg) * nents); 1958306998fSHans Petter Selasky sg_mark_end(&sg[nents - 1]); 1968306998fSHans Petter Selasky } 1978306998fSHans Petter Selasky 1982686b10dSAlex Richardson static inline void 1992686b10dSAlex Richardson sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen) 2002686b10dSAlex Richardson { 2012686b10dSAlex Richardson sg_init_table(sg, 1); 2022686b10dSAlex Richardson sg_set_buf(sg, buf, buflen); 2032686b10dSAlex Richardson } 2042686b10dSAlex Richardson 2058306998fSHans Petter Selasky static struct scatterlist * 2068306998fSHans Petter Selasky sg_kmalloc(unsigned int nents, gfp_t gfp_mask) 2078306998fSHans Petter Selasky { 2088306998fSHans Petter Selasky if (nents == SG_MAX_SINGLE_ALLOC) { 2098306998fSHans Petter Selasky return ((void *)__get_free_page(gfp_mask)); 2108306998fSHans Petter Selasky } else 2118306998fSHans Petter Selasky return (kmalloc(nents * sizeof(struct scatterlist), gfp_mask)); 2128306998fSHans Petter Selasky } 2138306998fSHans Petter Selasky 2148306998fSHans Petter Selasky static inline void 2158306998fSHans Petter Selasky sg_kfree(struct scatterlist *sg, unsigned int nents) 2168306998fSHans Petter Selasky { 2178306998fSHans Petter Selasky if (nents == SG_MAX_SINGLE_ALLOC) { 2188306998fSHans Petter Selasky free_page((unsigned long)sg); 2198306998fSHans Petter Selasky } else 2208306998fSHans Petter Selasky kfree(sg); 2218306998fSHans Petter Selasky } 2228306998fSHans Petter Selasky 2238306998fSHans Petter Selasky static inline void 2248306998fSHans Petter Selasky __sg_free_table(struct sg_table *table, unsigned int max_ents, 2258306998fSHans Petter Selasky bool skip_first_chunk, sg_free_fn * free_fn) 2268d59ecb2SHans Petter Selasky { 2278d59ecb2SHans Petter Selasky struct scatterlist *sgl, *next; 2288d59ecb2SHans Petter Selasky 2298d59ecb2SHans Petter Selasky if (unlikely(!table->sgl)) 2308d59ecb2SHans Petter Selasky return; 2318d59ecb2SHans Petter Selasky 2328d59ecb2SHans Petter Selasky sgl = table->sgl; 2338d59ecb2SHans Petter Selasky while (table->orig_nents) { 2348d59ecb2SHans Petter Selasky unsigned int alloc_size = table->orig_nents; 2358d59ecb2SHans Petter Selasky unsigned int sg_size; 2368d59ecb2SHans Petter Selasky 2378d59ecb2SHans Petter Selasky if (alloc_size > max_ents) { 2388306998fSHans Petter Selasky next = sg_chain_ptr(&sgl[max_ents - 1]); 2398d59ecb2SHans Petter Selasky alloc_size = max_ents; 2408d59ecb2SHans Petter Selasky sg_size = alloc_size - 1; 2418d59ecb2SHans Petter Selasky } else { 2428d59ecb2SHans Petter Selasky sg_size = alloc_size; 2438d59ecb2SHans Petter Selasky next = NULL; 2448d59ecb2SHans Petter Selasky } 2458d59ecb2SHans Petter Selasky 2468d59ecb2SHans Petter Selasky table->orig_nents -= sg_size; 2478306998fSHans Petter Selasky if (skip_first_chunk) 2488306998fSHans Petter Selasky skip_first_chunk = 0; 2498306998fSHans Petter Selasky else 2508306998fSHans Petter Selasky free_fn(sgl, alloc_size); 2518d59ecb2SHans Petter Selasky sgl = next; 2528d59ecb2SHans Petter Selasky } 2538d59ecb2SHans Petter Selasky 2548d59ecb2SHans Petter Selasky table->sgl = NULL; 2558d59ecb2SHans Petter Selasky } 2568d59ecb2SHans Petter Selasky 2578d59ecb2SHans Petter Selasky static inline void 2588d59ecb2SHans Petter Selasky sg_free_table(struct sg_table *table) 2598d59ecb2SHans Petter Selasky { 2608306998fSHans Petter Selasky __sg_free_table(table, SG_MAX_SINGLE_ALLOC, 0, sg_kfree); 2618d59ecb2SHans Petter Selasky } 2628d59ecb2SHans Petter Selasky 2638d59ecb2SHans Petter Selasky static inline int 2648d59ecb2SHans Petter Selasky __sg_alloc_table(struct sg_table *table, unsigned int nents, 2658306998fSHans Petter Selasky unsigned int max_ents, struct scatterlist *first_chunk, 2668306998fSHans Petter Selasky gfp_t gfp_mask, sg_alloc_fn *alloc_fn) 2678d59ecb2SHans Petter Selasky { 2688d59ecb2SHans Petter Selasky struct scatterlist *sg, *prv; 2698d59ecb2SHans Petter Selasky unsigned int left; 2708d59ecb2SHans Petter Selasky 2718d59ecb2SHans Petter Selasky memset(table, 0, sizeof(*table)); 2728d59ecb2SHans Petter Selasky 2738d59ecb2SHans Petter Selasky if (nents == 0) 2748306998fSHans Petter Selasky return (-EINVAL); 2758d59ecb2SHans Petter Selasky left = nents; 2768d59ecb2SHans Petter Selasky prv = NULL; 2778d59ecb2SHans Petter Selasky do { 2788d59ecb2SHans Petter Selasky unsigned int sg_size; 2798d59ecb2SHans Petter Selasky unsigned int alloc_size = left; 2808d59ecb2SHans Petter Selasky 2818d59ecb2SHans Petter Selasky if (alloc_size > max_ents) { 2828d59ecb2SHans Petter Selasky alloc_size = max_ents; 2838d59ecb2SHans Petter Selasky sg_size = alloc_size - 1; 2848d59ecb2SHans Petter Selasky } else 2858d59ecb2SHans Petter Selasky sg_size = alloc_size; 2868d59ecb2SHans Petter Selasky 2878d59ecb2SHans Petter Selasky left -= sg_size; 2888d59ecb2SHans Petter Selasky 2898306998fSHans Petter Selasky if (first_chunk) { 2908306998fSHans Petter Selasky sg = first_chunk; 2918306998fSHans Petter Selasky first_chunk = NULL; 2928306998fSHans Petter Selasky } else { 2938306998fSHans Petter Selasky sg = alloc_fn(alloc_size, gfp_mask); 2948306998fSHans Petter Selasky } 2958d59ecb2SHans Petter Selasky if (unlikely(!sg)) { 2968d59ecb2SHans Petter Selasky if (prv) 2978d59ecb2SHans Petter Selasky table->nents = ++table->orig_nents; 2988d59ecb2SHans Petter Selasky 2998306998fSHans Petter Selasky return (-ENOMEM); 3008d59ecb2SHans Petter Selasky } 3018d59ecb2SHans Petter Selasky sg_init_table(sg, alloc_size); 3028d59ecb2SHans Petter Selasky table->nents = table->orig_nents += sg_size; 3038d59ecb2SHans Petter Selasky 3048d59ecb2SHans Petter Selasky if (prv) 3058d59ecb2SHans Petter Selasky sg_chain(prv, max_ents, sg); 3068d59ecb2SHans Petter Selasky else 3078d59ecb2SHans Petter Selasky table->sgl = sg; 3088d59ecb2SHans Petter Selasky 3098d59ecb2SHans Petter Selasky if (!left) 3108d59ecb2SHans Petter Selasky sg_mark_end(&sg[sg_size - 1]); 3118d59ecb2SHans Petter Selasky 3128d59ecb2SHans Petter Selasky prv = sg; 3138d59ecb2SHans Petter Selasky } while (left); 3148d59ecb2SHans Petter Selasky 3158306998fSHans Petter Selasky return (0); 3168d59ecb2SHans Petter Selasky } 3178d59ecb2SHans Petter Selasky 3188d59ecb2SHans Petter Selasky static inline int 3198d59ecb2SHans Petter Selasky sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) 3208d59ecb2SHans Petter Selasky { 3218d59ecb2SHans Petter Selasky int ret; 3228d59ecb2SHans Petter Selasky 3238d59ecb2SHans Petter Selasky ret = __sg_alloc_table(table, nents, SG_MAX_SINGLE_ALLOC, 3248306998fSHans Petter Selasky NULL, gfp_mask, sg_kmalloc); 3258d59ecb2SHans Petter Selasky if (unlikely(ret)) 3268306998fSHans Petter Selasky __sg_free_table(table, SG_MAX_SINGLE_ALLOC, 0, sg_kfree); 3278d59ecb2SHans Petter Selasky 3288306998fSHans Petter Selasky return (ret); 3298306998fSHans Petter Selasky } 3308306998fSHans Petter Selasky 3313e0856b6SJean-Sébastien Pédron #if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 51300 3323e0856b6SJean-Sébastien Pédron static inline struct scatterlist * 3333e0856b6SJean-Sébastien Pédron __sg_alloc_table_from_pages(struct sg_table *sgt, 3343e0856b6SJean-Sébastien Pédron struct page **pages, unsigned int count, 3353e0856b6SJean-Sébastien Pédron unsigned long off, unsigned long size, 3363e0856b6SJean-Sébastien Pédron unsigned int max_segment, 3373e0856b6SJean-Sébastien Pédron struct scatterlist *prv, unsigned int left_pages, 3383e0856b6SJean-Sébastien Pédron gfp_t gfp_mask) 3393e0856b6SJean-Sébastien Pédron #else 3408306998fSHans Petter Selasky static inline int 34157a865f8SHans Petter Selasky __sg_alloc_table_from_pages(struct sg_table *sgt, 3428306998fSHans Petter Selasky struct page **pages, unsigned int count, 3438306998fSHans Petter Selasky unsigned long off, unsigned long size, 34457a865f8SHans Petter Selasky unsigned int max_segment, gfp_t gfp_mask) 3453e0856b6SJean-Sébastien Pédron #endif 3468306998fSHans Petter Selasky { 34757a865f8SHans Petter Selasky unsigned int i, segs, cur, len; 3488306998fSHans Petter Selasky int rc; 34909b0401eSAustin Shafer struct scatterlist *s, *sg_iter; 3508306998fSHans Petter Selasky 3513e0856b6SJean-Sébastien Pédron #if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 51300 3523e0856b6SJean-Sébastien Pédron if (prv != NULL) { 3533e0856b6SJean-Sébastien Pédron panic( 3543e0856b6SJean-Sébastien Pédron "Support for prv != NULL not implemented in " 3553e0856b6SJean-Sébastien Pédron "__sg_alloc_table_from_pages()"); 3563e0856b6SJean-Sébastien Pédron } 3573e0856b6SJean-Sébastien Pédron #endif 3583e0856b6SJean-Sébastien Pédron 35957a865f8SHans Petter Selasky if (__predict_false(!max_segment || offset_in_page(max_segment))) 3603e0856b6SJean-Sébastien Pédron #if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 51300 3613e0856b6SJean-Sébastien Pédron return (ERR_PTR(-EINVAL)); 3623e0856b6SJean-Sébastien Pédron #else 36357a865f8SHans Petter Selasky return (-EINVAL); 3643e0856b6SJean-Sébastien Pédron #endif 36557a865f8SHans Petter Selasky 36657a865f8SHans Petter Selasky len = 0; 3678306998fSHans Petter Selasky for (segs = i = 1; i < count; ++i) { 36857a865f8SHans Petter Selasky len += PAGE_SIZE; 36957a865f8SHans Petter Selasky if (len >= max_segment || 37057a865f8SHans Petter Selasky page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) { 3718306998fSHans Petter Selasky ++segs; 37257a865f8SHans Petter Selasky len = 0; 37357a865f8SHans Petter Selasky } 3748306998fSHans Petter Selasky } 3758306998fSHans Petter Selasky if (__predict_false((rc = sg_alloc_table(sgt, segs, gfp_mask)))) 3763e0856b6SJean-Sébastien Pédron #if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 51300 3773e0856b6SJean-Sébastien Pédron return (ERR_PTR(rc)); 3783e0856b6SJean-Sébastien Pédron #else 3798306998fSHans Petter Selasky return (rc); 3803e0856b6SJean-Sébastien Pédron #endif 3818306998fSHans Petter Selasky 3828306998fSHans Petter Selasky cur = 0; 38309b0401eSAustin Shafer for_each_sg(sgt->sgl, sg_iter, sgt->orig_nents, i) { 3848306998fSHans Petter Selasky unsigned long seg_size; 3858306998fSHans Petter Selasky unsigned int j; 3868306998fSHans Petter Selasky 38709b0401eSAustin Shafer /* 38809b0401eSAustin Shafer * We need to make sure that when we exit this loop "s" has the 38909b0401eSAustin Shafer * last sg in the chain so we can call sg_mark_end() on it. 39009b0401eSAustin Shafer * Only set this inside the loop since sg_iter will be iterated 39109b0401eSAustin Shafer * until it is NULL. 39209b0401eSAustin Shafer */ 39309b0401eSAustin Shafer s = sg_iter; 39409b0401eSAustin Shafer 39557a865f8SHans Petter Selasky len = 0; 39657a865f8SHans Petter Selasky for (j = cur + 1; j < count; ++j) { 39757a865f8SHans Petter Selasky len += PAGE_SIZE; 39857a865f8SHans Petter Selasky if (len >= max_segment || page_to_pfn(pages[j]) != 3998306998fSHans Petter Selasky page_to_pfn(pages[j - 1]) + 1) 4008306998fSHans Petter Selasky break; 40157a865f8SHans Petter Selasky } 4028306998fSHans Petter Selasky 4038306998fSHans Petter Selasky seg_size = ((j - cur) << PAGE_SHIFT) - off; 404a399cf13SHans Petter Selasky sg_set_page(s, pages[cur], MIN(size, seg_size), off); 4058306998fSHans Petter Selasky size -= seg_size; 4068306998fSHans Petter Selasky off = 0; 4078306998fSHans Petter Selasky cur = j; 4088306998fSHans Petter Selasky } 4093e0856b6SJean-Sébastien Pédron KASSERT(s != NULL, ("s is NULL after loop in __sg_alloc_table_from_pages()")); 4103e0856b6SJean-Sébastien Pédron 4113e0856b6SJean-Sébastien Pédron #if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 51300 4123e0856b6SJean-Sébastien Pédron if (left_pages == 0) 4133e0856b6SJean-Sébastien Pédron sg_mark_end(s); 4143e0856b6SJean-Sébastien Pédron 4153e0856b6SJean-Sébastien Pédron return (s); 4163e0856b6SJean-Sébastien Pédron #else 4178306998fSHans Petter Selasky return (0); 4183e0856b6SJean-Sébastien Pédron #endif 4198306998fSHans Petter Selasky } 4208306998fSHans Petter Selasky 42157a865f8SHans Petter Selasky static inline int 42257a865f8SHans Petter Selasky sg_alloc_table_from_pages(struct sg_table *sgt, 42357a865f8SHans Petter Selasky struct page **pages, unsigned int count, 42457a865f8SHans Petter Selasky unsigned long off, unsigned long size, 42557a865f8SHans Petter Selasky gfp_t gfp_mask) 42657a865f8SHans Petter Selasky { 42757a865f8SHans Petter Selasky 4283e0856b6SJean-Sébastien Pédron #if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 51300 4293e0856b6SJean-Sébastien Pédron return (PTR_ERR_OR_ZERO(__sg_alloc_table_from_pages(sgt, pages, count, off, size, 4303e0856b6SJean-Sébastien Pédron SCATTERLIST_MAX_SEGMENT, NULL, 0, gfp_mask))); 4313e0856b6SJean-Sébastien Pédron #else 43257a865f8SHans Petter Selasky return (__sg_alloc_table_from_pages(sgt, pages, count, off, size, 43357a865f8SHans Petter Selasky SCATTERLIST_MAX_SEGMENT, gfp_mask)); 4343e0856b6SJean-Sébastien Pédron #endif 43557a865f8SHans Petter Selasky } 4368306998fSHans Petter Selasky 4378306998fSHans Petter Selasky static inline int 438f3490083SJean-Sébastien Pédron sg_alloc_table_from_pages_segment(struct sg_table *sgt, 439f3490083SJean-Sébastien Pédron struct page **pages, unsigned int count, unsigned int off, 440f3490083SJean-Sébastien Pédron unsigned long size, unsigned int max_segment, gfp_t gfp_mask) 441f3490083SJean-Sébastien Pédron { 442f3490083SJean-Sébastien Pédron #if defined(LINUXKPI_VERSION) && LINUXKPI_VERSION >= 51300 443f3490083SJean-Sébastien Pédron return (PTR_ERR_OR_ZERO(__sg_alloc_table_from_pages(sgt, pages, count, off, size, 444f3490083SJean-Sébastien Pédron max_segment, NULL, 0, gfp_mask))); 445f3490083SJean-Sébastien Pédron #else 446f3490083SJean-Sébastien Pédron return (__sg_alloc_table_from_pages(sgt, pages, count, off, size, 447f3490083SJean-Sébastien Pédron max_segment, gfp_mask)); 448f3490083SJean-Sébastien Pédron #endif 449f3490083SJean-Sébastien Pédron } 450f3490083SJean-Sébastien Pédron 451f3490083SJean-Sébastien Pédron static inline int 4528306998fSHans Petter Selasky sg_nents(struct scatterlist *sg) 4538306998fSHans Petter Selasky { 4548306998fSHans Petter Selasky int nents; 4558306998fSHans Petter Selasky 4568306998fSHans Petter Selasky for (nents = 0; sg; sg = sg_next(sg)) 4578306998fSHans Petter Selasky nents++; 4588306998fSHans Petter Selasky return (nents); 4598306998fSHans Petter Selasky } 4608306998fSHans Petter Selasky 4618306998fSHans Petter Selasky static inline void 4628306998fSHans Petter Selasky __sg_page_iter_start(struct sg_page_iter *piter, 4638306998fSHans Petter Selasky struct scatterlist *sglist, unsigned int nents, 4648306998fSHans Petter Selasky unsigned long pgoffset) 4658306998fSHans Petter Selasky { 4668306998fSHans Petter Selasky piter->internal.pg_advance = 0; 4678306998fSHans Petter Selasky piter->internal.nents = nents; 4688306998fSHans Petter Selasky 4698306998fSHans Petter Selasky piter->sg = sglist; 4708306998fSHans Petter Selasky piter->sg_pgoffset = pgoffset; 4718d59ecb2SHans Petter Selasky } 4728d59ecb2SHans Petter Selasky 4738d59ecb2SHans Petter Selasky static inline void 4748d59ecb2SHans Petter Selasky _sg_iter_next(struct sg_page_iter *iter) 4758d59ecb2SHans Petter Selasky { 4768d59ecb2SHans Petter Selasky struct scatterlist *sg; 4778d59ecb2SHans Petter Selasky unsigned int pgcount; 4788d59ecb2SHans Petter Selasky 4798d59ecb2SHans Petter Selasky sg = iter->sg; 4808d59ecb2SHans Petter Selasky pgcount = (sg->offset + sg->length + PAGE_SIZE - 1) >> PAGE_SHIFT; 4818d59ecb2SHans Petter Selasky 4828d59ecb2SHans Petter Selasky ++iter->sg_pgoffset; 4838d59ecb2SHans Petter Selasky while (iter->sg_pgoffset >= pgcount) { 4848d59ecb2SHans Petter Selasky iter->sg_pgoffset -= pgcount; 4858d59ecb2SHans Petter Selasky sg = sg_next(sg); 4868d59ecb2SHans Petter Selasky --iter->maxents; 4878d59ecb2SHans Petter Selasky if (sg == NULL || iter->maxents == 0) 4888d59ecb2SHans Petter Selasky break; 4898d59ecb2SHans Petter Selasky pgcount = (sg->offset + sg->length + PAGE_SIZE - 1) >> PAGE_SHIFT; 4908d59ecb2SHans Petter Selasky } 4918d59ecb2SHans Petter Selasky iter->sg = sg; 4928d59ecb2SHans Petter Selasky } 4938d59ecb2SHans Petter Selasky 4948306998fSHans Petter Selasky static inline int 4958306998fSHans Petter Selasky sg_page_count(struct scatterlist *sg) 4968306998fSHans Petter Selasky { 4978306998fSHans Petter Selasky return (PAGE_ALIGN(sg->offset + sg->length) >> PAGE_SHIFT); 4988306998fSHans Petter Selasky } 499fe4b041aSHans Petter Selasky #define sg_dma_page_count(sg) \ 500fe4b041aSHans Petter Selasky sg_page_count(sg) 5018306998fSHans Petter Selasky 5028306998fSHans Petter Selasky static inline bool 5038306998fSHans Petter Selasky __sg_page_iter_next(struct sg_page_iter *piter) 5048306998fSHans Petter Selasky { 5055e6233ccSHans Petter Selasky unsigned int pgcount; 5065e6233ccSHans Petter Selasky 5078306998fSHans Petter Selasky if (piter->internal.nents == 0) 5088306998fSHans Petter Selasky return (0); 5098306998fSHans Petter Selasky if (piter->sg == NULL) 5108306998fSHans Petter Selasky return (0); 5118306998fSHans Petter Selasky 5128306998fSHans Petter Selasky piter->sg_pgoffset += piter->internal.pg_advance; 5138306998fSHans Petter Selasky piter->internal.pg_advance = 1; 5148306998fSHans Petter Selasky 5155e6233ccSHans Petter Selasky while (1) { 5165e6233ccSHans Petter Selasky pgcount = sg_page_count(piter->sg); 5175e6233ccSHans Petter Selasky if (likely(piter->sg_pgoffset < pgcount)) 5185e6233ccSHans Petter Selasky break; 5195e6233ccSHans Petter Selasky piter->sg_pgoffset -= pgcount; 5208306998fSHans Petter Selasky piter->sg = sg_next(piter->sg); 5218306998fSHans Petter Selasky if (--piter->internal.nents == 0) 5228306998fSHans Petter Selasky return (0); 5238306998fSHans Petter Selasky if (piter->sg == NULL) 5248306998fSHans Petter Selasky return (0); 5258306998fSHans Petter Selasky } 5268306998fSHans Petter Selasky return (1); 5278306998fSHans Petter Selasky } 528fe4b041aSHans Petter Selasky #define __sg_page_iter_dma_next(itr) \ 529fe4b041aSHans Petter Selasky __sg_page_iter_next(&(itr)->base) 5308306998fSHans Petter Selasky 5318d59ecb2SHans Petter Selasky static inline void 5328d59ecb2SHans Petter Selasky _sg_iter_init(struct scatterlist *sgl, struct sg_page_iter *iter, 5338d59ecb2SHans Petter Selasky unsigned int nents, unsigned long pgoffset) 5348d59ecb2SHans Petter Selasky { 5358d59ecb2SHans Petter Selasky if (nents) { 5368d59ecb2SHans Petter Selasky iter->sg = sgl; 5378d59ecb2SHans Petter Selasky iter->sg_pgoffset = pgoffset - 1; 5388d59ecb2SHans Petter Selasky iter->maxents = nents; 5398d59ecb2SHans Petter Selasky _sg_iter_next(iter); 5408d59ecb2SHans Petter Selasky } else { 5418d59ecb2SHans Petter Selasky iter->sg = NULL; 5428d59ecb2SHans Petter Selasky iter->sg_pgoffset = 0; 5438d59ecb2SHans Petter Selasky iter->maxents = 0; 5448d59ecb2SHans Petter Selasky } 5458d59ecb2SHans Petter Selasky } 5468d59ecb2SHans Petter Selasky 547fe4b041aSHans Petter Selasky /* 548fe4b041aSHans Petter Selasky * sg_page_iter_dma_address() is implemented as a macro because it 549fe4b041aSHans Petter Selasky * needs to accept two different and identical structure types. This 550fe4b041aSHans Petter Selasky * allows both old and new code to co-exist. The compile time assert 551fe4b041aSHans Petter Selasky * adds some safety, that the structure sizes match. 552fe4b041aSHans Petter Selasky */ 553fe4b041aSHans Petter Selasky #define sg_page_iter_dma_address(spi) ({ \ 554fe4b041aSHans Petter Selasky struct sg_page_iter *__spi = (void *)(spi); \ 555fe4b041aSHans Petter Selasky dma_addr_t __dma_address; \ 556fe4b041aSHans Petter Selasky CTASSERT(sizeof(*(spi)) == sizeof(*__spi)); \ 557fe4b041aSHans Petter Selasky __dma_address = __spi->sg->dma_address + \ 558fe4b041aSHans Petter Selasky (__spi->sg_pgoffset << PAGE_SHIFT); \ 559fe4b041aSHans Petter Selasky __dma_address; \ 560fe4b041aSHans Petter Selasky }) 5618d59ecb2SHans Petter Selasky 5628306998fSHans Petter Selasky static inline struct page * 5638306998fSHans Petter Selasky sg_page_iter_page(struct sg_page_iter *piter) 5648306998fSHans Petter Selasky { 5658306998fSHans Petter Selasky return (nth_page(sg_page(piter->sg), piter->sg_pgoffset)); 5668306998fSHans Petter Selasky } 5678d59ecb2SHans Petter Selasky 5684abbf816SBjoern A. Zeeb static __inline size_t 5694abbf816SBjoern A. Zeeb sg_pcopy_from_buffer(struct scatterlist *sgl, unsigned int nents, 5704abbf816SBjoern A. Zeeb const void *buf, size_t buflen, off_t skip) 5714abbf816SBjoern A. Zeeb { 5724abbf816SBjoern A. Zeeb struct sg_page_iter piter; 5734abbf816SBjoern A. Zeeb struct page *page; 5744abbf816SBjoern A. Zeeb struct sf_buf *sf; 5754abbf816SBjoern A. Zeeb size_t len, copied; 5764abbf816SBjoern A. Zeeb char *p, *b; 5774abbf816SBjoern A. Zeeb 5784abbf816SBjoern A. Zeeb if (buflen == 0) 5794abbf816SBjoern A. Zeeb return (0); 5804abbf816SBjoern A. Zeeb 5814abbf816SBjoern A. Zeeb b = __DECONST(char *, buf); 5824abbf816SBjoern A. Zeeb copied = 0; 5834abbf816SBjoern A. Zeeb sched_pin(); 5844abbf816SBjoern A. Zeeb for_each_sg_page(sgl, &piter, nents, 0) { 5854abbf816SBjoern A. Zeeb 5864abbf816SBjoern A. Zeeb /* Skip to the start. */ 5874abbf816SBjoern A. Zeeb if (piter.sg->length <= skip) { 5884abbf816SBjoern A. Zeeb skip -= piter.sg->length; 5894abbf816SBjoern A. Zeeb continue; 5904abbf816SBjoern A. Zeeb } 5914abbf816SBjoern A. Zeeb 5924abbf816SBjoern A. Zeeb /* See how much to copy. */ 5934abbf816SBjoern A. Zeeb KASSERT(((piter.sg->length - skip) != 0 && (buflen != 0)), 5944abbf816SBjoern A. Zeeb ("%s: sg len %u - skip %ju || buflen %zu is 0\n", 5954abbf816SBjoern A. Zeeb __func__, piter.sg->length, (uintmax_t)skip, buflen)); 5964abbf816SBjoern A. Zeeb len = min(piter.sg->length - skip, buflen); 5974abbf816SBjoern A. Zeeb 5984abbf816SBjoern A. Zeeb page = sg_page_iter_page(&piter); 5994abbf816SBjoern A. Zeeb sf = sf_buf_alloc(page, SFB_CPUPRIVATE | SFB_NOWAIT); 6004abbf816SBjoern A. Zeeb if (sf == NULL) 6014abbf816SBjoern A. Zeeb break; 6024abbf816SBjoern A. Zeeb p = (char *)sf_buf_kva(sf) + piter.sg_pgoffset + skip; 6034abbf816SBjoern A. Zeeb memcpy(p, b, len); 6044abbf816SBjoern A. Zeeb sf_buf_free(sf); 6054abbf816SBjoern A. Zeeb 606edfcdffeSBjoern A. Zeeb /* We copied so nothing more to skip. */ 607edfcdffeSBjoern A. Zeeb skip = 0; 6084abbf816SBjoern A. Zeeb copied += len; 6094abbf816SBjoern A. Zeeb /* Either we exactly filled the page, or we are done. */ 6104abbf816SBjoern A. Zeeb buflen -= len; 6114abbf816SBjoern A. Zeeb if (buflen == 0) 6124abbf816SBjoern A. Zeeb break; 6134abbf816SBjoern A. Zeeb b += len; 6144abbf816SBjoern A. Zeeb } 6154abbf816SBjoern A. Zeeb sched_unpin(); 6164abbf816SBjoern A. Zeeb 6174abbf816SBjoern A. Zeeb return (copied); 6184abbf816SBjoern A. Zeeb } 6194abbf816SBjoern A. Zeeb 620c072f6e8SVladimir Kondratyev static inline size_t 621c072f6e8SVladimir Kondratyev sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents, 622c072f6e8SVladimir Kondratyev const void *buf, size_t buflen) 623c072f6e8SVladimir Kondratyev { 624c072f6e8SVladimir Kondratyev return (sg_pcopy_from_buffer(sgl, nents, buf, buflen, 0)); 625c072f6e8SVladimir Kondratyev } 626c072f6e8SVladimir Kondratyev 627c072f6e8SVladimir Kondratyev static inline size_t 628c072f6e8SVladimir Kondratyev sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents, 629c072f6e8SVladimir Kondratyev void *buf, size_t buflen, off_t offset) 630c072f6e8SVladimir Kondratyev { 631c072f6e8SVladimir Kondratyev struct sg_page_iter iter; 632c072f6e8SVladimir Kondratyev struct scatterlist *sg; 633c072f6e8SVladimir Kondratyev struct page *page; 634c072f6e8SVladimir Kondratyev struct sf_buf *sf; 635c072f6e8SVladimir Kondratyev char *vaddr; 636c072f6e8SVladimir Kondratyev size_t total = 0; 637c072f6e8SVladimir Kondratyev size_t len; 638c072f6e8SVladimir Kondratyev 639c072f6e8SVladimir Kondratyev if (!PMAP_HAS_DMAP) 640c072f6e8SVladimir Kondratyev sched_pin(); 641c072f6e8SVladimir Kondratyev for_each_sg_page(sgl, &iter, nents, 0) { 642c072f6e8SVladimir Kondratyev sg = iter.sg; 643c072f6e8SVladimir Kondratyev 644c072f6e8SVladimir Kondratyev if (offset >= sg->length) { 645c072f6e8SVladimir Kondratyev offset -= sg->length; 646c072f6e8SVladimir Kondratyev continue; 647c072f6e8SVladimir Kondratyev } 648c072f6e8SVladimir Kondratyev len = ulmin(buflen, sg->length - offset); 649c072f6e8SVladimir Kondratyev if (len == 0) 650c072f6e8SVladimir Kondratyev break; 651c072f6e8SVladimir Kondratyev 652c072f6e8SVladimir Kondratyev page = sg_page_iter_page(&iter); 653c072f6e8SVladimir Kondratyev if (!PMAP_HAS_DMAP) { 654c072f6e8SVladimir Kondratyev sf = sf_buf_alloc(page, SFB_CPUPRIVATE | SFB_NOWAIT); 655c072f6e8SVladimir Kondratyev if (sf == NULL) 656c072f6e8SVladimir Kondratyev break; 657c072f6e8SVladimir Kondratyev vaddr = (char *)sf_buf_kva(sf); 658c072f6e8SVladimir Kondratyev } else 659d1ea0764SBjoern A. Zeeb vaddr = (char *)PHYS_TO_DMAP(page_to_phys(page)); 660c072f6e8SVladimir Kondratyev memcpy(buf, vaddr + sg->offset + offset, len); 661c072f6e8SVladimir Kondratyev if (!PMAP_HAS_DMAP) 662c072f6e8SVladimir Kondratyev sf_buf_free(sf); 663c072f6e8SVladimir Kondratyev 664c072f6e8SVladimir Kondratyev /* start at beginning of next page */ 665c072f6e8SVladimir Kondratyev offset = 0; 666c072f6e8SVladimir Kondratyev 667c072f6e8SVladimir Kondratyev /* advance buffer */ 668c072f6e8SVladimir Kondratyev buf = (char *)buf + len; 669c072f6e8SVladimir Kondratyev buflen -= len; 670c072f6e8SVladimir Kondratyev total += len; 671c072f6e8SVladimir Kondratyev } 672c072f6e8SVladimir Kondratyev if (!PMAP_HAS_DMAP) 673c072f6e8SVladimir Kondratyev sched_unpin(); 674c072f6e8SVladimir Kondratyev return (total); 675c072f6e8SVladimir Kondratyev } 676c072f6e8SVladimir Kondratyev 677307f78f3SVladimir Kondratyev #endif /* _LINUXKPI_LINUX_SCATTERLIST_H_ */ 678