|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* PPC64 performance-monitoring counters driver.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* based on Mikael Pettersson's 32 bit ppc code
|
|
Packit |
577717 |
* Copyright (C) 2004 David Gibson, IBM Corporation.
|
|
Packit |
577717 |
* Copyright (C) 2004, 2007 Mikael Pettersson
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
#include <linux/version.h>
|
|
Packit |
577717 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
|
|
Packit |
577717 |
#include <linux/config.h>
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
#include <linux/init.h>
|
|
Packit |
577717 |
#include <linux/sched.h>
|
|
Packit |
577717 |
#include <linux/fs.h>
|
|
Packit |
577717 |
#include <linux/perfctr.h>
|
|
Packit |
577717 |
#include <asm/prom.h>
|
|
Packit |
577717 |
#include <asm/time.h> /* tb_ticks_per_jiffy */
|
|
Packit |
577717 |
#include <asm/pmc.h>
|
|
Packit |
577717 |
#include <asm/cputable.h>
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include "ppc64_tests.h"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
extern void ppc64_enable_pmcs(void);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Support for lazy perfctr SPR updates. */
|
|
Packit |
577717 |
struct per_cpu_cache { /* roughly a subset of perfctr_cpu_state */
|
|
Packit |
577717 |
unsigned int id; /* cache owner id */
|
|
Packit |
577717 |
/* Physically indexed cache of the MMCRs. */
|
|
Packit |
577717 |
unsigned long ppc64_mmcr0, ppc64_mmcr1, ppc64_mmcra;
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
static DEFINE_PER_CPU(struct per_cpu_cache, per_cpu_cache);
|
|
Packit |
577717 |
#define __get_cpu_cache(cpu) (&per_cpu(per_cpu_cache, cpu))
|
|
Packit |
577717 |
#define get_cpu_cache() (&__get_cpu_var(per_cpu_cache))
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Structure for counter snapshots, as 32-bit values. */
|
|
Packit |
577717 |
struct perfctr_low_ctrs {
|
|
Packit |
577717 |
u64 tsc;
|
|
Packit |
577717 |
u32 pmc[8];
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned int new_id(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
static DEFINE_SPINLOCK(lock);
|
|
Packit |
577717 |
static unsigned int counter;
|
|
Packit |
577717 |
int id;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
spin_lock(&lock);
|
|
Packit |
577717 |
id = ++counter;
|
|
Packit |
577717 |
spin_unlock(&lock);
|
|
Packit |
577717 |
return id;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline u32 read_pmc(int pmc)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
switch (pmc) {
|
|
Packit |
577717 |
case 0:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC1);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC2);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 2:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC3);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 3:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC4);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 4:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC5);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 5:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC6);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 6:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC7);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 7:
|
|
Packit |
577717 |
return mfspr(SPRN_PMC8);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void write_pmc(int pmc, u32 val)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
switch (pmc) {
|
|
Packit |
577717 |
case 0:
|
|
Packit |
577717 |
mtspr(SPRN_PMC1, val);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 1:
|
|
Packit |
577717 |
mtspr(SPRN_PMC2, val);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 2:
|
|
Packit |
577717 |
mtspr(SPRN_PMC3, val);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 3:
|
|
Packit |
577717 |
mtspr(SPRN_PMC4, val);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 4:
|
|
Packit |
577717 |
mtspr(SPRN_PMC5, val);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 5:
|
|
Packit |
577717 |
mtspr(SPRN_PMC6, val);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 6:
|
|
Packit |
577717 |
mtspr(SPRN_PMC7, val);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case 7:
|
|
Packit |
577717 |
mtspr(SPRN_PMC8, val);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
static void perfctr_default_ihandler(unsigned long pc)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int mmcr0 = mfspr(SPRN_MMCR0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
mmcr0 &= ~MMCR0_PMXE;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, mmcr0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static perfctr_ihandler_t perfctr_ihandler = perfctr_default_ihandler;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void do_perfctr_interrupt(struct pt_regs *regs)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned long mmcr0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* interrupts are disabled here, so we don't need to
|
|
Packit |
577717 |
* preempt_disable() */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
(*perfctr_ihandler)(instruction_pointer(regs));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* clear PMAO so the interrupt doesn't reassert immediately */
|
|
Packit |
577717 |
mmcr0 = mfspr(SPRN_MMCR0) & ~MMCR0_PMAO;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, mmcr0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpu_set_ihandler(perfctr_ihandler_t ihandler)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
perfctr_ihandler = ihandler ? ihandler : perfctr_default_ihandler;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
#define perfctr_cstatus_has_ictrs(cstatus) 0
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if defined(CONFIG_SMP) && defined(CONFIG_PERFCTR_INTERRUPT_SUPPORT)
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void
|
|
Packit |
577717 |
set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
state->isuspend_cpu = cpu;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline int
|
|
Packit |
577717 |
is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return state->isuspend_cpu == cpu;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
state->isuspend_cpu = NR_CPUS;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
static inline void set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu) { }
|
|
Packit |
577717 |
static inline int is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu) { return 1; }
|
|
Packit |
577717 |
static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) { }
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void ppc64_clear_counters(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, 0);
|
|
Packit |
577717 |
mtspr(SPRN_MMCR1, 0);
|
|
Packit |
577717 |
mtspr(SPRN_MMCRA, 0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (cur_cpu_spec->num_pmcs >= 1)
|
|
Packit |
577717 |
mtspr(SPRN_PMC1, 0);
|
|
Packit |
577717 |
if (cur_cpu_spec->num_pmcs >= 2)
|
|
Packit |
577717 |
mtspr(SPRN_PMC2, 0);
|
|
Packit |
577717 |
if (cur_cpu_spec->num_pmcs >= 3)
|
|
Packit |
577717 |
mtspr(SPRN_PMC3, 0);
|
|
Packit |
577717 |
if (cur_cpu_spec->num_pmcs >= 4)
|
|
Packit |
577717 |
mtspr(SPRN_PMC4, 0);
|
|
Packit |
577717 |
if (cur_cpu_spec->num_pmcs >= 5)
|
|
Packit |
577717 |
mtspr(SPRN_PMC5, 0);
|
|
Packit |
577717 |
if (cur_cpu_spec->num_pmcs >= 6)
|
|
Packit |
577717 |
mtspr(SPRN_PMC6, 0);
|
|
Packit |
577717 |
if (cur_cpu_spec->num_pmcs >= 7)
|
|
Packit |
577717 |
mtspr(SPRN_PMC7, 0);
|
|
Packit |
577717 |
if (cur_cpu_spec->num_pmcs >= 8)
|
|
Packit |
577717 |
mtspr(SPRN_PMC8, 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Driver methods, internal and exported.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_write_control(const struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct per_cpu_cache *cache;
|
|
Packit |
577717 |
unsigned long long value;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cache = get_cpu_cache();
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Order matters here: update threshmult and event
|
|
Packit |
577717 |
* selectors before updating global control, which
|
|
Packit |
577717 |
* potentially enables PMIs.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Since mtspr doesn't accept a runtime value for the
|
|
Packit |
577717 |
* SPR number, unroll the loop so each mtspr targets
|
|
Packit |
577717 |
* a constant SPR.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* For processors without MMCR2, we ensure that the
|
|
Packit |
577717 |
* cache and the state indicate the same value for it,
|
|
Packit |
577717 |
* preventing any actual mtspr to it. Ditto for MMCR1.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
value = state->control.mmcra;
|
|
Packit |
577717 |
if (value != cache->ppc64_mmcra) {
|
|
Packit |
577717 |
cache->ppc64_mmcra = value;
|
|
Packit |
577717 |
mtspr(SPRN_MMCRA, value);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
value = state->control.mmcr1;
|
|
Packit |
577717 |
if (value != cache->ppc64_mmcr1) {
|
|
Packit |
577717 |
cache->ppc64_mmcr1 = value;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR1, value);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
value = state->control.mmcr0;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_ictrs(state->user.cstatus))
|
|
Packit |
577717 |
value |= MMCR0_PMXE;
|
|
Packit |
577717 |
if (value != cache->ppc64_mmcr0) {
|
|
Packit |
577717 |
cache->ppc64_mmcr0 = value;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, value);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
cache->id = state->id;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_read_counters(struct perfctr_cpu_state *state,
|
|
Packit |
577717 |
struct perfctr_low_ctrs *ctrs)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int cstatus, i, pmc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cstatus = state->user.cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit |
577717 |
ctrs->tsc = mftb();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (i = 0; i < perfctr_cstatus_nractrs(cstatus); ++i) {
|
|
Packit |
577717 |
pmc = state->control.pmc_map[i];
|
|
Packit |
577717 |
ctrs->pmc[i] = read_pmc(pmc);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
static void perfctr_cpu_isuspend(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int cstatus, nrctrs, i;
|
|
Packit |
577717 |
int cpu;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cpu = smp_processor_id();
|
|
Packit |
577717 |
set_isuspend_cpu(state, cpu); /* early to limit cpu's live range */
|
|
Packit |
577717 |
cstatus = state->user.cstatus;
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit |
577717 |
for (i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) {
|
|
Packit |
577717 |
int pmc = state->control.pmc_map[i];
|
|
Packit |
577717 |
u32 now = read_pmc(pmc);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
state->user.pmc[i].sum += (u32)(now-state->user.pmc[i].start);
|
|
Packit |
577717 |
state->user.pmc[i].start = now;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_iresume(const struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct per_cpu_cache *cache;
|
|
Packit |
577717 |
unsigned int cstatus, nrctrs, i;
|
|
Packit |
577717 |
int cpu;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cpu = smp_processor_id();
|
|
Packit |
577717 |
cache = __get_cpu_cache(cpu);
|
|
Packit |
577717 |
if (cache->id == state->id) {
|
|
Packit |
577717 |
/* Clearing cache->id to force write_control()
|
|
Packit |
577717 |
to unfreeze MMCR0 would be done here, but it
|
|
Packit |
577717 |
is subsumed by resume()'s MMCR0 reload logic. */
|
|
Packit |
577717 |
if (is_isuspend_cpu(state, cpu)) {
|
|
Packit |
577717 |
return; /* skip reload of PMCs */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* The CPU state wasn't ours.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* The counters must be frozen before being reinitialised,
|
|
Packit |
577717 |
* to prevent unexpected increments and missed overflows.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* All unused counters must be reset to a non-overflow state.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
if (!(cache->ppc64_mmcr0 & MMCR0_FC)) {
|
|
Packit |
577717 |
cache->ppc64_mmcr0 |= MMCR0_FC;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, cache->ppc64_mmcr0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
cstatus = state->user.cstatus;
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit |
577717 |
for (i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) {
|
|
Packit |
577717 |
write_pmc(state->control.pmc_map[i], state->user.pmc[i].start);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Call perfctr_cpu_ireload() just before perfctr_cpu_resume() to
|
|
Packit |
577717 |
bypass internal caching and force a reload if the I-mode PMCs. */
|
|
Packit |
577717 |
void perfctr_cpu_ireload(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
#ifdef CONFIG_SMP
|
|
Packit |
577717 |
clear_isuspend_cpu(state);
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
get_cpu_cache()->id = 0;
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* PRE: the counters have been suspended and sampled by perfctr_cpu_suspend() */
|
|
Packit |
577717 |
unsigned int perfctr_cpu_identify_overflow(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int cstatus, nractrs, nrctrs, i;
|
|
Packit |
577717 |
unsigned int pmc_mask = 0;
|
|
Packit |
577717 |
int nr_pmcs = cur_cpu_spec->num_pmcs;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cstatus = state->user.cstatus;
|
|
Packit |
577717 |
nractrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Ickity, ickity, ick. We don't have fine enough interrupt
|
|
Packit |
577717 |
* control to disable interrupts on all the counters we're not
|
|
Packit |
577717 |
* interested in. So, we have to deal with overflows on actrs
|
|
Packit |
577717 |
* amd unused PMCs as well as the ones we actually care
|
|
Packit |
577717 |
* about. */
|
|
Packit |
577717 |
for (i = 0; i < nractrs; ++i) {
|
|
Packit |
577717 |
int pmc = state->control.pmc_map[i];
|
|
Packit |
577717 |
u32 val = read_pmc(pmc);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* For actrs, force a sample if they overflowed */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if ((s32)val < 0) {
|
|
Packit |
577717 |
state->user.pmc[i].sum += (u32)(val - state->user.pmc[i].start);
|
|
Packit |
577717 |
state->user.pmc[i].start = 0;
|
|
Packit |
577717 |
write_pmc(pmc, 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
for (; i < nrctrs; ++i) {
|
|
Packit |
577717 |
if ((s32)state->user.pmc[i].start < 0) { /* PPC64-specific */
|
|
Packit |
577717 |
int pmc = state->control.pmc_map[i];
|
|
Packit |
577717 |
/* XXX: "+=" to correct for overshots */
|
|
Packit |
577717 |
state->user.pmc[i].start = state->control.ireset[pmc];
|
|
Packit |
577717 |
pmc_mask |= (1 << i);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Clear any unused overflowed counters, so we don't loop on
|
|
Packit |
577717 |
* the interrupt */
|
|
Packit |
577717 |
for (i = 0; i < nr_pmcs; ++i) {
|
|
Packit |
577717 |
if (! (state->unused_pmcs & (1<
|
|
Packit |
577717 |
continue;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if ((int)read_pmc(i) < 0)
|
|
Packit |
577717 |
write_pmc(i, 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* XXX: HW cleared MMCR0[ENINT]. We presumably cleared the entire
|
|
Packit |
577717 |
MMCR0, so the re-enable occurs automatically later, no? */
|
|
Packit |
577717 |
return pmc_mask;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline int check_ireset(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int nrctrs, i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
i = state->control.header.nractrs;
|
|
Packit |
577717 |
nrctrs = i + state->control.header.nrictrs;
|
|
Packit |
577717 |
for(; i < nrctrs; ++i) {
|
|
Packit |
577717 |
unsigned int pmc = state->control.pmc_map[i];
|
|
Packit |
577717 |
if ((int)state->control.ireset[pmc] < 0) /* PPC64-specific */
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
state->user.pmc[i].start = state->control.ireset[pmc];
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#else /* CONFIG_PERFCTR_INTERRUPT_SUPPORT */
|
|
Packit |
577717 |
static inline void perfctr_cpu_isuspend(struct perfctr_cpu_state *state) { }
|
|
Packit |
577717 |
static inline void perfctr_cpu_iresume(const struct perfctr_cpu_state *state) { }
|
|
Packit |
577717 |
static inline int check_ireset(struct perfctr_cpu_state *state) { return 0; }
|
|
Packit |
577717 |
#endif /* CONFIG_PERFCTR_INTERRUPT_SUPPORT */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int check_control(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int i, nractrs, nrctrs, pmc_mask, pmc;
|
|
Packit |
577717 |
unsigned int nr_pmcs = cur_cpu_spec->num_pmcs;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
nractrs = state->control.header.nractrs;
|
|
Packit |
577717 |
nrctrs = nractrs + state->control.header.nrictrs;
|
|
Packit |
577717 |
if (nrctrs < nractrs || nrctrs > nr_pmcs)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pmc_mask = 0;
|
|
Packit |
577717 |
for (i = 0; i < nrctrs; ++i) {
|
|
Packit |
577717 |
pmc = state->control.pmc_map[i];
|
|
Packit |
577717 |
if (pmc >= nr_pmcs || (pmc_mask & (1<
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
pmc_mask |= (1<
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* We need to retain internal control of PMXE and PMAO. PMXE
|
|
Packit |
577717 |
* will be set when ictrs are active. We can't really handle
|
|
Packit |
577717 |
* TB interrupts, so we don't allow those either. */
|
|
Packit |
577717 |
if ( (state->control.mmcr0 & MMCR0_PMXE)
|
|
Packit |
577717 |
|| (state->control.mmcr0 & MMCR0_PMAO)
|
|
Packit |
577717 |
|| (state->control.mmcr0 & MMCR0_TBEE) )
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
state->unused_pmcs = ((1 << nr_pmcs)-1) & ~pmc_mask;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
state->id = new_id();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int perfctr_cpu_update_control(struct perfctr_cpu_state *state, int is_global)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int err;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
clear_isuspend_cpu(state);
|
|
Packit |
577717 |
state->user.cstatus = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* disallow i-mode counters if we cannot catch the interrupts */
|
|
Packit |
577717 |
if (!(perfctr_info.cpu_features & PERFCTR_FEATURE_PCINT)
|
|
Packit |
577717 |
&& state->control.header.nrictrs)
|
|
Packit |
577717 |
return -EPERM;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
err = check_control(state); /* may initialise state->cstatus */
|
|
Packit |
577717 |
if (err < 0)
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
err = check_ireset(state);
|
|
Packit |
577717 |
if (err < 0)
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
state->user.cstatus |= perfctr_mk_cstatus(state->control.header.tsc_on,
|
|
Packit |
577717 |
state->control.header.nractrs,
|
|
Packit |
577717 |
state->control.header.nrictrs);
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* get_reg_offset() maps SPR numbers to offsets into struct perfctr_cpu_control.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
static const struct {
|
|
Packit |
577717 |
unsigned int spr;
|
|
Packit |
577717 |
unsigned int offset;
|
|
Packit |
577717 |
unsigned int size;
|
|
Packit |
577717 |
} reg_offsets[] = {
|
|
Packit |
577717 |
{ SPRN_MMCR0, offsetof(struct perfctr_cpu_control, mmcr0), sizeof(long) },
|
|
Packit |
577717 |
{ SPRN_MMCR1, offsetof(struct perfctr_cpu_control, mmcr1), sizeof(long) },
|
|
Packit |
577717 |
{ SPRN_MMCRA, offsetof(struct perfctr_cpu_control, mmcra), sizeof(long) },
|
|
Packit |
577717 |
{ SPRN_PMC1, offsetof(struct perfctr_cpu_control, ireset[1-1]), sizeof(int) },
|
|
Packit |
577717 |
{ SPRN_PMC2, offsetof(struct perfctr_cpu_control, ireset[2-1]), sizeof(int) },
|
|
Packit |
577717 |
{ SPRN_PMC3, offsetof(struct perfctr_cpu_control, ireset[3-1]), sizeof(int) },
|
|
Packit |
577717 |
{ SPRN_PMC4, offsetof(struct perfctr_cpu_control, ireset[4-1]), sizeof(int) },
|
|
Packit |
577717 |
{ SPRN_PMC5, offsetof(struct perfctr_cpu_control, ireset[5-1]), sizeof(int) },
|
|
Packit |
577717 |
{ SPRN_PMC6, offsetof(struct perfctr_cpu_control, ireset[6-1]), sizeof(int) },
|
|
Packit |
577717 |
{ SPRN_PMC7, offsetof(struct perfctr_cpu_control, ireset[7-1]), sizeof(int) },
|
|
Packit |
577717 |
{ SPRN_PMC8, offsetof(struct perfctr_cpu_control, ireset[8-1]), sizeof(int) },
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int get_reg_offset(unsigned int spr, unsigned int *size)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i = 0; i < ARRAY_SIZE(reg_offsets); ++i)
|
|
Packit |
577717 |
if (spr == reg_offsets[i].spr) {
|
|
Packit |
577717 |
*size = reg_offsets[i].size;
|
|
Packit |
577717 |
return reg_offsets[i].offset;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int access_regs(struct perfctr_cpu_control *control,
|
|
Packit |
577717 |
void *argp, unsigned int argbytes, int do_write)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct perfctr_cpu_reg *regs;
|
|
Packit |
577717 |
unsigned int i, nr_regs, size;
|
|
Packit |
577717 |
int offset;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
nr_regs = argbytes / sizeof(struct perfctr_cpu_reg);
|
|
Packit |
577717 |
if (nr_regs * sizeof(struct perfctr_cpu_reg) != argbytes)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
regs = (struct perfctr_cpu_reg*)argp;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for(i = 0; i < nr_regs; ++i) {
|
|
Packit |
577717 |
offset = get_reg_offset(regs[i].nr, &size);
|
|
Packit |
577717 |
if (offset < 0)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
if (size == sizeof(long)) {
|
|
Packit |
577717 |
unsigned long *where = (unsigned long*)((char*)control + offset);
|
|
Packit |
577717 |
if (do_write)
|
|
Packit |
577717 |
*where = regs[i].value;
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
regs[i].value = *where;
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
unsigned int *where = (unsigned int*)((char*)control + offset);
|
|
Packit |
577717 |
if (do_write)
|
|
Packit |
577717 |
*where = regs[i].value;
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
regs[i].value = *where;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return argbytes;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int perfctr_cpu_control_write(struct perfctr_cpu_control *control, unsigned int domain,
|
|
Packit |
577717 |
const void *srcp, unsigned int srcbytes)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (domain != PERFCTR_DOMAIN_CPU_REGS)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
return access_regs(control, (void*)srcp, srcbytes, 1);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int perfctr_cpu_control_read(const struct perfctr_cpu_control *control, unsigned int domain,
|
|
Packit |
577717 |
void *dstp, unsigned int dstbytes)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (domain != PERFCTR_DOMAIN_CPU_REGS)
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
return access_regs((struct perfctr_cpu_control*)control, dstp, dstbytes, 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpu_suspend(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int i, cstatus;
|
|
Packit |
577717 |
struct perfctr_low_ctrs now;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
write_perfseq_begin(&state->user.sequence);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* quiesce the counters */
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, MMCR0_FC);
|
|
Packit |
577717 |
get_cpu_cache()->ppc64_mmcr0 = MMCR0_FC;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (perfctr_cstatus_has_ictrs(state->user.cstatus))
|
|
Packit |
577717 |
perfctr_cpu_isuspend(state);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr_cpu_read_counters(state, &now;;
|
|
Packit |
577717 |
cstatus = state->user.cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit |
577717 |
state->user.tsc_sum += now.tsc - state->user.tsc_start;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (i = 0; i < perfctr_cstatus_nractrs(cstatus); ++i)
|
|
Packit |
577717 |
state->user.pmc[i].sum += (u32)(now.pmc[i]-state->user.pmc[i].start);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
write_perfseq_end(&state->user.sequence);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpu_resume(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct perfctr_low_ctrs now;
|
|
Packit |
577717 |
unsigned int i, cstatus;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
write_perfseq_begin(&state->user.sequence);
|
|
Packit |
577717 |
if (perfctr_cstatus_has_ictrs(state->user.cstatus))
|
|
Packit |
577717 |
perfctr_cpu_iresume(state);
|
|
Packit |
577717 |
perfctr_cpu_write_control(state);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr_cpu_read_counters(state, &now;;
|
|
Packit |
577717 |
cstatus = state->user.cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit |
577717 |
state->user.tsc_start = now.tsc;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
for (i = 0; i < perfctr_cstatus_nractrs(cstatus); ++i)
|
|
Packit |
577717 |
state->user.pmc[i].start = now.pmc[i];
|
|
Packit |
577717 |
|
|
Packit |
577717 |
write_perfseq_end(&state->user.sequence);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpu_sample(struct perfctr_cpu_state *state)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int i, cstatus, nractrs;
|
|
Packit |
577717 |
struct perfctr_low_ctrs now;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
write_perfseq_begin(&state->user.sequence);
|
|
Packit |
577717 |
perfctr_cpu_read_counters(state, &now;;
|
|
Packit |
577717 |
cstatus = state->user.cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus)) {
|
|
Packit |
577717 |
state->user.tsc_sum += now.tsc - state->user.tsc_start;
|
|
Packit |
577717 |
state->user.tsc_start = now.tsc;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
nractrs = perfctr_cstatus_nractrs(cstatus);
|
|
Packit |
577717 |
for(i = 0; i < nractrs; ++i) {
|
|
Packit |
577717 |
state->user.pmc[i].sum += (u32)(now.pmc[i]-state->user.pmc[i].start);
|
|
Packit |
577717 |
state->user.pmc[i].start = now.pmc[i];
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
write_perfseq_end(&state->user.sequence);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_clear_counters(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct per_cpu_cache *cache;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
cache = get_cpu_cache();
|
|
Packit |
577717 |
memset(cache, 0, sizeof *cache);
|
|
Packit |
577717 |
cache->id = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ppc64_clear_counters();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Processor detection and initialisation procedures. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void ppc64_cpu_setup(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* allow user to initialize these???? */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
unsigned long long mmcr0 = mfspr(SPRN_MMCR0);
|
|
Packit |
577717 |
unsigned long long mmcra = mfspr(SPRN_MMCRA);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ppc64_enable_pmcs();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
mmcr0 |= MMCR0_FC;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, mmcr0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
mmcr0 |= MMCR0_FCM1|MMCR0_PMXE|MMCR0_FCECE;
|
|
Packit |
577717 |
mmcr0 |= MMCR0_PMC1CE|MMCR0_PMCjCE;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, mmcr0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
mmcra |= MMCRA_SAMPLE_ENABLE;
|
|
Packit |
577717 |
mtspr(SPRN_MMCRA, mmcra);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
printk("setup on cpu %d, mmcr0 %lx\n", smp_processor_id(),
|
|
Packit |
577717 |
mfspr(SPRN_MMCR0));
|
|
Packit |
577717 |
printk("setup on cpu %d, mmcr1 %lx\n", smp_processor_id(),
|
|
Packit |
577717 |
mfspr(SPRN_MMCR1));
|
|
Packit |
577717 |
printk("setup on cpu %d, mmcra %lx\n", smp_processor_id(),
|
|
Packit |
577717 |
mfspr(SPRN_MMCRA));
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* mtmsrd(mfmsr() | MSR_PMM); */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ppc64_clear_counters();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
mmcr0 = mfspr(SPRN_MMCR0);
|
|
Packit |
577717 |
mmcr0 &= ~MMCR0_PMAO;
|
|
Packit |
577717 |
mmcr0 &= ~MMCR0_FC;
|
|
Packit |
577717 |
mtspr(SPRN_MMCR0, mmcr0);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
printk("start on cpu %d, mmcr0 %llx\n", smp_processor_id(), mmcr0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_clear_one(void *ignore)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* PREEMPT note: when called via on_each_cpu(),
|
|
Packit |
577717 |
this is in IRQ context with preemption disabled. */
|
|
Packit |
577717 |
perfctr_cpu_clear_counters();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void perfctr_cpu_reset(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
on_each_cpu(perfctr_cpu_clear_one, NULL, 1, 1);
|
|
Packit |
577717 |
perfctr_cpu_set_ihandler(NULL);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int __init perfctr_cpu_init(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
extern unsigned long ppc_proc_freq;
|
|
Packit |
577717 |
extern unsigned long ppc_tb_freq;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr_info.cpu_features = PERFCTR_FEATURE_RDTSC
|
|
Packit |
577717 |
| PERFCTR_FEATURE_RDPMC | PERFCTR_FEATURE_PCINT;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr_cpu_name = "PowerPC64";
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr_info.cpu_khz = ppc_proc_freq / 1000;
|
|
Packit |
577717 |
/* We need to round here rather than truncating, because in a
|
|
Packit |
577717 |
* few cases the raw ratio can end up being 7.9999 or
|
|
Packit |
577717 |
* suchlike */
|
|
Packit |
577717 |
perfctr_info.tsc_to_cpu_mult =
|
|
Packit |
577717 |
(ppc_proc_freq + ppc_tb_freq - 1) / ppc_tb_freq;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
on_each_cpu((void *)ppc64_cpu_setup, NULL, 0, 1);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr_ppc64_init_tests();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr_cpu_reset();
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void __exit perfctr_cpu_exit(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
perfctr_cpu_reset();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Hardware reservation. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static spinlock_t service_mutex = SPIN_LOCK_UNLOCKED;
|
|
Packit |
577717 |
static const char *current_service = NULL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
const char *perfctr_cpu_reserve(const char *service)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
const char *ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
spin_lock(&service_mutex);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = current_service;
|
|
Packit |
577717 |
if (ret)
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
ret = "unknown driver (oprofile?)";
|
|
Packit |
577717 |
if (reserve_pmc_hardware(do_perfctr_interrupt) != 0)
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
current_service = service;
|
|
Packit |
577717 |
ret = NULL;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
spin_unlock(&service_mutex);
|
|
Packit |
577717 |
return ret;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpu_release(const char *service)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
spin_lock(&service_mutex);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (service != current_service) {
|
|
Packit |
577717 |
printk(KERN_ERR "%s: attempt by %s to release while reserved by %s\n",
|
|
Packit |
577717 |
__FUNCTION__, service, current_service);
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* power down the counters */
|
|
Packit |
577717 |
perfctr_cpu_reset();
|
|
Packit |
577717 |
current_service = NULL;
|
|
Packit |
577717 |
release_pmc_hardware();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
spin_unlock(&service_mutex);
|
|
Packit |
577717 |
}
|