1 /*
2 *
3 * ===================================
4 * HARP | Host ATM Research Platform
5 * ===================================
6 *
7 *
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.
12 *
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.
20 *
21 * Copyright 1994-1998 Network Computing Services, Inc.
22 *
23 * Copies of this Software may be made, however, the above copyright
24 * notice must be reproduced on all copies.
25 *
26 * @(#) $FreeBSD: src/sys/dev/hea/eni_buffer.c,v 1.5 1999/08/28 00:41:43 peter Exp $
27 */
28
29 /*
30 * Efficient ENI Adapter Support
31 * -----------------------------
32 *
33 * Handle adapter memory buffers for ENI adapters
34 *
35 */
36
37 #include <netproto/atm/kern_include.h>
38
39 #include "eni_stats.h"
40 #include "eni.h"
41 #include "eni_var.h"
42
43 static int eni_test_memory (Eni_unit *);
44
45 /*
46 * The host is going to manage (that is, allocate and free) buffers
47 * in the adapters RAM space. We are going to implement this as a
48 * linked list describing FREE and INUSE memory segments. Initially,
49 * the list contains one element with all memory marked free. As requests
50 * are made, we search the list until we find the first free element
51 * which can satisfy the request. If necessary, we will break the free
52 * element into an INUSE element, and a new FREE element. When freeing
53 * memory, we look at adjacent elements and if one or more are free,
54 * we will combine into a single larger FREE element.
55 */
56
57 /*
58 * This is for testing purposes. Since there are two versions of
59 * the Efficient adapter with different memory sizes, this allows
60 * us to fool an adapter with more memory into thinking it has less.
61 */
62 static int eni_mem_max = MAX_ENI_MEM; /* Default to all available memory */
63
64 /*
65 * Size and test adapter RAM
66 *
67 * Walk through adapter RAM writing known patterns and reading back
68 * for comparison. We write more than one pattern on the off chance
69 * that we "get lucky" and read what we expected.
70 *
71 * Arguments:
72 * eup pointer to device unit structure
73 *
74 * Returns
75 * size memory size in bytes
76 */
77 static int
78 eni_test_memory(Eni_unit *eup)
79 {
80 int ram_size = 0;
81 int i;
82 Eni_mem mp;
83
84 /*
85 * Walk through to maximum looking for RAM
86 */
87 for ( i = 0; i < MAX_ENI_MEM; i += TEST_STEP ) {
88 mp = (Eni_mem)((int)eup->eu_ram + i);
89 /* write pattern */
90 *mp = (u_long)TEST_PAT;
91 /* read pattern, match? */
92 if ( *mp == (u_long)TEST_PAT ) {
93 /* yes - write inverse pattern */
94 *mp = (u_long)~TEST_PAT;
95 /* read pattern, match? */
96 if ( *mp == (u_long)~TEST_PAT ) {
97 /* yes - assume another 1K available */
98 ram_size = i + TEST_STEP;
99 } else
100 break;
101 } else
102 break;
103 }
104 /*
105 * Clear all RAM to initial value of zero.
106 * This makes sure we don't leave anything funny in the
107 * queues.
108 */
109 KM_ZERO ( eup->eu_ram, ram_size );
110
111 /*
112 * If we'd like to claim to have less memory, here's where
113 * we do so. We take the minimum of what we'd like and what
114 * we really found on the adapter.
115 */
116 ram_size = MIN ( ram_size, eni_mem_max );
117
118 return ( ram_size );
119
120 }
121
122 /*
123 * Initialize our memory allocator.
124 *
125 * Arguments:
126 * eup Pointer to per unit structure
127 *
128 * Returns:
129 * size Physical RAM size
130 * -1 failed to initialize memory
131 *
132 */
133 int
134 eni_init_memory(Eni_unit *eup)
135 {
136
137 /*
138 * Have we (somehow) been called before?
139 */
140 if ( eup->eu_memmap != NULL )
141 {
142 /* Oops - it's already been initialized */
143 return -1;
144 }
145
146 /*
147 * Allocate initial element which will hold all of memory
148 */
149 eup->eu_memmap = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_WAITOK);
150
151 /*
152 * Test and size memory
153 */
154 eup->eu_ramsize = eni_test_memory ( eup );
155
156 /*
157 * Initialize a one element list which contains
158 * all buffer memory
159 */
160 eup->eu_memmap->prev = eup->eu_memmap->next = NULL;
161 eup->eu_memmap->base = (caddr_t)SEGBUF_BASE;
162 eup->eu_memmap->size = eup->eu_ramsize - SEGBUF_BASE;
163 eup->eu_memmap->state = MEM_FREE;
164
165 return ( eup->eu_ramsize );
166 }
167
168 /*
169 * Allocate a buffer from adapter RAM. Due to constraints on the card,
170 * we may roundup the size request to the next largest chunksize. Note
171 * also that we must pay attention to address alignment within adapter
172 * memory as well.
173 *
174 * Arguments:
175 * eup pointer to per unit structure
176 * size pointer to requested size - in bytes
177 *
178 * Returns:
179 * addr address relative to adapter of allocated memory
180 * size modified to reflect actual size of buffer
181 *
182 */
183 caddr_t
184 eni_allocate_buffer(Eni_unit *eup, u_long *size)
185 {
186 int nsize;
187 int nclicks;
188 Mbd *eptr = eup->eu_memmap;
189
190 /*
191 * Initial size requested
192 */
193 nsize = *size;
194
195 /*
196 * Find the buffer size which will hold this request. There
197 * are 8 possible sizes, each a power of two up, starting at
198 * 256 words or 1024 bytes.
199 */
200 for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
201 if ( ( 1 << nclicks ) * ENI_BUF_PGSZ >= nsize )
202 break;
203
204 /*
205 * Request was for larger then the card supports
206 */
207 if ( nclicks >= ENI_BUF_NBIT ) {
208 eup->eu_stats.eni_st_drv.drv_mm_toobig++;
209 /* Indicate 0 bytes allocated */
210 *size = 0;
211 /* Return NULL buffer */
212 return ( NULL );
213 }
214
215 /*
216 * New size will be buffer size
217 */
218 nsize = ( 1 << nclicks ) * ENI_BUF_PGSZ;
219
220 /*
221 * Look through memory for a segment large enough to
222 * hold request
223 */
224 while ( eptr ) {
225 /*
226 * State must be FREE and size must hold request
227 */
228 if ( eptr->state == MEM_FREE && eptr->size >= nsize )
229 {
230 /*
231 * Request will fit - now check if the
232 * alignment needs fixing
233 */
234 if ( ((u_int)eptr->base & (nsize-1)) != 0 )
235 {
236 caddr_t nbase;
237
238 /*
239 * Calculate where the buffer would have to
240 * fall to be aligned.
241 */
242 nbase = (caddr_t)((u_int)( eptr->base + nsize ) &
243 ~(nsize-1));
244 /*
245 * If we use this alignment, will it still fit?
246 */
247 if ( (eptr->size - (nbase - eptr->base)) >= 0 )
248 {
249 Mbd *etmp;
250
251 /* Yep - create a new segment */
252 etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_WAITOK);
253 /* Place it in the list */
254 etmp->next = eptr->next;
255 if ( etmp->next )
256 etmp->next->prev = etmp;
257 etmp->prev = eptr;
258 eptr->next = etmp;
259 /* Fill in new base and size */
260 etmp->base = nbase;
261 etmp->size = eptr->size - ( nbase - eptr->base );
262 /* Adjust old size */
263 eptr->size -= etmp->size;
264 /* Mark its state */
265 etmp->state = MEM_FREE;
266 eptr = etmp;
267 /* Done - outa here */
268 break;
269 }
270 } else
271 break; /* Alignment is okay - we're done */
272 }
273 /* Haven't found anything yet - keep looking */
274 eptr = eptr->next;
275 }
276
277 if ( eptr != NULL )
278 {
279 /* Found a usable segment - grab what we need */
280 /* Exact fit? */
281 if ( eptr->size == nsize )
282 /* Mark it as INUSE */
283 eptr->state = MEM_INUSE;
284 else
285 {
286 Mbd *etmp;
287 /* larger then we need - split it */
288
289 etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_WAITOK);
290 /* Place new element in list */
291 etmp->next = eptr->next;
292 if ( etmp->next )
293 etmp->next->prev = etmp;
294 etmp->prev = eptr;
295 eptr->next = etmp;
296 /* Set new base, size and state */
297 etmp->base = eptr->base + nsize;
298 etmp->size = eptr->size - nsize;
299 etmp->state = MEM_FREE;
300 /* Adjust size and state of element we intend to use */
301 eptr->size = nsize;
302 eptr->state = MEM_INUSE;
303 }
304 }
305
306 /* After all that, did we find a usable buffer? */
307 if ( eptr )
308 {
309 /* Record another inuse buffer of this size */
310 if ( eptr->base )
311 eup->eu_memclicks[nclicks]++;
312
313 /*
314 * Return true size of allocated buffer
315 */
316 *size = eptr->size;
317 /*
318 * Make address relative to start of RAM since
319 * its (the address) for use by the adapter, not
320 * the host.
321 */
322 return ((caddr_t)eptr->base);
323 } else {
324 eup->eu_stats.eni_st_drv.drv_mm_nobuf++;
325 /* No buffer to return - indicate zero length */
326 *size = 0;
327 /* Return NULL buffer */
328 return ( NULL );
329 }
330 }
331
332 /*
333 * Procedure to release a buffer previously allocated from adapter
334 * RAM. When possible, we'll compact memory.
335 *
336 * Arguments:
337 * eup pointer to per unit structure
338 * base base adapter address of buffer to be freed
339 *
340 * Returns:
341 * none
342 *
343 */
344 void
345 eni_free_buffer(Eni_unit *eup, caddr_t base)
346 {
347 Mbd *eptr = eup->eu_memmap;
348 int nclicks;
349
350 /* Look through entire list */
351 while ( eptr )
352 {
353 /* Is this the buffer to be freed? */
354 if ( eptr->base == base )
355 {
356 /*
357 * We're probably asking for trouble but,
358 * assume this is it.
359 */
360 if ( eptr->state != MEM_INUSE )
361 {
362 eup->eu_stats.eni_st_drv.drv_mm_notuse++;
363 /* Huh? Something's wrong */
364 return;
365 }
366 /* Reset state to FREE */
367 eptr->state = MEM_FREE;
368
369 /* Determine size for stats info */
370 for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
371 if ( ( 1 << nclicks ) * ENI_BUF_PGSZ == eptr->size )
372 break;
373
374 /* Valid size? Yes - decrement inuse count */
375 if ( nclicks < ENI_BUF_NBIT )
376 eup->eu_memclicks[nclicks]--;
377
378 /* Try to compact neighbors */
379 /* with previous */
380 if ( eptr->prev )
381 if ( eptr->prev->state == MEM_FREE )
382 {
383 Mbd *etmp = eptr;
384 /* Add to previous block */
385 eptr->prev->size += eptr->size;
386 /* Set prev block to skip this one */
387 eptr->prev->next = eptr->next;
388 /* Set next block to skip this one */
389 if ( eptr->next )
390 eptr->next->prev = eptr->prev;
391 /* Reset to where we want to be */
392 eptr = eptr->prev;
393 /* and free this element */
394 (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
395 }
396 /* with next */
397 if ( eptr->next )
398 if ( eptr->next->state == MEM_FREE )
399 {
400 Mbd *etmp = eptr->next;
401
402 /* add following block in */
403 eptr->size += etmp->size;
404 /* set next next block to skip next block */
405 if ( etmp->next )
406 etmp->next->prev = eptr;
407 /* skip next block */
408 eptr->next = etmp->next;
409 /* and free next element */
410 (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
411 }
412 /*
413 * We've freed the buffer and done any compaction,
414 * we needn't look any further...
415 */
416 return;
417 }
418 eptr = eptr->next;
419 }
420
421 if ( eptr == NULL )
422 {
423 /* Oops - failed to find the buffer. This is BAD */
424 eup->eu_stats.eni_st_drv.drv_mm_notfnd++;
425 }
426
427 }
428
Cache object: 55d9d823f4f4ffd3c1f8126f7948a044
|