Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 994025ca authored by Jeremy Fitzhardinge's avatar Jeremy Fitzhardinge Committed by Ingo Molnar
Browse files

xen: add debugfs support



Add support for exporting statistics on mmu updates, multicall
batching and pv spinlocks into debugfs. The base path is xen/ and
each subsystem adds its own directory: mmu, multicalls, spinlocks.

In each directory, writing 1 to "zero_stats" will cause the
corresponding stats to be zeroed the next time they're updated.

Signed-off-by: default avatarJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Acked-by: default avatarJan Beulich <jbeulich@novell.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 168d2f46
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -28,3 +28,11 @@ config XEN_SAVE_RESTORE
       bool
       depends on PM
       default y

config XEN_DEBUG_FS
	bool "Enable Xen debug and tuning parameters in debugfs"
	depends on XEN && DEBUG_FS
	default n
	help
	  Enable statistics output and various tuning options in debugfs.
	  Enabling this option may incur a significant performance overhead.
 No newline at end of file
+2 −1
Original line number Diff line number Diff line
@@ -9,3 +9,4 @@ obj-y := enlighten.o setup.o multicalls.o mmu.o irq.o \
			time.o xen-asm_$(BITS).o grant-table.o suspend.o

obj-$(CONFIG_SMP)		+= smp.o spinlock.o
obj-$(CONFIG_XEN_DEBUG_FS)	+= debugfs.o
 No newline at end of file

arch/x86/xen/debugfs.c

0 → 100644
+123 −0
Original line number Diff line number Diff line
#include <linux/init.h>
#include <linux/debugfs.h>
#include <linux/module.h>

#include "debugfs.h"

static struct dentry *d_xen_debug;

struct dentry * __init xen_init_debugfs(void)
{
	if (!d_xen_debug) {
		d_xen_debug = debugfs_create_dir("xen", NULL);

		if (!d_xen_debug)
			pr_warning("Could not create 'xen' debugfs directory\n");
	}

	return d_xen_debug;
}

struct array_data
{
	void *array;
	unsigned elements;
};

static int u32_array_open(struct inode *inode, struct file *file)
{
	file->private_data = NULL;
	return nonseekable_open(inode, file);
}

static size_t format_array(char *buf, size_t bufsize, const char *fmt,
			   u32 *array, unsigned array_size)
{
	size_t ret = 0;
	unsigned i;

	for(i = 0; i < array_size; i++) {
		size_t len;

		len = snprintf(buf, bufsize, fmt, array[i]);
		len++;	/* ' ' or '\n' */
		ret += len;

		if (buf) {
			buf += len;
			bufsize -= len;
			buf[-1] = (i == array_size-1) ? '\n' : ' ';
		}
	}

	ret++;		/* \0 */
	if (buf)
		*buf = '\0';

	return ret;
}

static char *format_array_alloc(const char *fmt, u32 *array, unsigned array_size)
{
	size_t len = format_array(NULL, 0, fmt, array, array_size);
	char *ret;

	ret = kmalloc(len, GFP_KERNEL);
	if (ret == NULL)
		return NULL;

	format_array(ret, len, fmt, array, array_size);
	return ret;
}

static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len,
			      loff_t *ppos)
{
	struct inode *inode = file->f_path.dentry->d_inode;
	struct array_data *data = inode->i_private;
	size_t size;

	if (*ppos == 0) {
		if (file->private_data) {
			kfree(file->private_data);
			file->private_data = NULL;
		}

		file->private_data = format_array_alloc("%u", data->array, data->elements);
	}

	size = 0;
	if (file->private_data)
		size = strlen(file->private_data);

	return simple_read_from_buffer(buf, len, ppos, file->private_data, size);
}

static int xen_array_release(struct inode *inode, struct file *file)
{
	kfree(file->private_data);

	return 0;
}

static struct file_operations u32_array_fops = {
	.owner	= THIS_MODULE,
	.open	= u32_array_open,
	.release= xen_array_release,
	.read	= u32_array_read,
};

struct dentry *xen_debugfs_create_u32_array(const char *name, mode_t mode,
					    struct dentry *parent,
					    u32 *array, unsigned elements)
{
	struct array_data *data = kmalloc(sizeof(*data), GFP_KERNEL);

	if (data == NULL)
		return NULL;

	data->array = array;
	data->elements = elements;

	return debugfs_create_file(name, mode, parent, data, &u32_array_fops);
}

arch/x86/xen/debugfs.h

0 → 100644
+10 −0
Original line number Diff line number Diff line
#ifndef _XEN_DEBUGFS_H
#define _XEN_DEBUGFS_H

struct dentry * __init xen_init_debugfs(void);

struct dentry *xen_debugfs_create_u32_array(const char *name, mode_t mode,
					    struct dentry *parent,
					    u32 *array, unsigned elements);

#endif /* _XEN_DEBUGFS_H */
+161 −2
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@
 */
#include <linux/sched.h>
#include <linux/highmem.h>
#include <linux/debugfs.h>
#include <linux/bug.h>

#include <asm/pgtable.h>
@@ -57,6 +58,61 @@

