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