xref: /csrg-svn/sys/kern/uipc_mbuf.c (revision 36781)
1 /*
2  * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  *	@(#)uipc_mbuf.c	7.4.1.3 (Berkeley) 02/15/89
18  */
19 
20 #include "../machine/pte.h"
21 
22 #include "param.h"
23 #include "dir.h"
24 #include "user.h"
25 #include "proc.h"
26 #include "cmap.h"
27 #include "map.h"
28 #include "mbuf.h"
29 #include "vm.h"
30 #include "kernel.h"
31 #include "syslog.h"
32 #include "domain.h"
33 #include "protosw.h"
34 
35 mbinit()
36 {
37 	int s;
38 
39 #if CLBYTES < 4096
40 #define NCL_INIT	(4096/CLBYTES)
41 #else
42 #define NCL_INIT	1
43 #endif
44 	s = splimp();
45 	if (m_clalloc(NCL_INIT, MPG_MBUFS, M_DONTWAIT) == 0)
46 		goto bad;
47 	if (m_clalloc(NCL_INIT, MPG_CLUSTERS, M_DONTWAIT) == 0)
48 		goto bad;
49 	splx(s);
50 	return;
51 bad:
52 	panic("mbinit");
53 }
54 
55 /*
56  * Must be called at splimp.
57  */
58 /* ARGSUSED */
59 caddr_t
60 m_clalloc(ncl, how, canwait)
61 	register int ncl;
62 	int how;
63 {
64 	int npg, mbx;
65 	register struct mbuf *m;
66 	register int i;
67 	static int logged;
68 
69 	npg = ncl * CLSIZE;
70 	mbx = rmalloc(mbmap, (long)npg);
71 	if (mbx == 0) {
72 		if (logged == 0) {
73 			logged++;
74 			log(LOG_ERR, "mbuf map full\n");
75 		}
76 		return (0);
77 	}
78 	m = cltom(mbx * NBPG / MCLBYTES);
79 	if (memall(&Mbmap[mbx], npg, proc, CSYS) == 0) {
80 		rmfree(mbmap, (long)npg, (long)mbx);
81 		return (0);
82 	}
83 	vmaccess(&Mbmap[mbx], (caddr_t)m, npg);
84 	switch (how) {
85 
86 	case MPG_CLUSTERS:
87 		ncl = ncl * CLBYTES / MCLBYTES;
88 		for (i = 0; i < ncl; i++) {
89 			m->m_off = 0;
90 			m->m_next = mclfree;
91 			mclfree = m;
92 			m += MCLBYTES / sizeof (*m);
93 			mbstat.m_clfree++;
94 		}
95 		mbstat.m_clusters += ncl;
96 		break;
97 
98 	case MPG_MBUFS:
99 		for (i = ncl * CLBYTES / sizeof (*m); i > 0; i--) {
100 			m->m_off = 0;
101 			m->m_type = MT_DATA;
102 			mbstat.m_mtypes[MT_DATA]++;
103 			mbstat.m_mbufs++;
104 			(void) m_free(m);
105 			m++;
106 		}
107 		break;
108 
109 	case MPG_SPACE:
110 		mbstat.m_space++;
111 		break;
112 	}
113 	return ((caddr_t)m);
114 }
115 
116 /*
117  * Must be called at splimp.
118  */
119 m_expand(canwait)
120 	int canwait;
121 {
122 	register struct domain *dp;
123 	register struct protosw *pr;
124 	int tries;
125 
126 	for (tries = 0;; ) {
127 		if (m_clalloc(1, MPG_MBUFS, canwait))
128 			return (1);
129 		if (canwait == 0 || tries++)
130 			return (0);
131 
132 		/* ask protocols to free space */
133 		for (dp = domains; dp; dp = dp->dom_next)
134 			for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW;
135 			    pr++)
136 				if (pr->pr_drain)
137 					(*pr->pr_drain)();
138 		mbstat.m_drain++;
139 	}
140 }
141 
142 /* NEED SOME WAY TO RELEASE SPACE */
143 
144 /*
145  * Space allocation routines.
146  * These are also available as macros
147  * for critical paths.
148  */
149 struct mbuf *
150 m_get(canwait, type)
151 	int canwait, type;
152 {
153 	register struct mbuf *m;
154 
155 	MGET(m, canwait, type);
156 	return (m);
157 }
158 
159 struct mbuf *
160 m_getclr(canwait, type)
161 	int canwait, type;
162 {
163 	register struct mbuf *m;
164 
165 	MGET(m, canwait, type);
166 	if (m == 0)
167 		return (0);
168 	bzero(mtod(m, caddr_t), MLEN);
169 	return (m);
170 }
171 
172 struct mbuf *
173 m_free(m)
174 	struct mbuf *m;
175 {
176 	register struct mbuf *n;
177 
178 	MFREE(m, n);
179 	return (n);
180 }
181 
182 /*
183  * Get more mbufs; called from MGET macro if mfree list is empty.
184  * Must be called at splimp.
185  */
186 /*ARGSUSED*/
187 struct mbuf *
188 m_more(canwait, type)
189 	int canwait, type;
190 {
191 	register struct mbuf *m;
192 
193 	while (m_expand(canwait) == 0) {
194 		if (canwait == M_WAIT) {
195 			mbstat.m_wait++;
196 			m_want++;
197 			sleep((caddr_t)&mfree, PZERO - 1);
198 			if (mfree)
199 				break;
200 		} else {
201 			mbstat.m_drops++;
202 			return (NULL);
203 		}
204 	}
205 #define m_more(x,y) (panic("m_more"), (struct mbuf *)0)
206 	MGET(m, canwait, type);
207 #undef m_more
208 	return (m);
209 }
210 
211 m_freem(m)
212 	register struct mbuf *m;
213 {
214 	register struct mbuf *n;
215 	register int s;
216 
217 	if (m == NULL)
218 		return;
219 	s = splimp();
220 	do {
221 		MFREE(m, n);
222 	} while (m = n);
223 	splx(s);
224 }
225 
226 /*
227  * Mbuffer utility routines.
228  */
229 
230 /*
231 /*
232  * Make a copy of an mbuf chain starting "off" bytes from the beginning,
233  * continuing for "len" bytes.  If len is M_COPYALL, copy to end of mbuf.
234  * Should get M_WAIT/M_DONTWAIT from caller.
235  */
236 struct mbuf *
237 m_copy(m, off, len)
238 	register struct mbuf *m;
239 	int off;
240 	register int len;
241 {
242 	register struct mbuf *n, **np;
243 	struct mbuf *top, *p;
244 
245 	if (len == 0)
246 		return (0);
247 	if (off < 0 || len < 0)
248 		panic("m_copy");
249 	while (off > 0) {
250 		if (m == 0)
251 			panic("m_copy");
252 		if (off < m->m_len)
253 			break;
254 		off -= m->m_len;
255 		m = m->m_next;
256 	}
257 	np = &top;
258 	top = 0;
259 	while (len > 0) {
260 		if (m == 0) {
261 			if (len != M_COPYALL)
262 				panic("m_copy");
263 			break;
264 		}
265 		MGET(n, M_DONTWAIT, m->m_type);
266 		*np = n;
267 		if (n == 0)
268 			goto nospace;
269 		n->m_len = MIN(len, m->m_len - off);
270 		if (m->m_off > MMAXOFF) {
271 			p = mtod(m, struct mbuf *);
272 			n->m_off = ((int)p - (int)n) + off;
273 			mclrefcnt[mtocl(p)]++;
274 		} else
275 			bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t),
276 			    (unsigned)n->m_len);
277 		if (len != M_COPYALL)
278 			len -= n->m_len;
279 		off = 0;
280 		m = m->m_next;
281 		np = &n->m_next;
282 	}
283 	return (top);
284 nospace:
285 	m_freem(top);
286 	return (0);
287 }
288 
289 /*
290  * Copy data from an mbuf chain starting "off" bytes from the beginning,
291  * continuing for "len" bytes, into the indicated buffer.
292  */
293 m_copydata(m, off, len, cp)
294 	register struct mbuf *m;
295 	register int off;
296 	register int len;
297 	caddr_t cp;
298 {
299 	register unsigned count;
300 
301 	if (off < 0 || len < 0)
302 		panic("m_copydata");
303 	while (off > 0) {
304 		if (m == 0)
305 			panic("m_copydata");
306 		if (off < m->m_len)
307 			break;
308 		off -= m->m_len;
309 		m = m->m_next;
310 	}
311 	while (len > 0) {
312 		if (m == 0)
313 			panic("m_copydata");
314 		count = MIN(m->m_len - off, len);
315 		bcopy(mtod(m, caddr_t) + off, cp, count);
316 		len -= count;
317 		cp += count;
318 		off = 0;
319 		m = m->m_next;
320 	}
321 }
322 
323 m_cat(m, n)
324 	register struct mbuf *m, *n;
325 {
326 	while (m->m_next)
327 		m = m->m_next;
328 	while (n) {
329 		if (m->m_off >= MMAXOFF ||
330 		    m->m_off + m->m_len + n->m_len > MMAXOFF) {
331 			/* just join the two chains */
332 			m->m_next = n;
333 			return;
334 		}
335 		/* splat the data from one into the other */
336 		bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
337 		    (u_int)n->m_len);
338 		m->m_len += n->m_len;
339 		n = m_free(n);
340 	}
341 }
342 
343 m_adj(mp, len)
344 	struct mbuf *mp;
345 	register int len;
346 {
347 	register struct mbuf *m;
348 	register count;
349 
350 	if ((m = mp) == NULL)
351 		return;
352 	if (len >= 0) {
353 		while (m != NULL && len > 0) {
354 			if (m->m_len <= len) {
355 				len -= m->m_len;
356 				m->m_len = 0;
357 				m = m->m_next;
358 			} else {
359 				m->m_len -= len;
360 				m->m_off += len;
361 				break;
362 			}
363 		}
364 	} else {
365 		/*
366 		 * Trim from tail.  Scan the mbuf chain,
367 		 * calculating its length and finding the last mbuf.
368 		 * If the adjustment only affects this mbuf, then just
369 		 * adjust and return.  Otherwise, rescan and truncate
370 		 * after the remaining size.
371 		 */
372 		len = -len;
373 		count = 0;
374 		for (;;) {
375 			count += m->m_len;
376 			if (m->m_next == (struct mbuf *)0)
377 				break;
378 			m = m->m_next;
379 		}
380 		if (m->m_len >= len) {
381 			m->m_len -= len;
382 			return;
383 		}
384 		count -= len;
385 		/*
386 		 * Correct length for chain is "count".
387 		 * Find the mbuf with last data, adjust its length,
388 		 * and toss data from remaining mbufs on chain.
389 		 */
390 		for (m = mp; m; m = m->m_next) {
391 			if (m->m_len >= count) {
392 				m->m_len = count;
393 				break;
394 			}
395 			count -= m->m_len;
396 		}
397 		while (m = m->m_next)
398 			m->m_len = 0;
399 	}
400 }
401 
402 /*
403  * Rearange an mbuf chain so that len bytes are contiguous
404  * and in the data area of an mbuf (so that mtod and dtom
405  * will work for a structure of size len).  Returns the resulting
406  * mbuf chain on success, frees it and returns null on failure.
407  * If there is room, it will add up to MPULL_EXTRA bytes to the
408  * contiguous region in an attempt to avoid being called next time.
409  */
410 struct mbuf *
411 m_pullup(n, len)
412 	register struct mbuf *n;
413 	int len;
414 {
415 	register struct mbuf *m;
416 	register int count;
417 	int space;
418 
419 	if (n->m_off + len <= MMAXOFF && n->m_next) {
420 		m = n;
421 		n = n->m_next;
422 		len -= m->m_len;
423 	} else {
424 		if (len > MLEN)
425 			goto bad;
426 		MGET(m, M_DONTWAIT, n->m_type);
427 		if (m == 0)
428 			goto bad;
429 		m->m_len = 0;
430 	}
431 	space = MMAXOFF - m->m_off;
432 	do {
433 		count = MIN(MIN(space - m->m_len, len + MPULL_EXTRA), n->m_len);
434 		bcopy(mtod(n, caddr_t), mtod(m, caddr_t)+m->m_len,
435 		  (unsigned)count);
436 		len -= count;
437 		m->m_len += count;
438 		n->m_len -= count;
439 		if (n->m_len)
440 			n->m_off += count;
441 		else
442 			n = m_free(n);
443 	} while (len > 0 && n);
444 	if (len > 0) {
445 		(void) m_free(m);
446 		goto bad;
447 	}
448 	m->m_next = n;
449 	return (m);
450 bad:
451 	m_freem(n);
452 	return (0);
453 }
454