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 * 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 #ifndef _SYS_INDEFINITE2_H_ 35 #define _SYS_INDEFINITE2_H_ 36 37 /* 38 * Indefinite info collection and handling code for contention loops 39 */ 40 #ifndef _SYS_INDEFINITE_H_ 41 #include <sys/indefinite.h> 42 #endif 43 #ifndef _SYS_GLOBALDATA_H_ 44 #include <sys/globaldata.h> 45 #endif 46 47 /* 48 * Initialize the indefinite state (only if the TSC is supported) 49 */ 50 static __inline void 51 indefinite_init(indefinite_info_t *info, void *lock_addr, const char *ident, 52 char now, char type) 53 { 54 info->lock_addr = lock_addr; 55 info->ident = ident; 56 info->secs = 0; 57 info->count = 0; 58 info->reported = now; 59 60 if (tsc_frequency) { 61 info->type = type; 62 /* info->base = rdtsc(); (see indefinite_check()) */ 63 } else { 64 info->type = 0; 65 info->base = 0; 66 } 67 if (now && info->ident) { 68 mycpu->gd_cnt.v_lock_addr = lock_addr; 69 mycpu->gd_cnt.v_lock_name[0] = info->type; 70 strncpy(mycpu->gd_cnt.v_lock_name + 1, info->ident, 71 sizeof(mycpu->gd_cnt.v_lock_name) - 2); 72 } 73 } 74 75 /* 76 * Update the state during any loop, record collision time in microseconds. 77 */ 78 static __inline int 79 indefinite_check(indefinite_info_t *info) 80 { 81 tsc_uclock_t delta; 82 const char *str; 83 int doreport; 84 85 #ifdef _KERNEL_VIRTUAL 86 vkernel_yield(); 87 #else 88 cpu_pause(); 89 #endif 90 if (info->type == 0) 91 return FALSE; 92 if (info->count == INDEF_INFO_START) { /* start recording time */ 93 if (indefinite_uses_rdtsc) 94 info->base = rdtsc(); 95 else 96 info->base = ticks; 97 if (info->reported == 0 && info->ident) { 98 mycpu->gd_cnt.v_lock_addr = info->lock_addr; 99 mycpu->gd_cnt.v_lock_name[0] = info->type; 100 strncpy(mycpu->gd_cnt.v_lock_name + 1, info->ident, 101 sizeof(mycpu->gd_cnt.v_lock_name) - 2); 102 info->reported = 1; 103 } 104 } 105 if ((++info->count & 127) != 127) 106 return FALSE; 107 info->count = 128; 108 if (indefinite_uses_rdtsc) 109 delta = rdtsc() - info->base; 110 else 111 delta = ticks - info->base; 112 113 #if defined(INVARIANTS) 114 if (lock_test_mode > 0) { 115 --lock_test_mode; 116 print_backtrace(8); 117 } 118 #endif 119 120 /* 121 * Ignore minor one-second interval error accumulation in 122 * favor of ensuring that info->base is fully synchronized. 123 */ 124 doreport = 0; 125 if (indefinite_uses_rdtsc) { 126 if (delta >= tsc_frequency) { 127 info->secs += delta / tsc_frequency; 128 info->base += delta; 129 mycpu->gd_cnt.v_lock_colls += 1000000U; 130 doreport = 1; 131 } 132 } else { 133 if (delta >= hz) { 134 info->secs += delta / hz; 135 info->base += delta; 136 mycpu->gd_cnt.v_lock_colls += 1000000U; 137 doreport = 1; 138 } 139 } 140 if (doreport) { 141 switch(info->type) { 142 case 's': 143 str = "spin_lock_sh"; 144 break; 145 case 'S': 146 str = "spin_lock_ex"; 147 break; 148 case 'm': 149 str = "mutex_sh"; 150 break; 151 case 'M': 152 str = "mutex_ex"; 153 break; 154 case 'l': 155 str = "lock_sh"; 156 break; 157 case 'L': 158 str = "lock_ex"; 159 break; 160 case 't': 161 str = "token"; 162 break; 163 default: 164 str = "lock(?)"; 165 break; 166 } 167 kprintf("%s: %s, indefinite wait (%d secs)!\n", 168 str, info->ident, info->secs); 169 if (panicstr) 170 return TRUE; 171 #if defined(INVARIANTS) 172 if (lock_test_mode) { 173 print_backtrace(-1); 174 return TRUE; 175 } 176 #endif 177 #if defined(INVARIANTS) 178 if (info->secs == 11 && 179 (info->type == 's' || info->type == 'S')) { 180 print_backtrace(-1); 181 } 182 #endif 183 if (info->secs == 60 && 184 (info->type == 's' || info->type == 'S')) { 185 panic("%s: %s, indefinite wait!", str, info->ident); 186 } 187 188 } 189 return FALSE; 190 } 191 192 /* 193 * Finalize the state, record collision time in microseconds if 194 * we got past the initial load. 195 */ 196 static __inline void 197 indefinite_done(indefinite_info_t *info) 198 { 199 tsc_uclock_t delta; 200 globaldata_t gd; 201 202 if (info->type && info->count > INDEF_INFO_START) { 203 gd = mycpu; 204 if (indefinite_uses_rdtsc) { 205 delta = rdtsc() - info->base; 206 delta = delta * 1000000U / tsc_frequency; 207 gd->gd_cnt.v_lock_colls += delta; 208 } else { 209 delta = ticks - info->base; 210 delta = delta * 1000000U / hz; 211 gd->gd_cnt.v_lock_colls += delta; 212 } 213 } 214 info->type = 0; 215 } 216 217 #endif 218