VirtualBox

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

Last change on this file since 97441 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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