freebsd-nq/sys/dev/hea/eni_buffer.c
1999-08-28 01:08:13 +00:00

466 lines
11 KiB
C

/*
*
* ===================================
* HARP | Host ATM Research Platform
* ===================================
*
*
* This Host ATM Research Platform ("HARP") file (the "Software") is
* made available by Network Computing Services, Inc. ("NetworkCS")
* "AS IS". NetworkCS does not provide maintenance, improvements or
* support of any kind.
*
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
* In no event shall NetworkCS be responsible for any damages, including
* but not limited to consequential damages, arising from or relating to
* any use of the Software or related support.
*
* Copyright 1994-1998 Network Computing Services, Inc.
*
* Copies of this Software may be made, however, the above copyright
* notice must be reproduced on all copies.
*
* @(#) $FreeBSD$
*
*/
/*
* Efficient ENI Adapter Support
* -----------------------------
*
* Handle adapter memory buffers for ENI adapters
*
*/
#include <netatm/kern_include.h>
#include <dev/hea/eni_stats.h>
#include <dev/hea/eni.h>
#include <dev/hea/eni_var.h>
#ifndef lint
__RCSID("@(#) $FreeBSD$");
#endif
static int eni_test_memory __P((Eni_unit *));
/*
* The host is going to manage (that is, allocate and free) buffers
* in the adapters RAM space. We are going to implement this as a
* linked list describing FREE and INUSE memory segments. Initially,
* the list contains one element with all memory marked free. As requests
* are made, we search the list until we find the first free element
* which can satisfy the request. If necessary, we will break the free
* element into an INUSE element, and a new FREE element. When freeing
* memory, we look at adjacent elements and if one or more are free,
* we will combine into a single larger FREE element.
*/
/*
* This is for testing purposes. Since there are two versions of
* the Efficient adapter with different memory sizes, this allows
* us to fool an adapter with more memory into thinking it has less.
*/
static int eni_mem_max = MAX_ENI_MEM; /* Default to all available memory */
/*
* Size and test adapter RAM
*
* Walk through adapter RAM writing known patterns and reading back
* for comparison. We write more than one pattern on the off chance
* that we "get lucky" and read what we expected.
*
* Arguments:
* eup pointer to device unit structure
*
* Returns
* size memory size in bytes
*/
static int
eni_test_memory ( eup )
Eni_unit *eup;
{
int ram_size = 0;
int i;
Eni_mem mp;
/*
* Walk through to maximum looking for RAM
*/
for ( i = 0; i < MAX_ENI_MEM; i += TEST_STEP ) {
mp = (Eni_mem)((int)eup->eu_ram + i);
/* write pattern */
*mp = (u_long)TEST_PAT;
/* read pattern, match? */
if ( *mp == (u_long)TEST_PAT ) {
/* yes - write inverse pattern */
*mp = (u_long)~TEST_PAT;
/* read pattern, match? */
if ( *mp == (u_long)~TEST_PAT ) {
/* yes - assume another 1K available */
ram_size = i + TEST_STEP;
} else
break;
} else
break;
}
/*
* Clear all RAM to initial value of zero.
* This makes sure we don't leave anything funny in the
* queues.
*/
KM_ZERO ( eup->eu_ram, ram_size );
/*
* If we'd like to claim to have less memory, here's where
* we do so. We take the minimum of what we'd like and what
* we really found on the adapter.
*/
ram_size = MIN ( ram_size, eni_mem_max );
return ( ram_size );
}
/*
* Initialize our memory allocator.
*
* Arguments:
* eup Pointer to per unit structure
*
* Returns:
* size Physical RAM size
* -1 failed to initialize memory
*
*/
int
eni_init_memory ( eup )
Eni_unit *eup;
{
/*
* Have we (somehow) been called before?
*/
if ( eup->eu_memmap != NULL )
{
/* Oops - it's already been initialized */
return -1;
}
/*
* Allocate initial element which will hold all of memory
*/
if ( ( eup->eu_memmap = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF,
M_NOWAIT ) ) == NULL )
{
/* Memory allocation error */
return -1;
}
/*
* Test and size memory
*/
eup->eu_ramsize = eni_test_memory ( eup );
/*
* Initialize a one element list which contains
* all buffer memory
*/
eup->eu_memmap->prev = eup->eu_memmap->next = NULL;
eup->eu_memmap->base = (caddr_t)SEGBUF_BASE;
eup->eu_memmap->size = eup->eu_ramsize - SEGBUF_BASE;
eup->eu_memmap->state = MEM_FREE;
return ( eup->eu_ramsize );
}
/*
* Allocate a buffer from adapter RAM. Due to constraints on the card,
* we may roundup the size request to the next largest chunksize. Note
* also that we must pay attention to address alignment within adapter
* memory as well.
*
* Arguments:
* eup pointer to per unit structure
* size pointer to requested size - in bytes
*
* Returns:
* addr address relative to adapter of allocated memory
* size modified to reflect actual size of buffer
*
*/
caddr_t
eni_allocate_buffer ( eup, size )
Eni_unit *eup;
u_long *size;
{
int nsize;
int nclicks;
Mbd *eptr = eup->eu_memmap;
/*
* Initial size requested
*/
nsize = *size;
/*
* Find the buffer size which will hold this request. There
* are 8 possible sizes, each a power of two up, starting at
* 256 words or 1024 bytes.
*/
for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
if ( ( 1 << nclicks ) * ENI_BUF_PGSZ >= nsize )
break;
/*
* Request was for larger then the card supports
*/
if ( nclicks >= ENI_BUF_NBIT ) {
eup->eu_stats.eni_st_drv.drv_mm_toobig++;
/* Indicate 0 bytes allocated */
*size = 0;
/* Return NULL buffer */
return ( (caddr_t)NULL );
}
/*
* New size will be buffer size
*/
nsize = ( 1 << nclicks ) * ENI_BUF_PGSZ;
/*
* Look through memory for a segment large enough to
* hold request
*/
while ( eptr ) {
/*
* State must be FREE and size must hold request
*/
if ( eptr->state == MEM_FREE && eptr->size >= nsize )
{
/*
* Request will fit - now check if the
* alignment needs fixing
*/
if ( ((u_int)eptr->base & (nsize-1)) != 0 )
{
caddr_t nbase;
/*
* Calculate where the buffer would have to
* fall to be aligned.
*/
nbase = (caddr_t)((u_int)( eptr->base + nsize ) &
~(nsize-1));
/*
* If we use this alignment, will it still fit?
*/
if ( (eptr->size - (nbase - eptr->base)) >= 0 )
{
Mbd *etmp;
/* Yep - create a new segment */
etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF,
M_NOWAIT );
if ( etmp == (Mbd *)NULL ) {
/*
* Couldn't allocate a new descriptor. Indicate
* failure and exit now or we'll start losing
* memory.
*/
eup->eu_stats.eni_st_drv.drv_mm_nodesc++;
*size = 0;
return ( (caddr_t)NULL );
}
/* Place it in the list */
etmp->next = eptr->next;
if ( etmp->next )
etmp->next->prev = etmp;
etmp->prev = eptr;
eptr->next = etmp;
/* Fill in new base and size */
etmp->base = nbase;
etmp->size = eptr->size - ( nbase - eptr->base );
/* Adjust old size */
eptr->size -= etmp->size;
/* Mark its state */
etmp->state = MEM_FREE;
eptr = etmp;
/* Done - outa here */
break;
}
} else
break; /* Alignment is okay - we're done */
}
/* Haven't found anything yet - keep looking */
eptr = eptr->next;
}
if ( eptr != NULL )
{
/* Found a usable segment - grab what we need */
/* Exact fit? */
if ( eptr->size == nsize )
/* Mark it as INUSE */
eptr->state = MEM_INUSE;
else
{
Mbd *etmp;
/* larger then we need - split it */
etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_NOWAIT );
if ( etmp == (Mbd *)NULL ) {
/*
* Couldn't allocate new descriptor. Indicate
* failure and exit now or we'll start losing
* memory.
*/
eup->eu_stats.eni_st_drv.drv_mm_nodesc++;
*size = 0;
return ( (caddr_t)NULL );
}
/* Place new element in list */
etmp->next = eptr->next;
if ( etmp->next )
etmp->next->prev = etmp;
etmp->prev = eptr;
eptr->next = etmp;
/* Set new base, size and state */
etmp->base = eptr->base + nsize;
etmp->size = eptr->size - nsize;
etmp->state = MEM_FREE;
/* Adjust size and state of element we intend to use */
eptr->size = nsize;
eptr->state = MEM_INUSE;
}
}
/* After all that, did we find a usable buffer? */
if ( eptr )
{
/* Record another inuse buffer of this size */
if ( eptr->base )
eup->eu_memclicks[nclicks]++;
/*
* Return true size of allocated buffer
*/
*size = eptr->size;
/*
* Make address relative to start of RAM since
* its (the address) for use by the adapter, not
* the host.
*/
return ((caddr_t)eptr->base);
} else {
eup->eu_stats.eni_st_drv.drv_mm_nobuf++;
/* No buffer to return - indicate zero length */
*size = 0;
/* Return NULL buffer */
return ( (caddr_t)NULL );
}
}
/*
* Procedure to release a buffer previously allocated from adapter
* RAM. When possible, we'll compact memory.
*
* Arguments:
* eup pointer to per unit structure
* base base adapter address of buffer to be freed
*
* Returns:
* none
*
*/
void
eni_free_buffer ( eup, base )
Eni_unit *eup;
caddr_t base;
{
Mbd *eptr = eup->eu_memmap;
int nclicks;
/* Look through entire list */
while ( eptr )
{
/* Is this the buffer to be freed? */
if ( eptr->base == base )
{
/*
* We're probably asking for trouble but,
* assume this is it.
*/
if ( eptr->state != MEM_INUSE )
{
eup->eu_stats.eni_st_drv.drv_mm_notuse++;
/* Huh? Something's wrong */
return;
}
/* Reset state to FREE */
eptr->state = MEM_FREE;
/* Determine size for stats info */
for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
if ( ( 1 << nclicks ) * ENI_BUF_PGSZ == eptr->size )
break;
/* Valid size? Yes - decrement inuse count */
if ( nclicks < ENI_BUF_NBIT )
eup->eu_memclicks[nclicks]--;
/* Try to compact neighbors */
/* with previous */
if ( eptr->prev )
if ( eptr->prev->state == MEM_FREE )
{
Mbd *etmp = eptr;
/* Add to previous block */
eptr->prev->size += eptr->size;
/* Set prev block to skip this one */
eptr->prev->next = eptr->next;
/* Set next block to skip this one */
if ( eptr->next )
eptr->next->prev = eptr->prev;
/* Reset to where we want to be */
eptr = eptr->prev;
/* and free this element */
(void)KM_FREE(etmp, etmp->size, M_DEVBUF);
}
/* with next */
if ( eptr->next )
if ( eptr->next->state == MEM_FREE )
{
Mbd *etmp = eptr->next;
/* add following block in */
eptr->size += etmp->size;
/* set next next block to skip next block */
if ( etmp->next )
etmp->next->prev = eptr;
/* skip next block */
eptr->next = etmp->next;
/* and free next element */
(void)KM_FREE(etmp, etmp->size, M_DEVBUF);
}
/*
* We've freed the buffer and done any compaction,
* we needn't look any further...
*/
return;
}
eptr = eptr->next;
}
if ( eptr == NULL )
{
/* Oops - failed to find the buffer. This is BAD */
eup->eu_stats.eni_st_drv.drv_mm_notfnd++;
}
}