3 * ===================================
4 * HARP | Host ATM Research Platform
5 * ===================================
8 * This Host ATM Research Platform ("HARP") file (the "Software") is
9 * made available by Network Computing Services, Inc. ("NetworkCS")
10 * "AS IS". NetworkCS does not provide maintenance, improvements or
11 * support of any kind.
13 * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
14 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
15 * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
16 * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
17 * In no event shall NetworkCS be responsible for any damages, including
18 * but not limited to consequential damages, arising from or relating to
19 * any use of the Software or related support.
21 * Copyright 1994-1998 Network Computing Services, Inc.
23 * Copies of this Software may be made, however, the above copyright
24 * notice must be reproduced on all copies.
26 * @(#) $FreeBSD: src/sys/dev/hea/eni_buffer.c,v 1.5 1999/08/28 00:41:43 peter Exp $
31 * Efficient ENI Adapter Support
32 * -----------------------------
34 * Handle adapter memory buffers for ENI adapters
38 #include <netatm/kern_include.h>
40 #include <dev/hea/eni_stats.h>
41 #include <dev/hea/eni.h>
42 #include <dev/hea/eni_var.h>
45 __RCSID("@(#) $FreeBSD: src/sys/dev/hea/eni_buffer.c,v 1.5 1999/08/28 00:41:43 peter Exp $");
48 static int eni_test_memory __P((Eni_unit *));
51 * The host is going to manage (that is, allocate and free) buffers
52 * in the adapters RAM space. We are going to implement this as a
53 * linked list describing FREE and INUSE memory segments. Initially,
54 * the list contains one element with all memory marked free. As requests
55 * are made, we search the list until we find the first free element
56 * which can satisfy the request. If necessary, we will break the free
57 * element into an INUSE element, and a new FREE element. When freeing
58 * memory, we look at adjacent elements and if one or more are free,
59 * we will combine into a single larger FREE element.
63 * This is for testing purposes. Since there are two versions of
64 * the Efficient adapter with different memory sizes, this allows
65 * us to fool an adapter with more memory into thinking it has less.
67 static int eni_mem_max = MAX_ENI_MEM; /* Default to all available memory */
70 * Size and test adapter RAM
72 * Walk through adapter RAM writing known patterns and reading back
73 * for comparison. We write more than one pattern on the off chance
74 * that we "get lucky" and read what we expected.
77 * eup pointer to device unit structure
80 * size memory size in bytes
83 eni_test_memory ( eup )
91 * Walk through to maximum looking for RAM
93 for ( i = 0; i < MAX_ENI_MEM; i += TEST_STEP ) {
94 mp = (Eni_mem)((int)eup->eu_ram + i);
96 *mp = (u_long)TEST_PAT;
97 /* read pattern, match? */
98 if ( *mp == (u_long)TEST_PAT ) {
99 /* yes - write inverse pattern */
100 *mp = (u_long)~TEST_PAT;
101 /* read pattern, match? */
102 if ( *mp == (u_long)~TEST_PAT ) {
103 /* yes - assume another 1K available */
104 ram_size = i + TEST_STEP;
111 * Clear all RAM to initial value of zero.
112 * This makes sure we don't leave anything funny in the
115 KM_ZERO ( eup->eu_ram, ram_size );
118 * If we'd like to claim to have less memory, here's where
119 * we do so. We take the minimum of what we'd like and what
120 * we really found on the adapter.
122 ram_size = MIN ( ram_size, eni_mem_max );
129 * Initialize our memory allocator.
132 * eup Pointer to per unit structure
135 * size Physical RAM size
136 * -1 failed to initialize memory
140 eni_init_memory ( eup )
145 * Have we (somehow) been called before?
147 if ( eup->eu_memmap != NULL )
149 /* Oops - it's already been initialized */
154 * Allocate initial element which will hold all of memory
156 if ( ( eup->eu_memmap = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF,
157 M_NOWAIT ) ) == NULL )
159 /* Memory allocation error */
164 * Test and size memory
166 eup->eu_ramsize = eni_test_memory ( eup );
169 * Initialize a one element list which contains
172 eup->eu_memmap->prev = eup->eu_memmap->next = NULL;
173 eup->eu_memmap->base = (caddr_t)SEGBUF_BASE;
174 eup->eu_memmap->size = eup->eu_ramsize - SEGBUF_BASE;
175 eup->eu_memmap->state = MEM_FREE;
177 return ( eup->eu_ramsize );
181 * Allocate a buffer from adapter RAM. Due to constraints on the card,
182 * we may roundup the size request to the next largest chunksize. Note
183 * also that we must pay attention to address alignment within adapter
187 * eup pointer to per unit structure
188 * size pointer to requested size - in bytes
191 * addr address relative to adapter of allocated memory
192 * size modified to reflect actual size of buffer
196 eni_allocate_buffer ( eup, size )
202 Mbd *eptr = eup->eu_memmap;
205 * Initial size requested
210 * Find the buffer size which will hold this request. There
211 * are 8 possible sizes, each a power of two up, starting at
212 * 256 words or 1024 bytes.
214 for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
215 if ( ( 1 << nclicks ) * ENI_BUF_PGSZ >= nsize )
219 * Request was for larger then the card supports
221 if ( nclicks >= ENI_BUF_NBIT ) {
222 eup->eu_stats.eni_st_drv.drv_mm_toobig++;
223 /* Indicate 0 bytes allocated */
225 /* Return NULL buffer */
226 return ( (caddr_t)NULL );
230 * New size will be buffer size
232 nsize = ( 1 << nclicks ) * ENI_BUF_PGSZ;
235 * Look through memory for a segment large enough to
240 * State must be FREE and size must hold request
242 if ( eptr->state == MEM_FREE && eptr->size >= nsize )
245 * Request will fit - now check if the
246 * alignment needs fixing
248 if ( ((u_int)eptr->base & (nsize-1)) != 0 )
253 * Calculate where the buffer would have to
254 * fall to be aligned.
256 nbase = (caddr_t)((u_int)( eptr->base + nsize ) &
259 * If we use this alignment, will it still fit?
261 if ( (eptr->size - (nbase - eptr->base)) >= 0 )
265 /* Yep - create a new segment */
266 etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF,
268 if ( etmp == (Mbd *)NULL ) {
270 * Couldn't allocate a new descriptor. Indicate
271 * failure and exit now or we'll start losing
274 eup->eu_stats.eni_st_drv.drv_mm_nodesc++;
276 return ( (caddr_t)NULL );
278 /* Place it in the list */
279 etmp->next = eptr->next;
281 etmp->next->prev = etmp;
284 /* Fill in new base and size */
286 etmp->size = eptr->size - ( nbase - eptr->base );
287 /* Adjust old size */
288 eptr->size -= etmp->size;
290 etmp->state = MEM_FREE;
292 /* Done - outa here */
296 break; /* Alignment is okay - we're done */
298 /* Haven't found anything yet - keep looking */
304 /* Found a usable segment - grab what we need */
306 if ( eptr->size == nsize )
307 /* Mark it as INUSE */
308 eptr->state = MEM_INUSE;
312 /* larger then we need - split it */
314 etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_NOWAIT );
315 if ( etmp == (Mbd *)NULL ) {
317 * Couldn't allocate new descriptor. Indicate
318 * failure and exit now or we'll start losing
321 eup->eu_stats.eni_st_drv.drv_mm_nodesc++;
323 return ( (caddr_t)NULL );
325 /* Place new element in list */
326 etmp->next = eptr->next;
328 etmp->next->prev = etmp;
331 /* Set new base, size and state */
332 etmp->base = eptr->base + nsize;
333 etmp->size = eptr->size - nsize;
334 etmp->state = MEM_FREE;
335 /* Adjust size and state of element we intend to use */
337 eptr->state = MEM_INUSE;
341 /* After all that, did we find a usable buffer? */
344 /* Record another inuse buffer of this size */
346 eup->eu_memclicks[nclicks]++;
349 * Return true size of allocated buffer
353 * Make address relative to start of RAM since
354 * its (the address) for use by the adapter, not
357 return ((caddr_t)eptr->base);
359 eup->eu_stats.eni_st_drv.drv_mm_nobuf++;
360 /* No buffer to return - indicate zero length */
362 /* Return NULL buffer */
363 return ( (caddr_t)NULL );
368 * Procedure to release a buffer previously allocated from adapter
369 * RAM. When possible, we'll compact memory.
372 * eup pointer to per unit structure
373 * base base adapter address of buffer to be freed
380 eni_free_buffer ( eup, base )
384 Mbd *eptr = eup->eu_memmap;
387 /* Look through entire list */
390 /* Is this the buffer to be freed? */
391 if ( eptr->base == base )
394 * We're probably asking for trouble but,
397 if ( eptr->state != MEM_INUSE )
399 eup->eu_stats.eni_st_drv.drv_mm_notuse++;
400 /* Huh? Something's wrong */
403 /* Reset state to FREE */
404 eptr->state = MEM_FREE;
406 /* Determine size for stats info */
407 for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
408 if ( ( 1 << nclicks ) * ENI_BUF_PGSZ == eptr->size )
411 /* Valid size? Yes - decrement inuse count */
412 if ( nclicks < ENI_BUF_NBIT )
413 eup->eu_memclicks[nclicks]--;
415 /* Try to compact neighbors */
418 if ( eptr->prev->state == MEM_FREE )
421 /* Add to previous block */
422 eptr->prev->size += eptr->size;
423 /* Set prev block to skip this one */
424 eptr->prev->next = eptr->next;
425 /* Set next block to skip this one */
427 eptr->next->prev = eptr->prev;
428 /* Reset to where we want to be */
430 /* and free this element */
431 (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
435 if ( eptr->next->state == MEM_FREE )
437 Mbd *etmp = eptr->next;
439 /* add following block in */
440 eptr->size += etmp->size;
441 /* set next next block to skip next block */
443 etmp->next->prev = eptr;
444 /* skip next block */
445 eptr->next = etmp->next;
446 /* and free next element */
447 (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
450 * We've freed the buffer and done any compaction,
451 * we needn't look any further...
460 /* Oops - failed to find the buffer. This is BAD */
461 eup->eu_stats.eni_st_drv.drv_mm_notfnd++;