Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
rts-cpu-isolator.h 4.89 KiB
#ifndef RTS_CPU_ISOLATOR_H
#define RTS_CPU_ISOLATOR_H

#include <linux/version.h>
#include <linux/cpu.h>

//
// This is the function pointer type of the kernel's play_dead()
// function that we overwrite in order to inject LIGO's "real-time" 
// module code. It should match the play_dead() prototype in:
// https://elixir.bootlin.com/linux/v5.10/source/arch/x86/include/asm/smp.h
//
typedef void (*play_dead_handler_fp_t)( void );




/// @deprecated
/// @brief Proxy function for the is_cpu_occupied() function above.
///        This function is retained for compatibility with legacy code.
///
/// @param cpu The CPU number the caller is checking (0 indexed)
///
/// @return 0 if the core has been taken, >0 if it is free
///
extern int  is_cpu_taken_by_rcg_model( unsigned int cpu );

/// @deprecated
/// @brief Proxy function for the set_rt_callback() function above.
///        This function is retained for compatibility with legacy code.
///
/// @param rt_runner_func A function pointer to the "real-time" code
///                       you would like to run
///
/// @param cpu     The CPU number to isolate the code on (0 indexed)
///
extern void set_fe_code_idle( play_dead_handler_fp_t rt_runner_func, unsigned int cpu );


///
/// @brief Instead of calling CPU up/down directly, new real-time module
///        implementers should use the rts_isolator_exec() and 
///        rts_isolator_cleanup() functions defined here, and this header
///        will track any changes needed for differing kernel versions.
///
///        All CPU_IDs are 0 indexed.
///
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5,6,0) 
static inline int rts_isolator_exec( unsigned int CPU_ID ){ return cpu_down(CPU_ID); }
static inline int rts_isolator_cleanup( unsigned int CPU_ID ){ return cpu_up(CPU_ID); }
static inline int is_cpu_occupied( unsigned int cpu ) { return is_cpu_taken_by_rcg_model(cpu) };
static inline void set_rt_callback( play_dead_handler_fp_t rt_runner_func, unsigned int cpu ) { return set_fe_code_idle(rt_runner_func, cpu)};
#else //This is 5.10.0 and newer, caller interface is the same between versions 
static inline int rts_isolator_exec( unsigned int CPU_ID ){ return remove_cpu(CPU_ID); }
static inline int rts_isolator_cleanup( unsigned int CPU_ID ){ return add_cpu(CPU_ID); }

///
/// @brief This utility function is used to check if a module
///        has already used this interface to isolate the given CPU.
///
/// @param cpu The CPU number the caller is checking (0 indexed)
///
/// @return 0 if the core has been taken, >0 if it is free
///
extern int is_cpu_occupied( unsigned int cpu );

///
/// @brief This function is used to set the function that is
///        called when the "real-time" execution is started.
///        It must be set before calling rts_isolator_exec()
///
/// @param rt_runner_func A function pointer to the "real-time" code
///                       you would like to run
///
/// @param cpu     The CPU number to isolate the code on (0 indexed)
///
extern void set_rt_callback( play_dead_handler_fp_t rt_runner_func, unsigned int cpu );
#endif


/// Other Notes
///
///
/// The general usage for getting your module to run in "real-time"
/// mode with this driver is:
///
/// == In your module init ==
///
/// 1. Check if the core you are planning to use is free with:
///    if( is_cpu_taken_by_rcg_model( CPU_ID ) == 0 ) ERROR;
///
/// 2. Set your "real-time" module code function pointer:
///    set_fe_code_idle( rt_runner_func, CPU_ID );
///    msleep( 100 ); // Wait for a bit
///
/// 3. Isolate the core and run the "real-time" code. We bring the
///    CPU "down" so the callback we set with set_fe_code_idle() is run:
///    rts_isolator_exec( CPU_ID ); //This is usally the last thing done
///                                 //in the module_init() function
/// == End module init ==
///
/// 4. Allow your "real-time" module to run and do its job. 
///
/// 5. When you want to stop the "real-time" module and bring the CPU back.
///    First signal your "real-time" module to exit. This is usually done 
///    through the use of a global varable that your module_exit() function
///    and your "real-time" runner function (rt_runner_func above) can both
///    see. Second call the rts_isolator_cleanup() function. Ex:
///
///    void rt_user_mod_exit( void ) {
///      g_stop_rt_function = 1; //The rt_runner_func is checking this 
///                                each iteration to see if it should exit
///      msleep( 1000 ); //Wait for some time to let it exit,
///                      //or use another var to signal an exit
///      set_fe_code_idle( 0, CPU_ID ); //Unset the injected handler, code will
///                                       use linux default CPU shutdown if 0
///      msleep( 1000 ); //Wait for some time
///      rts_isolator_cleanup( CPU_ID ); //Call the cleanup function
///      msleep( 1000 ); //Wait for some more time
///    }
///    module_exit( rt_user_mod_exit );
///    

#endif // RTS_CPU_ISOLATOR_H