10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 56275Strevtom * Common Development and Distribution License (the "License"). 66275Strevtom * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*8803SJonathan.Haslam@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* 270Sstevel@tonic-gate * CPU Performance Counter system calls and device driver. 280Sstevel@tonic-gate * 290Sstevel@tonic-gate * This module uses a combination of thread context operators, and 300Sstevel@tonic-gate * thread-specific data to export CPU performance counters 310Sstevel@tonic-gate * via both a system call and a driver interface. 320Sstevel@tonic-gate * 330Sstevel@tonic-gate * There are three access methods exported - the 'shared' device 340Sstevel@tonic-gate * and the 'private' and 'agent' variants of the system call. 350Sstevel@tonic-gate * 360Sstevel@tonic-gate * The shared device treats the performance counter registers as 370Sstevel@tonic-gate * a processor metric, regardless of the work scheduled on them. 380Sstevel@tonic-gate * The private system call treats the performance counter registers 390Sstevel@tonic-gate * as a property of a single lwp. This is achieved by using the 400Sstevel@tonic-gate * thread context operators to virtualize the contents of the 410Sstevel@tonic-gate * performance counter registers between lwps. 420Sstevel@tonic-gate * 430Sstevel@tonic-gate * The agent method is like the private method, except that it must 440Sstevel@tonic-gate * be accessed via /proc's agent lwp to allow the counter context of 450Sstevel@tonic-gate * other threads to be examined safely. 460Sstevel@tonic-gate * 470Sstevel@tonic-gate * The shared usage fundamentally conflicts with the agent and private usage; 480Sstevel@tonic-gate * almost all of the complexity of the module is needed to allow these two 490Sstevel@tonic-gate * models to co-exist in a reasonable way. 500Sstevel@tonic-gate */ 510Sstevel@tonic-gate 520Sstevel@tonic-gate #include <sys/types.h> 530Sstevel@tonic-gate #include <sys/file.h> 540Sstevel@tonic-gate #include <sys/errno.h> 550Sstevel@tonic-gate #include <sys/open.h> 560Sstevel@tonic-gate #include <sys/cred.h> 570Sstevel@tonic-gate #include <sys/conf.h> 580Sstevel@tonic-gate #include <sys/stat.h> 590Sstevel@tonic-gate #include <sys/processor.h> 600Sstevel@tonic-gate #include <sys/cpuvar.h> 610Sstevel@tonic-gate #include <sys/disp.h> 620Sstevel@tonic-gate #include <sys/kmem.h> 630Sstevel@tonic-gate #include <sys/modctl.h> 640Sstevel@tonic-gate #include <sys/ddi.h> 650Sstevel@tonic-gate #include <sys/sunddi.h> 660Sstevel@tonic-gate #include <sys/nvpair.h> 670Sstevel@tonic-gate #include <sys/policy.h> 680Sstevel@tonic-gate #include <sys/machsystm.h> 690Sstevel@tonic-gate #include <sys/cpc_impl.h> 700Sstevel@tonic-gate #include <sys/cpc_pcbe.h> 710Sstevel@tonic-gate #include <sys/kcpc.h> 720Sstevel@tonic-gate 730Sstevel@tonic-gate static int kcpc_copyin_set(kcpc_set_t **set, void *ubuf, size_t len); 740Sstevel@tonic-gate static int kcpc_verify_set(kcpc_set_t *set); 750Sstevel@tonic-gate static uint32_t kcpc_nvlist_npairs(nvlist_t *list); 760Sstevel@tonic-gate 770Sstevel@tonic-gate /* 780Sstevel@tonic-gate * Generic attributes supported regardless of processor. 790Sstevel@tonic-gate */ 800Sstevel@tonic-gate 810Sstevel@tonic-gate #define ATTRLIST "picnum" 820Sstevel@tonic-gate #define SEPARATOR "," 830Sstevel@tonic-gate 840Sstevel@tonic-gate /* 850Sstevel@tonic-gate * System call to access CPU performance counters. 860Sstevel@tonic-gate */ 870Sstevel@tonic-gate static int 880Sstevel@tonic-gate cpc(int cmd, id_t lwpid, void *udata1, void *udata2, void *udata3) 890Sstevel@tonic-gate { 900Sstevel@tonic-gate kthread_t *t; 910Sstevel@tonic-gate int error; 920Sstevel@tonic-gate int size; 930Sstevel@tonic-gate const char *str; 940Sstevel@tonic-gate int code; 950Sstevel@tonic-gate 960Sstevel@tonic-gate /* 970Sstevel@tonic-gate * This CPC syscall should only be loaded if it found a PCBE to use. 980Sstevel@tonic-gate */ 990Sstevel@tonic-gate ASSERT(pcbe_ops != NULL); 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate if (curproc->p_agenttp == curthread) { 1020Sstevel@tonic-gate /* 1030Sstevel@tonic-gate * Only if /proc is invoking this system call from 1040Sstevel@tonic-gate * the agent thread do we allow the caller to examine 1050Sstevel@tonic-gate * the contexts of other lwps in the process. And 1060Sstevel@tonic-gate * because we know we're the agent, we know we don't 1070Sstevel@tonic-gate * have to grab p_lock because no-one else can change 1080Sstevel@tonic-gate * the state of the process. 1090Sstevel@tonic-gate */ 1100Sstevel@tonic-gate if ((t = idtot(curproc, lwpid)) == NULL || t == curthread) 1110Sstevel@tonic-gate return (set_errno(ESRCH)); 1120Sstevel@tonic-gate ASSERT(t->t_tid == lwpid && ttolwp(t) != NULL); 1130Sstevel@tonic-gate } else 1140Sstevel@tonic-gate t = curthread; 1150Sstevel@tonic-gate 1160Sstevel@tonic-gate if (t->t_cpc_set == NULL && (cmd == CPC_SAMPLE || cmd == CPC_RELE)) 1170Sstevel@tonic-gate return (set_errno(EINVAL)); 1180Sstevel@tonic-gate 1190Sstevel@tonic-gate switch (cmd) { 1200Sstevel@tonic-gate case CPC_BIND: 1210Sstevel@tonic-gate /* 1220Sstevel@tonic-gate * udata1 = pointer to packed nvlist buffer 1230Sstevel@tonic-gate * udata2 = size of packed nvlist buffer 1240Sstevel@tonic-gate * udata3 = User addr to return error subcode in. 1250Sstevel@tonic-gate */ 1260Sstevel@tonic-gate 1270Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_READER); 128*8803SJonathan.Haslam@Sun.COM if (kcpc_cpuctx || dtrace_cpc_in_use) { 1290Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1300Sstevel@tonic-gate return (set_errno(EAGAIN)); 1310Sstevel@tonic-gate } 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate if (kcpc_hw_lwp_hook() != 0) { 1340Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1350Sstevel@tonic-gate return (set_errno(EACCES)); 1360Sstevel@tonic-gate } 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate /* 1390Sstevel@tonic-gate * An LWP may only have one set bound to it at a time; if there 1400Sstevel@tonic-gate * is a set bound to this LWP already, we unbind it here. 1410Sstevel@tonic-gate */ 1420Sstevel@tonic-gate if (t->t_cpc_set != NULL) 1430Sstevel@tonic-gate (void) kcpc_unbind(t->t_cpc_set); 1440Sstevel@tonic-gate ASSERT(t->t_cpc_set == NULL); 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1, 1470Sstevel@tonic-gate (size_t)udata2)) != 0) { 1480Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1490Sstevel@tonic-gate return (set_errno(error)); 1500Sstevel@tonic-gate } 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) { 1530Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1540Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 1550Sstevel@tonic-gate t->t_cpc_set = NULL; 1560Sstevel@tonic-gate if (copyout(&error, udata3, sizeof (error)) == -1) 1570Sstevel@tonic-gate return (set_errno(EFAULT)); 1580Sstevel@tonic-gate return (set_errno(EINVAL)); 1590Sstevel@tonic-gate } 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate if ((error = kcpc_bind_thread(t->t_cpc_set, t, &code)) != 0) { 1620Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1630Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 1640Sstevel@tonic-gate t->t_cpc_set = NULL; 1650Sstevel@tonic-gate /* 1660Sstevel@tonic-gate * EINVAL and EACCES are the only errors with more 1670Sstevel@tonic-gate * specific subcodes. 1680Sstevel@tonic-gate */ 1690Sstevel@tonic-gate if ((error == EINVAL || error == EACCES) && 1700Sstevel@tonic-gate copyout(&code, udata3, sizeof (code)) == -1) 1710Sstevel@tonic-gate return (set_errno(EFAULT)); 1720Sstevel@tonic-gate return (set_errno(error)); 1730Sstevel@tonic-gate } 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1760Sstevel@tonic-gate return (0); 1770Sstevel@tonic-gate case CPC_SAMPLE: 1780Sstevel@tonic-gate /* 1790Sstevel@tonic-gate * udata1 = pointer to user's buffer 1800Sstevel@tonic-gate * udata2 = pointer to user's hrtime 1810Sstevel@tonic-gate * udata3 = pointer to user's tick 1820Sstevel@tonic-gate */ 1830Sstevel@tonic-gate /* 1840Sstevel@tonic-gate * We only allow thread-bound sets to be sampled via the 1850Sstevel@tonic-gate * syscall, so if this set has a CPU-bound context, return an 1860Sstevel@tonic-gate * error. 1870Sstevel@tonic-gate */ 1880Sstevel@tonic-gate if (t->t_cpc_set->ks_ctx->kc_cpuid != -1) 1890Sstevel@tonic-gate return (set_errno(EINVAL)); 1900Sstevel@tonic-gate if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2, 1910Sstevel@tonic-gate udata3)) != 0) 1920Sstevel@tonic-gate return (set_errno(error)); 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate return (0); 1950Sstevel@tonic-gate case CPC_PRESET: 1960Sstevel@tonic-gate case CPC_RESTART: 1970Sstevel@tonic-gate /* 1980Sstevel@tonic-gate * These are valid only if this lwp has a bound set. 1990Sstevel@tonic-gate */ 2000Sstevel@tonic-gate if (t->t_cpc_set == NULL) 2010Sstevel@tonic-gate return (set_errno(EINVAL)); 2020Sstevel@tonic-gate if (cmd == CPC_PRESET) { 2030Sstevel@tonic-gate /* 2040Sstevel@tonic-gate * The preset is shipped up to us from userland in two 2050Sstevel@tonic-gate * parts. This lets us handle 64-bit values from 32-bit 2060Sstevel@tonic-gate * and 64-bit applications in the same manner. 2070Sstevel@tonic-gate * 2080Sstevel@tonic-gate * udata1 = index of request to preset 2090Sstevel@tonic-gate * udata2 = new 64-bit preset (most sig. 32 bits) 2100Sstevel@tonic-gate * udata3 = new 64-bit preset (least sig. 32 bits) 2110Sstevel@tonic-gate */ 2120Sstevel@tonic-gate if ((error = kcpc_preset(t->t_cpc_set, (intptr_t)udata1, 2130Sstevel@tonic-gate ((uint64_t)(uintptr_t)udata2 << 32ULL) | 2140Sstevel@tonic-gate (uint64_t)(uintptr_t)udata3)) != 0) 2150Sstevel@tonic-gate return (set_errno(error)); 2160Sstevel@tonic-gate } else { 2170Sstevel@tonic-gate /* 2180Sstevel@tonic-gate * udata[1-3] = unused 2190Sstevel@tonic-gate */ 2200Sstevel@tonic-gate if ((error = kcpc_restart(t->t_cpc_set)) != 0) 2210Sstevel@tonic-gate return (set_errno(error)); 2220Sstevel@tonic-gate } 2230Sstevel@tonic-gate return (0); 2240Sstevel@tonic-gate case CPC_ENABLE: 2250Sstevel@tonic-gate case CPC_DISABLE: 2260Sstevel@tonic-gate udata1 = 0; 2270Sstevel@tonic-gate /*FALLTHROUGH*/ 2280Sstevel@tonic-gate case CPC_USR_EVENTS: 2290Sstevel@tonic-gate case CPC_SYS_EVENTS: 2300Sstevel@tonic-gate if (t != curthread || t->t_cpc_set == NULL) 2310Sstevel@tonic-gate return (set_errno(EINVAL)); 2320Sstevel@tonic-gate /* 2330Sstevel@tonic-gate * Provided for backwards compatibility with CPCv1. 2340Sstevel@tonic-gate * 2350Sstevel@tonic-gate * Stop the counters and record the current counts. Use the 2360Sstevel@tonic-gate * counts as the preset to rebind a new set with the requests 2370Sstevel@tonic-gate * reconfigured as requested. 2380Sstevel@tonic-gate * 2390Sstevel@tonic-gate * udata1: 1 == enable; 0 == disable 2400Sstevel@tonic-gate * udata{2,3}: unused 2410Sstevel@tonic-gate */ 2420Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_READER); 2430Sstevel@tonic-gate if ((error = kcpc_enable(t, 2440Sstevel@tonic-gate cmd, (int)(uintptr_t)udata1)) != 0) { 2450Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 2460Sstevel@tonic-gate return (set_errno(error)); 2470Sstevel@tonic-gate } 2480Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 2490Sstevel@tonic-gate return (0); 2500Sstevel@tonic-gate case CPC_NPIC: 2510Sstevel@tonic-gate return (cpc_ncounters); 2520Sstevel@tonic-gate case CPC_CAPS: 2530Sstevel@tonic-gate return (pcbe_ops->pcbe_caps); 2540Sstevel@tonic-gate case CPC_EVLIST_SIZE: 2550Sstevel@tonic-gate case CPC_LIST_EVENTS: 2560Sstevel@tonic-gate /* 2570Sstevel@tonic-gate * udata1 = pointer to user's int or buffer 2580Sstevel@tonic-gate * udata2 = picnum 2590Sstevel@tonic-gate * udata3 = unused 2600Sstevel@tonic-gate */ 2610Sstevel@tonic-gate if ((uintptr_t)udata2 >= cpc_ncounters) 2620Sstevel@tonic-gate return (set_errno(EINVAL)); 2630Sstevel@tonic-gate 2640Sstevel@tonic-gate size = strlen( 2650Sstevel@tonic-gate pcbe_ops->pcbe_list_events((uintptr_t)udata2)) + 1; 2660Sstevel@tonic-gate 2670Sstevel@tonic-gate if (cmd == CPC_EVLIST_SIZE) { 2680Sstevel@tonic-gate if (suword32(udata1, size) == -1) 2690Sstevel@tonic-gate return (set_errno(EFAULT)); 2700Sstevel@tonic-gate } else { 2710Sstevel@tonic-gate if (copyout( 2720Sstevel@tonic-gate pcbe_ops->pcbe_list_events((uintptr_t)udata2), 2730Sstevel@tonic-gate udata1, size) == -1) 2740Sstevel@tonic-gate return (set_errno(EFAULT)); 2750Sstevel@tonic-gate } 2760Sstevel@tonic-gate return (0); 2770Sstevel@tonic-gate case CPC_ATTRLIST_SIZE: 2780Sstevel@tonic-gate case CPC_LIST_ATTRS: 2790Sstevel@tonic-gate /* 2800Sstevel@tonic-gate * udata1 = pointer to user's int or buffer 2810Sstevel@tonic-gate * udata2 = unused 2820Sstevel@tonic-gate * udata3 = unused 2830Sstevel@tonic-gate * 2840Sstevel@tonic-gate * attrlist size is length of PCBE-supported attributes, plus 2850Sstevel@tonic-gate * room for "picnum\0" plus an optional ',' separator char. 2860Sstevel@tonic-gate */ 2870Sstevel@tonic-gate str = pcbe_ops->pcbe_list_attrs(); 2880Sstevel@tonic-gate size = strlen(str) + sizeof (SEPARATOR ATTRLIST) + 1; 2890Sstevel@tonic-gate if (str[0] != '\0') 2900Sstevel@tonic-gate /* 2910Sstevel@tonic-gate * A ',' separator character is necessary. 2920Sstevel@tonic-gate */ 2930Sstevel@tonic-gate size += 1; 2940Sstevel@tonic-gate 2950Sstevel@tonic-gate if (cmd == CPC_ATTRLIST_SIZE) { 2960Sstevel@tonic-gate if (suword32(udata1, size) == -1) 2970Sstevel@tonic-gate return (set_errno(EFAULT)); 2980Sstevel@tonic-gate } else { 2990Sstevel@tonic-gate /* 3000Sstevel@tonic-gate * Copyout the PCBE attributes, and then append the 3010Sstevel@tonic-gate * generic attribute list (with separator if necessary). 3020Sstevel@tonic-gate */ 3030Sstevel@tonic-gate if (copyout(str, udata1, strlen(str)) == -1) 3040Sstevel@tonic-gate return (set_errno(EFAULT)); 3050Sstevel@tonic-gate if (str[0] != '\0') { 3060Sstevel@tonic-gate if (copyout(SEPARATOR ATTRLIST, 3070Sstevel@tonic-gate ((char *)udata1) + strlen(str), 3080Sstevel@tonic-gate strlen(SEPARATOR ATTRLIST) + 1) 3090Sstevel@tonic-gate == -1) 3100Sstevel@tonic-gate return (set_errno(EFAULT)); 3110Sstevel@tonic-gate } else 3120Sstevel@tonic-gate if (copyout(ATTRLIST, 3130Sstevel@tonic-gate (char *)udata1 + strlen(str), 3140Sstevel@tonic-gate strlen(ATTRLIST) + 1) == -1) 3150Sstevel@tonic-gate return (set_errno(EFAULT)); 3160Sstevel@tonic-gate } 3170Sstevel@tonic-gate return (0); 3180Sstevel@tonic-gate case CPC_IMPL_NAME: 3190Sstevel@tonic-gate case CPC_CPUREF: 3200Sstevel@tonic-gate /* 3210Sstevel@tonic-gate * udata1 = pointer to user's buffer 3220Sstevel@tonic-gate * udata2 = unused 3230Sstevel@tonic-gate * udata3 = unused 3240Sstevel@tonic-gate */ 3250Sstevel@tonic-gate if (cmd == CPC_IMPL_NAME) { 3260Sstevel@tonic-gate str = pcbe_ops->pcbe_impl_name(); 3270Sstevel@tonic-gate ASSERT(strlen(str) < CPC_MAX_IMPL_NAME); 3280Sstevel@tonic-gate } else { 3290Sstevel@tonic-gate str = pcbe_ops->pcbe_cpuref(); 3300Sstevel@tonic-gate ASSERT(strlen(str) < CPC_MAX_CPUREF); 3310Sstevel@tonic-gate } 3320Sstevel@tonic-gate 3330Sstevel@tonic-gate if (copyout(str, udata1, strlen(str) + 1) != 0) 3340Sstevel@tonic-gate return (set_errno(EFAULT)); 3350Sstevel@tonic-gate return (0); 3360Sstevel@tonic-gate case CPC_INVALIDATE: 3370Sstevel@tonic-gate kcpc_invalidate(t); 3380Sstevel@tonic-gate return (0); 3390Sstevel@tonic-gate case CPC_RELE: 3400Sstevel@tonic-gate if ((error = kcpc_unbind(t->t_cpc_set)) != 0) 3410Sstevel@tonic-gate return (set_errno(error)); 3420Sstevel@tonic-gate return (0); 3430Sstevel@tonic-gate default: 3440Sstevel@tonic-gate return (set_errno(EINVAL)); 3450Sstevel@tonic-gate } 3460Sstevel@tonic-gate } 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate /* 3490Sstevel@tonic-gate * The 'shared' device allows direct access to the 3500Sstevel@tonic-gate * performance counter control register of the current CPU. 3510Sstevel@tonic-gate * The major difference between the contexts created here and those 3520Sstevel@tonic-gate * above is that the context handlers are -not- installed, thus 3530Sstevel@tonic-gate * no context switching behaviour occurs. 3540Sstevel@tonic-gate * 3550Sstevel@tonic-gate * Because they manipulate per-cpu state, these ioctls can 3560Sstevel@tonic-gate * only be invoked from a bound lwp, by a caller with the cpc_cpu privilege 3570Sstevel@tonic-gate * who can open the relevant entry in /devices (the act of holding it open 3580Sstevel@tonic-gate * causes other uses of the counters to be suspended). 3590Sstevel@tonic-gate * 3600Sstevel@tonic-gate * Note that for correct results, the caller -must- ensure that 3610Sstevel@tonic-gate * all existing per-lwp contexts are either inactive or marked invalid; 3620Sstevel@tonic-gate * that's what the open routine does. 3630Sstevel@tonic-gate */ 3640Sstevel@tonic-gate /*ARGSUSED*/ 3650Sstevel@tonic-gate static int 3660Sstevel@tonic-gate kcpc_ioctl(dev_t dev, int cmd, intptr_t data, int flags, cred_t *cr, int *rvp) 3670Sstevel@tonic-gate { 3680Sstevel@tonic-gate kthread_t *t = curthread; 3690Sstevel@tonic-gate processorid_t cpuid; 3700Sstevel@tonic-gate void *udata1 = NULL; 3710Sstevel@tonic-gate void *udata2 = NULL; 3720Sstevel@tonic-gate void *udata3 = NULL; 3730Sstevel@tonic-gate int error; 3740Sstevel@tonic-gate int code; 3750Sstevel@tonic-gate 3760Sstevel@tonic-gate STRUCT_DECL(__cpc_args, args); 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate STRUCT_INIT(args, flags); 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate if (curthread->t_bind_cpu != getminor(dev)) 3810Sstevel@tonic-gate return (EAGAIN); /* someone unbound it? */ 3820Sstevel@tonic-gate 3830Sstevel@tonic-gate cpuid = getminor(dev); 3840Sstevel@tonic-gate 3850Sstevel@tonic-gate if (cmd == CPCIO_BIND || cmd == CPCIO_SAMPLE) { 3860Sstevel@tonic-gate if (copyin((void *)data, STRUCT_BUF(args), 3870Sstevel@tonic-gate STRUCT_SIZE(args)) == -1) 3880Sstevel@tonic-gate return (EFAULT); 3890Sstevel@tonic-gate 3900Sstevel@tonic-gate udata1 = STRUCT_FGETP(args, udata1); 3910Sstevel@tonic-gate udata2 = STRUCT_FGETP(args, udata2); 3920Sstevel@tonic-gate udata3 = STRUCT_FGETP(args, udata3); 3930Sstevel@tonic-gate } 3940Sstevel@tonic-gate 3950Sstevel@tonic-gate switch (cmd) { 3960Sstevel@tonic-gate case CPCIO_BIND: 3970Sstevel@tonic-gate /* 3980Sstevel@tonic-gate * udata1 = pointer to packed nvlist buffer 3990Sstevel@tonic-gate * udata2 = size of packed nvlist buffer 4000Sstevel@tonic-gate * udata3 = User addr to return error subcode in. 4010Sstevel@tonic-gate */ 4020Sstevel@tonic-gate if (t->t_cpc_set != NULL) { 4030Sstevel@tonic-gate (void) kcpc_unbind(t->t_cpc_set); 4040Sstevel@tonic-gate ASSERT(t->t_cpc_set == NULL); 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1, 4080Sstevel@tonic-gate (size_t)udata2)) != 0) { 4090Sstevel@tonic-gate return (error); 4100Sstevel@tonic-gate } 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) { 4130Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 4140Sstevel@tonic-gate t->t_cpc_set = NULL; 4150Sstevel@tonic-gate if (copyout(&error, udata3, sizeof (error)) == -1) 4160Sstevel@tonic-gate return (EFAULT); 4170Sstevel@tonic-gate return (EINVAL); 4180Sstevel@tonic-gate } 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate if ((error = kcpc_bind_cpu(t->t_cpc_set, cpuid, &code)) != 0) { 4210Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 4220Sstevel@tonic-gate t->t_cpc_set = NULL; 4230Sstevel@tonic-gate /* 4240Sstevel@tonic-gate * Subcodes are only returned for EINVAL and EACCESS. 4250Sstevel@tonic-gate */ 4260Sstevel@tonic-gate if ((error == EINVAL || error == EACCES) && 4270Sstevel@tonic-gate copyout(&code, udata3, sizeof (code)) == -1) 4280Sstevel@tonic-gate return (EFAULT); 4290Sstevel@tonic-gate return (error); 4300Sstevel@tonic-gate } 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate return (0); 4330Sstevel@tonic-gate case CPCIO_SAMPLE: 4340Sstevel@tonic-gate /* 4350Sstevel@tonic-gate * udata1 = pointer to user's buffer 4360Sstevel@tonic-gate * udata2 = pointer to user's hrtime 4370Sstevel@tonic-gate * udata3 = pointer to user's tick 4380Sstevel@tonic-gate */ 4390Sstevel@tonic-gate /* 4400Sstevel@tonic-gate * Only CPU-bound sets may be sampled via the ioctl(). If this 4410Sstevel@tonic-gate * set has no CPU-bound context, return an error. 4420Sstevel@tonic-gate */ 4430Sstevel@tonic-gate if (t->t_cpc_set == NULL) 4440Sstevel@tonic-gate return (EINVAL); 4450Sstevel@tonic-gate if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2, 4460Sstevel@tonic-gate udata3)) != 0) 4470Sstevel@tonic-gate return (error); 4480Sstevel@tonic-gate return (0); 4490Sstevel@tonic-gate case CPCIO_RELE: 4500Sstevel@tonic-gate if (t->t_cpc_set == NULL) 4510Sstevel@tonic-gate return (EINVAL); 4520Sstevel@tonic-gate return (kcpc_unbind(t->t_cpc_set)); 4530Sstevel@tonic-gate default: 4540Sstevel@tonic-gate return (EINVAL); 4550Sstevel@tonic-gate } 4560Sstevel@tonic-gate } 4570Sstevel@tonic-gate 4580Sstevel@tonic-gate /* 4590Sstevel@tonic-gate * The device supports multiple opens, but only one open 4600Sstevel@tonic-gate * is allowed per processor. This is to enable multiple 4610Sstevel@tonic-gate * instances of tools looking at different processors. 4620Sstevel@tonic-gate */ 4630Sstevel@tonic-gate #define KCPC_MINOR_SHARED ((minor_t)0x3fffful) 4640Sstevel@tonic-gate 4650Sstevel@tonic-gate static ulong_t *kcpc_cpumap; /* bitmap of cpus */ 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate /*ARGSUSED1*/ 4680Sstevel@tonic-gate static int 4690Sstevel@tonic-gate kcpc_open(dev_t *dev, int flags, int otyp, cred_t *cr) 4700Sstevel@tonic-gate { 4710Sstevel@tonic-gate processorid_t cpuid; 4720Sstevel@tonic-gate int error; 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate ASSERT(pcbe_ops != NULL); 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate if ((error = secpolicy_cpc_cpu(cr)) != 0) 4770Sstevel@tonic-gate return (error); 4780Sstevel@tonic-gate if (getminor(*dev) != KCPC_MINOR_SHARED) 4790Sstevel@tonic-gate return (ENXIO); 4800Sstevel@tonic-gate if ((cpuid = curthread->t_bind_cpu) == PBIND_NONE) 4810Sstevel@tonic-gate return (EINVAL); 4820Sstevel@tonic-gate if (cpuid > max_cpuid) 4830Sstevel@tonic-gate return (EINVAL); 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_WRITER); 4860Sstevel@tonic-gate if (++kcpc_cpuctx == 1) { 4870Sstevel@tonic-gate ASSERT(kcpc_cpumap == NULL); 488*8803SJonathan.Haslam@Sun.COM 489*8803SJonathan.Haslam@Sun.COM /* 490*8803SJonathan.Haslam@Sun.COM * Bail out if DTrace is already using the counters. 491*8803SJonathan.Haslam@Sun.COM */ 492*8803SJonathan.Haslam@Sun.COM if (dtrace_cpc_in_use) { 493*8803SJonathan.Haslam@Sun.COM kcpc_cpuctx--; 494*8803SJonathan.Haslam@Sun.COM rw_exit(&kcpc_cpuctx_lock); 495*8803SJonathan.Haslam@Sun.COM return (EAGAIN); 496*8803SJonathan.Haslam@Sun.COM } 4970Sstevel@tonic-gate kcpc_cpumap = kmem_zalloc(BT_SIZEOFMAP(max_cpuid + 1), 4980Sstevel@tonic-gate KM_SLEEP); 4990Sstevel@tonic-gate /* 5000Sstevel@tonic-gate * When this device is open for processor-based contexts, 5010Sstevel@tonic-gate * no further lwp-based contexts can be created. 5020Sstevel@tonic-gate * 5030Sstevel@tonic-gate * Since this is the first open, ensure that all existing 5040Sstevel@tonic-gate * contexts are invalidated. 5050Sstevel@tonic-gate */ 5060Sstevel@tonic-gate kcpc_invalidate_all(); 5070Sstevel@tonic-gate } else if (BT_TEST(kcpc_cpumap, cpuid)) { 5080Sstevel@tonic-gate kcpc_cpuctx--; 5090Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5100Sstevel@tonic-gate return (EAGAIN); 5110Sstevel@tonic-gate } else if (kcpc_hw_cpu_hook(cpuid, kcpc_cpumap) != 0) { 5120Sstevel@tonic-gate kcpc_cpuctx--; 5130Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5140Sstevel@tonic-gate return (EACCES); 5150Sstevel@tonic-gate } 5160Sstevel@tonic-gate BT_SET(kcpc_cpumap, cpuid); 5170Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5180Sstevel@tonic-gate 5190Sstevel@tonic-gate *dev = makedevice(getmajor(*dev), (minor_t)cpuid); 5200Sstevel@tonic-gate 5210Sstevel@tonic-gate return (0); 5220Sstevel@tonic-gate } 5230Sstevel@tonic-gate 5240Sstevel@tonic-gate /*ARGSUSED1*/ 5250Sstevel@tonic-gate static int 5260Sstevel@tonic-gate kcpc_close(dev_t dev, int flags, int otyp, cred_t *cr) 5270Sstevel@tonic-gate { 5280Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_WRITER); 5290Sstevel@tonic-gate BT_CLEAR(kcpc_cpumap, getminor(dev)); 5300Sstevel@tonic-gate if (--kcpc_cpuctx == 0) { 5310Sstevel@tonic-gate kmem_free(kcpc_cpumap, BT_SIZEOFMAP(max_cpuid + 1)); 5320Sstevel@tonic-gate kcpc_cpumap = NULL; 5330Sstevel@tonic-gate } 5340Sstevel@tonic-gate ASSERT(kcpc_cpuctx >= 0); 5350Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5360Sstevel@tonic-gate 5370Sstevel@tonic-gate return (0); 5380Sstevel@tonic-gate } 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate /* 5410Sstevel@tonic-gate * Sane boundaries on the size of packed lists. In bytes. 5420Sstevel@tonic-gate */ 5430Sstevel@tonic-gate #define CPC_MIN_PACKSIZE 4 5440Sstevel@tonic-gate #define CPC_MAX_PACKSIZE 10000 5450Sstevel@tonic-gate 5460Sstevel@tonic-gate /* 5470Sstevel@tonic-gate * Sane boundary on the number of requests a set can contain. 5480Sstevel@tonic-gate */ 5490Sstevel@tonic-gate #define CPC_MAX_NREQS 100 5500Sstevel@tonic-gate 5510Sstevel@tonic-gate /* 5520Sstevel@tonic-gate * Sane boundary on the number of attributes a request can contain. 5530Sstevel@tonic-gate */ 5540Sstevel@tonic-gate #define CPC_MAX_ATTRS 50 5550Sstevel@tonic-gate 5560Sstevel@tonic-gate /* 5570Sstevel@tonic-gate * Copy in a packed nvlist from the user and create a request set out of it. 5580Sstevel@tonic-gate * If successful, return 0 and store a pointer to the set we've created. Returns 5590Sstevel@tonic-gate * error code on error. 5600Sstevel@tonic-gate */ 5610Sstevel@tonic-gate int 5620Sstevel@tonic-gate kcpc_copyin_set(kcpc_set_t **inset, void *ubuf, size_t len) 5630Sstevel@tonic-gate { 5640Sstevel@tonic-gate kcpc_set_t *set; 5650Sstevel@tonic-gate int i; 5660Sstevel@tonic-gate int j; 5670Sstevel@tonic-gate char *packbuf; 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate nvlist_t *nvl; 5700Sstevel@tonic-gate nvpair_t *nvp = NULL; 5710Sstevel@tonic-gate 5720Sstevel@tonic-gate nvlist_t *attrs; 5730Sstevel@tonic-gate nvpair_t *nvp_attr; 5740Sstevel@tonic-gate kcpc_attr_t *attrp; 5750Sstevel@tonic-gate 5760Sstevel@tonic-gate nvlist_t **reqlist; 5770Sstevel@tonic-gate uint_t nreqs; 5780Sstevel@tonic-gate uint64_t uint64; 5790Sstevel@tonic-gate uint32_t uint32; 5800Sstevel@tonic-gate uint32_t setflags = (uint32_t)-1; 5810Sstevel@tonic-gate char *string; 5820Sstevel@tonic-gate char *name; 5830Sstevel@tonic-gate 5840Sstevel@tonic-gate if (len < CPC_MIN_PACKSIZE || len > CPC_MAX_PACKSIZE) 5850Sstevel@tonic-gate return (EINVAL); 5860Sstevel@tonic-gate 5870Sstevel@tonic-gate packbuf = kmem_alloc(len, KM_SLEEP); 5880Sstevel@tonic-gate 5890Sstevel@tonic-gate if (copyin(ubuf, packbuf, len) == -1) { 5900Sstevel@tonic-gate kmem_free(packbuf, len); 5910Sstevel@tonic-gate return (EFAULT); 5920Sstevel@tonic-gate } 5930Sstevel@tonic-gate 5940Sstevel@tonic-gate if (nvlist_unpack(packbuf, len, &nvl, KM_SLEEP) != 0) { 5950Sstevel@tonic-gate kmem_free(packbuf, len); 5960Sstevel@tonic-gate return (EINVAL); 5970Sstevel@tonic-gate } 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate /* 6000Sstevel@tonic-gate * The nvlist has been unpacked so there is no need for the packed 6010Sstevel@tonic-gate * representation from this point on. 6020Sstevel@tonic-gate */ 6030Sstevel@tonic-gate kmem_free(packbuf, len); 6040Sstevel@tonic-gate 6050Sstevel@tonic-gate i = 0; 6060Sstevel@tonic-gate while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 6070Sstevel@tonic-gate switch (nvpair_type(nvp)) { 6080Sstevel@tonic-gate case DATA_TYPE_UINT32: 6090Sstevel@tonic-gate if (strcmp(nvpair_name(nvp), "flags") != 0 || 6100Sstevel@tonic-gate nvpair_value_uint32(nvp, &setflags) != 0) { 6110Sstevel@tonic-gate nvlist_free(nvl); 6120Sstevel@tonic-gate return (EINVAL); 6130Sstevel@tonic-gate } 6140Sstevel@tonic-gate break; 6150Sstevel@tonic-gate case DATA_TYPE_NVLIST_ARRAY: 6160Sstevel@tonic-gate if (strcmp(nvpair_name(nvp), "reqs") != 0 || 6170Sstevel@tonic-gate nvpair_value_nvlist_array(nvp, &reqlist, 6187240Srh87107 &nreqs) != 0) { 6190Sstevel@tonic-gate nvlist_free(nvl); 6200Sstevel@tonic-gate return (EINVAL); 6210Sstevel@tonic-gate } 6220Sstevel@tonic-gate break; 6230Sstevel@tonic-gate default: 6240Sstevel@tonic-gate nvlist_free(nvl); 6250Sstevel@tonic-gate return (EINVAL); 6260Sstevel@tonic-gate } 6270Sstevel@tonic-gate i++; 6280Sstevel@tonic-gate } 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate /* 6310Sstevel@tonic-gate * There should be two members in the top-level nvlist: 6320Sstevel@tonic-gate * an array of nvlists consisting of the requests, and flags. 6330Sstevel@tonic-gate * Anything else is an invalid set. 6340Sstevel@tonic-gate */ 6350Sstevel@tonic-gate if (i != 2) { 6360Sstevel@tonic-gate nvlist_free(nvl); 6370Sstevel@tonic-gate return (EINVAL); 6380Sstevel@tonic-gate } 6390Sstevel@tonic-gate 6400Sstevel@tonic-gate if (nreqs > CPC_MAX_NREQS) { 6410Sstevel@tonic-gate nvlist_free(nvl); 6420Sstevel@tonic-gate return (EINVAL); 6430Sstevel@tonic-gate } 6440Sstevel@tonic-gate 6450Sstevel@tonic-gate /* 6460Sstevel@tonic-gate * The requests are now stored in the nvlist array at reqlist. 6476275Strevtom * Note that the use of kmem_zalloc() to alloc the kcpc_set_t means 6486275Strevtom * we don't need to call the init routines for ks_lock and ks_condv. 6490Sstevel@tonic-gate */ 6506275Strevtom set = kmem_zalloc(sizeof (kcpc_set_t), KM_SLEEP); 6510Sstevel@tonic-gate set->ks_req = (kcpc_request_t *)kmem_zalloc(sizeof (kcpc_request_t) * 6520Sstevel@tonic-gate nreqs, KM_SLEEP); 6530Sstevel@tonic-gate set->ks_nreqs = nreqs; 6540Sstevel@tonic-gate /* 6550Sstevel@tonic-gate * If the nvlist didn't contain a flags member, setflags was initialized 6560Sstevel@tonic-gate * with an illegal value and this set will fail sanity checks later on. 6570Sstevel@tonic-gate */ 6580Sstevel@tonic-gate set->ks_flags = setflags; 6596275Strevtom /* 6606275Strevtom * Initialize bind/unbind set synchronization. 6616275Strevtom */ 6626275Strevtom set->ks_state &= ~KCPC_SET_BOUND; 6630Sstevel@tonic-gate 6640Sstevel@tonic-gate /* 6650Sstevel@tonic-gate * Build the set up one request at a time, always keeping it self- 6660Sstevel@tonic-gate * consistent so we can give it to kcpc_free_set() if we need to back 6670Sstevel@tonic-gate * out and return and error. 6680Sstevel@tonic-gate */ 6690Sstevel@tonic-gate for (i = 0; i < nreqs; i++) { 6700Sstevel@tonic-gate nvp = NULL; 6710Sstevel@tonic-gate set->ks_req[i].kr_picnum = -1; 6720Sstevel@tonic-gate while ((nvp = nvlist_next_nvpair(reqlist[i], nvp)) != NULL) { 6730Sstevel@tonic-gate name = nvpair_name(nvp); 6740Sstevel@tonic-gate switch (nvpair_type(nvp)) { 6750Sstevel@tonic-gate case DATA_TYPE_UINT32: 6760Sstevel@tonic-gate if (nvpair_value_uint32(nvp, &uint32) == EINVAL) 6770Sstevel@tonic-gate goto inval; 6780Sstevel@tonic-gate if (strcmp(name, "cr_flags") == 0) 6790Sstevel@tonic-gate set->ks_req[i].kr_flags = uint32; 6800Sstevel@tonic-gate if (strcmp(name, "cr_index") == 0) 6810Sstevel@tonic-gate set->ks_req[i].kr_index = uint32; 6820Sstevel@tonic-gate break; 6830Sstevel@tonic-gate case DATA_TYPE_UINT64: 6840Sstevel@tonic-gate if (nvpair_value_uint64(nvp, &uint64) == EINVAL) 6850Sstevel@tonic-gate goto inval; 6860Sstevel@tonic-gate if (strcmp(name, "cr_preset") == 0) 6870Sstevel@tonic-gate set->ks_req[i].kr_preset = uint64; 6880Sstevel@tonic-gate break; 6890Sstevel@tonic-gate case DATA_TYPE_STRING: 6900Sstevel@tonic-gate if (nvpair_value_string(nvp, &string) == EINVAL) 6910Sstevel@tonic-gate goto inval; 6920Sstevel@tonic-gate if (strcmp(name, "cr_event") == 0) 6930Sstevel@tonic-gate (void) strncpy(set->ks_req[i].kr_event, 6940Sstevel@tonic-gate string, CPC_MAX_EVENT_LEN); 6950Sstevel@tonic-gate break; 6960Sstevel@tonic-gate case DATA_TYPE_NVLIST: 6970Sstevel@tonic-gate if (strcmp(name, "cr_attr") != 0) 6980Sstevel@tonic-gate goto inval; 6990Sstevel@tonic-gate if (nvpair_value_nvlist(nvp, &attrs) == EINVAL) 7000Sstevel@tonic-gate goto inval; 7010Sstevel@tonic-gate nvp_attr = NULL; 7020Sstevel@tonic-gate /* 7030Sstevel@tonic-gate * If the picnum has been specified as an 7040Sstevel@tonic-gate * attribute, consume that attribute here and 7050Sstevel@tonic-gate * remove it from the list of attributes. 7060Sstevel@tonic-gate */ 7070Sstevel@tonic-gate if (nvlist_lookup_uint64(attrs, "picnum", 7080Sstevel@tonic-gate &uint64) == 0) { 7090Sstevel@tonic-gate if (nvlist_remove(attrs, "picnum", 7100Sstevel@tonic-gate DATA_TYPE_UINT64) != 0) 7110Sstevel@tonic-gate panic("nvlist %p faulty", 7127240Srh87107 (void *)attrs); 7130Sstevel@tonic-gate set->ks_req[i].kr_picnum = uint64; 7140Sstevel@tonic-gate } 7150Sstevel@tonic-gate 7160Sstevel@tonic-gate if ((set->ks_req[i].kr_nattrs = 7170Sstevel@tonic-gate kcpc_nvlist_npairs(attrs)) == 0) 7180Sstevel@tonic-gate break; 7190Sstevel@tonic-gate 7200Sstevel@tonic-gate if (set->ks_req[i].kr_nattrs > CPC_MAX_ATTRS) 7210Sstevel@tonic-gate goto inval; 7220Sstevel@tonic-gate 7230Sstevel@tonic-gate set->ks_req[i].kr_attr = 7240Sstevel@tonic-gate kmem_alloc(set->ks_req[i].kr_nattrs * 7250Sstevel@tonic-gate sizeof (kcpc_attr_t), KM_SLEEP); 7260Sstevel@tonic-gate j = 0; 7270Sstevel@tonic-gate 7280Sstevel@tonic-gate while ((nvp_attr = nvlist_next_nvpair(attrs, 7290Sstevel@tonic-gate nvp_attr)) != NULL) { 7300Sstevel@tonic-gate attrp = &set->ks_req[i].kr_attr[j]; 7310Sstevel@tonic-gate 7320Sstevel@tonic-gate if (nvpair_type(nvp_attr) != 7330Sstevel@tonic-gate DATA_TYPE_UINT64) 7340Sstevel@tonic-gate goto inval; 7350Sstevel@tonic-gate 7360Sstevel@tonic-gate (void) strncpy(attrp->ka_name, 7370Sstevel@tonic-gate nvpair_name(nvp_attr), 7380Sstevel@tonic-gate CPC_MAX_ATTR_LEN); 7390Sstevel@tonic-gate 7400Sstevel@tonic-gate if (nvpair_value_uint64(nvp_attr, 7410Sstevel@tonic-gate &(attrp->ka_val)) == EINVAL) 7420Sstevel@tonic-gate goto inval; 7430Sstevel@tonic-gate j++; 7440Sstevel@tonic-gate } 7450Sstevel@tonic-gate ASSERT(j == set->ks_req[i].kr_nattrs); 7460Sstevel@tonic-gate default: 7470Sstevel@tonic-gate break; 7480Sstevel@tonic-gate } 7490Sstevel@tonic-gate } 7500Sstevel@tonic-gate } 7510Sstevel@tonic-gate 7520Sstevel@tonic-gate nvlist_free(nvl); 7530Sstevel@tonic-gate *inset = set; 7540Sstevel@tonic-gate return (0); 7550Sstevel@tonic-gate 7560Sstevel@tonic-gate inval: 7570Sstevel@tonic-gate nvlist_free(nvl); 7580Sstevel@tonic-gate kcpc_free_set(set); 7590Sstevel@tonic-gate return (EINVAL); 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate /* 7630Sstevel@tonic-gate * Count the number of nvpairs in the supplied nvlist. 7640Sstevel@tonic-gate */ 7650Sstevel@tonic-gate static uint32_t 7660Sstevel@tonic-gate kcpc_nvlist_npairs(nvlist_t *list) 7670Sstevel@tonic-gate { 7680Sstevel@tonic-gate nvpair_t *nvp = NULL; 7690Sstevel@tonic-gate uint32_t n = 0; 7700Sstevel@tonic-gate 7710Sstevel@tonic-gate while ((nvp = nvlist_next_nvpair(list, nvp)) != NULL) 7720Sstevel@tonic-gate n++; 7730Sstevel@tonic-gate 7740Sstevel@tonic-gate return (n); 7750Sstevel@tonic-gate } 7760Sstevel@tonic-gate 7770Sstevel@tonic-gate /* 7780Sstevel@tonic-gate * Performs sanity checks on the given set. 7790Sstevel@tonic-gate * Returns 0 if the set checks out OK. 7800Sstevel@tonic-gate * Returns a detailed error subcode, or -1 if there is no applicable subcode. 7810Sstevel@tonic-gate */ 7820Sstevel@tonic-gate static int 7830Sstevel@tonic-gate kcpc_verify_set(kcpc_set_t *set) 7840Sstevel@tonic-gate { 7850Sstevel@tonic-gate kcpc_request_t *rp; 7860Sstevel@tonic-gate int i; 7870Sstevel@tonic-gate uint64_t bitmap = 0; 7880Sstevel@tonic-gate int n; 7890Sstevel@tonic-gate 7900Sstevel@tonic-gate if (set->ks_nreqs > cpc_ncounters) 7910Sstevel@tonic-gate return (-1); 7920Sstevel@tonic-gate 7930Sstevel@tonic-gate if (CPC_SET_VALID_FLAGS(set->ks_flags) == 0) 7940Sstevel@tonic-gate return (-1); 7950Sstevel@tonic-gate 7960Sstevel@tonic-gate for (i = 0; i < set->ks_nreqs; i++) { 7970Sstevel@tonic-gate rp = &set->ks_req[i]; 7980Sstevel@tonic-gate 7990Sstevel@tonic-gate /* 8000Sstevel@tonic-gate * The following comparison must cast cpc_ncounters to an int, 8010Sstevel@tonic-gate * because kr_picnum will be -1 if the request didn't explicitly 8020Sstevel@tonic-gate * choose a PIC. 8030Sstevel@tonic-gate */ 8040Sstevel@tonic-gate if (rp->kr_picnum >= (int)cpc_ncounters) 8050Sstevel@tonic-gate return (CPC_INVALID_PICNUM); 8060Sstevel@tonic-gate 8070Sstevel@tonic-gate /* 8080Sstevel@tonic-gate * Of the pics whose physical picnum has been specified, make 8090Sstevel@tonic-gate * sure each PIC appears only once in set. 8100Sstevel@tonic-gate */ 8110Sstevel@tonic-gate if ((n = set->ks_req[i].kr_picnum) != -1) { 8120Sstevel@tonic-gate if ((bitmap & (1 << n)) != 0) 8130Sstevel@tonic-gate return (-1); 8140Sstevel@tonic-gate bitmap |= (1 << n); 8150Sstevel@tonic-gate } 8160Sstevel@tonic-gate 8170Sstevel@tonic-gate /* 8180Sstevel@tonic-gate * Make sure the requested index falls within the range of all 8190Sstevel@tonic-gate * requests. 8200Sstevel@tonic-gate */ 8210Sstevel@tonic-gate if (rp->kr_index < 0 || rp->kr_index >= set->ks_nreqs) 8220Sstevel@tonic-gate return (-1); 8230Sstevel@tonic-gate 8240Sstevel@tonic-gate /* 8250Sstevel@tonic-gate * Make sure there are no unknown flags. 8260Sstevel@tonic-gate */ 8270Sstevel@tonic-gate if (KCPC_REQ_VALID_FLAGS(rp->kr_flags) == 0) 8280Sstevel@tonic-gate return (CPC_REQ_INVALID_FLAGS); 8290Sstevel@tonic-gate } 8300Sstevel@tonic-gate 8310Sstevel@tonic-gate return (0); 8320Sstevel@tonic-gate } 8330Sstevel@tonic-gate 8340Sstevel@tonic-gate static struct cb_ops cb_ops = { 8350Sstevel@tonic-gate kcpc_open, 8360Sstevel@tonic-gate kcpc_close, 8370Sstevel@tonic-gate nodev, /* strategy */ 8380Sstevel@tonic-gate nodev, /* print */ 8390Sstevel@tonic-gate nodev, /* dump */ 8400Sstevel@tonic-gate nodev, /* read */ 8410Sstevel@tonic-gate nodev, /* write */ 8420Sstevel@tonic-gate kcpc_ioctl, 8430Sstevel@tonic-gate nodev, /* devmap */ 8440Sstevel@tonic-gate nodev, /* mmap */ 8450Sstevel@tonic-gate nodev, /* segmap */ 8460Sstevel@tonic-gate nochpoll, /* poll */ 8470Sstevel@tonic-gate ddi_prop_op, 8480Sstevel@tonic-gate NULL, 8490Sstevel@tonic-gate D_NEW | D_MP 8500Sstevel@tonic-gate }; 8510Sstevel@tonic-gate 8520Sstevel@tonic-gate /*ARGSUSED*/ 8530Sstevel@tonic-gate static int 8540Sstevel@tonic-gate kcpc_probe(dev_info_t *devi) 8550Sstevel@tonic-gate { 8560Sstevel@tonic-gate return (DDI_PROBE_SUCCESS); 8570Sstevel@tonic-gate } 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate static dev_info_t *kcpc_devi; 8600Sstevel@tonic-gate 8610Sstevel@tonic-gate static int 8620Sstevel@tonic-gate kcpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 8630Sstevel@tonic-gate { 8640Sstevel@tonic-gate if (cmd != DDI_ATTACH) 8650Sstevel@tonic-gate return (DDI_FAILURE); 8660Sstevel@tonic-gate kcpc_devi = devi; 8670Sstevel@tonic-gate return (ddi_create_minor_node(devi, "shared", S_IFCHR, 8680Sstevel@tonic-gate KCPC_MINOR_SHARED, DDI_PSEUDO, 0)); 8690Sstevel@tonic-gate } 8700Sstevel@tonic-gate 8710Sstevel@tonic-gate /*ARGSUSED*/ 8720Sstevel@tonic-gate static int 8730Sstevel@tonic-gate kcpc_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result) 8740Sstevel@tonic-gate { 8750Sstevel@tonic-gate switch (cmd) { 8760Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 8770Sstevel@tonic-gate switch (getminor((dev_t)arg)) { 8780Sstevel@tonic-gate case KCPC_MINOR_SHARED: 8790Sstevel@tonic-gate *result = kcpc_devi; 8800Sstevel@tonic-gate return (DDI_SUCCESS); 8810Sstevel@tonic-gate default: 8820Sstevel@tonic-gate break; 8830Sstevel@tonic-gate } 8840Sstevel@tonic-gate break; 8850Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 8860Sstevel@tonic-gate *result = 0; 8870Sstevel@tonic-gate return (DDI_SUCCESS); 8880Sstevel@tonic-gate default: 8890Sstevel@tonic-gate break; 8900Sstevel@tonic-gate } 8910Sstevel@tonic-gate 8920Sstevel@tonic-gate return (DDI_FAILURE); 8930Sstevel@tonic-gate } 8940Sstevel@tonic-gate 8950Sstevel@tonic-gate static struct dev_ops dev_ops = { 8960Sstevel@tonic-gate DEVO_REV, 8970Sstevel@tonic-gate 0, 8980Sstevel@tonic-gate kcpc_getinfo, 8990Sstevel@tonic-gate nulldev, /* identify */ 9000Sstevel@tonic-gate kcpc_probe, 9010Sstevel@tonic-gate kcpc_attach, 9020Sstevel@tonic-gate nodev, /* detach */ 9030Sstevel@tonic-gate nodev, /* reset */ 9040Sstevel@tonic-gate &cb_ops, 9057656SSherry.Moore@Sun.COM (struct bus_ops *)0, 9067656SSherry.Moore@Sun.COM NULL, 9077656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */ 9080Sstevel@tonic-gate }; 9090Sstevel@tonic-gate 9100Sstevel@tonic-gate static struct modldrv modldrv = { 9110Sstevel@tonic-gate &mod_driverops, 9127240Srh87107 "cpc sampling driver", 9130Sstevel@tonic-gate &dev_ops 9140Sstevel@tonic-gate }; 9150Sstevel@tonic-gate 9160Sstevel@tonic-gate static struct sysent cpc_sysent = { 9170Sstevel@tonic-gate 5, 9180Sstevel@tonic-gate SE_NOUNLOAD | SE_ARGC | SE_32RVAL1, 9190Sstevel@tonic-gate cpc 9200Sstevel@tonic-gate }; 9210Sstevel@tonic-gate 9220Sstevel@tonic-gate static struct modlsys modlsys = { 9230Sstevel@tonic-gate &mod_syscallops, 9240Sstevel@tonic-gate "cpc sampling system call", 9250Sstevel@tonic-gate &cpc_sysent 9260Sstevel@tonic-gate }; 9270Sstevel@tonic-gate 9280Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 9290Sstevel@tonic-gate static struct modlsys modlsys32 = { 9300Sstevel@tonic-gate &mod_syscallops32, 9310Sstevel@tonic-gate "32-bit cpc sampling system call", 9320Sstevel@tonic-gate &cpc_sysent 9330Sstevel@tonic-gate }; 9340Sstevel@tonic-gate #endif 9350Sstevel@tonic-gate 9360Sstevel@tonic-gate static struct modlinkage modl = { 9370Sstevel@tonic-gate MODREV_1, 9380Sstevel@tonic-gate &modldrv, 9390Sstevel@tonic-gate &modlsys, 9400Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 9410Sstevel@tonic-gate &modlsys32, 9420Sstevel@tonic-gate #endif 9430Sstevel@tonic-gate }; 9440Sstevel@tonic-gate 9450Sstevel@tonic-gate static void 9460Sstevel@tonic-gate kcpc_init(void) 9470Sstevel@tonic-gate { 9480Sstevel@tonic-gate long hash; 9490Sstevel@tonic-gate 9500Sstevel@tonic-gate rw_init(&kcpc_cpuctx_lock, NULL, RW_DEFAULT, NULL); 9510Sstevel@tonic-gate for (hash = 0; hash < CPC_HASH_BUCKETS; hash++) 9520Sstevel@tonic-gate mutex_init(&kcpc_ctx_llock[hash], 9530Sstevel@tonic-gate NULL, MUTEX_DRIVER, (void *)(uintptr_t)15); 9540Sstevel@tonic-gate } 9550Sstevel@tonic-gate 9560Sstevel@tonic-gate static void 9570Sstevel@tonic-gate kcpc_fini(void) 9580Sstevel@tonic-gate { 9590Sstevel@tonic-gate long hash; 9600Sstevel@tonic-gate 9610Sstevel@tonic-gate for (hash = 0; hash < CPC_HASH_BUCKETS; hash++) 9620Sstevel@tonic-gate mutex_destroy(&kcpc_ctx_llock[hash]); 9630Sstevel@tonic-gate rw_destroy(&kcpc_cpuctx_lock); 9640Sstevel@tonic-gate } 9650Sstevel@tonic-gate 9660Sstevel@tonic-gate int 9670Sstevel@tonic-gate _init(void) 9680Sstevel@tonic-gate { 9690Sstevel@tonic-gate int ret; 9700Sstevel@tonic-gate 9710Sstevel@tonic-gate if (kcpc_hw_load_pcbe() != 0) 9720Sstevel@tonic-gate return (ENOTSUP); 9730Sstevel@tonic-gate 9740Sstevel@tonic-gate kcpc_init(); 9750Sstevel@tonic-gate if ((ret = mod_install(&modl)) != 0) 9760Sstevel@tonic-gate kcpc_fini(); 9770Sstevel@tonic-gate return (ret); 9780Sstevel@tonic-gate } 9790Sstevel@tonic-gate 9800Sstevel@tonic-gate int 9810Sstevel@tonic-gate _fini(void) 9820Sstevel@tonic-gate { 9830Sstevel@tonic-gate int ret; 9840Sstevel@tonic-gate 9850Sstevel@tonic-gate if ((ret = mod_remove(&modl)) == 0) 9860Sstevel@tonic-gate kcpc_fini(); 9870Sstevel@tonic-gate return (ret); 9880Sstevel@tonic-gate } 9890Sstevel@tonic-gate 9900Sstevel@tonic-gate int 9910Sstevel@tonic-gate _info(struct modinfo *mi) 9920Sstevel@tonic-gate { 9930Sstevel@tonic-gate return (mod_info(&modl, mi)); 9940Sstevel@tonic-gate } 995