VirtualBox

source: vbox/trunk/include/VBox/intnetinline.h@ 41338

Last change on this file since 41338 was 38549, checked in by vboxsync, 13 years ago

GSO: UDP fragmentation offloading (#5846)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.7 KB
Line 
1/* $Id: intnetinline.h 38549 2011-08-26 13:26:07Z vboxsync $ */
2/** @file
3 * INTNET - Internal Networking, Inlined Code. (DEV,++)
4 *
5 * This is all inlined because it's too tedious to create 2-3 libraries to
6 * contain it all. Large parts of this header is only accessible from C++
7 * sources because of mixed code and variables.
8 */
9
10/*
11 * Copyright (C) 2006-2010 Oracle Corporation
12 *
13 * This file is part of VirtualBox Open Source Edition (OSE), as
14 * available from http://www.virtualbox.org. This file is free software;
15 * you can redistribute it and/or modify it under the terms of the GNU
16 * General Public License (GPL) as published by the Free Software
17 * Foundation, in version 2 as it comes in the "COPYING" file of the
18 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
19 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20 *
21 * The contents of this file may alternatively be used under the terms
22 * of the Common Development and Distribution License Version 1.0
23 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
24 * VirtualBox OSE distribution, in which case the provisions of the
25 * CDDL are applicable instead of those of the GPL.
26 *
27 * You may elect to license modified versions of this file under the
28 * terms and conditions of either the GPL or the CDDL or both.
29 */
30
31#ifndef ___VBox_intnetinline_h
32#define ___VBox_intnetinline_h
33
34#include <VBox/intnet.h>
35#include <iprt/string.h>
36#include <iprt/assert.h>
37#include <iprt/err.h>
38#include <VBox/log.h>
39
40
41
42/**
43 * Valid internal networking frame type.
44 *
45 * @returns true / false.
46 * @param u16Type The frame type to check.
47 */
48DECLINLINE(bool) IntNetIsValidFrameType(uint16_t u16Type)
49{
50 if (RT_LIKELY( u16Type == INTNETHDR_TYPE_FRAME
51 || u16Type == INTNETHDR_TYPE_GSO
52 || u16Type == INTNETHDR_TYPE_PADDING))
53 return true;
54 return false;
55}
56
57
58/**
59 * Partly initializes a scatter / gather buffer, leaving the segments to the
60 * caller.
61 *
62 * @returns Pointer to the start of the frame.
63 * @param pSG Pointer to the scatter / gather structure.
64 * @param cbTotal The total size.
65 * @param cSegs The number of segments.
66 * @param cSegsUsed The number of used segments.
67 */
68DECLINLINE(void) IntNetSgInitTempSegs(PINTNETSG pSG, uint32_t cbTotal, unsigned cSegs, unsigned cSegsUsed)
69{
70 pSG->pvOwnerData = NULL;
71 pSG->pvUserData = NULL;
72 pSG->pvUserData2 = NULL;
73 pSG->cbTotal = cbTotal;
74 pSG->cUsers = 1;
75 pSG->fFlags = INTNETSG_FLAGS_TEMP;
76 pSG->GsoCtx.u8Type = (uint8_t)PDMNETWORKGSOTYPE_INVALID;
77 pSG->GsoCtx.cbHdrsTotal = 0;
78 pSG->GsoCtx.cbHdrsSeg = 0;
79 pSG->GsoCtx.cbMaxSeg= 0;
80 pSG->GsoCtx.offHdr1 = 0;
81 pSG->GsoCtx.offHdr2 = 0;
82 pSG->GsoCtx.u8Unused= 0;
83#if ARCH_BITS == 64
84 pSG->uPadding = 0;
85#endif
86 pSG->cSegsAlloc = (uint16_t)cSegs;
87 Assert(pSG->cSegsAlloc == cSegs);
88 pSG->cSegsUsed = (uint16_t)cSegsUsed;
89 Assert(pSG->cSegsUsed == cSegsUsed);
90 Assert(cSegs >= cSegsUsed);
91}
92
93
94/**
95 * Partly initializes a scatter / gather buffer w/ GSO, leaving the segments to
96 * the caller.
97 *
98 * @returns Pointer to the start of the frame.
99 * @param pSG Pointer to the scatter / gather structure.
100 * @param cbTotal The total size.
101 * @param cSegs The number of segments.
102 * @param cSegsUsed The number of used segments.
103 * @param pGso The GSO context.
104 */
105DECLINLINE(void) IntNetSgInitTempSegsGso(PINTNETSG pSG, uint32_t cbTotal, unsigned cSegs,
106 unsigned cSegsUsed, PCPDMNETWORKGSO pGso)
107{
108 pSG->pvOwnerData = NULL;
109 pSG->pvUserData = NULL;
110 pSG->pvUserData2 = NULL;
111 pSG->cbTotal = cbTotal;
112 pSG->cUsers = 1;
113 pSG->fFlags = INTNETSG_FLAGS_TEMP;
114 pSG->GsoCtx.u8Type = pGso->u8Type;
115 pSG->GsoCtx.cbHdrsTotal = pGso->cbHdrsTotal;
116 pSG->GsoCtx.cbHdrsSeg = pGso->cbHdrsSeg;
117 pSG->GsoCtx.cbMaxSeg= pGso->cbMaxSeg;
118 pSG->GsoCtx.offHdr1 = pGso->offHdr1;
119 pSG->GsoCtx.offHdr2 = pGso->offHdr2;
120 pSG->GsoCtx.u8Unused= 0;
121#if ARCH_BITS == 64
122 pSG->uPadding = 0;
123#endif
124 pSG->cSegsAlloc = (uint16_t)cSegs;
125 Assert(pSG->cSegsAlloc == cSegs);
126 pSG->cSegsUsed = (uint16_t)cSegsUsed;
127 Assert(pSG->cSegsUsed == cSegsUsed);
128 Assert(cSegs >= cSegsUsed);
129}
130
131
132
133/**
134 * Initializes a scatter / gather buffer describing a simple linear buffer.
135 *
136 * @returns Pointer to the start of the frame.
137 * @param pSG Pointer to the scatter / gather structure.
138 * @param pvFrame Pointer to the frame
139 * @param cbFrame The size of the frame.
140 */
141DECLINLINE(void) IntNetSgInitTemp(PINTNETSG pSG, void *pvFrame, uint32_t cbFrame)
142{
143 IntNetSgInitTempSegs(pSG, cbFrame, 1, 1);
144 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
145 pSG->aSegs[0].pv = pvFrame;
146 pSG->aSegs[0].cb = cbFrame;
147}
148
149/**
150 * Initializes a scatter / gather buffer describing a simple linear buffer.
151 *
152 * @returns Pointer to the start of the frame.
153 * @param pSG Pointer to the scatter / gather structure.
154 * @param pvFrame Pointer to the frame
155 * @param cbFrame The size of the frame.
156 * @param pGso The GSO context.
157 */
158DECLINLINE(void) IntNetSgInitTempGso(PINTNETSG pSG, void *pvFrame, uint32_t cbFrame, PCPDMNETWORKGSO pGso)
159{
160 IntNetSgInitTempSegsGso(pSG, cbFrame, 1, 1, pGso);
161 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
162 pSG->aSegs[0].pv = pvFrame;
163 pSG->aSegs[0].cb = cbFrame;
164}
165
166
167/**
168 * Reads an entire SG into a fittingly size buffer.
169 *
170 * @param pSG The SG list to read.
171 * @param pvBuf The buffer to read into (at least pSG->cbTotal in size).
172 */
173DECLINLINE(void) IntNetSgRead(PCINTNETSG pSG, void *pvBuf)
174{
175 memcpy(pvBuf, pSG->aSegs[0].pv, pSG->aSegs[0].cb);
176 if (pSG->cSegsUsed == 1)
177 Assert(pSG->cbTotal == pSG->aSegs[0].cb);
178 else
179 {
180 uint8_t *pbDst = (uint8_t *)pvBuf + pSG->aSegs[0].cb;
181 unsigned iSeg = 0;
182 unsigned const cSegs = pSG->cSegsUsed;
183 while (++iSeg < cSegs)
184 {
185 uint32_t cbSeg = pSG->aSegs[iSeg].cb;
186 Assert((uintptr_t)pbDst - (uintptr_t)pvBuf + cbSeg <= pSG->cbTotal);
187 memcpy(pbDst, pSG->aSegs[iSeg].pv, cbSeg);
188 pbDst += cbSeg;
189 }
190 }
191}
192
193
194/**
195 * Reads a portion of an SG into a buffer.
196 *
197 * @param pSG The SG list to read.
198 * @param offSrc The offset to start start copying from.
199 * @param cbToRead The number of bytes to copy.
200 * @param pvBuf The buffer to read into, cb or more in size.
201 */
202DECLINLINE(void) IntNetSgReadEx(PCINTNETSG pSG, uint32_t offSrc, uint32_t cbToRead, void *pvBuf)
203{
204 uint8_t *pbDst = (uint8_t *)pvBuf;
205 uint32_t iSeg = 0;
206
207 /* validate assumptions */
208 Assert(cbToRead <= pSG->cbTotal);
209 Assert(offSrc <= pSG->cbTotal);
210 Assert(offSrc + cbToRead <= pSG->cbTotal);
211
212 /* Find the right segment and copy any bits from within the segment. */
213 while (offSrc)
214 {
215 uint32_t cbSeg = pSG->aSegs[iSeg].cb;
216 if (offSrc < cbSeg)
217 {
218 uint32_t cbChunk = cbSeg - offSrc;
219 if (cbChunk >= cbToRead)
220 {
221 memcpy(pbDst, (uint8_t const *)pSG->aSegs[iSeg].pv + offSrc, cbToRead);
222 return;
223 }
224
225 memcpy(pbDst, (uint8_t const *)pSG->aSegs[iSeg].pv + offSrc, cbChunk);
226 pbDst += cbChunk;
227 cbToRead -= cbChunk;
228 break;
229 }
230
231 /* advance */
232 offSrc -= cbSeg;
233 iSeg++;
234 }
235
236 /* We're not at the start of a segment, copy until we're done. */
237 for (;;)
238 {
239 uint32_t cbSeg = pSG->aSegs[iSeg].cb;
240 if (cbSeg >= cbToRead)
241 {
242 memcpy(pbDst, pSG->aSegs[iSeg].pv, cbToRead);
243 return;
244 }
245
246 memcpy(pbDst, pSG->aSegs[iSeg].pv, cbSeg);
247 pbDst += cbSeg;
248 cbToRead -= cbSeg;
249 iSeg++;
250 Assert(iSeg < pSG->cSegsUsed);
251 }
252}
253
254#ifdef __cplusplus
255
256/**
257 * Get the amount of space available for writing.
258 *
259 * @returns Number of available bytes.
260 * @param pRingBuf The ring buffer.
261 */
262DECLINLINE(uint32_t) IntNetRingGetWritable(PINTNETRINGBUF pRingBuf)
263{
264 uint32_t const offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
265 uint32_t const offWriteInt = ASMAtomicUoReadU32(&pRingBuf->offWriteInt);
266 return offRead <= offWriteInt
267 ? pRingBuf->offEnd - offWriteInt + offRead - pRingBuf->offStart - 1
268 : offRead - offWriteInt - 1;
269}
270
271
272/**
273 * Checks if the ring has more for us to read.
274 *
275 * @returns Number of ready bytes.
276 * @param pRingBuf The ring buffer.
277 */
278DECLINLINE(bool) IntNetRingHasMoreToRead(PINTNETRINGBUF pRingBuf)
279{
280 uint32_t const offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
281 uint32_t const offWriteCom = ASMAtomicUoReadU32(&pRingBuf->offWriteCom);
282 return offRead != offWriteCom;
283}
284
285
286/**
287 * Gets the next frame to read.
288 *
289 * @returns Pointer to the next frame. NULL if done.
290 * @param pRingBuf The ring buffer.
291 */
292DECLINLINE(PINTNETHDR) IntNetRingGetNextFrameToRead(PINTNETRINGBUF pRingBuf)
293{
294 uint32_t const offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
295 uint32_t const offWriteCom = ASMAtomicUoReadU32(&pRingBuf->offWriteCom);
296 if (offRead == offWriteCom)
297 return NULL;
298 return (PINTNETHDR)((uint8_t *)pRingBuf + offRead);
299}
300
301
302/**
303 * Get the amount of data ready for reading.
304 *
305 * @returns Number of ready bytes.
306 * @param pRingBuf The ring buffer.
307 */
308DECLINLINE(uint32_t) IntNetRingGetReadable(PINTNETRINGBUF pRingBuf)
309{
310 uint32_t const offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
311 uint32_t const offWriteCom = ASMAtomicUoReadU32(&pRingBuf->offWriteCom);
312 return offRead <= offWriteCom
313 ? offWriteCom - offRead
314 : pRingBuf->offEnd - offRead + offWriteCom - pRingBuf->offStart;
315}
316
317
318/**
319 * Calculates the pointer to the frame.
320 *
321 * @returns Pointer to the start of the frame.
322 * @param pHdr Pointer to the packet header
323 * @param pBuf The buffer the header is within. Only used in strict builds.
324 */
325DECLINLINE(void *) IntNetHdrGetFramePtr(PCINTNETHDR pHdr, PCINTNETBUF pBuf)
326{
327 uint8_t *pu8 = (uint8_t *)pHdr + pHdr->offFrame;
328#ifdef VBOX_STRICT
329 const uintptr_t off = (uintptr_t)pu8 - (uintptr_t)pBuf;
330 Assert(IntNetIsValidFrameType(pHdr->u16Type));
331 Assert(off < pBuf->cbBuf);
332 Assert(off + pHdr->cbFrame <= pBuf->cbBuf);
333#endif
334 NOREF(pBuf);
335 return pu8;
336}
337
338
339/**
340 * Calculates the pointer to the GSO context.
341 *
342 * ASSUMES the frame is a GSO frame.
343 *
344 * The GSO context is immediately followed by the headers and payload. The size
345 * is INTNETBUF::cbFrame - sizeof(PDMNETWORKGSO).
346 *
347 * @returns Pointer to the GSO context.
348 * @param pHdr Pointer to the packet header
349 * @param pBuf The buffer the header is within. Only used in strict builds.
350 */
351DECLINLINE(PPDMNETWORKGSO) IntNetHdrGetGsoContext(PCINTNETHDR pHdr, PCINTNETBUF pBuf)
352{
353 PPDMNETWORKGSO pGso = (PPDMNETWORKGSO)((uint8_t *)pHdr + pHdr->offFrame);
354#ifdef VBOX_STRICT
355 const uintptr_t off = (uintptr_t)pGso - (uintptr_t)pBuf;
356 Assert(pHdr->u16Type == INTNETHDR_TYPE_GSO);
357 Assert(off < pBuf->cbBuf);
358 Assert(off + pHdr->cbFrame <= pBuf->cbBuf);
359#endif
360 NOREF(pBuf);
361 return pGso;
362}
363
364
365/**
366 * Skips to the next (read) frame in the buffer.
367 *
368 * @param pRingBuf The ring buffer in question.
369 */
370DECLINLINE(void) IntNetRingSkipFrame(PINTNETRINGBUF pRingBuf)
371{
372 uint32_t const offReadOld = ASMAtomicUoReadU32(&pRingBuf->offReadX);
373 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offReadOld);
374 Assert(offReadOld >= pRingBuf->offStart);
375 Assert(offReadOld < pRingBuf->offEnd);
376 Assert(RT_ALIGN_PT(pHdr, INTNETHDR_ALIGNMENT, INTNETHDR *) == pHdr);
377 Assert(IntNetIsValidFrameType(pHdr->u16Type));
378
379 /* skip the frame */
380 uint32_t offReadNew = offReadOld + pHdr->offFrame + pHdr->cbFrame;
381 offReadNew = RT_ALIGN_32(offReadNew, INTNETHDR_ALIGNMENT);
382 Assert(offReadNew <= pRingBuf->offEnd && offReadNew >= pRingBuf->offStart);
383 if (offReadNew >= pRingBuf->offEnd)
384 offReadNew = pRingBuf->offStart;
385 Log2(("IntNetRingSkipFrame: offReadX: %#x -> %#x (1)\n", offReadOld, offReadNew));
386#ifdef INTNET_POISON_READ_FRAMES
387 memset((uint8_t *)pHdr + pHdr->offFrame, 0xfe, RT_ALIGN_32(pHdr->cbFrame, INTNETHDR_ALIGNMENT));
388 memset(pHdr, 0xef, sizeof(*pHdr));
389#endif
390 ASMAtomicWriteU32(&pRingBuf->offReadX, offReadNew);
391}
392
393
394/**
395 * Allocates a frame in the specified ring.
396 *
397 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
398 * @param pRingBuf The ring buffer.
399 * @param cbFrame The frame size.
400 * @param ppHdr Where to return the frame header.
401 * Don't touch this!
402 * @param ppvFrame Where to return the frame pointer.
403 */
404DECLINLINE(int) intnetRingAllocateFrameInternal(PINTNETRINGBUF pRingBuf, uint32_t cbFrame, uint16_t u16Type,
405 PINTNETHDR *ppHdr, void **ppvFrame)
406{
407 /*
408 * Validate input and adjust the input.
409 */
410 INTNETRINGBUF_ASSERT_SANITY(pRingBuf);
411 Assert(cbFrame >= sizeof(RTMAC) * 2);
412
413 const uint32_t cb = RT_ALIGN_32(cbFrame, INTNETHDR_ALIGNMENT);
414 uint32_t offWriteInt = ASMAtomicUoReadU32(&pRingBuf->offWriteInt);
415 uint32_t offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
416 if (offRead <= offWriteInt)
417 {
418 /*
419 * Try fit it all before the end of the buffer.
420 */
421 if (pRingBuf->offEnd - offWriteInt >= cb + sizeof(INTNETHDR))
422 {
423 uint32_t offNew = offWriteInt + cb + sizeof(INTNETHDR);
424 if (offNew >= pRingBuf->offEnd)
425 offNew = pRingBuf->offStart;
426 if (RT_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
427 return VERR_WRONG_ORDER; /* race */
428 Log2(("intnetRingAllocateFrameInternal: offWriteInt: %#x -> %#x (1) (R=%#x T=%#x S=%#x)\n", offWriteInt, offNew, offRead, u16Type, cbFrame));
429
430 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
431 pHdr->u16Type = u16Type;
432 pHdr->cbFrame = (uint16_t)cbFrame; Assert(pHdr->cbFrame == cbFrame);
433 pHdr->offFrame = sizeof(INTNETHDR);
434
435 *ppHdr = pHdr;
436 *ppvFrame = pHdr + 1;
437 return VINF_SUCCESS;
438 }
439 /*
440 * Try fit the frame at the start of the buffer.
441 * (The header fits before the end of the buffer because of alignment.)
442 */
443 AssertMsg(pRingBuf->offEnd - offWriteInt >= sizeof(INTNETHDR), ("offEnd=%x offWriteInt=%x\n", pRingBuf->offEnd, offWriteInt));
444 if (offRead - pRingBuf->offStart > cb) /* not >= ! */
445 {
446 uint32_t offNew = pRingBuf->offStart + cb;
447 if (RT_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
448 return VERR_WRONG_ORDER; /* race */
449 Log2(("intnetRingAllocateFrameInternal: offWriteInt: %#x -> %#x (2) (R=%#x T=%#x S=%#x)\n", offWriteInt, offNew, offRead, u16Type, cbFrame));
450
451 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
452 pHdr->u16Type = u16Type;
453 pHdr->cbFrame = (uint16_t)cbFrame; Assert(pHdr->cbFrame == cbFrame);
454 pHdr->offFrame = pRingBuf->offStart - offWriteInt;
455
456 *ppHdr = pHdr;
457 *ppvFrame = (uint8_t *)pRingBuf + pRingBuf->offStart;
458 return VINF_SUCCESS;
459 }
460 }
461 /*
462 * The reader is ahead of the writer, try fit it into that space.
463 */
464 else if (offRead - offWriteInt > cb + sizeof(INTNETHDR)) /* not >= ! */
465 {
466 uint32_t offNew = offWriteInt + cb + sizeof(INTNETHDR);
467 if (RT_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
468 return VERR_WRONG_ORDER; /* race */
469 Log2(("intnetRingAllocateFrameInternal: offWriteInt: %#x -> %#x (3) (R=%#x T=%#x S=%#x)\n", offWriteInt, offNew, offRead, u16Type, cbFrame));
470
471 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
472 pHdr->u16Type = u16Type;
473 pHdr->cbFrame = (uint16_t)cbFrame; Assert(pHdr->cbFrame == cbFrame);
474 pHdr->offFrame = sizeof(INTNETHDR);
475
476 *ppHdr = pHdr;
477 *ppvFrame = pHdr + 1;
478 return VINF_SUCCESS;
479 }
480
481 /* (it didn't fit) */
482 *ppHdr = NULL; /* shut up gcc, */
483 *ppvFrame = NULL; /* ditto. */
484 STAM_REL_COUNTER_INC(&pRingBuf->cOverflows);
485 return VERR_BUFFER_OVERFLOW;
486}
487
488
489/**
490 * Allocates a normal frame in the specified ring.
491 *
492 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
493 * @param pRingBuf The ring buffer.
494 * @param cbFrame The frame size.
495 * @param ppHdr Where to return the frame header.
496 * Don't touch this!
497 * @param ppvFrame Where to return the frame pointer.
498 */
499DECLINLINE(int) IntNetRingAllocateFrame(PINTNETRINGBUF pRingBuf, uint32_t cbFrame, PINTNETHDR *ppHdr, void **ppvFrame)
500{
501 return intnetRingAllocateFrameInternal(pRingBuf, cbFrame, INTNETHDR_TYPE_FRAME, ppHdr, ppvFrame);
502}
503
504
505/**
506 * Allocates a GSO frame in the specified ring.
507 *
508 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
509 * @param pRingBuf The ring buffer.
510 * @param cbFrame The frame size.
511 * @param pGso Pointer to the GSO context.
512 * @param ppHdr Where to return the frame header.
513 * Don't touch this!
514 * @param ppvFrame Where to return the frame pointer.
515 */
516DECLINLINE(int) IntNetRingAllocateGsoFrame(PINTNETRINGBUF pRingBuf, uint32_t cbFrame, PCPDMNETWORKGSO pGso,
517 PINTNETHDR *ppHdr, void **ppvFrame)
518{
519 void *pvFrame = NULL; /* gcc maybe used uninitialized */
520 int rc = intnetRingAllocateFrameInternal(pRingBuf, cbFrame + sizeof(*pGso), INTNETHDR_TYPE_GSO, ppHdr, &pvFrame);
521 if (RT_SUCCESS(rc))
522 {
523 PPDMNETWORKGSO pGsoCopy = (PPDMNETWORKGSO)pvFrame;
524 *pGsoCopy = *pGso;
525 *ppvFrame = pGsoCopy + 1;
526 }
527 return rc;
528}
529
530
531/**
532 * Commits a frame.
533 *
534 * Make sure to commit the frames in the order they've been allocated!
535 *
536 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
537 * @param pRingBuf The ring buffer.
538 * @param pHdr The frame header returned by
539 * IntNetRingAllocateFrame.
540 */
541DECLINLINE(void) IntNetRingCommitFrame(PINTNETRINGBUF pRingBuf, PINTNETHDR pHdr)
542{
543 /*
544 * Validate input and commit order.
545 */
546 INTNETRINGBUF_ASSERT_SANITY(pRingBuf);
547 INTNETHDR_ASSERT_SANITY(pHdr, pRingBuf);
548 Assert(pRingBuf->offWriteCom == ((uintptr_t)pHdr - (uintptr_t)pRingBuf));
549
550 /*
551 * Figure out the offWriteCom for this packet and update the ring.
552 */
553 const uint32_t cbFrame = pHdr->cbFrame;
554 const uint32_t cb = RT_ALIGN_32(cbFrame, INTNETHDR_ALIGNMENT);
555 uint32_t offWriteCom = (uint32_t)((uintptr_t)pHdr - (uintptr_t)pRingBuf)
556 + pHdr->offFrame
557 + cb;
558 if (offWriteCom >= pRingBuf->offEnd)
559 {
560 Assert(offWriteCom == pRingBuf->offEnd);
561 offWriteCom = pRingBuf->offStart;
562 }
563 Log2(("IntNetRingCommitFrame: offWriteCom: %#x -> %#x (R=%#x T=%#x S=%#x)\n", pRingBuf->offWriteCom, offWriteCom, pRingBuf->offReadX, pHdr->u16Type, cbFrame));
564 ASMAtomicWriteU32(&pRingBuf->offWriteCom, offWriteCom);
565 STAM_REL_COUNTER_ADD(&pRingBuf->cbStatWritten, cbFrame);
566 STAM_REL_COUNTER_INC(&pRingBuf->cStatFrames);
567}
568
569
570/**
571 * Commits a frame and injects a filler frame if not all of the buffer was used.
572 *
573 * Make sure to commit the frames in the order they've been allocated!
574 *
575 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
576 * @param pRingBuf The ring buffer.
577 * @param pHdr The frame header returned by
578 * IntNetRingAllocateFrame.
579 * @param cbUsed The amount of space actually used. This does
580 * not include the GSO part.
581 */
582DECLINLINE(void) IntNetRingCommitFrameEx(PINTNETRINGBUF pRingBuf, PINTNETHDR pHdr, size_t cbUsed)
583{
584 /*
585 * Validate input and commit order.
586 */
587 INTNETRINGBUF_ASSERT_SANITY(pRingBuf);
588 INTNETHDR_ASSERT_SANITY(pHdr, pRingBuf);
589 Assert(pRingBuf->offWriteCom == ((uintptr_t)pHdr - (uintptr_t)pRingBuf));
590
591 if (pHdr->u16Type == INTNETHDR_TYPE_GSO)
592 cbUsed += sizeof(PDMNETWORKGSO);
593
594 /*
595 * Calc the new write commit offset.
596 */
597 const uint32_t cbAlignedFrame = RT_ALIGN_32(pHdr->cbFrame, INTNETHDR_ALIGNMENT);
598 const uint32_t cbAlignedUsed = RT_ALIGN_32(cbUsed, INTNETHDR_ALIGNMENT);
599 uint32_t offWriteCom = (uint32_t)((uintptr_t)pHdr - (uintptr_t)pRingBuf)
600 + pHdr->offFrame
601 + cbAlignedFrame;
602 if (offWriteCom >= pRingBuf->offEnd)
603 {
604 Assert(offWriteCom == pRingBuf->offEnd);
605 offWriteCom = pRingBuf->offStart;
606 }
607
608 /*
609 * Insert a dummy frame to pad any unused space.
610 */
611 if (cbAlignedFrame != cbAlignedUsed)
612 {
613 /** @todo Later: Try unallocate the extra memory. */
614 PINTNETHDR pHdrPadding = (PINTNETHDR)((uint8_t *)pHdr + pHdr->offFrame + cbAlignedUsed);
615 pHdrPadding->u16Type = INTNETHDR_TYPE_PADDING;
616 pHdrPadding->cbFrame = (uint16_t)(cbAlignedFrame - cbAlignedUsed - sizeof(INTNETHDR));
617 pHdrPadding->offFrame = sizeof(INTNETHDR);
618 pHdr->cbFrame = (uint16_t)cbUsed;
619 }
620
621 Log2(("IntNetRingCommitFrameEx: offWriteCom: %#x -> %#x (R=%#x T=%#x S=%#x P=%#x)\n", pRingBuf->offWriteCom, offWriteCom, pRingBuf->offReadX, pHdr->u16Type, pHdr->cbFrame, cbAlignedFrame - cbAlignedUsed));
622 ASMAtomicWriteU32(&pRingBuf->offWriteCom, offWriteCom);
623 STAM_REL_COUNTER_ADD(&pRingBuf->cbStatWritten, cbUsed);
624 STAM_REL_COUNTER_INC(&pRingBuf->cStatFrames);
625}
626
627
628/**
629 * Writes a frame to the specified ring.
630 *
631 * Make sure you don't have any uncommitted frames when calling this function!
632 *
633 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
634 * @param pRingBuf The ring buffer.
635 * @param pvFrame The bits to write.
636 * @param cbFrame How much to write.
637 */
638DECLINLINE(int) IntNetRingWriteFrame(PINTNETRINGBUF pRingBuf, const void *pvFrame, size_t cbFrame)
639{
640 /*
641 * Validate input.
642 */
643 INTNETRINGBUF_ASSERT_SANITY(pRingBuf);
644 Assert(cbFrame >= sizeof(RTMAC) * 2);
645
646 /*
647 * Align the size and read the volatile ring buffer variables.
648 */
649 const uint32_t cb = RT_ALIGN_32(cbFrame, INTNETHDR_ALIGNMENT);
650 uint32_t offWriteInt = ASMAtomicUoReadU32(&pRingBuf->offWriteInt);
651 uint32_t offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
652 if (offRead <= offWriteInt)
653 {
654 /*
655 * Try fit it all before the end of the buffer.
656 */
657 if (pRingBuf->offEnd - offWriteInt >= cb + sizeof(INTNETHDR))
658 {
659 uint32_t offNew = offWriteInt + cb + sizeof(INTNETHDR);
660 if (offNew >= pRingBuf->offEnd)
661 offNew = pRingBuf->offStart;
662 if (RT_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
663 return VERR_WRONG_ORDER; /* race */
664 Log2(("IntNetRingWriteFrame: offWriteInt: %#x -> %#x (1)\n", offWriteInt, offNew));
665
666 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
667 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
668 pHdr->cbFrame = (uint16_t)cbFrame; Assert(pHdr->cbFrame == cbFrame);
669 pHdr->offFrame = sizeof(INTNETHDR);
670
671 memcpy(pHdr + 1, pvFrame, cbFrame);
672
673 Log2(("IntNetRingWriteFrame: offWriteCom: %#x -> %#x (1)\n", pRingBuf->offWriteCom, offNew));
674 ASMAtomicWriteU32(&pRingBuf->offWriteCom, offNew);
675 STAM_REL_COUNTER_ADD(&pRingBuf->cbStatWritten, cbFrame);
676 STAM_REL_COUNTER_INC(&pRingBuf->cStatFrames);
677 return VINF_SUCCESS;
678 }
679 /*
680 * Try fit the frame at the start of the buffer.
681 * (The header fits before the end of the buffer because of alignment.)
682 */
683 AssertMsg(pRingBuf->offEnd - offWriteInt >= sizeof(INTNETHDR), ("offEnd=%x offWriteInt=%x\n", pRingBuf->offEnd, offWriteInt));
684 if (offRead - pRingBuf->offStart > cb) /* not >= ! */
685 {
686 uint32_t offNew = pRingBuf->offStart + cb;
687 if (RT_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
688 return VERR_WRONG_ORDER; /* race */
689 Log2(("IntNetRingWriteFrame: offWriteInt: %#x -> %#x (2)\n", offWriteInt, offNew));
690
691 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
692 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
693 pHdr->cbFrame = (uint16_t)cbFrame; Assert(pHdr->cbFrame == cbFrame);
694 pHdr->offFrame = pRingBuf->offStart - offWriteInt;
695
696 memcpy((uint8_t *)pRingBuf + pRingBuf->offStart, pvFrame, cbFrame);
697
698 Log2(("IntNetRingWriteFrame: offWriteCom: %#x -> %#x (2)\n", pRingBuf->offWriteCom, offNew));
699 ASMAtomicWriteU32(&pRingBuf->offWriteCom, offNew);
700 STAM_REL_COUNTER_ADD(&pRingBuf->cbStatWritten, cbFrame);
701 STAM_REL_COUNTER_INC(&pRingBuf->cStatFrames);
702 return VINF_SUCCESS;
703 }
704 }
705 /*
706 * The reader is ahead of the writer, try fit it into that space.
707 */
708 else if (offRead - offWriteInt > cb + sizeof(INTNETHDR)) /* not >= ! */
709 {
710 uint32_t offNew = offWriteInt + cb + sizeof(INTNETHDR);
711 if (RT_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
712 return VERR_WRONG_ORDER; /* race */
713 Log2(("IntNetRingWriteFrame: offWriteInt: %#x -> %#x (3)\n", offWriteInt, offNew));
714
715 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
716 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
717 pHdr->cbFrame = (uint16_t)cbFrame; Assert(pHdr->cbFrame == cbFrame);
718 pHdr->offFrame = sizeof(INTNETHDR);
719
720 memcpy(pHdr + 1, pvFrame, cbFrame);
721
722 Log2(("IntNetRingWriteFrame: offWriteCom: %#x -> %#x (3)\n", pRingBuf->offWriteCom, offNew));
723 ASMAtomicWriteU32(&pRingBuf->offWriteCom, offNew);
724 STAM_REL_COUNTER_ADD(&pRingBuf->cbStatWritten, cbFrame);
725 STAM_REL_COUNTER_INC(&pRingBuf->cStatFrames);
726 return VINF_SUCCESS;
727 }
728
729 /* (it didn't fit) */
730 STAM_REL_COUNTER_INC(&pRingBuf->cOverflows);
731 return VERR_BUFFER_OVERFLOW;
732}
733
734
735/**
736 * Reads the next frame in the buffer and moves the read cursor past it.
737 *
738 * @returns Size of the frame in bytes. 0 is returned if nothing in the buffer.
739 * @param pRingBuff The ring buffer to read from.
740 * @param pvFrameDst Where to put the frame. The caller is responsible for
741 * ensuring that there is sufficient space for the frame.
742 *
743 * @deprecated Bad interface, do NOT use it! Only for tstIntNetR0.
744 */
745DECLINLINE(uint32_t) IntNetRingReadAndSkipFrame(PINTNETRINGBUF pRingBuf, void *pvFrameDst)
746{
747 INTNETRINGBUF_ASSERT_SANITY(pRingBuf);
748
749 uint32_t offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
750 uint32_t const offWriteCom = ASMAtomicUoReadU32(&pRingBuf->offWriteCom);
751 if (offRead == offWriteCom)
752 return 0;
753
754 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offRead);
755 INTNETHDR_ASSERT_SANITY(pHdr, pRingBuf);
756
757 uint32_t const cbFrame = pHdr->cbFrame;
758 int32_t const offFrame = pHdr->offFrame;
759 const void *pvFrameSrc = (uint8_t *)pHdr + offFrame;
760 memcpy(pvFrameDst, pvFrameSrc, cbFrame);
761#ifdef INTNET_POISON_READ_FRAMES
762 memset((void *)pvFrameSrc, 0xfe, RT_ALIGN_32(cbFrame, INTNETHDR_ALIGNMENT));
763 memset(pHdr, 0xef, sizeof(*pHdr));
764#endif
765
766 /* skip the frame */
767 offRead += offFrame + cbFrame;
768 offRead = RT_ALIGN_32(offRead, INTNETHDR_ALIGNMENT);
769 Assert(offRead <= pRingBuf->offEnd && offRead >= pRingBuf->offStart);
770 if (offRead >= pRingBuf->offEnd)
771 offRead = pRingBuf->offStart;
772 ASMAtomicWriteU32(&pRingBuf->offReadX, offRead);
773 return cbFrame;
774}
775
776
777/**
778 * Initializes a buffer structure.
779 *
780 * @param pIntBuf The internal networking interface buffer. This
781 * expected to be cleared prior to calling this
782 * function.
783 * @param cbBuf The size of the whole buffer.
784 * @param cbRecv The receive size.
785 * @param cbSend The send size.
786 */
787DECLINLINE(void) IntNetBufInit(PINTNETBUF pIntBuf, uint32_t cbBuf, uint32_t cbRecv, uint32_t cbSend)
788{
789 AssertCompileSizeAlignment(INTNETBUF, INTNETHDR_ALIGNMENT);
790 AssertCompileSizeAlignment(INTNETBUF, INTNETRINGBUF_ALIGNMENT);
791 Assert(cbBuf >= sizeof(INTNETBUF) + cbRecv + cbSend);
792 Assert(RT_ALIGN_32(cbRecv, INTNETRINGBUF_ALIGNMENT) == cbRecv);
793 Assert(RT_ALIGN_32(cbSend, INTNETRINGBUF_ALIGNMENT) == cbSend);
794 Assert(ASMMemIsAll8(pIntBuf, cbBuf, '\0') == NULL);
795
796 pIntBuf->u32Magic = INTNETBUF_MAGIC;
797 pIntBuf->cbBuf = cbBuf;
798 pIntBuf->cbRecv = cbRecv;
799 pIntBuf->cbSend = cbSend;
800
801 /* receive ring buffer. */
802 uint32_t offBuf = RT_ALIGN_32(sizeof(INTNETBUF), INTNETRINGBUF_ALIGNMENT) - RT_OFFSETOF(INTNETBUF, Recv);
803 pIntBuf->Recv.offStart = offBuf;
804 pIntBuf->Recv.offReadX = offBuf;
805 pIntBuf->Recv.offWriteInt = offBuf;
806 pIntBuf->Recv.offWriteCom = offBuf;
807 pIntBuf->Recv.offEnd = offBuf + cbRecv;
808
809 /* send ring buffer. */
810 offBuf += cbRecv + RT_OFFSETOF(INTNETBUF, Recv) - RT_OFFSETOF(INTNETBUF, Send);
811 pIntBuf->Send.offStart = offBuf;
812 pIntBuf->Send.offReadX = offBuf;
813 pIntBuf->Send.offWriteCom = offBuf;
814 pIntBuf->Send.offWriteInt = offBuf;
815 pIntBuf->Send.offEnd = offBuf + cbSend;
816 Assert(cbBuf >= offBuf + cbSend);
817}
818
819#endif /* __cplusplus */
820
821#endif
822
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette