ac_del.c revision 1341:6d7c4f090a72
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#include <sys/types.h>
30#include <sys/systm.h>
31#include <sys/ddi.h>
32#include <sys/sunddi.h>
33#include <sys/ddi_impldefs.h>
34#include <sys/obpdefs.h>
35#include <sys/errno.h>
36#include <sys/kmem.h>
37#include <sys/vmem.h>
38#include <sys/debug.h>
39#include <sys/sysmacros.h>
40#include <sys/machsystm.h>
41#include <sys/machparam.h>
42#include <sys/modctl.h>
43#include <sys/fhc.h>
44#include <sys/ac.h>
45#include <sys/vm.h>
46#include <sys/cpu_module.h>
47#include <vm/seg_kmem.h>
48#include <vm/hat_sfmmu.h>
49#include <sys/mem_config.h>
50#include <sys/mem_cage.h>
51
52extern ac_err_t ac_kpm_err_cvt(int);
53
54int ac_del_clean = 0;
55
56/*
57 * Default timeout, in seconds, for delete.
58 * Time is counted when no progress is being made.
59 */
60static int ac_del_timeout = 60;
61
62#define	DEL_PAGESIZE	MMU_PAGESIZE
63
64struct del_status {
65	struct del_status *next;
66	memhandle_t	handle;
67	volatile int	its_done;
68	int		done_error;
69	kcondvar_t	ac_del_cv;
70	int		del_timeout;
71	int		del_noprogress;
72	ac_err_t	cancel_code;
73	timeout_id_t	to_id;
74	pgcnt_t		last_collected;
75};
76static struct del_status *ac_del_list;
77static kmutex_t ac_del_mutex;
78
79static struct del_status *
80ac_del_alloc_status()
81{
82	struct del_status *dsp;
83
84	dsp = (struct del_status *)kmem_zalloc(sizeof (*dsp), KM_SLEEP);
85	mutex_enter(&ac_del_mutex);
86	dsp->next = ac_del_list;
87	ac_del_list = dsp;
88	mutex_exit(&ac_del_mutex);
89
90	return (dsp);
91}
92
93static void
94ac_del_free_status(struct del_status *dsp)
95{
96	struct del_status **dspp;
97
98	mutex_enter(&ac_del_mutex);
99	dspp = &ac_del_list;
100	while (*dspp != NULL) {
101		if (*dspp == dsp)
102			break;
103		dspp = &(*dspp)->next;
104	}
105	ASSERT(*dspp == dsp);
106	if (*dspp == dsp) {
107		*dspp = dsp->next;
108	}
109	mutex_exit(&ac_del_mutex);
110	kmem_free((void *)dsp, sizeof (*dsp));
111}
112
113static void
114del_comp(void *arg, int error)
115{
116	struct del_status *dsp;
117
118	dsp = (struct del_status *)arg;
119	mutex_enter(&ac_del_mutex);
120#ifdef DEBUG
121	{
122		struct del_status *adsp;
123		for (adsp = ac_del_list; adsp != NULL; adsp = adsp->next) {
124			if (adsp == dsp)
125				break;
126		}
127		ASSERT(adsp != NULL);
128	}
129#endif /* DEBUG */
130	dsp->its_done = 1;
131	dsp->done_error = error;
132	cv_signal(&dsp->ac_del_cv);
133	mutex_exit(&ac_del_mutex);
134}
135
136/*ARGSUSED*/
137static void
138del_to_scan(void *arg)
139{
140	struct del_status *dsp;
141	int do_cancel;
142	memdelstat_t dstat;
143	int err;
144
145	dsp = arg;
146
147#ifdef DEBUG
148	{
149		struct del_status *adsp;
150
151		mutex_enter(&ac_del_mutex);
152		for (adsp = ac_del_list; adsp != NULL; adsp = adsp->next) {
153			if (adsp == dsp)
154				break;
155		}
156		ASSERT(adsp != NULL);
157		mutex_exit(&ac_del_mutex);
158	}
159#endif /* DEBUG */
160	do_cancel = 0;
161	err = kphysm_del_status(dsp->handle, &dstat);
162	mutex_enter(&ac_del_mutex);
163	if (dsp->its_done) {
164		mutex_exit(&ac_del_mutex);
165		return;
166	}
167	if ((err == KPHYSM_OK) &&
168	    (dsp->last_collected != dstat.collected)) {
169		dsp->del_noprogress = 0;
170		dsp->last_collected = dstat.collected;
171	} else {
172		dsp->del_noprogress++;
173		if (dsp->del_noprogress >= dsp->del_timeout) {
174			if (dsp->cancel_code == 0)
175				dsp->cancel_code = AC_ERR_TIMEOUT;
176			do_cancel = 1;
177		}
178	}
179	if (!do_cancel)
180		dsp->to_id = timeout(del_to_scan, arg, hz);
181	else
182		dsp->to_id = 0;
183	mutex_exit(&ac_del_mutex);
184	if (do_cancel)
185		(void) kphysm_del_cancel(dsp->handle);
186}
187
188static void
189del_to_start(struct del_status *dsp)
190{
191	if (dsp->del_timeout != 0)
192		dsp->to_id = timeout(del_to_scan, dsp, hz);
193}
194
195static void
196del_to_stop(struct del_status *dsp)
197{
198	timeout_id_t tid;
199
200	while ((tid = dsp->to_id) != 0) {
201		dsp->to_id = 0;
202		mutex_exit(&ac_del_mutex);
203		(void) untimeout(tid);
204		mutex_enter(&ac_del_mutex);
205	}
206}
207
208static int
209ac_del_bank_add_span(
210	memhandle_t handle,
211	ac_cfga_pkt_t *pkt)
212{
213	uint64_t		decode;
214	uint64_t		base_pa;
215	uint64_t		bank_size;
216	pfn_t			base;
217	pgcnt_t			npgs;
218	int			errs;
219	int			ret;
220	struct ac_soft_state	*asp = pkt->softsp;
221	uint_t			ilv;
222
223	/*
224	 * Cannot delete interleaved banks at the moment.
225	 */
226	ilv = (pkt->bank == Bank0) ?
227	    INTLV0(*asp->ac_memctl) : INTLV1(*asp->ac_memctl);
228	if (ilv != 1) {
229		AC_ERR_SET(pkt, AC_ERR_MEM_DEINTLV);
230		return (EINVAL);
231	}
232	/*
233	 * Determine the physical location of the selected bank
234	 */
235	decode = (pkt->bank == Bank0) ?
236	    *asp->ac_memdecode0 : *asp->ac_memdecode1;
237	base_pa = GRP_REALBASE(decode);
238	bank_size = GRP_UK2SPAN(decode);
239
240	base = base_pa >> PAGESHIFT;
241	npgs = bank_size >> PAGESHIFT;
242
243	/*
244	 * Delete the pages from the cage growth list.
245	 */
246	kcage_range_lock();
247	ret = kcage_range_delete(base, npgs);
248	kcage_range_unlock();
249	if (ret != 0) {
250		/* TODO: Should this be a separate error? */
251		AC_ERR_SET(pkt, AC_ERR_KPM_NONRELOC);
252		return (EINVAL);
253	}
254
255	/*
256	 * Add to delete memory list.
257	 */
258
259	if ((errs = kphysm_del_span(handle, base, npgs)) != KPHYSM_OK) {
260		AC_ERR_SET(pkt, ac_kpm_err_cvt(errs));
261		/*
262		 * Restore the pages to the cage growth list.
263		 * TODO: We should not unconditionally add back
264		 * if we conditionally add at memory add time.
265		 */
266		kcage_range_lock();
267		errs = kcage_range_add(base, npgs, 1);
268		/* TODO: deal with error return. */
269		if (errs != 0) {
270			AC_ERR_SET(pkt, ac_kpm_err_cvt(errs));
271			cmn_err(CE_NOTE, "ac_del_bank_add_span(): "
272			    "board %d, bank %d, "
273			    "kcage_range_add() returned %d",
274			    pkt->softsp->board, pkt->bank, errs);
275		}
276		kcage_range_unlock();
277		return (EINVAL);
278	}
279	return (0);
280}
281
282static void
283ac_del_bank_add_cage(
284	struct bd_list *del,
285	enum ac_bank_id bank)
286{
287	uint64_t		decode;
288	uint64_t		base_pa;
289	uint64_t		bank_size;
290	pfn_t			base;
291	pgcnt_t			npgs;
292	int			errs;
293	struct ac_soft_state	*asp = (struct ac_soft_state *)(del->ac_softsp);
294
295	/*
296	 * Determine the physical location of the selected bank
297	 */
298	decode = (bank == Bank0) ? *asp->ac_memdecode0 : *asp->ac_memdecode1;
299	base_pa = GRP_REALBASE(decode);
300	bank_size = GRP_UK2SPAN(decode);
301
302	base = base_pa >> PAGESHIFT;
303	npgs = bank_size >> PAGESHIFT;
304
305	/*
306	 * Restore the pages to the cage growth list.
307	 * TODO: We should not unconditionally add back
308	 * if we conditionally add at memory add time.
309	 */
310	kcage_range_lock();
311	errs = kcage_range_add(base, npgs, 1);
312	/* TODO: deal with error return. */
313	if (errs != 0)
314		cmn_err(CE_NOTE, "ac_del_bank_add_cage(): "
315		    "board %d, bank %d, "
316		    "kcage_range_add() returned %d",
317		    del->sc.board, bank, errs);
318	kcage_range_unlock();
319}
320
321static int
322ac_del_bank_run(struct del_status *dsp, ac_cfga_pkt_t *pkt)
323{
324	int errs;
325
326	dsp->its_done = 0;
327	if ((errs = kphysm_del_start(dsp->handle, del_comp, (void *)dsp)) !=
328	    KPHYSM_OK) {
329		AC_ERR_SET(pkt, ac_kpm_err_cvt(errs));
330		return (EINVAL);
331	}
332	/* Wait for it to complete. */
333	mutex_enter(&ac_del_mutex);
334	del_to_start(dsp);
335	while (!dsp->its_done) {
336		if (!cv_wait_sig(&dsp->ac_del_cv, &ac_del_mutex)) {
337			if (dsp->cancel_code == 0)
338				dsp->cancel_code = AC_ERR_INTR;
339			mutex_exit(&ac_del_mutex);
340			errs = kphysm_del_cancel(dsp->handle);
341			mutex_enter(&ac_del_mutex);
342			if (errs != KPHYSM_OK) {
343				ASSERT(errs == KPHYSM_ENOTRUNNING);
344			}
345			break;
346		}
347	}
348	/*
349	 * If the loop exited due to a signal, we must continue to wait
350	 * using cv_wait() as the signal is pending until syscall exit.
351	 */
352	while (!dsp->its_done) {
353		cv_wait(&dsp->ac_del_cv, &ac_del_mutex);
354	}
355	if (dsp->done_error != KPHYSM_OK) {
356		AC_ERR_SET(pkt, ac_kpm_err_cvt(dsp->done_error));
357		if ((dsp->done_error == KPHYSM_ECANCELLED) ||
358		    (dsp->done_error == KPHYSM_EREFUSED)) {
359			errs = EINTR;
360			if (dsp->cancel_code != 0) {
361				AC_ERR_SET(pkt, dsp->cancel_code);
362			}
363		} else {
364			errs = EINVAL;
365		}
366	} else
367		errs = 0;
368	del_to_stop(dsp);
369	mutex_exit(&ac_del_mutex);
370
371	return (errs);
372}
373
374
375/*
376 * set the memory to known state for debugging
377 */
378static void
379ac_bank_write_pattern(struct bd_list *del, enum ac_bank_id bank)
380{
381	uint64_t		decode;
382	uint64_t		base_pa;
383	uint64_t		limit_pa;
384	uint64_t		bank_size;
385	uint64_t		current_pa;
386	caddr_t			base_va;
387	caddr_t			fill_buf;
388	struct ac_soft_state	*asp = (struct ac_soft_state *)(del->ac_softsp);
389	int			linesize;
390
391	/*
392	 * Determine the physical location of the selected bank
393	 */
394	decode = (bank == Bank0) ? *asp->ac_memdecode0 : *asp->ac_memdecode1;
395	base_pa = GRP_REALBASE(decode);
396	bank_size = GRP_UK2SPAN(decode);
397	limit_pa = base_pa + bank_size;
398	linesize = cpunodes[CPU->cpu_id].ecache_linesize;
399
400	/*
401	 * We need a page_va and a fill buffer for this operation
402	 */
403	base_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
404	fill_buf = kmem_zalloc(DEL_PAGESIZE, KM_SLEEP);
405	{
406		typedef uint32_t patt_t;
407		patt_t *bf, *bfe, patt;
408
409		bf = (patt_t *)fill_buf;
410		bfe = (patt_t *)((char *)fill_buf + DEL_PAGESIZE);
411		patt = 0xbeaddeed;
412		while (bf < bfe)
413			*bf++ = patt;
414	}
415
416	/*
417	 * 'empty' the memory
418	 */
419	kpreempt_disable();
420	for (current_pa = base_pa; current_pa < limit_pa;
421	    current_pa += DEL_PAGESIZE) {
422
423		/* map current pa */
424		ac_mapin(current_pa, base_va);
425
426		/* fill the target page */
427		ac_blkcopy(fill_buf, base_va,
428			DEL_PAGESIZE/linesize, linesize);
429
430		/* tear down translation */
431		ac_unmap(base_va);
432	}
433	kpreempt_enable();
434
435	/*
436	 * clean up temporary resources
437	 */
438	{
439		/* Distinguish the fill buf from memory deleted! */
440		typedef uint32_t patt_t;
441		patt_t *bf, *bfe, patt;
442
443		bf = (patt_t *)fill_buf;
444		bfe = (patt_t *)((char *)fill_buf + DEL_PAGESIZE);
445		patt = 0xbeadfeed;
446		while (bf < bfe)
447			*bf++ = patt;
448	}
449	kmem_free(fill_buf, DEL_PAGESIZE);
450	vmem_free(heap_arena, base_va, PAGESIZE);
451}
452
453int
454ac_del_memory(ac_cfga_pkt_t *pkt)
455{
456	struct bd_list *board;
457	struct ac_mem_info *mem_info;
458	int busy_set;
459	struct del_status *dsp;
460	memdelstat_t dstat;
461	int retval;
462	int r_errs;
463	struct ac_soft_state *asp;
464
465	if (!kcage_on) {
466		static int cage_msg_done = 0;
467
468		if (!cage_msg_done) {
469			cage_msg_done = 1;
470			cmn_err(CE_NOTE, "ac: memory delete"
471			    " refused: cage is off");
472		}
473		AC_ERR_SET(pkt, ac_kpm_err_cvt(KPHYSM_ENONRELOC));
474		return (EINVAL);
475	}
476
477	dsp = ac_del_alloc_status();
478	if ((retval = kphysm_del_gethandle(&dsp->handle)) != KPHYSM_OK) {
479		ac_del_free_status(dsp);
480		AC_ERR_SET(pkt, ac_kpm_err_cvt(retval));
481		return (EINVAL);
482	}
483	retval = 0;
484	busy_set = 0;
485
486	board = fhc_bdlist_lock(pkt->softsp->board);
487	if (board == NULL || board->ac_softsp == NULL) {
488		fhc_bdlist_unlock();
489		AC_ERR_SET(pkt, AC_ERR_BD);
490		retval = EINVAL;
491		goto out;
492	}
493	ASSERT(pkt->softsp == board->ac_softsp);
494	asp = pkt->softsp;
495
496	/* verify the board is of the correct type */
497	switch (board->sc.type) {
498	case CPU_BOARD:
499	case MEM_BOARD:
500		break;
501	default:
502		fhc_bdlist_unlock();
503		AC_ERR_SET(pkt, AC_ERR_BD_TYPE);
504		retval = EINVAL;
505		goto out;
506	}
507
508	/* verify the memory condition is acceptable */
509	mem_info = &asp->bank[pkt->bank];
510	if (!MEM_BOARD_VISIBLE(board) || mem_info->busy ||
511	    fhc_bd_busy(pkt->softsp->board) ||
512	    mem_info->rstate != SYSC_CFGA_RSTATE_CONNECTED ||
513	    mem_info->ostate != SYSC_CFGA_OSTATE_CONFIGURED) {
514		fhc_bdlist_unlock();
515		AC_ERR_SET(pkt, AC_ERR_BD_STATE);
516		retval = EINVAL;
517		goto out;
518	}
519
520	if ((dsp->del_timeout = pkt->cmd_cfga.arg) == -1)
521		dsp->del_timeout = ac_del_timeout;
522
523	/*
524	 * at this point, we have an available bank to del.
525	 * mark it busy and initiate the del function.
526	 */
527	mem_info->busy = TRUE;
528	fhc_bdlist_unlock();
529
530	busy_set = 1;
531
532	retval = ac_del_bank_add_span(dsp->handle, pkt);
533out:
534	if (retval != 0) {
535		r_errs = kphysm_del_release(dsp->handle);
536		ASSERT(r_errs == KPHYSM_OK);
537
538		if (busy_set) {
539			board = fhc_bdlist_lock(pkt->softsp->board);
540			ASSERT(board != NULL && board->ac_softsp != NULL);
541
542			ASSERT(board->sc.type == CPU_BOARD ||
543			    board->sc.type == MEM_BOARD);
544			ASSERT(asp ==
545			    (struct ac_soft_state *)(board->ac_softsp));
546			mem_info = &asp->bank[pkt->bank];
547			ASSERT(mem_info->busy != FALSE);
548			ASSERT(mem_info->ostate == SYSC_CFGA_OSTATE_CONFIGURED);
549			mem_info->busy = FALSE;
550			fhc_bdlist_unlock();
551		}
552
553		ac_del_free_status(dsp);
554		return (retval);
555	}
556
557	(void) kphysm_del_status(dsp->handle, &dstat);
558
559	retval = ac_del_bank_run(dsp, pkt);
560
561	r_errs = kphysm_del_release(dsp->handle);
562	ASSERT(r_errs == KPHYSM_OK);
563
564	board = fhc_bdlist_lock(pkt->softsp->board);
565	ASSERT(board != NULL && board->ac_softsp != NULL);
566
567	ASSERT(board->sc.type == CPU_BOARD || board->sc.type == MEM_BOARD);
568	ASSERT(asp == (struct ac_soft_state *)(board->ac_softsp));
569	mem_info = &asp->bank[pkt->bank];
570	ASSERT(mem_info->busy != FALSE);
571	ASSERT(mem_info->ostate == SYSC_CFGA_OSTATE_CONFIGURED);
572	mem_info->busy = FALSE;
573	if (retval == 0) {
574		mem_info->ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
575		mem_info->status_change = ddi_get_time();
576
577		if (ac_del_clean) {
578			/* DEBUG - set memory to known state */
579			ac_bank_write_pattern(board, pkt->bank);
580		}
581	} else {
582		/*
583		 * Restore the pages to the cage growth list.
584		 */
585		ac_del_bank_add_cage(board, pkt->bank);
586	}
587	fhc_bdlist_unlock();
588
589	ac_del_free_status(dsp);
590
591	return (retval);
592}
593