Init basesystem source codes.
[staging/basesystem.git] / video_in_hal / nsframework / common_library / client / src / cl_region.c
1 /*
2  * @copyright Copyright (c) 2016-2020 TOYOTA MOTOR CORPORATION.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * @file cl_region.c
19  * @brief region manage
20  *
21  */
22
23 #include <stdio.h>
24 #include <sys/mman.h>
25 #include <native_service/cl_region.h>
26 #include "cl_error.h"
27
28 #define cl_align_ptr(p, a) \
29     (uint8_t *) (((long)(p) + ((long)a - 1)) & ~((long)a - 1))
30
31 static void *cl_region_anonmmap(size_t size);
32 static void *cl_region_expand(cl_region_t *region, size_t size, size_t align_size);
33 static void *cl_region_alloc_large(cl_region_t *region, size_t size);
34
35 cl_region_t *
36 CL_RegionCreate(size_t size) {
37   cl_region_t *r;
38   long pagesize;
39
40   pagesize = sysconf(_SC_PAGE_SIZE);
41   size = ((size + (size_t)pagesize - 1) / (size_t)pagesize) * (size_t)pagesize;
42
43   r = cl_region_anonmmap(size);
44   if (r == NULL) {
45     return NULL;
46   }
47
48   r->d.last = (uint8_t *) r + sizeof(cl_region_t);
49   r->d.end = (uint8_t *) r + size;
50   r->d.next = NULL;
51   r->d.failed = 0;
52
53   size = size - sizeof(cl_region_t);
54   r->max = (size < (pagesize - 1)) ? size : (size_t)(pagesize - 1);
55
56   r->current = r;
57   r->large = NULL;
58   r->cleanup = NULL;
59
60   return r;
61 }
62
63
64 void
65 CL_RegionDestroy(cl_region_t *region) {
66   cl_region_t *r, *n;
67   cl_region_large_t *l;
68   cl_region_cleanup_t *c;
69
70   for (c = region->cleanup; c; c = c->next) {
71     if (c->handler) {
72       CL_DBG_PRINT("run cleanup: %p\n", c->handler);
73       c->handler(c->data);
74     }
75   }
76
77   for (l = region->large; l; l = l->next) {
78
79     CL_DBG_PRINT("free: %p\n", l->alloc);
80
81     if (l->alloc) { // LCOV_EXCL_BR_LINE 6: double check, mmap in cl_monitor.c
82       if (munmap(l->alloc, l->size) < 0) { // LCOV_EXCL_BR_LINE 5: fail safe for libc munmap
83         AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
84         CL_PERROR("munmap"); // LCOV_EXCL_LINE 5: fail safe for libc munmap
85       }
86     }
87   }
88
89   for (r = region, n = region->d.next; /* void */; r = n, n = n->d.next) {
90     if (munmap(r, (size_t)(r->d.end - (uint8_t *)r)) < 0) {// LCOV_EXCL_BR_LINE 5: fail safe for libc munmap
91       AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
92       CL_PERROR("munmap"); // LCOV_EXCL_LINE 5: fail safe for libc munmap
93     }
94
95     if (n == NULL) {
96       break;
97     }
98   }
99 }
100
101
102 void *
103 cl_region_alloc(cl_region_t *region, size_t size, size_t align_size) {
104   uint8_t *m;
105   volatile uint8_t *old_last;
106   cl_region_t *r;
107
108   if (size <= region->max) {
109
110     r = region->current;
111
112     do {
113     retry:
114       old_last = r->d.last;
115       m = cl_align_ptr(old_last, align_size);
116       if ((size_t)(r->d.end - m) >= size) {
117         if (false == __sync_bool_compare_and_swap(&r->d.last, old_last, m + size)) {
118           goto retry;
119         }
120         return m;
121       }
122
123       r = r->d.next;
124
125     } while (r);
126
127     return cl_region_expand(region, size, align_size);
128   }
129
130   return cl_region_alloc_large(region, size);
131 }
132
133
134 static void *
135 cl_region_anonmmap(size_t size) {
136   uint8_t *p;
137
138   p = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
139   if (p == MAP_FAILED) {  /* pgr0203 */
140     CL_PERROR("mmap");
141     return NULL;
142   }
143
144   return p;
145 }
146
147
148 static void *
149 cl_region_expand(cl_region_t *region, size_t size, size_t align_size) {
150   uint8_t *m;
151   size_t psize;
152   cl_region_t *r, *new, *current;
153
154   psize = (size_t)(region->d.end - (uint8_t *)region);
155
156   m = cl_region_anonmmap(psize);
157   if (m == NULL) {  // LCOV_EXCL_BR_LINE 5: fail safe for libc mmap
158     AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
159     return NULL;// LCOV_EXCL_LINE 5: fail safe for libc mmap
160   }
161
162   new = (cl_region_t *)m;
163
164   new->d.end = m + psize;
165   new->d.next = NULL;
166   new->d.failed = 0;
167
168   m += sizeof(cl_region_data_t);
169   m = cl_align_ptr(m, align_size);
170   new->d.last = m + size;
171
172   current = region->current;
173
174   for (r = current; r->d.next; r = r->d.next) { /* pgr0689 */
175     if (r->d.failed++ > 4) {
176       current = r->d.next;
177     }
178   }
179
180   while (false == __sync_bool_compare_and_swap(&r->d.next, NULL, new)) {
181     for (r = r->d.next; r->d.next; r = r->d.next) {}  /* pgr0689 */
182   }
183
184   region->current = current ? current : new;
185
186   return m;
187 }
188
189
190 static void *
191 cl_region_alloc_large(cl_region_t *region, size_t size) {
192   void *p;
193   int n;
194   long pagesize;
195   cl_region_large_t *large;
196
197   pagesize = sysconf(_SC_PAGE_SIZE);
198   size = ((size + (size_t)pagesize - 1) / (size_t)pagesize) * (size_t)pagesize;
199
200   p = cl_region_anonmmap(size);
201
202   if (p == NULL) {  // LCOV_EXCL_BR_LINE 5: fail safe for libc mmap
203     AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
204     return NULL;// LCOV_EXCL_LINE 5: fail safe for libc mmap
205   }
206
207   n = 0;
208
209   for (large = region->large; large; large = large->next) {
210     if (large->alloc == NULL) {
211       if (false == __sync_bool_compare_and_swap(&large->alloc, NULL, p)) {
212         break;
213       }
214       large->size = size;
215       return p;
216     }
217
218     if (n++ > 3) {
219       break;
220     }
221   }
222
223   large = CL_RegionAlloc(region, cl_region_large_t, 1);
224   if (large == NULL) {  // LCOV_EXCL_START 5: fail safe for libc mmap
225     AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
226     if (munmap(p, size) < 0) {
227       CL_PERROR("munmap");
228     }
229     return NULL;
230   }  // LCOV_EXCL_STOP
231
232   large->alloc = p;
233   large->size = size;
234   large->next = region->large;
235   while (false == __sync_bool_compare_and_swap(&region->large, large->next, large)) {  // LCOV_EXCL_BR_LINE 100: race condition  // NOLINT (readability/nolint)
236     // LCOV_EXCL_START 100: race condition
237     AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
238     large->next = region->large;
239   } // LCOV_EXCL_STOP
240
241   return p;
242 }
243
244
245 bool
246 CL_RegionFree(cl_region_t *region, void *p) {
247   cl_region_large_t  *l;
248
249   for (l = region->large; l; l = l->next) {
250     if (p == l->alloc) {
251       CL_DBG_PRINT("free: %p\n", l->alloc);
252       if (munmap(l->alloc, l->size) < 0) {// LCOV_EXCL_BR_LINE 5: fail safe for libc munmap
253         AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
254         CL_PERROR("munmap");// LCOV_EXCL_LINE 5: fail safe for libc munmap
255       }
256       l->alloc = NULL;
257       return true;
258     }
259   }
260
261   return false;
262 }
263
264
265 cl_region_cleanup_t *
266 cl_region_cleanup_add(cl_region_t *region, size_t size, size_t align_size) {
267   cl_region_cleanup_t  *c;
268
269   c = CL_RegionAlloc(region, cl_region_cleanup_t, 1);
270   if (c == NULL) {  // LCOV_EXCL_START 5: fail safe for libc mmap
271     AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
272     return NULL;
273   }  // LCOV_EXCL_STOP
274
275   if (size) {
276     c->data = cl_region_alloc(region, size, align_size);
277     if (c->data == NULL) {  // LCOV_EXCL_START 5: fail safe for libc mmap
278       AGL_ASSERT_NOT_TESTED();  // LCOV_EXCL_LINE 200: test assert
279       return NULL;
280     }  // LCOV_EXCL_STOP
281   } else {
282     c->data = NULL;
283   }
284
285   c->handler = NULL;
286   c->next = region->cleanup;
287
288   region->cleanup = c;
289
290   CL_DBG_PRINT("add cleanup: %p\n", c);
291
292   return c;
293 }
294
295 /* vim:set ts=8 sw=2 sts=2: */