#include "multicalls.h"
#include "mmu.h"
#include "debugfs.h"

#define MMU_UPDATE_HISTO	30

#ifdef CONFIG_XEN_DEBUG_FS

static struct {
	u32 pgd_update;
	u32 pgd_update_pinned;
	u32 pgd_update_batched;

	u32 pud_update;
	u32 pud_update_pinned;
	u32 pud_update_batched;

	u32 pmd_update;
	u32 pmd_update_pinned;
	u32 pmd_update_batched;

	u32 pte_update;
	u32 pte_update_pinned;
	u32 pte_update_batched;

	u32 mmu_update;
	u32 mmu_update_extended;
	u32 mmu_update_histo[MMU_UPDATE_HISTO];

	u32 prot_commit;
	u32 prot_commit_batched;

	u32 set_pte_at;
	u32 set_pte_at_batched;
	u32 set_pte_at_pinned;
	u32 set_pte_at_current;
	u32 set_pte_at_kernel;
} mmu_stats;

static u8 zero_stats;

static inline void check_zero(void)
{
	if (unlikely(zero_stats)) {
		memset(&mmu_stats, 0, sizeof(mmu_stats));
		zero_stats = 0;
	}
}

#define ADD_STATS(elem, val)			\
	do { check_zero(); mmu_stats.elem += (val); } while(0)

#else  /* !CONFIG_XEN_DEBUG_FS */

#define ADD_STATS(elem, val)	do { (void)(val); } while(0)

#endif /* CONFIG_XEN_DEBUG_FS */

/*
 * Just beyond the highest usermode address.  STACK_TOP_MAX has a
@@ -243,11 +299,21 @@ static void xen_extend_mmu_update(const struct mmu_update *update)

	mcs = xen_mc_extend_args(__HYPERVISOR_mmu_update, sizeof(*u));

	if (mcs.mc != NULL)
	if (mcs.mc != NULL) {
		ADD_STATS(mmu_update_extended, 1);
		ADD_STATS(mmu_update_histo[mcs.mc->args[1]], -1);

		mcs.mc->args[1]++;
	else {

		if (mcs.mc->args[1] < MMU_UPDATE_HISTO)
			ADD_STATS(mmu_update_histo[mcs.mc->args[1]], 1);
		else
			ADD_STATS(mmu_update_histo[0], 1);
	} else {
		ADD_STATS(mmu_update, 1);
		mcs = __xen_mc_entry(sizeof(*u));
		MULTI_mmu_update(mcs.mc, mcs.args, 1, NULL, DOMID_SELF);
		ADD_STATS(mmu_update_histo[1], 1);
	}

	u = mcs.args;
@@ -267,6 +333,8 @@ void xen_set_pmd_hyper(pmd_t *ptr, pmd_t val)
	u.val = pmd_val_ma(val);
	xen_extend_mmu_update(&u);

	ADD_STATS(pmd_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);

	xen_mc_issue(PARAVIRT_LAZY_MMU);

	preempt_enable();
@@ -274,6 +342,8 @@ void xen_set_pmd_hyper(pmd_t *ptr, pmd_t val)

void xen_set_pmd(pmd_t *ptr, pmd_t val)
{
	ADD_STATS(pmd_update, 1);

	/* If page is not pinned, we can just update the entry
	   directly */
	if (!xen_page_pinned(ptr)) {
@@ -281,6 +351,8 @@ void xen_set_pmd(pmd_t *ptr, pmd_t val)
		return;
	}

	ADD_STATS(pmd_update_pinned, 1);

	xen_set_pmd_hyper(ptr, val);
}

