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 
241c2eb5b2SVishnu DASA /* Use a wide upper bound for the maximum contexts. */
251c2eb5b2SVishnu DASA #define VMCI_MAX_CONTEXTS 2000
261c2eb5b2SVishnu DASA 
2728d6692cSGeorge Zhang /*
2828d6692cSGeorge Zhang  * List of current VMCI contexts.  Contexts can be added by
2928d6692cSGeorge Zhang  * vmci_ctx_create() and removed via vmci_ctx_destroy().
3028d6692cSGeorge Zhang  * These, along with context lookup, are protected by the
3128d6692cSGeorge Zhang  * list structure's lock.
3228d6692cSGeorge Zhang  */
3328d6692cSGeorge Zhang static struct {
3428d6692cSGeorge Zhang 	struct list_head head;
3528d6692cSGeorge Zhang 	spinlock_t lock; /* Spinlock for context list operations */
3628d6692cSGeorge Zhang } ctx_list = {
3728d6692cSGeorge Zhang 	.head = LIST_HEAD_INIT(ctx_list.head),
3828d6692cSGeorge Zhang 	.lock = __SPIN_LOCK_UNLOCKED(ctx_list.lock),
3928d6692cSGeorge Zhang };
4028d6692cSGeorge Zhang 
4128d6692cSGeorge Zhang /* Used by contexts that did not set up notify flag pointers */
4228d6692cSGeorge Zhang static bool ctx_dummy_notify;
4328d6692cSGeorge Zhang 
ctx_signal_notify(struct vmci_ctx * context)4428d6692cSGeorge Zhang static void ctx_signal_notify(struct vmci_ctx *context)
4528d6692cSGeorge Zhang {
4628d6692cSGeorge Zhang 	*context->notify = true;
4728d6692cSGeorge Zhang }
4828d6692cSGeorge Zhang 
ctx_clear_notify(struct vmci_ctx * context)4928d6692cSGeorge Zhang static void ctx_clear_notify(struct vmci_ctx *context)
5028d6692cSGeorge Zhang {
5128d6692cSGeorge Zhang 	*context->notify = false;
5228d6692cSGeorge Zhang }
5328d6692cSGeorge Zhang 
5428d6692cSGeorge Zhang /*
5528d6692cSGeorge Zhang  * If nothing requires the attention of the guest, clears both
5628d6692cSGeorge Zhang  * notify flag and call.
5728d6692cSGeorge Zhang  */
ctx_clear_notify_call(struct vmci_ctx * context)5828d6692cSGeorge Zhang static void ctx_clear_notify_call(struct vmci_ctx *context)
5928d6692cSGeorge Zhang {
6028d6692cSGeorge Zhang 	if (context->pending_datagrams == 0 &&
6128d6692cSGeorge Zhang 	    vmci_handle_arr_get_size(context->pending_doorbell_array) == 0)
6228d6692cSGeorge Zhang 		ctx_clear_notify(context);
6328d6692cSGeorge Zhang }
6428d6692cSGeorge Zhang 
6528d6692cSGeorge Zhang /*
6628d6692cSGeorge Zhang  * Sets the context's notify flag iff datagrams are pending for this
6728d6692cSGeorge Zhang  * context.  Called from vmci_setup_notify().
6828d6692cSGeorge Zhang  */
vmci_ctx_check_signal_notify(struct vmci_ctx * context)6928d6692cSGeorge Zhang void vmci_ctx_check_signal_notify(struct vmci_ctx *context)
7028d6692cSGeorge Zhang {
7128d6692cSGeorge Zhang 	spin_lock(&context->lock);
7228d6692cSGeorge Zhang 	if (context->pending_datagrams)
7328d6692cSGeorge Zhang 		ctx_signal_notify(context);
7428d6692cSGeorge Zhang 	spin_unlock(&context->lock);
7528d6692cSGeorge Zhang }
7628d6692cSGeorge Zhang 
7728d6692cSGeorge Zhang /*
7828d6692cSGeorge Zhang  * Allocates and initializes a VMCI context.
7928d6692cSGeorge Zhang  */
vmci_ctx_create(u32 cid,u32 priv_flags,uintptr_t event_hnd,int user_version,const struct cred * cred)8028d6692cSGeorge Zhang struct vmci_ctx *vmci_ctx_create(u32 cid, u32 priv_flags,
8128d6692cSGeorge Zhang 				 uintptr_t event_hnd,
8228d6692cSGeorge Zhang 				 int user_version,
8328d6692cSGeorge Zhang 				 const struct cred *cred)
8428d6692cSGeorge Zhang {
8528d6692cSGeorge Zhang 	struct vmci_ctx *context;
8628d6692cSGeorge Zhang 	int error;
8728d6692cSGeorge Zhang 
8828d6692cSGeorge Zhang 	if (cid == VMCI_INVALID_ID) {
8928d6692cSGeorge Zhang 		pr_devel("Invalid context ID for VMCI context\n");
9028d6692cSGeorge Zhang 		error = -EINVAL;
9128d6692cSGeorge Zhang 		goto err_out;
9228d6692cSGeorge Zhang 	}
9328d6692cSGeorge Zhang 
9428d6692cSGeorge Zhang 	if (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS) {
9528d6692cSGeorge Zhang 		pr_devel("Invalid flag (flags=0x%x) for VMCI context\n",
9628d6692cSGeorge Zhang 			 priv_flags);
9728d6692cSGeorge Zhang 		error = -EINVAL;
9828d6692cSGeorge Zhang 		goto err_out;
9928d6692cSGeorge Zhang 	}
10028d6692cSGeorge Zhang 
10128d6692cSGeorge Zhang 	if (user_version == 0) {
10228d6692cSGeorge Zhang 		pr_devel("Invalid suer_version %d\n", user_version);
10328d6692cSGeorge Zhang 		error = -EINVAL;
10428d6692cSGeorge Zhang 		goto err_out;
10528d6692cSGeorge Zhang 	}
10628d6692cSGeorge Zhang 
10728d6692cSGeorge Zhang 	context = kzalloc(sizeof(*context), GFP_KERNEL);
10828d6692cSGeorge Zhang 	if (!context) {
10928d6692cSGeorge Zhang 		pr_warn("Failed to allocate memory for VMCI context\n");
1107487257cSJunlin Yang 		error = -ENOMEM;
11128d6692cSGeorge Zhang 		goto err_out;
11228d6692cSGeorge Zhang 	}
11328d6692cSGeorge Zhang 
11428d6692cSGeorge Zhang 	kref_init(&context->kref);
11528d6692cSGeorge Zhang 	spin_lock_init(&context->lock);
11628d6692cSGeorge Zhang 	INIT_LIST_HEAD(&context->list_item);
11728d6692cSGeorge Zhang 	INIT_LIST_HEAD(&context->datagram_queue);
11828d6692cSGeorge Zhang 	INIT_LIST_HEAD(&context->notifier_list);
11928d6692cSGeorge Zhang 
12028d6692cSGeorge Zhang 	/* Initialize host-specific VMCI context. */
12128d6692cSGeorge Zhang 	init_waitqueue_head(&context->host_context.wait_queue);
12228d6692cSGeorge Zhang 
1231c2eb5b2SVishnu DASA 	context->queue_pair_array =
1241c2eb5b2SVishnu DASA 		vmci_handle_arr_create(0, VMCI_MAX_GUEST_QP_COUNT);
12528d6692cSGeorge Zhang 	if (!context->queue_pair_array) {
12628d6692cSGeorge Zhang 		error = -ENOMEM;
12728d6692cSGeorge Zhang 		goto err_free_ctx;
12828d6692cSGeorge Zhang 	}
12928d6692cSGeorge Zhang 
1301c2eb5b2SVishnu DASA 	context->doorbell_array =
1311c2eb5b2SVishnu DASA 		vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT);
13228d6692cSGeorge Zhang 	if (!context->doorbell_array) {
13328d6692cSGeorge Zhang 		error = -ENOMEM;
13428d6692cSGeorge Zhang 		goto err_free_qp_array;
13528d6692cSGeorge Zhang 	}
13628d6692cSGeorge Zhang 
1371c2eb5b2SVishnu DASA 	context->pending_doorbell_array =
1381c2eb5b2SVishnu DASA 		vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT);
13928d6692cSGeorge Zhang 	if (!context->pending_doorbell_array) {
14028d6692cSGeorge Zhang 		error = -ENOMEM;
14128d6692cSGeorge Zhang 		goto err_free_db_array;
14228d6692cSGeorge Zhang 	}
14328d6692cSGeorge Zhang 
14428d6692cSGeorge Zhang 	context->user_version = user_version;
14528d6692cSGeorge Zhang 
14628d6692cSGeorge Zhang 	context->priv_flags = priv_flags;
14728d6692cSGeorge Zhang 
14828d6692cSGeorge Zhang 	if (cred)
14928d6692cSGeorge Zhang 		context->cred = get_cred(cred);
15028d6692cSGeorge Zhang 
15128d6692cSGeorge Zhang 	context->notify = &ctx_dummy_notify;
15228d6692cSGeorge Zhang 	context->notify_page = NULL;
15328d6692cSGeorge Zhang 
15428d6692cSGeorge Zhang 	/*
15528d6692cSGeorge Zhang 	 * If we collide with an existing context we generate a new
15628d6692cSGeorge Zhang 	 * and use it instead. The VMX will determine if regeneration
15728d6692cSGeorge Zhang 	 * is okay. Since there isn't 4B - 16 VMs running on a given
15828d6692cSGeorge Zhang 	 * host, the below loop will terminate.
15928d6692cSGeorge Zhang 	 */
16028d6692cSGeorge Zhang 	spin_lock(&ctx_list.lock);
16128d6692cSGeorge Zhang 
16228d6692cSGeorge Zhang 	while (vmci_ctx_exists(cid)) {
16328d6692cSGeorge Zhang 		/* We reserve the lowest 16 ids for fixed contexts. */
16428d6692cSGeorge Zhang 		cid = max(cid, VMCI_RESERVED_CID_LIMIT - 1) + 1;
16528d6692cSGeorge Zhang 		if (cid == VMCI_INVALID_ID)
16628d6692cSGeorge Zhang 			cid = VMCI_RESERVED_CID_LIMIT;
16728d6692cSGeorge Zhang 	}
16828d6692cSGeorge Zhang 	context->cid = cid;
16928d6692cSGeorge Zhang 
17028d6692cSGeorge Zhang 	list_add_tail_rcu(&context->list_item, &ctx_list.head);
17128d6692cSGeorge Zhang 	spin_unlock(&ctx_list.lock);
17228d6692cSGeorge Zhang 
17328d6692cSGeorge Zhang 	return context;
17428d6692cSGeorge Zhang 
17528d6692cSGeorge Zhang  err_free_db_array:
17628d6692cSGeorge Zhang 	vmci_handle_arr_destroy(context->doorbell_array);
17728d6692cSGeorge Zhang  err_free_qp_array:
17828d6692cSGeorge Zhang 	vmci_handle_arr_destroy(context->queue_pair_array);
17928d6692cSGeorge Zhang  err_free_ctx:
18028d6692cSGeorge Zhang 	kfree(context);
18128d6692cSGeorge Zhang  err_out:
18228d6692cSGeorge Zhang 	return ERR_PTR(error);
18328d6692cSGeorge Zhang }
18428d6692cSGeorge Zhang 
18528d6692cSGeorge Zhang /*
18628d6692cSGeorge Zhang  * Destroy VMCI context.
18728d6692cSGeorge Zhang  */
vmci_ctx_destroy(struct vmci_ctx * context)18828d6692cSGeorge Zhang void vmci_ctx_destroy(struct vmci_ctx *context)
18928d6692cSGeorge Zhang {
19028d6692cSGeorge Zhang 	spin_lock(&ctx_list.lock);
19128d6692cSGeorge Zhang 	list_del_rcu(&context->list_item);
19228d6692cSGeorge Zhang 	spin_unlock(&ctx_list.lock);
19328d6692cSGeorge Zhang 	synchronize_rcu();
19428d6692cSGeorge Zhang 
19528d6692cSGeorge Zhang 	vmci_ctx_put(context);
19628d6692cSGeorge Zhang }
19728d6692cSGeorge Zhang 
19828d6692cSGeorge Zhang /*
19928d6692cSGeorge Zhang  * Fire notification for all contexts interested in given cid.
20028d6692cSGeorge Zhang  */
ctx_fire_notification(u32 context_id,u32 priv_flags)20128d6692cSGeorge Zhang static int ctx_fire_notification(u32 context_id, u32 priv_flags)
20228d6692cSGeorge Zhang {
20328d6692cSGeorge Zhang 	u32 i, array_size;
20428d6692cSGeorge Zhang 	struct vmci_ctx *sub_ctx;
20528d6692cSGeorge Zhang 	struct vmci_handle_arr *subscriber_array;
20628d6692cSGeorge Zhang 	struct vmci_handle context_handle =
20728d6692cSGeorge Zhang 		vmci_make_handle(context_id, VMCI_EVENT_HANDLER);
20828d6692cSGeorge Zhang 
20928d6692cSGeorge Zhang 	/*
21028d6692cSGeorge Zhang 	 * We create an array to hold the subscribers we find when
21128d6692cSGeorge Zhang 	 * scanning through all contexts.
21228d6692cSGeorge Zhang 	 */
2131c2eb5b2SVishnu DASA 	subscriber_array = vmci_handle_arr_create(0, VMCI_MAX_CONTEXTS);
21428d6692cSGeorge Zhang 	if (subscriber_array == NULL)
21528d6692cSGeorge Zhang 		return VMCI_ERROR_NO_MEM;
21628d6692cSGeorge Zhang 
21728d6692cSGeorge Zhang 	/*
21828d6692cSGeorge Zhang 	 * Scan all contexts to find who is interested in being
21928d6692cSGeorge Zhang 	 * notified about given contextID.
22028d6692cSGeorge Zhang 	 */
22128d6692cSGeorge Zhang 	rcu_read_lock();
22228d6692cSGeorge Zhang 	list_for_each_entry_rcu(sub_ctx, &ctx_list.head, list_item) {
22328d6692cSGeorge Zhang 		struct vmci_handle_list *node;
22428d6692cSGeorge Zhang 
22528d6692cSGeorge Zhang 		/*
22628d6692cSGeorge Zhang 		 * We only deliver notifications of the removal of
22728d6692cSGeorge Zhang 		 * contexts, if the two contexts are allowed to
22828d6692cSGeorge Zhang 		 * interact.
22928d6692cSGeorge Zhang 		 */
23028d6692cSGeorge Zhang 		if (vmci_deny_interaction(priv_flags, sub_ctx->priv_flags))
23128d6692cSGeorge Zhang 			continue;
23228d6692cSGeorge Zhang 
23328d6692cSGeorge Zhang 		list_for_each_entry_rcu(node, &sub_ctx->notifier_list, node) {
23428d6692cSGeorge Zhang 			if (!vmci_handle_is_equal(node->handle, context_handle))
23528d6692cSGeorge Zhang 				continue;
23628d6692cSGeorge Zhang 
23728d6692cSGeorge Zhang 			vmci_handle_arr_append_entry(&subscriber_array,
23828d6692cSGeorge Zhang 					vmci_make_handle(sub_ctx->cid,
23928d6692cSGeorge Zhang 							 VMCI_EVENT_HANDLER));
24028d6692cSGeorge Zhang 		}
24128d6692cSGeorge Zhang 	}
24228d6692cSGeorge Zhang 	rcu_read_unlock();
24328d6692cSGeorge Zhang 
24428d6692cSGeorge Zhang 	/* Fire event to all subscribers. */
24528d6692cSGeorge Zhang 	array_size = vmci_handle_arr_get_size(subscriber_array);
24628d6692cSGeorge Zhang 	for (i = 0; i < array_size; i++) {
24728d6692cSGeorge Zhang 		int result;
24828d6692cSGeorge Zhang 		struct vmci_event_ctx ev;
24928d6692cSGeorge Zhang 
25028d6692cSGeorge Zhang 		ev.msg.hdr.dst = vmci_handle_arr_get_entry(subscriber_array, i);
25128d6692cSGeorge Zhang 		ev.msg.hdr.src = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
25228d6692cSGeorge Zhang 						  VMCI_CONTEXT_RESOURCE_ID);
25328d6692cSGeorge Zhang 		ev.msg.hdr.payload_size = sizeof(ev) - sizeof(ev.msg.hdr);
25428d6692cSGeorge Zhang 		ev.msg.event_data.event = VMCI_EVENT_CTX_REMOVED;
25528d6692cSGeorge Zhang 		ev.payload.context_id = context_id;
25628d6692cSGeorge Zhang 
25728d6692cSGeorge Zhang 		result = vmci_datagram_dispatch(VMCI_HYPERVISOR_CONTEXT_ID,
25828d6692cSGeorge Zhang 						&ev.msg.hdr, false);
25928d6692cSGeorge Zhang 		if (result < VMCI_SUCCESS) {
26028d6692cSGeorge Zhang 			pr_devel("Failed to enqueue event datagram (type=%d) for context (ID=0x%x)\n",
26128d6692cSGeorge Zhang 				 ev.msg.event_data.event,
26228d6692cSGeorge Zhang 				 ev.msg.hdr.dst.context);
26328d6692cSGeorge Zhang 			/* We continue to enqueue on next subscriber. */
26428d6692cSGeorge Zhang 		}
26528d6692cSGeorge Zhang 	}
26628d6692cSGeorge Zhang 	vmci_handle_arr_destroy(subscriber_array);
26728d6692cSGeorge Zhang 
26828d6692cSGeorge Zhang 	return VMCI_SUCCESS;
26928d6692cSGeorge Zhang }
27028d6692cSGeorge Zhang 
27128d6692cSGeorge Zhang /*
27228d6692cSGeorge Zhang  * Returns the current number of pending datagrams. The call may
27328d6692cSGeorge Zhang  * also serve as a synchronization point for the datagram queue,
27428d6692cSGeorge Zhang  * as no enqueue operations can occur concurrently.
27528d6692cSGeorge Zhang  */
vmci_ctx_pending_datagrams(u32 cid,u32 * pending)27628d6692cSGeorge Zhang int vmci_ctx_pending_datagrams(u32 cid, u32 *pending)
27728d6692cSGeorge Zhang {
27828d6692cSGeorge Zhang 	struct vmci_ctx *context;
27928d6692cSGeorge Zhang 
28028d6692cSGeorge Zhang 	context = vmci_ctx_get(cid);
28128d6692cSGeorge Zhang 	if (context == NULL)
28228d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
28328d6692cSGeorge Zhang 
28428d6692cSGeorge Zhang 	spin_lock(&context->lock);
28528d6692cSGeorge Zhang 	if (pending)
28628d6692cSGeorge Zhang 		*pending = context->pending_datagrams;
28728d6692cSGeorge Zhang 	spin_unlock(&context->lock);
28828d6692cSGeorge Zhang 	vmci_ctx_put(context);
28928d6692cSGeorge Zhang 
29028d6692cSGeorge Zhang 	return VMCI_SUCCESS;
29128d6692cSGeorge Zhang }
29228d6692cSGeorge Zhang 
29328d6692cSGeorge Zhang /*
29428d6692cSGeorge Zhang  * Queues a VMCI datagram for the appropriate target VM context.
29528d6692cSGeorge Zhang  */
vmci_ctx_enqueue_datagram(u32 cid,struct vmci_datagram * dg)29628d6692cSGeorge Zhang int vmci_ctx_enqueue_datagram(u32 cid, struct vmci_datagram *dg)
29728d6692cSGeorge Zhang {
29828d6692cSGeorge Zhang 	struct vmci_datagram_queue_entry *dq_entry;
29928d6692cSGeorge Zhang 	struct vmci_ctx *context;
30028d6692cSGeorge Zhang 	struct vmci_handle dg_src;
30128d6692cSGeorge Zhang 	size_t vmci_dg_size;
30228d6692cSGeorge Zhang 
30328d6692cSGeorge Zhang 	vmci_dg_size = VMCI_DG_SIZE(dg);
30428d6692cSGeorge Zhang 	if (vmci_dg_size > VMCI_MAX_DG_SIZE) {
3055b5e0928SAlexey Dobriyan 		pr_devel("Datagram too large (bytes=%zu)\n", vmci_dg_size);
30628d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
30728d6692cSGeorge Zhang 	}
30828d6692cSGeorge Zhang 
30928d6692cSGeorge Zhang 	/* Get the target VM's VMCI context. */
31028d6692cSGeorge Zhang 	context = vmci_ctx_get(cid);
31128d6692cSGeorge Zhang 	if (!context) {
31228d6692cSGeorge Zhang 		pr_devel("Invalid context (ID=0x%x)\n", cid);
31328d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
31428d6692cSGeorge Zhang 	}
31528d6692cSGeorge Zhang 
31628d6692cSGeorge Zhang 	/* Allocate guest call entry and add it to the target VM's queue. */
31728d6692cSGeorge Zhang 	dq_entry = kmalloc(sizeof(*dq_entry), GFP_KERNEL);
31828d6692cSGeorge Zhang 	if (dq_entry == NULL) {
31928d6692cSGeorge Zhang 		pr_warn("Failed to allocate memory for datagram\n");
32028d6692cSGeorge Zhang 		vmci_ctx_put(context);
32128d6692cSGeorge Zhang 		return VMCI_ERROR_NO_MEM;
32228d6692cSGeorge Zhang 	}
32328d6692cSGeorge Zhang 	dq_entry->dg = dg;
32428d6692cSGeorge Zhang 	dq_entry->dg_size = vmci_dg_size;
32528d6692cSGeorge Zhang 	dg_src = dg->src;
32628d6692cSGeorge Zhang 	INIT_LIST_HEAD(&dq_entry->list_item);
32728d6692cSGeorge Zhang 
32828d6692cSGeorge Zhang 	spin_lock(&context->lock);
32928d6692cSGeorge Zhang 
33028d6692cSGeorge Zhang 	/*
33128d6692cSGeorge Zhang 	 * We put a higher limit on datagrams from the hypervisor.  If
33228d6692cSGeorge Zhang 	 * the pending datagram is not from hypervisor, then we check
33328d6692cSGeorge Zhang 	 * if enqueueing it would exceed the
33428d6692cSGeorge Zhang 	 * VMCI_MAX_DATAGRAM_QUEUE_SIZE limit on the destination.  If
33528d6692cSGeorge Zhang 	 * the pending datagram is from hypervisor, we allow it to be
33628d6692cSGeorge Zhang 	 * queued at the destination side provided we don't reach the
33728d6692cSGeorge Zhang 	 * VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE limit.
33828d6692cSGeorge Zhang 	 */
33928d6692cSGeorge Zhang 	if (context->datagram_queue_size + vmci_dg_size >=
34028d6692cSGeorge Zhang 	    VMCI_MAX_DATAGRAM_QUEUE_SIZE &&
34128d6692cSGeorge Zhang 	    (!vmci_handle_is_equal(dg_src,
34228d6692cSGeorge Zhang 				vmci_make_handle
34328d6692cSGeorge Zhang 				(VMCI_HYPERVISOR_CONTEXT_ID,
34428d6692cSGeorge Zhang 				 VMCI_CONTEXT_RESOURCE_ID)) ||
34528d6692cSGeorge Zhang 	     context->datagram_queue_size + vmci_dg_size >=
34628d6692cSGeorge Zhang 	     VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE)) {
34728d6692cSGeorge Zhang 		spin_unlock(&context->lock);
34828d6692cSGeorge Zhang 		vmci_ctx_put(context);
34928d6692cSGeorge Zhang 		kfree(dq_entry);
35028d6692cSGeorge Zhang 		pr_devel("Context (ID=0x%x) receive queue is full\n", cid);
35128d6692cSGeorge Zhang 		return VMCI_ERROR_NO_RESOURCES;
35228d6692cSGeorge Zhang 	}
35328d6692cSGeorge Zhang 
35428d6692cSGeorge Zhang 	list_add(&dq_entry->list_item, &context->datagram_queue);
35528d6692cSGeorge Zhang 	context->pending_datagrams++;
35628d6692cSGeorge Zhang 	context->datagram_queue_size += vmci_dg_size;
35728d6692cSGeorge Zhang 	ctx_signal_notify(context);
35828d6692cSGeorge Zhang 	wake_up(&context->host_context.wait_queue);
35928d6692cSGeorge Zhang 	spin_unlock(&context->lock);
36028d6692cSGeorge Zhang 	vmci_ctx_put(context);
36128d6692cSGeorge Zhang 
36228d6692cSGeorge Zhang 	return vmci_dg_size;
36328d6692cSGeorge Zhang }
36428d6692cSGeorge Zhang 
36528d6692cSGeorge Zhang /*
36628d6692cSGeorge Zhang  * Verifies whether a context with the specified context ID exists.
36728d6692cSGeorge Zhang  * FIXME: utility is dubious as no decisions can be reliably made
36828d6692cSGeorge Zhang  * using this data as context can appear and disappear at any time.
36928d6692cSGeorge Zhang  */
vmci_ctx_exists(u32 cid)37028d6692cSGeorge Zhang bool vmci_ctx_exists(u32 cid)
37128d6692cSGeorge Zhang {
37228d6692cSGeorge Zhang 	struct vmci_ctx *context;
37328d6692cSGeorge Zhang 	bool exists = false;
37428d6692cSGeorge Zhang 
37528d6692cSGeorge Zhang 	rcu_read_lock();
37628d6692cSGeorge Zhang 
37728d6692cSGeorge Zhang 	list_for_each_entry_rcu(context, &ctx_list.head, list_item) {
37828d6692cSGeorge Zhang 		if (context->cid == cid) {
37928d6692cSGeorge Zhang 			exists = true;
38028d6692cSGeorge Zhang 			break;
38128d6692cSGeorge Zhang 		}
38228d6692cSGeorge Zhang 	}
38328d6692cSGeorge Zhang 
38428d6692cSGeorge Zhang 	rcu_read_unlock();
38528d6692cSGeorge Zhang 	return exists;
38628d6692cSGeorge Zhang }
38728d6692cSGeorge Zhang 
38828d6692cSGeorge Zhang /*
38928d6692cSGeorge Zhang  * Retrieves VMCI context corresponding to the given cid.
39028d6692cSGeorge Zhang  */
vmci_ctx_get(u32 cid)39128d6692cSGeorge Zhang struct vmci_ctx *vmci_ctx_get(u32 cid)
39228d6692cSGeorge Zhang {
39328d6692cSGeorge Zhang 	struct vmci_ctx *c, *context = NULL;
39428d6692cSGeorge Zhang 
39528d6692cSGeorge Zhang 	if (cid == VMCI_INVALID_ID)
39628d6692cSGeorge Zhang 		return NULL;
39728d6692cSGeorge Zhang 
39828d6692cSGeorge Zhang 	rcu_read_lock();
39928d6692cSGeorge Zhang 	list_for_each_entry_rcu(c, &ctx_list.head, list_item) {
40028d6692cSGeorge Zhang 		if (c->cid == cid) {
40128d6692cSGeorge Zhang 			/*
40228d6692cSGeorge Zhang 			 * The context owner drops its own reference to the
40328d6692cSGeorge Zhang 			 * context only after removing it from the list and
40428d6692cSGeorge Zhang 			 * waiting for RCU grace period to expire. This
40528d6692cSGeorge Zhang 			 * means that we are not about to increase the
40628d6692cSGeorge Zhang 			 * reference count of something that is in the
40728d6692cSGeorge Zhang 			 * process of being destroyed.
40828d6692cSGeorge Zhang 			 */
40928d6692cSGeorge Zhang 			context = c;
41028d6692cSGeorge Zhang 			kref_get(&context->kref);
41128d6692cSGeorge Zhang 			break;
41228d6692cSGeorge Zhang 		}
41328d6692cSGeorge Zhang 	}
41428d6692cSGeorge Zhang 	rcu_read_unlock();
41528d6692cSGeorge Zhang 
41628d6692cSGeorge Zhang 	return context;
41728d6692cSGeorge Zhang }
41828d6692cSGeorge Zhang 
41928d6692cSGeorge Zhang /*
42028d6692cSGeorge Zhang  * Deallocates all parts of a context data structure. This
42128d6692cSGeorge Zhang  * function doesn't lock the context, because it assumes that
42228d6692cSGeorge Zhang  * the caller was holding the last reference to context.
42328d6692cSGeorge Zhang  */
ctx_free_ctx(struct kref * kref)42428d6692cSGeorge Zhang static void ctx_free_ctx(struct kref *kref)
42528d6692cSGeorge Zhang {
42628d6692cSGeorge Zhang 	struct vmci_ctx *context = container_of(kref, struct vmci_ctx, kref);
42728d6692cSGeorge Zhang 	struct vmci_datagram_queue_entry *dq_entry, *dq_entry_tmp;
42828d6692cSGeorge Zhang 	struct vmci_handle temp_handle;
42928d6692cSGeorge Zhang 	struct vmci_handle_list *notifier, *tmp;
43028d6692cSGeorge Zhang 
43128d6692cSGeorge Zhang 	/*
43228d6692cSGeorge Zhang 	 * Fire event to all contexts interested in knowing this
43328d6692cSGeorge Zhang 	 * context is dying.
43428d6692cSGeorge Zhang 	 */
43528d6692cSGeorge Zhang 	ctx_fire_notification(context->cid, context->priv_flags);
43628d6692cSGeorge Zhang 
43728d6692cSGeorge Zhang 	/*
43828d6692cSGeorge Zhang 	 * Cleanup all queue pair resources attached to context.  If
43928d6692cSGeorge Zhang 	 * the VM dies without cleaning up, this code will make sure
44028d6692cSGeorge Zhang 	 * that no resources are leaked.
44128d6692cSGeorge Zhang 	 */
44228d6692cSGeorge Zhang 	temp_handle = vmci_handle_arr_get_entry(context->queue_pair_array, 0);
44328d6692cSGeorge Zhang 	while (!vmci_handle_is_equal(temp_handle, VMCI_INVALID_HANDLE)) {
44428d6692cSGeorge Zhang 		if (vmci_qp_broker_detach(temp_handle,
44528d6692cSGeorge Zhang 					  context) < VMCI_SUCCESS) {
44628d6692cSGeorge Zhang 			/*
44728d6692cSGeorge Zhang 			 * When vmci_qp_broker_detach() succeeds it
44828d6692cSGeorge Zhang 			 * removes the handle from the array.  If
44928d6692cSGeorge Zhang 			 * detach fails, we must remove the handle
45028d6692cSGeorge Zhang 			 * ourselves.
45128d6692cSGeorge Zhang 			 */
45228d6692cSGeorge Zhang 			vmci_handle_arr_remove_entry(context->queue_pair_array,
45328d6692cSGeorge Zhang 						     temp_handle);
45428d6692cSGeorge Zhang 		}
45528d6692cSGeorge Zhang 		temp_handle =
45628d6692cSGeorge Zhang 		    vmci_handle_arr_get_entry(context->queue_pair_array, 0);
45728d6692cSGeorge Zhang 	}
45828d6692cSGeorge Zhang 
45928d6692cSGeorge Zhang 	/*
46028d6692cSGeorge Zhang 	 * It is fine to destroy this without locking the callQueue, as
46128d6692cSGeorge Zhang 	 * this is the only thread having a reference to the context.
46228d6692cSGeorge Zhang 	 */
46328d6692cSGeorge Zhang 	list_for_each_entry_safe(dq_entry, dq_entry_tmp,
46428d6692cSGeorge Zhang 				 &context->datagram_queue, list_item) {
46528d6692cSGeorge Zhang 		WARN_ON(dq_entry->dg_size != VMCI_DG_SIZE(dq_entry->dg));
46628d6692cSGeorge Zhang 		list_del(&dq_entry->list_item);
46728d6692cSGeorge Zhang 		kfree(dq_entry->dg);
46828d6692cSGeorge Zhang 		kfree(dq_entry);
46928d6692cSGeorge Zhang 	}
47028d6692cSGeorge Zhang 
47128d6692cSGeorge Zhang 	list_for_each_entry_safe(notifier, tmp,
47228d6692cSGeorge Zhang 				 &context->notifier_list, node) {
47328d6692cSGeorge Zhang 		list_del(&notifier->node);
47428d6692cSGeorge Zhang 		kfree(notifier);
47528d6692cSGeorge Zhang 	}
47628d6692cSGeorge Zhang 
47728d6692cSGeorge Zhang 	vmci_handle_arr_destroy(context->queue_pair_array);
47828d6692cSGeorge Zhang 	vmci_handle_arr_destroy(context->doorbell_array);
47928d6692cSGeorge Zhang 	vmci_handle_arr_destroy(context->pending_doorbell_array);
48028d6692cSGeorge Zhang 	vmci_ctx_unset_notify(context);
48128d6692cSGeorge Zhang 	if (context->cred)
48228d6692cSGeorge Zhang 		put_cred(context->cred);
48328d6692cSGeorge Zhang 	kfree(context);
48428d6692cSGeorge Zhang }
48528d6692cSGeorge Zhang 
48628d6692cSGeorge Zhang /*
48728d6692cSGeorge Zhang  * Drops reference to VMCI context. If this is the last reference to
48828d6692cSGeorge Zhang  * the context it will be deallocated. A context is created with
48928d6692cSGeorge Zhang  * a reference count of one, and on destroy, it is removed from
49028d6692cSGeorge Zhang  * the context list before its reference count is decremented. Thus,
49128d6692cSGeorge Zhang  * if we reach zero, we are sure that nobody else are about to increment
49228d6692cSGeorge Zhang  * it (they need the entry in the context list for that), and so there
49328d6692cSGeorge Zhang  * is no need for locking.
49428d6692cSGeorge Zhang  */
vmci_ctx_put(struct vmci_ctx * context)49528d6692cSGeorge Zhang void vmci_ctx_put(struct vmci_ctx *context)
49628d6692cSGeorge Zhang {
49728d6692cSGeorge Zhang 	kref_put(&context->kref, ctx_free_ctx);
49828d6692cSGeorge Zhang }
49928d6692cSGeorge Zhang 
50028d6692cSGeorge Zhang /*
50128d6692cSGeorge Zhang  * Dequeues the next datagram and returns it to caller.
50228d6692cSGeorge Zhang  * The caller passes in a pointer to the max size datagram
50328d6692cSGeorge Zhang  * it can handle and the datagram is only unqueued if the
50428d6692cSGeorge Zhang  * size is less than max_size. If larger max_size is set to
50528d6692cSGeorge Zhang  * the size of the datagram to give the caller a chance to
50628d6692cSGeorge Zhang  * set up a larger buffer for the guestcall.
50728d6692cSGeorge Zhang  */
vmci_ctx_dequeue_datagram(struct vmci_ctx * context,size_t * max_size,struct vmci_datagram ** dg)50828d6692cSGeorge Zhang int vmci_ctx_dequeue_datagram(struct vmci_ctx *context,
50928d6692cSGeorge Zhang 			      size_t *max_size,
51028d6692cSGeorge Zhang 			      struct vmci_datagram **dg)
51128d6692cSGeorge Zhang {
51228d6692cSGeorge Zhang 	struct vmci_datagram_queue_entry *dq_entry;
51328d6692cSGeorge Zhang 	struct list_head *list_item;
51428d6692cSGeorge Zhang 	int rv;
51528d6692cSGeorge Zhang 
51628d6692cSGeorge Zhang 	/* Dequeue the next datagram entry. */
51728d6692cSGeorge Zhang 	spin_lock(&context->lock);
51828d6692cSGeorge Zhang 	if (context->pending_datagrams == 0) {
51928d6692cSGeorge Zhang 		ctx_clear_notify_call(context);
52028d6692cSGeorge Zhang 		spin_unlock(&context->lock);
52128d6692cSGeorge Zhang 		pr_devel("No datagrams pending\n");
52228d6692cSGeorge Zhang 		return VMCI_ERROR_NO_MORE_DATAGRAMS;
52328d6692cSGeorge Zhang 	}
52428d6692cSGeorge Zhang 
52528d6692cSGeorge Zhang 	list_item = context->datagram_queue.next;
52628d6692cSGeorge Zhang 
52728d6692cSGeorge Zhang 	dq_entry =
52828d6692cSGeorge Zhang 	    list_entry(list_item, struct vmci_datagram_queue_entry, list_item);
52928d6692cSGeorge Zhang 
53028d6692cSGeorge Zhang 	/* Check size of caller's buffer. */
53128d6692cSGeorge Zhang 	if (*max_size < dq_entry->dg_size) {
53228d6692cSGeorge Zhang 		*max_size = dq_entry->dg_size;
53328d6692cSGeorge Zhang 		spin_unlock(&context->lock);
53428d6692cSGeorge Zhang 		pr_devel("Caller's buffer should be at least (size=%u bytes)\n",
53528d6692cSGeorge Zhang 			 (u32) *max_size);
53628d6692cSGeorge Zhang 		return VMCI_ERROR_NO_MEM;
53728d6692cSGeorge Zhang 	}
53828d6692cSGeorge Zhang 
53928d6692cSGeorge Zhang 	list_del(list_item);
54028d6692cSGeorge Zhang 	context->pending_datagrams--;
54128d6692cSGeorge Zhang 	context->datagram_queue_size -= dq_entry->dg_size;
54228d6692cSGeorge Zhang 	if (context->pending_datagrams == 0) {
54328d6692cSGeorge Zhang 		ctx_clear_notify_call(context);
54428d6692cSGeorge Zhang 		rv = VMCI_SUCCESS;
54528d6692cSGeorge Zhang 	} else {
54628d6692cSGeorge Zhang 		/*
54728d6692cSGeorge Zhang 		 * Return the size of the next datagram.
54828d6692cSGeorge Zhang 		 */
54928d6692cSGeorge Zhang 		struct vmci_datagram_queue_entry *next_entry;
55028d6692cSGeorge Zhang 
55128d6692cSGeorge Zhang 		list_item = context->datagram_queue.next;
55228d6692cSGeorge Zhang 		next_entry =
55328d6692cSGeorge Zhang 		    list_entry(list_item, struct vmci_datagram_queue_entry,
55428d6692cSGeorge Zhang 			       list_item);
55528d6692cSGeorge Zhang 
55628d6692cSGeorge Zhang 		/*
55728d6692cSGeorge Zhang 		 * The following size_t -> int truncation is fine as
55828d6692cSGeorge Zhang 		 * the maximum size of a (routable) datagram is 68KB.
55928d6692cSGeorge Zhang 		 */
56028d6692cSGeorge Zhang 		rv = (int)next_entry->dg_size;
56128d6692cSGeorge Zhang 	}
56228d6692cSGeorge Zhang 	spin_unlock(&context->lock);
56328d6692cSGeorge Zhang 
56428d6692cSGeorge Zhang 	/* Caller must free datagram. */
56528d6692cSGeorge Zhang 	*dg = dq_entry->dg;
56628d6692cSGeorge Zhang 	dq_entry->dg = NULL;
56728d6692cSGeorge Zhang 	kfree(dq_entry);
56828d6692cSGeorge Zhang 
56928d6692cSGeorge Zhang 	return rv;
57028d6692cSGeorge Zhang }
57128d6692cSGeorge Zhang 
57228d6692cSGeorge Zhang /*
57328d6692cSGeorge Zhang  * Reverts actions set up by vmci_setup_notify().  Unmaps and unlocks the
57428d6692cSGeorge Zhang  * page mapped/locked by vmci_setup_notify().
57528d6692cSGeorge Zhang  */
vmci_ctx_unset_notify(struct vmci_ctx * context)57628d6692cSGeorge Zhang void vmci_ctx_unset_notify(struct vmci_ctx *context)
57728d6692cSGeorge Zhang {
57828d6692cSGeorge Zhang 	struct page *notify_page;
57928d6692cSGeorge Zhang 
58028d6692cSGeorge Zhang 	spin_lock(&context->lock);
58128d6692cSGeorge Zhang 
58228d6692cSGeorge Zhang 	notify_page = context->notify_page;
58328d6692cSGeorge Zhang 	context->notify = &ctx_dummy_notify;
58428d6692cSGeorge Zhang 	context->notify_page = NULL;
58528d6692cSGeorge Zhang 
58628d6692cSGeorge Zhang 	spin_unlock(&context->lock);
58728d6692cSGeorge Zhang 
58828d6692cSGeorge Zhang 	if (notify_page) {
58928d6692cSGeorge Zhang 		kunmap(notify_page);
59028d6692cSGeorge Zhang 		put_page(notify_page);
59128d6692cSGeorge Zhang 	}
59228d6692cSGeorge Zhang }
59328d6692cSGeorge Zhang 
59428d6692cSGeorge Zhang /*
59528d6692cSGeorge Zhang  * Add remote_cid to list of contexts current contexts wants
59628d6692cSGeorge Zhang  * notifications from/about.
59728d6692cSGeorge Zhang  */
vmci_ctx_add_notification(u32 context_id,u32 remote_cid)59828d6692cSGeorge Zhang int vmci_ctx_add_notification(u32 context_id, u32 remote_cid)
59928d6692cSGeorge Zhang {
60028d6692cSGeorge Zhang 	struct vmci_ctx *context;
60128d6692cSGeorge Zhang 	struct vmci_handle_list *notifier, *n;
60228d6692cSGeorge Zhang 	int result;
60328d6692cSGeorge Zhang 	bool exists = false;
60428d6692cSGeorge Zhang 
60528d6692cSGeorge Zhang 	context = vmci_ctx_get(context_id);
60628d6692cSGeorge Zhang 	if (!context)
60728d6692cSGeorge Zhang 		return VMCI_ERROR_NOT_FOUND;
60828d6692cSGeorge Zhang 
60928d6692cSGeorge Zhang 	if (VMCI_CONTEXT_IS_VM(context_id) && VMCI_CONTEXT_IS_VM(remote_cid)) {
61028d6692cSGeorge Zhang 		pr_devel("Context removed notifications for other VMs not supported (src=0x%x, remote=0x%x)\n",
61128d6692cSGeorge Zhang 			 context_id, remote_cid);
61228d6692cSGeorge Zhang 		result = VMCI_ERROR_DST_UNREACHABLE;
61328d6692cSGeorge Zhang 		goto out;
61428d6692cSGeorge Zhang 	}
61528d6692cSGeorge Zhang 
61628d6692cSGeorge Zhang 	if (context->priv_flags & VMCI_PRIVILEGE_FLAG_RESTRICTED) {
61728d6692cSGeorge Zhang 		result = VMCI_ERROR_NO_ACCESS;
61828d6692cSGeorge Zhang 		goto out;
61928d6692cSGeorge Zhang 	}
62028d6692cSGeorge Zhang 
62128d6692cSGeorge Zhang 	notifier = kmalloc(sizeof(struct vmci_handle_list), GFP_KERNEL);
62228d6692cSGeorge Zhang 	if (!notifier) {
62328d6692cSGeorge Zhang 		result = VMCI_ERROR_NO_MEM;
62428d6692cSGeorge Zhang 		goto out;
62528d6692cSGeorge Zhang 	}
62628d6692cSGeorge Zhang 
62728d6692cSGeorge Zhang 	INIT_LIST_HEAD(&notifier->node);
62828d6692cSGeorge Zhang 	notifier->handle = vmci_make_handle(remote_cid, VMCI_EVENT_HANDLER);
62928d6692cSGeorge Zhang 
63028d6692cSGeorge Zhang 	spin_lock(&context->lock);
63128d6692cSGeorge Zhang 
6321c2eb5b2SVishnu DASA 	if (context->n_notifiers < VMCI_MAX_CONTEXTS) {
63328d6692cSGeorge Zhang 		list_for_each_entry(n, &context->notifier_list, node) {
63428d6692cSGeorge Zhang 			if (vmci_handle_is_equal(n->handle, notifier->handle)) {
63528d6692cSGeorge Zhang 				exists = true;
63628d6692cSGeorge Zhang 				break;
63728d6692cSGeorge Zhang 			}
63828d6692cSGeorge Zhang 		}
63928d6692cSGeorge Zhang 
64028d6692cSGeorge Zhang 		if (exists) {
64128d6692cSGeorge Zhang 			kfree(notifier);
64228d6692cSGeorge Zhang 			result = VMCI_ERROR_ALREADY_EXISTS;
64328d6692cSGeorge Zhang 		} else {
6441c2eb5b2SVishnu DASA 			list_add_tail_rcu(&notifier->node,
6451c2eb5b2SVishnu DASA 					  &context->notifier_list);
64628d6692cSGeorge Zhang 			context->n_notifiers++;
64728d6692cSGeorge Zhang 			result = VMCI_SUCCESS;
64828d6692cSGeorge Zhang 		}
6491c2eb5b2SVishnu DASA 	} else {
6501c2eb5b2SVishnu DASA 		kfree(notifier);
6511c2eb5b2SVishnu DASA 		result = VMCI_ERROR_NO_MEM;
6521c2eb5b2SVishnu DASA 	}
65328d6692cSGeorge Zhang 
65428d6692cSGeorge Zhang 	spin_unlock(&context->lock);
65528d6692cSGeorge Zhang 
65628d6692cSGeorge Zhang  out:
65728d6692cSGeorge Zhang 	vmci_ctx_put(context);
65828d6692cSGeorge Zhang 	return result;
65928d6692cSGeorge Zhang }
66028d6692cSGeorge Zhang 
66128d6692cSGeorge Zhang /*
66228d6692cSGeorge Zhang  * Remove remote_cid from current context's list of contexts it is
66328d6692cSGeorge Zhang  * interested in getting notifications from/about.
66428d6692cSGeorge Zhang  */
vmci_ctx_remove_notification(u32 context_id,u32 remote_cid)66528d6692cSGeorge Zhang int vmci_ctx_remove_notification(u32 context_id, u32 remote_cid)
66628d6692cSGeorge Zhang {
66728d6692cSGeorge Zhang 	struct vmci_ctx *context;
668f61c5c83SJakob Koschel 	struct vmci_handle_list *notifier = NULL, *iter, *tmp;
66928d6692cSGeorge Zhang 	struct vmci_handle handle;
67028d6692cSGeorge Zhang 
67128d6692cSGeorge Zhang 	context = vmci_ctx_get(context_id);
67228d6692cSGeorge Zhang 	if (!context)
67328d6692cSGeorge Zhang 		return VMCI_ERROR_NOT_FOUND;
67428d6692cSGeorge Zhang 
67528d6692cSGeorge Zhang 	handle = vmci_make_handle(remote_cid, VMCI_EVENT_HANDLER);
67628d6692cSGeorge Zhang 
67728d6692cSGeorge Zhang 	spin_lock(&context->lock);
678f61c5c83SJakob Koschel 	list_for_each_entry_safe(iter, tmp,
67928d6692cSGeorge Zhang 				 &context->notifier_list, node) {
680f61c5c83SJakob Koschel 		if (vmci_handle_is_equal(iter->handle, handle)) {
681f61c5c83SJakob Koschel 			list_del_rcu(&iter->node);
68228d6692cSGeorge Zhang 			context->n_notifiers--;
683f61c5c83SJakob Koschel 			notifier = iter;
68428d6692cSGeorge Zhang 			break;
68528d6692cSGeorge Zhang 		}
68628d6692cSGeorge Zhang 	}
68728d6692cSGeorge Zhang 	spin_unlock(&context->lock);
68828d6692cSGeorge Zhang 
689f61c5c83SJakob Koschel 	if (notifier)
690*09b2286aSUladzislau Rezki (Sony) 		kvfree_rcu_mightsleep(notifier);
69128d6692cSGeorge Zhang 
69228d6692cSGeorge Zhang 	vmci_ctx_put(context);
69328d6692cSGeorge Zhang 
694f61c5c83SJakob Koschel 	return notifier ? VMCI_SUCCESS : VMCI_ERROR_NOT_FOUND;
69528d6692cSGeorge Zhang }
69628d6692cSGeorge Zhang 
vmci_ctx_get_chkpt_notifiers(struct vmci_ctx * context,u32 * buf_size,void ** pbuf)69728d6692cSGeorge Zhang static int vmci_ctx_get_chkpt_notifiers(struct vmci_ctx *context,
69828d6692cSGeorge Zhang 					u32 *buf_size, void **pbuf)
69928d6692cSGeorge Zhang {
70028d6692cSGeorge Zhang 	u32 *notifiers;
70128d6692cSGeorge Zhang 	size_t data_size;
70228d6692cSGeorge Zhang 	struct vmci_handle_list *entry;
70328d6692cSGeorge Zhang 	int i = 0;
70428d6692cSGeorge Zhang 
70528d6692cSGeorge Zhang 	if (context->n_notifiers == 0) {
70628d6692cSGeorge Zhang 		*buf_size = 0;
70728d6692cSGeorge Zhang 		*pbuf = NULL;
70828d6692cSGeorge Zhang 		return VMCI_SUCCESS;
70928d6692cSGeorge Zhang 	}
71028d6692cSGeorge Zhang 
71128d6692cSGeorge Zhang 	data_size = context->n_notifiers * sizeof(*notifiers);
71228d6692cSGeorge Zhang 	if (*buf_size < data_size) {
71328d6692cSGeorge Zhang 		*buf_size = data_size;
71428d6692cSGeorge Zhang 		return VMCI_ERROR_MORE_DATA;
71528d6692cSGeorge Zhang 	}
71628d6692cSGeorge Zhang 
71728d6692cSGeorge Zhang 	notifiers = kmalloc(data_size, GFP_ATOMIC); /* FIXME: want GFP_KERNEL */
71828d6692cSGeorge Zhang 	if (!notifiers)
71928d6692cSGeorge Zhang 		return VMCI_ERROR_NO_MEM;
72028d6692cSGeorge Zhang 
72128d6692cSGeorge Zhang 	list_for_each_entry(entry, &context->notifier_list, node)
72228d6692cSGeorge Zhang 		notifiers[i++] = entry->handle.context;
72328d6692cSGeorge Zhang 
72428d6692cSGeorge Zhang 	*buf_size = data_size;
72528d6692cSGeorge Zhang 	*pbuf = notifiers;
72628d6692cSGeorge Zhang 	return VMCI_SUCCESS;
72728d6692cSGeorge Zhang }
72828d6692cSGeorge Zhang 
vmci_ctx_get_chkpt_doorbells(struct vmci_ctx * context,u32 * buf_size,void ** pbuf)72928d6692cSGeorge Zhang static int vmci_ctx_get_chkpt_doorbells(struct vmci_ctx *context,
73028d6692cSGeorge Zhang 					u32 *buf_size, void **pbuf)
73128d6692cSGeorge Zhang {
73228d6692cSGeorge Zhang 	struct dbell_cpt_state *dbells;
7331c2eb5b2SVishnu DASA 	u32 i, n_doorbells;
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 
74331dcb6c3SAnant Thazhemadam 		dbells = kzalloc(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  */
vmci_ctx_get_chkpt_state(u32 context_id,u32 cpt_type,u32 * buf_size,void ** pbuf)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  */
vmci_ctx_set_chkpt_state(u32 context_id,u32 cpt_type,u32 buf_size,void * cpt_buf)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  */
vmci_ctx_rcv_notifications_get(u32 context_id,struct vmci_handle_arr ** db_handle_array,struct vmci_handle_arr ** qp_handle_array)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;
8711c2eb5b2SVishnu DASA 	context->pending_doorbell_array =
8721c2eb5b2SVishnu DASA 		vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT);
87328d6692cSGeorge Zhang 	if (!context->pending_doorbell_array) {
87428d6692cSGeorge Zhang 		context->pending_doorbell_array = *db_handle_array;
87528d6692cSGeorge Zhang 		*db_handle_array = NULL;
87628d6692cSGeorge Zhang 		result = VMCI_ERROR_NO_MEM;
87728d6692cSGeorge Zhang 	}
87828d6692cSGeorge Zhang 	*qp_handle_array = NULL;
87928d6692cSGeorge Zhang 
88028d6692cSGeorge Zhang 	spin_unlock(&context->lock);
88128d6692cSGeorge Zhang 	vmci_ctx_put(context);
88228d6692cSGeorge Zhang 
88328d6692cSGeorge Zhang 	return result;
88428d6692cSGeorge Zhang }
88528d6692cSGeorge Zhang 
88628d6692cSGeorge Zhang /*
88728d6692cSGeorge Zhang  * Releases handle arrays with pending notifications previously
88828d6692cSGeorge Zhang  * retrieved using vmci_ctx_rcv_notifications_get. If the
88928d6692cSGeorge Zhang  * notifications were not successfully handed over to the guest,
89028d6692cSGeorge Zhang  * success must be false.
89128d6692cSGeorge Zhang  */
vmci_ctx_rcv_notifications_release(u32 context_id,struct vmci_handle_arr * db_handle_array,struct vmci_handle_arr * qp_handle_array,bool success)89228d6692cSGeorge Zhang void vmci_ctx_rcv_notifications_release(u32 context_id,
89328d6692cSGeorge Zhang 					struct vmci_handle_arr *db_handle_array,
89428d6692cSGeorge Zhang 					struct vmci_handle_arr *qp_handle_array,
89528d6692cSGeorge Zhang 					bool success)
89628d6692cSGeorge Zhang {
89728d6692cSGeorge Zhang 	struct vmci_ctx *context = vmci_ctx_get(context_id);
89828d6692cSGeorge Zhang 
89928d6692cSGeorge Zhang 	spin_lock(&context->lock);
90028d6692cSGeorge Zhang 	if (!success) {
90128d6692cSGeorge Zhang 		struct vmci_handle handle;
90228d6692cSGeorge Zhang 
90328d6692cSGeorge Zhang 		/*
90428d6692cSGeorge Zhang 		 * New notifications may have been added while we were not
90528d6692cSGeorge Zhang 		 * holding the context lock, so we transfer any new pending
90628d6692cSGeorge Zhang 		 * doorbell notifications to the old array, and reinstate the
90728d6692cSGeorge Zhang 		 * old array.
90828d6692cSGeorge Zhang 		 */
90928d6692cSGeorge Zhang 
91028d6692cSGeorge Zhang 		handle = vmci_handle_arr_remove_tail(
91128d6692cSGeorge Zhang 					context->pending_doorbell_array);
91228d6692cSGeorge Zhang 		while (!vmci_handle_is_invalid(handle)) {
91328d6692cSGeorge Zhang 			if (!vmci_handle_arr_has_entry(db_handle_array,
91428d6692cSGeorge Zhang 						       handle)) {
91528d6692cSGeorge Zhang 				vmci_handle_arr_append_entry(
91628d6692cSGeorge Zhang 						&db_handle_array, handle);
91728d6692cSGeorge Zhang 			}
91828d6692cSGeorge Zhang 			handle = vmci_handle_arr_remove_tail(
91928d6692cSGeorge Zhang 					context->pending_doorbell_array);
92028d6692cSGeorge Zhang 		}
92128d6692cSGeorge Zhang 		vmci_handle_arr_destroy(context->pending_doorbell_array);
92228d6692cSGeorge Zhang 		context->pending_doorbell_array = db_handle_array;
92328d6692cSGeorge Zhang 		db_handle_array = NULL;
92428d6692cSGeorge Zhang 	} else {
92528d6692cSGeorge Zhang 		ctx_clear_notify_call(context);
92628d6692cSGeorge Zhang 	}
92728d6692cSGeorge Zhang 	spin_unlock(&context->lock);
92828d6692cSGeorge Zhang 	vmci_ctx_put(context);
92928d6692cSGeorge Zhang 
93028d6692cSGeorge Zhang 	if (db_handle_array)
93128d6692cSGeorge Zhang 		vmci_handle_arr_destroy(db_handle_array);
93228d6692cSGeorge Zhang 
93328d6692cSGeorge Zhang 	if (qp_handle_array)
93428d6692cSGeorge Zhang 		vmci_handle_arr_destroy(qp_handle_array);
93528d6692cSGeorge Zhang }
93628d6692cSGeorge Zhang 
93728d6692cSGeorge Zhang /*
93828d6692cSGeorge Zhang  * Registers that a new doorbell handle has been allocated by the
93928d6692cSGeorge Zhang  * context. Only doorbell handles registered can be notified.
94028d6692cSGeorge Zhang  */
vmci_ctx_dbell_create(u32 context_id,struct vmci_handle handle)94128d6692cSGeorge Zhang int vmci_ctx_dbell_create(u32 context_id, struct vmci_handle handle)
94228d6692cSGeorge Zhang {
94328d6692cSGeorge Zhang 	struct vmci_ctx *context;
94428d6692cSGeorge Zhang 	int result;
94528d6692cSGeorge Zhang 
94628d6692cSGeorge Zhang 	if (context_id == VMCI_INVALID_ID || vmci_handle_is_invalid(handle))
94728d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
94828d6692cSGeorge Zhang 
94928d6692cSGeorge Zhang 	context = vmci_ctx_get(context_id);
95028d6692cSGeorge Zhang 	if (context == NULL)
95128d6692cSGeorge Zhang 		return VMCI_ERROR_NOT_FOUND;
95228d6692cSGeorge Zhang 
95328d6692cSGeorge Zhang 	spin_lock(&context->lock);
9541c2eb5b2SVishnu DASA 	if (!vmci_handle_arr_has_entry(context->doorbell_array, handle))
9551c2eb5b2SVishnu DASA 		result = vmci_handle_arr_append_entry(&context->doorbell_array,
9561c2eb5b2SVishnu DASA 						      handle);
9571c2eb5b2SVishnu DASA 	else
95828d6692cSGeorge Zhang 		result = VMCI_ERROR_DUPLICATE_ENTRY;
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  */
vmci_ctx_dbell_destroy(u32 context_id,struct vmci_handle handle)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  */
vmci_ctx_dbell_destroy_all(u32 context_id)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  */
vmci_ctx_notify_dbell(u32 src_cid,struct vmci_handle handle,u32 src_priv_flags)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)) {
10941c2eb5b2SVishnu DASA 				result = vmci_handle_arr_append_entry(
109528d6692cSGeorge Zhang 					&dst_context->pending_doorbell_array,
109628d6692cSGeorge Zhang 					handle);
10971c2eb5b2SVishnu DASA 				if (result == VMCI_SUCCESS) {
109828d6692cSGeorge Zhang 					ctx_signal_notify(dst_context);
109928d6692cSGeorge Zhang 					wake_up(&dst_context->host_context.wait_queue);
110028d6692cSGeorge Zhang 				}
11011c2eb5b2SVishnu DASA 			} else {
110228d6692cSGeorge Zhang 				result = VMCI_SUCCESS;
110328d6692cSGeorge Zhang 			}
11041c2eb5b2SVishnu DASA 		}
110528d6692cSGeorge Zhang 		spin_unlock(&dst_context->lock);
110628d6692cSGeorge Zhang 	}
110728d6692cSGeorge Zhang 
110828d6692cSGeorge Zhang  out:
110928d6692cSGeorge Zhang 	vmci_ctx_put(dst_context);
111028d6692cSGeorge Zhang 
111128d6692cSGeorge Zhang 	return result;
111228d6692cSGeorge Zhang }
111328d6692cSGeorge Zhang 
vmci_ctx_supports_host_qp(struct vmci_ctx * context)111428d6692cSGeorge Zhang bool vmci_ctx_supports_host_qp(struct vmci_ctx *context)
111528d6692cSGeorge Zhang {
111628d6692cSGeorge Zhang 	return context && context->user_version >= VMCI_VERSION_HOSTQP;
111728d6692cSGeorge Zhang }
111828d6692cSGeorge Zhang 
111928d6692cSGeorge Zhang /*
112028d6692cSGeorge Zhang  * Registers that a new queue pair handle has been allocated by
112128d6692cSGeorge Zhang  * the context.
112228d6692cSGeorge Zhang  */
vmci_ctx_qp_create(struct vmci_ctx * context,struct vmci_handle handle)112328d6692cSGeorge Zhang int vmci_ctx_qp_create(struct vmci_ctx *context, struct vmci_handle handle)
112428d6692cSGeorge Zhang {
112528d6692cSGeorge Zhang 	int result;
112628d6692cSGeorge Zhang 
112728d6692cSGeorge Zhang 	if (context == NULL || vmci_handle_is_invalid(handle))
112828d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
112928d6692cSGeorge Zhang 
11301c2eb5b2SVishnu DASA 	if (!vmci_handle_arr_has_entry(context->queue_pair_array, handle))
11311c2eb5b2SVishnu DASA 		result = vmci_handle_arr_append_entry(
11321c2eb5b2SVishnu DASA 			&context->queue_pair_array, handle);
11331c2eb5b2SVishnu DASA 	else
113428d6692cSGeorge Zhang 		result = VMCI_ERROR_DUPLICATE_ENTRY;
113528d6692cSGeorge Zhang 
113628d6692cSGeorge Zhang 	return result;
113728d6692cSGeorge Zhang }
113828d6692cSGeorge Zhang 
113928d6692cSGeorge Zhang /*
114028d6692cSGeorge Zhang  * Unregisters a queue pair handle that was previously registered
114128d6692cSGeorge Zhang  * with vmci_ctx_qp_create.
114228d6692cSGeorge Zhang  */
vmci_ctx_qp_destroy(struct vmci_ctx * context,struct vmci_handle handle)114328d6692cSGeorge Zhang int vmci_ctx_qp_destroy(struct vmci_ctx *context, struct vmci_handle handle)
114428d6692cSGeorge Zhang {
114528d6692cSGeorge Zhang 	struct vmci_handle hndl;
114628d6692cSGeorge Zhang 
114728d6692cSGeorge Zhang 	if (context == NULL || vmci_handle_is_invalid(handle))
114828d6692cSGeorge Zhang 		return VMCI_ERROR_INVALID_ARGS;
114928d6692cSGeorge Zhang 
115028d6692cSGeorge Zhang 	hndl = vmci_handle_arr_remove_entry(context->queue_pair_array, handle);
115128d6692cSGeorge Zhang 
115228d6692cSGeorge Zhang 	return vmci_handle_is_invalid(hndl) ?
115328d6692cSGeorge Zhang 		VMCI_ERROR_NOT_FOUND : VMCI_SUCCESS;
115428d6692cSGeorge Zhang }
115528d6692cSGeorge Zhang 
115628d6692cSGeorge Zhang /*
115728d6692cSGeorge Zhang  * Determines whether a given queue pair handle is registered
115828d6692cSGeorge Zhang  * with the given context.
115928d6692cSGeorge Zhang  */
vmci_ctx_qp_exists(struct vmci_ctx * context,struct vmci_handle handle)116028d6692cSGeorge Zhang bool vmci_ctx_qp_exists(struct vmci_ctx *context, struct vmci_handle handle)
116128d6692cSGeorge Zhang {
116228d6692cSGeorge Zhang 	if (context == NULL || vmci_handle_is_invalid(handle))
116328d6692cSGeorge Zhang 		return false;
116428d6692cSGeorge Zhang 
116528d6692cSGeorge Zhang 	return vmci_handle_arr_has_entry(context->queue_pair_array, handle);
116628d6692cSGeorge Zhang }
116728d6692cSGeorge Zhang 
116828d6692cSGeorge Zhang /*
116928d6692cSGeorge Zhang  * vmci_context_get_priv_flags() - Retrieve privilege flags.
117028d6692cSGeorge Zhang  * @context_id: The context ID of the VMCI context.
117128d6692cSGeorge Zhang  *
117228d6692cSGeorge Zhang  * Retrieves privilege flags of the given VMCI context ID.
117328d6692cSGeorge Zhang  */
vmci_context_get_priv_flags(u32 context_id)117428d6692cSGeorge Zhang u32 vmci_context_get_priv_flags(u32 context_id)
117528d6692cSGeorge Zhang {
117628d6692cSGeorge Zhang 	if (vmci_host_code_active()) {
117728d6692cSGeorge Zhang 		u32 flags;
117828d6692cSGeorge Zhang 		struct vmci_ctx *context;
117928d6692cSGeorge Zhang 
118028d6692cSGeorge Zhang 		context = vmci_ctx_get(context_id);
118128d6692cSGeorge Zhang 		if (!context)
118228d6692cSGeorge Zhang 			return VMCI_LEAST_PRIVILEGE_FLAGS;
118328d6692cSGeorge Zhang 
118428d6692cSGeorge Zhang 		flags = context->priv_flags;
118528d6692cSGeorge Zhang 		vmci_ctx_put(context);
118628d6692cSGeorge Zhang 		return flags;
118728d6692cSGeorge Zhang 	}
118828d6692cSGeorge Zhang 	return VMCI_NO_PRIVILEGE_FLAGS;
118928d6692cSGeorge Zhang }
119028d6692cSGeorge Zhang EXPORT_SYMBOL_GPL(vmci_context_get_priv_flags);
119128d6692cSGeorge Zhang 
119228d6692cSGeorge Zhang /*
119328d6692cSGeorge Zhang  * vmci_is_context_owner() - Determimnes if user is the context owner
119428d6692cSGeorge Zhang  * @context_id: The context ID of the VMCI context.
119528d6692cSGeorge Zhang  * @uid:        The host user id (real kernel value).
119628d6692cSGeorge Zhang  *
119728d6692cSGeorge Zhang  * Determines whether a given UID is the owner of given VMCI context.
119828d6692cSGeorge Zhang  */
vmci_is_context_owner(u32 context_id,kuid_t uid)119928d6692cSGeorge Zhang bool vmci_is_context_owner(u32 context_id, kuid_t uid)
120028d6692cSGeorge Zhang {
120128d6692cSGeorge Zhang 	bool is_owner = false;
120228d6692cSGeorge Zhang 
120328d6692cSGeorge Zhang 	if (vmci_host_code_active()) {
120428d6692cSGeorge Zhang 		struct vmci_ctx *context = vmci_ctx_get(context_id);
120528d6692cSGeorge Zhang 		if (context) {
120628d6692cSGeorge Zhang 			if (context->cred)
120728d6692cSGeorge Zhang 				is_owner = uid_eq(context->cred->uid, uid);
120828d6692cSGeorge Zhang 			vmci_ctx_put(context);
120928d6692cSGeorge Zhang 		}
121028d6692cSGeorge Zhang 	}
121128d6692cSGeorge Zhang 
121228d6692cSGeorge Zhang 	return is_owner;
121328d6692cSGeorge Zhang }
121428d6692cSGeorge Zhang EXPORT_SYMBOL_GPL(vmci_is_context_owner);
1215