125cf1a30Sjl /*
225cf1a30Sjl * CDDL HEADER START
325cf1a30Sjl *
425cf1a30Sjl * The contents of this file are subject to the terms of the
525cf1a30Sjl * Common Development and Distribution License (the "License").
625cf1a30Sjl * You may not use this file except in compliance with the License.
725cf1a30Sjl *
825cf1a30Sjl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl * See the License for the specific language governing permissions
1125cf1a30Sjl * and limitations under the License.
1225cf1a30Sjl *
1325cf1a30Sjl * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl *
1925cf1a30Sjl * CDDL HEADER END
2025cf1a30Sjl */
2125cf1a30Sjl /*
22*56f33205SJonathan Adams * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
2325cf1a30Sjl * Use is subject to license terms.
2425cf1a30Sjl */
2525cf1a30Sjl
2625cf1a30Sjl /*
2725cf1a30Sjl * DR memory support routines.
2825cf1a30Sjl */
2925cf1a30Sjl
3025cf1a30Sjl #include <sys/note.h>
3125cf1a30Sjl #include <sys/debug.h>
3225cf1a30Sjl #include <sys/types.h>
3325cf1a30Sjl #include <sys/errno.h>
3425cf1a30Sjl #include <sys/param.h>
3525cf1a30Sjl #include <sys/dditypes.h>
3625cf1a30Sjl #include <sys/kmem.h>
3725cf1a30Sjl #include <sys/conf.h>
3825cf1a30Sjl #include <sys/ddi.h>
3925cf1a30Sjl #include <sys/sunddi.h>
4025cf1a30Sjl #include <sys/sunndi.h>
4125cf1a30Sjl #include <sys/ddi_impldefs.h>
4225cf1a30Sjl #include <sys/ndi_impldefs.h>
4325cf1a30Sjl #include <sys/sysmacros.h>
4425cf1a30Sjl #include <sys/machsystm.h>
4525cf1a30Sjl #include <sys/spitregs.h>
4625cf1a30Sjl #include <sys/cpuvar.h>
4725cf1a30Sjl #include <sys/promif.h>
4825cf1a30Sjl #include <vm/seg_kmem.h>
4925cf1a30Sjl #include <sys/lgrp.h>
5025cf1a30Sjl #include <sys/platform_module.h>
5125cf1a30Sjl
5225cf1a30Sjl #include <vm/page.h>
5325cf1a30Sjl
5425cf1a30Sjl #include <sys/dr.h>
5525cf1a30Sjl #include <sys/dr_util.h>
5625cf1a30Sjl #include <sys/drmach.h>
5768ac2337Sjl #include <sys/kobj.h>
5825cf1a30Sjl
5925cf1a30Sjl extern struct memlist *phys_install;
6068ac2337Sjl extern vnode_t *retired_pages;
6125cf1a30Sjl
6225cf1a30Sjl /* TODO: push this reference below drmach line */
6325cf1a30Sjl extern int kcage_on;
6425cf1a30Sjl
6525cf1a30Sjl /* for the DR*INTERNAL_ERROR macros. see sys/dr.h. */
6668ac2337Sjl static char *dr_ie_fmt = "dr_mem.c %d";
6725cf1a30Sjl
6825cf1a30Sjl typedef enum {
6925cf1a30Sjl DR_TP_INVALID = -1,
7025cf1a30Sjl DR_TP_SAME,
7125cf1a30Sjl DR_TP_LARGE,
7225cf1a30Sjl DR_TP_NONRELOC,
7325cf1a30Sjl DR_TP_FLOATING
7425cf1a30Sjl } dr_target_pref_t;
7525cf1a30Sjl
7625cf1a30Sjl static int dr_post_detach_mem_unit(dr_mem_unit_t *mp);
7725cf1a30Sjl static int dr_reserve_mem_spans(memhandle_t *mhp,
7825cf1a30Sjl struct memlist *mlist);
7925cf1a30Sjl static int dr_select_mem_target(dr_handle_t *hp,
8025cf1a30Sjl dr_mem_unit_t *mp, struct memlist *ml);
8125cf1a30Sjl static void dr_init_mem_unit_data(dr_mem_unit_t *mp);
8225cf1a30Sjl static struct memlist *dr_memlist_del_retired_pages(struct memlist *ml);
8325cf1a30Sjl static dr_target_pref_t dr_get_target_preference(dr_handle_t *hp,
8425cf1a30Sjl dr_mem_unit_t *t_mp, dr_mem_unit_t *s_mp,
8525cf1a30Sjl struct memlist *s_ml, struct memlist *x_ml,
8625cf1a30Sjl struct memlist *b_ml);
8725cf1a30Sjl
8825cf1a30Sjl static int memlist_canfit(struct memlist *s_mlist,
8925cf1a30Sjl struct memlist *t_mlist);
9025cf1a30Sjl static int dr_del_mlist_query(struct memlist *mlist,
9125cf1a30Sjl memquery_t *mp);
9225cf1a30Sjl static struct memlist *dr_get_copy_mlist(struct memlist *s_ml,
9325cf1a30Sjl struct memlist *t_ml, dr_mem_unit_t *s_mp,
9425cf1a30Sjl dr_mem_unit_t *t_mp);
9525cf1a30Sjl static struct memlist *dr_get_nonreloc_mlist(struct memlist *s_ml,
9625cf1a30Sjl dr_mem_unit_t *s_mp);
9725cf1a30Sjl static int dr_memlist_canfit(struct memlist *s_mlist,
9825cf1a30Sjl struct memlist *t_mlist, dr_mem_unit_t *s_mp,
9925cf1a30Sjl dr_mem_unit_t *t_mp);
10025cf1a30Sjl
10125cf1a30Sjl /*
10225cf1a30Sjl * dr_mem_unit_t.sbm_flags
10325cf1a30Sjl */
10425cf1a30Sjl #define DR_MFLAG_RESERVED 0x01 /* mem unit reserved for delete */
10525cf1a30Sjl #define DR_MFLAG_SOURCE 0x02 /* source brd of copy/rename op */
10625cf1a30Sjl #define DR_MFLAG_TARGET 0x04 /* target brd of copy/rename op */
10725cf1a30Sjl #define DR_MFLAG_RELOWNER 0x20 /* memory release (delete) owner */
10825cf1a30Sjl #define DR_MFLAG_RELDONE 0x40 /* memory release (delete) done */
10925cf1a30Sjl
11025cf1a30Sjl /* helper macros */
11125cf1a30Sjl #define _ptob64(p) ((uint64_t)(p) << PAGESHIFT)
11225cf1a30Sjl #define _b64top(b) ((pgcnt_t)((b) >> PAGESHIFT))
11325cf1a30Sjl
11425cf1a30Sjl static struct memlist *
dr_get_memlist(dr_mem_unit_t * mp)11525cf1a30Sjl dr_get_memlist(dr_mem_unit_t *mp)
11625cf1a30Sjl {
11725cf1a30Sjl struct memlist *mlist = NULL;
11825cf1a30Sjl sbd_error_t *err;
11925cf1a30Sjl static fn_t f = "dr_get_memlist";
12025cf1a30Sjl
12125cf1a30Sjl PR_MEM("%s for %s...\n", f, mp->sbm_cm.sbdev_path);
12225cf1a30Sjl
12325cf1a30Sjl /*
12425cf1a30Sjl * Return cached memlist, if present.
12525cf1a30Sjl * This memlist will be present following an
12625cf1a30Sjl * unconfigure (a.k.a: detach) of this memunit.
12725cf1a30Sjl * It should only be used in the case were a configure
12825cf1a30Sjl * is bringing this memunit back in without going
12925cf1a30Sjl * through the disconnect and connect states.
13025cf1a30Sjl */
13125cf1a30Sjl if (mp->sbm_mlist) {
13225cf1a30Sjl PR_MEM("%s: found cached memlist\n", f);
13325cf1a30Sjl
13425cf1a30Sjl mlist = memlist_dup(mp->sbm_mlist);
13525cf1a30Sjl } else {
13625cf1a30Sjl uint64_t basepa = _ptob64(mp->sbm_basepfn);
13725cf1a30Sjl
13825cf1a30Sjl /* attempt to construct a memlist using phys_install */
13925cf1a30Sjl
14025cf1a30Sjl /* round down to slice base address */
14125cf1a30Sjl basepa &= ~(mp->sbm_slice_size - 1);
14225cf1a30Sjl
14325cf1a30Sjl /* get a copy of phys_install to edit */
14425cf1a30Sjl memlist_read_lock();
14525cf1a30Sjl mlist = memlist_dup(phys_install);
14625cf1a30Sjl memlist_read_unlock();
14725cf1a30Sjl
14825cf1a30Sjl /* trim lower irrelevant span */
14925cf1a30Sjl if (mlist)
15025cf1a30Sjl mlist = memlist_del_span(mlist, 0ull, basepa);
15125cf1a30Sjl
15225cf1a30Sjl /* trim upper irrelevant span */
15325cf1a30Sjl if (mlist) {
15425cf1a30Sjl uint64_t endpa;
15525cf1a30Sjl
15625cf1a30Sjl basepa += mp->sbm_slice_size;
15725cf1a30Sjl endpa = _ptob64(physmax + 1);
15825cf1a30Sjl if (endpa > basepa)
15925cf1a30Sjl mlist = memlist_del_span(
1603103d4ceSjesusm mlist, basepa,
1613103d4ceSjesusm endpa - basepa);
16225cf1a30Sjl }
16325cf1a30Sjl
16425cf1a30Sjl if (mlist) {
16525cf1a30Sjl /* successfully built a memlist */
16625cf1a30Sjl PR_MEM("%s: derived memlist from phys_install\n", f);
16725cf1a30Sjl }
16825cf1a30Sjl
16925cf1a30Sjl /* if no mlist yet, try platform layer */
17025cf1a30Sjl if (!mlist) {
17125cf1a30Sjl err = drmach_mem_get_memlist(
1723103d4ceSjesusm mp->sbm_cm.sbdev_id, &mlist);
17325cf1a30Sjl if (err) {
17425cf1a30Sjl DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
17525cf1a30Sjl mlist = NULL; /* paranoia */
17625cf1a30Sjl }
17725cf1a30Sjl }
17825cf1a30Sjl }
17925cf1a30Sjl
18025cf1a30Sjl PR_MEM("%s: memlist for %s\n", f, mp->sbm_cm.sbdev_path);
18125cf1a30Sjl PR_MEMLIST_DUMP(mlist);
18225cf1a30Sjl
18325cf1a30Sjl return (mlist);
18425cf1a30Sjl }
18525cf1a30Sjl
18625cf1a30Sjl typedef struct {
18725cf1a30Sjl kcondvar_t cond;
18825cf1a30Sjl kmutex_t lock;
18925cf1a30Sjl int error;
19025cf1a30Sjl int done;
19125cf1a30Sjl } dr_release_mem_sync_t;
19225cf1a30Sjl
19325cf1a30Sjl /*
19425cf1a30Sjl * Memory has been logically removed by the time this routine is called.
19525cf1a30Sjl */
19625cf1a30Sjl static void
dr_mem_del_done(void * arg,int error)19725cf1a30Sjl dr_mem_del_done(void *arg, int error)
19825cf1a30Sjl {
19925cf1a30Sjl dr_release_mem_sync_t *ds = arg;
20025cf1a30Sjl
20125cf1a30Sjl mutex_enter(&ds->lock);
20225cf1a30Sjl ds->error = error;
20325cf1a30Sjl ds->done = 1;
20425cf1a30Sjl cv_signal(&ds->cond);
20525cf1a30Sjl mutex_exit(&ds->lock);
20625cf1a30Sjl }
20725cf1a30Sjl
20825cf1a30Sjl /*
20925cf1a30Sjl * When we reach here the memory being drained should have
21025cf1a30Sjl * already been reserved in dr_pre_release_mem().
21125cf1a30Sjl * Our only task here is to kick off the "drain" and wait
21225cf1a30Sjl * for it to finish.
21325cf1a30Sjl */
21425cf1a30Sjl void
dr_release_mem(dr_common_unit_t * cp)21525cf1a30Sjl dr_release_mem(dr_common_unit_t *cp)
21625cf1a30Sjl {
21725cf1a30Sjl dr_mem_unit_t *mp = (dr_mem_unit_t *)cp;
21825cf1a30Sjl int err;
21925cf1a30Sjl dr_release_mem_sync_t rms;
22025cf1a30Sjl static fn_t f = "dr_release_mem";
22125cf1a30Sjl
22225cf1a30Sjl /* check that this memory unit has been reserved */
22325cf1a30Sjl if (!(mp->sbm_flags & DR_MFLAG_RELOWNER)) {
22425cf1a30Sjl DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
22525cf1a30Sjl return;
22625cf1a30Sjl }
22725cf1a30Sjl
22825cf1a30Sjl bzero((void *) &rms, sizeof (rms));
22925cf1a30Sjl
23025cf1a30Sjl mutex_init(&rms.lock, NULL, MUTEX_DRIVER, NULL);
23125cf1a30Sjl cv_init(&rms.cond, NULL, CV_DRIVER, NULL);
23225cf1a30Sjl
23325cf1a30Sjl mutex_enter(&rms.lock);
23425cf1a30Sjl err = kphysm_del_start(mp->sbm_memhandle,
2353103d4ceSjesusm dr_mem_del_done, (void *) &rms);
23625cf1a30Sjl if (err == KPHYSM_OK) {
23725cf1a30Sjl /* wait for completion or interrupt */
23825cf1a30Sjl while (!rms.done) {
23925cf1a30Sjl if (cv_wait_sig(&rms.cond, &rms.lock) == 0) {
24025cf1a30Sjl /* then there is a pending UNIX signal */
24125cf1a30Sjl (void) kphysm_del_cancel(mp->sbm_memhandle);
24225cf1a30Sjl
24325cf1a30Sjl /* wait for completion */
24425cf1a30Sjl while (!rms.done)
24525cf1a30Sjl cv_wait(&rms.cond, &rms.lock);
24625cf1a30Sjl }
24725cf1a30Sjl }
24825cf1a30Sjl /* get the result of the memory delete operation */
24925cf1a30Sjl err = rms.error;
25025cf1a30Sjl }
25125cf1a30Sjl mutex_exit(&rms.lock);
25225cf1a30Sjl
25325cf1a30Sjl cv_destroy(&rms.cond);
25425cf1a30Sjl mutex_destroy(&rms.lock);
25525cf1a30Sjl
25625cf1a30Sjl if (err != KPHYSM_OK) {
25725cf1a30Sjl int e_code;
25825cf1a30Sjl
25925cf1a30Sjl switch (err) {
26025cf1a30Sjl case KPHYSM_ENOWORK:
26125cf1a30Sjl e_code = ESBD_NOERROR;
26225cf1a30Sjl break;
26325cf1a30Sjl
26425cf1a30Sjl case KPHYSM_EHANDLE:
26525cf1a30Sjl case KPHYSM_ESEQUENCE:
26625cf1a30Sjl e_code = ESBD_INTERNAL;
26725cf1a30Sjl break;
26825cf1a30Sjl
26925cf1a30Sjl case KPHYSM_ENOTVIABLE:
27025cf1a30Sjl e_code = ESBD_MEM_NOTVIABLE;
27125cf1a30Sjl break;
27225cf1a30Sjl
27325cf1a30Sjl case KPHYSM_EREFUSED:
27425cf1a30Sjl e_code = ESBD_MEM_REFUSED;
27525cf1a30Sjl break;
27625cf1a30Sjl
27725cf1a30Sjl case KPHYSM_ENONRELOC:
27825cf1a30Sjl e_code = ESBD_MEM_NONRELOC;
27925cf1a30Sjl break;
28025cf1a30Sjl
28125cf1a30Sjl case KPHYSM_ECANCELLED:
28225cf1a30Sjl e_code = ESBD_MEM_CANCELLED;
28325cf1a30Sjl break;
28425cf1a30Sjl
28525cf1a30Sjl case KPHYSM_ERESOURCE:
28625cf1a30Sjl e_code = ESBD_MEMFAIL;
28725cf1a30Sjl break;
28825cf1a30Sjl
28925cf1a30Sjl default:
29025cf1a30Sjl cmn_err(CE_WARN,
2913103d4ceSjesusm "%s: unexpected kphysm error code %d,"
2923103d4ceSjesusm " id 0x%p",
2933103d4ceSjesusm f, err, mp->sbm_cm.sbdev_id);
29425cf1a30Sjl
29525cf1a30Sjl e_code = ESBD_IO;
29625cf1a30Sjl break;
29725cf1a30Sjl }
29825cf1a30Sjl
29925cf1a30Sjl if (e_code != ESBD_NOERROR) {
300b307f191Sbm dr_dev_err(CE_WARN, &mp->sbm_cm, e_code);
30125cf1a30Sjl }
30225cf1a30Sjl }
30325cf1a30Sjl }
30425cf1a30Sjl
30525cf1a30Sjl void
dr_attach_mem(dr_handle_t * hp,dr_common_unit_t * cp)30625cf1a30Sjl dr_attach_mem(dr_handle_t *hp, dr_common_unit_t *cp)
30725cf1a30Sjl {
30825cf1a30Sjl _NOTE(ARGUNUSED(hp))
30925cf1a30Sjl
31025cf1a30Sjl dr_mem_unit_t *mp = (dr_mem_unit_t *)cp;
31125cf1a30Sjl struct memlist *ml, *mc;
31225cf1a30Sjl sbd_error_t *err;
31325cf1a30Sjl static fn_t f = "dr_attach_mem";
31425cf1a30Sjl
31525cf1a30Sjl PR_MEM("%s...\n", f);
31625cf1a30Sjl
31725cf1a30Sjl dr_lock_status(hp->h_bd);
31825cf1a30Sjl err = drmach_configure(cp->sbdev_id, 0);
31925cf1a30Sjl dr_unlock_status(hp->h_bd);
32025cf1a30Sjl if (err) {
32125cf1a30Sjl DRERR_SET_C(&cp->sbdev_error, &err);
32225cf1a30Sjl return;
32325cf1a30Sjl }
32425cf1a30Sjl
32525cf1a30Sjl ml = dr_get_memlist(mp);
326*56f33205SJonathan Adams for (mc = ml; mc; mc = mc->ml_next) {
32725cf1a30Sjl int rv;
32825cf1a30Sjl sbd_error_t *err;
32925cf1a30Sjl
33025cf1a30Sjl rv = kphysm_add_memory_dynamic(
331*56f33205SJonathan Adams (pfn_t)(mc->ml_address >> PAGESHIFT),
332*56f33205SJonathan Adams (pgcnt_t)(mc->ml_size >> PAGESHIFT));
33325cf1a30Sjl if (rv != KPHYSM_OK) {
33425cf1a30Sjl /*
33525cf1a30Sjl * translate kphysm error and
33625cf1a30Sjl * store in devlist error
33725cf1a30Sjl */
33825cf1a30Sjl switch (rv) {
33925cf1a30Sjl case KPHYSM_ERESOURCE:
34025cf1a30Sjl rv = ESBD_NOMEM;
34125cf1a30Sjl break;
34225cf1a30Sjl
34325cf1a30Sjl case KPHYSM_EFAULT:
34425cf1a30Sjl rv = ESBD_FAULT;
34525cf1a30Sjl break;
34625cf1a30Sjl
34725cf1a30Sjl default:
34825cf1a30Sjl rv = ESBD_INTERNAL;
34925cf1a30Sjl break;
35025cf1a30Sjl }
35125cf1a30Sjl
35225cf1a30Sjl if (rv == ESBD_INTERNAL) {
35325cf1a30Sjl DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
35425cf1a30Sjl } else
35525cf1a30Sjl dr_dev_err(CE_WARN, &mp->sbm_cm, rv);
35625cf1a30Sjl break;
35725cf1a30Sjl }
35825cf1a30Sjl
35925cf1a30Sjl err = drmach_mem_add_span(
360*56f33205SJonathan Adams mp->sbm_cm.sbdev_id, mc->ml_address, mc->ml_size);
36125cf1a30Sjl if (err) {
36225cf1a30Sjl DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
36325cf1a30Sjl break;
36425cf1a30Sjl }
36525cf1a30Sjl }
36625cf1a30Sjl
36725cf1a30Sjl memlist_delete(ml);
36825cf1a30Sjl
36925cf1a30Sjl /* back out if configure failed */
37025cf1a30Sjl if (mp->sbm_cm.sbdev_error != NULL) {
37125cf1a30Sjl dr_lock_status(hp->h_bd);
37225cf1a30Sjl err = drmach_unconfigure(cp->sbdev_id, 0);
37325cf1a30Sjl if (err)
37425cf1a30Sjl sbd_err_clear(&err);
37525cf1a30Sjl dr_unlock_status(hp->h_bd);
37625cf1a30Sjl }
37725cf1a30Sjl }
37825cf1a30Sjl
37925cf1a30Sjl static struct memlist *
dr_memlist_del_retired_pages(struct memlist * mlist)38025cf1a30Sjl dr_memlist_del_retired_pages(struct memlist *mlist)
38125cf1a30Sjl {
38225cf1a30Sjl page_t *pp;
38325cf1a30Sjl pfn_t pfn;
38425cf1a30Sjl kmutex_t *vphm;
38568ac2337Sjl vnode_t *vp = retired_pages;
38625cf1a30Sjl static fn_t f = "dr_memlist_del_retired_pages";
38725cf1a30Sjl
38825cf1a30Sjl vphm = page_vnode_mutex(vp);
38925cf1a30Sjl mutex_enter(vphm);
39025cf1a30Sjl
39125cf1a30Sjl PR_MEM("%s\n", f);
39225cf1a30Sjl
39325cf1a30Sjl if ((pp = vp->v_pages) == NULL) {
39425cf1a30Sjl mutex_exit(vphm);
39525cf1a30Sjl return (mlist);
39625cf1a30Sjl }
39725cf1a30Sjl
39825cf1a30Sjl do {
39925cf1a30Sjl ASSERT(pp != NULL);
40068ac2337Sjl ASSERT(pp->p_vnode == retired_pages);
40125cf1a30Sjl
4020c48eb93Sbm if (!page_try_reclaim_lock(pp, SE_SHARED, SE_RETIRED))
40325cf1a30Sjl continue;
40425cf1a30Sjl
40525cf1a30Sjl pfn = page_pptonum(pp);
40625cf1a30Sjl
40725cf1a30Sjl /*
40825cf1a30Sjl * Page retirement currently breaks large pages into PAGESIZE
40925cf1a30Sjl * pages. If this changes, need to remove the assert and deal
41025cf1a30Sjl * with different page sizes.
41125cf1a30Sjl */
41225cf1a30Sjl ASSERT(pp->p_szc == 0);
41325cf1a30Sjl
41425cf1a30Sjl if (address_in_memlist(mlist, ptob(pfn), PAGESIZE)) {
41525cf1a30Sjl mlist = memlist_del_span(mlist, ptob(pfn), PAGESIZE);
41625cf1a30Sjl PR_MEM("deleted retired page 0x%lx (pfn 0x%lx) "
41725cf1a30Sjl "from memlist\n", ptob(pfn), pfn);
41825cf1a30Sjl }
41925cf1a30Sjl
42025cf1a30Sjl page_unlock(pp);
42125cf1a30Sjl } while ((pp = pp->p_vpnext) != vp->v_pages);
42225cf1a30Sjl
42325cf1a30Sjl mutex_exit(vphm);
42425cf1a30Sjl
42525cf1a30Sjl return (mlist);
42625cf1a30Sjl }
42725cf1a30Sjl
42825cf1a30Sjl static int
dr_move_memory(dr_handle_t * hp,dr_mem_unit_t * s_mp,dr_mem_unit_t * t_mp)42925cf1a30Sjl dr_move_memory(dr_handle_t *hp, dr_mem_unit_t *s_mp, dr_mem_unit_t *t_mp)
43025cf1a30Sjl {
43125cf1a30Sjl int rv = -1;
43225cf1a30Sjl time_t copytime;
43325cf1a30Sjl drmachid_t cr_id;
43425cf1a30Sjl dr_sr_handle_t *srhp = NULL;
43525cf1a30Sjl dr_board_t *t_bp, *s_bp;
43625cf1a30Sjl struct memlist *c_ml, *d_ml;
43725cf1a30Sjl sbd_error_t *err;
43825cf1a30Sjl static fn_t f = "dr_move_memory";
43925cf1a30Sjl
44025cf1a30Sjl PR_MEM("%s: (INLINE) moving memory from %s to %s\n",
4413103d4ceSjesusm f,
4423103d4ceSjesusm s_mp->sbm_cm.sbdev_path,
4433103d4ceSjesusm t_mp->sbm_cm.sbdev_path);
44425cf1a30Sjl
44525cf1a30Sjl ASSERT(s_mp->sbm_flags & DR_MFLAG_SOURCE);
44625cf1a30Sjl ASSERT(s_mp->sbm_peer == t_mp);
44725cf1a30Sjl ASSERT(s_mp->sbm_mlist);
44825cf1a30Sjl
44925cf1a30Sjl ASSERT(t_mp->sbm_flags & DR_MFLAG_TARGET);
45025cf1a30Sjl ASSERT(t_mp->sbm_peer == s_mp);
45125cf1a30Sjl
45225cf1a30Sjl /*
45325cf1a30Sjl * create a memlist of spans to copy by removing
45425cf1a30Sjl * the spans that have been deleted, if any, from
45525cf1a30Sjl * the full source board memlist. s_mp->sbm_del_mlist
45625cf1a30Sjl * will be NULL if there were no spans deleted from
45725cf1a30Sjl * the source board.
45825cf1a30Sjl */
45925cf1a30Sjl c_ml = memlist_dup(s_mp->sbm_mlist);
46025cf1a30Sjl d_ml = s_mp->sbm_del_mlist;
46125cf1a30Sjl while (d_ml != NULL) {
462*56f33205SJonathan Adams c_ml = memlist_del_span(c_ml, d_ml->ml_address, d_ml->ml_size);
463*56f33205SJonathan Adams d_ml = d_ml->ml_next;
46425cf1a30Sjl }
46525cf1a30Sjl
46625cf1a30Sjl /*
46725cf1a30Sjl * Remove retired pages from the copy list. The page content
46825cf1a30Sjl * need not be copied since the pages are no longer in use.
46925cf1a30Sjl */
47025cf1a30Sjl PR_MEM("%s: copy list before removing retired pages (if any):\n", f);
47125cf1a30Sjl PR_MEMLIST_DUMP(c_ml);
47225cf1a30Sjl
47325cf1a30Sjl c_ml = dr_memlist_del_retired_pages(c_ml);
47425cf1a30Sjl
47525cf1a30Sjl PR_MEM("%s: copy list after removing retired pages:\n", f);
47625cf1a30Sjl PR_MEMLIST_DUMP(c_ml);
47725cf1a30Sjl
47825cf1a30Sjl /*
47925cf1a30Sjl * With parallel copy, it shouldn't make a difference which
48025cf1a30Sjl * CPU is the actual master during copy-rename since all
48125cf1a30Sjl * CPUs participate in the parallel copy anyway.
48225cf1a30Sjl */
48325cf1a30Sjl affinity_set(CPU_CURRENT);
48425cf1a30Sjl
48525cf1a30Sjl err = drmach_copy_rename_init(
4863103d4ceSjesusm t_mp->sbm_cm.sbdev_id, s_mp->sbm_cm.sbdev_id, c_ml, &cr_id);
48725cf1a30Sjl if (err) {
48825cf1a30Sjl DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
48925cf1a30Sjl affinity_clear();
49025cf1a30Sjl memlist_delete(c_ml);
49125cf1a30Sjl return (-1);
49225cf1a30Sjl }
49325cf1a30Sjl
49425cf1a30Sjl srhp = dr_get_sr_handle(hp);
49525cf1a30Sjl ASSERT(srhp);
49625cf1a30Sjl
497d3d50737SRafael Vanoni copytime = ddi_get_lbolt();
49825cf1a30Sjl
49925cf1a30Sjl /* Quiesce the OS. */
50025cf1a30Sjl if (dr_suspend(srhp)) {
50125cf1a30Sjl cmn_err(CE_WARN, "%s: failed to quiesce OS"
5023103d4ceSjesusm " for copy-rename", f);
50325cf1a30Sjl
50425cf1a30Sjl err = drmach_copy_rename_fini(cr_id);
50525cf1a30Sjl if (err) {
50625cf1a30Sjl /*
50725cf1a30Sjl * no error is expected since the program has
50825cf1a30Sjl * not yet run.
50925cf1a30Sjl */
51025cf1a30Sjl
51125cf1a30Sjl /* catch this in debug kernels */
51225cf1a30Sjl ASSERT(0);
51325cf1a30Sjl
51425cf1a30Sjl sbd_err_clear(&err);
51525cf1a30Sjl }
51625cf1a30Sjl
51725cf1a30Sjl /* suspend error reached via hp */
51825cf1a30Sjl s_mp->sbm_cm.sbdev_error = hp->h_err;
51925cf1a30Sjl hp->h_err = NULL;
52025cf1a30Sjl goto done;
52125cf1a30Sjl }
52225cf1a30Sjl
52325cf1a30Sjl drmach_copy_rename(cr_id);
52425cf1a30Sjl
52525cf1a30Sjl /* Resume the OS. */
52625cf1a30Sjl dr_resume(srhp);
52725cf1a30Sjl
528d3d50737SRafael Vanoni copytime = ddi_get_lbolt() - copytime;
52925cf1a30Sjl
53025cf1a30Sjl if (err = drmach_copy_rename_fini(cr_id))
53125cf1a30Sjl goto done;
53225cf1a30Sjl
53325cf1a30Sjl /*
53425cf1a30Sjl * Rename memory for lgroup.
53525cf1a30Sjl * Source and target board numbers are packaged in arg.
53625cf1a30Sjl */
53725cf1a30Sjl s_bp = s_mp->sbm_cm.sbdev_bp;
53825cf1a30Sjl t_bp = t_mp->sbm_cm.sbdev_bp;
53925cf1a30Sjl
54025cf1a30Sjl lgrp_plat_config(LGRP_CONFIG_MEM_RENAME,
5413103d4ceSjesusm (uintptr_t)(s_bp->b_num | (t_bp->b_num << 16)));
54225cf1a30Sjl
54325cf1a30Sjl
54425cf1a30Sjl PR_MEM("%s: copy-rename elapsed time = %ld ticks (%ld secs)\n",
5453103d4ceSjesusm f, copytime, copytime / hz);
54625cf1a30Sjl
54725cf1a30Sjl rv = 0;
54825cf1a30Sjl done:
54925cf1a30Sjl if (srhp)
55025cf1a30Sjl dr_release_sr_handle(srhp);
55125cf1a30Sjl if (err)
55225cf1a30Sjl DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
55325cf1a30Sjl affinity_clear();
55425cf1a30Sjl
55525cf1a30Sjl return (rv);
55625cf1a30Sjl }
55725cf1a30Sjl
55825cf1a30Sjl /*
55925cf1a30Sjl * If detaching node contains memory that is "non-permanent"
56025cf1a30Sjl * then the memory adr's are simply cleared. If the memory
56125cf1a30Sjl * is non-relocatable, then do a copy-rename.
56225cf1a30Sjl */
56325cf1a30Sjl void
dr_detach_mem(dr_handle_t * hp,dr_common_unit_t * cp)56425cf1a30Sjl dr_detach_mem(dr_handle_t *hp, dr_common_unit_t *cp)
56525cf1a30Sjl {
56625cf1a30Sjl int rv = 0;
56725cf1a30Sjl dr_mem_unit_t *s_mp = (dr_mem_unit_t *)cp;
56825cf1a30Sjl dr_mem_unit_t *t_mp;
56925cf1a30Sjl dr_state_t state;
57025cf1a30Sjl static fn_t f = "dr_detach_mem";
57125cf1a30Sjl
57225cf1a30Sjl PR_MEM("%s...\n", f);
57325cf1a30Sjl
57425cf1a30Sjl /* lookup target mem unit and target board structure, if any */
57525cf1a30Sjl if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
57625cf1a30Sjl t_mp = s_mp->sbm_peer;
57725cf1a30Sjl ASSERT(t_mp != NULL);
57825cf1a30Sjl ASSERT(t_mp->sbm_peer == s_mp);
57925cf1a30Sjl } else {
58025cf1a30Sjl t_mp = NULL;
58125cf1a30Sjl }
58225cf1a30Sjl
58325cf1a30Sjl /* verify mem unit's state is UNREFERENCED */
58425cf1a30Sjl state = s_mp->sbm_cm.sbdev_state;
58525cf1a30Sjl if (state != DR_STATE_UNREFERENCED) {
58625cf1a30Sjl dr_dev_err(CE_IGNORE, &s_mp->sbm_cm, ESBD_STATE);
58725cf1a30Sjl return;
58825cf1a30Sjl }
58925cf1a30Sjl
59025cf1a30Sjl /* verify target mem unit's state is UNREFERENCED, if any */
59125cf1a30Sjl if (t_mp != NULL) {
59225cf1a30Sjl state = t_mp->sbm_cm.sbdev_state;
59325cf1a30Sjl if (state != DR_STATE_UNREFERENCED) {
59425cf1a30Sjl dr_dev_err(CE_IGNORE, &t_mp->sbm_cm, ESBD_STATE);
59525cf1a30Sjl return;
59625cf1a30Sjl }
59725cf1a30Sjl }
59825cf1a30Sjl
59925cf1a30Sjl /*
60025cf1a30Sjl * If there is no target board (no copy/rename was needed), then
60125cf1a30Sjl * we're done!
60225cf1a30Sjl */
60325cf1a30Sjl if (t_mp == NULL) {
60425cf1a30Sjl sbd_error_t *err;
60525cf1a30Sjl /*
60625cf1a30Sjl * Reprogram interconnect hardware and disable
60725cf1a30Sjl * memory controllers for memory node that's going away.
60825cf1a30Sjl */
60925cf1a30Sjl
61025cf1a30Sjl err = drmach_mem_disable(s_mp->sbm_cm.sbdev_id);
61125cf1a30Sjl if (err) {
61225cf1a30Sjl DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
61325cf1a30Sjl rv = -1;
61425cf1a30Sjl }
61525cf1a30Sjl } else {
61625cf1a30Sjl rv = dr_move_memory(hp, s_mp, t_mp);
61725cf1a30Sjl PR_MEM("%s: %s memory COPY-RENAME (board %d -> %d)\n",
6183103d4ceSjesusm f,
6193103d4ceSjesusm rv ? "FAILED" : "COMPLETED",
6203103d4ceSjesusm s_mp->sbm_cm.sbdev_bp->b_num,
6213103d4ceSjesusm t_mp->sbm_cm.sbdev_bp->b_num);
62225cf1a30Sjl
62325cf1a30Sjl if (rv != 0)
62425cf1a30Sjl (void) dr_cancel_mem(s_mp);
62525cf1a30Sjl }
62625cf1a30Sjl
62725cf1a30Sjl if (rv == 0) {
62825cf1a30Sjl sbd_error_t *err;
62925cf1a30Sjl
63025cf1a30Sjl dr_lock_status(hp->h_bd);
63125cf1a30Sjl err = drmach_unconfigure(s_mp->sbm_cm.sbdev_id, 0);
63225cf1a30Sjl dr_unlock_status(hp->h_bd);
63325cf1a30Sjl if (err)
63425cf1a30Sjl sbd_err_clear(&err);
63525cf1a30Sjl }
63625cf1a30Sjl }
63725cf1a30Sjl
63825cf1a30Sjl /*
63925cf1a30Sjl * This routine acts as a wrapper for kphysm_del_span_query in order to
64025cf1a30Sjl * support potential memory holes in a board's physical address space.
64125cf1a30Sjl * It calls kphysm_del_span_query for each node in a memlist and accumulates
64225cf1a30Sjl * the results in *mp.
64325cf1a30Sjl */
64425cf1a30Sjl static int
dr_del_mlist_query(struct memlist * mlist,memquery_t * mp)64525cf1a30Sjl dr_del_mlist_query(struct memlist *mlist, memquery_t *mp)
64625cf1a30Sjl {
64725cf1a30Sjl struct memlist *ml;
64825cf1a30Sjl int rv = 0;
64925cf1a30Sjl
65025cf1a30Sjl
65125cf1a30Sjl if (mlist == NULL)
65225cf1a30Sjl cmn_err(CE_WARN, "dr_del_mlist_query: mlist=NULL\n");
65325cf1a30Sjl
65425cf1a30Sjl mp->phys_pages = 0;
65525cf1a30Sjl mp->managed = 0;
65625cf1a30Sjl mp->nonrelocatable = 0;
65725cf1a30Sjl mp->first_nonrelocatable = (pfn_t)-1; /* XXX */
65825cf1a30Sjl mp->last_nonrelocatable = 0;
65925cf1a30Sjl
660*56f33205SJonathan Adams for (ml = mlist; ml; ml = ml->ml_next) {
66125cf1a30Sjl memquery_t mq;
66225cf1a30Sjl
66325cf1a30Sjl rv = kphysm_del_span_query(
664*56f33205SJonathan Adams _b64top(ml->ml_address), _b64top(ml->ml_size), &mq);
66525cf1a30Sjl if (rv)
66625cf1a30Sjl break;
66725cf1a30Sjl
66825cf1a30Sjl mp->phys_pages += mq.phys_pages;
66925cf1a30Sjl mp->managed += mq.managed;
67025cf1a30Sjl mp->nonrelocatable += mq.nonrelocatable;
67125cf1a30Sjl
67225cf1a30Sjl if (mq.nonrelocatable != 0) {
67325cf1a30Sjl if (mq.first_nonrelocatable < mp->first_nonrelocatable)
67425cf1a30Sjl mp->first_nonrelocatable =
6753103d4ceSjesusm mq.first_nonrelocatable;
67625cf1a30Sjl if (mq.last_nonrelocatable > mp->last_nonrelocatable)
67725cf1a30Sjl mp->last_nonrelocatable =
6783103d4ceSjesusm mq.last_nonrelocatable;
67925cf1a30Sjl }
68025cf1a30Sjl }
68125cf1a30Sjl
68225cf1a30Sjl if (mp->nonrelocatable == 0)
68325cf1a30Sjl mp->first_nonrelocatable = 0; /* XXX */
68425cf1a30Sjl
68525cf1a30Sjl return (rv);
68625cf1a30Sjl }
68725cf1a30Sjl
68825cf1a30Sjl /*
68925cf1a30Sjl * NOTE: This routine is only partially smart about multiple
69025cf1a30Sjl * mem-units. Need to make mem-status structure smart
69125cf1a30Sjl * about them also.
69225cf1a30Sjl */
69325cf1a30Sjl int
dr_mem_status(dr_handle_t * hp,dr_devset_t devset,sbd_dev_stat_t * dsp)69425cf1a30Sjl dr_mem_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp)
69525cf1a30Sjl {
69625cf1a30Sjl int m, mix;
69725cf1a30Sjl memdelstat_t mdst;
69825cf1a30Sjl memquery_t mq;
69925cf1a30Sjl dr_board_t *bp;
70025cf1a30Sjl dr_mem_unit_t *mp;
70125cf1a30Sjl sbd_mem_stat_t *msp;
70225cf1a30Sjl static fn_t f = "dr_mem_status";
70325cf1a30Sjl
70425cf1a30Sjl bp = hp->h_bd;
70525cf1a30Sjl devset &= DR_DEVS_PRESENT(bp);
70625cf1a30Sjl
70725cf1a30Sjl for (m = mix = 0; m < MAX_MEM_UNITS_PER_BOARD; m++) {
70825cf1a30Sjl int rv;
70925cf1a30Sjl sbd_error_t *err;
71025cf1a30Sjl drmach_status_t pstat;
71125cf1a30Sjl dr_mem_unit_t *p_mp;
71225cf1a30Sjl
71325cf1a30Sjl if (DEVSET_IN_SET(devset, SBD_COMP_MEM, m) == 0)
71425cf1a30Sjl continue;
71525cf1a30Sjl
71625cf1a30Sjl mp = dr_get_mem_unit(bp, m);
71725cf1a30Sjl
71825cf1a30Sjl if (mp->sbm_cm.sbdev_state == DR_STATE_EMPTY) {
71925cf1a30Sjl /* present, but not fully initialized */
72025cf1a30Sjl continue;
72125cf1a30Sjl }
72225cf1a30Sjl
72325cf1a30Sjl if (mp->sbm_cm.sbdev_id == (drmachid_t)0)
72425cf1a30Sjl continue;
72525cf1a30Sjl
72625cf1a30Sjl /* fetch platform status */
72725cf1a30Sjl err = drmach_status(mp->sbm_cm.sbdev_id, &pstat);
72825cf1a30Sjl if (err) {
72925cf1a30Sjl DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
73025cf1a30Sjl continue;
73125cf1a30Sjl }
73225cf1a30Sjl
73325cf1a30Sjl msp = &dsp->d_mem;
73425cf1a30Sjl bzero((caddr_t)msp, sizeof (*msp));
73525cf1a30Sjl
73607d06da5SSurya Prakki (void) strncpy(msp->ms_cm.c_id.c_name, pstat.type,
7373103d4ceSjesusm sizeof (msp->ms_cm.c_id.c_name));
73825cf1a30Sjl msp->ms_cm.c_id.c_type = mp->sbm_cm.sbdev_type;
73925cf1a30Sjl msp->ms_cm.c_id.c_unit = SBD_NULL_UNIT;
74025cf1a30Sjl msp->ms_cm.c_cond = mp->sbm_cm.sbdev_cond;
74125cf1a30Sjl msp->ms_cm.c_busy = mp->sbm_cm.sbdev_busy | pstat.busy;
74225cf1a30Sjl msp->ms_cm.c_time = mp->sbm_cm.sbdev_time;
74325cf1a30Sjl msp->ms_cm.c_ostate = mp->sbm_cm.sbdev_ostate;
74425cf1a30Sjl
74525cf1a30Sjl msp->ms_totpages = mp->sbm_npages;
74625cf1a30Sjl msp->ms_basepfn = mp->sbm_basepfn;
74725cf1a30Sjl msp->ms_pageslost = mp->sbm_pageslost;
74825cf1a30Sjl msp->ms_cage_enabled = kcage_on;
74925cf1a30Sjl
75025cf1a30Sjl if (mp->sbm_flags & DR_MFLAG_RESERVED)
75125cf1a30Sjl p_mp = mp->sbm_peer;
75225cf1a30Sjl else
75325cf1a30Sjl p_mp = NULL;
75425cf1a30Sjl
75525cf1a30Sjl if (p_mp == NULL) {
75625cf1a30Sjl msp->ms_peer_is_target = 0;
75725cf1a30Sjl msp->ms_peer_ap_id[0] = '\0';
75825cf1a30Sjl } else if (p_mp->sbm_flags & DR_MFLAG_RESERVED) {
75925cf1a30Sjl char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
76025cf1a30Sjl char *minor;
76125cf1a30Sjl
76225cf1a30Sjl /*
76325cf1a30Sjl * b_dip doesn't have to be held for ddi_pathname()
76425cf1a30Sjl * because the board struct (dr_board_t) will be
76525cf1a30Sjl * destroyed before b_dip detaches.
76625cf1a30Sjl */
76725cf1a30Sjl (void) ddi_pathname(bp->b_dip, path);
76825cf1a30Sjl minor = strchr(p_mp->sbm_cm.sbdev_path, ':');
76925cf1a30Sjl
77007d06da5SSurya Prakki (void) snprintf(msp->ms_peer_ap_id,
77125cf1a30Sjl sizeof (msp->ms_peer_ap_id), "%s%s",
77225cf1a30Sjl path, (minor == NULL) ? "" : minor);
77325cf1a30Sjl
77425cf1a30Sjl kmem_free(path, MAXPATHLEN);
77525cf1a30Sjl
77625cf1a30Sjl if (p_mp->sbm_flags & DR_MFLAG_TARGET)
77725cf1a30Sjl msp->ms_peer_is_target = 1;
77825cf1a30Sjl }
77925cf1a30Sjl
78025cf1a30Sjl if (mp->sbm_flags & DR_MFLAG_RELOWNER)
78125cf1a30Sjl rv = kphysm_del_status(mp->sbm_memhandle, &mdst);
78225cf1a30Sjl else
78325cf1a30Sjl rv = KPHYSM_EHANDLE; /* force 'if' to fail */
78425cf1a30Sjl
78525cf1a30Sjl if (rv == KPHYSM_OK) {
78625cf1a30Sjl /*
78725cf1a30Sjl * Any pages above managed is "free",
78825cf1a30Sjl * i.e. it's collected.
78925cf1a30Sjl */
79025cf1a30Sjl msp->ms_detpages += (uint_t)(mdst.collected +
79125cf1a30Sjl mdst.phys_pages - mdst.managed);
79225cf1a30Sjl } else {
79325cf1a30Sjl /*
79425cf1a30Sjl * If we're UNREFERENCED or UNCONFIGURED,
79525cf1a30Sjl * then the number of detached pages is
79625cf1a30Sjl * however many pages are on the board.
79725cf1a30Sjl * I.e. detached = not in use by OS.
79825cf1a30Sjl */
79925cf1a30Sjl switch (msp->ms_cm.c_ostate) {
80025cf1a30Sjl /*
80125cf1a30Sjl * changed to use cfgadm states
80225cf1a30Sjl *
80325cf1a30Sjl * was:
80425cf1a30Sjl * case DR_STATE_UNREFERENCED:
80525cf1a30Sjl * case DR_STATE_UNCONFIGURED:
80625cf1a30Sjl */
80725cf1a30Sjl case SBD_STAT_UNCONFIGURED:
80825cf1a30Sjl msp->ms_detpages = msp->ms_totpages;
80925cf1a30Sjl break;
81025cf1a30Sjl
81125cf1a30Sjl default:
81225cf1a30Sjl break;
81325cf1a30Sjl }
81425cf1a30Sjl }
81525cf1a30Sjl
81625cf1a30Sjl /*
81725cf1a30Sjl * kphysm_del_span_query can report non-reloc pages = total
81825cf1a30Sjl * pages for memory that is not yet configured
81925cf1a30Sjl */
82025cf1a30Sjl if (mp->sbm_cm.sbdev_state != DR_STATE_UNCONFIGURED) {
82125cf1a30Sjl struct memlist *ml;
82225cf1a30Sjl
82325cf1a30Sjl ml = dr_get_memlist(mp);
82425cf1a30Sjl rv = ml ? dr_del_mlist_query(ml, &mq) : -1;
82525cf1a30Sjl memlist_delete(ml);
82625cf1a30Sjl
82725cf1a30Sjl if (rv == KPHYSM_OK) {
82825cf1a30Sjl msp->ms_managed_pages = mq.managed;
82925cf1a30Sjl msp->ms_noreloc_pages = mq.nonrelocatable;
83025cf1a30Sjl msp->ms_noreloc_first =
83125cf1a30Sjl mq.first_nonrelocatable;
83225cf1a30Sjl msp->ms_noreloc_last =
83325cf1a30Sjl mq.last_nonrelocatable;
83425cf1a30Sjl msp->ms_cm.c_sflags = 0;
83525cf1a30Sjl if (mq.nonrelocatable) {
83625cf1a30Sjl SBD_SET_SUSPEND(SBD_CMD_UNCONFIGURE,
83725cf1a30Sjl msp->ms_cm.c_sflags);
83825cf1a30Sjl }
83925cf1a30Sjl } else {
84025cf1a30Sjl PR_MEM("%s: kphysm_del_span_query() = %d\n",
84125cf1a30Sjl f, rv);
84225cf1a30Sjl }
84325cf1a30Sjl }
84425cf1a30Sjl
84525cf1a30Sjl /*
84625cf1a30Sjl * Check source unit state during copy-rename
84725cf1a30Sjl */
84825cf1a30Sjl if ((mp->sbm_flags & DR_MFLAG_SOURCE) &&
84925cf1a30Sjl (mp->sbm_cm.sbdev_state == DR_STATE_UNREFERENCED ||
85025cf1a30Sjl mp->sbm_cm.sbdev_state == DR_STATE_RELEASE))
85125cf1a30Sjl msp->ms_cm.c_ostate = SBD_STAT_CONFIGURED;
85225cf1a30Sjl
85325cf1a30Sjl mix++;
85425cf1a30Sjl dsp++;
85525cf1a30Sjl }
85625cf1a30Sjl
85725cf1a30Sjl return (mix);
85825cf1a30Sjl }
85925cf1a30Sjl
86025cf1a30Sjl int
dr_pre_attach_mem(dr_handle_t * hp,dr_common_unit_t ** devlist,int devnum)86125cf1a30Sjl dr_pre_attach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
86225cf1a30Sjl {
863