@@ -300,12 +372,18 @@ void xen_set_pte_at(struct mm_struct *mm, unsigned long addr,
	if (mm == &init_mm)
		preempt_disable();

	ADD_STATS(set_pte_at, 1);
//	ADD_STATS(set_pte_at_pinned, xen_page_pinned(ptep));
	ADD_STATS(set_pte_at_current, mm == current->mm);
	ADD_STATS(set_pte_at_kernel, mm == &init_mm);

	if (mm == current->mm || mm == &init_mm) {
		if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU) {
			struct multicall_space mcs;
			mcs = xen_mc_entry(0);

			MULTI_update_va_mapping(mcs.mc, addr, pteval, 0);
			ADD_STATS(set_pte_at_batched, 1);
			xen_mc_issue(PARAVIRT_LAZY_MMU);
			goto out;
		} else
@@ -336,6 +414,9 @@ void xen_ptep_modify_prot_commit(struct mm_struct *mm, unsigned long addr,
	u.val = pte_val_ma(pte);
	xen_extend_mmu_update(&u);

	ADD_STATS(prot_commit, 1);
	ADD_STATS(prot_commit_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);

	xen_mc_issue(PARAVIRT_LAZY_MMU);
}

@@ -402,6 +483,8 @@ void xen_set_pud_hyper(pud_t *ptr, pud_t val)
	u.val = pud_val_ma(val);
	xen_extend_mmu_update(&u);

	ADD_STATS(pud_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);

	xen_mc_issue(PARAVIRT_LAZY_MMU);

	preempt_enable();
@@ -409,6 +492,8 @@ void xen_set_pud_hyper(pud_t *ptr, pud_t val)

void xen_set_pud(pud_t *ptr, pud_t val)
{
	ADD_STATS(pud_update, 1);

	/* If page is not pinned, we can just update the entry
	   directly */
	if (!xen_page_pinned(ptr)) {
@@ -416,11 +501,17 @@ void xen_set_pud(pud_t *ptr, pud_t val)
		return;
	}

	ADD_STATS(pud_update_pinned, 1);

	xen_set_pud_hyper(ptr, val);
}

void xen_set_pte(pte_t *ptep, pte_t pte)
{
	ADD_STATS(pte_update, 1);
//	ADD_STATS(pte_update_pinned, xen_page_pinned(ptep));
	ADD_STATS(pte_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);

#ifdef CONFIG_X86_PAE
	ptep->pte_high = pte.pte_high;
	smp_wmb();
@@ -517,6 +608,8 @@ void xen_set_pgd(pgd_t *ptr, pgd_t val)
{
	pgd_t *user_ptr = xen_get_user_pgd(ptr);

	ADD_STATS(pgd_update, 1);

	/* If page is not pinned, we can just update the entry
	   directly */
	if (!xen_page_pinned(ptr)) {
@@ -528,6 +621,9 @@ void xen_set_pgd(pgd_t *ptr, pgd_t val)
		return;
	}

	ADD_STATS(pgd_update_pinned, 1);
	ADD_STATS(pgd_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);

	/* If it's pinned, then we can at least batch the kernel and
	   user updates together. */
	xen_mc_batch();
@@ -1003,3 +1099,66 @@ void xen_exit_mmap(struct mm_struct *mm)

	spin_unlock(&mm->page_table_lock);
}

#ifdef CONFIG_XEN_DEBUG_FS

static struct dentry *d_mmu_debug;

static int __init xen_mmu_debugfs(void)
{
	struct dentry *d_xen = xen_init_debugfs();

	if (d_xen == NULL)
		return -ENOMEM;

	d_mmu_debug = debugfs_create_dir("mmu", d_xen);

	debugfs_create_u8("zero_stats", 0644, d_mmu_debug, &zero_stats);

	debugfs_create_u32("pgd_update", 0444, d_mmu_debug, &mmu_stats.pgd_update);
	debugfs_create_u32("pgd_update_pinned", 0444, d_mmu_debug,
			   &mmu_stats.pgd_update_pinned);
	debugfs_create_u32("pgd_update_batched", 0444, d_mmu_debug,
			   &mmu_stats.pgd_update_pinned);

	debugfs_create_u32("pud_update", 0444, d_mmu_debug, &mmu_stats.pud_update);
	debugfs_create_u32("pud_update_pinned", 0444, d_mmu_debug,
			   &mmu_stats.pud_update_pinned);
	debugfs_create_u32("pud_update_batched", 0444, d_mmu_debug,
			   &mmu_stats.pud_update_pinned);

	debugfs_create_u32("pmd_update", 0444, d_mmu_debug, &mmu_stats.pmd_update);
	debugfs_create_u32("pmd_update_pinned", 0444, d_mmu_debug,
			   &mmu_stats.pmd_update_pinned);
	debugfs_create_u32("pmd_update_batched", 0444, d_mmu_debug,
			   &mmu_stats.pmd_update_pinned);

	debugfs_create_u32("pte_update", 0444, d_mmu_debug, &mmu_stats.pte_update);
//	debugfs_create_u32("pte_update_pinned", 0444, d_mmu_debug,
//			   &mmu_stats.pte_update_pinned);
	debugfs_create_u32("pte_update_batched", 0444, d_mmu_debug,
			   &mmu_stats.pte_update_pinned);

	debugfs_create_u32("mmu_update", 0444, d_mmu_debug, &mmu_stats.mmu_update);
	debugfs_create_u32("mmu_update_extended", 0444, d_mmu_debug,
			   &mmu_stats.mmu_update_extended);
	xen_debugfs_create_u32_array("mmu_update_histo", 0444, d_mmu_debug,
				     mmu_stats.mmu_update_histo, 20);

	debugfs_create_u32("set_pte_at", 0444, d_mmu_debug, &mmu_stats.set_pte_at);
	debugfs_create_u32("set_pte_at_batched", 0444, d_mmu_debug,
			   &mmu_stats.set_pte_at_batched);
	debugfs_create_u32("set_pte_at_current", 0444, d_mmu_debug,
			   &mmu_stats.set_pte_at_current);
	debugfs_create_u32("set_pte_at_kernel", 0444, d_mmu_debug,
			   &mmu_stats.set_pte_at_kernel);

	debugfs_create_u32("prot_commit", 0444, d_mmu_debug, &mmu_stats.prot_commit);
	debugfs_create_u32("prot_commit_batched", 0444, d_mmu_debug,
			   &mmu_stats.prot_commit_batched);

	return 0;
}
fs_initcall(xen_mmu_debugfs);

#endif	/* CONFIG_XEN_DEBUG_FS */
Loading