VirtualBox

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

Last change on this file since 60871 was 59747, checked in by vboxsync, 9 years ago

iprt/asm.h: Cleaned up the ASMMemIsAll8/U32 mess and implmeneted the former in assembly. (Found inverted usage due to bad naming in copyUtf8Block, but it is fortunately an unused method.) Replaces the complicated ASMBitFirstSet based scanning in RTSgBufIsZero with a simple call to the new ASMMemIsZero function.

  • 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 59747 2016-02-19 23:18:18Z 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-2015 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_OFFSETOF(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_OFFSETOF(INTNETBUF, Recv) - RT_OFFSETOF(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.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette