VirtualBox

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

Last change on this file since 76585 was 76585, checked in by vboxsync, 6 years ago

*: scm --fix-header-guard-endif

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