17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 54568bee7Strevtom * Common Development and Distribution License (the "License"). 64568bee7Strevtom * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22b9e93c10SJonathan Haslam * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * CPU Performance Counter system calls and device driver. 287c478bd9Sstevel@tonic-gate * 297c478bd9Sstevel@tonic-gate * This module uses a combination of thread context operators, and 307c478bd9Sstevel@tonic-gate * thread-specific data to export CPU performance counters 317c478bd9Sstevel@tonic-gate * via both a system call and a driver interface. 327c478bd9Sstevel@tonic-gate * 337c478bd9Sstevel@tonic-gate * There are three access methods exported - the 'shared' device 347c478bd9Sstevel@tonic-gate * and the 'private' and 'agent' variants of the system call. 357c478bd9Sstevel@tonic-gate * 367c478bd9Sstevel@tonic-gate * The shared device treats the performance counter registers as 377c478bd9Sstevel@tonic-gate * a processor metric, regardless of the work scheduled on them. 387c478bd9Sstevel@tonic-gate * The private system call treats the performance counter registers 397c478bd9Sstevel@tonic-gate * as a property of a single lwp. This is achieved by using the 407c478bd9Sstevel@tonic-gate * thread context operators to virtualize the contents of the 417c478bd9Sstevel@tonic-gate * performance counter registers between lwps. 427c478bd9Sstevel@tonic-gate * 437c478bd9Sstevel@tonic-gate * The agent method is like the private method, except that it must 447c478bd9Sstevel@tonic-gate * be accessed via /proc's agent lwp to allow the counter context of 457c478bd9Sstevel@tonic-gate * other threads to be examined safely. 467c478bd9Sstevel@tonic-gate * 477c478bd9Sstevel@tonic-gate * The shared usage fundamentally conflicts with the agent and private usage; 487c478bd9Sstevel@tonic-gate * almost all of the complexity of the module is needed to allow these two 497c478bd9Sstevel@tonic-gate * models to co-exist in a reasonable way. 507c478bd9Sstevel@tonic-gate */ 517c478bd9Sstevel@tonic-gate 527c478bd9Sstevel@tonic-gate #include <sys/types.h> 537c478bd9Sstevel@tonic-gate #include <sys/file.h> 547c478bd9Sstevel@tonic-gate #include <sys/errno.h> 557c478bd9Sstevel@tonic-gate #include <sys/open.h> 567c478bd9Sstevel@tonic-gate #include <sys/cred.h> 577c478bd9Sstevel@tonic-gate #include <sys/conf.h> 587c478bd9Sstevel@tonic-gate #include <sys/stat.h> 597c478bd9Sstevel@tonic-gate #include <sys/processor.h> 607c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 617c478bd9Sstevel@tonic-gate #include <sys/disp.h> 627c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 637c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 647c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 657c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 667c478bd9Sstevel@tonic-gate #include <sys/nvpair.h> 677c478bd9Sstevel@tonic-gate #include <sys/policy.h> 687c478bd9Sstevel@tonic-gate #include <sys/machsystm.h> 697c478bd9Sstevel@tonic-gate #include <sys/cpc_impl.h> 707c478bd9Sstevel@tonic-gate #include <sys/cpc_pcbe.h> 717c478bd9Sstevel@tonic-gate #include <sys/kcpc.h> 727c478bd9Sstevel@tonic-gate 737c478bd9Sstevel@tonic-gate static int kcpc_copyin_set(kcpc_set_t **set, void *ubuf, size_t len); 747c478bd9Sstevel@tonic-gate static int kcpc_verify_set(kcpc_set_t *set); 757c478bd9Sstevel@tonic-gate static uint32_t kcpc_nvlist_npairs(nvlist_t *list); 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate /* 787c478bd9Sstevel@tonic-gate * Generic attributes supported regardless of processor. 797c478bd9Sstevel@tonic-gate */ 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate #define ATTRLIST "picnum" 827c478bd9Sstevel@tonic-gate #define SEPARATOR "," 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate /* 857c478bd9Sstevel@tonic-gate * System call to access CPU performance counters. 867c478bd9Sstevel@tonic-gate */ 877c478bd9Sstevel@tonic-gate static int 887c478bd9Sstevel@tonic-gate cpc(int cmd, id_t lwpid, void *udata1, void *udata2, void *udata3) 897c478bd9Sstevel@tonic-gate { 907c478bd9Sstevel@tonic-gate kthread_t *t; 917c478bd9Sstevel@tonic-gate int error; 927c478bd9Sstevel@tonic-gate int size; 937c478bd9Sstevel@tonic-gate const char *str; 947c478bd9Sstevel@tonic-gate int code; 957c478bd9Sstevel@tonic-gate 967c478bd9Sstevel@tonic-gate /* 977c478bd9Sstevel@tonic-gate * This CPC syscall should only be loaded if it found a PCBE to use. 987c478bd9Sstevel@tonic-gate */ 997c478bd9Sstevel@tonic-gate ASSERT(pcbe_ops != NULL); 1007c478bd9Sstevel@tonic-gate 1017c478bd9Sstevel@tonic-gate if (curproc->p_agenttp == curthread) { 1027c478bd9Sstevel@tonic-gate /* 1037c478bd9Sstevel@tonic-gate * Only if /proc is invoking this system call from 1047c478bd9Sstevel@tonic-gate * the agent thread do we allow the caller to examine 1057c478bd9Sstevel@tonic-gate * the contexts of other lwps in the process. And 1067c478bd9Sstevel@tonic-gate * because we know we're the agent, we know we don't 1077c478bd9Sstevel@tonic-gate * have to grab p_lock because no-one else can change 1087c478bd9Sstevel@tonic-gate * the state of the process. 1097c478bd9Sstevel@tonic-gate */ 1107c478bd9Sstevel@tonic-gate if ((t = idtot(curproc, lwpid)) == NULL || t == curthread) 1117c478bd9Sstevel@tonic-gate return (set_errno(ESRCH)); 1127c478bd9Sstevel@tonic-gate ASSERT(t->t_tid == lwpid && ttolwp(t) != NULL); 1137c478bd9Sstevel@tonic-gate } else 1147c478bd9Sstevel@tonic-gate t = curthread; 1157c478bd9Sstevel@tonic-gate 1167c478bd9Sstevel@tonic-gate if (t->t_cpc_set == NULL && (cmd == CPC_SAMPLE || cmd == CPC_RELE)) 1177c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate switch (cmd) { 1207c478bd9Sstevel@tonic-gate case CPC_BIND: 1217c478bd9Sstevel@tonic-gate /* 1227c478bd9Sstevel@tonic-gate * udata1 = pointer to packed nvlist buffer 1237c478bd9Sstevel@tonic-gate * udata2 = size of packed nvlist buffer 1247c478bd9Sstevel@tonic-gate * udata3 = User addr to return error subcode in. 1257c478bd9Sstevel@tonic-gate */ 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_READER); 128b9e93c10SJonathan Haslam if (kcpc_cpuctx || dtrace_cpc_in_use) { 1297c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1307c478bd9Sstevel@tonic-gate return (set_errno(EAGAIN)); 1317c478bd9Sstevel@tonic-gate } 1327c478bd9Sstevel@tonic-gate 1337c478bd9Sstevel@tonic-gate if (kcpc_hw_lwp_hook() != 0) { 1347c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1357c478bd9Sstevel@tonic-gate return (set_errno(EACCES)); 1367c478bd9Sstevel@tonic-gate } 1377c478bd9Sstevel@tonic-gate 1387c478bd9Sstevel@tonic-gate /* 1397c478bd9Sstevel@tonic-gate * An LWP may only have one set bound to it at a time; if there 1407c478bd9Sstevel@tonic-gate * is a set bound to this LWP already, we unbind it here. 1417c478bd9Sstevel@tonic-gate */ 1427c478bd9Sstevel@tonic-gate if (t->t_cpc_set != NULL) 1437c478bd9Sstevel@tonic-gate (void) kcpc_unbind(t->t_cpc_set); 1447c478bd9Sstevel@tonic-gate ASSERT(t->t_cpc_set == NULL); 1457c478bd9Sstevel@tonic-gate 1467c478bd9Sstevel@tonic-gate if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1, 1477c478bd9Sstevel@tonic-gate (size_t)udata2)) != 0) { 1487c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1497c478bd9Sstevel@tonic-gate return (set_errno(error)); 1507c478bd9Sstevel@tonic-gate } 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) { 1537c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1547c478bd9Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 1557c478bd9Sstevel@tonic-gate t->t_cpc_set = NULL; 1567c478bd9Sstevel@tonic-gate if (copyout(&error, udata3, sizeof (error)) == -1) 1577c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 1587c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 1597c478bd9Sstevel@tonic-gate } 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate if ((error = kcpc_bind_thread(t->t_cpc_set, t, &code)) != 0) { 1627c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1637c478bd9Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 1647c478bd9Sstevel@tonic-gate t->t_cpc_set = NULL; 1657c478bd9Sstevel@tonic-gate /* 1667c478bd9Sstevel@tonic-gate * EINVAL and EACCES are the only errors with more 1677c478bd9Sstevel@tonic-gate * specific subcodes. 1687c478bd9Sstevel@tonic-gate */ 1697c478bd9Sstevel@tonic-gate if ((error == EINVAL || error == EACCES) && 1707c478bd9Sstevel@tonic-gate copyout(&code, udata3, sizeof (code)) == -1) 1717c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 1727c478bd9Sstevel@tonic-gate return (set_errno(error)); 1737c478bd9Sstevel@tonic-gate } 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1767c478bd9Sstevel@tonic-gate return (0); 1777c478bd9Sstevel@tonic-gate case CPC_SAMPLE: 1787c478bd9Sstevel@tonic-gate /* 1797c478bd9Sstevel@tonic-gate * udata1 = pointer to user's buffer 1807c478bd9Sstevel@tonic-gate * udata2 = pointer to user's hrtime 1817c478bd9Sstevel@tonic-gate * udata3 = pointer to user's tick 1827c478bd9Sstevel@tonic-gate */ 1837c478bd9Sstevel@tonic-gate /* 1847c478bd9Sstevel@tonic-gate * We only allow thread-bound sets to be sampled via the 1857c478bd9Sstevel@tonic-gate * syscall, so if this set has a CPU-bound context, return an 1867c478bd9Sstevel@tonic-gate * error. 1877c478bd9Sstevel@tonic-gate */ 1887c478bd9Sstevel@tonic-gate if (t->t_cpc_set->ks_ctx->kc_cpuid != -1) 1897c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 1907c478bd9Sstevel@tonic-gate if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2, 1917c478bd9Sstevel@tonic-gate udata3)) != 0) 1927c478bd9Sstevel@tonic-gate return (set_errno(error)); 1937c478bd9Sstevel@tonic-gate 1947c478bd9Sstevel@tonic-gate return (0); 1957c478bd9Sstevel@tonic-gate case CPC_PRESET: 1967c478bd9Sstevel@tonic-gate case CPC_RESTART: 1977c478bd9Sstevel@tonic-gate /* 1987c478bd9Sstevel@tonic-gate * These are valid only if this lwp has a bound set. 1997c478bd9Sstevel@tonic-gate */ 2007c478bd9Sstevel@tonic-gate if (t->t_cpc_set == NULL) 2017c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 2027c478bd9Sstevel@tonic-gate if (cmd == CPC_PRESET) { 2037c478bd9Sstevel@tonic-gate /* 2047c478bd9Sstevel@tonic-gate * The preset is shipped up to us from userland in two 2057c478bd9Sstevel@tonic-gate * parts. This lets us handle 64-bit values from 32-bit 2067c478bd9Sstevel@tonic-gate * and 64-bit applications in the same manner. 2077c478bd9Sstevel@tonic-gate * 2087c478bd9Sstevel@tonic-gate * udata1 = index of request to preset 2097c478bd9Sstevel@tonic-gate * udata2 = new 64-bit preset (most sig. 32 bits) 2107c478bd9Sstevel@tonic-gate * udata3 = new 64-bit preset (least sig. 32 bits) 2117c478bd9Sstevel@tonic-gate */ 2127c478bd9Sstevel@tonic-gate if ((error = kcpc_preset(t->t_cpc_set, (intptr_t)udata1, 2137c478bd9Sstevel@tonic-gate ((uint64_t)(uintptr_t)udata2 << 32ULL) | 2147c478bd9Sstevel@tonic-gate (uint64_t)(uintptr_t)udata3)) != 0) 2157c478bd9Sstevel@tonic-gate return (set_errno(error)); 2167c478bd9Sstevel@tonic-gate } else { 2177c478bd9Sstevel@tonic-gate /* 2187c478bd9Sstevel@tonic-gate * udata[1-3] = unused 2197c478bd9Sstevel@tonic-gate */ 2207c478bd9Sstevel@tonic-gate if ((error = kcpc_restart(t->t_cpc_set)) != 0) 2217c478bd9Sstevel@tonic-gate return (set_errno(error)); 2227c478bd9Sstevel@tonic-gate } 2237c478bd9Sstevel@tonic-gate return (0); 2247c478bd9Sstevel@tonic-gate case CPC_ENABLE: 2257c478bd9Sstevel@tonic-gate case CPC_DISABLE: 2267c478bd9Sstevel@tonic-gate udata1 = 0; 2277c478bd9Sstevel@tonic-gate /*FALLTHROUGH*/ 2287c478bd9Sstevel@tonic-gate case CPC_USR_EVENTS: 2297c478bd9Sstevel@tonic-gate case CPC_SYS_EVENTS: 2307c478bd9Sstevel@tonic-gate if (t != curthread || t->t_cpc_set == NULL) 2317c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 2327c478bd9Sstevel@tonic-gate /* 2337c478bd9Sstevel@tonic-gate * Provided for backwards compatibility with CPCv1. 2347c478bd9Sstevel@tonic-gate * 2357c478bd9Sstevel@tonic-gate * Stop the counters and record the current counts. Use the 2367c478bd9Sstevel@tonic-gate * counts as the preset to rebind a new set with the requests 2377c478bd9Sstevel@tonic-gate * reconfigured as requested. 2387c478bd9Sstevel@tonic-gate * 2397c478bd9Sstevel@tonic-gate * udata1: 1 == enable; 0 == disable 2407c478bd9Sstevel@tonic-gate * udata{2,3}: unused 2417c478bd9Sstevel@tonic-gate */ 2427c478bd9Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_READER); 2437c478bd9Sstevel@tonic-gate if ((error = kcpc_enable(t, 2447c478bd9Sstevel@tonic-gate cmd, (int)(uintptr_t)udata1)) != 0) { 2457c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 2467c478bd9Sstevel@tonic-gate return (set_errno(error)); 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 2497c478bd9Sstevel@tonic-gate return (0); 2507c478bd9Sstevel@tonic-gate case CPC_NPIC: 2517c478bd9Sstevel@tonic-gate return (cpc_ncounters); 2527c478bd9Sstevel@tonic-gate case CPC_CAPS: 2537c478bd9Sstevel@tonic-gate return (pcbe_ops->pcbe_caps); 2547c478bd9Sstevel@tonic-gate case CPC_EVLIST_SIZE: 2557c478bd9Sstevel@tonic-gate case CPC_LIST_EVENTS: 2567c478bd9Sstevel@tonic-gate /* 2577c478bd9Sstevel@tonic-gate * udata1 = pointer to user's int or buffer 2587c478bd9Sstevel@tonic-gate * udata2 = picnum 2597c478bd9Sstevel@tonic-gate * udata3 = unused 2607c478bd9Sstevel@tonic-gate */ 2617c478bd9Sstevel@tonic-gate if ((uintptr_t)udata2 >= cpc_ncounters) 2627c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate size = strlen( 2657c478bd9Sstevel@tonic-gate pcbe_ops->pcbe_list_events((uintptr_t)udata2)) + 1; 2667c478bd9Sstevel@tonic-gate 2677c478bd9Sstevel@tonic-gate if (cmd == CPC_EVLIST_SIZE) { 2687c478bd9Sstevel@tonic-gate if (suword32(udata1, size) == -1) 2697c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 2707c478bd9Sstevel@tonic-gate } else { 2717c478bd9Sstevel@tonic-gate if (copyout( 2727c478bd9Sstevel@tonic-gate pcbe_ops->pcbe_list_events((uintptr_t)udata2), 2737c478bd9Sstevel@tonic-gate udata1, size) == -1) 2747c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 2757c478bd9Sstevel@tonic-gate } 2767c478bd9Sstevel@tonic-gate return (0); 2777c478bd9Sstevel@tonic-gate case CPC_ATTRLIST_SIZE: 2787c478bd9Sstevel@tonic-gate case CPC_LIST_ATTRS: 2797c478bd9Sstevel@tonic-gate /* 2807c478bd9Sstevel@tonic-gate * udata1 = pointer to user's int or buffer 2817c478bd9Sstevel@tonic-gate * udata2 = unused 2827c478bd9Sstevel@tonic-gate * udata3 = unused 2837c478bd9Sstevel@tonic-gate * 2847c478bd9Sstevel@tonic-gate * attrlist size is length of PCBE-supported attributes, plus 2857c478bd9Sstevel@tonic-gate * room for "picnum\0" plus an optional ',' separator char. 2867c478bd9Sstevel@tonic-gate */ 2877c478bd9Sstevel@tonic-gate str = pcbe_ops->pcbe_list_attrs(); 2887c478bd9Sstevel@tonic-gate size = strlen(str) + sizeof (SEPARATOR ATTRLIST) + 1; 2897c478bd9Sstevel@tonic-gate if (str[0] != '\0') 2907c478bd9Sstevel@tonic-gate /* 2917c478bd9Sstevel@tonic-gate * A ',' separator character is necessary. 2927c478bd9Sstevel@tonic-gate */ 2937c478bd9Sstevel@tonic-gate size += 1; 2947c478bd9Sstevel@tonic-gate 2957c478bd9Sstevel@tonic-gate if (cmd == CPC_ATTRLIST_SIZE) { 2967c478bd9Sstevel@tonic-gate if (suword32(udata1, size) == -1) 2977c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 2987c478bd9Sstevel@tonic-gate } else { 2997c478bd9Sstevel@tonic-gate /* 3007c478bd9Sstevel@tonic-gate * Copyout the PCBE attributes, and then append the 3017c478bd9Sstevel@tonic-gate * generic attribute list (with separator if necessary). 3027c478bd9Sstevel@tonic-gate */ 3037c478bd9Sstevel@tonic-gate if (copyout(str, udata1, strlen(str)) == -1) 3047c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 3057c478bd9Sstevel@tonic-gate if (str[0] != '\0') { 3067c478bd9Sstevel@tonic-gate if (copyout(SEPARATOR ATTRLIST, 3077c478bd9Sstevel@tonic-gate ((char *)udata1) + strlen(str), 3087c478bd9Sstevel@tonic-gate strlen(SEPARATOR ATTRLIST) + 1) 3097c478bd9Sstevel@tonic-gate == -1) 3107c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 3117c478bd9Sstevel@tonic-gate } else 3127c478bd9Sstevel@tonic-gate if (copyout(ATTRLIST, 3137c478bd9Sstevel@tonic-gate (char *)udata1 + strlen(str), 3147c478bd9Sstevel@tonic-gate strlen(ATTRLIST) + 1) == -1) 3157c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 3167c478bd9Sstevel@tonic-gate } 3177c478bd9Sstevel@tonic-gate return (0); 3187c478bd9Sstevel@tonic-gate case CPC_IMPL_NAME: 3197c478bd9Sstevel@tonic-gate case CPC_CPUREF: 3207c478bd9Sstevel@tonic-gate /* 3217c478bd9Sstevel@tonic-gate * udata1 = pointer to user's buffer 3227c478bd9Sstevel@tonic-gate * udata2 = unused 3237c478bd9Sstevel@tonic-gate * udata3 = unused 3247c478bd9Sstevel@tonic-gate */ 3257c478bd9Sstevel@tonic-gate if (cmd == CPC_IMPL_NAME) { 3267c478bd9Sstevel@tonic-gate str = pcbe_ops->pcbe_impl_name(); 3277c478bd9Sstevel@tonic-gate ASSERT(strlen(str) < CPC_MAX_IMPL_NAME); 3287c478bd9Sstevel@tonic-gate } else { 3297c478bd9Sstevel@tonic-gate str = pcbe_ops->pcbe_cpuref(); 3307c478bd9Sstevel@tonic-gate ASSERT(strlen(str) < CPC_MAX_CPUREF); 3317c478bd9Sstevel@tonic-gate } 3327c478bd9Sstevel@tonic-gate 3337c478bd9Sstevel@tonic-gate if (copyout(str, udata1, strlen(str) + 1) != 0) 3347c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 3357c478bd9Sstevel@tonic-gate return (0); 3367c478bd9Sstevel@tonic-gate case CPC_INVALIDATE: 3377c478bd9Sstevel@tonic-gate kcpc_invalidate(t); 3387c478bd9Sstevel@tonic-gate return (0); 3397c478bd9Sstevel@tonic-gate case CPC_RELE: 3407c478bd9Sstevel@tonic-gate if ((error = kcpc_unbind(t->t_cpc_set)) != 0) 3417c478bd9Sstevel@tonic-gate return (set_errno(error)); 3427c478bd9Sstevel@tonic-gate return (0); 3437c478bd9Sstevel@tonic-gate default: 3447c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 3457c478bd9Sstevel@tonic-gate } 3467c478bd9Sstevel@tonic-gate } 3477c478bd9Sstevel@tonic-gate 3487c478bd9Sstevel@tonic-gate /* 3497c478bd9Sstevel@tonic-gate * The 'shared' device allows direct access to the 3507c478bd9Sstevel@tonic-gate * performance counter control register of the current CPU. 3517c478bd9Sstevel@tonic-gate * The major difference between the contexts created here and those 3527c478bd9Sstevel@tonic-gate * above is that the context handlers are -not- installed, thus 3537c478bd9Sstevel@tonic-gate * no context switching behaviour occurs. 3547c478bd9Sstevel@tonic-gate * 3557c478bd9Sstevel@tonic-gate * Because they manipulate per-cpu state, these ioctls can 3567c478bd9Sstevel@tonic-gate * only be invoked from a bound lwp, by a caller with the cpc_cpu privilege 3577c478bd9Sstevel@tonic-gate * who can open the relevant entry in /devices (the act of holding it open 3587c478bd9Sstevel@tonic-gate * causes other uses of the counters to be suspended). 3597c478bd9Sstevel@tonic-gate * 3607c478bd9Sstevel@tonic-gate * Note that for correct results, the caller -must- ensure that 3617c478bd9Sstevel@tonic-gate * all existing per-lwp contexts are either inactive or marked invalid; 3627c478bd9Sstevel@tonic-gate * that's what the open routine does. 3637c478bd9Sstevel@tonic-gate */ 3647c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 3657c478bd9Sstevel@tonic-gate static int 3667c478bd9Sstevel@tonic-gate kcpc_ioctl(dev_t dev, int cmd, intptr_t data, int flags, cred_t *cr, int *rvp) 3677c478bd9Sstevel@tonic-gate { 3687c478bd9Sstevel@tonic-gate kthread_t *t = curthread; 3697c478bd9Sstevel@tonic-gate processorid_t cpuid; 3707c478bd9Sstevel@tonic-gate void *udata1 = NULL; 3717c478bd9Sstevel@tonic-gate void *udata2 = NULL; 3727c478bd9Sstevel@tonic-gate void *udata3 = NULL; 3737c478bd9Sstevel@tonic-gate int error; 3747c478bd9Sstevel@tonic-gate int code; 3757c478bd9Sstevel@tonic-gate 3767c478bd9Sstevel@tonic-gate STRUCT_DECL(__cpc_args, args); 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate STRUCT_INIT(args, flags); 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate if (curthread->t_bind_cpu != getminor(dev)) 3817c478bd9Sstevel@tonic-gate return (EAGAIN); /* someone unbound it? */ 3827c478bd9Sstevel@tonic-gate 3837c478bd9Sstevel@tonic-gate cpuid = getminor(dev); 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate if (cmd == CPCIO_BIND || cmd == CPCIO_SAMPLE) { 3867c478bd9Sstevel@tonic-gate if (copyin((void *)data, STRUCT_BUF(args), 3877c478bd9Sstevel@tonic-gate STRUCT_SIZE(args)) == -1) 3887c478bd9Sstevel@tonic-gate return (EFAULT); 3897c478bd9Sstevel@tonic-gate 3907c478bd9Sstevel@tonic-gate udata1 = STRUCT_FGETP(args, udata1); 3917c478bd9Sstevel@tonic-gate udata2 = STRUCT_FGETP(args, udata2); 3927c478bd9Sstevel@tonic-gate udata3 = STRUCT_FGETP(args, udata3); 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate 3957c478bd9Sstevel@tonic-gate switch (cmd) { 3967c478bd9Sstevel@tonic-gate case CPCIO_BIND: 3977c478bd9Sstevel@tonic-gate /* 3987c478bd9Sstevel@tonic-gate * udata1 = pointer to packed nvlist buffer 3997c478bd9Sstevel@tonic-gate * udata2 = size of packed nvlist buffer 4007c478bd9Sstevel@tonic-gate * udata3 = User addr to return error subcode in. 4017c478bd9Sstevel@tonic-gate */ 4027c478bd9Sstevel@tonic-gate if (t->t_cpc_set != NULL) { 4037c478bd9Sstevel@tonic-gate (void) kcpc_unbind(t->t_cpc_set); 4047c478bd9Sstevel@tonic-gate ASSERT(t->t_cpc_set == NULL); 4057c478bd9Sstevel@tonic-gate } 4067c478bd9Sstevel@tonic-gate 4077c478bd9Sstevel@tonic-gate if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1, 4087c478bd9Sstevel@tonic-gate (size_t)udata2)) != 0) { 4097c478bd9Sstevel@tonic-gate return (error); 4107c478bd9Sstevel@tonic-gate } 4117c478bd9Sstevel@tonic-gate 4127c478bd9Sstevel@tonic-gate if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) { 4137c478bd9Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 4147c478bd9Sstevel@tonic-gate t->t_cpc_set = NULL; 4157c478bd9Sstevel@tonic-gate if (copyout(&error, udata3, sizeof (error)) == -1) 4167c478bd9Sstevel@tonic-gate return (EFAULT); 4177c478bd9Sstevel@tonic-gate return (EINVAL); 4187c478bd9Sstevel@tonic-gate } 4197c478bd9Sstevel@tonic-gate 4207c478bd9Sstevel@tonic-gate if ((error = kcpc_bind_cpu(t->t_cpc_set, cpuid, &code)) != 0) { 4217c478bd9Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 4227c478bd9Sstevel@tonic-gate t->t_cpc_set = NULL; 4237c478bd9Sstevel@tonic-gate /* 4247c478bd9Sstevel@tonic-gate * Subcodes are only returned for EINVAL and EACCESS. 4257c478bd9Sstevel@tonic-gate */ 4267c478bd9Sstevel@tonic-gate if ((error == EINVAL || error == EACCES) && 4277c478bd9Sstevel@tonic-gate copyout(&code, udata3, sizeof (code)) == -1) 4287c478bd9Sstevel@tonic-gate return (EFAULT); 4297c478bd9Sstevel@tonic-gate return (error); 4307c478bd9Sstevel@tonic-gate } 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate return (0); 4337c478bd9Sstevel@tonic-gate case CPCIO_SAMPLE: 4347c478bd9Sstevel@tonic-gate /* 4357c478bd9Sstevel@tonic-gate * udata1 = pointer to user's buffer 4367c478bd9Sstevel@tonic-gate * udata2 = pointer to user's hrtime 4377c478bd9Sstevel@tonic-gate * udata3 = pointer to user's tick 4387c478bd9Sstevel@tonic-gate */ 4397c478bd9Sstevel@tonic-gate /* 4407c478bd9Sstevel@tonic-gate * Only CPU-bound sets may be sampled via the ioctl(). If this 4417c478bd9Sstevel@tonic-gate * set has no CPU-bound context, return an error. 4427c478bd9Sstevel@tonic-gate */ 4437c478bd9Sstevel@tonic-gate if (t->t_cpc_set == NULL) 4447c478bd9Sstevel@tonic-gate return (EINVAL); 4457c478bd9Sstevel@tonic-gate if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2, 4467c478bd9Sstevel@tonic-gate udata3)) != 0) 4477c478bd9Sstevel@tonic-gate return (error); 4487c478bd9Sstevel@tonic-gate return (0); 4497c478bd9Sstevel@tonic-gate case CPCIO_RELE: 4507c478bd9Sstevel@tonic-gate if (t->t_cpc_set == NULL) 4517c478bd9Sstevel@tonic-gate return (EINVAL); 4527c478bd9Sstevel@tonic-gate return (kcpc_unbind(t->t_cpc_set)); 4537c478bd9Sstevel@tonic-gate default: 4547c478bd9Sstevel@tonic-gate return (EINVAL); 4557c478bd9Sstevel@tonic-gate } 4567c478bd9Sstevel@tonic-gate } 4577c478bd9Sstevel@tonic-gate 4587c478bd9Sstevel@tonic-gate /* 4597c478bd9Sstevel@tonic-gate * The device supports multiple opens, but only one open 4607c478bd9Sstevel@tonic-gate * is allowed per processor. This is to enable multiple 4617c478bd9Sstevel@tonic-gate * instances of tools looking at different processors. 4627c478bd9Sstevel@tonic-gate */ 4637c478bd9Sstevel@tonic-gate #define KCPC_MINOR_SHARED ((minor_t)0x3fffful) 4647c478bd9Sstevel@tonic-gate 4657c478bd9Sstevel@tonic-gate static ulong_t *kcpc_cpumap; /* bitmap of cpus */ 4667c478bd9Sstevel@tonic-gate 4677c478bd9Sstevel@tonic-gate /*ARGSUSED1*/ 4687c478bd9Sstevel@tonic-gate static int 4697c478bd9Sstevel@tonic-gate kcpc_open(dev_t *dev, int flags, int otyp, cred_t *cr) 4707c478bd9Sstevel@tonic-gate { 4717c478bd9Sstevel@tonic-gate processorid_t cpuid; 4727c478bd9Sstevel@tonic-gate int error; 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate ASSERT(pcbe_ops != NULL); 4757c478bd9Sstevel@tonic-gate 4767c478bd9Sstevel@tonic-gate if ((error = secpolicy_cpc_cpu(cr)) != 0) 4777c478bd9Sstevel@tonic-gate return (error); 4787c478bd9Sstevel@tonic-gate if (getminor(*dev) != KCPC_MINOR_SHARED) 4797c478bd9Sstevel@tonic-gate return (ENXIO); 4807c478bd9Sstevel@tonic-gate if ((cpuid = curthread->t_bind_cpu) == PBIND_NONE) 4817c478bd9Sstevel@tonic-gate return (EINVAL); 4827c478bd9Sstevel@tonic-gate if (cpuid > max_cpuid) 4837c478bd9Sstevel@tonic-gate return (EINVAL); 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_WRITER); 4867c478bd9Sstevel@tonic-gate if (++kcpc_cpuctx == 1) { 4877c478bd9Sstevel@tonic-gate ASSERT(kcpc_cpumap == NULL); 488b9e93c10SJonathan Haslam 489b9e93c10SJonathan Haslam /* 490b9e93c10SJonathan Haslam * Bail out if DTrace is already using the counters. 491b9e93c10SJonathan Haslam */ 492b9e93c10SJonathan Haslam if (dtrace_cpc_in_use) { 493b9e93c10SJonathan Haslam kcpc_cpuctx--; 494b9e93c10SJonathan Haslam rw_exit(&kcpc_cpuctx_lock); 495b9e93c10SJonathan Haslam return (EAGAIN); 496b9e93c10SJonathan Haslam } 4977c478bd9Sstevel@tonic-gate kcpc_cpumap = kmem_zalloc(BT_SIZEOFMAP(max_cpuid + 1), 4987c478bd9Sstevel@tonic-gate KM_SLEEP); 4997c478bd9Sstevel@tonic-gate /* 5007c478bd9Sstevel@tonic-gate * When this device is open for processor-based contexts, 5017c478bd9Sstevel@tonic-gate * no further lwp-based contexts can be created. 5027c478bd9Sstevel@tonic-gate * 5037c478bd9Sstevel@tonic-gate * Since this is the first open, ensure that all existing 5047c478bd9Sstevel@tonic-gate * contexts are invalidated. 5057c478bd9Sstevel@tonic-gate */ 5067c478bd9Sstevel@tonic-gate kcpc_invalidate_all(); 5077c478bd9Sstevel@tonic-gate } else if (BT_TEST(kcpc_cpumap, cpuid)) { 5087c478bd9Sstevel@tonic-gate kcpc_cpuctx--; 5097c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5107c478bd9Sstevel@tonic-gate return (EAGAIN); 5117c478bd9Sstevel@tonic-gate } else if (kcpc_hw_cpu_hook(cpuid, kcpc_cpumap) != 0) { 5127c478bd9Sstevel@tonic-gate kcpc_cpuctx--; 5137c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5147c478bd9Sstevel@tonic-gate return (EACCES); 5157c478bd9Sstevel@tonic-gate } 5167c478bd9Sstevel@tonic-gate BT_SET(kcpc_cpumap, cpuid); 5177c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5187c478bd9Sstevel@tonic-gate 5197c478bd9Sstevel@tonic-gate *dev = makedevice(getmajor(*dev), (minor_t)cpuid); 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate return (0); 5227c478bd9Sstevel@tonic-gate } 5237c478bd9Sstevel@tonic-gate 5247c478bd9Sstevel@tonic-gate /*ARGSUSED1*/ 5257c478bd9Sstevel@tonic-gate static int 5267c478bd9Sstevel@tonic-gate kcpc_close(dev_t dev, int flags, int otyp, cred_t *cr) 5277c478bd9Sstevel@tonic-gate { 5287c478bd9Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_WRITER); 5297c478bd9Sstevel@tonic-gate BT_CLEAR(kcpc_cpumap, getminor(dev)); 5307c478bd9Sstevel@tonic-gate if (--kcpc_cpuctx == 0) { 5317c478bd9Sstevel@tonic-gate kmem_free(kcpc_cpumap, BT_SIZEOFMAP(max_cpuid + 1)); 5327c478bd9Sstevel@tonic-gate kcpc_cpumap = NULL; 5337c478bd9Sstevel@tonic-gate } 5347c478bd9Sstevel@tonic-gate ASSERT(kcpc_cpuctx >= 0); 5357c478bd9Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5367c478bd9Sstevel@tonic-gate 5377c478bd9Sstevel@tonic-gate return (0); 5387c478bd9Sstevel@tonic-gate } 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate /* 5417c478bd9Sstevel@tonic-gate * Sane boundaries on the size of packed lists. In bytes. 5427c478bd9Sstevel@tonic-gate */ 5437c478bd9Sstevel@tonic-gate #define CPC_MIN_PACKSIZE 4 5447c478bd9Sstevel@tonic-gate #define CPC_MAX_PACKSIZE 10000 5457c478bd9Sstevel@tonic-gate 5467c478bd9Sstevel@tonic-gate /* 5477c478bd9Sstevel@tonic-gate * Sane boundary on the number of requests a set can contain. 5487c478bd9Sstevel@tonic-gate */ 5497c478bd9Sstevel@tonic-gate #define CPC_MAX_NREQS 100 5507c478bd9Sstevel@tonic-gate 5517c478bd9Sstevel@tonic-gate /* 5527c478bd9Sstevel@tonic-gate * Sane boundary on the number of attributes a request can contain. 5537c478bd9Sstevel@tonic-gate */ 5547c478bd9Sstevel@tonic-gate #define CPC_MAX_ATTRS 50 5557c478bd9Sstevel@tonic-gate 5567c478bd9Sstevel@tonic-gate /* 5577c478bd9Sstevel@tonic-gate * Copy in a packed nvlist from the user and create a request set out of it. 5587c478bd9Sstevel@tonic-gate * If successful, return 0 and store a pointer to the set we've created. Returns 5597c478bd9Sstevel@tonic-gate * error code on error. 5607c478bd9Sstevel@tonic-gate */ 5617c478bd9Sstevel@tonic-gate int 5627c478bd9Sstevel@tonic-gate kcpc_copyin_set(kcpc_set_t **inset, void *ubuf, size_t len) 5637c478bd9Sstevel@tonic-gate { 5647c478bd9Sstevel@tonic-gate kcpc_set_t *set; 5657c478bd9Sstevel@tonic-gate int i; 5667c478bd9Sstevel@tonic-gate int j; 5677c478bd9Sstevel@tonic-gate char *packbuf; 5687c478bd9Sstevel@tonic-gate 5697c478bd9Sstevel@tonic-gate nvlist_t *nvl; 5707c478bd9Sstevel@tonic-gate nvpair_t *nvp = NULL; 5717c478bd9Sstevel@tonic-gate 5727c478bd9Sstevel@tonic-gate nvlist_t *attrs; 5737c478bd9Sstevel@tonic-gate nvpair_t *nvp_attr; 5747c478bd9Sstevel@tonic-gate kcpc_attr_t *attrp; 5757c478bd9Sstevel@tonic-gate 5767c478bd9Sstevel@tonic-gate nvlist_t **reqlist; 5777c478bd9Sstevel@tonic-gate uint_t nreqs; 5787c478bd9Sstevel@tonic-gate uint64_t uint64; 5797c478bd9Sstevel@tonic-gate uint32_t uint32; 5807c478bd9Sstevel@tonic-gate uint32_t setflags = (uint32_t)-1; 5817c478bd9Sstevel@tonic-gate char *string; 5827c478bd9Sstevel@tonic-gate char *name; 5837c478bd9Sstevel@tonic-gate 5847c478bd9Sstevel@tonic-gate if (len < CPC_MIN_PACKSIZE || len > CPC_MAX_PACKSIZE) 5857c478bd9Sstevel@tonic-gate return (EINVAL); 5867c478bd9Sstevel@tonic-gate 5877c478bd9Sstevel@tonic-gate packbuf = kmem_alloc(len, KM_SLEEP); 5887c478bd9Sstevel@tonic-gate 5897c478bd9Sstevel@tonic-gate if (copyin(ubuf, packbuf, len) == -1) { 5907c478bd9Sstevel@tonic-gate kmem_free(packbuf, len); 5917c478bd9Sstevel@tonic-gate return (EFAULT); 5927c478bd9Sstevel@tonic-gate } 5937c478bd9Sstevel@tonic-gate 5947c478bd9Sstevel@tonic-gate if (nvlist_unpack(packbuf, len, &nvl, KM_SLEEP) != 0) { 5957c478bd9Sstevel@tonic-gate kmem_free(packbuf, len); 5967c478bd9Sstevel@tonic-gate return (EINVAL); 5977c478bd9Sstevel@tonic-gate } 5987c478bd9Sstevel@tonic-gate 5997c478bd9Sstevel@tonic-gate /* 6007c478bd9Sstevel@tonic-gate * The nvlist has been unpacked so there is no need for the packed 6017c478bd9Sstevel@tonic-gate * representation from this point on. 6027c478bd9Sstevel@tonic-gate */ 6037c478bd9Sstevel@tonic-gate kmem_free(packbuf, len); 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate i = 0; 6067c478bd9Sstevel@tonic-gate while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 6077c478bd9Sstevel@tonic-gate switch (nvpair_type(nvp)) { 6087c478bd9Sstevel@tonic-gate case DATA_TYPE_UINT32: 6097c478bd9Sstevel@tonic-gate if (strcmp(nvpair_name(nvp), "flags") != 0 || 6107c478bd9Sstevel@tonic-gate nvpair_value_uint32(nvp, &setflags) != 0) { 6117c478bd9Sstevel@tonic-gate nvlist_free(nvl); 6127c478bd9Sstevel@tonic-gate return (EINVAL); 6137c478bd9Sstevel@tonic-gate } 6147c478bd9Sstevel@tonic-gate break; 6157c478bd9Sstevel@tonic-gate case DATA_TYPE_NVLIST_ARRAY: 6167c478bd9Sstevel@tonic-gate if (strcmp(nvpair_name(nvp), "reqs") != 0 || 6177c478bd9Sstevel@tonic-gate nvpair_value_nvlist_array(nvp, &reqlist, 6187c478bd9Sstevel@tonic-gate &nreqs) != 0) { 6197c478bd9Sstevel@tonic-gate nvlist_free(nvl); 6207c478bd9Sstevel@tonic-gate return (EINVAL); 6217c478bd9Sstevel@tonic-gate } 6227c478bd9Sstevel@tonic-gate break; 6237c478bd9Sstevel@tonic-gate default: 6247c478bd9Sstevel@tonic-gate nvlist_free(nvl); 6257c478bd9Sstevel@tonic-gate return (EINVAL); 6267c478bd9Sstevel@tonic-gate } 6277c478bd9Sstevel@tonic-gate i++; 6287c478bd9Sstevel@tonic-gate } 6297c478bd9Sstevel@tonic-gate 6307c478bd9Sstevel@tonic-gate /* 6317c478bd9Sstevel@tonic-gate * There should be two members in the top-level nvlist: 6327c478bd9Sstevel@tonic-gate * an array of nvlists consisting of the requests, and flags. 6337c478bd9Sstevel@tonic-gate * Anything else is an invalid set. 6347c478bd9Sstevel@tonic-gate */ 6357c478bd9Sstevel@tonic-gate if (i != 2) { 6367c478bd9Sstevel@tonic-gate nvlist_free(nvl); 6377c478bd9Sstevel@tonic-gate return (EINVAL); 6387c478bd9Sstevel@tonic-gate } 6397c478bd9Sstevel@tonic-gate 6407c478bd9Sstevel@tonic-gate if (nreqs > CPC_MAX_NREQS) { 6417c478bd9Sstevel@tonic-gate nvlist_free(nvl); 6427c478bd9Sstevel@tonic-gate return (EINVAL); 6437c478bd9Sstevel@tonic-gate } 6447c478bd9Sstevel@tonic-gate 6457c478bd9Sstevel@tonic-gate /* 6467c478bd9Sstevel@tonic-gate * The requests are now stored in the nvlist array at reqlist. 6474568bee7Strevtom * Note that the use of kmem_zalloc() to alloc the kcpc_set_t means 6484568bee7Strevtom * we don't need to call the init routines for ks_lock and ks_condv. 6497c478bd9Sstevel@tonic-gate */ 6504568bee7Strevtom set = kmem_zalloc(sizeof (kcpc_set_t), KM_SLEEP); 6517c478bd9Sstevel@tonic-gate set->ks_req = (kcpc_request_t *)kmem_zalloc(sizeof (kcpc_request_t) * 6527c478bd9Sstevel@tonic-gate nreqs, KM_SLEEP); 6537c478bd9Sstevel@tonic-gate set->ks_nreqs = nreqs; 6547c478bd9Sstevel@tonic-gate /* 6557c478bd9Sstevel@tonic-gate * If the nvlist didn't contain a flags member, setflags was initialized 6567c478bd9Sstevel@tonic-gate * with an illegal value and this set will fail sanity checks later on. 6577c478bd9Sstevel@tonic-gate */ 6587c478bd9Sstevel@tonic-gate set->ks_flags = setflags; 6594568bee7Strevtom /* 6604568bee7Strevtom * Initialize bind/unbind set synchronization. 6614568bee7Strevtom */ 6624568bee7Strevtom set->ks_state &= ~KCPC_SET_BOUND; 6637c478bd9Sstevel@tonic-gate 6647c478bd9Sstevel@tonic-gate /* 6657c478bd9Sstevel@tonic-gate * Build the set up one request at a time, always keeping it self- 6667c478bd9Sstevel@tonic-gate * consistent so we can give it to kcpc_free_set() if we need to back 6677c478bd9Sstevel@tonic-gate * out and return and error. 6687c478bd9Sstevel@tonic-gate */ 6697c478bd9Sstevel@tonic-gate for (i = 0; i < nreqs; i++) { 6707c478bd9Sstevel@tonic-gate nvp = NULL; 6717c478bd9Sstevel@tonic-gate set->ks_req[i].kr_picnum = -1; 6727c478bd9Sstevel@tonic-gate while ((nvp = nvlist_next_nvpair(reqlist[i], nvp)) != NULL) { 6737c478bd9Sstevel@tonic-gate name = nvpair_name(nvp); 6747c478bd9Sstevel@tonic-gate switch (nvpair_type(nvp)) { 6757c478bd9Sstevel@tonic-gate case DATA_TYPE_UINT32: 6767c478bd9Sstevel@tonic-gate if (nvpair_value_uint32(nvp, &uint32) == EINVAL) 6777c478bd9Sstevel@tonic-gate goto inval; 6787c478bd9Sstevel@tonic-gate if (strcmp(name, "cr_flags") == 0) 6797c478bd9Sstevel@tonic-gate set->ks_req[i].kr_flags = uint32; 6807c478bd9Sstevel@tonic-gate if (strcmp(name, "cr_index") == 0) 6817c478bd9Sstevel@tonic-gate set->ks_req[i].kr_index = uint32; 6827c478bd9Sstevel@tonic-gate break; 6837c478bd9Sstevel@tonic-gate case DATA_TYPE_UINT64: 6847c478bd9Sstevel@tonic-gate if (nvpair_value_uint64(nvp, &uint64) == EINVAL) 6857c478bd9Sstevel@tonic-gate goto inval; 6867c478bd9Sstevel@tonic-gate if (strcmp(name, "cr_preset") == 0) 6877c478bd9Sstevel@tonic-gate set->ks_req[i].kr_preset = uint64; 6887c478bd9Sstevel@tonic-gate break; 6897c478bd9Sstevel@tonic-gate case DATA_TYPE_STRING: 6907c478bd9Sstevel@tonic-gate if (nvpair_value_string(nvp, &string) == EINVAL) 6917c478bd9Sstevel@tonic-gate goto inval; 6927c478bd9Sstevel@tonic-gate if (strcmp(name, "cr_event") == 0) 6937c478bd9Sstevel@tonic-gate (void) strncpy(set->ks_req[i].kr_event, 6947c478bd9Sstevel@tonic-gate string, CPC_MAX_EVENT_LEN); 6957c478bd9Sstevel@tonic-gate break; 6967c478bd9Sstevel@tonic-gate case DATA_TYPE_NVLIST: 6977c478bd9Sstevel@tonic-gate if (strcmp(name, "cr_attr") != 0) 6987c478bd9Sstevel@tonic-gate goto inval; 6997c478bd9Sstevel@tonic-gate if (nvpair_value_nvlist(nvp, &attrs) == EINVAL) 7007c478bd9Sstevel@tonic-gate goto inval; 7017c478bd9Sstevel@tonic-gate nvp_attr = NULL; 7027c478bd9Sstevel@tonic-gate /* 7037c478bd9Sstevel@tonic-gate * If the picnum has been specified as an 7047c478bd9Sstevel@tonic-gate * attribute, consume that attribute here and 7057c478bd9Sstevel@tonic-gate * remove it from the list of attributes. 7067c478bd9Sstevel@tonic-gate */ 7077c478bd9Sstevel@tonic-gate if (nvlist_lookup_uint64(attrs, "picnum", 7087c478bd9Sstevel@tonic-gate &uint64) == 0) { 7097c478bd9Sstevel@tonic-gate if (nvlist_remove(attrs, "picnum", 7107c478bd9Sstevel@tonic-gate DATA_TYPE_UINT64) != 0) 7117c478bd9Sstevel@tonic-gate panic("nvlist %p faulty", 712903a11ebSrh87107 (void *)attrs); 7137c478bd9Sstevel@tonic-gate set->ks_req[i].kr_picnum = uint64; 7147c478bd9Sstevel@tonic-gate } 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate if ((set->ks_req[i].kr_nattrs = 7177c478bd9Sstevel@tonic-gate kcpc_nvlist_npairs(attrs)) == 0) 7187c478bd9Sstevel@tonic-gate break; 7197c478bd9Sstevel@tonic-gate 7207c478bd9Sstevel@tonic-gate if (set->ks_req[i].kr_nattrs > CPC_MAX_ATTRS) 7217c478bd9Sstevel@tonic-gate goto inval; 7227c478bd9Sstevel@tonic-gate 7237c478bd9Sstevel@tonic-gate set->ks_req[i].kr_attr = 7247c478bd9Sstevel@tonic-gate kmem_alloc(set->ks_req[i].kr_nattrs * 7257c478bd9Sstevel@tonic-gate sizeof (kcpc_attr_t), KM_SLEEP); 7267c478bd9Sstevel@tonic-gate j = 0; 7277c478bd9Sstevel@tonic-gate 7287c478bd9Sstevel@tonic-gate while ((nvp_attr = nvlist_next_nvpair(attrs, 7297c478bd9Sstevel@tonic-gate nvp_attr)) != NULL) { 7307c478bd9Sstevel@tonic-gate attrp = &set->ks_req[i].kr_attr[j]; 7317c478bd9Sstevel@tonic-gate 7327c478bd9Sstevel@tonic-gate if (nvpair_type(nvp_attr) != 7337c478bd9Sstevel@tonic-gate DATA_TYPE_UINT64) 7347c478bd9Sstevel@tonic-gate goto inval; 7357c478bd9Sstevel@tonic-gate 7367c478bd9Sstevel@tonic-gate (void) strncpy(attrp->ka_name, 7377c478bd9Sstevel@tonic-gate nvpair_name(nvp_attr), 7387c478bd9Sstevel@tonic-gate CPC_MAX_ATTR_LEN); 7397c478bd9Sstevel@tonic-gate 7407c478bd9Sstevel@tonic-gate if (nvpair_value_uint64(nvp_attr, 7417c478bd9Sstevel@tonic-gate &(attrp->ka_val)) == EINVAL) 7427c478bd9Sstevel@tonic-gate goto inval; 7437c478bd9Sstevel@tonic-gate j++; 7447c478bd9Sstevel@tonic-gate } 7457c478bd9Sstevel@tonic-gate ASSERT(j == set->ks_req[i].kr_nattrs); 7467c478bd9Sstevel@tonic-gate default: 7477c478bd9Sstevel@tonic-gate break; 7487c478bd9Sstevel@tonic-gate } 7497c478bd9Sstevel@tonic-gate } 7507c478bd9Sstevel@tonic-gate } 7517c478bd9Sstevel@tonic-gate 7527c478bd9Sstevel@tonic-gate nvlist_free(nvl); 7537c478bd9Sstevel@tonic-gate *inset = set; 7547c478bd9Sstevel@tonic-gate return (0); 7557c478bd9Sstevel@tonic-gate 7567c478bd9Sstevel@tonic-gate inval: 7577c478bd9Sstevel@tonic-gate nvlist_free(nvl); 7587c478bd9Sstevel@tonic-gate kcpc_free_set(set); 7597c478bd9Sstevel@tonic-gate return (EINVAL); 7607c478bd9Sstevel@tonic-gate } 7617c478bd9Sstevel@tonic-gate 7627c478bd9Sstevel@tonic-gate /* 7637c478bd9Sstevel@tonic-gate * Count the number of nvpairs in the supplied nvlist. 7647c478bd9Sstevel@tonic-gate */ 7657c478bd9Sstevel@tonic-gate static uint32_t 7667c478bd9Sstevel@tonic-gate kcpc_nvlist_npairs(nvlist_t *list) 7677c478bd9Sstevel@tonic-gate { 7687c478bd9Sstevel@tonic-gate nvpair_t *nvp = NULL; 7697c478bd9Sstevel@tonic-gate uint32_t n = 0; 7707c478bd9Sstevel@tonic-gate 7717c478bd9Sstevel@tonic-gate while ((nvp = nvlist_next_nvpair(list, nvp)) != NULL) 7727c478bd9Sstevel@tonic-gate n++; 7737c478bd9Sstevel@tonic-gate 7747c478bd9Sstevel@tonic-gate return (n); 7757c478bd9Sstevel@tonic-gate } 7767c478bd9Sstevel@tonic-gate 7777c478bd9Sstevel@tonic-gate /* 7787c478bd9Sstevel@tonic-gate * Performs sanity checks on the given set. 7797c478bd9Sstevel@tonic-gate * Returns 0 if the set checks out OK. 7807c478bd9Sstevel@tonic-gate * Returns a detailed error subcode, or -1 if there is no applicable subcode. 7817c478bd9Sstevel@tonic-gate */ 7827c478bd9Sstevel@tonic-gate static int 7837c478bd9Sstevel@tonic-gate kcpc_verify_set(kcpc_set_t *set) 7847c478bd9Sstevel@tonic-gate { 7857c478bd9Sstevel@tonic-gate kcpc_request_t *rp; 7867c478bd9Sstevel@tonic-gate int i; 7877c478bd9Sstevel@tonic-gate uint64_t bitmap = 0; 7887c478bd9Sstevel@tonic-gate int n; 7897c478bd9Sstevel@tonic-gate 7907c478bd9Sstevel@tonic-gate if (set->ks_nreqs > cpc_ncounters) 7917c478bd9Sstevel@tonic-gate return (-1); 7927c478bd9Sstevel@tonic-gate 7937c478bd9Sstevel@tonic-gate if (CPC_SET_VALID_FLAGS(set->ks_flags) == 0) 7947c478bd9Sstevel@tonic-gate return (-1); 7957c478bd9Sstevel@tonic-gate 7967c478bd9Sstevel@tonic-gate for (i = 0; i < set->ks_nreqs; i++) { 7977c478bd9Sstevel@tonic-gate rp = &set->ks_req[i]; 7987c478bd9Sstevel@tonic-gate 7997c478bd9Sstevel@tonic-gate /* 8007c478bd9Sstevel@tonic-gate * The following comparison must cast cpc_ncounters to an int, 8017c478bd9Sstevel@tonic-gate * because kr_picnum will be -1 if the request didn't explicitly 8027c478bd9Sstevel@tonic-gate * choose a PIC. 8037c478bd9Sstevel@tonic-gate */ 8047c478bd9Sstevel@tonic-gate if (rp->kr_picnum >= (int)cpc_ncounters) 8057c478bd9Sstevel@tonic-gate return (CPC_INVALID_PICNUM); 8067c478bd9Sstevel@tonic-gate 8077c478bd9Sstevel@tonic-gate /* 8087c478bd9Sstevel@tonic-gate * Of the pics whose physical picnum has been specified, make 8097c478bd9Sstevel@tonic-gate * sure each PIC appears only once in set. 8107c478bd9Sstevel@tonic-gate */ 8117c478bd9Sstevel@tonic-gate if ((n = set->ks_req[i].kr_picnum) != -1) { 8127c478bd9Sstevel@tonic-gate if ((bitmap & (1 << n)) != 0) 8137c478bd9Sstevel@tonic-gate return (-1); 8147c478bd9Sstevel@tonic-gate bitmap |= (1 << n); 8157c478bd9Sstevel@tonic-gate } 8167c478bd9Sstevel@tonic-gate 8177c478bd9Sstevel@tonic-gate /* 8187c478bd9Sstevel@tonic-gate * Make sure the requested index falls within the range of all 8197c478bd9Sstevel@tonic-gate * requests. 8207c478bd9Sstevel@tonic-gate */ 8217c478bd9Sstevel@tonic-gate if (rp->kr_index < 0 || rp->kr_index >= set->ks_nreqs) 8227c478bd9Sstevel@tonic-gate return (-1); 8237c478bd9Sstevel@tonic-gate 8247c478bd9Sstevel@tonic-gate /* 8257c478bd9Sstevel@tonic-gate * Make sure there are no unknown flags. 8267c478bd9Sstevel@tonic-gate */ 8277c478bd9Sstevel@tonic-gate if (KCPC_REQ_VALID_FLAGS(rp->kr_flags) == 0) 8287c478bd9Sstevel@tonic-gate return (CPC_REQ_INVALID_FLAGS); 8297c478bd9Sstevel@tonic-gate } 8307c478bd9Sstevel@tonic-gate 8317c478bd9Sstevel@tonic-gate return (0); 8327c478bd9Sstevel@tonic-gate } 8337c478bd9Sstevel@tonic-gate 8347c478bd9Sstevel@tonic-gate static struct cb_ops cb_ops = { 8357c478bd9Sstevel@tonic-gate kcpc_open, 8367c478bd9Sstevel@tonic-gate kcpc_close, 8377c478bd9Sstevel@tonic-gate nodev, /* strategy */ 8387c478bd9Sstevel@tonic-gate nodev, /* print */ 8397c478bd9Sstevel@tonic-gate nodev, /* dump */ 8407c478bd9Sstevel@tonic-gate nodev, /* read */ 8417c478bd9Sstevel@tonic-gate nodev, /* write */ 8427c478bd9Sstevel@tonic-gate kcpc_ioctl, 8437c478bd9Sstevel@tonic-gate nodev, /* devmap */ 8447c478bd9Sstevel@tonic-gate nodev, /* mmap */ 8457c478bd9Sstevel@tonic-gate nodev, /* segmap */ 8467c478bd9Sstevel@tonic-gate nochpoll, /* poll */ 8477c478bd9Sstevel@tonic-gate ddi_prop_op, 8487c478bd9Sstevel@tonic-gate NULL, 8497c478bd9Sstevel@tonic-gate D_NEW | D_MP 8507c478bd9Sstevel@tonic-gate }; 8517c478bd9Sstevel@tonic-gate 8527c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 8537c478bd9Sstevel@tonic-gate static int 8547c478bd9Sstevel@tonic-gate kcpc_probe(dev_info_t *devi) 8557c478bd9Sstevel@tonic-gate { 8567c478bd9Sstevel@tonic-gate return (DDI_PROBE_SUCCESS); 8577c478bd9Sstevel@tonic-gate } 8587c478bd9Sstevel@tonic-gate 8597c478bd9Sstevel@tonic-gate static dev_info_t *kcpc_devi; 8607c478bd9Sstevel@tonic-gate 8617c478bd9Sstevel@tonic-gate static int 8627c478bd9Sstevel@tonic-gate kcpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 8637c478bd9Sstevel@tonic-gate { 8647c478bd9Sstevel@tonic-gate if (cmd != DDI_ATTACH) 8657c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8667c478bd9Sstevel@tonic-gate kcpc_devi = devi; 8677c478bd9Sstevel@tonic-gate return (ddi_create_minor_node(devi, "shared", S_IFCHR, 8687c478bd9Sstevel@tonic-gate KCPC_MINOR_SHARED, DDI_PSEUDO, 0)); 8697c478bd9Sstevel@tonic-gate } 8707c478bd9Sstevel@tonic-gate 8717c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 8727c478bd9Sstevel@tonic-gate static int 8737c478bd9Sstevel@tonic-gate kcpc_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result) 8747c478bd9Sstevel@tonic-gate { 8757c478bd9Sstevel@tonic-gate switch (cmd) { 8767c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 8777c478bd9Sstevel@tonic-gate switch (getminor((dev_t)arg)) { 8787c478bd9Sstevel@tonic-gate case KCPC_MINOR_SHARED: 8797c478bd9Sstevel@tonic-gate *result = kcpc_devi; 8807c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 8817c478bd9Sstevel@tonic-gate default: 8827c478bd9Sstevel@tonic-gate break; 8837c478bd9Sstevel@tonic-gate } 8847c478bd9Sstevel@tonic-gate break; 8857c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 8867c478bd9Sstevel@tonic-gate *result = 0; 8877c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 8887c478bd9Sstevel@tonic-gate default: 8897c478bd9Sstevel@tonic-gate break; 8907c478bd9Sstevel@tonic-gate } 8917c478bd9Sstevel@tonic-gate 8927c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8937c478bd9Sstevel@tonic-gate } 8947c478bd9Sstevel@tonic-gate 8957c478bd9Sstevel@tonic-gate static struct dev_ops dev_ops = { 8967c478bd9Sstevel@tonic-gate DEVO_REV, 8977c478bd9Sstevel@tonic-gate 0, 8987c478bd9Sstevel@tonic-gate kcpc_getinfo, 8997c478bd9Sstevel@tonic-gate nulldev, /* identify */ 9007c478bd9Sstevel@tonic-gate kcpc_probe, 9017c478bd9Sstevel@tonic-gate kcpc_attach, 9027c478bd9Sstevel@tonic-gate nodev, /* detach */ 9037c478bd9Sstevel@tonic-gate nodev, /* reset */ 9047c478bd9Sstevel@tonic-gate &cb_ops, 90519397407SSherry Moore (struct bus_ops *)0, 90619397407SSherry Moore NULL, 90719397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 9087c478bd9Sstevel@tonic-gate }; 9097c478bd9Sstevel@tonic-gate 9107c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 9117c478bd9Sstevel@tonic-gate &mod_driverops, 912903a11ebSrh87107 "cpc sampling driver", 9137c478bd9Sstevel@tonic-gate &dev_ops 9147c478bd9Sstevel@tonic-gate }; 9157c478bd9Sstevel@tonic-gate 9167c478bd9Sstevel@tonic-gate static struct sysent cpc_sysent = { 9177c478bd9Sstevel@tonic-gate 5, 9187c478bd9Sstevel@tonic-gate SE_NOUNLOAD | SE_ARGC | SE_32RVAL1, 9197c478bd9Sstevel@tonic-gate cpc 9207c478bd9Sstevel@tonic-gate }; 9217c478bd9Sstevel@tonic-gate 9227c478bd9Sstevel@tonic-gate static struct modlsys modlsys = { 9237c478bd9Sstevel@tonic-gate &mod_syscallops, 9247c478bd9Sstevel@tonic-gate "cpc sampling system call", 9257c478bd9Sstevel@tonic-gate &cpc_sysent 9267c478bd9Sstevel@tonic-gate }; 9277c478bd9Sstevel@tonic-gate 9287c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 9297c478bd9Sstevel@tonic-gate static struct modlsys modlsys32 = { 9307c478bd9Sstevel@tonic-gate &mod_syscallops32, 9317c478bd9Sstevel@tonic-gate "32-bit cpc sampling system call", 9327c478bd9Sstevel@tonic-gate &cpc_sysent 9337c478bd9Sstevel@tonic-gate }; 9347c478bd9Sstevel@tonic-gate #endif 9357c478bd9Sstevel@tonic-gate 9367c478bd9Sstevel@tonic-gate static struct modlinkage modl = { 9377c478bd9Sstevel@tonic-gate MODREV_1, 9387c478bd9Sstevel@tonic-gate &modldrv, 9397c478bd9Sstevel@tonic-gate &modlsys, 9407c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 9417c478bd9Sstevel@tonic-gate &modlsys32, 9427c478bd9Sstevel@tonic-gate #endif 9437c478bd9Sstevel@tonic-gate }; 9447c478bd9Sstevel@tonic-gate 9457c478bd9Sstevel@tonic-gate int 9467c478bd9Sstevel@tonic-gate _init(void) 9477c478bd9Sstevel@tonic-gate { 948*b885580bSAlexander Kolbasov if (kcpc_init() != 0) 9497c478bd9Sstevel@tonic-gate return (ENOTSUP); 9507c478bd9Sstevel@tonic-gate 951*b885580bSAlexander Kolbasov return (mod_install(&modl)); 9527c478bd9Sstevel@tonic-gate } 9537c478bd9Sstevel@tonic-gate 9547c478bd9Sstevel@tonic-gate int 9557c478bd9Sstevel@tonic-gate _fini(void) 9567c478bd9Sstevel@tonic-gate { 957*b885580bSAlexander Kolbasov return (mod_remove(&modl)); 9587c478bd9Sstevel@tonic-gate } 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate int 9617c478bd9Sstevel@tonic-gate _info(struct modinfo *mi) 9627c478bd9Sstevel@tonic-gate { 9637c478bd9Sstevel@tonic-gate return (mod_info(&modl, mi)); 9647c478bd9Sstevel@tonic-gate } 965