VirtualBox

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

Last change on this file since 28329 was 28075, checked in by vboxsync, 15 years ago

intnetinline.h: INTNETRingCommitFrameEx must account for the GSO header.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.5 KB
Line 
1/* $Id: intnetinline.h 28075 2010-04-07 23:42:21Z 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-2010 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
31 * Clara, CA 95054 USA or visit http://www.sun.com if you need
32 * additional information or have any questions.
33 */
34
35#ifndef ___VBox_intnetinline_h
36#define ___VBox_intnetinline_h
37
38#include <VBox/intnet.h>
39#include <iprt/string.h>
40#include <iprt/assert.h>
41#include <iprt/err.h>
42#include <VBox/log.h>
43
44
45
46/**
47 * Valid internal networking frame type.
48 *
49 * @returns true / false.
50 * @param u16Type The frame type to check.
51 */
52DECLINLINE(bool) INETNETIsValidFrameType(uint16_t u16Type)
53{
54 if (RT_LIKELY( u16Type == INTNETHDR_TYPE_FRAME
55 || u16Type == INTNETHDR_TYPE_GSO
56 || u16Type == INTNETHDR_TYPE_PADDING))
57 return true;
58 return false;
59}
60
61
62/**
63 * Partly initializes a scatter / gather buffer, leaving the segments to the
64 * caller.
65 *
66 * @returns Pointer to the start of the frame.
67 * @param pSG Pointer to the scatter / gather structure.
68 * @param cbTotal The total size.
69 * @param cSegs The number of segments.
70 * @param cSegsUsed The number of used segments.
71 */
72DECLINLINE(void) INTNETSgInitTempSegs(PINTNETSG pSG, uint32_t cbTotal, unsigned cSegs, unsigned cSegsUsed)
73{
74 pSG->pvOwnerData = NULL;
75 pSG->pvUserData = NULL;
76 pSG->pvUserData2 = NULL;
77 pSG->cbTotal = cbTotal;
78 pSG->cUsers = 1;
79 pSG->fFlags = INTNETSG_FLAGS_TEMP;
80 pSG->GsoCtx.u8Type = (uint8_t)PDMNETWORKGSOTYPE_INVALID;
81 pSG->GsoCtx.cbHdrs = 0;
82 pSG->GsoCtx.cbMaxSeg= 0;
83 pSG->GsoCtx.offHdr1 = 0;
84 pSG->GsoCtx.offHdr2 = 0;
85 pSG->GsoCtx.au8Unused[0] = 0;
86 pSG->GsoCtx.au8Unused[1] = 0;
87#if ARCH_BITS == 64
88 pSG->uPadding = 0;
89#endif
90 pSG->cSegsAlloc = (uint16_t)cSegs;
91 Assert(pSG->cSegsAlloc == cSegs);
92 pSG->cSegsUsed = (uint16_t)cSegsUsed;
93 Assert(pSG->cSegsUsed == cSegsUsed);
94 Assert(cSegs >= cSegsUsed);
95}
96
97
98/**
99 * Partly initializes a scatter / gather buffer w/ GSO, leaving the segments to
100 * the caller.
101 *
102 * @returns Pointer to the start of the frame.
103 * @param pSG Pointer to the scatter / gather structure.
104 * @param cbTotal The total size.
105 * @param cSegs The number of segments.
106 * @param cSegsUsed The number of used segments.
107 * @param pGso The GSO context.
108 */
109DECLINLINE(void) INTNETSgInitTempSegsGso(PINTNETSG pSG, uint32_t cbTotal, unsigned cSegs,
110 unsigned cSegsUsed, PCPDMNETWORKGSO pGso)
111{
112 pSG->pvOwnerData = NULL;
113 pSG->pvUserData = NULL;
114 pSG->pvUserData2 = NULL;
115 pSG->cbTotal = cbTotal;
116 pSG->cUsers = 1;
117 pSG->fFlags = INTNETSG_FLAGS_TEMP;
118 pSG->GsoCtx.u8Type = pGso->u8Type;
119 pSG->GsoCtx.cbHdrs = pGso->cbHdrs;
120 pSG->GsoCtx.cbMaxSeg= pGso->cbMaxSeg;
121 pSG->GsoCtx.offHdr1 = pGso->offHdr1;
122 pSG->GsoCtx.offHdr2 = pGso->offHdr2;
123 pSG->GsoCtx.au8Unused[0] = 0;
124 pSG->GsoCtx.au8Unused[1] = 0;
125#if ARCH_BITS == 64
126 pSG->uPadding = 0;
127#endif
128 pSG->cSegsAlloc = (uint16_t)cSegs;
129 Assert(pSG->cSegsAlloc == cSegs);
130 pSG->cSegsUsed = (uint16_t)cSegsUsed;
131 Assert(pSG->cSegsUsed == cSegsUsed);
132 Assert(cSegs >= cSegsUsed);
133}
134
135
136
137/**
138 * Initializes a scatter / gather buffer describing a simple linear buffer.
139 *
140 * @returns Pointer to the start of the frame.
141 * @param pSG Pointer to the scatter / gather structure.
142 * @param pvFrame Pointer to the frame
143 * @param cbFrame The size of the frame.
144 */
145DECLINLINE(void) INTNETSgInitTemp(PINTNETSG pSG, void *pvFrame, uint32_t cbFrame)
146{
147 INTNETSgInitTempSegs(pSG, cbFrame, 1, 1);
148 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
149 pSG->aSegs[0].pv = pvFrame;
150 pSG->aSegs[0].cb = cbFrame;
151}
152
153/**
154 * Initializes a scatter / gather buffer describing a simple linear buffer.
155 *
156 * @returns Pointer to the start of the frame.
157 * @param pSG Pointer to the scatter / gather structure.
158 * @param pvFrame Pointer to the frame
159 * @param cbFrame The size of the frame.
160 * @param pGso The GSO context.
161 */
162DECLINLINE(void) INTNETSgInitTempGso(PINTNETSG pSG, void *pvFrame, uint32_t cbFrame, PCPDMNETWORKGSO pGso)
163{
164 INTNETSgInitTempSegsGso(pSG, cbFrame, 1, 1, pGso);
165 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
166 pSG->aSegs[0].pv = pvFrame;
167 pSG->aSegs[0].cb = cbFrame;
168}
169
170
171/**
172 * Reads an entire SG into a fittingly size buffer.
173 *
174 * @param pSG The SG list to read.
175 * @param pvBuf The buffer to read into (at least pSG->cbTotal in size).
176 */
177DECLINLINE(void) INTNETSgRead(PCINTNETSG pSG, void *pvBuf)
178{
179 memcpy(pvBuf, pSG->aSegs[0].pv, pSG->aSegs[0].cb);
180 if (pSG->cSegsUsed == 1)
181 Assert(pSG->cbTotal == pSG->aSegs[0].cb);
182 else
183 {
184 uint8_t *pbDst = (uint8_t *)pvBuf + pSG->aSegs[0].cb;
185 unsigned iSeg = 0;
186 unsigned const cSegs = pSG->cSegsUsed;
187 while (++iSeg < cSegs)
188 {
189 uint32_t cbSeg = pSG->aSegs[iSeg].cb;
190 Assert((uintptr_t)pbDst - (uintptr_t)pvBuf + cbSeg <= pSG->cbTotal);
191 memcpy(pbDst, pSG->aSegs[iSeg].pv, cbSeg);
192 pbDst += cbSeg;
193 }
194 }
195}
196
197
198/**
199 * Reads a portion of an SG into a buffer.
200 *
201 * @param pSG The SG list to read.
202 * @param offSrc The offset to start start copying from.
203 * @param cbToRead The number of bytes to copy.
204 * @param pvBuf The buffer to read into, cb or more in size.
205 */
206DECLINLINE(void) INTNETSgReadEx(PCINTNETSG pSG, uint32_t offSrc, uint32_t cbToRead, void *pvBuf)
207{
208 uint8_t *pbDst = (uint8_t *)pvBuf;
209 uint32_t iSeg = 0;
210
211 /* validate assumptions */
212 Assert(cbToRead < pSG->cbTotal);
213 Assert(offSrc <= pSG->cbTotal);
214 Assert(offSrc + cbToRead <= pSG->cbTotal);
215
216 /* Find the right segment and copy any bits from within the segment. */
217 while (offSrc)
218 {
219 uint32_t cbSeg = pSG->aSegs[iSeg].cb;
220 if (offSrc < cbSeg)
221 {
222 uint32_t cbChunk = cbSeg - offSrc;
223 if (cbChunk >= cbToRead)
224 {
225 memcpy(pbDst, (uint8_t const *)pSG->aSegs[iSeg].pv + offSrc, cbToRead);
226 return;
227 }
228
229 memcpy(pbDst, (uint8_t const *)pSG->aSegs[iSeg].pv + offSrc, cbChunk);
230 pbDst += cbChunk;
231 cbToRead -= cbChunk;
232 break;
233 }
234
235 /* advance */
236 offSrc -= cbSeg;
237 iSeg++;
238 }
239
240 /* We're not at the start of a segment, copy until we're done. */
241 for (;;)
242 {
243 uint32_t cbSeg = pSG->aSegs[iSeg].cb;
244 if (cbSeg >= cbToRead)
245 {
246 memcpy(pbDst, pSG->aSegs[iSeg].pv, cbToRead);
247 return;
248 }
249
250 memcpy(pbDst, pSG->aSegs[iSeg].pv, cbSeg);
251 pbDst += cbSeg;
252 cbToRead -= cbSeg;
253 iSeg++;
254 Assert(iSeg < pSG->cSegsUsed);
255 }
256}
257
258#ifdef __cplusplus
259
260/**
261 * Get the amount of space available for writing.
262 *
263 * @returns Number of available bytes.
264 * @param pRingBuf The ring buffer.
265 */
266DECLINLINE(uint32_t) INTNETRingGetWritable(PINTNETRINGBUF pRingBuf)
267{
268 uint32_t const offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
269 uint32_t const offWriteInt = ASMAtomicUoReadU32(&pRingBuf->offWriteInt);
270 return offRead <= offWriteInt
271 ? pRingBuf->offEnd - offWriteInt + offRead - pRingBuf->offStart - 1
272 : offRead - offWriteInt - 1;
273}
274
275
276/**
277 * Checks if the ring has more for us to read.
278 *
279 * @returns Number of ready bytes.
280 * @param pRingBuf The ring buffer.
281 */
282DECLINLINE(bool) INTNETRingHasMoreToRead(PINTNETRINGBUF pRingBuf)
283{
284 uint32_t const offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
285 uint32_t const offWriteCom = ASMAtomicUoReadU32(&pRingBuf->offWriteCom);
286 return offRead != offWriteCom;
287}
288
289
290/**
291 * Gets the next frame to read.
292 *
293 * @returns Pointer to the next frame. NULL if done.
294 * @param pRingBuf The ring buffer.
295 */
296DECLINLINE(PINTNETHDR) INTNETRingGetNextFrameToRead(PINTNETRINGBUF pRingBuf)
297{
298 uint32_t const offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
299 uint32_t const offWriteCom = ASMAtomicUoReadU32(&pRingBuf->offWriteCom);
300 if (offRead == offWriteCom)
301 return NULL;
302 return (PINTNETHDR)((uint8_t *)pRingBuf + offRead);
303}
304
305
306/**
307 * Get the amount of data ready for reading.
308 *
309 * @returns Number of ready bytes.
310 * @param pRingBuf The ring buffer.
311 */
312DECLINLINE(uint32_t) INTNETRingGetReadable(PINTNETRINGBUF pRingBuf)
313{
314 uint32_t const offRead = ASMAtomicUoReadU32(&pRingBuf->offReadX);
315 uint32_t const offWriteCom = ASMAtomicUoReadU32(&pRingBuf->offWriteCom);
316 return offRead <= offWriteCom
317 ? offWriteCom - offRead
318 : pRingBuf->offEnd - offRead + offWriteCom - pRingBuf->offStart;
319}
320
321
322/**
323 * Calculates the pointer to the frame.
324 *
325 * @returns Pointer to the start of the frame.
326 * @param pHdr Pointer to the packet header
327 * @param pBuf The buffer the header is within. Only used in strict builds.
328 */
329DECLINLINE(void *) INTNETHdrGetFramePtr(PCINTNETHDR pHdr, PCINTNETBUF pBuf)
330{
331 uint8_t *pu8 = (uint8_t *)pHdr + pHdr->offFrame;
332#ifdef VBOX_STRICT
333 const uintptr_t off = (uintptr_t)pu8 - (uintptr_t)pBuf;
334 Assert(INETNETIsValidFrameType(pHdr->u16Type));
335 Assert(off < pBuf->cbBuf);
336 Assert(off + pHdr->cbFrame <= pBuf->cbBuf);
337#endif
338 NOREF(pBuf);
339 return pu8;
340}
341
342
343/**
344 * Calculates the pointer to the GSO context.
345 *
346 * ASSUMES the frame is a GSO frame.
347 *
348 * The GSO context is immediately followed by the headers and payload. The size
349 * is INTNETBUF::cbFrame - sizeof(PDMNETWORKGSO).
350 *
351 * @returns Pointer to the GSO context.
352 * @param pHdr Pointer to the packet header
353 * @param pBuf The buffer the header is within. Only used in strict builds.
354 */
355DECLINLINE(PPDMNETWORKGSO) INTNETHdrGetGsoContext(PCINTNETHDR pHdr, PCINTNETBUF pBuf)
356{
357 PPDMNETWORKGSO pGso = (PPDMNETWORKGSO)((uint8_t *)pHdr + pHdr->offFrame);
358#ifdef VBOX_STRICT
359 const uintptr_t off = (uintptr_t)pGso - (uintptr_t)pBuf;
360 Assert(pHdr->u16Type == INTNETHDR_TYPE_GSO);
361 Assert(off < pBuf->cbBuf);
362 Assert(off + pHdr->cbFrame <= pBuf->cbBuf);
363#endif
364 NOREF(pBuf);
365 return pGso;
366}
367
368
369/**
370 * Skips to the next (read) frame in the buffer.
371 *
372 * @param pRingBuf The ring buffer in question.
373 */
374DECLINLINE(void) INTNETRingSkipFrame(PINTNETRINGBUF pRingBuf)
375{
376 uint32_t const offReadOld = ASMAtomicUoReadU32(&pRingBuf->offReadX);
377 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offReadOld);
378 Assert(offReadOld >= pRingBuf->offStart);
379 Assert(offReadOld < pRingBuf->offEnd);
380 Assert(RT_ALIGN_PT(pHdr, INTNETHDR_ALIGNMENT, INTNETHDR *) == pHdr);
381 Assert(INETNETIsValidFrameType(pHdr->u16Type));
382
383 /* skip the frame */
384 uint32_t offReadNew = offReadOld + pHdr->offFrame + pHdr->cbFrame;
385 offReadNew = RT_ALIGN_32(offReadNew, INTNETHDR_ALIGNMENT);
386 Assert(offReadNew <= pRingBuf->offEnd && offReadNew >= pRingBuf->offStart);
387 if (offReadNew >= pRingBuf->offEnd)
388 offReadNew = pRingBuf->offStart;
389 Log2(("INTNETRingSkipFrame: offReadX: %#x -> %#x (1)\n", offReadOld, offReadNew));
390#ifdef INTNET_POISON_READ_FRAMES
391 memset((uint8_t *)pHdr + pHdr->offFrame, 0xfe, RT_ALIGN_32(pHdr->cbFrame, INTNETHDR_ALIGNMENT));
392 memset(pHdr, 0xef, sizeof(*pHdr));
393#endif
394 ASMAtomicWriteU32(&pRingBuf->offReadX, offReadNew);
395}
396
397
398/**
399 * Allocates a frame in the specified ring.
400 *
401 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
402 * @param pRingBuf The ring buffer.
403 * @param cbFrame The frame size.
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, uint16_t u16Type,
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_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
431 return VERR_WRONG_ORDER; /* race */
432 Log2(("INTNETRingAllocateFrame: offWriteInt: %#x -> %#x (1) (offRead=%#x)\n", offWriteInt, offNew, offRead));
433
434 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
435 pHdr->u16Type = u16Type;
436 pHdr->cbFrame = (uint16_t)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_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
452 return VERR_WRONG_ORDER; /* race */
453 Log2(("INTNETRingAllocateFrame: offWriteInt: %#x -> %#x (2) (offRead=%#x)\n", offWriteInt, offNew, offRead));
454
455 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
456 pHdr->u16Type = u16Type;
457 pHdr->cbFrame = (uint16_t)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_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
472 return VERR_WRONG_ORDER; /* race */
473 Log2(("INTNETRingAllocateFrame: offWriteInt: %#x -> %#x (3) (offRead=%#x)\n", offWriteInt, offNew, offRead));
474
475 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pRingBuf + offWriteInt);
476 pHdr->u16Type = u16Type;
477 pHdr->cbFrame = (uint16_t)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 STAM_REL_COUNTER_INC(&pRingBuf->cOverflows);
487 return VERR_BUFFER_OVERFLOW;
488}
489
490
491/**
492 * Allocates a normal frame in the specified ring.
493 *
494 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
495 * @param pRingBuf The ring buffer.
496 * @param cbFrame The frame size.
497 * @param ppHdr Where to return the frame header.
498 * Don't touch this!
499 * @param ppvFrame Where to return the frame pointer.
500 */
501DECLINLINE(int) INTNETRingAllocateFrame(PINTNETRINGBUF pRingBuf, uint32_t cbFrame, PINTNETHDR *ppHdr, void **ppvFrame)
502{
503 return intnetRingAllocateFrameInternal(pRingBuf, cbFrame, INTNETHDR_TYPE_FRAME, ppHdr, ppvFrame);
504}
505
506
507/**
508 * Allocates a GSO frame in the specified ring.
509 *
510 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
511 * @param pRingBuf The ring buffer.
512 * @param cbFrame The frame size.
513 * @param pGso Pointer to the GSO context.
514 * @param ppHdr Where to return the frame header.
515 * Don't touch this!
516 * @param ppvFrame Where to return the frame pointer.
517 */
518DECLINLINE(int) INTNETRingAllocateGsoFrame(PINTNETRINGBUF pRingBuf, uint32_t cbFrame, PCPDMNETWORKGSO pGso,
519 PINTNETHDR *ppHdr, void **ppvFrame)
520{
521 void *pvFrame = NULL; /* gcc maybe used uninitialized */
522 int rc = intnetRingAllocateFrameInternal(pRingBuf, cbFrame + sizeof(*pGso), INTNETHDR_TYPE_GSO, ppHdr, &pvFrame);
523 if (RT_SUCCESS(rc))
524 {
525 PPDMNETWORKGSO pGsoCopy = (PPDMNETWORKGSO)pvFrame;
526 *pGsoCopy = *pGso;
527 *ppvFrame = pGsoCopy + 1;
528 }
529 return rc;
530}
531
532
533/**
534 * Commits a frame.
535 *
536 * Make sure to commit the frames in the order they've been allocated!
537 *
538 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
539 * @param pRingBuf The ring buffer.
540 * @param pHdr The frame header returned by
541 * INTNETRingAllocateFrame.
542 */
543DECLINLINE(void) INTNETRingCommitFrame(PINTNETRINGBUF pRingBuf, PINTNETHDR pHdr)
544{
545 /*
546 * Validate input and commit order.
547 */
548 INTNETRINGBUF_ASSERT_SANITY(pRingBuf);
549 INTNETHDR_ASSERT_SANITY(pHdr, pRingBuf);
550 Assert(pRingBuf->offWriteCom == ((uintptr_t)pHdr - (uintptr_t)pRingBuf));
551
552 /*
553 * Figure out the offWriteCom for this packet and update the ring.
554 */
555 const uint32_t cbFrame = pHdr->cbFrame;
556 const uint32_t cb = RT_ALIGN_32(cbFrame, INTNETHDR_ALIGNMENT);
557 uint32_t offWriteCom = (uint32_t)((uintptr_t)pHdr - (uintptr_t)pRingBuf)
558 + pHdr->offFrame
559 + cb;
560 if (offWriteCom >= pRingBuf->offEnd)
561 {
562 Assert(offWriteCom == pRingBuf->offEnd);
563 offWriteCom = pRingBuf->offStart;
564 }
565 Log2(("INTNETRingCommitFrame: offWriteCom: %#x -> %#x (offRead=%#x)\n", pRingBuf->offWriteCom, offWriteCom, pRingBuf->offReadX));
566 ASMAtomicWriteU32(&pRingBuf->offWriteCom, offWriteCom);
567 STAM_REL_COUNTER_ADD(&pRingBuf->cbStatWritten, cbFrame);
568 STAM_REL_COUNTER_INC(&pRingBuf->cStatFrames);
569}
570
571
572/**
573 * Commits a frame and injects a filler frame if not all of the buffer was used.
574 *
575 * Make sure to commit the frames in the order they've been allocated!
576 *
577 * @returns VINF_SUCCESS or VERR_BUFFER_OVERFLOW.
578 * @param pRingBuf The ring buffer.
579 * @param pHdr The frame header returned by
580 * INTNETRingAllocateFrame.
581 * @param cbUsed The amount of space actually used. This does
582 * not include the GSO part.
583 */
584DECLINLINE(void) INTNETRingCommitFrameEx(PINTNETRINGBUF pRingBuf, PINTNETHDR pHdr, size_t cbUsed)
585{
586 /*
587 * Validate input and commit order.
588 */
589 INTNETRINGBUF_ASSERT_SANITY(pRingBuf);
590 INTNETHDR_ASSERT_SANITY(pHdr, pRingBuf);
591 Assert(pRingBuf->offWriteCom == ((uintptr_t)pHdr - (uintptr_t)pRingBuf));
592
593 if (pHdr->u16Type == INTNETHDR_TYPE_GSO)
594 cbUsed += sizeof(PDMNETWORKGSO);
595
596 /*
597 * Calc the new write commit offset.
598 */
599 const uint32_t cbAlignedFrame = RT_ALIGN_32(pHdr->cbFrame, INTNETHDR_ALIGNMENT);
600 const uint32_t cbAlignedUsed = RT_ALIGN_32(cbUsed, INTNETHDR_ALIGNMENT);
601 uint32_t offWriteCom = (uint32_t)((uintptr_t)pHdr - (uintptr_t)pRingBuf)
602 + pHdr->offFrame
603 + cbAlignedFrame;
604 if (offWriteCom >= pRingBuf->offEnd)
605 {
606 Assert(offWriteCom == pRingBuf->offEnd);
607 offWriteCom = pRingBuf->offStart;
608 }
609
610 /*
611 * Insert a dummy frame to pad any unused space.
612 */
613 if (cbAlignedFrame != cbAlignedUsed)
614 {
615 /** @todo Later: Try unallocate the extra memory. */
616 PINTNETHDR pHdrPadding = (PINTNETHDR)((uint8_t *)pHdr + pHdr->offFrame + cbAlignedUsed);
617 pHdrPadding->u16Type = INTNETHDR_TYPE_PADDING;
618 pHdrPadding->cbFrame = (uint16_t)(cbAlignedFrame - cbAlignedUsed - sizeof(INTNETHDR));
619 pHdrPadding->offFrame = sizeof(INTNETHDR);
620 pHdr->cbFrame = (uint16_t)cbUsed;
621 }
622
623 Log2(("INTNETRingCommitFrame: offWriteCom: %#x -> %#x (offRead=%#x)\n", pRingBuf->offWriteCom, offWriteCom, pRingBuf->offReadX));
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_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
665 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->u16Type = INTNETHDR_TYPE_FRAME;
670 pHdr->cbFrame = (uint16_t)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_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
690 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->u16Type = INTNETHDR_TYPE_FRAME;
695 pHdr->cbFrame = (uint16_t)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_UNLIKELY(!ASMAtomicCmpXchgU32(&pRingBuf->offWriteInt, offNew, offWriteInt)))
714 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->u16Type = INTNETHDR_TYPE_FRAME;
719 pHdr->cbFrame = (uint16_t)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 pRingBuff 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(ASMMemIsAll8(pIntBuf, cbBuf, '\0') == NULL);
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