ext2fs - A few bug fixes and syntax adjustments.
[dragonfly.git] / sys / dev / atm / hea / eni_buffer.c
blobef2cf81cd0fd56151aea11c28ac3108a19368356
1 /*
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"
41 #include "eni.h"
42 #include "eni_var.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.
72 * Arguments:
73 * eup pointer to device unit structure
75 * Returns
76 * size memory size in bytes
78 static int
79 eni_test_memory(Eni_unit *eup)
81 int ram_size = 0;
82 int i;
83 Eni_mem mp;
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);
90 /* write pattern */
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;
100 } else
101 break;
102 } else
103 break;
106 * Clear all RAM to initial value of zero.
107 * This makes sure we don't leave anything funny in the
108 * queues.
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 );
119 return ( ram_size );
124 * Initialize our memory allocator.
126 * Arguments:
127 * eup Pointer to per unit structure
129 * Returns:
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 */
144 return -1;
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
159 * all buffer memory
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
173 * memory as well.
175 * Arguments:
176 * eup pointer to per unit structure
177 * size pointer to requested size - in bytes
179 * Returns:
180 * addr address relative to adapter of allocated memory
181 * size modified to reflect actual size of buffer
184 caddr_t
185 eni_allocate_buffer(Eni_unit *eup, u_long *size)
187 int nsize;
188 int nclicks;
189 Mbd *eptr = eup->eu_memmap;
192 * Initial size requested
194 nsize = *size;
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 )
203 break;
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 */
211 *size = 0;
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
223 * hold request
225 while ( eptr ) {
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 )
237 caddr_t nbase;
240 * Calculate where the buffer would have to
241 * fall to be aligned.
243 nbase = (caddr_t)((u_int)( eptr->base + nsize ) &
244 ~(nsize-1));
246 * If we use this alignment, will it still fit?
248 if ( (eptr->size - (nbase - eptr->base)) >= 0 )
250 Mbd *etmp;
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;
256 if ( etmp->next )
257 etmp->next->prev = etmp;
258 etmp->prev = eptr;
259 eptr->next = etmp;
260 /* Fill in new base and size */
261 etmp->base = nbase;
262 etmp->size = eptr->size - ( nbase - eptr->base );
263 /* Adjust old size */
264 eptr->size -= etmp->size;
265 /* Mark its state */
266 etmp->state = MEM_FREE;
267 eptr = etmp;
268 /* Done - outa here */
269 break;
271 } else
272 break; /* Alignment is okay - we're done */
274 /* Haven't found anything yet - keep looking */
275 eptr = eptr->next;
278 if ( eptr != NULL )
280 /* Found a usable segment - grab what we need */
281 /* Exact fit? */
282 if ( eptr->size == nsize )
283 /* Mark it as INUSE */
284 eptr->state = MEM_INUSE;
285 else
287 Mbd *etmp;
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;
293 if ( etmp->next )
294 etmp->next->prev = etmp;
295 etmp->prev = eptr;
296 eptr->next = 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 */
302 eptr->size = nsize;
303 eptr->state = MEM_INUSE;
307 /* After all that, did we find a usable buffer? */
308 if ( eptr )
310 /* Record another inuse buffer of this size */
311 if ( eptr->base )
312 eup->eu_memclicks[nclicks]++;
315 * Return true size of allocated buffer
317 *size = eptr->size;
319 * Make address relative to start of RAM since
320 * its (the address) for use by the adapter, not
321 * the host.
323 return ((caddr_t)eptr->base);
324 } else {
325 eup->eu_stats.eni_st_drv.drv_mm_nobuf++;
326 /* No buffer to return - indicate zero length */
327 *size = 0;
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.
337 * Arguments:
338 * eup pointer to per unit structure
339 * base base adapter address of buffer to be freed
341 * Returns:
342 * none
345 void
346 eni_free_buffer(Eni_unit *eup, caddr_t base)
348 Mbd *eptr = eup->eu_memmap;
349 int nclicks;
351 /* Look through entire list */
352 while ( eptr )
354 /* Is this the buffer to be freed? */
355 if ( eptr->base == base )
358 * We're probably asking for trouble but,
359 * assume this is it.
361 if ( eptr->state != MEM_INUSE )
363 eup->eu_stats.eni_st_drv.drv_mm_notuse++;
364 /* Huh? Something's wrong */
365 return;
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 )
373 break;
375 /* Valid size? Yes - decrement inuse count */
376 if ( nclicks < ENI_BUF_NBIT )
377 eup->eu_memclicks[nclicks]--;
379 /* Try to compact neighbors */
380 /* with previous */
381 if ( eptr->prev )
382 if ( eptr->prev->state == MEM_FREE )
384 Mbd *etmp = eptr;
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 */
390 if ( eptr->next )
391 eptr->next->prev = eptr->prev;
392 /* Reset to where we want to be */
393 eptr = eptr->prev;
394 /* and free this element */
395 (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
397 /* with next */
398 if ( eptr->next )
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 */
406 if ( etmp->next )
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...
417 return;
419 eptr = eptr->next;
422 if ( eptr == NULL )
424 /* Oops - failed to find the buffer. This is BAD */
425 eup->eu_stats.eni_st_drv.drv_mm_notfnd++;