Support of lock profiling Signed-off-by: juergen.gross@ts.fujitsu.com diff -r 729567f615c1 tools/libxc/xc_misc.c --- a/tools/libxc/xc_misc.c Wed Oct 07 16:29:03 2009 +0100 +++ b/tools/libxc/xc_misc.c Fri Oct 09 09:35:45 2009 +0200 @@ -121,6 +121,30 @@ 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 -r 729567f615c1 tools/libxc/xenctrl.h --- a/tools/libxc/xenctrl.h Wed Oct 07 16:29:03 2009 +0100 +++ b/tools/libxc/xenctrl.h Fri Oct 09 09:35:45 2009 +0200 @@ -700,6 +700,14 @@ 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 -r 729567f615c1 tools/misc/Makefile --- a/tools/misc/Makefile Wed Oct 07 16:29:03 2009 +0100 +++ b/tools/misc/Makefile Fri Oct 09 09:35:45 2009 +0200 @@ -10,7 +10,7 @@ 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-$(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 @@ %.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 -r 729567f615c1 tools/misc/xenlockprof.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/misc/xenlockprof.c Fri Oct 09 09:35:45 2009 +0200 @@ -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 +#include +#include +#include +#include +#include + +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 -r 729567f615c1 xen/Rules.mk --- a/xen/Rules.mk Wed Oct 07 16:29:03 2009 +0100 +++ b/xen/Rules.mk Fri Oct 09 09:35:45 2009 +0200 @@ -6,6 +6,7 @@ verbose ?= n perfc ?= n perfc_arrays ?= n +lock_profile ?= n crash_debug ?= n frame_pointer ?= n @@ -49,6 +50,7 @@ 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 ifneq ($(max_phys_cpus),) diff -r 729567f615c1 xen/arch/x86/pci.c --- a/xen/arch/x86/pci.c Wed Oct 07 16:29:03 2009 +0100 +++ b/xen/arch/x86/pci.c Fri Oct 09 09:35:45 2009 +0200 @@ -8,7 +8,7 @@ #include #include -static DEFINE_SPINLOCK(pci_config_lock); +DEFINE_SPINLOCK_STATIC_PROF(pci_config_lock); uint32_t pci_conf_read(uint32_t cf8, uint8_t offset, uint8_t bytes) { diff -r 729567f615c1 xen/arch/x86/smp.c --- a/xen/arch/x86/smp.c Wed Oct 07 16:29:03 2009 +0100 +++ b/xen/arch/x86/smp.c Fri Oct 09 09:35:45 2009 +0200 @@ -201,7 +201,7 @@ local_irq_restore(flags); } -static DEFINE_SPINLOCK(flush_lock); +DEFINE_SPINLOCK_STATIC_PROF(flush_lock); static cpumask_t flush_cpumask; static const void *flush_va; static unsigned int flush_flags; diff -r 729567f615c1 xen/arch/x86/xen.lds.S --- a/xen/arch/x86/xen.lds.S Wed Oct 07 16:29:03 2009 +0100 +++ b/xen/arch/x86/xen.lds.S Fri Oct 09 09:35:45 2009 +0200 @@ -62,6 +62,13 @@ *(.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 -r 729567f615c1 xen/common/domain.c --- a/xen/common/domain.c Wed Oct 07 16:29:03 2009 +0100 +++ b/xen/common/domain.c Fri Oct 09 09:35:45 2009 +0200 @@ -226,8 +226,8 @@ 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); @@ -884,6 +884,29 @@ return -ENOSYS; } +#ifdef LOCK_PROFILE +void domain_lock_prof_func(lock_profile_subfunc *sub, void *par) +{ + struct domain *d; + + rcu_read_lock(&domlist_read_lock); + for_each_domain(d) + { + sub((struct lock_profile_data_array *)(&d->lock_profile_array), + LOCKPROF_TYPE_PERDOM, "domain", d->domain_id, par); + } + rcu_read_unlock(&domlist_read_lock); + return; +} + +static int __init domain_lock_prof_init(void) +{ + lock_profile_funcs[LOCKPROF_TYPE_PERDOM] = domain_lock_prof_func; + return 0; +} +__initcall(domain_lock_prof_init); +#endif + /* * Local variables: * mode: C diff -r 729567f615c1 xen/common/keyhandler.c --- a/xen/common/keyhandler.c Wed Oct 07 16:29:03 2009 +0100 +++ b/xen/common/keyhandler.c Fri Oct 09 09:35:45 2009 +0200 @@ -347,6 +347,20 @@ }; #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 @@ 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 -r 729567f615c1 xen/common/spinlock.c --- a/xen/common/spinlock.c Wed Oct 07 16:29:03 2009 +0100 +++ b/xen/common/spinlock.c Fri Oct 09 09:35:45 2009 +0200 @@ -1,7 +1,11 @@ +#include #include #include #include +#include #include +#include +#include #include #ifndef NDEBUG @@ -41,56 +45,104 @@ #endif +#ifdef LOCK_PROFILE + +#define LOCK_PROFILE_REL \ + if (lock->profile.data) \ + { \ + lock->profile.data->time_hold += NOW() - lock->profile.time_locked; \ + lock->profile.data->lock_cnt++; \ + } +#define LOCK_PROFILE_VAR s_time_t block = 0 +#define LOCK_PROFILE_BLOCK if (lock->profile.data) block = block ? : NOW(); +#define LOCK_PROFILE_GOT \ + if (lock->profile.data) \ + { \ + lock->profile.time_locked = NOW(); \ + if (block) \ + { \ + lock->profile.data->time_block += \ + lock->profile.time_locked - block; \ + lock->profile.data->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 +156,35 @@ 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; + if (lock->profile.data) + { + 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.data) + { + lock->profile.data->time_block += NOW() - block; + lock->profile.data->block_cnt++; + } +#else check_lock(&lock->debug); do { mb(); } while ( _raw_spin_is_locked(&lock->raw) ); +#endif mb(); } @@ -248,3 +322,189 @@ check_lock(&lock->debug); return _raw_rw_is_write_locked(&lock->raw); } + +#ifdef LOCK_PROFILE +extern struct lock_profile_data __lock_profile_start; +extern struct lock_profile_data __lock_profile_end; + +static s_time_t lock_profile_start = 0; + +lock_profile_func *lock_profile_funcs[LOCKPROF_TYPE_N]; + +void spinlock_profile_iterate(lock_profile_subfunc *sub, void *par) +{ + int i; + + for (i = 0; i < LOCKPROF_TYPE_N; i++) + { + if (lock_profile_funcs[i]) + lock_profile_funcs[i](sub, par); + } + return; +} + +void spinlock_profile_print_array(struct lock_profile_data_array *array, + uint32_t type, char *txt, uint32_t idx, void *par) +{ + int i; + struct lock_profile_data *q; + + for (i = 0; i < array->cnt; i++) + { + q = array->array + i; + if (q->name == NULL) + continue; + printk("%s %d %s:\n", txt, idx, q->name); + printk(" lock:%12ld(%08X:%08X), block:%12ld(%08X:%08X)\n", + q->lock_cnt, (u32)(q->time_hold >> 32), (u32)q->time_hold, + q->block_cnt, (u32)(q->time_block >> 32), (u32)q->time_block); + } + return; +} + +void spinlock_profile_printall(unsigned char key) +{ + s_time_t now = NOW(); + s_time_t diff; + struct lock_profile_data *q; + + 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); + for (q = &__lock_profile_start; q < &__lock_profile_end; q++) + { + printk("%s:\n", q->name); + printk(" lock:%12ld(%08X:%08X), block:%12ld(%08X:%08X)\n", + q->lock_cnt, (u32)(q->time_hold >> 32), (u32)q->time_hold, + q->block_cnt, (u32)(q->time_block >> 32), (u32)q->time_block); + } + spinlock_profile_iterate(spinlock_profile_print_array, NULL); + return; +} + +void spinlock_profile_reset_array(struct lock_profile_data_array *array, + uint32_t type, char *txt, uint32_t idx, void *par) +{ + int i; + + for (i = 0; i < array->cnt; i++) + { + array->array[i].lock_cnt = 0; + array->array[i].block_cnt = 0; + array->array[i].time_hold = 0; + array->array[i].time_block = 0; + } + return; +} + +void spinlock_profile_reset(unsigned char key) +{ + s_time_t now = NOW(); + struct lock_profile_data *q; + + if ( key != '\0' ) + printk("Xen lock profile info RESET (now = %08X:%08X)\n", + (u32)(now>>32), (u32)now); + lock_profile_start = now; + for (q = &__lock_profile_start; q < &__lock_profile_end; q++) + { + q->lock_cnt = 0; + q->block_cnt = 0; + q->time_hold = 0; + q->time_block = 0; + } + spinlock_profile_iterate(spinlock_profile_reset_array, NULL); + return; +} + +typedef struct { + xen_sysctl_lockprof_op_t *pc; + int rc; +} spinlock_profile_ucopy_t; + +void spinlock_profile_ucopy_array(struct lock_profile_data_array *array, + uint32_t type, char *txt, uint32_t idx, void *par) +{ + int i; + struct lock_profile_data *q; + spinlock_profile_ucopy_t *p; + xen_sysctl_lockprof_data_t elem; + + p = (spinlock_profile_ucopy_t *)par; + if (p->rc) + return; + + for (i = 0; i < array->cnt; i++) + { + q = array->array + i; + if (q->name == NULL) + continue; + if (p->pc->nr_elem < p->pc->max_elem) + { + safe_strcpy(elem.name, q->name); + elem.type = type; + elem.idx = idx; + elem.lock_cnt = q->lock_cnt; + elem.block_cnt = q->block_cnt; + elem.lock_time = q->time_hold; + elem.block_time = q->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; + struct lock_profile_data *q; + xen_sysctl_lockprof_data_t elem; + + rc = 0; + switch (pc->cmd) + { + case XEN_SYSCTL_LOCKPROF_reset: + spinlock_profile_reset('\0'); + break; + case XEN_SYSCTL_LOCKPROF_query: + q = &__lock_profile_start; + pc->nr_elem = 0; + par.rc = 0; + par.pc = pc; + for (q = &__lock_profile_start; q < &__lock_profile_end; q++) + { + if (pc->nr_elem < pc->max_elem) + { + safe_strcpy(elem.name, q->name); + elem.type = LOCKPROF_TYPE_GLOBAL; + elem.idx = 0; + elem.lock_cnt = q->lock_cnt; + elem.block_cnt = q->block_cnt; + elem.lock_time = q->time_hold; + elem.block_time = q->time_block; + if (copy_to_guest_offset(pc->data, pc->nr_elem, &elem, 1)) + { + return -EFAULT; + } + } + pc->nr_elem++; + } + spinlock_profile_iterate(spinlock_profile_ucopy_array, &par); + pc->time = NOW() - lock_profile_start; + rc = par.rc; + break; + default: + rc = -EINVAL; + break; + } + return rc; +} +#endif diff -r 729567f615c1 xen/common/sysctl.c --- a/xen/common/sysctl.c Wed Oct 07 16:29:03 2009 +0100 +++ b/xen/common/sysctl.c Fri Oct 09 09:35:45 2009 +0200 @@ -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 @@ 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 -r 729567f615c1 xen/include/public/sysctl.h --- a/xen/include/public/sysctl.h Wed Oct 07 16:29:03 2009 +0100 +++ b/xen/include/public/sysctl.h Fri Oct 09 09:35:45 2009 +0200 @@ -454,6 +454,38 @@ #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_t lock_cnt; /* # of locking succeeded */ + uint64_t block_cnt; /* # of wait for lock */ + uint64_t lock_time; /* nsecs lock held */ + uint64_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_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_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 -r 729567f615c1 xen/include/xen/sched.h --- a/xen/include/xen/sched.h Wed Oct 07 16:29:03 2009 +0100 +++ b/xen/include/xen/sched.h Fri Oct 09 09:35:45 2009 +0200 @@ -276,6 +276,8 @@ /* transcendent memory, auto-allocated on first tmem op by each domain */ void *tmem; + + SPIN_LOCK_PROF_ARRAY(5); }; struct domain_setup_info diff -r 729567f615c1 xen/include/xen/spinlock.h --- a/xen/include/xen/spinlock.h Wed Oct 07 16:29:03 2009 +0100 +++ b/xen/include/xen/spinlock.h Fri Oct 09 09:35:45 2009 +0200 @@ -19,17 +19,119 @@ #define spin_debug_disable() ((void)0) #endif +#ifdef LOCK_PROFILE +/* + lock profiling on: + + Global locks which sould be subject to profiling must be declared via + DEFINE_SPINLOCK_PROF or DEFINE_SPINLOCK_STATIC_PROF. + + For locks in structures further measures are necessary: + The structure must include a profiling data section declared via + SPIN_LOCK_PROF_ARRAY(n) + with n being the maximum number of profiled locks in the structure. + Each lock in the structure subject to profiling has to be initialized via + spin_lock_init_prof(s, l) + with s being the structure pointer and l the lock field. + To print the profile data an iterator function must be registered (only if + LOCK_PROFILE is defined): + + void func (lock_profile_subfunc *sub, void *par) + { + struct xyz *x; + + for_each_xyz(x) + { + sub((struct lock_profile_data_array *)(&x->lock_profile_array), + LOCKPROF_TYPE_XYZ, "xyz", x->index, par); + } + return; + } + + static int __init xyz_init(void) + { + lock_profile_funcs[LOCKPROF_TYPE_XYZ] = func; + return 0; + } + __initcall(xyz_init); +*/ + +struct lock_profile_data { + char *name; /* lock name, may contain %d for idx */ + 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 */ +}; + +struct lock_profile { + struct lock_profile_data *data; /* profiling data */ + s64 time_locked; /* system time of last locking */ +}; + +struct lock_profile_data_array { + int cnt; /* # of populated entries */ + struct lock_profile_data array[100]; /* profile data */ +}; + +typedef void lock_profile_subfunc(struct lock_profile_data_array *, + uint32_t, char *, uint32_t, void *); +typedef void lock_profile_func(lock_profile_subfunc *, void *); + +extern lock_profile_func *lock_profile_funcs[]; + +#define _LOCK_PROFILE(name) { &name, 0 } +#define _LOCK_NO_PROFILE { NULL, 0 } +#define _LOCK_PROFILE_DATA(name) \ + struct lock_profile_data __lock_profile_##name \ + __attribute__ ((__section__(".lockprofile.data"))) = { #name, 0, 0, 0, 0 }; +#define SPIN_LOCK_PROF_ARRAY(n) \ + struct { \ + int cnt; \ + struct lock_profile_data data[n]; \ + } lock_profile_array +#define _spin_lock_init_prof(s, l) ; \ + BUG_ON((s)->lock_profile_array.cnt >= \ + (sizeof((s)->lock_profile_array.data) / \ + sizeof(struct lock_profile_data))); \ + (s)->l.profile.data = \ + (s)->lock_profile_array.data + (s)->lock_profile_array.cnt; \ + (s)->lock_profile_array.cnt++; \ + (s)->l.profile.data->name = #l + +#else +struct lock_profile { }; +#define _LOCK_PROFILE(name) { } +#define _LOCK_NO_PROFILE { } +#define _LOCK_PROFILE_DATA(name) +#define SPIN_LOCK_PROF_ARRAY(n) struct { } lock_profile_array +#define _spin_lock_init_prof(s, l) +#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 _SPIN_LOCK_UNLOCKED(x) { _RAW_SPIN_LOCK_UNLOCKED, 0xfffu, 0, \ + _LOCK_DEBUG, x } +#define SPIN_LOCK_UNLOCKED _SPIN_LOCK_UNLOCKED(_LOCK_NO_PROFILE) +#define SPIN_LOCK_UNLOCKED_PROF(n) \ + _SPIN_LOCK_UNLOCKED(_LOCK_PROFILE(__lock_profile_##n)) #define DEFINE_SPINLOCK(l) spinlock_t l = SPIN_LOCK_UNLOCKED +#define DEFINE_SPINLOCK_PROF(l) _LOCK_PROFILE_DATA(l) \ + spinlock_t l = SPIN_LOCK_UNLOCKED_PROF(l) +#define DEFINE_SPINLOCK_STATIC(l) static spinlock_t l = SPIN_LOCK_UNLOCKED +#define DEFINE_SPINLOCK_STATIC_PROF(l) _LOCK_PROFILE_DATA(l) \ + static spinlock_t l = SPIN_LOCK_UNLOCKED_PROF(l) #define spin_lock_init(l) (*(l) = (spinlock_t)SPIN_LOCK_UNLOCKED) +#define spin_lock_init_prof(s, l) \ + spin_lock_init(&(s)->l) \ + _spin_lock_init_prof(s, l) typedef struct { raw_rwlock_t raw;