summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--c/src/lib/libbsp/sparc/leon3/include/cache_.h2
-rw-r--r--c/src/lib/libcpu/shared/src/cache_manager.c258
-rw-r--r--cpukit/rtems/include/rtems/rtems/cache.h82
-rw-r--r--cpukit/score/include/rtems/score/smpimpl.h19
4 files changed, 355 insertions, 6 deletions
diff --git a/c/src/lib/libbsp/sparc/leon3/include/cache_.h b/c/src/lib/libbsp/sparc/leon3/include/cache_.h
index c7813678b9..ced5b6dd0a 100644
--- a/c/src/lib/libbsp/sparc/leon3/include/cache_.h
+++ b/c/src/lib/libbsp/sparc/leon3/include/cache_.h
@@ -26,6 +26,8 @@ extern "C" {
#define CPU_CACHE_SUPPORT_PROVIDES_CACHE_SIZE_FUNCTIONS
+#define CPU_CACHE_NO_INSTRUCTION_CACHE_SNOOPING
+
#define CPU_INSTRUCTION_CACHE_ALIGNMENT 64
#define CPU_DATA_CACHE_ALIGNMENT 64
diff --git a/c/src/lib/libcpu/shared/src/cache_manager.c b/c/src/lib/libcpu/shared/src/cache_manager.c
index 420a01321f..7dd408fd0f 100644
--- a/c/src/lib/libcpu/shared/src/cache_manager.c
+++ b/c/src/lib/libcpu/shared/src/cache_manager.c
@@ -37,6 +37,214 @@
#include <rtems.h>
#include "cache_.h"
+#include <rtems/score/smpimpl.h>
+#include <rtems/score/smplock.h>
+#include <rtems/score/chainimpl.h>
+#include <rtems/score/sysstate.h>
+
+#if defined( RTEMS_SMP )
+
+typedef void (*Cache_manager_Function_ptr)(const void *d_addr, size_t n_bytes);
+
+typedef struct {
+ Chain_Node Node;
+ Cache_manager_Function_ptr func;
+ const void *addr;
+ size_t size;
+ cpu_set_t *recipients;
+ size_t setsize;
+ Atomic_Ulong done;
+} Cache_manager_SMP_node;
+
+typedef struct {
+ SMP_lock_Control Lock;
+ Chain_Control List;
+} Cache_manager_SMP_control;
+
+static Cache_manager_SMP_control _Cache_manager_SMP_control = {
+ .Lock = SMP_LOCK_INITIALIZER("cachemgr"),
+ .List = CHAIN_INITIALIZER_EMPTY(_Cache_manager_SMP_control.List)
+};
+
+void
+_SMP_Cache_manager_message_handler(void)
+{
+ SMP_lock_Context lock_context;
+ Cache_manager_SMP_node *node;
+ Cache_manager_SMP_node *next;
+ uint32_t cpu_self_idx;
+
+ _SMP_lock_ISR_disable_and_acquire( &_Cache_manager_SMP_control.Lock,
+ &lock_context );
+ cpu_self_idx = _SMP_Get_current_processor();
+
+ node = (Cache_manager_SMP_node*)_Chain_First(
+ &_Cache_manager_SMP_control.List );
+ while ( !_Chain_Is_tail( &_Cache_manager_SMP_control.List, &node->Node ) ) {
+ next = (Cache_manager_SMP_node*)_Chain_Next( &node->Node );
+ if ( CPU_ISSET_S ( cpu_self_idx, node->setsize, node->recipients ) ) {
+ CPU_CLR_S ( cpu_self_idx, node->setsize, node->recipients );
+
+ node->func( node->addr, node->size );
+
+ if ( CPU_COUNT_S( node->setsize, node->recipients ) == 0 ) {
+ _Chain_Extract_unprotected( &node->Node );
+ _Atomic_Store_ulong( &node->done, 1, ATOMIC_ORDER_RELEASE );
+ }
+ }
+ node = next;
+ }
+
+ _SMP_lock_Release_and_ISR_enable( &_Cache_manager_SMP_control.Lock,
+ &lock_context );
+}
+
+#if defined(CPU_DATA_CACHE_ALIGNMENT) || \
+ (defined(CPU_INSTRUCTION_CACHE_ALIGNMENT) && \
+ defined(CPU_CACHE_NO_INSTRUCTION_CACHE_SNOOPING))
+
+static void
+_Cache_manager_Process_cache_messages( void )
+{
+ unsigned long message;
+ Per_CPU_Control *cpu_self;
+ ISR_Level isr_level;
+
+ _ISR_Disable_without_giant( isr_level );
+
+ cpu_self = _Per_CPU_Get();
+
+ message = _Atomic_Load_ulong( &cpu_self->message, ATOMIC_ORDER_RELAXED );
+
+ if ( message & SMP_MESSAGE_CACHE_MANAGER ) {
+ if ( _Atomic_Compare_exchange_ulong( &cpu_self->message, &message,
+ message & ~SMP_MESSAGE_CACHE_MANAGER, ATOMIC_ORDER_RELAXED,
+ ATOMIC_ORDER_RELAXED ) ) {
+ _SMP_Cache_manager_message_handler();
+ }
+ }
+
+ _ISR_Enable_without_giant( isr_level );
+}
+
+/*
+ * We can not make this function static as we need to access it
+ * from the test program.
+ */
+void
+_Cache_manager_Send_smp_msg(
+ const size_t setsize,
+ const cpu_set_t *set,
+ Cache_manager_Function_ptr func,
+ const void * addr,
+ size_t size
+ );
+
+void
+_Cache_manager_Send_smp_msg(
+ const size_t setsize,
+ const cpu_set_t *set,
+ Cache_manager_Function_ptr func,
+ const void * addr,
+ size_t size
+ )
+{
+ uint32_t i;
+ Cache_manager_SMP_node node;
+ size_t set_size = CPU_ALLOC_SIZE( _SMP_Get_processor_count() );
+ char cpu_set_copy[set_size];
+ SMP_lock_Context lock_context;
+
+ if ( ! _System_state_Is_up( _System_state_Get() ) ) {
+ func( addr, size );
+ return;
+ }
+
+ memset( cpu_set_copy, 0, set_size );
+ if( set == NULL ) {
+ for( i=0; i<_SMP_Get_processor_count(); ++i )
+ CPU_SET_S( i, set_size, (cpu_set_t *)cpu_set_copy );
+ } else {
+ for( i=0; i<_SMP_Get_processor_count(); ++i )
+ if( CPU_ISSET_S( i, set_size, set ) )
+ CPU_SET_S( i, set_size, (cpu_set_t *)cpu_set_copy );
+ }
+
+ node.func = func;
+ node.addr = addr;
+ node.size = size;
+ node.setsize = set_size;
+ node.recipients = (cpu_set_t *)cpu_set_copy;
+ _Atomic_Store_ulong( &node.done, 0, ATOMIC_ORDER_RELAXED );
+
+
+ _SMP_lock_ISR_disable_and_acquire( &_Cache_manager_SMP_control.Lock,
+ &lock_context );
+ _Chain_Prepend_unprotected( &_Cache_manager_SMP_control.List, &node.Node );
+ _SMP_lock_Release_and_ISR_enable( &_Cache_manager_SMP_control.Lock,
+ &lock_context );
+
+ _SMP_Send_message_multicast( set_size, node.recipients,
+ SMP_MESSAGE_CACHE_MANAGER );
+
+ _Cache_manager_Process_cache_messages();
+
+ while ( !_Atomic_Load_uint( &node.done, ATOMIC_ORDER_ACQUIRE ) );
+}
+#endif
+
+void
+rtems_cache_flush_multiple_data_lines_processor_set(
+ const void *addr,
+ size_t size,
+ const size_t setsize,
+ const cpu_set_t *set
+)
+{
+#if defined(CPU_DATA_CACHE_ALIGNMENT)
+ _Cache_manager_Send_smp_msg( setsize, set,
+ rtems_cache_flush_multiple_data_lines, addr, size );
+#endif
+}
+
+void
+rtems_cache_invalidate_multiple_data_lines_processor_set(
+ const void *addr,
+ size_t size,
+ const size_t setsize,
+ const cpu_set_t *set
+)
+{
+#if defined(CPU_DATA_CACHE_ALIGNMENT)
+ _Cache_manager_Send_smp_msg( setsize, set,
+ rtems_cache_invalidate_multiple_data_lines, addr, size );
+#endif
+}
+
+void
+rtems_cache_flush_entire_data_processor_set(
+ const size_t setsize,
+ const cpu_set_t *set
+)
+{
+#if defined(CPU_DATA_CACHE_ALIGNMENT)
+ _Cache_manager_Send_smp_msg( setsize, set,
+ (Cache_manager_Function_ptr)rtems_cache_flush_entire_data, 0, 0 );
+#endif
+}
+
+void
+rtems_cache_invalidate_entire_data_processor_set(
+ const size_t setsize,
+ const cpu_set_t *set
+)
+{
+#if defined(CPU_DATA_CACHE_ALIGNMENT)
+ _Cache_manager_Send_smp_msg( setsize, set,
+ (Cache_manager_Function_ptr)rtems_cache_invalidate_entire_data, 0, 0 );
+#endif
+}
+#endif
/*
* THESE FUNCTIONS ONLY HAVE BODIES IF WE HAVE A DATA CACHE
@@ -219,18 +427,21 @@ rtems_cache_disable_data( void )
* THESE FUNCTIONS ONLY HAVE BODIES IF WE HAVE AN INSTRUCTION CACHE
*/
+
+
/*
* This function is responsible for performing an instruction cache
* invalidate. It must determine how many cache lines need to be invalidated
* and then perform the invalidations.
*/
-void
-rtems_cache_invalidate_multiple_instruction_lines( const void * i_addr, size_t n_bytes )
+
+#if !defined(CPU_CACHE_SUPPORT_PROVIDES_RANGE_FUNCTIONS)
+static void
+_invalidate_multiple_instruction_lines_no_range_functions(
+ const void * i_addr,
+ size_t n_bytes
+)
{
-#if defined(CPU_INSTRUCTION_CACHE_ALIGNMENT)
-#if defined(CPU_CACHE_SUPPORT_PROVIDES_RANGE_FUNCTIONS)
- _CPU_cache_invalidate_instruction_range( i_addr, n_bytes );
-#else
const void * final_address;
/*
@@ -249,6 +460,35 @@ rtems_cache_invalidate_multiple_instruction_lines( const void * i_addr, size_t n
_CPU_cache_invalidate_1_instruction_line( i_addr );
i_addr = (void *)((size_t)i_addr + CPU_INSTRUCTION_CACHE_ALIGNMENT);
}
+}
+#endif
+
+void
+rtems_cache_invalidate_multiple_instruction_lines(
+ const void * i_addr,
+ size_t n_bytes
+)
+{
+#if defined(CPU_INSTRUCTION_CACHE_ALIGNMENT)
+#if defined(CPU_CACHE_SUPPORT_PROVIDES_RANGE_FUNCTIONS)
+
+#if defined(RTEMS_SMP) && defined(CPU_CACHE_NO_INSTRUCTION_CACHE_SNOOPING)
+ _Cache_manager_Send_smp_msg( 0, 0, _CPU_cache_invalidate_instruction_range,
+ i_addr, n_bytes );
+#else
+ _CPU_cache_invalidate_instruction_range( i_addr, n_bytes );
+#endif
+
+#else
+
+#if defined(RTEMS_SMP) && defined(CPU_CACHE_NO_INSTRUCTION_CACHE_SNOOPING)
+ _Cache_manager_Send_smp_msg( 0, 0,
+ _invalidate_multiple_instruction_lines_no_range_functions, i_addr,
+ n_bytes );
+#else
+ _invalidate_multiple_instruction_lines_no_range_functions( i_addr, n_bytes );
+#endif
+
#endif
#endif
}
@@ -266,8 +506,14 @@ rtems_cache_invalidate_entire_instruction( void )
* Call the CPU-specific routine
*/
+#if defined(RTEMS_SMP) && defined(CPU_CACHE_NO_INSTRUCTION_CACHE_SNOOPING)
+ _Cache_manager_Send_smp_msg( 0, 0,
+ (Cache_manager_Function_ptr)_CPU_cache_invalidate_entire_instruction,
+ 0, 0 );
+#else
_CPU_cache_invalidate_entire_instruction();
#endif
+#endif
}
diff --git a/cpukit/rtems/include/rtems/rtems/cache.h b/cpukit/rtems/include/rtems/rtems/cache.h
index 05f661208e..ce399c65fe 100644
--- a/cpukit/rtems/include/rtems/rtems/cache.h
+++ b/cpukit/rtems/include/rtems/rtems/cache.h
@@ -113,6 +113,9 @@ void rtems_cache_invalidate_multiple_data_lines(
*
* The cache lines covering the area are marked as invalid. A later
* instruction fetch from the area will result in a load from memory.
+ * In SMP mode, on processors without instruction cache snooping, this
+ * operation will invalidate the instruction cache lines on all processors.
+ * It should not be called from interrupt context in such case.
*
* @param[in] addr The start address of the area to invalidate.
* @param[in] size The size in bytes of the area to invalidate.
@@ -188,6 +191,85 @@ void rtems_cache_disable_instruction( void );
*/
void *rtems_cache_aligned_malloc ( size_t nbytes );
+#if defined( RTEMS_SMP )
+
+/**
+ * @brief Flushes multiple data cache lines for a set of processors
+ *
+ * Dirty cache lines covering the area are transferred to memory.
+ * Depending on the cache implementation this may mark the lines as invalid.
+ *
+ * This operation should not be called from interrupt context.
+ *
+ * @param[in] addr The start address of the area to flush.
+ * @param[in] size The size in bytes of the area to flush.
+ * @param[in] setsize The size of the processor set.
+ * @param[in] set The target processor set.
+ */
+void rtems_cache_flush_multiple_data_lines_processor_set(
+ const void *addr,
+ size_t size,
+ const size_t setsize,
+ const cpu_set_t *set
+);
+
+/**
+ * @brief Invalidates multiple data cache lines for a set of processors
+ *
+ * The cache lines covering the area are marked as invalid. A later read
+ * access in the area will load the data from memory.
+ *
+ * In case the area is not aligned on cache line boundaries, then this
+ * operation may destroy unrelated data.
+ *
+ * This operation should not be called from interrupt context.
+ *
+ * @param[in] addr The start address of the area to invalidate.
+ * @param[in] size The size in bytes of the area to invalidate.
+ * @param[in] setsize The size of the processor set.
+ * @param[in] set The target processor set.
+ */
+void rtems_cache_invalidate_multiple_data_lines_processor_set(
+ const void *addr,
+ size_t size,
+ const size_t setsize,
+ const cpu_set_t *set
+);
+
+/**
+ * @brief Flushes the entire data cache for a set of processors
+ *
+ * This operation should not be called from interrupt context.
+ *
+ * @see rtems_cache_flush_multiple_data_lines().
+ *
+ * @param[in] setsize The size of the processor set.
+ * @param[in] set The target processor set.
+ */
+void rtems_cache_flush_entire_data_processor_set(
+ const size_t setsize,
+ const cpu_set_t *set
+);
+
+/**
+ * @brief Invalidates the entire cache for a set of processors
+ *
+ * This function is responsible for performing a data cache
+ * invalidate. It invalidates the entire cache for a set of
+ * processors.
+ *
+ * This operation should not be called from interrupt context.
+ *
+ * @param[in] setsize The size of the processor set.
+ * @param[in] set The target processor set.
+ */
+void rtems_cache_invalidate_entire_data_processor_set(
+ const size_t setsize,
+ const cpu_set_t *set
+);
+
+#endif
+
/**@}*/
#ifdef __cplusplus
diff --git a/cpukit/score/include/rtems/score/smpimpl.h b/cpukit/score/include/rtems/score/smpimpl.h
index cbc64280de..dca8a6bec8 100644
--- a/cpukit/score/include/rtems/score/smpimpl.h
+++ b/cpukit/score/include/rtems/score/smpimpl.h
@@ -21,6 +21,7 @@
#include <rtems/score/smp.h>
#include <rtems/score/percpu.h>
#include <rtems/fatal.h>
+#include <rtems/rtems/cache.h>
#ifdef __cplusplus
extern "C" {
@@ -51,6 +52,13 @@ extern "C" {
#define SMP_MESSAGE_TEST 0x2UL
/**
+ * @brief SMP message to request a cache manager invocation.
+ *
+ * @see _SMP_Send_message().
+ */
+#define SMP_MESSAGE_CACHE_MANAGER 0x4UL
+
+/**
* @brief SMP fatal codes.
*/
typedef enum {
@@ -127,6 +135,12 @@ static inline void _SMP_Set_test_message_handler(
}
/**
+ * @brief Handles cache invalidation/flush requests from a remote processor.
+ *
+ */
+void _SMP_Cache_manager_message_handler( void );
+
+/**
* @brief Interrupt handler for inter-processor interrupts.
*/
static inline void _SMP_Inter_processor_interrupt_handler( void )
@@ -148,6 +162,11 @@ static inline void _SMP_Inter_processor_interrupt_handler( void )
if ( ( message & SMP_MESSAGE_TEST ) != 0 ) {
( *_SMP_Test_message_handler )( cpu_self );
}
+
+ if ( ( message & SMP_MESSAGE_CACHE_MANAGER ) != 0 ) {
+ _SMP_Cache_manager_message_handler();
+ }
+
}
}