• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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