e0f1759f524ca07380ba9bc011fc049c8d696d7a
[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.6 2004/05/13 19:44: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 (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         eup->eu_memmap = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_WAITOK);
153
154         /*
155          * Test and size memory
156          */
157         eup->eu_ramsize = eni_test_memory ( eup );
158
159         /*
160          * Initialize a one element list which contains
161          * all buffer memory
162          */
163         eup->eu_memmap->prev = eup->eu_memmap->next = NULL;
164         eup->eu_memmap->base = (caddr_t)SEGBUF_BASE;
165         eup->eu_memmap->size = eup->eu_ramsize - SEGBUF_BASE;
166         eup->eu_memmap->state = MEM_FREE;
167
168         return ( eup->eu_ramsize );
169 }
170
171 /*
172  * Allocate a buffer from adapter RAM. Due to constraints on the card,
173  * we may roundup the size request to the next largest chunksize. Note
174  * also that we must pay attention to address alignment within adapter
175  * memory as well.
176  *
177  * Arguments:
178  *      eup             pointer to per unit structure
179  *      size            pointer to requested size - in bytes
180  *
181  * Returns:
182  *      addr            address relative to adapter of allocated memory
183  *      size            modified to reflect actual size of buffer
184  *
185  */
186 caddr_t
187 eni_allocate_buffer ( eup, size )
188         Eni_unit *eup;
189         u_long  *size;
190 {
191         int     nsize;
192         int     nclicks;
193         Mbd     *eptr = eup->eu_memmap;
194
195         /*
196          * Initial size requested
197          */
198         nsize = *size;
199
200         /*
201          * Find the buffer size which will hold this request. There
202          * are 8 possible sizes, each a power of two up, starting at
203          * 256 words or 1024 bytes.
204          */
205         for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
206                 if ( ( 1 << nclicks ) * ENI_BUF_PGSZ >= nsize )
207                         break;
208
209         /*
210          * Request was for larger then the card supports
211          */
212         if ( nclicks >= ENI_BUF_NBIT ) {
213                 eup->eu_stats.eni_st_drv.drv_mm_toobig++;
214                 /* Indicate 0 bytes allocated */
215                 *size = 0;
216                 /* Return NULL buffer */
217                 return ( (caddr_t)NULL );
218         }
219
220         /*
221          * New size will be buffer size
222          */
223         nsize = ( 1 << nclicks ) * ENI_BUF_PGSZ;
224
225         /*
226          * Look through memory for a segment large enough to
227          * hold request
228          */
229         while ( eptr ) {
230             /*
231              * State must be FREE and size must hold request
232              */
233             if ( eptr->state == MEM_FREE && eptr->size >= nsize )
234             {
235                 /*
236                  * Request will fit - now check if the
237                  * alignment needs fixing
238                  */
239                 if ( ((u_int)eptr->base & (nsize-1)) != 0 )
240                 {
241                     caddr_t     nbase;
242
243                     /*
244                      * Calculate where the buffer would have to
245                      * fall to be aligned.
246                      */
247                     nbase = (caddr_t)((u_int)( eptr->base + nsize ) &
248                         ~(nsize-1));
249                     /*
250                      * If we use this alignment, will it still fit?
251                      */
252                     if ( (eptr->size - (nbase - eptr->base)) >= 0 )
253                     {
254                         Mbd     *etmp;
255
256                         /* Yep - create a new segment */
257                         etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_WAITOK);
258                         /* Place it in the list */
259                         etmp->next = eptr->next;
260                         if ( etmp->next )
261                             etmp->next->prev = etmp;
262                         etmp->prev = eptr;
263                         eptr->next = etmp;
264                         /* Fill in new base and size */
265                         etmp->base = nbase;
266                         etmp->size = eptr->size - ( nbase - eptr->base );
267                         /* Adjust old size */
268                         eptr->size -= etmp->size;
269                         /* Mark its state */
270                         etmp->state = MEM_FREE;
271                         eptr = etmp;
272                         /* Done - outa here */
273                         break;
274                     }
275                 } else
276                     break;              /* Alignment is okay  - we're done */
277             }
278             /* Haven't found anything yet - keep looking */
279             eptr = eptr->next;
280         }
281
282         if ( eptr != NULL )
283         {
284             /* Found a usable segment - grab what we need */
285             /* Exact fit? */
286             if ( eptr->size == nsize )
287                 /* Mark it as INUSE */
288                 eptr->state = MEM_INUSE;
289             else
290             {
291                 Mbd     *etmp;
292                 /* larger then we need - split it */
293
294                 etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_WAITOK);
295                 /* Place new element in list */
296                 etmp->next = eptr->next;
297                 if ( etmp->next )
298                     etmp->next->prev = etmp;
299                 etmp->prev = eptr;
300                 eptr->next = etmp;
301                 /* Set new base, size and state */
302                 etmp->base = eptr->base + nsize;
303                 etmp->size = eptr->size - nsize;
304                 etmp->state = MEM_FREE;
305                 /* Adjust size and state of element we intend to use */
306                 eptr->size = nsize;
307                 eptr->state = MEM_INUSE;
308             }
309         }
310
311         /* After all that, did we find a usable buffer? */
312         if ( eptr )
313         {
314                 /* Record another inuse buffer of this size */
315                 if ( eptr->base )
316                         eup->eu_memclicks[nclicks]++;
317
318                 /*
319                  * Return true size of allocated buffer
320                  */
321                 *size = eptr->size;
322                 /*
323                  * Make address relative to start of RAM since
324                  * its (the address) for use by the adapter, not
325                  * the host.
326                  */
327                 return ((caddr_t)eptr->base);
328         } else {
329                 eup->eu_stats.eni_st_drv.drv_mm_nobuf++;
330                 /* No buffer to return - indicate zero length */
331                 *size = 0;
332                 /* Return NULL buffer */
333                 return ( (caddr_t)NULL );
334         }
335 }
336
337 /*
338  * Procedure to release a buffer previously allocated from adapter
339  * RAM. When possible, we'll compact memory.
340  *
341  * Arguments:
342  *      eup             pointer to per unit structure
343  *      base            base adapter address of buffer to be freed
344  *
345  * Returns:
346  *      none
347  *
348  */
349 void
350 eni_free_buffer ( eup, base )
351         Eni_unit *eup;
352         caddr_t base;
353 {
354         Mbd     *eptr = eup->eu_memmap;
355         int     nclicks;
356
357         /* Look through entire list */
358         while ( eptr )
359         {
360                 /* Is this the buffer to be freed? */
361                 if ( eptr->base == base )
362                 {
363                         /*
364                          * We're probably asking for trouble but,
365                          * assume this is it.
366                          */
367                         if ( eptr->state != MEM_INUSE )
368                         {
369                                 eup->eu_stats.eni_st_drv.drv_mm_notuse++;
370                                 /* Huh? Something's wrong */
371                                 return;
372                         }
373                         /* Reset state to FREE */
374                         eptr->state = MEM_FREE;
375
376                         /* Determine size for stats info */
377                         for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
378                             if ( ( 1 << nclicks ) * ENI_BUF_PGSZ == eptr->size )
379                                 break;
380
381                         /* Valid size? Yes - decrement inuse count */
382                         if ( nclicks < ENI_BUF_NBIT )
383                                 eup->eu_memclicks[nclicks]--;
384
385                         /* Try to compact neighbors */
386                         /* with previous */
387                         if ( eptr->prev )
388                             if ( eptr->prev->state == MEM_FREE )
389                             {
390                                 Mbd     *etmp = eptr;
391                                 /* Add to previous block */
392                                 eptr->prev->size += eptr->size;
393                                 /* Set prev block to skip this one */
394                                 eptr->prev->next = eptr->next;
395                                 /* Set next block to skip this one */
396                                 if ( eptr->next )
397                                         eptr->next->prev = eptr->prev;
398                                 /* Reset to where we want to be */
399                                 eptr = eptr->prev;
400                                 /* and free this element */
401                                 (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
402                             }
403                         /* with next */
404                         if ( eptr->next )
405                             if ( eptr->next->state == MEM_FREE )
406                             {
407                                 Mbd     *etmp = eptr->next;
408
409                                 /* add following block in */
410                                 eptr->size += etmp->size;
411                                 /* set next next block to skip next block */
412                                 if ( etmp->next )
413                                         etmp->next->prev = eptr;
414                                 /* skip next block */
415                                 eptr->next = etmp->next;
416                                 /* and free next element */
417                                 (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
418                             }
419                         /*
420                          * We've freed the buffer and done any compaction,
421                          * we needn't look any further...
422                          */
423                         return;
424                 }
425                 eptr = eptr->next;
426         }
427
428         if ( eptr == NULL )
429         {
430                 /* Oops - failed to find the buffer. This is BAD */
431                 eup->eu_stats.eni_st_drv.drv_mm_notfnd++;
432         }
433
434 }
435