1917Selowe /*
2917Selowe  * CDDL HEADER START
3917Selowe  *
4917Selowe  * The contents of this file are subject to the terms of the
53253Smec  * Common Development and Distribution License (the "License").
63253Smec  * You may not use this file except in compliance with the License.
7917Selowe  *
8917Selowe  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9917Selowe  * or http://www.opensolaris.org/os/licensing.
10917Selowe  * See the License for the specific language governing permissions
11917Selowe  * and limitations under the License.
12917Selowe  *
13917Selowe  * When distributing Covered Code, include this CDDL HEADER in each
14917Selowe  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15917Selowe  * If applicable, add the following below this CDDL HEADER, with the
16917Selowe  * fields enclosed by brackets "[]" replaced with your own identifying
17917Selowe  * information: Portions Copyright [yyyy] [name of copyright owner]
18917Selowe  *
19917Selowe  * CDDL HEADER END
20917Selowe  */
21917Selowe /*
228555SJustin.Frank@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23917Selowe  * Use is subject to license terms.
24917Selowe  */
25917Selowe 
26917Selowe /*
27917Selowe  * Page Retire - Big Theory Statement.
28917Selowe  *
29917Selowe  * This file handles removing sections of faulty memory from use when the
30917Selowe  * user land FMA Diagnosis Engine requests that a page be removed or when
31917Selowe  * a CE or UE is detected by the hardware.
32917Selowe  *
33917Selowe  * In the bad old days, the kernel side of Page Retire did a lot of the work
34917Selowe  * on its own. Now, with the DE keeping track of errors, the kernel side is
35917Selowe  * rather simple minded on most platforms.
36917Selowe  *
37917Selowe  * Errors are all reflected to the DE, and after digesting the error and
38917Selowe  * looking at all previously reported errors, the DE decides what should
39917Selowe  * be done about the current error. If the DE wants a particular page to
40917Selowe  * be retired, then the kernel page retire code is invoked via an ioctl.
41917Selowe  * On non-FMA platforms, the ue_drain and ce_drain paths ends up calling
42917Selowe  * page retire to handle the error. Since page retire is just a simple
43917Selowe  * mechanism it doesn't need to differentiate between the different callers.
44917Selowe  *
45917Selowe  * The p_toxic field in the page_t is used to indicate which errors have
46917Selowe  * occurred and what action has been taken on a given page. Because errors are
47917Selowe  * reported without regard to the locked state of a page, no locks are used
48917Selowe  * to SET the error bits in p_toxic. However, in order to clear the error
49917Selowe  * bits, the page_t must be held exclusively locked.
50917Selowe  *
51917Selowe  * When page_retire() is called, it must be able to acquire locks, sleep, etc.
52917Selowe  * It must not be called from high-level interrupt context.
53917Selowe  *
54917Selowe  * Depending on how the requested page is being used at the time of the retire
55917Selowe  * request (and on the availability of sufficient system resources), the page
56917Selowe  * may be retired immediately, or just marked for retirement later. For
57917Selowe  * example, locked pages are marked, while free pages are retired. Multiple
58917Selowe  * requests may be made to retire the same page, although there is no need
59917Selowe  * to: once the p_toxic flags are set, the page will be retired as soon as it
60917Selowe  * can be exclusively locked.
61917Selowe  *
62917Selowe  * The retire mechanism is driven centrally out of page_unlock(). To expedite
63917Selowe  * the retirement of pages, further requests for SE_SHARED locks are denied
64917Selowe  * as long as a page retirement is pending. In addition, as long as pages are
65917Selowe  * pending retirement a background thread runs periodically trying to retire
66917Selowe  * those pages. Pages which could not be retired while the system is running
67917Selowe  * are scrubbed prior to rebooting to avoid latent errors on the next boot.
68917Selowe  *
691338Selowe  * UE pages without persistent errors are scrubbed and returned to service.
701338Selowe  * Recidivist pages, as well as FMA-directed requests for retirement, result
711338Selowe  * in the page being taken out of service. Once the decision is made to take
721338Selowe  * a page out of service, the page is cleared, hashed onto the retired_pages
731338Selowe  * vnode, marked as retired, and it is unlocked.  No other requesters (except
741338Selowe  * for unretire) are allowed to lock retired pages.
75917Selowe  *
76917Selowe  * The public routines return (sadly) 0 if they worked and a non-zero error
77917Selowe  * value if something went wrong. This is done for the ioctl side of the
78917Selowe  * world to allow errors to be reflected all the way out to user land. The
79917Selowe  * non-zero values are explained in comments atop each function.
80917Selowe  */
81917Selowe 
82917Selowe /*
83917Selowe  * Things to fix:
84917Selowe  *
853253Smec  * 	1. Trying to retire non-relocatable kvp pages may result in a
86917Selowe  *      quagmire. This is because seg_kmem() no longer keeps its pages locked,
87917Selowe  *      and calls page_lookup() in the free path; since kvp pages are modified
88917Selowe  *      and don't have a usable backing store, page_retire() can't do anything
89917Selowe  *      with them, and we'll keep denying the lock to seg_kmem_free() in a
90917Selowe  *      vicious cycle. To prevent that, we don't deny locks to kvp pages, and
913253Smec  *      hence only try to retire a page from page_unlock() in the free path.
92917Selowe  *      Since most kernel pages are indefinitely held anyway, and don't
93917Selowe  *      participate in I/O, this is of little consequence.
94917Selowe  *
953253Smec  *      2. Low memory situations will be interesting. If we don't have
96917Selowe  *      enough memory for page_relocate() to succeed, we won't be able to
97917Selowe  *      retire dirty pages; nobody will be able to push them out to disk
98917Selowe  *      either, since we aggressively deny the page lock. We could change
99917Selowe  *      fsflush so it can recognize this situation, grab the lock, and push
100917Selowe  *      the page out, where we'll catch it in the free path and retire it.
101917Selowe  *
1023253Smec  *	3. Beware of places that have code like this in them:
103917Selowe  *
104917Selowe  *		if (! page_tryupgrade(pp)) {
105917Selowe  *			page_unlock(pp);
106917Selowe  *			while (! page_lock(pp, SE_EXCL, NULL, P_RECLAIM)) {
107917Selowe  *				/ *NOTHING* /
108917Selowe  *			}
109917Selowe  *		}
110917Selowe  *		page_free(pp);
111917Selowe  *
112917Selowe  *	The problem is that pp can change identity right after the
113917Selowe  *	page_unlock() call.  In particular, page_retire() can step in
114917Selowe  *	there, change pp's identity, and hash pp onto the retired_vnode.
115917Selowe  *
116917Selowe  *	Of course, other functions besides page_retire() can have the
117917Selowe  *	same effect. A kmem reader can waltz by, set up a mapping to the
118917Selowe  *	page, and then unlock the page. Page_free() will then go castors
119917Selowe  *	up. So if anybody is doing this, it's already a bug.
120917Selowe  *
1213253Smec  *      4. mdboot()'s call into page_retire_mdboot() should probably be
122917Selowe  *      moved lower. Where the call is made now, we can get into trouble
123917Selowe  *      by scrubbing a kernel page that is then accessed later.
124917Selowe  */
125917Selowe 
126917Selowe #include <sys/types.h>
127917Selowe #include <sys/param.h>
128917Selowe #include <sys/systm.h>
129917Selowe #include <sys/mman.h>
130917Selowe #include <sys/vnode.h>
1313898Srsb #include <sys/vfs_opreg.h>
132917Selowe #include <sys/cmn_err.h>
133917Selowe #include <sys/ksynch.h>
134917Selowe #include <sys/thread.h>
135917Selowe #include <sys/disp.h>
136917Selowe #include <sys/ontrap.h>
137917Selowe #include <sys/vmsystm.h>
138917Selowe #include <sys/mem_config.h>
139917Selowe #include <sys/atomic.h>
140917Selowe #include <sys/callb.h>
141917Selowe #include <vm/page.h>
142917Selowe #include <vm/vm_dep.h>
143917Selowe #include <vm/as.h>
144917Selowe #include <vm/hat.h>
145*11185SSean.McEnroe@Sun.COM #include <vm/seg_kmem.h>
146917Selowe 
147917Selowe /*
148917Selowe  * vnode for all pages which are retired from the VM system;
149917Selowe  */
150917Selowe vnode_t *retired_pages;
151917Selowe 
1523253Smec static int page_retire_pp_finish(page_t *, void *, uint_t);
153917Selowe 
154917Selowe /*
155917Selowe  * Make a list of all of the pages that have been marked for retirement
156917Selowe  * but are not yet retired.  At system shutdown, we will scrub all of the
157917Selowe  * pages in the list in case there are outstanding UEs.  Then, we
158917Selowe  * cross-check this list against the number of pages that are yet to be
159917Selowe  * retired, and if we find inconsistencies, we scan every page_t in the
160917Selowe  * whole system looking for any pages that need to be scrubbed for UEs.
161917Selowe  * The background thread also uses this queue to determine which pages
162917Selowe  * it should keep trying to retire.
163917Selowe  */
164917Selowe #ifdef	DEBUG
165917Selowe #define	PR_PENDING_QMAX	32
166917Selowe #else	/* DEBUG */
167917Selowe #define	PR_PENDING_QMAX	256
168917Selowe #endif	/* DEBUG */
169917Selowe page_t		*pr_pending_q[PR_PENDING_QMAX];
170917Selowe kmutex_t	pr_q_mutex;
171917Selowe 
172917Selowe /*
173917Selowe  * Page retire global kstats
174917Selowe  */
175917Selowe struct page_retire_kstat {
176917Selowe 	kstat_named_t	pr_retired;
177917Selowe 	kstat_named_t	pr_requested;
178917Selowe 	kstat_named_t	pr_requested_free;
179917Selowe 	kstat_named_t	pr_enqueue_fail;
180917Selowe 	kstat_named_t	pr_dequeue_fail;
181917Selowe 	kstat_named_t	pr_pending;
1829544SChristopher.Baumbauer@Sun.COM 	kstat_named_t	pr_pending_kas;
183917Selowe 	kstat_named_t	pr_failed;
184917Selowe 	kstat_named_t	pr_failed_kernel;
185917Selowe 	kstat_named_t	pr_limit;
186917Selowe 	kstat_named_t	pr_limit_exceeded;
187917Selowe 	kstat_named_t	pr_fma;
188917Selowe 	kstat_named_t	pr_mce;
189917Selowe 	kstat_named_t	pr_ue;
190917Selowe 	kstat_named_t	pr_ue_cleared_retire;
191917Selowe 	kstat_named_t	pr_ue_cleared_free;
192917Selowe 	kstat_named_t	pr_ue_persistent;
193917Selowe 	kstat_named_t	pr_unretired;
194917Selowe };
195917Selowe 
196917Selowe static struct page_retire_kstat page_retire_kstat = {
197917Selowe 	{ "pages_retired",		KSTAT_DATA_UINT64},
198917Selowe 	{ "pages_retire_request",	KSTAT_DATA_UINT64},
199917Selowe 	{ "pages_retire_request_free",	KSTAT_DATA_UINT64},
200917Selowe 	{ "pages_notenqueued", 		KSTAT_DATA_UINT64},
201917Selowe 	{ "pages_notdequeued", 		KSTAT_DATA_UINT64},
202917Selowe 	{ "pages_pending", 		KSTAT_DATA_UINT64},
2039544SChristopher.Baumbauer@Sun.COM 	{ "pages_pending_kas", 		KSTAT_DATA_UINT64},
204917Selowe 	{ "pages_deferred",		KSTAT_DATA_UINT64},
205917Selowe 	{ "pages_deferred_kernel",	KSTAT_DATA_UINT64},
206917Selowe 	{ "pages_limit",		KSTAT_DATA_UINT64},
207917Selowe 	{ "pages_limit_exceeded",	KSTAT_DATA_UINT64},
208917Selowe 	{ "pages_fma",			KSTAT_DATA_UINT64},
209917Selowe 	{ "pages_multiple_ce",		KSTAT_DATA_UINT64},
210917Selowe 	{ "pages_ue",			KSTAT_DATA_UINT64},
211917Selowe 	{ "pages_ue_cleared_retired",	KSTAT_DATA_UINT64},
212917Selowe 	{ "pages_ue_cleared_freed",	KSTAT_DATA_UINT64},
213917Selowe 	{ "pages_ue_persistent",	KSTAT_DATA_UINT64},
214917Selowe 	{ "pages_unretired",		KSTAT_DATA_UINT64},
215917Selowe };
216917Selowe 
217917Selowe static kstat_t  *page_retire_ksp = NULL;
218917Selowe 
219917Selowe #define	PR_INCR_KSTAT(stat)	\
220917Selowe 	atomic_add_64(&(page_retire_kstat.stat.value.ui64), 1)
221917Selowe #define	PR_DECR_KSTAT(stat)	\
222917Selowe 	atomic_add_64(&(page_retire_kstat.stat.value.ui64), -1)
223917Selowe 
224917Selowe #define	PR_KSTAT_RETIRED_CE	(page_retire_kstat.pr_mce.value.ui64)
225917Selowe #define	PR_KSTAT_RETIRED_FMA	(page_retire_kstat.pr_fma.value.ui64)
226917Selowe #define	PR_KSTAT_RETIRED_NOTUE	(PR_KSTAT_RETIRED_CE + PR_KSTAT_RETIRED_FMA)
227917Selowe #define	PR_KSTAT_PENDING	(page_retire_kstat.pr_pending.value.ui64)
2289544SChristopher.Baumbauer@Sun.COM #define	PR_KSTAT_PENDING_KAS	(page_retire_kstat.pr_pending_kas.value.ui64)
229917Selowe #define	PR_KSTAT_EQFAIL		(page_retire_kstat.pr_enqueue_fail.value.ui64)
230917Selowe #define	PR_KSTAT_DQFAIL		(page_retire_kstat.pr_dequeue_fail.value.ui64)
231917Selowe 
232917Selowe /*
2333253Smec  * page retire kstats to list all retired pages
2343253Smec  */
2353253Smec static int pr_list_kstat_update(kstat_t *ksp, int rw);
2363253Smec static int pr_list_kstat_snapshot(kstat_t *ksp, void *buf, int rw);
2373253Smec kmutex_t pr_list_kstat_mutex;
2383253Smec 
2393253Smec /*
240917Selowe  * Limit the number of multiple CE page retires.
241917Selowe  * The default is 0.1% of physmem, or 1 in 1000 pages. This is set in
242917Selowe  * basis points, where 100 basis points equals one percent.
243917Selowe  */
244917Selowe #define	MCE_BPT	10
245917Selowe uint64_t	max_pages_retired_bps = MCE_BPT;
246917Selowe #define	PAGE_RETIRE_LIMIT	((physmem * max_pages_retired_bps) / 10000)
247917Selowe 
248917Selowe /*
249917Selowe  * Control over the verbosity of page retirement.
250917Selowe  *
251917Selowe  * When set to zero (the default), no messages will be printed.
252917Selowe  * When set to one, summary messages will be printed.
253917Selowe  * When set > one, all messages will be printed.
254917Selowe  *
255917Selowe  * A value of one will trigger detailed messages for retirement operations,
256917Selowe  * and is intended as a platform tunable for processors where FMA's DE does
257917Selowe  * not run (e.g., spitfire). Values > one are intended for debugging only.
258917Selowe  */
259917Selowe int page_retire_messages = 0;
260917Selowe 
261917Selowe /*
262917Selowe  * Control whether or not we return scrubbed UE pages to service.
263917Selowe  * By default we do not since FMA wants to run its diagnostics first
264917Selowe  * and then ask us to unretire the page if it passes. Non-FMA platforms
265917Selowe  * may set this to zero so we will only retire recidivist pages. It should
266917Selowe  * not be changed by the user.
267917Selowe  */
268917Selowe int page_retire_first_ue = 1;
269917Selowe 
270917Selowe /*
271917Selowe  * Master enable for page retire. This prevents a CE or UE early in boot
272917Selowe  * from trying to retire a page before page_retire_init() has finished
273917Selowe  * setting things up. This is internal only and is not a tunable!
274917Selowe  */
275917Selowe static int pr_enable = 0;
276917Selowe 
277917Selowe #ifdef	DEBUG
278917Selowe struct page_retire_debug {
2791381Selowe 	int prd_dup1;
2801381Selowe 	int prd_dup2;
2811381Selowe 	int prd_qdup;
282917Selowe 	int prd_noaction;
283917Selowe 	int prd_queued;
284917Selowe 	int prd_notqueued;
285917Selowe 	int prd_dequeue;
286917Selowe 	int prd_top;
287917Selowe 	int prd_locked;
288917Selowe 	int prd_reloc;
289973Selowe 	int prd_relocfail;
290973Selowe 	int prd_mod;
291973Selowe 	int prd_mod_late;
292917Selowe 	int prd_kern;
293917Selowe 	int prd_free;
294917Selowe 	int prd_noreclaim;
295917Selowe 	int prd_hashout;
296917Selowe 	int prd_fma;
297917Selowe 	int prd_uescrubbed;
298917Selowe 	int prd_uenotscrubbed;
299917Selowe 	int prd_mce;
300917Selowe 	int prd_prlocked;
301917Selowe 	int prd_prnotlocked;
302917Selowe 	int prd_prretired;
303917Selowe 	int prd_ulocked;
304917Selowe 	int prd_unotretired;
305917Selowe 	int prd_udestroy;
306917Selowe 	int prd_uhashout;
307917Selowe 	int prd_uunretired;
308917Selowe 	int prd_unotlocked;
309917Selowe 	int prd_checkhit;
3101381Selowe 	int prd_checkmiss_pend;
3111381Selowe 	int prd_checkmiss_noerr;
312917Selowe 	int prd_tctop;
313917Selowe 	int prd_tclocked;
314917Selowe 	int prd_hunt;
315917Selowe 	int prd_dohunt;
316917Selowe 	int prd_earlyhunt;
317917Selowe 	int prd_latehunt;
318917Selowe 	int prd_nofreedemote;
319917Selowe 	int prd_nodemote;
320917Selowe 	int prd_demoted;
321917Selowe } pr_debug;
322917Selowe 
323917Selowe #define	PR_DEBUG(foo)	((pr_debug.foo)++)
324917Selowe 
325917Selowe /*
326917Selowe  * A type histogram. We record the incidence of the various toxic
327917Selowe  * flag combinations along with the interesting page attributes. The
328917Selowe  * goal is to get as many combinations as we can while driving all
329917Selowe  * pr_debug values nonzero (indicating we've exercised all possible
330917Selowe  * code paths across all possible page types). Not all combinations
331917Selowe  * will make sense -- e.g. PRT_MOD|PRT_KERNEL.
332917Selowe  *
333917Selowe  * pr_type offset bit encoding (when examining with a debugger):
334917Selowe  *
335917Selowe  *    PRT_NAMED  - 0x4
336917Selowe  *    PRT_KERNEL - 0x8
337917Selowe  *    PRT_FREE   - 0x10
338917Selowe  *    PRT_MOD    - 0x20
339917Selowe  *    PRT_FMA    - 0x0
340917Selowe  *    PRT_MCE    - 0x40
341917Selowe  *    PRT_UE     - 0x80
342917Selowe  */
343917Selowe 
344917Selowe #define	PRT_NAMED	0x01
345917Selowe #define	PRT_KERNEL	0x02
346917Selowe #define	PRT_FREE	0x04
347917Selowe #define	PRT_MOD		0x08
348917Selowe #define	PRT_FMA		0x00	/* yes, this is not a mistake */
349917Selowe #define	PRT_MCE		0x10
350917Selowe #define	PRT_UE		0x20
351917Selowe #define	PRT_ALL		0x3F
352917Selowe 
353917Selowe int pr_types[PRT_ALL+1];
354917Selowe 
355917Selowe #define	PR_TYPES(pp)	{			\
356917Selowe 	int whichtype = 0;			\
357917Selowe 	if (pp->p_vnode)			\
358917Selowe 		whichtype |= PRT_NAMED;		\
3593290Sjohansen 	if (PP_ISKAS(pp))			\
360917Selowe 		whichtype |= PRT_KERNEL;	\
361917Selowe 	if (PP_ISFREE(pp))			\
362917Selowe 		whichtype |= PRT_FREE;		\
363917Selowe 	if (hat_ismod(pp))			\
364917Selowe 		whichtype |= PRT_MOD;		\
365917Selowe 	if (pp->p_toxic & PR_UE)		\
366917Selowe 		whichtype |= PRT_UE;		\
367917Selowe 	if (pp->p_toxic & PR_MCE)		\
368917Selowe 		whichtype |= PRT_MCE;		\
369917Selowe 	pr_types[whichtype]++;			\
370917Selowe }
371917Selowe 
372917Selowe int recl_calls;
373917Selowe int recl_mtbf = 3;
374917Selowe int reloc_calls;
375917Selowe int reloc_mtbf = 7;
376917Selowe int pr_calls;
377917Selowe int pr_mtbf = 15;
378917Selowe 
379917Selowe #define	MTBF(v, f)	(((++(v)) & (f)) != (f))
380917Selowe 
381917Selowe #else	/* DEBUG */
382917Selowe 
383917Selowe #define	PR_DEBUG(foo)	/* nothing */
384917Selowe #define	PR_TYPES(foo)	/* nothing */
385917Selowe #define	MTBF(v, f)	(1)
386917Selowe 
387917Selowe #endif	/* DEBUG */
388917Selowe 
389917Selowe /*
390917Selowe  * page_retire_done() - completion processing
391917Selowe  *
392917Selowe  * Used by the page_retire code for common completion processing.
393917Selowe  * It keeps track of how many times a given result has happened,
394917Selowe  * and writes out an occasional message.
395917Selowe  *
396917Selowe  * May be called with a NULL pp (PRD_INVALID_PA case).
397917Selowe  */
398917Selowe #define	PRD_INVALID_KEY		-1
399917Selowe #define	PRD_SUCCESS		0
400917Selowe #define	PRD_PENDING		1
401917Selowe #define	PRD_FAILED		2
402917Selowe #define	PRD_DUPLICATE		3
403917Selowe #define	PRD_INVALID_PA		4
404917Selowe #define	PRD_LIMIT		5
405917Selowe #define	PRD_UE_SCRUBBED		6
406917Selowe #define	PRD_UNR_SUCCESS		7
407917Selowe #define	PRD_UNR_CANTLOCK	8
408917Selowe #define	PRD_UNR_NOT		9
409917Selowe 
410917Selowe typedef struct page_retire_op {
411917Selowe 	int	pr_key;		/* one of the PRD_* defines from above */
412917Selowe 	int	pr_count;	/* How many times this has happened */
413917Selowe 	int	pr_retval;	/* return value */
414917Selowe 	int	pr_msglvl;	/* message level - when to print */
415917Selowe 	char	*pr_message;	/* Cryptic message for field service */
416917Selowe } page_retire_op_t;
417917Selowe 
418917Selowe static page_retire_op_t page_retire_ops[] = {
419917Selowe 	/* key			count	retval	msglvl	message */
420917Selowe 	{PRD_SUCCESS,		0,	0,	1,
421917Selowe 		"Page 0x%08x.%08x removed from service"},
422917Selowe 	{PRD_PENDING,		0,	EAGAIN,	2,
423917Selowe 		"Page 0x%08x.%08x will be retired on free"},
424917Selowe 	{PRD_FAILED,		0,	EAGAIN,	0, NULL},
4251381Selowe 	{PRD_DUPLICATE,		0,	EIO,	2,
4261381Selowe 		"Page 0x%08x.%08x already retired or pending"},
427917Selowe 	{PRD_INVALID_PA,	0,	EINVAL, 2,
428917Selowe 		"PA 0x%08x.%08x is not a relocatable page"},
429917Selowe 	{PRD_LIMIT,		0,	0,	1,
430917Selowe 		"Page 0x%08x.%08x not retired due to limit exceeded"},
431917Selowe 	{PRD_UE_SCRUBBED,	0,	0,	1,
432917Selowe 		"Previously reported error on page 0x%08x.%08x cleared"},
433917Selowe 	{PRD_UNR_SUCCESS,	0,	0,	1,
434917Selowe 		"Page 0x%08x.%08x returned to service"},
435917Selowe 	{PRD_UNR_CANTLOCK,	0,	EAGAIN,	2,
436917Selowe 		"Page 0x%08x.%08x could not be unretired"},
4371381Selowe 	{PRD_UNR_NOT,		0,	EIO,	2,
438917Selowe 		"Page 0x%08x.%08x is not retired"},
439917Selowe 	{PRD_INVALID_KEY,	0,	0,	0, NULL} /* MUST BE LAST! */
440917Selowe };
441917Selowe 
442917Selowe /*
443917Selowe  * print a message if page_retire_messages is true.
444917Selowe  */
445917Selowe #define	PR_MESSAGE(debuglvl, msglvl, msg, pa)				\
446917Selowe {									\
447917Selowe 	uint64_t p = (uint64_t)pa;					\
448917Selowe 	if (page_retire_messages >= msglvl && msg != NULL) {		\
449917Selowe 		cmn_err(debuglvl, msg,					\
450917Selowe 		    (uint32_t)(p >> 32), (uint32_t)p);			\
451917Selowe 	}								\
452917Selowe }
453917Selowe 
454917Selowe /*
455917Selowe  * Note that multiple bits may be set in a single settoxic operation.
456917Selowe  * May be called without the page locked.
457917Selowe  */
458917Selowe void
459917Selowe page_settoxic(page_t *pp, uchar_t bits)
460917Selowe {
461917Selowe 	atomic_or_8(&pp->p_toxic, bits);
462917Selowe }
463917Selowe 
464917Selowe /*
465917Selowe  * Note that multiple bits may cleared in a single clrtoxic operation.
4661338Selowe  * Must be called with the page exclusively locked to prevent races which
4671338Selowe  * may attempt to retire a page without any toxic bits set.
4683253Smec  * Note that the PR_CAPTURE bit can be cleared without the exclusive lock
4693253Smec  * being held as there is a separate mutex which protects that bit.
470917Selowe  */
471917Selowe void
472917Selowe page_clrtoxic(page_t *pp, uchar_t bits)
473917Selowe {
4743253Smec 	ASSERT((bits & PR_CAPTURE) || PAGE_EXCL(pp));
475917Selowe 	atomic_and_8(&pp->p_toxic, ~bits);
476917Selowe }
477917Selowe 
478917Selowe /*
479917Selowe  * Prints any page retire messages to the user, and decides what
480917Selowe  * error code is appropriate for the condition reported.
481917Selowe  */
482917Selowe static int
483917Selowe page_retire_done(page_t *pp, int code)
484917Selowe {
485917Selowe 	page_retire_op_t *prop;
486917Selowe 	uint64_t	pa = 0;
487917Selowe 	int		i;
488917Selowe 
489917Selowe 	if (pp != NULL) {
4901338Selowe 		pa = mmu_ptob((uint64_t)pp->p_pagenum);
491917Selowe 	}
492917Selowe 
493917Selowe 	prop = NULL;
494917Selowe 	for (i = 0; page_retire_ops[i].pr_key != PRD_INVALID_KEY; i++) {
495917Selowe 		if (page_retire_ops[i].pr_key == code) {
496917Selowe 			prop = &page_retire_ops[i];
497917Selowe 			break;
498917Selowe 		}
499917Selowe 	}
500917Selowe 
501917Selowe #ifdef	DEBUG
502917Selowe 	if (page_retire_ops[i].pr_key == PRD_INVALID_KEY) {
503917Selowe 		cmn_err(CE_PANIC, "page_retire_done: Invalid opcode %d", code);
504917Selowe 	}
505917Selowe #endif
506917Selowe 
507917Selowe 	ASSERT(prop->pr_key == code);
508917Selowe 
509917Selowe 	prop->pr_count++;
510917Selowe 
511917Selowe 	PR_MESSAGE(CE_NOTE, prop->pr_msglvl, prop->pr_message, pa);
512917Selowe 	if (pp != NULL) {
513917Selowe 		page_settoxic(pp, PR_MSG);
514917Selowe 	}
515917Selowe 
516917Selowe 	return (prop->pr_retval);
517917Selowe }
518917Selowe 
519917Selowe /*
520917Selowe  * Act like page_destroy(), but instead of freeing the page, hash it onto
521917Selowe  * the retired_pages vnode, and mark it retired.
522917Selowe  *
523917Selowe  * For fun, we try to scrub the page until it's squeaky clean.
524917Selowe  * availrmem is adjusted here.
525917Selowe  */
526917Selowe static void
527917Selowe page_retire_destroy(page_t *pp)
528917Selowe {
529973Selowe 	u_offset_t off = (u_offset_t)((uintptr_t)pp);
530973Selowe 
531917Selowe 	ASSERT(PAGE_EXCL(pp));
532917Selowe 	ASSERT(!PP_ISFREE(pp));
533917Selowe 	ASSERT(pp->p_szc == 0);
534917Selowe 	ASSERT(!hat_page_is_mapped(pp));
535917Selowe 	ASSERT(!pp->p_vnode);
536917Selowe 
53710271SJason.Beloro@Sun.COM 	page_clr_all_props(pp);
538917Selowe 	pagescrub(pp, 0, MMU_PAGESIZE);
539917Selowe 
540917Selowe 	pp->p_next = NULL;
541917Selowe 	pp->p_prev = NULL;
542973Selowe 	if (page_hashin(pp, retired_pages, off, NULL) == 0) {
543917Selowe 		cmn_err(CE_PANIC, "retired page %p hashin failed", (void *)pp);
544917Selowe 	}
545917Selowe 
546917Selowe 	page_settoxic(pp, PR_RETIRED);
547917Selowe 	PR_INCR_KSTAT(pr_retired);
548917Selowe 
549917Selowe 	if (pp->p_toxic & PR_FMA) {
550917Selowe 		PR_INCR_KSTAT(pr_fma);
551917Selowe 	} else if (pp->p_toxic & PR_UE) {
552917Selowe 		PR_INCR_KSTAT(pr_ue);
553917Selowe 	} else {
554917Selowe 		PR_INCR_KSTAT(pr_mce);
555917Selowe 	}
556917Selowe 
557917Selowe 	mutex_enter(&freemem_lock);
558917Selowe 	availrmem--;
559917Selowe 	mutex_exit(&freemem_lock);
560917Selowe 
561917Selowe 	page_unlock(pp);
562917Selowe }
563917Selowe 
564917Selowe /*
565917Selowe  * Check whether the number of pages which have been retired already exceeds
566917Selowe  * the maximum allowable percentage of memory which may be retired.
567917Selowe  *
568917Selowe  * Returns 1 if the limit has been exceeded.
569917Selowe  */
570917Selowe static int
571917Selowe page_retire_limit(void)
572917Selowe {
573917Selowe 	if (PR_KSTAT_RETIRED_NOTUE >= (uint64_t)PAGE_RETIRE_LIMIT) {
574917Selowe 		PR_INCR_KSTAT(pr_limit_exceeded);
575917Selowe 		return (1);
576917Selowe 	}
577917Selowe 
578917Selowe 	return (0);
579917Selowe }
580917Selowe 
581917Selowe #define	MSG_DM	"Data Mismatch occurred at PA 0x%08x.%08x"		\
582917Selowe 	"[ 0x%x != 0x%x ] while attempting to clear previously "	\
583917Selowe 	"reported error; page removed from service"
584917Selowe 
585917Selowe #define	MSG_UE	"Uncorrectable Error occurred at PA 0x%08x.%08x while "	\
586917Selowe 	"attempting to clear previously reported error; page removed "	\
587917Selowe 	"from service"
588917Selowe 
589917Selowe /*
590917Selowe  * Attempt to clear a UE from a page.
591917Selowe  * Returns 1 if the error has been successfully cleared.
592917Selowe  */
593917Selowe static int
594917Selowe page_clear_transient_ue(page_t *pp)
595917Selowe {
596917Selowe 	caddr_t		kaddr;
597917Selowe 	uint8_t		rb, wb;
598917Selowe 	uint64_t	pa;
599917Selowe 	uint32_t	pa_hi, pa_lo;
600917Selowe 	on_trap_data_t	otd;
601917Selowe 	int		errors = 0;
602917Selowe 	int		i;
603917Selowe 
604917Selowe 	ASSERT(PAGE_EXCL(pp));
605917Selowe 	ASSERT(PP_PR_REQ(pp));
606917Selowe 	ASSERT(pp->p_szc == 0);
607917Selowe 	ASSERT(!hat_page_is_mapped(pp));
608917Selowe 
609917Selowe 	/*
610917Selowe 	 * Clear the page and attempt to clear the UE.  If we trap
611917Selowe 	 * on the next access to the page, we know the UE has recurred.
612917Selowe 	 */
613917Selowe 	pagescrub(pp, 0, PAGESIZE);
614917Selowe 
615917Selowe 	/*
616917Selowe 	 * Map the page and write a bunch of bit patterns to compare
617917Selowe 	 * what we wrote with what we read back.  This isn't a perfect
618917Selowe 	 * test but it should be good enough to catch most of the
619917Selowe 	 * recurring UEs. If this fails to catch a recurrent UE, we'll
620917Selowe 	 * retire the page the next time we see a UE on the page.
621917Selowe 	 */
622917Selowe 	kaddr = ppmapin(pp, PROT_READ|PROT_WRITE, (caddr_t)-1);
623917Selowe 
624917Selowe 	pa = ptob((uint64_t)page_pptonum(pp));
625917Selowe 	pa_hi = (uint32_t)(pa >> 32);
626917Selowe 	pa_lo = (uint32_t)pa;
627917Selowe 
628917Selowe 	/*
6297458SChristopher.Baumbauer@Sun.COM 	 * Disable preemption to prevent the off chance that
6307458SChristopher.Baumbauer@Sun.COM 	 * we migrate while in the middle of running through
6317458SChristopher.Baumbauer@Sun.COM 	 * the bit pattern and run on a different processor
6327458SChristopher.Baumbauer@Sun.COM 	 * than what we started on.
6337458SChristopher.Baumbauer@Sun.COM 	 */
6347458SChristopher.Baumbauer@Sun.COM 	kpreempt_disable();
6357458SChristopher.Baumbauer@Sun.COM 
6367458SChristopher.Baumbauer@Sun.COM 	/*
637917Selowe 	 * Fill the page with each (0x00 - 0xFF] bit pattern, flushing
638917Selowe 	 * the cache in between reading and writing.  We do this under
639917Selowe 	 * on_trap() protection to avoid recursion.
640917Selowe 	 */
641917Selowe 	if (on_trap(&otd, OT_DATA_EC)) {
642917Selowe 		PR_MESSAGE(CE_WARN, 1, MSG_UE, pa);
643917Selowe 		errors = 1;
644917Selowe 	} else {
645917Selowe 		for (wb = 0xff; wb > 0; wb--) {
646917Selowe 			for (i = 0; i < PAGESIZE; i++) {
647917Selowe 				kaddr[i] = wb;
648917Selowe 			}
649917Selowe 
650917Selowe 			sync_data_memory(kaddr, PAGESIZE);
651917Selowe 
652917Selowe 			for (i = 0; i < PAGESIZE; i++) {
653917Selowe 				rb = kaddr[i];
654917Selowe 				if (rb != wb) {
655917Selowe 					/*
656917Selowe 					 * We had a mismatch without a trap.
657917Selowe 					 * Uh-oh. Something is really wrong
658917Selowe 					 * with this system.
659917Selowe 					 */
660917Selowe 					if (page_retire_messages) {
661917Selowe 						cmn_err(CE_WARN, MSG_DM,
662917Selowe 						    pa_hi, pa_lo, rb, wb);
663917Selowe 					}
664917Selowe 					errors = 1;
665917Selowe 					goto out;	/* double break */
666917Selowe 				}
667917Selowe 			}
668917Selowe 		}
669917Selowe 	}
670917Selowe out:
671917Selowe 	no_trap();
6727458SChristopher.Baumbauer@Sun.COM 	kpreempt_enable();
673917Selowe 	ppmapout(kaddr);
674917Selowe 
675917Selowe 	return (errors ? 0 : 1);
676917Selowe }
677917Selowe 
678917Selowe /*
679917Selowe  * Try to clear a page_t with a single UE. If the UE was transient, it is
680917Selowe  * returned to service, and we return 1. Otherwise we return 0 meaning
681917Selowe  * that further processing is required to retire the page.
682917Selowe  */
683917Selowe static int
684917Selowe page_retire_transient_ue(page_t *pp)
685917Selowe {
686917Selowe 	ASSERT(PAGE_EXCL(pp));
687917Selowe 	ASSERT(!hat_page_is_mapped(pp));
688917Selowe 
689917Selowe 	/*
690917Selowe 	 * If this page is a repeat offender, retire him under the
691917Selowe 	 * "two strikes and you're out" rule. The caller is responsible
692917Selowe 	 * for scrubbing the page to try to clear the error.
693917Selowe 	 */
694917Selowe 	if (pp->p_toxic & PR_UE_SCRUBBED) {
695917Selowe 		PR_INCR_KSTAT(pr_ue_persistent);
696917Selowe 		return (0);
697917Selowe 	}
698917Selowe 
699917Selowe 	if (page_clear_transient_ue(pp)) {
700917Selowe 		/*
701917Selowe 		 * We set the PR_SCRUBBED_UE bit; if we ever see this
702917Selowe 		 * page again, we will retire it, no questions asked.
703917Selowe 		 */
704917Selowe 		page_settoxic(pp, PR_UE_SCRUBBED);
705917Selowe 
706917Selowe 		if (page_retire_first_ue) {
707917Selowe 			PR_INCR_KSTAT(pr_ue_cleared_retire);
708917Selowe 			return (0);
709917Selowe 		} else {
710917Selowe 			PR_INCR_KSTAT(pr_ue_cleared_free);
711917Selowe 
7123253Smec 			page_clrtoxic(pp, PR_UE | PR_MCE | PR_MSG);
713917Selowe 
714917Selowe 			/* LINTED: CONSTCOND */
715917Selowe 			VN_DISPOSE(pp, B_FREE, 1, kcred);
716917Selowe 			return (1);
717917Selowe 		}
718917Selowe 	}
719917Selowe 
720917Selowe 	PR_INCR_KSTAT(pr_ue_persistent);
721917Selowe 	return (0);
722917Selowe }
723917Selowe 
724917Selowe /*
725917Selowe  * Update the statistics dynamically when our kstat is read.
726917Selowe  */
727917Selowe static int
728917Selowe page_retire_kstat_update(kstat_t *ksp, int rw)
729917Selowe {
730917Selowe 	struct page_retire_kstat *pr;
731917Selowe 
732917Selowe 	if (ksp == NULL)
7337458SChristopher.Baumbauer@Sun.COM 		return (EINVAL);
734917Selowe 
735917Selowe 	switch (rw) {
736917Selowe 
737917Selowe 	case KSTAT_READ:
738917Selowe 		pr = (struct page_retire_kstat *)ksp->ks_data;
739917Selowe 		ASSERT(pr == &page_retire_kstat);
740917Selowe 		pr->pr_limit.value.ui64 = PAGE_RETIRE_LIMIT;
741917Selowe 		return (0);
742917Selowe 
743917Selowe 	case KSTAT_WRITE:
744917Selowe 		return (EACCES);
745917Selowe 
746917Selowe 	default:
747917Selowe 		return (EINVAL);
748917Selowe 	}
749917Selowe 	/*NOTREACHED*/
750917Selowe }
751917Selowe 
7523253Smec static int
7533253Smec pr_list_kstat_update(kstat_t *ksp, int rw)
7543253Smec {
7553253Smec 	uint_t count;
7563253Smec 	page_t *pp;
7573253Smec 	kmutex_t *vphm;
7583253Smec 
7593253Smec 	if (rw == KSTAT_WRITE)
7603253Smec 		return (EACCES);
7613253Smec 
7623253Smec 	vphm = page_vnode_mutex(retired_pages);
7633253Smec 	mutex_enter(vphm);
7643253Smec 	/* Needs to be under a lock so that for loop will work right */
7653253Smec 	if (retired_pages->v_pages == NULL) {
7663253Smec 		mutex_exit(vphm);
7673253Smec 		ksp->ks_ndata = 0;
7683253Smec 		ksp->ks_data_size = 0;
7693253Smec 		return (0);
7703253Smec 	}
7713253Smec 
7723253Smec 	count = 1;
7733253Smec 	for (pp = retired_pages->v_pages->p_vpnext;
7743253Smec 	    pp != retired_pages->v_pages; pp = pp->p_vpnext) {
7753253Smec 		count++;
7763253Smec 	}
7773253Smec 	mutex_exit(vphm);
7783253Smec 
7793253Smec 	ksp->ks_ndata = count;
7803253Smec 	ksp->ks_data_size = count * 2 * sizeof (uint64_t);
7813253Smec 
7823253Smec 	return (0);
7833253Smec }
7843253Smec 
7853253Smec /*
7863253Smec  * all spans will be pagesize and no coalescing will be done with the
7873253Smec  * list produced.
7883253Smec  */
7893253Smec static int
7903253Smec pr_list_kstat_snapshot(kstat_t *ksp, void *buf, int rw)
7913253Smec {
7923253Smec 	kmutex_t *vphm;
7933253Smec 	page_t *pp;
7943253Smec 	struct memunit {
7953253Smec 		uint64_t address;
7963253Smec 		uint64_t size;
7973253Smec 	} *kspmem;
7983253Smec 
7993253Smec 	if (rw == KSTAT_WRITE)
8003253Smec 		return (EACCES);
8013253Smec 
8023253Smec 	ksp->ks_snaptime = gethrtime();
8033253Smec 
8043253Smec 	kspmem = (struct memunit *)buf;
8053253Smec 
8063253Smec 	vphm = page_vnode_mutex(retired_pages);
8073253Smec 	mutex_enter(vphm);
8083253Smec 	pp = retired_pages->v_pages;
8093253Smec 	if (((caddr_t)kspmem >= (caddr_t)buf + ksp->ks_data_size) ||
8103253Smec 	    (pp == NULL)) {
8113253Smec 		mutex_exit(vphm);
8123253Smec 		return (0);
8133253Smec 	}
8143253Smec 	kspmem->address = ptob(pp->p_pagenum);
8153253Smec 	kspmem->size = PAGESIZE;
8163253Smec 	kspmem++;
8173253Smec 	for (pp = pp->p_vpnext; pp != retired_pages->v_pages;
8183253Smec 	    pp = pp->p_vpnext, kspmem++) {
8193253Smec 		if ((caddr_t)kspmem >= (caddr_t)buf + ksp->ks_data_size)
8203253Smec 			break;
8213253Smec 		kspmem->address = ptob(pp->p_pagenum);
8223253Smec 		kspmem->size = PAGESIZE;
8233253Smec 	}
8243253Smec 	mutex_exit(vphm);
8253253Smec 
8263253Smec 	return (0);
8273253Smec }
8283253Smec 
829917Selowe /*
8303480Sjfrank  * page_retire_pend_count -- helper function for page_capture_thread,
8313480Sjfrank  * returns the number of pages pending retirement.
8323480Sjfrank  */
8333480Sjfrank uint64_t
8343480Sjfrank page_retire_pend_count(void)
8353480Sjfrank {
8363480Sjfrank 	return (PR_KSTAT_PENDING);
8373480Sjfrank }
8383480Sjfrank 
8399544SChristopher.Baumbauer@Sun.COM uint64_t
8409544SChristopher.Baumbauer@Sun.COM page_retire_pend_kas_count(void)
8413480Sjfrank {
8429544SChristopher.Baumbauer@Sun.COM 	return (PR_KSTAT_PENDING_KAS);
8433480Sjfrank }
8443480Sjfrank 
8453480Sjfrank void
8469544SChristopher.Baumbauer@Sun.COM page_retire_incr_pend_count(void *datap)
8479544SChristopher.Baumbauer@Sun.COM {
8489544SChristopher.Baumbauer@Sun.COM 	PR_INCR_KSTAT(pr_pending);
8499544SChristopher.Baumbauer@Sun.COM 
8509544SChristopher.Baumbauer@Sun.COM 	if ((datap == &kvp) || (datap == &zvp)) {
8519544SChristopher.Baumbauer@Sun.COM 		PR_INCR_KSTAT(pr_pending_kas);
8529544SChristopher.Baumbauer@Sun.COM 	}
8539544SChristopher.Baumbauer@Sun.COM }
8549544SChristopher.Baumbauer@Sun.COM 
8559544SChristopher.Baumbauer@Sun.COM void
8569544SChristopher.Baumbauer@Sun.COM page_retire_decr_pend_count(void *datap)
8573480Sjfrank {
8583480Sjfrank 	PR_DECR_KSTAT(pr_pending);
8599544SChristopher.Baumbauer@Sun.COM 
8609544SChristopher.Baumbauer@Sun.COM 	if ((datap == &kvp) || (datap == &zvp)) {
8619544SChristopher.Baumbauer@Sun.COM 		PR_DECR_KSTAT(pr_pending_kas);
8629544SChristopher.Baumbauer@Sun.COM 	}
8633480Sjfrank }
8643480Sjfrank 
8653480Sjfrank /*
866917Selowe  * Initialize the page retire mechanism:
867917Selowe  *
868917Selowe  *   - Establish the correctable error retire limit.
869917Selowe  *   - Initialize locks.
870917Selowe  *   - Build the retired_pages vnode.
871917Selowe  *   - Set up the kstats.
872917Selowe  *   - Fire off the background thread.
8733253Smec  *   - Tell page_retire() it's OK to start retiring pages.
874917Selowe  */
875917Selowe void
876917Selowe page_retire_init(void)
877917Selowe {
8783898Srsb 	const fs_operation_def_t retired_vnodeops_template[] = {
8793898Srsb 		{ NULL, NULL }
8803898Srsb 	};
881917Selowe 	struct vnodeops *vops;
8823253Smec 	kstat_t *ksp;
883917Selowe 
884917Selowe 	const uint_t page_retire_ndata =
885917Selowe 	    sizeof (page_retire_kstat) / sizeof (kstat_named_t);
886917Selowe 
887917Selowe 	ASSERT(page_retire_ksp == NULL);
888917Selowe 
889917Selowe 	if (max_pages_retired_bps <= 0) {
890917Selowe 		max_pages_retired_bps = MCE_BPT;
891917Selowe 	}
892917Selowe 
893917Selowe 	mutex_init(&pr_q_mutex, NULL, MUTEX_DEFAULT, NULL);
894917Selowe 
895917Selowe 	retired_pages = vn_alloc(KM_SLEEP);
896917Selowe 	if (vn_make_ops("retired_pages", retired_vnodeops_template, &vops)) {
897917Selowe 		cmn_err(CE_PANIC,
898917Selowe 		    "page_retired_init: can't make retired vnodeops");
899917Selowe 	}
900917Selowe 	vn_setops(retired_pages, vops);
901917Selowe 
902917Selowe 	if ((page_retire_ksp = kstat_create("unix", 0, "page_retire",
903917Selowe 	    "misc", KSTAT_TYPE_NAMED, page_retire_ndata,
904917Selowe 	    KSTAT_FLAG_VIRTUAL)) == NULL) {
905917Selowe 		cmn_err(CE_WARN, "kstat_create for page_retire failed");
906917Selowe 	} else {
907917Selowe 		page_retire_ksp->ks_data = (void *)&page_retire_kstat;
908917Selowe 		page_retire_ksp->ks_update = page_retire_kstat_update;
909917Selowe 		kstat_install(page_retire_ksp);
910917Selowe 	}
911917Selowe 
9123253Smec 	mutex_init(&pr_list_kstat_mutex, NULL, MUTEX_DEFAULT, NULL);
9133253Smec 	ksp = kstat_create("unix", 0, "page_retire_list", "misc",
9143253Smec 	    KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VAR_SIZE | KSTAT_FLAG_VIRTUAL);
9153253Smec 	if (ksp != NULL) {
9163253Smec 		ksp->ks_update = pr_list_kstat_update;
9173253Smec 		ksp->ks_snapshot = pr_list_kstat_snapshot;
9183253Smec 		ksp->ks_lock = &pr_list_kstat_mutex;
9193253Smec 		kstat_install(ksp);
9203253Smec 	}
921917Selowe 
9223253Smec 	page_capture_register_callback(PC_RETIRE, -1, page_retire_pp_finish);
923917Selowe 	pr_enable = 1;
924917Selowe }
925917Selowe 
926917Selowe /*
927917Selowe  * page_retire_hunt() callback for the retire thread.
928917Selowe  */
929917Selowe static void
930917Selowe page_retire_thread_cb(page_t *pp)
931917Selowe {
932917Selowe 	PR_DEBUG(prd_tctop);
9333290Sjohansen 	if (!PP_ISKAS(pp) && page_trylock(pp, SE_EXCL)) {
934917Selowe 		PR_DEBUG(prd_tclocked);
935917Selowe 		page_unlock(pp);
936917Selowe 	}
937917Selowe }
938917Selowe 
939917Selowe /*
9403253Smec  * Callback used by page_trycapture() to finish off retiring a page.
9413253Smec  * The page has already been cleaned and we've been given sole access to
9423253Smec  * it.
9433253Smec  * Always returns 0 to indicate that callback succeded as the callback never
9443253Smec  * fails to finish retiring the given page.
945917Selowe  */
9463253Smec /*ARGSUSED*/
947917Selowe static int
9483253Smec page_retire_pp_finish(page_t *pp, void *notused, uint_t flags)
949917Selowe {
950917Selowe 	int		toxic;
951917Selowe 
952917Selowe 	ASSERT(PAGE_EXCL(pp));
953917Selowe 	ASSERT(pp->p_iolock_state == 0);
954917Selowe 	ASSERT(pp->p_szc == 0);
955917Selowe 
956917Selowe 	toxic = pp->p_toxic;
957917Selowe 
958917Selowe 	/*
959917Selowe 	 * The problem page is locked, demoted, unmapped, not free,
960917Selowe 	 * hashed out, and not COW or mlocked (whew!).
961917Selowe 	 *
962917Selowe 	 * Now we select our ammunition, take it around back, and shoot it.
963917Selowe 	 */
964917Selowe 	if (toxic & PR_UE) {
9653253Smec ue_error:
966917Selowe 		if (page_retire_transient_ue(pp)) {
967917Selowe 			PR_DEBUG(prd_uescrubbed);
9683253Smec 			(void) page_retire_done(pp, PRD_UE_SCRUBBED);
969917Selowe 		} else {
970917Selowe 			PR_DEBUG(prd_uenotscrubbed);
971917Selowe 			page_retire_destroy(pp);
9723253Smec 			(void) page_retire_done(pp, PRD_SUCCESS);
973917Selowe 		}
9743253Smec 		return (0);
975917Selowe 	} else if (toxic & PR_FMA) {
976917Selowe 		PR_DEBUG(prd_fma);
977917Selowe 		page_retire_destroy(pp);
9783253Smec 		(void) page_retire_done(pp, PRD_SUCCESS);
9793253Smec 		return (0);
980917Selowe 	} else if (toxic & PR_MCE) {
981917Selowe 		PR_DEBUG(prd_mce);
982917Selowe 		page_retire_destroy(pp);
9833253Smec 		(void) page_retire_done(pp, PRD_SUCCESS);
9843253Smec 		return (0);
985917Selowe 	}
986917Selowe 
987917Selowe 	/*
9883253Smec 	 * When page_retire_first_ue is set to zero and a UE occurs which is
9893253Smec 	 * transient, it's possible that we clear some flags set by a second
9903253Smec 	 * UE error on the page which occurs while the first is currently being
9913253Smec 	 * handled and thus we need to handle the case where none of the above
9923253Smec 	 * are set.  In this instance, PR_UE_SCRUBBED should be set and thus
9933253Smec 	 * we should execute the UE code above.
994917Selowe 	 */
9953253Smec 	if (toxic & PR_UE_SCRUBBED) {
9963253Smec 		goto ue_error;
997917Selowe 	}
9983253Smec 
9993253Smec 	/*
10003253Smec 	 * It's impossible to get here.
10013253Smec 	 */
10023253Smec 	panic("bad toxic flags 0x%x in page_retire_pp_finish\n", toxic);
10033253Smec 	return (0);
1004917Selowe }
1005917Selowe 
1006917Selowe /*
1007917Selowe  * page_retire() - the front door in to retire a page.
1008917Selowe  *
1009917Selowe  * Ideally, page_retire() would instantly retire the requested page.
1010917Selowe  * Unfortunately, some pages are locked or otherwise tied up and cannot be
10113253Smec  * retired right away.  We use the page capture logic to deal with this
10123253Smec  * situation as it will continuously try to retire the page in the background
10133253Smec  * if the first attempt fails.  Success is determined by looking to see whether
10143253Smec  * the page has been retired after the page_trycapture() attempt.
1015917Selowe  *
1016917Selowe  * Returns:
1017917Selowe  *
1018917Selowe  *   - 0 on success,
1019917Selowe  *   - EINVAL when the PA is whacko,
10201381Selowe  *   - EIO if the page is already retired or already pending retirement, or
10211381Selowe  *   - EAGAIN if the page could not be _immediately_ retired but is pending.
1022917Selowe  */
1023917Selowe int
1024917Selowe page_retire(uint64_t pa, uchar_t reason)
1025917Selowe {
1026917Selowe 	page_t	*pp;
1027917Selowe 
1028917Selowe 	ASSERT(reason & PR_REASONS);		/* there must be a reason */
1029917Selowe 	ASSERT(!(reason & ~PR_REASONS));	/* but no other bits */
1030917Selowe 
1031917Selowe 	pp = page_numtopp_nolock(mmu_btop(pa));
1032917Selowe 	if (pp == NULL) {
1033917Selowe 		PR_MESSAGE(CE_WARN, 1, "Cannot schedule clearing of error on"
1034917Selowe 		    " page 0x%08x.%08x; page is not relocatable memory", pa);
1035917Selowe 		return (page_retire_done(pp, PRD_INVALID_PA));
1036917Selowe 	}
1037917Selowe 	if (PP_RETIRED(pp)) {
10381381Selowe 		PR_DEBUG(prd_dup1);
1039917Selowe 		return (page_retire_done(pp, PRD_DUPLICATE));
1040917Selowe 	}
1041917Selowe 
10421381Selowe 	if ((reason & PR_UE) && !PP_TOXIC(pp)) {
1043917Selowe 		PR_MESSAGE(CE_NOTE, 1, "Scheduling clearing of error on"
1044917Selowe 		    " page 0x%08x.%08x", pa);
10451381Selowe 	} else if (PP_PR_REQ(pp)) {
10461381Selowe 		PR_DEBUG(prd_dup2);
10471381Selowe 		return (page_retire_done(pp, PRD_DUPLICATE));
1048917Selowe 	} else {
1049917Selowe 		PR_MESSAGE(CE_NOTE, 1, "Scheduling removal of"
1050917Selowe 		    " page 0x%08x.%08x", pa);
1051917Selowe 	}
10523253Smec 
10533253Smec 	/* Avoid setting toxic bits in the first place */
10543253Smec 	if ((reason & (PR_FMA | PR_MCE)) && !(reason & PR_UE) &&
10553253Smec 	    page_retire_limit()) {
10563253Smec 		return (page_retire_done(pp, PRD_LIMIT));
10573253Smec 	}
1058917Selowe 
10593253Smec 	if (MTBF(pr_calls, pr_mtbf)) {
10603253Smec 		page_settoxic(pp, reason);
10619544SChristopher.Baumbauer@Sun.COM 		if (page_trycapture(pp, 0, CAPTURE_RETIRE, pp->p_vnode) == 0) {
10623253Smec 			PR_DEBUG(prd_prlocked);
10633253Smec 		} else {
10643253Smec 			PR_DEBUG(prd_prnotlocked);
10653253Smec 		}
1066917Selowe 	} else {
1067917Selowe 		PR_DEBUG(prd_prnotlocked);
1068917Selowe 	}
1069917Selowe 
1070917Selowe 	if (PP_RETIRED(pp)) {
1071917Selowe 		PR_DEBUG(prd_prretired);
1072917Selowe 		return (0);
1073917Selowe 	} else {
10743253Smec 		cv_signal(&pc_cv);
1075917Selowe 		PR_INCR_KSTAT(pr_failed);
1076917Selowe 
1077917Selowe 		if (pp->p_toxic & PR_MSG) {
1078917Selowe 			return (page_retire_done(pp, PRD_FAILED));
1079917Selowe 		} else {
1080917Selowe 			return (page_retire_done(pp, PRD_PENDING));
1081917Selowe 		}
1082917Selowe 	}
1083917Selowe }
1084917Selowe 
1085917Selowe /*
1086917Selowe  * Take a retired page off the retired-pages vnode and clear the toxic flags.
1087917Selowe  * If "free" is nonzero, lock it and put it back on the freelist. If "free"
1088917Selowe  * is zero, the caller already holds SE_EXCL lock so we simply unretire it
1089917Selowe  * and don't do anything else with it.
1090917Selowe  *
1091917Selowe  * Any unretire messages are printed from this routine.
1092917Selowe  *
1093917Selowe  * Returns 0 if page pp was unretired; else an error code.
10943253Smec  *
10953253Smec  * If flags is:
10963253Smec  *	PR_UNR_FREE - lock the page, clear the toxic flags and free it
10973253Smec  *	    to the freelist.
10983253Smec  *	PR_UNR_TEMP - lock the page, unretire it, leave the toxic
10993253Smec  *	    bits set as is and return it to the caller.
11003253Smec  *	PR_UNR_CLEAN - page is SE_EXCL locked, unretire it, clear the
11013253Smec  *	    toxic flags and return it to caller as is.
1102917Selowe  */
1103917Selowe int
11043253Smec page_unretire_pp(page_t *pp, int flags)
1105917Selowe {
1106917Selowe 	/*
1107917Selowe 	 * To be retired, a page has to be hashed onto the retired_pages vnode
1108917Selowe 	 * and have PR_RETIRED set in p_toxic.
1109917Selowe 	 */
11103253Smec 	if (flags == PR_UNR_CLEAN ||
11113253Smec 	    page_try_reclaim_lock(pp, SE_EXCL, SE_RETIRED)) {
1112917Selowe 		ASSERT(PAGE_EXCL(pp));
1113917Selowe 		PR_DEBUG(prd_ulocked);
1114917Selowe 		if (!PP_RETIRED(pp)) {
1115917Selowe 			PR_DEBUG(prd_unotretired);
1116917Selowe 			page_unlock(pp);
1117917Selowe 			return (page_retire_done(pp, PRD_UNR_NOT));
1118917Selowe 		}
1119917Selowe 
1120917Selowe 		PR_MESSAGE(CE_NOTE, 1, "unretiring retired"
11211338Selowe 		    " page 0x%08x.%08x", mmu_ptob((uint64_t)pp->p_pagenum));
1122917Selowe 		if (pp->p_toxic & PR_FMA) {
1123917Selowe 			PR_DECR_KSTAT(pr_fma);
1124917Selowe 		} else if (pp->p_toxic & PR_UE) {
1125917Selowe 			PR_DECR_KSTAT(pr_ue);
1126917Selowe 		} else {
1127917Selowe 			PR_DECR_KSTAT(pr_mce);
1128917Selowe 		}
1129917Selowe 
11303253Smec 		if (flags == PR_UNR_TEMP)
11313253Smec 			page_clrtoxic(pp, PR_RETIRED);
11323253Smec 		else
11333253Smec 			page_clrtoxic(pp, PR_TOXICFLAGS);
11343253Smec 
11353253Smec 		if (flags == PR_UNR_FREE) {
1136917Selowe 			PR_DEBUG(prd_udestroy);
1137917Selowe 			page_destroy(pp, 0);
1138917Selowe 		} else {
1139917Selowe 			PR_DEBUG(prd_uhashout);
1140917Selowe 			page_hashout(pp, NULL);
1141917Selowe 		}
1142917Selowe 
1143917Selowe 		mutex_enter(&freemem_lock);
1144917Selowe 		availrmem++;
1145917Selowe 		mutex_exit(&freemem_lock);
1146917Selowe 
1147917Selowe 		PR_DEBUG(prd_uunretired);
1148917Selowe 		PR_DECR_KSTAT(pr_retired);
1149917Selowe 		PR_INCR_KSTAT(pr_unretired);
1150917Selowe 		return (page_retire_done(pp, PRD_UNR_SUCCESS));
1151917Selowe 	}
1152917Selowe 	PR_DEBUG(prd_unotlocked);
1153917Selowe 	return (page_retire_done(pp, PRD_UNR_CANTLOCK));
1154917Selowe }
1155917Selowe 
1156917Selowe /*
1157917Selowe  * Return a page to service by moving it from the retired_pages vnode
1158917Selowe  * onto the freelist.
1159917Selowe  *
1160917Selowe  * Called from mmioctl_page_retire() on behalf of the FMA DE.
1161917Selowe  *
1162917Selowe  * Returns:
1163917Selowe  *
1164917Selowe  *   - 0 if the page is unretired,
1165917Selowe  *   - EAGAIN if the pp can not be locked,
1166917Selowe  *   - EINVAL if the PA is whacko, and
11671381Selowe  *   - EIO if the pp is not retired.
1168917Selowe  */
1169917Selowe int
1170917Selowe page_unretire(uint64_t pa)
1171917Selowe {
1172917Selowe 	page_t	*pp;
1173917Selowe 
1174917Selowe 	pp = page_numtopp_nolock(mmu_btop(pa));
1175917Selowe 	if (pp == NULL) {
1176917Selowe 		return (page_retire_done(pp, PRD_INVALID_PA));
1177917Selowe 	}
1178917Selowe 
11793253Smec 	return (page_unretire_pp(pp, PR_UNR_FREE));
1180917Selowe }
1181917Selowe 
1182917Selowe /*
1183917Selowe  * Test a page to see if it is retired. If errors is non-NULL, the toxic
1184917Selowe  * bits of the page are returned. Returns 0 on success, error code on failure.
1185917Selowe  */
1186917Selowe int
1187917Selowe page_retire_check_pp(page_t *pp, uint64_t *errors)
1188917Selowe {
1189917Selowe 	int rc;
1190917Selowe 
1191917Selowe 	if (PP_RETIRED(pp)) {
1192917Selowe 		PR_DEBUG(prd_checkhit);
1193917Selowe 		rc = 0;
11941381Selowe 	} else if (PP_PR_REQ(pp)) {
11951381Selowe 		PR_DEBUG(prd_checkmiss_pend);
11961381Selowe 		rc = EAGAIN;
1197917Selowe 	} else {
11981381Selowe 		PR_DEBUG(prd_checkmiss_noerr);
11991381Selowe 		rc = EIO;
1200917Selowe 	}
1201917Selowe 
1202917Selowe 	/*
1203917Selowe 	 * We have magically arranged the bit values returned to fmd(1M)
1204917Selowe 	 * to line up with the FMA, MCE, and UE bits of the page_t.
1205917Selowe 	 */
1206917Selowe 	if (errors) {
1207917Selowe 		uint64_t toxic = (uint64_t)(pp->p_toxic & PR_ERRMASK);
1208917Selowe 		if (toxic & PR_UE_SCRUBBED) {
1209917Selowe 			toxic &= ~PR_UE_SCRUBBED;
1210917Selowe 			toxic |= PR_UE;
1211917Selowe 		}
1212917Selowe 		*errors = toxic;
1213917Selowe 	}
1214917Selowe 
1215917Selowe 	return (rc);
1216917Selowe }
1217917Selowe 
1218917Selowe /*
1219917Selowe  * Test to see if the page_t for a given PA is retired, and return the
1220917Selowe  * hardware errors we have seen on the page if requested.
1221917Selowe  *
1222917Selowe  * Called from mmioctl_page_retire on behalf of the FMA DE.
1223917Selowe  *
1224917Selowe  * Returns:
1225917Selowe  *
1226917Selowe  *   - 0 if the page is retired,
12271381Selowe  *   - EIO if the page is not retired and has no errors,
12281381Selowe  *   - EAGAIN if the page is not retired but is pending; and
1229917Selowe  *   - EINVAL if the PA is whacko.
1230917Selowe  */
1231917Selowe int
1232917Selowe page_retire_check(uint64_t pa, uint64_t *errors)
1233917Selowe {
1234917Selowe 	page_t	*pp;
1235917Selowe 
1236917Selowe 	if (errors) {
1237917Selowe 		*errors = 0;
1238917Selowe 	}
1239917Selowe 
1240917Selowe 	pp = page_numtopp_nolock(mmu_btop(pa));
1241917Selowe 	if (pp == NULL) {
1242917Selowe 		return (page_retire_done(pp, PRD_INVALID_PA));
1243917Selowe 	}
1244917Selowe 
1245917Selowe 	return (page_retire_check_pp(pp, errors));
1246917Selowe }
1247917Selowe 
1248917Selowe /*
1249917Selowe  * Page retire self-test. For now, it always returns 0.
1250917Selowe  */
1251917Selowe int
1252917Selowe page_retire_test(void)
1253917Selowe {
1254917Selowe 	page_t *first, *pp, *cpp, *cpp2, *lpp;
1255917Selowe 
1256917Selowe 	/*
1257917Selowe 	 * Tests the corner case where a large page can't be retired
1258917Selowe 	 * because one of the constituent pages is locked. We mark
1259917Selowe 	 * one page to be retired and try to retire it, and mark the
1260917Selowe 	 * other page to be retired but don't try to retire it, so
1261917Selowe 	 * that page_unlock() in the failure path will recurse and try
1262917Selowe 	 * to retire THAT page. This is the worst possible situation
1263917Selowe 	 * we can get ourselves into.
1264917Selowe 	 */
1265917Selowe 	memsegs_lock(0);
1266917Selowe 	pp = first = page_first();
1267917Selowe 	do {
1268917Selowe 		if (pp->p_szc && PP_PAGEROOT(pp) == pp) {
1269917Selowe 			cpp = pp + 1;
1270917Selowe 			lpp = PP_ISFREE(pp)? pp : pp + 2;
1271917Selowe 			cpp2 = pp + 3;
1272917Selowe 			if (!page_trylock(lpp, pp == lpp? SE_EXCL : SE_SHARED))
1273917Selowe 				continue;
1274917Selowe 			if (!page_trylock(cpp, SE_EXCL)) {
1275917Selowe 				page_unlock(lpp);
1276917Selowe 				continue;
1277917Selowe 			}
12783253Smec 
12793253Smec 			/* fails */
12803253Smec 			(void) page_retire(ptob(cpp->p_pagenum), PR_FMA);
12813253Smec 
1282917Selowe 			page_unlock(lpp);
12833253Smec 			page_unlock(cpp);
12843253Smec 			(void) page_retire(ptob(cpp->p_pagenum), PR_FMA);
12853253Smec 			(void) page_retire(ptob(cpp2->p_pagenum), PR_FMA);
1286917Selowe 		}
1287917Selowe 	} while ((pp = page_next(pp)) != first);
1288917Selowe 	memsegs_unlock(0);
1289917Selowe 
1290917Selowe 	return (0);
1291917Selowe }
1292