xref: /illumos-gate/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c (revision 4fceebdf03eeac0d7c58a4f70cc19b00a8c40a73)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * AMD Athlon64/Opteron CPU Module Machine-Check Poller
31  *
32  * The AMD Opteron processor doesn't yet report correctable errors via #mc's.
33  * Instead, it fixes the problem, silently updates the error state MSRs, and
34  * resumes operation.  In order to discover occurrances of correctable errors,
35  * we have to poll in the background using the omni cyclics mechanism.  The
36  * error injector also has the ability to manually request an immediate poll.
37  * Locking is fairly simple within the poller: the per-CPU mutex
38  * ao->ao_mca.ao_mca_poll_lock ensures that only one poll request is active.
39  */
40 
41 #include <sys/types.h>
42 #include <sys/sysmacros.h>
43 #include <sys/x86_archext.h>
44 #include <sys/ddi.h>
45 #include <sys/sunddi.h>
46 #include <sys/ksynch.h>
47 #include <sys/sdt.h>
48 
49 #include "ao.h"
50 
51 static uint_t ao_mca_poll_trace_nent = 100;
52 #ifdef DEBUG
53 static uint_t ao_mca_poll_trace_always = 1;
54 #else
55 static uint_t ao_mca_poll_trace_always = 0;
56 #endif
57 
58 cyclic_id_t ao_mca_poll_cycid;
59 hrtime_t ao_mca_poll_interval = NANOSEC * 10ULL;
60 
61 static void
62 ao_mca_poll_trace(ao_mca_t *mca, uint32_t what, uint32_t nerr)
63 {
64 	uint_t next;
65 	ao_mca_poll_trace_t *pt;
66 
67 	ASSERT(MUTEX_HELD(&mca->ao_mca_poll_lock));
68 	DTRACE_PROBE2(ao__poll__trace, uint32_t, what, uint32_t, nerr);
69 
70 	if (mca->ao_mca_poll_trace == NULL)
71 		return; /* poll trace buffer is disabled */
72 
73 	next = (mca->ao_mca_poll_curtrace + 1) % ao_mca_poll_trace_nent;
74 	pt = &mca->ao_mca_poll_trace[next];
75 
76 	pt->mpt_when = 0;
77 	pt->mpt_what = what;
78 
79 	if (what == AO_MPT_WHAT_CYC_ERR)
80 		pt->mpt_nerr = MIN(nerr, UINT8_MAX);
81 
82 	pt->mpt_when = gethrtime_waitfree();
83 	mca->ao_mca_poll_curtrace = next;
84 }
85 
86 /*
87  * Once aos_nb_poll_lock is acquired the caller must not block.  The
88  * ao_mca_trap code also requires that once we take the aos_nb_poll_lock
89  * that we do not get preempted so that it can check whether the
90  * thread it has interrupted is the lock owner.
91  */
92 static void
93 ao_mca_poll_common(ao_data_t *ao, int what, int pollnb)
94 {
95 	ao_mca_t *mca = &ao->ao_mca;
96 	ao_cpu_logout_t *acl = &mca->ao_mca_logout[AO_MCA_LOGOUT_POLLER];
97 	int i, n, fatal;
98 
99 	if (mca->ao_mca_flags & AO_MCA_F_UNFAULTING) {
100 		mca->ao_mca_flags &= ~AO_MCA_F_UNFAULTING;
101 		ao_mca_poll_trace(mca, AO_MPT_WHAT_UNFAULTING, 0);
102 
103 		/*
104 		 * On the first poll after re-enabling a faulty CPU we clear
105 		 * the status registers; see ao_faulted_exit() for more info.
106 		 */
107 		if (what == AO_MPT_WHAT_CYC_ERR) {
108 			for (i = 0; i < AMD_MCA_BANK_COUNT; i++)
109 				wrmsr(ao_bank_regs[i].abr_status, 0);
110 			return;
111 		}
112 	}
113 
114 	fatal = ao_mca_logout(acl, NULL, &n, !pollnb,
115 	    ao->ao_shared->aos_chiprev);
116 	ao_mca_poll_trace(mca, what, n);
117 
118 	if (fatal && cmi_panic_on_uncorrectable_error)
119 		fm_panic("Unrecoverable Machine-Check Error (polled)");
120 }
121 
122 /*
123  * Decide whether the caller should poll the NB.  The decision is made
124  * and any poll is performed under protection of the chip-wide aos_nb_poll_lock,
125  * so that assures that no two cores poll the NB at once.  To avoid the
126  * NB poll ping-ponging between different detectors we'll normally stick
127  * with the first winner.
128  */
129 static int
130 ao_mca_nb_pollowner(ao_data_t *ao)
131 {
132 	uint64_t last = ao->ao_shared->aos_nb_poll_timestamp;
133 	uint64_t now = gethrtime_waitfree();
134 	int rv = 0;
135 
136 	ASSERT(MUTEX_HELD(&ao->ao_shared->aos_nb_poll_lock));
137 
138 	if (now - last > 2 * ao_mca_poll_interval || last == 0) {
139 		/* Nominal owner making little progress - we'll take over */
140 		ao->ao_shared->aos_nb_poll_owner = CPU->cpu_id;
141 		rv = 1;
142 	} else if (CPU->cpu_id == ao->ao_shared->aos_nb_poll_owner) {
143 		rv = 1;
144 	}
145 
146 	if (rv == 1)
147 		ao->ao_shared->aos_nb_poll_timestamp = now;
148 
149 	return (rv);
150 }
151 
152 /*
153  * Wrapper called from cyclic handler or from an injector poke.
154  * In the former case we are a CYC_LOW_LEVEL handler while in the
155  * latter we're in user context so in both cases we are allowed
156  * to block.  Once we acquire the shared and adaptive aos_nb_poll_lock, however,
157  * we must not block or be preempted (see ao_mca_trap).
158  */
159 static void
160 ao_mca_poll_wrapper(void *arg, int what)
161 {
162 	ao_data_t *ao = arg;
163 	int pollnb;
164 
165 	if (ao == NULL)
166 		return;
167 
168 	mutex_enter(&ao->ao_mca.ao_mca_poll_lock);
169 	kpreempt_disable();
170 	mutex_enter(&ao->ao_shared->aos_nb_poll_lock);
171 
172 	if ((pollnb = ao_mca_nb_pollowner(ao)) == 0) {
173 		mutex_exit(&ao->ao_shared->aos_nb_poll_lock);
174 		kpreempt_enable();
175 	}
176 
177 	ao_mca_poll_common(ao, what, pollnb);
178 
179 	if (pollnb) {
180 		mutex_exit(&ao->ao_shared->aos_nb_poll_lock);
181 		kpreempt_enable();
182 	}
183 	mutex_exit(&ao->ao_mca.ao_mca_poll_lock);
184 }
185 
186 static void
187 ao_mca_poll_cyclic(void *arg)
188 {
189 	ao_mca_poll_wrapper(arg, AO_MPT_WHAT_CYC_ERR);
190 }
191 
192 void
193 ao_mca_poke(void *arg)
194 {
195 	ao_mca_poll_wrapper(arg, AO_MPT_WHAT_POKE_ERR);
196 }
197 
198 /*ARGSUSED*/
199 static void
200 ao_mca_poll_online(void *arg, cpu_t *cpu, cyc_handler_t *cyh, cyc_time_t *cyt)
201 {
202 	cyt->cyt_when = 0;
203 	cyh->cyh_level = CY_LOW_LEVEL;
204 
205 	/*
206 	 * If the CPU coming on-line isn't supported by this CPU module, then
207 	 * disable the cylic by cranking cyt_interval and setting arg to NULL.
208 	 */
209 	if (cpu->cpu_m.mcpu_cmi != NULL &&
210 	    cpu->cpu_m.mcpu_cmi->cmi_ops != &_cmi_ops) {
211 		cyt->cyt_interval = INT64_MAX;
212 		cyh->cyh_func = ao_mca_poll_cyclic;
213 		cyh->cyh_arg = NULL;
214 	} else {
215 		cyt->cyt_interval = ao_mca_poll_interval;
216 		cyh->cyh_func = ao_mca_poll_cyclic;
217 		cyh->cyh_arg = cpu->cpu_m.mcpu_cmidata;
218 	}
219 }
220 
221 /*ARGSUSED*/
222 static void
223 ao_mca_poll_offline(void *arg, cpu_t *cpu, void *cyh_arg)
224 {
225 	ao_data_t *ao = cpu->cpu_m.mcpu_cmidata;
226 
227 	/*
228 	 * Any sibling core may begin to poll NB MCA registers
229 	 */
230 	if (cpu->cpu_id == ao->ao_shared->aos_nb_poll_owner)
231 		ao->ao_shared->aos_nb_poll_timestamp = 0;
232 }
233 
234 void
235 ao_mca_poll_init(ao_data_t *ao, int donb)
236 {
237 	ao_mca_t *mca = &ao->ao_mca;
238 
239 	mutex_init(&mca->ao_mca_poll_lock, NULL, MUTEX_DRIVER, NULL);
240 
241 	if (donb)
242 		mutex_init(&ao->ao_shared->aos_nb_poll_lock, NULL, MUTEX_DRIVER,
243 		    NULL);
244 
245 	if (ao_mca_poll_trace_always) {
246 		mca->ao_mca_poll_trace =
247 		    kmem_zalloc(sizeof (ao_mca_poll_trace_t) *
248 		    ao_mca_poll_trace_nent, KM_SLEEP);
249 		mca->ao_mca_poll_curtrace = 0;
250 	}
251 }
252 
253 void
254 ao_mca_poll_start(void)
255 {
256 	cyc_omni_handler_t cyo;
257 
258 	if (ao_mca_poll_interval == 0)
259 		return; /* if manually tuned to zero, disable polling */
260 
261 	cyo.cyo_online = ao_mca_poll_online;
262 	cyo.cyo_offline = ao_mca_poll_offline;
263 	cyo.cyo_arg = NULL;
264 
265 	mutex_enter(&cpu_lock);
266 	ao_mca_poll_cycid = cyclic_add_omni(&cyo);
267 	mutex_exit(&cpu_lock);
268 }
269