18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2007 Jens Axboe <jens.axboe@oracle.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Scatterlist handling helpers. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/export.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 108c2ecf20Sopenharmony_ci#include <linux/highmem.h> 118c2ecf20Sopenharmony_ci#include <linux/kmemleak.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/** 148c2ecf20Sopenharmony_ci * sg_next - return the next scatterlist entry in a list 158c2ecf20Sopenharmony_ci * @sg: The current sg entry 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Description: 188c2ecf20Sopenharmony_ci * Usually the next entry will be @sg@ + 1, but if this sg element is part 198c2ecf20Sopenharmony_ci * of a chained scatterlist, it could jump to the start of a new 208c2ecf20Sopenharmony_ci * scatterlist array. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci **/ 238c2ecf20Sopenharmony_cistruct scatterlist *sg_next(struct scatterlist *sg) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci if (sg_is_last(sg)) 268c2ecf20Sopenharmony_ci return NULL; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci sg++; 298c2ecf20Sopenharmony_ci if (unlikely(sg_is_chain(sg))) 308c2ecf20Sopenharmony_ci sg = sg_chain_ptr(sg); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci return sg; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_next); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/** 378c2ecf20Sopenharmony_ci * sg_nents - return total count of entries in scatterlist 388c2ecf20Sopenharmony_ci * @sg: The scatterlist 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * Description: 418c2ecf20Sopenharmony_ci * Allows to know how many entries are in sg, taking into acount 428c2ecf20Sopenharmony_ci * chaining as well 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci **/ 458c2ecf20Sopenharmony_ciint sg_nents(struct scatterlist *sg) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci int nents; 488c2ecf20Sopenharmony_ci for (nents = 0; sg; sg = sg_next(sg)) 498c2ecf20Sopenharmony_ci nents++; 508c2ecf20Sopenharmony_ci return nents; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_nents); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * sg_nents_for_len - return total count of entries in scatterlist 568c2ecf20Sopenharmony_ci * needed to satisfy the supplied length 578c2ecf20Sopenharmony_ci * @sg: The scatterlist 588c2ecf20Sopenharmony_ci * @len: The total required length 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * Description: 618c2ecf20Sopenharmony_ci * Determines the number of entries in sg that are required to meet 628c2ecf20Sopenharmony_ci * the supplied length, taking into acount chaining as well 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * Returns: 658c2ecf20Sopenharmony_ci * the number of sg entries needed, negative error on failure 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci **/ 688c2ecf20Sopenharmony_ciint sg_nents_for_len(struct scatterlist *sg, u64 len) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci int nents; 718c2ecf20Sopenharmony_ci u64 total; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (!len) 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci for (nents = 0, total = 0; sg; sg = sg_next(sg)) { 778c2ecf20Sopenharmony_ci nents++; 788c2ecf20Sopenharmony_ci total += sg->length; 798c2ecf20Sopenharmony_ci if (total >= len) 808c2ecf20Sopenharmony_ci return nents; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return -EINVAL; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_nents_for_len); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/** 888c2ecf20Sopenharmony_ci * sg_last - return the last scatterlist entry in a list 898c2ecf20Sopenharmony_ci * @sgl: First entry in the scatterlist 908c2ecf20Sopenharmony_ci * @nents: Number of entries in the scatterlist 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * Description: 938c2ecf20Sopenharmony_ci * Should only be used casually, it (currently) scans the entire list 948c2ecf20Sopenharmony_ci * to get the last entry. 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * Note that the @sgl@ pointer passed in need not be the first one, 978c2ecf20Sopenharmony_ci * the important bit is that @nents@ denotes the number of entries that 988c2ecf20Sopenharmony_ci * exist from @sgl@. 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci **/ 1018c2ecf20Sopenharmony_cistruct scatterlist *sg_last(struct scatterlist *sgl, unsigned int nents) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct scatterlist *sg, *ret = NULL; 1048c2ecf20Sopenharmony_ci unsigned int i; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, nents, i) 1078c2ecf20Sopenharmony_ci ret = sg; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci BUG_ON(!sg_is_last(ret)); 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_last); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/** 1158c2ecf20Sopenharmony_ci * sg_init_table - Initialize SG table 1168c2ecf20Sopenharmony_ci * @sgl: The SG table 1178c2ecf20Sopenharmony_ci * @nents: Number of entries in table 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * Notes: 1208c2ecf20Sopenharmony_ci * If this is part of a chained sg table, sg_mark_end() should be 1218c2ecf20Sopenharmony_ci * used only on the last table part. 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci **/ 1248c2ecf20Sopenharmony_civoid sg_init_table(struct scatterlist *sgl, unsigned int nents) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci memset(sgl, 0, sizeof(*sgl) * nents); 1278c2ecf20Sopenharmony_ci sg_init_marker(sgl, nents); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_init_table); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/** 1328c2ecf20Sopenharmony_ci * sg_init_one - Initialize a single entry sg list 1338c2ecf20Sopenharmony_ci * @sg: SG entry 1348c2ecf20Sopenharmony_ci * @buf: Virtual address for IO 1358c2ecf20Sopenharmony_ci * @buflen: IO length 1368c2ecf20Sopenharmony_ci * 1378c2ecf20Sopenharmony_ci **/ 1388c2ecf20Sopenharmony_civoid sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci sg_init_table(sg, 1); 1418c2ecf20Sopenharmony_ci sg_set_buf(sg, buf, buflen); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_init_one); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * The default behaviour of sg_alloc_table() is to use these kmalloc/kfree 1478c2ecf20Sopenharmony_ci * helpers. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_cistatic struct scatterlist *sg_kmalloc(unsigned int nents, gfp_t gfp_mask) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci if (nents == SG_MAX_SINGLE_ALLOC) { 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * Kmemleak doesn't track page allocations as they are not 1548c2ecf20Sopenharmony_ci * commonly used (in a raw form) for kernel data structures. 1558c2ecf20Sopenharmony_ci * As we chain together a list of pages and then a normal 1568c2ecf20Sopenharmony_ci * kmalloc (tracked by kmemleak), in order to for that last 1578c2ecf20Sopenharmony_ci * allocation not to become decoupled (and thus a 1588c2ecf20Sopenharmony_ci * false-positive) we need to inform kmemleak of all the 1598c2ecf20Sopenharmony_ci * intermediate allocations. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci void *ptr = (void *) __get_free_page(gfp_mask); 1628c2ecf20Sopenharmony_ci kmemleak_alloc(ptr, PAGE_SIZE, 1, gfp_mask); 1638c2ecf20Sopenharmony_ci return ptr; 1648c2ecf20Sopenharmony_ci } else 1658c2ecf20Sopenharmony_ci return kmalloc_array(nents, sizeof(struct scatterlist), 1668c2ecf20Sopenharmony_ci gfp_mask); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void sg_kfree(struct scatterlist *sg, unsigned int nents) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci if (nents == SG_MAX_SINGLE_ALLOC) { 1728c2ecf20Sopenharmony_ci kmemleak_free(sg); 1738c2ecf20Sopenharmony_ci free_page((unsigned long) sg); 1748c2ecf20Sopenharmony_ci } else 1758c2ecf20Sopenharmony_ci kfree(sg); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/** 1798c2ecf20Sopenharmony_ci * __sg_free_table - Free a previously mapped sg table 1808c2ecf20Sopenharmony_ci * @table: The sg table header to use 1818c2ecf20Sopenharmony_ci * @max_ents: The maximum number of entries per single scatterlist 1828c2ecf20Sopenharmony_ci * @nents_first_chunk: Number of entries int the (preallocated) first 1838c2ecf20Sopenharmony_ci * scatterlist chunk, 0 means no such preallocated first chunk 1848c2ecf20Sopenharmony_ci * @free_fn: Free function 1858c2ecf20Sopenharmony_ci * 1868c2ecf20Sopenharmony_ci * Description: 1878c2ecf20Sopenharmony_ci * Free an sg table previously allocated and setup with 1888c2ecf20Sopenharmony_ci * __sg_alloc_table(). The @max_ents value must be identical to 1898c2ecf20Sopenharmony_ci * that previously used with __sg_alloc_table(). 1908c2ecf20Sopenharmony_ci * 1918c2ecf20Sopenharmony_ci **/ 1928c2ecf20Sopenharmony_civoid __sg_free_table(struct sg_table *table, unsigned int max_ents, 1938c2ecf20Sopenharmony_ci unsigned int nents_first_chunk, sg_free_fn *free_fn) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct scatterlist *sgl, *next; 1968c2ecf20Sopenharmony_ci unsigned curr_max_ents = nents_first_chunk ?: max_ents; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (unlikely(!table->sgl)) 1998c2ecf20Sopenharmony_ci return; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci sgl = table->sgl; 2028c2ecf20Sopenharmony_ci while (table->orig_nents) { 2038c2ecf20Sopenharmony_ci unsigned int alloc_size = table->orig_nents; 2048c2ecf20Sopenharmony_ci unsigned int sg_size; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * If we have more than max_ents segments left, 2088c2ecf20Sopenharmony_ci * then assign 'next' to the sg table after the current one. 2098c2ecf20Sopenharmony_ci * sg_size is then one less than alloc size, since the last 2108c2ecf20Sopenharmony_ci * element is the chain pointer. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci if (alloc_size > curr_max_ents) { 2138c2ecf20Sopenharmony_ci next = sg_chain_ptr(&sgl[curr_max_ents - 1]); 2148c2ecf20Sopenharmony_ci alloc_size = curr_max_ents; 2158c2ecf20Sopenharmony_ci sg_size = alloc_size - 1; 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci sg_size = alloc_size; 2188c2ecf20Sopenharmony_ci next = NULL; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci table->orig_nents -= sg_size; 2228c2ecf20Sopenharmony_ci if (nents_first_chunk) 2238c2ecf20Sopenharmony_ci nents_first_chunk = 0; 2248c2ecf20Sopenharmony_ci else 2258c2ecf20Sopenharmony_ci free_fn(sgl, alloc_size); 2268c2ecf20Sopenharmony_ci sgl = next; 2278c2ecf20Sopenharmony_ci curr_max_ents = max_ents; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci table->sgl = NULL; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__sg_free_table); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/** 2358c2ecf20Sopenharmony_ci * sg_free_table - Free a previously allocated sg table 2368c2ecf20Sopenharmony_ci * @table: The mapped sg table header 2378c2ecf20Sopenharmony_ci * 2388c2ecf20Sopenharmony_ci **/ 2398c2ecf20Sopenharmony_civoid sg_free_table(struct sg_table *table) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci __sg_free_table(table, SG_MAX_SINGLE_ALLOC, false, sg_kfree); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_free_table); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/** 2468c2ecf20Sopenharmony_ci * __sg_alloc_table - Allocate and initialize an sg table with given allocator 2478c2ecf20Sopenharmony_ci * @table: The sg table header to use 2488c2ecf20Sopenharmony_ci * @nents: Number of entries in sg list 2498c2ecf20Sopenharmony_ci * @max_ents: The maximum number of entries the allocator returns per call 2508c2ecf20Sopenharmony_ci * @nents_first_chunk: Number of entries int the (preallocated) first 2518c2ecf20Sopenharmony_ci * scatterlist chunk, 0 means no such preallocated chunk provided by user 2528c2ecf20Sopenharmony_ci * @gfp_mask: GFP allocation mask 2538c2ecf20Sopenharmony_ci * @alloc_fn: Allocator to use 2548c2ecf20Sopenharmony_ci * 2558c2ecf20Sopenharmony_ci * Description: 2568c2ecf20Sopenharmony_ci * This function returns a @table @nents long. The allocator is 2578c2ecf20Sopenharmony_ci * defined to return scatterlist chunks of maximum size @max_ents. 2588c2ecf20Sopenharmony_ci * Thus if @nents is bigger than @max_ents, the scatterlists will be 2598c2ecf20Sopenharmony_ci * chained in units of @max_ents. 2608c2ecf20Sopenharmony_ci * 2618c2ecf20Sopenharmony_ci * Notes: 2628c2ecf20Sopenharmony_ci * If this function returns non-0 (eg failure), the caller must call 2638c2ecf20Sopenharmony_ci * __sg_free_table() to cleanup any leftover allocations. 2648c2ecf20Sopenharmony_ci * 2658c2ecf20Sopenharmony_ci **/ 2668c2ecf20Sopenharmony_ciint __sg_alloc_table(struct sg_table *table, unsigned int nents, 2678c2ecf20Sopenharmony_ci unsigned int max_ents, struct scatterlist *first_chunk, 2688c2ecf20Sopenharmony_ci unsigned int nents_first_chunk, gfp_t gfp_mask, 2698c2ecf20Sopenharmony_ci sg_alloc_fn *alloc_fn) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct scatterlist *sg, *prv; 2728c2ecf20Sopenharmony_ci unsigned int left; 2738c2ecf20Sopenharmony_ci unsigned curr_max_ents = nents_first_chunk ?: max_ents; 2748c2ecf20Sopenharmony_ci unsigned prv_max_ents; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci memset(table, 0, sizeof(*table)); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (nents == 0) 2798c2ecf20Sopenharmony_ci return -EINVAL; 2808c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_NO_SG_CHAIN 2818c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(nents > max_ents)) 2828c2ecf20Sopenharmony_ci return -EINVAL; 2838c2ecf20Sopenharmony_ci#endif 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci left = nents; 2868c2ecf20Sopenharmony_ci prv = NULL; 2878c2ecf20Sopenharmony_ci do { 2888c2ecf20Sopenharmony_ci unsigned int sg_size, alloc_size = left; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (alloc_size > curr_max_ents) { 2918c2ecf20Sopenharmony_ci alloc_size = curr_max_ents; 2928c2ecf20Sopenharmony_ci sg_size = alloc_size - 1; 2938c2ecf20Sopenharmony_ci } else 2948c2ecf20Sopenharmony_ci sg_size = alloc_size; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci left -= sg_size; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (first_chunk) { 2998c2ecf20Sopenharmony_ci sg = first_chunk; 3008c2ecf20Sopenharmony_ci first_chunk = NULL; 3018c2ecf20Sopenharmony_ci } else { 3028c2ecf20Sopenharmony_ci sg = alloc_fn(alloc_size, gfp_mask); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci if (unlikely(!sg)) { 3058c2ecf20Sopenharmony_ci /* 3068c2ecf20Sopenharmony_ci * Adjust entry count to reflect that the last 3078c2ecf20Sopenharmony_ci * entry of the previous table won't be used for 3088c2ecf20Sopenharmony_ci * linkage. Without this, sg_kfree() may get 3098c2ecf20Sopenharmony_ci * confused. 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_ci if (prv) 3128c2ecf20Sopenharmony_ci table->nents = ++table->orig_nents; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return -ENOMEM; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci sg_init_table(sg, alloc_size); 3188c2ecf20Sopenharmony_ci table->nents = table->orig_nents += sg_size; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* 3218c2ecf20Sopenharmony_ci * If this is the first mapping, assign the sg table header. 3228c2ecf20Sopenharmony_ci * If this is not the first mapping, chain previous part. 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_ci if (prv) 3258c2ecf20Sopenharmony_ci sg_chain(prv, prv_max_ents, sg); 3268c2ecf20Sopenharmony_ci else 3278c2ecf20Sopenharmony_ci table->sgl = sg; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* 3308c2ecf20Sopenharmony_ci * If no more entries after this one, mark the end 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci if (!left) 3338c2ecf20Sopenharmony_ci sg_mark_end(&sg[sg_size - 1]); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci prv = sg; 3368c2ecf20Sopenharmony_ci prv_max_ents = curr_max_ents; 3378c2ecf20Sopenharmony_ci curr_max_ents = max_ents; 3388c2ecf20Sopenharmony_ci } while (left); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return 0; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__sg_alloc_table); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/** 3458c2ecf20Sopenharmony_ci * sg_alloc_table - Allocate and initialize an sg table 3468c2ecf20Sopenharmony_ci * @table: The sg table header to use 3478c2ecf20Sopenharmony_ci * @nents: Number of entries in sg list 3488c2ecf20Sopenharmony_ci * @gfp_mask: GFP allocation mask 3498c2ecf20Sopenharmony_ci * 3508c2ecf20Sopenharmony_ci * Description: 3518c2ecf20Sopenharmony_ci * Allocate and initialize an sg table. If @nents@ is larger than 3528c2ecf20Sopenharmony_ci * SG_MAX_SINGLE_ALLOC a chained sg table will be setup. 3538c2ecf20Sopenharmony_ci * 3548c2ecf20Sopenharmony_ci **/ 3558c2ecf20Sopenharmony_ciint sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci int ret; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci ret = __sg_alloc_table(table, nents, SG_MAX_SINGLE_ALLOC, 3608c2ecf20Sopenharmony_ci NULL, 0, gfp_mask, sg_kmalloc); 3618c2ecf20Sopenharmony_ci if (unlikely(ret)) 3628c2ecf20Sopenharmony_ci __sg_free_table(table, SG_MAX_SINGLE_ALLOC, 0, sg_kfree); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return ret; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_alloc_table); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic struct scatterlist *get_next_sg(struct sg_table *table, 3698c2ecf20Sopenharmony_ci struct scatterlist *cur, 3708c2ecf20Sopenharmony_ci unsigned long needed_sges, 3718c2ecf20Sopenharmony_ci gfp_t gfp_mask) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct scatterlist *new_sg, *next_sg; 3748c2ecf20Sopenharmony_ci unsigned int alloc_size; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (cur) { 3778c2ecf20Sopenharmony_ci next_sg = sg_next(cur); 3788c2ecf20Sopenharmony_ci /* Check if last entry should be keeped for chainning */ 3798c2ecf20Sopenharmony_ci if (!sg_is_last(next_sg) || needed_sges == 1) 3808c2ecf20Sopenharmony_ci return next_sg; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci alloc_size = min_t(unsigned long, needed_sges, SG_MAX_SINGLE_ALLOC); 3848c2ecf20Sopenharmony_ci new_sg = sg_kmalloc(alloc_size, gfp_mask); 3858c2ecf20Sopenharmony_ci if (!new_sg) 3868c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3878c2ecf20Sopenharmony_ci sg_init_table(new_sg, alloc_size); 3888c2ecf20Sopenharmony_ci if (cur) { 3898c2ecf20Sopenharmony_ci __sg_chain(next_sg, new_sg); 3908c2ecf20Sopenharmony_ci table->orig_nents += alloc_size - 1; 3918c2ecf20Sopenharmony_ci } else { 3928c2ecf20Sopenharmony_ci table->sgl = new_sg; 3938c2ecf20Sopenharmony_ci table->orig_nents = alloc_size; 3948c2ecf20Sopenharmony_ci table->nents = 0; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci return new_sg; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/** 4008c2ecf20Sopenharmony_ci * __sg_alloc_table_from_pages - Allocate and initialize an sg table from 4018c2ecf20Sopenharmony_ci * an array of pages 4028c2ecf20Sopenharmony_ci * @sgt: The sg table header to use 4038c2ecf20Sopenharmony_ci * @pages: Pointer to an array of page pointers 4048c2ecf20Sopenharmony_ci * @n_pages: Number of pages in the pages array 4058c2ecf20Sopenharmony_ci * @offset: Offset from start of the first page to the start of a buffer 4068c2ecf20Sopenharmony_ci * @size: Number of valid bytes in the buffer (after offset) 4078c2ecf20Sopenharmony_ci * @max_segment: Maximum size of a scatterlist element in bytes 4088c2ecf20Sopenharmony_ci * @prv: Last populated sge in sgt 4098c2ecf20Sopenharmony_ci * @left_pages: Left pages caller have to set after this call 4108c2ecf20Sopenharmony_ci * @gfp_mask: GFP allocation mask 4118c2ecf20Sopenharmony_ci * 4128c2ecf20Sopenharmony_ci * Description: 4138c2ecf20Sopenharmony_ci * If @prv is NULL, allocate and initialize an sg table from a list of pages, 4148c2ecf20Sopenharmony_ci * else reuse the scatterlist passed in at @prv. 4158c2ecf20Sopenharmony_ci * Contiguous ranges of the pages are squashed into a single scatterlist 4168c2ecf20Sopenharmony_ci * entry up to the maximum size specified in @max_segment. A user may 4178c2ecf20Sopenharmony_ci * provide an offset at a start and a size of valid data in a buffer 4188c2ecf20Sopenharmony_ci * specified by the page array. 4198c2ecf20Sopenharmony_ci * 4208c2ecf20Sopenharmony_ci * Returns: 4218c2ecf20Sopenharmony_ci * Last SGE in sgt on success, PTR_ERR on otherwise. 4228c2ecf20Sopenharmony_ci * The allocation in @sgt must be released by sg_free_table. 4238c2ecf20Sopenharmony_ci * 4248c2ecf20Sopenharmony_ci * Notes: 4258c2ecf20Sopenharmony_ci * If this function returns non-0 (eg failure), the caller must call 4268c2ecf20Sopenharmony_ci * sg_free_table() to cleanup any leftover allocations. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_cistruct scatterlist *__sg_alloc_table_from_pages(struct sg_table *sgt, 4298c2ecf20Sopenharmony_ci struct page **pages, unsigned int n_pages, unsigned int offset, 4308c2ecf20Sopenharmony_ci unsigned long size, unsigned int max_segment, 4318c2ecf20Sopenharmony_ci struct scatterlist *prv, unsigned int left_pages, 4328c2ecf20Sopenharmony_ci gfp_t gfp_mask) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci unsigned int chunks, cur_page, seg_len, i, prv_len = 0; 4358c2ecf20Sopenharmony_ci unsigned int added_nents = 0; 4368c2ecf20Sopenharmony_ci struct scatterlist *s = prv; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* 4398c2ecf20Sopenharmony_ci * The algorithm below requires max_segment to be aligned to PAGE_SIZE 4408c2ecf20Sopenharmony_ci * otherwise it can overshoot. 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_ci max_segment = ALIGN_DOWN(max_segment, PAGE_SIZE); 4438c2ecf20Sopenharmony_ci if (WARN_ON(max_segment < PAGE_SIZE)) 4448c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARCH_NO_SG_CHAIN) && prv) 4478c2ecf20Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (prv) { 4508c2ecf20Sopenharmony_ci unsigned long paddr = (page_to_pfn(sg_page(prv)) * PAGE_SIZE + 4518c2ecf20Sopenharmony_ci prv->offset + prv->length) / 4528c2ecf20Sopenharmony_ci PAGE_SIZE; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (WARN_ON(offset)) 4558c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Merge contiguous pages into the last SG */ 4588c2ecf20Sopenharmony_ci prv_len = prv->length; 4598c2ecf20Sopenharmony_ci while (n_pages && page_to_pfn(pages[0]) == paddr) { 4608c2ecf20Sopenharmony_ci if (prv->length + PAGE_SIZE > max_segment) 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci prv->length += PAGE_SIZE; 4638c2ecf20Sopenharmony_ci paddr++; 4648c2ecf20Sopenharmony_ci pages++; 4658c2ecf20Sopenharmony_ci n_pages--; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci if (!n_pages) 4688c2ecf20Sopenharmony_ci goto out; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* compute number of contiguous chunks */ 4728c2ecf20Sopenharmony_ci chunks = 1; 4738c2ecf20Sopenharmony_ci seg_len = 0; 4748c2ecf20Sopenharmony_ci for (i = 1; i < n_pages; i++) { 4758c2ecf20Sopenharmony_ci seg_len += PAGE_SIZE; 4768c2ecf20Sopenharmony_ci if (seg_len >= max_segment || 4778c2ecf20Sopenharmony_ci page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) { 4788c2ecf20Sopenharmony_ci chunks++; 4798c2ecf20Sopenharmony_ci seg_len = 0; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* merging chunks and putting them into the scatterlist */ 4848c2ecf20Sopenharmony_ci cur_page = 0; 4858c2ecf20Sopenharmony_ci for (i = 0; i < chunks; i++) { 4868c2ecf20Sopenharmony_ci unsigned int j, chunk_size; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* look for the end of the current chunk */ 4898c2ecf20Sopenharmony_ci seg_len = 0; 4908c2ecf20Sopenharmony_ci for (j = cur_page + 1; j < n_pages; j++) { 4918c2ecf20Sopenharmony_ci seg_len += PAGE_SIZE; 4928c2ecf20Sopenharmony_ci if (seg_len >= max_segment || 4938c2ecf20Sopenharmony_ci page_to_pfn(pages[j]) != 4948c2ecf20Sopenharmony_ci page_to_pfn(pages[j - 1]) + 1) 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* Pass how many chunks might be left */ 4998c2ecf20Sopenharmony_ci s = get_next_sg(sgt, s, chunks - i + left_pages, gfp_mask); 5008c2ecf20Sopenharmony_ci if (IS_ERR(s)) { 5018c2ecf20Sopenharmony_ci /* 5028c2ecf20Sopenharmony_ci * Adjust entry length to be as before function was 5038c2ecf20Sopenharmony_ci * called. 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_ci if (prv) 5068c2ecf20Sopenharmony_ci prv->length = prv_len; 5078c2ecf20Sopenharmony_ci return s; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset; 5108c2ecf20Sopenharmony_ci sg_set_page(s, pages[cur_page], 5118c2ecf20Sopenharmony_ci min_t(unsigned long, size, chunk_size), offset); 5128c2ecf20Sopenharmony_ci added_nents++; 5138c2ecf20Sopenharmony_ci size -= chunk_size; 5148c2ecf20Sopenharmony_ci offset = 0; 5158c2ecf20Sopenharmony_ci cur_page = j; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci sgt->nents += added_nents; 5188c2ecf20Sopenharmony_ciout: 5198c2ecf20Sopenharmony_ci if (!left_pages) 5208c2ecf20Sopenharmony_ci sg_mark_end(s); 5218c2ecf20Sopenharmony_ci return s; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__sg_alloc_table_from_pages); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci/** 5268c2ecf20Sopenharmony_ci * sg_alloc_table_from_pages - Allocate and initialize an sg table from 5278c2ecf20Sopenharmony_ci * an array of pages 5288c2ecf20Sopenharmony_ci * @sgt: The sg table header to use 5298c2ecf20Sopenharmony_ci * @pages: Pointer to an array of page pointers 5308c2ecf20Sopenharmony_ci * @n_pages: Number of pages in the pages array 5318c2ecf20Sopenharmony_ci * @offset: Offset from start of the first page to the start of a buffer 5328c2ecf20Sopenharmony_ci * @size: Number of valid bytes in the buffer (after offset) 5338c2ecf20Sopenharmony_ci * @gfp_mask: GFP allocation mask 5348c2ecf20Sopenharmony_ci * 5358c2ecf20Sopenharmony_ci * Description: 5368c2ecf20Sopenharmony_ci * Allocate and initialize an sg table from a list of pages. Contiguous 5378c2ecf20Sopenharmony_ci * ranges of the pages are squashed into a single scatterlist node. A user 5388c2ecf20Sopenharmony_ci * may provide an offset at a start and a size of valid data in a buffer 5398c2ecf20Sopenharmony_ci * specified by the page array. The returned sg table is released by 5408c2ecf20Sopenharmony_ci * sg_free_table. 5418c2ecf20Sopenharmony_ci * 5428c2ecf20Sopenharmony_ci * Returns: 5438c2ecf20Sopenharmony_ci * 0 on success, negative error on failure 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_ciint sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, 5468c2ecf20Sopenharmony_ci unsigned int n_pages, unsigned int offset, 5478c2ecf20Sopenharmony_ci unsigned long size, gfp_t gfp_mask) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(__sg_alloc_table_from_pages(sgt, pages, n_pages, 5508c2ecf20Sopenharmony_ci offset, size, UINT_MAX, NULL, 0, gfp_mask)); 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_alloc_table_from_pages); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci#ifdef CONFIG_SGL_ALLOC 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci/** 5578c2ecf20Sopenharmony_ci * sgl_alloc_order - allocate a scatterlist and its pages 5588c2ecf20Sopenharmony_ci * @length: Length in bytes of the scatterlist. Must be at least one 5598c2ecf20Sopenharmony_ci * @order: Second argument for alloc_pages() 5608c2ecf20Sopenharmony_ci * @chainable: Whether or not to allocate an extra element in the scatterlist 5618c2ecf20Sopenharmony_ci * for scatterlist chaining purposes 5628c2ecf20Sopenharmony_ci * @gfp: Memory allocation flags 5638c2ecf20Sopenharmony_ci * @nent_p: [out] Number of entries in the scatterlist that have pages 5648c2ecf20Sopenharmony_ci * 5658c2ecf20Sopenharmony_ci * Returns: A pointer to an initialized scatterlist or %NULL upon failure. 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_cistruct scatterlist *sgl_alloc_order(unsigned long long length, 5688c2ecf20Sopenharmony_ci unsigned int order, bool chainable, 5698c2ecf20Sopenharmony_ci gfp_t gfp, unsigned int *nent_p) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct scatterlist *sgl, *sg; 5728c2ecf20Sopenharmony_ci struct page *page; 5738c2ecf20Sopenharmony_ci unsigned int nent, nalloc; 5748c2ecf20Sopenharmony_ci u32 elem_len; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci nent = round_up(length, PAGE_SIZE << order) >> (PAGE_SHIFT + order); 5778c2ecf20Sopenharmony_ci /* Check for integer overflow */ 5788c2ecf20Sopenharmony_ci if (length > (nent << (PAGE_SHIFT + order))) 5798c2ecf20Sopenharmony_ci return NULL; 5808c2ecf20Sopenharmony_ci nalloc = nent; 5818c2ecf20Sopenharmony_ci if (chainable) { 5828c2ecf20Sopenharmony_ci /* Check for integer overflow */ 5838c2ecf20Sopenharmony_ci if (nalloc + 1 < nalloc) 5848c2ecf20Sopenharmony_ci return NULL; 5858c2ecf20Sopenharmony_ci nalloc++; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci sgl = kmalloc_array(nalloc, sizeof(struct scatterlist), 5888c2ecf20Sopenharmony_ci gfp & ~GFP_DMA); 5898c2ecf20Sopenharmony_ci if (!sgl) 5908c2ecf20Sopenharmony_ci return NULL; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci sg_init_table(sgl, nalloc); 5938c2ecf20Sopenharmony_ci sg = sgl; 5948c2ecf20Sopenharmony_ci while (length) { 5958c2ecf20Sopenharmony_ci elem_len = min_t(u64, length, PAGE_SIZE << order); 5968c2ecf20Sopenharmony_ci page = alloc_pages(gfp, order); 5978c2ecf20Sopenharmony_ci if (!page) { 5988c2ecf20Sopenharmony_ci sgl_free_order(sgl, order); 5998c2ecf20Sopenharmony_ci return NULL; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci sg_set_page(sg, page, elem_len, 0); 6038c2ecf20Sopenharmony_ci length -= elem_len; 6048c2ecf20Sopenharmony_ci sg = sg_next(sg); 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci WARN_ONCE(length, "length = %lld\n", length); 6078c2ecf20Sopenharmony_ci if (nent_p) 6088c2ecf20Sopenharmony_ci *nent_p = nent; 6098c2ecf20Sopenharmony_ci return sgl; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sgl_alloc_order); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci/** 6148c2ecf20Sopenharmony_ci * sgl_alloc - allocate a scatterlist and its pages 6158c2ecf20Sopenharmony_ci * @length: Length in bytes of the scatterlist 6168c2ecf20Sopenharmony_ci * @gfp: Memory allocation flags 6178c2ecf20Sopenharmony_ci * @nent_p: [out] Number of entries in the scatterlist 6188c2ecf20Sopenharmony_ci * 6198c2ecf20Sopenharmony_ci * Returns: A pointer to an initialized scatterlist or %NULL upon failure. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_cistruct scatterlist *sgl_alloc(unsigned long long length, gfp_t gfp, 6228c2ecf20Sopenharmony_ci unsigned int *nent_p) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci return sgl_alloc_order(length, 0, false, gfp, nent_p); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sgl_alloc); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/** 6298c2ecf20Sopenharmony_ci * sgl_free_n_order - free a scatterlist and its pages 6308c2ecf20Sopenharmony_ci * @sgl: Scatterlist with one or more elements 6318c2ecf20Sopenharmony_ci * @nents: Maximum number of elements to free 6328c2ecf20Sopenharmony_ci * @order: Second argument for __free_pages() 6338c2ecf20Sopenharmony_ci * 6348c2ecf20Sopenharmony_ci * Notes: 6358c2ecf20Sopenharmony_ci * - If several scatterlists have been chained and each chain element is 6368c2ecf20Sopenharmony_ci * freed separately then it's essential to set nents correctly to avoid that a 6378c2ecf20Sopenharmony_ci * page would get freed twice. 6388c2ecf20Sopenharmony_ci * - All pages in a chained scatterlist can be freed at once by setting @nents 6398c2ecf20Sopenharmony_ci * to a high number. 6408c2ecf20Sopenharmony_ci */ 6418c2ecf20Sopenharmony_civoid sgl_free_n_order(struct scatterlist *sgl, int nents, int order) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci struct scatterlist *sg; 6448c2ecf20Sopenharmony_ci struct page *page; 6458c2ecf20Sopenharmony_ci int i; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci for_each_sg(sgl, sg, nents, i) { 6488c2ecf20Sopenharmony_ci if (!sg) 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci page = sg_page(sg); 6518c2ecf20Sopenharmony_ci if (page) 6528c2ecf20Sopenharmony_ci __free_pages(page, order); 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci kfree(sgl); 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sgl_free_n_order); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci/** 6598c2ecf20Sopenharmony_ci * sgl_free_order - free a scatterlist and its pages 6608c2ecf20Sopenharmony_ci * @sgl: Scatterlist with one or more elements 6618c2ecf20Sopenharmony_ci * @order: Second argument for __free_pages() 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_civoid sgl_free_order(struct scatterlist *sgl, int order) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci sgl_free_n_order(sgl, INT_MAX, order); 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sgl_free_order); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci/** 6708c2ecf20Sopenharmony_ci * sgl_free - free a scatterlist and its pages 6718c2ecf20Sopenharmony_ci * @sgl: Scatterlist with one or more elements 6728c2ecf20Sopenharmony_ci */ 6738c2ecf20Sopenharmony_civoid sgl_free(struct scatterlist *sgl) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci sgl_free_order(sgl, 0); 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sgl_free); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci#endif /* CONFIG_SGL_ALLOC */ 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_civoid __sg_page_iter_start(struct sg_page_iter *piter, 6828c2ecf20Sopenharmony_ci struct scatterlist *sglist, unsigned int nents, 6838c2ecf20Sopenharmony_ci unsigned long pgoffset) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci piter->__pg_advance = 0; 6868c2ecf20Sopenharmony_ci piter->__nents = nents; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci piter->sg = sglist; 6898c2ecf20Sopenharmony_ci piter->sg_pgoffset = pgoffset; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__sg_page_iter_start); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int sg_page_count(struct scatterlist *sg) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci return PAGE_ALIGN(sg->offset + sg->length) >> PAGE_SHIFT; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cibool __sg_page_iter_next(struct sg_page_iter *piter) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci if (!piter->__nents || !piter->sg) 7018c2ecf20Sopenharmony_ci return false; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci piter->sg_pgoffset += piter->__pg_advance; 7048c2ecf20Sopenharmony_ci piter->__pg_advance = 1; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci while (piter->sg_pgoffset >= sg_page_count(piter->sg)) { 7078c2ecf20Sopenharmony_ci piter->sg_pgoffset -= sg_page_count(piter->sg); 7088c2ecf20Sopenharmony_ci piter->sg = sg_next(piter->sg); 7098c2ecf20Sopenharmony_ci if (!--piter->__nents || !piter->sg) 7108c2ecf20Sopenharmony_ci return false; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci return true; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__sg_page_iter_next); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic int sg_dma_page_count(struct scatterlist *sg) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci return PAGE_ALIGN(sg->offset + sg_dma_len(sg)) >> PAGE_SHIFT; 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cibool __sg_page_iter_dma_next(struct sg_dma_page_iter *dma_iter) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci struct sg_page_iter *piter = &dma_iter->base; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (!piter->__nents || !piter->sg) 7278c2ecf20Sopenharmony_ci return false; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci piter->sg_pgoffset += piter->__pg_advance; 7308c2ecf20Sopenharmony_ci piter->__pg_advance = 1; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci while (piter->sg_pgoffset >= sg_dma_page_count(piter->sg)) { 7338c2ecf20Sopenharmony_ci piter->sg_pgoffset -= sg_dma_page_count(piter->sg); 7348c2ecf20Sopenharmony_ci piter->sg = sg_next(piter->sg); 7358c2ecf20Sopenharmony_ci if (!--piter->__nents || !piter->sg) 7368c2ecf20Sopenharmony_ci return false; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci return true; 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__sg_page_iter_dma_next); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci/** 7448c2ecf20Sopenharmony_ci * sg_miter_start - start mapping iteration over a sg list 7458c2ecf20Sopenharmony_ci * @miter: sg mapping iter to be started 7468c2ecf20Sopenharmony_ci * @sgl: sg list to iterate over 7478c2ecf20Sopenharmony_ci * @nents: number of sg entries 7488c2ecf20Sopenharmony_ci * 7498c2ecf20Sopenharmony_ci * Description: 7508c2ecf20Sopenharmony_ci * Starts mapping iterator @miter. 7518c2ecf20Sopenharmony_ci * 7528c2ecf20Sopenharmony_ci * Context: 7538c2ecf20Sopenharmony_ci * Don't care. 7548c2ecf20Sopenharmony_ci */ 7558c2ecf20Sopenharmony_civoid sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl, 7568c2ecf20Sopenharmony_ci unsigned int nents, unsigned int flags) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci memset(miter, 0, sizeof(struct sg_mapping_iter)); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci __sg_page_iter_start(&miter->piter, sgl, nents, 0); 7618c2ecf20Sopenharmony_ci WARN_ON(!(flags & (SG_MITER_TO_SG | SG_MITER_FROM_SG))); 7628c2ecf20Sopenharmony_ci miter->__flags = flags; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_miter_start); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic bool sg_miter_get_next_page(struct sg_mapping_iter *miter) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci if (!miter->__remaining) { 7698c2ecf20Sopenharmony_ci struct scatterlist *sg; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if (!__sg_page_iter_next(&miter->piter)) 7728c2ecf20Sopenharmony_ci return false; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci sg = miter->piter.sg; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci miter->__offset = miter->piter.sg_pgoffset ? 0 : sg->offset; 7778c2ecf20Sopenharmony_ci miter->piter.sg_pgoffset += miter->__offset >> PAGE_SHIFT; 7788c2ecf20Sopenharmony_ci miter->__offset &= PAGE_SIZE - 1; 7798c2ecf20Sopenharmony_ci miter->__remaining = sg->offset + sg->length - 7808c2ecf20Sopenharmony_ci (miter->piter.sg_pgoffset << PAGE_SHIFT) - 7818c2ecf20Sopenharmony_ci miter->__offset; 7828c2ecf20Sopenharmony_ci miter->__remaining = min_t(unsigned long, miter->__remaining, 7838c2ecf20Sopenharmony_ci PAGE_SIZE - miter->__offset); 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return true; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci/** 7908c2ecf20Sopenharmony_ci * sg_miter_skip - reposition mapping iterator 7918c2ecf20Sopenharmony_ci * @miter: sg mapping iter to be skipped 7928c2ecf20Sopenharmony_ci * @offset: number of bytes to plus the current location 7938c2ecf20Sopenharmony_ci * 7948c2ecf20Sopenharmony_ci * Description: 7958c2ecf20Sopenharmony_ci * Sets the offset of @miter to its current location plus @offset bytes. 7968c2ecf20Sopenharmony_ci * If mapping iterator @miter has been proceeded by sg_miter_next(), this 7978c2ecf20Sopenharmony_ci * stops @miter. 7988c2ecf20Sopenharmony_ci * 7998c2ecf20Sopenharmony_ci * Context: 8008c2ecf20Sopenharmony_ci * Don't care if @miter is stopped, or not proceeded yet. 8018c2ecf20Sopenharmony_ci * Otherwise, preemption disabled if the SG_MITER_ATOMIC is set. 8028c2ecf20Sopenharmony_ci * 8038c2ecf20Sopenharmony_ci * Returns: 8048c2ecf20Sopenharmony_ci * true if @miter contains the valid mapping. false if end of sg 8058c2ecf20Sopenharmony_ci * list is reached. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_cibool sg_miter_skip(struct sg_mapping_iter *miter, off_t offset) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci sg_miter_stop(miter); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci while (offset) { 8128c2ecf20Sopenharmony_ci off_t consumed; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (!sg_miter_get_next_page(miter)) 8158c2ecf20Sopenharmony_ci return false; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci consumed = min_t(off_t, offset, miter->__remaining); 8188c2ecf20Sopenharmony_ci miter->__offset += consumed; 8198c2ecf20Sopenharmony_ci miter->__remaining -= consumed; 8208c2ecf20Sopenharmony_ci offset -= consumed; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return true; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_miter_skip); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci/** 8288c2ecf20Sopenharmony_ci * sg_miter_next - proceed mapping iterator to the next mapping 8298c2ecf20Sopenharmony_ci * @miter: sg mapping iter to proceed 8308c2ecf20Sopenharmony_ci * 8318c2ecf20Sopenharmony_ci * Description: 8328c2ecf20Sopenharmony_ci * Proceeds @miter to the next mapping. @miter should have been started 8338c2ecf20Sopenharmony_ci * using sg_miter_start(). On successful return, @miter->page, 8348c2ecf20Sopenharmony_ci * @miter->addr and @miter->length point to the current mapping. 8358c2ecf20Sopenharmony_ci * 8368c2ecf20Sopenharmony_ci * Context: 8378c2ecf20Sopenharmony_ci * Preemption disabled if SG_MITER_ATOMIC. Preemption must stay disabled 8388c2ecf20Sopenharmony_ci * till @miter is stopped. May sleep if !SG_MITER_ATOMIC. 8398c2ecf20Sopenharmony_ci * 8408c2ecf20Sopenharmony_ci * Returns: 8418c2ecf20Sopenharmony_ci * true if @miter contains the next mapping. false if end of sg 8428c2ecf20Sopenharmony_ci * list is reached. 8438c2ecf20Sopenharmony_ci */ 8448c2ecf20Sopenharmony_cibool sg_miter_next(struct sg_mapping_iter *miter) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci sg_miter_stop(miter); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* 8498c2ecf20Sopenharmony_ci * Get to the next page if necessary. 8508c2ecf20Sopenharmony_ci * __remaining, __offset is adjusted by sg_miter_stop 8518c2ecf20Sopenharmony_ci */ 8528c2ecf20Sopenharmony_ci if (!sg_miter_get_next_page(miter)) 8538c2ecf20Sopenharmony_ci return false; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci miter->page = sg_page_iter_page(&miter->piter); 8568c2ecf20Sopenharmony_ci miter->consumed = miter->length = miter->__remaining; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (miter->__flags & SG_MITER_ATOMIC) 8598c2ecf20Sopenharmony_ci miter->addr = kmap_atomic(miter->page) + miter->__offset; 8608c2ecf20Sopenharmony_ci else 8618c2ecf20Sopenharmony_ci miter->addr = kmap(miter->page) + miter->__offset; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci return true; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_miter_next); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci/** 8688c2ecf20Sopenharmony_ci * sg_miter_stop - stop mapping iteration 8698c2ecf20Sopenharmony_ci * @miter: sg mapping iter to be stopped 8708c2ecf20Sopenharmony_ci * 8718c2ecf20Sopenharmony_ci * Description: 8728c2ecf20Sopenharmony_ci * Stops mapping iterator @miter. @miter should have been started 8738c2ecf20Sopenharmony_ci * using sg_miter_start(). A stopped iteration can be resumed by 8748c2ecf20Sopenharmony_ci * calling sg_miter_next() on it. This is useful when resources (kmap) 8758c2ecf20Sopenharmony_ci * need to be released during iteration. 8768c2ecf20Sopenharmony_ci * 8778c2ecf20Sopenharmony_ci * Context: 8788c2ecf20Sopenharmony_ci * Preemption disabled if the SG_MITER_ATOMIC is set. Don't care 8798c2ecf20Sopenharmony_ci * otherwise. 8808c2ecf20Sopenharmony_ci */ 8818c2ecf20Sopenharmony_civoid sg_miter_stop(struct sg_mapping_iter *miter) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci WARN_ON(miter->consumed > miter->length); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci /* drop resources from the last iteration */ 8868c2ecf20Sopenharmony_ci if (miter->addr) { 8878c2ecf20Sopenharmony_ci miter->__offset += miter->consumed; 8888c2ecf20Sopenharmony_ci miter->__remaining -= miter->consumed; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if ((miter->__flags & SG_MITER_TO_SG) && 8918c2ecf20Sopenharmony_ci !PageSlab(miter->page)) 8928c2ecf20Sopenharmony_ci flush_kernel_dcache_page(miter->page); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (miter->__flags & SG_MITER_ATOMIC) { 8958c2ecf20Sopenharmony_ci WARN_ON_ONCE(preemptible()); 8968c2ecf20Sopenharmony_ci kunmap_atomic(miter->addr); 8978c2ecf20Sopenharmony_ci } else 8988c2ecf20Sopenharmony_ci kunmap(miter->page); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci miter->page = NULL; 9018c2ecf20Sopenharmony_ci miter->addr = NULL; 9028c2ecf20Sopenharmony_ci miter->length = 0; 9038c2ecf20Sopenharmony_ci miter->consumed = 0; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_miter_stop); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci/** 9098c2ecf20Sopenharmony_ci * sg_copy_buffer - Copy data between a linear buffer and an SG list 9108c2ecf20Sopenharmony_ci * @sgl: The SG list 9118c2ecf20Sopenharmony_ci * @nents: Number of SG entries 9128c2ecf20Sopenharmony_ci * @buf: Where to copy from 9138c2ecf20Sopenharmony_ci * @buflen: The number of bytes to copy 9148c2ecf20Sopenharmony_ci * @skip: Number of bytes to skip before copying 9158c2ecf20Sopenharmony_ci * @to_buffer: transfer direction (true == from an sg list to a 9168c2ecf20Sopenharmony_ci * buffer, false == from a buffer to an sg list) 9178c2ecf20Sopenharmony_ci * 9188c2ecf20Sopenharmony_ci * Returns the number of copied bytes. 9198c2ecf20Sopenharmony_ci * 9208c2ecf20Sopenharmony_ci **/ 9218c2ecf20Sopenharmony_cisize_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf, 9228c2ecf20Sopenharmony_ci size_t buflen, off_t skip, bool to_buffer) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci unsigned int offset = 0; 9258c2ecf20Sopenharmony_ci struct sg_mapping_iter miter; 9268c2ecf20Sopenharmony_ci unsigned int sg_flags = SG_MITER_ATOMIC; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (to_buffer) 9298c2ecf20Sopenharmony_ci sg_flags |= SG_MITER_FROM_SG; 9308c2ecf20Sopenharmony_ci else 9318c2ecf20Sopenharmony_ci sg_flags |= SG_MITER_TO_SG; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci sg_miter_start(&miter, sgl, nents, sg_flags); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci if (!sg_miter_skip(&miter, skip)) 9368c2ecf20Sopenharmony_ci return 0; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci while ((offset < buflen) && sg_miter_next(&miter)) { 9398c2ecf20Sopenharmony_ci unsigned int len; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci len = min(miter.length, buflen - offset); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (to_buffer) 9448c2ecf20Sopenharmony_ci memcpy(buf + offset, miter.addr, len); 9458c2ecf20Sopenharmony_ci else 9468c2ecf20Sopenharmony_ci memcpy(miter.addr, buf + offset, len); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci offset += len; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci sg_miter_stop(&miter); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci return offset; 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_copy_buffer); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci/** 9588c2ecf20Sopenharmony_ci * sg_copy_from_buffer - Copy from a linear buffer to an SG list 9598c2ecf20Sopenharmony_ci * @sgl: The SG list 9608c2ecf20Sopenharmony_ci * @nents: Number of SG entries 9618c2ecf20Sopenharmony_ci * @buf: Where to copy from 9628c2ecf20Sopenharmony_ci * @buflen: The number of bytes to copy 9638c2ecf20Sopenharmony_ci * 9648c2ecf20Sopenharmony_ci * Returns the number of copied bytes. 9658c2ecf20Sopenharmony_ci * 9668c2ecf20Sopenharmony_ci **/ 9678c2ecf20Sopenharmony_cisize_t sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents, 9688c2ecf20Sopenharmony_ci const void *buf, size_t buflen) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci return sg_copy_buffer(sgl, nents, (void *)buf, buflen, 0, false); 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_copy_from_buffer); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci/** 9758c2ecf20Sopenharmony_ci * sg_copy_to_buffer - Copy from an SG list to a linear buffer 9768c2ecf20Sopenharmony_ci * @sgl: The SG list 9778c2ecf20Sopenharmony_ci * @nents: Number of SG entries 9788c2ecf20Sopenharmony_ci * @buf: Where to copy to 9798c2ecf20Sopenharmony_ci * @buflen: The number of bytes to copy 9808c2ecf20Sopenharmony_ci * 9818c2ecf20Sopenharmony_ci * Returns the number of copied bytes. 9828c2ecf20Sopenharmony_ci * 9838c2ecf20Sopenharmony_ci **/ 9848c2ecf20Sopenharmony_cisize_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents, 9858c2ecf20Sopenharmony_ci void *buf, size_t buflen) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci return sg_copy_buffer(sgl, nents, buf, buflen, 0, true); 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_copy_to_buffer); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci/** 9928c2ecf20Sopenharmony_ci * sg_pcopy_from_buffer - Copy from a linear buffer to an SG list 9938c2ecf20Sopenharmony_ci * @sgl: The SG list 9948c2ecf20Sopenharmony_ci * @nents: Number of SG entries 9958c2ecf20Sopenharmony_ci * @buf: Where to copy from 9968c2ecf20Sopenharmony_ci * @buflen: The number of bytes to copy 9978c2ecf20Sopenharmony_ci * @skip: Number of bytes to skip before copying 9988c2ecf20Sopenharmony_ci * 9998c2ecf20Sopenharmony_ci * Returns the number of copied bytes. 10008c2ecf20Sopenharmony_ci * 10018c2ecf20Sopenharmony_ci **/ 10028c2ecf20Sopenharmony_cisize_t sg_pcopy_from_buffer(struct scatterlist *sgl, unsigned int nents, 10038c2ecf20Sopenharmony_ci const void *buf, size_t buflen, off_t skip) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci return sg_copy_buffer(sgl, nents, (void *)buf, buflen, skip, false); 10068c2ecf20Sopenharmony_ci} 10078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_pcopy_from_buffer); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci/** 10108c2ecf20Sopenharmony_ci * sg_pcopy_to_buffer - Copy from an SG list to a linear buffer 10118c2ecf20Sopenharmony_ci * @sgl: The SG list 10128c2ecf20Sopenharmony_ci * @nents: Number of SG entries 10138c2ecf20Sopenharmony_ci * @buf: Where to copy to 10148c2ecf20Sopenharmony_ci * @buflen: The number of bytes to copy 10158c2ecf20Sopenharmony_ci * @skip: Number of bytes to skip before copying 10168c2ecf20Sopenharmony_ci * 10178c2ecf20Sopenharmony_ci * Returns the number of copied bytes. 10188c2ecf20Sopenharmony_ci * 10198c2ecf20Sopenharmony_ci **/ 10208c2ecf20Sopenharmony_cisize_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents, 10218c2ecf20Sopenharmony_ci void *buf, size_t buflen, off_t skip) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci return sg_copy_buffer(sgl, nents, buf, buflen, skip, true); 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_pcopy_to_buffer); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci/** 10288c2ecf20Sopenharmony_ci * sg_zero_buffer - Zero-out a part of a SG list 10298c2ecf20Sopenharmony_ci * @sgl: The SG list 10308c2ecf20Sopenharmony_ci * @nents: Number of SG entries 10318c2ecf20Sopenharmony_ci * @buflen: The number of bytes to zero out 10328c2ecf20Sopenharmony_ci * @skip: Number of bytes to skip before zeroing 10338c2ecf20Sopenharmony_ci * 10348c2ecf20Sopenharmony_ci * Returns the number of bytes zeroed. 10358c2ecf20Sopenharmony_ci **/ 10368c2ecf20Sopenharmony_cisize_t sg_zero_buffer(struct scatterlist *sgl, unsigned int nents, 10378c2ecf20Sopenharmony_ci size_t buflen, off_t skip) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci unsigned int offset = 0; 10408c2ecf20Sopenharmony_ci struct sg_mapping_iter miter; 10418c2ecf20Sopenharmony_ci unsigned int sg_flags = SG_MITER_ATOMIC | SG_MITER_TO_SG; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci sg_miter_start(&miter, sgl, nents, sg_flags); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci if (!sg_miter_skip(&miter, skip)) 10468c2ecf20Sopenharmony_ci return false; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci while (offset < buflen && sg_miter_next(&miter)) { 10498c2ecf20Sopenharmony_ci unsigned int len; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci len = min(miter.length, buflen - offset); 10528c2ecf20Sopenharmony_ci memset(miter.addr, 0, len); 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci offset += len; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci sg_miter_stop(&miter); 10588c2ecf20Sopenharmony_ci return offset; 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sg_zero_buffer); 1061