Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / dev / atm / hea / eni_buffer.c
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 /*
31  * Efficient ENI Adapter Support
32  * -----------------------------
33  *
34  * Handle adapter memory buffers for ENI adapters
35  *
36  */
37
38 #include <netatm/kern_include.h>
39
40 #include <dev/hea/eni_stats.h>
41 #include <dev/hea/eni.h>
42 #include <dev/hea/eni_var.h>
43
44 #ifndef lint
45 __RCSID("@(#) $FreeBSD: src/sys/dev/hea/eni_buffer.c,v 1.5 1999/08/28 00:41:43 peter Exp $");
46 #endif
47
48 static int      eni_test_memory __P((Eni_unit *));
49
50 /*
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.
60  */
61
62 /*
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.
66  */
67 static int eni_mem_max = MAX_ENI_MEM;   /* Default to all available memory */
68
69 /*
70  * Size and test adapter RAM
71  *
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.
75  *
76  * Arguments:
77  *      eup             pointer to device unit structure
78  *
79  * Returns
80  *      size            memory size in bytes
81  */
82 static int
83 eni_test_memory ( eup )
84         Eni_unit *eup;
85 {
86         int     ram_size = 0;
87         int     i;
88         Eni_mem mp;
89
90         /*
91          * Walk through to maximum looking for RAM
92          */
93         for ( i = 0; i < MAX_ENI_MEM; i += TEST_STEP ) {
94                 mp = (Eni_mem)((int)eup->eu_ram + i);
95                 /* write pattern */
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;
105                         } else
106                             break;
107                 } else
108                         break;
109         }
110         /*
111          * Clear all RAM to initial value of zero.
112          * This makes sure we don't leave anything funny in the
113          * queues.
114          */
115         KM_ZERO ( eup->eu_ram, ram_size );
116
117         /*
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.
121          */
122         ram_size = MIN ( ram_size, eni_mem_max ); 
123
124         return ( ram_size );
125
126 }
127
128 /*
129  * Initialize our memory allocator.
130  *
131  * Arguments:
132  *      eup             Pointer to per unit structure
133  *
134  * Returns:
135  *      size            Physical RAM size
136  *      -1              failed to initialize memory
137  *
138  */
139 int
140 eni_init_memory ( eup )
141         Eni_unit *eup;
142 {
143
144         /*
145          * Have we (somehow) been called before?
146          */
147         if ( eup->eu_memmap != NULL )
148         {
149                 /* Oops  - it's already been initialized */
150                 return -1;
151         }
152
153         /*
154          * Allocate initial element which will hold all of memory
155          */
156         if ( ( eup->eu_memmap = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF,
157             M_NOWAIT ) ) == NULL )
158         {
159                 /* Memory allocation error */
160                 return -1;
161         }
162
163         /*
164          * Test and size memory
165          */
166         eup->eu_ramsize = eni_test_memory ( eup );
167
168         /*
169          * Initialize a one element list which contains
170          * all buffer memory
171          */
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;
176
177         return ( eup->eu_ramsize );
178 }
179
180 /*
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
184  * memory as well.
185  *
186  * Arguments:
187  *      eup             pointer to per unit structure
188  *      size            pointer to requested size - in bytes
189  *
190  * Returns:
191  *      addr            address relative to adapter of allocated memory
192  *      size            modified to reflect actual size of buffer
193  *
194  */
195 caddr_t
196 eni_allocate_buffer ( eup, size )
197         Eni_unit *eup;
198         u_long  *size;
199 {
200         int     nsize;
201         int     nclicks;
202         Mbd     *eptr = eup->eu_memmap;
203
204         /*
205          * Initial size requested
206          */
207         nsize = *size;
208
209         /*
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.
213          */
214         for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
215                 if ( ( 1 << nclicks ) * ENI_BUF_PGSZ >= nsize )
216                         break;
217
218         /*
219          * Request was for larger then the card supports
220          */
221         if ( nclicks >= ENI_BUF_NBIT ) {
222                 eup->eu_stats.eni_st_drv.drv_mm_toobig++;
223                 /* Indicate 0 bytes allocated */
224                 *size = 0;
225                 /* Return NULL buffer */
226                 return ( (caddr_t)NULL );
227         }
228
229         /*
230          * New size will be buffer size
231          */
232         nsize = ( 1 << nclicks ) * ENI_BUF_PGSZ;
233
234         /*
235          * Look through memory for a segment large enough to
236          * hold request
237          */
238         while ( eptr ) {
239             /*
240              * State must be FREE and size must hold request
241              */
242             if ( eptr->state == MEM_FREE && eptr->size >= nsize )
243             {
244                 /*
245                  * Request will fit - now check if the
246                  * alignment needs fixing
247                  */
248                 if ( ((u_int)eptr->base & (nsize-1)) != 0 )
249                 {
250                     caddr_t     nbase;
251
252                     /*
253                      * Calculate where the buffer would have to
254                      * fall to be aligned.
255                      */
256                     nbase = (caddr_t)((u_int)( eptr->base + nsize ) &
257                         ~(nsize-1));
258                     /*
259                      * If we use this alignment, will it still fit?
260                      */
261                     if ( (eptr->size - (nbase - eptr->base)) >= 0 )
262                     {
263                         Mbd     *etmp;
264
265                         /* Yep - create a new segment */
266                         etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF,
267                             M_NOWAIT );
268                         if ( etmp == (Mbd *)NULL ) {
269                                 /*
270                                  * Couldn't allocate a new descriptor. Indicate 
271                                  * failure and exit now or we'll start losing
272                                  * memory.
273                                  */
274                                 eup->eu_stats.eni_st_drv.drv_mm_nodesc++;
275                                 *size = 0;
276                                 return ( (caddr_t)NULL );
277                         }
278                         /* Place it in the list */
279                         etmp->next = eptr->next;
280                         if ( etmp->next )
281                             etmp->next->prev = etmp;
282                         etmp->prev = eptr;
283                         eptr->next = etmp;
284                         /* Fill in new base and size */
285                         etmp->base = nbase;
286                         etmp->size = eptr->size - ( nbase - eptr->base );
287                         /* Adjust old size */
288                         eptr->size -= etmp->size;
289                         /* Mark its state */
290                         etmp->state = MEM_FREE;
291                         eptr = etmp;
292                         /* Done - outa here */
293                         break;
294                     }
295                 } else
296                     break;              /* Alignment is okay  - we're done */
297             }
298             /* Haven't found anything yet - keep looking */
299             eptr = eptr->next;
300         }
301
302         if ( eptr != NULL )
303         {
304             /* Found a usable segment - grab what we need */
305             /* Exact fit? */
306             if ( eptr->size == nsize )
307                 /* Mark it as INUSE */
308                 eptr->state = MEM_INUSE;
309             else
310             {
311                 Mbd     *etmp;
312                 /* larger then we need - split it */
313
314                 etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_NOWAIT );
315                 if ( etmp == (Mbd *)NULL ) {
316                         /*
317                          * Couldn't allocate new descriptor. Indicate
318                          * failure and exit now or we'll start losing
319                          * memory.
320                          */
321                         eup->eu_stats.eni_st_drv.drv_mm_nodesc++;
322                         *size = 0;
323                         return ( (caddr_t)NULL );
324                 }
325                 /* Place new element in list */
326                 etmp->next = eptr->next;
327                 if ( etmp->next )
328                     etmp->next->prev = etmp;
329                 etmp->prev = eptr;
330                 eptr->next = 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 */
336                 eptr->size = nsize;
337                 eptr->state = MEM_INUSE;
338             }
339         }
340
341         /* After all that, did we find a usable buffer? */
342         if ( eptr )
343         {
344                 /* Record another inuse buffer of this size */
345                 if ( eptr->base )
346                         eup->eu_memclicks[nclicks]++;
347
348                 /*
349                  * Return true size of allocated buffer
350                  */
351                 *size = eptr->size;
352                 /*
353                  * Make address relative to start of RAM since
354                  * its (the address) for use by the adapter, not
355                  * the host.
356                  */
357                 return ((caddr_t)eptr->base);
358         } else {
359                 eup->eu_stats.eni_st_drv.drv_mm_nobuf++;
360                 /* No buffer to return - indicate zero length */
361                 *size = 0;
362                 /* Return NULL buffer */
363                 return ( (caddr_t)NULL );
364         }
365 }
366
367 /*
368  * Procedure to release a buffer previously allocated from adapter
369  * RAM. When possible, we'll compact memory.
370  *
371  * Arguments:
372  *      eup             pointer to per unit structure
373  *      base            base adapter address of buffer to be freed
374  *
375  * Returns:
376  *      none
377  *
378  */
379 void
380 eni_free_buffer ( eup, base )
381         Eni_unit *eup;
382         caddr_t base;
383 {
384         Mbd     *eptr = eup->eu_memmap;
385         int     nclicks;
386
387         /* Look through entire list */
388         while ( eptr )
389         {
390                 /* Is this the buffer to be freed? */
391                 if ( eptr->base == base )
392                 {
393                         /*
394                          * We're probably asking for trouble but,
395                          * assume this is it.
396                          */
397                         if ( eptr->state != MEM_INUSE )
398                         {
399                                 eup->eu_stats.eni_st_drv.drv_mm_notuse++;
400                                 /* Huh? Something's wrong */
401                                 return;
402                         }
403                         /* Reset state to FREE */
404                         eptr->state = MEM_FREE;
405
406                         /* Determine size for stats info */
407                         for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
408                             if ( ( 1 << nclicks ) * ENI_BUF_PGSZ == eptr->size )
409                                 break;
410
411                         /* Valid size? Yes - decrement inuse count */
412                         if ( nclicks < ENI_BUF_NBIT )
413                                 eup->eu_memclicks[nclicks]--;
414
415                         /* Try to compact neighbors */
416                         /* with previous */
417                         if ( eptr->prev )
418                             if ( eptr->prev->state == MEM_FREE )
419                             {
420                                 Mbd     *etmp = eptr;
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 */
426                                 if ( eptr->next )
427                                         eptr->next->prev = eptr->prev;
428                                 /* Reset to where we want to be */
429                                 eptr = eptr->prev;
430                                 /* and free this element */
431                                 (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
432                             }
433                         /* with next */
434                         if ( eptr->next )
435                             if ( eptr->next->state == MEM_FREE )
436                             {
437                                 Mbd     *etmp = eptr->next;
438
439                                 /* add following block in */
440                                 eptr->size += etmp->size;
441                                 /* set next next block to skip next block */
442                                 if ( etmp->next )
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);
448                             }
449                         /*
450                          * We've freed the buffer and done any compaction,
451                          * we needn't look any further...
452                          */
453                         return;
454                 }
455                 eptr = eptr->next;
456         }
457
458         if ( eptr == NULL )
459         {
460                 /* Oops - failed to find the buffer. This is BAD */
461                 eup->eu_stats.eni_st_drv.drv_mm_notfnd++;
462         }
463
464 }
465