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