VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/linux/mp-r0drv-linux.c@ 53517

Last change on this file since 53517 was 52618, checked in by vboxsync, 10 years ago

HostDrivers, Runtime, Devices, Additions: TSC delta measurement and other changes resulting from bumping supdrv major version. TSC delta measurement currently disabled.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 KB
Line 
1/* $Id: mp-r0drv-linux.c 52618 2014-09-05 12:07:29Z vboxsync $ */
2/** @file
3 * IPRT - Multiprocessor, Ring-0 Driver, Linux.
4 */
5
6/*
7 * Copyright (C) 2008-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include "the-linux-kernel.h"
32#include "internal/iprt.h"
33
34#include <iprt/mp.h>
35#include <iprt/cpuset.h>
36#include <iprt/err.h>
37#include <iprt/asm.h>
38#include <iprt/thread.h>
39#include "r0drv/mp-r0drv.h"
40
41
42RTDECL(RTCPUID) RTMpCpuId(void)
43{
44 return smp_processor_id();
45}
46RT_EXPORT_SYMBOL(RTMpCpuId);
47
48
49RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
50{
51 return idCpu < RTCPUSET_MAX_CPUS && idCpu < NR_CPUS ? (int)idCpu : -1;
52}
53RT_EXPORT_SYMBOL(RTMpCpuIdToSetIndex);
54
55
56RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
57{
58 return iCpu < NR_CPUS ? (RTCPUID)iCpu : NIL_RTCPUID;
59}
60RT_EXPORT_SYMBOL(RTMpCpuIdFromSetIndex);
61
62
63RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
64{
65 return NR_CPUS - 1; //???
66}
67RT_EXPORT_SYMBOL(RTMpGetMaxCpuId);
68
69
70RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
71{
72#if defined(CONFIG_SMP)
73 if (RT_UNLIKELY(idCpu >= NR_CPUS))
74 return false;
75
76# if defined(cpu_possible)
77 return cpu_possible(idCpu);
78# else /* < 2.5.29 */
79 return idCpu < (RTCPUID)smp_num_cpus;
80# endif
81#else
82 return idCpu == RTMpCpuId();
83#endif
84}
85RT_EXPORT_SYMBOL(RTMpIsCpuPossible);
86
87
88RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
89{
90 RTCPUID idCpu;
91
92 RTCpuSetEmpty(pSet);
93 idCpu = RTMpGetMaxCpuId();
94 do
95 {
96 if (RTMpIsCpuPossible(idCpu))
97 RTCpuSetAdd(pSet, idCpu);
98 } while (idCpu-- > 0);
99 return pSet;
100}
101RT_EXPORT_SYMBOL(RTMpGetSet);
102
103
104RTDECL(RTCPUID) RTMpGetCount(void)
105{
106#ifdef CONFIG_SMP
107# if defined(CONFIG_HOTPLUG_CPU) /* introduced & uses cpu_present */
108 return num_present_cpus();
109# elif defined(num_possible_cpus)
110 return num_possible_cpus();
111# elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
112 return smp_num_cpus;
113# else
114 RTCPUSET Set;
115 RTMpGetSet(&Set);
116 return RTCpuSetCount(&Set);
117# endif
118#else
119 return 1;
120#endif
121}
122RT_EXPORT_SYMBOL(RTMpGetCount);
123
124
125RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
126{
127#ifdef CONFIG_SMP
128 if (RT_UNLIKELY(idCpu >= NR_CPUS))
129 return false;
130# ifdef cpu_online
131 return cpu_online(idCpu);
132# else /* 2.4: */
133 return cpu_online_map & RT_BIT_64(idCpu);
134# endif
135#else
136 return idCpu == RTMpCpuId();
137#endif
138}
139RT_EXPORT_SYMBOL(RTMpIsCpuOnline);
140
141
142RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
143{
144#ifdef CONFIG_SMP
145 RTCPUID idCpu;
146
147 RTCpuSetEmpty(pSet);
148 idCpu = RTMpGetMaxCpuId();
149 do
150 {
151 if (RTMpIsCpuOnline(idCpu))
152 RTCpuSetAdd(pSet, idCpu);
153 } while (idCpu-- > 0);
154#else
155 RTCpuSetEmpty(pSet);
156 RTCpuSetAdd(pSet, RTMpCpuId());
157#endif
158 return pSet;
159}
160RT_EXPORT_SYMBOL(RTMpGetOnlineSet);
161
162
163RTDECL(RTCPUID) RTMpGetOnlineCount(void)
164{
165#ifdef CONFIG_SMP
166# if defined(num_online_cpus)
167 return num_online_cpus();
168# else
169 RTCPUSET Set;
170 RTMpGetOnlineSet(&Set);
171 return RTCpuSetCount(&Set);
172# endif
173#else
174 return 1;
175#endif
176}
177RT_EXPORT_SYMBOL(RTMpGetOnlineCount);
178
179
180RTDECL(bool) RTMpIsCpuWorkPending(void)
181{
182 /** @todo (not used on non-Windows platforms yet). */
183 return false;
184}
185RT_EXPORT_SYMBOL(RTMpIsCpuWorkPending);
186
187
188/**
189 * Wrapper between the native linux per-cpu callbacks and PFNRTWORKER.
190 *
191 * @param pvInfo Pointer to the RTMPARGS package.
192 */
193static void rtmpLinuxWrapper(void *pvInfo)
194{
195 PRTMPARGS pArgs = (PRTMPARGS)pvInfo;
196 ASMAtomicIncU32(&pArgs->cHits);
197 pArgs->pfnWorker(RTMpCpuId(), pArgs->pvUser1, pArgs->pvUser2);
198}
199
200
201/**
202 * Wrapper between the native linux all-cpu callbacks and PFNRTWORKER.
203 *
204 * @param pvInfo Pointer to the RTMPARGS package.
205 */
206static void rtmpLinuxAllWrapper(void *pvInfo)
207{
208 PRTMPARGS pArgs = (PRTMPARGS)pvInfo;
209 PRTCPUSET pWorkerSet = pArgs->pWorkerSet;
210 RTCPUID idCpu = RTMpCpuId();
211 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
212
213 if (RTCpuSetIsMember(pWorkerSet, idCpu))
214 {
215 pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
216 RTCpuSetDel(pWorkerSet, idCpu);
217 }
218}
219
220
221RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
222{
223 int rc;
224 RTMPARGS Args;
225 RTCPUSET OnlineSet;
226 RTCPUID idCpu;
227 uint32_t cLoops;
228
229 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
230
231 Args.pfnWorker = pfnWorker;
232 Args.pvUser1 = pvUser1;
233 Args.pvUser2 = pvUser2;
234 Args.idCpu = NIL_RTCPUID;
235 Args.cHits = 0;
236
237 RTThreadPreemptDisable(&PreemptState);
238 RTMpGetOnlineSet(&OnlineSet);
239 Args.pWorkerSet = &OnlineSet;
240 idCpu = RTMpCpuId();
241
242 if (RTCpuSetCount(&OnlineSet) > 1)
243 {
244 /* Fire the function on all other CPUs without waiting for completion. */
245#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
246 rc = smp_call_function(rtmpLinuxAllWrapper, &Args, 0 /* wait */);
247#else
248 rc = smp_call_function(rtmpLinuxAllWrapper, &Args, 0 /* retry */, 0 /* wait */);
249#endif
250 Assert(!rc); NOREF(rc);
251 }
252
253 /* Fire the function on this CPU. */
254 Args.pfnWorker(idCpu, Args.pvUser1, Args.pvUser2);
255 RTCpuSetDel(Args.pWorkerSet, idCpu);
256
257 /* Wait for all of them finish. */
258 cLoops = 64000;
259 while (!RTCpuSetIsEmpty(Args.pWorkerSet))
260 {
261 /* Periodically check if any CPU in the wait set has gone offline, if so update the wait set. */
262 if (!cLoops--)
263 {
264 RTCPUSET OnlineSetNow;
265 RTMpGetOnlineSet(&OnlineSetNow);
266 RTCpuSetAnd(Args.pWorkerSet, &OnlineSetNow);
267
268 cLoops = 64000;
269 }
270
271 ASMNopPause();
272 }
273
274 RTThreadPreemptRestore(&PreemptState);
275 return VINF_SUCCESS;
276}
277RT_EXPORT_SYMBOL(RTMpOnAll);
278
279
280RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
281{
282 int rc;
283 RTMPARGS Args;
284
285 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
286 Args.pfnWorker = pfnWorker;
287 Args.pvUser1 = pvUser1;
288 Args.pvUser2 = pvUser2;
289 Args.idCpu = NIL_RTCPUID;
290 Args.cHits = 0;
291
292 RTThreadPreemptDisable(&PreemptState);
293#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
294 rc = smp_call_function(rtmpLinuxWrapper, &Args, 1 /* wait */);
295#else /* older kernels */
296 rc = smp_call_function(rtmpLinuxWrapper, &Args, 0 /* retry */, 1 /* wait */);
297#endif /* older kernels */
298 RTThreadPreemptRestore(&PreemptState);
299
300 Assert(rc == 0); NOREF(rc);
301 return VINF_SUCCESS;
302}
303RT_EXPORT_SYMBOL(RTMpOnOthers);
304
305
306#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
307/**
308 * Wrapper between the native linux per-cpu callbacks and PFNRTWORKER
309 * employed by RTMpOnSpecific on older kernels that lacks smp_call_function_single.
310 *
311 * @param pvInfo Pointer to the RTMPARGS package.
312 */
313static void rtmpOnSpecificLinuxWrapper(void *pvInfo)
314{
315 PRTMPARGS pArgs = (PRTMPARGS)pvInfo;
316 RTCPUID idCpu = RTMpCpuId();
317
318 if (idCpu == pArgs->idCpu)
319 {
320 pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
321 ASMAtomicIncU32(&pArgs->cHits);
322 }
323}
324#endif
325
326
327RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
328{
329 int rc;
330 RTMPARGS Args;
331
332 RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
333 Args.pfnWorker = pfnWorker;
334 Args.pvUser1 = pvUser1;
335 Args.pvUser2 = pvUser2;
336 Args.idCpu = idCpu;
337 Args.cHits = 0;
338
339 if (!RTMpIsCpuPossible(idCpu))
340 return VERR_CPU_NOT_FOUND;
341
342 RTThreadPreemptDisable(&PreemptState);
343 if (idCpu != RTMpCpuId())
344 {
345 if (RTMpIsCpuOnline(idCpu))
346 {
347#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
348 rc = smp_call_function_single(idCpu, rtmpLinuxWrapper, &Args, 1 /* wait */);
349#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
350 rc = smp_call_function_single(idCpu, rtmpLinuxWrapper, &Args, 0 /* retry */, 1 /* wait */);
351#else /* older kernels */
352 rc = smp_call_function(rtmpOnSpecificLinuxWrapper, &Args, 0 /* retry */, 1 /* wait */);
353#endif /* older kernels */
354 Assert(rc == 0);
355 rc = Args.cHits ? VINF_SUCCESS : VERR_CPU_OFFLINE;
356 }
357 else
358 rc = VERR_CPU_OFFLINE;
359 }
360 else
361 {
362 rtmpLinuxWrapper(&Args);
363 rc = VINF_SUCCESS;
364 }
365 RTThreadPreemptRestore(&PreemptState);;
366
367 NOREF(rc);
368 return rc;
369}
370RT_EXPORT_SYMBOL(RTMpOnSpecific);
371
372
373#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
374/**
375 * Dummy callback used by RTMpPokeCpu.
376 *
377 * @param pvInfo Ignored.
378 */
379static void rtmpLinuxPokeCpuCallback(void *pvInfo)
380{
381 NOREF(pvInfo);
382}
383#endif
384
385
386RTDECL(int) RTMpPokeCpu(RTCPUID idCpu)
387{
388#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
389 int rc;
390
391 if (!RTMpIsCpuPossible(idCpu))
392 return VERR_CPU_NOT_FOUND;
393 if (!RTMpIsCpuOnline(idCpu))
394 return VERR_CPU_OFFLINE;
395
396# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
397 rc = smp_call_function_single(idCpu, rtmpLinuxPokeCpuCallback, NULL, 0 /* wait */);
398# elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
399 rc = smp_call_function_single(idCpu, rtmpLinuxPokeCpuCallback, NULL, 0 /* retry */, 0 /* wait */);
400# else /* older kernels */
401# error oops
402# endif /* older kernels */
403 NOREF(rc);
404 Assert(rc == 0);
405 return VINF_SUCCESS;
406
407#else /* older kernels */
408 /* no unicast here? */
409 return VERR_NOT_SUPPORTED;
410#endif /* older kernels */
411}
412RT_EXPORT_SYMBOL(RTMpPokeCpu);
413
414
415RTDECL(bool) RTMpOnAllIsConcurrentSafe(void)
416{
417 return true;
418}
419RT_EXPORT_SYMBOL(RTMpOnAllIsConcurrentSafe);
420
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette