in vmw_vmci/vmci_queue_pair.c [1281:1442]
static int qp_broker_create(struct vmci_handle handle,
u32 peer,
u32 flags,
u32 priv_flags,
u64 produce_size,
u64 consume_size,
struct vmci_qp_page_store *page_store,
struct vmci_ctx *context,
vmci_event_release_cb wakeup_cb,
void *client_data, struct qp_broker_entry **ent)
{
struct qp_broker_entry *entry = NULL;
const u32 context_id = vmci_ctx_get_id(context);
bool is_local = flags & VMCI_QPFLAG_LOCAL;
int result;
u64 guest_produce_size;
u64 guest_consume_size;
/* Do not create if the caller asked not to. */
if (flags & VMCI_QPFLAG_ATTACH_ONLY)
return VMCI_ERROR_NOT_FOUND;
/*
* Creator's context ID should match handle's context ID or the creator
* must allow the context in handle's context ID as the "peer".
*/
if (handle.context != context_id && handle.context != peer)
return VMCI_ERROR_NO_ACCESS;
if (VMCI_CONTEXT_IS_VM(context_id) && VMCI_CONTEXT_IS_VM(peer))
return VMCI_ERROR_DST_UNREACHABLE;
/*
* Creator's context ID for local queue pairs should match the
* peer, if a peer is specified.
*/
if (is_local && peer != VMCI_INVALID_ID && context_id != peer)
return VMCI_ERROR_NO_ACCESS;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry)
return VMCI_ERROR_NO_MEM;
if (vmci_ctx_get_id(context) == VMCI_HOST_CONTEXT_ID && !is_local) {
/*
* The queue pair broker entry stores values from the guest
* point of view, so a creating host side endpoint should swap
* produce and consume values -- unless it is a local queue
* pair, in which case no swapping is necessary, since the local
* attacher will swap queues.
*/
guest_produce_size = consume_size;
guest_consume_size = produce_size;
} else {
guest_produce_size = produce_size;
guest_consume_size = consume_size;
}
entry->qp.handle = handle;
entry->qp.peer = peer;
entry->qp.flags = flags;
entry->qp.produce_size = guest_produce_size;
entry->qp.consume_size = guest_consume_size;
entry->qp.ref_count = 1;
entry->create_id = context_id;
entry->attach_id = VMCI_INVALID_ID;
entry->state = VMCIQPB_NEW;
entry->require_trusted_attach =
!!(context->priv_flags & VMCI_PRIVILEGE_FLAG_RESTRICTED);
entry->created_by_trusted =
!!(priv_flags & VMCI_PRIVILEGE_FLAG_TRUSTED);
entry->vmci_page_files = false;
entry->wakeup_cb = wakeup_cb;
entry->client_data = client_data;
entry->produce_q = qp_host_alloc_queue(guest_produce_size);
if (entry->produce_q == NULL) {
result = VMCI_ERROR_NO_MEM;
goto error;
}
entry->consume_q = qp_host_alloc_queue(guest_consume_size);
if (entry->consume_q == NULL) {
result = VMCI_ERROR_NO_MEM;
goto error;
}
qp_init_queue_mutex(entry->produce_q, entry->consume_q);
INIT_LIST_HEAD(&entry->qp.list_item);
if (is_local) {
u8 *tmp;
entry->local_mem = kcalloc(QPE_NUM_PAGES(entry->qp),
PAGE_SIZE, GFP_KERNEL);
if (entry->local_mem == NULL) {
result = VMCI_ERROR_NO_MEM;
goto error;
}
entry->state = VMCIQPB_CREATED_MEM;
entry->produce_q->q_header = entry->local_mem;
tmp = (u8 *)entry->local_mem + PAGE_SIZE *
(DIV_ROUND_UP(entry->qp.produce_size, PAGE_SIZE) + 1);
entry->consume_q->q_header = (struct vmci_queue_header *)tmp;
} else if (page_store) {
/*
* The VMX already initialized the queue pair headers, so no
* need for the kernel side to do that.
*/
result = qp_host_register_user_memory(page_store,
entry->produce_q,
entry->consume_q);
if (result < VMCI_SUCCESS)
goto error;
entry->state = VMCIQPB_CREATED_MEM;
} else {
/*
* A create without a page_store may be either a host
* side create (in which case we are waiting for the
* guest side to supply the memory) or an old style
* queue pair create (in which case we will expect a
* set page store call as the next step).
*/
entry->state = VMCIQPB_CREATED_NO_MEM;
}
qp_list_add_entry(&qp_broker_list, &entry->qp);
if (ent != NULL)
*ent = entry;
/* Add to resource obj */
result = vmci_resource_add(&entry->resource,
VMCI_RESOURCE_TYPE_QPAIR_HOST,
handle);
if (result != VMCI_SUCCESS) {
pr_warn("Failed to add new resource (handle=0x%x:0x%x), error: %d",
handle.context, handle.resource, result);
goto error;
}
entry->qp.handle = vmci_resource_handle(&entry->resource);
if (is_local) {
vmci_q_header_init(entry->produce_q->q_header,
entry->qp.handle);
vmci_q_header_init(entry->consume_q->q_header,
entry->qp.handle);
}
vmci_ctx_qp_create(context, entry->qp.handle);
return VMCI_SUCCESS;
error:
if (entry != NULL) {
qp_host_free_queue(entry->produce_q, guest_produce_size);
qp_host_free_queue(entry->consume_q, guest_consume_size);
kfree(entry);
}
return result;
}