557 lines
15 KiB
C++
557 lines
15 KiB
C++
/*
|
|
* Copyright (C) 2020-2025 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
*/
|
|
|
|
#include "level_zero/tools/source/sysman/linux/fs_access.h"
|
|
|
|
#include <climits>
|
|
|
|
#include <array>
|
|
#include <cerrno>
|
|
#include <csignal>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
|
|
namespace L0 {
|
|
|
|
static ze_result_t getResult(int err) {
|
|
if ((EPERM == err) || (EACCES == err)) {
|
|
return ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS;
|
|
} else if (ENOENT == err) {
|
|
return ZE_RESULT_ERROR_NOT_AVAILABLE;
|
|
} else if (EBUSY == err) {
|
|
return ZE_RESULT_ERROR_HANDLE_OBJECT_IN_USE;
|
|
} else {
|
|
return ZE_RESULT_ERROR_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
void FdCache::eraseLeastUsedEntryFromCache() {
|
|
auto it = fdMap.begin();
|
|
uint32_t fdCountRef = it->second.second;
|
|
std::map<std::string, std::pair<int, uint32_t>>::iterator leastUsedIterator = it;
|
|
while (++it != fdMap.end()) {
|
|
if (it->second.second < fdCountRef) {
|
|
fdCountRef = it->second.second;
|
|
leastUsedIterator = it;
|
|
}
|
|
}
|
|
NEO::SysCalls::close(leastUsedIterator->second.first);
|
|
fdMap.erase(leastUsedIterator);
|
|
}
|
|
|
|
int FdCache::getFd(std::string file) {
|
|
int fd = -1;
|
|
if (fdMap.find(file) == fdMap.end()) {
|
|
fd = NEO::SysCalls::open(file.c_str(), O_RDONLY);
|
|
if (fd < 0) {
|
|
return -1;
|
|
}
|
|
if (fdMap.size() == maxSize) {
|
|
eraseLeastUsedEntryFromCache();
|
|
}
|
|
fdMap[file] = std::make_pair(fd, 1);
|
|
} else {
|
|
auto &fdPair = fdMap[file];
|
|
fdPair.second++;
|
|
}
|
|
return fdMap[file].first;
|
|
}
|
|
|
|
FdCache::~FdCache() {
|
|
for (auto it = fdMap.begin(); it != fdMap.end(); ++it) {
|
|
NEO::SysCalls::close(it->second.first);
|
|
}
|
|
fdMap.clear();
|
|
}
|
|
|
|
template <typename T>
|
|
ze_result_t FsAccess::readValue(const std::string file, T &val) {
|
|
auto lock = this->obtainMutex();
|
|
|
|
std::string readVal(64, '\0');
|
|
int fd = pFdCache->getFd(file);
|
|
if (fd < 0) {
|
|
return getResult(errno);
|
|
}
|
|
|
|
ssize_t bytesRead = NEO::SysCalls::pread(fd, readVal.data(), readVal.size(), 0);
|
|
if (bytesRead < 0) {
|
|
return getResult(errno);
|
|
}
|
|
|
|
std::istringstream stream(readVal);
|
|
stream >> val;
|
|
if (stream.fail()) {
|
|
return ZE_RESULT_ERROR_UNKNOWN;
|
|
}
|
|
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
|
|
// Generic Filesystem Access
|
|
FsAccess::FsAccess() {
|
|
pFdCache = std::make_unique<FdCache>();
|
|
}
|
|
|
|
FsAccess::FsAccess(const FsAccess &fsAccess) : pFdCache(std::unique_ptr<FdCache>(new FdCache())) {}
|
|
|
|
FsAccess *FsAccess::create() {
|
|
return new FsAccess();
|
|
}
|
|
|
|
ze_result_t FsAccess::read(const std::string file, uint64_t &val) {
|
|
return readValue<uint64_t>(file, val);
|
|
}
|
|
|
|
ze_result_t FsAccess::read(const std::string file, double &val) {
|
|
return readValue<double>(file, val);
|
|
}
|
|
|
|
ze_result_t FsAccess::read(const std::string file, int32_t &val) {
|
|
return readValue<int32_t>(file, val);
|
|
}
|
|
|
|
ze_result_t FsAccess::read(const std::string file, uint32_t &val) {
|
|
return readValue<uint32_t>(file, val);
|
|
}
|
|
|
|
ze_result_t FsAccess::read(const std::string file, std::string &val) {
|
|
// Read a single line from text file without trailing newline
|
|
std::ifstream fs;
|
|
val.clear();
|
|
|
|
fs.open(file.c_str());
|
|
if (fs.fail()) {
|
|
return getResult(errno);
|
|
}
|
|
fs >> val;
|
|
if (fs.fail()) {
|
|
fs.close();
|
|
return getResult(errno);
|
|
}
|
|
fs.close();
|
|
// Strip trailing newline
|
|
if (val.back() == '\n') {
|
|
val.pop_back();
|
|
}
|
|
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
|
|
ze_result_t FsAccess::read(const std::string file, std::vector<std::string> &val) {
|
|
// Read a entire text file, one line per vector entry
|
|
std::string line;
|
|
std::ifstream fs;
|
|
val.clear();
|
|
|
|
fs.open(file.c_str());
|
|
if (fs.fail()) {
|
|
return getResult(errno);
|
|
}
|
|
while (std::getline(fs, line)) {
|
|
if (fs.fail()) {
|
|
fs.close();
|
|
return getResult(errno);
|
|
}
|
|
if (line.back() == '\n') {
|
|
line.pop_back();
|
|
}
|
|
val.push_back(line);
|
|
}
|
|
fs.close();
|
|
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
|
|
ze_result_t FsAccess::write(const std::string file, const std::string val) {
|
|
|
|
int fd = NEO::SysCalls::open(file.c_str(), O_WRONLY);
|
|
if (fd < 0) {
|
|
return getResult(errno);
|
|
}
|
|
|
|
ssize_t bytesWritten = NEO::SysCalls::pwrite(fd, val.data(), val.size(), 0);
|
|
NEO::SysCalls::close(fd);
|
|
if (bytesWritten < 0) {
|
|
return getResult(errno);
|
|
}
|
|
|
|
if (bytesWritten != static_cast<ssize_t>(val.size())) {
|
|
return ZE_RESULT_ERROR_UNKNOWN;
|
|
}
|
|
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
|
|
ze_result_t FsAccess::canRead(const std::string file) {
|
|
struct stat sb;
|
|
if (statSyscall(file.c_str(), &sb) != 0) {
|
|
return ZE_RESULT_ERROR_UNKNOWN;
|
|
}
|
|
if (sb.st_mode & S_IRUSR) {
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
return ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS;
|
|
}
|
|
|
|
ze_result_t FsAccess::canWrite(const std::string file) {
|
|
struct stat sb;
|
|
if (statSyscall(file.c_str(), &sb) != 0) {
|
|
return ZE_RESULT_ERROR_UNKNOWN;
|
|
}
|
|
if (sb.st_mode & S_IWUSR) {
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
return ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS;
|
|
}
|
|
|
|
bool FsAccess::fileExists(const std::string file) {
|
|
if (NEO::SysCalls::access(file.c_str(), F_OK)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ze_result_t FsAccess::getFileMode(const std::string file, ::mode_t &mode) {
|
|
struct stat sb;
|
|
if (0 != stat(file.c_str(), &sb)) {
|
|
return getResult(errno);
|
|
}
|
|
mode = sb.st_mode;
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
|
|
ze_result_t FsAccess::readSymLink(const std::string path, std::string &val) {
|
|
// returns the value of symlink at path
|
|
char buf[PATH_MAX];
|
|
ssize_t len = ::readlink(path.c_str(), buf, PATH_MAX - 1);
|
|
if (len < 0) {
|
|
return getResult(errno);
|
|
}
|
|
buf[len] = '\0';
|
|
val = std::string(buf);
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
|
|
ze_result_t FsAccess::getRealPath(const std::string path, std::string &val) {
|
|
// returns the real file path after resolving all symlinks in path
|
|
char buf[PATH_MAX];
|
|
char *realPath = ::realpath(path.c_str(), buf);
|
|
if (!realPath) {
|
|
return getResult(errno);
|
|
}
|
|
val = std::string(buf);
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
|
|
ze_result_t FsAccess::listDirectory(const std::string path, std::vector<std::string> &list) {
|
|
list.clear();
|
|
::DIR *procDir = NEO::SysCalls::opendir(path.c_str());
|
|
if (!procDir) {
|
|
return getResult(errno);
|
|
}
|
|
struct ::dirent *ent;
|
|
int err = 0;
|
|
// readdir doesn't clear errno, so make sure it is clear
|
|
errno = 0;
|
|
while (NULL != (ent = NEO::SysCalls::readdir(procDir))) {
|
|
// Ignore . and ..
|
|
std::string name = std::string(ent->d_name);
|
|
if (!name.compare(".") || !name.compare("..")) {
|
|
errno = 0;
|
|
continue;
|
|
}
|
|
list.push_back(std::string(ent->d_name));
|
|
errno = 0;
|
|
}
|
|
err = errno;
|
|
NEO::SysCalls::closedir(procDir);
|
|
// Check if in above while loop, readdir encountered any error.
|
|
if ((err != 0) && (err != ENOENT)) {
|
|
list.clear();
|
|
return getResult(err);
|
|
}
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
|
|
std::string FsAccess::getBaseName(const std::string path) {
|
|
size_t pos = path.rfind("/");
|
|
if (std::string::npos == pos) {
|
|
return path;
|
|
}
|
|
return path.substr(pos + 1, std::string::npos);
|
|
}
|
|
|
|
std::string FsAccess::getDirName(const std::string path) {
|
|
size_t pos = path.rfind("/");
|
|
if (std::string::npos == pos) {
|
|
return std::string("");
|
|
}
|
|
// Include trailing slash
|
|
return path.substr(0, pos);
|
|
}
|
|
|
|
bool FsAccess::isRootUser() {
|
|
return (geteuid() == 0);
|
|
}
|
|
|
|
bool FsAccess::directoryExists(const std::string path) {
|
|
if (accessSyscall(path.c_str(), F_OK)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::unique_lock<std::mutex> FsAccess::obtainMutex() {
|
|
return std::unique_lock<std::mutex>(this->fsMutex);
|
|
}
|
|
|
|
// Procfs Access
|
|
const std::string ProcfsAccess::procDir = "/proc/";
|
|
const std::string ProcfsAccess::fdDir = "/fd/";
|
|
|
|
std::string ProcfsAccess::fullPath(const ::pid_t pid) {
|
|
// Returns the full path for proc entry for process pid
|
|
return std::string(procDir + std::to_string(pid));
|
|
}
|
|
|
|
std::string ProcfsAccess::fdDirPath(const ::pid_t pid) {
|
|
// Returns the full path to file descritpor directory
|
|
// for process pid
|
|
return std::string(fullPath(pid) + fdDir);
|
|
}
|
|
|
|
std::string ProcfsAccess::fullFdPath(const ::pid_t pid, const int fd) {
|
|
// Returns the full path for filedescriptor fd
|
|
// for process pid
|
|
return std::string(fdDirPath(pid) + std::to_string(fd));
|
|
}
|
|
|
|
ProcfsAccess *ProcfsAccess::create() {
|
|
return new ProcfsAccess();
|
|
}
|
|
|
|
ze_result_t ProcfsAccess::listProcesses(std::vector<::pid_t> &list) {
|
|
// Returns a vector with all the active process ids in the system
|
|
list.clear();
|
|
std::vector<std::string> dir;
|
|
ze_result_t result = FsAccess::listDirectory(procDir, dir);
|
|
if (ZE_RESULT_SUCCESS != result) {
|
|
return result;
|
|
}
|
|
for (auto &&file : dir) {
|
|
::pid_t pid;
|
|
std::istringstream stream(file);
|
|
stream >> pid;
|
|
if (stream.fail()) {
|
|
// Non numeric filename, not a process, skip
|
|
continue;
|
|
}
|
|
list.push_back(pid);
|
|
}
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
|
|
ze_result_t ProcfsAccess::getFileDescriptors(const ::pid_t pid, std::vector<int> &list) {
|
|
// Returns a vector with all the filedescriptor numbers opened by a pid
|
|
list.clear();
|
|
std::vector<std::string> dir;
|
|
ze_result_t result = FsAccess::listDirectory(fdDirPath(pid), dir);
|
|
if (ZE_RESULT_SUCCESS != result) {
|
|
return result;
|
|
}
|
|
for (auto &&file : dir) {
|
|
int fd;
|
|
std::istringstream stream(file);
|
|
stream >> fd;
|
|
if (stream.fail()) {
|
|
// Non numeric filename, not a file descriptor
|
|
continue;
|
|
}
|
|
list.push_back(fd);
|
|
}
|
|
return ZE_RESULT_SUCCESS;
|
|
}
|
|
|
|
ze_result_t ProcfsAccess::getFileName(const ::pid_t pid, const int fd, std::string &val) {
|
|
// Given a process id and a file descriptor number
|
|
// return full name of the open file.
|
|
// NOTE: For sockets, the name will be of the format "socket:[nnnnnnn]"
|
|
return FsAccess::readSymLink(fullFdPath(pid, fd), val);
|
|
}
|
|
|
|
bool ProcfsAccess::isAlive(const ::pid_t pid) {
|
|
return FsAccess::fileExists(fullPath(pid));
|
|
}
|
|
|
|
void ProcfsAccess::kill(const ::pid_t pid) {
|
|
::kill(pid, SIGKILL);
|
|
}
|
|
|
|
::pid_t ProcfsAccess::myProcessId() {
|
|
return ::getpid();
|
|
}
|
|
|
|
// Sysfs Access
|
|
const std::string SysfsAccess::drmPath = "/sys/class/drm/";
|
|
const std::string SysfsAccess::devicesPath = "device/drm/";
|
|
const std::string SysfsAccess::primaryDevName = "card";
|
|
const std::string SysfsAccess::drmDriverDevNodeDir = "/dev/dri/";
|
|
const std::string SysfsAccess::intelGpuBindEntry = "/sys/bus/pci/drivers/i915/bind";
|
|
const std::string SysfsAccess::intelGpuUnbindEntry = "/sys/bus/pci/drivers/i915/unbind";
|
|
|
|
std::string SysfsAccess::fullPath(const std::string file) {
|
|
// Prepend sysfs directory path for this device
|
|
return std::string(dirname + file);
|
|
}
|
|
|
|
SysfsAccess::SysfsAccess(const std::string dev) {
|
|
// dev could be either /dev/dri/cardX or /dev/dri/renderDX
|
|
std::string fileName = FsAccess::getBaseName(dev);
|
|
std::string devicesDir = drmPath + fileName + std::string("/") + devicesPath;
|
|
|
|
FsAccess::listDirectory(devicesDir, deviceNames);
|
|
for (auto &&next : deviceNames) {
|
|
if (!next.compare(0, primaryDevName.length(), primaryDevName)) {
|
|
dirname = drmPath + next + std::string("/");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SysfsAccess *SysfsAccess::create(const std::string dev) {
|
|
return new SysfsAccess(dev);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::canRead(const std::string file) {
|
|
// Prepend sysfs directory path and call the base canRead
|
|
return FsAccess::canRead(fullPath(file));
|
|
}
|
|
|
|
ze_result_t SysfsAccess::canWrite(const std::string file) {
|
|
// Prepend sysfs directory path and call the base canWrite
|
|
return FsAccess::canWrite(fullPath(file));
|
|
}
|
|
|
|
ze_result_t SysfsAccess::getFileMode(const std::string file, ::mode_t &mode) {
|
|
// Prepend sysfs directory path and call the base getFileMode
|
|
return FsAccess::getFileMode(fullPath(file), mode);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::read(const std::string file, std::string &val) {
|
|
// Prepend sysfs directory path and call the base read
|
|
return FsAccess::read(fullPath(file), val);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::read(const std::string file, int32_t &val) {
|
|
return FsAccess::read(fullPath(file), val);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::read(const std::string file, uint32_t &val) {
|
|
return FsAccess::read(fullPath(file), val);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::read(const std::string file, double &val) {
|
|
return FsAccess::read(fullPath(file), val);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::read(const std::string file, uint64_t &val) {
|
|
return FsAccess::read(fullPath(file), val);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::read(const std::string file, std::vector<std::string> &val) {
|
|
// Prepend sysfs directory path and call the base read
|
|
return FsAccess::read(fullPath(file), val);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::write(const std::string file, const std::string val) {
|
|
// Prepend sysfs directory path and call the base write
|
|
return FsAccess::write(fullPath(file), val);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::write(const std::string file, const int val) {
|
|
std::ostringstream stream;
|
|
stream << val;
|
|
|
|
if (stream.fail()) {
|
|
return ZE_RESULT_ERROR_UNKNOWN;
|
|
}
|
|
return FsAccess::write(fullPath(file), stream.str());
|
|
}
|
|
|
|
ze_result_t SysfsAccess::write(const std::string file, const double val) {
|
|
std::ostringstream stream;
|
|
stream << val;
|
|
|
|
if (stream.fail()) {
|
|
return ZE_RESULT_ERROR_UNKNOWN;
|
|
}
|
|
return FsAccess::write(fullPath(file), stream.str());
|
|
}
|
|
|
|
ze_result_t SysfsAccess::write(const std::string file, const uint64_t val) {
|
|
std::ostringstream stream;
|
|
stream << val;
|
|
|
|
if (stream.fail()) {
|
|
return ZE_RESULT_ERROR_UNKNOWN;
|
|
}
|
|
return FsAccess::write(fullPath(file), stream.str());
|
|
}
|
|
|
|
ze_result_t SysfsAccess::scanDirEntries(const std::string path, std::vector<std::string> &list) {
|
|
list.clear();
|
|
return FsAccess::listDirectory(fullPath(path), list);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::readSymLink(const std::string path, std::string &val) {
|
|
// Prepend sysfs directory path and call the base readSymLink
|
|
return FsAccess::readSymLink(fullPath(path), val);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::getRealPath(const std::string path, std::string &val) {
|
|
// Prepend sysfs directory path and call the base getRealPath
|
|
return FsAccess::getRealPath(fullPath(path), val);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::bindDevice(std::string device) {
|
|
return FsAccess::write(intelGpuBindEntry, device);
|
|
}
|
|
|
|
ze_result_t SysfsAccess::unbindDevice(std::string device) {
|
|
return FsAccess::write(intelGpuUnbindEntry, device);
|
|
}
|
|
|
|
bool SysfsAccess::fileExists(const std::string file) {
|
|
// Prepend sysfs directory path and call the base fileExists
|
|
return FsAccess::fileExists(fullPath(file));
|
|
}
|
|
|
|
bool SysfsAccess::directoryExists(const std::string path) {
|
|
return FsAccess::directoryExists(fullPath(path));
|
|
}
|
|
|
|
bool SysfsAccess::isMyDeviceFile(const std::string dev) {
|
|
// dev is a full pathname.
|
|
if (getDirName(dev).compare(drmDriverDevNodeDir)) {
|
|
for (auto &&next : deviceNames) {
|
|
if (!getBaseName(dev).compare(next)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SysfsAccess::isRootUser() {
|
|
return FsAccess::isRootUser();
|
|
}
|
|
|
|
} // namespace L0
|