summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2014-11-30 22:26:16 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2014-11-30 22:26:16 +0000
commitff831e7be50a6e2e6bf9256e12d24eb4b1385a64 (patch)
tree5e2677d756028a7a986882306319a5e2f1f78c8e /sys/arch
parent2a01c4afbe3052d3e848806555c6e5a63e4d86a4 (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.h6
-rw-r--r--sys/arch/sparc64/sparc64/autoconf.c42
-rw-r--r--sys/arch/sparc64/sparc64/lock_machdep.c15
-rw-r--r--sys/arch/sparc64/sparc64/locore.s12
-rw-r--r--sys/arch/sparc64/sparc64/mdesc.c50
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);
+}