aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-10-14 09:07:51 +0100
committerKeir Fraser <keir.fraser@citrix.com>2009-10-14 09:07:51 +0100
commit0482af4d96d0a48c0e7eb9f9033408c014de9a4d (patch)
treecd5f29e8439383e5ed3ca6b6a3aa8a10c2ca06b6
parentc637aa4f78d48ffcd39b8352a7fd325add0f89f9 (diff)
downloadxen-0482af4d96d0a48c0e7eb9f9033408c014de9a4d.tar.gz
xen-0482af4d96d0a48c0e7eb9f9033408c014de9a4d.tar.bz2
xen-0482af4d96d0a48c0e7eb9f9033408c014de9a4d.zip
Spinlock profiling (enable in build with lock_profile=y)
Adds new tool xenlockprof to run from dom0. From: Juergen Gross <juergen.gross@ts.fujitsu.com> Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
-rw-r--r--tools/libxc/xc_misc.c24
-rw-r--r--tools/libxc/xenctrl.h8
-rw-r--r--tools/misc/Makefile6
-rw-r--r--tools/misc/xenlockprof.c136
-rw-r--r--xen/Rules.mk7
-rw-r--r--xen/arch/x86/domain.c2
-rw-r--r--xen/arch/x86/xen.lds.S7
-rw-r--r--xen/common/domain.c7
-rw-r--r--xen/common/keyhandler.c20
-rw-r--r--xen/common/spinlock.c258
-rw-r--r--xen/common/sysctl.c12
-rw-r--r--xen/include/public/sysctl.h35
-rw-r--r--xen/include/xen/sched.h2
-rw-r--r--xen/include/xen/spinlock.h97
14 files changed, 609 insertions, 12 deletions
diff --git a/tools/libxc/xc_misc.c b/tools/libxc/xc_misc.c
index 7fa1607896..ffc04fb50f 100644
--- a/tools/libxc/xc_misc.c
+++ b/tools/libxc/xc_misc.c
@@ -121,6 +121,30 @@ int xc_perfc_control(int xc_handle,
return rc;
}
+int xc_lockprof_control(int xc_handle,
+ uint32_t opcode,
+ uint32_t *n_elems,
+ uint64_t *time,
+ xc_lockprof_data_t *data)
+{
+ int rc;
+ DECLARE_SYSCTL;
+
+ sysctl.cmd = XEN_SYSCTL_lockprof_op;
+ sysctl.u.lockprof_op.cmd = opcode;
+ sysctl.u.lockprof_op.max_elem = n_elems ? *n_elems : 0;
+ set_xen_guest_handle(sysctl.u.lockprof_op.data, data);
+
+ rc = do_sysctl(xc_handle, &sysctl);
+
+ if (n_elems)
+ *n_elems = sysctl.u.lockprof_op.nr_elem;
+ if (time)
+ *time = sysctl.u.lockprof_op.time;
+
+ return rc;
+}
+
int xc_getcpuinfo(int xc_handle, int max_cpus,
xc_cpuinfo_t *info, int *nr_cpus)
{
diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h
index 24e6f1004e..ea52fcc446 100644
--- a/tools/libxc/xenctrl.h
+++ b/tools/libxc/xenctrl.h
@@ -700,6 +700,14 @@ int xc_perfc_control(int xc_handle,
int *nbr_desc,
int *nbr_val);
+typedef xen_sysctl_lockprof_data_t xc_lockprof_data_t;
+/* IMPORTANT: The caller is responsible for mlock()'ing the @data array. */
+int xc_lockprof_control(int xc_handle,
+ uint32_t opcode,
+ uint32_t *n_elems,
+ uint64_t *time,
+ xc_lockprof_data_t *data);
+
/**
* Memory maps a range within one domain to a local address range. Mappings
* should be unmapped with munmap and should follow the same rules as mmap
diff --git a/tools/misc/Makefile b/tools/misc/Makefile
index d609971813..6f91da5cb1 100644
--- a/tools/misc/Makefile
+++ b/tools/misc/Makefile
@@ -10,7 +10,7 @@ CFLAGS += $(INCLUDES)
HDRS = $(wildcard *.h)
-TARGETS-y := xenperf xenpm xen-tmem-list-parse gtraceview gtracestat
+TARGETS-y := xenperf xenpm xen-tmem-list-parse gtraceview gtracestat xenlockprof
TARGETS-$(CONFIG_X86) += xen-detect xen-hvmctx
TARGETS := $(TARGETS-y)
@@ -22,7 +22,7 @@ INSTALL_BIN-y := xencons
INSTALL_BIN-$(CONFIG_X86) += xen-detect
INSTALL_BIN := $(INSTALL_BIN-y)
-INSTALL_SBIN-y := xm xen-bugtool xen-python-path xend xenperf xsview xenpm xen-tmem-list-parse gtraceview gtracestat
+INSTALL_SBIN-y := xm xen-bugtool xen-python-path xend xenperf xsview xenpm xen-tmem-list-parse gtraceview gtracestat xenlockprof
INSTALL_SBIN-$(CONFIG_X86) += xen-hvmctx
INSTALL_SBIN := $(INSTALL_SBIN-y)
@@ -49,7 +49,7 @@ clean:
%.o: %.c $(HDRS) Makefile
$(CC) -c $(CFLAGS) -o $@ $<
-xen-hvmctx xenperf xenpm gtracestat: %: %.o Makefile
+xen-hvmctx xenperf xenpm gtracestat xenlockprof: %: %.o Makefile
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LDFLAGS_libxenctrl)
gtraceview: %: %.o Makefile
diff --git a/tools/misc/xenlockprof.c b/tools/misc/xenlockprof.c
new file mode 100644
index 0000000000..f2a0a90568
--- /dev/null
+++ b/tools/misc/xenlockprof.c
@@ -0,0 +1,136 @@
+/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
+ ****************************************************************************
+ * (C) 2009 - Juergen Gross - Fujitsu Technology Solutions
+ ****************************************************************************
+ *
+ * File: xenlockprof.c
+ * Author: Juergen Gross (juergen.gross@ts.fujitsu.com)
+ * Date: Oct 2009
+ *
+ * Description:
+ */
+
+#include <xenctrl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+
+static int lock_pages(void *addr, size_t len)
+{
+ int e = 0;
+#ifndef __sun__
+ e = mlock(addr, len);
+#endif
+ return (e);
+}
+
+static void unlock_pages(void *addr, size_t len)
+{
+#ifndef __sun__
+ munlock(addr, len);
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+ int xc_handle;
+ uint32_t i, j, n;
+ uint64_t time;
+ double l, b, sl, sb;
+ char name[60];
+ xc_lockprof_data_t *data;
+
+ if ((argc > 2) || ((argc == 2) && (strcmp(argv[1], "-r") != 0)))
+ {
+ printf("%s: [-r]\n", argv[0]);
+ printf("no args: print lock profile data\n");
+ printf(" -r : reset profile data\n");
+ return 1;
+ }
+
+ if ((xc_handle = xc_interface_open()) == -1)
+ {
+ fprintf(stderr, "Error opening xc interface: %d (%s)\n",
+ errno, strerror(errno));
+ return 1;
+ }
+
+ if (argc > 1)
+ {
+ if (xc_lockprof_control(xc_handle, XEN_SYSCTL_LOCKPROF_reset, NULL,
+ NULL, NULL) != 0)
+ {
+ fprintf(stderr, "Error reseting profile data: %d (%s)\n",
+ errno, strerror(errno));
+ return 1;
+ }
+ return 1;
+ }
+
+ n = 0;
+ if (xc_lockprof_control(xc_handle, XEN_SYSCTL_LOCKPROF_query, &n,
+ NULL, NULL) != 0)
+ {
+ fprintf(stderr, "Error getting number of profile records: %d (%s)\n",
+ errno, strerror(errno));
+ return 1;
+ }
+
+ n += 32; /* just to be sure */
+ data = malloc(sizeof(*data) * n);
+ if ((data == NULL) || (lock_pages(data, sizeof(*data) * n) != 0))
+ {
+ fprintf(stderr, "Could not alloc or lock buffers: %d (%s)\n",
+ errno, strerror(errno));
+ return 1;
+ }
+
+ i = n;
+ if ( xc_lockprof_control(xc_handle, XEN_SYSCTL_LOCKPROF_query, &i,
+ &time, data) != 0)
+ {
+ fprintf(stderr, "Error getting profile records: %d (%s)\n",
+ errno, strerror(errno));
+ return 1;
+ }
+
+ unlock_pages(data, sizeof(*data) * n);
+
+ if (i > n)
+ {
+ printf("data incomplete, %d records are missing!\n\n", i - n);
+ i = n;
+ }
+ sl = 0;
+ sb = 0;
+ for (j = 0; j < i; j++)
+ {
+ switch (data[j].type)
+ {
+ case LOCKPROF_TYPE_GLOBAL:
+ sprintf(name, "global lock %s", data[j].name);
+ break;
+ case LOCKPROF_TYPE_PERDOM:
+ sprintf(name, "domain %d lock %s", data[j].idx, data[j].name);
+ break;
+ default:
+ sprintf(name, "unknown type(%d) %d lock %s", data[j].type,
+ data[j].idx, data[j].name);
+ break;
+ }
+ l = (double)(data[j].lock_time) / 1E+09;
+ b = (double)(data[j].block_time) / 1E+09;
+ sl += l;
+ sb += b;
+ printf("%-50s: lock:%12ld(%20.9fs), block:%12ld(%20.9fs)\n",
+ name, data[j].lock_cnt, l, data[j].block_cnt, b);
+ }
+ l = (double)time / 1E+09;
+ printf("total profiling time: %20.9fs\n", l);
+ printf("total locked time: %20.9fs\n", sl);
+ printf("total blocked time: %20.9fs\n", sb);
+
+ return 0;
+}
diff --git a/xen/Rules.mk b/xen/Rules.mk
index 3f141c33bc..a28efaf902 100644
--- a/xen/Rules.mk
+++ b/xen/Rules.mk
@@ -6,6 +6,7 @@
verbose ?= n
perfc ?= n
perfc_arrays ?= n
+lock_profile ?= n
crash_debug ?= n
frame_pointer ?= n
@@ -52,7 +53,9 @@ CFLAGS-$(verbose) += -DVERBOSE
CFLAGS-$(crash_debug) += -DCRASH_DEBUG
CFLAGS-$(perfc) += -DPERF_COUNTERS
CFLAGS-$(perfc_arrays) += -DPERF_ARRAYS
+CFLAGS-$(lock_profile) += -DLOCK_PROFILE
CFLAGS-$(frame_pointer) += -fno-omit-frame-pointer -DCONFIG_FRAME_POINTER
+CFLAGS-$(privileged_stubdoms) += -DPRIVILEGED_STUBDOMS
ifneq ($(max_phys_cpus),)
CFLAGS-y += -DMAX_PHYS_CPUS=$(max_phys_cpus)
@@ -61,10 +64,6 @@ ifneq ($(max_phys_irqs),)
CFLAGS-y += -DMAX_PHYS_IRQS=$(max_phys_irqs)
endif
-ifeq ($(privileged_stubdoms),y)
-CFLAGS += -DPRIVILEGED_STUBDOMS
-endif
-
AFLAGS-y += -D__ASSEMBLY__
ALL_OBJS := $(ALL_OBJS-y)
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 20d3e3fc80..61a605e19f 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -32,6 +32,7 @@
#include <xen/acpi.h>
#include <xen/pci.h>
#include <xen/paging.h>
+#include <public/sysctl.h>
#include <asm/regs.h>
#include <asm/mc146818rtc.h>
#include <asm/system.h>
@@ -185,6 +186,7 @@ struct domain *alloc_domain_struct(void)
void free_domain_struct(struct domain *d)
{
+ lock_profile_deregister_struct(LOCKPROF_TYPE_PERDOM, d);
free_xenheap_pages(d, get_order_from_bytes(sizeof(*d)));
}
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 5de98dff64..242f8bcb11 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -62,6 +62,13 @@ SECTIONS
*(.data.read_mostly)
} :text
+#ifdef LOCK_PROFILE
+ . = ALIGN(32);
+ __lock_profile_start = .;
+ .lockprofile.data : { *(.lockprofile.data) } :text
+ __lock_profile_end = .;
+#endif
+
. = ALIGN(4096); /* Init code and data */
__init_begin = .;
.init.text : {
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 86f0743a34..465aa00a44 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -29,6 +29,7 @@
#include <acpi/cpufreq/cpufreq.h>
#include <asm/debugger.h>
#include <public/sched.h>
+#include <public/sysctl.h>
#include <public/vcpu.h>
#include <xsm/xsm.h>
#include <xen/trace.h>
@@ -221,13 +222,15 @@ struct domain *domain_create(
memset(d, 0, sizeof(*d));
d->domain_id = domid;
+ lock_profile_register_struct(LOCKPROF_TYPE_PERDOM, d, domid, "Domain");
+
if ( xsm_alloc_security_domain(d) != 0 )
goto fail;
init_status |= INIT_xsm;
atomic_set(&d->refcnt, 1);
- spin_lock_init(&d->domain_lock);
- spin_lock_init(&d->page_alloc_lock);
+ spin_lock_init_prof(d, domain_lock);
+ spin_lock_init_prof(d, page_alloc_lock);
spin_lock_init(&d->shutdown_lock);
spin_lock_init(&d->hypercall_deadlock_mutex);
INIT_PAGE_LIST_HEAD(&d->page_list);
diff --git a/xen/common/keyhandler.c b/xen/common/keyhandler.c
index d8122ca096..844d890df7 100644
--- a/xen/common/keyhandler.c
+++ b/xen/common/keyhandler.c
@@ -347,6 +347,20 @@ static struct keyhandler perfc_reset_keyhandler = {
};
#endif
+#ifdef LOCK_PROFILE
+extern void spinlock_profile_printall(unsigned char key);
+static struct keyhandler spinlock_printall_keyhandler = {
+ .diagnostic = 1,
+ .u.fn = spinlock_profile_printall,
+ .desc = "print lock profile info"
+};
+extern void spinlock_profile_reset(unsigned char key);
+static struct keyhandler spinlock_reset_keyhandler = {
+ .u.fn = spinlock_profile_reset,
+ .desc = "reset lock profile info"
+};
+#endif
+
static void run_all_nonirq_keyhandlers(unsigned long unused)
{
/* Fire all the non-IRQ-context diagnostic keyhandlers */
@@ -428,6 +442,12 @@ void __init initialize_keytable(void)
register_keyhandler('p', &perfc_printall_keyhandler);
register_keyhandler('P', &perfc_reset_keyhandler);
#endif
+
+#ifdef LOCK_PROFILE
+ register_keyhandler('l', &spinlock_printall_keyhandler);
+ register_keyhandler('L', &spinlock_reset_keyhandler);
+#endif
+
}
/*
diff --git a/xen/common/spinlock.c b/xen/common/spinlock.c
index a17f0b2124..e6ad0436f5 100644
--- a/xen/common/spinlock.c
+++ b/xen/common/spinlock.c
@@ -1,7 +1,11 @@
+#include <xen/lib.h>
#include <xen/config.h>
#include <xen/irq.h>
#include <xen/smp.h>
+#include <xen/time.h>
#include <xen/spinlock.h>
+#include <xen/guest_access.h>
+#include <public/sysctl.h>
#include <asm/processor.h>
#ifndef NDEBUG
@@ -41,56 +45,97 @@ void spin_debug_disable(void)
#endif
+#ifdef LOCK_PROFILE
+
+#define LOCK_PROFILE_REL \
+ lock->profile.time_hold += NOW() - lock->profile.time_locked; \
+ lock->profile.lock_cnt++;
+#define LOCK_PROFILE_VAR s_time_t block = 0
+#define LOCK_PROFILE_BLOCK block = block ? : NOW();
+#define LOCK_PROFILE_GOT \
+ lock->profile.time_locked = NOW(); \
+ if (block) \
+ { \
+ lock->profile.time_block += lock->profile.time_locked - block; \
+ lock->profile.block_cnt++; \
+ }
+
+#else
+
+#define LOCK_PROFILE_REL
+#define LOCK_PROFILE_VAR
+#define LOCK_PROFILE_BLOCK
+#define LOCK_PROFILE_GOT
+
+#endif
+
void _spin_lock(spinlock_t *lock)
{
+ LOCK_PROFILE_VAR;
+
check_lock(&lock->debug);
while ( unlikely(!_raw_spin_trylock(&lock->raw)) )
+ {
+ LOCK_PROFILE_BLOCK;
while ( likely(_raw_spin_is_locked(&lock->raw)) )
cpu_relax();
+ }
+ LOCK_PROFILE_GOT;
}
void _spin_lock_irq(spinlock_t *lock)
{
+ LOCK_PROFILE_VAR;
+
ASSERT(local_irq_is_enabled());
local_irq_disable();
check_lock(&lock->debug);
while ( unlikely(!_raw_spin_trylock(&lock->raw)) )
{
+ LOCK_PROFILE_BLOCK;
local_irq_enable();
while ( likely(_raw_spin_is_locked(&lock->raw)) )
cpu_relax();
local_irq_disable();
}
+ LOCK_PROFILE_GOT;
}
unsigned long _spin_lock_irqsave(spinlock_t *lock)
{
unsigned long flags;
+ LOCK_PROFILE_VAR;
+
local_irq_save(flags);
check_lock(&lock->debug);
while ( unlikely(!_raw_spin_trylock(&lock->raw)) )
{
+ LOCK_PROFILE_BLOCK;
local_irq_restore(flags);
while ( likely(_raw_spin_is_locked(&lock->raw)) )
cpu_relax();
local_irq_save(flags);
}
+ LOCK_PROFILE_GOT;
return flags;
}
void _spin_unlock(spinlock_t *lock)
{
+ LOCK_PROFILE_REL;
_raw_spin_unlock(&lock->raw);
}
void _spin_unlock_irq(spinlock_t *lock)
{
+ LOCK_PROFILE_REL;
_raw_spin_unlock(&lock->raw);
local_irq_enable();
}
void _spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
{
+ LOCK_PROFILE_REL;
_raw_spin_unlock(&lock->raw);
local_irq_restore(flags);
}
@@ -104,13 +149,32 @@ int _spin_is_locked(spinlock_t *lock)
int _spin_trylock(spinlock_t *lock)
{
check_lock(&lock->debug);
+#ifndef LOCK_PROFILE
return _raw_spin_trylock(&lock->raw);
+#else
+ if (!_raw_spin_trylock(&lock->raw)) return 0;
+ lock->profile.time_locked = NOW();
+ return 1;
+#endif
}
void _spin_barrier(spinlock_t *lock)
{
+#ifdef LOCK_PROFILE
+ s_time_t block = NOW();
+ u64 loop = 0;
+
+ check_lock(&lock->debug);
+ do { mb(); loop++;} while ( _raw_spin_is_locked(&lock->raw) );
+ if (loop > 1)
+ {
+ lock->profile.time_block += NOW() - block;
+ lock->profile.block_cnt++;
+ }
+#else
check_lock(&lock->debug);
do { mb(); } while ( _raw_spin_is_locked(&lock->raw) );
+#endif
mb();
}
@@ -248,3 +312,197 @@ int _rw_is_write_locked(rwlock_t *lock)
check_lock(&lock->debug);
return _raw_rw_is_write_locked(&lock->raw);
}
+
+#ifdef LOCK_PROFILE
+struct lock_profile_anc {
+ struct lock_profile_qhead *head_q; /* first head of this type */
+ char *name; /* descriptive string for print */
+};
+
+typedef void lock_profile_subfunc(struct lock_profile *, int32_t, int32_t,
+ void *);
+
+extern struct lock_profile *__lock_profile_start;
+extern struct lock_profile *__lock_profile_end;
+
+static s_time_t lock_profile_start = 0;
+static struct lock_profile_anc lock_profile_ancs[LOCKPROF_TYPE_N];
+static struct lock_profile_qhead lock_profile_glb_q;
+static spinlock_t lock_profile_lock = SPIN_LOCK_UNLOCKED;
+
+static void spinlock_profile_iterate(lock_profile_subfunc *sub, void *par)
+{
+ int i;
+ struct lock_profile_qhead *hq;
+ struct lock_profile *eq;
+
+ spin_lock(&lock_profile_lock);
+ for (i = 0; i < LOCKPROF_TYPE_N; i++)
+ {
+ for (hq = lock_profile_ancs[i].head_q; hq; hq = hq->head_q)
+ {
+ for (eq = hq->elem_q; eq; eq = eq->next)
+ {
+ sub(eq, i, hq->idx, par);
+ }
+ }
+ }
+ spin_unlock(&lock_profile_lock);
+ return;
+}
+
+static void spinlock_profile_print_elem(struct lock_profile *data,
+ int32_t type, int32_t idx, void *par)
+{
+ if (type == LOCKPROF_TYPE_GLOBAL)
+ printk("%s %s:\n", lock_profile_ancs[idx].name, data->name);
+ else
+ printk("%s %d %s:\n", lock_profile_ancs[idx].name, idx, data->name);
+ printk(" lock:%12ld(%08X:%08X), block:%12ld(%08X:%08X)\n",
+ data->lock_cnt, (u32)(data->time_hold >> 32), (u32)data->time_hold,
+ data->block_cnt, (u32)(data->time_block >> 32), (u32)data->time_block);
+ return;
+}
+
+void spinlock_profile_printall(unsigned char key)
+{
+ s_time_t now = NOW();
+ s_time_t diff;
+
+ diff = now - lock_profile_start;
+ printk("Xen lock profile info SHOW (now = %08X:%08X, "
+ "total = %08X:%08X)\n", (u32)(now>>32), (u32)now,
+ (u32)(diff>>32), (u32)diff);
+ spinlock_profile_iterate(spinlock_profile_print_elem, NULL);
+ return;
+}
+
+static void spinlock_profile_reset_elem(struct lock_profile *data,
+ int32_t type, int32_t idx, void *par)
+{
+ data->lock_cnt = 0;
+ data->block_cnt = 0;
+ data->time_hold = 0;
+ data->time_block = 0;
+ return;
+}
+
+void spinlock_profile_reset(unsigned char key)
+{
+ s_time_t now = NOW();
+
+ if ( key != '\0' )
+ printk("Xen lock profile info RESET (now = %08X:%08X)\n",
+ (u32)(now>>32), (u32)now);
+ lock_profile_start = now;
+ spinlock_profile_iterate(spinlock_profile_reset_elem, NULL);
+ return;
+}
+
+typedef struct {
+ xen_sysctl_lockprof_op_t *pc;
+ int rc;
+} spinlock_profile_ucopy_t;
+
+static void spinlock_profile_ucopy_elem(struct lock_profile *data,
+ int32_t type, int32_t idx, void *par)
+{
+ spinlock_profile_ucopy_t *p;
+ xen_sysctl_lockprof_data_t elem;
+
+ p = (spinlock_profile_ucopy_t *)par;
+ if (p->rc)
+ return;
+
+ if (p->pc->nr_elem < p->pc->max_elem)
+ {
+ safe_strcpy(elem.name, data->name);
+ elem.type = type;
+ elem.idx = idx;
+ elem.lock_cnt = data->lock_cnt;
+ elem.block_cnt = data->block_cnt;
+ elem.lock_time = data->time_hold;
+ elem.block_time = data->time_block;
+ if (copy_to_guest_offset(p->pc->data, p->pc->nr_elem, &elem, 1))
+ {
+ p->rc = -EFAULT;
+ return;
+ }
+ }
+ p->pc->nr_elem++;
+
+ return;
+}
+
+/* Dom0 control of lock profiling */
+int spinlock_profile_control(xen_sysctl_lockprof_op_t *pc)
+{
+ int rc;
+ spinlock_profile_ucopy_t par;
+
+ rc = 0;
+ switch (pc->cmd)
+ {
+ case XEN_SYSCTL_LOCKPROF_reset:
+ spinlock_profile_reset('\0');
+ break;
+ case XEN_SYSCTL_LOCKPROF_query:
+ pc->nr_elem = 0;
+ par.rc = 0;
+ par.pc = pc;
+ spinlock_profile_iterate(spinlock_profile_ucopy_elem, &par);
+ pc->time = NOW() - lock_profile_start;
+ rc = par.rc;
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+void _lock_profile_register_struct(int32_t type,
+ struct lock_profile_qhead *qhead, int32_t idx, char *name)
+{
+ qhead->idx = idx;
+ spin_lock(&lock_profile_lock);
+ qhead->head_q = lock_profile_ancs[type].head_q;
+ lock_profile_ancs[type].head_q = qhead;
+ lock_profile_ancs[type].name = name;
+ spin_unlock(&lock_profile_lock);
+ return;
+}
+
+void _lock_profile_deregister_struct(int32_t type,
+ struct lock_profile_qhead *qhead)
+{
+ struct lock_profile_qhead **q;
+
+ spin_lock(&lock_profile_lock);
+ for (q = &lock_profile_ancs[type].head_q; *q; q = &((*q)->head_q))
+ {
+ if (*q == qhead)
+ {
+ *q = qhead->head_q;
+ break;
+ }
+ }
+ spin_unlock(&lock_profile_lock);
+ return;
+}
+
+static int __init lock_prof_init(void)
+{
+ struct lock_profile **q;
+
+ for (q = &__lock_profile_start; q < &__lock_profile_end; q++)
+ {
+ (*q)->next = lock_profile_glb_q.elem_q;
+ lock_profile_glb_q.elem_q = *q;
+ }
+ _lock_profile_register_struct(LOCKPROF_TYPE_GLOBAL, &lock_profile_glb_q,
+ 0, "Global lock");
+ return 0;
+}
+__initcall(lock_prof_init);
+#endif
diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c
index 4723de8dc1..dbe6fc1083 100644
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -29,6 +29,9 @@
extern long arch_do_sysctl(
struct xen_sysctl *op, XEN_GUEST_HANDLE(xen_sysctl_t) u_sysctl);
+#ifdef LOCK_PROFILE
+extern int spinlock_profile_control(xen_sysctl_lockprof_op_t *pc);
+#endif
long do_sysctl(XEN_GUEST_HANDLE(xen_sysctl_t) u_sysctl)
{
@@ -144,6 +147,15 @@ long do_sysctl(XEN_GUEST_HANDLE(xen_sysctl_t) u_sysctl)
break;
#endif
+#ifdef LOCK_PROFILE
+ case XEN_SYSCTL_lockprof_op:
+ {
+ ret = spinlock_profile_control(&op->u.lockprof_op);
+ if ( copy_to_guest(u_sysctl, op, 1) )
+ ret = -EFAULT;
+ }
+ break;
+#endif
case XEN_SYSCTL_debug_keys:
{
char c;
diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h
index f17cd45072..6dccf310e7 100644
--- a/xen/include/public/sysctl.h
+++ b/xen/include/public/sysctl.h
@@ -396,7 +396,7 @@ struct xen_sysctl_pm_op {
struct xen_get_cpufreq_para get_para;
struct xen_set_cpufreq_gov set_gov;
struct xen_set_cpufreq_para set_para;
- uint64_t get_avgfreq;
+ uint64_aligned_t get_avgfreq;
struct xen_get_cputopo get_topo;
uint32_t set_sched_opt_smt;
uint32_t get_max_cstate;
@@ -454,6 +454,38 @@ struct xen_sysctl_page_offline_op {
#define PG_OFFLINE_OWNER_SHIFT 16
+#define XEN_SYSCTL_lockprof_op 11
+/* Sub-operations: */
+#define XEN_SYSCTL_LOCKPROF_reset 1 /* Reset all profile data to zero. */
+#define XEN_SYSCTL_LOCKPROF_query 2 /* Get lock profile information. */
+/* Record-type: */
+#define LOCKPROF_TYPE_GLOBAL 0 /* global lock, idx meaningless */
+#define LOCKPROF_TYPE_PERDOM 1 /* per-domain lock, idx is domid */
+#define LOCKPROF_TYPE_N 2 /* number of types */
+struct xen_sysctl_lockprof_data {
+ char name[40]; /* lock name (may include up to 2 %d specifiers) */
+ int32_t type; /* LOCKPROF_TYPE_??? */
+ int32_t idx; /* index (e.g. domain id) */
+ uint64_aligned_t lock_cnt; /* # of locking succeeded */
+ uint64_aligned_t block_cnt; /* # of wait for lock */
+ uint64_aligned_t lock_time; /* nsecs lock held */
+ uint64_aligned_t block_time; /* nsecs waited for lock */
+};
+typedef struct xen_sysctl_lockprof_data xen_sysctl_lockprof_data_t;
+DEFINE_XEN_GUEST_HANDLE(xen_sysctl_lockprof_data_t);
+struct xen_sysctl_lockprof_op {
+ /* IN variables. */
+ uint32_t cmd; /* XEN_SYSCTL_LOCKPROF_??? */
+ uint32_t max_elem; /* size of output buffer */
+ /* OUT variables (query only). */
+ uint32_t nr_elem; /* number of elements available */
+ uint64_aligned_t time; /* nsecs of profile measurement */
+ /* profile information (or NULL) */
+ XEN_GUEST_HANDLE_64(xen_sysctl_lockprof_data_t) data;
+};
+typedef struct xen_sysctl_lockprof_op xen_sysctl_lockprof_op_t;
+DEFINE_XEN_GUEST_HANDLE(xen_sysctl_lockprof_op_t);
+
struct xen_sysctl {
uint32_t cmd;
uint32_t interface_version; /* XEN_SYSCTL_INTERFACE_VERSION */
@@ -471,6 +503,7 @@ struct xen_sysctl {
struct xen_sysctl_cpu_hotplug cpu_hotplug;
struct xen_sysctl_pm_op pm_op;
struct xen_sysctl_page_offline_op page_offline;
+ struct xen_sysctl_lockprof_op lockprof_op;
uint8_t pad[128];
} u;
};
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index d37c83fccb..3bf3a6aacd 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -276,6 +276,8 @@ struct domain
/* transcendent memory, auto-allocated on first tmem op by each domain */
void *tmem;
+
+ struct lock_profile_qhead profile_head;
};
struct domain_setup_info
diff --git a/xen/include/xen/spinlock.h b/xen/include/xen/spinlock.h
index a952f0700d..7c1a8fbe80 100644
--- a/xen/include/xen/spinlock.h
+++ b/xen/include/xen/spinlock.h
@@ -19,16 +19,109 @@ struct lock_debug { };
#define spin_debug_disable() ((void)0)
#endif
+#ifdef LOCK_PROFILE
+/*
+ lock profiling on:
+
+ Global locks which should be subject to profiling must be declared via
+ DEFINE_SPINLOCK.
+
+ For locks in structures further measures are necessary:
+ - the structure definition must include a profile_head with exactly this
+ name:
+
+ struct lock_profile_qhead profile_head;
+
+ - the single locks which are subject to profiling have to be initialized
+ via
+
+ spin_lock_init_prof(ptr, lock);
+
+ with ptr being the main structure pointer and lock the spinlock field
+
+ - each structure has to be added to profiling with
+
+ lock_profile_register_struct(type, ptr, idx, print);
+
+ with:
+ type: something like LOCKPROF_TYPE_PERDOM
+ ptr: pointer to the structure
+ idx: index of that structure, e.g. domid
+ print: descriptive string like "domain"
+
+ - removing of a structure is done via
+
+ lock_profile_deregister_struct(type, ptr);
+*/
+
+struct lock_profile {
+ struct lock_profile *next; /* forward link */
+ char *name; /* lock name */
+ u64 lock_cnt; /* # of complete locking ops */
+ u64 block_cnt; /* # of complete wait for lock */
+ s64 time_hold; /* cumulated lock time */
+ s64 time_block; /* cumulated wait time */
+ s64 time_locked; /* system time of last locking */
+};
+
+struct lock_profile_qhead {
+ struct lock_profile_qhead *head_q; /* next head of this type */
+ struct lock_profile *elem_q; /* first element in q */
+ int32_t idx; /* index for printout */
+};
+
+#define _LOCK_PROFILE(name) { 0, name, 0, 0, 0, 0, 0 }
+#define _LOCK_NO_PROFILE _LOCK_PROFILE(NULL)
+#define _LOCK_PROFILE_PTR(name) \
+ static struct lock_profile *__lock_profile_##name __attribute_used__ \
+ __attribute__ ((__section__(".lockprofile.data"))) = &name.profile
+#define _SPIN_LOCK_UNLOCKED(x) { _RAW_SPIN_LOCK_UNLOCKED, 0xfffu, 0, \
+ _LOCK_DEBUG, x }
+#define SPIN_LOCK_UNLOCKED _SPIN_LOCK_UNLOCKED(_LOCK_NO_PROFILE)
+#define DEFINE_SPINLOCK(l) \
+ spinlock_t l = _SPIN_LOCK_UNLOCKED(_LOCK_PROFILE(#l)); \
+ _LOCK_PROFILE_PTR(l)
+
+#define spin_lock_init_prof(s, l) \
+ do { \
+ (s)->l = (spinlock_t)_SPIN_LOCK_UNLOCKED(_LOCK_PROFILE(#l)); \
+ (s)->l.profile.next = (s)->profile_head.elem_q; \
+ (s)->profile_head.elem_q = &((s)->l.profile); \
+ } while(0)
+
+void _lock_profile_register_struct(int32_t, struct lock_profile_qhead *, \
+ int32_t, char *);
+void _lock_profile_deregister_struct(int32_t, struct lock_profile_qhead *);
+
+#define lock_profile_register_struct(type, ptr, idx, print) \
+ _lock_profile_register_struct(type, &((ptr)->profile_head), idx, print)
+#define lock_profile_deregister_struct(type, ptr) \
+ _lock_profile_deregister_struct(type, &((ptr)->profile_head))
+
+#else
+
+struct lock_profile { };
+struct lock_profile_qhead { };
+
+#define SPIN_LOCK_UNLOCKED \
+ { _RAW_SPIN_LOCK_UNLOCKED, 0xfffu, 0, _LOCK_DEBUG, { } }
+#define DEFINE_SPINLOCK(l) spinlock_t l = SPIN_LOCK_UNLOCKED
+
+#define spin_lock_init_prof(s, l) spin_lock_init(&((s)->l))
+#define lock_profile_register_struct(type, ptr, idx, print)
+#define lock_profile_deregister_struct(type, ptr)
+
+#endif
+
typedef struct {
raw_spinlock_t raw;
u16 recurse_cpu:12;
u16 recurse_cnt:4;
struct lock_debug debug;
+ struct lock_profile profile;
} spinlock_t;
-#define SPIN_LOCK_UNLOCKED { _RAW_SPIN_LOCK_UNLOCKED, 0xfffu, 0, _LOCK_DEBUG }
-#define DEFINE_SPINLOCK(l) spinlock_t l = SPIN_LOCK_UNLOCKED
#define spin_lock_init(l) (*(l) = (spinlock_t)SPIN_LOCK_UNLOCKED)
typedef struct {