in vmw_vmci/vmci_queue_pair.c [1092:1261]
static int qp_alloc_guest_work(struct vmci_handle *handle,
struct vmci_queue **produce_q,
u64 produce_size,
struct vmci_queue **consume_q,
u64 consume_size,
u32 peer,
u32 flags,
u32 priv_flags)
{
const u64 num_produce_pages =
DIV_ROUND_UP(produce_size, PAGE_SIZE) + 1;
const u64 num_consume_pages =
DIV_ROUND_UP(consume_size, PAGE_SIZE) + 1;
void *my_produce_q = NULL;
void *my_consume_q = NULL;
int result;
struct qp_guest_endpoint *queue_pair_entry = NULL;
if (priv_flags != VMCI_NO_PRIVILEGE_FLAGS)
return VMCI_ERROR_NO_ACCESS;
mutex_lock(&qp_guest_endpoints.mutex);
queue_pair_entry = qp_guest_handle_to_entry(*handle);
if (queue_pair_entry) {
if (queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) {
/* Local attach case. */
if (queue_pair_entry->qp.ref_count > 1) {
pr_devel("Error attempting to attach more than once\n");
result = VMCI_ERROR_UNAVAILABLE;
goto error_keep_entry;
}
if (queue_pair_entry->qp.produce_size != consume_size ||
queue_pair_entry->qp.consume_size !=
produce_size ||
queue_pair_entry->qp.flags !=
(flags & ~VMCI_QPFLAG_ATTACH_ONLY)) {
pr_devel("Error mismatched queue pair in local attach\n");
result = VMCI_ERROR_QUEUEPAIR_MISMATCH;
goto error_keep_entry;
}
/*
* Do a local attach. We swap the consume and
* produce queues for the attacher and deliver
* an attach event.
*/
result = qp_notify_peer_local(true, *handle);
if (result < VMCI_SUCCESS)
goto error_keep_entry;
my_produce_q = queue_pair_entry->consume_q;
my_consume_q = queue_pair_entry->produce_q;
goto out;
}
result = VMCI_ERROR_ALREADY_EXISTS;
goto error_keep_entry;
}
my_produce_q = qp_alloc_queue(produce_size, flags);
if (!my_produce_q) {
pr_warn("Error allocating pages for produce queue\n");
result = VMCI_ERROR_NO_MEM;
goto error;
}
my_consume_q = qp_alloc_queue(consume_size, flags);
if (!my_consume_q) {
pr_warn("Error allocating pages for consume queue\n");
result = VMCI_ERROR_NO_MEM;
goto error;
}
queue_pair_entry = qp_guest_endpoint_create(*handle, peer, flags,
produce_size, consume_size,
my_produce_q, my_consume_q);
if (!queue_pair_entry) {
pr_warn("Error allocating memory in %s\n", __func__);
result = VMCI_ERROR_NO_MEM;
goto error;
}
result = qp_alloc_ppn_set(my_produce_q, num_produce_pages, my_consume_q,
num_consume_pages,
&queue_pair_entry->ppn_set);
if (result < VMCI_SUCCESS) {
pr_warn("qp_alloc_ppn_set failed\n");
goto error;
}
/*
* It's only necessary to notify the host if this queue pair will be
* attached to from another context.
*/
if (queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) {
/* Local create case. */
u32 context_id = vmci_get_context_id();
/*
* Enforce similar checks on local queue pairs as we
* do for regular ones. The handle's context must
* match the creator or attacher context id (here they
* are both the current context id) and the
* attach-only flag cannot exist during create. We
* also ensure specified peer is this context or an
* invalid one.
*/
if (queue_pair_entry->qp.handle.context != context_id ||
(queue_pair_entry->qp.peer != VMCI_INVALID_ID &&
queue_pair_entry->qp.peer != context_id)) {
result = VMCI_ERROR_NO_ACCESS;
goto error;
}
if (queue_pair_entry->qp.flags & VMCI_QPFLAG_ATTACH_ONLY) {
result = VMCI_ERROR_NOT_FOUND;
goto error;
}
} else {
result = qp_alloc_hypercall(queue_pair_entry);
if (result < VMCI_SUCCESS) {
pr_devel("qp_alloc_hypercall result = %d\n", result);
goto error;
}
}
qp_init_queue_mutex((struct vmci_queue *)my_produce_q,
(struct vmci_queue *)my_consume_q);
qp_list_add_entry(&qp_guest_endpoints, &queue_pair_entry->qp);
out:
queue_pair_entry->qp.ref_count++;
*handle = queue_pair_entry->qp.handle;
*produce_q = (struct vmci_queue *)my_produce_q;
*consume_q = (struct vmci_queue *)my_consume_q;
/*
* We should initialize the queue pair header pages on a local
* queue pair create. For non-local queue pairs, the
* hypervisor initializes the header pages in the create step.
*/
if ((queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) &&
queue_pair_entry->qp.ref_count == 1) {
vmci_q_header_init((*produce_q)->q_header, *handle);
vmci_q_header_init((*consume_q)->q_header, *handle);
}
mutex_unlock(&qp_guest_endpoints.mutex);
return VMCI_SUCCESS;
error:
mutex_unlock(&qp_guest_endpoints.mutex);
if (queue_pair_entry) {
/* The queues will be freed inside the destroy routine. */
qp_guest_endpoint_destroy(queue_pair_entry);
} else {
qp_free_queue(my_produce_q, produce_size);
qp_free_queue(my_consume_q, consume_size);
}
return result;
error_keep_entry:
/* This path should only be used when an existing entry was found. */
mutex_unlock(&qp_guest_endpoints.mutex);
return result;
}