]> Pileus Git - ~andy/linux/blob - drivers/staging/hv/RingBuffer.c
Staging: hv: remove UINT32 and INT32 typedefs
[~andy/linux] / drivers / staging / hv / RingBuffer.c
1 /*
2  *
3  * Copyright (c) 2009, Microsoft Corporation.
4  *
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.
8  *
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
12  * more details.
13  *
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.
17  *
18  * Authors:
19  *   Haiyang Zhang <haiyangz@microsoft.com>
20  *   Hank Janssen  <hjanssen@microsoft.com>
21  *
22  */
23
24
25 #include "include/logging.h"
26 #include "RingBuffer.h"
27
28 //
29 // #defines
30 //
31
32 // Amount of space to write to
33 #define BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r))?((z) - ((w) - (r))):((r) - (w))
34
35
36 /*++
37
38 Name:
39         GetRingBufferAvailBytes()
40
41 Description:
42         Get number of bytes available to read and to write to
43         for the specified ring buffer
44
45 --*/
46 static inline void
47 GetRingBufferAvailBytes(RING_BUFFER_INFO *rbi, u32 *read, u32 *write)
48 {
49         u32 read_loc,write_loc;
50
51         // Capture the read/write indices before they changed
52         read_loc = rbi->RingBuffer->ReadIndex;
53         write_loc = rbi->RingBuffer->WriteIndex;
54
55         *write = BYTES_AVAIL_TO_WRITE(read_loc, write_loc, rbi->RingDataSize);
56         *read = rbi->RingDataSize - *write;
57 }
58
59 /*++
60
61 Name:
62         GetNextWriteLocation()
63
64 Description:
65         Get the next write location for the specified ring buffer
66
67 --*/
68 static inline u32
69 GetNextWriteLocation(RING_BUFFER_INFO* RingInfo)
70 {
71         u32 next = RingInfo->RingBuffer->WriteIndex;
72
73         ASSERT(next < RingInfo->RingDataSize);
74
75         return next;
76 }
77
78 /*++
79
80 Name:
81         SetNextWriteLocation()
82
83 Description:
84         Set the next write location for the specified ring buffer
85
86 --*/
87 static inline void
88 SetNextWriteLocation(RING_BUFFER_INFO* RingInfo, u32 NextWriteLocation)
89 {
90         RingInfo->RingBuffer->WriteIndex = NextWriteLocation;
91 }
92
93 /*++
94
95 Name:
96         GetNextReadLocation()
97
98 Description:
99         Get the next read location for the specified ring buffer
100
101 --*/
102 static inline u32
103 GetNextReadLocation(RING_BUFFER_INFO* RingInfo)
104 {
105         u32 next = RingInfo->RingBuffer->ReadIndex;
106
107         ASSERT(next < RingInfo->RingDataSize);
108
109         return next;
110 }
111
112 /*++
113
114 Name:
115         GetNextReadLocationWithOffset()
116
117 Description:
118         Get the next read location + offset for the specified ring buffer.
119         This allows the caller to skip
120
121 --*/
122 static inline u32
123 GetNextReadLocationWithOffset(RING_BUFFER_INFO* RingInfo, u32 Offset)
124 {
125         u32 next = RingInfo->RingBuffer->ReadIndex;
126
127         ASSERT(next < RingInfo->RingDataSize);
128         next += Offset;
129         next %= RingInfo->RingDataSize;
130
131         return next;
132 }
133
134 /*++
135
136 Name:
137         SetNextReadLocation()
138
139 Description:
140         Set the next read location for the specified ring buffer
141
142 --*/
143 static inline void
144 SetNextReadLocation(RING_BUFFER_INFO* RingInfo, u32 NextReadLocation)
145 {
146         RingInfo->RingBuffer->ReadIndex = NextReadLocation;
147 }
148
149
150 /*++
151
152 Name:
153         GetRingBuffer()
154
155 Description:
156         Get the start of the ring buffer
157
158 --*/
159 static inline void *
160 GetRingBuffer(RING_BUFFER_INFO* RingInfo)
161 {
162         return (void *)RingInfo->RingBuffer->Buffer;
163 }
164
165
166 /*++
167
168 Name:
169         GetRingBufferSize()
170
171 Description:
172         Get the size of the ring buffer
173
174 --*/
175 static inline u32
176 GetRingBufferSize(RING_BUFFER_INFO* RingInfo)
177 {
178         return RingInfo->RingDataSize;
179 }
180
181 /*++
182
183 Name:
184         GetRingBufferIndices()
185
186 Description:
187         Get the read and write indices as UINT64 of the specified ring buffer
188
189 --*/
190 static inline UINT64
191 GetRingBufferIndices(RING_BUFFER_INFO* RingInfo)
192 {
193         return ((UINT64)RingInfo->RingBuffer->WriteIndex << 32) || RingInfo->RingBuffer->ReadIndex;
194 }
195
196
197 /*++
198
199 Name:
200         DumpRingInfo()
201
202 Description:
203         Dump out to console the ring buffer info
204
205 --*/
206 void
207 DumpRingInfo(RING_BUFFER_INFO* RingInfo, char *Prefix)
208 {
209         u32 bytesAvailToWrite;
210         u32 bytesAvailToRead;
211
212         GetRingBufferAvailBytes(RingInfo, &bytesAvailToRead, &bytesAvailToWrite);
213
214         DPRINT(VMBUS, DEBUG_RING_LVL, "%s <<ringinfo %p buffer %p avail write %u avail read %u read idx %u write idx %u>>",
215                 Prefix,
216                 RingInfo,
217                 RingInfo->RingBuffer->Buffer,
218                 bytesAvailToWrite,
219                 bytesAvailToRead,
220                 RingInfo->RingBuffer->ReadIndex,
221                 RingInfo->RingBuffer->WriteIndex);
222 }
223
224 //
225 // Internal routines
226 //
227 static u32
228 CopyToRingBuffer(
229         RING_BUFFER_INFO        *RingInfo,
230         u32                             StartWriteOffset,
231         void *                          Src,
232         u32                             SrcLen);
233
234 static u32
235 CopyFromRingBuffer(
236         RING_BUFFER_INFO        *RingInfo,
237         void *                          Dest,
238         u32                             DestLen,
239         u32                             StartReadOffset);
240
241
242
243 /*++
244
245 Name:
246         RingBufferGetDebugInfo()
247
248 Description:
249         Get various debug metrics for the specified ring buffer
250
251 --*/
252 void
253 RingBufferGetDebugInfo(
254         RING_BUFFER_INFO                *RingInfo,
255         RING_BUFFER_DEBUG_INFO  *DebugInfo
256         )
257 {
258         u32 bytesAvailToWrite;
259         u32 bytesAvailToRead;
260
261         if (RingInfo->RingBuffer)
262         {
263                 GetRingBufferAvailBytes(RingInfo, &bytesAvailToRead, &bytesAvailToWrite);
264
265                 DebugInfo->BytesAvailToRead = bytesAvailToRead;
266                 DebugInfo->BytesAvailToWrite = bytesAvailToWrite;
267                 DebugInfo->CurrentReadIndex = RingInfo->RingBuffer->ReadIndex;
268                 DebugInfo->CurrentWriteIndex = RingInfo->RingBuffer->WriteIndex;
269
270                 DebugInfo->CurrentInterruptMask = RingInfo->RingBuffer->InterruptMask;
271         }
272 }
273
274
275 /*++
276
277 Name:
278         GetRingBufferInterruptMask()
279
280 Description:
281         Get the interrupt mask for the specified ring buffer
282
283 --*/
284 u32
285 GetRingBufferInterruptMask(
286         RING_BUFFER_INFO *rbi
287         )
288 {
289         return rbi->RingBuffer->InterruptMask;
290 }
291
292 /*++
293
294 Name:
295         RingBufferInit()
296
297 Description:
298         Initialize the ring buffer
299
300 --*/
301 int
302 RingBufferInit(
303         RING_BUFFER_INFO        *RingInfo,
304         void                            *Buffer,
305         u32                             BufferLen
306         )
307 {
308         ASSERT(sizeof(RING_BUFFER) == PAGE_SIZE);
309
310         memset(RingInfo, 0, sizeof(RING_BUFFER_INFO));
311
312         RingInfo->RingBuffer = (RING_BUFFER*)Buffer;
313         RingInfo->RingBuffer->ReadIndex = RingInfo->RingBuffer->WriteIndex = 0;
314
315         RingInfo->RingSize = BufferLen;
316         RingInfo->RingDataSize = BufferLen - sizeof(RING_BUFFER);
317
318         RingInfo->RingLock = SpinlockCreate();
319
320         return 0;
321 }
322
323 /*++
324
325 Name:
326         RingBufferCleanup()
327
328 Description:
329         Cleanup the ring buffer
330
331 --*/
332 void
333 RingBufferCleanup(
334         RING_BUFFER_INFO* RingInfo
335         )
336 {
337         SpinlockClose(RingInfo->RingLock);
338 }
339
340 /*++
341
342 Name:
343         RingBufferWrite()
344
345 Description:
346         Write to the ring buffer
347
348 --*/
349 int
350 RingBufferWrite(
351         RING_BUFFER_INFO*       OutRingInfo,
352         SG_BUFFER_LIST          SgBuffers[],
353         u32                             SgBufferCount
354         )
355 {
356         int i=0;
357         u32 byteAvailToWrite;
358         u32 byteAvailToRead;
359         u32 totalBytesToWrite=0;
360
361         volatile u32 nextWriteLocation;
362         UINT64 prevIndices=0;
363
364         DPRINT_ENTER(VMBUS);
365
366         for (i=0; i < SgBufferCount; i++)
367         {
368                 totalBytesToWrite += SgBuffers[i].Length;
369         }
370
371         totalBytesToWrite += sizeof(UINT64);
372
373         SpinlockAcquire(OutRingInfo->RingLock);
374
375         GetRingBufferAvailBytes(OutRingInfo, &byteAvailToRead, &byteAvailToWrite);
376
377         DPRINT_DBG(VMBUS, "Writing %u bytes...", totalBytesToWrite);
378
379         //DumpRingInfo(OutRingInfo, "BEFORE ");
380
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)
384         {
385                 DPRINT_DBG(VMBUS, "No more space left on outbound ring buffer (needed %u, avail %u)", totalBytesToWrite, byteAvailToWrite);
386
387                 SpinlockRelease(OutRingInfo->RingLock);
388
389                 DPRINT_EXIT(VMBUS);
390
391                 return -1;
392         }
393
394         // Write to the ring buffer
395         nextWriteLocation = GetNextWriteLocation(OutRingInfo);
396
397         for (i=0; i < SgBufferCount; i++)
398         {
399                  nextWriteLocation = CopyToRingBuffer(OutRingInfo,
400                                                                                                 nextWriteLocation,
401                                                                                                 SgBuffers[i].Data,
402                                                                                                 SgBuffers[i].Length);
403         }
404
405         // Set previous packet start
406         prevIndices = GetRingBufferIndices(OutRingInfo);
407
408         nextWriteLocation = CopyToRingBuffer(OutRingInfo,
409                                                                                                 nextWriteLocation,
410                                                                                                 &prevIndices,
411                                                                                                 sizeof(UINT64));
412
413         // Make sure we flush all writes before updating the writeIndex
414         MemoryFence();
415
416         // Now, update the write location
417         SetNextWriteLocation(OutRingInfo, nextWriteLocation);
418
419         //DumpRingInfo(OutRingInfo, "AFTER ");
420
421         SpinlockRelease(OutRingInfo->RingLock);
422
423         DPRINT_EXIT(VMBUS);
424
425         return 0;
426 }
427
428
429 /*++
430
431 Name:
432         RingBufferPeek()
433
434 Description:
435         Read without advancing the read index
436
437 --*/
438 int
439 RingBufferPeek(
440         RING_BUFFER_INFO*       InRingInfo,
441         void*                           Buffer,
442         u32                             BufferLen
443         )
444 {
445         u32 bytesAvailToWrite;
446         u32 bytesAvailToRead;
447         u32 nextReadLocation=0;
448
449         SpinlockAcquire(InRingInfo->RingLock);
450
451         GetRingBufferAvailBytes(InRingInfo, &bytesAvailToRead, &bytesAvailToWrite);
452
453         // Make sure there is something to read
454         if (bytesAvailToRead < BufferLen )
455         {
456                 //DPRINT_DBG(VMBUS, "got callback but not enough to read <avail to read %d read size %d>!!", bytesAvailToRead, BufferLen);
457
458                 SpinlockRelease(InRingInfo->RingLock);
459
460                 return -1;
461         }
462
463         // Convert to byte offset
464         nextReadLocation = GetNextReadLocation(InRingInfo);
465
466         nextReadLocation = CopyFromRingBuffer(InRingInfo,
467                                                                                         Buffer,
468                                                                                         BufferLen,
469                                                                                         nextReadLocation);
470
471         SpinlockRelease(InRingInfo->RingLock);
472
473         return 0;
474 }
475
476
477 /*++
478
479 Name:
480         RingBufferRead()
481
482 Description:
483         Read and advance the read index
484
485 --*/
486 int
487 RingBufferRead(
488         RING_BUFFER_INFO*       InRingInfo,
489         void *                          Buffer,
490         u32                             BufferLen,
491         u32                             Offset
492         )
493 {
494         u32 bytesAvailToWrite;
495         u32 bytesAvailToRead;
496         u32 nextReadLocation=0;
497         UINT64 prevIndices=0;
498
499         ASSERT(BufferLen > 0);
500
501         SpinlockAcquire(InRingInfo->RingLock);
502
503         GetRingBufferAvailBytes(InRingInfo, &bytesAvailToRead, &bytesAvailToWrite);
504
505         DPRINT_DBG(VMBUS, "Reading %u bytes...", BufferLen);
506
507         //DumpRingInfo(InRingInfo, "BEFORE ");
508
509         // Make sure there is something to read
510         if (bytesAvailToRead < BufferLen )
511         {
512                 DPRINT_DBG(VMBUS, "got callback but not enough to read <avail to read %d read size %d>!!", bytesAvailToRead, BufferLen);
513
514                 SpinlockRelease(InRingInfo->RingLock);
515
516                 return -1;
517         }
518
519         nextReadLocation = GetNextReadLocationWithOffset(InRingInfo, Offset);
520
521         nextReadLocation = CopyFromRingBuffer(InRingInfo,
522                                                                                         Buffer,
523                                                                                         BufferLen,
524                                                                                         nextReadLocation);
525
526         nextReadLocation = CopyFromRingBuffer(InRingInfo,
527                                                                                         &prevIndices,
528                                                                                         sizeof(UINT64),
529                                                                                         nextReadLocation);
530
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
533         MemoryFence();
534
535         // Update the read index
536         SetNextReadLocation(InRingInfo, nextReadLocation);
537
538         //DumpRingInfo(InRingInfo, "AFTER ");
539
540         SpinlockRelease(InRingInfo->RingLock);
541
542         return 0;
543 }
544
545
546 /*++
547
548 Name:
549         CopyToRingBuffer()
550
551 Description:
552         Helper routine to copy from source to ring buffer.
553         Assume there is enough room. Handles wrap-around in dest case only!!
554
555 --*/
556 u32
557 CopyToRingBuffer(
558         RING_BUFFER_INFO        *RingInfo,
559         u32                             StartWriteOffset,
560         void *                          Src,
561         u32                             SrcLen)
562 {
563         void * ringBuffer=GetRingBuffer(RingInfo);
564         u32 ringBufferSize=GetRingBufferSize(RingInfo);
565         u32 fragLen;
566
567         if (SrcLen > ringBufferSize - StartWriteOffset) // wrap-around detected!
568         {
569                 DPRINT_DBG(VMBUS, "wrap-around detected!");
570
571                 fragLen = ringBufferSize - StartWriteOffset;
572                 memcpy(ringBuffer + StartWriteOffset, Src, fragLen);
573                 memcpy(ringBuffer, Src + fragLen, SrcLen - fragLen);
574         }
575         else
576         {
577                 memcpy(ringBuffer + StartWriteOffset, Src, SrcLen);
578         }
579
580         StartWriteOffset += SrcLen;
581         StartWriteOffset %= ringBufferSize;
582
583         return StartWriteOffset;
584 }
585
586
587 /*++
588
589 Name:
590         CopyFromRingBuffer()
591
592 Description:
593         Helper routine to copy to source from ring buffer.
594         Assume there is enough room. Handles wrap-around in src case only!!
595
596 --*/
597 u32
598 CopyFromRingBuffer(
599         RING_BUFFER_INFO        *RingInfo,
600         void *                          Dest,
601         u32                             DestLen,
602         u32                             StartReadOffset)
603 {
604         void * ringBuffer=GetRingBuffer(RingInfo);
605         u32 ringBufferSize=GetRingBufferSize(RingInfo);
606
607         u32 fragLen;
608
609         if (DestLen > ringBufferSize - StartReadOffset) // wrap-around detected at the src
610         {
611                 DPRINT_DBG(VMBUS, "src wrap-around detected!");
612
613                 fragLen = ringBufferSize - StartReadOffset;
614
615                 memcpy(Dest, ringBuffer + StartReadOffset, fragLen);
616                 memcpy(Dest + fragLen, ringBuffer, DestLen - fragLen);
617         }
618         else
619         {
620                 memcpy(Dest, ringBuffer + StartReadOffset, DestLen);
621         }
622
623         StartReadOffset += DestLen;
624         StartReadOffset %= ringBufferSize;
625
626         return StartReadOffset;
627 }
628
629
630 // eof