diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2014-11-30 22:26:16 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2014-11-30 22:26:16 +0000 |
commit | ff831e7be50a6e2e6bf9256e12d24eb4b1385a64 (patch) | |
tree | 5e2677d756028a7a986882306319a5e2f1f78c8e /sys/arch | |
parent | 2a01c4afbe3052d3e848806555c6e5a63e4d86a4 (diff) |
SPARC T4 and later have a pause instruction to voluntarily pause a virtual
processor in order to give other strands a chance to run. Use it in
__mp_lock_spin_hook() to avoid wasting CPU cycles if we're waiting for
the kernel or scheduler locks. This is instruction is patched in, just like
we already do for the sleep instruction on SPARC64 VI processors. We look
at the hwcap-list property of the cpu nodes in the machine description to
decide whether the pause instruction is available.
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/sparc64/include/mdesc.h | 6 | ||||
-rw-r--r-- | sys/arch/sparc64/sparc64/autoconf.c | 42 | ||||
-rw-r--r-- | sys/arch/sparc64/sparc64/lock_machdep.c | 15 | ||||
-rw-r--r-- | sys/arch/sparc64/sparc64/locore.s | 12 | ||||
-rw-r--r-- | sys/arch/sparc64/sparc64/mdesc.c | 50 |
5 files changed, 117 insertions, 8 deletions
diff --git a/sys/arch/sparc64/include/mdesc.h b/sys/arch/sparc64/include/mdesc.h index 5e5581bd739..bacc926141a 100644 --- a/sys/arch/sparc64/include/mdesc.h +++ b/sys/arch/sparc64/include/mdesc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: mdesc.h,v 1.2 2009/05/10 12:37:01 kettenis Exp $ */ +/* $OpenBSD: mdesc.h,v 1.3 2014/11/30 22:26:14 kettenis Exp $ */ /* * Copyright (c) 2009 Mark Kettenis * @@ -42,7 +42,9 @@ extern size_t mdesc_len; void mdesc_init(void); uint64_t mdesc_get_prop_val(int, const char *); -const char * mdesc_get_prop_str(int, const char *); +const char *mdesc_get_prop_str(int, const char *); +const char *mdesc_get_prop_data(int, const char *, size_t *); int mdesc_find(const char *, uint64_t); int mdesc_find_child(int, const char *, uint64_t); +int mdesc_find_node(const char *); #endif diff --git a/sys/arch/sparc64/sparc64/autoconf.c b/sys/arch/sparc64/sparc64/autoconf.c index 956262d6389..2a1a7b020e9 100644 --- a/sys/arch/sparc64/sparc64/autoconf.c +++ b/sys/arch/sparc64/sparc64/autoconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: autoconf.c,v 1.122 2014/11/26 20:06:53 stsp Exp $ */ +/* $OpenBSD: autoconf.c,v 1.123 2014/11/30 22:26:14 kettenis Exp $ */ /* $NetBSD: autoconf.c,v 1.51 2001/07/24 19:32:11 eeh Exp $ */ /* @@ -648,8 +648,46 @@ void cpu_configure() { #ifdef SUN4V - if (CPU_ISSUN4V) + int pause = 0; + + if (CPU_ISSUN4V) { + const char *prop; + size_t len; + int idx; + mdesc_init(); + idx = mdesc_find_node("cpu"); + prop = mdesc_get_prop_data(idx, "hwcap-list", &len); + if (prop) { + while (len > 0) { + if (strcmp(prop, "pause") == 0) + pause = 1; + len -= strlen(prop) + 1; + prop += strlen(prop) + 1; + } + } + } + + if (pause) { + struct sun4v_patch { + u_int32_t addr; + u_int32_t insn; + } *p; + paddr_t pa; + + extern struct sun4v_patch sun4v_pause_patch; + extern struct sun4v_patch sun4v_pause_patch_end; + + /* + * Use physical addresses to patch since kernel .text + * is already mapped read-only at this point. + */ + for (p = &sun4v_pause_patch; p < &sun4v_pause_patch_end; p++) { + pmap_extract(pmap_kernel(), (vaddr_t)p->addr, &pa); + stwa(pa, ASI_PHYS_NON_CACHED, p->insn); + flush((void *)(vaddr_t)p->addr); + } + } #endif if (obd.version == BOOTDATA_VERSION && diff --git a/sys/arch/sparc64/sparc64/lock_machdep.c b/sys/arch/sparc64/sparc64/lock_machdep.c index 92d59254703..f361ffb8f7c 100644 --- a/sys/arch/sparc64/sparc64/lock_machdep.c +++ b/sys/arch/sparc64/sparc64/lock_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lock_machdep.c,v 1.7 2014/03/29 18:09:30 guenther Exp $ */ +/* $OpenBSD: lock_machdep.c,v 1.8 2014/11/30 22:26:14 kettenis Exp $ */ /* * Copyright (c) 2007 Artur Grabowski <art@openbsd.org> @@ -53,7 +53,10 @@ extern int __mp_lock_spinout; * effectively nops, executing them on earlier non-CMT processors is * harmless, so we make this the default. * - * On SPARC64 VI and it successors we execute the processor-specific + * On SPARC T4 and later, we can use the processor-specific pause + * instruction. + * + * On SPARC64 VI and its successors we execute the processor-specific * sleep instruction. */ static __inline void @@ -63,6 +66,14 @@ __mp_lock_spin_hook(void) "999: rd %%ccr, %%g0 \n" " rd %%ccr, %%g0 \n" " rd %%ccr, %%g0 \n" + " .section .sun4v_pause_patch, \"ax\" \n" + " .word 999b \n" + " .word 0xb7802080 ! pause 128 \n" + " .word 999b + 4 \n" + " nop \n" + " .word 999b + 8 \n" + " nop \n" + " .previous \n" " .section .sun4u_mtp_patch, \"ax\" \n" " .word 999b \n" " .word 0x81b01060 ! sleep \n" diff --git a/sys/arch/sparc64/sparc64/locore.s b/sys/arch/sparc64/sparc64/locore.s index f0d35a494b2..dfec6907347 100644 --- a/sys/arch/sparc64/sparc64/locore.s +++ b/sys/arch/sparc64/sparc64/locore.s @@ -1,4 +1,4 @@ -/* $OpenBSD: locore.s,v 1.178 2014/11/24 10:55:49 kettenis Exp $ */ +/* $OpenBSD: locore.s,v 1.179 2014/11/30 22:26:14 kettenis Exp $ */ /* $NetBSD: locore.s,v 1.137 2001/08/13 06:10:10 jdolecek Exp $ */ /* @@ -106,6 +106,16 @@ _C_LABEL(sun4v_patch): _C_LABEL(sun4v_patch_end): .previous + .section .sun4v_pause_patch, "ax" + .globl _C_LABEL(sun4v_pause_patch) +_C_LABEL(sun4v_pause_patch): + .previous + + .section .sun4v_pause_patch_end, "ax" + .globl _C_LABEL(sun4v_pause_patch_end) +_C_LABEL(sun4v_pause_patch_end): + .previous + #ifdef MULTIPROCESSOR .section .sun4v_mp_patch, "ax" .globl _C_LABEL(sun4v_mp_patch) diff --git a/sys/arch/sparc64/sparc64/mdesc.c b/sys/arch/sparc64/sparc64/mdesc.c index 2d905f4ef72..73d51353bc2 100644 --- a/sys/arch/sparc64/sparc64/mdesc.c +++ b/sys/arch/sparc64/sparc64/mdesc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mdesc.c,v 1.6 2014/11/16 12:30:59 deraadt Exp $ */ +/* $OpenBSD: mdesc.c,v 1.7 2014/11/30 22:26:15 kettenis Exp $ */ /* * Copyright (c) 2009 Mark Kettenis * @@ -138,6 +138,32 @@ mdesc_get_prop_str(int idx, const char *name) return (NULL); } +const char * +mdesc_get_prop_data(int idx, const char *name, size_t *len) +{ + struct md_header *hdr; + struct md_element *elem; + const char *name_blk; + const char *data_blk; + const char *str; + + hdr = (struct md_header *)mdesc; + elem = (struct md_element *)(mdesc + sizeof(struct md_header)); + name_blk = mdesc + sizeof(struct md_header) + hdr->node_blk_sz; + data_blk = name_blk + hdr->name_blk_sz; + + while (elem[idx].tag != 'E') { + str = name_blk + elem[idx].name_offset; + if (elem[idx].tag == 'd' && strcmp(str, name) == 0) { + *len = elem[idx].d.y.data_len; + return (data_blk + elem[idx].d.y.data_offset); + } + idx++; + } + + return (NULL); +} + int mdesc_find(const char *name, uint64_t cfg_handle) { @@ -188,3 +214,25 @@ mdesc_find_child(int idx, const char *name, uint64_t cfg_handle) return (-1); } + +int +mdesc_find_node(const char *name) +{ + struct md_header *hdr; + struct md_element *elem; + const char *name_blk; + const char *str; + int idx; + + hdr = (struct md_header *)mdesc; + elem = (struct md_element *)(mdesc + sizeof(struct md_header)); + name_blk = mdesc + sizeof(struct md_header) + hdr->node_blk_sz; + + for (idx = 0; elem[idx].tag == 'N'; idx = elem[idx].d.val) { + str = name_blk + elem[idx].name_offset; + if (str && strcmp(str, name) == 0) + return (idx); + } + + return (-1); +} |