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>
24 #include <linux/kernel.h>
28 #include "RingBuffer.h"
34 /* Amount of space to write to */
35 #define BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r))?((z) - ((w) - (r))):((r) - (w))
41 GetRingBufferAvailBytes()
44 Get number of bytes available to read and to write to
45 for the specified ring buffer
49 GetRingBufferAvailBytes(RING_BUFFER_INFO
*rbi
, u32
*read
, u32
*write
)
51 u32 read_loc
,write_loc
;
53 /* Capture the read/write indices before they changed */
54 read_loc
= rbi
->RingBuffer
->ReadIndex
;
55 write_loc
= rbi
->RingBuffer
->WriteIndex
;
57 *write
= BYTES_AVAIL_TO_WRITE(read_loc
, write_loc
, rbi
->RingDataSize
);
58 *read
= rbi
->RingDataSize
- *write
;
64 GetNextWriteLocation()
67 Get the next write location for the specified ring buffer
71 GetNextWriteLocation(RING_BUFFER_INFO
* RingInfo
)
73 u32 next
= RingInfo
->RingBuffer
->WriteIndex
;
75 ASSERT(next
< RingInfo
->RingDataSize
);
83 SetNextWriteLocation()
86 Set the next write location for the specified ring buffer
90 SetNextWriteLocation(RING_BUFFER_INFO
* RingInfo
, u32 NextWriteLocation
)
92 RingInfo
->RingBuffer
->WriteIndex
= NextWriteLocation
;
101 Get the next read location for the specified ring buffer
105 GetNextReadLocation(RING_BUFFER_INFO
* RingInfo
)
107 u32 next
= RingInfo
->RingBuffer
->ReadIndex
;
109 ASSERT(next
< RingInfo
->RingDataSize
);
117 GetNextReadLocationWithOffset()
120 Get the next read location + offset for the specified ring buffer.
121 This allows the caller to skip
125 GetNextReadLocationWithOffset(RING_BUFFER_INFO
* RingInfo
, u32 Offset
)
127 u32 next
= RingInfo
->RingBuffer
->ReadIndex
;
129 ASSERT(next
< RingInfo
->RingDataSize
);
131 next
%= RingInfo
->RingDataSize
;
139 SetNextReadLocation()
142 Set the next read location for the specified ring buffer
146 SetNextReadLocation(RING_BUFFER_INFO
* RingInfo
, u32 NextReadLocation
)
148 RingInfo
->RingBuffer
->ReadIndex
= NextReadLocation
;
158 Get the start of the ring buffer
162 GetRingBuffer(RING_BUFFER_INFO
* RingInfo
)
164 return (void *)RingInfo
->RingBuffer
->Buffer
;
174 Get the size of the ring buffer
178 GetRingBufferSize(RING_BUFFER_INFO
* RingInfo
)
180 return RingInfo
->RingDataSize
;
186 GetRingBufferIndices()
189 Get the read and write indices as u64 of the specified ring buffer
193 GetRingBufferIndices(RING_BUFFER_INFO
* RingInfo
)
195 return ((u64
)RingInfo
->RingBuffer
->WriteIndex
<< 32) || RingInfo
->RingBuffer
->ReadIndex
;
205 Dump out to console the ring buffer info
208 void DumpRingInfo(RING_BUFFER_INFO
*RingInfo
, char *Prefix
)
210 u32 bytesAvailToWrite
;
211 u32 bytesAvailToRead
;
213 GetRingBufferAvailBytes(RingInfo
, &bytesAvailToRead
, &bytesAvailToWrite
);
215 DPRINT(VMBUS
, DEBUG_RING_LVL
, "%s <<ringinfo %p buffer %p avail write %u avail read %u read idx %u write idx %u>>",
218 RingInfo
->RingBuffer
->Buffer
,
221 RingInfo
->RingBuffer
->ReadIndex
,
222 RingInfo
->RingBuffer
->WriteIndex
);
226 /* Internal routines */
230 RING_BUFFER_INFO
*RingInfo
,
231 u32 StartWriteOffset
,
237 RING_BUFFER_INFO
*RingInfo
,
240 u32 StartReadOffset
);
247 RingBufferGetDebugInfo()
250 Get various debug metrics for the specified ring buffer
253 void RingBufferGetDebugInfo(RING_BUFFER_INFO
*RingInfo
,
254 RING_BUFFER_DEBUG_INFO
*DebugInfo
)
256 u32 bytesAvailToWrite
;
257 u32 bytesAvailToRead
;
259 if (RingInfo
->RingBuffer
)
261 GetRingBufferAvailBytes(RingInfo
, &bytesAvailToRead
, &bytesAvailToWrite
);
263 DebugInfo
->BytesAvailToRead
= bytesAvailToRead
;
264 DebugInfo
->BytesAvailToWrite
= bytesAvailToWrite
;
265 DebugInfo
->CurrentReadIndex
= RingInfo
->RingBuffer
->ReadIndex
;
266 DebugInfo
->CurrentWriteIndex
= RingInfo
->RingBuffer
->WriteIndex
;
268 DebugInfo
->CurrentInterruptMask
= RingInfo
->RingBuffer
->InterruptMask
;
276 GetRingBufferInterruptMask()
279 Get the interrupt mask for the specified ring buffer
282 u32
GetRingBufferInterruptMask(RING_BUFFER_INFO
*rbi
)
284 return rbi
->RingBuffer
->InterruptMask
;
293 Initialize the ring buffer
296 int RingBufferInit(RING_BUFFER_INFO
*RingInfo
, void *Buffer
, u32 BufferLen
)
298 ASSERT(sizeof(RING_BUFFER
) == PAGE_SIZE
);
300 memset(RingInfo
, 0, sizeof(RING_BUFFER_INFO
));
302 RingInfo
->RingBuffer
= (RING_BUFFER
*)Buffer
;
303 RingInfo
->RingBuffer
->ReadIndex
= RingInfo
->RingBuffer
->WriteIndex
= 0;
305 RingInfo
->RingSize
= BufferLen
;
306 RingInfo
->RingDataSize
= BufferLen
- sizeof(RING_BUFFER
);
308 spin_lock_init(&RingInfo
->ring_lock
);
319 Cleanup the ring buffer
322 void RingBufferCleanup(RING_BUFFER_INFO
* RingInfo
)
332 Write to the ring buffer
335 int RingBufferWrite(RING_BUFFER_INFO
*OutRingInfo
,
336 struct scatterlist
*sglist
, u32 sgcount
)
339 u32 byteAvailToWrite
;
341 u32 totalBytesToWrite
=0;
343 struct scatterlist
*sg
;
344 volatile u32 nextWriteLocation
;
350 for_each_sg(sglist
, sg
, sgcount
, i
)
352 totalBytesToWrite
+= sg
->length
;
355 totalBytesToWrite
+= sizeof(u64
);
357 spin_lock_irqsave(&OutRingInfo
->ring_lock
, flags
);
359 GetRingBufferAvailBytes(OutRingInfo
, &byteAvailToRead
, &byteAvailToWrite
);
361 DPRINT_DBG(VMBUS
, "Writing %u bytes...", totalBytesToWrite
);
363 /* DumpRingInfo(OutRingInfo, "BEFORE "); */
365 /* If there is only room for the packet, assume it is full. Otherwise, the next time around, we think the ring buffer */
366 /* is empty since the read index == write index */
367 if (byteAvailToWrite
<= totalBytesToWrite
)
369 DPRINT_DBG(VMBUS
, "No more space left on outbound ring buffer (needed %u, avail %u)", totalBytesToWrite
, byteAvailToWrite
);
371 spin_unlock_irqrestore(&OutRingInfo
->ring_lock
, flags
);
378 /* Write to the ring buffer */
379 nextWriteLocation
= GetNextWriteLocation(OutRingInfo
);
381 for_each_sg(sglist
, sg
, sgcount
, i
)
383 nextWriteLocation
= CopyToRingBuffer(OutRingInfo
,
389 /* Set previous packet start */
390 prevIndices
= GetRingBufferIndices(OutRingInfo
);
392 nextWriteLocation
= CopyToRingBuffer(OutRingInfo
,
397 /* Make sure we flush all writes before updating the writeIndex */
400 /* Now, update the write location */
401 SetNextWriteLocation(OutRingInfo
, nextWriteLocation
);
403 /* DumpRingInfo(OutRingInfo, "AFTER "); */
405 spin_unlock_irqrestore(&OutRingInfo
->ring_lock
, flags
);
419 Read without advancing the read index
422 int RingBufferPeek(RING_BUFFER_INFO
*InRingInfo
, void *Buffer
, u32 BufferLen
)
424 u32 bytesAvailToWrite
;
425 u32 bytesAvailToRead
;
426 u32 nextReadLocation
=0;
429 spin_lock_irqsave(&InRingInfo
->ring_lock
, flags
);
431 GetRingBufferAvailBytes(InRingInfo
, &bytesAvailToRead
, &bytesAvailToWrite
);
433 /* Make sure there is something to read */
434 if (bytesAvailToRead
< BufferLen
)
436 /* DPRINT_DBG(VMBUS, "got callback but not enough to read <avail to read %d read size %d>!!", bytesAvailToRead, BufferLen); */
438 spin_unlock_irqrestore(&InRingInfo
->ring_lock
, flags
);
443 /* Convert to byte offset */
444 nextReadLocation
= GetNextReadLocation(InRingInfo
);
446 nextReadLocation
= CopyFromRingBuffer(InRingInfo
,
451 spin_unlock_irqrestore(&InRingInfo
->ring_lock
, flags
);
463 Read and advance the read index
466 int RingBufferRead(RING_BUFFER_INFO
*InRingInfo
, void *Buffer
,
467 u32 BufferLen
, u32 Offset
)
469 u32 bytesAvailToWrite
;
470 u32 bytesAvailToRead
;
471 u32 nextReadLocation
=0;
475 ASSERT(BufferLen
> 0);
477 spin_lock_irqsave(&InRingInfo
->ring_lock
, flags
);
479 GetRingBufferAvailBytes(InRingInfo
, &bytesAvailToRead
, &bytesAvailToWrite
);
481 DPRINT_DBG(VMBUS
, "Reading %u bytes...", BufferLen
);
483 /* DumpRingInfo(InRingInfo, "BEFORE "); */
485 /* Make sure there is something to read */
486 if (bytesAvailToRead
< BufferLen
)
488 DPRINT_DBG(VMBUS
, "got callback but not enough to read <avail to read %d read size %d>!!", bytesAvailToRead
, BufferLen
);
490 spin_unlock_irqrestore(&InRingInfo
->ring_lock
, flags
);
495 nextReadLocation
= GetNextReadLocationWithOffset(InRingInfo
, Offset
);
497 nextReadLocation
= CopyFromRingBuffer(InRingInfo
,
502 nextReadLocation
= CopyFromRingBuffer(InRingInfo
,
507 /* Make sure all reads are done before we update the read index since */
508 /* the writer may start writing to the read area once the read index is updated */
511 /* Update the read index */
512 SetNextReadLocation(InRingInfo
, nextReadLocation
);
514 /* DumpRingInfo(InRingInfo, "AFTER "); */
516 spin_unlock_irqrestore(&InRingInfo
->ring_lock
, flags
);
528 Helper routine to copy from source to ring buffer.
529 Assume there is enough room. Handles wrap-around in dest case only!!
534 RING_BUFFER_INFO
*RingInfo
,
535 u32 StartWriteOffset
,
539 void * ringBuffer
=GetRingBuffer(RingInfo
);
540 u32 ringBufferSize
=GetRingBufferSize(RingInfo
);
543 if (SrcLen
> ringBufferSize
- StartWriteOffset
) /* wrap-around detected! */
545 DPRINT_DBG(VMBUS
, "wrap-around detected!");
547 fragLen
= ringBufferSize
- StartWriteOffset
;
548 memcpy(ringBuffer
+ StartWriteOffset
, Src
, fragLen
);
549 memcpy(ringBuffer
, Src
+ fragLen
, SrcLen
- fragLen
);
553 memcpy(ringBuffer
+ StartWriteOffset
, Src
, SrcLen
);
556 StartWriteOffset
+= SrcLen
;
557 StartWriteOffset
%= ringBufferSize
;
559 return StartWriteOffset
;
569 Helper routine to copy to source from ring buffer.
570 Assume there is enough room. Handles wrap-around in src case only!!
575 RING_BUFFER_INFO
*RingInfo
,
580 void * ringBuffer
=GetRingBuffer(RingInfo
);
581 u32 ringBufferSize
=GetRingBufferSize(RingInfo
);
585 if (DestLen
> ringBufferSize
- StartReadOffset
) /* wrap-around detected at the src */
587 DPRINT_DBG(VMBUS
, "src wrap-around detected!");
589 fragLen
= ringBufferSize
- StartReadOffset
;
591 memcpy(Dest
, ringBuffer
+ StartReadOffset
, fragLen
);
592 memcpy(Dest
+ fragLen
, ringBuffer
, DestLen
- fragLen
);
596 memcpy(Dest
, ringBuffer
+ StartReadOffset
, DestLen
);
599 StartReadOffset
+= DestLen
;
600 StartReadOffset
%= ringBufferSize
;
602 return StartReadOffset
;