1 /* 2 * Copyright (c) 2017 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 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Collects general statistics on a 10-second interval. 34 */ 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/kernel.h> 39 #include <sys/proc.h> 40 #include <sys/kinfo.h> 41 #include <sys/queue.h> 42 #include <sys/sysctl.h> 43 #include <sys/kthread.h> 44 #include <machine/cpu.h> 45 #include <sys/lock.h> 46 #include <sys/spinlock.h> 47 #include <sys/kcollect.h> 48 #include <sys/malloc.h> 49 50 #include <sys/thread2.h> 51 #include <sys/spinlock2.h> 52 53 #include <vm/vm.h> 54 #include <vm/vm_param.h> 55 #include <vm/vm_kern.h> 56 #include <vm/vm_object.h> 57 #include <vm/vm_page.h> 58 #include <vm/vm_map.h> 59 #include <vm/vm_pager.h> 60 #include <vm/vm_extern.h> 61 62 #include <machine/stdarg.h> 63 #include <machine/smp.h> 64 #include <machine/clock.h> 65 66 static uint32_t kcollect_samples = -1; /* 0 to disable */ 67 TUNABLE_INT("kern.collect_samples", &kcollect_samples); 68 SYSCTL_UINT(_kern, OID_AUTO, collect_samples, CTLFLAG_RD, 69 &kcollect_samples, 0, "number of 10-second samples"); 70 71 static uint64_t kcollect_index; 72 static const char *kcollect_slots[KCOLLECT_ENTRIES]; 73 static kcallback_t kcollect_callback[KCOLLECT_ENTRIES]; 74 static kcollect_t kcollect_scale; 75 static kcollect_t *kcollect_ary; 76 static struct lock kcollect_lock = LOCK_INITIALIZER("kcolk", 0, 0); 77 78 MALLOC_DEFINE(M_KCOLLECT, "kcollect", "kcollect array"); 79 80 int 81 kcollect_register(int which, const char *id, kcallback_t func, uint64_t scale) 82 { 83 int n; 84 85 lockmgr(&kcollect_lock, LK_EXCLUSIVE); 86 if (which >= 0) { 87 n = which; 88 } else { 89 for (n = KCOLLECT_DYNAMIC_START; n < KCOLLECT_ENTRIES; ++n) { 90 if (kcollect_slots[n] == NULL) 91 break; 92 } 93 } 94 if (n < 0 || n >= KCOLLECT_ENTRIES) { 95 n = -1; 96 } else { 97 kcollect_slots[n] = id; 98 kcollect_callback[n] = func; 99 kcollect_scale.data[n] = scale; 100 } 101 lockmgr(&kcollect_lock, LK_RELEASE); 102 103 return n; 104 } 105 106 void 107 kcollect_unregister(int n) 108 { 109 lockmgr(&kcollect_lock, LK_EXCLUSIVE); 110 if (n >= 0 && n < KCOLLECT_ENTRIES) { 111 kcollect_slots[n] = NULL; 112 kcollect_callback[n] = NULL; 113 } 114 lockmgr(&kcollect_lock, LK_RELEASE); 115 } 116 117 /* 118 * Typically called by a rollup function in the callback from the 119 * collection thread. Not usually called ad-hoc. This allows a 120 * subsystem to register several collection ids but only one callback 121 * which populates all of them. 122 */ 123 void 124 kcollect_setvalue(int n, uint64_t value) 125 { 126 uint32_t i; 127 128 if (n >= 0 && n < KCOLLECT_ENTRIES) { 129 i = kcollect_index % kcollect_samples; 130 kcollect_ary[i].data[n] = value; 131 } 132 } 133 134 /* 135 * Callback to change scale adjustment, if necessary. Certain statistics 136 * have scale info available (such as KCOLLECT_SWAPANO and SWAPCAC). 137 */ 138 void 139 kcollect_setscale(int n, uint64_t value) 140 { 141 if (n >= 0 && n < KCOLLECT_ENTRIES) { 142 kcollect_scale.data[n] = value; 143 } 144 } 145 146 static 147 void 148 kcollect_thread(void *dummy) 149 { 150 uint32_t i; 151 int n; 152 153 for (;;) { 154 lockmgr(&kcollect_lock, LK_EXCLUSIVE); 155 i = kcollect_index % kcollect_samples; 156 bzero(&kcollect_ary[i], sizeof(kcollect_ary[i])); 157 crit_enter(); 158 kcollect_ary[i].ticks = ticks; 159 getmicrotime(&kcollect_ary[i].realtime); 160 crit_exit(); 161 for (n = 0; n < KCOLLECT_ENTRIES; ++n) { 162 if (kcollect_callback[n]) { 163 kcollect_ary[i].data[n] = 164 kcollect_callback[n](n); 165 } 166 } 167 cpu_sfence(); 168 ++kcollect_index; 169 lockmgr(&kcollect_lock, LK_RELEASE); 170 tsleep(&dummy, 0, "sleep", hz * KCOLLECT_INTERVAL); 171 } 172 } 173 174 /* 175 * No requirements. 176 */ 177 static int 178 sysctl_kcollect_data(SYSCTL_HANDLER_ARGS) 179 { 180 int error; 181 uint32_t i; 182 uint32_t start; 183 uint64_t count; 184 kcollect_t scale; 185 kcollect_t id; 186 187 if (kcollect_samples == (uint32_t)-1 || 188 kcollect_samples == 0) { 189 return EINVAL; 190 } 191 192 error = 0; 193 count = kcollect_index; 194 start = count % kcollect_samples; 195 if (count >= kcollect_samples) 196 count = kcollect_samples - 1; 197 198 /* 199 * Sizing request 200 */ 201 if (req->oldptr == NULL) { 202 error = SYSCTL_OUT(req, 0, sizeof(kcollect_t) * (count + 2)); 203 return error; 204 } 205 206 /* 207 * Output request. We output a scale record, a string record, and 208 * N collection records. The strings in the string record can be 209 * up to 8 characters long, and if a string is 8 characters long it 210 * will not be zero-terminated. 211 * 212 * The low byte of the scale record specifies the format. To get 213 * the scale value shift right by 8. 214 */ 215 if (kcollect_ary == NULL) 216 return ENOTSUP; 217 218 lockmgr(&kcollect_lock, LK_EXCLUSIVE); 219 scale = kcollect_scale; 220 scale.ticks = ticks; 221 scale.hz = hz; 222 223 bzero(&id, sizeof(id)); 224 for (i = 0; i < KCOLLECT_ENTRIES; ++i) { 225 if (kcollect_slots[i]) { 226 char *ptr = (char *)&id.data[i]; 227 size_t len = strlen(kcollect_slots[i]); 228 if (len > sizeof(id.data[0])) 229 len = sizeof(id.data[0]); 230 bcopy(kcollect_slots[i], ptr, len); 231 } 232 } 233 lockmgr(&kcollect_lock, LK_RELEASE); 234 235 error = SYSCTL_OUT(req, &scale, sizeof(scale)); 236 if (error == 0) 237 error = SYSCTL_OUT(req, &id, sizeof(id)); 238 239 /* 240 * Start at the current entry (not yet populated) and work 241 * backwards. This allows callers of the sysctl to acquire 242 * a lesser amount of data aligned to the most recent side of 243 * the array. 244 */ 245 i = start; 246 while (count) { 247 if (req->oldlen - req->oldidx < sizeof(kcollect_t)) 248 break; 249 if (i == 0) 250 i = kcollect_samples - 1; 251 else 252 --i; 253 error = SYSCTL_OUT(req, &kcollect_ary[i], sizeof(kcollect_t)); 254 if (error) 255 break; 256 --count; 257 } 258 return error; 259 } 260 SYSCTL_PROC(_kern, OID_AUTO, collect_data, 261 CTLFLAG_RD | CTLTYPE_STRUCT, 0, 0, 262 sysctl_kcollect_data, "S,kcollect", "Dump collected statistics"); 263 264 static 265 void 266 kcollect_thread_init(void) 267 { 268 thread_t td = NULL; 269 270 /* 271 * Autosize sample retention (10 second interval) 272 */ 273 if ((int)kcollect_samples < 0) { 274 if (kmem_lim_size() < 1024) 275 kcollect_samples = 1024; 276 else 277 kcollect_samples = 8192; 278 } 279 280 if (kcollect_samples) { 281 kcollect_ary = kmalloc(kcollect_samples * sizeof(kcollect_t), 282 M_KCOLLECT, M_WAITOK | M_ZERO); 283 lwkt_create(kcollect_thread, NULL, &td, NULL, 0, 0, "kcollect"); 284 } 285 } 286 SYSINIT(kcol, SI_SUB_HELPER_THREADS, SI_ORDER_ANY, kcollect_thread_init, 0); 287