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