diff options
Diffstat (limited to 'include/iprt/mp.h')
-rw-r--r-- | include/iprt/mp.h | 521 |
1 files changed, 521 insertions, 0 deletions
diff --git a/include/iprt/mp.h b/include/iprt/mp.h new file mode 100644 index 00000000..2af10ad3 --- /dev/null +++ b/include/iprt/mp.h @@ -0,0 +1,521 @@ +/** @file + * IPRT - Multiprocessor. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef IPRT_INCLUDED_mp_h +#define IPRT_INCLUDED_mp_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/cdefs.h> +#include <iprt/types.h> + + +RT_C_DECLS_BEGIN + +/** @defgroup grp_rt_mp RTMp - Multiprocessor + * @ingroup grp_rt + * @{ + */ + +/** + * Gets the identifier of the CPU executing the call. + * + * When called from a system mode where scheduling is active, like ring-3 or + * kernel mode with interrupts enabled on some systems, no assumptions should + * be made about the current CPU when the call returns. + * + * @returns CPU Id. + */ +RTDECL(RTCPUID) RTMpCpuId(void); + +/** + * Get the CPU set index of the CPU executing the call. + * + * Same scheduling warnings as for RTMpCpuId(). + * + * @returns CPU set index. + */ +RTDECL(int) RTMpCurSetIndex(void); + +/** + * Get the CPU set index and identifier of the CPU executing the call. + * + * Same scheduling warnings as for RTMpCpuId(). + * + * @returns CPU set index. + * @param pidCpu Where to return the CPU identifier. (not optional) + */ +RTDECL(int) RTMpCurSetIndexAndId(PRTCPUID pidCpu); + +/** + * Converts a CPU identifier to a CPU set index. + * + * This may or may not validate the presence of the CPU. + * + * @returns The CPU set index on success, -1 on failure. + * @param idCpu The identifier of the CPU. + */ +RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu); + +/** + * Converts a CPU set index to a a CPU identifier. + * + * This may or may not validate the presence of the CPU, so, use + * RTMpIsCpuPossible for that. + * + * @returns The corresponding CPU identifier, NIL_RTCPUID on failure. + * @param iCpu The CPU set index. + */ +RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu); + +/** + * Translates an NT process group member to a CPU set index. + * + * @returns CPU set index, -1 if not valid. + * @param idxGroup The CPU group. + * @param idxMember The CPU group member number. + * + * @remarks Only available on Windows. + */ +RTDECL(int) RTMpSetIndexFromCpuGroupMember(uint32_t idxGroup, uint32_t idxMember); + +/** + * Gets the member numbers for a CPU group. + * + * @returns Maximum number of group members. + * @param idxGroup The CPU group. + * @param pcActive Where to return the number of active members. + * + * @remarks Only available on Windows. + */ +RTDECL(uint32_t) RTMpGetCpuGroupCounts(uint32_t idxGroup, uint32_t *pcActive); + +/** + * Get the maximum number of CPU groups. + * + * @returns Maximum number of CPU groups. + * + * @remarks Only available on Windows. + */ +RTDECL(uint32_t) RTMpGetMaxCpuGroupCount(void); + +/** + * Gets the max CPU identifier (inclusive). + * + * Intended for brute force enumerations, but use with + * care as it may be expensive. + * + * @returns The current higest CPU identifier value. + */ +RTDECL(RTCPUID) RTMpGetMaxCpuId(void); + +/** + * Gets the size of a CPU array that is indexed by CPU set index. + * + * This takes both online, offline and hot-plugged cpus into account. + * + * @returns Number of elements. + * + * @remarks Use RTMpCpuIdToSetIndex to convert a RTCPUID into an array index. + */ +RTDECL(uint32_t) RTMpGetArraySize(void); + +/** + * Checks if a CPU exists in the system or may possibly be hotplugged later. + * + * @returns true/false accordingly. + * @param idCpu The identifier of the CPU. + */ +RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu); + +/** + * Gets set of the CPUs present in the system plus any that may + * possibly be hotplugged later. + * + * @returns pSet. + * @param pSet Where to put the set. + */ +RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet); + +/** + * Get the count of CPUs present in the system plus any that may + * possibly be hotplugged later. + * + * @returns The count. + * @remarks Don't use this for CPU array sizing, use RTMpGetArraySize instead. + */ +RTDECL(RTCPUID) RTMpGetCount(void); + +/** + * Get the count of physical CPU cores present in the system plus any that may + * possibly be hotplugged later. + * + * @returns The number of cores. + */ +RTDECL(RTCPUID) RTMpGetCoreCount(void); + +/** + * Gets set of the CPUs present that are currently online. + * + * @returns pSet. + * @param pSet Where to put the set. + */ +RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet); + +/** + * Get the count of CPUs that are currently online. + * + * @return The count. + */ +RTDECL(RTCPUID) RTMpGetOnlineCount(void); + +/** + * Get the count of physical CPU cores in the system with one or more online + * threads. + * + * @returns The number of online cores. + */ +RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void); + +/** + * Checks if a CPU is online or not. + * + * @returns true/false accordingly. + * @param idCpu The identifier of the CPU. + */ +RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu); + + +/** + * Gets set of the CPUs present in the system. + * + * @returns pSet. + * @param pSet Where to put the set. + */ +RTDECL(PRTCPUSET) RTMpGetPresentSet(PRTCPUSET pSet); + +/** + * Get the count of CPUs that are present in the system. + * + * @return The count. + */ +RTDECL(RTCPUID) RTMpGetPresentCount(void); + +/** + * Get the count of physical CPU cores present in the system. + * + * @returns The number of cores. + */ +RTDECL(RTCPUID) RTMpGetPresentCoreCount(void); + +/** + * Checks if a CPU is present in the system. + * + * @returns true/false accordingly. + * @param idCpu The identifier of the CPU. + */ +RTDECL(bool) RTMpIsCpuPresent(RTCPUID idCpu); + + +/** + * Get the current frequency of a CPU. + * + * The CPU must be online. + * + * @returns The frequency as MHz. 0 if the CPU is offline + * or the information is not available. + * @param idCpu The identifier of the CPU. + */ +RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu); + +/** + * Get the maximum frequency of a CPU. + * + * The CPU must be online. + * + * @returns The frequency as MHz. 0 if the CPU is offline + * or the information is not available. + * @param idCpu The identifier of the CPU. + */ +RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu); + +/** + * Get the CPU description string. + * + * The CPU must be online. + * + * @returns IPRT status code. + * @param idCpu The identifier of the CPU. NIL_RTCPUID can be used to + * indicate the current CPU. + * @param pszBuf The output buffer. + * @param cbBuf The size of the output buffer. + */ +RTDECL(int) RTMpGetDescription(RTCPUID idCpu, char *pszBuf, size_t cbBuf); + + +#ifdef IN_RING0 + +/** + * Check if there's work (DPCs on Windows) pending on the current CPU. + * + * @return true if there's pending work on the current CPU, false otherwise. + */ +RTDECL(bool) RTMpIsCpuWorkPending(void); + + +/** + * Worker function passed to RTMpOnAll, RTMpOnOthers and RTMpOnSpecific that + * is to be called on the target cpus. + * + * @param idCpu The identifier for the CPU the function is called on. + * @param pvUser1 The 1st user argument. + * @param pvUser2 The 2nd user argument. + */ +typedef DECLCALLBACKTYPE(void, FNRTMPWORKER,(RTCPUID idCpu, void *pvUser1, void *pvUser2)); +/** Pointer to a FNRTMPWORKER. */ +typedef FNRTMPWORKER *PFNRTMPWORKER; + +/** @name RTMPON_F_XXX - RTMpOn flags. + * @{ */ +/** Caller doesn't care if pfnWorker is executed at the same time on the + * specified CPUs or not, as long as it gets executed. */ +#define RTMPON_F_WHATEVER_EXEC 0 +/** The caller insists on pfnWorker being executed more or less concurrently + * on the specified CPUs. */ +#define RTMPON_F_CONCURRENT_EXEC RT_BIT_32(1) +/** Mask of valid bits. */ +#define RTMPON_F_VALID_MASK UINT32_C(0x00000001) +/** @}*/ + +/** + * Checks if the RTMpOnAll() is safe with regards to all threads executing + * concurrently. + * + * If for instance, the RTMpOnAll() is implemented in a way where the threads + * might cause a classic deadlock, it is considered -not- concurrent safe. + * Windows currently is one such platform where it isn't safe. + * + * @returns true if RTMpOnAll() is concurrent safe, false otherwise. + */ +RTDECL(bool) RTMpOnAllIsConcurrentSafe(void); + +/** + * Executes a function on each (online) CPU in the system. + * + * @returns IPRT status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_NOT_SUPPORTED if this kind of operation isn't supported by the system. + * + * @param pfnWorker The worker function. + * @param pvUser1 The first user argument for the worker. + * @param pvUser2 The second user argument for the worker. + * + * @remarks The execution isn't in any way guaranteed to be simultaneous, + * it might even be serial (cpu by cpu). + */ +RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); + +/** + * Executes a function on all other (online) CPUs in the system. + * + * The caller must disable preemption prior to calling this API if the outcome + * is to make any sense. But do *not* disable interrupts. + * + * @returns IPRT status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_NOT_SUPPORTED if this kind of operation isn't supported by the system. + * + * @param pfnWorker The worker function. + * @param pvUser1 The first user argument for the worker. + * @param pvUser2 The second user argument for the worker. + * + * @remarks The execution isn't in any way guaranteed to be simultaneous, + * it might even be serial (cpu by cpu). + */ +RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); + +/** + * Executes a function on a specific CPU in the system. + * + * @returns IPRT status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_NOT_SUPPORTED if this kind of operation isn't supported by the system. + * @retval VERR_CPU_OFFLINE if the CPU is offline. + * @retval VERR_CPU_NOT_FOUND if the CPU wasn't found. + * + * @param idCpu The id of the CPU. + * @param pfnWorker The worker function. + * @param pvUser1 The first user argument for the worker. + * @param pvUser2 The second user argument for the worker. + */ +RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); + +/** + * Executes a function on two specific CPUs in the system. + * + * @returns IPRT status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_NOT_SUPPORTED if this kind of operation isn't supported by the + * system or if the specified modifier flag isn't supported. + * @retval VERR_CPU_OFFLINE if one or more of the CPUs are offline (see + * remarks). + * @retval VERR_CPU_NOT_FOUND if on or both of the CPUs weren't found. + * @retval VERR_NOT_ALL_CPUS_SHOWED if one of the CPUs didn't show. + * + * @param idCpu1 The id of the first CPU. + * @param idCpu2 The id of the second CPU. + * @param fFlags Combination of RTMPON_F_XXX flags. + * @param pfnWorker The worker function. + * @param pvUser1 The first user argument for the worker. + * @param pvUser2 The second user argument for the worker. + * + * @remarks There is a possible race between one (or both) of the CPUs going + * offline while setting up the call. The worker function must take + * this into account. + */ +RTDECL(int) RTMpOnPair(RTCPUID idCpu1, RTCPUID idCpu2, uint32_t fFlags, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); + +/** + * Indicates whether RTMpOnPair supports running the pfnWorker concurrently on + * both CPUs using RTMPON_F_CONCURRENT_EXEC. + * + * @returns true if supported, false if not. + */ +RTDECL(bool) RTMpOnPairIsConcurrentExecSupported(void); + + +/** + * Pokes the specified CPU. + * + * This should cause the execution on the CPU to be interrupted and forcing it + * to enter kernel context. It is optimized version of a RTMpOnSpecific call + * with a worker which returns immediately. + * + * @returns IPRT status code. + * @retval VERR_NOT_SUPPORTED if this kind of operation isn't supported by the + * system. The caller must not automatically assume that this API works + * when any of the RTMpOn* APIs works. This is because not all systems + * supports unicast MP events and this API will not be implemented as a + * broadcast. + * @retval VERR_CPU_OFFLINE if the CPU is offline. + * @retval VERR_CPU_NOT_FOUND if the CPU wasn't found. + * + * @param idCpu The id of the CPU to poke. + */ +RTDECL(int) RTMpPokeCpu(RTCPUID idCpu); + + +/** + * MP event, see FNRTMPNOTIFICATION. + */ +typedef enum RTMPEVENT +{ + /** The CPU goes online. */ + RTMPEVENT_ONLINE = 1, + /** The CPU goes offline. */ + RTMPEVENT_OFFLINE +} RTMPEVENT; + +/** + * Notification callback. + * + * The context this is called in differs a bit from platform to platform, so be + * careful while in here. + * + * On Windows we're running with IRQL=PASSIVE_LEVEL (reschedulable) according to + * the KeRegisterProcessorChangeCallback documentation - unrestricted API + * access. Probably not being called on the onlined/offlined CPU... + * + * On Solaris we're holding the cpu_lock, IPL/SPL/PIL is not yet known, however + * we will most likely -not- be firing on the CPU going offline/online. + * + * On Linux it looks like we're called with preemption enabled on any CPU and + * not necessarily on the CPU going offline/online. + * + * There is no callbacks for darwin at the moment, due to lack of suitable KPI. + * + * @param idCpu The CPU this applies to. + * @param enmEvent The event. + * @param pvUser The user argument. + */ +typedef DECLCALLBACKTYPE(void, FNRTMPNOTIFICATION,(RTMPEVENT enmEvent, RTCPUID idCpu, void *pvUser)); +/** Pointer to a FNRTMPNOTIFICATION(). */ +typedef FNRTMPNOTIFICATION *PFNRTMPNOTIFICATION; + +/** + * Registers a notification callback for cpu events. + * + * On platforms which doesn't do cpu offline/online events this API + * will just be a no-op that pretends to work. + * + * @todo We'll be adding a flag to this soon to indicate whether the callback should be called on all + * CPUs that are currently online while it's being registered. This is to help avoid some race + * conditions (we'll hopefully be able to implement this on linux, solaris/win is no issue). + * + * @returns IPRT status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_NO_MEMORY if a registration record cannot be allocated. + * @retval VERR_ALREADY_EXISTS if the pfnCallback and pvUser already exist + * in the callback list. + * + * @param pfnCallback The callback. + * @param pvUser The user argument to the callback function. + */ +RTDECL(int) RTMpNotificationRegister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser); + +/** + * This deregisters a notification callback registered via RTMpNotificationRegister(). + * + * The pfnCallback and pvUser arguments must be identical to the registration call + * of we won't find the right entry. + * + * @returns IPRT status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_NOT_FOUND if no matching entry was found. + * + * @param pfnCallback The callback. + * @param pvUser The user argument to the callback function. + */ +RTDECL(int) RTMpNotificationDeregister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser); + +#endif /* IN_RING0 */ + +/** @} */ + +RT_C_DECLS_END + +#endif /* !IPRT_INCLUDED_mp_h */ + |