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