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