/* * * =================================== * 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 #include #include #include #include #include #include #include #include #include #include #include #include #ifndef lint __RCSID("@(#) $FreeBSD$"); #endif static int eni_test_memory(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 ( (uintptr_t)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++; } }