1 /* $OpenBSD: subr_evcount.c,v 1.2 2004/06/28 01:59:57 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2004 Artur Grabowski <art@openbsd.org> 4 * Copyright (c) 2004 Aaron Campbell <aaron@openbsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 19 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/evcount.h> 30 #include <sys/timeout.h> 31 #include <sys/kernel.h> 32 #include <sys/systm.h> 33 #include <sys/sysctl.h> 34 35 #ifdef __HAVE_EVCOUNT 36 37 static TAILQ_HEAD(,evcount) evcount_list; 38 static struct evcount *evcount_next_sync; 39 40 /* 41 * Standard evcount parents. 42 */ 43 struct evcount evcount_intr; 44 45 void evcount_timeout(void *); 46 void evcount_init(void); 47 void evcount_sync(struct evcount *); 48 49 void 50 evcount_init(void) 51 { 52 #ifndef __LP64__ 53 static struct timeout ec_to; 54 55 timeout_set(&ec_to, evcount_timeout, &ec_to); 56 timeout_add(&ec_to, hz); 57 #endif 58 TAILQ_INIT(&evcount_list); 59 60 evcount_attach(&evcount_intr, "intr", NULL, NULL); 61 } 62 63 64 void 65 evcount_attach(ec, name, data, parent) 66 struct evcount *ec; 67 const char *name; 68 void *data; 69 struct evcount *parent; 70 { 71 static int nextid = 0; 72 73 if (nextid == 0) { 74 nextid++; /* start with 1 */ 75 evcount_init(); 76 } 77 78 memset(ec, 0, sizeof(*ec)); 79 ec->ec_name = name; 80 ec->ec_parent = parent; 81 ec->ec_id = nextid++; 82 ec->ec_data = data; 83 TAILQ_INSERT_TAIL(&evcount_list, ec, next); 84 } 85 86 void 87 evcount_detach(ec) 88 struct evcount *ec; 89 { 90 if (evcount_next_sync == ec) 91 evcount_next_sync = TAILQ_NEXT(ec, next); 92 93 TAILQ_REMOVE(&evcount_list, ec, next); 94 } 95 96 int 97 evcount_sysctl(name, namelen, oldp, oldlenp, newp, newlen) 98 int *name; 99 u_int namelen; 100 void *oldp; 101 size_t *oldlenp; 102 void *newp; 103 size_t newlen; 104 { 105 int error = 0, s, nintr, i; 106 struct evcount *ec; 107 u_int64_t count; 108 109 if (newp != NULL) 110 return (EPERM); 111 112 if (name[0] != KERN_INTRCNT_NUM) { 113 if (namelen != 2) 114 return (ENOTDIR); 115 if (name[1] < 0) 116 return (EINVAL); 117 i = name[1]; 118 } else 119 i = -1; 120 121 nintr = 0; 122 TAILQ_FOREACH(ec, &evcount_list, next) { 123 if (ec->ec_parent != &evcount_intr) 124 continue; 125 if (nintr++ == i) 126 break; 127 } 128 129 switch (name[0]) { 130 case KERN_INTRCNT_NUM: 131 error = sysctl_rdint(oldp, oldlenp, NULL, nintr); 132 break; 133 case KERN_INTRCNT_CNT: 134 if (ec == NULL) 135 return (ENOENT); 136 /* XXX - bogus cast to int, but we can't do better. */ 137 s = splhigh(); 138 count = ec->ec_count; 139 splx(s); 140 error = sysctl_rdint(oldp, oldlenp, NULL, (int)count); 141 break; 142 case KERN_INTRCNT_NAME: 143 if (ec == NULL) 144 return (ENOENT); 145 error = sysctl_rdstring(oldp, oldlenp, NULL, ec->ec_name); 146 break; 147 case KERN_INTRCNT_VECTOR: 148 if (ec == NULL || ec->ec_data == NULL) 149 return (ENOENT); 150 error = sysctl_rdint(oldp, oldlenp, NULL, 151 *((int *)ec->ec_data)); 152 break; 153 default: 154 error = EOPNOTSUPP; 155 break; 156 } 157 158 return (error); 159 } 160 161 #ifndef __LP64__ 162 /* 163 * This timeout has to run once in a while for every event counter to 164 * sync the real 64 bit counter with the 32 bit temporary counter, because 165 * we cannot atomically increment the 64 bit counter on 32 bit systems. 166 */ 167 void 168 evcount_timeout(void *v) 169 { 170 struct timeout *to = v; 171 int s; 172 173 s = splhigh(); 174 if (evcount_next_sync == NULL) 175 evcount_next_sync = TAILQ_FIRST(&evcount_list); 176 177 evcount_sync(evcount_next_sync); 178 evcount_next_sync = TAILQ_NEXT(evcount_next_sync, next); 179 splx(s); 180 181 timeout_add(to, hz); 182 } 183 #endif 184 185 void 186 evcount_sync(struct evcount *ec) 187 { 188 #ifndef __LP64__ 189 /* XXX race */ 190 ec->ec_count += ec->ec_count32; 191 ec->ec_count32 = 0; 192 #endif 193 } 194 195 #endif /* __HAVE_EVCOUNT */ 196