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