3 * Copyright (c) 2009, Microsoft Corporation.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place - Suite 330, Boston, MA 02111-1307 USA.
19 * Haiyang Zhang <haiyangz@microsoft.com>
20 * Hank Janssen <hjanssen@microsoft.com>
25 #include "include/logging.h"
26 #include "RingBuffer.h"
32 // Amount of space to write to
33 #define BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r))?((z) - ((w) - (r))):((r) - (w))
39 GetRingBufferAvailBytes()
42 Get number of bytes available to read and to write to
43 for the specified ring buffer
47 GetRingBufferAvailBytes(RING_BUFFER_INFO
*rbi
, UINT32
*read
, UINT32
*write
)
49 UINT32 read_loc
,write_loc
;
51 // Capture the read/write indices before they changed
52 read_loc
= rbi
->RingBuffer
->ReadIndex
;
53 write_loc
= rbi
->RingBuffer
->WriteIndex
;
55 *write
= BYTES_AVAIL_TO_WRITE(read_loc
, write_loc
, rbi
->RingDataSize
);
56 *read
= rbi
->RingDataSize
- *write
;
62 GetNextWriteLocation()
65 Get the next write location for the specified ring buffer
69 GetNextWriteLocation(RING_BUFFER_INFO
* RingInfo
)
71 UINT32 next
= RingInfo
->RingBuffer
->WriteIndex
;
73 ASSERT(next
< RingInfo
->RingDataSize
);
81 SetNextWriteLocation()
84 Set the next write location for the specified ring buffer
88 SetNextWriteLocation(RING_BUFFER_INFO
* RingInfo
, UINT32 NextWriteLocation
)
90 RingInfo
->RingBuffer
->WriteIndex
= NextWriteLocation
;
99 Get the next read location for the specified ring buffer
103 GetNextReadLocation(RING_BUFFER_INFO
* RingInfo
)
105 UINT32 next
= RingInfo
->RingBuffer
->ReadIndex
;
107 ASSERT(next
< RingInfo
->RingDataSize
);
115 GetNextReadLocationWithOffset()
118 Get the next read location + offset for the specified ring buffer.
119 This allows the caller to skip
123 GetNextReadLocationWithOffset(RING_BUFFER_INFO
* RingInfo
, UINT32 Offset
)
125 UINT32 next
= RingInfo
->RingBuffer
->ReadIndex
;
127 ASSERT(next
< RingInfo
->RingDataSize
);
129 next
%= RingInfo
->RingDataSize
;
137 SetNextReadLocation()
140 Set the next read location for the specified ring buffer
144 SetNextReadLocation(RING_BUFFER_INFO
* RingInfo
, UINT32 NextReadLocation
)
146 RingInfo
->RingBuffer
->ReadIndex
= NextReadLocation
;
156 Get the start of the ring buffer
160 GetRingBuffer(RING_BUFFER_INFO
* RingInfo
)
162 return (void *)RingInfo
->RingBuffer
->Buffer
;
172 Get the size of the ring buffer
176 GetRingBufferSize(RING_BUFFER_INFO
* RingInfo
)
178 return RingInfo
->RingDataSize
;
184 GetRingBufferIndices()
187 Get the read and write indices as UINT64 of the specified ring buffer
191 GetRingBufferIndices(RING_BUFFER_INFO
* RingInfo
)
193 return ((UINT64
)RingInfo
->RingBuffer
->WriteIndex
<< 32) || RingInfo
->RingBuffer
->ReadIndex
;
203 Dump out to console the ring buffer info
207 DumpRingInfo(RING_BUFFER_INFO
* RingInfo
, char *Prefix
)
209 UINT32 bytesAvailToWrite
;
210 UINT32 bytesAvailToRead
;
212 GetRingBufferAvailBytes(RingInfo
, &bytesAvailToRead
, &bytesAvailToWrite
);
214 DPRINT(VMBUS
, DEBUG_RING_LVL
, "%s <<ringinfo %p buffer %p avail write %u avail read %u read idx %u write idx %u>>",
217 RingInfo
->RingBuffer
->Buffer
,
220 RingInfo
->RingBuffer
->ReadIndex
,
221 RingInfo
->RingBuffer
->WriteIndex
);
229 RING_BUFFER_INFO
*RingInfo
,
230 UINT32 StartWriteOffset
,
236 RING_BUFFER_INFO
*RingInfo
,
239 UINT32 StartReadOffset
);
246 RingBufferGetDebugInfo()
249 Get various debug metrics for the specified ring buffer
253 RingBufferGetDebugInfo(
254 RING_BUFFER_INFO
*RingInfo
,
255 RING_BUFFER_DEBUG_INFO
*DebugInfo
258 UINT32 bytesAvailToWrite
;
259 UINT32 bytesAvailToRead
;
261 if (RingInfo
->RingBuffer
)
263 GetRingBufferAvailBytes(RingInfo
, &bytesAvailToRead
, &bytesAvailToWrite
);
265 DebugInfo
->BytesAvailToRead
= bytesAvailToRead
;
266 DebugInfo
->BytesAvailToWrite
= bytesAvailToWrite
;
267 DebugInfo
->CurrentReadIndex
= RingInfo
->RingBuffer
->ReadIndex
;
268 DebugInfo
->CurrentWriteIndex
= RingInfo
->RingBuffer
->WriteIndex
;
270 DebugInfo
->CurrentInterruptMask
= RingInfo
->RingBuffer
->InterruptMask
;
278 GetRingBufferInterruptMask()
281 Get the interrupt mask for the specified ring buffer
285 GetRingBufferInterruptMask(
286 RING_BUFFER_INFO
*rbi
289 return rbi
->RingBuffer
->InterruptMask
;
298 Initialize the ring buffer
303 RING_BUFFER_INFO
*RingInfo
,
308 ASSERT(sizeof(RING_BUFFER
) == PAGE_SIZE
);
310 memset(RingInfo
, 0, sizeof(RING_BUFFER_INFO
));
312 RingInfo
->RingBuffer
= (RING_BUFFER
*)Buffer
;
313 RingInfo
->RingBuffer
->ReadIndex
= RingInfo
->RingBuffer
->WriteIndex
= 0;
315 RingInfo
->RingSize
= BufferLen
;
316 RingInfo
->RingDataSize
= BufferLen
- sizeof(RING_BUFFER
);
318 RingInfo
->RingLock
= SpinlockCreate();
329 Cleanup the ring buffer
334 RING_BUFFER_INFO
* RingInfo
337 SpinlockClose(RingInfo
->RingLock
);
346 Write to the ring buffer
351 RING_BUFFER_INFO
* OutRingInfo
,
352 SG_BUFFER_LIST SgBuffers
[],
357 UINT32 byteAvailToWrite
;
358 UINT32 byteAvailToRead
;
359 UINT32 totalBytesToWrite
=0;
361 volatile UINT32 nextWriteLocation
;
362 UINT64 prevIndices
=0;
366 for (i
=0; i
< SgBufferCount
; i
++)
368 totalBytesToWrite
+= SgBuffers
[i
].Length
;
371 totalBytesToWrite
+= sizeof(UINT64
);
373 SpinlockAcquire(OutRingInfo
->RingLock
);
375 GetRingBufferAvailBytes(OutRingInfo
, &byteAvailToRead
, &byteAvailToWrite
);
377 DPRINT_DBG(VMBUS
, "Writing %u bytes...", totalBytesToWrite
);
379 //DumpRingInfo(OutRingInfo, "BEFORE ");
381 // If there is only room for the packet, assume it is full. Otherwise, the next time around, we think the ring buffer
382 // is empty since the read index == write index
383 if (byteAvailToWrite
<= totalBytesToWrite
)
385 DPRINT_DBG(VMBUS
, "No more space left on outbound ring buffer (needed %u, avail %u)", totalBytesToWrite
, byteAvailToWrite
);
387 SpinlockRelease(OutRingInfo
->RingLock
);
394 // Write to the ring buffer
395 nextWriteLocation
= GetNextWriteLocation(OutRingInfo
);
397 for (i
=0; i
< SgBufferCount
; i
++)
399 nextWriteLocation
= CopyToRingBuffer(OutRingInfo
,
402 SgBuffers
[i
].Length
);
405 // Set previous packet start
406 prevIndices
= GetRingBufferIndices(OutRingInfo
);
408 nextWriteLocation
= CopyToRingBuffer(OutRingInfo
,
413 // Make sure we flush all writes before updating the writeIndex
416 // Now, update the write location
417 SetNextWriteLocation(OutRingInfo
, nextWriteLocation
);
419 //DumpRingInfo(OutRingInfo, "AFTER ");
421 SpinlockRelease(OutRingInfo
->RingLock
);
435 Read without advancing the read index
440 RING_BUFFER_INFO
* InRingInfo
,
445 UINT32 bytesAvailToWrite
;
446 UINT32 bytesAvailToRead
;
447 UINT32 nextReadLocation
=0;
449 SpinlockAcquire(InRingInfo
->RingLock
);
451 GetRingBufferAvailBytes(InRingInfo
, &bytesAvailToRead
, &bytesAvailToWrite
);
453 // Make sure there is something to read
454 if (bytesAvailToRead
< BufferLen
)
456 //DPRINT_DBG(VMBUS, "got callback but not enough to read <avail to read %d read size %d>!!", bytesAvailToRead, BufferLen);
458 SpinlockRelease(InRingInfo
->RingLock
);
463 // Convert to byte offset
464 nextReadLocation
= GetNextReadLocation(InRingInfo
);
466 nextReadLocation
= CopyFromRingBuffer(InRingInfo
,
471 SpinlockRelease(InRingInfo
->RingLock
);
483 Read and advance the read index
488 RING_BUFFER_INFO
* InRingInfo
,
494 UINT32 bytesAvailToWrite
;
495 UINT32 bytesAvailToRead
;
496 UINT32 nextReadLocation
=0;
497 UINT64 prevIndices
=0;
499 ASSERT(BufferLen
> 0);
501 SpinlockAcquire(InRingInfo
->RingLock
);
503 GetRingBufferAvailBytes(InRingInfo
, &bytesAvailToRead
, &bytesAvailToWrite
);
505 DPRINT_DBG(VMBUS
, "Reading %u bytes...", BufferLen
);
507 //DumpRingInfo(InRingInfo, "BEFORE ");
509 // Make sure there is something to read
510 if (bytesAvailToRead
< BufferLen
)
512 DPRINT_DBG(VMBUS
, "got callback but not enough to read <avail to read %d read size %d>!!", bytesAvailToRead
, BufferLen
);
514 SpinlockRelease(InRingInfo
->RingLock
);
519 nextReadLocation
= GetNextReadLocationWithOffset(InRingInfo
, Offset
);
521 nextReadLocation
= CopyFromRingBuffer(InRingInfo
,
526 nextReadLocation
= CopyFromRingBuffer(InRingInfo
,
531 // Make sure all reads are done before we update the read index since
532 // the writer may start writing to the read area once the read index is updated
535 // Update the read index
536 SetNextReadLocation(InRingInfo
, nextReadLocation
);
538 //DumpRingInfo(InRingInfo, "AFTER ");
540 SpinlockRelease(InRingInfo
->RingLock
);
552 Helper routine to copy from source to ring buffer.
553 Assume there is enough room. Handles wrap-around in dest case only!!
558 RING_BUFFER_INFO
*RingInfo
,
559 UINT32 StartWriteOffset
,
563 void * ringBuffer
=GetRingBuffer(RingInfo
);
564 UINT32 ringBufferSize
=GetRingBufferSize(RingInfo
);
567 if (SrcLen
> ringBufferSize
- StartWriteOffset
) // wrap-around detected!
569 DPRINT_DBG(VMBUS
, "wrap-around detected!");
571 fragLen
= ringBufferSize
- StartWriteOffset
;
572 memcpy(ringBuffer
+ StartWriteOffset
, Src
, fragLen
);
573 memcpy(ringBuffer
, Src
+ fragLen
, SrcLen
- fragLen
);
577 memcpy(ringBuffer
+ StartWriteOffset
, Src
, SrcLen
);
580 StartWriteOffset
+= SrcLen
;
581 StartWriteOffset
%= ringBufferSize
;
583 return StartWriteOffset
;
593 Helper routine to copy to source from ring buffer.
594 Assume there is enough room. Handles wrap-around in src case only!!
599 RING_BUFFER_INFO
*RingInfo
,
602 UINT32 StartReadOffset
)
604 void * ringBuffer
=GetRingBuffer(RingInfo
);
605 UINT32 ringBufferSize
=GetRingBufferSize(RingInfo
);
609 if (DestLen
> ringBufferSize
- StartReadOffset
) // wrap-around detected at the src
611 DPRINT_DBG(VMBUS
, "src wrap-around detected!");
613 fragLen
= ringBufferSize
- StartReadOffset
;
615 memcpy(Dest
, ringBuffer
+ StartReadOffset
, fragLen
);
616 memcpy(Dest
+ fragLen
, ringBuffer
, DestLen
- fragLen
);
620 memcpy(Dest
, ringBuffer
+ StartReadOffset
, DestLen
);
623 StartReadOffset
+= DestLen
;
624 StartReadOffset
%= ringBufferSize
;
626 return StartReadOffset
;