Skip to content

Commit

Permalink
[SPARC64]: Provide mmu statistics via sysfs.
Browse files Browse the repository at this point in the history
If the system supports hypervisor based statistics, allow them to
be fetched, enabled, and disabled via sysfs.

Enable and disable via the boolean:

/sys/devices/systems/cpu/cpuN/mmustat_enable

Statistic values are provided under:

/sys/devices/systems/cpu/cpuN/mmu_status/

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David Miller authored and David S. Miller committed Jun 5, 2007
1 parent 48b6735 commit d887ab3
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 15 deletions.
22 changes: 22 additions & 0 deletions arch/sparc64/kernel/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -2570,3 +2570,25 @@ sun4v_svc_clrstatus:
retl
nop
.size sun4v_svc_clrstatus, .-sun4v_svc_clrstatus

.globl sun4v_mmustat_conf
.type sun4v_mmustat_conf,#function
sun4v_mmustat_conf:
mov %o1, %o4
mov HV_FAST_MMUSTAT_CONF, %o5
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
.size sun4v_mmustat_conf, .-sun4v_mmustat_conf

.globl sun4v_mmustat_info
.type sun4v_mmustat_info,#function
sun4v_mmustat_info:
mov %o0, %o4
mov HV_FAST_MMUSTAT_INFO, %o5
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
.size sun4v_mmustat_info, .-sun4v_mmustat_info
215 changes: 200 additions & 15 deletions arch/sparc64/kernel/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,198 @@
#include <linux/percpu.h>
#include <linux/init.h>

static DEFINE_PER_CPU(struct cpu, cpu_devices);
#include <asm/hypervisor.h>
#include <asm/spitfire.h>

static DEFINE_PER_CPU(struct hv_mmu_statistics, mmu_stats) __attribute__((aligned(64)));

#define SHOW_ULONG_NAME(NAME, MEMBER) \
#define SHOW_MMUSTAT_ULONG(NAME) \
static ssize_t show_##NAME(struct sys_device *dev, char *buf) \
{ \
struct cpu *cpu = container_of(dev, struct cpu, sysdev); \
cpuinfo_sparc *c = &cpu_data(cpu->sysdev.id); \
struct hv_mmu_statistics *p = &per_cpu(mmu_stats, dev->id); \
return sprintf(buf, "%lu\n", p->NAME); \
} \
static SYSDEV_ATTR(NAME, 0444, show_##NAME, NULL)

SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_8k_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_8k_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_64k_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_64k_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_4mb_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_4mb_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_256mb_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_256mb_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_8k_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_8k_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_64k_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_64k_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_4mb_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_4mb_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_256mb_tte);
SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_256mb_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_8k_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_8k_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_64k_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_64k_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_4mb_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_4mb_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_256mb_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_256mb_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_8k_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_8k_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_64k_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_64k_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_4mb_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_4mb_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_256mb_tte);
SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_256mb_tte);

