updated PMC driver
This commit is contained in:
parent
d358a78e56
commit
c1e52dc8c2
4 changed files with 219 additions and 53 deletions
106
apps/memory.c
106
apps/memory.c
|
@ -333,62 +333,87 @@ int bench(void)
|
|||
|
||||
// setup PMCs
|
||||
pmc_stop_all();
|
||||
pmc_config(0, PMC_EVT_PAGE_WALK_CLKS, PMC_EVTSEL_OS | PMC_EVTSEL_EN, 0, 0);
|
||||
pmc_config(1, PMC_EVT_PAGE_WALK_COUNT, PMC_EVTSEL_OS | PMC_EVTSEL_EN, 0, 0);
|
||||
pmc_gp_config(0, PMC_EVT_PAGE_WALK_CLKS, PMC_EVTSEL_OS | PMC_EVTSEL_EN, 0, 0);
|
||||
pmc_gp_config(1, PMC_EVT_PAGE_WALK_COUNT, PMC_EVTSEL_OS | PMC_EVTSEL_EN, 0, 0);
|
||||
|
||||
// allocate space for results
|
||||
uint64_t *data = kmalloc(ITERATIONS * sizeof(uint64_t));
|
||||
if (!data)
|
||||
return -1;
|
||||
|
||||
// clear caches
|
||||
tlb_flush();
|
||||
flush_cache();
|
||||
size_t phyaddr = get_page();
|
||||
size_t viraddr;
|
||||
size_t pages = 512*511;
|
||||
size_t virbase = 2*KERNEL_SPACE;
|
||||
kprintf("virbase %#llx KERNEL_SPACE %#llx\n", virbase, KERNEL_SPACE);
|
||||
for (viraddr = virbase; viraddr < virbase+pages*PAGE_SIZE; viraddr += PAGE_SIZE) {
|
||||
kprintf("map at %#llx\n", viraddr);
|
||||
size_t ret = map_region(viraddr, phyaddr, 1, MAP_KERNEL_SPACE);
|
||||
if (ret != viraddr) {
|
||||
kprintf("map failed at %#llx\n", viraddr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i=0; i < ITERATIONS; i++) {
|
||||
pmc_write(0, 0);
|
||||
pmc_write(1, 0);
|
||||
tlb_flush();
|
||||
|
||||
pmc_reset_all();
|
||||
pmc_start_all();
|
||||
|
||||
#if 0
|
||||
int i = 100;
|
||||
while (i--) {
|
||||
tlb_flush();
|
||||
page_stats(0);
|
||||
for (viraddr = virbase; viraddr < virbase+pages*PAGE_SIZE; viraddr += PAGE_SIZE) {
|
||||
char * p = (char *) viraddr;
|
||||
(*p)++;
|
||||
}
|
||||
#else
|
||||
//flush_cache();
|
||||
//tlb_flush();
|
||||
page_stats(0);
|
||||
#endif
|
||||
|
||||
pmc_stop_all();
|
||||
|
||||
uint64_t clks = pmc_read(0);
|
||||
uint64_t count = pmc_read(1);
|
||||
uint64_t clks = pmc_gp_read(0);
|
||||
uint64_t count = pmc_gp_read(1);
|
||||
|
||||
/*kprintf("Number of Page table walks: %lu\n", count);
|
||||
kprintf("Page table walks clock cycles: %lu\n", clks);
|
||||
kprintf("Cycles per table walk: %lu.%u\n", clks / count, (1000 * clks / count) % 1000 );*/
|
||||
|
||||
data[i] = 1000000 * clks / count;
|
||||
kprintf("%llu\n", 1000000 * clks / count);
|
||||
}
|
||||
|
||||
// dump results
|
||||
for (i=0; i<ITERATIONS; i++)
|
||||
kprintf("%u\t%lu\n", i, data[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smp(void* arg)
|
||||
{
|
||||
kprintf("Hello from Core %d\n", smp_id());
|
||||
page_dump(PG_XD | PG_GLOBAL | PG_USER | PG_RW);
|
||||
|
||||
return 33;
|
||||
}
|
||||
|
||||
/** @brief This is a simple procedure to test memory management subsystem */
|
||||
int memory(void* arg)
|
||||
{
|
||||
int ret;
|
||||
tid_t id;
|
||||
|
||||
#if 0
|
||||
size_t t0, t1, t2, t3;
|
||||
size_t pages;
|
||||
for (pages = 1; pages < (1 << 25); pages++) {
|
||||
t0 = rdtsc();
|
||||
size_t ret = map_region((1 << 28), 0x1000, pages, MAP_KERNEL_SPACE);
|
||||
t1 = rdtsc();
|
||||
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
t2 = rdtsc();
|
||||
ret = unmap_region((1 << 28), pages);
|
||||
t3 = rdtsc();
|
||||
|
||||
kprintf("%llu\t%llu\t%llu\n", pages, t1-t0, t3-t2);
|
||||
}
|
||||
|
||||
kprintf("======== USER: malloc test...\n");
|
||||
char* argv[] = {"/bin/memtest", "25", "10"};
|
||||
|
||||
ret = create_user_task(&id, argv[0], argv);
|
||||
test(!ret, "calling %s %s %s with id = %i, ret = %i", argv[0], argv[1], argv[2], id, ret);
|
||||
wait(&ret);
|
||||
test(!ret, "userspace task returned with code = %d", ret);
|
||||
return 0;
|
||||
|
||||
kprintf("======== PAGING: test started...\n");
|
||||
paging();
|
||||
|
||||
|
@ -398,16 +423,21 @@ int memory(void* arg)
|
|||
kprintf("======== MALLOC: test started...\n");
|
||||
malloc();
|
||||
|
||||
kprintf("======== USER: test userspace...\n");
|
||||
char* argv[] = {"/bin/fork", NULL};
|
||||
ret = create_user_task(&id, argv[0], argv);
|
||||
test(!ret, "calling %s with id = %i, ret = %i", argv[0], id, ret);
|
||||
kprintf("======== USER: test fork...\n");
|
||||
char* argv2[] = {"/bin/fork", NULL};
|
||||
ret = create_user_task(&id, argv2[0], argv2);
|
||||
test(!ret, "calling %s with id = %i, ret = %i", argv2[0], id, ret);
|
||||
wait(&ret);
|
||||
test(!ret, "userspace task returned with code = %d", ret);
|
||||
|
||||
#endif
|
||||
kprintf("======== BENCH: memory and TLB benchmark started...\n");
|
||||
bench();
|
||||
|
||||
kprintf("======== SMP: test multicore...\n");
|
||||
ret = create_kernel_task_on_core(&id, smp, NULL, NORMAL_PRIO, 1);
|
||||
wait(&ret);
|
||||
test(!ret, "smp task returned with code = %d", ret);
|
||||
|
||||
kprintf("======== All tests finished successfull...\n");
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -38,9 +38,12 @@
|
|||
#define IA32_PERFEVTSEL(i) (0x186 + i) // general purpose PMC configuration register
|
||||
#define IA32_PMC(i) (0x0C1 + i) // general purpose PMC counter register
|
||||
#define IA32_A_PMC(i) (0x4C1 + i) // general purpose alias PMC counter register for full width writes
|
||||
|
||||
#define MSR_PERF_FIXED_CTR(i) (0x309 + i) // fixed function PMC counter register
|
||||
#define MSR_PERF_FIXED_CTR_CTRL 0x38D // fixed functiion PMC configuration register
|
||||
|
||||
#define PMC_FIXED_OS (1 << 0)
|
||||
#define PMC_FIXED_USR (1 << 1)
|
||||
#define PMV_FIXED_PMI (1 << 3)
|
||||
|
||||
/* For Intel Core 2 Duo the MSR_PERF_FIXED_CTRs are mapped as followed:
|
||||
* MSR_PERF_FIXED_CTR(0) => INST_RETIRED.ANY
|
||||
|
@ -89,6 +92,14 @@
|
|||
#define PMC_EVT_PAGE_WALK_COUNT 0x010C // Number of page-walks executed
|
||||
#define PMC_EVT_PAGE_WALK_CLKS 0x020C // Duration of page-walks in core cycles
|
||||
|
||||
#define PMC_EVT_MEM_LOAD_RETIRED_L1D_MISS 0x01CB // Retired loads that miss the L1 data cache (precise event)
|
||||
#define PMC_EVT_MEM_LOAD_RETIRED_L1D_LINE_MISS 0x02CB // L1 data cache line missed by retired loads (precise event)
|
||||
|
||||
#define PMC_EVT_MEM_LOAD_RETIRED_L2_MISS 0x04CB // Retired loads that miss the L2 cache (precise event)
|
||||
#define PMC_EVT_MEM_LOAD_RETIRED_L2_LINE_MISS 0x08CB // L2 cache line missed by retired loads (precise event)
|
||||
|
||||
#define PMC_EVT_MEM_LOAD_RETIRED_DTLB_MISS 0x10CB // Retired loads that miss the DTLB (precise event)
|
||||
|
||||
struct pmc {
|
||||
uint8_t id;
|
||||
|
||||
|
@ -134,7 +145,17 @@ struct pmc_caps* pmc_init();
|
|||
* - 0 on success
|
||||
* - else failure (invalid counter or flags)
|
||||
*/
|
||||
int pmc_config(uint8_t i, uint16_t event, uint32_t flags, uint8_t umask, uint8_t cmask);
|
||||
int pmc_gp_config(uint8_t i, uint16_t event, uint32_t flags, uint8_t umask, uint8_t cmask);
|
||||
|
||||
/** @brief Setups and stops the fixed function PMCs
|
||||
*
|
||||
* @param i The counter number to configure (positive for gp PMCs, negative for ff PMCs)
|
||||
* @param flags Flags for the MSR_PERF_FIXED_CTR_CTRL register
|
||||
* @return
|
||||
* - 0 on success
|
||||
* - else failure (invalid counter or flags)
|
||||
*/
|
||||
int pmc_ff_config(uint8_t i, uint8_t flags);
|
||||
|
||||
/** @brief Start a single general purpose PMC
|
||||
*
|
||||
|
@ -143,7 +164,7 @@ int pmc_config(uint8_t i, uint16_t event, uint32_t flags, uint8_t umask, uint8_t
|
|||
* - 0 on success
|
||||
* - -EINVAL on invalid counter number
|
||||
*/
|
||||
inline int pmc_start(uint8_t i);
|
||||
inline int pmc_gp_start(uint8_t i);
|
||||
|
||||
/** @brief Stop a single general purpose PMC
|
||||
*
|
||||
|
@ -152,11 +173,28 @@ inline int pmc_start(uint8_t i);
|
|||
* - 0 on success
|
||||
* - -EINVAL on invalid counter number
|
||||
*/
|
||||
inline int pmc_stop(uint8_t i);
|
||||
inline int pmc_gp_stop(uint8_t i);
|
||||
|
||||
/** @brief Start a single fixed function PMC
|
||||
*
|
||||
* @param i The counter number
|
||||
* @return
|
||||
* - 0 on success
|
||||
* - -EINVAL on invalid counter number
|
||||
*/
|
||||
inline int pmc_ff_start(uint8_t i);
|
||||
|
||||
/** @brief Stop a single fixed function PMC
|
||||
*
|
||||
* @param i The counter number
|
||||
* @return
|
||||
* - 0 on success
|
||||
* - -EINVAL on invalid counter number
|
||||
*/
|
||||
inline int pmc_ff_stop(uint8_t i);
|
||||
|
||||
/** @brief Start all PMCs at the same time
|
||||
*
|
||||
* @param i The counter number
|
||||
* @return
|
||||
* - 0 on success
|
||||
* - -EINVAL on invalid counter number
|
||||
|
@ -165,19 +203,33 @@ inline int pmc_start_all();
|
|||
|
||||
/** @brief Stop all PMCs at the same time
|
||||
*
|
||||
* @param i The counter number
|
||||
* @return
|
||||
* - 0 on success
|
||||
* - -EINVAL on invalid counter number
|
||||
*/
|
||||
inline int pmc_stop_all();
|
||||
|
||||
/** @brief Reset all PMCs to zero
|
||||
*
|
||||
* @return
|
||||
* - 0 on success
|
||||
* - -EINVAL on invalid counter number
|
||||
*/
|
||||
inline int pmc_reset_all();
|
||||
|
||||
/** @brief Read a single general purpose PMC
|
||||
*
|
||||
* @param i The counter number
|
||||
* @return The counter value (see struct pmc_caps.gp_width)
|
||||
*/
|
||||
inline uint64_t pmc_read(uint8_t i);
|
||||
inline uint64_t pmc_gp_read(uint8_t i);
|
||||
|
||||
/** @brief Read a single fixed function PMC
|
||||
*
|
||||
* @param i The counter number
|
||||
* @return The counter value (see struct pmc_caps.gp_width)
|
||||
*/
|
||||
inline uint64_t pmc_ff_read(uint8_t i);
|
||||
|
||||
/** @brief Write a single general purpose PMC value
|
||||
*
|
||||
|
@ -188,6 +240,13 @@ inline uint64_t pmc_read(uint8_t i);
|
|||
* @param i The counter number
|
||||
* @param val The counter value (see struct pmc_caps.gp_width)
|
||||
*/
|
||||
inline int pmc_write(uint8_t i, uint64_t val);
|
||||
inline int pmc_gp_write(uint8_t i, uint64_t val);
|
||||
|
||||
/** @brief Write a single fixed function PMC value
|
||||
*
|
||||
* @param i The counter number
|
||||
* @param val The counter value (see struct pmc_caps.ff_width)
|
||||
*/
|
||||
inline int pmc_ff_write(uint8_t i, uint64_t val);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
* @brief Simple interface to IA32 Performance Monitor Counters
|
||||
*
|
||||
* This implementation is in parts specific for Intel Core 2 Duo Processors!
|
||||
* General purpose PMCS => pmc_gp_*()
|
||||
* Fixed function PMCs => pmc_ff_*()
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -45,8 +47,8 @@ struct pmc_caps* pmc_init()
|
|||
caps.arch_events = (b >> 0) & 0x3f;
|
||||
|
||||
// check if IA32_PERF_CAPABILITIES MSR is available
|
||||
cpuid(0x01, &a, &b, &c, &d);
|
||||
if (caps.version >= 2) {
|
||||
cpuid(0x01, &a, &b, &c, &d);
|
||||
if (c & (1 << 15 /* PDCM */))
|
||||
caps.msr = rdmsr(IA32_PERF_CAPABILITIES);
|
||||
}
|
||||
|
@ -55,7 +57,7 @@ struct pmc_caps* pmc_init()
|
|||
return ∩︀
|
||||
}
|
||||
|
||||
int pmc_config(uint8_t i, uint16_t event, uint32_t flags, uint8_t umask, uint8_t cmask)
|
||||
int pmc_gp_config(uint8_t i, uint16_t event, uint32_t flags, uint8_t umask, uint8_t cmask)
|
||||
{
|
||||
if (BUILTIN_EXPECT(i > caps.gp_count, 0))
|
||||
return -EINVAL;
|
||||
|
@ -64,12 +66,28 @@ int pmc_config(uint8_t i, uint16_t event, uint32_t flags, uint8_t umask, uint8_t
|
|||
evtsel |= (cmask << PMC_EVTSEL_CMASK) | (umask << PMC_EVTSEL_UMASK);
|
||||
|
||||
wrmsr(IA32_PERFEVTSEL(i), evtsel);
|
||||
wrmsr(IA32_PMC(i), 0);
|
||||
wrmsr(IA32_PMC(i), 0); // reset counter
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int pmc_start(uint8_t i)
|
||||
int pmc_ff_config(uint8_t i, uint8_t flags)
|
||||
{
|
||||
if (BUILTIN_EXPECT(i > caps.ff_count, 0))
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
uint64_t ctrl = rdmsr(MSR_PERF_FIXED_CTR_CTRL);
|
||||
|
||||
ctrl &= ~(0x0f << i*4); // clear flags
|
||||
ctrl |= (flags & 0xf) << i*4;
|
||||
|
||||
wrmsr(MSR_PERF_FIXED_CTR_CTRL, ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int pmc_gp_start(uint8_t i)
|
||||
{
|
||||
if (BUILTIN_EXPECT(i > caps.gp_count, 0))
|
||||
return -EINVAL;
|
||||
|
@ -79,7 +97,7 @@ inline int pmc_start(uint8_t i)
|
|||
return 0;
|
||||
}
|
||||
|
||||
inline int pmc_stop(uint8_t i)
|
||||
inline int pmc_gp_stop(uint8_t i)
|
||||
{
|
||||
if (BUILTIN_EXPECT(i > caps.gp_count, 0))
|
||||
return -EINVAL;
|
||||
|
@ -89,6 +107,26 @@ inline int pmc_stop(uint8_t i)
|
|||
return 0;
|
||||
}
|
||||
|
||||
inline int pmc_ff_start(uint8_t i)
|
||||
{
|
||||
if (BUILTIN_EXPECT(i > caps.ff_count, 0))
|
||||
return -EINVAL;
|
||||
|
||||
// TODO
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int pmc_ff_stop(uint8_t i)
|
||||
{
|
||||
if (BUILTIN_EXPECT(i > caps.ff_count, 0))
|
||||
return -EINVAL;
|
||||
|
||||
// TODO
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int pmc_start_all()
|
||||
{
|
||||
if (BUILTIN_EXPECT(caps.version < 2, 0))
|
||||
|
@ -109,7 +147,22 @@ inline int pmc_stop_all()
|
|||
return 0;
|
||||
}
|
||||
|
||||
inline uint64_t pmc_read(uint8_t i)
|
||||
inline int pmc_reset_all()
|
||||
{
|
||||
if (BUILTIN_EXPECT(caps.version < 2, 0))
|
||||
return -EINVAL;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < caps.gp_count; i++)
|
||||
pmc_gp_write(i, 0);
|
||||
|
||||
for (i = 0; i < caps.ff_count; i++)
|
||||
pmc_ff_write(i, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline uint64_t pmc_gp_read(uint8_t i)
|
||||
{
|
||||
if (BUILTIN_EXPECT(i > caps.gp_count, 0))
|
||||
return 0;
|
||||
|
@ -119,7 +172,17 @@ inline uint64_t pmc_read(uint8_t i)
|
|||
return 0;
|
||||
}
|
||||
|
||||
inline int pmc_write(uint8_t i, uint64_t val)
|
||||
inline uint64_t pmc_ff_read(uint8_t i)
|
||||
{
|
||||
if (BUILTIN_EXPECT(i > caps.ff_count, 0))
|
||||
return 0;
|
||||
|
||||
return rdmsr(MSR_PERF_FIXED_CTR(i));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int pmc_gp_write(uint8_t i, uint64_t val)
|
||||
{
|
||||
if (BUILTIN_EXPECT(i > caps.gp_count, 0))
|
||||
return -EINVAL;
|
||||
|
@ -131,3 +194,13 @@ inline int pmc_write(uint8_t i, uint64_t val)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int pmc_ff_write(uint8_t i, uint64_t val)
|
||||
{
|
||||
if (BUILTIN_EXPECT(i > caps.ff_count, 0))
|
||||
return -EINVAL;
|
||||
|
||||
wrmsr(MSR_PERF_FIXED_CTR(i), val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
* This file is part of MetalSVM.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Steffen Vogel <steffen.vogel@rwth-aachen.de>
|
||||
*/
|
||||
|
||||
#include <metalsvm/malloc.h>
|
||||
#include <metalsvm/spinlock.h>
|
||||
#include <metalsvm/stdio.h>
|
||||
|
|
Loading…
Add table
Reference in a new issue