xref: /dragonfly/sys/sys/indefinite2.h (revision f9993810)
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