Commit | Line | Data |
---|---|---|
3e7ee490 HJ |
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 | ||
09d50ff8 | 25 | #include "include/logging.h" |
3e7ee490 HJ |
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, UINT32 *read, UINT32 *write) | |
48 | { | |
49 | UINT32 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 UINT32 | |
69 | GetNextWriteLocation(RING_BUFFER_INFO* RingInfo) | |
70 | { | |
71 | UINT32 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, UINT32 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 UINT32 | |
103 | GetNextReadLocation(RING_BUFFER_INFO* RingInfo) | |
104 | { | |
105 | UINT32 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 UINT32 | |
123 | GetNextReadLocationWithOffset(RING_BUFFER_INFO* RingInfo, UINT32 Offset) | |
124 | { | |
125 | UINT32 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, UINT32 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 | --*/ | |
8282c400 | 159 | static inline void * |
3e7ee490 HJ |
160 | GetRingBuffer(RING_BUFFER_INFO* RingInfo) |
161 | { | |
8282c400 | 162 | return (void *)RingInfo->RingBuffer->Buffer; |
3e7ee490 HJ |
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 UINT32 | |
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 | UINT32 bytesAvailToWrite; | |
210 | UINT32 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 UINT32 | |
228 | CopyToRingBuffer( | |
229 | RING_BUFFER_INFO *RingInfo, | |
230 | UINT32 StartWriteOffset, | |
8282c400 | 231 | void * Src, |
3e7ee490 HJ |
232 | UINT32 SrcLen); |
233 | ||
234 | static UINT32 | |
235 | CopyFromRingBuffer( | |
236 | RING_BUFFER_INFO *RingInfo, | |
8282c400 | 237 | void * Dest, |
3e7ee490 HJ |
238 | UINT32 DestLen, |
239 | UINT32 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 | UINT32 bytesAvailToWrite; | |
259 | UINT32 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 | UINT32 | |
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, | |
e20f683b | 304 | void *Buffer, |
3e7ee490 HJ |
305 | UINT32 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 | UINT32 SgBufferCount | |
354 | ) | |
355 | { | |
356 | int i=0; | |
357 | UINT32 byteAvailToWrite; | |
358 | UINT32 byteAvailToRead; | |
359 | UINT32 totalBytesToWrite=0; | |
360 | ||
361 | volatile UINT32 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 | UINT32 BufferLen | |
443 | ) | |
444 | { | |
445 | UINT32 bytesAvailToWrite; | |
446 | UINT32 bytesAvailToRead; | |
447 | UINT32 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, | |
8282c400 | 489 | void * Buffer, |
3e7ee490 HJ |
490 | UINT32 BufferLen, |
491 | UINT32 Offset | |
492 | ) | |
493 | { | |
494 | UINT32 bytesAvailToWrite; | |
495 | UINT32 bytesAvailToRead; | |
496 | UINT32 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 | UINT32 | |
557 | CopyToRingBuffer( | |
558 | RING_BUFFER_INFO *RingInfo, | |
559 | UINT32 StartWriteOffset, | |
8282c400 | 560 | void * Src, |
3e7ee490 HJ |
561 | UINT32 SrcLen) |
562 | { | |
8282c400 | 563 | void * ringBuffer=GetRingBuffer(RingInfo); |
3e7ee490 HJ |
564 | UINT32 ringBufferSize=GetRingBufferSize(RingInfo); |
565 | UINT32 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 | UINT32 | |
598 | CopyFromRingBuffer( | |
599 | RING_BUFFER_INFO *RingInfo, | |
8282c400 | 600 | void * Dest, |
3e7ee490 HJ |
601 | UINT32 DestLen, |
602 | UINT32 StartReadOffset) | |
603 | { | |
8282c400 | 604 | void * ringBuffer=GetRingBuffer(RingInfo); |
3e7ee490 HJ |
605 | UINT32 ringBufferSize=GetRingBufferSize(RingInfo); |
606 | ||
607 | UINT32 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 |