VirtualBox

Changeset 15399 in vbox


Ignore:
Timestamp:
Dec 12, 2008 10:02:14 PM (16 years ago)
Author:
vboxsync
Message:

iprt: new Linux sysfs APIs

Location:
trunk
Files:
1 added
1 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/linux/sysfs.h

    r14990 r15399  
     1/* $Id$ */
    12/** @file
    2  * IPRT - Multiprocessor.
     3 * IPRT - Linux sysfs access.
    34 */
    45
     
    2829 */
    2930
    30 #ifndef ___iprt_mp_h
    31 #define ___iprt_mp_h
     31#ifndef ___iprt_linux_sysfs_h
     32#define ___iprt_linux_sysfs_h
    3233
    3334#include <iprt/cdefs.h>
    3435#include <iprt/types.h>
    3536
     37#include <stdarg.h>
    3638
    3739__BEGIN_DECLS
    3840
    39 /** @defgroup grp_rt_mp RTMp - Multiprocessor
     41/** @defgroup grp_rt_mp RTLinuxSysfs - Linux sysfs
    4042 * @ingroup grp_rt
    4143 * @{
     
    4345
    4446/**
    45  * Gets the identifier of the CPU executing the call.
     47 * Checks if a sysfs file (or directory, device, symlink, whatever) exists.
    4648 *
    47  * When called from a system mode where scheduling is active, like ring-3 or
    48  * kernel mode with interrupts enabled on some systems, no assumptions should
    49  * be made about the current CPU when the call returns.
    50  *
    51  * @returns CPU Id.
     49 * @returns true / false, errno is preserved.
     50 * @param   pszFormat   The name format, either absolute or relative to "/sys/".
     51 * @param   va          The format args.
    5252 */
    53 RTDECL(RTCPUID) RTMpCpuId(void);
     53RTDECL(bool) RTLinuxSysFsExistsV(const char *pszFormat, va_list va);
    5454
    5555/**
    56  * Converts a CPU identifier to a CPU set index.
     56 * Checks if a sysfs file (or directory, device, symlink, whatever) exists.
    5757 *
    58  * This may or may not validate the presence of the CPU.
    59  *
    60  * @returns The CPU set index on success, -1 on failure.
    61  * @param   idCpu       The identifier of the CPU.
     58 * @returns true / false, errno is preserved.
     59 * @param   pszFormat   The name format, either absolute or relative to "/sys/".
     60 * @param   ...         The format args.
    6261 */
    63 RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu);
     62RTDECL(bool) RTLinuxSysFsExists(const char *pszFormat, ...);
    6463
    6564/**
    66  * Converts a CPU set index to a a CPU identifier.
     65 * Opens a sysfs file.
    6766 *
    68  * This may or may not validate the presence of the CPU, so, use
    69  * RTMpIsCpuPossible for that.
    70  *
    71  * @returns The corresponding CPU identifier, NIL_RTCPUID on failure.
    72  * @param   iCpu    The CPU set index.
     67 * @returns The file descriptor. -1 and errno on failure.
     68 * @param   pszFormat   The name format, either absolute or relative to "/sys/".
     69 * @param   va          The format args.
    7370 */
    74 RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu);
     71RTDECL(int) RTLinuxSysFsOpenV(const char *pszFormat, va_list va);
    7572
    7673/**
    77  * Gets the max CPU identifier (inclusive).
     74 * Opens a sysfs file.
    7875 *
    79  * Inteded for brute force enumerations, but use with
    80  * care as it may be expensive.
    81  *
    82  * @returns The current higest CPU identifier value.
     76 * @returns The file descriptor. -1 and errno on failure.
     77 * @param   pszFormat   The name format, either absolute or relative to "/sys/".
     78 * @param   ...         The format args.
    8379 */
    84 RTDECL(RTCPUID) RTMpGetMaxCpuId(void);
    85 
     80RTDECL(int) RTLinuxSysFsOpen(const char *pszFormat, ...);
    8681
    8782/**
    88  * Checks if a CPU exists in the system or may possibly be hotplugged later.
     83 * Closes a file opened with RTLinuxSysFsOpen or RTLinuxSysFsOpenV.
    8984 *
    90  * @returns true/false accordingly.
    91  * @param   idCpu       The identifier of the CPU.
     85 * @param   fd
    9286 */
    93 RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu);
     87RTDECL(void) RTLinuxSysFsClose(int fd);
    9488
    9589/**
    96  * Gets set of the CPUs present in the system pluss any that may
    97  * possibly be hotplugged later.
     90 * Reads a string from a file opened with RTLinuxSysFsOpen or RTLinuxSysFsOpenV.
    9891 *
    99  * @returns pSet.
    100  * @param   pSet    Where to put the set.
     92 * @returns The number of bytes read. -1 and errno on failure.
     93 * @param   fd          The file descriptor returned by RTLinuxSysFsOpen or RTLinuxSysFsOpenV.
     94 * @param   pszBuf      Where to store the string.
     95 * @param   cchBuf      The size of the buffer. Must be at least 2 bytes.
    10196 */
    102 RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet);
     97RTDECL(ssize_t) RTLinuxSysFsReadStr(int fd, char *pszBuf, size_t cchBuf);
    10398
    10499/**
    105  * Get the count of CPUs present in the system plus any that may
    106  * possibly be hotplugged later.
     100 * Reads a number from a sysfs file.
    107101 *
    108  * @return The count.
     102 * @returns 64-bit signed value on success, -1 and errno on failure.
     103 * @param   uBase       The number base, 0 for autodetect.
     104 * @param   pszFormat   The filename format, either absolute or relative to "/sys/".
     105 * @param   va          Format args.
    109106 */
    110 RTDECL(RTCPUID) RTMpGetCount(void);
    111 
     107RTDECL(int64_t) RTLinuxSysFsReadIntFileV(unsigned uBase, const char *pszFormat, va_list va);
    112108
    113109/**
    114  * Gets set of the CPUs present that are currently online.
     110 * Reads a number from a sysfs file.
    115111 *
    116  * @returns pSet.
    117  * @param   pSet    Where to put the set.
     112 * @returns 64-bit signed value on success, -1 and errno on failure.
     113 * @param   uBase       The number base, 0 for autodetect.
     114 * @param   pszFormat   The filename format, either absolute or relative to "/sys/".
     115 * @param   ...         Format args.
    118116 */
    119 RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet);
     117RTDECL(int64_t) RTLinuxSysFsReadIntFile(unsigned uBase, const char *pszFormat, ...);
    120118
    121119/**
    122  * Get the count of CPUs that are currently online.
     120 * Reads a string from a sysfs file.  If the file contains a newline, we only
     121 * return the text up until there.
    123122 *
    124  * @return The count.
     123 * @returns number of characters read on success, -1 and errno on failure.
     124 * @param   pszBuf      Where to store the path element.  Must be at least two
     125 *                      characters, but a longer buffer would be advisable.
     126 * @param   cchBuf      The size of the buffer pointed to by @a pszBuf.
     127 * @param   pszFormat   The filename format, either absolute or relative to "/sys/".
     128 * @param   va          Format args.
    125129 */
    126 RTDECL(RTCPUID) RTMpGetOnlineCount(void);
     130RTDECL(ssize_t) RTLinuxSysFsReadStrFileV(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va);
    127131
    128132/**
    129  * Checks if a CPU is online or not.
     133 * Reads a string from a sysfs file.  If the file contains a newline, we only
     134 * return the text up until there.
    130135 *
    131  * @returns true/false accordingly.
    132  * @param   idCpu       The identifier of the CPU.
     136 * @returns number of characters read on success, -1 and errno on failure.
     137 * @param   pszBuf      Where to store the path element.  Must be at least two
     138 *                      characters, but a longer buffer would be advisable.
     139 * @param   cchBuf      The size of the buffer pointed to by @a pszBuf.
     140 * @param   pszFormat   The filename format, either absolute or relative to "/sys/".
     141 * @param   ...         Format args.
    133142 */
    134 RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu);
    135 
     143RTDECL(ssize_t) RTLinuxSysFsReadStrFile(char *pszBuf, size_t cchBuf, const char *pszFormat, ...);
    136144
    137145/**
    138  * Gets set of the CPUs present in the system.
     146 * Reads the last element of the path of the file pointed to by the symbolic
     147 * link specified.  This is needed at least to get the name of the driver
     148 * associated with a device, where pszFormat should be the "driver" link in the
     149 * devices sysfs directory.
    139150 *
    140  * @returns pSet.
    141  * @param   pSet    Where to put the set.
     151 * @returns The number of characters written on success, -1 and errno on failure.
     152 * @param   pszBuf      Where to store the path element.  Must be at least two
     153 *                      characters, but a longer buffer would be advisable.
     154 * @param   cchBuf      The size of the buffer pointed to by @a pszBuf.
     155 * @param   pszFormat   The filename format, either absolute or relative to "/sys/".
     156 * @param   ...         Format args.
    142157 */
    143 RTDECL(PRTCPUSET) RTMpGetPresentSet(PRTCPUSET pSet);
    144 
    145 /**
    146  * Get the count of CPUs that are present in the system.
    147  *
    148  * @return The count.
    149  */
    150 RTDECL(RTCPUID) RTMpGetPresentCount(void);
    151 
    152 /**
    153  * Checks if a CPU is present in the system.
    154  *
    155  * @returns true/false accordingly.
    156  * @param   idCpu       The identifier of the CPU.
    157  */
    158 RTDECL(bool) RTMpIsCpuPresent(RTCPUID idCpu);
    159 
    160 
    161 /**
    162  * Get the current frequency of a CPU.
    163  *
    164  * The CPU must be online.
    165  *
    166  * @returns The frequency as MHz. 0 if the CPU is offline
    167  *          or the information is not available.
    168  * @param   idCpu       The identifier of the CPU.
    169  */
    170 RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu);
    171 
    172 
    173 /**
    174  * Get the maximum frequency of a CPU.
    175  *
    176  * The CPU must be online.
    177  *
    178  * @returns The frequency as MHz. 0 if the CPU is offline
    179  *          or the information is not available.
    180  * @param   idCpu       The identifier of the CPU.
    181  */
    182 RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu);
    183 
    184 
    185 #ifdef IN_RING0
    186 
    187 /**
    188  * Worker function passed to RTMpOnAll, RTMpOnOthers and RTMpOnSpecific that
    189  * is to be called on the target cpus.
    190  *
    191  * @param   idCpu       The identifier for the CPU the function is called on.
    192  * @param   pvUser1     The 1st user argument.
    193  * @param   pvUser2     The 2nd user argument.
    194  */
    195 typedef DECLCALLBACK(void) FNRTMPWORKER(RTCPUID idCpu, void *pvUser1, void *pvUser2);
    196 /** Pointer to a FNRTMPWORKER. */
    197 typedef FNRTMPWORKER *PFNRTMPWORKER;
    198 
    199 /**
    200  * Executes a function on each (online) CPU in the system.
    201  *
    202  * @returns IPRT status code.
    203  * @retval  VINF_SUCCESS on success.
    204  * @retval  VERR_NOT_SUPPORTED if this kind of operation isn't supported by the system.
    205  *
    206  * @param   pfnWorker       The worker function.
    207  * @param   pvUser1         The first user argument for the worker.
    208  * @param   pvUser2         The second user argument for the worker.
    209  *
    210  * @remarks The execution isn't in any way guaranteed to be simultaneous,
    211  *          it might even be serial (cpu by cpu).
    212  */
    213 RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2);
    214 
    215 /**
    216  * Executes a function on a all other (online) CPUs in the system.
    217  *
    218  * @returns IPRT status code.
    219  * @retval  VINF_SUCCESS on success.
    220  * @retval  VERR_NOT_SUPPORTED if this kind of operation isn't supported by the system.
    221  *
    222  * @param   pfnWorker       The worker function.
    223  * @param   pvUser1         The first user argument for the worker.
    224  * @param   pvUser2         The second user argument for the worker.
    225  *
    226  * @remarks The execution isn't in any way guaranteed to be simultaneous,
    227  *          it might even be serial (cpu by cpu).
    228  */
    229 RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2);
    230 
    231 /**
    232  * Executes a function on a specific CPU in the system.
    233  *
    234  * @returns IPRT status code.
    235  * @retval  VINF_SUCCESS on success.
    236  * @retval  VERR_NOT_SUPPORTED if this kind of operation isn't supported by the system.
    237  * @retval  VERR_CPU_OFFLINE if the CPU is offline.
    238  * @retval  VERR_CPU_NOT_FOUND if the CPU wasn't found.
    239  *
    240  * @param   idCpu           The id of the CPU.
    241  * @param   pfnWorker       The worker function.
    242  * @param   pvUser1         The first user argument for the worker.
    243  * @param   pvUser2         The second user argument for the worker.
    244  */
    245 RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2);
    246 
    247 
    248 /**
    249  * MP event, see FNRTMPNOTIFICATION.
    250  */
    251 typedef enum RTMPEVENT
    252 {
    253     /** The CPU goes online. */
    254     RTMPEVENT_ONLINE = 1,
    255     /** The CPU goes offline. */
    256     RTMPEVENT_OFFLINE
    257 } RTMPEVENT;
    258 
    259 /**
    260  * Notification callback.
    261  *
    262  * The context this is called in differs a bit from platform to
    263  * platform, so be careful while in here.
    264  *
    265  * @param   idCpu       The CPU this applies to.
    266  * @param   enmEvent    The event.
    267  * @param   pvUser      The user argument.
    268  */
    269 typedef DECLCALLBACK(void) FNRTMPNOTIFICATION(RTMPEVENT enmEvent, RTCPUID idCpu, void *pvUser);
    270 /** Pointer to a FNRTMPNOTIFICATION(). */
    271 typedef FNRTMPNOTIFICATION *PFNRTMPNOTIFICATION;
    272 
    273 /**
    274  * Registers a notification callback for cpu events.
    275  *
    276  * On platforms which doesn't do cpu offline/online events this API
    277  * will just be a no-op that pretends to work.
    278  *
    279  * @todo We'll be adding a flag to this soon to indicate whether the callback should be called on all
    280  *       CPUs that are currently online while it's being registered. This is to help avoid some race
    281  *       conditions (we'll hopefully be able to implement this on linux, solaris/win is no issue).
    282  *
    283  * @returns IPRT status code.
    284  * @retval  VINF_SUCCESS on success.
    285  * @retval  VERR_NO_MEMORY if a registration record cannot be allocated.
    286  * @retval  VERR_ALREADY_EXISTS if the pfnCallback and pvUser already exist
    287  *          in the callback list.
    288  *
    289  * @param   pfnCallback     The callback.
    290  * @param   pvUser          The user argument to the callback function.
    291  */
    292 RTDECL(int) RTMpNotificationRegister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser);
    293 
    294 /**
    295  * This deregisters a notification callback registered via RTMpNotificationRegister().
    296  *
    297  * The pfnCallback and pvUser arguments must be identical to the registration call
    298  * of we won't find the right entry.
    299  *
    300  * @returns IPRT status code.
    301  * @retval  VINF_SUCCESS on success.
    302  * @retval  VERR_NOT_FOUND if no matching entry was found.
    303  *
    304  * @param   pfnCallback     The callback.
    305  * @param   pvUser          The user argument to the callback function.
    306  */
    307 RTDECL(int) RTMpNotificationDeregister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser);
    308 
    309 #endif /* IN_RING0 */
     158RTDECL(int) RTLinuxSysFsGetLinkDest(char *pszBuf, size_t cchBuf, const char *pszFormat, ...);
    310159
    311160/** @} */
  • trunk/src/VBox/Runtime/r3/linux/mp-linux.cpp

    r10482 r15399  
    3434*******************************************************************************/
    3535#define LOG_GROUP RTLOGGROUP_SYSTEM
    36 #include <unistd.h>
    3736#include <stdio.h>
    38 #include <sys/sysctl.h>
    39 #include <sys/stat.h>
    40 #include <sys/fcntl.h>
    4137#include <errno.h>
    4238
     
    4541#include <iprt/assert.h>
    4642#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  */
    58 bool 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  */
    85 bool 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  */
    102 int 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  */
    124 int 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  */
    139 void 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  */
    155 ssize_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  */
    172 int64_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  */
    206 static 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 }
     43#include <iprt/linux/sysfs.h>
    21444
    21545
     
    23161        int cMax = 1;
    23262        for (unsigned iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
    233             if (rtLinuxSysFsExists("devices/system/cpu/cpu%d", iCpu))
     63            if (RTLinuxSysFsExists("devices/system/cpu/cpu%d", iCpu))
    23464                cMax = iCpu + 1;
    23565        ASMAtomicUoWriteU32((uint32_t volatile *)&s_cMax, cMax);
     
    310140{
    311141    /** @todo check if there is a simpler interface than this... */
    312     int i = rtLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/online", (int)idCpu);
     142    int i = RTLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/online", (int)idCpu);
    313143    if (    i == -1
    314         &&  rtLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu))
    315     {
    316         Assert(!rtLinuxSysFsExists("devices/system/cpu/cpu%d/online", (int)idCpu));
     144        &&  RTLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu))
     145    {
     146        Assert(!RTLinuxSysFsExists("devices/system/cpu/cpu%d/online", (int)idCpu));
    317147        i = 1;
    318148    }
     
    326156{
    327157    /** @todo check this up with hotplugging! */
    328     return rtLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu);
     158    return RTLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu);
    329159}
    330160
     
    370200RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
    371201{
    372     int64_t kHz = rtLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq", (int)idCpu);
     202    int64_t kHz = RTLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq", (int)idCpu);
    373203    if (kHz == -1)
    374204    {
     
    388218RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
    389219{
    390     int64_t kHz = rtLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu);
     220    int64_t kHz = RTLinuxSysFsReadIntFile(0, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu);
    391221    if (kHz == -1)
    392222    {
     
    395225         * would provide current frequency information, which is wrong.
    396226         */
    397         if (!rtLinuxSysFsExists("devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu))
     227        if (!RTLinuxSysFsExists("devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu))
    398228            kHz = rtMpLinuxGetFrequency(idCpu) * 1000;
    399229        else
  • trunk/src/VBox/Runtime/r3/linux/sysfs.cpp

    r14990 r15399  
    11/* $Id$ */
    22/** @file
    3  * IPRT - Multiprocessor, Linux.
     3 * IPRT - Linux sysfs access.
    44 */
    55
     
    4141#include <errno.h>
    4242
    43 #include <iprt/mp.h>
    44 #include <iprt/cpuset.h>
     43#include <iprt/linux/sysfs.h>
    4544#include <iprt/assert.h>
     45#include <iprt/param.h>
    4646#include <iprt/string.h>
    4747
    4848
    49 /** @todo move the rtLinuxSysFs* bits into sysfs.cpp and sysfs.h. */
     49/**
     50 * Constructs the path of a sysfs file from the format paramaters passed,
     51 * prepending "/sys/" if the path is relative.
     52 *
     53 * @returns The number of characters written, or -1 and errno on failure.
     54 * @param   pszBuf     Where to write the path.  Must be at least
     55 *                     sizeof("/sys/") characters long
     56 * @param   cchBuf     The size of the buffer pointed to by @a pszBuf
     57 * @param   pszFormat  The name format, either absolute or relative to "/sys/".
     58 * @param   va         The format args.
     59 */
     60static ssize_t rtLinuxSysFsConstructPath(char *pszBuf, size_t cchBuf,
     61                                         const char *pszFormat, va_list va)
     62{
     63    char szFilename[RTPATH_MAX];
     64    const char szPrefix[] = "/sys/";
     65    if (cchBuf < sizeof(szPrefix))
     66        AssertFailedReturnStmt(errno = EINVAL, -1);
     67    size_t cch = RTStrPrintfV(szFilename, sizeof(szFilename), pszFormat, va);
     68    if (szFilename[0] == '/')
     69        *pszBuf = '\0';
     70    else
     71        strcpy(pszBuf, szPrefix);
     72    strncat(pszBuf, szFilename, cchBuf);
     73    return strlen(pszBuf);
     74}
    5075
    5176/**
     
    5378 *
    5479 * @returns true / false, errno is preserved.
    55  * @param   pszFormat   The name format, without "/sys/".
     80 * @param   pszFormat   The name format, either absolute or relative to "/sys/".
    5681 * @param   va          The format args.
    5782 */
    58 bool rtLinuxSysFsExistsV(const char *pszFormat, va_list va)
     83bool RTLinuxSysFsExistsV(const char *pszFormat, va_list va)
    5984{
    6085    int iSavedErrno = errno;
     
    6388     * Construct the filename and call stat.
    6489     */
    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;
     90    char szFilename[RTPATH_MAX];
     91    bool fRet = false;
     92    ssize_t rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename),
     93                                           pszFormat, va);
     94    if (rc > 0 && static_cast<size_t>(rc) < sizeof(szFilename))
     95    {
     96        struct stat st;
     97        fRet = stat(szFilename, &st) == 0;
     98    }
    7399
    74100    errno = iSavedErrno;
     
    80106 *
    81107 * @returns true / false, errno is preserved.
    82  * @param   pszFormat   The name format, without "/sys/".
     108 * @param   pszFormat   The name format, either absolute or relative to "/sys/".
    83109 * @param   ...         The format args.
    84110 */
    85 bool rtLinuxSysFsExists(const char *pszFormat, ...)
    86 {
    87     va_list va;
    88     va_start(va, pszFormat);
    89     bool fRet = rtLinuxSysFsExistsV(pszFormat, va);
     111bool RTLinuxSysFsExists(const char *pszFormat, ...)
     112{
     113    va_list va;
     114    va_start(va, pszFormat);
     115    bool fRet = RTLinuxSysFsExistsV(pszFormat, va);
    90116    va_end(va);
    91117    return fRet;
     
    97123 *
    98124 * @returns The file descriptor. -1 and errno on failure.
    99  * @param   pszFormat   The name format, without "/sys/".
     125 * @param   pszFormat   The name format, either absolute or relative to "/sys/".
    100126 * @param   va          The format args.
    101127 */
    102 int rtLinuxSysFsOpenV(const char *pszFormat, va_list va)
     128int RTLinuxSysFsOpenV(const char *pszFormat, va_list va)
    103129{
    104130    /*
    105131     * Construct the filename and call open.
    106132     */
    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);
     133    char szFilename[RTPATH_MAX];
     134    ssize_t rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename),
     135                                           pszFormat, va);
     136    if (rc > 0 && static_cast<size_t>(rc) < sizeof(szFilename))
     137        rc = open(szFilename, O_RDONLY, 0);
     138    return rc;
    114139}
    115140
     
    119144 *
    120145 * @returns The file descriptor. -1 and errno on failure.
    121  * @param   pszFormat   The name format, without "/sys/".
     146 * @param   pszFormat   The name format, either absolute or relative to "/sys/".
    122147 * @param   ...         The format args.
    123148 */
    124 int rtLinuxSysFsOpen(const char *pszFormat, ...)
    125 {
    126     va_list va;
    127     va_start(va, pszFormat);
    128     int fd = rtLinuxSysFsOpenV(pszFormat, va);
     149int RTLinuxSysFsOpen(const char *pszFormat, ...)
     150{
     151    va_list va;
     152    va_start(va, pszFormat);
     153    int fd = RTLinuxSysFsOpenV(pszFormat, va);
    129154    va_end(va);
    130155    return fd;
     
    133158
    134159/**
    135  * Closes a file opened with rtLinuxSysFsOpen or rtLinuxSysFsOpenV.
     160 * Closes a file opened with RTLinuxSysFsOpen or RTLinuxSysFsOpenV.
    136161 *
    137162 * @param   fd
    138163 */
    139 void rtLinuxSysFsClose(int fd)
     164void RTLinuxSysFsClose(int fd)
    140165{
    141166    int iSavedErrno = errno;
     
    146171
    147172/**
    148  * Closes a file opened with rtLinuxSysFsOpen or rtLinuxSysFsOpenV.
     173 * Reads a string from a file opened with RTLinuxSysFsOpen or RTLinuxSysFsOpenV.
    149174 *
    150175 * @returns The number of bytes read. -1 and errno on failure.
    151  * @param   fd          The file descriptor returned by rtLinuxSysFsOpen or rtLinuxSysFsOpenV.
     176 * @param   fd          The file descriptor returned by RTLinuxSysFsOpen or RTLinuxSysFsOpenV.
    152177 * @param   pszBuf      Where to store the string.
    153178 * @param   cchBuf      The size of the buffer. Must be at least 2 bytes.
    154179 */
    155 ssize_t rtLinuxSysFsReadStr(int fd, char *pszBuf, size_t cchBuf)
     180ssize_t RTLinuxSysFsReadStr(int fd, char *pszBuf, size_t cchBuf)
    156181{
    157182    Assert(cchBuf > 1);
     
    163188
    164189/**
    165  * Reads a sysfs file.
     190 * Reads a number from a sysfs file.
    166191 *
    167192 * @returns 64-bit signed value on success, -1 and errno on failure.
    168193 * @param   uBase       The number base, 0 for autodetect.
    169  * @param   pszFormat   The filename format, without "/sys/".
     194 * @param   pszFormat   The filename format, either absolute or relative to "/sys/".
    170195 * @param   va          Format args.
    171196 */
    172 int64_t rtLinuxSysFsReadIntFileV(unsigned uBase, const char *pszFormat, va_list va)
    173 {
    174     int fd = rtLinuxSysFsOpenV(pszFormat, va);
     197int64_t RTLinuxSysFsReadIntFileV(unsigned uBase, const char *pszFormat, va_list va)
     198{
     199    int fd = RTLinuxSysFsOpenV(pszFormat, va);
    175200    if (fd == -1)
    176201        return -1;
     
    178203    int64_t i64Ret = -1;
    179204    char szNum[128];
    180     ssize_t cchNum = rtLinuxSysFsReadStr(fd, szNum, sizeof(szNum));
     205    ssize_t cchNum = RTLinuxSysFsReadStr(fd, szNum, sizeof(szNum));
    181206    if (cchNum > 0)
    182207    {
     
    191216        errno = -ETXTBSY; /* just something that won't happen at read / open. */
    192217
    193     rtLinuxSysFsClose(fd);
     218    RTLinuxSysFsClose(fd);
    194219    return i64Ret;
    195220}
     
    197222
    198223/**
    199  * Reads a sysfs file.
     224 * Reads a number from a sysfs file.
    200225 *
    201226 * @returns 64-bit signed value on success, -1 and errno on failure.
    202227 * @param   uBase       The number base, 0 for autodetect.
    203  * @param   pszFormat   The filename format, without "/sys/".
     228 * @param   pszFormat   The filename format, either absolute or relative to "/sys/".
    204229 * @param   ...         Format args.
    205230 */
    206 static 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);
     231int64_t RTLinuxSysFsReadIntFile(unsigned uBase, const char *pszFormat, ...)
     232{
     233    va_list va;
     234    va_start(va, pszFormat);
     235    int64_t i64Ret = RTLinuxSysFsReadIntFileV(uBase, pszFormat, va);
    211236    va_end(va);
    212237    return i64Ret;
     
    215240
    216241/**
    217  * Internal worker that determins the max possible CPU count.
    218  *
    219  * @returns Max cpus.
    220  */
    221 static 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  */
    247 static 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 
    291 RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
    292 {
    293     return idCpu < rtMpLinuxMaxCpus() ? idCpu : -1;
    294 }
    295 
    296 
    297 RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
    298 {
    299     return (unsigned)iCpu < rtMpLinuxMaxCpus() ? iCpu : NIL_RTCPUID;
    300 }
    301 
    302 
    303 RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
    304 {
    305     return rtMpLinuxMaxCpus() - 1;
    306 }
    307 
    308 
    309 RTDECL(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 
    325 RTDECL(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 
    332 RTDECL(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 
    343 RTDECL(RTCPUID) RTMpGetCount(void)
    344 {
    345     RTCPUSET Set;
    346     RTMpGetSet(&Set);
    347     return RTCpuSetCount(&Set);
    348 }
    349 
    350 
    351 RTDECL(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 
    362 RTDECL(RTCPUID) RTMpGetOnlineCount(void)
    363 {
    364     RTCPUSET Set;
    365     RTMpGetOnlineSet(&Set);
    366     return RTCpuSetCount(&Set);
    367 }
    368 
    369 
    370 RTDECL(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 
    388 RTDECL(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 }
     242 * Reads a string from a sysfs file.  If the file contains a newline, we only
     243 * return the text up until there.
     244 *
     245 * @returns number of characters read on success, -1 and errno on failure.
     246 * @param   pszBuf      Where to store the path element.  Must be at least two
     247 *                      characters, but a longer buffer would be advisable.
     248 * @param   cchBuf      The size of the buffer pointed to by @a pszBuf.
     249 * @param   pszFormat   The filename format, either absolute or relative to "/sys/".
     250 * @param   va          Format args.
     251 */
     252ssize_t RTLinuxSysFsReadStrFileV(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va)
     253{
     254    int fd = RTLinuxSysFsOpenV(pszFormat, va);
     255    if (fd == -1)
     256        return -1;
     257
     258    ssize_t cchRet = RTLinuxSysFsReadStr(fd, pszBuf, cchBuf);
     259    RTLinuxSysFsClose(fd);
     260    char *pchNewLine = NULL;
     261    if (cchRet > 0)
     262        pchNewLine = reinterpret_cast<char *>(memchr(pszBuf, '\n', cchRet));
     263    if (pchNewLine != NULL)
     264        *pchNewLine = '\0';
     265    return cchRet;
     266}
     267
     268
     269/**
     270 * Reads a string from a sysfs file.  If the file contains a newline, we only
     271 * return the text up until there.
     272 *
     273 * @returns number of characters read on success, -1 and errno on failure.
     274 * @param   pszBuf      Where to store the path element.  Must be at least two
     275 *                      characters, but a longer buffer would be advisable.
     276 * @param   cchBuf      The size of the buffer pointed to by @a pszBuf.
     277 * @param   pszFormat   The filename format, either absolute or relative to "/sys/".
     278 * @param   ...         Format args.
     279 */
     280ssize_t RTLinuxSysFsReadStrFile(char *pszBuf, size_t cchBuf, const char *pszFormat, ...)
     281{
     282    va_list va;
     283    va_start(va, pszFormat);
     284    ssize_t cchRet = RTLinuxSysFsReadStrFileV(pszBuf, cchBuf, pszFormat, va);
     285    va_end(va);
     286    return cchRet;
     287}
     288
     289
     290/**
     291 * Reads the last element of the path of the file pointed to by the symbolic
     292 * link specified.  This is needed at least to get the name of the driver
     293 * associated with a device, where pszFormat should be the "driver" link in the
     294 * devices sysfs directory.
     295 *
     296 * @returns The number of characters written on success, -1 and errno on failure.
     297 * @param   pszBuf      Where to store the path element.  Must be at least two
     298 *                      characters, but a longer buffer would be advisable.
     299 * @param   cchBuf      The size of the buffer pointed to by @a pszBuf.
     300 * @param   pszFormat   The filename format, either absolute or relative to "/sys/".
     301 * @param   ...         Format args.
     302 */
     303int RTLinuxSysFsGetLinkDest(char *pszBuf, size_t cchBuf, const char *pszFormat, ...)
     304{
     305    if (cchBuf < 2)
     306        AssertFailedReturnStmt(errno = EINVAL, -1);
     307    va_list va;
     308    va_start(va, pszFormat);
     309    char szFilename[RTPATH_MAX], szLink[RTPATH_MAX];
     310    int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
     311    va_end(va);
     312    if (rc == sizeof(szFilename))
     313    {
     314        rc = -1;
     315        errno = EINVAL;  /* Bad format arguements */
     316    }
     317    if (rc > 0)
     318        rc = readlink(szFilename, szLink, sizeof(szLink));
     319    if (rc > 0 && static_cast<size_t>(rc) > sizeof(szLink) - 1)
     320    {
     321        rc = -1;
     322        errno = EIO;  /* The path name can't be this big. */
     323    }
     324    if (rc >= 0)
     325    {
     326        szLink[rc] = '\0';
     327        char *lastPart = strrchr(szLink, '/');
     328        Assert(lastPart != NULL);  /* rtLinuxSysFsConstructPath guarantees a path
     329                                    * starting with '/'. */
     330        *pszBuf = '\0';
     331        strncat(pszBuf, lastPart + 1, cchBuf);
     332        rc = strlen(pszBuf);
     333    }
     334    return rc;
     335}
     336
Note: See TracChangeset for help on using the changeset viewer.

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