# Copyright (c) 2024 Intel Corp.
# SPDX-License-Identifier: Apache-2.0
#
menu "SMP Options"

config SMP
	bool "Symmetric multiprocessing support"
	depends on USE_SWITCH
	help
	  When true, kernel will be built with SMP support, allowing
	  more than one CPU to schedule Zephyr tasks at a time.

config MP_MAX_NUM_CPUS
	int "Maximum number of CPUs/cores"
	default 1
	range 1 32
	help
	  Maximum number of multiprocessing-capable cores available to the
	  multicpu API and SMP features.

if SMP

config SMP_BOOT_DELAY
	bool "Delay booting secondary cores"
	depends on SMP
	help
	  By default Zephyr will boot all available CPUs during start up.
	  Select this option to skip this and allow custom code
	  (architecture/SoC/board/application) to boot secondary CPUs at
	  a later time.

config SCHED_IPI_SUPPORTED
	bool
	select EVENTS
	help
	  True if the architecture supports a call to arch_sched_broadcast_ipi()
	  to broadcast an interrupt that will call z_sched_ipi() on other CPUs
	  in the system.  Required for k_thread_abort() to operate with
	  reasonable latency (otherwise we might have to wait for the other
	  thread to take an interrupt, which can be arbitrarily far in the
	  future).

config SCHED_IPI_CASCADE
	bool "Use cascading IPIs to correct localized scheduling"
	depends on SCHED_CPU_MASK && !SCHED_CPU_MASK_PIN_ONLY
	default n
	help
	  Threads that are preempted by a local thread (a thread that is
	  restricted by its CPU mask to execute on a subset of all CPUs) may
	  trigger additional IPIs when the preempted thread is of higher
	  priority than a currently executing thread on another CPU. Although
	  these cascading IPIs will ensure that the system will settle upon a
	  valid set of high priority threads, it comes at a performance cost.

config SCHED_CPU_MASK
	bool "CPU mask affinity/pinning API"
	help
	  When true, the application will have access to the
	  k_thread_cpu_mask_*() APIs which control per-CPU affinity masks in
	  SMP mode, allowing applications to pin threads to specific CPUs or
	  disallow threads from running on given CPUs.

	  The API is supported with all three scheduler backends.  The
	  performance characteristics of the mask-aware "best thread" search
	  differ per backend:

	  SCHED_SIMPLE (default): The run queue is a sorted linked list.
	  Finding the best runnable thread for the current CPU requires a
	  linear scan of the list until a thread whose cpu_mask includes the
	  current CPU is found.  This is O(N) in the number of runnable
	  threads.  Because the list is priority-ordered the scan terminates
	  as soon as the first eligible thread is found, so in practice the
	  cost is proportional to the number of higher-priority threads that
	  are pinned away from the current CPU.

	  SCHED_SCALABLE: The run queue is a red/black tree ordered by
	  priority.  The mask-aware search performs an in-order tree walk
	  and returns the first thread whose cpu_mask matches.  The walk is
	  O(N) in the worst case but terminates early once a matching thread
	  is found.  Because the tree is already sorted by priority the walk
	  typically visits only a small number of nodes.  The additional
	  ~2 kB of code required by the rbtree is shared with WAITQ_SCALABLE
	  when both are enabled.

	  SCHED_MULTIQ: The run queue is an array of per-priority doubly
	  linked lists together with a bitmap of non-empty levels.  The
	  mask-aware search iterates over non-empty priority levels from
	  highest to lowest using the bitmap (O(P) where P is the number of
	  distinct priorities in use) and within each level scans the thread
	  list for the first thread whose cpu_mask includes the current CPU.
	  In the common case where affinity-constrained threads are sparse,
	  the search is fast; in the pathological case where many threads at
	  the same high priority are all pinned away from the current CPU
	  the inner list scan becomes O(N).  Note that SCHED_MULTIQ is
	  incompatible with SCHED_DEADLINE.

	  In all three cases the cpu_mask check adds overhead that is absent
	  without this option, so it should only be enabled when CPU affinity
	  is actually needed.

config SCHED_CPU_MASK_PIN_ONLY
	bool "CPU mask variant with single-CPU pinning only"
	depends on SCHED_CPU_MASK
	help
	  When true, enables a variant of SCHED_CPU_MASK where only
	  one CPU may be specified for every thread.  Effectively, all
	  threads have a single "assigned" CPU and they will never be
	  scheduled symmetrically.  In general this is not helpful,
	  but some applications have a carefully designed threading
	  architecture and want to make their own decisions about how
	  to assign work to CPUs.  In that circumstance, some moderate
	  optimizations can be made (e.g. having a separate run queue
	  per CPU, keeping the list length shorter). When selected,
	  the CPU mask becomes an immutable thread attribute. It can
	  only be modified before a thread is started.  Most
	  applications don't want this.

config TRACE_SCHED_IPI
	bool "Test IPI"
	help
	  When true, it will add a hook into z_sched_ipi(), in order
	  to check if schedule IPI has called or not, for testing
	  purpose.
	depends on SCHED_IPI_SUPPORTED
	depends on MP_MAX_NUM_CPUS>1

config IPI_OPTIMIZE
	bool "Optimize IPI delivery"
	default n
	depends on SCHED_IPI_SUPPORTED && MP_MAX_NUM_CPUS>1
	help
	  When selected, the kernel will attempt to determine the minimum
	  set of CPUs that need an IPI to trigger a reschedule in response to
	  a thread newly made ready for execution. This increases the
	  computation required at every scheduler operation by a value that is
	  O(N) in the number of CPUs, and in exchange reduces the number of
	  interrupts delivered. Which to choose is going to depend on
	  application behavior. If the architecture also supports directing
	  IPIs to specific CPUs then this has the potential to significantly
	  reduce the number of IPIs (and consequently ISRs) processed by the
	  system as the number of CPUs increases. If not, the only benefit
	  would be to not issue any IPIs if the newly readied thread is of
	  lower priority than all the threads currently executing on other CPUs.

config KERNEL_COHERENCE
	bool "Place all shared data into coherent memory"
	depends on CACHE_CAN_SAY_MEM_COHERENCE
	default y if SMP && MP_MAX_NUM_CPUS > 1
	select THREAD_STACK_INFO
	help
	  When available and selected, the kernel will build in a mode
	  where all shared data is placed in multiprocessor-coherent
	  (generally "uncached") memory.  Thread stacks will remain
	  cached, as will application memory declared with
	  __incoherent.  This is intended for Zephyr SMP kernels
	  running on cache-incoherent architectures only.  Note that
	  when this is selected, there is an implicit API change that
	  assumes cache coherence to any memory passed to the kernel.
	  Code that creates kernel data structures in uncached regions
	  may fail strangely.  Some assertions exist to catch these
	  mistakes, but not all circumstances can be tested.

config TICKET_SPINLOCKS
	bool "Ticket spinlocks for lock acquisition fairness [EXPERIMENTAL]"
	select EXPERIMENTAL
	help
	  Basic spinlock implementation is based on single
	  atomic variable and doesn't guarantee locking fairness
	  across multiple CPUs. It's even possible that single CPU
	  will win the contention every time which will result
	  in a live-lock.
	  Ticket spinlocks provide a FIFO order of lock acquisition
	  which resolves such unfairness issue at the cost of slightly
	  increased memory footprint.
endif
endmenu
