xref: /freebsd/sys/dev/hyperv/vmbus/vmbus_br.c (revision 8881d206f4e68b564c2c5f50fc717086fc3e827a)
1 /*-
2  * Copyright (c) 2009-2012,2016 Microsoft Corp.
3  * Copyright (c) 2012 NetApp Inc.
4  * Copyright (c) 2012 Citrix Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/lock.h>
34 #include <sys/mutex.h>
35 #include <sys/sysctl.h>
36 
37 #include <dev/hyperv/vmbus/vmbus_reg.h>
38 #include <dev/hyperv/vmbus/vmbus_brvar.h>
39 
40 /* Amount of space available for write */
41 #define	VMBUS_BR_WAVAIL(r, w, z)	\
42 	(((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w)))
43 
44 /* Increase bufing index */
45 #define VMBUS_BR_IDXINC(idx, inc, sz)	(((idx) + (inc)) % (sz))
46 
47 static int			vmbus_br_sysctl_state(SYSCTL_HANDLER_ARGS);
48 static int			vmbus_br_sysctl_state_bin(SYSCTL_HANDLER_ARGS);
49 static void			vmbus_br_setup(struct vmbus_br *, void *, int);
50 
51 static int
52 vmbus_br_sysctl_state(SYSCTL_HANDLER_ARGS)
53 {
54 	const struct vmbus_br *br = arg1;
55 	uint32_t rindex, windex, imask, psndsz, fvalue, ravail, wavail;
56 	uint64_t intrcnt;
57 	char state[256];
58 
59 	intrcnt = br->vbr_intrcnt;
60 	rindex = br->vbr_rindex;
61 	windex = br->vbr_windex;
62 	imask = br->vbr_imask;
63 	psndsz = br->vbr_psndsz;
64 	fvalue = br->vbr_fvalue;
65 	wavail = VMBUS_BR_WAVAIL(rindex, windex, br->vbr_dsize);
66 	ravail = br->vbr_dsize - wavail;
67 
68 	snprintf(state, sizeof(state),
69 	    "intrcnt:%ju rindex:%u windex:%u imask:%u psndsz:%u fvalue:%u "
70 	    "ravail:%u wavail:%u",
71 	    (uintmax_t)intrcnt, rindex, windex, imask, psndsz, fvalue,
72 	    ravail, wavail);
73 	return sysctl_handle_string(oidp, state, sizeof(state), req);
74 }
75 
76 /*
77  * Binary bufring states.
78  */
79 static int
80 vmbus_br_sysctl_state_bin(SYSCTL_HANDLER_ARGS)
81 {
82 #define BR_STATE_RIDX	0
83 #define BR_STATE_WIDX	1
84 #define BR_STATE_IMSK	2
85 #define BR_STATE_PSSZ	3
86 #define BR_STATE_FVAL	4
87 #define BR_STATE_RSPC	5
88 #define BR_STATE_WSPC	6
89 #define BR_STATE_MAX	7
90 
91 	const struct vmbus_br *br = arg1;
92 	uint32_t rindex, windex, wavail, state[BR_STATE_MAX];
93 
94 	rindex = br->vbr_rindex;
95 	windex = br->vbr_windex;
96 	wavail = VMBUS_BR_WAVAIL(rindex, windex, br->vbr_dsize);
97 
98 	state[BR_STATE_RIDX] = rindex;
99 	state[BR_STATE_WIDX] = windex;
100 	state[BR_STATE_IMSK] = br->vbr_imask;
101 	state[BR_STATE_PSSZ] = br->vbr_psndsz;
102 	state[BR_STATE_FVAL] = br->vbr_fvalue;
103 	state[BR_STATE_WSPC] = wavail;
104 	state[BR_STATE_RSPC] = br->vbr_dsize - wavail;
105 
106 	return sysctl_handle_opaque(oidp, state, sizeof(state), req);
107 }
108 
109 void
110 vmbus_br_sysctl_create(struct sysctl_ctx_list *ctx, struct sysctl_oid *br_tree,
111     struct vmbus_br *br, const char *name)
112 {
113 	struct sysctl_oid *tree;
114 	char desc[64];
115 
116 	tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(br_tree), OID_AUTO,
117 	    name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
118 	if (tree == NULL)
119 		return;
120 
121 	snprintf(desc, sizeof(desc), "%s state", name);
122 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "state",
123 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
124 	    br, 0, vmbus_br_sysctl_state, "A", desc);
125 
126 	snprintf(desc, sizeof(desc), "%s binary state", name);
127 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "state_bin",
128 	    CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE,
129 	    br, 0, vmbus_br_sysctl_state_bin, "IU", desc);
130 }
131 
132 void
133 vmbus_rxbr_intr_mask(struct vmbus_rxbr *rbr)
134 {
135 	rbr->rxbr_imask = 1;
136 	mb();
137 }
138 
139 static __inline uint32_t
140 vmbus_rxbr_avail(const struct vmbus_rxbr *rbr)
141 {
142 	uint32_t rindex, windex;
143 
144 	/* Get snapshot */
145 	rindex = rbr->rxbr_rindex;
146 	windex = rbr->rxbr_windex;
147 
148 	return (rbr->rxbr_dsize -
149 	    VMBUS_BR_WAVAIL(rindex, windex, rbr->rxbr_dsize));
150 }
151 
152 uint32_t
153 vmbus_rxbr_available(const struct vmbus_rxbr *rbr)
154 {
155 	return (vmbus_rxbr_avail(rbr));
156 }
157 
158 uint32_t
159 vmbus_rxbr_intr_unmask(struct vmbus_rxbr *rbr)
160 {
161 	rbr->rxbr_imask = 0;
162 	mb();
163 
164 	/*
165 	 * Now check to see if the ring buffer is still empty.
166 	 * If it is not, we raced and we need to process new
167 	 * incoming channel packets.
168 	 */
169 	return vmbus_rxbr_avail(rbr);
170 }
171 
172 static void
173 vmbus_br_setup(struct vmbus_br *br, void *buf, int blen)
174 {
175 	br->vbr = buf;
176 	br->vbr_dsize = blen - sizeof(struct vmbus_bufring);
177 }
178 
179 void
180 vmbus_rxbr_init(struct vmbus_rxbr *rbr)
181 {
182 	mtx_init(&rbr->rxbr_lock, "vmbus_rxbr", NULL, MTX_SPIN);
183 }
184 
185 void
186 vmbus_rxbr_deinit(struct vmbus_rxbr *rbr)
187 {
188 	mtx_destroy(&rbr->rxbr_lock);
189 }
190 
191 void
192 vmbus_rxbr_setup(struct vmbus_rxbr *rbr, void *buf, int blen)
193 {
194 	vmbus_br_setup(&rbr->rxbr, buf, blen);
195 }
196 
197 static __inline boolean_t
198 vmbus_rxbr_need_signal(const struct vmbus_rxbr *rbr, uint32_t bytes_read)
199 {
200 	uint32_t pending_snd_sz, canwrite_size;
201 
202 	/* No need to signal if host doesn't want us to */
203 	if (!rbr->rxbr_fpsndsz)
204 		return false;
205 
206 	mb();
207 
208 	pending_snd_sz = rbr->rxbr_psndsz;
209 	/* No need to signal if host sets pending_snd_sz to 0 */
210 	if (!pending_snd_sz)
211 		return false;
212 
213 	mb();
214 
215 	canwrite_size = rbr->rxbr_dsize - vmbus_rxbr_avail(rbr);
216 
217 	/* No need to signal if br already has enough space before read */
218 	if (canwrite_size - bytes_read > pending_snd_sz)
219 		return false;
220 
221 	/*
222 	 * No need to signal if still doesn't have enough space
223 	 * asked by host
224 	 */
225 	if (canwrite_size <= pending_snd_sz)
226 		return false;
227 
228 	return true;
229 }
230 
231 void
232 vmbus_txbr_init(struct vmbus_txbr *tbr)
233 {
234 	mtx_init(&tbr->txbr_lock, "vmbus_txbr", NULL, MTX_SPIN);
235 }
236 
237 void
238 vmbus_txbr_deinit(struct vmbus_txbr *tbr)
239 {
240 	mtx_destroy(&tbr->txbr_lock);
241 }
242 
243 void
244 vmbus_txbr_setup(struct vmbus_txbr *tbr, void *buf, int blen)
245 {
246 	vmbus_br_setup(&tbr->txbr, buf, blen);
247 
248 	/* Set feature bit enabling flow control */
249 	tbr->txbr_fpsndsz = 1;
250 }
251 
252 uint32_t
253 vmbus_txbr_get_imask(const struct vmbus_txbr *tbr)
254 {
255 	mb();
256 
257 	return(tbr->txbr_imask);
258 }
259 
260 void
261 vmbus_txbr_set_pending_snd_sz(struct vmbus_txbr *tbr, uint32_t size)
262 {
263 	tbr->txbr_psndsz = size;
264 }
265 
266 /*
267  * When we write to the ring buffer, check if the host needs to be
268  * signaled.
269  *
270  * The contract:
271  * - The host guarantees that while it is draining the TX bufring,
272  *   it will set the br_imask to indicate it does not need to be
273  *   interrupted when new data are added.
274  * - The host guarantees that it will completely drain the TX bufring
275  *   before exiting the read loop.  Further, once the TX bufring is
276  *   empty, it will clear the br_imask and re-check to see if new
277  *   data have arrived.
278  */
279 static __inline boolean_t
280 vmbus_txbr_need_signal(const struct vmbus_txbr *tbr, uint32_t old_windex)
281 {
282 	mb();
283 	if (tbr->txbr_imask)
284 		return (FALSE);
285 
286 	__compiler_membar();
287 
288 	/*
289 	 * This is the only case we need to signal when the
290 	 * ring transitions from being empty to non-empty.
291 	 */
292 	if (old_windex == tbr->txbr_rindex)
293 		return (TRUE);
294 
295 	return (FALSE);
296 }
297 
298 static __inline uint32_t
299 vmbus_txbr_avail(const struct vmbus_txbr *tbr)
300 {
301 	uint32_t rindex, windex;
302 
303 	/* Get snapshot */
304 	rindex = tbr->txbr_rindex;
305 	windex = tbr->txbr_windex;
306 
307 	return VMBUS_BR_WAVAIL(rindex, windex, tbr->txbr_dsize);
308 }
309 
310 static __inline uint32_t
311 vmbus_txbr_copyto(const struct vmbus_txbr *tbr, uint32_t windex,
312     const void *src0, uint32_t cplen)
313 {
314 	const uint8_t *src = src0;
315 	uint8_t *br_data = tbr->txbr_data;
316 	uint32_t br_dsize = tbr->txbr_dsize;
317 
318 	if (cplen > br_dsize - windex) {
319 		uint32_t fraglen = br_dsize - windex;
320 
321 		/* Wrap-around detected */
322 		memcpy(br_data + windex, src, fraglen);
323 		memcpy(br_data, src + fraglen, cplen - fraglen);
324 	} else {
325 		memcpy(br_data + windex, src, cplen);
326 	}
327 	return VMBUS_BR_IDXINC(windex, cplen, br_dsize);
328 }
329 
330 static __inline uint32_t
331 vmbus_txbr_copyto_call(const struct vmbus_txbr *tbr, uint32_t windex,
332     uint32_t cplen, vmbus_br_copy_callback_t cb, void *cbarg, int *ret)
333 {
334 	uint8_t *br_data = tbr->txbr_data;
335 	uint32_t br_dsize = tbr->txbr_dsize;
336 	int err = 0;
337 
338 	if (cplen > br_dsize - windex) {
339 		uint32_t fraglen = br_dsize - windex;
340 
341 		/* Wrap-around detected */
342 		err = cb((void *)(br_data + windex), fraglen, cbarg);
343 		if (!err)
344 			err = cb((void *)br_data, cplen - fraglen, cbarg);
345 	} else {
346 		err = cb((void *)(br_data + windex), cplen, cbarg);
347 	}
348 
349 	*ret = err;
350 
351 	return VMBUS_BR_IDXINC(windex, cplen, br_dsize);
352 }
353 
354 uint32_t
355 vmbus_txbr_available(const struct vmbus_txbr *tbr)
356 {
357 	return (vmbus_txbr_avail(tbr));
358 }
359 
360 /*
361  * NOTE:
362  * Not holding lock when calling user provided callback routine.
363  * Caller should hold lock to serialize ring buffer accesses.
364  */
365 int
366 vmbus_txbr_write_call(struct vmbus_txbr *tbr,
367     const struct iovec iov[], int iovlen,
368     vmbus_br_copy_callback_t cb, void *cbarg,
369     boolean_t *need_sig)
370 {
371 	uint32_t old_windex, windex, total;
372 	uint64_t save_windex;
373 	int i;
374 	int cb_ret = 0;
375 
376 	total = 0;
377 	for (i = 0; i < iovlen; i++)
378 		total += iov[i].iov_len;
379 	total += sizeof(save_windex);
380 
381 
382 	/*
383 	 * NOTE:
384 	 * If this write is going to make br_windex same as br_rindex,
385 	 * i.e. the available space for write is same as the write size,
386 	 * we can't do it then, since br_windex == br_rindex means that
387 	 * the bufring is empty.
388 	 */
389 	if (vmbus_txbr_avail(tbr) <= total) {
390 		return (EAGAIN);
391 	}
392 
393 	/* Save br_windex for later use */
394 	old_windex = tbr->txbr_windex;
395 
396 	/*
397 	 * Copy the scattered channel packet to the TX bufring.
398 	 */
399 	windex = old_windex;
400 	for (i = 0; i < iovlen; i++) {
401 		if (iov[i].iov_base != NULL) {
402 			windex = vmbus_txbr_copyto(tbr, windex,
403 			    iov[i].iov_base, iov[i].iov_len);
404 		} else if (cb != NULL) {
405 			windex = vmbus_txbr_copyto_call(tbr, windex,
406 			    iov[i].iov_len, cb, cbarg, &cb_ret);
407 			/*
408 			 * If callback fails, return without updating
409 			 * write index.
410 			 */
411 			if (cb_ret)
412 				return (cb_ret);
413 		}
414 	}
415 
416 	mtx_lock_spin(&tbr->txbr_lock);
417 
418 	/*
419 	 * Set the offset of the current channel packet.
420 	 */
421 	save_windex = ((uint64_t)old_windex) << 32;
422 	windex = vmbus_txbr_copyto(tbr, windex, &save_windex,
423 	    sizeof(save_windex));
424 
425 	/*
426 	 * Update the write index _after_ the channel packet
427 	 * is copied.
428 	 */
429 	__compiler_membar();
430 	tbr->txbr_windex = windex;
431 
432 	mtx_unlock_spin(&tbr->txbr_lock);
433 
434 	if (need_sig)
435 		*need_sig = vmbus_txbr_need_signal(tbr, old_windex);
436 
437 	return (0);
438 }
439 
440 /*
441  * Write scattered channel packet to TX bufring.
442  *
443  * The offset of this channel packet is written as a 64bits value
444  * immediately after this channel packet.
445  */
446 int
447 vmbus_txbr_write(struct vmbus_txbr *tbr, const struct iovec iov[], int iovlen,
448     boolean_t *need_sig)
449 {
450 	uint32_t old_windex, windex, total;
451 	uint64_t save_windex;
452 	int i;
453 
454 	total = 0;
455 	for (i = 0; i < iovlen; i++)
456 		total += iov[i].iov_len;
457 	total += sizeof(save_windex);
458 
459 	mtx_lock_spin(&tbr->txbr_lock);
460 
461 	/*
462 	 * NOTE:
463 	 * If this write is going to make br_windex same as br_rindex,
464 	 * i.e. the available space for write is same as the write size,
465 	 * we can't do it then, since br_windex == br_rindex means that
466 	 * the bufring is empty.
467 	 */
468 	if (vmbus_txbr_avail(tbr) <= total) {
469 		mtx_unlock_spin(&tbr->txbr_lock);
470 		return (EAGAIN);
471 	}
472 
473 	/* Save br_windex for later use */
474 	old_windex = tbr->txbr_windex;
475 
476 	/*
477 	 * Copy the scattered channel packet to the TX bufring.
478 	 */
479 	windex = old_windex;
480 	for (i = 0; i < iovlen; i++) {
481 		windex = vmbus_txbr_copyto(tbr, windex,
482 		    iov[i].iov_base, iov[i].iov_len);
483 	}
484 
485 	/*
486 	 * Set the offset of the current channel packet.
487 	 */
488 	save_windex = ((uint64_t)old_windex) << 32;
489 	windex = vmbus_txbr_copyto(tbr, windex, &save_windex,
490 	    sizeof(save_windex));
491 
492 	/*
493 	 * Update the write index _after_ the channel packet
494 	 * is copied.
495 	 */
496 	__compiler_membar();
497 	tbr->txbr_windex = windex;
498 
499 	mtx_unlock_spin(&tbr->txbr_lock);
500 
501 	*need_sig = vmbus_txbr_need_signal(tbr, old_windex);
502 
503 	return (0);
504 }
505 
506 static __inline uint32_t
507 vmbus_rxbr_copyfrom(const struct vmbus_rxbr *rbr, uint32_t rindex,
508     void *dst0, int cplen)
509 {
510 	uint8_t *dst = dst0;
511 	const uint8_t *br_data = rbr->rxbr_data;
512 	uint32_t br_dsize = rbr->rxbr_dsize;
513 
514 	if (cplen > br_dsize - rindex) {
515 		uint32_t fraglen = br_dsize - rindex;
516 
517 		/* Wrap-around detected. */
518 		memcpy(dst, br_data + rindex, fraglen);
519 		memcpy(dst + fraglen, br_data, cplen - fraglen);
520 	} else {
521 		memcpy(dst, br_data + rindex, cplen);
522 	}
523 	return VMBUS_BR_IDXINC(rindex, cplen, br_dsize);
524 }
525 
526 static __inline uint32_t
527 vmbus_rxbr_copyfrom_call(const struct vmbus_rxbr *rbr, uint32_t rindex,
528     int cplen, vmbus_br_copy_callback_t cb, void *cbarg)
529 {
530 	uint8_t *br_data = rbr->rxbr_data;
531 	uint32_t br_dsize = rbr->rxbr_dsize;
532 	int error = 0;
533 
534 	if (cplen > br_dsize - rindex) {
535 		uint32_t fraglen = br_dsize - rindex;
536 
537 		/* Wrap-around detected. */
538 		error = cb((void *)(br_data + rindex), fraglen, cbarg);
539 		if (!error)
540 			error = cb((void *)br_data, cplen - fraglen, cbarg);
541 	} else {
542 		error = cb((void *)(br_data + rindex), cplen, cbarg);
543 	}
544 	return (error);
545 }
546 
547 int
548 vmbus_rxbr_peek(struct vmbus_rxbr *rbr, void *data, int dlen)
549 {
550 	mtx_lock_spin(&rbr->rxbr_lock);
551 
552 	/*
553 	 * The requested data and the 64bits channel packet
554 	 * offset should be there at least.
555 	 */
556 	if (vmbus_rxbr_avail(rbr) < dlen + sizeof(uint64_t)) {
557 		mtx_unlock_spin(&rbr->rxbr_lock);
558 		return (EAGAIN);
559 	}
560 	vmbus_rxbr_copyfrom(rbr, rbr->rxbr_rindex, data, dlen);
561 
562 	mtx_unlock_spin(&rbr->rxbr_lock);
563 
564 	return (0);
565 }
566 
567 /*
568  * NOTE:
569  * We only hold spin lock to check the ring buffer space. It is
570  * released before calling user provided callback routine.
571  * Caller should hold lock to serialize ring buffer accesses.
572  */
573 int
574 vmbus_rxbr_peek_call(struct vmbus_rxbr *rbr, int dlen, uint32_t skip,
575     vmbus_br_copy_callback_t cb, void *cbarg)
576 {
577 	uint32_t rindex, br_dsize0 = rbr->rxbr_dsize;
578 	int ret;
579 
580 	mtx_lock_spin(&rbr->rxbr_lock);
581 	/*
582 	 * The requested data + skip and the 64bits channel packet
583 	 * offset should be there at least.
584 	 */
585 	if (vmbus_rxbr_avail(rbr) < skip + dlen + sizeof(uint64_t)) {
586 		mtx_unlock_spin(&rbr->rxbr_lock);
587 		return (EAGAIN);
588 	}
589 
590 	rindex = VMBUS_BR_IDXINC(rbr->rxbr_rindex, skip, br_dsize0);
591 	mtx_unlock_spin(&rbr->rxbr_lock);
592 
593 	ret = vmbus_rxbr_copyfrom_call(rbr, rindex, dlen, cb, cbarg);
594 
595 	return (ret);
596 }
597 
598 /*
599  * NOTE:
600  * We assume idx_adv == sizeof(channel packet).
601  */
602 int
603 vmbus_rxbr_idxadv_peek(struct vmbus_rxbr *rbr, void *data, int dlen,
604     uint32_t idx_adv, boolean_t *need_sig)
605 {
606 	uint32_t rindex, br_dsize = rbr->rxbr_dsize;
607 
608 	mtx_lock_spin(&rbr->rxbr_lock);
609 	/*
610 	 * Make sure it has enough data to read.
611 	 */
612 	if (vmbus_rxbr_avail(rbr) < idx_adv + sizeof(uint64_t) + dlen) {
613 		mtx_unlock_spin(&rbr->rxbr_lock);
614 		return (EAGAIN);
615 	}
616 
617 	if (idx_adv > 0) {
618 		/*
619 		 * Advance the read index first, including the channel's 64bit
620 		 * previous write offset.
621 		 */
622 		rindex = VMBUS_BR_IDXINC(rbr->rxbr_rindex,
623 		    idx_adv + sizeof(uint64_t), br_dsize);
624 		__compiler_membar();
625 		rbr->rxbr_rindex = rindex;
626 	}
627 
628 	vmbus_rxbr_copyfrom(rbr, rbr->rxbr_rindex, data, dlen);
629 
630 	mtx_unlock_spin(&rbr->rxbr_lock);
631 
632 	if (need_sig) {
633 		if (idx_adv > 0)
634 			*need_sig =
635 			    vmbus_rxbr_need_signal(rbr, idx_adv +
636 			    sizeof(uint64_t));
637 		else
638 			*need_sig = false;
639 	}
640 
641 	return (0);
642 }
643 
644 /*
645  * NOTE:
646  * Just update the RX rb index.
647  */
648 int
649 vmbus_rxbr_idxadv(struct vmbus_rxbr *rbr, uint32_t idx_adv,
650     boolean_t *need_sig)
651 {
652 	uint32_t rindex, br_dsize = rbr->rxbr_dsize;
653 
654 	mtx_lock_spin(&rbr->rxbr_lock);
655 	/*
656 	 * Make sure it has enough space to advance.
657 	 */
658 	if (vmbus_rxbr_avail(rbr) < idx_adv + sizeof(uint64_t)) {
659 		mtx_unlock_spin(&rbr->rxbr_lock);
660 		return (EAGAIN);
661 	}
662 
663 	/*
664 	 * Advance the read index, including the channel's 64bit
665 	 * previous write offset.
666 	 */
667 	rindex = VMBUS_BR_IDXINC(rbr->rxbr_rindex,
668 	    idx_adv + sizeof(uint64_t), br_dsize);
669 	__compiler_membar();
670 	rbr->rxbr_rindex = rindex;
671 
672 	mtx_unlock_spin(&rbr->rxbr_lock);
673 
674 	if (need_sig) {
675 		*need_sig =
676 		    vmbus_rxbr_need_signal(rbr, idx_adv + sizeof(uint64_t));
677 	}
678 
679 	return (0);
680 }
681 
682 /*
683  * NOTE:
684  * We assume (dlen + skip) == sizeof(channel packet).
685  */
686 int
687 vmbus_rxbr_read(struct vmbus_rxbr *rbr, void *data, int dlen, uint32_t skip)
688 {
689 	uint32_t rindex, br_dsize = rbr->rxbr_dsize;
690 
691 	KASSERT(dlen + skip > 0, ("invalid dlen %d, offset %u", dlen, skip));
692 
693 	mtx_lock_spin(&rbr->rxbr_lock);
694 
695 	if (vmbus_rxbr_avail(rbr) < dlen + skip + sizeof(uint64_t)) {
696 		mtx_unlock_spin(&rbr->rxbr_lock);
697 		return (EAGAIN);
698 	}
699 
700 	/*
701 	 * Copy channel packet from RX bufring.
702 	 */
703 	rindex = VMBUS_BR_IDXINC(rbr->rxbr_rindex, skip, br_dsize);
704 	rindex = vmbus_rxbr_copyfrom(rbr, rindex, data, dlen);
705 
706 	/*
707 	 * Discard this channel packet's 64bits offset, which is useless to us.
708 	 */
709 	rindex = VMBUS_BR_IDXINC(rindex, sizeof(uint64_t), br_dsize);
710 
711 	/*
712 	 * Update the read index _after_ the channel packet is fetched.
713 	 */
714 	__compiler_membar();
715 	rbr->rxbr_rindex = rindex;
716 
717 	mtx_unlock_spin(&rbr->rxbr_lock);
718 
719 	return (0);
720 }
721