VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/solaris/mp-solaris.cpp@ 106580

Last change on this file since 106580 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.1 KB
Line 
1/* $Id: mp-solaris.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Multiprocessor, Solaris.
4 */
5
6/*
7 * Copyright (C) 2008-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_DEFAULT
42#include <unistd.h>
43#include <stdio.h>
44#include <errno.h>
45#include <kstat.h>
46#include <sys/processor.h>
47
48#include <iprt/mp.h>
49#include <iprt/cpuset.h>
50#include <iprt/assert.h>
51#include <iprt/critsect.h>
52#include <iprt/err.h>
53#include <iprt/mem.h>
54#include <iprt/log.h>
55#include <iprt/once.h>
56#include <iprt/string.h>
57
58
59/*********************************************************************************************************************************
60* Global Variables *
61*********************************************************************************************************************************/
62/** Initialization serializing (rtMpSolarisOnce). */
63static RTONCE g_MpSolarisOnce = RTONCE_INITIALIZER;
64/** Critical section serializing access to kstat. */
65static RTCRITSECT g_MpSolarisCritSect;
66/** The kstat handle. */
67static kstat_ctl_t *g_pKsCtl;
68/** Array pointing to the cpu_info instances. */
69static kstat_t **g_papCpuInfo;
70/** The number of entries in g_papCpuInfo */
71static RTCPUID g_capCpuInfo;
72/** Array of core ids. */
73static uint64_t *g_pu64CoreIds;
74/** Number of entries in g_pu64CoreIds. */
75static size_t g_cu64CoreIds;
76/** Number of cores in the system. */
77static size_t g_cCores;
78
79
80/**
81 * Helper for getting the core ID for a given CPU/strand/hyperthread.
82 *
83 * @returns The core ID.
84 * @param idCpu The CPU ID instance.
85 */
86static inline uint64_t rtMpSolarisGetCoreId(RTCPUID idCpu)
87{
88 kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(g_papCpuInfo[idCpu], (char *)"core_id");
89 Assert(pStat->data_type == KSTAT_DATA_LONG);
90 Assert(pStat->value.l >= 0);
91 AssertCompile(sizeof(uint64_t) >= sizeof(long)); /* Paranoia. */
92 return (uint64_t)pStat->value.l;
93}
94
95
96/**
97 * Populates 'g_pu64CoreIds' array with unique core identifiers in the system.
98 *
99 * @returns VBox status code.
100 */
101static int rtMpSolarisGetCoreIds(void)
102{
103 for (RTCPUID idCpu = 0; idCpu < g_capCpuInfo; idCpu++)
104 {
105 /*
106 * It is possible that the number of cores don't match the maximum number
107 * of cores possible on the system. Hence check if we have a valid cpu_info
108 * object. We don't want to break out if it's NULL, the array may be sparse
109 * in theory, see @bugref{8469}.
110 */
111 if (g_papCpuInfo[idCpu])
112 {
113 if (kstat_read(g_pKsCtl, g_papCpuInfo[idCpu], 0) != -1)
114 {
115 /* Strands/Hyperthreads share the same core ID. */
116 uint64_t u64CoreId = rtMpSolarisGetCoreId(idCpu);
117 bool fAddedCore = false;
118 for (RTCPUID i = 0; i < g_cCores; i++)
119 {
120 if (g_pu64CoreIds[i] == u64CoreId)
121 {
122 fAddedCore = true;
123 break;
124 }
125 }
126
127 if (!fAddedCore)
128 {
129 g_pu64CoreIds[g_cCores] = u64CoreId;
130 ++g_cCores;
131 }
132 }
133 else
134 return VERR_INTERNAL_ERROR_2;
135 }
136 }
137
138 return VINF_SUCCESS;
139}
140
141
142/**
143 * Run once function that initializes the kstats we need here.
144 *
145 * @returns IPRT status code.
146 * @param pvUser Unused.
147 */
148static DECLCALLBACK(int) rtMpSolarisOnce(void *pvUser)
149{
150 int rc = VINF_SUCCESS;
151 NOREF(pvUser);
152
153 /*
154 * Open kstat and find the cpu_info entries for each of the CPUs.
155 */
156 g_pKsCtl = kstat_open();
157 if (g_pKsCtl)
158 {
159 g_capCpuInfo = RTMpGetCount();
160 if (RT_LIKELY(g_capCpuInfo > 0))
161 {
162 g_papCpuInfo = (kstat_t **)RTMemAllocZ(g_capCpuInfo * sizeof(kstat_t *));
163 if (g_papCpuInfo)
164 {
165 g_cu64CoreIds = g_capCpuInfo;
166 g_pu64CoreIds = (uint64_t *)RTMemAllocZ(g_cu64CoreIds * sizeof(uint64_t));
167 if (g_pu64CoreIds)
168 {
169 rc = RTCritSectInit(&g_MpSolarisCritSect);
170 if (RT_SUCCESS(rc))
171 {
172 RTCPUID i = 0;
173 for (kstat_t *pKsp = g_pKsCtl->kc_chain; pKsp != NULL; pKsp = pKsp->ks_next)
174 {
175 if (!RTStrCmp(pKsp->ks_module, "cpu_info"))
176 {
177 AssertBreak(i < g_capCpuInfo);
178 g_papCpuInfo[i++] = pKsp;
179 /** @todo ks_instance == cpu_id (/usr/src/uts/common/os/cpu.c)? Check this and fix it ASAP. */
180 }
181 }
182
183 rc = rtMpSolarisGetCoreIds();
184 if (RT_SUCCESS(rc))
185 return VINF_SUCCESS;
186 else
187 Log(("rtMpSolarisGetCoreIds failed. rc=%Rrc\n", rc));
188 }
189
190 RTMemFree(g_pu64CoreIds);
191 g_pu64CoreIds = NULL;
192 }
193 else
194 rc = VERR_NO_MEMORY;
195
196 /* bail out, we failed. */
197 RTMemFree(g_papCpuInfo);
198 g_papCpuInfo = NULL;
199 }
200 else
201 rc = VERR_NO_MEMORY;
202 }
203 else
204 rc = VERR_CPU_IPE_1;
205 kstat_close(g_pKsCtl);
206 g_pKsCtl = NULL;
207 }
208 else
209 {
210 rc = RTErrConvertFromErrno(errno);
211 if (RT_SUCCESS(rc))
212 rc = VERR_INTERNAL_ERROR;
213 Log(("kstat_open() -> %d (%Rrc)\n", errno, rc));
214 }
215
216 return rc;
217}
218
219
220/**
221 * RTOnceEx() cleanup function.
222 *
223 * @param pvUser Unused.
224 * @param fLazyCleanUpOk Whether lazy cleanup is okay or not.
225 */
226static DECLCALLBACK(void) rtMpSolarisCleanUp(void *pvUser, bool fLazyCleanUpOk)
227{
228 if (g_pKsCtl)
229 kstat_close(g_pKsCtl);
230 RTMemFree(g_pu64CoreIds);
231 RTMemFree(g_papCpuInfo);
232}
233
234
235/**
236 * Worker for RTMpGetCurFrequency and RTMpGetMaxFrequency.
237 *
238 * @returns The desired frequency on success, 0 on failure.
239 *
240 * @param idCpu The CPU ID.
241 * @param pszStatName The cpu_info stat name.
242 */
243static uint64_t rtMpSolarisGetFrequency(RTCPUID idCpu, const char *pszStatName)
244{
245 uint64_t u64 = 0;
246 int rc = RTOnceEx(&g_MpSolarisOnce, rtMpSolarisOnce, rtMpSolarisCleanUp, NULL /* pvUser */);
247 if (RT_SUCCESS(rc))
248 {
249 if ( idCpu < g_capCpuInfo
250 && g_papCpuInfo[idCpu])
251 {
252 rc = RTCritSectEnter(&g_MpSolarisCritSect);
253 AssertRC(rc);
254 if (RT_SUCCESS(rc))
255 {
256 if (kstat_read(g_pKsCtl, g_papCpuInfo[idCpu], 0) != -1)
257 {
258 /* Solaris really need to fix their APIs. Explicitly cast for now. */
259 kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(g_papCpuInfo[idCpu], (char*)pszStatName);
260 if (pStat)
261 {
262 Assert(pStat->data_type == KSTAT_DATA_UINT64 || pStat->data_type == KSTAT_DATA_LONG);
263 switch (pStat->data_type)
264 {
265 case KSTAT_DATA_UINT64: u64 = pStat->value.ui64; break; /* current_clock_Hz */
266 case KSTAT_DATA_INT32: u64 = pStat->value.i32; break; /* clock_MHz */
267
268 /* just in case... */
269 case KSTAT_DATA_UINT32: u64 = pStat->value.ui32; break;
270 case KSTAT_DATA_INT64: u64 = pStat->value.i64; break;
271 default:
272 AssertMsgFailed(("%d\n", pStat->data_type));
273 break;
274 }
275 }
276 else
277 Log(("kstat_data_lookup(%s) -> %d\n", pszStatName, errno));
278 }
279 else
280 Log(("kstat_read() -> %d\n", errno));
281 RTCritSectLeave(&g_MpSolarisCritSect);
282 }
283 }
284 else
285 Log(("invalid idCpu: %d (g_capCpuInfo=%d)\n", (int)idCpu, (int)g_capCpuInfo));
286 }
287
288 return u64;
289}
290
291
292RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
293{
294 return rtMpSolarisGetFrequency(idCpu, "current_clock_Hz") / 1000000;
295}
296
297
298RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
299{
300 return rtMpSolarisGetFrequency(idCpu, "clock_MHz");
301}
302
303
304#if defined(RT_ARCH_SPARC) || defined(RT_ARCH_SPARC64)
305RTDECL(RTCPUID) RTMpCpuId(void)
306{
307 /** @todo implement RTMpCpuId on solaris/r3! */
308 return NIL_RTCPUID;
309}
310#endif
311
312
313RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
314{
315 return idCpu < RTCPUSET_MAX_CPUS ? (int)idCpu : -1;
316}
317
318
319RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
320{
321 return (unsigned)iCpu < RTCPUSET_MAX_CPUS ? iCpu : NIL_RTCPUID;
322}
323
324
325RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
326{
327 return RTMpGetCount() - 1;
328}
329
330
331RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
332{
333 return idCpu != NIL_RTCPUID
334 && idCpu < (RTCPUID)RTMpGetCount();
335}
336
337
338RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
339{
340 int iStatus = p_online(idCpu, P_STATUS);
341 return iStatus == P_ONLINE
342 || iStatus == P_NOINTR;
343}
344
345
346RTDECL(bool) RTMpIsCpuPresent(RTCPUID idCpu)
347{
348 int iStatus = p_online(idCpu, P_STATUS);
349 return iStatus != -1;
350}
351
352
353RTDECL(RTCPUID) RTMpGetCount(void)
354{
355 /*
356 * Solaris has sysconf.
357 */
358 int cCpus = sysconf(_SC_NPROCESSORS_MAX);
359 if (cCpus < 0)
360 cCpus = sysconf(_SC_NPROCESSORS_CONF);
361 return cCpus;
362}
363
364
365RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
366{
367 RTCpuSetEmpty(pSet);
368 int idCpu = RTMpGetCount();
369 while (idCpu-- > 0)
370 RTCpuSetAdd(pSet, idCpu);
371 return pSet;
372}
373
374
375RTDECL(RTCPUID) RTMpGetOnlineCount(void)
376{
377 /*
378 * Solaris has sysconf.
379 */
380 return sysconf(_SC_NPROCESSORS_ONLN);
381}
382
383
384RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
385{
386 RTCpuSetEmpty(pSet);
387 RTCPUID cCpus = RTMpGetCount();
388 for (RTCPUID idCpu = 0; idCpu < cCpus; idCpu++)
389 if (RTMpIsCpuOnline(idCpu))
390 RTCpuSetAdd(pSet, idCpu);
391 return pSet;
392}
393
394
395RTDECL(PRTCPUSET) RTMpGetPresentSet(PRTCPUSET pSet)
396{
397#ifdef RT_STRICT
398 RTCPUID cCpusPresent = 0;
399#endif
400 RTCpuSetEmpty(pSet);
401 RTCPUID cCpus = RTMpGetCount();
402 for (RTCPUID idCpu = 0; idCpu < cCpus; idCpu++)
403 if (RTMpIsCpuPresent(idCpu))
404 {
405 RTCpuSetAdd(pSet, idCpu);
406#ifdef RT_STRICT
407 cCpusPresent++;
408#endif
409 }
410 Assert(cCpusPresent == RTMpGetPresentCount());
411 return pSet;
412}
413
414
415RTDECL(RTCPUID) RTMpGetPresentCount(void)
416{
417 /*
418 * Solaris has sysconf.
419 */
420 return sysconf(_SC_NPROCESSORS_CONF);
421}
422
423
424RTDECL(RTCPUID) RTMpGetPresentCoreCount(void)
425{
426 return RTMpGetCoreCount();
427}
428
429
430RTDECL(RTCPUID) RTMpGetCoreCount(void)
431{
432 int rc = RTOnceEx(&g_MpSolarisOnce, rtMpSolarisOnce, rtMpSolarisCleanUp, NULL /* pvUser */);
433 if (RT_SUCCESS(rc))
434 return g_cCores;
435
436 return 0;
437}
438
439
440RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
441{
442 RTCPUID uOnlineCores = 0;
443 int rc = RTOnceEx(&g_MpSolarisOnce, rtMpSolarisOnce, rtMpSolarisCleanUp, NULL /* pvUser */);
444 if (RT_SUCCESS(rc))
445 {
446 rc = RTCritSectEnter(&g_MpSolarisCritSect);
447 AssertRC(rc);
448
449 /*
450 * For each core in the system, count how many are currently online.
451 */
452 for (RTCPUID j = 0; j < g_cCores; j++)
453 {
454 uint64_t u64CoreId = g_pu64CoreIds[j];
455 for (RTCPUID idCpu = 0; idCpu < g_capCpuInfo; idCpu++)
456 {
457 rc = kstat_read(g_pKsCtl, g_papCpuInfo[idCpu], 0);
458 AssertReturn(rc != -1, 0 /* rc */);
459 uint64_t u64ThreadCoreId = rtMpSolarisGetCoreId(idCpu);
460 if (u64ThreadCoreId == u64CoreId)
461 {
462 kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(g_papCpuInfo[idCpu], (char *)"state");
463 Assert(pStat->data_type == KSTAT_DATA_CHAR);
464 if( !RTStrNCmp(pStat->value.c, PS_ONLINE, sizeof(PS_ONLINE) - 1)
465 || !RTStrNCmp(pStat->value.c, PS_NOINTR, sizeof(PS_NOINTR) - 1))
466 {
467 uOnlineCores++;
468 break; /* Move to the next core. We have at least 1 hyperthread online in the current core. */
469 }
470 }
471 }
472 }
473
474 RTCritSectLeave(&g_MpSolarisCritSect);
475 }
476
477 return uOnlineCores;
478}
479
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