xref: /dflybsd-src/sys/dev/drm/linux_scatterlist.c (revision f0bba3d1dd5ba7adfae61269b5424d4ad443ab97)
1d6aa1cc5SFrançois Tigeot /*
2d6aa1cc5SFrançois Tigeot  * Copyright (c) 2010 Isilon Systems, Inc.
3d6aa1cc5SFrançois Tigeot  * Copyright (c) 2010 iX Systems, Inc.
4d6aa1cc5SFrançois Tigeot  * Copyright (c) 2010 Panasas, Inc.
5d6aa1cc5SFrançois Tigeot  * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd.
6d6aa1cc5SFrançois Tigeot  * Copyright (c) 2015 Matthew Dillon <dillon@backplane.com>
7d6aa1cc5SFrançois Tigeot  * Copyright (c) 2016 Matt Macy <mmacy@nextbsd.org>
8d6aa1cc5SFrançois Tigeot  * Copyright (c) 2017-2018 François Tigeot <ftigeot@wolfpond.org>
9d6aa1cc5SFrançois Tigeot  * All rights reserved.
10d6aa1cc5SFrançois Tigeot  *
11d6aa1cc5SFrançois Tigeot  * Redistribution and use in source and binary forms, with or without
12d6aa1cc5SFrançois Tigeot  * modification, are permitted provided that the following conditions
13d6aa1cc5SFrançois Tigeot  * are met:
14d6aa1cc5SFrançois Tigeot  * 1. Redistributions of source code must retain the above copyright
15d6aa1cc5SFrançois Tigeot  *    notice unmodified, this list of conditions, and the following
16d6aa1cc5SFrançois Tigeot  *    disclaimer.
17d6aa1cc5SFrançois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
18d6aa1cc5SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer in the
19d6aa1cc5SFrançois Tigeot  *    documentation and/or other materials provided with the distribution.
20d6aa1cc5SFrançois Tigeot  *
21d6aa1cc5SFrançois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22d6aa1cc5SFrançois Tigeot  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23d6aa1cc5SFrançois Tigeot  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24d6aa1cc5SFrançois Tigeot  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25d6aa1cc5SFrançois Tigeot  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26d6aa1cc5SFrançois Tigeot  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27d6aa1cc5SFrançois Tigeot  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28d6aa1cc5SFrançois Tigeot  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29d6aa1cc5SFrançois Tigeot  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30d6aa1cc5SFrançois Tigeot  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31d6aa1cc5SFrançois Tigeot  */
32d6aa1cc5SFrançois Tigeot 
33d6aa1cc5SFrançois Tigeot #include <linux/slab.h>
34d6aa1cc5SFrançois Tigeot #include <linux/scatterlist.h>
35d6aa1cc5SFrançois Tigeot #include <linux/highmem.h>
36d6aa1cc5SFrançois Tigeot 
37d6aa1cc5SFrançois Tigeot /**
38d6aa1cc5SFrançois Tigeot  * __sg_alloc_table - Allocate and initialize an sg table with given allocator
39d6aa1cc5SFrançois Tigeot  * @table:      The sg table header to use
40d6aa1cc5SFrançois Tigeot  * @nents:      Number of entries in sg list
41d6aa1cc5SFrançois Tigeot  * @max_ents:   The maximum number of entries the allocator returns per call
42d6aa1cc5SFrançois Tigeot  * @gfp_mask:   GFP allocation mask
43d6aa1cc5SFrançois Tigeot  *
44d6aa1cc5SFrançois Tigeot  * Description:
45d6aa1cc5SFrançois Tigeot  *   This function returns a @table @nents long. The allocator is
46d6aa1cc5SFrançois Tigeot  *   defined to return scatterlist chunks of maximum size @max_ents.
47d6aa1cc5SFrançois Tigeot  *   Thus if @nents is bigger than @max_ents, the scatterlists will be
48d6aa1cc5SFrançois Tigeot  *   chained in units of @max_ents.
49d6aa1cc5SFrançois Tigeot  *
50d6aa1cc5SFrançois Tigeot  * Notes:
51d6aa1cc5SFrançois Tigeot  *   If this function returns non-0 (eg failure), the caller must call
52d6aa1cc5SFrançois Tigeot  *   __sg_free_table() to cleanup any leftover allocations.
53d6aa1cc5SFrançois Tigeot  *
54d6aa1cc5SFrançois Tigeot  **/
55d6aa1cc5SFrançois Tigeot int
__sg_alloc_table(struct sg_table * table,unsigned int nents,unsigned int max_ents,gfp_t gfp_mask)56d6aa1cc5SFrançois Tigeot __sg_alloc_table(struct sg_table *table, unsigned int nents,
57d6aa1cc5SFrançois Tigeot 		unsigned int max_ents, gfp_t gfp_mask)
58d6aa1cc5SFrançois Tigeot {
59d6aa1cc5SFrançois Tigeot 	struct scatterlist *sg, *prv;
60d6aa1cc5SFrançois Tigeot 	unsigned int left;
61d6aa1cc5SFrançois Tigeot 
62d6aa1cc5SFrançois Tigeot 	memset(table, 0, sizeof(*table));
63d6aa1cc5SFrançois Tigeot 
64d6aa1cc5SFrançois Tigeot 	if (nents == 0)
65d6aa1cc5SFrançois Tigeot 		return -EINVAL;
66d6aa1cc5SFrançois Tigeot 	left = nents;
67d6aa1cc5SFrançois Tigeot 	prv = NULL;
68d6aa1cc5SFrançois Tigeot 	do {
69d6aa1cc5SFrançois Tigeot 		unsigned int sg_size, alloc_size = left;
70d6aa1cc5SFrançois Tigeot 
71d6aa1cc5SFrançois Tigeot 		if (alloc_size > max_ents) {
72d6aa1cc5SFrançois Tigeot 			alloc_size = max_ents;
73d6aa1cc5SFrançois Tigeot 			sg_size = alloc_size - 1;
74d6aa1cc5SFrançois Tigeot 		} else
75d6aa1cc5SFrançois Tigeot 			sg_size = alloc_size;
76d6aa1cc5SFrançois Tigeot 
77d6aa1cc5SFrançois Tigeot 		left -= sg_size;
78d6aa1cc5SFrançois Tigeot 
79d6aa1cc5SFrançois Tigeot 		sg = kmalloc(alloc_size * sizeof(struct scatterlist), M_DRM, gfp_mask);
80d6aa1cc5SFrançois Tigeot 		if (unlikely(!sg)) {
81d6aa1cc5SFrançois Tigeot 		/*
82d6aa1cc5SFrançois Tigeot 		 * Adjust entry count to reflect that the last
83d6aa1cc5SFrançois Tigeot 		 * entry of the previous table won't be used for
84d6aa1cc5SFrançois Tigeot 		 * linkage.  Without this, sg_kfree() may get
85d6aa1cc5SFrançois Tigeot 		 * confused.
86d6aa1cc5SFrançois Tigeot 		 */
87d6aa1cc5SFrançois Tigeot 			if (prv)
88d6aa1cc5SFrançois Tigeot 				table->nents = ++table->orig_nents;
89d6aa1cc5SFrançois Tigeot 
90d6aa1cc5SFrançois Tigeot 			return -ENOMEM;
91d6aa1cc5SFrançois Tigeot 		}
92d6aa1cc5SFrançois Tigeot 
93d6aa1cc5SFrançois Tigeot 		sg_init_table(sg, alloc_size);
94d6aa1cc5SFrançois Tigeot 		table->nents = table->orig_nents += sg_size;
95d6aa1cc5SFrançois Tigeot 
96d6aa1cc5SFrançois Tigeot 		/*
97d6aa1cc5SFrançois Tigeot 		 * If this is the first mapping, assign the sg table header.
98d6aa1cc5SFrançois Tigeot 		 * If this is not the first mapping, chain previous part.
99d6aa1cc5SFrançois Tigeot 		 */
100d6aa1cc5SFrançois Tigeot 		if (prv)
101d6aa1cc5SFrançois Tigeot 			sg_chain(prv, max_ents, sg);
102d6aa1cc5SFrançois Tigeot 		else
103d6aa1cc5SFrançois Tigeot 			table->sgl = sg;
104d6aa1cc5SFrançois Tigeot 
105d6aa1cc5SFrançois Tigeot 		/*
106d6aa1cc5SFrançois Tigeot 		* If no more entries after this one, mark the end
107d6aa1cc5SFrançois Tigeot 		*/
108d6aa1cc5SFrançois Tigeot 		if (!left)
109d6aa1cc5SFrançois Tigeot 			sg_mark_end(&sg[sg_size - 1]);
110d6aa1cc5SFrançois Tigeot 
111d6aa1cc5SFrançois Tigeot 		prv = sg;
112d6aa1cc5SFrançois Tigeot 	} while (left);
113d6aa1cc5SFrançois Tigeot 
114d6aa1cc5SFrançois Tigeot 	return 0;
115d6aa1cc5SFrançois Tigeot }
116d6aa1cc5SFrançois Tigeot 
117d6aa1cc5SFrançois Tigeot void
__sg_free_table(struct sg_table * table,unsigned int max_ents)118d6aa1cc5SFrançois Tigeot __sg_free_table(struct sg_table *table, unsigned int max_ents)
119d6aa1cc5SFrançois Tigeot {
120d6aa1cc5SFrançois Tigeot 	struct scatterlist *sgl, *next;
121d6aa1cc5SFrançois Tigeot 
122d6aa1cc5SFrançois Tigeot 	if (unlikely(!table->sgl))
123d6aa1cc5SFrançois Tigeot 		return;
124d6aa1cc5SFrançois Tigeot 
125d6aa1cc5SFrançois Tigeot 	sgl = table->sgl;
126d6aa1cc5SFrançois Tigeot 	while (table->orig_nents) {
127d6aa1cc5SFrançois Tigeot 		unsigned int alloc_size = table->orig_nents;
128d6aa1cc5SFrançois Tigeot 		unsigned int sg_size;
129d6aa1cc5SFrançois Tigeot 
130d6aa1cc5SFrançois Tigeot 		/*
131d6aa1cc5SFrançois Tigeot 		 * If we have more than max_ents segments left,
132d6aa1cc5SFrançois Tigeot 		 * then assign 'next' to the sg table after the current one.
133d6aa1cc5SFrançois Tigeot 		 * sg_size is then one less than alloc size, since the last
134d6aa1cc5SFrançois Tigeot 		 * element is the chain pointer.
135d6aa1cc5SFrançois Tigeot 		 */
136d6aa1cc5SFrançois Tigeot 		if (alloc_size > max_ents) {
137d6aa1cc5SFrançois Tigeot 			next = sgl[max_ents - 1].sl_un.sg;
138d6aa1cc5SFrançois Tigeot 			alloc_size = max_ents;
139d6aa1cc5SFrançois Tigeot 			sg_size = alloc_size - 1;
140d6aa1cc5SFrançois Tigeot 		} else {
141d6aa1cc5SFrançois Tigeot 			sg_size = alloc_size;
142d6aa1cc5SFrançois Tigeot 			next = NULL;
143d6aa1cc5SFrançois Tigeot 		}
144d6aa1cc5SFrançois Tigeot 
145d6aa1cc5SFrançois Tigeot 		table->orig_nents -= sg_size;
146d6aa1cc5SFrançois Tigeot 		kfree(sgl);
147d6aa1cc5SFrançois Tigeot 		sgl = next;
148d6aa1cc5SFrançois Tigeot 	}
149d6aa1cc5SFrançois Tigeot 
150d6aa1cc5SFrançois Tigeot 	table->sgl = NULL;
151d6aa1cc5SFrançois Tigeot }
152d6aa1cc5SFrançois Tigeot 
153d6aa1cc5SFrançois Tigeot size_t
sg_pcopy_from_buffer(struct scatterlist * sgl,unsigned int nents,const void * buf,size_t buflen,off_t skip)154d6aa1cc5SFrançois Tigeot sg_pcopy_from_buffer(struct scatterlist *sgl, unsigned int nents,
155d6aa1cc5SFrançois Tigeot 		     const void *buf, size_t buflen, off_t skip)
156d6aa1cc5SFrançois Tigeot {
157d6aa1cc5SFrançois Tigeot 	off_t off;
158d6aa1cc5SFrançois Tigeot 	int len, curlen, curoff;
159d6aa1cc5SFrançois Tigeot 	struct sg_page_iter iter;
160d6aa1cc5SFrançois Tigeot 	struct scatterlist *sg;
161*f0bba3d1SFrançois Tigeot 	struct page *page;
162d6aa1cc5SFrançois Tigeot 	char *vaddr;
163d6aa1cc5SFrançois Tigeot 
164d6aa1cc5SFrançois Tigeot 	off = 0;
165d6aa1cc5SFrançois Tigeot 	for_each_sg_page(sgl, &iter, nents, 0) {
166d6aa1cc5SFrançois Tigeot 		sg = iter.sg;
167d6aa1cc5SFrançois Tigeot 		curlen = sg->length;
168d6aa1cc5SFrançois Tigeot 		curoff = sg->offset;
169d6aa1cc5SFrançois Tigeot 		if (skip && curlen >= skip) {
170d6aa1cc5SFrançois Tigeot 			skip -= curlen;
171d6aa1cc5SFrançois Tigeot 			continue;
172d6aa1cc5SFrançois Tigeot 		}
173d6aa1cc5SFrançois Tigeot 		if (skip) {
174d6aa1cc5SFrançois Tigeot 			curlen -= skip;
175d6aa1cc5SFrançois Tigeot 			curoff += skip;
176d6aa1cc5SFrançois Tigeot 			skip = 0;
177d6aa1cc5SFrançois Tigeot 		}
178d6aa1cc5SFrançois Tigeot 		len = min(curlen, buflen - off);
179d6aa1cc5SFrançois Tigeot 		page = sg_page_iter_page(&iter);
180d6aa1cc5SFrançois Tigeot 		vaddr = (char *)kmap(page) + sg->offset;
181d6aa1cc5SFrançois Tigeot 		memcpy(vaddr, (const char *)buf + off, len);
182d6aa1cc5SFrançois Tigeot 		off += len;
183d6aa1cc5SFrançois Tigeot 		kunmap(page);
184d6aa1cc5SFrançois Tigeot 	}
185d6aa1cc5SFrançois Tigeot 
186d6aa1cc5SFrançois Tigeot 	return (off);
187d6aa1cc5SFrançois Tigeot }
188d6aa1cc5SFrançois Tigeot 
189d6aa1cc5SFrançois Tigeot size_t
sg_pcopy_to_buffer(struct scatterlist * sgl,unsigned int nents,void * buf,size_t buflen,off_t skip)190d6aa1cc5SFrançois Tigeot sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents,
191d6aa1cc5SFrançois Tigeot 		   void *buf, size_t buflen, off_t skip)
192d6aa1cc5SFrançois Tigeot {
193d6aa1cc5SFrançois Tigeot 	off_t off;
194d6aa1cc5SFrançois Tigeot 	int len, curlen, curoff;
195d6aa1cc5SFrançois Tigeot 	struct sg_page_iter iter;
196d6aa1cc5SFrançois Tigeot 	struct scatterlist *sg;
197*f0bba3d1SFrançois Tigeot 	struct page *page;
198d6aa1cc5SFrançois Tigeot 	char *vaddr;
199d6aa1cc5SFrançois Tigeot 
200d6aa1cc5SFrançois Tigeot 	off = 0;
201d6aa1cc5SFrançois Tigeot 	for_each_sg_page(sgl, &iter, nents, 0) {
202d6aa1cc5SFrançois Tigeot 		sg = iter.sg;
203d6aa1cc5SFrançois Tigeot 		curlen = sg->length;
204d6aa1cc5SFrançois Tigeot 		curoff = sg->offset;
205d6aa1cc5SFrançois Tigeot 		if (skip && curlen >= skip) {
206d6aa1cc5SFrançois Tigeot 			skip -= curlen;
207d6aa1cc5SFrançois Tigeot 			continue;
208d6aa1cc5SFrançois Tigeot 		}
209d6aa1cc5SFrançois Tigeot 		if (skip) {
210d6aa1cc5SFrançois Tigeot 			curlen -= skip;
211d6aa1cc5SFrançois Tigeot 			curoff += skip;
212d6aa1cc5SFrançois Tigeot 			skip = 0;
213d6aa1cc5SFrançois Tigeot 		}
214d6aa1cc5SFrançois Tigeot 		len = min(curlen, buflen - off);
215d6aa1cc5SFrançois Tigeot 		page = sg_page_iter_page(&iter);
216d6aa1cc5SFrançois Tigeot 		vaddr = (char *)kmap(page) + sg->offset;
217d6aa1cc5SFrançois Tigeot 		memcpy((char *)buf + off, vaddr, len);
218d6aa1cc5SFrançois Tigeot 		off += len;
219d6aa1cc5SFrançois Tigeot 		kunmap(page);
220d6aa1cc5SFrançois Tigeot 	}
221d6aa1cc5SFrançois Tigeot 
222d6aa1cc5SFrançois Tigeot 	return (off);
223d6aa1cc5SFrançois Tigeot }
224