VirtualBox

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

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

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use