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