#ifndef lint
static  char sccsid[] = "@(#)mb_machdep.c 1.1 94/10/31 SMI";
#endif

/*
 * Copyright (c) 1988-1991 by Sun Microsystems, Inc.
 */

/*
 * Machine dependent mainbus support routines.
 *
 * N.B. The mainbus code no longer requires the mainbus structures (mb_ctlr
 * and so on) to ask for DVMA space or to queue up when DVMA is not available.
 * Drivers can now send in a generic map structure and a pointer to a function
 * which is queued and invoked later when space frees up again. This mechanism
 * allows devices to manage their own queues and especially to queue multiple
 * I/O's to the same device. The old mbsetup()/mbrelse() interface has been
 * retained and can be used in the same way as before (these routines can
 * be found in <sundev/mb.c>).
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/vmmac.h>
#include <sys/vmmeter.h>
#include <sys/vmparam.h>
#include <sys/map.h>
#include <sys/buf.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/mman.h>
#include <sys/debug.h>
#include <sys/kmem_alloc.h>

#include <machine/mmu.h>
#include <machine/cpu.h>
#include <machine/psl.h>
#include <machine/pte.h>
#include <machine/reg.h>

#include <sundev/mbvar.h>

#include <vm/hat.h>
#include <vm/as.h>
#include <vm/page.h>
#include <vm/seg.h>
#include <machine/seg_kmem.h>

#ifdef IOMMU
#include <machine/iommu.h>
#endif IOMMU

#ifdef IOC
#include <machine/iocache.h>
#endif IOC

/*
 * Drivers queue function pointers here when they have to wait for
 * resources. Subsequent calls from the same driver that are forced to
 * wait need not queue up again, as the function pointed to will run the
 * driver's internal queues until done or space runs out again. The arg
 * parameter can be used by drivers as a cookie to be passed back by the
 * callback function. They should pass in NULL if not using the parameter.
 * Note that if drivers keep calling the allocation routine after
 * resources ran out that the arg parameter associated with subsequent
 * calls is ignored.
 *
 * The system priority level at which the allocation routine is called at
 * also indicates an implicit ordering as to whom is called back first.
 * When a resource is freed, the current system priority level is used to
 * screen out and not call back directly waiters who called the allocation
 * routine at a lower system priority level- they instead are called via
 * softcall() (if resources are left after calling back directly those who
 * can be called back directly). Note that if the original call had stored
 * up at priority 0, the intent of this indirect mechanism cannot be
 * achieved (and a warning message will be printed upon the storing of
 * such a callback).
 *
 * Because elements may be removed out of order (due to pri differences),
 * a simple cycle through a limited array isn't enough if we still wan't
 * preserve any semblance of FIFO servicing. A simple linked list will fit
 * the bill. In fact, an array could still be used, but it turns out that
 * a linked list uses fewer instructions to manage in this fashion (while
 * losing locality of reference and possible cache gains). On the other
 * hand, this is more like we'll want this to be, and it probably doesn't
 * make that big a difference.
 */

#define	MAXMBQLEN	10

static struct mbq_elem {
	struct mbq_elem	*mbq_next;
	func_t		mbq_funcp;
	caddr_t		mbq_arg;
	int		mbq_pri;
} mbq[MAXMBQLEN];


/*
 * MB related stats
 */
static struct mbs {
	int m_runout;   	/* times DVMA has run out */
	int m_queued;    	/* driver already had request queued */
	int m_bigreq;   	/* largest request received (bytes) */
	int m_hiqlen;    	/* longest length of wait queue */
	int m_softcall;		/* number of callbacks via softcall */
} mbs;

static int mbqlen;
static struct mbq_elem *mbqfree, *mbwaitq;
static void mbq_store();	/* store element */
static void mbq_retrieve();	/* retrieve element */
static int mbq_softcall();	/* entry into callback via softcall */

/*
 * run_mhq && mb_setup now in sundev/mb.c
 */

