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