Sound Open Firmware
Loading...
Searching...
No Matches
coherent.h
Go to the documentation of this file.
1/* SPDX-License-Identifier: BSD-3-Clause
2 *
3 * Copyright(c) 2021 Intel Corporation. All rights reserved.
4 *
5 * Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6 */
7
8#ifndef __SOF_COHERENT_H__
9#define __SOF_COHERENT_H__
10
11#include <stdbool.h>
12#include <stddef.h>
13#include <stdint.h>
14
15#include <rtos/spinlock.h>
16#include <sof/list.h>
17#include <sof/lib/memory.h>
18#include <sof/lib/cpu.h>
19
20#define __coherent __aligned(PLATFORM_DCACHE_ALIGN)
21
22/*
23 * The coherent API allows optimized access to memory by multiple cores, using
24 * cache, taking care about coherence. The intended use is to let cores acquire
25 * ownership of such shared objects, use them, and then release them, possibly
26 * to be re-acquired by other cores. Such shared objects must only be accessed
27 * via this API. It's designed to be primarily used with dynamically allocated
28 * objects because of their well-defined life span. It can also be used with
29 * objects from .data or .bss sections but greater care must be takenwith them
30 * to strictly follow the API flow.
31 *
32 * The API assumes, that in the beginning no core has cache lines associated
33 * with the memory area, used with it. That is true for dynamically allocated
34 * memory, because when such memory is freed, its cache is invalidated - as long
35 * as that memory was never accessed by other cores, except by using this API.
36 * The first call must be coherent_init(), which initializes the header. If the
37 * object will be used by multiple cores, next coherent_shared() must be called.
38 * After that to use that memory, coherent_acquire() must be called, which
39 * acquires ownership of the object and returns a cached address of the memory.
40 * After that the user can perform cached access to the memory. To release the
41 * memory, coherent_release() must be called. The only time when the memory is
42 * accessed using cache is between those two calls, so only when releasing the
43 * memory we have to write back and invalidate caches to make sure, that next
44 * time we acquire this memory, our uncached header access will not be
45 * overwritten! When memory is not needed any more, typically before freeing the
46 * memory, coherent_free() should be called.
47 *
48 * This structure needs to be embedded at the start of any container to ensure
49 * container object cache alignment and to minimise non cache access when
50 * acquiring ownership.
51 *
52 * This structure must not be accessed outside of these APIs.
53 * The shared flag is only set at coherent init and thereafter it's RO.
54 */
55struct coherent {
56 union {
57 struct {
58 struct k_spinlock lock; /* locking mechanism */
59 k_spinlock_key_t key; /* lock flags */
60 };
61#ifdef __ZEPHYR__
62 struct k_mutex mutex;
63#endif
64 };
65 uint8_t sleep_allowed; /* the object will never be acquired or released
66 * in atomic context */
67 uint8_t shared; /* shared on other non coherent cores */
68 uint16_t core; /* owner core if not shared */
69 struct list_item list; /* coherent list iteration */
71
72#if CONFIG_INCOHERENT
73# ifdef __ZEPHYR__
74BUILD_ASSERT(sizeof(struct coherent) <= DCACHE_LINE_SIZE, "DCACHE_LINE_SIZE too small");
75# else
76STATIC_ASSERT(sizeof(struct coherent) <= DCACHE_LINE_SIZE, DCACHE_LINE_SIZE_too_small);
77# endif
78#endif
79
80/* debug address aliases */
81#ifdef COHERENT_CHECK_ALIAS
82#define ADDR_IS_INCOHERENT(_c) assert(!is_uncached(_c))
83#define ADDR_IS_COHERENT(_c) assert(is_uncached(_c))
84#else
85#define ADDR_IS_INCOHERENT(_c)
86#define ADDR_IS_COHERENT(_c)
87#endif
88
89/* debug sharing amongst cores */
90#ifdef COHERENT_CHECK_NONSHARED_CORES
91
92#define CORE_CHECK_STRUCT_FIELD uint32_t __core; bool __is_shared
93#define CORE_CHECK_STRUCT_INIT(_c, is_shared) { (_c)->__core = cpu_get_id(); \
94 (_c)->__is_shared = is_shared; }
95#define CORE_CHECK_STRUCT(_c) { assert(!!(_c)->__is_shared == !!is_uncached(_c)); \
96 assert(cpu_get_id() == (_c)->__core || (_c)->__is_shared); }
97
98#define CHECK_COHERENT_CORE(_c) assert((_c)->core == cpu_get_id())
99
100#else
101
102#define CORE_CHECK_STRUCT_FIELD
103#define CORE_CHECK_STRUCT_INIT(_c, is_shared)
104#define CORE_CHECK_STRUCT(_c)
105
106#define CHECK_COHERENT_CORE(_c)
107
108#endif
109
110#ifdef __ZEPHYR__
111#define CHECK_ISR() __ASSERT(!k_is_in_isr(), "Attempt to sleep in ISR!")
112#define CHECK_SLEEP(_c) __ASSERT((_c)->sleep_allowed, \
113 "This context hasn't been initialized for sleeping!")
114#define CHECK_ATOMIC(_c) __ASSERT(!(_c)->sleep_allowed, \
115 "This context has been initialized for sleeping!")
116#else
117#define CHECK_ISR() assert(!k_is_in_isr())
118#define CHECK_SLEEP(_c) assert((_c)->sleep_allowed)
119#define CHECK_ATOMIC(_c) assert(!(_c)->sleep_allowed)
120#endif
121
122#if CONFIG_INCOHERENT
123/* When coherent_acquire() is called, we are sure not to have cache for this memory */
124__must_check static inline struct coherent __sparse_cache *coherent_acquire(struct coherent *c,
125 const size_t size)
126{
127 struct coherent __sparse_cache *cc = uncache_to_cache(c);
128
129 /* assert if someone passes a cache/local address in here. */
131 CHECK_ATOMIC(c);
132
133 /* access the shared coherent object */
134 if (c->shared) {
136
137 c->key = k_spin_lock(&c->lock);
138
139 /*
140 * FIXME: This is wrong. dcache_invalidate_region() only makes
141 * sense if we assume, that dirty cache lines might exist for
142 * this object. But in that case those lines could be written
143 * back here thus overwriting either user data, or the coherent
144 * header or both. When coherent_acquire() is called it must be
145 * guaranteed that the object isn't in cache. Before it is
146 * acquired no cached access to it is allowed. This has to be
147 * fixed here and on multiple further occasions below.
148 */
149
150 /* invalidate local copy */
151 dcache_invalidate_region(cc, size);
152 }
153
154 /* client can now use cached object safely */
155 return cc;
156}
157
158static inline void coherent_release(struct coherent __sparse_cache *c,
159 const size_t size)
160{
161 /* assert if someone passes a coherent address in here. */
163 CHECK_ATOMIC(c);
164
165 /* access the local copy of object */
166 if (c->shared) {
167 struct coherent *uc = cache_to_uncache(c);
169
170 /* wtb and inv local data to coherent object */
172
173 /* unlock on uncache alias */
174 k_spin_unlock(&uc->lock, uc->key);
175 }
176}
177
178static inline void *__coherent_init(size_t offset, const size_t size)
179{
180 /*
181 * Allocate an object with an uncached alias but align size on a cache-
182 * line boundary to avoid sharing a cache line with the adjacent
183 * allocation
184 */
186 ALIGN_UP(size, PLATFORM_DCACHE_ALIGN));
187 struct coherent *c;
188
189 if (!object)
190 return NULL;
191
192 c = (struct coherent *)((uint8_t *)object + offset);
193
194 /* TODO static assert if we are not cache aligned */
196 c->sleep_allowed = false;
197 c->shared = false;
198 c->core = cpu_get_id();
199 list_init(&c->list);
200 /* inv local data to coherent object */
201 dcache_invalidate_region(uncache_to_cache(object), size);
202
203 return object;
204}
205
206#define coherent_init(type, member) __coherent_init(offsetof(type, member), \
207 sizeof(type))
208
209/* set the object to shared mode with coherency managed by SW */
210static inline void __coherent_shared(struct coherent *c, const size_t size)
211{
212 /* assert if someone passes a cache/local address in here. */
214 CHECK_ATOMIC(c);
215
216 c->key = k_spin_lock(&c->lock);
217 c->shared = true;
218 dcache_invalidate_region(uncache_to_cache(c), size);
219 k_spin_unlock(&c->lock, c->key);
220}
221
222#define coherent_shared(object, member) __coherent_shared(&(object)->member, \
223 sizeof(*object))
224
225#ifdef __ZEPHYR__
226
228 struct coherent *c, const size_t size)
229{
230 struct coherent __sparse_cache *cc = uncache_to_cache(c);
231
232 /* assert if someone passes a cache/local address in here. */
234 CHECK_SLEEP(c);
235 CHECK_ISR();
236
237 /* access the shared coherent object */
238 if (c->shared) {
240
241 k_mutex_lock(&c->mutex, K_FOREVER);
242
243 /* invalidate local copy */
244 dcache_invalidate_region(cc, size);
245 }
246
247 /* client can now use cached object safely */
248 return cc;
249}
250
251static inline void coherent_release_thread(struct coherent __sparse_cache *c,
252 const size_t size)
253{
254 /* assert if someone passes a coherent address in here. */
256 CHECK_SLEEP(c);
257 CHECK_ISR();
258
259 /* access the local copy of object */
260 if (c->shared) {
261 struct coherent *uc = cache_to_uncache(c);
263
264 /* wtb and inv local data to coherent object */
266
267 /* unlock on uncache alias */
268 k_mutex_unlock(&uc->mutex);
269 }
270}
271
272static inline void *__coherent_init_thread(size_t offset, const size_t size)
273{
274 /* As above - prevent cache line sharing */
276 ALIGN_UP(size, PLATFORM_DCACHE_ALIGN));
277 struct coherent *c;
278
279 if (!object)
280 return NULL;
281
282 c = (struct coherent *)((uint8_t *)object + offset);
283
284 /* TODO static assert if we are not cache aligned */
285 k_mutex_init(&c->mutex);
286 c->sleep_allowed = true;
287 c->shared = false;
288 c->core = cpu_get_id();
289 list_init(&c->list);
290 /* inv local data to coherent object */
291 dcache_invalidate_region(uncache_to_cache(object), size);
292
293 return object;
294}
295
296#define coherent_init_thread(type, member) __coherent_init_thread(offsetof(type, member), \
297 sizeof(type))
298
299static inline void __coherent_shared_thread(struct coherent *c, const size_t size)
300{
301 /* assert if someone passes a cache/local address in here. */
303 CHECK_SLEEP(c);
304 CHECK_ISR();
305
306 k_mutex_lock(&c->mutex, K_FOREVER);
307 c->shared = true;
308 dcache_invalidate_region(uncache_to_cache(c), size);
309 k_mutex_unlock(&c->mutex);
310}
311
312#define coherent_shared_thread(object, member) __coherent_shared_thread(&(object)->member, \
313 sizeof(*object))
314
315#endif /* __ZEPHYR__ */
316
317#define coherent_free(object, member) \
318 do { \
319 /* assert if someone passes a cache address in here. */ \
320 ADDR_IS_COHERENT(object); \
321 /* wtb and inv local data to coherent object */ \
322 dcache_writeback_invalidate_region(uncache_to_cache(object), \
323 sizeof(*object)); \
324 rfree(object); \
325 } while (0)
326
327#else /* CONFIG_INCOHERENT */
328
329/*
330 * Coherent devices only require locking to manage shared access.
331 */
333 const size_t size)
334{
335 if (c->shared)
336 c->key = k_spin_lock(&c->lock);
337
338 return (__sparse_force struct coherent __sparse_cache *)c;
339}
340
341static inline void coherent_release(struct coherent __sparse_cache *c,
342 const size_t size)
343{
344 if (c->shared)
345 k_spin_unlock(&c->lock, c->key);
346}
347
348static inline void *__coherent_init(size_t offset, const size_t size)
349{
350 /* As in CONFIG_INCOHERENT case - prevent cache line sharing */
352 ALIGN_UP(size, PLATFORM_DCACHE_ALIGN));
353 struct coherent *c;
354
355 if (!object)
356 return NULL;
357
358 c = (struct coherent *)((uint8_t *)object + offset);
359
360 /* TODO static assert if we are not cache aligned */
362 c->shared = 0;
363 c->core = cpu_get_id();
364 list_init(&c->list);
365
366 return object;
367}
368
369#define coherent_init(type, member) __coherent_init(offsetof(type, member), \
370 sizeof(type))
371
372#define coherent_free(object, member) rfree(object)
373
374static inline void __coherent_shared(struct coherent *c, const size_t size)
375{
376 c->key = k_spin_lock(&c->lock);
377 c->shared = true;
378 k_spin_unlock(&c->lock, c->key);
379}
380
381#define coherent_shared(object, member) __coherent_shared(&(object)->member, \
382 sizeof(*object))
383
384#ifdef __ZEPHYR__
386 struct coherent *c, const size_t size)
387{
388 if (c->shared)
389 k_mutex_lock(&c->mutex, K_FOREVER);
390
391 return (__sparse_force struct coherent __sparse_cache *)c;
392}
393
394static inline void coherent_release_thread(struct coherent __sparse_cache *c,
395 const size_t size)
396{
397 if (c->shared)
398 k_mutex_unlock(&c->mutex);
399}
400
401static inline void *__coherent_init_thread(size_t offset, const size_t size)
402{
403 /* As above - prevent cache line sharing */
405 ALIGN_UP(size, PLATFORM_DCACHE_ALIGN));
406 struct coherent *c;
407
408 if (!object)
409 return NULL;
410
411 c = (struct coherent *)((uint8_t *)object + offset);
412
413 /* TODO static assert if we are not cache aligned */
414 k_mutex_init(&c->mutex);
415 c->shared = 0;
416 c->core = cpu_get_id();
417 list_init(&c->list);
418
419 return object;
420}
421
422#define coherent_init_thread(type, member) __coherent_init_thread(offsetof(type, member), \
423 sizeof(type))
424
425static inline void __coherent_shared_thread(struct coherent *c, const size_t size)
426{
427 k_mutex_lock(&c->mutex, K_FOREVER);
428 c->shared = true;
429 k_mutex_unlock(&c->mutex);
430}
431
432#define coherent_shared_thread(object, member) __coherent_shared_thread(&(object)->member, \
433 sizeof(*object))
434#endif /* __ZEPHYR__ */
435
436#endif /* CONFIG_INCOHERENT */
437
438#ifndef __ZEPHYR__
439#define coherent_acquire_thread coherent_acquire
440#define coherent_release_thread coherent_release
441#define coherent_init_thread coherent_init
442#define coherent_shared_thread coherent_shared
443#endif
444
445#define coherent_free_thread coherent_free
446
447#define is_coherent_shared(object, member) ((object)->member.shared)
448#endif
static void dcache_writeback_invalidate_region(void __sparse_cache *addr, size_t size)
Definition cache.h:56
static void dcache_invalidate_region(void __sparse_cache *addr, size_t size)
Definition cache.h:44
#define coherent_release_thread
Definition coherent.h:440
#define CHECK_ISR()
Definition coherent.h:117
static void __coherent_shared(struct coherent *c, const size_t size)
Definition coherent.h:374
#define ADDR_IS_COHERENT(_c)
Definition coherent.h:86
#define CHECK_COHERENT_CORE(_c)
Definition coherent.h:106
static void * __coherent_init(size_t offset, const size_t size)
Definition coherent.h:348
static void coherent_release(struct coherent __sparse_cache *c, const size_t size)
Definition coherent.h:341
#define CHECK_SLEEP(_c)
Definition coherent.h:118
static __must_check struct coherent __sparse_cache * coherent_acquire(struct coherent *c, const size_t size)
Definition coherent.h:332
#define ADDR_IS_INCOHERENT(_c)
Definition coherent.h:85
#define __coherent
Definition coherent.h:20
#define coherent_acquire_thread
Definition coherent.h:439
#define CHECK_ATOMIC(_c)
Definition coherent.h:119
#define STATIC_ASSERT(COND, MESSAGE)
Definition common.h:158
#define ALIGN_UP(size, alignment)
Definition common.h:95
#define __must_check
Definition common.h:15
CPU header file.
static int cpu_get_id(void)
Definition cpu.h:98
void * rzalloc(uint32_t flags, size_t bytes)
Similar to rmalloc(), guarantees that returned block is zeroed.
#define SOF_MEM_FLAG_USER
Allocate user memory address.
Definition alloc.h:45
#define SOF_MEM_FLAG_COHERENT
Allocate uncached address.
Definition alloc.h:37
static void list_init(struct list_item *list)
Definition list.h:25
#define list_item(item, type, member)
Definition list.h:80
static void k_spinlock_init(struct k_spinlock *lock)
Definition spinlock.h:13
Definition coherent.h:55
k_spinlock_key_t key
Definition coherent.h:59
uint16_t core
Definition coherent.h:68
struct k_spinlock lock
Definition coherent.h:58
uint8_t shared
Definition coherent.h:67
uint8_t sleep_allowed
Definition coherent.h:65
struct list_item list
Definition coherent.h:69
#define __sparse_force
Definition compiler_attributes.h:12
#define __sparse_cache
Definition compiler_attributes.h:11