/*
 * Allocate and setup Mainbus map registers.  Flags says whether the
 * caller can't wait (e.g. if the caller is at interrupt level).  We
 * also allow DMA to memory already mapped at the Mainbus (e.g., for
 * Sun Ethernet board memory) if buscheck() says it's ok.
 *
 * We assume that the physical translations for the pages have already
 * been locked down before we were called.
 */

/*
 * New interface for mainbus resources. The main reason for this is to
 * provide smart controllers with the ability to queue multiple I/Os to
 * a single device, which was impossible with the old stuff. This is also
 * a move towards cleaning up the complexity of the queueing structures
 * both for DVMA resources and for autoconf.
 *
 * This routine accepts a function pointer which is queued if there is no
 * no space available and the caller cannot sleep.
 */
int
mb_mapalloc(map, bp, flags, waitfp, arg)
	register struct map *map;
	register struct buf *bp;
	int flags;
	func_t waitfp;
	caddr_t arg;
{
	register int reg, s, o, npf;
	struct mbcookie mbcookie;
#if defined(sun3x) || defined(sun4m)
	struct pte pte;
#endif sun3x || sun4m
#ifdef sun2
	struct ctx *ctx;
#endif sun2

#if defined(sun4m) && defined(IOMMU)
	struct map *iom_choose_map();
	extern int iom;
#endif defined(sun4m) && defined(IOMMU)

	/*
	 * Size the xfer, rounding up a page if necessary.
	 */
	o = (int)bp->b_un.b_addr & PAGEOFFSET;
	npf = mmu_btopr(bp->b_bcount + o);

	reg = buscheck(bp);
	if (reg < 0)
		panic("mbsetup buscheck fail");
#ifndef	sun4c
#if defined(sun4m) && defined(IOMMU)
	if (reg > 0) {
		/*
		 * If DVMA target is not main memory. Make
		 * sure they can be DVMA'ed.
		 */
		switch (bustype(reg)) {
		case BT_SBUS:
			if (map == mbutlmap) {
				/*
				 * mbutlmap use only from m_clalloc
				 */
				return(0);
			}
		case BT_VIDEO:
			/* go ahead to setup IOMMU */
			break;

		case BT_VME:
			if (map == mb_hd.mh_map) {
				/*
				 * VME-> VME.
				 *
				 * This does not involve IOC/IOMMU,
				 * just return VME slave address.
				 */
				mbcookie.mbi_mapreg = reg;
				mbcookie.mbi_offset = o;
				return (*(int *)&mbcookie);
			} else {
				/* Sbus to VME, not supported!! */
				return(0);
			}

		case BT_UNKNOWN:
		case BT_OBIO:
			/* NO DVMA allowed to these spaces! */
			return(0);
		}
	}

#else defined(sun4m) && defined(IOMMU)

	/*
	 * If not OBMEM, for non-sun4c machines, return the
	 * cookie that buscheck makes up for VME addresses.
	 *
	 * For sun4c, we skip this and force a mapping across
	 * DVMA[] (I/O on sun4c always goes through MMU context
	 * 0- even SBus to SBus transactions).
	 */
	if (reg > 0) {
		/*
		 * In contiguous mainbus memory,
		 * i.e., not on on-board memory
		 * (mainbus is historical).
		 */
		mbcookie.mbi_mapreg = reg;
		mbcookie.mbi_offset = o;
		return (*(int *)&mbcookie);
	}
#endif defined(sun4m) && defined(IOMMU)
#endif	sun4c

#if defined(sun4m) && defined(IOMMU)
	/* see if we can use vme32map */
	if ((map == mb_hd.mh_map) && (flags & MDR_VME32) &&
	    		!(flags & MDR_DVMA_PEEK))
		map = vme32map;

	/* see if we can use bigsbusmap */
	if ((map == dvmamap) && (flags & MDR_BIGSBUS))
		map = bigsbusmap;

	/* see if we can use altsbusmap */
	if ((map == dvmamap) && (flags & MDR_ALTSBUS))
		map = altsbusmap;
#endif defined(sun4m) && defined(IOMMU)

	/*
	 * Else the request is for all type 0 memory which still needs
	 * to be mapped into DVMA space.  Allocate virtual space for the
	 * mapping including one extra for redzone at end of mapping.
	 */

	s = splvm();
	while ((reg = bp_alloc(map, bp, npf + 1)) == 0) {
		/*
		 * No DVMA available. We either queue the function ptr
		 * or sleep, depending on the caller's state of mind.
		 */
		if (flags & MB_CANTWAIT) {
			if (waitfp) {
				mbq_store(waitfp, arg, spltoipl(s));
				mbs.m_bigreq = MAX(ctob(npf), mbs.m_bigreq);
			}
			(void) splx(s);
			return (0);
		}
		mapwant(map)++;
		(void) sleep((caddr_t)map, PSWP);
	}
	(void) splx(s);

#ifdef sun2
	ctx = mmu_getctx();
	if (ctx != kctx)
		mmu_setctx(kctx);
#endif sun2

#if defined(sun4m) && defined(IOMMU)
	if (!iom)
		bp_map(bp, &DVMA[mmu_ptob(reg)]);
	/* 
	 * if it's vme24map, pass DVMA_PEEK flag along
	 * so that bp_iom_map will load host SRMMU also.
	 */ 
	else if (map == vme24map)
		bp_iom_map(bp, reg, flags | DVMA_PEEK, map);
	else
		bp_iom_map(bp, reg, flags, map);
#else
	/*
	 * Map the bp into kernel virtual address in DVMA space.
	 * We over allocate one slot and mark this mapping
	 * invalid.  This protects against run away transfers
	 * and is also used as termination condition in mb_mapfree.
	 * We set the redzone to invalid every time because we're
	 * paranoid, but we use mmu_setpte instead of hat_pteload
	 * because we want it to be fast.
	 */
	bp_map(bp, &DVMA[mmu_ptob(reg)]);
#endif defined(sun4m) && defined(IOMMU)

	/* invalid red-zone's mapping */
#if defined(sun3x) 
	*(u_int *)&pte = MMU_INVALIDPTE;
	hat_pteload(&kseg, &DVMA[mmu_ptob(reg + npf)],
		(struct page *)NULL, &pte, PTELD_LOCK | PTELD_NOSYNC);
#elif defined(sun4m)
#ifdef IOMMU
	/* if iommu is on, only vme24map needs red zone on host */ 
	if (iom==0 || map == vme24map) {
		*(u_int *)&pte = MMU_STD_INVALIDPTE;
		hat_pteload(&kseg, &DVMA[mmu_ptob(reg + npf)],
		(struct page *)NULL, &pte, PTELD_LOCK | PTELD_NOSYNC);

	}
#else
	*(u_int *)&pte = MMU_STD_INVALIDPTE;
	hat_pteload(&kseg, &DVMA[mmu_ptob(reg + npf)],
		(struct page *)NULL, &pte, PTELD_LOCK | PTELD_NOSYNC);
#endif IOMMU
#else
	mmu_setpte(&DVMA[mmu_ptob(reg + npf)], mmu_pteinvalid);
#endif sun3x


#ifdef sun2
	if (ctx != kctx)
		mmu_setctx(ctx);
#endif sun2

	mbcookie.mbi_mapreg = reg;
	mbcookie.mbi_offset = o;
	return (*(int *)&mbcookie);
}

