xref: /freebsd/sys/dev/vmware/vmci/vmci_qpair.c (revision 370e009188ba90c3290b1479aa06ec98b66e140a)
1 /*-
2  * Copyright (c) 2018 VMware, Inc.
3  *
4  * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
5  */
6 
7 /* This file implements Queue accessor methods. */
8 
9 /*
10  * vmci_qpair is an interface that hides the queue pair internals. Rather than
11  * access each queue in a pair directly, operations are performed on the queue
12  * as a whole. This is simpler and less error-prone, and allows for future
13  * queue pair features to be added under the hood with no change to the client
14  * code.
15  */
16 
17 #include <sys/cdefs.h>
18 __FBSDID("$FreeBSD$");
19 
20 #include "vmci_kernel_api.h"
21 #include "vmci_kernel_defs.h"
22 #include "vmci_kernel_if.h"
23 #include "vmci_queue.h"
24 #include "vmci_queue_pair.h"
25 
26 /* This structure is opaque to the clients. */
27 struct vmci_qpair {
28 	struct vmci_handle	handle;
29 	struct vmci_queue	*produce_q;
30 	struct vmci_queue	*consume_q;
31 	uint64_t		produce_q_size;
32 	uint64_t		consume_q_size;
33 	vmci_id			peer;
34 	uint32_t		flags;
35 	vmci_privilege_flags	priv_flags;
36 	uint32_t		blocked;
37 	vmci_event		event;
38 };
39 
40 static void	vmci_qpair_get_queue_headers(const struct vmci_qpair *qpair,
41 		    struct vmci_queue_header **produce_q_header,
42 		    struct vmci_queue_header **consume_q_header);
43 
44 /*
45  *------------------------------------------------------------------------------
46  *
47  * vmci_queue_add_producer_tail --
48  *
49  *     Helper routine to increment the Producer Tail.
50  *
51  * Results:
52  *     VMCI_ERROR_NOT_FOUND if the vmm_world registered with the queue cannot
53  *     be found. Otherwise VMCI_SUCCESS.
54  *
55  * Side effects:
56  *     None.
57  *
58  *------------------------------------------------------------------------------
59  */
60 
61 static inline int
62 vmci_queue_add_producer_tail(struct vmci_queue *queue,
63     size_t add, uint64_t queue_size)
64 {
65 
66 	vmci_queue_header_add_producer_tail(queue->q_header, add, queue_size);
67 	return (VMCI_SUCCESS);
68 }
69 
70 /*
71  *------------------------------------------------------------------------------
72  *
73  * vmci_queue_add_consumer_head --
74  *
75  *     Helper routine to increment the Consumer Head.
76  *
77  * Results:
78  *     VMCI_ERROR_NOT_FOUND if the vmm_world registered with the queue cannot
79  *     be found. Otherwise VMCI_SUCCESS.
80  *
81  * Side effects:
82  *     None.
83  *
84  *------------------------------------------------------------------------------
85  */
86 
87 static inline int
88 vmci_queue_add_consumer_head(struct vmci_queue *queue,
89     size_t add, uint64_t queue_size)
90 {
91 
92 	vmci_queue_header_add_consumer_head(queue->q_header, add, queue_size);
93 	return (VMCI_SUCCESS);
94 }
95 
96 /*
97  *------------------------------------------------------------------------------
98  *
99  * vmci_qpair_get_queue_headers --
100  *
101  *     Helper routine that will retrieve the produce and consume headers of a
102  *     given queue pair.
103  *
104  * Results:
105  *     VMCI_SUCCESS if either current or saved queue headers are found.
106  *     Appropriate error code otherwise.
107  *
108  * Side effects:
109  *     None.
110  *
111  *------------------------------------------------------------------------------
112  */
113 
114 static void
115 vmci_qpair_get_queue_headers(const struct vmci_qpair *qpair,
116     struct vmci_queue_header **produce_q_header,
117     struct vmci_queue_header **consume_q_header)
118 {
119 
120 	ASSERT((qpair->produce_q != NULL) && (qpair->consume_q != NULL));
121 	*produce_q_header = qpair->produce_q->q_header;
122 	*consume_q_header = qpair->consume_q->q_header;
123 }
124 
125 /*
126  *------------------------------------------------------------------------------
127  *
128  * vmci_qpair_alloc --
129  *
130  *     This is the client interface for allocating the memory for a vmci_qpair
131  *     structure and then attaching to the underlying queue. If an error occurs
132  *     allocating the memory for the vmci_qpair structure, no attempt is made to
133  *     attach. If an error occurs attaching, then there's the vmci_qpair
134  *     structure is freed.
135  *
136  * Results:
137  *     An err, if < 0.
138  *
139  * Side effects:
140  *     None.
141  *
142  *------------------------------------------------------------------------------
143  */
144 
145 int
146 vmci_qpair_alloc(struct vmci_qpair **qpair, struct vmci_handle *handle,
147     uint64_t produce_q_size, uint64_t consume_q_size, vmci_id peer,
148     uint32_t flags, vmci_privilege_flags priv_flags)
149 {
150 	struct vmci_qpair *my_qpair;
151 	int retval;
152 
153 	/*
154 	 * Restrict the size of a queuepair. Though the device enforces a limit
155 	 * on the total amount of memory that can be allocated to queuepairs for
156 	 * a guest, we avoid unnecessarily allocating a lot of memory. Also, we
157 	 * try to allocate this memory before we make the queuepair allocation
158 	 * hypercall.
159 	 *
160 	 * (Note that this doesn't prevent all cases; a user with only this much
161 	 * physical memory could still get into trouble.) The error used by the
162 	 * device is NO_RESOURCES, so use that here too.
163 	 */
164 
165 	if (produce_q_size + consume_q_size <
166 	    MAX(produce_q_size, consume_q_size) ||
167 	    produce_q_size + consume_q_size > VMCI_MAX_GUEST_QP_MEMORY)
168 		return (VMCI_ERROR_NO_RESOURCES);
169 
170 	if (flags & VMCI_QPFLAG_NONBLOCK)
171 		return (VMCI_ERROR_INVALID_ARGS);
172 
173 	my_qpair = vmci_alloc_kernel_mem(sizeof(*my_qpair), VMCI_MEMORY_NORMAL);
174 	if (!my_qpair)
175 		return (VMCI_ERROR_NO_MEM);
176 
177 	my_qpair->produce_q_size = produce_q_size;
178 	my_qpair->consume_q_size = consume_q_size;
179 	my_qpair->peer = peer;
180 	my_qpair->flags = flags;
181 	my_qpair->priv_flags = priv_flags;
182 
183 	retval = vmci_queue_pair_alloc(handle, &my_qpair->produce_q,
184 	    my_qpair->produce_q_size, &my_qpair->consume_q,
185 	    my_qpair->consume_q_size, my_qpair->peer, my_qpair->flags,
186 	    my_qpair->priv_flags);
187 
188 	if (retval < VMCI_SUCCESS) {
189 		vmci_free_kernel_mem(my_qpair, sizeof(*my_qpair));
190 		return (retval);
191 	}
192 
193 	*qpair = my_qpair;
194 	my_qpair->handle = *handle;
195 
196 	return (retval);
197 }
198 
199 /*
200  *------------------------------------------------------------------------------
201  *
202  * vmci_qpair_detach --
203  *
204  *     This is the client interface for detaching from a vmci_qpair. Note that
205  *     this routine will free the memory allocated for the vmci_qpair structure,
206  *     too.
207  *
208  * Results:
209  *     An error, if < 0.
210  *
211  * Side effects:
212  *     Will clear the caller's pointer to the vmci_qpair structure.
213  *
214  *------------------------------------------------------------------------------
215  */
216 
217 int
218 vmci_qpair_detach(struct vmci_qpair **qpair)
219 {
220 	struct vmci_qpair *old_qpair;
221 	int result;
222 
223 	if (!qpair || !(*qpair))
224 		return (VMCI_ERROR_INVALID_ARGS);
225 
226 	old_qpair = *qpair;
227 	result = vmci_queue_pair_detach(old_qpair->handle);
228 
229 	/*
230 	 * The guest can fail to detach for a number of reasons, and if it does
231 	 * so, it will cleanup the entry (if there is one). We need to release
232 	 * the qpair struct here; there isn't much the caller can do, and we
233 	 * don't want to leak.
234 	 */
235 
236 	if (old_qpair->flags & VMCI_QPFLAG_LOCAL)
237 		vmci_destroy_event(&old_qpair->event);
238 
239 	vmci_free_kernel_mem(old_qpair, sizeof(*old_qpair));
240 	*qpair = NULL;
241 
242 	return (result);
243 }
244 
245 /*
246  *------------------------------------------------------------------------------
247  *
248  * vmci_qpair_get_produce_indexes --
249  *
250  *     This is the client interface for getting the current indexes of the
251  *     qpair from the point of the view of the caller as the producer.
252  *
253  * Results:
254  *     err, if < 0
255  *     Success otherwise.
256  *
257  * Side effects:
258  *     None.
259  *
260  *------------------------------------------------------------------------------
261  */
262 
263 int
264 vmci_qpair_get_produce_indexes(const struct vmci_qpair *qpair,
265     uint64_t *producer_tail, uint64_t *consumer_head)
266 {
267 	struct vmci_queue_header *consume_q_header;
268 	struct vmci_queue_header *produce_q_header;
269 
270 	if (!qpair)
271 		return (VMCI_ERROR_INVALID_ARGS);
272 
273 	vmci_qpair_get_queue_headers(qpair, &produce_q_header,
274 	    &consume_q_header);
275 	vmci_queue_header_get_pointers(produce_q_header, consume_q_header,
276 	    producer_tail, consumer_head);
277 
278 	if ((producer_tail && *producer_tail >= qpair->produce_q_size) ||
279 	    (consumer_head && *consumer_head >= qpair->produce_q_size))
280 		return (VMCI_ERROR_INVALID_SIZE);
281 
282 	return (VMCI_SUCCESS);
283 }
284 
285 /*
286  *------------------------------------------------------------------------------
287  *
288  * vmci_qpair_get_consume_indexes --
289  *
290  *     This is the client interface for getting the current indexes of the
291  *     QPair from the point of the view of the caller as the consumer.
292  *
293  * Results:
294  *     err, if < 0
295  *     Success otherwise.
296  *
297  * Side effects:
298  *     None.
299  *
300  *------------------------------------------------------------------------------
301  */
302 
303 int
304 vmci_qpair_get_consume_indexes(const struct vmci_qpair *qpair,
305     uint64_t *consumer_tail, uint64_t *producer_head)
306 {
307 	struct vmci_queue_header *consume_q_header;
308 	struct vmci_queue_header *produce_q_header;
309 
310 	if (!qpair)
311 		return (VMCI_ERROR_INVALID_ARGS);
312 
313 	vmci_qpair_get_queue_headers(qpair, &produce_q_header,
314 	    &consume_q_header);
315 	vmci_queue_header_get_pointers(consume_q_header, produce_q_header,
316 	    consumer_tail, producer_head);
317 
318 	if ((consumer_tail && *consumer_tail >= qpair->consume_q_size) ||
319 	    (producer_head && *producer_head >= qpair->consume_q_size))
320 		return (VMCI_ERROR_INVALID_SIZE);
321 
322 	return (VMCI_SUCCESS);
323 }
324 
325 /*
326  *------------------------------------------------------------------------------
327  *
328  * vmci_qpair_produce_free_space --
329  *
330  *     This is the client interface for getting the amount of free space in the
331  *     QPair from the point of the view of the caller as the producer which is
332  *     the common case.
333  *
334  * Results:
335  *     Err, if < 0.
336  *     Full queue if = 0.
337  *     Number of available bytes into which data can be enqueued if > 0.
338  *
339  * Side effects:
340  *     None.
341  *
342  *------------------------------------------------------------------------------
343  */
344 
345 int64_t
346 vmci_qpair_produce_free_space(const struct vmci_qpair *qpair)
347 {
348 	struct vmci_queue_header *consume_q_header;
349 	struct vmci_queue_header *produce_q_header;
350 	int64_t result;
351 
352 	if (!qpair)
353 		return (VMCI_ERROR_INVALID_ARGS);
354 
355 	vmci_qpair_get_queue_headers(qpair, &produce_q_header,
356 	    &consume_q_header);
357 	result = vmci_queue_header_free_space(produce_q_header, consume_q_header,
358 	    qpair->produce_q_size);
359 
360 	return (result);
361 }
362 
363 /*
364  *------------------------------------------------------------------------------
365  *
366  * vmci_qpair_consume_free_space --
367  *
368  *     This is the client interface for getting the amount of free space in the
369  *     QPair from the point of the view of the caller as the consumer which is
370  *     not the common case (see vmci_qpair_Produce_free_space(), above).
371  *
372  * Results:
373  *     Err, if < 0.
374  *     Full queue if = 0.
375  *     Number of available bytes into which data can be enqueued if > 0.
376  *
377  * Side effects:
378  *     None.
379  *
380  *------------------------------------------------------------------------------
381  */
382 
383 int64_t
384 vmci_qpair_consume_free_space(const struct vmci_qpair *qpair)
385 {
386 	struct vmci_queue_header *consume_q_header;
387 	struct vmci_queue_header *produce_q_header;
388 	int64_t result;
389 
390 	if (!qpair)
391 		return (VMCI_ERROR_INVALID_ARGS);
392 
393 	vmci_qpair_get_queue_headers(qpair, &produce_q_header,
394 	    &consume_q_header);
395 	result = vmci_queue_header_free_space(consume_q_header, produce_q_header,
396 	    qpair->consume_q_size);
397 
398 	return (result);
399 }
400 
401 /*
402  *------------------------------------------------------------------------------
403  *
404  * vmci_qpair_produce_buf_ready --
405  *
406  *     This is the client interface for getting the amount of enqueued data in
407  *     the QPair from the point of the view of the caller as the producer which
408  *     is not the common case (see vmci_qpair_Consume_buf_ready(), above).
409  *
410  * Results:
411  *     Err, if < 0.
412  *     Empty queue if = 0.
413  *     Number of bytes ready to be dequeued if > 0.
414  *
415  * Side effects:
416  *     None.
417  *
418  *------------------------------------------------------------------------------
419  */
420 
421 int64_t
422 vmci_qpair_produce_buf_ready(const struct vmci_qpair *qpair)
423 {
424 	struct vmci_queue_header *consume_q_header;
425 	struct vmci_queue_header *produce_q_header;
426 	int64_t result;
427 
428 	if (!qpair)
429 		return (VMCI_ERROR_INVALID_ARGS);
430 
431 	vmci_qpair_get_queue_headers(qpair, &produce_q_header,
432 	    &consume_q_header);
433 	result = vmci_queue_header_buf_ready(produce_q_header, consume_q_header,
434 	    qpair->produce_q_size);
435 
436 	return (result);
437 }
438 
439 /*
440  *------------------------------------------------------------------------------
441  *
442  * vmci_qpair_consume_buf_ready --
443  *
444  *     This is the client interface for getting the amount of enqueued data in
445  *     the QPair from the point of the view of the caller as the consumer which
446  *     is the normal case.
447  *
448  * Results:
449  *     Err, if < 0.
450  *     Empty queue if = 0.
451  *     Number of bytes ready to be dequeued if > 0.
452  *
453  * Side effects:
454  *     None.
455  *
456  *------------------------------------------------------------------------------
457  */
458 
459 int64_t
460 vmci_qpair_consume_buf_ready(const struct vmci_qpair *qpair)
461 {
462 	struct vmci_queue_header *consume_q_header;
463 	struct vmci_queue_header *produce_q_header;
464 	int64_t result;
465 
466 	if (!qpair)
467 		return (VMCI_ERROR_INVALID_ARGS);
468 
469 	vmci_qpair_get_queue_headers(qpair, &produce_q_header,
470 	    &consume_q_header);
471 	result = vmci_queue_header_buf_ready(consume_q_header, produce_q_header,
472 	    qpair->consume_q_size);
473 
474 	return (result);
475 }
476 
477 /*
478  *------------------------------------------------------------------------------
479  *
480  * enqueue --
481  *
482  *     Enqueues a given buffer to the produce queue using the provided function.
483  *     As many bytes as possible (space available in the queue) are enqueued.
484  *
485  * Results:
486  *     VMCI_ERROR_QUEUEPAIR_NOSPACE if no space was available to enqueue data.
487  *     VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue
488  *     (as defined by the queue size).
489  *     VMCI_ERROR_INVALID_ARGS, if an error occurred when accessing the buffer.
490  *     VMCI_ERROR_QUEUEPAIR_NOTATTACHED, if the queue pair pages aren't
491  *     available.
492  *     Otherwise, the number of bytes written to the queue is returned.
493  *
494  * Side effects:
495  *     Updates the tail pointer of the produce queue.
496  *
497  *------------------------------------------------------------------------------
498  */
499 
500 static ssize_t
501 enqueue(struct vmci_queue *produce_q, struct vmci_queue *consume_q,
502     const uint64_t produce_q_size, const void *buf, size_t buf_size,
503     int buf_type, vmci_memcpy_to_queue_func memcpy_to_queue, bool can_block)
504 {
505 	ssize_t result;
506 	size_t written;
507 	int64_t free_space;
508 	uint64_t tail;
509 
510 	ASSERT((produce_q != NULL) && (consume_q != NULL));
511 
512 	free_space = vmci_queue_header_free_space(produce_q->q_header,
513 	    consume_q->q_header,
514 	    produce_q_size);
515 	if (free_space == 0)
516 		return (VMCI_ERROR_QUEUEPAIR_NOSPACE);
517 
518 	if (free_space < VMCI_SUCCESS)
519 		return ((ssize_t)free_space);
520 
521 	written = (size_t)(free_space > buf_size ? buf_size : free_space);
522 	tail = vmci_queue_header_producer_tail(produce_q->q_header);
523 	if (LIKELY(tail + written < produce_q_size))
524 		result = memcpy_to_queue(produce_q, tail, buf, 0, written,
525 		    buf_type, can_block);
526 	else {
527 		/* Tail pointer wraps around. */
528 
529 		const size_t tmp = (size_t)(produce_q_size - tail);
530 
531 		result = memcpy_to_queue(produce_q, tail, buf, 0, tmp, buf_type,
532 		    can_block);
533 		if (result >= VMCI_SUCCESS)
534 			result = memcpy_to_queue(produce_q, 0, buf, tmp,
535 			    written - tmp, buf_type, can_block);
536 	}
537 
538 	if (result < VMCI_SUCCESS)
539 		return (result);
540 
541 	result = vmci_queue_add_producer_tail(produce_q, written,
542 	    produce_q_size);
543 	if (result < VMCI_SUCCESS)
544 		return (result);
545 	return (written);
546 }
547 
548 /*
549  *------------------------------------------------------------------------------
550  *
551  * dequeue --
552  *
553  *     Dequeues data (if available) from the given consume queue. Writes data
554  *     to the user provided buffer using the provided function.
555  *
556  * Results:
557  *     VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue.
558  *     VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue
559  *     (as defined by the queue size).
560  *     VMCI_ERROR_INVALID_ARGS, if an error occurred when accessing the buffer.
561  *     VMCI_ERROR_NOT_FOUND, if the vmm_world registered with the queue pair
562  *     cannot be found.
563  *     Otherwise the number of bytes dequeued is returned.
564  *
565  * Side effects:
566  *     Updates the head pointer of the consume queue.
567  *
568  *------------------------------------------------------------------------------
569  */
570 
571 static ssize_t
572 dequeue(struct vmci_queue *produce_q,
573     struct vmci_queue *consume_q, const uint64_t consume_q_size, void *buf,
574     size_t buf_size, int buf_type,
575     vmci_memcpy_from_queue_func memcpy_from_queue, bool update_consumer,
576     bool can_block)
577 {
578 	ssize_t result;
579 	size_t read;
580 	int64_t buf_ready;
581 	uint64_t head;
582 
583 	ASSERT((produce_q != NULL) && (consume_q != NULL));
584 
585 	buf_ready = vmci_queue_header_buf_ready(consume_q->q_header,
586 	    produce_q->q_header, consume_q_size);
587 	if (buf_ready == 0)
588 		return (VMCI_ERROR_QUEUEPAIR_NODATA);
589 	if (buf_ready < VMCI_SUCCESS)
590 		return ((ssize_t)buf_ready);
591 
592 	read = (size_t)(buf_ready > buf_size ? buf_size : buf_ready);
593 	head = vmci_queue_header_consumer_head(produce_q->q_header);
594 	if (LIKELY(head + read < consume_q_size))
595 		result = memcpy_from_queue(buf, 0, consume_q, head, read,
596 		    buf_type, can_block);
597 	else {
598 		/* Head pointer wraps around. */
599 
600 		const size_t tmp = (size_t)(consume_q_size - head);
601 
602 		result = memcpy_from_queue(buf, 0, consume_q, head, tmp,
603 		    buf_type, can_block);
604 		if (result >= VMCI_SUCCESS)
605 			result = memcpy_from_queue(buf, tmp, consume_q, 0,
606 			    read - tmp, buf_type, can_block);
607 	}
608 
609 	if (result < VMCI_SUCCESS)
610 		return (result);
611 
612 	if (update_consumer) {
613 		result = vmci_queue_add_consumer_head(produce_q, read,
614 		    consume_q_size);
615 		if (result < VMCI_SUCCESS)
616 			return (result);
617 	}
618 
619 	return (read);
620 }
621 
622 /*
623  *------------------------------------------------------------------------------
624  *
625  * vmci_qpair_enqueue --
626  *
627  *     This is the client interface for enqueueing data into the queue.
628  *
629  * Results:
630  *     Err, if < 0.
631  *     Number of bytes enqueued if >= 0.
632  *
633  * Side effects:
634  *     None.
635  *
636  *------------------------------------------------------------------------------
637  */
638 
639 ssize_t
640 vmci_qpair_enqueue(struct vmci_qpair *qpair, const void *buf, size_t buf_size,
641     int buf_type)
642 {
643 	ssize_t result;
644 
645 	if (!qpair || !buf)
646 		return (VMCI_ERROR_INVALID_ARGS);
647 
648 	result = enqueue(qpair->produce_q, qpair->consume_q,
649 	    qpair->produce_q_size, buf, buf_size, buf_type,
650 	    qpair->flags & VMCI_QPFLAG_LOCAL?
651 	    vmci_memcpy_to_queue_local : vmci_memcpy_to_queue,
652 	    !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
653 
654 	return (result);
655 }
656 
657 /*
658  *------------------------------------------------------------------------------
659  *
660  * vmci_qpair_dequeue --
661  *
662  *     This is the client interface for dequeueing data from the queue.
663  *
664  * Results:
665  *     Err, if < 0.
666  *     Number of bytes dequeued if >= 0.
667  *
668  * Side effects:
669  *     None.
670  *
671  *------------------------------------------------------------------------------
672  */
673 
674 ssize_t
675 vmci_qpair_dequeue(struct vmci_qpair *qpair, void *buf, size_t buf_size,
676     int buf_type)
677 {
678 	ssize_t result;
679 
680 	if (!qpair || !buf)
681 		return (VMCI_ERROR_INVALID_ARGS);
682 
683 	result = dequeue(qpair->produce_q, qpair->consume_q,
684 	    qpair->consume_q_size, buf, buf_size, buf_type,
685 	    qpair->flags & VMCI_QPFLAG_LOCAL?
686 	    vmci_memcpy_from_queue_local : vmci_memcpy_from_queue, true,
687 	    !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
688 
689 	return (result);
690 }
691 
692 /*
693  *------------------------------------------------------------------------------
694  *
695  * vmci_qpair_peek --
696  *
697  *     This is the client interface for peeking into a queue.  (I.e., copy
698  *     data from the queue without updating the head pointer.)
699  *
700  * Results:
701  *     Err, if < 0.
702  *     Number of bytes peeked, if >= 0.
703  *
704  * Side effects:
705  *     None.
706  *
707  *------------------------------------------------------------------------------
708  */
709 
710 ssize_t
711 vmci_qpair_peek(struct vmci_qpair *qpair, void *buf, size_t buf_size,
712     int buf_type)
713 {
714 	ssize_t result;
715 
716 	if (!qpair || !buf)
717 		return (VMCI_ERROR_INVALID_ARGS);
718 
719 	result = dequeue(qpair->produce_q, qpair->consume_q,
720 	    qpair->consume_q_size, buf, buf_size, buf_type,
721 	    qpair->flags & VMCI_QPFLAG_LOCAL?
722 	    vmci_memcpy_from_queue_local : vmci_memcpy_from_queue, false,
723 	    !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
724 
725 	return (result);
726 }
727 
728 /*
729  *------------------------------------------------------------------------------
730  *
731  * vmci_qpair_enquev --
732  *
733  *     This is the client interface for enqueueing data into the queue.
734  *
735  * Results:
736  *     Err, if < 0.
737  *     Number of bytes enqueued if >= 0.
738  *
739  * Side effects:
740  *     None.
741  *
742  *------------------------------------------------------------------------------
743  */
744 
745 ssize_t
746 vmci_qpair_enquev(struct vmci_qpair *qpair, void *iov, size_t iov_size,
747     int buf_type)
748 {
749 	ssize_t result;
750 
751 	if (!qpair || !iov)
752 		return (VMCI_ERROR_INVALID_ARGS);
753 
754 	result = enqueue(qpair->produce_q, qpair->consume_q,
755 	    qpair->produce_q_size, iov, iov_size, buf_type,
756 	    qpair->flags & VMCI_QPFLAG_LOCAL?
757 	    vmci_memcpy_to_queue_v_local : vmci_memcpy_to_queue_v,
758 	    !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
759 
760 	return (result);
761 }
762 
763 /*
764  *------------------------------------------------------------------------------
765  *
766  * vmci_qpair_dequev --
767  *
768  *     This is the client interface for dequeueing data from the queue.
769  *
770  * Results:
771  *     Err, if < 0.
772  *     Number of bytes dequeued if >= 0.
773  *
774  * Side effects:
775  *     None.
776  *
777  *------------------------------------------------------------------------------
778  */
779 
780 ssize_t
781 vmci_qpair_dequev(struct vmci_qpair *qpair, void *iov, size_t iov_size,
782     int buf_type)
783 {
784 	ssize_t result;
785 
786 	if (!qpair || !iov)
787 		return (VMCI_ERROR_INVALID_ARGS);
788 
789 	result = dequeue(qpair->produce_q, qpair->consume_q,
790 	    qpair->consume_q_size, iov, iov_size, buf_type,
791 	    qpair->flags & VMCI_QPFLAG_LOCAL?
792 	    vmci_memcpy_from_queue_v_local : vmci_memcpy_from_queue_v, true,
793 	    !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
794 
795 	return (result);
796 }
797 
798 /*
799  *------------------------------------------------------------------------------
800  *
801  * vmci_qpair_peekv --
802  *
803  *     This is the client interface for peeking into a queue.  (I.e., copy
804  *     data from the queue without updating the head pointer.)
805  *
806  * Results:
807  *     Err, if < 0.
808  *     Number of bytes peeked, if >= 0.
809  *
810  * Side effects:
811  *     None.
812  *
813  *------------------------------------------------------------------------------
814  */
815 
816 ssize_t
817 vmci_qpair_peekv(struct vmci_qpair *qpair, void *iov, size_t iov_size,
818     int buf_type)
819 {
820 	ssize_t result;
821 
822 	if (!qpair || !iov)
823 		return (VMCI_ERROR_INVALID_ARGS);
824 
825 	result = dequeue(qpair->produce_q, qpair->consume_q,
826 	    qpair->consume_q_size, iov, iov_size, buf_type,
827 	    qpair->flags & VMCI_QPFLAG_LOCAL?
828 	    vmci_memcpy_from_queue_v_local : vmci_memcpy_from_queue_v, false,
829 	    !(qpair->flags & VMCI_QPFLAG_NONBLOCK));
830 
831 	return (result);
832 }
833