zircon/kernel/kernel/mp.cc
// Unplug a single CPU.  Must be called while holding the hotplug lock
Unplug a single CPU.  Must be called while holding the hotplug lock
Unplug a single CPU . Must be called while holding the hotplug lock
static zx_status_t mp_unplug_cpu_mask_single_locked(cpu_num_t cpu_id, zx_time_t deadline,
                                                    Thread** leaked_thread) {
  percpu& percpu_to_unplug = percpu::Get(cpu_id);
  Thread* thread = nullptr;
  auto cleanup_thread = fbl::MakeAutoCall([&thread, &leaked_thread, cpu_id]() {
    if (leaked_thread) {
      *leaked_thread = thread;
    } else if (thread != nullptr) {
      TRACEF("WARNING: leaking thread for cpu %u\n", cpu_id);
    }
  });
  zx_status_t status = percpu_to_unplug.dpc_queue.Shutdown(deadline);
  if (status != ZX_OK) {
    return status;
  }
   This thread is pinned to the target CPU and set to run with the
   highest priority.  This should cause it to pick up the thread
   immediately (or very soon, if for some reason there is another
   HIGHEST_PRIORITY task scheduled in between when we resume the
   thread and when the CPU is woken up).
  Event unplug_done;
  thread = Thread::CreateEtc(nullptr, "unplug_thread", nullptr, &unplug_done, HIGHEST_PRIORITY,
                             mp_unplug_trampoline);
  if (thread == nullptr) {
    return ZX_ERR_NO_MEMORY;
  }
  status = platform_mp_prep_cpu_unplug(cpu_id);
  if (status != ZX_OK) {
    return status;
  }
   Pin to the target CPU
  thread->SetCpuAffinity(cpu_num_to_mask(cpu_id));
  thread->SetDeadline({ZX_MSEC(9), ZX_MSEC(10), ZX_MSEC(10)});
  status = thread->DetachAndResume();
  if (status != ZX_OK) {
    return status;
  }
   Wait for the unplug thread to get scheduled on the target.
  status = unplug_done.WaitDeadline(deadline, Interruptible::No);
  if (status != ZX_OK) {
    return status;
  }
   Now that the cpu is no longer processing tasks, migrate
   |percpu_to_unplug|'s TimerQueue and DpcQueue to this cpu.
  percpu& current_percpu = *get_local_percpu();
  current_percpu.timer_queue.TransitionOffCpu(percpu_to_unplug.timer_queue);
  current_percpu.dpc_queue.TransitionOffCpu(percpu_to_unplug.dpc_queue);
  return platform_mp_cpu_unplug(cpu_id);
}
static zx_status_t mp_unplug_cpu_mask_single_locked ( cpu_num_t cpu_id , zx_time_t deadline , Thread * * leaked_thread ) { percpu & percpu_to_unplug = percpu :: Get ( cpu_id ) ; Thread * thread = nullptr ; auto cleanup_thread = fbl :: MakeAutoCall ( [ & thread , & leaked_thread , cpu_id ] ( ) { if ( leaked_thread ) { * leaked_thread = thread ; } else if ( thread != nullptr ) { TRACEF ( " \n " , cpu_id ) ; } } ) ; zx_status_t status = percpu_to_unplug . dpc_queue . Shutdown ( deadline ) ; if ( status != ZX_OK ) { return status ; } Event unplug_done ; thread = Thread :: CreateEtc ( nullptr , " " , nullptr , & unplug_done , HIGHEST_PRIORITY , mp_unplug_trampoline ) ; if ( thread == nullptr ) { return ZX_ERR_NO_MEMORY ; } status = platform_mp_prep_cpu_unplug ( cpu_id ) ; if ( status != ZX_OK ) { return status ; } thread -> SetCpuAffinity ( cpu_num_to_mask ( cpu_id ) ) ; thread -> SetDeadline ( { ZX_MSEC ( 9 ) , ZX_MSEC ( 10 ) , ZX_MSEC ( 10 ) } ) ; status = thread -> DetachAndResume ( ) ; if ( status != ZX_OK ) { return status ; } status = unplug_done . WaitDeadline ( deadline , Interruptible :: No ) ; if ( status != ZX_OK ) { return status ; } percpu & current_percpu = * get_local_percpu ( ) ; current_percpu . timer_queue . TransitionOffCpu ( percpu_to_unplug . timer_queue ) ; current_percpu . dpc_queue . TransitionOffCpu ( percpu_to_unplug . dpc_queue ) ; return platform_mp_cpu_unplug ( cpu_id ) ; }
Unplug a single CPU.
Unplug a single CPU .
// TODO(fxbug.dev/34447): Work around a race in thread cleanup by leaking the thread and stack // structure. Since we're only using this while turning off the system currently, it's not a big // problem leaking the thread structure and stack. // Wait for |percpu_to_unplug| to complete any in-progress DPCs and terminate its DPC thread. // Later, once nothing is running on it, we'll migrate its queued DPCs to another CPU. // TODO(maniscalco): |cpu_to_unplug| is about to shutdown.  We should ensure it has no pinned // threads (except maybe the idle thread).  Once we're confident we've terminated/migrated them // all, this would be a good place to DEBUG_ASSERT. // Create a thread for the unplug.  We will cause the target CPU to // context switch to this thread.  After this happens, it should no // longer be accessing system state and can be safely shut down. // // This thread is pinned to the target CPU and set to run with the // highest priority.  This should cause it to pick up the thread // immediately (or very soon, if for some reason there is another // HIGHEST_PRIORITY task scheduled in between when we resume the // thread and when the CPU is woken up). // Pin to the target CPU // Wait for the unplug thread to get scheduled on the target. // Now that the cpu is no longer processing tasks, migrate // |percpu_to_unplug|'s TimerQueue and DpcQueue to this cpu.