VirtualBox

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

Last change on this file since 78402 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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