VirtualBox

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

Last change on this file since 10470 was 10470, checked in by vboxsync, 16 years ago

warning

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.8 KB
Line 
1/* $Id: mp-linux.cpp 10470 2008-07-10 13:42:20Z 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.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 * 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 NOREF(cch);
113 Assert(cch < sizeof(szFilename) - cchPrefix - 1);
114
115 return open(szFilename, O_RDONLY, 0);
116}
117
118
119/**
120 * Opens a sysfs file.
121 *
122 * @returns The file descriptor. -1 and errno on failure.
123 * @param pszFormat The name format, without "/sys/".
124 * @param ... The format args.
125 */
126int rtLinuxSysFsOpen(const char *pszFormat, ...)
127{
128 va_list va;
129 va_start(va, pszFormat);
130 int fd = rtLinuxSysFsOpenV(pszFormat, va);
131 va_end(va);
132 return fd;
133}
134
135
136/**
137 * Closes a file opened with rtLinuxSysFsOpen or rtLinuxSysFsOpenV.
138 *
139 * @param fd
140 */
141void rtLinuxSysFsClose(int fd)
142{
143 int iSavedErrno = errno;
144 close(fd);
145 errno = iSavedErrno;
146}
147
148
149/**
150 * Closes a file opened with rtLinuxSysFsOpen or rtLinuxSysFsOpenV.
151 *
152 * @returns The number of bytes read. -1 and errno on failure.
153 * @param fd The file descriptor returned by rtLinuxSysFsOpen or rtLinuxSysFsOpenV.
154 * @param pszBuf Where to store the string.
155 * @param cchBuf The size of the buffer. Must be at least 2 bytes.
156 */
157ssize_t rtLinuxSysFsReadStr(int fd, char *pszBuf, size_t cchBuf)
158{
159 Assert(cchBuf > 1);
160 ssize_t cchRead = read(fd, pszBuf, cchBuf - 1);
161 pszBuf[cchRead >= 0 ? cchRead : 0] = '\0';
162 return cchRead;
163}
164
165
166/**
167 * Reads a sysfs file.
168 *
169 * @returns 64-bit signed value on success, -1 and errno on failure.
170 * @param uBase The number base, 0 for autodetect.
171 * @param pszFormat The filename format, without "/sys/".
172 * @param va Format args.
173 */
174int64_t rtLinuxSysFsReadIntFileV(unsigned uBase, const char *pszFormat, va_list va)
175{
176 int fd = rtLinuxSysFsOpenV(pszFormat, va);
177 if (fd == -1)
178 return -1;
179
180 int64_t i64Ret = -1;
181 char szNum[128];
182 ssize_t cchNum = rtLinuxSysFsReadStr(fd, szNum, sizeof(szNum));
183 if (cchNum > 0)
184 {
185 int rc = RTStrToInt64Ex(szNum, NULL, uBase, &i64Ret);
186 if (RT_FAILURE(rc))
187 {
188 i64Ret = -1;
189 errno = -ETXTBSY; /* just something that won't happen at read / open. */
190 }
191 }
192 else if (cchNum == 0)
193 errno = -ETXTBSY; /* just something that won't happen at read / open. */
194
195 rtLinuxSysFsClose(fd);
196 return i64Ret;
197}
198
199
200/**
201 * Reads a sysfs file.
202 *
203 * @returns 64-bit signed value on success, -1 and errno on failure.
204 * @param uBase The number base, 0 for autodetect.
205 * @param pszFormat The filename format, without "/sys/".
206 * @param ... Format args.
207 */
208static int64_t rtLinuxSysFsReadIntFile(unsigned uBase, const char *pszFormat, ...)
209{
210 va_list va;
211 va_start(va, pszFormat);
212 int64_t i64Ret = rtLinuxSysFsReadIntFileV(uBase, pszFormat, va);
213 va_end(va);
214 return i64Ret;
215}
216
217
218/**
219 * Internal worker that determins the max possible CPU count.
220 *
221 * @returns Max cpus.
222 */
223static RTCPUID rtMpLinuxMaxCpus(void)
224{
225#if 0 /* this doesn't do the right thing :-/ */
226 int cMax = sysconf(_SC_NPROCESSORS_CONF);
227 Assert(cMax >= 1);
228 return cMax;
229#else
230 static uint32_t s_cMax = 0;
231 if (!s_cMax)
232 {
233 int cMax = 1;
234 for (unsigned iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
235 if (rtLinuxSysFsExists("devices/system/cpu/cpu%d", iCpu))
236 cMax = iCpu + 1;
237 ASMAtomicUoWriteU32((uint32_t volatile *)&s_cMax, cMax);
238 return cMax;
239 }
240 return s_cMax;
241#endif
242}
243
244/**
245 * Internal worker that picks the processor speed in MHz from /proc/cpuinfo.
246 *
247 * @returns CPU frequency.
248 */
249static uint32_t rtMpLinuxGetFrequency(RTCPUID idCpu)
250{
251 FILE *f = fopen("/proc/cpuinfo", "r");
252 if (!f)
253 return 0;
254
255 char sz[256];
256 char *psz = NULL;
257 RTCPUID idCpuFound = NIL_RTCPUID;
258 uint32_t freq = 0;
259 while (fgets(sz, sizeof(sz), f))
260 {
261 if ( !strncmp(sz, "processor", 9)
262 && (sz[10] == ' ' || sz[10] == '\t' || sz[10] == ':')
263 && (psz = strchr(sz, ':')))
264 {
265 psz += 2;
266 int64_t iCpu;
267 int rc = RTStrToInt64Ex(psz, NULL, 0, &iCpu);
268 if (RT_SUCCESS(rc))
269 idCpuFound = iCpu;
270 }
271 else if ( idCpu == idCpuFound
272 && !strncmp(sz, "cpu MHz", 7)
273 && (sz[10] == ' ' || sz[10] == '\t' || sz[10] == ':')
274 && (psz = strchr(sz, ':')))
275 {
276 psz += 2;
277 int64_t v;
278 int rc = RTStrToInt64Ex(psz, &psz, 0, &v);
279 if (RT_SUCCESS(rc))
280 {
281 freq = v;
282 break;
283 }
284 }
285 }
286 fclose(f);
287 return freq;
288}
289
290
291/** @todo RTmpCpuId(). */
292
293RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
294{
295 return idCpu < rtMpLinuxMaxCpus() ? idCpu : -1;
296}
297
298
299RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
300{
301 return (unsigned)iCpu < rtMpLinuxMaxCpus() ? iCpu : NIL_RTCPUID;
302}
303
304
305RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
306{
307 return rtMpLinuxMaxCpus() - 1;
308}
309
310
311RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
312{
313 /** @todo check if there is a simpler interface than this... */
314 int i = rtLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/online", (int)idCpu);
315 if ( i == -1
316 && rtLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu))
317 {
318 Assert(!rtLinuxSysFsExists("devices/system/cpu/cpu%d/online", (int)idCpu));
319 i = 1;
320 }
321
322 Assert(i == 0 || i == -1 || i == 1);
323 return i != 0 && i != -1;
324}
325
326
327RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
328{
329 /** @todo check this up with hotplugging! */
330 return rtLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu);
331}
332
333
334RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
335{
336 RTCpuSetEmpty(pSet);
337 RTCPUID cMax = rtMpLinuxMaxCpus();
338 for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
339 if (RTMpIsCpuPossible(idCpu))
340 RTCpuSetAdd(pSet, idCpu);
341 return pSet;
342}
343
344
345RTDECL(RTCPUID) RTMpGetCount(void)
346{
347 RTCPUSET Set;
348 RTMpGetSet(&Set);
349 return RTCpuSetCount(&Set);
350}
351
352
353RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
354{
355 RTCpuSetEmpty(pSet);
356 RTCPUID cMax = rtMpLinuxMaxCpus();
357 for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
358 if (RTMpIsCpuOnline(idCpu))
359 RTCpuSetAdd(pSet, idCpu);
360 return pSet;
361}
362
363
364RTDECL(RTCPUID) RTMpGetOnlineCount(void)
365{
366 RTCPUSET Set;
367 RTMpGetOnlineSet(&Set);
368 return RTCpuSetCount(&Set);
369}
370
371
372RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
373{
374 int64_t kHz = rtLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq", (int)idCpu);
375 if (kHz == -1)
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 kHz = rtMpLinuxGetFrequency(idCpu) * 1000;
383 }
384 return (kHz + 999) / 1000;
385}
386
387
388RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
389{
390 int64_t kHz = rtLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu);
391 if (kHz == -1)
392 {
393 /* Check if the file isn't there - if it is there, then /proc/cpuinfo
394 * would provide current frequency information, which is wrong. */
395 if (!rtLinuxSysFsExists("devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu))
396 kHz = rtMpLinuxGetFrequency(idCpu) * 1000;
397 else
398 kHz = 0;
399 }
400 return (kHz + 999) / 1000;
401}
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