128d6692cSGeorge Zhang /*
228d6692cSGeorge Zhang  * VMware VMCI Driver
328d6692cSGeorge Zhang  *
428d6692cSGeorge Zhang  * Copyright (C) 2012 VMware, Inc. All rights reserved.
528d6692cSGeorge Zhang  *
628d6692cSGeorge Zhang  * This program is free software; you can redistribute it and/or modify it
728d6692cSGeorge Zhang  * under the terms of the GNU General Public License as published by the
828d6692cSGeorge Zhang  * Free Software Foundation version 2 and no later version.
928d6692cSGeorge Zhang  *
1028d6692cSGeorge Zhang  * This program is distributed in the hope that it will be useful, but
1128d6692cSGeorge Zhang  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
1228d6692cSGeorge Zhang  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1328d6692cSGeorge Zhang  * for more details.
1428d6692cSGeorge Zhang  */
1528d6692cSGeorge Zhang 
1628d6692cSGeorge Zhang #include <linux/vmw_vmci_defs.h>
1728d6692cSGeorge Zhang #include <linux/vmw_vmci_api.h>
1828d6692cSGeorge Zhang #include <linux/highmem.h>
1928d6692cSGeorge Zhang #include <linux/kernel.h>
2028d6692cSGeorge Zhang #include <linux/module.h>
2128d6692cSGeorge Zhang #include <linux/sched.h>
225b825c3aSIngo Molnar #include <linux/cred.h>
2328d6692cSGeorge Zhang #include <linux/slab.h>
2428d6692cSGeorge Zhang 
2528d6692cSGeorge Zhang #include "vmci_queue_pair.h"
2628d6692cSGeorge Zhang #include "vmci_datagram.h"
2728d6692cSGeorge Zhang #include "vmci_doorbell.h"
2828d6692cSGeorge Zhang #include "vmci_context.h"
2928d6692cSGeorge Zhang #include "vmci_driver.h"
3028d6692cSGeorge Zhang #include "vmci_event.h"
3128d6692cSGeorge Zhang 
3228d6692cSGeorge Zhang /*
3328d6692cSGeorge Zhang  * List of current VMCI contexts.  Contexts can be added by
3428d6692cSGeorge Zhang  * vmci_ctx_create() and removed via vmci_ctx_destroy().
3528d6692cSGeorge Zhang  * These, along with context lookup, are protected by the
3628d6692cSGeorge Zhang  * list structure's lock.
3728d6692cSGeorge Zhang  */
3828d6692cSGeorge Zhang static struct {
3928d6692cSGeorge Zhang 	struct list_head head;
4028d6692cSGeorge Zhang 	spinlock_t lock; /* Spinlock for context list operations */
4128d6692cSGeorge Zhang } ctx_list = {
4228d6692cSGeorge Zhang 	.head = LIST_HEAD_INIT(ctx_list.head),
4328d6692cSGeorge Zhang 	.lock = __SPIN_LOCK_UNLOCKED(ctx_list.lock),
4428d6692cSGeorge Zhang };
4528d6692cSGeorge Zhang 
4628d6692cSGeorge Zhang /* Used by contexts that did not set up notify flag pointers */
4728d6692cSGeorge Zhang static bool ctx_dummy_notify;
4828d6692cSGeorge Zhang 
4928d6692cSGeorge Zhang static void ctx_signal_notify(struct vmci_ctx *context)
5028d6692cSGeorge Zhang {
5128d6692cSGeorge Zhang 	*context->notify = true;
5228d6692cSGeorge Zhang }
5328d6692cSGeorge Zhang 
5428d6692cSGeorge Zhang static void ctx_clear_notify(struct vmci_ctx *context)
5528d6692cSGeorge Zhang {
5628d6692cSGeorge Zhang 	*context->notify = false;
5728d6692cSGeorge Zhang }
5828d6692cSGeorge Zhang 
5928d6692cSGeorge Zhang /*
6028d6692cSGeorge Zhang  * If nothing requires the attention of the guest, clears both
6128d6692cSGeorge Zhang  * notify flag and call.
6228d6692cSGeorge Zhang  */
6328d6692cSGeorge Zhang static void ctx_clear_notify_call(struct vmci_ctx *context)
6428d6692cSGeorge Zhang {
6528d6692cSGeorge Zhang 	if (context->pending_datagrams == 0 &&
6628d6692cSGeorge Zhang 	    vmci_handle_arr_get_size(context->pending_doorbell_array) == 0)
6728d6692cSGeorge Zhang 		ctx_clear_notify(context);
6828d6692cSGeorge Zhang }
6928d6692cSGeorge Zhang 
7028d6692cSGeorge Zhang /*
7128d6692cSGeorge Zhang  * Sets the context's notify flag iff datagrams are pending for this
7228d6692cSGeorge Zhang  * context.  Called from vmci_setup_notify().
7328d6692cSGeorge Zhang  */
7428d6692cSGeorge Zhang void vmci_ctx_check_signal_notify(struct vmci_ctx *context)
7528d6692cSGeorge Zhang {
7628d6692cSGeorge Zhang 	spin_lock(&context->lock);
7728d6692cSGeorge Zhang 	if (context->pending_datagrams)
7828d6692cSGeorge Zhang 		ctx_signal_notify(context);
7928d6692cSGeorge Zhang 	spin_unlock(&context->lock);
8028d6692cSGeorge Zhang }
8128d6692cSGeorge Zhang 
8228d6692cSGeorge Zhang /*
8328d6692cSGeorge Zhang  * Allocates and initializes a VMCI context.
8428d6692cSGeorge Zhang  */
8528d6692cSGeorge Zhang struct vmci_ctx *vmci_ctx_create(u32 cid, u32 priv_flags,
8628d6692cSGeorge Zhang 				 uintptr_t event_hnd,
8728d6692cSGeorge Zhang 				 int user_version,
8828d6692cSGeorge Zhang 				 const struct cred *cred)
8928d6692cSGeorge Zhang {
9028d6692cSGeorge Zhang 	struct vmci_ctx *context;
9128d6692cSGeorge Zhang 	int error;
9228d6692cSGeorge Zhang 
9328d6692cSGeorge Zhang 	if (cid == VMCI_INVALID_ID) {
9428d6692cSGeorge Zhang 		pr_devel("Invalid context ID for VMCI context\n");
9528d6692cSGeorge Zhang 		error = -EINVAL;
9628d6692cSGeorge Zhang 		goto err_out;
9728d6692cSGeorge Zhang 	}
9828d6692cSGeorge Zhang 
9928d6692cSGeorge Zhang 	if (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS) {
10028d6692cSGeorge Zhang 		pr_devel("Invalid flag (flags=0x%x) for VMCI context\n",
10128d6692cSGeorge Zhang 			 priv_flags);
10228d6692cSGeorge Zhang 		error = -EINVAL;
10328d6692cSGeorge Zhang 		goto err_out;
10428d6692cSGeorge Zhang 	}
10528d6692cSGeorge Zhang 
10628d6692cSGeorge Zhang 	if (user_version == 0) {
10728d6692cSGeorge Zhang 		pr_devel("Invalid suer_version %d\n", user_version);
10828d6692cSGeorge Zhang 		error = -EINVAL;
10928d6692cSGeorge Zhang 		goto err_out;
11028d6692cSGeorge Zhang 	}
11128d6692cSGeorge Zhang 
11228d6692cSGeorge Zhang 	context = kzalloc(sizeof(*context), GFP_KERNEL);
11328d6692cSGeorge Zhang 	if (!context) {
11428d6692cSGeorge Zhang 		pr_warn("Failed to allocate memory for VMCI context\n");
11528d6692cSGeorge Zhang 		error = -EINVAL;
11628d6692cSGeorge Zhang 		goto err_out;
11728d6692cSGeorge Zhang 	}
11828d6692cSGeorge Zhang 
11928d6692cSGeorge Zhang 	kref_init(&context->kref);
12028d6692cSGeorge Zhang 	spin_lock_init(&context->lock);
12128d6692cSGeorge Zhang 	INIT_LIST_HEAD(&context->list_item);
12228d6692cSGeorge Zhang 	INIT_LIST_HEAD(&context->datagram_queue);
12328d6692cSGeorge Zhang 	INIT_LIST_HEAD(&context->notifier_list);
12428d6692cSGeorge Zhang 
12528d6692cSGeorge Zhang 	/* Initialize host-specific VMCI context. */
12628d6692cSGeorge Zhang 	init_waitqueue_head(&context->host_context.wait_queue);
12728d6692cSGeorge Zhang 
12828d6692cSGeorge Zhang 	context->queue_pair_array = vmci_handle_arr_create(0);
12928d6692cSGeorge Zhang 	if (!context->queue_pair_array) {
13028d6692cSGeorge Zhang 		error = -ENOMEM;
13128d6692cSGeorge Zhang 		goto err_free_ctx;
13228d6692cSGeorge Zhang 	}
13328d6692cSGeorge Zhang 
13428d6692cSGeorge Zhang 	context->doorbell_array = vmci_handle_arr_create(0);
13528d6692cSGeorge Zhang 	if (!context->doorbell_array) {
13628d6692cSGeorge Zhang 		error = -ENOMEM;
13728d6692cSGeorge Zhang 		goto err_free_qp_array;
13828d6692cSGeorge Zhang 	}
13928d6692cSGeorge Zhang 
14028d6692cSGeorge Zhang 	context->pending_doorbell_array = vmci_handle_arr_create(0);
14128d6692cSGeorge Zhang 	if (!context->pending_doorbell_array) {
14228d6692cSGeorge Zhang 		error = -ENOMEM;
14328d6692cSGeorge Zhang 		goto err_free_db_array;
14428d6692cSGeorge Zhang 	}
14528d6692cSGeorge Zhang 
14628d6692cSGeorge Zhang 	context->user_version = user_version;
14728d6692cSGeorge Zhang 
14828d6692cSGeorge Zhang 	context->priv_flags = priv_flags;
14928d6692cSGeorge Zhang 
15028d6692cSGeorge Zhang 	if (cred)
15128d6692cSGeorge Zhang 		context->cred = get_cred(cred);
15228d6692cSGeorge Zhang 
15328d6692cSGeorge Zhang 	context->notify = &ctx_dummy_notify;
15428d6692cSGeorge Zhang 	context->notify_page = NULL;
15528d6692cSGeorge Zhang 
15628d6692cSGeorge Zhang 	/*
15728d6692cSGeorge Zhang 	 * If we collide with an existing context we generate a new
15828d6692cSGeorge Zhang 	 * and use it instead. The VMX will determine if regeneration
15928d6692cSGeorge Zhang 	 * is okay. Since there isn't 4B - 16 VMs running on a given
16028d6692cSGeorge Zhang 	 * host, the below loop will terminate.
16128d6692cSGeorge Zhang 	 */
16228d6692cSGeorge Zhang 	spin_lock(&ctx_list.lock);
16328d6692cSGeorge Zhang 
16428d6692cSGeorge Zhang 	while (vmci_ctx_exists(cid)) {
16528d6692cSGeorge Zhang 		/* We reserve the lowest 16 ids for fixed contexts. */
16628d6692cSGeorge Zhang 		cid = max(cid, VMCI_RESERVED_CID_LIMIT - 1) + 1;
16728d6692cSGeorge Zhang 		if (cid == VMCI_INVALID_ID)
16828d6692cSGeorge Zhang 			cid = VMCI_RESERVED_CID_LIMIT;
16928d6692cSGeorge Zhang 	}
17028d6692cSGeorge Zhang 	context->cid = cid;
17128d6692cSGeorge Zhang 
17228d6692cSGeorge Zhang 	list_add_tail_rcu(&context->list_item, &ctx_list.head);
17328d6692cSGeorge Zhang 	spin_unlock(&ctx_list.lock);
17428d6692cSGeorge Zhang 
17528d6692cSGeorge Zhang 	return context;
17628d6692cSGeorge Zhang 
17728d6692cSGeorge Zhang  err_free_db_array:
17828d6692cSGeorge Zhang 	vmci_handle_arr_destroy(context->doorbell_array);
17928d6692cSGeorge Zhang  err_free_qp_array:
18028d6692cSGeorge Zhang 	vmci_handle_arr_destroy(context->queue_pair_array);
18128d6692cSGeorge Zhang  err_free_ctx:
18228d6692cSGeorge Zhang 	kfree(context);
18328d6692cSGeorge Zhang  err_out:
18428d6692cSGeorge Zhang 	return ERR_PTR(error);
18528d6692cSGeorge Zhang }
18628d6692cSGeorge Zhang 
18728d6692cSGeorge Zhang /*
18828d6692cSGeorge Zhang  * Destroy VMCI context.
18928d6692cSGeorge Zhang  */
19028d6692cSGeorge Zhang void vmci_ctx_destroy(struct vmci_ctx *context)
19128d6692cSGeorge Zhang {
19228d6692cSGeorge Zhang 	spin_lock(&ctx_list.lock);
19328d6692cSGeorge Zhang 	list_del_rcu(&context->list_item);
19428d6692cSGeorge Zhang 	spin_unlock(&ctx_list.lock);
19528d6692cSGeorge Zhang 	synchronize_rcu();
19628d6692cSGeorge Zhang 
19728d6692cSGeorge Zhang 	vmci_ctx_put(context);
19828d6692cSGeorge Zhang }
19928d6692cSGeorge Zhang 
20028d6692cSGeorge Zhang /*
20128d6692cSGeorge Zhang  * Fire notification for all contexts interested in given cid.
20228d6692cSGeorge Zhang  */
20328d6692cSGeorge Zhang static int ctx_fire_notification(u32 context_id, u32 priv_flags)
20428d6692cSGeorge Zhang {
20528d6692cSGeorge Zhang 	u32 i, array_size;
20628d6692cSGeorge Zhang 	struct vmci_ctx *sub_ctx;
20728d6692cSGeorge Zhang 	struct vmci_handle_arr *subscriber_array;
20828d6692cSGeorge Zhang 	struct vmci_handle context_handle =
20928d6692cSGeorge Zhang 		vmci_make_handle(context_id, VMCI_EVENT_HANDLER);
21028d6692cSGeorge Zhang 
21128d6692cSGeorge Zhang 	/*
21228d6692cSGeorge Zhang 	 * We create an array to hold the subscribers we find when
21328d6692cSGeorge Zhang 	 * scanning through all contexts.
21428d6692cSGeorge Zhang 	 */
21528d6692cSGeorge Zhang 	subscriber_array = vmci_handle_arr_create(0);
21628d6692cSGeorge Zhang 	if (subscriber_array == NULL)
21728d6692cSGeorge Zhang 		return VMCI_ERROR_NO_MEM;
21828d6692cSGeorge Zhang 
21928d6692cSGeorge Zhang 	/*
22028d6692cSGeorge Zhang 	 * Scan all contexts to find who is interested in being
22128d6692cSGeorge Zhang 	 * notified about given contextID.
22228d6692cSGeorge Zhang 	 */
22328d6692cSGeorge Zhang 	rcu_read_lock();
22428d6692cSGeorge Zhang 	list_for_each_entry_rcu(sub_ctx, &ctx_list.head, list_item) {
22528d6692cSGeorge Zhang 		struct vmci_handle_list *node;
22628d6692cSGeorge Zhang 
22728d6692cSGeorge Zhang 		/*
22828d6692cSGeorge Zhang 		 * We only deliver notifications of the removal of
22928d6692cSGeorge Zhang 		 * contexts, if the two contexts are allowed to
23028d6692cSGeorge Zhang 		 * interact.
23128d6692cSGeorge Zhang 		 */
23228d6692cSGeorge Zhang 		if (vmci_deny_interaction(priv_flags, sub_ctx->priv_flags))
23328d6692cSGeorge Zhang 			continue;
23428d6692cSGeorge Zhang 
23528d6692cSGeorge Zhang 		list_for_each_entry_rcu(node, &sub_ctx->notifier_list, node) {
23628d6692cSGeorge Zhang 			if (!vmci_handle_is_equal(node->handle, context_handle))
23728d6692cSGeorge Zhang 				continue;
23828d6692cSGeorge Zhang 
23928d6692cSGeorge Zhang 			vmci_handle_arr_append_entry(&subscriber_array,
24028d6692cSGeorge Zhang 					vmci_make_handle(sub_ctx->cid,
24128d6692cSGeorge Zhang 							 VMCI_EVENT_HANDLER));
24228d6692cSGeorge Zhang 		}
24328d6692cSGeorge Zhang 	}
24428d6692cSGeorge Zhang 	rcu_read_unlock();
24528d6692cSGeorge Zhang 
24628d6692cSGeorge Zhang 	/* Fire event to all subscribers. */
24728d6692cSGeorge Zhang 	array_size = vmci_handle_arr_get_size(subscriber_array);
24828d6692cSGeorge Zhang 	for (i = 0; i < array_size; i++) {
24928d6692cSGeorge Zhang 		int result;
25028d6692cSGeorge Zhang 		struct vmci_event_ctx ev;
25128d6692cSGeorge Zhang 
25228d6692cSGeorge Zhang 		ev.msg.hdr.dst = vmci_handle_arr_get_entry(subscriber_array, i);
25328d6692cSGeorge Zhang 		ev.msg.hdr.src = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
25428d6692cSGeorge Zhang 						  VMCI_CONTEXT_RESOURCE_ID);
25528d6692cSGeorge Zhang 		ev.msg.hdr.payload_size = sizeof(ev) - sizeof(ev.msg.hdr);
25628d6692cSGeorge Zhang 		ev.msg.event_data.event = VMCI_EVENT_CTX_REMOVED;
25728d6692cSGeorge Zhang 		ev.payload.context_id = context_id;
25828d6692cSGeorge Zhang 
25928d6692cSGeorge Zhang 		result = vmci_datagram_dispatch(VMCI_HYPERVISOR_CONTEXT_ID,
26028d6692cSGeorge Zhang 						&ev.msg.hdr, false);
26128d6692cSGeorge Zhang 		if (result < VMCI_SUCCESS) {
26228d6692cSGeorge Zhang 			pr_devel("Failed to enqueue event datagram (type=%d) for context (ID=0x%x)\n",
26328d6692cSGeorge Zhang 				 ev.msg.event_data.event,
26428d6692cSGeorge Zhang 				 ev.msg.hdr.dst.context);
26528d6692cSGeorge Zhang 			/* We continue to enqueue on next subscriber. */
26628d6692cSGeorge Zhang 		}
26728d6692cSGeorge Zhang 	}
26828d6692cSGeorge Zhang 	vmci_handle_arr_destroy(subscriber_array);
26928d6692cSGeorge Zhang 
27028d6692cSGeorge Zhang 	return VMCI_SUCCESS;
27128d6692cSGeorge Zhang }
27228d6692cSGeorge Zhang 
27328d6692cSGeorge Zhang /*
27428d6692cSGeorge Zhang  * Returns the current number of pending datagrams. The call may
27528d6692cSGeorge Zhang  * also serve as a synchronization point for the datagram queue,
27628d6692cSGeorge Zhang  * as no enqueue operations can occur concurrently.
27728d6692cSGeorge Zhang  */
27828d6692cSGeorge Zhang int vmci_ctx_pending_datagrams(u32 cid, u32 *pending)
27928d6692cSGeorge Zhang {
28028d6692cSGeorge Zhang 	struct vmci_ctx *context;
28128d6692cSGeorge Zhang 
28228d6692cSGeorge Zhang 	context = vmci_ctx_get(cid);
28328d6692cSGeorge Zhang 	if (context == NULL)
28428d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
28528d6692cSGeorge Zhang 
28628d6692cSGeorge Zhang 	spin_lock(&context->lock);
28728d6692cSGeorge Zhang 	if (pending)
28828d6692cSGeorge Zhang 		*pending = context->pending_datagrams;
28928d6692cSGeorge Zhang 	spin_unlock(&context->lock);
29028d6692cSGeorge Zhang 	vmci_ctx_put(context);
29128d6692cSGeorge Zhang 
29228d6692cSGeorge Zhang 	return VMCI_SUCCESS;
29328d6692cSGeorge Zhang }
29428d6692cSGeorge Zhang 
29528d6692cSGeorge Zhang /*
29628d6692cSGeorge Zhang  * Queues a VMCI datagram for the appropriate target VM context.
29728d6692cSGeorge Zhang  */
29828d6692cSGeorge Zhang int vmci_ctx_enqueue_datagram(u32 cid, struct vmci_datagram *dg)
29928d6692cSGeorge Zhang {
30028d6692cSGeorge Zhang 	struct vmci_datagram_queue_entry *dq_entry;
30128d6692cSGeorge Zhang 	struct vmci_ctx *context;
30228d6692cSGeorge Zhang 	struct vmci_handle dg_src;
30328d6692cSGeorge Zhang 	size_t vmci_dg_size;
30428d6692cSGeorge Zhang 
30528d6692cSGeorge Zhang 	vmci_dg_size = VMCI_DG_SIZE(dg);
30628d6692cSGeorge Zhang 	if (vmci_dg_size > VMCI_MAX_DG_SIZE) {
3075b5e0928SAlexey Dobriyan 		pr_devel("Datagram too large (bytes=%zu)\n", vmci_dg_size);
30828d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
30928d6692cSGeorge Zhang 	}
31028d6692cSGeorge Zhang 
31128d6692cSGeorge Zhang 	/* Get the target VM's VMCI context. */
31228d6692cSGeorge Zhang 	context = vmci_ctx_get(cid);
31328d6692cSGeorge Zhang 	if (!context) {
31428d6692cSGeorge Zhang 		pr_devel("Invalid context (ID=0x%x)\n", cid);
31528d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
31628d6692cSGeorge Zhang 	}
31728d6692cSGeorge Zhang 
31828d6692cSGeorge Zhang 	/* Allocate guest call entry and add it to the target VM's queue. */
31928d6692cSGeorge Zhang 	dq_entry = kmalloc(sizeof(*dq_entry), GFP_KERNEL);
32028d6692cSGeorge Zhang 	if (dq_entry == NULL) {
32128d6692cSGeorge Zhang 		pr_warn("Failed to allocate memory for datagram\n");
32228d6692cSGeorge Zhang 		vmci_ctx_put(context);
32328d6692cSGeorge Zhang 		return VMCI_ERROR_NO_MEM;
32428d6692cSGeorge Zhang 	}
32528d6692cSGeorge Zhang 	dq_entry->dg = dg;
32628d6692cSGeorge Zhang 	dq_entry->dg_size = vmci_dg_size;
32728d6692cSGeorge Zhang 	dg_src = dg->src;
32828d6692cSGeorge Zhang 	INIT_LIST_HEAD(&dq_entry->list_item);
32928d6692cSGeorge Zhang 
33028d6692cSGeorge Zhang 	spin_lock(&context->lock);
33128d6692cSGeorge Zhang 
33228d6692cSGeorge Zhang 	/*
33328d6692cSGeorge Zhang 	 * We put a higher limit on datagrams from the hypervisor.  If
33428d6692cSGeorge Zhang 	 * the pending datagram is not from hypervisor, then we check
33528d6692cSGeorge Zhang 	 * if enqueueing it would exceed the
33628d6692cSGeorge Zhang 	 * VMCI_MAX_DATAGRAM_QUEUE_SIZE limit on the destination.  If
33728d6692cSGeorge Zhang 	 * the pending datagram is from hypervisor, we allow it to be
33828d6692cSGeorge Zhang 	 * queued at the destination side provided we don't reach the
33928d6692cSGeorge Zhang 	 * VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE limit.
34028d6692cSGeorge Zhang 	 */
34128d6692cSGeorge Zhang 	if (context->datagram_queue_size + vmci_dg_size >=
34228d6692cSGeorge Zhang 	    VMCI_MAX_DATAGRAM_QUEUE_SIZE &&
34328d6692cSGeorge Zhang 	    (!vmci_handle_is_equal(dg_src,
34428d6692cSGeorge Zhang 				vmci_make_handle
34528d6692cSGeorge Zhang 				(VMCI_HYPERVISOR_CONTEXT_ID,
34628d6692cSGeorge Zhang 				 VMCI_CONTEXT_RESOURCE_ID)) ||
34728d6692cSGeorge Zhang 	     context->datagram_queue_size + vmci_dg_size >=
34828d6692cSGeorge Zhang 	     VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE)) {
34928d6692cSGeorge Zhang 		spin_unlock(&context->lock);
35028d6692cSGeorge Zhang 		vmci_ctx_put(context);
35128d6692cSGeorge Zhang 		kfree(dq_entry);
35228d6692cSGeorge Zhang 		pr_devel("Context (ID=0x%x) receive queue is full\n", cid);
35328d6692cSGeorge Zhang 		return VMCI_ERROR_NO_RESOURCES;
35428d6692cSGeorge Zhang 	}
35528d6692cSGeorge Zhang 
35628d6692cSGeorge Zhang 	list_add(&dq_entry->list_item, &context->datagram_queue);
35728d6692cSGeorge Zhang 	context->pending_datagrams++;
35828d6692cSGeorge Zhang 	context->datagram_queue_size += vmci_dg_size;
35928d6692cSGeorge Zhang 	ctx_signal_notify(context);
36028d6692cSGeorge Zhang 	wake_up(&context->host_context.wait_queue);
36128d6692cSGeorge Zhang 	spin_unlock(&context->lock);
36228d6692cSGeorge Zhang 	vmci_ctx_put(context);
36328d6692cSGeorge Zhang 
36428d6692cSGeorge Zhang 	return vmci_dg_size;
36528d6692cSGeorge Zhang }
36628d6692cSGeorge Zhang 
36728d6692cSGeorge Zhang /*
36828d6692cSGeorge Zhang  * Verifies whether a context with the specified context ID exists.
36928d6692cSGeorge Zhang  * FIXME: utility is dubious as no decisions can be reliably made
37028d6692cSGeorge Zhang  * using this data as context can appear and disappear at any time.
37128d6692cSGeorge Zhang  */
37228d6692cSGeorge Zhang bool vmci_ctx_exists(u32 cid)
37328d6692cSGeorge Zhang {
37428d6692cSGeorge Zhang 	struct vmci_ctx *context;
37528d6692cSGeorge Zhang 	bool exists = false;
37628d6692cSGeorge Zhang 
37728d6692cSGeorge Zhang 	rcu_read_lock();
37828d6692cSGeorge Zhang 
37928d6692cSGeorge Zhang 	list_for_each_entry_rcu(context, &ctx_list.head, list_item) {
38028d6692cSGeorge Zhang 		if (context->cid == cid) {
38128d6692cSGeorge Zhang 			exists = true;
38228d6692cSGeorge Zhang 			break;
38328d6692cSGeorge Zhang 		}
38428d6692cSGeorge Zhang 	}
38528d6692cSGeorge Zhang 
38628d6692cSGeorge Zhang 	rcu_read_unlock();
38728d6692cSGeorge Zhang 	return exists;
38828d6692cSGeorge Zhang }
38928d6692cSGeorge Zhang 
39028d6692cSGeorge Zhang /*
39128d6692cSGeorge Zhang  * Retrieves VMCI context corresponding to the given cid.
39228d6692cSGeorge Zhang  */
39328d6692cSGeorge Zhang struct vmci_ctx *vmci_ctx_get(u32 cid)
39428d6692cSGeorge Zhang {
39528d6692cSGeorge Zhang 	struct vmci_ctx *c, *context = NULL;
39628d6692cSGeorge Zhang 
39728d6692cSGeorge Zhang 	if (cid == VMCI_INVALID_ID)
39828d6692cSGeorge Zhang 		return NULL;
39928d6692cSGeorge Zhang 
40028d6692cSGeorge Zhang 	rcu_read_lock();
40128d6692cSGeorge Zhang 	list_for_each_entry_rcu(c, &ctx_list.head, list_item) {
40228d6692cSGeorge Zhang 		if (c->cid == cid) {
40328d6692cSGeorge Zhang 			/*
40428d6692cSGeorge Zhang 			 * The context owner drops its own reference to the
40528d6692cSGeorge Zhang 			 * context only after removing it from the list and
40628d6692cSGeorge Zhang 			 * waiting for RCU grace period to expire. This
40728d6692cSGeorge Zhang 			 * means that we are not about to increase the
40828d6692cSGeorge Zhang 			 * reference count of something that is in the
40928d6692cSGeorge Zhang 			 * process of being destroyed.
41028d6692cSGeorge Zhang 			 */
41128d6692cSGeorge Zhang 			context = c;
41228d6692cSGeorge Zhang 			kref_get(&context->kref);
41328d6692cSGeorge Zhang 			break;
41428d6692cSGeorge Zhang 		}
41528d6692cSGeorge Zhang 	}
41628d6692cSGeorge Zhang 	rcu_read_unlock();
41728d6692cSGeorge Zhang 
41828d6692cSGeorge Zhang 	return context;
41928d6692cSGeorge Zhang }
42028d6692cSGeorge Zhang 
42128d6692cSGeorge Zhang /*
42228d6692cSGeorge Zhang  * Deallocates all parts of a context data structure. This
42328d6692cSGeorge Zhang  * function doesn't lock the context, because it assumes that
42428d6692cSGeorge Zhang  * the caller was holding the last reference to context.
42528d6692cSGeorge Zhang  */
42628d6692cSGeorge Zhang static void ctx_free_ctx(struct kref *kref)
42728d6692cSGeorge Zhang {
42828d6692cSGeorge Zhang 	struct vmci_ctx *context = container_of(kref, struct vmci_ctx, kref);
42928d6692cSGeorge Zhang 	struct vmci_datagram_queue_entry *dq_entry, *dq_entry_tmp;
43028d6692cSGeorge Zhang 	struct vmci_handle temp_handle;
43128d6692cSGeorge Zhang 	struct vmci_handle_list *notifier, *tmp;
43228d6692cSGeorge Zhang 
43328d6692cSGeorge Zhang 	/*
43428d6692cSGeorge Zhang 	 * Fire event to all contexts interested in knowing this
43528d6692cSGeorge Zhang 	 * context is dying.
43628d6692cSGeorge Zhang 	 */
43728d6692cSGeorge Zhang 	ctx_fire_notification(context->cid, context->priv_flags);
43828d6692cSGeorge Zhang 
43928d6692cSGeorge Zhang 	/*
44028d6692cSGeorge Zhang 	 * Cleanup all queue pair resources attached to context.  If
44128d6692cSGeorge Zhang 	 * the VM dies without cleaning up, this code will make sure
44228d6692cSGeorge Zhang 	 * that no resources are leaked.
44328d6692cSGeorge Zhang 	 */
44428d6692cSGeorge Zhang 	temp_handle = vmci_handle_arr_get_entry(context->queue_pair_array, 0);
44528d6692cSGeorge Zhang 	while (!vmci_handle_is_equal(temp_handle, VMCI_INVALID_HANDLE)) {
44628d6692cSGeorge Zhang 		if (vmci_qp_broker_detach(temp_handle,
44728d6692cSGeorge Zhang 					  context) < VMCI_SUCCESS) {
44828d6692cSGeorge Zhang 			/*
44928d6692cSGeorge Zhang 			 * When vmci_qp_broker_detach() succeeds it
45028d6692cSGeorge Zhang 			 * removes the handle from the array.  If
45128d6692cSGeorge Zhang 			 * detach fails, we must remove the handle
45228d6692cSGeorge Zhang 			 * ourselves.
45328d6692cSGeorge Zhang 			 */
45428d6692cSGeorge Zhang 			vmci_handle_arr_remove_entry(context->queue_pair_array,
45528d6692cSGeorge Zhang 						     temp_handle);
45628d6692cSGeorge Zhang 		}
45728d6692cSGeorge Zhang 		temp_handle =
45828d6692cSGeorge Zhang 		    vmci_handle_arr_get_entry(context->queue_pair_array, 0);
45928d6692cSGeorge Zhang 	}
46028d6692cSGeorge Zhang 
46128d6692cSGeorge Zhang 	/*
46228d6692cSGeorge Zhang 	 * It is fine to destroy this without locking the callQueue, as
46328d6692cSGeorge Zhang 	 * this is the only thread having a reference to the context.
46428d6692cSGeorge Zhang 	 */
46528d6692cSGeorge Zhang 	list_for_each_entry_safe(dq_entry, dq_entry_tmp,
46628d6692cSGeorge Zhang 				 &context->datagram_queue, list_item) {
46728d6692cSGeorge Zhang 		WARN_ON(dq_entry->dg_size != VMCI_DG_SIZE(dq_entry->dg));
46828d6692cSGeorge Zhang 		list_del(&dq_entry->list_item);
46928d6692cSGeorge Zhang 		kfree(dq_entry->dg);
47028d6692cSGeorge Zhang 		kfree(dq_entry);
47128d6692cSGeorge Zhang 	}
47228d6692cSGeorge Zhang 
47328d6692cSGeorge Zhang 	list_for_each_entry_safe(notifier, tmp,
47428d6692cSGeorge Zhang 				 &context->notifier_list, node) {
47528d6692cSGeorge Zhang 		list_del(&notifier->node);
47628d6692cSGeorge Zhang 		kfree(notifier);
47728d6692cSGeorge Zhang 	}
47828d6692cSGeorge Zhang 
47928d6692cSGeorge Zhang 	vmci_handle_arr_destroy(context->queue_pair_array);
48028d6692cSGeorge Zhang 	vmci_handle_arr_destroy(context->doorbell_array);
48128d6692cSGeorge Zhang 	vmci_handle_arr_destroy(context->pending_doorbell_array);
48228d6692cSGeorge Zhang 	vmci_ctx_unset_notify(context);
48328d6692cSGeorge Zhang 	if (context->cred)
48428d6692cSGeorge Zhang 		put_cred(context->cred);
48528d6692cSGeorge Zhang 	kfree(context);
48628d6692cSGeorge Zhang }
48728d6692cSGeorge Zhang 
48828d6692cSGeorge Zhang /*
48928d6692cSGeorge Zhang  * Drops reference to VMCI context. If this is the last reference to
49028d6692cSGeorge Zhang  * the context it will be deallocated. A context is created with
49128d6692cSGeorge Zhang  * a reference count of one, and on destroy, it is removed from
49228d6692cSGeorge Zhang  * the context list before its reference count is decremented. Thus,
49328d6692cSGeorge Zhang  * if we reach zero, we are sure that nobody else are about to increment
49428d6692cSGeorge Zhang  * it (they need the entry in the context list for that), and so there
49528d6692cSGeorge Zhang  * is no need for locking.
49628d6692cSGeorge Zhang  */
49728d6692cSGeorge Zhang void vmci_ctx_put(struct vmci_ctx *context)
49828d6692cSGeorge Zhang {
49928d6692cSGeorge Zhang 	kref_put(&context->kref, ctx_free_ctx);
50028d6692cSGeorge Zhang }
50128d6692cSGeorge Zhang 
50228d6692cSGeorge Zhang /*
50328d6692cSGeorge Zhang  * Dequeues the next datagram and returns it to caller.
50428d6692cSGeorge Zhang  * The caller passes in a pointer to the max size datagram
50528d6692cSGeorge Zhang  * it can handle and the datagram is only unqueued if the
50628d6692cSGeorge Zhang  * size is less than max_size. If larger max_size is set to
50728d6692cSGeorge Zhang  * the size of the datagram to give the caller a chance to
50828d6692cSGeorge Zhang  * set up a larger buffer for the guestcall.
50928d6692cSGeorge Zhang  */
51028d6692cSGeorge Zhang int vmci_ctx_dequeue_datagram(struct vmci_ctx *context,
51128d6692cSGeorge Zhang 			      size_t *max_size,
51228d6692cSGeorge Zhang 			      struct vmci_datagram **dg)
51328d6692cSGeorge Zhang {
51428d6692cSGeorge Zhang 	struct vmci_datagram_queue_entry *dq_entry;
51528d6692cSGeorge Zhang 	struct list_head *list_item;
51628d6692cSGeorge Zhang 	int rv;
51728d6692cSGeorge Zhang 
51828d6692cSGeorge Zhang 	/* Dequeue the next datagram entry. */
51928d6692cSGeorge Zhang 	spin_lock(&context->lock);
52028d6692cSGeorge Zhang 	if (context->pending_datagrams == 0) {
52128d6692cSGeorge Zhang 		ctx_clear_notify_call(context);
52228d6692cSGeorge Zhang 		spin_unlock(&context->lock);
52328d6692cSGeorge Zhang 		pr_devel("No datagrams pending\n");
52428d6692cSGeorge Zhang 		return VMCI_ERROR_NO_MORE_DATAGRAMS;
52528d6692cSGeorge Zhang 	}
52628d6692cSGeorge Zhang 
52728d6692cSGeorge Zhang 	list_item = context->datagram_queue.next;
52828d6692cSGeorge Zhang 
52928d6692cSGeorge Zhang 	dq_entry =
53028d6692cSGeorge Zhang 	    list_entry(list_item, struct vmci_datagram_queue_entry, list_item);
53128d6692cSGeorge Zhang 
53228d6692cSGeorge Zhang 	/* Check size of caller's buffer. */
53328d6692cSGeorge Zhang 	if (*max_size < dq_entry->dg_size) {
53428d6692cSGeorge Zhang 		*max_size = dq_entry->dg_size;
53528d6692cSGeorge Zhang 		spin_unlock(&context->lock);
53628d6692cSGeorge Zhang 		pr_devel("Caller's buffer should be at least (size=%u bytes)\n",
53728d6692cSGeorge Zhang 			 (u32) *max_size);
53828d6692cSGeorge Zhang 		return VMCI_ERROR_NO_MEM;
53928d6692cSGeorge Zhang 	}
54028d6692cSGeorge Zhang 
54128d6692cSGeorge Zhang 	list_del(list_item);
54228d6692cSGeorge Zhang 	context->pending_datagrams--;
54328d6692cSGeorge Zhang 	context->datagram_queue_size -= dq_entry->dg_size;
54428d6692cSGeorge Zhang 	if (context->pending_datagrams == 0) {
54528d6692cSGeorge Zhang 		ctx_clear_notify_call(context);
54628d6692cSGeorge Zhang 		rv = VMCI_SUCCESS;
54728d6692cSGeorge Zhang 	} else {
54828d6692cSGeorge Zhang 		/*
54928d6692cSGeorge Zhang 		 * Return the size of the next datagram.
55028d6692cSGeorge Zhang 		 */
55128d6692cSGeorge Zhang 		struct vmci_datagram_queue_entry *next_entry;
55228d6692cSGeorge Zhang 
55328d6692cSGeorge Zhang 		list_item = context->datagram_queue.next;
55428d6692cSGeorge Zhang 		next_entry =
55528d6692cSGeorge Zhang 		    list_entry(list_item, struct vmci_datagram_queue_entry,
55628d6692cSGeorge Zhang 			       list_item);
55728d6692cSGeorge Zhang 
55828d6692cSGeorge Zhang 		/*
55928d6692cSGeorge Zhang 		 * The following size_t -> int truncation is fine as
56028d6692cSGeorge Zhang 		 * the maximum size of a (routable) datagram is 68KB.
56128d6692cSGeorge Zhang 		 */
56228d6692cSGeorge Zhang 		rv = (int)next_entry->dg_size;
56328d6692cSGeorge Zhang 	}
56428d6692cSGeorge Zhang 	spin_unlock(&context->lock);
56528d6692cSGeorge Zhang 
56628d6692cSGeorge Zhang 	/* Caller must free datagram. */
56728d6692cSGeorge Zhang 	*dg = dq_entry->dg;
56828d6692cSGeorge Zhang 	dq_entry->dg = NULL;
56928d6692cSGeorge Zhang 	kfree(dq_entry);
57028d6692cSGeorge Zhang 
57128d6692cSGeorge Zhang 	return rv;
57228d6692cSGeorge Zhang }
57328d6692cSGeorge Zhang 
57428d6692cSGeorge Zhang /*
57528d6692cSGeorge Zhang  * Reverts actions set up by vmci_setup_notify().  Unmaps and unlocks the
57628d6692cSGeorge Zhang  * page mapped/locked by vmci_setup_notify().
57728d6692cSGeorge Zhang  */
57828d6692cSGeorge Zhang void vmci_ctx_unset_notify(struct vmci_ctx *context)
57928d6692cSGeorge Zhang {
58028d6692cSGeorge Zhang 	struct page *notify_page;
58128d6692cSGeorge Zhang 
58228d6692cSGeorge Zhang 	spin_lock(&context->lock);
58328d6692cSGeorge Zhang 
58428d6692cSGeorge Zhang 	notify_page = context->notify_page;
58528d6692cSGeorge Zhang 	context->notify = &ctx_dummy_notify;
58628d6692cSGeorge Zhang 	context->notify_page = NULL;
58728d6692cSGeorge Zhang 
58828d6692cSGeorge Zhang 	spin_unlock(&context->lock);
58928d6692cSGeorge Zhang 
59028d6692cSGeorge Zhang 	if (notify_page) {
59128d6692cSGeorge Zhang 		kunmap(notify_page);
59228d6692cSGeorge Zhang 		put_page(notify_page);
59328d6692cSGeorge Zhang 	}
59428d6692cSGeorge Zhang }
59528d6692cSGeorge Zhang 
59628d6692cSGeorge Zhang /*
59728d6692cSGeorge Zhang  * Add remote_cid to list of contexts current contexts wants
59828d6692cSGeorge Zhang  * notifications from/about.
59928d6692cSGeorge Zhang  */
60028d6692cSGeorge Zhang int vmci_ctx_add_notification(u32 context_id, u32 remote_cid)
60128d6692cSGeorge Zhang {
60228d6692cSGeorge Zhang 	struct vmci_ctx *context;
60328d6692cSGeorge Zhang 	struct vmci_handle_list *notifier, *n;
60428d6692cSGeorge Zhang 	int result;
60528d6692cSGeorge Zhang 	bool exists = false;
60628d6692cSGeorge Zhang 
60728d6692cSGeorge Zhang 	context = vmci_ctx_get(context_id);
60828d6692cSGeorge Zhang 	if (!context)
60928d6692cSGeorge Zhang 		return VMCI_ERROR_NOT_FOUND;
61028d6692cSGeorge Zhang 
61128d6692cSGeorge Zhang 	if (VMCI_CONTEXT_IS_VM(context_id) && VMCI_CONTEXT_IS_VM(remote_cid)) {
61228d6692cSGeorge Zhang 		pr_devel("Context removed notifications for other VMs not supported (src=0x%x, remote=0x%x)\n",
61328d6692cSGeorge Zhang 			 context_id, remote_cid);
61428d6692cSGeorge Zhang 		result = VMCI_ERROR_DST_UNREACHABLE;
61528d6692cSGeorge Zhang 		goto out;
61628d6692cSGeorge Zhang 	}
61728d6692cSGeorge Zhang 
61828d6692cSGeorge Zhang 	if (context->priv_flags & VMCI_PRIVILEGE_FLAG_RESTRICTED) {
61928d6692cSGeorge Zhang 		result = VMCI_ERROR_NO_ACCESS;
62028d6692cSGeorge Zhang 		goto out;
62128d6692cSGeorge Zhang 	}
62228d6692cSGeorge Zhang 
62328d6692cSGeorge Zhang 	notifier = kmalloc(sizeof(struct vmci_handle_list), GFP_KERNEL);
62428d6692cSGeorge Zhang 	if (!notifier) {
62528d6692cSGeorge Zhang 		result = VMCI_ERROR_NO_MEM;
62628d6692cSGeorge Zhang 		goto out;
62728d6692cSGeorge Zhang 	}
62828d6692cSGeorge Zhang 
62928d6692cSGeorge Zhang 	INIT_LIST_HEAD(&notifier->node);
63028d6692cSGeorge Zhang 	notifier->handle = vmci_make_handle(remote_cid, VMCI_EVENT_HANDLER);
63128d6692cSGeorge Zhang 
63228d6692cSGeorge Zhang 	spin_lock(&context->lock);
63328d6692cSGeorge Zhang 
63428d6692cSGeorge Zhang 	list_for_each_entry(n, &context->notifier_list, node) {
63528d6692cSGeorge Zhang 		if (vmci_handle_is_equal(n->handle, notifier->handle)) {
63628d6692cSGeorge Zhang 			exists = true;
63728d6692cSGeorge Zhang 			break;
63828d6692cSGeorge Zhang 		}
63928d6692cSGeorge Zhang 	}
64028d6692cSGeorge Zhang 
64128d6692cSGeorge Zhang 	if (exists) {
64228d6692cSGeorge Zhang 		kfree(notifier);
64328d6692cSGeorge Zhang 		result = VMCI_ERROR_ALREADY_EXISTS;
64428d6692cSGeorge Zhang 	} else {
64528d6692cSGeorge Zhang 		list_add_tail_rcu(&notifier->node, &context->notifier_list);
64628d6692cSGeorge Zhang 		context->n_notifiers++;
64728d6692cSGeorge Zhang 		result = VMCI_SUCCESS;
64828d6692cSGeorge Zhang 	}
64928d6692cSGeorge Zhang 
65028d6692cSGeorge Zhang 	spin_unlock(&context->lock);
65128d6692cSGeorge Zhang 
65228d6692cSGeorge Zhang  out:
65328d6692cSGeorge Zhang 	vmci_ctx_put(context);
65428d6692cSGeorge Zhang 	return result;
65528d6692cSGeorge Zhang }
65628d6692cSGeorge Zhang 
65728d6692cSGeorge Zhang /*
65828d6692cSGeorge Zhang  * Remove remote_cid from current context's list of contexts it is
65928d6692cSGeorge Zhang  * interested in getting notifications from/about.
66028d6692cSGeorge Zhang  */
66128d6692cSGeorge Zhang int vmci_ctx_remove_notification(u32 context_id, u32 remote_cid)
66228d6692cSGeorge Zhang {
66328d6692cSGeorge Zhang 	struct vmci_ctx *context;
66428d6692cSGeorge Zhang 	struct vmci_handle_list *notifier, *tmp;
66528d6692cSGeorge Zhang 	struct vmci_handle handle;
66628d6692cSGeorge Zhang 	bool found = false;
66728d6692cSGeorge Zhang 
66828d6692cSGeorge Zhang 	context = vmci_ctx_get(context_id);
66928d6692cSGeorge Zhang 	if (!context)
67028d6692cSGeorge Zhang 		return VMCI_ERROR_NOT_FOUND;
67128d6692cSGeorge Zhang 
67228d6692cSGeorge Zhang 	handle = vmci_make_handle(remote_cid, VMCI_EVENT_HANDLER);
67328d6692cSGeorge Zhang 
67428d6692cSGeorge Zhang 	spin_lock(&context->lock);
67528d6692cSGeorge Zhang 	list_for_each_entry_safe(notifier, tmp,
67628d6692cSGeorge Zhang 				 &context->notifier_list, node) {
67728d6692cSGeorge Zhang 		if (vmci_handle_is_equal(notifier->handle, handle)) {
67828d6692cSGeorge Zhang 			list_del_rcu(&notifier->node);
67928d6692cSGeorge Zhang 			context->n_notifiers--;
68028d6692cSGeorge Zhang 			found = true;
68128d6692cSGeorge Zhang 			break;
68228d6692cSGeorge Zhang 		}
68328d6692cSGeorge Zhang 	}
68428d6692cSGeorge Zhang 	spin_unlock(&context->lock);
68528d6692cSGeorge Zhang 
68628d6692cSGeorge Zhang 	if (found) {
68728d6692cSGeorge Zhang 		synchronize_rcu();
68828d6692cSGeorge Zhang 		kfree(notifier);
68928d6692cSGeorge Zhang 	}
69028d6692cSGeorge Zhang 
69128d6692cSGeorge Zhang 	vmci_ctx_put(context);
69228d6692cSGeorge Zhang 
69328d6692cSGeorge Zhang 	return found ? VMCI_SUCCESS : VMCI_ERROR_NOT_FOUND;
69428d6692cSGeorge Zhang }
69528d6692cSGeorge Zhang 
69628d6692cSGeorge Zhang static int vmci_ctx_get_chkpt_notifiers(struct vmci_ctx *context,
69728d6692cSGeorge Zhang 					u32 *buf_size, void **pbuf)
69828d6692cSGeorge Zhang {
69928d6692cSGeorge Zhang 	u32 *notifiers;
70028d6692cSGeorge Zhang 	size_t data_size;
70128d6692cSGeorge Zhang 	struct vmci_handle_list *entry;
70228d6692cSGeorge Zhang 	int i = 0;
70328d6692cSGeorge Zhang 
70428d6692cSGeorge Zhang 	if (context->n_notifiers == 0) {
70528d6692cSGeorge Zhang 		*buf_size = 0;
70628d6692cSGeorge Zhang 		*pbuf = NULL;
70728d6692cSGeorge Zhang 		return VMCI_SUCCESS;
70828d6692cSGeorge Zhang 	}
70928d6692cSGeorge Zhang 
71028d6692cSGeorge Zhang 	data_size = context->n_notifiers * sizeof(*notifiers);
71128d6692cSGeorge Zhang 	if (*buf_size < data_size) {
71228d6692cSGeorge Zhang 		*buf_size = data_size;
71328d6692cSGeorge Zhang 		return VMCI_ERROR_MORE_DATA;
71428d6692cSGeorge Zhang 	}
71528d6692cSGeorge Zhang 
71628d6692cSGeorge Zhang 	notifiers = kmalloc(data_size, GFP_ATOMIC); /* FIXME: want GFP_KERNEL */
71728d6692cSGeorge Zhang 	if (!notifiers)
71828d6692cSGeorge Zhang 		return VMCI_ERROR_NO_MEM;
71928d6692cSGeorge Zhang 
72028d6692cSGeorge Zhang 	list_for_each_entry(entry, &context->notifier_list, node)
72128d6692cSGeorge Zhang 		notifiers[i++] = entry->handle.context;
72228d6692cSGeorge Zhang 
72328d6692cSGeorge Zhang 	*buf_size = data_size;
72428d6692cSGeorge Zhang 	*pbuf = notifiers;
72528d6692cSGeorge Zhang 	return VMCI_SUCCESS;
72628d6692cSGeorge Zhang }
72728d6692cSGeorge Zhang 
72828d6692cSGeorge Zhang static int vmci_ctx_get_chkpt_doorbells(struct vmci_ctx *context,
72928d6692cSGeorge Zhang 					u32 *buf_size, void **pbuf)
73028d6692cSGeorge Zhang {
73128d6692cSGeorge Zhang 	struct dbell_cpt_state *dbells;
73228d6692cSGeorge Zhang 	size_t n_doorbells;
73328d6692cSGeorge Zhang 	int i;
73428d6692cSGeorge Zhang 
73528d6692cSGeorge Zhang 	n_doorbells = vmci_handle_arr_get_size(context->doorbell_array);
73628d6692cSGeorge Zhang 	if (n_doorbells > 0) {
73728d6692cSGeorge Zhang 		size_t data_size = n_doorbells * sizeof(*dbells);
73828d6692cSGeorge Zhang 		if (*buf_size < data_size) {
73928d6692cSGeorge Zhang 			*buf_size = data_size;
74028d6692cSGeorge Zhang 			return VMCI_ERROR_MORE_DATA;
74128d6692cSGeorge Zhang 		}
74228d6692cSGeorge Zhang 
74328d6692cSGeorge Zhang 		dbells = kmalloc(data_size, GFP_ATOMIC);
74428d6692cSGeorge Zhang 		if (!dbells)
74528d6692cSGeorge Zhang 			return VMCI_ERROR_NO_MEM;
74628d6692cSGeorge Zhang 
74728d6692cSGeorge Zhang 		for (i = 0; i < n_doorbells; i++)
74828d6692cSGeorge Zhang 			dbells[i].handle = vmci_handle_arr_get_entry(
74928d6692cSGeorge Zhang 						context->doorbell_array, i);
75028d6692cSGeorge Zhang 
75128d6692cSGeorge Zhang 		*buf_size = data_size;
75228d6692cSGeorge Zhang 		*pbuf = dbells;
75328d6692cSGeorge Zhang 	} else {
75428d6692cSGeorge Zhang 		*buf_size = 0;
75528d6692cSGeorge Zhang 		*pbuf = NULL;
75628d6692cSGeorge Zhang 	}
75728d6692cSGeorge Zhang 
75828d6692cSGeorge Zhang 	return VMCI_SUCCESS;
75928d6692cSGeorge Zhang }
76028d6692cSGeorge Zhang 
76128d6692cSGeorge Zhang /*
76228d6692cSGeorge Zhang  * Get current context's checkpoint state of given type.
76328d6692cSGeorge Zhang  */
76428d6692cSGeorge Zhang int vmci_ctx_get_chkpt_state(u32 context_id,
76528d6692cSGeorge Zhang 			     u32 cpt_type,
76628d6692cSGeorge Zhang 			     u32 *buf_size,
76728d6692cSGeorge Zhang 			     void **pbuf)
76828d6692cSGeorge Zhang {
76928d6692cSGeorge Zhang 	struct vmci_ctx *context;
77028d6692cSGeorge Zhang 	int result;
77128d6692cSGeorge Zhang 
77228d6692cSGeorge Zhang 	context = vmci_ctx_get(context_id);
77328d6692cSGeorge Zhang 	if (!context)
77428d6692cSGeorge Zhang 		return VMCI_ERROR_NOT_FOUND;
77528d6692cSGeorge Zhang 
77628d6692cSGeorge Zhang 	spin_lock(&context->lock);
77728d6692cSGeorge Zhang 
77828d6692cSGeorge Zhang 	switch (cpt_type) {
77928d6692cSGeorge Zhang 	case VMCI_NOTIFICATION_CPT_STATE:
78028d6692cSGeorge Zhang 		result = vmci_ctx_get_chkpt_notifiers(context, buf_size, pbuf);
78128d6692cSGeorge Zhang 		break;
78228d6692cSGeorge Zhang 
78328d6692cSGeorge Zhang 	case VMCI_WELLKNOWN_CPT_STATE:
78428d6692cSGeorge Zhang 		/*
78528d6692cSGeorge Zhang 		 * For compatibility with VMX'en with VM to VM communication, we
78628d6692cSGeorge Zhang 		 * always return zero wellknown handles.
78728d6692cSGeorge Zhang 		 */
78828d6692cSGeorge Zhang 
78928d6692cSGeorge Zhang 		*buf_size = 0;
79028d6692cSGeorge Zhang 		*pbuf = NULL;
79128d6692cSGeorge Zhang 		result = VMCI_SUCCESS;
79228d6692cSGeorge Zhang 		break;
79328d6692cSGeorge Zhang 
79428d6692cSGeorge Zhang 	case VMCI_DOORBELL_CPT_STATE:
79528d6692cSGeorge Zhang 		result = vmci_ctx_get_chkpt_doorbells(context, buf_size, pbuf);
79628d6692cSGeorge Zhang 		break;
79728d6692cSGeorge Zhang 
79828d6692cSGeorge Zhang 	default:
79928d6692cSGeorge Zhang 		pr_devel("Invalid cpt state (type=%d)\n", cpt_type);
80028d6692cSGeorge Zhang 		result = VMCI_ERROR_INVALID_ARGS;
80128d6692cSGeorge Zhang 		break;
80228d6692cSGeorge Zhang 	}
80328d6692cSGeorge Zhang 
80428d6692cSGeorge Zhang 	spin_unlock(&context->lock);
80528d6692cSGeorge Zhang 	vmci_ctx_put(context);
80628d6692cSGeorge Zhang 
80728d6692cSGeorge Zhang 	return result;
80828d6692cSGeorge Zhang }
80928d6692cSGeorge Zhang 
81028d6692cSGeorge Zhang /*
81128d6692cSGeorge Zhang  * Set current context's checkpoint state of given type.
81228d6692cSGeorge Zhang  */
81328d6692cSGeorge Zhang int vmci_ctx_set_chkpt_state(u32 context_id,
81428d6692cSGeorge Zhang 			     u32 cpt_type,
81528d6692cSGeorge Zhang 			     u32 buf_size,
81628d6692cSGeorge Zhang 			     void *cpt_buf)
81728d6692cSGeorge Zhang {
81828d6692cSGeorge Zhang 	u32 i;
81928d6692cSGeorge Zhang 	u32 current_id;
82028d6692cSGeorge Zhang 	int result = VMCI_SUCCESS;
82128d6692cSGeorge Zhang 	u32 num_ids = buf_size / sizeof(u32);
82228d6692cSGeorge Zhang 
82328d6692cSGeorge Zhang 	if (cpt_type == VMCI_WELLKNOWN_CPT_STATE && num_ids > 0) {
82428d6692cSGeorge Zhang 		/*
82528d6692cSGeorge Zhang 		 * We would end up here if VMX with VM to VM communication
82628d6692cSGeorge Zhang 		 * attempts to restore a checkpoint with wellknown handles.
82728d6692cSGeorge Zhang 		 */
82828d6692cSGeorge Zhang 		pr_warn("Attempt to restore checkpoint with obsolete wellknown handles\n");
82928d6692cSGeorge Zhang 		return VMCI_ERROR_OBSOLETE;
83028d6692cSGeorge Zhang 	}
83128d6692cSGeorge Zhang 
83228d6692cSGeorge Zhang 	if (cpt_type != VMCI_NOTIFICATION_CPT_STATE) {
83328d6692cSGeorge Zhang 		pr_devel("Invalid cpt state (type=%d)\n", cpt_type);
83428d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
83528d6692cSGeorge Zhang 	}
83628d6692cSGeorge Zhang 
83728d6692cSGeorge Zhang 	for (i = 0; i < num_ids && result == VMCI_SUCCESS; i++) {
83828d6692cSGeorge Zhang 		current_id = ((u32 *)cpt_buf)[i];
83928d6692cSGeorge Zhang 		result = vmci_ctx_add_notification(context_id, current_id);
84028d6692cSGeorge Zhang 		if (result != VMCI_SUCCESS)
84128d6692cSGeorge Zhang 			break;
84228d6692cSGeorge Zhang 	}
84328d6692cSGeorge Zhang 	if (result != VMCI_SUCCESS)
84428d6692cSGeorge Zhang 		pr_devel("Failed to set cpt state (type=%d) (error=%d)\n",
84528d6692cSGeorge Zhang 			 cpt_type, result);
84628d6692cSGeorge Zhang 
84728d6692cSGeorge Zhang 	return result;
84828d6692cSGeorge Zhang }
84928d6692cSGeorge Zhang 
85028d6692cSGeorge Zhang /*
85128d6692cSGeorge Zhang  * Retrieves the specified context's pending notifications in the
85228d6692cSGeorge Zhang  * form of a handle array. The handle arrays returned are the
85328d6692cSGeorge Zhang  * actual data - not a copy and should not be modified by the
85428d6692cSGeorge Zhang  * caller. They must be released using
85528d6692cSGeorge Zhang  * vmci_ctx_rcv_notifications_release.
85628d6692cSGeorge Zhang  */
85728d6692cSGeorge Zhang int vmci_ctx_rcv_notifications_get(u32 context_id,
85828d6692cSGeorge Zhang 				   struct vmci_handle_arr **db_handle_array,
85928d6692cSGeorge Zhang 				   struct vmci_handle_arr **qp_handle_array)
86028d6692cSGeorge Zhang {
86128d6692cSGeorge Zhang 	struct vmci_ctx *context;
86228d6692cSGeorge Zhang 	int result = VMCI_SUCCESS;
86328d6692cSGeorge Zhang 
86428d6692cSGeorge Zhang 	context = vmci_ctx_get(context_id);
86528d6692cSGeorge Zhang 	if (context == NULL)
86628d6692cSGeorge Zhang 		return VMCI_ERROR_NOT_FOUND;
86728d6692cSGeorge Zhang 
86828d6692cSGeorge Zhang 	spin_lock(&context->lock);
86928d6692cSGeorge Zhang 
87028d6692cSGeorge Zhang 	*db_handle_array = context->pending_doorbell_array;
87128d6692cSGeorge Zhang 	context->pending_doorbell_array = vmci_handle_arr_create(0);
87228d6692cSGeorge Zhang 	if (!context->pending_doorbell_array) {
87328d6692cSGeorge Zhang 		context->pending_doorbell_array = *db_handle_array;
87428d6692cSGeorge Zhang 		*db_handle_array = NULL;
87528d6692cSGeorge Zhang 		result = VMCI_ERROR_NO_MEM;
87628d6692cSGeorge Zhang 	}
87728d6692cSGeorge Zhang 	*qp_handle_array = NULL;
87828d6692cSGeorge Zhang 
87928d6692cSGeorge Zhang 	spin_unlock(&context->lock);
88028d6692cSGeorge Zhang 	vmci_ctx_put(context);
88128d6692cSGeorge Zhang 
88228d6692cSGeorge Zhang 	return result;
88328d6692cSGeorge Zhang }
88428d6692cSGeorge Zhang 
88528d6692cSGeorge Zhang /*
88628d6692cSGeorge Zhang  * Releases handle arrays with pending notifications previously
88728d6692cSGeorge Zhang  * retrieved using vmci_ctx_rcv_notifications_get. If the
88828d6692cSGeorge Zhang  * notifications were not successfully handed over to the guest,
88928d6692cSGeorge Zhang  * success must be false.
89028d6692cSGeorge Zhang  */
89128d6692cSGeorge Zhang void vmci_ctx_rcv_notifications_release(u32 context_id,
89228d6692cSGeorge Zhang 					struct vmci_handle_arr *db_handle_array,
89328d6692cSGeorge Zhang 					struct vmci_handle_arr *qp_handle_array,
89428d6692cSGeorge Zhang 					bool success)
89528d6692cSGeorge Zhang {
89628d6692cSGeorge Zhang 	struct vmci_ctx *context = vmci_ctx_get(context_id);
89728d6692cSGeorge Zhang 
89828d6692cSGeorge Zhang 	spin_lock(&context->lock);
89928d6692cSGeorge Zhang 	if (!success) {
90028d6692cSGeorge Zhang 		struct vmci_handle handle;
90128d6692cSGeorge Zhang 
90228d6692cSGeorge Zhang 		/*
90328d6692cSGeorge Zhang 		 * New notifications may have been added while we were not
90428d6692cSGeorge Zhang 		 * holding the context lock, so we transfer any new pending
90528d6692cSGeorge Zhang 		 * doorbell notifications to the old array, and reinstate the
90628d6692cSGeorge Zhang 		 * old array.
90728d6692cSGeorge Zhang 		 */
90828d6692cSGeorge Zhang 
90928d6692cSGeorge Zhang 		handle = vmci_handle_arr_remove_tail(
91028d6692cSGeorge Zhang 					context->pending_doorbell_array);
91128d6692cSGeorge Zhang 		while (!vmci_handle_is_invalid(handle)) {
91228d6692cSGeorge Zhang 			if (!vmci_handle_arr_has_entry(db_handle_array,
91328d6692cSGeorge Zhang 						       handle)) {
91428d6692cSGeorge Zhang 				vmci_handle_arr_append_entry(
91528d6692cSGeorge Zhang 						&db_handle_array, handle);
91628d6692cSGeorge Zhang 			}
91728d6692cSGeorge Zhang 			handle = vmci_handle_arr_remove_tail(
91828d6692cSGeorge Zhang 					context->pending_doorbell_array);
91928d6692cSGeorge Zhang 		}
92028d6692cSGeorge Zhang 		vmci_handle_arr_destroy(context->pending_doorbell_array);
92128d6692cSGeorge Zhang 		context->pending_doorbell_array = db_handle_array;
92228d6692cSGeorge Zhang 		db_handle_array = NULL;
92328d6692cSGeorge Zhang 	} else {
92428d6692cSGeorge Zhang 		ctx_clear_notify_call(context);
92528d6692cSGeorge Zhang 	}
92628d6692cSGeorge Zhang 	spin_unlock(&context->lock);
92728d6692cSGeorge Zhang 	vmci_ctx_put(context);
92828d6692cSGeorge Zhang 
92928d6692cSGeorge Zhang 	if (db_handle_array)
93028d6692cSGeorge Zhang 		vmci_handle_arr_destroy(db_handle_array);
93128d6692cSGeorge Zhang 
93228d6692cSGeorge Zhang 	if (qp_handle_array)
93328d6692cSGeorge Zhang 		vmci_handle_arr_destroy(qp_handle_array);
93428d6692cSGeorge Zhang }
93528d6692cSGeorge Zhang 
93628d6692cSGeorge Zhang /*
93728d6692cSGeorge Zhang  * Registers that a new doorbell handle has been allocated by the
93828d6692cSGeorge Zhang  * context. Only doorbell handles registered can be notified.
93928d6692cSGeorge Zhang  */
94028d6692cSGeorge Zhang int vmci_ctx_dbell_create(u32 context_id, struct vmci_handle handle)
94128d6692cSGeorge Zhang {
94228d6692cSGeorge Zhang 	struct vmci_ctx *context;
94328d6692cSGeorge Zhang 	int result;
94428d6692cSGeorge Zhang 
94528d6692cSGeorge Zhang 	if (context_id == VMCI_INVALID_ID || vmci_handle_is_invalid(handle))
94628d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
94728d6692cSGeorge Zhang 
94828d6692cSGeorge Zhang 	context = vmci_ctx_get(context_id);
94928d6692cSGeorge Zhang 	if (context == NULL)
95028d6692cSGeorge Zhang 		return VMCI_ERROR_NOT_FOUND;
95128d6692cSGeorge Zhang 
95228d6692cSGeorge Zhang 	spin_lock(&context->lock);
95328d6692cSGeorge Zhang 	if (!vmci_handle_arr_has_entry(context->doorbell_array, handle)) {
95428d6692cSGeorge Zhang 		vmci_handle_arr_append_entry(&context->doorbell_array, handle);
95528d6692cSGeorge Zhang 		result = VMCI_SUCCESS;
95628d6692cSGeorge Zhang 	} else {
95728d6692cSGeorge Zhang 		result = VMCI_ERROR_DUPLICATE_ENTRY;
95828d6692cSGeorge Zhang 	}
95928d6692cSGeorge Zhang 
96028d6692cSGeorge Zhang 	spin_unlock(&context->lock);
96128d6692cSGeorge Zhang 	vmci_ctx_put(context);
96228d6692cSGeorge Zhang 
96328d6692cSGeorge Zhang 	return result;
96428d6692cSGeorge Zhang }
96528d6692cSGeorge Zhang 
96628d6692cSGeorge Zhang /*
96728d6692cSGeorge Zhang  * Unregisters a doorbell handle that was previously registered
96828d6692cSGeorge Zhang  * with vmci_ctx_dbell_create.
96928d6692cSGeorge Zhang  */
97028d6692cSGeorge Zhang int vmci_ctx_dbell_destroy(u32 context_id, struct vmci_handle handle)
97128d6692cSGeorge Zhang {
97228d6692cSGeorge Zhang 	struct vmci_ctx *context;
97328d6692cSGeorge Zhang 	struct vmci_handle removed_handle;
97428d6692cSGeorge Zhang 
97528d6692cSGeorge Zhang 	if (context_id == VMCI_INVALID_ID || vmci_handle_is_invalid(handle))
97628d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
97728d6692cSGeorge Zhang 
97828d6692cSGeorge Zhang 	context = vmci_ctx_get(context_id);
97928d6692cSGeorge Zhang 	if (context == NULL)
98028d6692cSGeorge Zhang 		return VMCI_ERROR_NOT_FOUND;
98128d6692cSGeorge Zhang 
98228d6692cSGeorge Zhang 	spin_lock(&context->lock);
98328d6692cSGeorge Zhang 	removed_handle =
98428d6692cSGeorge Zhang 	    vmci_handle_arr_remove_entry(context->doorbell_array, handle);
98528d6692cSGeorge Zhang 	vmci_handle_arr_remove_entry(context->pending_doorbell_array, handle);
98628d6692cSGeorge Zhang 	spin_unlock(&context->lock);
98728d6692cSGeorge Zhang 
98828d6692cSGeorge Zhang 	vmci_ctx_put(context);
98928d6692cSGeorge Zhang 
99028d6692cSGeorge Zhang 	return vmci_handle_is_invalid(removed_handle) ?
99128d6692cSGeorge Zhang 	    VMCI_ERROR_NOT_FOUND : VMCI_SUCCESS;
99228d6692cSGeorge Zhang }
99328d6692cSGeorge Zhang 
99428d6692cSGeorge Zhang /*
99528d6692cSGeorge Zhang  * Unregisters all doorbell handles that were previously
99628d6692cSGeorge Zhang  * registered with vmci_ctx_dbell_create.
99728d6692cSGeorge Zhang  */
99828d6692cSGeorge Zhang int vmci_ctx_dbell_destroy_all(u32 context_id)
99928d6692cSGeorge Zhang {
100028d6692cSGeorge Zhang 	struct vmci_ctx *context;
100128d6692cSGeorge Zhang 	struct vmci_handle handle;
100228d6692cSGeorge Zhang 
100328d6692cSGeorge Zhang 	if (context_id == VMCI_INVALID_ID)
100428d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
100528d6692cSGeorge Zhang 
100628d6692cSGeorge Zhang 	context = vmci_ctx_get(context_id);
100728d6692cSGeorge Zhang 	if (context == NULL)
100828d6692cSGeorge Zhang 		return VMCI_ERROR_NOT_FOUND;
100928d6692cSGeorge Zhang 
101028d6692cSGeorge Zhang 	spin_lock(&context->lock);
101128d6692cSGeorge Zhang 	do {
101228d6692cSGeorge Zhang 		struct vmci_handle_arr *arr = context->doorbell_array;
101328d6692cSGeorge Zhang 		handle = vmci_handle_arr_remove_tail(arr);
101428d6692cSGeorge Zhang 	} while (!vmci_handle_is_invalid(handle));
101528d6692cSGeorge Zhang 	do {
101628d6692cSGeorge Zhang 		struct vmci_handle_arr *arr = context->pending_doorbell_array;
101728d6692cSGeorge Zhang 		handle = vmci_handle_arr_remove_tail(arr);
101828d6692cSGeorge Zhang 	} while (!vmci_handle_is_invalid(handle));
101928d6692cSGeorge Zhang 	spin_unlock(&context->lock);
102028d6692cSGeorge Zhang 
102128d6692cSGeorge Zhang 	vmci_ctx_put(context);
102228d6692cSGeorge Zhang 
102328d6692cSGeorge Zhang 	return VMCI_SUCCESS;
102428d6692cSGeorge Zhang }
102528d6692cSGeorge Zhang 
102628d6692cSGeorge Zhang /*
102728d6692cSGeorge Zhang  * Registers a notification of a doorbell handle initiated by the
102828d6692cSGeorge Zhang  * specified source context. The notification of doorbells are
102928d6692cSGeorge Zhang  * subject to the same isolation rules as datagram delivery. To
103028d6692cSGeorge Zhang  * allow host side senders of notifications a finer granularity
103128d6692cSGeorge Zhang  * of sender rights than those assigned to the sending context
103228d6692cSGeorge Zhang  * itself, the host context is required to specify a different
103328d6692cSGeorge Zhang  * set of privilege flags that will override the privileges of
103428d6692cSGeorge Zhang  * the source context.
103528d6692cSGeorge Zhang  */
103628d6692cSGeorge Zhang int vmci_ctx_notify_dbell(u32 src_cid,
103728d6692cSGeorge Zhang 			  struct vmci_handle handle,
103828d6692cSGeorge Zhang 			  u32 src_priv_flags)
103928d6692cSGeorge Zhang {
104028d6692cSGeorge Zhang 	struct vmci_ctx *dst_context;
104128d6692cSGeorge Zhang 	int result;
104228d6692cSGeorge Zhang 
104328d6692cSGeorge Zhang 	if (vmci_handle_is_invalid(handle))
104428d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
104528d6692cSGeorge Zhang 
104628d6692cSGeorge Zhang 	/* Get the target VM's VMCI context. */
104728d6692cSGeorge Zhang 	dst_context = vmci_ctx_get(handle.context);
104828d6692cSGeorge Zhang 	if (!dst_context) {
104928d6692cSGeorge Zhang 		pr_devel("Invalid context (ID=0x%x)\n", handle.context);
105028d6692cSGeorge Zhang 		return VMCI_ERROR_NOT_FOUND;
105128d6692cSGeorge Zhang 	}
105228d6692cSGeorge Zhang 
105328d6692cSGeorge Zhang 	if (src_cid != handle.context) {
105428d6692cSGeorge Zhang 		u32 dst_priv_flags;
105528d6692cSGeorge Zhang 
105628d6692cSGeorge Zhang 		if (VMCI_CONTEXT_IS_VM(src_cid) &&
105728d6692cSGeorge Zhang 		    VMCI_CONTEXT_IS_VM(handle.context)) {
105828d6692cSGeorge Zhang 			pr_devel("Doorbell notification from VM to VM not supported (src=0x%x, dst=0x%x)\n",
105928d6692cSGeorge Zhang 				 src_cid, handle.context);
106028d6692cSGeorge Zhang 			result = VMCI_ERROR_DST_UNREACHABLE;
106128d6692cSGeorge Zhang 			goto out;
106228d6692cSGeorge Zhang 		}
106328d6692cSGeorge Zhang 
106428d6692cSGeorge Zhang 		result = vmci_dbell_get_priv_flags(handle, &dst_priv_flags);
106528d6692cSGeorge Zhang 		if (result < VMCI_SUCCESS) {
106628d6692cSGeorge Zhang 			pr_warn("Failed to get privilege flags for destination (handle=0x%x:0x%x)\n",
106728d6692cSGeorge Zhang 				handle.context, handle.resource);
106828d6692cSGeorge Zhang 			goto out;
106928d6692cSGeorge Zhang 		}
107028d6692cSGeorge Zhang 
107128d6692cSGeorge Zhang 		if (src_cid != VMCI_HOST_CONTEXT_ID ||
107228d6692cSGeorge Zhang 		    src_priv_flags == VMCI_NO_PRIVILEGE_FLAGS) {
107328d6692cSGeorge Zhang 			src_priv_flags = vmci_context_get_priv_flags(src_cid);
107428d6692cSGeorge Zhang 		}
107528d6692cSGeorge Zhang 
107628d6692cSGeorge Zhang 		if (vmci_deny_interaction(src_priv_flags, dst_priv_flags)) {
107728d6692cSGeorge Zhang 			result = VMCI_ERROR_NO_ACCESS;
107828d6692cSGeorge Zhang 			goto out;
107928d6692cSGeorge Zhang 		}
108028d6692cSGeorge Zhang 	}
108128d6692cSGeorge Zhang 
108228d6692cSGeorge Zhang 	if (handle.context == VMCI_HOST_CONTEXT_ID) {
108328d6692cSGeorge Zhang 		result = vmci_dbell_host_context_notify(src_cid, handle);
108428d6692cSGeorge Zhang 	} else {
108528d6692cSGeorge Zhang 		spin_lock(&dst_context->lock);
108628d6692cSGeorge Zhang 
108728d6692cSGeorge Zhang 		if (!vmci_handle_arr_has_entry(dst_context->doorbell_array,
108828d6692cSGeorge Zhang 					       handle)) {
108928d6692cSGeorge Zhang 			result = VMCI_ERROR_NOT_FOUND;
109028d6692cSGeorge Zhang 		} else {
109128d6692cSGeorge Zhang 			if (!vmci_handle_arr_has_entry(
109228d6692cSGeorge Zhang 					dst_context->pending_doorbell_array,
109328d6692cSGeorge Zhang 					handle)) {
109428d6692cSGeorge Zhang 				vmci_handle_arr_append_entry(
109528d6692cSGeorge Zhang 					&dst_context->pending_doorbell_array,
109628d6692cSGeorge Zhang 					handle);
109728d6692cSGeorge Zhang 
109828d6692cSGeorge Zhang 				ctx_signal_notify(dst_context);
109928d6692cSGeorge Zhang 				wake_up(&dst_context->host_context.wait_queue);
110028d6692cSGeorge Zhang 
110128d6692cSGeorge Zhang 			}
110228d6692cSGeorge Zhang 			result = VMCI_SUCCESS;
110328d6692cSGeorge Zhang 		}
110428d6692cSGeorge Zhang 		spin_unlock(&dst_context->lock);
110528d6692cSGeorge Zhang 	}
110628d6692cSGeorge Zhang 
110728d6692cSGeorge Zhang  out:
110828d6692cSGeorge Zhang 	vmci_ctx_put(dst_context);
110928d6692cSGeorge Zhang 
111028d6692cSGeorge Zhang 	return result;
111128d6692cSGeorge Zhang }
111228d6692cSGeorge Zhang 
111328d6692cSGeorge Zhang bool vmci_ctx_supports_host_qp(struct vmci_ctx *context)
111428d6692cSGeorge Zhang {
111528d6692cSGeorge Zhang 	return context && context->user_version >= VMCI_VERSION_HOSTQP;
111628d6692cSGeorge Zhang }
111728d6692cSGeorge Zhang 
111828d6692cSGeorge Zhang /*
111928d6692cSGeorge Zhang  * Registers that a new queue pair handle has been allocated by
112028d6692cSGeorge Zhang  * the context.
112128d6692cSGeorge Zhang  */
112228d6692cSGeorge Zhang int vmci_ctx_qp_create(struct vmci_ctx *context, struct vmci_handle handle)
112328d6692cSGeorge Zhang {
112428d6692cSGeorge Zhang 	int result;
112528d6692cSGeorge Zhang 
112628d6692cSGeorge Zhang 	if (context == NULL || vmci_handle_is_invalid(handle))
112728d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
112828d6692cSGeorge Zhang 
112928d6692cSGeorge Zhang 	if (!vmci_handle_arr_has_entry(context->queue_pair_array, handle)) {
113028d6692cSGeorge Zhang 		vmci_handle_arr_append_entry(&context->queue_pair_array,
113128d6692cSGeorge Zhang 					     handle);
113228d6692cSGeorge Zhang 		result = VMCI_SUCCESS;
113328d6692cSGeorge Zhang 	} else {
113428d6692cSGeorge Zhang 		result = VMCI_ERROR_DUPLICATE_ENTRY;
113528d6692cSGeorge Zhang 	}
113628d6692cSGeorge Zhang 
113728d6692cSGeorge Zhang 	return result;
113828d6692cSGeorge Zhang }
113928d6692cSGeorge Zhang 
114028d6692cSGeorge Zhang /*
114128d6692cSGeorge Zhang  * Unregisters a queue pair handle that was previously registered
114228d6692cSGeorge Zhang  * with vmci_ctx_qp_create.
114328d6692cSGeorge Zhang  */
114428d6692cSGeorge Zhang int vmci_ctx_qp_destroy(struct vmci_ctx *context, struct vmci_handle handle)
114528d6692cSGeorge Zhang {
114628d6692cSGeorge Zhang 	struct vmci_handle hndl;
114728d6692cSGeorge Zhang 
114828d6692cSGeorge Zhang 	if (context == NULL || vmci_handle_is_invalid(handle))
114928d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
115028d6692cSGeorge Zhang 
115128d6692cSGeorge Zhang 	hndl = vmci_handle_arr_remove_entry(context->queue_pair_array, handle);
115228d6692cSGeorge Zhang 
115328d6692cSGeorge Zhang 	return vmci_handle_is_invalid(hndl) ?
115428d6692cSGeorge Zhang 		VMCI_ERROR_NOT_FOUND : VMCI_SUCCESS;
115528d6692cSGeorge Zhang }
115628d6692cSGeorge Zhang 
115728d6692cSGeorge Zhang /*
115828d6692cSGeorge Zhang  * Determines whether a given queue pair handle is registered
115928d6692cSGeorge Zhang  * with the given context.
116028d6692cSGeorge Zhang  */
116128d6692cSGeorge Zhang bool vmci_ctx_qp_exists(struct vmci_ctx *context, struct vmci_handle handle)
116228d6692cSGeorge Zhang {
116328d6692cSGeorge Zhang 	if (context == NULL || vmci_handle_is_invalid(handle))
116428d6692cSGeorge Zhang 		return false;
116528d6692cSGeorge Zhang 
116628d6692cSGeorge Zhang 	return vmci_handle_arr_has_entry(context->queue_pair_array, handle);
116728d6692cSGeorge Zhang }
116828d6692cSGeorge Zhang 
116928d6692cSGeorge Zhang /*
117028d6692cSGeorge Zhang  * vmci_context_get_priv_flags() - Retrieve privilege flags.
117128d6692cSGeorge Zhang  * @context_id: The context ID of the VMCI context.
117228d6692cSGeorge Zhang  *
117328d6692cSGeorge Zhang  * Retrieves privilege flags of the given VMCI context ID.
117428d6692cSGeorge Zhang  */
117528d6692cSGeorge Zhang u32 vmci_context_get_priv_flags(u32 context_id)
117628d6692cSGeorge Zhang {
117728d6692cSGeorge Zhang 	if (vmci_host_code_active()) {
117828d6692cSGeorge Zhang 		u32 flags;
117928d6692cSGeorge Zhang 		struct vmci_ctx *context;
118028d6692cSGeorge Zhang 
118128d6692cSGeorge Zhang 		context = vmci_ctx_get(context_id);
118228d6692cSGeorge Zhang 		if (!context)
118328d6692cSGeorge Zhang 			return VMCI_LEAST_PRIVILEGE_FLAGS;
118428d6692cSGeorge Zhang 
118528d6692cSGeorge Zhang 		flags = context->priv_flags;
118628d6692cSGeorge Zhang 		vmci_ctx_put(context);
118728d6692cSGeorge Zhang 		return flags;
118828d6692cSGeorge Zhang 	}
118928d6692cSGeorge Zhang 	return VMCI_NO_PRIVILEGE_FLAGS;
119028d6692cSGeorge Zhang }
119128d6692cSGeorge Zhang EXPORT_SYMBOL_GPL(vmci_context_get_priv_flags);
119228d6692cSGeorge Zhang 
119328d6692cSGeorge Zhang /*
119428d6692cSGeorge Zhang  * vmci_is_context_owner() - Determimnes if user is the context owner
119528d6692cSGeorge Zhang  * @context_id: The context ID of the VMCI context.
119628d6692cSGeorge Zhang  * @uid:        The host user id (real kernel value).
119728d6692cSGeorge Zhang  *
119828d6692cSGeorge Zhang  * Determines whether a given UID is the owner of given VMCI context.
119928d6692cSGeorge Zhang  */
120028d6692cSGeorge Zhang bool vmci_is_context_owner(u32 context_id, kuid_t uid)
120128d6692cSGeorge Zhang {
120228d6692cSGeorge Zhang 	bool is_owner = false;
120328d6692cSGeorge Zhang 
120428d6692cSGeorge Zhang 	if (vmci_host_code_active()) {
120528d6692cSGeorge Zhang 		struct vmci_ctx *context = vmci_ctx_get(context_id);
120628d6692cSGeorge Zhang 		if (context) {
120728d6692cSGeorge Zhang 			if (context->cred)
120828d6692cSGeorge Zhang 				is_owner = uid_eq(context->cred->uid, uid);
120928d6692cSGeorge Zhang 			vmci_ctx_put(context);
121028d6692cSGeorge Zhang 		}
121128d6692cSGeorge Zhang 	}
121228d6692cSGeorge Zhang 
121328d6692cSGeorge Zhang 	return is_owner;
121428d6692cSGeorge Zhang }
121528d6692cSGeorge Zhang EXPORT_SYMBOL_GPL(vmci_is_context_owner);
1216