VirtualBox

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

Last change on this file since 103393 was 103005, checked in by vboxsync, 10 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

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