xref: /OK3568_Linux_fs/external/mpp/osal/mpp_list.cpp (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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