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