1 /*
2 * Copyright (C) 2021 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18 #ifndef _OAL_LITEOS_SCATTERLIST_H
19 #define _OAL_LITEOS_SCATTERLIST_H
20
21 #include <linux/scatterlist.h>
22
23 #ifdef __cplusplus
24 #if __cplusplus
25 extern "C" {
26 #endif /* __cplusplus */
27 #endif /* __cplusplus */
28
29 struct sg_table {
30 struct scatterlist *sgl; /* the list */
31 unsigned int nents; /* number of mapped entries */
32 unsigned int orig_nents; /* original size of list */
33 };
34
35 #define sg_is_chain(sg) ((sg)->page_link & 0x01)
36 #define sg_is_last(sg) ((sg)->page_link & 0x02)
37 #define sg_chain_ptr(sg) ((struct scatterlist *) ((uintptr_t)((sg)->page_link) & ~0x03))
38 /* Loop over each sg element, following the pointer to a new list if necessary */
39 #define for_each_sg(sglist, sg, nr, __i) for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg))
40
41 #define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist))
42
43 typedef struct scatterlist *(sg_alloc_fn)(unsigned int, gfp_t);
44 typedef void(sg_free_fn)(struct scatterlist *, unsigned int);
45
sg_next(struct scatterlist * sg)46 static struct scatterlist *sg_next(struct scatterlist *sg)
47 {
48 #ifdef CONFIG_DEBUG_SG
49 BUG_ON(sg->sg_magic != SG_MAGIC);
50 #endif
51 if (sg_is_last(sg)) {
52 return NULL;
53 }
54
55 sg++;
56 if (unlikely(sg_is_chain(sg))) {
57 sg = sg_chain_ptr(sg);
58 }
59
60 return sg;
61 }
62
sg_virt(struct scatterlist * sg)63 inline void *sg_virt(struct scatterlist *sg)
64 {
65 return ((void *)(uintptr_t)(sg->dma_address + sg->offset));
66 }
67
sg_kfree(struct scatterlist * sg,unsigned int nents)68 static void sg_kfree(struct scatterlist *sg, unsigned int nents)
69 {
70 hi_unref_param(nents);
71 kfree(sg);
72 }
73
__sg_free_table(struct sg_table * table,unsigned int max_ents,sg_free_fn * free_fn)74 void __sg_free_table(struct sg_table *table, unsigned int max_ents, sg_free_fn *free_fn)
75 {
76 struct scatterlist *sgl = NULL;
77 struct scatterlist *next = NULL;
78
79 if (unlikely(table->sgl == NULL)) {
80 return;
81 }
82
83 sgl = table->sgl;
84 while (table->orig_nents) {
85 unsigned int alloc_size = table->orig_nents;
86 unsigned int sg_size;
87
88 /*
89 * If we have more than max_ents segments left,
90 * then assign 'next' to the sg table after the current one.
91 * sg_size is then one less than alloc size, since the last
92 * element is the chain pointer.
93 */
94 if (unlikely(sgl == NULL)) {
95 break;
96 }
97 if (alloc_size > max_ents) {
98 next = sg_chain_ptr(&sgl[max_ents - 1]);
99 alloc_size = max_ents;
100 sg_size = alloc_size - 1;
101 } else {
102 sg_size = alloc_size;
103 next = NULL;
104 }
105
106 table->orig_nents -= sg_size;
107 free_fn(sgl, alloc_size);
108 sgl = next;
109 }
110
111 table->sgl = NULL;
112 }
113
sg_free_table(struct sg_table * table)114 void sg_free_table(struct sg_table *table)
115 {
116 __sg_free_table(table, SG_MAX_SINGLE_ALLOC, sg_kfree);
117 }
118
sg_kmalloc(unsigned int nents,gfp_t gfp_mask)119 static struct scatterlist *sg_kmalloc(unsigned int nents, gfp_t gfp_mask)
120 {
121 hi_unref_param(gfp_mask);
122 return kmalloc(nents * sizeof(struct scatterlist), gfp_mask);
123 }
124
sg_chain(struct scatterlist * prv,unsigned int prv_nents,struct scatterlist * sgl)125 static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents, struct scatterlist *sgl)
126 {
127 #ifndef ARCH_HAS_SG_CHAIN
128 BUG();
129 #endif
130
131 /*
132 * offset and length are unused for chain entry. Clear them.
133 */
134 prv[prv_nents - 1].offset = 0;
135 prv[prv_nents - 1].length = 0;
136
137 /*
138 * Set lowest bit to indicate a link pointer, and make sure to clear
139 * the termination bit if it happens to be set.
140 */
141 prv[prv_nents - 1].page_link = ((unsigned long)(uintptr_t)sgl | 0x01) & ~0x02;
142 }
143
__sg_alloc_table(struct sg_table * table,unsigned int nents,unsigned int max_ents,gfp_t gfp_mask,sg_alloc_fn * alloc_fn)144 int __sg_alloc_table(struct sg_table *table, unsigned int nents, unsigned int max_ents, gfp_t gfp_mask,
145 sg_alloc_fn *alloc_fn)
146 {
147 struct scatterlist *sg = NULL;
148 struct scatterlist *prv = NULL;
149 unsigned int left;
150
151 /* 安全编程规则6.6例外(1) 固定长度的结构体进行内存初始化 */
152 memset_s(table, sizeof(struct sg_table), 0, sizeof(struct sg_table));
153
154 left = nents;
155 prv = NULL;
156 do {
157 unsigned int sg_size;
158 unsigned int alloc_size = left;
159
160 if (alloc_size > max_ents) {
161 alloc_size = max_ents;
162 sg_size = alloc_size - 1;
163 } else {
164 sg_size = alloc_size;
165 }
166
167 left -= sg_size;
168
169 sg = alloc_fn(alloc_size, gfp_mask);
170 if (unlikely(sg == NULL)) {
171 /*
172 * Adjust entry count to reflect that the last
173 * entry of the previous table won't be used for
174 * linkage. Without this, sg_kfree() may get
175 * confused.
176 */
177 if (prv != NULL) {
178 table->nents = ++table->orig_nents;
179 }
180
181 return -ENOMEM;
182 }
183
184 sg_init_table(sg, alloc_size);
185 table->nents = table->orig_nents += sg_size;
186
187 /*
188 * If this is the first mapping, assign the sg table header.
189 * If this is not the first mapping, chain previous part.
190 */
191 if (prv != NULL) {
192 sg_chain(prv, max_ents, sg);
193 } else {
194 table->sgl = sg;
195 }
196
197 /*
198 * If no more entries after this one, mark the end
199 */
200 if (!left) {
201 sg_mark_end(&sg[sg_size - 1]);
202 }
203
204 prv = sg;
205 } while (left);
206
207 return 0;
208 }
209
sg_alloc_table(struct sg_table * table,unsigned int nents,gfp_t gfp_mask)210 int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
211 {
212 int ret;
213
214 ret = __sg_alloc_table(table, nents, SG_MAX_SINGLE_ALLOC, gfp_mask, sg_kmalloc);
215 if (unlikely(ret)) {
216 __sg_free_table(table, SG_MAX_SINGLE_ALLOC, sg_kfree);
217 }
218
219 return ret;
220 }
221
222 #ifdef __cplusplus
223 #if __cplusplus
224 }
225 #endif /* __cplusplus */
226 #endif /* __cplusplus */
227 #endif /* _LINUX_SCATTERLIST_H */
228