static struct attribute *mmu_stat_attrs[] = {
&attr_immu_tsb_hits_ctx0_8k_tte.attr,
&attr_immu_tsb_ticks_ctx0_8k_tte.attr,
&attr_immu_tsb_hits_ctx0_64k_tte.attr,
&attr_immu_tsb_ticks_ctx0_64k_tte.attr,
&attr_immu_tsb_hits_ctx0_4mb_tte.attr,
&attr_immu_tsb_ticks_ctx0_4mb_tte.attr,
&attr_immu_tsb_hits_ctx0_256mb_tte.attr,
&attr_immu_tsb_ticks_ctx0_256mb_tte.attr,
&attr_immu_tsb_hits_ctxnon0_8k_tte.attr,
&attr_immu_tsb_ticks_ctxnon0_8k_tte.attr,
&attr_immu_tsb_hits_ctxnon0_64k_tte.attr,
&attr_immu_tsb_ticks_ctxnon0_64k_tte.attr,
&attr_immu_tsb_hits_ctxnon0_4mb_tte.attr,
&attr_immu_tsb_ticks_ctxnon0_4mb_tte.attr,
&attr_immu_tsb_hits_ctxnon0_256mb_tte.attr,
&attr_immu_tsb_ticks_ctxnon0_256mb_tte.attr,
&attr_dmmu_tsb_hits_ctx0_8k_tte.attr,
&attr_dmmu_tsb_ticks_ctx0_8k_tte.attr,
&attr_dmmu_tsb_hits_ctx0_64k_tte.attr,
&attr_dmmu_tsb_ticks_ctx0_64k_tte.attr,
&attr_dmmu_tsb_hits_ctx0_4mb_tte.attr,
&attr_dmmu_tsb_ticks_ctx0_4mb_tte.attr,
&attr_dmmu_tsb_hits_ctx0_256mb_tte.attr,
&attr_dmmu_tsb_ticks_ctx0_256mb_tte.attr,
&attr_dmmu_tsb_hits_ctxnon0_8k_tte.attr,
&attr_dmmu_tsb_ticks_ctxnon0_8k_tte.attr,
&attr_dmmu_tsb_hits_ctxnon0_64k_tte.attr,
&attr_dmmu_tsb_ticks_ctxnon0_64k_tte.attr,
&attr_dmmu_tsb_hits_ctxnon0_4mb_tte.attr,
&attr_dmmu_tsb_ticks_ctxnon0_4mb_tte.attr,
&attr_dmmu_tsb_hits_ctxnon0_256mb_tte.attr,
&attr_dmmu_tsb_ticks_ctxnon0_256mb_tte.attr,
NULL,
};

static struct attribute_group mmu_stat_group = {
.attrs = mmu_stat_attrs,
.name = "mmu_stats",
};

/* XXX convert to rusty's on_one_cpu */
static unsigned long run_on_cpu(unsigned long cpu,
unsigned long (*func)(unsigned long),
unsigned long arg)
{
cpumask_t old_affinity = current->cpus_allowed;
unsigned long ret;

/* should return -EINVAL to userspace */
if (set_cpus_allowed(current, cpumask_of_cpu(cpu)))
return 0;

ret = func(arg);

set_cpus_allowed(current, old_affinity);

return ret;
}

static unsigned long read_mmustat_enable(unsigned long junk)
{
unsigned long ra = 0;

sun4v_mmustat_info(&ra);

return ra != 0;
}

static unsigned long write_mmustat_enable(unsigned long val)
{
unsigned long ra, orig_ra;

if (val)
ra = __pa(&per_cpu(mmu_stats, smp_processor_id()));
else
ra = 0UL;

return sun4v_mmustat_conf(ra, &orig_ra);
}

static ssize_t show_mmustat_enable(struct sys_device *s, char *buf)
{
unsigned long val = run_on_cpu(s->id, read_mmustat_enable, 0);
return sprintf(buf, "%lx\n", val);
}

static ssize_t store_mmustat_enable(struct sys_device *s, const char *buf, size_t count)
{
unsigned long val, err;
int ret = sscanf(buf, "%ld", &val);

if (ret != 1)
return -EINVAL;

err = run_on_cpu(s->id, write_mmustat_enable, val);
if (err)
return -EIO;

return count;
}

static SYSDEV_ATTR(mmustat_enable, 0644, show_mmustat_enable, store_mmustat_enable);

static int mmu_stats_supported;

static int register_mmu_stats(struct sys_device *s)
{
if (!mmu_stats_supported)
return 0;
sysdev_create_file(s, &attr_mmustat_enable);
return sysfs_create_group(&s->kobj, &mmu_stat_group);
}

#ifdef CONFIG_HOTPLUG_CPU
static void unregister_mmu_stats(struct sys_device *s)
{
if (!mmu_stats_supported)
return;
sysfs_remove_group(&s->kobj, &mmu_stat_group);
sysdev_remove_file(s, &attr_mmustat_enable);
}
#endif

#define SHOW_CPUDATA_ULONG_NAME(NAME, MEMBER) \
static ssize_t show_##NAME(struct sys_device *dev, char *buf) \
{ \
cpuinfo_sparc *c = &cpu_data(dev->id); \
return sprintf(buf, "%lu\n", c->MEMBER); \
}

