1 /* 2 * Copyright (c) 2005 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 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. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sys/kern/kern_cputimer.c,v 1.3 2005/06/01 22:25:12 dillon Exp $ 35 */ 36 /* 37 * Generic cputimer - access to a reliable, free-running counter. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/kernel.h> 42 #include <sys/systm.h> 43 #include <sys/thread.h> 44 #include <sys/globaldata.h> 45 #include <sys/systimer.h> 46 #include <sys/sysctl.h> 47 #include <sys/thread2.h> 48 49 static sysclock_t dummy_cputimer_count(void); 50 51 static struct cputimer dummy_cputimer = { 52 NULL, 53 "dummy", 54 CPUTIMER_PRI_DUMMY, 55 CPUTIMER_DUMMY, 56 dummy_cputimer_count, 57 cputimer_default_fromhz, 58 cputimer_default_fromus, 59 cputimer_default_construct, 60 cputimer_default_destruct, 61 1000000, 62 (1000000LL << 32) / 1000000, 63 (1000000000LL << 32) / 1000000, 64 0 65 }; 66 67 static struct cputimer *cputimer_reg_base = &dummy_cputimer; 68 struct cputimer *sys_cputimer = &dummy_cputimer; 69 70 /* 71 * Generic cputimer API 72 */ 73 void 74 cputimer_select(struct cputimer *timer, int pri) 75 { 76 sysclock_t oldclock; 77 78 /* 79 * Calculate helper fields 80 */ 81 cputimer_set_frequency(timer, timer->freq); 82 83 /* 84 * Install a new cputimer if its priority allows it. If timer is 85 * passed as NULL we deinstall the current timer and revert to our 86 * dummy. 87 */ 88 if (pri == 0) 89 pri = timer->pri; 90 if (timer == NULL || pri >= sys_cputimer->pri) { 91 oldclock = sys_cputimer->count(); 92 sys_cputimer->destruct(sys_cputimer); 93 sys_cputimer = &dummy_cputimer; 94 if (timer) { 95 sys_cputimer = timer; 96 timer->construct(timer, oldclock); 97 cputimer_intr_config(timer); 98 } 99 } 100 } 101 102 /* 103 * Register a timer. If the timer has already been registered, do nothing. 104 */ 105 void 106 cputimer_register(struct cputimer *timer) 107 { 108 struct cputimer **scanpp; 109 struct cputimer *scan; 110 111 for (scanpp = &cputimer_reg_base; 112 (scan = *scanpp) != NULL; 113 scanpp = &scan->next 114 ) { 115 if (scan == timer) 116 return; 117 } 118 timer->next = NULL; 119 *scanpp = timer; 120 } 121 122 /* 123 * Deregister a timer. If the timer has already been deregistered, do nothing. 124 */ 125 void 126 cputimer_deregister(struct cputimer *timer) 127 { 128 struct cputimer **scanpp; 129 struct cputimer *scan; 130 struct cputimer *best; 131 132 /* 133 * Locate and remove the timer. If the timer is our currently active 134 * timer, revert to the dummy timer. 135 */ 136 scanpp = &cputimer_reg_base; 137 while ((scan = *scanpp) != NULL) { 138 if (scan == timer) { 139 *scanpp = timer->next; 140 if (timer == sys_cputimer) 141 cputimer_select(&dummy_cputimer, 0x7FFFFFFF); 142 } else { 143 scanpp = &scan->next; 144 } 145 } 146 147 /* 148 * If sys_cputimer reverted to the dummy, select the best one 149 */ 150 if (sys_cputimer == &dummy_cputimer) { 151 best = NULL; 152 for (scan = cputimer_reg_base; scan; scan = scan->next) { 153 if (best == NULL || scan->pri > best->pri) 154 best = scan; 155 } 156 if (best) 157 cputimer_select(best, 0x7FFFFFFF); 158 } 159 } 160 161 /* 162 * Calculate usec / tick and nsec / tick, scaled by (1 << 32). 163 * 164 * so e.g. a 3 mhz timer would be 3 usec / tick x (1 << 32), 165 * or 3000 nsec / tick x (1 << 32) 166 */ 167 void 168 cputimer_set_frequency(struct cputimer *timer, int freq) 169 { 170 timer->freq = freq; 171 timer->freq64_usec = (1000000LL << 32) / freq; 172 timer->freq64_nsec = (1000000000LL << 32) / freq; 173 if (timer == sys_cputimer) 174 cputimer_intr_config(timer); 175 } 176 177 sysclock_t 178 cputimer_default_fromhz(int freq) 179 { 180 return(sys_cputimer->freq / freq + 1); 181 } 182 183 sysclock_t 184 cputimer_default_fromus(int us) 185 { 186 return((int64_t)sys_cputimer->freq * us / 1000000); 187 } 188 189 /* 190 * Dummy counter implementation 191 */ 192 static 193 sysclock_t 194 dummy_cputimer_count(void) 195 { 196 return(++dummy_cputimer.base); 197 } 198 199 void 200 cputimer_default_construct(struct cputimer *cputimer, sysclock_t oldclock) 201 { 202 cputimer->base = oldclock; 203 } 204 205 void 206 cputimer_default_destruct(struct cputimer *cputimer) 207 { 208 } 209 210 /************************************************************************ 211 * SYSCTL SUPPORT * 212 ************************************************************************ 213 * 214 * Note: the ability to change the systimer is not currently enabled 215 * because it will mess up systimer calculations. You have to live 216 * with what is configured at boot. 217 */ 218 static int 219 sysctl_cputimer_reglist(SYSCTL_HANDLER_ARGS) 220 { 221 struct cputimer *scan; 222 int error = 0; 223 int loop = 0; 224 225 /* 226 * Build a list of available timers 227 */ 228 for (scan = cputimer_reg_base; scan; scan = scan->next) { 229 if (error == 0 && loop) 230 error = SYSCTL_OUT(req, " ", 1); 231 if (error == 0) 232 error = SYSCTL_OUT(req, scan->name, strlen(scan->name)); 233 ++loop; 234 } 235 return (error); 236 } 237 238 static int 239 sysctl_cputimer_name(SYSCTL_HANDLER_ARGS) 240 { 241 int error; 242 243 error = SYSCTL_OUT(req, sys_cputimer->name, strlen(sys_cputimer->name)); 244 return (error); 245 } 246 247 static int 248 sysctl_cputimer_clock(SYSCTL_HANDLER_ARGS) 249 { 250 sysclock_t clock; 251 int error; 252 253 clock = sys_cputimer->count(); 254 error = SYSCTL_OUT(req, &clock, sizeof(clock)); 255 return (error); 256 } 257 258 static int 259 sysctl_cputimer_freq(SYSCTL_HANDLER_ARGS) 260 { 261 int error; 262 263 error = SYSCTL_OUT(req, &sys_cputimer->freq, sizeof(sys_cputimer->freq)); 264 return (error); 265 } 266 267 SYSCTL_DECL(_kern_cputimer); 268 SYSCTL_NODE(_kern, OID_AUTO, cputimer, CTLFLAG_RW, NULL, "cputimer"); 269 270 SYSCTL_PROC(_kern_cputimer, OID_AUTO, select, CTLTYPE_STRING|CTLFLAG_RD, 271 NULL, NULL, sysctl_cputimer_reglist, "A", ""); 272 SYSCTL_PROC(_kern_cputimer, OID_AUTO, name, CTLTYPE_STRING|CTLFLAG_RD, 273 NULL, NULL, sysctl_cputimer_name, "A", ""); 274 SYSCTL_PROC(_kern_cputimer, OID_AUTO, clock, CTLTYPE_UINT|CTLFLAG_RD, 275 NULL, NULL, sysctl_cputimer_clock, "IU", ""); 276 SYSCTL_PROC(_kern_cputimer, OID_AUTO, freq, CTLTYPE_INT|CTLFLAG_RD, 277 NULL, NULL, sysctl_cputimer_freq, "I", ""); 278 279 280