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