1 /* $OpenBSD: subr_evcount.c,v 1.16 2023/09/16 09:33:27 mpi 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/systm.h> 31 #include <sys/sysctl.h> 32 #include <sys/percpu.h> 33 34 static TAILQ_HEAD(,evcount) evcount_list = TAILQ_HEAD_INITIALIZER(evcount_list); 35 static TAILQ_HEAD(,evcount) evcount_percpu_init_list = 36 TAILQ_HEAD_INITIALIZER(evcount_percpu_init_list); 37 static int evcount_percpu_done; 38 39 void 40 evcount_attach(struct evcount *ec, const char *name, void *data) 41 { 42 static int nextid = 0; 43 44 memset(ec, 0, sizeof(*ec)); 45 ec->ec_name = name; 46 ec->ec_id = ++nextid; 47 ec->ec_data = data; 48 TAILQ_INSERT_TAIL(&evcount_list, ec, next); 49 } 50 51 void 52 evcount_percpu(struct evcount *ec) 53 { 54 if (evcount_percpu_done == 0) { 55 TAILQ_REMOVE(&evcount_list, ec, next); 56 TAILQ_INSERT_TAIL(&evcount_percpu_init_list, ec, next); 57 } else { 58 ec->ec_percpu = counters_alloc(1); 59 } 60 } 61 62 void 63 evcount_init_percpu(void) 64 { 65 struct evcount *ec; 66 67 KASSERT(evcount_percpu_done == 0); 68 TAILQ_FOREACH(ec, &evcount_percpu_init_list, next) { 69 ec->ec_percpu = counters_alloc(1); 70 counters_add(ec->ec_percpu, 0, ec->ec_count); 71 ec->ec_count = 0; 72 } 73 TAILQ_CONCAT(&evcount_list, &evcount_percpu_init_list, next); 74 evcount_percpu_done = 1; 75 } 76 77 void 78 evcount_detach(struct evcount *ec) 79 { 80 TAILQ_REMOVE(&evcount_list, ec, next); 81 if (ec->ec_percpu != NULL) { 82 counters_free(ec->ec_percpu, 1); 83 ec->ec_percpu = NULL; 84 } 85 } 86 87 void 88 evcount_inc(struct evcount *ec) 89 { 90 if (ec->ec_percpu != NULL) 91 counters_inc(ec->ec_percpu, 0); 92 else 93 ec->ec_count++; 94 } 95 96 #ifndef SMALL_KERNEL 97 98 int 99 evcount_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 100 void *newp, size_t newlen) 101 { 102 int error = 0, s, nintr, i; 103 struct evcount *ec; 104 uint64_t count, scratch; 105 106 if (newp != NULL) 107 return (EPERM); 108 109 if (name[0] != KERN_INTRCNT_NUM) { 110 if (namelen != 2) 111 return (ENOTDIR); 112 if (name[1] < 0) 113 return (EINVAL); 114 i = name[1]; 115 } else 116 i = -1; 117 118 nintr = 0; 119 TAILQ_FOREACH(ec, &evcount_list, next) { 120 if (nintr++ == i) 121 break; 122 } 123 124 switch (name[0]) { 125 case KERN_INTRCNT_NUM: 126 error = sysctl_rdint(oldp, oldlenp, NULL, nintr); 127 break; 128 case KERN_INTRCNT_CNT: 129 if (ec == NULL) 130 return (ENOENT); 131 if (ec->ec_percpu != NULL) { 132 counters_read(ec->ec_percpu, &count, 1, &scratch); 133 } else { 134 s = splhigh(); 135 count = ec->ec_count; 136 splx(s); 137 } 138 error = sysctl_rdquad(oldp, oldlenp, NULL, count); 139 break; 140 case KERN_INTRCNT_NAME: 141 if (ec == NULL) 142 return (ENOENT); 143 error = sysctl_rdstring(oldp, oldlenp, NULL, ec->ec_name); 144 break; 145 case KERN_INTRCNT_VECTOR: 146 if (ec == NULL || ec->ec_data == NULL) 147 return (ENOENT); 148 error = sysctl_rdint(oldp, oldlenp, NULL, 149 *((int *)ec->ec_data)); 150 break; 151 default: 152 error = EOPNOTSUPP; 153 break; 154 } 155 156 return (error); 157 } 158 #endif /* SMALL_KERNEL */ 159