/*
 * mb_relse now back in sundev/mb.c
 */

/*
 * Release resources on Mainbus, and then unblock resource waiters.
 */
void
mb_mapfree(map, amr)
	register struct map *map;
	int *amr;
{
#if !defined(sun4m) || !defined(IOMMU)
	register char *addr;
	register int reg;
#endif
	register int npf;
#if defined(sun3x) || (defined(sun4m) && !defined(IOMMU))
	struct pte *ppte;
#endif
#if defined(sun3x) || defined(sun4m)
#ifdef SUN3X_470
	int flush = 0;
#endif SUN3X_470
#else sun3x || sun4m
	struct pte pte;
#endif sun3x || sun4m
	int mr, s;
#ifdef sun2
	struct ctx *ctx;
#endif sun2
#ifdef	sun4m
	int flush = 0;
#ifdef IOMMU
	struct map *ret_map;
	union iommu_pte *p_iopte;
	union iommu_pte tiopte; 
	u_int get_iommu_entry();
        extern struct iommuinfo iommu_info;
#endif
#ifdef IOC
	int ioc_mr;
        extern void flush_writebuffers();
#endif
#endif

	/*
	 * Carefully see if we should release the space.
	 */
	s = splvm();
	mr = *amr;
	if (mr == 0) {
		printf("mb_mapfree: MR == 0!!!\n");
		(void) splx(s);
		return;
	}
	*amr = 0;
	(void) splx(s);		/* we're supposed to be safe for awhile */

#if defined(sun3x) || (defined(sun4m) && !defined(IOMMU))
	if ((reg = MBI_MR(mr)) < dvmasize) {		/* DVMA memory */
		/*
		 * First we have to read the hardware maps to figure
		 * out how big the transfer really was.  This relies
		 * on having an invalid pte to terminate the mapping.
		 * We start looking at the 2nd page to avoid always
		 * looking at the first page.
		 */
		addr = &DVMA[mmu_ptob(reg + 1)];
		/*
		 * NOTE -- this assumes that the ptes for DVMA space
		 * are contiguous and ascending. This must remain true!
		 */
		ppte = (struct pte *)hat_ptefind(&kas, addr);
		if (ppte == NULL)
			panic("mbrelse no ptes");
		/*
		 * See if the first page of the mapping was I/O cacheable.
		 */
#ifdef SUN3X_470
		if (cpu == CPU_SUN3X_470) {
			if ((ppte - 1)->pte_iocache)
				flush = 1;
		}
#endif SUN3X_470

#if defined(sun4m) && defined(IOC)
		if ((ptetospte(ppte -1))->spte_iocache)
			flush = 1;
#endif	sun4m && IOC

		npf = 2;
		for (;;) {
			if (!pte_valid(ppte))
				break;
			npf++;
			ppte++;
		}
#ifdef SUN3X_470
		if (cpu == CPU_SUN3X_470) {
			/*
			 * If the mapping was I/O cacheable, loop
			 * through the cache flushing all the lines
			 * for this mapping.
			 */
			if (flush) {
				for (flush = 0; flush < npf; flush++)
					ioc_flush(reg + flush);
			}
		}
#endif SUN3X_470

#if defined(sun4m) && defined(IOC)
		if (flush) {
			for (flush = 0; flush < npf; flush++)
				fast_ioc_flush(reg + flush);
		}
		flush_writebuffers();
#endif	sun4m && IOC

		/*
		 * Now use hat_unload to unload the translations
		 * without getting the referenced and modified bits.
		 * Note that even though sun4m needs to have its
		 * vac tended to, the following should do the
		 * trick, since it will end up going through the
		 * hat code.
		 */
		hat_unload(&kseg, &DVMA[mmu_ptob(reg)],
			(u_int)(npf << MMU_PAGESHIFT));
		/*
		 * Put back the registers in the resource map.
		 * The map code must not be reentered, so we
		 * do this at high spl.
		 */
		s = splvm();
		rmfree(map, (long)npf, (u_long)reg);
		(void) splx(s);
	}

#elif defined(sun4m) && defined(IOMMU)
	mr= mmu_btop(mr);	/* we really work on "pages" */
	if ((ret_map= map_addr_to_map(mr, map)) != NULL) {	/* DVMA */
#ifdef IOC
		/* IOC works with VME devices only, no S-bus devices */
		if (ret_map == vme32map || ret_map == vme24map) {
			/* see if IOC is on. */
			if (ioc_read_tag(mr) & IOC_LINE_ENABLE) {
				flush= 1;
			}
			/* see if there is padding at beginning */
			ioc_mr= mr= ioc_adj_start(mr);
		}

#endif IOC

		if ((p_iopte= iom_ptefind(mr, ret_map)) == NULL)
			panic("mb_mapfree: no iopte");

		/* count how many pages are there */
		for (npf= 0; ; p_iopte++, npf++) {
			/* don't do in-line non-cached loads. Bug 1101875.
			 * Affects Viking-only mode.
			 * call the assembly routine to do it right.
			 */
		        tiopte.iopte_uint  = get_iommu_entry(p_iopte);
			if (tiopte.iopte.valid == 0)
				break;

#ifdef IOC
			/* flush every other page, we know ioc is 8K */
			if (flush && ((npf & 1)==0)) {
				fast_ioc_flush(ioc_mr);
				ioc_setup(ioc_mr, IOC_LINE_INVALID);
				ioc_mr += IOC_PAGES_PER_LINE;
			}
#endif IOC

			/* sync mod/ref bits */
			/* NOTE: I do not think we really need
			 *	 this iom_pagesync() call.
			 *
			 * ****************************
			 * iom_pagesync(p_iopte);
			 * ****************************
			 */
                        /* 
                         * Sync the cache if it is not DVMA coherent and the
                         * DVMA has (might have) written to memory.
                         */
                        if (p_iopte->iopte.write && iommu_info.ccoher == 0) {
                                if (cache == CACHE_VAC) {
                                        hat_cachesync(MAKE_PFNUM(p_iopte));
                                } else {
                                        /* XXX - optimize for flash clear */
                                        hat_cachesync(MAKE_PFNUM(p_iopte));
                                }
                        }
			/* unload IOMMU */
			iom_pteunload(p_iopte);
		}
		flush_writebuffers();

		if (ret_map == vme24map) {
			/* 
			 * NOTE: mr and npf here INCLUDES paddings.
			 * 	 Although we never loaded padding
			 *	 pages into host SRMMU, but we ask
			 *	 hat_unload to unload them anyway,
			 *	 since it can take this kind of 
			 *	 abuse. Otherwise, we will have
			 *	 to figure out the exact pages
			 *	 to be unloaded (excluding paddings).
			 */
			hat_unload(&kseg, &DVMA[mmu_ptob(mr)],
			    (u_int)mmu_ptob(npf));
		}

		s = splvm();
		/* add one for red zone */
		rmfree(ret_map, (long) (npf+1), (u_long)mr);
		(void) splx(s);
	}

#else defined(sun4m)
	if ((reg = MBI_MR(mr)) < dvmasize) {		/* DVMA memory */

#ifdef sun2
		ctx = mmu_getctx();
		if (ctx != kctx)
			mmu_setctx(kctx);
#endif sun2

#ifdef sun4c
		/*
		 * First we have to read the hardware maps to figure
		 * out how big the transfer really was.  This relies
		 * on having an invalid pte to terminate the mapping.
		 * We start looking at the first page since we are
		 * syncing the cache as we go. We only sync the
		 * cache for dirty pages, since output doesn't
		 * make the cache stale. This assumes that the
		 * I/O is going through the MMU and thus setting
		 * the dirty bit when appropriate.
		 *
		 * NOTE: this method of syncing the cache only
		 * works for write-through caches, where the flush
		 * is always an invalidate. A write-back cache
		 * would not work this way.
		 *
		 * Ah, but as this is an allocation in DVMA, then
		 * we didn't use segkmem and the hat layer to load
		 * things up, as cache consistency is not germane
		 * to sun4c where I/O doesn't go through the cache
		 * anyway. We still have to flush any I/U cache
		 * references to this physical page, but we can
		 * just nuke the pte mapping as we go..
		 */

		addr = &DVMA[mmu_ptob(reg)];
		npf = 0;
		for (;;) {
			mmu_getpte((addr_t)addr, &pte);
			if (!pte_valid(&pte))
				break;
#ifdef	VAC
			if (vac && pte.pg_m)
				hat_vacsync(MAKE_PFNUM(&pte));
#endif	VAC
			mmu_setpte((addr_t)addr, mmu_pteinvalid);
			addr += MMU_PAGESIZE;
			npf++;
		}
#else sun4c
#ifdef VAC
#ifdef sun4m
		if (cache) {
#else
		if (vac) {
#endif sun4m
			/*
			 * First we have to read the hardware maps to figure
			 * out how big the transfer really was.  This relies
			 * on having an invalid pte to terminate the mapping.
			 * If there is an i/o cache we have to check the
			 * first page to see if it needs to be flushed, so
			 * to make this code cleaner we just always start at
			 * the first page even though for non ioc machines
			 * we could start at the second.
			 */
			addr = &DVMA[mmu_ptob(reg)];
			npf = 0;
			for (;;) {
				mmu_getpte((addr_t)addr, &pte);
				if (!pte_valid(&pte))
					break;
#ifdef IOC
				if (pte.pg_ioc)
					ioc_flush(reg + npf);
#endif IOC
				addr += MMU_PAGESIZE;
				npf++;
			}
			/*
			 * Now use hat_unload to unload the translations
			 * without getting the referenced and modified bits.
			 */
			hat_unload(&kseg, &DVMA[mmu_ptob(reg)],
			    (u_int)mmu_ptob(npf));
		} else
#endif VAC
		{
			/*
			 * No vac, can just use mmu operations to unload
			 * the DVMA mappings.
			 */
			addr = &DVMA[mmu_ptob(reg)];
			npf = 0;
			for (;;) {
				mmu_getpte((addr_t)addr, &pte);
				if (!pte_valid(&pte))
					break;
#ifdef IOC
				if (pte.pg_ioc)
					ioc_flush(reg + npf);
#endif IOC
				mmu_setpte((addr_t)addr, mmu_pteinvalid);
				addr += MMU_PAGESIZE;
				npf++;
			}
		}
#endif sun4c
#ifdef sun2
		if (ctx != kctx)
			mmu_setctx(ctx);
#endif sun2

		/*
		 * Put back the registers in the resource map.
		 * The map code must not be reentered, so we
		 * do this at high spl.
		 */
		s = splvm();
		rmfree(map, (long)(npf + 1), (u_long)reg);
		(void) splx(s);
	}
#endif sun3x

	/*
	 * Try to map waiting devices, using the queued function ptr.
	 * If a request can't be mapped, we just return, as mb_mapalloc
	 * has already requeued the request.
	 */
	s = splvm();
	if (mbwaitq)
		mbq_retrieve(spltoipl(s));
	(void) splx(s);
}

/*
 * Store a queue element, returning if the funcp is already queued.
 * Always called at splvm().
 */
static void
mbq_store(funcp, arg, pri)
	func_t funcp;
	caddr_t arg;
	int pri;
{
	register struct mbq_elem *lq = NULL;
	register struct mbq_elem *q =  mbwaitq;

	/*
	 * Search the queue to see whether funcp is still queued
	 */

	while (q != NULL) {
		if (q->mbq_funcp == funcp) {
			mbs.m_queued++;
			return;
		}
		lq = q;
		q = q->mbq_next;
	}

	if ((q = mbqfree) == NULL) {
		int i;

		/*
		 * If mbwaitq is NULL when mbqfree is NULL, then
		 * this is the first time thru, else we just got
		 * too many callers. Die.
		 */

		if (mbwaitq) {
			panic("mbq_store: too many callers");
			/*NOTREACHED*/
		}

		/*
		 * ...else initialize the free queue
		 */

		for (i = 0; i < MAXMBQLEN-1; i++)
			mbq[i].mbq_next = &mbq[i+1];
		q = mbqfree = mbq;
	}

	mbqfree = q->mbq_next;

	q->mbq_funcp = funcp;
	q->mbq_arg = arg;
	if ((q->mbq_pri = pri) == 0) {
		printf("Warning: MB callback stored at priority 0!\n");
	}

	if (lq == NULL) {
		q->mbq_next = mbwaitq;
		mbwaitq = q;
	} else {
		q->mbq_next = (struct mbq_elem *) NULL;
		lq->mbq_next = q;
	}

	mbs.m_runout++;
	mbqlen++;
	mbs.m_hiqlen = MAX(mbqlen, mbs.m_hiqlen);
}

/*
 * Retrieve queue elements from the wait queue.
 * Always called at splvm(). Honor protocol of
 * first calling directly only waiters who are
 * waiting at pri or greater, using softcall
 * to call everybody else (if resources are
 * still left).
 */

static void
mbq_retrieve(pri)
	int pri;
{
	register struct mbq_elem *q, *pq;
	int stat;

	if ((q = mbwaitq) == (struct mbq_elem *) NULL)
		return;

	pq = (struct mbq_elem *) NULL;
	stat = 0;

	do {
		if (q->mbq_pri >= pri) {
			register func_t func;
			register caddr_t arg;

			/*
			 * save a copy of the function/argument pair
			 */

			func = q->mbq_funcp;
			arg = q->mbq_arg;

			/*
			 * Snip the queue element out of the chain
			 * and put it back on the free list before
			 * doing the callback.
			 */

			if (pq) {
				pq->mbq_next = q->mbq_next;
				q->mbq_next = mbqfree;
				mbqfree = q;
				q = pq->mbq_next;
			} else {
				mbwaitq = q->mbq_next;
				q->mbq_next = mbqfree;
				mbqfree = q;
				q = mbwaitq;
			}

			mbqlen -= 1;

			/*
			 * and call the function specified
			 */

			stat = (*(func)) (arg);

			/*
			 * If we ran out again, all bets are off
			 */
			if (stat == DVMA_RUNOUT)
				break;
		} else {
			pq = q;
			q = q->mbq_next;
		}

	} while (q != (struct mbq_elem *) NULL);

	if (stat != DVMA_RUNOUT && mbwaitq != (struct mbq_elem *) NULL) {
		softcall(mbq_softcall, (caddr_t) 0);
	}
}

/*ARGSUSED*/
static int
mbq_softcall(arg)
caddr_t arg;
{
	register s = splvm();
	mbs.m_softcall++;
	mbq_retrieve(0);
	(void) splx(s);
}

/*
 * Put swab here, as mb_machdep.c is compiled common to
 * both sun4c and sun{2, 3, 4} builds.
 */
/*
 * Swap bytes in 16-bit [half-]words
 * for going between the 11 and the interdata
 */
void
swab(pf, pt, n)
	register caddr_t pf, pt;
	register int n;
{
	register char temp;

	n = (n+1)>>1;

	while (--n >= 0) {
		temp = *pf++;
		*pt++ = *pf++;
		*pt++ = temp;
	}
}

/*
 * Non buffer setup interface... set up a buffer and call mb_mapalloc.
 * If DVMA runs out. waitfp is queued and invoked later when space becomes
 * available.
 */
int
mb_nbmapalloc(map, addr, bcnt, flags, waitfp, arg)
	struct map *map;
	caddr_t addr;
	int bcnt, flags;
	func_t waitfp;
	caddr_t arg;
{
	struct buf mbbuf;

	bzero((caddr_t)&mbbuf, sizeof (mbbuf));
	mbbuf.b_un.b_addr = addr;
	mbbuf.b_flags = B_BUSY;
	mbbuf.b_bcount = bcnt;
	return (mb_mapalloc(map, &mbbuf, flags, waitfp, arg));
}
