VirtualBox

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

Last change on this file since 55306 was 46904, checked in by vboxsync, 11 years ago

IntNet, VirtioNet, NetFilter: Large frame support + drop oversized + UFO fix (#6821)

  • 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 46904 2013-07-02 12:59:56Z 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-2011 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 u8Type The frame type to check.
47 */
48DECLINLINE(bool) IntNetIsValidFrameType(uint8_t u8Type)
49{
50 if (RT_LIKELY( u8Type == INTNETHDR_TYPE_FRAME
51 || u8Type == INTNETHDR_TYPE_GSO
52 || u8Type == 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->u8Type));
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->u8Type == 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->u8Type));
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, uint8_t u8Type,
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, u8Type, cbFrame));
429
430 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
431 pHdr->u8Type = u8Type;
432 pHdr->cbFrame = 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, u8Type, cbFrame));
450
451 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
452 pHdr->u8Type = u8Type;
453 pHdr->cbFrame = 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, u8Type, cbFrame));
470
471 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
472 pHdr->u8Type = u8Type;
473 pHdr->cbFrame = 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->u8Type, 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->u8Type == 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->u8Type = INTNETHDR_TYPE_PADDING;
616 pHdrPadding->cbFrame = cbAlignedFrame - cbAlignedUsed - sizeof(INTNETHDR);
617 Assert(pHdrPadding->cbFrame == cbAlignedFrame - cbAlignedUsed - sizeof(INTNETHDR));
618 pHdrPadding->offFrame = sizeof(INTNETHDR);
619 pHdr->cbFrame = cbUsed; Assert(pHdr->cbFrame == cbUsed);
620 }
621
622 Log2(("IntNetRingCommitFrameEx: offWriteCom: %#x -> %#x (R=%#x T=%#x S=%#x P=%#x)\n", pRingBuf->offWriteCom, offWriteCom, pRingBuf->offReadX, pHdr->u8Type, pHdr->cbFrame, cbAlignedFrame - cbAlignedUsed));
623 ASMAtomicWriteU32(&pRingBuf->offWriteCom, offWriteCom);
624 STAM_REL_COUNTER_ADD(&pRingBuf->cbStatWritten, cbUsed);
625 STAM_REL_COUNTER_INC(&pRingBuf->cStatFrames);
626}
627
628
629/**
630 * Writes a frame to the specified ring.
631 *
632 * Make sure you don't have any uncommitted frames when calling this function!
633 *
634 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
635 * @param pRingBuf The ring buffer.
636 * @param pvFrame The bits to write.
637 * @param cbFrame How much to write.
638 */
639DECLINLINE(int) IntNetRingWriteFrame(PINTNETRINGBUF pRingBuf, const void *pvFrame, size_t cbFrame)
640{
641 /*
642 * Validate input.
643 */
644 INTNETRINGBUF_ASSERT_SANITY(pRingBuf);
645 Assert(cbFrame >= sizeof(RTMAC) * 2);
646
647 /*
648 * Align the size and read the volatile ring buffer variables.
649 */
650 const uint32_t cb = RT_ALIGN_32(cbFrame, INTNETHDR_ALIGNMENT);
651 uint32_t offWriteInt = ASMAtomicUoReadU32(&pRingBuf->offWriteInt);
652 uint32_t offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
653 if (offRead <= offWriteInt)
654 {
655 /*
656 * Try fit it all before the end of the buffer.
657 */
658 if (pRingBuf->offEnd - offWriteInt >= cb + sizeof(INTNETHDR))
659 {
660 uint32_t offNew = offWriteInt + cb + sizeof(INTNETHDR);
661 if (offNew >= pRingBuf->offEnd)
662 offNew = pRingBuf->offStart;
663 if (RT_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
664 return VERR_WRONG_ORDER; /* race */
665 Log2(("IntNetRingWriteFrame: offWriteInt: %#x -> %#x (1)\n", offWriteInt, offNew));
666
667 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
668 pHdr->u8Type = INTNETHDR_TYPE_FRAME;
669 pHdr->cbFrame = cbFrame; Assert(pHdr->cbFrame == cbFrame);
670 pHdr->offFrame = sizeof(INTNETHDR);
671
672 memcpy(pHdr + 1, pvFrame, cbFrame);
673
674 Log2(("IntNetRingWriteFrame: offWriteCom: %#x -> %#x (1)\n", pRingBuf->offWriteCom, offNew));
675 ASMAtomicWriteU32(&pRingBuf->offWriteCom, offNew);
676 STAM_REL_COUNTER_ADD(&pRingBuf->cbStatWritten, cbFrame);
677 STAM_REL_COUNTER_INC(&pRingBuf->cStatFrames);
678 return VINF_SUCCESS;
679 }
680 /*
681 * Try fit the frame at the start of the buffer.
682 * (The header fits before the end of the buffer because of alignment.)
683 */
684 AssertMsg(pRingBuf->offEnd - offWriteInt >= sizeof(INTNETHDR), ("offEnd=%x offWriteInt=%x\n", pRingBuf->offEnd, offWriteInt));
685 if (offRead - pRingBuf->offStart > cb) /* not >= ! */
686 {
687 uint32_t offNew = pRingBuf->offStart + cb;
688 if (RT_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
689 return VERR_WRONG_ORDER; /* race */
690 Log2(("IntNetRingWriteFrame: offWriteInt: %#x -> %#x (2)\n", offWriteInt, offNew));
691
692 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
693 pHdr->u8Type = INTNETHDR_TYPE_FRAME;
694 pHdr->cbFrame = cbFrame; Assert(pHdr->cbFrame == cbFrame);
695 pHdr->offFrame = pRingBuf->offStart - offWriteInt;
696
697 memcpy((uint8_t *)pRingBuf + pRingBuf->offStart, pvFrame, cbFrame);
698
699 Log2(("IntNetRingWriteFrame: offWriteCom: %#x -> %#x (2)\n", pRingBuf->offWriteCom, offNew));
700 ASMAtomicWriteU32(&pRingBuf->offWriteCom, offNew);
701 STAM_REL_COUNTER_ADD(&pRingBuf->cbStatWritten, cbFrame);
702 STAM_REL_COUNTER_INC(&pRingBuf->cStatFrames);
703 return VINF_SUCCESS;
704 }
705 }
706 /*
707 * The reader is ahead of the writer, try fit it into that space.
708 */
709 else if (offRead - offWriteInt > cb + sizeof(INTNETHDR)) /* not >= ! */
710 {
711 uint32_t offNew = offWriteInt + cb + sizeof(INTNETHDR);
712 if (RT_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
713 return VERR_WRONG_ORDER; /* race */
714 Log2(("IntNetRingWriteFrame: offWriteInt: %#x -> %#x (3)\n", offWriteInt, offNew));
715
716 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
717 pHdr->u8Type = INTNETHDR_TYPE_FRAME;
718 pHdr->cbFrame = cbFrame; Assert(pHdr->cbFrame == cbFrame);
719 pHdr->offFrame = sizeof(INTNETHDR);
720
721 memcpy(pHdr + 1, pvFrame, cbFrame);
722
723 Log2(("IntNetRingWriteFrame: offWriteCom: %#x -> %#x (3)\n", pRingBuf->offWriteCom, offNew));
724 ASMAtomicWriteU32(&pRingBuf->offWriteCom, offNew);
725 STAM_REL_COUNTER_ADD(&pRingBuf->cbStatWritten, cbFrame);
726 STAM_REL_COUNTER_INC(&pRingBuf->cStatFrames);
727 return VINF_SUCCESS;
728 }
729
730 /* (it didn't fit) */
731 STAM_REL_COUNTER_INC(&pRingBuf->cOverflows);
732 return VERR_BUFFER_OVERFLOW;
733}
734
735
736/**
737 * Reads the next frame in the buffer and moves the read cursor past it.
738 *
739 * @returns Size of the frame in bytes. 0 is returned if nothing in the buffer.
740 * @param pRingBuff The ring buffer to read from.
741 * @param pvFrameDst Where to put the frame. The caller is responsible for
742 * ensuring that there is sufficient space for the frame.
743 *
744 * @deprecated Bad interface, do NOT use it! Only for tstIntNetR0.
745 */
746DECLINLINE(uint32_t) IntNetRingReadAndSkipFrame(PINTNETRINGBUF pRingBuf, void *pvFrameDst)
747{
748 INTNETRINGBUF_ASSERT_SANITY(pRingBuf);
749
750 uint32_t offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
751 uint32_t const offWriteCom = ASMAtomicUoReadU32(&pRingBuf->offWriteCom);
752 if (offRead == offWriteCom)
753 return 0;
754
755 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offRead);
756 INTNETHDR_ASSERT_SANITY(pHdr, pRingBuf);
757
758 uint32_t const cbFrame = pHdr->cbFrame;
759 int32_t const offFrame = pHdr->offFrame;
760 const void *pvFrameSrc = (uint8_t *)pHdr + offFrame;
761 memcpy(pvFrameDst, pvFrameSrc, cbFrame);
762#ifdef INTNET_POISON_READ_FRAMES
763 memset((void *)pvFrameSrc, 0xfe, RT_ALIGN_32(cbFrame, INTNETHDR_ALIGNMENT));
764 memset(pHdr, 0xef, sizeof(*pHdr));
765#endif
766
767 /* skip the frame */
768 offRead += offFrame + cbFrame;
769 offRead = RT_ALIGN_32(offRead, INTNETHDR_ALIGNMENT);
770 Assert(offRead <= pRingBuf->offEnd && offRead >= pRingBuf->offStart);
771 if (offRead >= pRingBuf->offEnd)
772 offRead = pRingBuf->offStart;
773 ASMAtomicWriteU32(&pRingBuf->offReadX, offRead);
774 return cbFrame;
775}
776
777
778/**
779 * Initializes a buffer structure.
780 *
781 * @param pIntBuf The internal networking interface buffer. This
782 * expected to be cleared prior to calling this
783 * function.
784 * @param cbBuf The size of the whole buffer.
785 * @param cbRecv The receive size.
786 * @param cbSend The send size.
787 */
788DECLINLINE(void) IntNetBufInit(PINTNETBUF pIntBuf, uint32_t cbBuf, uint32_t cbRecv, uint32_t cbSend)
789{
790 AssertCompileSizeAlignment(INTNETBUF, INTNETHDR_ALIGNMENT);
791 AssertCompileSizeAlignment(INTNETBUF, INTNETRINGBUF_ALIGNMENT);
792 Assert(cbBuf >= sizeof(INTNETBUF) + cbRecv + cbSend);
793 Assert(RT_ALIGN_32(cbRecv, INTNETRINGBUF_ALIGNMENT) == cbRecv);
794 Assert(RT_ALIGN_32(cbSend, INTNETRINGBUF_ALIGNMENT) == cbSend);
795 Assert(ASMMemIsAll8(pIntBuf, cbBuf, '\0') == NULL);
796
797 pIntBuf->u32Magic = INTNETBUF_MAGIC;
798 pIntBuf->cbBuf = cbBuf;
799 pIntBuf->cbRecv = cbRecv;
800 pIntBuf->cbSend = cbSend;
801
802 /* receive ring buffer. */
803 uint32_t offBuf = RT_ALIGN_32(sizeof(INTNETBUF), INTNETRINGBUF_ALIGNMENT) - RT_OFFSETOF(INTNETBUF, Recv);
804 pIntBuf->Recv.offStart = offBuf;
805 pIntBuf->Recv.offReadX = offBuf;
806 pIntBuf->Recv.offWriteInt = offBuf;
807 pIntBuf->Recv.offWriteCom = offBuf;
808 pIntBuf->Recv.offEnd = offBuf + cbRecv;
809
810 /* send ring buffer. */
811 offBuf += cbRecv + RT_OFFSETOF(INTNETBUF, Recv) - RT_OFFSETOF(INTNETBUF, Send);
812 pIntBuf->Send.offStart = offBuf;
813 pIntBuf->Send.offReadX = offBuf;
814 pIntBuf->Send.offWriteCom = offBuf;
815 pIntBuf->Send.offWriteInt = offBuf;
816 pIntBuf->Send.offEnd = offBuf + cbSend;
817 Assert(cbBuf >= offBuf + cbSend);
818}
819
820#endif /* __cplusplus */
821
822#endif
823
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