VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/linux/mp-linux.cpp@ 10481

Last change on this file since 10481 was 10481, checked in by vboxsync, 17 years ago

cosmetics.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.8 KB
Line 
1/* $Id: mp-linux.cpp 10481 2008-07-10 19:58:15Z vboxsync $ */
2/** @file
3 * IPRT - Multiprocessor, Linux.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#define LOG_GROUP RTLOGGROUP_SYSTEM
36#include <unistd.h>
37#include <stdio.h>
38#include <sys/sysctl.h>
39#include <sys/stat.h>
40#include <sys/fcntl.h>
41#include <errno.h>
42
43#include <iprt/mp.h>
44#include <iprt/cpuset.h>
45#include <iprt/assert.h>
46#include <iprt/string.h>
47
48
49/** @todo move the rtLinuxSysFs* bits into sysfs.cpp and sysfs.h. */
50
51/**
52 * Checks if a sysfs file (or directory, device, symlink, whatever) exists.
53 *
54 * @returns true / false, errno is preserved.
55 * @param pszFormat The name format, without "/sys/".
56 * @param va The format args.
57 */
58bool rtLinuxSysFsExistsV(const char *pszFormat, va_list va)
59{
60 int iSavedErrno = errno;
61
62 /*
63 * Construct the filename and call stat.
64 */
65 char szFilename[128];
66 static const size_t cchPrefix = sizeof("/sys/") - 1;
67 strcpy(szFilename, "/sys/");
68 size_t cch = RTStrPrintfV(&szFilename[cchPrefix], sizeof(szFilename) - cchPrefix, pszFormat, va);
69 NOREF(cch);
70 Assert(cch < sizeof(szFilename) - cchPrefix - 1);
71
72 struct stat st;
73 bool fRet = stat(szFilename, &st) == 0;
74
75 errno = iSavedErrno;
76 return fRet;
77}
78
79/**
80 * Checks if a sysfs file (or directory, device, symlink, whatever) exists.
81 *
82 * @returns true / false, errno is preserved.
83 * @param pszFormat The name format, without "/sys/".
84 * @param ... The format args.
85 */
86bool rtLinuxSysFsExists(const char *pszFormat, ...)
87{
88 va_list va;
89 va_start(va, pszFormat);
90 bool fRet = rtLinuxSysFsExistsV(pszFormat, va);
91 va_end(va);
92 return fRet;
93}
94
95
96/**
97 * Opens a sysfs file.
98 *
99 * @returns The file descriptor. -1 and errno on failure.
100 * @param pszFormat The name format, without "/sys/".
101 * @param va The format args.
102 */
103int rtLinuxSysFsOpenV(const char *pszFormat, va_list va)
104{
105 /*
106 * Construct the filename and call open.
107 */
108 char szFilename[128];
109 static const size_t cchPrefix = sizeof("/sys/") - 1;
110 strcpy(szFilename, "/sys/");
111 size_t cch = RTStrPrintfV(&szFilename[cchPrefix], sizeof(szFilename) - cchPrefix, pszFormat, va);
112 Assert(cch < sizeof(szFilename) - cchPrefix - 1); NOREF(cch);
113
114 return open(szFilename, O_RDONLY, 0);
115}
116
117
118/**
119 * Opens a sysfs file.
120 *
121 * @returns The file descriptor. -1 and errno on failure.
122 * @param pszFormat The name format, without "/sys/".
123 * @param ... The format args.
124 */
125int rtLinuxSysFsOpen(const char *pszFormat, ...)
126{
127 va_list va;
128 va_start(va, pszFormat);
129 int fd = rtLinuxSysFsOpenV(pszFormat, va);
130 va_end(va);
131 return fd;
132}
133
134
135/**
136 * Closes a file opened with rtLinuxSysFsOpen or rtLinuxSysFsOpenV.
137 *
138 * @param fd
139 */
140void rtLinuxSysFsClose(int fd)
141{
142 int iSavedErrno = errno;
143 close(fd);
144 errno = iSavedErrno;
145}
146
147
148/**
149 * Closes a file opened with rtLinuxSysFsOpen or rtLinuxSysFsOpenV.
150 *
151 * @returns The number of bytes read. -1 and errno on failure.
152 * @param fd The file descriptor returned by rtLinuxSysFsOpen or rtLinuxSysFsOpenV.
153 * @param pszBuf Where to store the string.
154 * @param cchBuf The size of the buffer. Must be at least 2 bytes.
155 */
156ssize_t rtLinuxSysFsReadStr(int fd, char *pszBuf, size_t cchBuf)
157{
158 Assert(cchBuf > 1);
159 ssize_t cchRead = read(fd, pszBuf, cchBuf - 1);
160 pszBuf[cchRead >= 0 ? cchRead : 0] = '\0';
161 return cchRead;
162}
163
164
165/**
166 * Reads a sysfs file.
167 *
168 * @returns 64-bit signed value on success, -1 and errno on failure.
169 * @param uBase The number base, 0 for autodetect.
170 * @param pszFormat The filename format, without "/sys/".
171 * @param va Format args.
172 */
173int64_t rtLinuxSysFsReadIntFileV(unsigned uBase, const char *pszFormat, va_list va)
174{
175 int fd = rtLinuxSysFsOpenV(pszFormat, va);
176 if (fd == -1)
177 return -1;
178
179 int64_t i64Ret = -1;
180 char szNum[128];
181 ssize_t cchNum = rtLinuxSysFsReadStr(fd, szNum, sizeof(szNum));
182 if (cchNum > 0)
183 {
184 int rc = RTStrToInt64Ex(szNum, NULL, uBase, &i64Ret);
185 if (RT_FAILURE(rc))
186 {
187 i64Ret = -1;
188 errno = -ETXTBSY; /* just something that won't happen at read / open. */
189 }
190 }
191 else if (cchNum == 0)
192 errno = -ETXTBSY; /* just something that won't happen at read / open. */
193
194 rtLinuxSysFsClose(fd);
195 return i64Ret;
196}
197
198
199/**
200 * Reads a sysfs file.
201 *
202 * @returns 64-bit signed value on success, -1 and errno on failure.
203 * @param uBase The number base, 0 for autodetect.
204 * @param pszFormat The filename format, without "/sys/".
205 * @param ... Format args.
206 */
207static int64_t rtLinuxSysFsReadIntFile(unsigned uBase, const char *pszFormat, ...)
208{
209 va_list va;
210 va_start(va, pszFormat);
211 int64_t i64Ret = rtLinuxSysFsReadIntFileV(uBase, pszFormat, va);
212 va_end(va);
213 return i64Ret;
214}
215
216
217/**
218 * Internal worker that determins the max possible CPU count.
219 *
220 * @returns Max cpus.
221 */
222static RTCPUID rtMpLinuxMaxCpus(void)
223{
224#if 0 /* this doesn't do the right thing :-/ */
225 int cMax = sysconf(_SC_NPROCESSORS_CONF);
226 Assert(cMax >= 1);
227 return cMax;
228#else
229 static uint32_t s_cMax = 0;
230 if (!s_cMax)
231 {
232 int cMax = 1;
233 for (unsigned iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
234 if (rtLinuxSysFsExists("devices/system/cpu/cpu%d", iCpu))
235 cMax = iCpu + 1;
236 ASMAtomicUoWriteU32((uint32_t volatile *)&s_cMax, cMax);
237 return cMax;
238 }
239 return s_cMax;
240#endif
241}
242
243/**
244 * Internal worker that picks the processor speed in MHz from /proc/cpuinfo.
245 *
246 * @returns CPU frequency.
247 */
248static uint32_t rtMpLinuxGetFrequency(RTCPUID idCpu)
249{
250 FILE *pFile = fopen("/proc/cpuinfo", "r");
251 if (!pFile)
252 return 0;
253
254 char sz[256];
255 RTCPUID idCpuFound = NIL_RTCPUID;
256 uint32_t Frequency = 0;
257 while (fgets(sz, sizeof(sz), pFile))
258 {
259 char *psz;
260 if ( !strncmp(sz, "processor", 9)
261 && (sz[10] == ' ' || sz[10] == '\t' || sz[10] == ':')
262 && (psz = strchr(sz, ':')))
263 {
264 psz += 2;
265 int64_t iCpu;
266 int rc = RTStrToInt64Ex(psz, NULL, 0, &iCpu);
267 if (RT_SUCCESS(rc))
268 idCpuFound = iCpu;
269 }
270 else if ( idCpu == idCpuFound
271 && !strncmp(sz, "cpu MHz", 7)
272 && (sz[10] == ' ' || sz[10] == '\t' || sz[10] == ':')
273 && (psz = strchr(sz, ':')))
274 {
275 psz += 2;
276 int64_t v;
277 int rc = RTStrToInt64Ex(psz, &psz, 0, &v);
278 if (RT_SUCCESS(rc))
279 {
280 Frequency = v;
281 break;
282 }
283 }
284 }
285 fclose(pFile);
286 return Frequency;
287}
288
289
290/** @todo RTmpCpuId(). */
291
292RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
293{
294 return idCpu < rtMpLinuxMaxCpus() ? idCpu : -1;
295}
296
297
298RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
299{
300 return (unsigned)iCpu < rtMpLinuxMaxCpus() ? iCpu : NIL_RTCPUID;
301}
302
303
304RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
305{
306 return rtMpLinuxMaxCpus() - 1;
307}
308
309
310RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
311{
312 /** @todo check if there is a simpler interface than this... */
313 int i = rtLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/online", (int)idCpu);
314 if ( i == -1
315 && rtLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu))
316 {
317 Assert(!rtLinuxSysFsExists("devices/system/cpu/cpu%d/online", (int)idCpu));
318 i = 1;
319 }
320
321 Assert(i == 0 || i == -1 || i == 1);
322 return i != 0 && i != -1;
323}
324
325
326RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
327{
328 /** @todo check this up with hotplugging! */
329 return rtLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu);
330}
331
332
333RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
334{
335 RTCpuSetEmpty(pSet);
336 RTCPUID cMax = rtMpLinuxMaxCpus();
337 for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
338 if (RTMpIsCpuPossible(idCpu))
339 RTCpuSetAdd(pSet, idCpu);
340 return pSet;
341}
342
343
344RTDECL(RTCPUID) RTMpGetCount(void)
345{
346 RTCPUSET Set;
347 RTMpGetSet(&Set);
348 return RTCpuSetCount(&Set);
349}
350
351
352RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
353{
354 RTCpuSetEmpty(pSet);
355 RTCPUID cMax = rtMpLinuxMaxCpus();
356 for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
357 if (RTMpIsCpuOnline(idCpu))
358 RTCpuSetAdd(pSet, idCpu);
359 return pSet;
360}
361
362
363RTDECL(RTCPUID) RTMpGetOnlineCount(void)
364{
365 RTCPUSET Set;
366 RTMpGetOnlineSet(&Set);
367 return RTCpuSetCount(&Set);
368}
369
370
371RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
372{
373 int64_t kHz = rtLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq", (int)idCpu);
374 if (kHz == -1)
375 {
376 /*
377 * The file may be just unreadable - in that case use plan B, i.e.
378 * /proc/cpuinfo to get the data we want. The assumption is that if
379 * cpuinfo_cur_freq doesn't exist then the speed won't change, and
380 * thus cur == max. If it does exist then cpuinfo contains the
381 * current frequency.
382 */
383 kHz = rtMpLinuxGetFrequency(idCpu) * 1000;
384 }
385 return (kHz + 999) / 1000;
386}
387
388
389RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
390{
391 int64_t kHz = rtLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu);
392 if (kHz == -1)
393 {
394 /*
395 * Check if the file isn't there - if it is there, then /proc/cpuinfo
396 * would provide current frequency information, which is wrong.
397 */
398 if (!rtLinuxSysFsExists("devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu))
399 kHz = rtMpLinuxGetFrequency(idCpu) * 1000;
400 else
401 kHz = 0;
402 }
403 return (kHz + 999) / 1000;
404}
Note: See TracBrowser for help on using the repository browser.

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