summaryrefslogtreecommitdiffstats
path: root/sys/dev/hea/eni_buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/hea/eni_buffer.c')
-rw-r--r--sys/dev/hea/eni_buffer.c465
1 files changed, 465 insertions, 0 deletions
diff --git a/sys/dev/hea/eni_buffer.c b/sys/dev/hea/eni_buffer.c
new file mode 100644
index 0000000..35d1b13
--- /dev/null
+++ b/sys/dev/hea/eni_buffer.c
@@ -0,0 +1,465 @@
+/*
+ *
+ * ===================================
+ * 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.
+ *
+ * @(#) $Id: eni_buffer.c,v 1.8 1998/08/26 23:28:53 mks Exp $
+ *
+ */
+
+/*
+ * Efficient ENI Adapter Support
+ * -----------------------------
+ *
+ * Handle adapter memory buffers for ENI adapters
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: eni_buffer.c,v 1.8 1998/08/26 23:28:53 mks Exp $";
+#endif
+
+#include <netatm/kern_include.h>
+
+#include <dev/hea/eni_stats.h>
+#include <dev/hea/eni.h>
+#include <dev/hea/eni_var.h>
+
+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.
+ */
+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++;
+ }
+
+}
+
OpenPOWER on IntegriCloud