#include "config.h"
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/fcntl.h>
#include <linux/perf_event.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <rdma/fi_errno.h>
#include "linux/rdpmc.h"
typedef unsigned long long u64;
#define rmb() asm volatile("" ::: "memory")
static int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu,
int group_fd, unsigned long flags)
{
return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
}
int rdpmc_open(unsigned counter, struct rdpmc_ctx *ctx)
{
struct perf_event_attr attr = {
.type = counter > 10 ? PERF_TYPE_RAW : PERF_TYPE_HARDWARE,
.size = PERF_ATTR_SIZE_VER0,
.config = counter,
.sample_type = PERF_SAMPLE_READ,
.exclude_kernel = 1,
};
return rdpmc_open_attr(&attr, ctx, NULL);
}
int rdpmc_open_attr(struct perf_event_attr *attr, struct rdpmc_ctx *ctx,
struct rdpmc_ctx *leader_ctx)
{
ctx->fd = perf_event_open(attr, 0, -1,
leader_ctx ? leader_ctx->fd : -1, 0);
if (ctx->fd < 0) {
perror("perf_event_open");
return -1;
}
ctx->buf = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ, MAP_SHARED, ctx->fd, 0);
if (ctx->buf == MAP_FAILED) {
close(ctx->fd);
perror("mmap on perf fd");
return -1;
}
if (ctx->buf->index == 0) {
munmap(ctx->buf, sysconf(_SC_PAGESIZE));
close(ctx->fd);
return -1;
}
return 0;
}
void rdpmc_close(struct rdpmc_ctx *ctx)
{
close(ctx->fd);
munmap(ctx->buf, sysconf(_SC_PAGESIZE));
}
unsigned long long rdpmc_read(struct rdpmc_ctx *ctx)
{
u64 val;
unsigned seq;
u64 offset;
typeof (ctx->buf) buf = ctx->buf;
unsigned index;
do {
seq = buf->lock;
rmb();
index = buf->index;
offset = buf->offset;
if (index == 0)
return offset;
val = __builtin_ia32_rdpmc(index - 1);
rmb();
} while (buf->lock != seq);
return val + offset;
}
static uint64_t rdpmc_hw_id(uint32_t cntr_id)
{
switch (cntr_id) {
case OFI_PMC_CPU_CYCLES:
return PERF_COUNT_HW_CPU_CYCLES;
case OFI_PMC_CPU_INSTR:
return PERF_COUNT_HW_INSTRUCTIONS;
default:
return ~0;
}
}
static uint64_t rdpmc_cache_id(uint32_t cntr_id, uint32_t flags)
{
return ~0;
}
static uint64_t rdpmc_sw_id(uint32_t cntr_id)
{
switch (cntr_id) {
case OFI_PMC_OS_PAGE_FAULT:
return PERF_COUNT_SW_PAGE_FAULTS;
default:
return ~0;
}
}
int ofi_pmu_open(struct ofi_perf_ctx **ctx, enum ofi_perf_domain domain,
uint32_t cntr_id, uint32_t flags)
{
struct perf_event_attr attr = {
.size = PERF_ATTR_SIZE_VER0,
.sample_type = PERF_SAMPLE_READ,
.exclude_kernel = 1,
};
int ret;
*ctx = calloc(1, sizeof **ctx);
if (!*ctx)
return -FI_ENOMEM;
switch(domain) {
case OFI_PMU_CPU:
attr.type = PERF_TYPE_HARDWARE;
attr.config = rdpmc_hw_id(cntr_id);
break;
case OFI_PMU_CACHE:
attr.type = PERF_TYPE_HW_CACHE;
attr.config = rdpmc_cache_id(cntr_id, flags);
break;
case OFI_PMU_OS:
attr.type = PERF_TYPE_SOFTWARE;
attr.config = rdpmc_sw_id(cntr_id);
break;
default:
return -FI_ENOSYS;
}
if (attr.config == ~0)
return -FI_ENOSYS;
ret = rdpmc_open_attr(&attr, &(*ctx)->ctx, NULL);
return ret ? -errno : 0;
}
inline uint64_t ofi_pmu_read(struct ofi_perf_ctx *ctx)
{
return rdpmc_read(&ctx->ctx);
}
inline void ofi_pmu_close(struct ofi_perf_ctx *ctx)
{
rdpmc_close(&ctx->ctx);
free(ctx);
}