#define SHOW_UINT_NAME(NAME, MEMBER) \
#define SHOW_CPUDATA_UINT_NAME(NAME, MEMBER) \
static ssize_t show_##NAME(struct sys_device *dev, char *buf) \
{ \
struct cpu *cpu = container_of(dev, struct cpu, sysdev); \
cpuinfo_sparc *c = &cpu_data(cpu->sysdev.id); \
cpuinfo_sparc *c = &cpu_data(dev->id); \
return sprintf(buf, "%u\n", c->MEMBER); \
}

SHOW_ULONG_NAME(clock_tick, clock_tick);
SHOW_ULONG_NAME(udelay_val, udelay_val);
SHOW_UINT_NAME(l1_dcache_size, dcache_size);
SHOW_UINT_NAME(l1_dcache_line_size, dcache_line_size);
SHOW_UINT_NAME(l1_icache_size, icache_size);
SHOW_UINT_NAME(l1_icache_line_size, icache_line_size);
SHOW_UINT_NAME(l2_cache_size, ecache_size);
SHOW_UINT_NAME(l2_cache_line_size, ecache_line_size);
SHOW_CPUDATA_ULONG_NAME(clock_tick, clock_tick);
SHOW_CPUDATA_ULONG_NAME(udelay_val, udelay_val);
SHOW_CPUDATA_UINT_NAME(l1_dcache_size, dcache_size);
SHOW_CPUDATA_UINT_NAME(l1_dcache_line_size, dcache_line_size);
SHOW_CPUDATA_UINT_NAME(l1_icache_size, icache_size);
SHOW_CPUDATA_UINT_NAME(l1_icache_line_size, icache_line_size);
SHOW_CPUDATA_UINT_NAME(l2_cache_size, ecache_size);
SHOW_CPUDATA_UINT_NAME(l2_cache_line_size, ecache_line_size);

static struct sysdev_attribute cpu_core_attrs[] = {
_SYSDEV_ATTR(clock_tick, 0444, show_clock_tick, NULL),
Expand All @@ -46,6 +212,8 @@ static struct sysdev_attribute cpu_core_attrs[] = {
_SYSDEV_ATTR(l2_cache_line_size, 0444, show_l2_cache_line_size, NULL),
};

static DEFINE_PER_CPU(struct cpu, cpu_devices);

static void register_cpu_online(unsigned int cpu)
{
struct cpu *c = &per_cpu(cpu_devices, cpu);
Expand All @@ -54,6 +222,8 @@ static void register_cpu_online(unsigned int cpu)

for (i = 0; i < ARRAY_SIZE(cpu_core_attrs); i++)
sysdev_create_file(s, &cpu_core_attrs[i]);

register_mmu_stats(s);
}

#ifdef CONFIG_HOTPLUG_CPU
Expand All @@ -63,6 +233,7 @@ static void unregister_cpu_online(unsigned int cpu)
struct sys_device *s = &c->sysdev;
int i;

unregister_mmu_stats(s);
for (i = 0; i < ARRAY_SIZE(cpu_core_attrs); i++)
sysdev_remove_file(s, &cpu_core_attrs[i]);
}
Expand Down Expand Up @@ -92,10 +263,24 @@ static struct notifier_block __cpuinitdata sysfs_cpu_nb = {
.notifier_call = sysfs_cpu_notify,
};

static void __init check_mmu_stats(void)
{
unsigned long dummy1, err;

if (tlb_type != hypervisor)
return;

err = sun4v_mmustat_info(&dummy1);
if (!err)
mmu_stats_supported = 1;
}

static int __init topology_init(void)
{
int cpu;

check_mmu_stats();

register_cpu_notifier(&sysfs_cpu_nb);

for_each_possible_cpu(cpu) {
Expand Down
5 changes: 5 additions & 0 deletions include/asm-sparc64/hypervisor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2798,6 +2798,11 @@ struct hv_mmu_statistics {
*/
#define HV_FAST_MMUSTAT_INFO 0x103

#ifndef __ASSEMBLY__
extern unsigned long sun4v_mmustat_conf(unsigned long ra, unsigned long *orig_ra);
extern unsigned long sun4v_mmustat_info(unsigned long *ra);
#endif

/* NCS crypto services */

/* ncs_request() sub-function numbers */
Expand Down

0 comments on commit d887ab3

Please sign in to comment.