|
|
1.1 root 1: /*
2: * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /* IOMBufMemoryCursor.cpp created by gvdl on 1999-1-20 */
23:
24: #include <sys/cdefs.h>
25:
26: __BEGIN_DECLS
27: #include <IOKit/assert.h>
28:
29: #include <sys/param.h>
30: #include <sys/mbuf.h>
31: #include <architecture/byte_order.h>
32: __END_DECLS
33:
34: #include <IOKit/network/IOMBufMemoryCursor.h>
35: #include <IOKit/IOLib.h>
36:
37: #ifndef MIN
38: #define MIN(a,b) (((a)<(b))?(a):(b))
39: #endif /* MIN */
40:
41: #define next_page(x) trunc_page(x + PAGE_SIZE)
42:
43: /* Define the meta class stuff for the entire file here */
44: OSDefineMetaClass(IOMBufMemoryCursor, IOMemoryCursor)
45: OSDefineAbstractStructors(IOMBufMemoryCursor, IOMemoryCursor)
46:
47: OSDefineMetaClassAndStructors(IOMBufNaturalMemoryCursor, IOMBufMemoryCursor)
48: OSDefineMetaClassAndStructors(IOMBufBigMemoryCursor, IOMBufMemoryCursor)
49: OSDefineMetaClassAndStructors(IOMBufLittleMemoryCursor, IOMBufMemoryCursor)
50:
51: #ifdef __ppc__
52: OSDefineMetaClassAndStructors(IOMBufDBDMAMemoryCursor, IOMBufMemoryCursor)
53: #endif /* __ppc__ */
54:
55: /*********************** class IOMBufMemoryCursor ***********************/
56: #define super IOMemoryCursor
57:
58: bool IOMBufMemoryCursor::initWithSpecification(OutputSegmentFunc outSeg,
59: UInt maxSegmentSize,
60: UInt maxTransferSize,
61: UInt align)
62: {
63: return false;
64: }
65:
66: bool IOMBufMemoryCursor::initWithSpecification(OutputSegmentFunc inOutSeg,
67: UInt inMaxSegmentSize,
68: UInt inMaxNumSegments)
69: {
70: if (!super::initWithSpecification(inOutSeg, inMaxSegmentSize, 0, 1))
71: return false;
72:
73: #if 0
74: // It is too confusing to force the max segment size to be at least
75: // as large as a page. Most Enet devices only have 11-12 bit fields,
76: // enough for a full size frame, and also the PAGE_SIZE parameter
77: // may be architecture dependent.
78:
79: assert(inMaxSegmentSize >= PAGE_SIZE);
80: if (inMaxSegmentSize < PAGE_SIZE)
81: return false;
82: #else
83: if (!inMaxSegmentSize)
84: return false;
85: #endif
86:
87: maxSegmentSize = MIN(maxSegmentSize, PAGE_SIZE);
88: maxNumSegments = inMaxNumSegments;
89: coalesceCount = 0;
90:
91: return true;
92: }
93:
94: //
95: // Copy the src packet into the destination packet. The amount to copy is
96: // determined by the dstm->m_len, which is setup by analyseSegments, see below.
97: // The source mbuf is not freed nor modified.
98: //
99: #define BCOPY(s, d, l) do { bcopy((void *) s, (void *) d, l); } while(0)
100:
101: static inline void coalesceSegments(struct mbuf *srcm, struct mbuf *dstm)
102: {
103: vm_offset_t src, dst;
104: SInt srcLen, dstLen;
105: struct mbuf *temp;
106:
107: srcLen = srcm->m_len;
108: src = mtod(srcm, vm_offset_t);
109:
110: dstLen = dstm->m_len;
111: dst = mtod(dstm, vm_offset_t);
112:
113: for (;;) {
114: if (srcLen < dstLen) {
115:
116: // Copy remainder of src mbuf to current dst.
117: BCOPY(src, dst, srcLen);
118: dst += srcLen;
119: dstLen -= srcLen;
120:
121: // Move on to the next source mbuf.
122: temp = srcm->m_next; assert(temp);
123: srcm = temp;
124:
125: srcLen = srcm->m_len;
126: src = mtod(srcm, vm_offset_t);
127: }
128: else if (srcLen > dstLen) {
129:
130: // Copy some of src mbuf to remaining space in dst mbuf.
131: BCOPY(src, dst, dstLen);
132: src += dstLen;
133: srcLen -= dstLen;
134:
135: // Move on to the next destination mbuf.
136: temp = dstm->m_next; assert(temp);
137: dstm = temp;
138:
139: dstLen = dstm->m_len;
140: dst = mtod(dstm, vm_offset_t);
141: }
142: else { /* (srcLen == dstLen) */
143:
144: // copy remainder of src into remaining space of current dst
145: BCOPY(src, dst, srcLen);
146:
147: // Free current mbuf and move the current onto the next
148: srcm = srcm->m_next;
149:
150: // Do we have any data left to copy?
151: if (!dstm->m_next)
152: break;
153: dstm = dstm->m_next;
154:
155: assert(srcm);
156: dstLen = dstm->m_len;
157: dst = mtod(dstm, vm_offset_t);
158: srcLen = srcm->m_len;
159: src = mtod(srcm, vm_offset_t);
160: }
161: }
162: }
163:
164: static const UInt kMBufDataCacheSize = 16;
165:
166: static inline bool analyseSegments(
167: struct mbuf *packet, /* input packet mbuf */
168: const UInt mbufsInCache, /* number of entries in segsPerMBuf[] */
169: const UInt segsPerMBuf[], /* segments required per mbuf */
170: SInt numSegs, /* total number of segments */
171: const UInt maxSegs) /* max controller segments per mbuf */
172: {
173: struct mbuf *newPacket; // output mbuf chain.
174: struct mbuf *out; // current output mbuf link.
175: SInt outSize; // size of current output mbuf link.
176: SInt outSegs; // segments for current output mbuf link.
177: SInt doneSegs; // segments for output mbuf chain.
178: SInt outLen; // remaining length of input buffer.
179:
180: struct mbuf *in = packet; // save the original input packet pointer.
181: UInt inIndex = 0;
182:
183: // Allocate a mbuf (non header mbuf) to begin the output mbuf chain.
184: //
185: MGET(newPacket, M_DONTWAIT, MT_DATA);
186: if (!newPacket) {
187: IOLog("analyseSegments: MGET() 1 error\n");
188: return false;
189: }
190:
191: /* Initialise outgoing packet controls */
192: out = newPacket;
193: outSize = MLEN;
194: doneSegs = outSegs = outLen = 0;
195:
196: // numSegs stores the delta between the total and the max. For each
197: // input mbuf consumed, we decrement numSegs.
198: //
199: numSegs -= maxSegs;
200:
201: // Loop through the input packet mbuf 'in' and construct a new mbuf chain
202: // large enough to make (numSegs + doneSegs + outSegs) less than or
203: // equal to zero.
204: //
205: do {
206: vm_offset_t vmo;
207:
208: outLen += in->m_len;
209:
210: while (outLen > outSize) {
211: // Oh dear the current outgoing length is too big.
212: if (outSize != MCLBYTES) {
213: // Current mbuf is not yet a cluster so promote, then
214: // check for error.
215:
216: MCLGET(out, M_DONTWAIT);
217: if ( !(out->m_flags & M_EXT) ) {
218: IOLog("analyseSegments: MCLGET() error\n");
219: goto bombAnalysis;
220: }
221:
222: outSize = MCLBYTES;
223:
224: continue;
225: }
226:
227: vmo = mtod(out, vm_offset_t);
228: out->m_len = MCLBYTES; /* Fill in target copy size */
229: doneSegs += (round_page(vmo + MCLBYTES) - trunc_page(vmo))
230: / PAGE_SIZE;
231:
232: // If the number of segments of the output chain, plus
233: // the segment for the mbuf we are about to allocate is greater
234: // than maxSegs, then abort.
235: //
236: if (doneSegs + 1 > (int) maxSegs) {
237: IOLog("analyseSegments: maxSegs limit 1 reached! %d %d\n",
238: doneSegs, maxSegs);
239: goto bombAnalysis;
240: }
241:
242: MGET(out->m_next, M_DONTWAIT, MT_DATA);
243: if (!out->m_next) {
244: IOLog("analyseSegments: MGET() error\n");
245: goto bombAnalysis;
246: }
247:
248: out = out->m_next;
249: outSize = MLEN;
250: outLen -= MCLBYTES;
251: }
252:
253: // Compute number of segment in current outgoing mbuf.
254: vmo = mtod(out, vm_offset_t);
255: outSegs = (round_page(vmo + outLen) - trunc_page(vmo)) / PAGE_SIZE;
256: if (doneSegs + outSegs > (int) maxSegs) {
257: IOLog("analyseSegments: maxSegs limit 2 reached! %d %d %d\n",
258: doneSegs, outSegs, maxSegs);
259: goto bombAnalysis;
260: }
261:
262: // Get the number of segments in the current inbuf
263: if (inIndex < mbufsInCache)
264: numSegs -= segsPerMBuf[inIndex]; // Yeah, in cache
265: else {
266: // Hmm, we have to recompute from scratch. Copy code from genPhys.
267: int thisLen = 0, mbufLen;
268:
269: vmo = mtod(in, vm_offset_t);
270: for (mbufLen = in->m_len; mbufLen; mbufLen -= thisLen) {
271: thisLen = MIN(next_page(vmo), vmo + mbufLen) - vmo;
272: vmo += thisLen;
273: numSegs--;
274: }
275: }
276:
277: // Walk the incoming buffer on one.
278: in = in->m_next;
279: inIndex++;
280:
281: // continue looping until the total number of segments has dropped
282: // to an acceptable level, or if we ran out of mbuf links.
283:
284: } while (in && ((numSegs + doneSegs + outSegs) > 0));
285:
286: if ( (int) (numSegs + doneSegs + outSegs) <= 0) { // success
287:
288: out->m_len = outLen; // Set last mbuf with the remaining length.
289:
290: // The amount to copy is determine by the segment length in each
291: // mbuf linked to newPacket. The sum can be smaller than
292: // packet->pkthdr.len;
293: //
294: coalesceSegments(packet, newPacket);
295:
296: // Copy complete.
297:
298: // If 'in' is non zero, then it means that we only need to copy part
299: // of the input packet, beginning at the start. The mbuf chain
300: // beginning at 'in' must be preserved and linked to the new
301: // output packet chain. Everything before 'in', except for the
302: // header mbuf can be freed.
303: //
304: struct mbuf *m = packet->m_next;
305: while (m != in)
306: m = m_free(m);
307:
308: // The initial header mbuf is preserved, its length set to zero, and
309: // linked to the new packet chain.
310:
311: packet->m_len = 0;
312: packet->m_next = newPacket;
313: newPacket->m_next = in;
314:
315: return true;
316: }
317:
318: bombAnalysis:
319:
320: m_freem(newPacket);
321: return false;
322: }
323:
324: UInt IOMBufMemoryCursor::genPhysicalSegments(struct mbuf *packet, void *vector,
325: UInt maxSegs, bool doCoalesce)
326: {
327: bool doneCoalesce = false;
328:
329: if (!packet || !(packet->m_flags & M_PKTHDR))
330: return 0;
331:
332: if (!maxSegs)
333: maxSegs = maxNumSegments;
334: if (!maxSegs)
335: return 0;
336:
337: if ( packet->m_next == 0 )
338: {
339: vm_offset_t src;
340: struct IOPhysicalSegment physSeg;
341: /*
342: * the packet consists of only 1 mbuf
343: * so if the data buffer doesn't span a page boundary
344: * we can take the simple way out
345: */
346: src = mtod(packet, vm_offset_t);
347:
348: if ( trunc_page(src) == trunc_page(src+packet->m_len-1) )
349: {
350: if ((physSeg.location =
351: (IOPhysicalAddress)mcl_to_paddr((char *)src)) == 0)
352: physSeg.location = (IOPhysicalAddress)pmap_extract(kernel_pmap, src);
353: if (!physSeg.location)
354: return 0;
355: physSeg.length = packet->m_len;
356: (*outSeg)(physSeg, vector, 0);
357:
358: return 1;
359: }
360: }
361:
362: if ( doCoalesce == true && maxSegs == 1 )
363: {
364: vm_offset_t src;
365: vm_offset_t dst;
366: struct mbuf *m;
367: struct mbuf *mnext;
368: struct mbuf *out;
369: UInt len = 0;
370: struct IOPhysicalSegment physSeg;
371:
372: m = packet;
373:
374: MGET(out, M_DONTWAIT, MT_DATA);
375: if ( out == 0 ) return 0;
376:
377: MCLGET(out, M_DONTWAIT);
378: if ( !(out->m_flags & M_EXT) )
379: {
380: m_free( out );
381: return 0;
382: }
383: dst = mtod(out, vm_offset_t);
384:
385: do
386: {
387: src = mtod(m, vm_offset_t);
388: BCOPY( src, dst, m->m_len );
389: dst += m->m_len;
390: len += m->m_len;
391: } while ( (m = m->m_next) != 0 );
392:
393: out->m_len = len;
394:
395: dst = mtod(out, vm_offset_t);
396: if ((physSeg.location = (IOPhysicalAddress)mcl_to_paddr((char *)dst)) == 0)
397: physSeg.location = (IOPhysicalAddress)pmap_extract(kernel_pmap, dst);
398: if (!physSeg.location)
399: return 0;
400: physSeg.length = out->m_len;
401: (*outSeg)(physSeg, vector, 0);
402:
403: m = packet->m_next;
404: while (m != 0)
405: {
406: mnext = m->m_next;
407: m_free(m);
408: m = mnext;
409: }
410:
411: // The initial header mbuf is preserved, its length set to zero, and
412: // linked to the new packet chain.
413:
414: packet->m_len = 0;
415: packet->m_next = out;
416: out->m_next = 0;
417:
418: return 1;
419: }
420:
421:
422: //
423: // Iterate over the mbuf, translating segments were allowed. When we
424: // are not allowed to translate segments then accumulate segment
425: // statistics up to kMBufDataCacheSize of mbufs. Finally
426: // if we overflow our cache just count how many segments this
427: // packet represents.
428: //
429: UInt segsPerMBuf[kMBufDataCacheSize];
430:
431: tryAgain:
432: UInt curMBufIndex = 0;
433: UInt curSegIndex = 0;
434: UInt lastSegCount = 0;
435: struct mbuf *m = packet;
436:
437: // For each mbuf in incoming packet.
438: do {
439: vm_size_t mbufLen, thisLen = 0;
440: vm_offset_t src;
441:
442: // Step through each segment in the current mbuf
443: for (mbufLen = m->m_len, src = mtod(m, vm_offset_t);
444: mbufLen;
445: src += thisLen, mbufLen -= thisLen)
446: {
447: // If maxSegmentSize is atleast PAGE_SIZE, then
448: // thisLen = MIN(next_page(src), src + mbufLen) - src;
449:
450: thisLen = MIN(mbufLen, maxSegmentSize);
451: thisLen = MIN(next_page(src), src + thisLen) - src;
452:
453: // If room left then find the current segment addr and output
454: if (curSegIndex < maxSegs) {
455: struct IOPhysicalSegment physSeg;
456:
457: if ((physSeg.location =
458: (IOPhysicalAddress)mcl_to_paddr((char *)src)) == 0)
459: physSeg.location = (IOPhysicalAddress)pmap_extract(kernel_pmap, src);
460: if (!physSeg.location)
461: return 0;
462: physSeg.length = thisLen;
463: (*outSeg)(physSeg, vector, curSegIndex);
464: }
465: // Count segments if we are coalescing.
466: curSegIndex++;
467: }
468:
469: // Cache the segment count data if room is available.
470: if (curMBufIndex < kMBufDataCacheSize) {
471: segsPerMBuf[curMBufIndex] = curSegIndex - lastSegCount;
472: lastSegCount = curSegIndex;
473: }
474:
475: // Move on to next imcoming mbuf
476: curMBufIndex++;
477: m = m->m_next;
478: } while (m);
479:
480: // If we finished cleanly return number of segments found
481: if (curSegIndex <= maxSegs)
482: return curSegIndex;
483: if (!doCoalesce)
484: return 0; // if !coalescing we've got a problem.
485:
486:
487: // If we are coalescing and it is possible then attempt coalesce,
488: if (!doneCoalesce
489: && (UInt) packet->m_pkthdr.len <= maxSegs * maxSegmentSize) {
490: // Hmm, we have to do some coalescing.
491: bool analysisRet;
492:
493: analysisRet = analyseSegments(packet,
494: MIN(curMBufIndex, kMBufDataCacheSize),
495: segsPerMBuf,
496: curSegIndex, maxSegs);
497: if (analysisRet) {
498: doneCoalesce = true;
499: coalesceCount++;
500: goto tryAgain;
501: }
502: }
503:
504: assert(!doneCoalesce); // Problem in Coalesce code.
505: packetTooBigErrors++;
506: return 0;
507: }
508:
509: /********************* class IOMBufBigMemoryCursor **********************/
510: IOMBufBigMemoryCursor *
511: IOMBufBigMemoryCursor::withSpecification(UInt maxSegSize, UInt maxNumSegs)
512: {
513: IOMBufBigMemoryCursor *me = new IOMBufBigMemoryCursor;
514:
515: if (me && !me->initWithSpecification(&bigOutputSegment,
516: maxSegSize, maxNumSegs)) {
517: me->release();
518: return 0;
519: }
520:
521: return me;
522: }
523:
524:
525: /******************* class IOMBufNaturalMemoryCursor ********************/
526: IOMBufNaturalMemoryCursor *
527: IOMBufNaturalMemoryCursor::withSpecification(UInt maxSegSize, UInt maxNumSegs)
528: {
529: IOMBufNaturalMemoryCursor *me = new IOMBufNaturalMemoryCursor;
530:
531: if (me && !me->initWithSpecification(&naturalOutputSegment,
532: maxSegSize, maxNumSegs)) {
533: me->release();
534: return 0;
535: }
536:
537: return me;
538: }
539:
540:
541: /******************** class IOMBufLittleMemoryCursor ********************/
542: IOMBufLittleMemoryCursor *
543: IOMBufLittleMemoryCursor::withSpecification(UInt maxSegSize, UInt maxNumSegs)
544: {
545: IOMBufLittleMemoryCursor *me = new IOMBufLittleMemoryCursor;
546:
547: if (me && !me->initWithSpecification(&littleOutputSegment,
548: maxSegSize, maxNumSegs)) {
549: me->release();
550: return 0;
551: }
552:
553: return me;
554: }
555:
556:
557: /******************** class IOMBufDBDMAMemoryCursor *********************/
558: #ifdef __ppc__
559:
560: IOMBufDBDMAMemoryCursor *
561: IOMBufDBDMAMemoryCursor::withSpecification(UInt maxSegSize, UInt maxNumSegs)
562: {
563: IOMBufDBDMAMemoryCursor *me = new IOMBufDBDMAMemoryCursor;
564:
565: if (me && !me->initWithSpecification(&dbdmaOutputSegment,
566: maxSegSize, maxNumSegs)) {
567: me->release();
568: return 0;
569: }
570:
571: return me;
572: }
573: #endif /* __ppc__ */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.