Files
compute-runtime/level_zero/tools/source/sysman/linux/fs_access.cpp
Bill Jordan ccd5abfbfd Add zesDeviceReset ULTs.
This patch adds two ULTs.

Verify that an IN USE error is returned if the device is open by another
process.

Verify that reset succeeds if device is not in use.

Verify that reset with force succeeds if device is in use.

Verify that reset without force succeeds if device is opened during
reset, and process is killed.

Verify that reset with force fails with device in use
if device is opened during reset, and process will not die

Change-Id: I232572c192bc481d8d63ef39c7494976100325ff
Signed-off-by: Bill Jordan <bill.jordan@intel.com>
2020-10-19 15:01:19 +02:00

566 lines
15 KiB
C++

/*
* Copyright (C) 2020 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 {
return ZE_RESULT_ERROR_UNKNOWN;
}
}
// Generic Filesystem Access
FsAccess::FsAccess() {
}
FsAccess *FsAccess::create() {
return new FsAccess();
}
ze_result_t FsAccess::read(const std::string file, uint64_t &val) {
// Read a single line from text file without trailing newline
std::ifstream fs;
fs.open(file.c_str());
if (fs.fail()) {
return getResult(errno);
}
fs >> val;
if (fs.fail()) {
fs.close();
return getResult(errno);
}
fs.close();
return ZE_RESULT_SUCCESS;
}
ze_result_t FsAccess::read(const std::string file, double &val) {
// Read a single line from text file without trailing newline
std::ifstream fs;
fs.open(file.c_str());
if (fs.fail()) {
return getResult(errno);
}
fs >> val;
if (fs.fail()) {
fs.close();
return getResult(errno);
}
fs.close();
return ZE_RESULT_SUCCESS;
}
ze_result_t FsAccess::read(const std::string file, int32_t &val) {
// Read a single line from text file without trailing newline
std::ifstream fs;
fs.open(file.c_str());
if (fs.fail()) {
return getResult(errno);
}
fs >> val;
if (fs.fail()) {
fs.close();
return getResult(errno);
}
fs.close();
return ZE_RESULT_SUCCESS;
}
ze_result_t FsAccess::read(const std::string file, uint32_t &val) {
// Read a single line from text file without trailing newline
std::ifstream fs;
fs.open(file.c_str());
if (fs.fail()) {
return getResult(errno);
}
fs >> val;
if (fs.fail()) {
fs.close();
return getResult(errno);
}
fs.close();
return ZE_RESULT_SUCCESS;
}
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) {
std::ofstream sysfs;
sysfs.open(file.c_str());
if (sysfs.fail()) {
return getResult(errno);
}
sysfs << val << std::endl;
if (sysfs.fail()) {
sysfs.close();
return getResult(errno);
}
sysfs.close();
return ZE_RESULT_SUCCESS;
}
ze_result_t FsAccess::canRead(const std::string file) {
if (access(file.c_str(), F_OK)) {
return ZE_RESULT_ERROR_UNKNOWN;
}
if (access(file.c_str(), R_OK)) {
return ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS;
}
return ZE_RESULT_SUCCESS;
}
ze_result_t FsAccess::canWrite(const std::string file) {
if (access(file.c_str(), F_OK)) {
return ZE_RESULT_ERROR_UNKNOWN;
}
if (access(file.c_str(), W_OK)) {
return ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS;
}
return ZE_RESULT_SUCCESS;
}
bool FsAccess::fileExists(const std::string file) {
if (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 = ::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 = ::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;
::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);
}
// 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).c_str(), val);
}
ze_result_t SysfsAccess::read(const std::string file, int32_t &val) {
std::string str;
ze_result_t result;
result = FsAccess::read(fullPath(file), str);
if (ZE_RESULT_SUCCESS != result) {
return result;
}
std::istringstream stream(str);
stream >> val;
if (stream.fail()) {
return ZE_RESULT_ERROR_UNKNOWN;
}
return ZE_RESULT_SUCCESS;
}
ze_result_t SysfsAccess::read(const std::string file, uint32_t &val) {
std::string str;
ze_result_t result;
result = FsAccess::read(fullPath(file), str);
if (ZE_RESULT_SUCCESS != result) {
return result;
}
std::istringstream stream(str);
stream >> val;
if (stream.fail()) {
return ZE_RESULT_ERROR_UNKNOWN;
}
return ZE_RESULT_SUCCESS;
}
ze_result_t SysfsAccess::read(const std::string file, double &val) {
std::string str;
ze_result_t result;
result = FsAccess::read(fullPath(file), str);
if (ZE_RESULT_SUCCESS != result) {
return result;
}
std::istringstream stream(str);
stream >> val;
if (stream.fail()) {
return ZE_RESULT_ERROR_UNKNOWN;
}
return ZE_RESULT_SUCCESS;
}
ze_result_t SysfsAccess::read(const std::string file, uint64_t &val) {
std::string str;
ze_result_t result;
result = FsAccess::read(fullPath(file), str);
if (ZE_RESULT_SUCCESS != result) {
return result;
}
std::istringstream stream(str);
stream >> val;
if (stream.fail()) {
return ZE_RESULT_ERROR_UNKNOWN;
}
return ZE_RESULT_SUCCESS;
}
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).c_str(), 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).c_str(), 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).c_str(), 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).c_str(), 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).c_str());
}
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;
}
} // namespace L0