static int qp_broker_create()

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;
}