/* SPDX-License-Identifier: BSD-2-Clause */ /** * @file * * @ingroup RTEMSImplClassicScheduler * * @brief This source file contains the implementation of * rtems_scheduler_remove_processor(). */ /* * Copyright (c) 2016 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #if defined(RTEMS_SMP) typedef struct { const Scheduler_Control *scheduler; rtems_status_code status; } Scheduler_Processor_removal_context; static bool _Scheduler_Check_processor_not_required( Thread_Control *the_thread, void *arg ) { Scheduler_Processor_removal_context *iter_context; Thread_queue_Context queue_context; ISR_lock_Context state_context; if ( the_thread->is_idle ) { return false; } iter_context = arg; _Thread_queue_Context_initialize( &queue_context ); _Thread_Wait_acquire( the_thread, &queue_context ); _Thread_State_acquire_critical( the_thread, &state_context ); if ( _Thread_Scheduler_get_home( the_thread ) == iter_context->scheduler && !_Processor_mask_Has_overlap( &the_thread->Scheduler.Affinity, _Scheduler_Get_processors( iter_context->scheduler ) ) ) { iter_context->status = RTEMS_RESOURCE_IN_USE; } _Thread_State_release_critical( the_thread, &state_context ); _Thread_Wait_release( the_thread, &queue_context ); return iter_context->status != RTEMS_SUCCESSFUL; } static bool _Scheduler_Check_no_helping( Thread_Control *the_thread, void *arg ) { Scheduler_Processor_removal_context *iter_context; ISR_lock_Context lock_context; const Chain_Node *node; const Chain_Node *tail; if ( the_thread->is_idle ) { return false; } iter_context = arg; _Thread_State_acquire( the_thread, &lock_context ); node = _Chain_Immutable_first( &the_thread->Scheduler.Scheduler_nodes ); tail = _Chain_Immutable_tail( &the_thread->Scheduler.Scheduler_nodes ); do { const Scheduler_Node *scheduler_node; const Scheduler_Control *scheduler; scheduler_node = SCHEDULER_NODE_OF_THREAD_SCHEDULER_NODE( node ); scheduler = _Scheduler_Node_get_scheduler( scheduler_node ); if ( scheduler == iter_context->scheduler ) { iter_context->status = RTEMS_RESOURCE_IN_USE; break; } node = _Chain_Immutable_next( node ); } while ( node != tail ); _Thread_State_release( the_thread, &lock_context ); return iter_context->status != RTEMS_SUCCESSFUL; } #endif rtems_status_code rtems_scheduler_remove_processor( rtems_id scheduler_id, uint32_t cpu_index ) { const Scheduler_Control *scheduler; #if defined(RTEMS_SMP) Scheduler_Processor_removal_context iter_context; ISR_lock_Context lock_context; Scheduler_Context *scheduler_context; Per_CPU_Control *cpu; Per_CPU_Control *cpu_self; #endif scheduler = _Scheduler_Get_by_id( scheduler_id ); if ( scheduler == NULL ) { return RTEMS_INVALID_ID; } if ( cpu_index >= rtems_configuration_get_maximum_processors() ) { return RTEMS_INVALID_NUMBER; } #if defined(RTEMS_SMP) iter_context.scheduler = scheduler; iter_context.status = RTEMS_SUCCESSFUL; scheduler_context = _Scheduler_Get_context( scheduler ); cpu = _Per_CPU_Get_by_index( cpu_index ); _Objects_Allocator_lock(); if ( cpu->Scheduler.control != scheduler ) { _Objects_Allocator_unlock(); return RTEMS_INVALID_NUMBER; } /* * This prevents the selection of this scheduler instance by new threads in * case the processor count changes to zero. */ _ISR_lock_ISR_disable( &lock_context ); _Scheduler_Acquire_critical( scheduler, &lock_context ); _Processor_mask_Clear( &scheduler_context->Processors, cpu_index ); _Scheduler_Release_critical( scheduler, &lock_context ); _ISR_lock_ISR_enable( &lock_context ); _Thread_Iterate( _Scheduler_Check_processor_not_required, &iter_context ); if ( _Processor_mask_Is_zero( &scheduler_context->Processors ) && iter_context.status == RTEMS_SUCCESSFUL ) { _Thread_Iterate( _Scheduler_Check_no_helping, &iter_context ); } _ISR_lock_ISR_disable( &lock_context ); _Scheduler_Acquire_critical( scheduler, &lock_context ); if ( iter_context.status == RTEMS_SUCCESSFUL ) { Thread_Control *idle; Scheduler_Node *scheduler_node; cpu->Scheduler.control = NULL; cpu->Scheduler.context = NULL; idle = ( *scheduler->Operations.remove_processor )( scheduler, cpu ); cpu->Scheduler.idle_if_online_and_unused = idle; scheduler_node = _Thread_Scheduler_get_home_node( idle ); _Priority_Plain_extract( &scheduler_node->Wait.Priority, &idle->Real_priority ); _Assert( _Priority_Is_empty( &scheduler_node->Wait.Priority ) ); _Chain_Extract_unprotected( &scheduler_node->Thread.Wait_node ); _Assert( _Chain_Is_empty( &idle->Scheduler.Wait_nodes ) ); _Chain_Extract_unprotected( &scheduler_node->Thread.Scheduler_node.Chain ); _Assert( _Chain_Is_empty( &idle->Scheduler.Scheduler_nodes ) ); } else { _Processor_mask_Set( &scheduler_context->Processors, cpu_index ); } cpu_self = _Thread_Dispatch_disable_critical( &lock_context ); _Scheduler_Release_critical( scheduler, &lock_context ); _ISR_lock_ISR_enable( &lock_context ); _Thread_Dispatch_direct( cpu_self ); _Objects_Allocator_unlock(); return iter_context.status; #else return RTEMS_RESOURCE_IN_USE; #endif }