1 #include "HalideRuntime.h"
2 #include "hexagon_dma_pool.h"
3 #include "mini_hexagon_dma.h"
4 #include "printer.h"
5 #include "scoped_mutex_lock.h"
6 
7 namespace Halide {
8 namespace Runtime {
9 namespace Internal {
10 namespace Hexagon {
11 
12 typedef struct hexagon_local_cache {
13     void *l2memory;
14     bool used;
15     size_t bytes;
16     struct hexagon_local_cache *next;
17 } hexagon_cache_pool_t;
18 
19 typedef hexagon_cache_pool_t *pcache_pool;
20 
21 WEAK pcache_pool hexagon_cache_pool = NULL;
22 WEAK halide_mutex hexagon_cache_mutex;
23 
24 }  // namespace Hexagon
25 }  // namespace Internal
26 }  // namespace Runtime
27 }  // namespace Halide
28 
29 using namespace Halide::Runtime::Internal::Hexagon;
30 
31 namespace {
32 
free_unused_buffers(void * user_context)33 inline void *free_unused_buffers(void *user_context) {
34     // Walk the list and deallocate unused blocks.
35     ScopedMutexLock lock(&hexagon_cache_mutex);
36     pcache_pool temp2 = hexagon_cache_pool;
37     pcache_pool prev_node = hexagon_cache_pool;
38     while (temp2 != NULL) {
39         if (temp2->used == false) {
40             int err = HAP_cache_unlock(temp2->l2memory);
41             if (err != 0) {
42                 error(user_context) << "Hexagon: HAP_cache_unlock failure on unused free\n";
43                 return NULL;
44             }
45             // Set previous node details.
46             prev_node->next = (temp2->next)->next;
47             prev_node = temp2->next;
48             // Set Head.
49             if (temp2 == hexagon_cache_pool) {
50                 hexagon_cache_pool = temp2->next;
51             }
52             // Free node and reassign the variable.
53             free(temp2);
54             temp2 = prev_node;
55         }
56         prev_node = temp2;
57         temp2 = temp2->next;
58     }
59     return (void *)prev_node;
60 }
61 
62 // Retry logic if enabled will walk the list and deallocate unused blocks to make room for a larger block size
63 // Once all unused blocks are deallocated it will try to allocate a larger block
hexagon_cache_pool_get(void * user_context,size_t size,bool retry)64 inline void *hexagon_cache_pool_get(void *user_context, size_t size, bool retry) {
65 
66     pcache_pool prev = NULL;
67     pcache_pool temp = hexagon_cache_pool;
68     // Walk the list to find free buffer
69     {
70         ScopedMutexLock lock(&hexagon_cache_mutex);
71         while (temp != NULL) {
72             if ((temp->used == false) &&
73                 (size == temp->bytes)) {
74                 temp->used = true;
75                 return (void *)temp->l2memory;
76             }
77             prev = temp;
78             temp = temp->next;
79         }
80     }
81 
82     // If we are still here that means temp was null.
83     temp = (pcache_pool)malloc(sizeof(hexagon_cache_pool_t));
84     if (temp == NULL) {
85         error(user_context) << "Hexagon: Out of memory (Cache Pool Allocation Failed)\n";
86         return NULL;
87     }
88     uint8_t *mem = (uint8_t *)HAP_cache_lock(sizeof(char) * size, NULL);
89     if ((mem == NULL) && retry) {
90         pcache_pool prev_node = (pcache_pool)free_unused_buffers(user_context);
91         // Retry one more time after deallocating unused nodes.
92         mem = (uint8_t *)HAP_cache_lock(sizeof(char) * size, NULL);
93         prev = prev_node;
94         if (mem == NULL) {
95             free(temp);
96             error(user_context) << "Hexagon: Out of memory (HAP_cache_lock retry failed)\n";
97             return NULL;
98         }
99     } else if (mem == NULL) {
100         free(temp);
101         error(user_context) << "Hexagon: Out of memory (HAP_cache_lock failed)\n";
102         return NULL;
103     }
104     temp->l2memory = (void *)mem;
105     temp->bytes = size;
106     temp->used = true;
107     temp->next = NULL;
108 
109     {
110         ScopedMutexLock lock_obj(&hexagon_cache_mutex);
111         if (prev != NULL) {
112             prev->next = temp;
113         } else if (hexagon_cache_pool == NULL) {
114             hexagon_cache_pool = temp;
115         }
116     }
117     return (void *)temp->l2memory;
118 }
119 
hexagon_cache_pool_put(void * user_context,void * cache_mem)120 inline void hexagon_cache_pool_put(void *user_context, void *cache_mem) {
121     ScopedMutexLock lock(&hexagon_cache_mutex);
122     halide_assert(user_context, cache_mem);
123     pcache_pool temp = hexagon_cache_pool;
124     while (temp != NULL) {
125         if (temp->l2memory == cache_mem) {
126             temp->used = false;
127             return;
128         }
129         temp = temp->next;
130     }
131 }
132 
hexagon_cache_pool_free(void * user_context)133 inline int hexagon_cache_pool_free(void *user_context) {
134     ScopedMutexLock lock(&hexagon_cache_mutex);
135     pcache_pool temp = hexagon_cache_pool;
136     pcache_pool prev = hexagon_cache_pool;
137     int err = QURT_EOK;
138     while (temp != NULL) {
139         if (temp->l2memory != NULL) {
140             err = HAP_cache_unlock(temp->l2memory);
141             if (err != QURT_EOK) {
142                 error(user_context) << "Hexagon: HAP_cache_unlock failed on pool free\n";
143                 return err;
144             }
145         }
146         prev = temp->next;
147         free(temp);
148         temp = prev;
149     }
150     hexagon_cache_pool = NULL;
151     return QURT_EOK;
152 }
153 
154 }  // namespace
155 
156 extern "C" {
157 
halide_locked_cache_malloc(void * user_context,size_t size)158 WEAK void *halide_locked_cache_malloc(void *user_context, size_t size) {
159     // TODO Currently option to retry allocation is disabled, we will have to decide if can be
160     // set by user or pipeline.
161     bool retry = false;
162     debug(user_context) << "halide_locked_cache_malloc\n";
163     return hexagon_cache_pool_get(user_context, size, retry);
164 }
165 
halide_locked_cache_free(void * user_context,void * ptr)166 WEAK void halide_locked_cache_free(void *user_context, void *ptr) {
167     debug(user_context) << "halide_locked_cache_free\n";
168     hexagon_cache_pool_put(user_context, ptr);
169 }
170 
halide_hexagon_allocate_l2_pool(void * user_context)171 WEAK int halide_hexagon_allocate_l2_pool(void *user_context) {
172     // TODO not sure what is required to be done here ?
173     debug(user_context) << "halide_hexagon_allocate_l2_pool\n";
174     return halide_error_code_success;
175 }
176 
halide_hexagon_free_l2_pool(void * user_context)177 WEAK int halide_hexagon_free_l2_pool(void *user_context) {
178     debug(user_context) << "halide_hexagon_free_l2_pool\n";
179     return hexagon_cache_pool_free(user_context);
180 }
181 }
182