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 $
27 * @(#) $DragonFly: src/sys/dev/atm/hea/eni_buffer.c,v 1.7 2008/03/01 22:03:13 swildner Exp $
31 * Efficient ENI Adapter Support
32 * -----------------------------
34 * Handle adapter memory buffers for ENI adapters
38 #include <netproto/atm/kern_include.h>
40 #include "eni_stats.h"
44 static int eni_test_memory (Eni_unit
*);
47 * The host is going to manage (that is, allocate and free) buffers
48 * in the adapters RAM space. We are going to implement this as a
49 * linked list describing FREE and INUSE memory segments. Initially,
50 * the list contains one element with all memory marked free. As requests
51 * are made, we search the list until we find the first free element
52 * which can satisfy the request. If necessary, we will break the free
53 * element into an INUSE element, and a new FREE element. When freeing
54 * memory, we look at adjacent elements and if one or more are free,
55 * we will combine into a single larger FREE element.
59 * This is for testing purposes. Since there are two versions of
60 * the Efficient adapter with different memory sizes, this allows
61 * us to fool an adapter with more memory into thinking it has less.
63 static int eni_mem_max
= MAX_ENI_MEM
; /* Default to all available memory */
66 * Size and test adapter RAM
68 * Walk through adapter RAM writing known patterns and reading back
69 * for comparison. We write more than one pattern on the off chance
70 * that we "get lucky" and read what we expected.
73 * eup pointer to device unit structure
76 * size memory size in bytes
79 eni_test_memory(Eni_unit
*eup
)
86 * Walk through to maximum looking for RAM
88 for ( i
= 0; i
< MAX_ENI_MEM
; i
+= TEST_STEP
) {
89 mp
= (Eni_mem
)((int)eup
->eu_ram
+ i
);
91 *mp
= (u_long
)TEST_PAT
;
92 /* read pattern, match? */
93 if ( *mp
== (u_long
)TEST_PAT
) {
94 /* yes - write inverse pattern */
95 *mp
= (u_long
)~TEST_PAT
;
96 /* read pattern, match? */
97 if ( *mp
== (u_long
)~TEST_PAT
) {
98 /* yes - assume another 1K available */
99 ram_size
= i
+ TEST_STEP
;
106 * Clear all RAM to initial value of zero.
107 * This makes sure we don't leave anything funny in the
110 KM_ZERO ( eup
->eu_ram
, ram_size
);
113 * If we'd like to claim to have less memory, here's where
114 * we do so. We take the minimum of what we'd like and what
115 * we really found on the adapter.
117 ram_size
= MIN ( ram_size
, eni_mem_max
);
124 * Initialize our memory allocator.
127 * eup Pointer to per unit structure
130 * size Physical RAM size
131 * -1 failed to initialize memory
135 eni_init_memory(Eni_unit
*eup
)
139 * Have we (somehow) been called before?
141 if ( eup
->eu_memmap
!= NULL
)
143 /* Oops - it's already been initialized */
148 * Allocate initial element which will hold all of memory
150 eup
->eu_memmap
= (Mbd
*)KM_ALLOC(sizeof(Mbd
), M_DEVBUF
, M_WAITOK
);
153 * Test and size memory
155 eup
->eu_ramsize
= eni_test_memory ( eup
);
158 * Initialize a one element list which contains
161 eup
->eu_memmap
->prev
= eup
->eu_memmap
->next
= NULL
;
162 eup
->eu_memmap
->base
= (caddr_t
)SEGBUF_BASE
;
163 eup
->eu_memmap
->size
= eup
->eu_ramsize
- SEGBUF_BASE
;
164 eup
->eu_memmap
->state
= MEM_FREE
;
166 return ( eup
->eu_ramsize
);
170 * Allocate a buffer from adapter RAM. Due to constraints on the card,
171 * we may roundup the size request to the next largest chunksize. Note
172 * also that we must pay attention to address alignment within adapter
176 * eup pointer to per unit structure
177 * size pointer to requested size - in bytes
180 * addr address relative to adapter of allocated memory
181 * size modified to reflect actual size of buffer
185 eni_allocate_buffer(Eni_unit
*eup
, u_long
*size
)
189 Mbd
*eptr
= eup
->eu_memmap
;
192 * Initial size requested
197 * Find the buffer size which will hold this request. There
198 * are 8 possible sizes, each a power of two up, starting at
199 * 256 words or 1024 bytes.
201 for ( nclicks
= 0; nclicks
< ENI_BUF_NBIT
; nclicks
++ )
202 if ( ( 1 << nclicks
) * ENI_BUF_PGSZ
>= nsize
)
206 * Request was for larger then the card supports
208 if ( nclicks
>= ENI_BUF_NBIT
) {
209 eup
->eu_stats
.eni_st_drv
.drv_mm_toobig
++;
210 /* Indicate 0 bytes allocated */
212 /* Return NULL buffer */
213 return ( (caddr_t
)NULL
);
217 * New size will be buffer size
219 nsize
= ( 1 << nclicks
) * ENI_BUF_PGSZ
;
222 * Look through memory for a segment large enough to
227 * State must be FREE and size must hold request
229 if ( eptr
->state
== MEM_FREE
&& eptr
->size
>= nsize
)
232 * Request will fit - now check if the
233 * alignment needs fixing
235 if ( ((u_int
)eptr
->base
& (nsize
-1)) != 0 )
240 * Calculate where the buffer would have to
241 * fall to be aligned.
243 nbase
= (caddr_t
)((u_int
)( eptr
->base
+ nsize
) &
246 * If we use this alignment, will it still fit?
248 if ( (eptr
->size
- (nbase
- eptr
->base
)) >= 0 )
252 /* Yep - create a new segment */
253 etmp
= (Mbd
*)KM_ALLOC(sizeof(Mbd
), M_DEVBUF
, M_WAITOK
);
254 /* Place it in the list */
255 etmp
->next
= eptr
->next
;
257 etmp
->next
->prev
= etmp
;
260 /* Fill in new base and size */
262 etmp
->size
= eptr
->size
- ( nbase
- eptr
->base
);
263 /* Adjust old size */
264 eptr
->size
-= etmp
->size
;
266 etmp
->state
= MEM_FREE
;
268 /* Done - outa here */
272 break; /* Alignment is okay - we're done */
274 /* Haven't found anything yet - keep looking */
280 /* Found a usable segment - grab what we need */
282 if ( eptr
->size
== nsize
)
283 /* Mark it as INUSE */
284 eptr
->state
= MEM_INUSE
;
288 /* larger then we need - split it */
290 etmp
= (Mbd
*)KM_ALLOC(sizeof(Mbd
), M_DEVBUF
, M_WAITOK
);
291 /* Place new element in list */
292 etmp
->next
= eptr
->next
;
294 etmp
->next
->prev
= etmp
;
297 /* Set new base, size and state */
298 etmp
->base
= eptr
->base
+ nsize
;
299 etmp
->size
= eptr
->size
- nsize
;
300 etmp
->state
= MEM_FREE
;
301 /* Adjust size and state of element we intend to use */
303 eptr
->state
= MEM_INUSE
;
307 /* After all that, did we find a usable buffer? */
310 /* Record another inuse buffer of this size */
312 eup
->eu_memclicks
[nclicks
]++;
315 * Return true size of allocated buffer
319 * Make address relative to start of RAM since
320 * its (the address) for use by the adapter, not
323 return ((caddr_t
)eptr
->base
);
325 eup
->eu_stats
.eni_st_drv
.drv_mm_nobuf
++;
326 /* No buffer to return - indicate zero length */
328 /* Return NULL buffer */
329 return ( (caddr_t
)NULL
);
334 * Procedure to release a buffer previously allocated from adapter
335 * RAM. When possible, we'll compact memory.
338 * eup pointer to per unit structure
339 * base base adapter address of buffer to be freed
346 eni_free_buffer(Eni_unit
*eup
, caddr_t base
)
348 Mbd
*eptr
= eup
->eu_memmap
;
351 /* Look through entire list */
354 /* Is this the buffer to be freed? */
355 if ( eptr
->base
== base
)
358 * We're probably asking for trouble but,
361 if ( eptr
->state
!= MEM_INUSE
)
363 eup
->eu_stats
.eni_st_drv
.drv_mm_notuse
++;
364 /* Huh? Something's wrong */
367 /* Reset state to FREE */
368 eptr
->state
= MEM_FREE
;
370 /* Determine size for stats info */
371 for ( nclicks
= 0; nclicks
< ENI_BUF_NBIT
; nclicks
++ )
372 if ( ( 1 << nclicks
) * ENI_BUF_PGSZ
== eptr
->size
)
375 /* Valid size? Yes - decrement inuse count */
376 if ( nclicks
< ENI_BUF_NBIT
)
377 eup
->eu_memclicks
[nclicks
]--;
379 /* Try to compact neighbors */
382 if ( eptr
->prev
->state
== MEM_FREE
)
385 /* Add to previous block */
386 eptr
->prev
->size
+= eptr
->size
;
387 /* Set prev block to skip this one */
388 eptr
->prev
->next
= eptr
->next
;
389 /* Set next block to skip this one */
391 eptr
->next
->prev
= eptr
->prev
;
392 /* Reset to where we want to be */
394 /* and free this element */
395 (void)KM_FREE(etmp
, etmp
->size
, M_DEVBUF
);
399 if ( eptr
->next
->state
== MEM_FREE
)
401 Mbd
*etmp
= eptr
->next
;
403 /* add following block in */
404 eptr
->size
+= etmp
->size
;
405 /* set next next block to skip next block */
407 etmp
->next
->prev
= eptr
;
408 /* skip next block */
409 eptr
->next
= etmp
->next
;
410 /* and free next element */
411 (void)KM_FREE(etmp
, etmp
->size
, M_DEVBUF
);
414 * We've freed the buffer and done any compaction,
415 * we needn't look any further...
424 /* Oops - failed to find the buffer. This is BAD */
425 eup
->eu_stats
.eni_st_drv
.drv_mm_notfnd
++;