1 /*
2 * Copyright (c) 2021 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8 #include <stddef.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <sys/bitarray.h>
12 #include <sys/check.h>
13 #include <sys/sys_io.h>
14
15 /* Number of bits represented by one bundle */
16 #define bundle_bitness(ba) (sizeof(ba->bundles[0]) * 8)
17
18 struct bundle_data {
19 /* Start and end index of bundles */
20 size_t sidx, eidx;
21
22 /* Offset inside start and end bundles */
23 size_t soff, eoff;
24
25 /* Masks for start/end bundles */
26 uint32_t smask, emask;
27 };
28
setup_bundle_data(sys_bitarray_t * bitarray,struct bundle_data * bd,size_t offset,size_t num_bits)29 static void setup_bundle_data(sys_bitarray_t *bitarray,
30 struct bundle_data *bd,
31 size_t offset, size_t num_bits)
32 {
33 bd->sidx = offset / bundle_bitness(bitarray);
34 bd->soff = offset % bundle_bitness(bitarray);
35
36 bd->eidx = (offset + num_bits - 1) / bundle_bitness(bitarray);
37 bd->eoff = (offset + num_bits - 1) % bundle_bitness(bitarray);
38
39 bd->smask = ~(BIT(bd->soff) - 1);
40 bd->emask = (BIT(bd->eoff) - 1) | BIT(bd->eoff);
41
42 if (bd->sidx == bd->eidx) {
43 /* The region lies within the same bundle. So combine the masks. */
44 bd->smask &= bd->emask;
45 }
46 }
47
48 /*
49 * Find out if the bits in a region is all set or all clear.
50 *
51 * @param[in] bitarray Bitarray struct
52 * @param[in] offset Starting bit location
53 * @param[in] num_bits Number of bits in the region
54 * @param[in] match_set True if matching all set bits,
55 * False if matching all cleared bits
56 * @param[out] bd Data related to matching which can be
57 * used later to find out where the region
58 * lies in the bitarray bundles.
59 * @param[out] mismatch Offset to the mismatched bit.
60 * Can be NULL.
61 *
62 * @retval true If all bits are set or cleared
63 * @retval false Not all bits are set or cleared
64 */
match_region(sys_bitarray_t * bitarray,size_t offset,size_t num_bits,bool match_set,struct bundle_data * bd,size_t * mismatch)65 static bool match_region(sys_bitarray_t *bitarray, size_t offset,
66 size_t num_bits, bool match_set,
67 struct bundle_data *bd,
68 size_t *mismatch)
69 {
70 int idx;
71 uint32_t bundle;
72 uint32_t mismatch_bundle;
73 uint32_t mismatch_mask;
74 size_t mismatch_bundle_idx;
75 size_t mismatch_bit_off;
76
77 setup_bundle_data(bitarray, bd, offset, num_bits);
78
79 if (bd->sidx == bd->eidx) {
80 bundle = bitarray->bundles[bd->sidx];
81 if (!match_set) {
82 bundle = ~bundle;
83 }
84
85 if ((bundle & bd->smask) != bd->smask) {
86 /* Not matching to mask. */
87 mismatch_bundle = ~bundle & bd->smask;
88 mismatch_bundle_idx = bd->sidx;
89 mismatch_mask = bd->smask;
90 goto mismatch;
91 } else {
92 /* Matching to mask. */
93 goto out;
94 }
95 }
96
97 /* Region lies in a number of bundles. Need to loop through them. */
98
99 /* Start of bundles */
100 bundle = bitarray->bundles[bd->sidx];
101 if (!match_set) {
102 bundle = ~bundle;
103 }
104
105 if ((bundle & bd->smask) != bd->smask) {
106 /* Start bundle not matching to mask. */
107 mismatch_bundle = ~bundle & bd->smask;
108 mismatch_bundle_idx = bd->sidx;
109 mismatch_mask = bd->smask;
110 goto mismatch;
111 }
112
113 /* End of bundles */
114 bundle = bitarray->bundles[bd->eidx];
115 if (!match_set) {
116 bundle = ~bundle;
117 }
118
119 if ((bundle & bd->emask) != bd->emask) {
120 /* End bundle not matching to mask. */
121 mismatch_bundle = ~bundle & bd->emask;
122 mismatch_bundle_idx = bd->eidx;
123 mismatch_mask = bd->emask;
124 goto mismatch;
125 }
126
127 /* In-between bundles */
128 for (idx = bd->sidx + 1; idx < bd->eidx; idx++) {
129 /* Note that this is opposite from above so that
130 * we are simply checking if bundle == 0.
131 */
132 bundle = bitarray->bundles[idx];
133 if (match_set) {
134 bundle = ~bundle;
135 }
136
137 if (bundle != 0U) {
138 /* Bits in "between bundles" do not match */
139 mismatch_bundle = ~bundle;
140 mismatch_bundle_idx = idx;
141 mismatch_mask = ~0U;
142 goto mismatch;
143 }
144 }
145
146 out:
147 /* All bits in region matched. */
148 return true;
149
150 mismatch:
151 if (mismatch != NULL) {
152 /* Must have at least 1 bit set to indicate
153 * where the mismatch is.
154 */
155 __ASSERT_NO_MSG(mismatch_bundle != 0);
156
157 mismatch_bit_off = find_lsb_set(mismatch_bundle) - 1;
158 mismatch_bit_off += mismatch_bundle_idx *
159 bundle_bitness(bitarray);
160 *mismatch = (uint32_t)mismatch_bit_off;
161 }
162 return false;
163 }
164
165 /*
166 * Set or clear a region of bits.
167 *
168 * @param bitarray Bitarray struct
169 * @param offset Starting bit location
170 * @param num_bits Number of bits in the region
171 * @param to_set True if to set all bits.
172 * False if to clear all bits.
173 * @param bd Bundle data. Can reuse the output from
174 * match_region(). NULL if there is no
175 * prior call to match_region().
176 */
set_region(sys_bitarray_t * bitarray,size_t offset,size_t num_bits,bool to_set,struct bundle_data * bd)177 static void set_region(sys_bitarray_t *bitarray, size_t offset,
178 size_t num_bits, bool to_set,
179 struct bundle_data *bd)
180 {
181 int idx;
182 struct bundle_data bdata;
183
184 if (bd == NULL) {
185 bd = &bdata;
186 setup_bundle_data(bitarray, bd, offset, num_bits);
187 }
188
189 if (bd->sidx == bd->eidx) {
190 /* Start/end at same bundle */
191 if (to_set) {
192 bitarray->bundles[bd->sidx] |= bd->smask;
193 } else {
194 bitarray->bundles[bd->sidx] &= ~bd->smask;
195 }
196 } else {
197 /* Start/end at different bundle.
198 * So set/clear the bits in start and end bundles
199 * separately. For in-between bundles,
200 * set/clear all bits.
201 */
202 if (to_set) {
203 bitarray->bundles[bd->sidx] |= bd->smask;
204 bitarray->bundles[bd->eidx] |= bd->emask;
205 for (idx = bd->sidx + 1; idx < bd->eidx; idx++) {
206 bitarray->bundles[idx] = ~0U;
207 }
208 } else {
209 bitarray->bundles[bd->sidx] &= ~bd->smask;
210 bitarray->bundles[bd->eidx] &= ~bd->emask;
211 for (idx = bd->sidx + 1; idx < bd->eidx; idx++) {
212 bitarray->bundles[idx] = 0U;
213 }
214 }
215 }
216 }
217
sys_bitarray_set_bit(sys_bitarray_t * bitarray,size_t bit)218 int sys_bitarray_set_bit(sys_bitarray_t *bitarray, size_t bit)
219 {
220 k_spinlock_key_t key;
221 int ret;
222 size_t idx, off;
223
224 key = k_spin_lock(&bitarray->lock);
225
226 __ASSERT_NO_MSG(bitarray->num_bits > 0);
227
228 if (bit >= bitarray->num_bits) {
229 ret = -EINVAL;
230 goto out;
231 }
232
233 idx = bit / bundle_bitness(bitarray);
234 off = bit % bundle_bitness(bitarray);
235
236 bitarray->bundles[idx] |= BIT(off);
237
238 ret = 0;
239
240 out:
241 k_spin_unlock(&bitarray->lock, key);
242 return ret;
243 }
244
sys_bitarray_clear_bit(sys_bitarray_t * bitarray,size_t bit)245 int sys_bitarray_clear_bit(sys_bitarray_t *bitarray, size_t bit)
246 {
247 k_spinlock_key_t key;
248 int ret;
249 size_t idx, off;
250
251 key = k_spin_lock(&bitarray->lock);
252
253 __ASSERT_NO_MSG(bitarray->num_bits > 0);
254
255 if (bit >= bitarray->num_bits) {
256 ret = -EINVAL;
257 goto out;
258 }
259
260 idx = bit / bundle_bitness(bitarray);
261 off = bit % bundle_bitness(bitarray);
262
263 bitarray->bundles[idx] &= ~BIT(off);
264
265 ret = 0;
266
267 out:
268 k_spin_unlock(&bitarray->lock, key);
269 return ret;
270 }
271
sys_bitarray_test_bit(sys_bitarray_t * bitarray,size_t bit,int * val)272 int sys_bitarray_test_bit(sys_bitarray_t *bitarray, size_t bit, int *val)
273 {
274 k_spinlock_key_t key;
275 int ret;
276 size_t idx, off;
277
278 key = k_spin_lock(&bitarray->lock);
279
280 __ASSERT_NO_MSG(bitarray->num_bits > 0);
281
282 CHECKIF(val == NULL) {
283 ret = -EINVAL;
284 goto out;
285 }
286
287 if (bit >= bitarray->num_bits) {
288 ret = -EINVAL;
289 goto out;
290 }
291
292 idx = bit / bundle_bitness(bitarray);
293 off = bit % bundle_bitness(bitarray);
294
295 if ((bitarray->bundles[idx] & BIT(off)) != 0) {
296 *val = 1;
297 } else {
298 *val = 0;
299 }
300
301 ret = 0;
302
303 out:
304 k_spin_unlock(&bitarray->lock, key);
305 return ret;
306 }
307
sys_bitarray_test_and_set_bit(sys_bitarray_t * bitarray,size_t bit,int * prev_val)308 int sys_bitarray_test_and_set_bit(sys_bitarray_t *bitarray, size_t bit, int *prev_val)
309 {
310 k_spinlock_key_t key;
311 int ret;
312 size_t idx, off;
313
314 key = k_spin_lock(&bitarray->lock);
315
316 __ASSERT_NO_MSG(bitarray->num_bits > 0);
317
318 CHECKIF(prev_val == NULL) {
319 ret = -EINVAL;
320 goto out;
321 }
322
323 if (bit >= bitarray->num_bits) {
324 ret = -EINVAL;
325 goto out;
326 }
327
328 idx = bit / bundle_bitness(bitarray);
329 off = bit % bundle_bitness(bitarray);
330
331 if ((bitarray->bundles[idx] & BIT(off)) != 0) {
332 *prev_val = 1;
333 } else {
334 *prev_val = 0;
335 }
336
337 bitarray->bundles[idx] |= BIT(off);
338
339 ret = 0;
340
341 out:
342 k_spin_unlock(&bitarray->lock, key);
343 return ret;
344 }
345
sys_bitarray_test_and_clear_bit(sys_bitarray_t * bitarray,size_t bit,int * prev_val)346 int sys_bitarray_test_and_clear_bit(sys_bitarray_t *bitarray, size_t bit, int *prev_val)
347 {
348 k_spinlock_key_t key;
349 int ret;
350 size_t idx, off;
351
352 key = k_spin_lock(&bitarray->lock);
353
354 __ASSERT_NO_MSG(bitarray->num_bits > 0);
355
356 CHECKIF(prev_val == NULL) {
357 ret = -EINVAL;
358 goto out;
359 }
360
361 if (bit >= bitarray->num_bits) {
362 ret = -EINVAL;
363 goto out;
364 }
365
366 idx = bit / bundle_bitness(bitarray);
367 off = bit % bundle_bitness(bitarray);
368
369 if ((bitarray->bundles[idx] & BIT(off)) != 0) {
370 *prev_val = 1;
371 } else {
372 *prev_val = 0;
373 }
374
375 bitarray->bundles[idx] &= ~BIT(off);
376
377 ret = 0;
378
379 out:
380 k_spin_unlock(&bitarray->lock, key);
381 return ret;
382 }
383
sys_bitarray_alloc(sys_bitarray_t * bitarray,size_t num_bits,size_t * offset)384 int sys_bitarray_alloc(sys_bitarray_t *bitarray, size_t num_bits,
385 size_t *offset)
386 {
387 k_spinlock_key_t key;
388 uint32_t bit_idx;
389 int ret;
390 struct bundle_data bd;
391 size_t off_start, off_end;
392 size_t mismatch;
393
394 key = k_spin_lock(&bitarray->lock);
395
396 __ASSERT_NO_MSG(bitarray->num_bits > 0);
397
398 CHECKIF(offset == NULL) {
399 ret = -EINVAL;
400 goto out;
401 }
402
403 if ((num_bits == 0) || (num_bits > bitarray->num_bits)) {
404 ret = -EINVAL;
405 goto out;
406 }
407
408 bit_idx = 0;
409
410 /* Find the first non-allocated bit by looking at bundles
411 * instead of individual bits.
412 *
413 * On RISC-V 64-bit, it complains about undefined reference to `ffs`.
414 * So don't use this on RISCV64.
415 */
416 for (ret = 0; ret < bitarray->num_bundles; ret++) {
417 if (~bitarray->bundles[ret] == 0U) {
418 /* bundle is all 1s => all allocated, skip */
419 bit_idx += bundle_bitness(bitarray);
420 continue;
421 }
422
423 if (bitarray->bundles[ret] != 0U) {
424 /* Find the first free bit in bundle if not all free */
425 off_start = find_lsb_set(~bitarray->bundles[ret]) - 1;
426 bit_idx += off_start;
427 }
428
429 break;
430 }
431
432 off_end = bitarray->num_bits - num_bits;
433 ret = -ENOSPC;
434 while (bit_idx <= off_end) {
435 if (match_region(bitarray, bit_idx, num_bits, false,
436 &bd, &mismatch)) {
437 off_end = bit_idx + num_bits - 1;
438
439 set_region(bitarray, bit_idx, num_bits, true, &bd);
440
441 *offset = bit_idx;
442 ret = 0;
443 break;
444 }
445
446 /* Fast-forward to the bit just after
447 * the mismatched bit.
448 */
449 bit_idx = mismatch + 1;
450 }
451
452 out:
453 k_spin_unlock(&bitarray->lock, key);
454 return ret;
455 }
456
sys_bitarray_free(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)457 int sys_bitarray_free(sys_bitarray_t *bitarray, size_t num_bits,
458 size_t offset)
459 {
460 k_spinlock_key_t key;
461 int ret;
462 size_t off_end = offset + num_bits - 1;
463 struct bundle_data bd;
464
465 key = k_spin_lock(&bitarray->lock);
466
467 __ASSERT_NO_MSG(bitarray->num_bits > 0);
468
469 if ((num_bits == 0)
470 || (num_bits > bitarray->num_bits)
471 || (offset >= bitarray->num_bits)
472 || (off_end >= bitarray->num_bits)) {
473 ret = -EINVAL;
474 goto out;
475 }
476
477 /* Note that we need to make sure the bits in specified region
478 * (offset to offset + num_bits) are all allocated before we clear
479 * them.
480 */
481 if (match_region(bitarray, offset, num_bits, true, &bd, NULL)) {
482 set_region(bitarray, offset, num_bits, false, &bd);
483 ret = 0;
484 } else {
485 ret = -EFAULT;
486 }
487
488 out:
489 k_spin_unlock(&bitarray->lock, key);
490 return ret;
491 }
492
is_region_set_clear(sys_bitarray_t * bitarray,size_t num_bits,size_t offset,bool to_set)493 static bool is_region_set_clear(sys_bitarray_t *bitarray, size_t num_bits,
494 size_t offset, bool to_set)
495 {
496 bool ret;
497 struct bundle_data bd;
498 size_t off_end = offset + num_bits - 1;
499 k_spinlock_key_t key = k_spin_lock(&bitarray->lock);
500
501 __ASSERT_NO_MSG(bitarray->num_bits > 0);
502
503 if ((num_bits == 0)
504 || (num_bits > bitarray->num_bits)
505 || (offset >= bitarray->num_bits)
506 || (off_end >= bitarray->num_bits)) {
507 ret = false;
508 goto out;
509 }
510
511 ret = match_region(bitarray, offset, num_bits, to_set, &bd, NULL);
512
513 out:
514 k_spin_unlock(&bitarray->lock, key);
515 return ret;
516 }
517
sys_bitarray_is_region_set(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)518 bool sys_bitarray_is_region_set(sys_bitarray_t *bitarray, size_t num_bits,
519 size_t offset)
520 {
521 return is_region_set_clear(bitarray, num_bits, offset, true);
522 }
523
sys_bitarray_is_region_cleared(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)524 bool sys_bitarray_is_region_cleared(sys_bitarray_t *bitarray, size_t num_bits,
525 size_t offset)
526 {
527 return is_region_set_clear(bitarray, num_bits, offset, false);
528 }
529
set_clear_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset,bool to_set)530 static int set_clear_region(sys_bitarray_t *bitarray, size_t num_bits,
531 size_t offset, bool to_set)
532 {
533 int ret;
534 size_t off_end = offset + num_bits - 1;
535 k_spinlock_key_t key = k_spin_lock(&bitarray->lock);
536
537 __ASSERT_NO_MSG(bitarray->num_bits > 0);
538
539 if ((num_bits == 0)
540 || (num_bits > bitarray->num_bits)
541 || (offset >= bitarray->num_bits)
542 || (off_end >= bitarray->num_bits)) {
543 ret = -EINVAL;
544 goto out;
545 }
546
547 set_region(bitarray, offset, num_bits, to_set, NULL);
548 ret = 0;
549
550 out:
551 k_spin_unlock(&bitarray->lock, key);
552 return ret;
553 }
554
sys_bitarray_set_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)555 int sys_bitarray_set_region(sys_bitarray_t *bitarray, size_t num_bits,
556 size_t offset)
557 {
558 return set_clear_region(bitarray, num_bits, offset, true);
559 }
560
sys_bitarray_clear_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)561 int sys_bitarray_clear_region(sys_bitarray_t *bitarray, size_t num_bits,
562 size_t offset)
563 {
564 return set_clear_region(bitarray, num_bits, offset, false);
565 }
566