1 /*
2 * Copyright 2015 Rockchip Electronics Co. LTD
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 #define MODULE_TAG "mpp_list"
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stdarg.h>
22 #include <errno.h>
23
24 #include "mpp_log.h"
25 #include "mpp_list.h"
26 #include "mpp_common.h"
27
28
29 #define LIST_DEBUG(fmt, ...) mpp_log(fmt, ## __VA_ARGS__)
30 #define LIST_ERROR(fmt, ...) mpp_err(fmt, ## __VA_ARGS__)
31
32 RK_U32 mpp_list::keys = 0;
33
34 typedef struct mpp_list_node {
35 mpp_list_node* prev;
36 mpp_list_node* next;
37 RK_U32 key;
38 RK_S32 size;
39 } mpp_list_node;
40
list_node_init(mpp_list_node * node)41 static inline void list_node_init(mpp_list_node *node)
42 {
43 node->prev = node->next = node;
44 }
45
list_node_init_with_key_and_size(mpp_list_node * node,RK_U32 key,RK_S32 size)46 static inline void list_node_init_with_key_and_size(mpp_list_node *node, RK_U32 key, RK_S32 size)
47 {
48 list_node_init(node);
49 node->key = key;
50 node->size = size;
51 }
52
create_list(void * data,RK_S32 size,RK_U32 key)53 static mpp_list_node* create_list(void *data, RK_S32 size, RK_U32 key)
54 {
55 mpp_list_node *node = (mpp_list_node*)malloc(sizeof(mpp_list_node) + size);
56 if (node) {
57 void *dst = (void*)(node + 1);
58 list_node_init_with_key_and_size(node, key, size);
59 memcpy(dst, data, size);
60 } else {
61 LIST_ERROR("failed to allocate list node");
62 }
63 return node;
64 }
65
_mpp_list_add(mpp_list_node * _new,mpp_list_node * prev,mpp_list_node * next)66 static inline void _mpp_list_add(mpp_list_node * _new, mpp_list_node * prev, mpp_list_node * next)
67 {
68 next->prev = _new;
69 _new->next = next;
70 _new->prev = prev;
71 prev->next = _new;
72 }
73
mpp_list_add(mpp_list_node * _new,mpp_list_node * head)74 static inline void mpp_list_add(mpp_list_node *_new, mpp_list_node *head)
75 {
76 _mpp_list_add(_new, head, head->next);
77 }
78
mpp_list_add_tail(mpp_list_node * _new,mpp_list_node * head)79 static inline void mpp_list_add_tail(mpp_list_node *_new, mpp_list_node *head)
80 {
81 _mpp_list_add(_new, head->prev, head);
82 }
83
add_at_head(void * data,RK_S32 size)84 RK_S32 mpp_list::add_at_head(void *data, RK_S32 size)
85 {
86 RK_S32 ret = -EINVAL;
87 if (head) {
88 mpp_list_node *node = create_list(data, size, 0);
89 if (node) {
90 mpp_list_add(node, head);
91 count++;
92 ret = 0;
93 } else {
94 ret = -ENOMEM;
95 }
96 }
97 return ret;
98 }
99
add_at_tail(void * data,RK_S32 size)100 RK_S32 mpp_list::add_at_tail(void *data, RK_S32 size)
101 {
102 RK_S32 ret = -EINVAL;
103 if (head) {
104 mpp_list_node *node = create_list(data, size, 0);
105 if (node) {
106 mpp_list_add_tail(node, head);
107 count++;
108 ret = 0;
109 } else {
110 ret = -ENOMEM;
111 }
112 }
113 return ret;
114 }
115
release_list(mpp_list_node * node,void * data,RK_S32 size)116 static void release_list(mpp_list_node*node, void *data, RK_S32 size)
117 {
118 void *src = (void*)(node + 1);
119 if (node->size == size) {
120 if (data)
121 memcpy(data, src, size);
122 } else {
123 LIST_ERROR("node size check failed when release_list");
124 size = (size < node->size) ? (size) : (node->size);
125 if (data)
126 memcpy(data, src, size);
127 }
128 free(node);
129 }
130
_mpp_list_del(mpp_list_node * prev,mpp_list_node * next)131 static inline void _mpp_list_del(mpp_list_node *prev, mpp_list_node *next)
132 {
133 next->prev = prev;
134 prev->next = next;
135 }
136
mpp_list_del_init(mpp_list_node * node)137 static inline void mpp_list_del_init(mpp_list_node *node)
138 {
139 _mpp_list_del(node->prev, node->next);
140 list_node_init(node);
141 }
142
_list_del_node_no_lock(mpp_list_node * node,void * data,RK_S32 size)143 static inline void _list_del_node_no_lock(mpp_list_node *node, void *data, RK_S32 size)
144 {
145 mpp_list_del_init(node);
146 release_list(node, data, size);
147 }
148
del_at_head(void * data,RK_S32 size)149 RK_S32 mpp_list::del_at_head(void *data, RK_S32 size)
150 {
151 RK_S32 ret = -EINVAL;
152 if (head && count) {
153 _list_del_node_no_lock(head->next, data, size);
154 count--;
155 ret = 0;
156 }
157 return ret;
158 }
159
del_at_tail(void * data,RK_S32 size)160 RK_S32 mpp_list::del_at_tail(void *data, RK_S32 size)
161 {
162 RK_S32 ret = -EINVAL;
163 if (head && count) {
164 _list_del_node_no_lock(head->prev, data, size);
165 count--;
166 ret = 0;
167 }
168 return ret;
169 }
170
create_list_with_size(void * data,RK_S32 size,RK_U32 key)171 static mpp_list_node* create_list_with_size(void *data, RK_S32 size, RK_U32 key)
172 {
173 mpp_list_node *node = (mpp_list_node*)malloc(sizeof(mpp_list_node) +
174 sizeof(size) + size);
175 if (node) {
176 RK_S32 *dst = (RK_S32 *)(node + 1);
177 list_node_init_with_key_and_size(node, key, size);
178 *dst++ = size;
179 memcpy(dst, data, size);
180 } else {
181 LIST_ERROR("failed to allocate list node");
182 }
183 return node;
184 }
185
fifo_wr(void * data,RK_S32 size)186 RK_S32 mpp_list::fifo_wr(void *data, RK_S32 size)
187 {
188 RK_S32 ret = -EINVAL;
189 if (head) {
190 mpp_list_node *node = create_list_with_size(data, size, 0);
191 if (node) {
192 mpp_list_add_tail(node, head);
193 count++;
194 ret = 0;
195 } else {
196 ret = -ENOMEM;
197 }
198 }
199 return ret;
200 }
201
release_list_with_size(mpp_list_node * node,void * data,RK_S32 * size)202 static void release_list_with_size(mpp_list_node* node, void *data, RK_S32 *size)
203 {
204 RK_S32 *src = (RK_S32*)(node + 1);
205 RK_S32 data_size = *src++;
206
207 *size = data_size;
208
209 if (data)
210 memcpy(data, src, data_size);
211
212 free(node);
213 }
214
fifo_rd(void * data,RK_S32 * size)215 RK_S32 mpp_list::fifo_rd(void *data, RK_S32 *size)
216 {
217 RK_S32 ret = -EINVAL;
218 if (head && count) {
219 mpp_list_node *node = head->next;
220
221 mpp_list_del_init(node);
222 release_list_with_size(node, data, size);
223 count--;
224 ret = 0;
225 }
226 return ret;
227 }
228
list_is_empty()229 RK_S32 mpp_list::list_is_empty()
230 {
231 RK_S32 ret = (count == 0);
232 return ret;
233 }
234
list_size()235 RK_S32 mpp_list::list_size()
236 {
237 RK_S32 ret = count;
238 return ret;
239 }
240
add_by_key(void * data,RK_S32 size,RK_U32 * key)241 RK_S32 mpp_list::add_by_key(void *data, RK_S32 size, RK_U32 *key)
242 {
243 RK_S32 ret = 0;
244 if (head) {
245 RK_U32 list_key = get_key();
246 *key = list_key;
247 mpp_list_node *node = create_list(data, size, list_key);
248 if (node) {
249 mpp_list_add_tail(node, head);
250 count++;
251 ret = 0;
252 } else {
253 ret = -ENOMEM;
254 }
255 }
256 return ret;
257 }
258
del_by_key(void * data,RK_S32 size,RK_U32 key)259 RK_S32 mpp_list::del_by_key(void *data, RK_S32 size, RK_U32 key)
260 {
261 RK_S32 ret = 0;
262 if (head && count) {
263 struct mpp_list_node *tmp = head->next;
264 ret = -EINVAL;
265 while (tmp->next != head) {
266 if (tmp->key == key) {
267 _list_del_node_no_lock(tmp, data, size);
268 count--;
269 break;
270 }
271 }
272 }
273 return ret;
274 }
275
276
show_by_key(void * data,RK_U32 key)277 RK_S32 mpp_list::show_by_key(void *data, RK_U32 key)
278 {
279 RK_S32 ret = -EINVAL;
280 (void)data;
281 (void)key;
282 return ret;
283 }
284
flush()285 RK_S32 mpp_list::flush()
286 {
287 if (head) {
288 while (count) {
289 mpp_list_node* node = head->next;
290 mpp_list_del_init(node);
291 if (destroy) {
292 destroy((void*)(node + 1));
293 }
294 free(node);
295 count--;
296 }
297 }
298 signal();
299 return 0;
300 }
301
wait_lt(RK_S64 timeout,RK_S32 val)302 MPP_RET mpp_list::wait_lt(RK_S64 timeout, RK_S32 val)
303 {
304 if (list_size() < val)
305 return MPP_OK;
306
307 if (!timeout)
308 return MPP_NOK;
309
310 if (timeout < 0)
311 wait();
312 else
313 wait(timeout);
314
315 return list_size() < val ? MPP_OK : MPP_NOK;
316 }
317
wait_le(RK_S64 timeout,RK_S32 val)318 MPP_RET mpp_list::wait_le(RK_S64 timeout, RK_S32 val)
319 {
320 if (list_size() <= val)
321 return MPP_OK;
322
323 if (!timeout)
324 return MPP_NOK;
325
326 if (timeout < 0)
327 wait();
328 else
329 wait(timeout);
330
331 return list_size() <= val ? MPP_OK : MPP_NOK;
332 }
333
wait_gt(RK_S64 timeout,RK_S32 val)334 MPP_RET mpp_list::wait_gt(RK_S64 timeout, RK_S32 val)
335 {
336 if (list_size() > val)
337 return MPP_OK;
338
339 if (!timeout)
340 return MPP_NOK;
341
342 if (timeout < 0)
343 wait();
344 else
345 wait(timeout);
346
347 return list_size() > val ? MPP_OK : MPP_NOK;
348 }
349
wait_ge(RK_S64 timeout,RK_S32 val)350 MPP_RET mpp_list::wait_ge(RK_S64 timeout, RK_S32 val)
351 {
352 if (list_size() >= val)
353 return MPP_OK;
354
355 if (!timeout)
356 return MPP_NOK;
357
358 if (timeout < 0)
359 wait();
360 else
361 wait(timeout);
362
363 return list_size() >= val ? MPP_OK : MPP_NOK;
364 }
365
get_key()366 RK_U32 mpp_list::get_key()
367 {
368 return keys++;
369 }
370
mpp_list(node_destructor func)371 mpp_list::mpp_list(node_destructor func)
372 : destroy(NULL),
373 head(NULL),
374 count(0)
375 {
376 destroy = func;
377 head = (mpp_list_node*)malloc(sizeof(mpp_list_node));
378 if (NULL == head) {
379 LIST_ERROR("failed to allocate list header");
380 } else {
381 list_node_init_with_key_and_size(head, 0, 0);
382 }
383 }
384
~mpp_list()385 mpp_list::~mpp_list()
386 {
387 flush();
388 if (head) free(head);
389 head = NULL;
390 destroy = NULL;
391 }
392
393 /* list sort porting from kernel list_sort.c */
394
395 /*
396 * Returns a list organized in an intermediate format suited
397 * to chaining of merge() calls: null-terminated, no reserved or
398 * sentinel head node, "prev" links not maintained.
399 */
merge(void * priv,list_cmp_func_t cmp,struct list_head * a,struct list_head * b)400 static struct list_head *merge(void *priv, list_cmp_func_t cmp,
401 struct list_head *a, struct list_head *b)
402 {
403 struct list_head *head, **tail = &head;
404
405 for (;;) {
406 /* if equal, take 'a' -- important for sort stability */
407 if (cmp(priv, a, b) <= 0) {
408 *tail = a;
409 tail = &a->next;
410 a = a->next;
411 if (!a) {
412 *tail = b;
413 break;
414 }
415 } else {
416 *tail = b;
417 tail = &b->next;
418 b = b->next;
419 if (!b) {
420 *tail = a;
421 break;
422 }
423 }
424 }
425 return head;
426 }
427
428 /*
429 * Combine final list merge with restoration of standard doubly-linked
430 * list structure. This approach duplicates code from merge(), but
431 * runs faster than the tidier alternatives of either a separate final
432 * prev-link restoration pass, or maintaining the prev links
433 * throughout.
434 */
merge_final(void * priv,list_cmp_func_t cmp,struct list_head * head,struct list_head * a,struct list_head * b)435 static void merge_final(void *priv, list_cmp_func_t cmp, struct list_head *head,
436 struct list_head *a, struct list_head *b)
437 {
438 struct list_head *tail = head;
439 RK_U8 count = 0;
440
441 for (;;) {
442 /* if equal, take 'a' -- important for sort stability */
443 if (cmp(priv, a, b) <= 0) {
444 tail->next = a;
445 a->prev = tail;
446 tail = a;
447 a = a->next;
448 if (!a)
449 break;
450 } else {
451 tail->next = b;
452 b->prev = tail;
453 tail = b;
454 b = b->next;
455 if (!b) {
456 b = a;
457 break;
458 }
459 }
460 }
461
462 /* Finish linking remainder of list b on to tail */
463 tail->next = b;
464 do {
465 /*
466 * If the merge is highly unbalanced (e.g. the input is
467 * already sorted), this loop may run many iterations.
468 * Continue callbacks to the client even though no
469 * element comparison is needed, so the client's cmp()
470 * routine can invoke cond_resched() periodically.
471 */
472 if (!++count)
473 cmp(priv, b, b);
474 b->prev = tail;
475 tail = b;
476 b = b->next;
477 } while (b);
478
479 /* And the final links to make a circular doubly-linked list */
480 tail->next = head;
481 head->prev = tail;
482 }
483
list_sort(void * priv,struct list_head * head,list_cmp_func_t cmp)484 void list_sort(void *priv, struct list_head *head, list_cmp_func_t cmp)
485 {
486 struct list_head *list = head->next, *pending = NULL;
487 size_t count = 0; /* Count of pending */
488
489 if (list == head->prev) /* Zero or one elements */
490 return;
491
492 /* Convert to a null-terminated singly-linked list. */
493 head->prev->next = NULL;
494
495 /*
496 * Data structure invariants:
497 * - All lists are singly linked and null-terminated; prev
498 * pointers are not maintained.
499 * - pending is a prev-linked "list of lists" of sorted
500 * sublists awaiting further merging.
501 * - Each of the sorted sublists is power-of-two in size.
502 * - Sublists are sorted by size and age, smallest & newest at front.
503 * - There are zero to two sublists of each size.
504 * - A pair of pending sublists are merged as soon as the number
505 * of following pending elements equals their size (i.e.
506 * each time count reaches an odd multiple of that size).
507 * That ensures each later final merge will be at worst 2:1.
508 * - Each round consists of:
509 * - Merging the two sublists selected by the highest bit
510 * which flips when count is incremented, and
511 * - Adding an element from the input as a size-1 sublist.
512 */
513 do {
514 size_t bits;
515 struct list_head **tail = &pending;
516
517 /* Find the least-significant clear bit in count */
518 for (bits = count; bits & 1; bits >>= 1)
519 tail = &(*tail)->prev;
520 /* Do the indicated merge */
521 if (bits) {
522 struct list_head *a = *tail, *b = a->prev;
523
524 a = merge(priv, cmp, b, a);
525 /* Install the merged result in place of the inputs */
526 a->prev = b->prev;
527 *tail = a;
528 }
529
530 /* Move one element from input list to pending */
531 list->prev = pending;
532 pending = list;
533 list = list->next;
534 pending->next = NULL;
535 count++;
536 } while (list);
537
538 /* End of input; merge together all the pending lists. */
539 list = pending;
540 pending = pending->prev;
541 for (;;) {
542 struct list_head *next = pending->prev;
543
544 if (!next)
545 break;
546 list = merge(priv, cmp, pending, list);
547 pending = next;
548 }
549 /* The final merge, rebuilding prev links */
550 merge_final(priv, cmp, head, pending, list);
551 }
552
553 #if BUILD_RK_LIST_TEST
554 #include "vpu_mem.h"
555 #include <stdio.h>
556 #include <stdlib.h>
557 #include <string.h>
558
559 #define LOOP_RK_LIST 600
560
561 #define COUNT_ADD 100
562 #define COUNT_DEL 99
563
564 volatile int err = 0;
565
mpp_list_fifo_test(mpp_list * list_0)566 static int mpp_list_fifo_test(mpp_list *list_0)
567 {
568 int count;
569 VPUMemLinear_t m;
570 for (count = 0; count < COUNT_ADD; count++) {
571 err |= VPUMallocLinear(&m, 100);
572 if (err) {
573 printf("VPUMallocLinear in mpp_list_fifo_test\n");
574 break;
575 }
576 err |= list_0->add_at_head(&m, sizeof(m));
577 if (err) {
578 printf("add_at_head in mpp_list_fifo_test\n");
579 break;
580 }
581 }
582
583 if (!err) {
584 for (count = 0; count < COUNT_DEL; count++) {
585 err |= list_0->del_at_tail(&m, sizeof(m));
586 if (err) {
587 printf("del_at_tail in mpp_list_fifo_test\n");
588 break;
589 }
590 err |= VPUFreeLinear(&m);
591 if (err) {
592 printf("VPUFreeLinear in mpp_list_fifo_test\n");
593 break;
594 }
595 }
596 }
597 return err;
598 }
599
mpp_list_filo_test(mpp_list * list_0)600 static int mpp_list_filo_test(mpp_list *list_0)
601 {
602 int count;
603 VPUMemLinear_t m;
604 for (count = 0; count < COUNT_ADD + COUNT_DEL; count++) {
605 if (count & 1) {
606 err |= list_0->del_at_head(&m, sizeof(m));
607 if (err) {
608 printf("del_at_head in mpp_list_filo_test\n");
609 break;
610 }
611 err |= VPUFreeLinear(&m);
612 if (err) {
613 printf("VPUFreeLinear in mpp_list_fifo_test\n");
614 break;
615 }
616 } else {
617 err |= VPUMallocLinear(&m, 100);
618 if (err) {
619 printf("VPUMallocLinear in mpp_list_filo_test\n");
620 break;
621 }
622 err |= list_0->add_at_head(&m, sizeof(m));
623 if (err) {
624 printf("add_at_head in mpp_list_fifo_test\n");
625 break;
626 }
627 }
628 }
629
630 return err;
631 }
632
633
mpp_list_test_loop_0(void * pdata)634 void *mpp_list_test_loop_0(void *pdata)
635 {
636 int i;
637 mpp_list *list_0 = (mpp_list *)pdata;
638
639 printf("mpp_list test 0 loop start\n");
640 for (i = 0; i < LOOP_RK_LIST; i++) {
641 err |= mpp_list_filo_test(list_0);
642 if (err) break;
643 }
644
645 if (err) {
646 printf("thread: found vpu mem operation err %d\n", err);
647 } else {
648 printf("thread: test done and found no err\n");
649 }
650 return NULL;
651 }
652
mpp_list_test_0()653 int mpp_list_test_0()
654 {
655 int i, err = 0;
656 printf("mpp_list test 0 FIFO start\n");
657
658 mpp_list *list_0 = new mpp_list((node_destructor)VPUFreeLinear);
659
660 pthread_t mThread;
661 pthread_attr_t attr;
662 pthread_attr_init(&attr);
663 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
664
665 pthread_create(&mThread, &attr, mpp_list_test_loop_0, (void*)list_0);
666 pthread_attr_destroy(&attr);
667
668 for (i = 0; i < LOOP_RK_LIST; i++) {
669 err |= mpp_list_fifo_test(list_0);
670 if (err) break;
671 }
672 if (err) {
673 printf("main : found mpp_list operation err %d\n", err);
674 } else {
675 printf("main : test done and found no err\n");
676 }
677
678 void *dummy;
679 pthread_join(mThread, &dummy);
680
681 printf("mpp_list test 0 end size %d\n", list_0->list_size());
682 delete list_0;
683 return err;
684 }
685
686 #define TOTAL_RK_LIST_TEST_COUNT 1
687
688 typedef int (*RK_LIST_TEST_FUNC)(void);
689 RK_LIST_TEST_FUNC test_func[TOTAL_RK_LIST_TEST_COUNT] = {
690 mpp_list_test_0,
691 };
692
main(int argc,char * argv[])693 int main(int argc, char *argv[])
694 {
695 int i, start = 0, end = 0;
696 if (argc < 2) {
697 end = TOTAL_RK_LIST_TEST_COUNT;
698 } else if (argc == 2) {
699 start = atoi(argv[1]);
700 end = start + 1;
701 } else if (argc == 3) {
702 start = atoi(argv[1]);
703 end = atoi(argv[2]);
704 } else {
705 printf("too many argc %d\n", argc);
706 return -1;
707 }
708 if (start < 0 || start > TOTAL_RK_LIST_TEST_COUNT || end < 0 || end > TOTAL_RK_LIST_TEST_COUNT) {
709 printf("invalid input: start %d end %d\n", start, end);
710 return -1;
711 }
712 for (i = start; i < end; i++) {
713 int err = test_func[i]();
714 if (err) {
715 printf("test case %d return err %d\n", i, err);
716 break;
717 }
718 }
719 return 0;
720 }
721 #endif
722
723