diff options
author | Jonathan Haslam <Jonathan.Haslam@Sun.COM> | 2009-02-13 07:13:13 +0000 |
---|---|---|
committer | Jonathan Haslam <Jonathan.Haslam@Sun.COM> | 2009-02-13 07:13:13 +0000 |
commit | b9e93c10c0a2a4bb069d38bb311021a9478c4711 (patch) | |
tree | 8a82dbbc533447409d902987f543b2ad72e8e78a /usr/src | |
parent | 2e5e9e19867a0d75685f5beb2fe1b0e31491d49b (diff) | |
download | illumos-joyent-b9e93c10c0a2a4bb069d38bb311021a9478c4711.tar.gz |
PSARC 2008/480 DTrace CPC Provider
6486156 DTrace cpc provider
Diffstat (limited to 'usr/src')
63 files changed, 2503 insertions, 185 deletions
diff --git a/usr/src/cmd/devfsadm/dtrace_link.c b/usr/src/cmd/devfsadm/dtrace_link.c index 5dcc2c8208..72a455c179 100644 --- a/usr/src/cmd/devfsadm/dtrace_link.c +++ b/usr/src/cmd/devfsadm/dtrace_link.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <devfsadm.h> #include <strings.h> #include <stdio.h> @@ -49,6 +46,8 @@ static devfsadm_create_t dtrace_create_cbt[] = { TYPE_EXACT | DRV_EXACT, ILEVEL_0, dtrace_provider }, { "pseudo", "ddi_pseudo", "systrace", TYPE_EXACT | DRV_EXACT, ILEVEL_0, dtrace_provider }, + { "pseudo", "ddi_pseudo", "dcpc", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, dtrace_provider }, }; DEVFSADM_CREATE_INIT_V0(dtrace_create_cbt); diff --git a/usr/src/cmd/dtrace/test/tst/common/cpc/err.D_PDESC_ZERO.lowfrequency.d b/usr/src/cmd/dtrace/test/tst/common/cpc/err.D_PDESC_ZERO.lowfrequency.d new file mode 100644 index 0000000000..1015a251cf --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/cpc/err.D_PDESC_ZERO.lowfrequency.d @@ -0,0 +1,45 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Test to check that attempting to enable a valid event with a frequency + * lower than the default platform limit will fail. + * + * This test will fail if: + * 1) The system under test does not define the 'PAPI_tot_ins' event. + * 2) The 'dcpc-min-overflow' variable in dcpc.conf has been modified. + */ + +#pragma D option quiet + +BEGIN +{ + exit(0); +} + +cpc:::PAPI_tot_ins-all-100 +{ +} diff --git a/usr/src/cmd/dtrace/test/tst/common/cpc/err.D_PDESC_ZERO.malformedoverflow.d b/usr/src/cmd/dtrace/test/tst/common/cpc/err.D_PDESC_ZERO.malformedoverflow.d new file mode 100644 index 0000000000..f9b780942f --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/cpc/err.D_PDESC_ZERO.malformedoverflow.d @@ -0,0 +1,40 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Tests that specifying an overflow value containing extraneous characters + * (only digits are allowed) will fail. + */ + +BEGIN +{ + exit(0); +} + +cpc:::PAPI_tot_ins-all-10000bonehead +{ + @[probename] = count(); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/cpc/err.D_PDESC_ZERO.nonexistentevent.d b/usr/src/cmd/dtrace/test/tst/common/cpc/err.D_PDESC_ZERO.nonexistentevent.d new file mode 100644 index 0000000000..73f9575fbe --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/cpc/err.D_PDESC_ZERO.nonexistentevent.d @@ -0,0 +1,40 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Tests that attempting to enable a probe containing a non existent event + * will fail. + */ + +BEGIN +{ + exit(0); +} + +cpc:::PAPI_cpc_bad-all-10000 +{ + @[probename] = count(); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/cpc/err.cpcvscpustatpart1.ksh b/usr/src/cmd/dtrace/test/tst/common/cpc/err.cpcvscpustatpart1.ksh new file mode 100644 index 0000000000..767d493a76 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/cpc/err.cpcvscpustatpart1.ksh @@ -0,0 +1,78 @@ +#!/bin/ksh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + + +# +# This tests that cpustat(1) should fail to start if the cpc provider +# is already calling the shots. +# +# This script will fail if: +# 1) The system under test does not define the 'PAPI_tot_ins' +# generic event. + +script() +{ + $dtrace -o $dtraceout -s /dev/stdin <<EOF + #pragma D option bufsize=128k + + cpc:::PAPI_tot_ins-all-10000 + { + @[probename] = count(); + } +EOF +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +dtraceout=/tmp/dtrace.out.$$ +script 2>/dev/null & +timeout=15 + +# +# Sleep while the above script fires into life. To guard against dtrace dying +# and us sleeping forever we allow 15 secs for this to happen. This should be +# enough for even the slowest systems. +# +while [ ! -f $dtraceout ]; do + sleep 1 + timeout=$(($timeout-1)) + if [ $timeout -eq 0 ]; then + echo "dtrace failed to start. Exiting." + exit 1 + fi +done + +cpustat -c PAPI_tot_ins 1 5 +status=$? + +rm $dtraceout + +exit $status diff --git a/usr/src/cmd/dtrace/test/tst/common/cpc/err.cpcvscpustatpart2.ksh b/usr/src/cmd/dtrace/test/tst/common/cpc/err.cpcvscpustatpart2.ksh new file mode 100644 index 0000000000..584469c3fe --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/cpc/err.cpcvscpustatpart2.ksh @@ -0,0 +1,70 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + + +# +# This tests that enablings from the cpc provider will fail if cpustat(1) is +# already master of the universe. +# +# This script will fail if: +# 1) The system under test does not define the 'PAPI_tot_ins' +# generic event. + +script() +{ + $dtrace -s /dev/stdin <<EOF + #pragma D option bufsize=128k + + BEGIN + { + exit(0); + } + + cpc:::PAPI_tot_ins-all-10000 + { + @[probename] = count(); + } +EOF +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +dtraceout=/tmp/dtrace.out.$$ + +cpustat -c PAPI_tot_ins 1 20 & +pid=$! +sleep 5 +script 2>/dev/null + +status=$? + +kill $pid +exit $status diff --git a/usr/src/cmd/dtrace/test/tst/common/cpc/err.cputrackfailtostart.ksh b/usr/src/cmd/dtrace/test/tst/common/cpc/err.cputrackfailtostart.ksh new file mode 100644 index 0000000000..f62b83d571 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/cpc/err.cputrackfailtostart.ksh @@ -0,0 +1,77 @@ +#!/bin/ksh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +# +# This script ensures that cputrack(1M) will fail to start when the cpc +# provider has active enablings. +# +# The script will fail if: +# 1) The system under test does not define the 'PAPI_tot_ins' event. +# + +script() +{ + $dtrace -o $dtraceout -s /dev/stdin <<EOF + #pragma D option bufsize=128k + + cpc:::PAPI_tot_ins-all-10000 + { + @[probename] = count(); + } +EOF +} + + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +dtraceout=/tmp/dtrace.out.$$ +script 2>/dev/null & +timeout=15 + +# +# Sleep while the above script fires into life. To guard against dtrace dying +# and us sleeping forever we allow 15 secs for this to happen. This should be +# enough for even the slowest systems. +# +while [ ! -f $dtraceout ]; do + sleep 1 + timeout=$(($timeout-1)) + if [ $timeout -eq 0 ]; then + echo "dtrace failed to start. Exiting." + exit 1 + fi +done + +cputrack -c PAPI_tot_ins sleep 10 +status=$? + +rm $dtraceout + +exit $status diff --git a/usr/src/cmd/dtrace/test/tst/common/cpc/err.cputrackterminates.ksh b/usr/src/cmd/dtrace/test/tst/common/cpc/err.cputrackterminates.ksh new file mode 100644 index 0000000000..58d1e798f1 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/cpc/err.cputrackterminates.ksh @@ -0,0 +1,70 @@ +#!/bin/ksh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. + +# +# This script ensures that cputrack(1) will terminate when the cpc provider +# kicks into life. +# +# The script will fail if: +# 1) The system under test does not define the 'PAPI_tot_ins' event. +# + +script() +{ + $dtrace -s /dev/stdin <<EOF + #pragma D option bufsize=128k + + cpc:::PAPI_tot_ins-all-10000 + { + @[probename] = count(); + } + + tick-1s + /n++ > 10/ + { + exit(0); + } +EOF +} + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 + +cputrack -c PAPI_tot_ins sleep 20 & +cputrack_pid=$! +sleep 5 +script 2>/dev/null & + +wait $cputrack_pid +status=$? + +rm $dtraceout + +exit $status diff --git a/usr/src/cmd/dtrace/test/tst/common/cpc/err.toomanyenablings.d b/usr/src/cmd/dtrace/test/tst/common/cpc/err.toomanyenablings.d new file mode 100644 index 0000000000..a50bd71ae6 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/cpc/err.toomanyenablings.d @@ -0,0 +1,55 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Test to check that attempting to enable too many probes will fail. + * + * This test will fail if: + * 1) We ever execute on a platform which is capable of programming 10 + * 'PAPI_tot_ins' events simultaneously (which no current platforms are + * capable of doing). + * 2) The system under test does not define the 'PAPI_tot_ins' event. + */ + +#pragma D option quiet + +BEGIN +{ + exit(0); +} + +cpc:::PAPI_tot_ins-all-10000, +cpc:::PAPI_tot_ins-all-10001, +cpc:::PAPI_tot_ins-all-10002, +cpc:::PAPI_tot_ins-all-10003, +cpc:::PAPI_tot_ins-all-10004, +cpc:::PAPI_tot_ins-all-10005, +cpc:::PAPI_tot_ins-all-10006, +cpc:::PAPI_tot_ins-all-10007, +cpc:::PAPI_tot_ins-all-10008, +cpc:::PAPI_tot_ins-all-10009 +{ +} diff --git a/usr/src/cmd/dtrace/test/tst/common/cpc/tst.allcpus.ksh b/usr/src/cmd/dtrace/test/tst/common/cpc/tst.allcpus.ksh new file mode 100644 index 0000000000..7f026c9ebc --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/cpc/tst.allcpus.ksh @@ -0,0 +1,107 @@ +#!/bin/ksh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# This script verifies that we can fire a probe on each CPU that is in +# an online state. +# +# The script will fail if: +# 1) The system under test does not define the 'PAPI_tot_ins' event. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +numproc=`psrinfo | tail -1 | cut -f1` +cpu=0 +dtraceout=/var/tmp/dtrace.out.$$ +scriptout=/var/tmp/script.out.$$ + +spin() +{ + while [ 1 ]; do + : + done +} + +script() +{ + $dtrace -o $dtraceout -s /dev/stdin <<EOF + #pragma D option bufsize=128k + #pragma D option quiet + + cpc:::PAPI_tot_ins-user-10000 + /cpus[cpu] != 1/ + { + cpus[cpu] = 1; + @a[cpu] = count(); + } + + tick-1s + /n++ > 10/ + { + printa(@a); + exit(0); + } +EOF +} + +echo "" > $scriptout +while [ $cpu -le $numproc ] +do + if [ "`psrinfo -s $cpu 2> /dev/null`" -eq 1 ]; then + printf "%9d %16d\n" $cpu 1 >> $scriptout + spin & + allpids[$cpu]=$! + pbind -b $cpu $! + fi + cpu=$(($cpu+1)) +done +echo "" >> $scriptout + +script + +diff $dtraceout $scriptout >/dev/null 2>&1 +status=$? + +# kill off the spinner processes +cpu=0 +while [ $cpu -le $numproc ] +do + if [ "`psrinfo -s $cpu 2> /dev/null`" -eq 1 ]; then + kill ${allpids[$cpu]} + fi + cpu=$(($cpu+1)) +done + +rm $dtraceout +rm $scriptout + +exit $status diff --git a/usr/src/cmd/dtrace/test/tst/common/cpc/tst.genericevent.d b/usr/src/cmd/dtrace/test/tst/common/cpc/tst.genericevent.d new file mode 100644 index 0000000000..7ebf844e9c --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/cpc/tst.genericevent.d @@ -0,0 +1,48 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Test that we can successfully enable a probe using a generic event. + * Currently, all platforms implement 'PAPI_tot_ins' so we'll use that. + * Note that this test will fail if the system under test does not + * implement that event. + * + * This test will fail if: + * 1) The system under test does not define the 'PAPI_tot_ins' event. + */ + +#pragma D option quiet +#pragma D option bufsize=128k + +cpc:::PAPI_tot_ins-all-10000 +{ + @[probename] = count(); +} + +tick-1s +/n++ > 10/ +{ + exit(0); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/cpc/tst.platformevent.ksh b/usr/src/cmd/dtrace/test/tst/common/cpc/tst.platformevent.ksh new file mode 100644 index 0000000000..96d1ab8c6a --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/cpc/tst.platformevent.ksh @@ -0,0 +1,81 @@ +#!/bin/ksh +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# This script ensures that we can enable a probe which specifies a platform +# specific event. +# + +if [ $# != 1 ]; then + print -u2 "expected one argument: <dtrace-path>" + exit 2 +fi + +dtrace=$1 + +get_event() +{ + perl /dev/stdin /dev/stdout << EOF + open CPUSTAT, '/usr/sbin/cpustat -h |' + or die "Couldn't run cpustat: \$!\n"; + while (<CPUSTAT>) { + if (/(\s+)event\[*[0-9]-*[0-9]*\]*:/ && !/PAPI/) { + @a = split(/ /, \$_); + \$event = \$a[\$#a-1]; + } + } + + close CPUSTAT; + print "\$event\n"; +EOF +} + +script() +{ + $dtrace -s /dev/stdin << EOD + #pragma D option quiet + #pragma D option bufsize=128k + + cpc:::$1-all-10000 + { + @[probename] = count(); + } + + tick-1s + /n++ > 5/ + { + exit(0); + } +EOD +} + +event=$(get_event) +script $event + +status=$? + +exit $status diff --git a/usr/src/lib/libdtrace/common/dt_error.c b/usr/src/lib/libdtrace/common/dt_error.c index 5005f593a4..0bfabc919c 100644 --- a/usr/src/lib/libdtrace/common/dt_error.c +++ b/usr/src/lib/libdtrace/common/dt_error.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <strings.h> #include <dt_impl.h> @@ -105,7 +103,8 @@ static const struct { { EDT_BADSETOPT, "Invalid setopt() library action" }, { EDT_BADSTACKPC, "Invalid stack program counter size" }, { EDT_BADAGGVAR, "Invalid aggregation variable identifier" }, - { EDT_OVERSION, "Client requested deprecated version of library" } + { EDT_OVERSION, "Client requested deprecated version of library" }, + { EDT_ENABLING_ERR, "Failed to enable probe" } }; static const int _dt_nerr = sizeof (_dt_errlist) / sizeof (_dt_errlist[0]); diff --git a/usr/src/lib/libdtrace/common/dt_impl.h b/usr/src/lib/libdtrace/common/dt_impl.h index 9b22dfbb64..adf0f74094 100644 --- a/usr/src/lib/libdtrace/common/dt_impl.h +++ b/usr/src/lib/libdtrace/common/dt_impl.h @@ -20,15 +20,13 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _DT_IMPL_H #define _DT_IMPL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/param.h> #include <sys/objfs.h> #include <setjmp.h> @@ -498,7 +496,8 @@ enum { EDT_BADSETOPT, /* invalid setopt library action */ EDT_BADSTACKPC, /* invalid stack program counter size */ EDT_BADAGGVAR, /* invalid aggregation variable identifier */ - EDT_OVERSION /* client is requesting deprecated version */ + EDT_OVERSION, /* client is requesting deprecated version */ + EDT_ENABLING_ERR /* failed to enable probe */ }; /* diff --git a/usr/src/lib/libdtrace/common/dt_program.c b/usr/src/lib/libdtrace/common/dt_program.c index 29d883aca4..8105df0737 100644 --- a/usr/src/lib/libdtrace/common/dt_program.c +++ b/usr/src/lib/libdtrace/common/dt_program.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <unistd.h> #include <strings.h> #include <stdlib.h> @@ -173,6 +171,9 @@ dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, case E2BIG: err = EDT_DIFSIZE; break; + case EBUSY: + err = EDT_ENABLING_ERR; + break; default: err = errno; } diff --git a/usr/src/pkgdefs/SUNWdtrp/prototype_com b/usr/src/pkgdefs/SUNWdtrp/prototype_com index 500f2e98e4..3c743a3110 100644 --- a/usr/src/pkgdefs/SUNWdtrp/prototype_com +++ b/usr/src/pkgdefs/SUNWdtrp/prototype_com @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" -# i pkginfo i copyright i depend @@ -40,4 +37,5 @@ f none kernel/drv/lockstat.conf 644 root sys f none kernel/drv/profile.conf 644 root sys f none kernel/drv/sdt.conf 644 root sys f none kernel/drv/systrace.conf 644 root sys +f none kernel/drv/dcpc.conf 644 root sys d none kernel/dtrace 755 root sys diff --git a/usr/src/pkgdefs/SUNWdtrp/prototype_i386 b/usr/src/pkgdefs/SUNWdtrp/prototype_i386 index e1734cf717..61adf49851 100644 --- a/usr/src/pkgdefs/SUNWdtrp/prototype_i386 +++ b/usr/src/pkgdefs/SUNWdtrp/prototype_i386 @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,10 +19,9 @@ # CDDL HEADER END # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" !include prototype_com @@ -34,12 +32,14 @@ f none kernel/drv/lockstat 755 root sys f none kernel/drv/profile 755 root sys f none kernel/drv/sdt 755 root sys f none kernel/drv/systrace 755 root sys +f none kernel/drv/dcpc 755 root sys l none kernel/dtrace/fasttrap=../../kernel/drv/fasttrap l none kernel/dtrace/fbt=../../kernel/drv/fbt l none kernel/dtrace/lockstat=../../kernel/drv/lockstat l none kernel/dtrace/profile=../../kernel/drv/profile l none kernel/dtrace/sdt=../../kernel/drv/sdt l none kernel/dtrace/systrace=../../kernel/drv/systrace +l none kernel/dtrace/dcpc=../../kernel/drv/dcpc d none kernel/drv/amd64 755 root sys f none kernel/drv/amd64/dtrace 755 root sys f none kernel/drv/amd64/fasttrap 755 root sys @@ -48,6 +48,7 @@ f none kernel/drv/amd64/lockstat 755 root sys f none kernel/drv/amd64/profile 755 root sys f none kernel/drv/amd64/sdt 755 root sys f none kernel/drv/amd64/systrace 755 root sys +f none kernel/drv/amd64/dcpc 755 root sys d none kernel/dtrace/amd64 755 root sys l none kernel/dtrace/amd64/fasttrap=../../../kernel/drv/amd64/fasttrap l none kernel/dtrace/amd64/fbt=../../../kernel/drv/amd64/fbt @@ -55,3 +56,4 @@ l none kernel/dtrace/amd64/lockstat=../../../kernel/drv/amd64/lockstat l none kernel/dtrace/amd64/profile=../../../kernel/drv/amd64/profile l none kernel/dtrace/amd64/sdt=../../../kernel/drv/amd64/sdt l none kernel/dtrace/amd64/systrace=../../../kernel/drv/amd64/systrace +l none kernel/dtrace/amd64/dcpc=../../../kernel/drv/amd64/dcpc diff --git a/usr/src/pkgdefs/SUNWdtrp/prototype_sparc b/usr/src/pkgdefs/SUNWdtrp/prototype_sparc index 735e74de8a..fb1026469e 100644 --- a/usr/src/pkgdefs/SUNWdtrp/prototype_sparc +++ b/usr/src/pkgdefs/SUNWdtrp/prototype_sparc @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,10 +19,9 @@ # CDDL HEADER END # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" !include prototype_com @@ -35,6 +33,7 @@ f none kernel/drv/sparcv9/lockstat 755 root sys f none kernel/drv/sparcv9/profile 755 root sys f none kernel/drv/sparcv9/sdt 755 root sys f none kernel/drv/sparcv9/systrace 755 root sys +f none kernel/drv/sparcv9/dcpc 755 root sys d none kernel/dtrace/sparcv9 755 root sys l none kernel/dtrace/sparcv9/fasttrap=../../../kernel/drv/sparcv9/fasttrap l none kernel/dtrace/sparcv9/fbt=../../../kernel/drv/sparcv9/fbt @@ -42,3 +41,4 @@ l none kernel/dtrace/sparcv9/lockstat=../../../kernel/drv/sparcv9/lockstat l none kernel/dtrace/sparcv9/profile=../../../kernel/drv/sparcv9/profile l none kernel/dtrace/sparcv9/sdt=../../../kernel/drv/sparcv9/sdt l none kernel/dtrace/sparcv9/systrace=../../../kernel/drv/sparcv9/systrace +l none kernel/dtrace/sparcv9/dcpc=../../../kernel/drv/sparcv9/dcpc diff --git a/usr/src/pkgdefs/SUNWdtrt/prototype_com b/usr/src/pkgdefs/SUNWdtrt/prototype_com index 30aaa3f86e..4c51d03349 100644 --- a/usr/src/pkgdefs/SUNWdtrt/prototype_com +++ b/usr/src/pkgdefs/SUNWdtrt/prototype_com @@ -20,7 +20,7 @@ # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # i pkginfo @@ -320,6 +320,18 @@ f none SUNWdtrt/tst/common/clauses/tst.nopred.d 0444 root bin f none SUNWdtrt/tst/common/clauses/tst.pred.d 0444 root bin f none SUNWdtrt/tst/common/clauses/tst.predfirst.d 0444 root bin f none SUNWdtrt/tst/common/clauses/tst.predlast.d 0444 root bin +d none SUNWdtrt/tst/common/cpc 0755 root bin +f none SUNWdtrt/tst/common/cpc/tst.platformevent.ksh 0444 root bin +f none SUNWdtrt/tst/common/cpc/tst.genericevent.d 0444 root bin +f none SUNWdtrt/tst/common/cpc/tst.allcpus.ksh 0444 root bin +f none SUNWdtrt/tst/common/cpc/err.toomanyenablings.d 0444 root bin +f none SUNWdtrt/tst/common/cpc/err.D_PDESC_ZERO.nonexistentevent.d 0444 root bin +f none SUNWdtrt/tst/common/cpc/err.D_PDESC_ZERO.lowfrequency.d 0444 root bin +f none SUNWdtrt/tst/common/cpc/err.D_PDESC_ZERO.malformedoverflow.d 0444 root bin +f none SUNWdtrt/tst/common/cpc/err.cputrackfailtostart.ksh 0444 root bin +f none SUNWdtrt/tst/common/cpc/err.cputrackterminates.ksh 0444 root bin +f none SUNWdtrt/tst/common/cpc/err.cpcvscpustatpart1.ksh 0444 root bin +f none SUNWdtrt/tst/common/cpc/err.cpcvscpustatpart2.ksh 0444 root bin d none SUNWdtrt/tst/common/decls 0755 root bin f none SUNWdtrt/tst/common/decls/err.D_DECL_PROTO_NAME.VoidName.d 0444 root bin f none SUNWdtrt/tst/common/decls/err.D_DECL_PROTO_TYPE.Dyn.d 0444 root bin diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index ecefe16b9f..9da74d6444 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -406,6 +406,8 @@ LOCKSTAT_OBJS += lockstat.o FASTTRAP_OBJS += fasttrap.o fasttrap_isa.o +DCPC_OBJS += dcpc.o + # # Driver (pseudo-driver) Modules # diff --git a/usr/src/uts/common/brand/lx/dtrace/lx_systrace.c b/usr/src/uts/common/brand/lx/dtrace/lx_systrace.c index ab9828a589..bfeb78330a 100644 --- a/usr/src/uts/common/brand/lx/dtrace/lx_systrace.c +++ b/usr/src/uts/common/brand/lx/dtrace/lx_systrace.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -121,7 +121,7 @@ lx_systrace_provide(void *arg, const dtrace_probedesc_t *desc) } /*ARGSUSED*/ -static void +static int lx_systrace_enable(void *arg, dtrace_id_t id, void *parg) { int sysnum = LX_SYSTRACE_SYSNUM((uintptr_t)parg); @@ -138,6 +138,7 @@ lx_systrace_enable(void *arg, dtrace_id_t id, void *parg) } else { lx_systrace_sysent[sysnum].lss_return = id; } + return (0); } /*ARGSUSED*/ diff --git a/usr/src/uts/common/dtrace/dcpc.c b/usr/src/uts/common/dtrace/dcpc.c new file mode 100644 index 0000000000..05f304fdd7 --- /dev/null +++ b/usr/src/uts/common/dtrace/dcpc.c @@ -0,0 +1,1096 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/errno.h> +#include <sys/cpuvar.h> +#include <sys/stat.h> +#include <sys/modctl.h> +#include <sys/cmn_err.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/ksynch.h> +#include <sys/conf.h> +#include <sys/kmem.h> +#include <sys/kcpc.h> +#include <sys/cpc_pcbe.h> +#include <sys/cpc_impl.h> +#include <sys/dtrace_impl.h> + +/* + * DTrace CPU Performance Counter Provider + * --------------------------------------- + * + * The DTrace cpc provider allows DTrace consumers to access the CPU + * performance counter overflow mechanism of a CPU. The configuration + * presented in a probe specification is programmed into the performance + * counter hardware of all available CPUs on a system. Programming the + * hardware causes a counter on each CPU to begin counting events of the + * given type. When the specified number of events have occurred, an overflow + * interrupt will be generated and the probe is fired. + * + * The required configuration for the performance counter is encoded into + * the probe specification and this includes the performance counter event + * name, processor mode, overflow rate and an optional unit mask. + * + * Most processors provide several counters (PICs) which can count all or a + * subset of the events available for a given CPU. However, when overflow + * profiling is being used, not all CPUs can detect which counter generated the + * overflow interrupt. In this case we cannot reliably determine which counter + * overflowed and we therefore only allow such CPUs to configure one event at + * a time. Processors that can determine the counter which overflowed are + * allowed to program as many events at one time as possible (in theory up to + * the number of instrumentation counters supported by that platform). + * Therefore, multiple consumers can enable multiple probes at the same time + * on such platforms. Platforms which cannot determine the source of an + * overflow interrupt are only allowed to program a single event at one time. + * + * The performance counter hardware is made available to consumers on a + * first-come, first-served basis. Only a finite amount of hardware resource + * is available and, while we make every attempt to accomodate requests from + * consumers, we must deny requests when hardware resources have been exhausted. + * A consumer will fail to enable probes when resources are currently in use. + * + * The cpc provider contends for shared hardware resources along with other + * consumers of the kernel CPU performance counter subsystem (e.g. cpustat(1M)). + * Only one such consumer can use the performance counters at any one time and + * counters are made available on a first-come, first-served basis. As with + * cpustat, the cpc provider has priority over per-LWP libcpc usage (e.g. + * cputrack(1)). Invoking the cpc provider will cause all existing per-LWP + * counter contexts to be invalidated. + */ + +typedef struct dcpc_probe { + char dcpc_event_name[CPC_MAX_EVENT_LEN]; + int dcpc_flag; /* flags (USER/SYS) */ + uint32_t dcpc_ovfval; /* overflow value */ + int64_t dcpc_umask; /* umask/emask for this event */ + int dcpc_picno; /* pic this event is programmed in */ + int dcpc_enabled; /* probe is actually enabled? */ + int dcpc_disabling; /* probe is currently being disabled */ + dtrace_id_t dcpc_id; /* probeid this request is enabling */ + int dcpc_actv_req_idx; /* idx into dcpc_actv_reqs[] */ +} dcpc_probe_t; + +static dev_info_t *dcpc_devi; +static dtrace_provider_id_t dcpc_pid; +static dcpc_probe_t **dcpc_actv_reqs; +static uint32_t dcpc_enablings = 0; +static int dcpc_ovf_mask = 0; +static int dcpc_mult_ovf_cap = 0; +static int dcpc_mask_type = 0; + +/* + * When the dcpc provider is loaded, dcpc_min_overflow is set to either + * DCPC_MIN_OVF_DEFAULT or the value that dcpc-min-overflow is set to in + * the dcpc.conf file. Decrease this value to set probes with smaller + * overflow values. Remember that very small values could render a system + * unusable with frequently occurring events. + */ +#define DCPC_MIN_OVF_DEFAULT 5000 +static uint32_t dcpc_min_overflow; + +static int dcpc_aframes = 0; /* override for artificial frame setting */ +#if defined(__x86) +#define DCPC_ARTIFICIAL_FRAMES 8 +#elif defined(__sparc) +#define DCPC_ARTIFICIAL_FRAMES 2 +#endif + +/* + * Called from the platform overflow interrupt handler. 'bitmap' is a mask + * which contains the pic(s) that have overflowed. + */ +static void +dcpc_fire(uint64_t bitmap) +{ + int i; + + /* + * No counter was marked as overflowing. Shout about it and get out. + */ + if ((bitmap & dcpc_ovf_mask) == 0) { + cmn_err(CE_NOTE, "dcpc_fire: no counter overflow found\n"); + return; + } + + /* + * This is the common case of a processor that doesn't support + * multiple overflow events. Such systems are only allowed a single + * enabling and therefore we just look for the first entry in + * the active request array. + */ + if (!dcpc_mult_ovf_cap) { + for (i = 0; i < cpc_ncounters; i++) { + if (dcpc_actv_reqs[i] != NULL) { + dtrace_probe(dcpc_actv_reqs[i]->dcpc_id, + CPU->cpu_cpcprofile_pc, + CPU->cpu_cpcprofile_upc, 0, 0, 0); + return; + } + } + return; + } + + /* + * This is a processor capable of handling multiple overflow events. + * Iterate over the array of active requests and locate the counters + * that overflowed (note: it is possible for more than one counter to + * have overflowed at the same time). + */ + for (i = 0; i < cpc_ncounters; i++) { + if (dcpc_actv_reqs[i] != NULL && + (bitmap & (1ULL << dcpc_actv_reqs[i]->dcpc_picno))) { + dtrace_probe(dcpc_actv_reqs[i]->dcpc_id, + CPU->cpu_cpcprofile_pc, + CPU->cpu_cpcprofile_upc, 0, 0, 0); + } + } +} + +static void +dcpc_create_probe(dtrace_provider_id_t id, const char *probename, + char *eventname, int64_t umask, uint32_t ovfval, char flag) +{ + dcpc_probe_t *pp; + int nr_frames = DCPC_ARTIFICIAL_FRAMES + dtrace_mach_aframes(); + + if (dcpc_aframes) + nr_frames = dcpc_aframes; + + if (dtrace_probe_lookup(id, NULL, NULL, probename) != 0) + return; + + pp = kmem_zalloc(sizeof (dcpc_probe_t), KM_SLEEP); + (void) strncpy(pp->dcpc_event_name, eventname, + sizeof (pp->dcpc_event_name) - 1); + pp->dcpc_event_name[sizeof (pp->dcpc_event_name) - 1] = '\0'; + pp->dcpc_flag = flag | CPC_OVF_NOTIFY_EMT; + pp->dcpc_ovfval = ovfval; + pp->dcpc_umask = umask; + pp->dcpc_actv_req_idx = pp->dcpc_picno = pp->dcpc_disabling = -1; + + pp->dcpc_id = dtrace_probe_create(id, NULL, NULL, probename, + nr_frames, pp); +} + +/*ARGSUSED*/ +static void +dcpc_provide(void *arg, const dtrace_probedesc_t *desc) +{ + /* + * The format of a probe is: + * + * event_name-mode-{optional_umask}-overflow_rate + * e.g. + * DC_refill_from_system-user-0x1e-50000, or, + * DC_refill_from_system-all-10000 + * + */ + char *str, *end, *p; + int i, flag = 0; + char event[CPC_MAX_EVENT_LEN]; + long umask = -1, val = 0; + size_t evlen, len; + + /* + * The 'cpc' provider offers no probes by default. + */ + if (desc == NULL) + return; + + len = strlen(desc->dtpd_name); + p = str = kmem_alloc(len + 1, KM_SLEEP); + (void) strcpy(str, desc->dtpd_name); + + /* + * We have a poor man's strtok() going on here. Replace any hyphens + * in the the probe name with NULL characters in order to make it + * easy to parse the string with regular string functions. + */ + for (i = 0; i < len; i++) { + if (str[i] == '-') + str[i] = '\0'; + } + + /* + * The first part of the string must be either a platform event + * name or a generic event name. + */ + evlen = strlen(p); + (void) strncpy(event, p, CPC_MAX_EVENT_LEN - 1); + event[CPC_MAX_EVENT_LEN - 1] = '\0'; + + /* + * The next part of the name is the mode specification. Valid + * settings are "user", "kernel" or "all". + */ + p += evlen + 1; + + if (strcmp(p, "user") == 0) + flag |= CPC_COUNT_USER; + else if (strcmp(p, "kernel") == 0) + flag |= CPC_COUNT_SYSTEM; + else if (strcmp(p, "all") == 0) + flag |= CPC_COUNT_USER | CPC_COUNT_SYSTEM; + else + goto err; + + /* + * Next we either have a mask specification followed by an overflow + * rate or just an overflow rate on its own. + */ + p += strlen(p) + 1; + if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { + /* + * A unit mask can only be specified if: + * 1) this performance counter back end supports masks. + * 2) the specified event is platform specific. + * 3) a valid hex number is converted. + * 4) no extraneous characters follow the mask specification. + */ + if (dcpc_mask_type != 0 && strncmp(event, "PAPI", 4) != 0 && + ddi_strtol(p, &end, 16, &umask) == 0 && + end == p + strlen(p)) { + p += strlen(p) + 1; + } else { + goto err; + } + } + + /* + * This final part must be an overflow value which has to be greater + * than the minimum permissible overflow rate. + */ + if ((ddi_strtol(p, &end, 10, &val) != 0) || end != p + strlen(p) || + val < dcpc_min_overflow) + goto err; + + /* + * Validate the event and create the probe. + */ + for (i = 0; i < cpc_ncounters; i++) { + if (strstr(kcpc_list_events(i), event) != NULL) + dcpc_create_probe(dcpc_pid, desc->dtpd_name, event, + umask, (uint32_t)val, flag); + } + +err: + kmem_free(str, len + 1); +} + +/*ARGSUSED*/ +static void +dcpc_destroy(void *arg, dtrace_id_t id, void *parg) +{ + dcpc_probe_t *pp = parg; + + ASSERT(pp->dcpc_enabled == 0); + kmem_free(pp, sizeof (dcpc_probe_t)); +} + +/*ARGSUSED*/ +static int +dcpc_usermode(void *arg, dtrace_id_t id, void *parg) +{ + return (CPU->cpu_cpcprofile_pc == 0); +} + +static void +dcpc_populate_set(cpu_t *c, dcpc_probe_t *pp, kcpc_set_t *set, int reqno) +{ + kcpc_set_t *oset; + int i; + + (void) strncpy(set->ks_req[reqno].kr_event, pp->dcpc_event_name, + CPC_MAX_EVENT_LEN); + set->ks_req[reqno].kr_config = NULL; + set->ks_req[reqno].kr_index = reqno; + set->ks_req[reqno].kr_picnum = -1; + set->ks_req[reqno].kr_flags = pp->dcpc_flag; + + /* + * If a unit mask has been specified then detect which attribute + * the platform needs. For now, it's either "umask" or "emask". + */ + if (pp->dcpc_umask >= 0) { + set->ks_req[reqno].kr_attr = + kmem_zalloc(sizeof (kcpc_attr_t), KM_SLEEP); + set->ks_req[reqno].kr_nattrs = 1; + if (dcpc_mask_type & DCPC_UMASK) + (void) strncpy(set->ks_req[reqno].kr_attr->ka_name, + "umask", 5); + else + (void) strncpy(set->ks_req[reqno].kr_attr->ka_name, + "emask", 5); + set->ks_req[reqno].kr_attr->ka_val = pp->dcpc_umask; + } else { + set->ks_req[reqno].kr_attr = NULL; + set->ks_req[reqno].kr_nattrs = 0; + } + + /* + * If this probe is enabled, obtain its current countdown value + * and use that. The CPUs cpc context might not exist yet if we + * are dealing with a CPU that is just coming online. + */ + if (pp->dcpc_enabled && (c->cpu_cpc_ctx != NULL)) { + oset = c->cpu_cpc_ctx->kc_set; + + for (i = 0; i < oset->ks_nreqs; i++) { + if (strcmp(oset->ks_req[i].kr_event, + set->ks_req[reqno].kr_event) == 0) { + set->ks_req[reqno].kr_preset = + *(oset->ks_req[i].kr_data); + } + } + } else { + set->ks_req[reqno].kr_preset = UINT64_MAX - pp->dcpc_ovfval; + } + + set->ks_nreqs++; +} + + +/* + * Create a fresh request set for the enablings represented in the + * 'dcpc_actv_reqs' array which contains the probes we want to be + * in the set. This can be called for several reasons: + * + * 1) We are on a single or multi overflow platform and we have no + * current events so we can just create the set and initialize it. + * 2) We are on a multi-overflow platform and we already have one or + * more existing events and we are adding a new enabling. Create a + * new set and copy old requests in and then add the new request. + * 3) We are on a multi-overflow platform and we have just removed an + * enabling but we still have enablings whch are valid. Create a new + * set and copy in still valid requests. + */ +static kcpc_set_t * +dcpc_create_set(cpu_t *c) +{ + int i, reqno = 0; + int active_requests = 0; + kcpc_set_t *set; + + /* + * First get a count of the number of currently active requests. + * Note that dcpc_actv_reqs[] should always reflect which requests + * we want to be in the set that is to be created. It is the + * responsibility of the caller of dcpc_create_set() to adjust that + * array accordingly beforehand. + */ + for (i = 0; i < cpc_ncounters; i++) { + if (dcpc_actv_reqs[i] != NULL) + active_requests++; + } + + set = kmem_zalloc(sizeof (kcpc_set_t), KM_SLEEP); + + set->ks_req = + kmem_zalloc(sizeof (kcpc_request_t) * active_requests, KM_SLEEP); + + set->ks_data = + kmem_zalloc(active_requests * sizeof (uint64_t), KM_SLEEP); + + /* + * Look for valid entries in the active requests array and populate + * the request set for any entries found. + */ + for (i = 0; i < cpc_ncounters; i++) { + if (dcpc_actv_reqs[i] != NULL) { + dcpc_populate_set(c, dcpc_actv_reqs[i], set, reqno); + reqno++; + } + } + + return (set); +} + +static int +dcpc_program_cpu_event(cpu_t *c) +{ + int i, j, subcode; + kcpc_ctx_t *ctx, *octx; + kcpc_set_t *set; + + set = dcpc_create_set(c); + + octx = NULL; + set->ks_ctx = ctx = kcpc_ctx_alloc(); + ctx->kc_set = set; + ctx->kc_cpuid = c->cpu_id; + + if (kcpc_assign_reqs(set, ctx) != 0) + goto err; + + if (kcpc_configure_reqs(ctx, set, &subcode) != 0) + goto err; + + for (i = 0; i < set->ks_nreqs; i++) { + for (j = 0; j < cpc_ncounters; j++) { + if (dcpc_actv_reqs[j] != NULL && + strcmp(set->ks_req[i].kr_event, + dcpc_actv_reqs[j]->dcpc_event_name) == 0) { + dcpc_actv_reqs[j]->dcpc_picno = + set->ks_req[i].kr_picnum; + } + } + } + + /* + * If we already have an active enabling then save the current cpc + * context away. + */ + if (c->cpu_cpc_ctx != NULL) + octx = c->cpu_cpc_ctx; + + c->cpu_cpc_ctx = ctx; + kcpc_remote_program(c); + + if (octx != NULL) { + kcpc_set_t *oset = octx->kc_set; + kmem_free(oset->ks_data, oset->ks_nreqs * sizeof (uint64_t)); + kcpc_free_set(oset); + kcpc_ctx_free(octx); + } + + return (0); + +err: + /* + * We failed to configure this request up so free things up and + * get out. + */ + kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t)); + kcpc_free_set(set); + kcpc_ctx_free(ctx); + + return (-1); +} + +static void +dcpc_disable_cpu(cpu_t *c) +{ + kcpc_ctx_t *ctx; + kcpc_set_t *set; + + /* + * Leave this CPU alone if it's already offline. + */ + if (c->cpu_flags & CPU_OFFLINE) + return; + + kcpc_remote_stop(c); + + ctx = c->cpu_cpc_ctx; + set = ctx->kc_set; + + kcpc_free_configs(set); + + kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t)); + kcpc_free_set(set); + kcpc_ctx_free(ctx); + c->cpu_cpc_ctx = NULL; +} + +/* + * Stop overflow interrupts being actively processed so that per-CPU + * configuration state can be changed safely and correctly. Each CPU has a + * dcpc interrupt state byte which is transitioned from DCPC_INTR_FREE (the + * "free" state) to DCPC_INTR_CONFIG (the "configuration in process" state) + * before any configuration state is changed on any CPUs. The hardware overflow + * handler, kcpc_hw_overflow_intr(), will only process an interrupt when a + * configuration is not in process (i.e. the state is marked as free). During + * interrupt processing the state is set to DCPC_INTR_PROCESSING by the + * overflow handler. + */ +static void +dcpc_block_interrupts(void) +{ + cpu_t *c; + uint8_t *state; + + c = cpu_list; + + do { + state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state; + + while (atomic_cas_8(state, DCPC_INTR_FREE, + DCPC_INTR_CONFIG) != DCPC_INTR_FREE) + continue; + + } while ((c = c->cpu_next) != cpu_list); +} + +/* + * Set all CPUs dcpc interrupt state to DCPC_INTR_FREE to indicate that + * overflow interrupts can be processed safely. + */ +static void +dcpc_release_interrupts(void) +{ + cpu_t *c = cpu_list; + + do { + cpu_core[c->cpu_id].cpuc_dcpc_intr_state = DCPC_INTR_FREE; + membar_producer(); + } while ((c = c->cpu_next) != cpu_list); +} + +/* + * dcpc_program_event() can be called owing to a new enabling or if a multi + * overflow platform has disabled a request but needs to program the requests + * that are still valid. + * + * Every invocation of dcpc_program_event() will create a new kcpc_ctx_t + * and a new request set which contains the new enabling and any old enablings + * which are still valid (possible with multi-overflow platforms). + */ +static int +dcpc_program_event(dcpc_probe_t *pp) +{ + cpu_t *c; + int ret = 0; + + ASSERT(MUTEX_HELD(&cpu_lock)); + + kpreempt_disable(); + + dcpc_block_interrupts(); + + c = cpu_list; + + do { + /* + * Skip CPUs that are currently offline. + */ + if (c->cpu_flags & CPU_OFFLINE) + continue; + + if (c->cpu_cpc_ctx != NULL) + kcpc_remote_stop(c); + } while ((c = c->cpu_next) != cpu_list); + + dcpc_release_interrupts(); + + /* + * If this enabling is being removed (in the case of a multi event + * capable system with more than one active enabling), we can now + * update the active request array to reflect the enablings that need + * to be reprogrammed. + */ + if (pp->dcpc_disabling == 1) + dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; + + do { + /* + * Skip CPUs that are currently offline. + */ + if (c->cpu_flags & CPU_OFFLINE) + continue; + + ret = dcpc_program_cpu_event(c); + } while ((c = c->cpu_next) != cpu_list && ret == 0); + + /* + * If dcpc_program_cpu_event() fails then it is because we couldn't + * configure the requests in the set for the CPU and not because of + * an error programming the hardware. If we have a failure here then + * we assume no CPUs have been programmed in the above step as they + * are all configured identically. + */ + if (ret != 0) { + pp->dcpc_enabled = 0; + kpreempt_enable(); + return (-1); + } + + if (pp->dcpc_disabling != 1) + pp->dcpc_enabled = 1; + + kpreempt_enable(); + + return (0); +} + +/*ARGSUSED*/ +static int +dcpc_enable(void *arg, dtrace_id_t id, void *parg) +{ + dcpc_probe_t *pp = parg; + int i, found = 0; + cpu_t *c; + + ASSERT(MUTEX_HELD(&cpu_lock)); + + /* + * Bail out if the counters are being used by a libcpc consumer. + */ + rw_enter(&kcpc_cpuctx_lock, RW_READER); + if (kcpc_cpuctx > 0) { + rw_exit(&kcpc_cpuctx_lock); + return (-1); + } + + dtrace_cpc_in_use++; + rw_exit(&kcpc_cpuctx_lock); + + /* + * Locate this enabling in the first free entry of the active + * request array. + */ + for (i = 0; i < cpc_ncounters; i++) { + if (dcpc_actv_reqs[i] == NULL) { + dcpc_actv_reqs[i] = pp; + pp->dcpc_actv_req_idx = i; + found = 1; + break; + } + } + + /* + * If we couldn't find a slot for this probe then there is no + * room at the inn. + */ + if (!found) { + dtrace_cpc_in_use--; + return (-1); + } + + ASSERT(pp->dcpc_actv_req_idx >= 0); + + /* + * The following must hold true if we are to (attempt to) enable + * this request: + * + * 1) No enablings currently exist. We allow all platforms to + * proceed if this is true. + * + * OR + * + * 2) If the platform is multi overflow capable and there are + * less valid enablings than there are counters. There is no + * guarantee that a platform can accommodate as many events as + * it has counters for but we will at least try to program + * up to that many requests. + * + * The 'dcpc_enablings' variable is implictly protected by locking + * provided by the DTrace framework and the cpu management framework. + */ + if (dcpc_enablings == 0 || (dcpc_mult_ovf_cap && + dcpc_enablings < cpc_ncounters)) { + /* + * Before attempting to program the first enabling we need to + * invalidate any lwp-based contexts. + */ + if (dcpc_enablings == 0) + kcpc_invalidate_all(); + + if (dcpc_program_event(pp) == 0) { + dcpc_enablings++; + return (0); + } + } + + /* + * If active enablings existed before we failed to enable this probe + * on a multi event capable platform then we need to restart counters + * as they will have been stopped in the attempted configuration. The + * context should now just contain the request prior to this failed + * enabling. + */ + if (dcpc_enablings > 0 && dcpc_mult_ovf_cap) { + c = cpu_list; + + ASSERT(dcpc_mult_ovf_cap == 1); + do { + /* + * Skip CPUs that are currently offline. + */ + if (c->cpu_flags & CPU_OFFLINE) + continue; + + kcpc_remote_program(c); + } while ((c = c->cpu_next) != cpu_list); + } + + dtrace_cpc_in_use--; + dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; + pp->dcpc_actv_req_idx = pp->dcpc_picno = -1; + + return (-1); +} + +/* + * If only one enabling is active then remove the context and free + * everything up. If there are multiple enablings active then remove this + * one, its associated meta-data and re-program the hardware. + */ +/*ARGSUSED*/ +static void +dcpc_disable(void *arg, dtrace_id_t id, void *parg) +{ + cpu_t *c; + dcpc_probe_t *pp = parg; + + ASSERT(MUTEX_HELD(&cpu_lock)); + + kpreempt_disable(); + + /* + * This probe didn't actually make it as far as being fully enabled + * so we needn't do anything with it. + */ + if (pp->dcpc_enabled == 0) { + /* + * If we actually allocated this request a slot in the + * request array but failed to enabled it then remove the + * entry in the array. + */ + if (pp->dcpc_actv_req_idx >= 0) { + dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; + pp->dcpc_actv_req_idx = pp->dcpc_picno = + pp->dcpc_disabling = -1; + } + + kpreempt_enable(); + return; + } + + /* + * If this is the only enabling then stop all the counters and + * free up the meta-data. + */ + if (dcpc_enablings == 1) { + ASSERT(dtrace_cpc_in_use == 1); + + dcpc_block_interrupts(); + + c = cpu_list; + + do { + dcpc_disable_cpu(c); + } while ((c = c->cpu_next) != cpu_list); + + dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; + dcpc_release_interrupts(); + } else { + /* + * This platform can support multiple overflow events and + * the enabling being disabled is not the last one. Remove this + * enabling and re-program the hardware with the new config. + */ + ASSERT(dcpc_mult_ovf_cap); + ASSERT(dcpc_enablings > 1); + + pp->dcpc_disabling = 1; + (void) dcpc_program_event(pp); + } + + kpreempt_enable(); + + dcpc_enablings--; + dtrace_cpc_in_use--; + pp->dcpc_enabled = 0; + pp->dcpc_actv_req_idx = pp->dcpc_picno = pp->dcpc_disabling = -1; +} + +/*ARGSUSED*/ +static int +dcpc_cpu_setup(cpu_setup_t what, processorid_t cpu, void *arg) +{ + cpu_t *c; + uint8_t *state; + + ASSERT(MUTEX_HELD(&cpu_lock)); + + switch (what) { + case CPU_OFF: + /* + * Offline CPUs are not allowed to take part so remove this + * CPU if we are actively tracing. + */ + if (dtrace_cpc_in_use) { + c = cpu_get(cpu); + state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state; + + /* + * Indicate that a configuration is in process in + * order to stop overflow interrupts being processed + * on this CPU while we disable it. + */ + while (atomic_cas_8(state, DCPC_INTR_FREE, + DCPC_INTR_CONFIG) != DCPC_INTR_FREE) + continue; + + dcpc_disable_cpu(c); + + /* + * Reset this CPUs interrupt state as the configuration + * has ended. + */ + cpu_core[c->cpu_id].cpuc_dcpc_intr_state = + DCPC_INTR_FREE; + membar_producer(); + } + break; + + case CPU_ON: + case CPU_SETUP: + /* + * This CPU is being initialized or brought online so program + * it with the current request set if we are actively tracing. + */ + if (dtrace_cpc_in_use) { + c = cpu_get(cpu); + + (void) dcpc_program_cpu_event(c); + } + break; + + default: + break; + } + + return (0); +} + +static dtrace_pattr_t dcpc_attr = { +{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, +{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, +{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, +{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_CPU }, +{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, +}; + +static dtrace_pops_t dcpc_pops = { + dcpc_provide, + NULL, + dcpc_enable, + dcpc_disable, + NULL, + NULL, + NULL, + NULL, + dcpc_usermode, + dcpc_destroy +}; + +/*ARGSUSED*/ +static int +dcpc_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) +{ + return (0); +} + +/*ARGSUSED*/ +static int +dcpc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) +{ + int error; + + switch (infocmd) { + case DDI_INFO_DEVT2DEVINFO: + *result = (void *)dcpc_devi; + error = DDI_SUCCESS; + break; + case DDI_INFO_DEVT2INSTANCE: + *result = (void *)0; + error = DDI_SUCCESS; + break; + default: + error = DDI_FAILURE; + } + return (error); +} + +static int +dcpc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) +{ + switch (cmd) { + case DDI_DETACH: + break; + case DDI_SUSPEND: + return (DDI_SUCCESS); + default: + return (DDI_FAILURE); + } + + if (dtrace_unregister(dcpc_pid) != 0) + return (DDI_FAILURE); + + ddi_remove_minor_node(devi, NULL); + + mutex_enter(&cpu_lock); + unregister_cpu_setup_func(dcpc_cpu_setup, NULL); + mutex_exit(&cpu_lock); + + kmem_free(dcpc_actv_reqs, cpc_ncounters * sizeof (dcpc_probe_t *)); + + kcpc_unregister_dcpc(); + + return (DDI_SUCCESS); +} + +static int +dcpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) +{ + uint_t caps; + char *attrs; + + switch (cmd) { + case DDI_ATTACH: + break; + case DDI_RESUME: + return (DDI_SUCCESS); + default: + return (DDI_FAILURE); + } + + if (kcpc_pcbe_loaded() == -1) + return (DDI_FAILURE); + + caps = kcpc_pcbe_capabilities(); + + if (!(caps & CPC_CAP_OVERFLOW_INTERRUPT)) { + cmn_err(CE_WARN, "dcpc: Counter Overflow not supported"\ + " on this processor\n"); + return (DDI_FAILURE); + } + + if (ddi_create_minor_node(devi, "dcpc", S_IFCHR, 0, + DDI_PSEUDO, NULL) == DDI_FAILURE || + dtrace_register("cpc", &dcpc_attr, DTRACE_PRIV_KERNEL, + NULL, &dcpc_pops, NULL, &dcpc_pid) != 0) { + ddi_remove_minor_node(devi, NULL); + return (DDI_FAILURE); + } + + mutex_enter(&cpu_lock); + register_cpu_setup_func(dcpc_cpu_setup, NULL); + mutex_exit(&cpu_lock); + + dcpc_ovf_mask = (1 << cpc_ncounters) - 1; + ASSERT(dcpc_ovf_mask != 0); + + if (caps & CPC_CAP_OVERFLOW_PRECISE) + dcpc_mult_ovf_cap = 1; + + /* + * Determine which, if any, mask attribute the back-end can use. + */ + attrs = kcpc_list_attrs(); + if (strstr(attrs, "umask") != NULL) + dcpc_mask_type |= DCPC_UMASK; + else if (strstr(attrs, "emask") != NULL) + dcpc_mask_type |= DCPC_EMASK; + + /* + * The dcpc_actv_reqs array is used to store the requests that + * we currently have programmed. The order of requests in this + * array is not necessarily the order that the event appears in + * the kcpc_request_t array. Once entered into a slot in the array + * the entry is not moved until it's removed. + */ + dcpc_actv_reqs = + kmem_zalloc(cpc_ncounters * sizeof (dcpc_probe_t *), KM_SLEEP); + + dcpc_min_overflow = ddi_prop_get_int(DDI_DEV_T_ANY, devi, + DDI_PROP_DONTPASS, "dcpc-min-overflow", DCPC_MIN_OVF_DEFAULT); + + kcpc_register_dcpc(dcpc_fire); + + ddi_report_dev(devi); + dcpc_devi = devi; + + return (DDI_SUCCESS); +} + +static struct cb_ops dcpc_cb_ops = { + dcpc_open, /* open */ + nodev, /* close */ + nulldev, /* strategy */ + nulldev, /* print */ + nodev, /* dump */ + nodev, /* read */ + nodev, /* write */ + nodev, /* ioctl */ + nodev, /* devmap */ + nodev, /* mmap */ + nodev, /* segmap */ + nochpoll, /* poll */ + ddi_prop_op, /* cb_prop_op */ + 0, /* streamtab */ + D_NEW | D_MP /* Driver compatibility flag */ +}; + +static struct dev_ops dcpc_ops = { + DEVO_REV, /* devo_rev, */ + 0, /* refcnt */ + dcpc_info, /* get_dev_info */ + nulldev, /* identify */ + nulldev, /* probe */ + dcpc_attach, /* attach */ + dcpc_detach, /* detach */ + nodev, /* reset */ + &dcpc_cb_ops, /* driver operations */ + NULL, /* bus operations */ + nodev, /* dev power */ + ddi_quiesce_not_needed /* quiesce */ +}; + +/* + * Module linkage information for the kernel. + */ +static struct modldrv modldrv = { + &mod_driverops, /* module type */ + "DTrace CPC Module", /* name of module */ + &dcpc_ops, /* driver ops */ +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&modldrv, + NULL +}; + +int +_init(void) +{ + return (mod_install(&modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +int +_fini(void) +{ + return (mod_remove(&modlinkage)); +} diff --git a/usr/src/uts/common/dtrace/dcpc.conf b/usr/src/uts/common/dtrace/dcpc.conf new file mode 100644 index 0000000000..3f18694062 --- /dev/null +++ b/usr/src/uts/common/dtrace/dcpc.conf @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +name="dcpc" parent="pseudo" instance=0; + +# +# dcpc-min-overflow is the lower limit on the rate of overflow that can be +# set on a probe. The default value is 5000. Decrease this value to create +# probes with lower overflow values. Setting this value too low on a rapidly +# increasing event could render a system unusable. +# +#dcpc-min-overflow=5000; diff --git a/usr/src/uts/common/dtrace/dtrace.c b/usr/src/uts/common/dtrace/dtrace.c index 91e3230737..00e71888cd 100644 --- a/usr/src/uts/common/dtrace/dtrace.c +++ b/usr/src/uts/common/dtrace/dtrace.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -239,10 +239,16 @@ static void dtrace_nullop(void) {} +static int +dtrace_enable_nullop(void) +{ + return (0); +} + static dtrace_pops_t dtrace_provider_ops = { (void (*)(void *, const dtrace_probedesc_t *))dtrace_nullop, (void (*)(void *, struct modctl *))dtrace_nullop, - (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, + (int (*)(void *, dtrace_id_t, void *))dtrace_enable_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, (void (*)(void *, dtrace_id_t, void *))dtrace_nullop, @@ -426,6 +432,7 @@ dtrace_load##bits(uintptr_t addr) \ #define DTRACE_DYNHASH_SINK 1 #define DTRACE_DYNHASH_VALID 2 +#define DTRACE_MATCH_FAIL -1 #define DTRACE_MATCH_NEXT 0 #define DTRACE_MATCH_DONE 1 #define DTRACE_ANCHORED(probe) ((probe)->dtpr_func[0] != '\0') @@ -6654,7 +6661,7 @@ dtrace_match(const dtrace_probekey_t *pkp, uint32_t priv, uid_t uid, { dtrace_probe_t template, *probe; dtrace_hash_t *hash = NULL; - int len, best = INT_MAX, nmatched = 0; + int len, rc, best = INT_MAX, nmatched = 0; dtrace_id_t i; ASSERT(MUTEX_HELD(&dtrace_lock)); @@ -6666,7 +6673,8 @@ dtrace_match(const dtrace_probekey_t *pkp, uint32_t priv, uid_t uid, if (pkp->dtpk_id != DTRACE_IDNONE) { if ((probe = dtrace_probe_lookup_id(pkp->dtpk_id)) != NULL && dtrace_match_probe(probe, pkp, priv, uid, zoneid) > 0) { - (void) (*matched)(probe, arg); + if ((*matched)(probe, arg) == DTRACE_MATCH_FAIL) + return (DTRACE_MATCH_FAIL); nmatched++; } return (nmatched); @@ -6713,8 +6721,12 @@ dtrace_match(const dtrace_probekey_t *pkp, uint32_t priv, uid_t uid, nmatched++; - if ((*matched)(probe, arg) != DTRACE_MATCH_NEXT) + if ((rc = (*matched)(probe, arg)) != + DTRACE_MATCH_NEXT) { + if (rc == DTRACE_MATCH_FAIL) + return (DTRACE_MATCH_FAIL); break; + } } return (nmatched); @@ -6733,8 +6745,11 @@ dtrace_match(const dtrace_probekey_t *pkp, uint32_t priv, uid_t uid, nmatched++; - if ((*matched)(probe, arg) != DTRACE_MATCH_NEXT) + if ((rc = (*matched)(probe, arg)) != DTRACE_MATCH_NEXT) { + if (rc == DTRACE_MATCH_FAIL) + return (DTRACE_MATCH_FAIL); break; + } } return (nmatched); @@ -6954,7 +6969,7 @@ dtrace_unregister(dtrace_provider_id_t id) dtrace_probe_t *probe, *first = NULL; if (old->dtpv_pops.dtps_enable == - (void (*)(void *, dtrace_id_t, void *))dtrace_nullop) { + (int (*)(void *, dtrace_id_t, void *))dtrace_enable_nullop) { /* * If DTrace itself is the provider, we're called with locks * already held. @@ -7100,7 +7115,7 @@ dtrace_invalidate(dtrace_provider_id_t id) dtrace_provider_t *pvp = (dtrace_provider_t *)id; ASSERT(pvp->dtpv_pops.dtps_enable != - (void (*)(void *, dtrace_id_t, void *))dtrace_nullop); + (int (*)(void *, dtrace_id_t, void *))dtrace_enable_nullop); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); @@ -7141,7 +7156,7 @@ dtrace_condense(dtrace_provider_id_t id) * Make sure this isn't the dtrace provider itself. */ ASSERT(prov->dtpv_pops.dtps_enable != - (void (*)(void *, dtrace_id_t, void *))dtrace_nullop); + (int (*)(void *, dtrace_id_t, void *))dtrace_enable_nullop); mutex_enter(&dtrace_provider_lock); mutex_enter(&dtrace_lock); @@ -9095,7 +9110,7 @@ dtrace_ecb_add(dtrace_state_t *state, dtrace_probe_t *probe) return (ecb); } -static void +static int dtrace_ecb_enable(dtrace_ecb_t *ecb) { dtrace_probe_t *probe = ecb->dte_probe; @@ -9108,7 +9123,7 @@ dtrace_ecb_enable(dtrace_ecb_t *ecb) /* * This is the NULL probe -- there's nothing to do. */ - return; + return (0); } if (probe->dtpr_ecb == NULL) { @@ -9122,8 +9137,8 @@ dtrace_ecb_enable(dtrace_ecb_t *ecb) if (ecb->dte_predicate != NULL) probe->dtpr_predcache = ecb->dte_predicate->dtp_cacheid; - prov->dtpv_pops.dtps_enable(prov->dtpv_arg, - probe->dtpr_id, probe->dtpr_arg); + return (prov->dtpv_pops.dtps_enable(prov->dtpv_arg, + probe->dtpr_id, probe->dtpr_arg)); } else { /* * This probe is already active. Swing the last pointer to @@ -9136,6 +9151,7 @@ dtrace_ecb_enable(dtrace_ecb_t *ecb) probe->dtpr_predcache = 0; dtrace_sync(); + return (0); } } @@ -9919,7 +9935,9 @@ dtrace_ecb_create_enable(dtrace_probe_t *probe, void *arg) if ((ecb = dtrace_ecb_create(state, probe, enab)) == NULL) return (DTRACE_MATCH_DONE); - dtrace_ecb_enable(ecb); + if (dtrace_ecb_enable(ecb) < 0) + return (DTRACE_MATCH_FAIL); + return (DTRACE_MATCH_NEXT); } @@ -10714,7 +10732,7 @@ static int dtrace_enabling_match(dtrace_enabling_t *enab, int *nmatched) { int i = 0; - int matched = 0; + int total_matched = 0, matched = 0; ASSERT(MUTEX_HELD(&cpu_lock)); ASSERT(MUTEX_HELD(&dtrace_lock)); @@ -10725,7 +10743,14 @@ dtrace_enabling_match(dtrace_enabling_t *enab, int *nmatched) enab->dten_current = ep; enab->dten_error = 0; - matched += dtrace_probe_enable(&ep->dted_probe, enab); + /* + * If a provider failed to enable a probe then get out and + * let the consumer know we failed. + */ + if ((matched = dtrace_probe_enable(&ep->dted_probe, enab)) < 0) + return (EBUSY); + + total_matched += matched; if (enab->dten_error != 0) { /* @@ -10753,7 +10778,7 @@ dtrace_enabling_match(dtrace_enabling_t *enab, int *nmatched) enab->dten_probegen = dtrace_probegen; if (nmatched != NULL) - *nmatched = matched; + *nmatched = total_matched; return (0); } diff --git a/usr/src/uts/common/dtrace/fasttrap.c b/usr/src/uts/common/dtrace/fasttrap.c index fee6d60a57..d25e85e6b9 100644 --- a/usr/src/uts/common/dtrace/fasttrap.c +++ b/usr/src/uts/common/dtrace/fasttrap.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -875,7 +875,7 @@ fasttrap_disable_callbacks(void) } /*ARGSUSED*/ -static void +static int fasttrap_pid_enable(void *arg, dtrace_id_t id, void *parg) { fasttrap_probe_t *probe = parg; @@ -903,7 +903,7 @@ fasttrap_pid_enable(void *arg, dtrace_id_t id, void *parg) * provider can't go away while we're in this code path. */ if (probe->ftp_prov->ftp_retired) - return; + return (0); /* * If we can't find the process, it may be that we're in the context of @@ -912,7 +912,7 @@ fasttrap_pid_enable(void *arg, dtrace_id_t id, void *parg) */ if ((p = sprlock(probe->ftp_pid)) == NULL) { if ((curproc->p_flag & SFORKING) == 0) - return; + return (0); mutex_enter(&pidlock); p = prfind(probe->ftp_pid); @@ -974,7 +974,7 @@ fasttrap_pid_enable(void *arg, dtrace_id_t id, void *parg) * drop our reference on the trap table entry. */ fasttrap_disable_callbacks(); - return; + return (0); } } @@ -982,6 +982,7 @@ fasttrap_pid_enable(void *arg, dtrace_id_t id, void *parg) sprunlock(p); probe->ftp_enabled = 1; + return (0); } /*ARGSUSED*/ diff --git a/usr/src/uts/common/dtrace/lockstat.c b/usr/src/uts/common/dtrace/lockstat.c index 55b3fcf8ff..69c8b72544 100644 --- a/usr/src/uts/common/dtrace/lockstat.c +++ b/usr/src/uts/common/dtrace/lockstat.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -83,7 +83,7 @@ static kmutex_t lockstat_test; /* for testing purposes only */ static dtrace_provider_id_t lockstat_id; /*ARGSUSED*/ -static void +static int lockstat_enable(void *arg, dtrace_id_t id, void *parg) { lockstat_probe_t *probe = parg; @@ -102,6 +102,7 @@ lockstat_enable(void *arg, dtrace_id_t id, void *parg) */ mutex_enter(&lockstat_test); mutex_exit(&lockstat_test); + return (0); } /*ARGSUSED*/ diff --git a/usr/src/uts/common/dtrace/profile.c b/usr/src/uts/common/dtrace/profile.c index da8f58a378..c1a2d1f1c1 100644 --- a/usr/src/uts/common/dtrace/profile.c +++ b/usr/src/uts/common/dtrace/profile.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -360,7 +360,7 @@ profile_offline(void *arg, cpu_t *cpu, void *oarg) } /*ARGSUSED*/ -static void +static int profile_enable(void *arg, dtrace_id_t id, void *parg) { profile_probe_t *prof = parg; @@ -390,6 +390,7 @@ profile_enable(void *arg, dtrace_id_t id, void *parg) } else { prof->prof_cyclic = cyclic_add_omni(&omni); } + return (0); } /*ARGSUSED*/ diff --git a/usr/src/uts/common/dtrace/systrace.c b/usr/src/uts/common/dtrace/systrace.c index fe7bee1ac8..b864041c45 100644 --- a/usr/src/uts/common/dtrace/systrace.c +++ b/usr/src/uts/common/dtrace/systrace.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -140,7 +140,7 @@ systrace_destroy(void *arg, dtrace_id_t id, void *parg) } /*ARGSUSED*/ -static void +static int systrace_enable(void *arg, dtrace_id_t id, void *parg) { int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg); @@ -161,7 +161,7 @@ systrace_enable(void *arg, dtrace_id_t id, void *parg) if (enabled) { ASSERT(sysent[sysnum].sy_callc == dtrace_systrace_syscall); - return; + return (0); } (void) casptr(&sysent[sysnum].sy_callc, @@ -172,6 +172,7 @@ systrace_enable(void *arg, dtrace_id_t id, void *parg) (void *)systrace_sysent32[sysnum].stsy_underlying, (void *)dtrace_systrace_syscall32); #endif + return (0); } /*ARGSUSED*/ diff --git a/usr/src/uts/common/io/cpc.c b/usr/src/uts/common/io/cpc.c index fbc0b77f92..6881380251 100644 --- a/usr/src/uts/common/io/cpc.c +++ b/usr/src/uts/common/io/cpc.c @@ -19,11 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - /* * CPU Performance Counter system calls and device driver. * @@ -126,7 +125,7 @@ cpc(int cmd, id_t lwpid, void *udata1, void *udata2, void *udata3) */ rw_enter(&kcpc_cpuctx_lock, RW_READER); - if (kcpc_cpuctx) { + if (kcpc_cpuctx || dtrace_cpc_in_use) { rw_exit(&kcpc_cpuctx_lock); return (set_errno(EAGAIN)); } @@ -486,6 +485,15 @@ kcpc_open(dev_t *dev, int flags, int otyp, cred_t *cr) rw_enter(&kcpc_cpuctx_lock, RW_WRITER); if (++kcpc_cpuctx == 1) { ASSERT(kcpc_cpumap == NULL); + + /* + * Bail out if DTrace is already using the counters. + */ + if (dtrace_cpc_in_use) { + kcpc_cpuctx--; + rw_exit(&kcpc_cpuctx_lock); + return (EAGAIN); + } kcpc_cpumap = kmem_zalloc(BT_SIZEOFMAP(max_cpuid + 1), KM_SLEEP); /* diff --git a/usr/src/uts/common/os/dtrace_subr.c b/usr/src/uts/common/os/dtrace_subr.c index 6c0b7d3369..f2a9ac1b7d 100644 --- a/usr/src/uts/common/os/dtrace_subr.c +++ b/usr/src/uts/common/os/dtrace_subr.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/dtrace.h> #include <sys/cmn_err.h> #include <sys/tnf.h> @@ -45,6 +43,7 @@ void (*dtrace_helpers_cleanup)(void); void (*dtrace_helpers_fork)(proc_t *, proc_t *); void (*dtrace_cpustart_init)(void); void (*dtrace_cpustart_fini)(void); +void (*dtrace_cpc_fire)(uint64_t); void (*dtrace_debugger_init)(void); void (*dtrace_debugger_fini)(void); @@ -52,6 +51,20 @@ void (*dtrace_debugger_fini)(void); dtrace_vtime_state_t dtrace_vtime_active = 0; dtrace_cacheid_t dtrace_predcache_id = DTRACE_CACHEIDNONE + 1; +/* + * dtrace_cpc_in_use usage statement: this global variable is used by the cpc + * hardware overflow interrupt handler and the kernel cpc framework to check + * whether or not the DTrace cpc provider is currently in use. The variable is + * set before counters are enabled with the first enabling and cleared when + * the last enabling is disabled. Its value at any given time indicates the + * number of active dcpc based enablings. The global 'kcpc_cpuctx_lock' rwlock + * is held during initial setting to protect races between kcpc_open() and the + * first enabling. The locking provided by the DTrace subsystem, the kernel + * cpc framework and the cpu management framework protect consumers from race + * conditions on enabling and disabling probes. + */ +uint32_t dtrace_cpc_in_use = 0; + typedef struct dtrace_hrestime { lock_t dthr_lock; /* lock for this element */ timestruc_t dthr_hrestime; /* hrestime value */ diff --git a/usr/src/uts/common/os/kcpc.c b/usr/src/uts/common/os/kcpc.c index 8c9a135243..e5cab151b8 100644 --- a/usr/src/uts/common/os/kcpc.c +++ b/usr/src/uts/common/os/kcpc.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -65,24 +65,16 @@ static uint32_t kcpc_intrctx_count; /* # overflows in an interrupt handler */ static uint32_t kcpc_nullctx_count; /* # overflows in a thread with no ctx */ /* - * Is misbehaviour (overflow in a thread with no context) fatal? + * By setting 'kcpc_nullctx_panic' to 1, any overflow interrupts in a thread + * with no valid context will result in a panic. */ -#ifdef DEBUG -static int kcpc_nullctx_panic = 1; -#else static int kcpc_nullctx_panic = 0; -#endif static void kcpc_lwp_create(kthread_t *t, kthread_t *ct); static void kcpc_restore(kcpc_ctx_t *ctx); static void kcpc_save(kcpc_ctx_t *ctx); static void kcpc_free(kcpc_ctx_t *ctx, int isexec); -static int kcpc_configure_reqs(kcpc_ctx_t *ctx, kcpc_set_t *set, int *subcode); -static void kcpc_free_configs(kcpc_set_t *set); -static kcpc_ctx_t *kcpc_ctx_alloc(void); static void kcpc_ctx_clone(kcpc_ctx_t *ctx, kcpc_ctx_t *cctx); -static void kcpc_ctx_free(kcpc_ctx_t *ctx); -static int kcpc_assign_reqs(kcpc_set_t *set, kcpc_ctx_t *ctx); static int kcpc_tryassign(kcpc_set_t *set, int starting_req, int *scratch); static kcpc_set_t *kcpc_dup_set(kcpc_set_t *set); @@ -93,6 +85,18 @@ kcpc_register_pcbe(pcbe_ops_t *ops) cpc_ncounters = pcbe_ops->pcbe_ncounters(); } +void +kcpc_register_dcpc(void (*func)(uint64_t)) +{ + dtrace_cpc_fire = func; +} + +void +kcpc_unregister_dcpc(void) +{ + dtrace_cpc_fire = NULL; +} + int kcpc_bind_cpu(kcpc_set_t *set, processorid_t cpuid, int *subcode) { @@ -272,7 +276,7 @@ kcpc_bind_thread(kcpc_set_t *set, kthread_t *t, int *subcode) * Walk through each request in the set and ask the PCBE to configure a * corresponding counter. */ -static int +int kcpc_configure_reqs(kcpc_ctx_t *ctx, kcpc_set_t *set, int *subcode) { int i; @@ -327,7 +331,7 @@ kcpc_configure_reqs(kcpc_ctx_t *ctx, kcpc_set_t *set, int *subcode) return (0); } -static void +void kcpc_free_configs(kcpc_set_t *set) { int i; @@ -710,7 +714,7 @@ kcpc_next_config(void *token, void *current, uint64_t **data) } -static kcpc_ctx_t * +kcpc_ctx_t * kcpc_ctx_alloc(void) { kcpc_ctx_t *ctx; @@ -790,7 +794,7 @@ kcpc_ctx_clone(kcpc_ctx_t *ctx, kcpc_ctx_t *cctx) } -static void +void kcpc_ctx_free(kcpc_ctx_t *ctx) { kcpc_ctx_t **loc; @@ -881,9 +885,33 @@ kcpc_overflow_intr(caddr_t arg, uint64_t bitmap) * or something went terribly wrong i.e. we ended up * running a passivated interrupt thread, a kernel * thread or we interrupted idle, all of which are Very Bad. + * + * We also could end up here owing to an incredibly unlikely + * race condition that exists on x86 based architectures when + * the cpc provider is in use; overflow interrupts are directed + * to the cpc provider if the 'dtrace_cpc_in_use' variable is + * set when we enter the handler. This variable is unset after + * overflow interrupts have been disabled on all CPUs and all + * contexts have been torn down. To stop interrupts, the cpc + * provider issues a xcall to the remote CPU before it tears + * down that CPUs context. As high priority xcalls, on an x86 + * architecture, execute at a higher PIL than this handler, it + * is possible (though extremely unlikely) that the xcall could + * interrupt the overflow handler before the handler has + * checked the 'dtrace_cpc_in_use' variable, stop the counters, + * return to the cpc provider which could then rip down + * contexts and unset 'dtrace_cpc_in_use' *before* the CPUs + * overflow handler has had a chance to check the variable. In + * that case, the handler would direct the overflow into this + * code and no valid context will be found. The default behavior + * when no valid context is found is now to shout a warning to + * the console and bump the 'kcpc_nullctx_count' variable. */ if (kcpc_nullctx_panic) panic("null cpc context, thread %p", (void *)t); + + cmn_err(CE_WARN, + "null cpc context found in overflow handler!\n"); atomic_add_32(&kcpc_nullctx_count, 1); } else if ((ctx->kc_flags & KCPC_CTX_INVALID) == 0) { /* @@ -925,8 +953,9 @@ kcpc_overflow_intr(caddr_t arg, uint64_t bitmap) uint_t kcpc_hw_overflow_intr(caddr_t arg1, caddr_t arg2) { - kcpc_ctx_t *ctx; - uint64_t bitmap; + kcpc_ctx_t *ctx; + uint64_t bitmap; + uint8_t *state; if (pcbe_ops == NULL || (bitmap = pcbe_ops->pcbe_overflow_bitmap()) == 0) @@ -937,8 +966,53 @@ kcpc_hw_overflow_intr(caddr_t arg1, caddr_t arg2) */ pcbe_ops->pcbe_allstop(); + if (dtrace_cpc_in_use) { + state = &cpu_core[CPU->cpu_id].cpuc_dcpc_intr_state; + + /* + * Set the per-CPU state bit to indicate that we are currently + * processing an interrupt if it is currently free. Drop the + * interrupt if the state isn't free (i.e. a configuration + * event is taking place). + */ + if (atomic_cas_8(state, DCPC_INTR_FREE, + DCPC_INTR_PROCESSING) == DCPC_INTR_FREE) { + int i; + kcpc_request_t req; + + ASSERT(dtrace_cpc_fire != NULL); + + (*dtrace_cpc_fire)(bitmap); + + ctx = curthread->t_cpu->cpu_cpc_ctx; + + /* Reset any counters that have overflowed */ + for (i = 0; i < ctx->kc_set->ks_nreqs; i++) { + req = ctx->kc_set->ks_req[i]; + + if (bitmap & (1 << req.kr_picnum)) { + pcbe_ops->pcbe_configure(req.kr_picnum, + req.kr_event, req.kr_preset, + req.kr_flags, req.kr_nattrs, + req.kr_attr, &(req.kr_config), + (void *)ctx); + } + } + pcbe_ops->pcbe_program(ctx); + + /* + * We've finished processing the interrupt so set + * the state back to free. + */ + cpu_core[CPU->cpu_id].cpuc_dcpc_intr_state = + DCPC_INTR_FREE; + membar_producer(); + } + return (DDI_INTR_CLAIMED); + } + /* - * Invoke the "generic" handler. + * DTrace isn't involved so pass on accordingly. * * If the interrupt has occurred in the context of an lwp owning * the counters, then the handler posts an AST to the lwp to @@ -1441,7 +1515,7 @@ kcpc_passivate(void) * Returns 0 if successful, -1 on failure. */ /*ARGSUSED*/ -static int +int kcpc_assign_reqs(kcpc_set_t *set, kcpc_ctx_t *ctx) { int i; @@ -1616,3 +1690,33 @@ kcpc_pcbe_tryload(const char *prefix, uint_t first, uint_t second, uint_t third) return (modload_qualified("pcbe", "pcbe", prefix, ".", s, 3, NULL) < 0 ? -1 : 0); } + +char * +kcpc_list_attrs(void) +{ + ASSERT(pcbe_ops != NULL); + + return (pcbe_ops->pcbe_list_attrs()); +} + +char * +kcpc_list_events(uint_t pic) +{ + ASSERT(pcbe_ops != NULL); + + return (pcbe_ops->pcbe_list_events(pic)); +} + +uint_t +kcpc_pcbe_capabilities(void) +{ + ASSERT(pcbe_ops != NULL); + + return (pcbe_ops->pcbe_caps); +} + +int +kcpc_pcbe_loaded(void) +{ + return (pcbe_ops == NULL ? -1 : 0); +} diff --git a/usr/src/uts/common/sys/cpc_impl.h b/usr/src/uts/common/sys/cpc_impl.h index bb9dc5e6e3..f9f6b4fdfc 100644 --- a/usr/src/uts/common/sys/cpc_impl.h +++ b/usr/src/uts/common/sys/cpc_impl.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_CPC_IMPL_H #define _SYS_CPC_IMPL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/time.h> #include <sys/ksynch.h> @@ -181,6 +179,27 @@ typedef struct __cpc_args32 { */ #define KCPC_PIC_OVERFLOWED 0x1 /* pic overflowed & requested notify */ +/* + * The following flags are used by the DTrace CPU performance counter provider + * and the overflow handler. The 'DCPC_INTR_*' flags are used to synchronize + * performance counter configuration events performed by the cpc provider and + * interrupt processing carried out by the overflow handler. The 'DCPC_?MASK' + * flags are used by the dcpc provider to indicate which type of mask attribute + * a platform supports. + */ + +/* No configuration events or overflow interrupts are currently in process. */ +#define DCPC_INTR_FREE 0 + +/* An overflow interrupt is currently being processed. */ +#define DCPC_INTR_PROCESSING 1 + +/* The cpc subsystem is currently being configured by the dcpc provider. */ +#define DCPC_INTR_CONFIG 2 + +#define DCPC_UMASK 1 /* The platform supports a "umask" attribute. */ +#define DCPC_EMASK 2 /* The platform supports an "emask" attribute. */ + #ifdef __sparc extern uint64_t ultra_gettick(void); #define KCPC_GET_TICK ultra_gettick @@ -204,6 +223,14 @@ extern void kcpc_invalidate_all(void); extern void kcpc_passivate(void); extern void kcpc_remote_stop(struct cpu *cp); extern int kcpc_pcbe_tryload(const char *, uint_t, uint_t, uint_t); +extern void kcpc_remote_program(struct cpu *cp); +extern void kcpc_register_dcpc(void (*func)(uint64_t)); +extern void kcpc_unregister_dcpc(void); +extern kcpc_ctx_t *kcpc_ctx_alloc(void); +extern int kcpc_assign_reqs(struct _kcpc_set *, kcpc_ctx_t *); +extern void kcpc_ctx_free(kcpc_ctx_t *); +extern int kcpc_configure_reqs(kcpc_ctx_t *, struct _kcpc_set *, int *); +extern void kcpc_free_configs(struct _kcpc_set *); #endif /* _KERNEL */ diff --git a/usr/src/uts/common/sys/cpuvar.h b/usr/src/uts/common/sys/cpuvar.h index 69e324ffa6..2d056fa6ab 100644 --- a/usr/src/uts/common/sys/cpuvar.h +++ b/usr/src/uts/common/sys/cpuvar.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -211,6 +211,9 @@ typedef struct cpu { uint64_t cpu_curr_clock; /* current clock freq in Hz */ char *cpu_supp_freqs; /* supported freqs in Hz */ + uintptr_t cpu_cpcprofile_pc; /* kernel PC in cpc interrupt */ + uintptr_t cpu_cpcprofile_upc; /* user PC in cpc interrupt */ + /* * Interrupt load factor used by dispatcher & softcall */ @@ -240,12 +243,13 @@ typedef struct cpu { * is up to the platform to assure that this is performed properly. Note that * the structure is sized to avoid false sharing. */ -#define CPUC_SIZE (sizeof (uint16_t) + sizeof (uintptr_t) + \ - sizeof (kmutex_t)) +#define CPUC_SIZE (sizeof (uint16_t) + sizeof (uint8_t) + \ + sizeof (uintptr_t) + sizeof (kmutex_t)) #define CPUC_PADSIZE CPU_CACHE_COHERENCE_SIZE - CPUC_SIZE typedef struct cpu_core { uint16_t cpuc_dtrace_flags; /* DTrace flags */ + uint8_t cpuc_dcpc_intr_state; /* DCPC provider intr state */ uint8_t cpuc_pad[CPUC_PADSIZE]; /* padding */ uintptr_t cpuc_dtrace_illval; /* DTrace illegal value */ kmutex_t cpuc_pid_lock; /* DTrace pid provider lock */ @@ -715,7 +719,8 @@ typedef enum { CPU_ON, CPU_OFF, CPU_CPUPART_IN, - CPU_CPUPART_OUT + CPU_CPUPART_OUT, + CPU_SETUP } cpu_setup_t; typedef int cpu_setup_func_t(cpu_setup_t, int, void *); diff --git a/usr/src/uts/common/sys/dtrace.h b/usr/src/uts/common/sys/dtrace.h index b6e52ec1c4..046dd13dd4 100644 --- a/usr/src/uts/common/sys/dtrace.h +++ b/usr/src/uts/common/sys/dtrace.h @@ -20,15 +20,13 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_DTRACE_H #define _SYS_DTRACE_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -1382,7 +1380,7 @@ typedef struct dof_helper { * dtps_provide_module(); see "Arguments and Notes" for dtrace_register(), * below. * - * 1.4 void dtps_enable(void *arg, dtrace_id_t id, void *parg) + * 1.4 int dtps_enable(void *arg, dtrace_id_t id, void *parg) * * 1.4.1 Overview * @@ -1403,7 +1401,8 @@ typedef struct dof_helper { * * 1.4.3 Return value * - * None. + * On success, dtps_enable() should return 0. On failure, -1 should be + * returned. * * 1.4.4 Caller's context * @@ -1957,7 +1956,7 @@ typedef struct dof_helper { typedef struct dtrace_pops { void (*dtps_provide)(void *arg, const dtrace_probedesc_t *spec); void (*dtps_provide_module)(void *arg, struct modctl *mp); - void (*dtps_enable)(void *arg, dtrace_id_t id, void *parg); + int (*dtps_enable)(void *arg, dtrace_id_t id, void *parg); void (*dtps_disable)(void *arg, dtrace_id_t id, void *parg); void (*dtps_suspend)(void *arg, dtrace_id_t id, void *parg); void (*dtps_resume)(void *arg, dtrace_id_t id, void *parg); diff --git a/usr/src/uts/common/sys/kcpc.h b/usr/src/uts/common/sys/kcpc.h index 0763723bdf..f30e093f78 100644 --- a/usr/src/uts/common/sys/kcpc.h +++ b/usr/src/uts/common/sys/kcpc.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_KCPC_H #define _SYS_KCPC_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/cpc_impl.h> #include <sys/ksynch.h> @@ -130,11 +128,24 @@ extern void kcpc_idle_restore(struct cpu *cp); extern krwlock_t kcpc_cpuctx_lock; /* lock for 'kcpc_cpuctx' below */ extern int kcpc_cpuctx; /* number of cpu-specific contexts */ +/* + * 'dtrace_cpc_in_use' contains the number of currently active cpc provider + * based enablings. See the block comment in uts/common/os/dtrace_subr.c for + * details of its actual usage. + */ +extern uint32_t dtrace_cpc_in_use; +extern void (*dtrace_cpc_fire)(uint64_t); + extern void kcpc_free_set(kcpc_set_t *set); extern void *kcpc_next_config(void *token, void *current, uint64_t **data); extern void kcpc_invalidate_config(void *token); +extern char *kcpc_list_attrs(void); +extern char *kcpc_list_events(uint_t pic); +extern void kcpc_free_configs(kcpc_set_t *set); +extern uint_t kcpc_pcbe_capabilities(void); +extern int kcpc_pcbe_loaded(void); /* * Called by a PCBE to determine if nonprivileged access to counters should be diff --git a/usr/src/uts/common/xen/dtrace/xdt.c b/usr/src/uts/common/xen/dtrace/xdt.c index bac7f100e9..85c876b479 100644 --- a/usr/src/uts/common/xen/dtrace/xdt.c +++ b/usr/src/uts/common/xen/dtrace/xdt.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -782,7 +782,7 @@ xdt_set_trace_mask(uint32_t mask) } /*ARGSUSED*/ -static void +static int xdt_enable(void *arg, dtrace_id_t id, void *parg) { xdt_probe_t *p = parg; @@ -801,22 +801,15 @@ xdt_enable(void *arg, dtrace_id_t id, void *parg) } if (xdt_cyclic == CYCLIC_NONE) { - /* - * DTrace doesn't have the notion of failing an enabling. It - * works on the premise that, if you have advertised a probe - * via the pops->dtps_provide() function, you can enable it. - * Failure is not an option. In the case where we can't enable - * Xen tracing the consumer will carry on regardless and - * think all is OK except the probes will never fire. - */ tbuf_op.cmd = XEN_SYSCTL_TBUFOP_enable; if (xdt_sysctl_tbuf(&tbuf_op) != 0) { cmn_err(CE_NOTE, "Couldn't enable hypervisor tracing."); - return; + return (-1); } xdt_cyclic_enable(); } + return (0); } /*ARGSUSED*/ diff --git a/usr/src/uts/i86pc/os/intr.c b/usr/src/uts/i86pc/os/intr.c index 12d4304973..2f4b66ddf2 100644 --- a/usr/src/uts/i86pc/os/intr.c +++ b/usr/src/uts/i86pc/os/intr.c @@ -20,12 +20,10 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/cpuvar.h> #include <sys/regset.h> #include <sys/psw.h> @@ -121,9 +119,13 @@ hilevel_intr_prolog(struct cpu *cpu, uint_t pil, uint_t oldpil, struct regs *rp) if (USERMODE(rp->r_cs)) { cpu->cpu_profile_pc = 0; cpu->cpu_profile_upc = rp->r_pc; + cpu->cpu_cpcprofile_pc = 0; + cpu->cpu_cpcprofile_upc = rp->r_pc; } else { cpu->cpu_profile_pc = rp->r_pc; cpu->cpu_profile_upc = 0; + cpu->cpu_cpcprofile_pc = rp->r_pc; + cpu->cpu_cpcprofile_upc = 0; } } diff --git a/usr/src/uts/i86pc/os/mp_startup.c b/usr/src/uts/i86pc/os/mp_startup.c index 19a59af0d0..54eb2f4369 100644 --- a/usr/src/uts/i86pc/os/mp_startup.c +++ b/usr/src/uts/i86pc/os/mp_startup.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -1405,6 +1405,10 @@ start_other_cpus(int cprboot) } if (start_cpu(who) != 0) CPUSET_DEL(mp_cpus, who); + + mutex_enter(&cpu_lock); + cpu_state_change_notify(who, CPU_SETUP); + mutex_exit(&cpu_lock); } /* Free the space allocated to hold the microcode file */ diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared index 95c2dd9538..134a4030ad 100644 --- a/usr/src/uts/intel/Makefile.intel.shared +++ b/usr/src/uts/intel/Makefile.intel.shared @@ -387,6 +387,7 @@ DRV_KMODS += profile DRV_KMODS += sdt DRV_KMODS += systrace DRV_KMODS += fasttrap +DRV_KMODS += dcpc # # I/O framework test drivers diff --git a/usr/src/uts/intel/dcpc/Makefile b/usr/src/uts/intel/dcpc/Makefile new file mode 100644 index 0000000000..6378018a34 --- /dev/null +++ b/usr/src/uts/intel/dcpc/Makefile @@ -0,0 +1,67 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +UTSBASE = ../.. + +MODULE = dcpc +OBJECTS = $(DCPC_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(DCPC_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +ROOTLINK = $(ROOT_DTRACE_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/common/dtrace + +ALL_TARGET = $(BINARY) $(SRC_CONFILE) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE) + +INC_PATH += -I$(UTSBASE)/i86pc + +include $(UTSBASE)/intel/Makefile.intel + +LDFLAGS += -dy -Ndrv/dtrace -Ndrv/cpc + +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +$(ROOTLINK): $(ROOT_DTRACE_DIR) $(ROOTMODULE) + -$(RM) $@; ln $(ROOTMODULE) $@ + +include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/dtrace/fbt.c b/usr/src/uts/intel/dtrace/fbt.c index a5a4292a61..b9353bd30c 100644 --- a/usr/src/uts/intel/dtrace/fbt.c +++ b/usr/src/uts/intel/dtrace/fbt.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -486,7 +486,7 @@ fbt_destroy(void *arg, dtrace_id_t id, void *parg) } /*ARGSUSED*/ -static void +static int fbt_enable(void *arg, dtrace_id_t id, void *parg) { fbt_probe_t *fbt = parg; @@ -501,7 +501,7 @@ fbt_enable(void *arg, dtrace_id_t id, void *parg) fbt->fbtp_name, ctl->mod_modname); } - return; + return (0); } /* @@ -516,11 +516,13 @@ fbt_enable(void *arg, dtrace_id_t id, void *parg) fbt->fbtp_name, ctl->mod_modname); } - return; + return (0); } for (; fbt != NULL; fbt = fbt->fbtp_next) *fbt->fbtp_patchpoint = fbt->fbtp_patchval; + + return (0); } /*ARGSUSED*/ diff --git a/usr/src/uts/intel/dtrace/sdt.c b/usr/src/uts/intel/dtrace/sdt.c index 5217ea6ae3..38be2233b1 100644 --- a/usr/src/uts/intel/dtrace/sdt.c +++ b/usr/src/uts/intel/dtrace/sdt.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -229,7 +229,7 @@ sdt_destroy(void *arg, dtrace_id_t id, void *parg) } /*ARGSUSED*/ -static void +static int sdt_enable(void *arg, dtrace_id_t id, void *parg) { sdt_probe_t *sdp = parg; @@ -269,7 +269,7 @@ sdt_enable(void *arg, dtrace_id_t id, void *parg) sdp = sdp->sdp_next; } err: - ; + return (0); } /*ARGSUSED*/ diff --git a/usr/src/uts/intel/ia32/os/cpc_subr.c b/usr/src/uts/intel/ia32/os/cpc_subr.c index 62929f6414..466cf6ba9f 100644 --- a/usr/src/uts/intel/ia32/os/cpc_subr.c +++ b/usr/src/uts/intel/ia32/os/cpc_subr.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -292,3 +292,28 @@ kcpc_hw_lwp_hook(void) mutex_exit(&cpu_lock); return (0); } + +static int +kcpc_remoteprogram_func(void) +{ + ASSERT(CPU->cpu_cpc_ctx != NULL); + + pcbe_ops->pcbe_program(CPU->cpu_cpc_ctx); + + return (0); +} + +/* + * Ensure counters are enabled on the given processor. + */ +void +kcpc_remote_program(cpu_t *cp) +{ + cpuset_t set; + + CPUSET_ZERO(set); + + CPUSET_ADD(set, cp->cpu_id); + + xc_sync(0, 0, 0, X_CALL_HIPRI, set, (xc_func_t)kcpc_remoteprogram_func); +} diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major index eb70695abd..973020e4c9 100644 --- a/usr/src/uts/intel/os/name_to_major +++ b/usr/src/uts/intel/os/name_to_major @@ -130,6 +130,7 @@ domcaps 200 balloon 201 acpippm 202 srn 203 +dcpc 204 smbsrv 210 did 239 lx_ptm 240 diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index 39fba551aa..2dd7a8675c 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -203,7 +203,7 @@ ALL_DEFS = $(MACHINE_DEFS) $(DEBUG_DEFS) $(OPTION_DEFS) # DRV_KMODS += aggr arp bl bofi clone cn conskbd consms cpuid DRV_KMODS += crypto cryptoadm devinfo dump -DRV_KMODS += dtrace fasttrap fbt lockstat profile sdt systrace +DRV_KMODS += dtrace fasttrap fbt lockstat profile sdt systrace dcpc DRV_KMODS += fssnap icmp icmp6 ip ip6 ipnet ipsecah DRV_KMODS += ipsecesp iwscn keysock kmdb kstat ksyms llc1 DRV_KMODS += lofi diff --git a/usr/src/uts/sparc/dcpc/Makefile b/usr/src/uts/sparc/dcpc/Makefile new file mode 100644 index 0000000000..1a8c50de78 --- /dev/null +++ b/usr/src/uts/sparc/dcpc/Makefile @@ -0,0 +1,65 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +UTSBASE = ../.. + +MODULE = dcpc +OBJECTS = $(DCPC_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(DCPC_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +ROOTLINK = $(ROOT_DTRACE_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/common/dtrace + +ALL_TARGET = $(BINARY) $(SRC_CONFILE) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE) + +include $(UTSBASE)/sparc/Makefile.sparc + +LDFLAGS += -dy -Ndrv/dtrace -Ndrv/cpc + +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +$(ROOTLINK): $(ROOT_DTRACE_DIR) $(ROOTMODULE) + -$(RM) $@; ln $(ROOTMODULE) $@ + +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/dtrace/fbt.c b/usr/src/uts/sparc/dtrace/fbt.c index a2eba46965..58169bac81 100644 --- a/usr/src/uts/sparc/dtrace/fbt.c +++ b/usr/src/uts/sparc/dtrace/fbt.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -1483,7 +1483,7 @@ fbt_destroy(void *arg, dtrace_id_t id, void *parg) } /*ARGSUSED*/ -static void +static int fbt_enable(void *arg, dtrace_id_t id, void *parg) { fbt_probe_t *fbt = parg, *f; @@ -1503,7 +1503,7 @@ fbt_enable(void *arg, dtrace_id_t id, void *parg) fbt->fbtp_name, ctl->mod_modname); } - return; + return (0); } } @@ -1518,7 +1518,7 @@ fbt_enable(void *arg, dtrace_id_t id, void *parg) fbt->fbtp_name, ctl->mod_modname); } - return; + return (0); } /* @@ -1533,11 +1533,13 @@ fbt_enable(void *arg, dtrace_id_t id, void *parg) fbt->fbtp_name, ctl->mod_modname); } - return; + return (0); } for (; fbt != NULL; fbt = fbt->fbtp_next) *fbt->fbtp_patchpoint = fbt->fbtp_patchval; + + return (0); } /*ARGSUSED*/ diff --git a/usr/src/uts/sparc/dtrace/sdt.c b/usr/src/uts/sparc/dtrace/sdt.c index ce6656e8ee..af9ea8e155 100644 --- a/usr/src/uts/sparc/dtrace/sdt.c +++ b/usr/src/uts/sparc/dtrace/sdt.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -269,7 +269,7 @@ sdt_destroy(void *arg, dtrace_id_t id, void *parg) } /*ARGSUSED*/ -static void +static int sdt_enable(void *arg, dtrace_id_t id, void *parg) { sdt_probe_t *sdp = parg; @@ -310,7 +310,7 @@ sdt_enable(void *arg, dtrace_id_t id, void *parg) } err: - ; + return (0); } /*ARGSUSED*/ diff --git a/usr/src/uts/sparc/os/name_to_major b/usr/src/uts/sparc/os/name_to_major index 9702d00ad7..1346b28e45 100644 --- a/usr/src/uts/sparc/os/name_to_major +++ b/usr/src/uts/sparc/os/name_to_major @@ -227,3 +227,4 @@ bmc 278 fm 279 nulldriver 280 ipnet 281 +dcpc 282 diff --git a/usr/src/uts/sun4/cpu/cpu_module.c b/usr/src/uts/sun4/cpu/cpu_module.c index 717cc26f90..f20d13276b 100644 --- a/usr/src/uts/sun4/cpu/cpu_module.c +++ b/usr/src/uts/sun4/cpu/cpu_module.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/cpu_module.h> #include <sys/lockstat.h> @@ -328,6 +326,10 @@ void pil14_interrupt(void) { } +void +pil15_interrupt(void) +{ } + /* ARGSUSED */ void cpu_init_private(struct cpu *cp) diff --git a/usr/src/uts/sun4/ml/offsets.in b/usr/src/uts/sun4/ml/offsets.in index d28b700fdd..c4ce99506e 100644 --- a/usr/src/uts/sun4/ml/offsets.in +++ b/usr/src/uts/sun4/ml/offsets.in @@ -1,5 +1,5 @@ \ offsets.in: input file to produce assym.h using the stabs program -\ Copyright 2007 Sun Microsystems, Inc. All rights reserved. +\ Copyright 2009 Sun Microsystems, Inc. All rights reserved. \ Use is subject to license terms. \ \ CDDL HEADER START @@ -68,8 +68,6 @@ \ and all of the nested structures/unions together. See the many \ examples already in this file. -#pragma ident "%Z%%M% %I% %E% SMI" - #ifndef _GENASSYM #define _GENASSYM #endif @@ -365,6 +363,8 @@ cpu CPUSIZE cpu_ftrace.ftd_state CPU_FTRACE_STATE cpu_mstate cpu_intracct + cpu_cpcprofile_pc + cpu_cpcprofile_upc cpu_m CPU_MCPU cpu_m.divisor CPU_DIVISOR cpu_m.intrstat CPU_INTRSTAT diff --git a/usr/src/uts/sun4/os/mp_startup.c b/usr/src/uts/sun4/os/mp_startup.c index 6d51e89b6e..658495a382 100644 --- a/usr/src/uts/sun4/os/mp_startup.c +++ b/usr/src/uts/sun4/os/mp_startup.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -753,10 +753,8 @@ start_other_cpus(int flag) cmn_err(CE_CONT, "!cpu%d initialization complete - online\n", cpuid); - /* - * XXX: register_cpu_setup() callbacks should be called here - * with a new setup code, CPU_BOOT (or something). - */ + cpu_state_change_notify(cpuid, CPU_SETUP); + if (dtrace_cpu_init != NULL) (*dtrace_cpu_init)(cpuid); } diff --git a/usr/src/uts/sun4/os/startup.c b/usr/src/uts/sun4/os/startup.c index a8c2a585db..1459eb1ce4 100644 --- a/usr/src/uts/sun4/os/startup.c +++ b/usr/src/uts/sun4/os/startup.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -2107,6 +2107,11 @@ startup_end(void) kern_setup1(); /* + * Perform CPC initialization for this CPU. + */ + kcpc_hw_init(); + + /* * Intialize the VM arenas for allocating physically * contiguus memory chunk for interrupt queues snd * allocate/register boot cpu's queues, if any and diff --git a/usr/src/uts/sun4u/cpu/common_asm.s b/usr/src/uts/sun4u/cpu/common_asm.s index b13ec0ef67..46c12c4775 100644 --- a/usr/src/uts/sun4u/cpu/common_asm.s +++ b/usr/src/uts/sun4u/cpu/common_asm.s @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #if !defined(lint) #include "assym.h" #endif /* !lint */ @@ -1056,6 +1054,34 @@ pil14_interrupt(int level) #endif /* lint */ +#if defined(lint) + +/* ARGSUSED */ +void +pil15_interrupt(int level) +{} + +#else /* lint */ + +/* + * Level-15 interrupt prologue. + */ + ENTRY_NP(pil15_interrupt) + CPU_ADDR(%g1, %g2) + rdpr %tstate, %g6 + rdpr %tpc, %g5 + btst TSTATE_PRIV, %g6 ! trap from supervisor mode? + bnz,a,pt %xcc, 1f + stn %g5, [%g1 + CPU_CPCPROFILE_PC] ! if so, record kernel PC + stn %g5, [%g1 + CPU_CPCPROFILE_UPC] ! if not, record user PC + ba pil15_epilogue ! must be large-disp branch + stn %g0, [%g1 + CPU_CPCPROFILE_PC] ! zero kernel PC +1: ba pil15_epilogue ! must be large-disp branch + stn %g0, [%g1 + CPU_CPCPROFILE_UPC] ! zero user PC + SET_SIZE(pil15_interrupt) + +#endif /* lint */ + #if defined(lint) || defined(__lint) /* ARGSUSED */ diff --git a/usr/src/uts/sun4u/cpu/us3_cheetah.c b/usr/src/uts/sun4u/cpu/us3_cheetah.c index 44fefd1ecf..eadaebc099 100644 --- a/usr/src/uts/sun4u/cpu/us3_cheetah.c +++ b/usr/src/uts/sun4u/cpu/us3_cheetah.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/systm.h> #include <sys/ddi.h> @@ -79,7 +77,7 @@ void cpu_init_trap(void) { - CH_SET_TRAP(tt_pil15, ch_pil15_interrupt_instr); + CH_SET_TRAP(pil15_epilogue, ch_pil15_interrupt_instr); CH_SET_TRAP(tt0_fecc, fecc_err_instr); CH_SET_TRAP(tt1_fecc, fecc_err_tl1_instr); diff --git a/usr/src/uts/sun4u/cpu/us3_cheetahplus.c b/usr/src/uts/sun4u/cpu/us3_cheetahplus.c index 52a664c3eb..7cda4df713 100644 --- a/usr/src/uts/sun4u/cpu/us3_cheetahplus.c +++ b/usr/src/uts/sun4u/cpu/us3_cheetahplus.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/systm.h> #include <sys/ddi.h> @@ -89,7 +87,7 @@ static cpu_t *cpu_get_sibling_core(cpu_t *cpup); void cpu_init_trap(void) { - CH_SET_TRAP(tt_pil15, ch_pil15_interrupt_instr); + CH_SET_TRAP(pil15_epilogue, ch_pil15_interrupt_instr); CH_SET_TRAP(tt0_fecc, fecc_err_instr); CH_SET_TRAP(tt1_fecc, fecc_err_tl1_instr); diff --git a/usr/src/uts/sun4u/cpu/us3_jalapeno.c b/usr/src/uts/sun4u/cpu/us3_jalapeno.c index 6bbd1a3d4a..bb0cb0c961 100644 --- a/usr/src/uts/sun4u/cpu/us3_jalapeno.c +++ b/usr/src/uts/sun4u/cpu/us3_jalapeno.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/systm.h> #include <sys/ddi.h> @@ -102,7 +100,7 @@ uint64_t jp_estar_tl1_data[8]; void cpu_init_trap(void) { - CH_SET_TRAP(tt_pil15, ch_pil15_interrupt_instr); + CH_SET_TRAP(pil15_epilogue, ch_pil15_interrupt_instr); CH_SET_TRAP(tt0_fecc, fecc_err_instr); CH_SET_TRAP(tt1_fecc, fecc_err_tl1_instr); diff --git a/usr/src/uts/sun4u/ml/trap_table.s b/usr/src/uts/sun4u/ml/trap_table.s index 8b8e15d926..e8219e3017 100644 --- a/usr/src/uts/sun4u/ml/trap_table.s +++ b/usr/src/uts/sun4u/ml/trap_table.s @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #if !defined(lint) #include "assym.h" #endif /* !lint */ @@ -940,6 +938,11 @@ tt_pil/**/level: ;\ mov PIL_14, %g4 ;\ .align 32 +#define LEVEL15_INTERRUPT \ + ba pil15_interrupt ;\ + mov PIL_15, %g4 ;\ + .align 32 + #define VECTOR_INTERRUPT \ ldxa [%g0]ASI_INTR_RECEIVE_STATUS, %g1 ;\ btst IRSR_BUSY, %g1 ;\ @@ -1405,7 +1408,7 @@ trap_table0: LEVEL_INTERRUPT(12); /* 04C interrupt level 12 */ LEVEL_INTERRUPT(13); /* 04D interrupt level 13 */ LEVEL14_INTERRUPT; /* 04E interrupt level 14 */ - LEVEL_INTERRUPT(15); /* 04F interrupt level 15 */ + LEVEL15_INTERRUPT; /* 04F interrupt level 15 */ NOT4; NOT4; NOT4; NOT4; /* 050 - 05F reserved */ VECTOR_INTERRUPT; /* 060 interrupt vector */ GOTO(kmdb_trap); /* 061 PA watchpoint */ @@ -2927,6 +2930,13 @@ trace_dataprot: #endif /* TRAPTRACE */ + .align 32 + .global pil15_epilogue +pil15_epilogue: + ba pil_interrupt_common + nop + .align 32 + /* * fast_trap_done, fast_trap_done_chk_intr: * diff --git a/usr/src/uts/sun4u/os/cpc_subr.c b/usr/src/uts/sun4u/os/cpc_subr.c index 5a814e0aa7..a9c64681fd 100644 --- a/usr/src/uts/sun4u/os/cpc_subr.c +++ b/usr/src/uts/sun4u/os/cpc_subr.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * sun4u common CPC subroutines. */ @@ -48,8 +46,8 @@ #include <sys/modctl.h> #include <sys/sdt.h> -uint64_t cpc_level15_inum; /* used in interrupt.s */ -int cpc_has_overflow_intr; /* set in cheetah.c */ +uint64_t cpc_level15_inum; /* used in interrupt.s */ +int cpc_has_overflow_intr; /* set in cheetah.c */ extern kcpc_ctx_t *kcpc_overflow_intr(caddr_t arg, uint64_t bitmap); extern int kcpc_counts_include_idle; @@ -144,3 +142,21 @@ kcpc_hw_lwp_hook(void) { return (0); } + +/*ARGSUSED*/ +static void +kcpc_remoteprogram_func(uint64_t arg1, uint64_t arg2) +{ + ASSERT(CPU->cpu_cpc_ctx != NULL); + + pcbe_ops->pcbe_program(CPU->cpu_cpc_ctx); +} + +/* + * Ensure counters are enabled on the given processor. + */ +void +kcpc_remote_program(cpu_t *cp) +{ + xc_one(cp->cpu_id, kcpc_remoteprogram_func, 0, 0); +} diff --git a/usr/src/uts/sun4u/sys/us3_module.h b/usr/src/uts/sun4u/sys/us3_module.h index 2244915ab3..77ee9fceb7 100644 --- a/usr/src/uts/sun4u/sys/us3_module.h +++ b/usr/src/uts/sun4u/sys/us3_module.h @@ -19,14 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_US3_MODULE_H #define _SYS_US3_MODULE_H -#pragma ident "%Z%%M% %I% %E% SMI" #include <sys/async.h> #ifdef __cplusplus @@ -661,7 +660,7 @@ extern void *tt1_swtrap0; /* * Address of trap table level 15 interrupt handler in the trap table. */ -extern void *tt_pil15; +extern void *pil15_epilogue; /* * D$ and I$ global parameters. */ diff --git a/usr/src/uts/sun4v/cpu/common_asm.s b/usr/src/uts/sun4v/cpu/common_asm.s index d0d017824e..cf30c07365 100644 --- a/usr/src/uts/sun4v/cpu/common_asm.s +++ b/usr/src/uts/sun4v/cpu/common_asm.s @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -870,6 +870,34 @@ pil14_interrupt(int level) #endif /* lint */ #if defined(lint) + +/* ARGSUSED */ +void +pil15_interrupt(int level) +{} + +#else /* lint */ + +/* + * Level-15 interrupt prologue. + */ + ENTRY_NP(pil15_interrupt) + CPU_ADDR(%g1, %g2) + rdpr %tstate, %g6 + rdpr %tpc, %g5 + btst TSTATE_PRIV, %g6 ! trap from supervisor mode? + bnz,a,pt %xcc, 1f + stn %g5, [%g1 + CPU_CPCPROFILE_PC] ! if so, record kernel PC + stn %g5, [%g1 + CPU_CPCPROFILE_UPC] ! if not, record user PC + ba pil15_epilogue ! must be large-disp branch + stn %g0, [%g1 + CPU_CPCPROFILE_PC] ! zero kernel PC +1: ba pil15_epilogue ! must be large-disp branch + stn %g0, [%g1 + CPU_CPCPROFILE_UPC] ! zero user PC + SET_SIZE(pil15_interrupt) + +#endif /* lint */ + +#if defined(lint) /* * Prefetch a page_t for write or read, this assumes a linear * scan of sequential page_t's. diff --git a/usr/src/uts/sun4v/ml/trap_table.s b/usr/src/uts/sun4v/ml/trap_table.s index 0d3876f79a..cbe2eccd8d 100644 --- a/usr/src/uts/sun4v/ml/trap_table.s +++ b/usr/src/uts/sun4v/ml/trap_table.s @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -807,6 +807,11 @@ tt_pil/**/level: ;\ mov PIL_14, %g4 ;\ .align 32 +#define LEVEL15_INTERRUPT \ + ba pil15_interrupt ;\ + mov PIL_15, %g4 ;\ + .align 32 + #define CPU_MONDO \ ba,a,pt %xcc, cpu_mondo ;\ .align 32 @@ -1171,7 +1176,7 @@ trap_table0: LEVEL_INTERRUPT(12); /* 04C interrupt level 12 */ LEVEL_INTERRUPT(13); /* 04D interrupt level 13 */ LEVEL14_INTERRUPT; /* 04E interrupt level 14 */ - LEVEL_INTERRUPT(15); /* 04F interrupt level 15 */ + LEVEL15_INTERRUPT; /* 04F interrupt level 15 */ NOT4; NOT4; NOT4; NOT4; /* 050 - 05F reserved */ NOT; /* 060 interrupt vector */ GOTO(kmdb_trap); /* 061 PA watchpoint */ @@ -2766,6 +2771,13 @@ trace_dataprot: mov T_DATA_EXCEPTION, %g1 SET_SIZE(.dmmu_exception) + .align 32 + .global pil15_epilogue +pil15_epilogue: + ba pil_interrupt_common + nop + .align 32 + /* * fast_trap_done, fast_trap_done_chk_intr: * diff --git a/usr/src/uts/sun4v/os/cpc_subr.c b/usr/src/uts/sun4v/os/cpc_subr.c index 7944fb9e02..8e58d85513 100644 --- a/usr/src/uts/sun4v/os/cpc_subr.c +++ b/usr/src/uts/sun4v/os/cpc_subr.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * sun4u common CPC subroutines. */ @@ -48,8 +46,8 @@ #include <sys/modctl.h> #include <sys/sdt.h> -uint64_t cpc_level15_inum; /* used in interrupt.s */ -int cpc_has_overflow_intr; /* set in cheetah.c */ +uint64_t cpc_level15_inum; /* used in interrupt.s */ +int cpc_has_overflow_intr; /* set in cheetah.c */ extern kcpc_ctx_t *kcpc_overflow_intr(caddr_t arg, uint64_t bitmap); extern int kcpc_counts_include_idle; @@ -163,3 +161,21 @@ kcpc_hw_lwp_hook(void) { return (0); } + +/*ARGSUSED*/ +static void +kcpc_remoteprogram_func(uint64_t arg1, uint64_t arg2) +{ + ASSERT(CPU->cpu_cpc_ctx != NULL); + + pcbe_ops->pcbe_program(CPU->cpu_cpc_ctx); +} + +/* + * Ensure counters are enabled on the given processor. + */ +void +kcpc_remote_program(cpu_t *cp) +{ + xc_one(cp->cpu_id, kcpc_remoteprogram_func, 0, 0); +} |