]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/cxgb/sys/uipc_mvec.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / dev / cxgb / sys / uipc_mvec.c
1 /**************************************************************************
2  *
3  * Copyright (c) 2007, Kip Macy kmacy@freebsd.org
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice,
10  *    this list of conditions and the following disclaimer.
11  *
12  * 2. The name of Kip Macy nor the names of other
13  *    contributors may be used to endorse or promote products derived from
14  *    this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  *
28  *
29  ***************************************************************************/
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/ktr.h>
41 #include <sys/sf_buf.h>
42
43 #include <vm/vm.h>
44 #include <vm/pmap.h>
45
46 #include <machine/bus.h>
47
48
49
50 #ifdef CONFIG_DEFINED
51 #include <cxgb_include.h>
52 #include <sys/mvec.h>
53 #else
54 #include <dev/cxgb/cxgb_include.h>
55 #include <dev/cxgb/sys/mvec.h>
56 #endif
57
58 #include "opt_zero.h"
59
60 #include <vm/vm.h>
61 #include <vm/vm_page.h>
62 #include <vm/pmap.h>
63
64 #ifdef INVARIANTS
65 #define M_SANITY m_sanity
66 #else
67 #define M_SANITY(a, b)
68 #endif
69
70 #define MAX_BUFS 36
71 #define MAX_HVEC 8
72
73 extern uint32_t collapse_free;
74 extern uint32_t mb_free_vec_free;
75
76 uma_zone_t zone_miovec;
77 static int mi_inited = 0;
78 int cxgb_mbufs_outstanding = 0;
79 int cxgb_pack_outstanding = 0;
80
81 void
82 mi_init(void)
83 {
84         if (mi_inited > 0)
85                 return;
86         else
87                 mi_inited++;
88
89         zone_miovec = uma_zcreate("MBUF IOVEC", MIOVBYTES,
90             NULL, NULL, NULL, NULL,
91             UMA_ALIGN_PTR, UMA_ZONE_MAXBUCKET);
92 }
93
94 void
95 mi_deinit(void)
96 {
97         mi_inited--;
98         if (mi_inited == 0)
99                 uma_zdestroy(zone_miovec);
100 }
101         
102 void
103 dump_mi(struct mbuf_iovec *mi)
104 {
105         int i;
106         struct mbuf_vec *mv;
107         
108         printf("mi_flags=0x%08x mi_base=%p mi_data=%p mi_len=%d mi_type=%d\n",
109             mi->mi_flags, mi->mi_base, mi->mi_data, mi->mi_len, mi->mi_type);
110
111         if (mi->mi_type == EXT_CLIOVEC ||
112             mi->mi_type == EXT_IOVEC) {
113                 mv = mtomv((struct mbuf *)mi->mi_base);
114                 mi = mv->mv_vec;
115                 for (i = 0; i < mv->mv_count; i++, mi++) 
116                         dump_mi(mi);
117
118         }
119 }
120
121 static __inline struct mbuf *
122 _mcl_collapse_mbuf(struct mbuf_iovec *mi, struct mbuf *m)
123 {
124         struct mbuf *n = m->m_next;
125
126         prefetch(n);
127
128         mi->mi_flags = m->m_flags;
129         mi->mi_len = m->m_len;
130         mi->mi_mbuf = NULL;
131         
132         if (m->m_flags & M_PKTHDR) {
133                 mi->mi_ether_vtag = m->m_pkthdr.ether_vtag;
134                 mi->mi_tso_segsz = m->m_pkthdr.tso_segsz;
135 #ifdef IFNET_MULTIQ             
136                 mi->mi_rss_hash = m->m_pkthdr.rss_hash;
137 #endif          
138         }
139         if (m->m_type != MT_DATA) {
140                 mi->mi_data = NULL;
141                 mi->mi_base = (caddr_t)m;
142                 /*
143                  * XXX JMPIOVEC
144                  */
145                 mi->mi_size = (m->m_type == EXT_CLIOVEC) ? MCLBYTES : MIOVBYTES;
146                 mi->mi_type = m->m_type;
147                 mi->mi_len = m->m_pkthdr.len;
148                 KASSERT(mi->mi_len, ("empty packet"));
149                 mi->mi_refcnt = NULL;
150         } else if (m->m_flags & M_EXT) {
151                 memcpy(&mi->mi_ext, &m->m_ext, sizeof(struct m_ext_));
152                 mi->mi_data = m->m_data;
153                 mi->mi_base = m->m_ext.ext_buf;
154                 mi->mi_type = m->m_ext.ext_type;
155                 mi->mi_size = m->m_ext.ext_size;
156                 mi->mi_refcnt = m->m_ext.ref_cnt;
157                 mi->mi_mbuf = m;
158         } else {
159                 mi->mi_base = (caddr_t)m;
160                 mi->mi_data = m->m_data;
161                 mi->mi_size = MSIZE;
162                 mi->mi_type = EXT_MBUF;
163                 mi->mi_refcnt = NULL;
164         }
165         KASSERT(mi->mi_len != 0, ("miov has len 0"));
166         KASSERT(mi->mi_type > 0, ("mi_type is invalid"));
167         KASSERT(mi->mi_base, ("mi_base is invalid"));
168         return (n);
169 }
170
171 struct mbuf *
172 mi_collapse_mbuf(struct mbuf_iovec *mi, struct mbuf *m)
173 {
174         return _mcl_collapse_mbuf(mi, m);
175 }
176         
177 void *
178 mcl_alloc(int seg_count, int *type) 
179 {
180         uma_zone_t zone;
181         
182         if (seg_count > MAX_CL_IOV) {
183                 zone = zone_jumbop;
184                 *type = EXT_JMPIOVEC;
185         } else if (seg_count > MAX_MIOVEC_IOV) {
186                 zone = zone_clust;
187                 *type = EXT_CLIOVEC;
188         } else {
189                 *type = EXT_IOVEC;
190                 zone = zone_miovec;
191         }
192         return uma_zalloc_arg(zone, NULL, M_NOWAIT);
193 }
194
195 int
196 busdma_map_sg_collapse(struct mbuf **m, bus_dma_segment_t *segs, int *nsegs)
197 {
198         struct mbuf *m0, *mhead, *n = *m;
199         struct mbuf_iovec *mi;
200         struct mbuf *marray[TX_MAX_SEGS];
201         int i, type, seg_count, defragged = 0, err = 0;
202         struct mbuf_vec *mv;
203         int skipped, freed, outstanding, pack_outstanding, mbuf_outstanding;
204
205         
206         
207         KASSERT(n->m_pkthdr.len, ("packet has zero header len"));
208                 
209         if (n->m_flags & M_PKTHDR && !SLIST_EMPTY(&n->m_pkthdr.tags)) 
210                 m_tag_delete_chain(n, NULL);
211
212         if (n->m_pkthdr.len <= PIO_LEN)
213                 return (0);
214 retry:
215         seg_count = 0;
216         if (n->m_next == NULL) {
217                 busdma_map_mbuf_fast(n, segs);
218                 *nsegs = 1;
219                 if ((n->m_flags & M_EXT) &&
220                     (n->m_ext.ext_type == EXT_PACKET)) 
221                         cxgb_pack_outstanding++;
222                 else if ((n->m_flags & M_NOFREE) == 0) 
223                         cxgb_mbufs_outstanding++;
224                 return (0);
225         }
226         skipped = freed = outstanding = pack_outstanding = mbuf_outstanding = 0;
227         while (n && seg_count < TX_MAX_SEGS) {
228                 marray[seg_count] = n;
229                 
230                 /*
231                  * firmware doesn't like empty segments
232                  */
233                 if (__predict_true(n->m_len != 0))  
234                         seg_count++;
235                 else
236                         skipped++;
237
238                 n = n->m_next;
239         }
240         if (seg_count == 0) {
241                 if (cxgb_debug)
242                         printf("empty segment chain\n");
243                 err = EFBIG;
244                 goto err_out;
245         }  else if (seg_count >= TX_MAX_SEGS) {
246                 if (cxgb_debug)
247                         printf("mbuf chain too long: %d max allowed %d\n",
248                             seg_count, TX_MAX_SEGS);
249                 if (!defragged) {
250                         n = m_defrag(*m, M_DONTWAIT);
251                         if (n == NULL) {
252                                 err = ENOBUFS;
253                                 goto err_out;
254                         }
255                         *m = n;
256                         defragged = 1;
257                         goto retry;
258                 }
259                 err = EFBIG;
260                 goto err_out;
261         }
262
263         if ((m0 = mcl_alloc(seg_count, &type)) == NULL) {
264                 err = ENOMEM;
265                 goto err_out;
266         }
267         
268         memcpy(m0, *m, sizeof(struct m_hdr) + sizeof(struct pkthdr));
269         m0->m_type = type;
270         KASSERT(m0->m_pkthdr.len, ("empty packet being marshalled"));
271         mv = mtomv(m0);
272         mv->mv_count = seg_count;
273         mv->mv_first = 0;
274         for (i = 0, mi = mv->mv_vec; i < seg_count; mi++, segs++, i++) {
275                 n = marray[i];
276                 busdma_map_mbuf_fast(n, segs);
277                 _mcl_collapse_mbuf(mi, n);
278         }
279         n = *m;
280         while (n) {
281                 if (n->m_len == 0)
282                         /* do nothing - free if mbuf or cluster */; 
283                 else if ((n->m_flags & M_EXT) == 0) {
284                         mbuf_outstanding++;
285                         goto skip;
286                 } else if ((n->m_flags & M_EXT) &&
287                     (n->m_ext.ext_type == EXT_PACKET)) {
288                         pack_outstanding++;
289                         goto skip;
290                 } else if (n->m_flags & M_NOFREE) 
291                         goto skip; 
292                 else if ((n->m_flags & (M_EXT|M_NOFREE)) == M_EXT)
293                         n->m_flags &= ~M_EXT; 
294                 mhead = n->m_next;
295                 m_free(n);
296                 n = mhead;
297                 freed++;
298                 continue;
299         skip:
300                 /*
301                  * is an immediate mbuf or is from the packet zone
302                  */
303                 n = n->m_next;
304         }
305         *nsegs = seg_count;
306         *m = m0;
307         DPRINTF("pktlen=%d m0=%p *m=%p m=%p\n", m0->m_pkthdr.len, m0, *m, m);
308         cxgb_mbufs_outstanding += mbuf_outstanding;
309         cxgb_pack_outstanding += pack_outstanding;
310         return (0);
311 err_out:
312         m_freem(*m);
313         *m = NULL;      
314         return (err);
315 }
316
317 int 
318 busdma_map_sg_vec(struct mbuf **m, struct mbuf **mret, bus_dma_segment_t *segs, int count)
319 {
320         struct mbuf *m0, **mp;
321         struct mbuf_iovec *mi;
322         struct mbuf_vec *mv;
323         int i;
324         
325         if (count > MAX_MIOVEC_IOV) {
326                 if ((m0 = uma_zalloc_arg(zone_clust, NULL, M_NOWAIT)) == NULL) 
327                         return (ENOMEM);
328                 m0->m_type = EXT_CLIOVEC;
329         } else {
330                 if ((m0 = uma_zalloc_arg(zone_miovec, NULL, M_NOWAIT)) == NULL)
331                         return (ENOMEM);
332                 m0->m_type = EXT_IOVEC;
333         }
334
335         m0->m_flags = 0;
336         m0->m_pkthdr.len = m0->m_len = (*m)->m_len; /* not the real length but needs to be non-zero */
337         mv = mtomv(m0);
338         mv->mv_count = count;
339         mv->mv_first = 0;
340         for (mp = m, i = 0, mi = mv->mv_vec; i < count; mp++, segs++, mi++, i++) {
341                 if ((*mp)->m_flags & M_PKTHDR && !SLIST_EMPTY(&(*mp)->m_pkthdr.tags)) 
342                         m_tag_delete_chain(*mp, NULL);
343                 busdma_map_mbuf_fast(*mp, segs);
344                 _mcl_collapse_mbuf(mi, *mp);
345                 KASSERT(mi->mi_len, ("empty packet"));
346         }
347
348         for (mp = m, i = 0; i < count; i++, mp++) {
349                 (*mp)->m_next = (*mp)->m_nextpkt = NULL;
350                 if (((*mp)->m_flags & (M_EXT|M_NOFREE)) == M_EXT) {
351                         (*mp)->m_flags &= ~M_EXT;
352                         cxgb_mbufs_outstanding--;
353                         m_free(*mp);
354                 }
355         }
356
357         *mret = m0;
358         return (0);
359 }
360
361 void
362 mb_free_ext_fast(struct mbuf_iovec *mi, int type, int idx)
363 {
364         int dofree;
365         caddr_t cl;
366         
367         if (type == EXT_PACKET) {
368                 cxgb_pack_outstanding--;
369                 m_free(mi->mi_mbuf);
370                 return;
371         }
372
373         /* Account for lazy ref count assign. */
374         dofree = (mi->mi_refcnt == NULL);
375         if (dofree == 0) {
376                     KASSERT(mi->mi_type != EXT_MBUF,
377                         ("refcnt must be null for mbuf"));
378                     if (*(mi->mi_refcnt) == 1 ||
379                     atomic_fetchadd_int(mi->mi_refcnt, -1) == 1)
380                             dofree = 1;
381         }
382         if (dofree == 0)
383                 return;
384
385         cl = mi->mi_base;
386         switch (type) {
387         case EXT_MBUF:
388                 KASSERT((mi->mi_flags & M_NOFREE) == 0, ("no free set on mbuf"));
389                 cxgb_mbufs_outstanding--;
390                 m_free_fast((struct mbuf *)cl);
391                 break;
392         case EXT_CLUSTER:
393                 cxgb_cache_put(zone_clust, cl);
394                 break;          
395         case EXT_JUMBOP:
396                 cxgb_cache_put(zone_jumbop, cl);
397                 break;          
398         case EXT_JUMBO9:
399                 cxgb_cache_put(zone_jumbo9, cl);
400                 break;          
401         case EXT_JUMBO16:
402                 cxgb_cache_put(zone_jumbo16, cl);
403                 break;
404         case EXT_SFBUF:
405         case EXT_NET_DRV:
406         case EXT_MOD_TYPE:
407         case EXT_DISPOSABLE:
408                 *(mi->mi_refcnt) = 0;
409                 uma_zfree(zone_ext_refcnt, __DEVOLATILE(u_int *,
410                         mi->mi_ext.ref_cnt));
411                 /* FALLTHROUGH */
412         case EXT_EXTREF:
413                 KASSERT(mi->mi_ext.ext_free != NULL,
414                     ("%s: ext_free not set", __func__));
415 #if __FreeBSD_version >= 800016
416                 (*(mi->mi_ext.ext_free))(mi->mi_ext.ext_arg1,
417                     mi->mi_ext.ext_arg2);
418 #else
419                 (*(mi->mi_ext.ext_free))(mi->mi_ext.ext_buf,
420                     mi->mi_ext.ext_args);
421 #endif
422                 break;
423         default:
424                 dump_mi(mi);
425                 panic("unknown mv type in m_free_vec type=%d idx=%d", type, idx);
426                 break;
427         }
428 }
429
430 int
431 _m_explode(struct mbuf *m)
432 {
433         panic("IMPLEMENT ME!!!");
434 }
435         
436