VirtualBox

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

Last change on this file since 99739 was 99739, checked in by vboxsync, 13 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use