Event counter support

XGMI related events are supported

Change-Id: If17036fe890c8be45da3654353599821b5828c14
This commit is contained in:
Chris Freehill
2019-06-22 20:54:31 -05:00
szülő 04479f0b44
commit ea26baec20
13 fájl változott, egészen pontosan 1344 új sor hozzáadva és 19 régi sor törölve
+163 -1
Fájl megtekintése
@@ -61,6 +61,7 @@
#include "rocm_smi/rocm_smi_device.h"
#include "rocm_smi/rocm_smi_utils.h"
#include "rocm_smi/rocm_smi_exception.h"
#include "rocm_smi/rocm_smi_counters.h"
#include "rocm_smi/rocm_smi64Config.h"
@@ -93,11 +94,15 @@ static rsmi_status_t handleException() {
#define TRY try {
#define CATCH } catch (...) {return handleException();}
#define GET_DEV_FROM_INDX \
#define CHECK_DV_IND_RANGE \
amd::smi::RocmSMI& smi = amd::smi::RocmSMI::getInstance(); \
if (dv_ind >= smi.monitor_devices().size()) { \
return RSMI_STATUS_INVALID_ARGS; \
} \
#define GET_DEV_FROM_INDX \
CHECK_DV_IND_RANGE \
std::shared_ptr<amd::smi::Device> dev = smi.monitor_devices()[dv_ind]; \
assert(dev != nullptr);
@@ -128,7 +133,10 @@ static rsmi_status_t errno_to_rsmi_status(uint32_t err) {
case EACCES: return RSMI_STATUS_PERMISSION;
case EPERM:
case ENOENT: return RSMI_STATUS_NOT_SUPPORTED;
case EBADF:
case EISDIR: return RSMI_STATUS_FILE_ERROR;
case EINTR: return RSMI_STATUS_INTERRUPT;
case EIO: return RSMI_STATUS_UNEXPECTED_SIZE;
default: return RSMI_STATUS_UNKNOWN_ERROR;
}
}
@@ -1970,6 +1978,10 @@ rsmi_status_string(rsmi_status_t status, const char **status_string) {
" successfully";
break;
case RSMI_STATUS_INTERRUPT:
*status_string = "An interrupt occurred while executing the function";
break;
default:
*status_string = "An unknown error occurred";
return RSMI_STATUS_UNKNOWN_ERROR;
@@ -2118,3 +2130,153 @@ rsmi_dev_unique_id_get(uint32_t dv_ind, uint64_t *unique_id) {
CATCH
}
rsmi_status_t
rsmi_dev_counter_create(uint32_t dv_ind, rsmi_event_type_t type,
rsmi_event_handle_t *evnt_handle) {
TRY
DEVICE_MUTEX
REQUIRE_ROOT_ACCESS
CHECK_DV_IND_RANGE
if (evnt_handle == nullptr) {
return RSMI_STATUS_INVALID_ARGS;
}
if (type < RSMI_EVNT_FIRST || type > RSMI_EVNT_LAST) {
return RSMI_STATUS_INVALID_ARGS;
}
*evnt_handle = reinterpret_cast<uintptr_t>(
new amd::smi::evt::Event(type, dv_ind));
if (evnt_handle == nullptr) {
return RSMI_STATUS_OUT_OF_RESOURCES;
}
return RSMI_STATUS_SUCCESS;
CATCH
}
rsmi_status_t
rsmi_dev_counter_destroy(rsmi_event_handle_t evnt_handle) {
TRY
if (evnt_handle == 0) {
return RSMI_STATUS_INVALID_ARGS;
}
amd::smi::evt::Event *evt =
reinterpret_cast<amd::smi::evt::Event *>(evnt_handle);
uint32_t dv_ind = evt->dev_ind();
DEVICE_MUTEX
REQUIRE_ROOT_ACCESS
delete evt;
return RSMI_STATUS_SUCCESS;
CATCH
}
rsmi_status_t
rsmi_counter_control(rsmi_event_handle_t evt_handle,
rsmi_counter_command_t cmd, void *cmd_args) {
TRY
amd::smi::evt::Event *evt =
reinterpret_cast<amd::smi::evt::Event *>(evt_handle);
amd::smi::pthread_wrap _pw(*get_mutex(evt->dev_ind()));
amd::smi::ScopedPthread _lock(_pw);
REQUIRE_ROOT_ACCESS
uint32_t ret;
// This is for future command args. This would work in conjunction with a
// new function to set perf attributes.
(void) cmd_args;
if (evt_handle == 0) {
return RSMI_STATUS_INVALID_ARGS;
}
switch (cmd) {
case RSMI_CNTR_CMD_START:
ret = evt->startCounter();
break;
case RSMI_CNTR_CMD_STOP:
ret = evt->stopCounter();
break;
default:
assert(!"Unexpected perf counter command");
}
return errno_to_rsmi_status(ret);
CATCH
}
rsmi_status_t
rsmi_counter_read(rsmi_event_handle_t evt_handle,
rsmi_counter_value_t *value) {
TRY
if (value == nullptr || evt_handle == 0) {
return RSMI_STATUS_INVALID_ARGS;
}
amd::smi::evt::Event *evt =
reinterpret_cast<amd::smi::evt::Event *>(evt_handle);
uint32_t dv_ind = evt->dev_ind();
DEVICE_MUTEX
REQUIRE_ROOT_ACCESS
uint32_t ret;
ret = evt->getValue(value);
return errno_to_rsmi_status(ret);
CATCH
}
rsmi_status_t
rsmi_counter_available_counters_get(uint32_t dv_ind,
rsmi_event_group_t grp, uint32_t *available) {
rsmi_status_t ret;
TRY
if (available == nullptr) {
return RSMI_STATUS_INVALID_ARGS;
}
DEVICE_MUTEX
uint64_t val;
switch (grp) {
case RSMI_EVNT_GRP_XGMI:
ret = get_dev_value_int(amd::smi::kDevDFCountersAvailable, dv_ind, &val);
assert(val < UINT32_MAX);
*available = static_cast<uint32_t>(val);
break;
default:
return RSMI_STATUS_INVALID_ARGS;
}
return ret;
CATCH
}
rsmi_status_t
rsmi_dev_counter_group_supported(uint32_t dv_ind, rsmi_event_group_t group) {
TRY
DEVICE_MUTEX
GET_DEV_FROM_INDX
amd::smi::evt::dev_evt_grp_set_t *grp = dev->supported_event_groups();
if (grp->find(group) == grp->end()) {
return RSMI_STATUS_NOT_SUPPORTED;
} else {
return RSMI_STATUS_SUCCESS;
}
CATCH
}
+407
Fájl megtekintése
@@ -0,0 +1,407 @@
/*
* =============================================================================
* The University of Illinois/NCSA
* Open Source License (NCSA)
*
* Copyright (c) 2019, Advanced Micro Devices, Inc.
* All rights reserved.
*
* Developed by:
*
* AMD Research and AMD ROC Software Development
*
* Advanced Micro Devices, Inc.
*
* www.amd.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal with the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimers.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimers in
* the documentation and/or other materials provided with the distribution.
* - Neither the names of <Name of Development Group, Name of Institution>,
* nor the names of its contributors may be used to endorse or promote
* products derived from this Software without specific prior written
* permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS WITH THE SOFTWARE.
*
*/
#include <assert.h>
#include <string.h>
#include <linux/perf_event.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <map>
#include <sstream>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <unordered_set>
#include "rocm_smi/rocm_smi.h"
#include "rocm_smi/rocm_smi_counters.h"
#include "rocm_smi/rocm_smi_utils.h"
#include "rocm_smi/rocm_smi_exception.h"
#include "rocm_smi/rocm_smi_main.h"
#include "rocm_smi/rocm_smi_device.h"
namespace amd {
namespace smi {
namespace evt {
static const char *kPathDeviceEventRoot = "/sys/bus/event_source/devices";
// Event group names
static const char *kEvGrpDataFabricFName = "amdgpu_df_#";
// Data Fabric event file names
static const char *kDFEvtCake0FtiReqAllocFName = "cake0_ftiinstat_reqalloc";
static const char *kDFEvtCake0FtiRspAllocFName = "cake0_ftiinstat_rspalloc";
static const char *kDFEvtCake0PcsOutTxDataFName = "cake0_pcsout_txdata";
static const char *kDFEvtCake0PcsOutTxMetaFName = "cake0_pcsout_txmeta";
static const char *kDFEvtCake1FtiReqAllocFName = "cake1_ftiinstat_reqalloc";
static const char *kDFEvtCake1FtiRspAllocFName = "cake1_ftiinstat_rspalloc";
static const char *kDFEvtCake1PcsOutTxDataFName = "cake1_pcsout_txdata";
static const char *kDFEvtCake1PcsOutTxMetaFName = "cake1_pcsout_txmeta";
static const std::map<rsmi_event_type_t, const char *> kEventFNameMap = {
{RSMI_EVNT_XGMI_0_NOP_TX, kDFEvtCake0PcsOutTxMetaFName},
{RSMI_EVNT_XGMI_0_REQUEST_TX, kDFEvtCake0FtiReqAllocFName},
{RSMI_EVNT_XGMI_0_RESPONSE_TX, kDFEvtCake0FtiRspAllocFName},
{RSMI_EVNT_XGMI_0_BEATS_TX, kDFEvtCake0PcsOutTxDataFName},
{RSMI_EVNT_XGMI_1_NOP_TX, kDFEvtCake1PcsOutTxMetaFName},
{RSMI_EVNT_XGMI_1_REQUEST_TX, kDFEvtCake1FtiReqAllocFName},
{RSMI_EVNT_XGMI_1_RESPONSE_TX, kDFEvtCake1FtiRspAllocFName},
{RSMI_EVNT_XGMI_1_BEATS_TX, kDFEvtCake1PcsOutTxDataFName},
};
static const std::map<rsmi_event_group_t, const char *> kEvtGrpFNameMap = {
{RSMI_EVNT_GRP_XGMI, kEvGrpDataFabricFName},
{RSMI_EVNT_GRP_INVALID, "bogus"},
};
static rsmi_event_group_t EvtGrpFromEvtID(rsmi_event_type_t evnt) {
#define EVNT_GRP_RANGE_CHK(EVGRP_SHORT, EVGRP_ENUM) \
if (evnt >= RSMI_EVNT_##EVGRP_SHORT##_FIRST && \
evnt <= RSMI_EVNT_##EVGRP_SHORT##_LAST) { \
return EVGRP_ENUM; \
}
EVNT_GRP_RANGE_CHK(XGMI, RSMI_EVNT_GRP_XGMI);
return RSMI_EVNT_GRP_INVALID;
}
// Note below that dev_num is not the same as the usual dv_ind.
// dev_num is the number of the device (e.g., 1 for card1) whereas dv_ind
// is usually the index into the vector of devices
void
GetSupportedEventGroups(uint32_t dev_num, dev_evt_grp_set_t *supported_grps) {
assert(supported_grps != nullptr);
std::string grp_path_base;
std::string grp_path;
uint32_t ret;
grp_path_base = kPathDeviceEventRoot;
grp_path_base += '/';
struct stat file_stat;
for (auto g : kEvtGrpFNameMap) {
grp_path = grp_path_base;
grp_path += g.second;
std::replace(grp_path.begin(), grp_path.end(), '#',
static_cast<char>('0' + dev_num));
ret = stat(grp_path.c_str(), &file_stat);
if (ret) {
assert(errno == ENOENT);
continue;
}
if (S_ISDIR(file_stat.st_mode)) {
supported_grps->insert(g.first);
}
}
}
// /sys/bus/event_source/devices/<hw block>_<instance>/type
Event::Event(rsmi_event_type_t event, uint32_t dev_ind) :
event_type_(event) {
fd_ = -1;
rsmi_event_group_t grp = EvtGrpFromEvtID(event);
assert(grp != RSMI_EVNT_GRP_INVALID); // This should have failed before now
evt_path_root_ = kPathDeviceEventRoot;
evt_path_root_ += '/';
evt_path_root_ += kEvtGrpFNameMap.at(grp);
amd::smi::RocmSMI& smi = amd::smi::RocmSMI::getInstance();
assert(dev_ind < smi.monitor_devices().size());
std::shared_ptr<amd::smi::Device> dev = smi.monitor_devices()[dev_ind];
assert(dev != nullptr);
dev_ind_ = dev_ind;
dev_file_ind_ = dev->index();
std::replace(evt_path_root_.begin(), evt_path_root_.end(), '#',
static_cast<char>('0' + dev_file_ind_));
}
Event::~Event(void) {
int ret;
if (fd_ != -1) {
ret = close(fd_);
if (ret == -1) {
perror("Failed to close file descriptor.");
}
}
}
static void
parse_field_config(std::string fstr, evnt_info_t *val) {
std::stringstream ss(fstr);
std::stringstream fs;
std::string config_ln;
std::string field_name;
uint32_t start_bit;
uint32_t end_bit;
char jnk;
assert(val != nullptr);
getline(ss, config_ln, ':');
ss >> start_bit;
ss >> jnk;
assert(jnk == '-');
ss >> end_bit;
val->start_bit = start_bit;
val->field_size = end_bit - start_bit + 1;
}
static uint32_t
get_event_bitfield_info(std::string *config_path, evnt_info_t *val) {
uint32_t err;
std::string fstr;
err = ReadSysfsStr(*config_path, &fstr);
if (err) {
return err;
}
parse_field_config(fstr, val);
return 0;
}
uint32_t
Event::get_event_file_info(void) {
uint32_t err;
std::string fn = evt_path_root_;
std::string fstr;
fn += "/events/";
fn += kEventFNameMap.at(event_type_);
err = ReadSysfsStr(fn, &fstr);
if (err) {
return err;
}
// parse_perf_attr(fstr, &event_id_.event_field_vals);
std::stringstream ss(fstr);
std::stringstream fs;
std::string field_assgn;
std::string field_name;
evnt_info_t ev_info;
while (ss.rdbuf()->in_avail() != 0) {
ev_info = {};
getline(ss, field_assgn, ',');
fs.clear();
fs << field_assgn;
getline(fs, field_name, '=');
fs >> std::hex >> ev_info.value;
assert(fs.rdbuf()->in_avail() == 0);
// Now, get the corresponding bitfield
std::string config_path = evt_path_root_;
config_path += "/format/";
config_path += field_name;
err = get_event_bitfield_info(&config_path, &ev_info);
if (err) {
return err;
}
event_info_.push_back(ev_info);
}
return 0;
}
uint32_t
Event::get_event_type(uint32_t *ev_type) {
assert(ev_type != nullptr);
if (ev_type == nullptr) {
return EINVAL;
}
std::string fn = evt_path_root_;
std::string fstr;
fn += "/type";
std::ifstream fs;
fs.open(fn);
if (!fs.is_open()) {
return errno;
}
fs >> *ev_type;
fs.close();
return 0;
}
static uint64_t
get_perf_attr_config(std::vector<evnt_info_t> *ev_info) {
uint64_t ret_val = 0;
assert(ev_info != nullptr);
for (const evnt_info_t& ev : *ev_info) {
ret_val |= ev.value << ev.start_bit;
}
return ret_val;
}
uint32_t
amd::smi::evt::Event::openPerfHandle(void) {
uint32_t ret;
memset(&attr_, 0, sizeof(struct perf_event_attr));
ret = get_event_file_info();
if (ret) {
return ret;
}
ret = get_event_type(&attr_.type);
if (ret) {
return ret;
}
attr_.size = sizeof(struct perf_event_attr);
attr_.config = get_perf_attr_config(&event_info_);
attr_.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING;
attr_.disabled = 1;
attr_.inherit = 1;
fd_ = syscall(__NR_perf_event_open, &attr_,
-1, 0, -1, PERF_FLAG_FD_NO_GROUP);
if (fd_ < 0) {
return errno;
}
return 0;
}
uint32_t
amd::smi::evt::Event::startCounter(void) {
int32_t ret;
if (fd_ == -1) {
ret = openPerfHandle();
if (ret != 0) {
return ret;
}
}
ret = ioctl(fd_, PERF_EVENT_IOC_ENABLE, NULL);
if (ret == -1) {
return errno;
}
assert(ret == 0); // We're expecting the ioctl call to return -1 or 0
return 0;
}
uint32_t
amd::smi::evt::Event::stopCounter(void) {
int32_t ret;
if (fd_ == -1) {
return EBADF;
}
ret = ioctl(fd_, PERF_EVENT_IOC_DISABLE, NULL);
if (ret == -1) {
return errno;
}
assert(ret == 0); // We're expecting the ioctl call to return -1 or 0
return 0;
}
static ssize_t
readn(int fd, void *buf, size_t n) {
size_t left = n;
ssize_t bytes;
while (left) {
bytes = read(fd, buf, left);
if (!bytes) /* reach EOF */
return (n - left);
if (bytes < 0) {
if (errno == EINTR) /* read got interrupted */
continue;
else
return -errno;
}
left -= bytes;
buf = reinterpret_cast<void *>((reinterpret_cast<uint8_t *>(buf) + bytes));
}
return n;
}
uint32_t
amd::smi::evt::Event::getValue(rsmi_counter_value_t *val) {
assert(val != nullptr);
ssize_t ret;
perf_read_format_t pvalue;
ret = readn(fd_, &pvalue, sizeof(perf_read_format_t));
if (ret < 0) {
return -ret;
}
if (ret != sizeof(perf_read_format_t)) {
return EIO;
}
val->value = pvalue.value;
val->time_enabled = pvalue.enabled_time;
val->time_running = pvalue.run_time;
return 0;
}
} // namespace evt
} // namespace smi
} // namespace amd
+22 -4
Fájl megtekintése
@@ -98,6 +98,7 @@ static const char *kDevMemUsedVisVRAMFName = "mem_info_vis_vram_used";
static const char *kDevMemUsedVRAMFName = "mem_info_vram_used";
static const char *kDevPCIEReplayCountFName = "pcie_replay_count";
static const char *kDevUniqueIdFName = "unique_id";
static const char *kDevDFCountersAvailableFName = "df_cntr_avail";
// Strings that are found within sysfs files
static const char *kDevPerfLevelAutoStr = "auto";
@@ -140,6 +141,7 @@ static const std::map<DevInfoTypes, const char *> kDevAttribNameMap = {
{kDevMemUsedVRAM, kDevMemUsedVRAMFName},
{kDevPCIEReplayCount, kDevPCIEReplayCountFName},
{kDevUniqueId, kDevUniqueIdFName},
{kDevDFCountersAvailable, kDevDFCountersAvailableFName},
};
static const std::map<rsmi_dev_perf_level, const char *> kDevPerfLvlMap = {
@@ -155,10 +157,18 @@ static const std::map<rsmi_dev_perf_level, const char *> kDevPerfLvlMap = {
{RSMI_DEV_PERF_LEVEL_UNKNOWN, kDevPerfLevelUnknownStr},
};
static bool isRegularFile(std::string fname) {
static int isRegularFile(std::string fname, bool *is_reg) {
struct stat file_stat;
stat(fname.c_str(), &file_stat);
return S_ISREG(file_stat.st_mode);
int ret;
assert(is_reg != nullptr);
ret = stat(fname.c_str(), &file_stat);
if (ret) {
return errno;
}
*is_reg = S_ISREG(file_stat.st_mode);
return 0;
}
#define RET_IF_NONZERO(X) { \
@@ -205,7 +215,14 @@ int Device::openSysfsFileStream(DevInfoTypes type, T *fs, const char *str) {
sysfs_path += kDevAttribNameMap.at(type);
DBG_FILE_ERROR(sysfs_path, str);
if (!isRegularFile(sysfs_path)) {
bool reg_file;
int ret = isRegularFile(sysfs_path, &reg_file);
if (ret != 0) {
return ret;
}
if (!reg_file) {
return ENOENT;
}
@@ -372,6 +389,7 @@ int Device::readDevInfo(DevInfoTypes type, uint64_t *val) {
case kDevMemUsedVisVRAM:
case kDevMemUsedVRAM:
case kDevPCIEReplayCount:
case kDevDFCountersAvailable:
ret = readDevInfoStr(type, &tempStr);
RET_IF_NONZERO(ret);
*val = std::stoul(tempStr, 0);
+2
Fájl megtekintése
@@ -58,6 +58,7 @@
#include <cerrno>
#include "rocm_smi/rocm_smi.h"
#include "rocm_smi/rocm_smi_device.h"
#include "rocm_smi/rocm_smi_main.h"
#include "rocm_smi/rocm_smi_exception.h"
@@ -332,6 +333,7 @@ RocmSMI::AddToDeviceList(std::string dev_name) {
uint32_t d_index = GetDeviceIndex(d_name);
dev->set_index(d_index);
GetSupportedEventGroups(d_index, dev->supported_event_groups());
devices_.push_back(dev);
return;