summaryrefslogtreecommitdiffstats
path: root/include/iprt/mp.h
blob: e418bceb99287ab6342c1dcb326118324ccac007 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
/** @file
 * IPRT - Multiprocessor.
 */

/*
 * Copyright (C) 2008-2022 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 */