source: rtems/cpukit/libnetworking/kern/uipc_mbuf.c @ 11cdbeb

4.104.114.84.95
Last change on this file since 11cdbeb was 11cdbeb, checked in by Joel Sherrill <joel.sherrill@…>, on May 27, 1999 at 6:03:50 PM

Patch from Eric Norum <eric@…> to eliminate a panic when the
network stack runs out of mbufs.

  • Property mode set to 100644
File size: 15.3 KB
Line 
1/*
2 * Copyright (c) 1982, 1986, 1988, 1991, 1993
3 *      The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *      This product includes software developed by the University of
16 *      California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 *      @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94
34 * $Id$
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/proc.h>
40#include <sys/malloc.h>
41#define MBTYPES
42#include <sys/mbuf.h>
43#include <sys/kernel.h>
44#include <sys/syslog.h>
45#include <sys/domain.h>
46#include <sys/protosw.h>
47
48#include <vm/vm.h>
49#include <vm/vm_param.h>
50#include <vm/vm_kern.h>
51#include <vm/vm_extern.h>
52
53static void mbinit __P((void *))  __attribute__ ((unused));
54SYSINIT(mbuf, SI_SUB_MBUF, SI_ORDER_FIRST, mbinit, NULL)
55
56struct mbuf *mbutl;
57char    *mclrefcnt;
58struct mbstat mbstat;
59struct mbuf *mmbfree;
60union mcluster *mclfree;
61int     max_linkhdr;
62int     max_protohdr;
63int     max_hdr;
64int     max_datalen;
65
66/* "number of clusters of pages" */
67#define NCL_INIT        1
68
69#define NMB_INIT        16
70
71/*
72 * When MGET failes, ask protocols to free space when short of memory,
73 * then re-attempt to allocate an mbuf.
74 */
75struct mbuf *
76m_retry(i, t)
77        int i, t;
78{
79        register struct mbuf *m;
80
81        m_reclaim();
82#define m_retry(i, t)   (struct mbuf *)0
83        MGET(m, i, t);
84#undef m_retry
85        if (m != NULL)
86                mbstat.m_wait++;
87        else
88                mbstat.m_drops++;
89        return (m);
90}
91
92/*
93 * As above; retry an MGETHDR.
94 */
95struct mbuf *
96m_retryhdr(i, t)
97        int i, t;
98{
99        register struct mbuf *m;
100
101        m_reclaim();
102#define m_retryhdr(i, t) (struct mbuf *)0
103        MGETHDR(m, i, t);
104#undef m_retryhdr
105        if (m != NULL)
106                mbstat.m_wait++;
107        else
108                mbstat.m_drops++;
109        return (m);
110}
111
112void
113m_reclaim(void)
114{
115        register struct domain *dp;
116        register struct protosw *pr;
117        int s = splimp();
118
119        for (dp = domains; dp; dp = dp->dom_next)
120                for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
121                        if (pr->pr_drain)
122                                (*pr->pr_drain)();
123        splx(s);
124        mbstat.m_drain++;
125}
126
127/*
128 * Space allocation routines.
129 * These are also available as macros
130 * for critical paths.
131 */
132struct mbuf *
133m_get(nowait, type)
134        int nowait, type;
135{
136        register struct mbuf *m;
137
138        MGET(m, nowait, type);
139        return (m);
140}
141
142struct mbuf *
143m_gethdr(nowait, type)
144        int nowait, type;
145{
146        register struct mbuf *m;
147
148        MGETHDR(m, nowait, type);
149        return (m);
150}
151
152struct mbuf *
153m_getclr(nowait, type)
154        int nowait, type;
155{
156        register struct mbuf *m;
157
158        MGET(m, nowait, type);
159        if (m == 0)
160                return (0);
161        bzero(mtod(m, caddr_t), MLEN);
162        return (m);
163}
164
165struct mbuf *
166m_free(m)
167        struct mbuf *m;
168{
169        register struct mbuf *n;
170
171        MFREE(m, n);
172        return (n);
173}
174
175void
176m_freem(m)
177        register struct mbuf *m;
178{
179        register struct mbuf *n;
180
181        if (m == NULL)
182                return;
183        do {
184                MFREE(m, n);
185                m = n;
186        } while (m);
187}
188
189/*
190 * Mbuffer utility routines.
191 */
192
193/*
194 * Lesser-used path for M_PREPEND:
195 * allocate new mbuf to prepend to chain,
196 * copy junk along.
197 */
198struct mbuf *
199m_prepend(m, len, how)
200        register struct mbuf *m;
201        int len, how;
202{
203        struct mbuf *mn;
204
205        MGET(mn, how, m->m_type);
206        if (mn == (struct mbuf *)NULL) {
207                m_freem(m);
208                return ((struct mbuf *)NULL);
209        }
210        if (m->m_flags & M_PKTHDR) {
211                M_COPY_PKTHDR(mn, m);
212                m->m_flags &= ~M_PKTHDR;
213        }
214        mn->m_next = m;
215        m = mn;
216        if (len < MHLEN)
217                MH_ALIGN(m, len);
218        m->m_len = len;
219        return (m);
220}
221
222/*
223 * Make a copy of an mbuf chain starting "off0" bytes from the beginning,
224 * continuing for "len" bytes.  If len is M_COPYALL, copy to end of mbuf.
225 * The wait parameter is a choice of M_WAIT/M_DONTWAIT from caller.
226 */
227static int MCFail;
228
229struct mbuf *
230m_copym(m, off0, len, wait)
231        register struct mbuf *m;
232        int off0, wait;
233        register int len;
234{
235        register struct mbuf *n, **np;
236        register int off = off0;
237        struct mbuf *top;
238        int copyhdr = 0;
239
240        if (off < 0 || len < 0)
241                panic("m_copym");
242        if (off == 0 && m->m_flags & M_PKTHDR)
243                copyhdr = 1;
244        while (off > 0) {
245                if (m == 0)
246                        panic("m_copym");
247                if (off < m->m_len)
248                        break;
249                off -= m->m_len;
250                m = m->m_next;
251        }
252        np = &top;
253        top = 0;
254        while (len > 0) {
255                if (m == 0) {
256                        if (len != M_COPYALL)
257                                panic("m_copym");
258                        break;
259                }
260                MGET(n, wait, m->m_type);
261                *np = n;
262                if (n == 0)
263                        goto nospace;
264                if (copyhdr) {
265                        M_COPY_PKTHDR(n, m);
266                        if (len == M_COPYALL)
267                                n->m_pkthdr.len -= off0;
268                        else
269                                n->m_pkthdr.len = len;
270                        copyhdr = 0;
271                }
272                n->m_len = min(len, m->m_len - off);
273                if (m->m_flags & M_EXT) {
274                        n->m_data = m->m_data + off;
275                        if(!m->m_ext.ext_ref)
276                                mclrefcnt[mtocl(m->m_ext.ext_buf)]++;
277                        else
278                                (*(m->m_ext.ext_ref))(m->m_ext.ext_buf,
279                                                        m->m_ext.ext_size);
280                        n->m_ext = m->m_ext;
281                        n->m_flags |= M_EXT;
282                } else
283                        bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t),
284                            (unsigned)n->m_len);
285                if (len != M_COPYALL)
286                        len -= n->m_len;
287                off = 0;
288                m = m->m_next;
289                np = &n->m_next;
290        }
291        if (top == 0)
292                MCFail++;
293        return (top);
294nospace:
295        m_freem(top);
296        MCFail++;
297        return (0);
298}
299
300/*
301 * Copy an entire packet, including header (which must be present).
302 * An optimization of the common case `m_copym(m, 0, M_COPYALL, how)'.
303 */
304struct mbuf *
305m_copypacket(m, how)
306        struct mbuf *m;
307        int how;
308{
309        struct mbuf *top, *n, *o;
310
311        MGET(n, how, m->m_type);
312        top = n;
313        if (!n)
314                goto nospace;
315
316        M_COPY_PKTHDR(n, m);
317        n->m_len = m->m_len;
318        if (m->m_flags & M_EXT) {
319                n->m_data = m->m_data;
320                mclrefcnt[mtocl(m->m_ext.ext_buf)]++;
321                n->m_ext = m->m_ext;
322                n->m_flags |= M_EXT;
323        } else {
324                bcopy(mtod(m, char *), mtod(n, char *), n->m_len);
325        }
326
327        m = m->m_next;
328        while (m) {
329                MGET(o, how, m->m_type);
330                if (!o)
331                        goto nospace;
332
333                n->m_next = o;
334                n = n->m_next;
335
336                n->m_len = m->m_len;
337                if (m->m_flags & M_EXT) {
338                        n->m_data = m->m_data;
339                        mclrefcnt[mtocl(m->m_ext.ext_buf)]++;
340                        n->m_ext = m->m_ext;
341                        n->m_flags |= M_EXT;
342                } else {
343                        bcopy(mtod(m, char *), mtod(n, char *), n->m_len);
344                }
345
346                m = m->m_next;
347        }
348        return top;
349nospace:
350        m_freem(top);
351        MCFail++;
352        return 0;
353}
354
355/*
356 * Copy data from an mbuf chain starting "off" bytes from the beginning,
357 * continuing for "len" bytes, into the indicated buffer.
358 */
359void
360m_copydata(m, off, len, cp)
361        register struct mbuf *m;
362        register int off;
363        register int len;
364        caddr_t cp;
365{
366        register unsigned count;
367
368        if (off < 0 || len < 0)
369                panic("m_copydata");
370        while (off > 0) {
371                if (m == 0)
372                        panic("m_copydata");
373                if (off < m->m_len)
374                        break;
375                off -= m->m_len;
376                m = m->m_next;
377        }
378        while (len > 0) {
379                if (m == 0)
380                        panic("m_copydata");
381                count = min(m->m_len - off, len);
382                bcopy(mtod(m, caddr_t) + off, cp, count);
383                len -= count;
384                cp += count;
385                off = 0;
386                m = m->m_next;
387        }
388}
389
390/*
391 * Concatenate mbuf chain n to m.
392 * Both chains must be of the same type (e.g. MT_DATA).
393 * Any m_pkthdr is not updated.
394 */
395void
396m_cat(m, n)
397        register struct mbuf *m, *n;
398{
399        while (m->m_next)
400                m = m->m_next;
401        while (n) {
402                if (m->m_flags & M_EXT ||
403                    m->m_data + m->m_len + n->m_len >= &m->m_dat[MLEN]) {
404                        /* just join the two chains */
405                        m->m_next = n;
406                        return;
407                }
408                /* splat the data from one into the other */
409                bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
410                    (u_int)n->m_len);
411                m->m_len += n->m_len;
412                n = m_free(n);
413        }
414}
415
416void
417m_adj(mp, req_len)
418        struct mbuf *mp;
419        int req_len;
420{
421        register int len = req_len;
422        register struct mbuf *m;
423        register int count;
424
425        if ((m = mp) == NULL)
426                return;
427        if (len >= 0) {
428                /*
429                 * Trim from head.
430                 */
431                while (m != NULL && len > 0) {
432                        if (m->m_len <= len) {
433                                len -= m->m_len;
434                                m->m_len = 0;
435                                m = m->m_next;
436                        } else {
437                                m->m_len -= len;
438                                m->m_data += len;
439                                len = 0;
440                        }
441                }
442                m = mp;
443                if (mp->m_flags & M_PKTHDR)
444                        m->m_pkthdr.len -= (req_len - len);
445        } else {
446                /*
447                 * Trim from tail.  Scan the mbuf chain,
448                 * calculating its length and finding the last mbuf.
449                 * If the adjustment only affects this mbuf, then just
450                 * adjust and return.  Otherwise, rescan and truncate
451                 * after the remaining size.
452                 */
453                len = -len;
454                count = 0;
455                for (;;) {
456                        count += m->m_len;
457                        if (m->m_next == (struct mbuf *)0)
458                                break;
459                        m = m->m_next;
460                }
461                if (m->m_len >= len) {
462                        m->m_len -= len;
463                        if (mp->m_flags & M_PKTHDR)
464                                mp->m_pkthdr.len -= len;
465                        return;
466                }
467                count -= len;
468                if (count < 0)
469                        count = 0;
470                /*
471                 * Correct length for chain is "count".
472                 * Find the mbuf with last data, adjust its length,
473                 * and toss data from remaining mbufs on chain.
474                 */
475                m = mp;
476                if (m->m_flags & M_PKTHDR)
477                        m->m_pkthdr.len = count;
478                for (; m; m = m->m_next) {
479                        if (m->m_len >= count) {
480                                m->m_len = count;
481                                break;
482                        }
483                        count -= m->m_len;
484                }
485                while (m->m_next)
486                        (m = m->m_next) ->m_len = 0;
487        }
488}
489
490/*
491 * Rearange an mbuf chain so that len bytes are contiguous
492 * and in the data area of an mbuf (so that mtod and dtom
493 * will work for a structure of size len).  Returns the resulting
494 * mbuf chain on success, frees it and returns null on failure.
495 * If there is room, it will add up to max_protohdr-len extra bytes to the
496 * contiguous region in an attempt to avoid being called next time.
497 */
498static int MPFail;
499
500struct mbuf *
501m_pullup(n, len)
502        register struct mbuf *n;
503        int len;
504{
505        register struct mbuf *m;
506        register int count;
507        int space;
508
509        /*
510         * If first mbuf has no cluster, and has room for len bytes
511         * without shifting current data, pullup into it,
512         * otherwise allocate a new mbuf to prepend to the chain.
513         */
514        if ((n->m_flags & M_EXT) == 0 &&
515            n->m_data + len < &n->m_dat[MLEN] && n->m_next) {
516                if (n->m_len >= len)
517                        return (n);
518                m = n;
519                n = n->m_next;
520                len -= m->m_len;
521        } else {
522                if (len > MHLEN)
523                        goto bad;
524                MGET(m, M_DONTWAIT, n->m_type);
525                if (m == 0)
526                        goto bad;
527                m->m_len = 0;
528                if (n->m_flags & M_PKTHDR) {
529                        M_COPY_PKTHDR(m, n);
530                        n->m_flags &= ~M_PKTHDR;
531                }
532        }
533        space = &m->m_dat[MLEN] - (m->m_data + m->m_len);
534        do {
535                count = min(min(max(len, max_protohdr), space), n->m_len);
536                bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
537                  (unsigned)count);
538                len -= count;
539                m->m_len += count;
540                n->m_len -= count;
541                space -= count;
542                if (n->m_len)
543                        n->m_data += count;
544                else
545                        n = m_free(n);
546        } while (len > 0 && n);
547        if (len > 0) {
548                (void) m_free(m);
549                goto bad;
550        }
551        m->m_next = n;
552        return (m);
553bad:
554        m_freem(n);
555        MPFail++;
556        return (0);
557}
558
559/*
560 * Partition an mbuf chain in two pieces, returning the tail --
561 * all but the first len0 bytes.  In case of failure, it returns NULL and
562 * attempts to restore the chain to its original state.
563 */
564struct mbuf *
565m_split(m0, len0, wait)
566        register struct mbuf *m0;
567        int len0, wait;
568{
569        register struct mbuf *m, *n;
570        unsigned len = len0, remain;
571
572        for (m = m0; m && len > m->m_len; m = m->m_next)
573                len -= m->m_len;
574        if (m == 0)
575                return (0);
576        remain = m->m_len - len;
577        if (m0->m_flags & M_PKTHDR) {
578                MGETHDR(n, wait, m0->m_type);
579                if (n == 0)
580                        return (0);
581                n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif;
582                n->m_pkthdr.len = m0->m_pkthdr.len - len0;
583                m0->m_pkthdr.len = len0;
584                if (m->m_flags & M_EXT)
585                        goto extpacket;
586                if (remain > MHLEN) {
587                        /* m can't be the lead packet */
588                        MH_ALIGN(n, 0);
589                        n->m_next = m_split(m, len, wait);
590                        if (n->m_next == 0) {
591                                (void) m_free(n);
592                                return (0);
593                        } else
594                                return (n);
595                } else
596                        MH_ALIGN(n, remain);
597        } else if (remain == 0) {
598                n = m->m_next;
599                m->m_next = 0;
600                return (n);
601        } else {
602                MGET(n, wait, m->m_type);
603                if (n == 0)
604                        return (0);
605                M_ALIGN(n, remain);
606        }
607extpacket:
608        if (m->m_flags & M_EXT) {
609                n->m_flags |= M_EXT;
610                n->m_ext = m->m_ext;
611                if(!m->m_ext.ext_ref)
612                        mclrefcnt[mtocl(m->m_ext.ext_buf)]++;
613                else
614                        (*(m->m_ext.ext_ref))(m->m_ext.ext_buf,
615                                                m->m_ext.ext_size);
616                m->m_ext.ext_size = 0; /* For Accounting XXXXXX danger */
617                n->m_data = m->m_data + len;
618        } else {
619                bcopy(mtod(m, caddr_t) + len, mtod(n, caddr_t), remain);
620        }
621        n->m_len = remain;
622        m->m_len = len;
623        n->m_next = m->m_next;
624        m->m_next = 0;
625        return (n);
626}
627/*
628 * Routine to copy from device local memory into mbufs.
629 */
630struct mbuf *
631m_devget(buf, totlen, off0, ifp, copy)
632        char *buf;
633        int totlen, off0;
634        struct ifnet *ifp;
635        void (*copy) __P((char *from, caddr_t to, u_int len));
636{
637        register struct mbuf *m;
638        struct mbuf *top = 0, **mp = &top;
639        register int off = off0, len;
640        register char *cp;
641        char *epkt;
642
643        cp = buf;
644        epkt = cp + totlen;
645        if (off) {
646                cp += off + 2 * sizeof(u_short);
647                totlen -= 2 * sizeof(u_short);
648        }
649        MGETHDR(m, M_DONTWAIT, MT_DATA);
650        if (m == 0)
651                return (0);
652        m->m_pkthdr.rcvif = ifp;
653        m->m_pkthdr.len = totlen;
654        m->m_len = MHLEN;
655
656        while (totlen > 0) {
657                if (top) {
658                        MGET(m, M_DONTWAIT, MT_DATA);
659                        if (m == 0) {
660                                m_freem(top);
661                                return (0);
662                        }
663                        m->m_len = MLEN;
664                }
665                len = min(totlen, epkt - cp);
666                if (len >= MINCLSIZE) {
667                        MCLGET(m, M_DONTWAIT);
668                        if (m->m_flags & M_EXT)
669                                m->m_len = len = min(len, MCLBYTES);
670                        else
671                                len = m->m_len;
672                } else {
673                        /*
674                         * Place initial small packet/header at end of mbuf.
675                         */
676                        if (len < m->m_len) {
677                                if (top == 0 && len + max_linkhdr <= m->m_len)
678                                        m->m_data += max_linkhdr;
679                                m->m_len = len;
680                        } else
681                                len = m->m_len;
682                }
683                if (copy)
684                        copy(cp, mtod(m, caddr_t), (unsigned)len);
685                else
686                        bcopy(cp, mtod(m, caddr_t), (unsigned)len);
687                cp += len;
688                *mp = m;
689                mp = &m->m_next;
690                totlen -= len;
691                if (cp == epkt)
692                        cp = buf;
693        }
694        return (top);
695}
696
697/*
698 * Copy data from a buffer back into the indicated mbuf chain,
699 * starting "off" bytes from the beginning, extending the mbuf
700 * chain if necessary.
701 */
702void
703m_copyback(m0, off, len, cp)
704        struct  mbuf *m0;
705        register int off;
706        register int len;
707        caddr_t cp;
708{
709        register int mlen;
710        register struct mbuf *m = m0, *n;
711        int totlen = 0;
712
713        if (m0 == 0)
714                return;
715        while (off > (mlen = m->m_len)) {
716                off -= mlen;
717                totlen += mlen;
718                if (m->m_next == 0) {
719                        n = m_getclr(M_DONTWAIT, m->m_type);
720                        if (n == 0)
721                                goto out;
722                        n->m_len = min(MLEN, len + off);
723                        m->m_next = n;
724                }
725                m = m->m_next;
726        }
727        while (len > 0) {
728                mlen = min (m->m_len - off, len);
729                bcopy(cp, off + mtod(m, caddr_t), (unsigned)mlen);
730                cp += mlen;
731                len -= mlen;
732                mlen += off;
733                off = 0;
734                totlen += mlen;
735                if (len == 0)
736                        break;
737                if (m->m_next == 0) {
738                        n = m_get(M_DONTWAIT, m->m_type);
739                        if (n == 0)
740                                break;
741                        n->m_len = min(MLEN, len);
742                        m->m_next = n;
743                }
744                m = m->m_next;
745        }
746out:    if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
747                m->m_pkthdr.len = totlen;
748}
Note: See TracBrowser for help on using the repository browser.