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
14#ifndef __SOF_COHERENT_H__
15#define __SOF_COHERENT_H__
16
17#include <stdbool.h>
18#include <stddef.h>
19#include <stdint.h>
20
21#include <rtos/spinlock.h>
22#include <sof/list.h>
23#include <sof/lib/memory.h>
24#include <sof/lib/cpu.h>
25
31#define __coherent __aligned(PLATFORM_DCACHE_ALIGN)
32
33/*
34 * The coherent API allows optimized access to memory by multiple cores, using
35 * cache, taking care about coherence. The intended use is to let cores acquire
36 * ownership of such shared objects, use them, and then release them, possibly
37 * to be re-acquired by other cores. Such shared objects must only be accessed
38 * via this API. It's designed to be primarily used with dynamically allocated
39 * objects because of their well-defined life span. It can also be used with
40 * objects from .data or .bss sections but greater care must be takenwith them
41 * to strictly follow the API flow.
42 *
43 * The API assumes, that in the beginning no core has cache lines associated
44 * with the memory area, used with it. That is true for dynamically allocated
45 * memory, because when such memory is freed, its cache is invalidated - as long
46 * as that memory was never accessed by other cores, except by using this API.
47 * The first call must be coherent_init(), which initializes the header. If the
48 * object will be used by multiple cores, next coherent_shared() must be called.
49 * After that to use that memory, coherent_acquire() must be called, which
50 * acquires ownership of the object and returns a cached address of the memory.
51 * After that the user can perform cached access to the memory. To release the
52 * memory, coherent_release() must be called. The only time when the memory is
53 * accessed using cache is between those two calls, so only when releasing the
54 * memory we have to write back and invalidate caches to make sure, that next
55 * time we acquire this memory, our uncached header access will not be
56 * overwritten! When memory is not needed any more, typically before freeing the
57 * memory, coherent_free() should be called.
58 *
59 * This structure needs to be embedded at the start of any container to ensure
60 * container object cache alignment and to minimise non cache access when
61 * acquiring ownership.
62 *
63 * This structure must not be accessed outside of these APIs.
64 * The shared flag is only set at coherent init and thereafter it's RO.
65 */
66
73struct coherent {
74 union {
75 struct {
76 struct k_spinlock lock;
77 k_spinlock_key_t key;
78 };
79#ifdef __ZEPHYR__
80 struct k_mutex mutex;
81#endif
82 };
83 uint8_t sleep_allowed;
84 uint8_t shared;
85 uint16_t core;
86 struct list_item list;
88
89#if CONFIG_INCOHERENT
90# ifdef __ZEPHYR__
91BUILD_ASSERT(sizeof(struct coherent) <= DCACHE_LINE_SIZE, "DCACHE_LINE_SIZE too small");
92# else
93STATIC_ASSERT(sizeof(struct coherent) <= DCACHE_LINE_SIZE, DCACHE_LINE_SIZE_too_small);
94# endif
95#endif
96
97/* debug address aliases */
98#ifdef COHERENT_CHECK_ALIAS
99#define ADDR_IS_INCOHERENT(_c) assert(!is_uncached(_c))
100#define ADDR_IS_COHERENT(_c) assert(is_uncached(_c))
101#else
102#define ADDR_IS_INCOHERENT(_c)
103#define ADDR_IS_COHERENT(_c)
104#endif
105
106/* debug sharing amongst cores */
107#ifdef COHERENT_CHECK_NONSHARED_CORES
108
109#define CORE_CHECK_STRUCT_FIELD uint32_t __core; bool __is_shared
110#define CORE_CHECK_STRUCT_INIT(_c, is_shared) { (_c)->__core = cpu_get_id(); \
111 (_c)->__is_shared = is_shared; }
112#define CORE_CHECK_STRUCT(_c) { assert(!!(_c)->__is_shared == !!is_uncached(_c)); \
113 assert(cpu_get_id() == (_c)->__core || (_c)->__is_shared); }
114
115#define CHECK_COHERENT_CORE(_c) assert((_c)->core == cpu_get_id())
116
117#else
118
119#define CORE_CHECK_STRUCT_FIELD
120#define CORE_CHECK_STRUCT_INIT(_c, is_shared)
121#define CORE_CHECK_STRUCT(_c)
122
123#define CHECK_COHERENT_CORE(_c)
124
125#endif
126
127#ifdef __ZEPHYR__
128#define CHECK_ISR() __ASSERT(!k_is_in_isr(), "Attempt to sleep in ISR!")
129#define CHECK_SLEEP(_c) __ASSERT((_c)->sleep_allowed, \
130 "This context hasn't been initialized for sleeping!")
131#define CHECK_ATOMIC(_c) __ASSERT(!(_c)->sleep_allowed, \
132 "This context has been initialized for sleeping!")
133#else
134#define CHECK_ISR() assert(!k_is_in_isr())
135#define CHECK_SLEEP(_c) assert((_c)->sleep_allowed)
136#define CHECK_ATOMIC(_c) assert(!(_c)->sleep_allowed)
137#endif
138
139#if CONFIG_INCOHERENT
151__must_check static inline struct coherent __sparse_cache *coherent_acquire(struct coherent *c,
152 const size_t size)
153{
154 struct coherent __sparse_cache *cc = uncache_to_cache(c);
155
156 /* assert if someone passes a cache/local address in here. */
158 CHECK_ATOMIC(c);
159
160 /* access the shared coherent object */
161 if (c->shared) {
163
164 c->key = k_spin_lock(&c->lock);
165
166 /*
167 * FIXME: This is wrong. dcache_invalidate_region() only makes
168 * sense if we assume, that dirty cache lines might exist for
169 * this object. But in that case those lines could be written
170 * back here thus overwriting either user data, or the coherent
171 * header or both. When coherent_acquire() is called it must be
172 * guaranteed that the object isn't in cache. Before it is
173 * acquired no cached access to it is allowed. This has to be
174 * fixed here and on multiple further occasions below.
175 */
176
177 /* invalidate local copy */
178 dcache_invalidate_region(cc, size);
179 }
180
181 /* client can now use cached object safely */
182 return cc;
183}
184
194static inline void coherent_release(struct coherent __sparse_cache *c,
195 const size_t size)
196{
197 /* assert if someone passes a coherent address in here. */
199 CHECK_ATOMIC(c);
200
201 /* access the local copy of object */
202 if (c->shared) {
203 struct coherent *uc = cache_to_uncache(c);
205
206 /* wtb and inv local data to coherent object */
208
209 /* unlock on uncache alias */
210 k_spin_unlock(&uc->lock, uc->key);
211 }
212}
213
224static inline void *__coherent_init(size_t offset, const size_t size)
225{
226 /*
227 * Allocate an object with an uncached alias but align size on a cache-
228 * line boundary to avoid sharing a cache line with the adjacent
229 * allocation
230 */
232 ALIGN_UP(size, PLATFORM_DCACHE_ALIGN));
233 struct coherent *c;
234
235 if (!object)
236 return NULL;
237
238 c = (struct coherent *)((uint8_t *)object + offset);
239
240 /* TODO static assert if we are not cache aligned */
242 c->sleep_allowed = false;
243 c->shared = false;
244 c->core = cpu_get_id();
245 list_init(&c->list);
246 /* inv local data to coherent object */
247 dcache_invalidate_region(uncache_to_cache(object), size);
248
249 return object;
250}
251
260#define coherent_init(type, member) __coherent_init(offsetof(type, member), \
261 sizeof(type))
262
272static inline void __coherent_shared(struct coherent *c, const size_t size)
273{
274 /* assert if someone passes a cache/local address in here. */
276 CHECK_ATOMIC(c);
277
278 c->key = k_spin_lock(&c->lock);
279 c->shared = true;
280 dcache_invalidate_region(uncache_to_cache(c), size);
281 k_spin_unlock(&c->lock, c->key);
282}
283
292#define coherent_shared(object, member) __coherent_shared(&(object)->member, \
293 sizeof(*object))
294
295#ifdef __ZEPHYR__
296
307__must_check static inline struct coherent __sparse_cache *coherent_acquire_thread(
308 struct coherent *c, const size_t size)
309{
310 struct coherent __sparse_cache *cc = uncache_to_cache(c);
311
312 /* assert if someone passes a cache/local address in here. */
314 CHECK_SLEEP(c);
315 CHECK_ISR();
316
317 /* access the shared coherent object */
318 if (c->shared) {
320
321 k_mutex_lock(&c->mutex, K_FOREVER);
322
323 /* invalidate local copy */
324 dcache_invalidate_region(cc, size);
325 }
326
327 /* client can now use cached object safely */
328 return cc;
329}
330
340static inline void coherent_release_thread(struct coherent __sparse_cache *c,
341 const size_t size)
342{
343 /* assert if someone passes a coherent address in here. */
345 CHECK_SLEEP(c);
346 CHECK_ISR();
347
348 /* access the local copy of object */
349 if (c->shared) {
350 struct coherent *uc = cache_to_uncache(c);
352
353 /* wtb and inv local data to coherent object */
355
356 /* unlock on uncache alias */
357 k_mutex_unlock(&uc->mutex);
358 }
359}
360
371static inline void *__coherent_init_thread(size_t offset, const size_t size)
372{
373 /* As above - prevent cache line sharing */
375 ALIGN_UP(size, PLATFORM_DCACHE_ALIGN));
376 struct coherent *c;
377
378 if (!object)
379 return NULL;
380
381 c = (struct coherent *)((uint8_t *)object + offset);
382
383 /* TODO static assert if we are not cache aligned */
384 k_mutex_init(&c->mutex);
385 c->sleep_allowed = true;
386 c->shared = false;
387 c->core = cpu_get_id();
388 list_init(&c->list);
389 /* inv local data to coherent object */
390 dcache_invalidate_region(uncache_to_cache(object), size);
391
392 return object;
393}
394
403#define coherent_init_thread(type, member) __coherent_init_thread(offsetof(type, member), \
404 sizeof(type))
405
415static inline void __coherent_shared_thread(struct coherent *c, const size_t size)
416{
417 /* assert if someone passes a cache/local address in here. */
419 CHECK_SLEEP(c);
420 CHECK_ISR();
421
422 k_mutex_lock(&c->mutex, K_FOREVER);
423 c->shared = true;
424 dcache_invalidate_region(uncache_to_cache(c), size);
425 k_mutex_unlock(&c->mutex);
426}
427
436#define coherent_shared_thread(object, member) __coherent_shared_thread(&(object)->member, \
437 sizeof(*object))
438
439#endif /* __ZEPHYR__ */
440
449#define coherent_free(object, member) \
450 do { \
451 /* assert if someone passes a cache address in here. */ \
452 ADDR_IS_COHERENT(object); \
453 /* wtb and inv local data to coherent object */ \
454 dcache_writeback_invalidate_region(uncache_to_cache(object), \
455 sizeof(*object)); \
456 rfree(object); \
457 } while (0)
458
459#else
460
461/*
462 * Coherent devices only require locking to manage shared access.
463 */
464__must_check static inline struct coherent __sparse_cache *coherent_acquire(struct coherent *c,
465 const size_t size)
466{
467 if (c->shared)
468 c->key = k_spin_lock(&c->lock);
469
470 return (__sparse_force struct coherent __sparse_cache *)c;
471}
472
473static inline void coherent_release(struct coherent __sparse_cache *c,
474 const size_t size)
475{
476 if (c->shared)
477 k_spin_unlock(&c->lock, c->key);
478}
479
480static inline void *__coherent_init(size_t offset, const size_t size)
481{
482 /* As in CONFIG_INCOHERENT case - prevent cache line sharing */
484 ALIGN_UP(size, PLATFORM_DCACHE_ALIGN));
485 struct coherent *c;
486
487 if (!object)
488 return NULL;
489
490 c = (struct coherent *)((uint8_t *)object + offset);
491
492 /* TODO static assert if we are not cache aligned */
494 c->shared = 0;
495 c->core = cpu_get_id();
496 list_init(&c->list);
497
498 return object;
499}
500
501#define coherent_init(type, member) __coherent_init(offsetof(type, member), \
502 sizeof(type))
503
504#define coherent_free(object, member) rfree(object)
505
506static inline void __coherent_shared(struct coherent *c, const size_t size)
507{
508 c->key = k_spin_lock(&c->lock);
509 c->shared = true;
510 k_spin_unlock(&c->lock, c->key);
511}
512
513#define coherent_shared(object, member) __coherent_shared(&(object)->member, \
514 sizeof(*object))
515
516#ifdef __ZEPHYR__
517__must_check static inline struct coherent __sparse_cache *coherent_acquire_thread(
518 struct coherent *c, const size_t size)
519{
520 if (c->shared)
521 k_mutex_lock(&c->mutex, K_FOREVER);
522
523 return (__sparse_force struct coherent __sparse_cache *)c;
524}
525
526static inline void coherent_release_thread(struct coherent __sparse_cache *c,
527 const size_t size)
528{
529 if (c->shared)
530 k_mutex_unlock(&c->mutex);
531}
532
533static inline void *__coherent_init_thread(size_t offset, const size_t size)
534{
535 /* As above - prevent cache line sharing */
537 ALIGN_UP(size, PLATFORM_DCACHE_ALIGN));
538 struct coherent *c;
539
540 if (!object)
541 return NULL;
542
543 c = (struct coherent *)((uint8_t *)object + offset);
544
545 /* TODO static assert if we are not cache aligned */
546 k_mutex_init(&c->mutex);
547 c->shared = 0;
548 c->core = cpu_get_id();
549 list_init(&c->list);
550
551 return object;
552}
553
554#define coherent_init_thread(type, member) __coherent_init_thread(offsetof(type, member), \
555 sizeof(type))
556
557static inline void __coherent_shared_thread(struct coherent *c, const size_t size)
558{
559 k_mutex_lock(&c->mutex, K_FOREVER);
560 c->shared = true;
561 k_mutex_unlock(&c->mutex);
562}
563
564#define coherent_shared_thread(object, member) __coherent_shared_thread(&(object)->member, \
565 sizeof(*object))
566#endif /* __ZEPHYR__ */
567
568#endif /* CONFIG_INCOHERENT */
569
570#ifndef __ZEPHYR__
571#define coherent_acquire_thread coherent_acquire
572#define coherent_release_thread coherent_release
573#define coherent_init_thread coherent_init
574#define coherent_shared_thread coherent_shared
575#endif
576
585#define coherent_free_thread coherent_free
586
593#define is_coherent_shared(object, member) ((object)->member.shared)
594
597#endif
static void dcache_writeback_invalidate_region(void *addr, size_t size)
Write back and invalidate data cache lines in the specified memory region.
Definition cache.h:95
static void dcache_invalidate_region(void *addr, size_t size)
Invalidate data cache lines in the specified memory region.
Definition cache.h:67
#define STATIC_ASSERT(COND, MESSAGE)
Definition common.h:156
#define ALIGN_UP(size, alignment)
Definition common.h:93
#define __must_check
Definition common.h:15
CPU header file.
int cpu_get_id(void)
#define CHECK_ISR()
Definition coherent.h:128
static void __coherent_shared(struct coherent *c, const size_t size)
Internal helper to mark an allocated coherent object as actively shared.
Definition coherent.h:272
#define ADDR_IS_COHERENT(_c)
Definition coherent.h:103
#define CHECK_COHERENT_CORE(_c)
Definition coherent.h:123
static struct coherent * coherent_acquire(struct coherent *c, const size_t size)
Acquire exclusive access to a cache-coherent shared object in atomic/spinlock context.
Definition coherent.h:151
void * rzalloc(uint32_t flags, size_t bytes)
Allocate zero-initialized memory block with default natural alignment.
static void * __coherent_init(size_t offset, const size_t size)
Internal helper to allocate and initialize a coherent shared object.
Definition coherent.h:224
#define SOF_MEM_FLAG_USER
Base allocation flag denoting memory allocated for user-space threads and module execution.
Definition alloc.h:69
static void * __coherent_init_thread(size_t offset, const size_t size)
Internal helper to allocate and initialize a thread-capable coherent shared object.
Definition coherent.h:371
static struct coherent * coherent_acquire_thread(struct coherent *c, const size_t size)
Acquire exclusive access to a cache-coherent shared object in thread/mutex context.
Definition coherent.h:307
static void coherent_release(struct coherent *c, const size_t size)
Release exclusive access to a cache-coherent shared object in atomic/spinlock context.
Definition coherent.h:194
static void __coherent_shared_thread(struct coherent *c, const size_t size)
Internal helper to mark a thread-capable coherent object as actively shared.
Definition coherent.h:415
#define CHECK_SLEEP(_c)
Definition coherent.h:129
static void coherent_release_thread(struct coherent *c, const size_t size)
Release exclusive access to a cache-coherent shared object in thread/mutex context.
Definition coherent.h:340
#define SOF_MEM_FLAG_COHERENT
Allocate uncached / coherent memory address.
Definition alloc.h:53
#define ADDR_IS_INCOHERENT(_c)
Definition coherent.h:102
#define __coherent
Cache line alignment specifier for coherent shared memory objects.
Definition coherent.h:31
#define CHECK_ATOMIC(_c)
Definition coherent.h:131
static void list_init(struct list_item *list)
Definition list.h:25
Memory attributes, cold/hot execution paths, and platform memory inclusions.
RTOS spinlock primitives and initialization.
static void k_spinlock_init(struct k_spinlock *lock)
Initialize a spinlock structure to the unlocked state.
Definition spinlock.h:25
Header embedded at start of multi-core shared memory objects for cache coherence.
Definition coherent.h:73
k_spinlock_key_t key
Spinlock key storing interrupt flags.
Definition coherent.h:77
uint16_t core
Owner core ID when object is not shared.
Definition coherent.h:85
struct k_spinlock lock
Spinlock synchronizing multi-core access.
Definition coherent.h:76
struct k_mutex mutex
Mutex synchronizing thread-level access when sleeping is allowed.
Definition coherent.h:80
uint8_t shared
Non-zero if object is shared across non-coherent cores.
Definition coherent.h:84
uint8_t sleep_allowed
Non-zero if context allows sleeping/blocking mutex operations.
Definition coherent.h:83
struct list_item list
Node for coherent tracking list iteration.
Definition coherent.h:86
Definition list.h:16