VirtualBox

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

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