162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * VMware VMCI Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 VMware, Inc. All rights reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/vmw_vmci_defs.h>
962306a36Sopenharmony_ci#include <linux/vmw_vmci_api.h>
1062306a36Sopenharmony_ci#include <linux/highmem.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/sched.h>
1462306a36Sopenharmony_ci#include <linux/cred.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "vmci_queue_pair.h"
1862306a36Sopenharmony_ci#include "vmci_datagram.h"
1962306a36Sopenharmony_ci#include "vmci_doorbell.h"
2062306a36Sopenharmony_ci#include "vmci_context.h"
2162306a36Sopenharmony_ci#include "vmci_driver.h"
2262306a36Sopenharmony_ci#include "vmci_event.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* Use a wide upper bound for the maximum contexts. */
2562306a36Sopenharmony_ci#define VMCI_MAX_CONTEXTS 2000
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci * List of current VMCI contexts.  Contexts can be added by
2962306a36Sopenharmony_ci * vmci_ctx_create() and removed via vmci_ctx_destroy().
3062306a36Sopenharmony_ci * These, along with context lookup, are protected by the
3162306a36Sopenharmony_ci * list structure's lock.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_cistatic struct {
3462306a36Sopenharmony_ci	struct list_head head;
3562306a36Sopenharmony_ci	spinlock_t lock; /* Spinlock for context list operations */
3662306a36Sopenharmony_ci} ctx_list = {
3762306a36Sopenharmony_ci	.head = LIST_HEAD_INIT(ctx_list.head),
3862306a36Sopenharmony_ci	.lock = __SPIN_LOCK_UNLOCKED(ctx_list.lock),
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* Used by contexts that did not set up notify flag pointers */
4262306a36Sopenharmony_cistatic bool ctx_dummy_notify;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic void ctx_signal_notify(struct vmci_ctx *context)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	*context->notify = true;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void ctx_clear_notify(struct vmci_ctx *context)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	*context->notify = false;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/*
5562306a36Sopenharmony_ci * If nothing requires the attention of the guest, clears both
5662306a36Sopenharmony_ci * notify flag and call.
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_cistatic void ctx_clear_notify_call(struct vmci_ctx *context)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	if (context->pending_datagrams == 0 &&
6162306a36Sopenharmony_ci	    vmci_handle_arr_get_size(context->pending_doorbell_array) == 0)
6262306a36Sopenharmony_ci		ctx_clear_notify(context);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/*
6662306a36Sopenharmony_ci * Sets the context's notify flag iff datagrams are pending for this
6762306a36Sopenharmony_ci * context.  Called from vmci_setup_notify().
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_civoid vmci_ctx_check_signal_notify(struct vmci_ctx *context)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	spin_lock(&context->lock);
7262306a36Sopenharmony_ci	if (context->pending_datagrams)
7362306a36Sopenharmony_ci		ctx_signal_notify(context);
7462306a36Sopenharmony_ci	spin_unlock(&context->lock);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/*
7862306a36Sopenharmony_ci * Allocates and initializes a VMCI context.
7962306a36Sopenharmony_ci */
8062306a36Sopenharmony_cistruct vmci_ctx *vmci_ctx_create(u32 cid, u32 priv_flags,
8162306a36Sopenharmony_ci				 uintptr_t event_hnd,
8262306a36Sopenharmony_ci				 int user_version,
8362306a36Sopenharmony_ci				 const struct cred *cred)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct vmci_ctx *context;
8662306a36Sopenharmony_ci	int error;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (cid == VMCI_INVALID_ID) {
8962306a36Sopenharmony_ci		pr_devel("Invalid context ID for VMCI context\n");
9062306a36Sopenharmony_ci		error = -EINVAL;
9162306a36Sopenharmony_ci		goto err_out;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS) {
9562306a36Sopenharmony_ci		pr_devel("Invalid flag (flags=0x%x) for VMCI context\n",
9662306a36Sopenharmony_ci			 priv_flags);
9762306a36Sopenharmony_ci		error = -EINVAL;
9862306a36Sopenharmony_ci		goto err_out;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (user_version == 0) {
10262306a36Sopenharmony_ci		pr_devel("Invalid suer_version %d\n", user_version);
10362306a36Sopenharmony_ci		error = -EINVAL;
10462306a36Sopenharmony_ci		goto err_out;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	context = kzalloc(sizeof(*context), GFP_KERNEL);
10862306a36Sopenharmony_ci	if (!context) {
10962306a36Sopenharmony_ci		pr_warn("Failed to allocate memory for VMCI context\n");
11062306a36Sopenharmony_ci		error = -ENOMEM;
11162306a36Sopenharmony_ci		goto err_out;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	kref_init(&context->kref);
11562306a36Sopenharmony_ci	spin_lock_init(&context->lock);
11662306a36Sopenharmony_ci	INIT_LIST_HEAD(&context->list_item);
11762306a36Sopenharmony_ci	INIT_LIST_HEAD(&context->datagram_queue);
11862306a36Sopenharmony_ci	INIT_LIST_HEAD(&context->notifier_list);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Initialize host-specific VMCI context. */
12162306a36Sopenharmony_ci	init_waitqueue_head(&context->host_context.wait_queue);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	context->queue_pair_array =
12462306a36Sopenharmony_ci		vmci_handle_arr_create(0, VMCI_MAX_GUEST_QP_COUNT);
12562306a36Sopenharmony_ci	if (!context->queue_pair_array) {
12662306a36Sopenharmony_ci		error = -ENOMEM;
12762306a36Sopenharmony_ci		goto err_free_ctx;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	context->doorbell_array =
13162306a36Sopenharmony_ci		vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT);
13262306a36Sopenharmony_ci	if (!context->doorbell_array) {
13362306a36Sopenharmony_ci		error = -ENOMEM;
13462306a36Sopenharmony_ci		goto err_free_qp_array;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	context->pending_doorbell_array =
13862306a36Sopenharmony_ci		vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT);
13962306a36Sopenharmony_ci	if (!context->pending_doorbell_array) {
14062306a36Sopenharmony_ci		error = -ENOMEM;
14162306a36Sopenharmony_ci		goto err_free_db_array;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	context->user_version = user_version;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	context->priv_flags = priv_flags;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (cred)
14962306a36Sopenharmony_ci		context->cred = get_cred(cred);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	context->notify = &ctx_dummy_notify;
15262306a36Sopenharmony_ci	context->notify_page = NULL;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/*
15562306a36Sopenharmony_ci	 * If we collide with an existing context we generate a new
15662306a36Sopenharmony_ci	 * and use it instead. The VMX will determine if regeneration
15762306a36Sopenharmony_ci	 * is okay. Since there isn't 4B - 16 VMs running on a given
15862306a36Sopenharmony_ci	 * host, the below loop will terminate.
15962306a36Sopenharmony_ci	 */
16062306a36Sopenharmony_ci	spin_lock(&ctx_list.lock);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	while (vmci_ctx_exists(cid)) {
16362306a36Sopenharmony_ci		/* We reserve the lowest 16 ids for fixed contexts. */
16462306a36Sopenharmony_ci		cid = max(cid, VMCI_RESERVED_CID_LIMIT - 1) + 1;
16562306a36Sopenharmony_ci		if (cid == VMCI_INVALID_ID)
16662306a36Sopenharmony_ci			cid = VMCI_RESERVED_CID_LIMIT;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci	context->cid = cid;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	list_add_tail_rcu(&context->list_item, &ctx_list.head);
17162306a36Sopenharmony_ci	spin_unlock(&ctx_list.lock);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return context;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci err_free_db_array:
17662306a36Sopenharmony_ci	vmci_handle_arr_destroy(context->doorbell_array);
17762306a36Sopenharmony_ci err_free_qp_array:
17862306a36Sopenharmony_ci	vmci_handle_arr_destroy(context->queue_pair_array);
17962306a36Sopenharmony_ci err_free_ctx:
18062306a36Sopenharmony_ci	kfree(context);
18162306a36Sopenharmony_ci err_out:
18262306a36Sopenharmony_ci	return ERR_PTR(error);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/*
18662306a36Sopenharmony_ci * Destroy VMCI context.
18762306a36Sopenharmony_ci */
18862306a36Sopenharmony_civoid vmci_ctx_destroy(struct vmci_ctx *context)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	spin_lock(&ctx_list.lock);
19162306a36Sopenharmony_ci	list_del_rcu(&context->list_item);
19262306a36Sopenharmony_ci	spin_unlock(&ctx_list.lock);
19362306a36Sopenharmony_ci	synchronize_rcu();
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	vmci_ctx_put(context);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/*
19962306a36Sopenharmony_ci * Fire notification for all contexts interested in given cid.
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_cistatic int ctx_fire_notification(u32 context_id, u32 priv_flags)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	u32 i, array_size;
20462306a36Sopenharmony_ci	struct vmci_ctx *sub_ctx;
20562306a36Sopenharmony_ci	struct vmci_handle_arr *subscriber_array;
20662306a36Sopenharmony_ci	struct vmci_handle context_handle =
20762306a36Sopenharmony_ci		vmci_make_handle(context_id, VMCI_EVENT_HANDLER);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/*
21062306a36Sopenharmony_ci	 * We create an array to hold the subscribers we find when
21162306a36Sopenharmony_ci	 * scanning through all contexts.
21262306a36Sopenharmony_ci	 */
21362306a36Sopenharmony_ci	subscriber_array = vmci_handle_arr_create(0, VMCI_MAX_CONTEXTS);
21462306a36Sopenharmony_ci	if (subscriber_array == NULL)
21562306a36Sopenharmony_ci		return VMCI_ERROR_NO_MEM;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/*
21862306a36Sopenharmony_ci	 * Scan all contexts to find who is interested in being
21962306a36Sopenharmony_ci	 * notified about given contextID.
22062306a36Sopenharmony_ci	 */
22162306a36Sopenharmony_ci	rcu_read_lock();
22262306a36Sopenharmony_ci	list_for_each_entry_rcu(sub_ctx, &ctx_list.head, list_item) {
22362306a36Sopenharmony_ci		struct vmci_handle_list *node;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		/*
22662306a36Sopenharmony_ci		 * We only deliver notifications of the removal of
22762306a36Sopenharmony_ci		 * contexts, if the two contexts are allowed to
22862306a36Sopenharmony_ci		 * interact.
22962306a36Sopenharmony_ci		 */
23062306a36Sopenharmony_ci		if (vmci_deny_interaction(priv_flags, sub_ctx->priv_flags))
23162306a36Sopenharmony_ci			continue;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		list_for_each_entry_rcu(node, &sub_ctx->notifier_list, node) {
23462306a36Sopenharmony_ci			if (!vmci_handle_is_equal(node->handle, context_handle))
23562306a36Sopenharmony_ci				continue;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci			vmci_handle_arr_append_entry(&subscriber_array,
23862306a36Sopenharmony_ci					vmci_make_handle(sub_ctx->cid,
23962306a36Sopenharmony_ci							 VMCI_EVENT_HANDLER));
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci	rcu_read_unlock();
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Fire event to all subscribers. */
24562306a36Sopenharmony_ci	array_size = vmci_handle_arr_get_size(subscriber_array);
24662306a36Sopenharmony_ci	for (i = 0; i < array_size; i++) {
24762306a36Sopenharmony_ci		int result;
24862306a36Sopenharmony_ci		struct vmci_event_ctx ev;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		ev.msg.hdr.dst = vmci_handle_arr_get_entry(subscriber_array, i);
25162306a36Sopenharmony_ci		ev.msg.hdr.src = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
25262306a36Sopenharmony_ci						  VMCI_CONTEXT_RESOURCE_ID);
25362306a36Sopenharmony_ci		ev.msg.hdr.payload_size = sizeof(ev) - sizeof(ev.msg.hdr);
25462306a36Sopenharmony_ci		ev.msg.event_data.event = VMCI_EVENT_CTX_REMOVED;
25562306a36Sopenharmony_ci		ev.payload.context_id = context_id;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		result = vmci_datagram_dispatch(VMCI_HYPERVISOR_CONTEXT_ID,
25862306a36Sopenharmony_ci						&ev.msg.hdr, false);
25962306a36Sopenharmony_ci		if (result < VMCI_SUCCESS) {
26062306a36Sopenharmony_ci			pr_devel("Failed to enqueue event datagram (type=%d) for context (ID=0x%x)\n",
26162306a36Sopenharmony_ci				 ev.msg.event_data.event,
26262306a36Sopenharmony_ci				 ev.msg.hdr.dst.context);
26362306a36Sopenharmony_ci			/* We continue to enqueue on next subscriber. */
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci	vmci_handle_arr_destroy(subscriber_array);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return VMCI_SUCCESS;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/*
27262306a36Sopenharmony_ci * Returns the current number of pending datagrams. The call may
27362306a36Sopenharmony_ci * also serve as a synchronization point for the datagram queue,
27462306a36Sopenharmony_ci * as no enqueue operations can occur concurrently.
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_ciint vmci_ctx_pending_datagrams(u32 cid, u32 *pending)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct vmci_ctx *context;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	context = vmci_ctx_get(cid);
28162306a36Sopenharmony_ci	if (context == NULL)
28262306a36Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	spin_lock(&context->lock);
28562306a36Sopenharmony_ci	if (pending)
28662306a36Sopenharmony_ci		*pending = context->pending_datagrams;
28762306a36Sopenharmony_ci	spin_unlock(&context->lock);
28862306a36Sopenharmony_ci	vmci_ctx_put(context);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return VMCI_SUCCESS;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/*
29462306a36Sopenharmony_ci * Queues a VMCI datagram for the appropriate target VM context.
29562306a36Sopenharmony_ci */
29662306a36Sopenharmony_ciint vmci_ctx_enqueue_datagram(u32 cid, struct vmci_datagram *dg)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct vmci_datagram_queue_entry *dq_entry;
29962306a36Sopenharmony_ci	struct vmci_ctx *context;
30062306a36Sopenharmony_ci	struct vmci_handle dg_src;
30162306a36Sopenharmony_ci	size_t vmci_dg_size;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	vmci_dg_size = VMCI_DG_SIZE(dg);
30462306a36Sopenharmony_ci	if (vmci_dg_size > VMCI_MAX_DG_SIZE) {
30562306a36Sopenharmony_ci		pr_devel("Datagram too large (bytes=%zu)\n", vmci_dg_size);
30662306a36Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* Get the target VM's VMCI context. */
31062306a36Sopenharmony_ci	context = vmci_ctx_get(cid);
31162306a36Sopenharmony_ci	if (!context) {
31262306a36Sopenharmony_ci		pr_devel("Invalid context (ID=0x%x)\n", cid);
31362306a36Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* Allocate guest call entry and add it to the target VM's queue. */
31762306a36Sopenharmony_ci	dq_entry = kmalloc(sizeof(*dq_entry), GFP_KERNEL);
31862306a36Sopenharmony_ci	if (dq_entry == NULL) {
31962306a36Sopenharmony_ci		pr_warn("Failed to allocate memory for datagram\n");
32062306a36Sopenharmony_ci		vmci_ctx_put(context);
32162306a36Sopenharmony_ci		return VMCI_ERROR_NO_MEM;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	dq_entry->dg = dg;
32462306a36Sopenharmony_ci	dq_entry->dg_size = vmci_dg_size;
32562306a36Sopenharmony_ci	dg_src = dg->src;
32662306a36Sopenharmony_ci	INIT_LIST_HEAD(&dq_entry->list_item);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	spin_lock(&context->lock);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/*
33162306a36Sopenharmony_ci	 * We put a higher limit on datagrams from the hypervisor.  If
33262306a36Sopenharmony_ci	 * the pending datagram is not from hypervisor, then we check
33362306a36Sopenharmony_ci	 * if enqueueing it would exceed the
33462306a36Sopenharmony_ci	 * VMCI_MAX_DATAGRAM_QUEUE_SIZE limit on the destination.  If
33562306a36Sopenharmony_ci	 * the pending datagram is from hypervisor, we allow it to be
33662306a36Sopenharmony_ci	 * queued at the destination side provided we don't reach the
33762306a36Sopenharmony_ci	 * VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE limit.
33862306a36Sopenharmony_ci	 */
33962306a36Sopenharmony_ci	if (context->datagram_queue_size + vmci_dg_size >=
34062306a36Sopenharmony_ci	    VMCI_MAX_DATAGRAM_QUEUE_SIZE &&
34162306a36Sopenharmony_ci	    (!vmci_handle_is_equal(dg_src,
34262306a36Sopenharmony_ci				vmci_make_handle
34362306a36Sopenharmony_ci				(VMCI_HYPERVISOR_CONTEXT_ID,
34462306a36Sopenharmony_ci				 VMCI_CONTEXT_RESOURCE_ID)) ||
34562306a36Sopenharmony_ci	     context->datagram_queue_size + vmci_dg_size >=
34662306a36Sopenharmony_ci	     VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE)) {
34762306a36Sopenharmony_ci		spin_unlock(&context->lock);
34862306a36Sopenharmony_ci		vmci_ctx_put(context);
34962306a36Sopenharmony_ci		kfree(dq_entry);
35062306a36Sopenharmony_ci		pr_devel("Context (ID=0x%x) receive queue is full\n", cid);
35162306a36Sopenharmony_ci		return VMCI_ERROR_NO_RESOURCES;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	list_add(&dq_entry->list_item, &context->datagram_queue);
35562306a36Sopenharmony_ci	context->pending_datagrams++;
35662306a36Sopenharmony_ci	context->datagram_queue_size += vmci_dg_size;
35762306a36Sopenharmony_ci	ctx_signal_notify(context);
35862306a36Sopenharmony_ci	wake_up(&context->host_context.wait_queue);
35962306a36Sopenharmony_ci	spin_unlock(&context->lock);
36062306a36Sopenharmony_ci	vmci_ctx_put(context);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	return vmci_dg_size;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci/*
36662306a36Sopenharmony_ci * Verifies whether a context with the specified context ID exists.
36762306a36Sopenharmony_ci * FIXME: utility is dubious as no decisions can be reliably made
36862306a36Sopenharmony_ci * using this data as context can appear and disappear at any time.
36962306a36Sopenharmony_ci */
37062306a36Sopenharmony_cibool vmci_ctx_exists(u32 cid)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct vmci_ctx *context;
37362306a36Sopenharmony_ci	bool exists = false;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	rcu_read_lock();
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	list_for_each_entry_rcu(context, &ctx_list.head, list_item) {
37862306a36Sopenharmony_ci		if (context->cid == cid) {
37962306a36Sopenharmony_ci			exists = true;
38062306a36Sopenharmony_ci			break;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	rcu_read_unlock();
38562306a36Sopenharmony_ci	return exists;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/*
38962306a36Sopenharmony_ci * Retrieves VMCI context corresponding to the given cid.
39062306a36Sopenharmony_ci */
39162306a36Sopenharmony_cistruct vmci_ctx *vmci_ctx_get(u32 cid)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct vmci_ctx *c, *context = NULL;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (cid == VMCI_INVALID_ID)
39662306a36Sopenharmony_ci		return NULL;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	rcu_read_lock();
39962306a36Sopenharmony_ci	list_for_each_entry_rcu(c, &ctx_list.head, list_item) {
40062306a36Sopenharmony_ci		if (c->cid == cid) {
40162306a36Sopenharmony_ci			/*
40262306a36Sopenharmony_ci			 * The context owner drops its own reference to the
40362306a36Sopenharmony_ci			 * context only after removing it from the list and
40462306a36Sopenharmony_ci			 * waiting for RCU grace period to expire. This
40562306a36Sopenharmony_ci			 * means that we are not about to increase the
40662306a36Sopenharmony_ci			 * reference count of something that is in the
40762306a36Sopenharmony_ci			 * process of being destroyed.
40862306a36Sopenharmony_ci			 */
40962306a36Sopenharmony_ci			context = c;
41062306a36Sopenharmony_ci			kref_get(&context->kref);
41162306a36Sopenharmony_ci			break;
41262306a36Sopenharmony_ci		}
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci	rcu_read_unlock();
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	return context;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci/*
42062306a36Sopenharmony_ci * Deallocates all parts of a context data structure. This
42162306a36Sopenharmony_ci * function doesn't lock the context, because it assumes that
42262306a36Sopenharmony_ci * the caller was holding the last reference to context.
42362306a36Sopenharmony_ci */
42462306a36Sopenharmony_cistatic void ctx_free_ctx(struct kref *kref)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct vmci_ctx *context = container_of(kref, struct vmci_ctx, kref);
42762306a36Sopenharmony_ci	struct vmci_datagram_queue_entry *dq_entry, *dq_entry_tmp;
42862306a36Sopenharmony_ci	struct vmci_handle temp_handle;
42962306a36Sopenharmony_ci	struct vmci_handle_list *notifier, *tmp;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/*
43262306a36Sopenharmony_ci	 * Fire event to all contexts interested in knowing this
43362306a36Sopenharmony_ci	 * context is dying.
43462306a36Sopenharmony_ci	 */
43562306a36Sopenharmony_ci	ctx_fire_notification(context->cid, context->priv_flags);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/*
43862306a36Sopenharmony_ci	 * Cleanup all queue pair resources attached to context.  If
43962306a36Sopenharmony_ci	 * the VM dies without cleaning up, this code will make sure
44062306a36Sopenharmony_ci	 * that no resources are leaked.
44162306a36Sopenharmony_ci	 */
44262306a36Sopenharmony_ci	temp_handle = vmci_handle_arr_get_entry(context->queue_pair_array, 0);
44362306a36Sopenharmony_ci	while (!vmci_handle_is_equal(temp_handle, VMCI_INVALID_HANDLE)) {
44462306a36Sopenharmony_ci		if (vmci_qp_broker_detach(temp_handle,
44562306a36Sopenharmony_ci					  context) < VMCI_SUCCESS) {
44662306a36Sopenharmony_ci			/*
44762306a36Sopenharmony_ci			 * When vmci_qp_broker_detach() succeeds it
44862306a36Sopenharmony_ci			 * removes the handle from the array.  If
44962306a36Sopenharmony_ci			 * detach fails, we must remove the handle
45062306a36Sopenharmony_ci			 * ourselves.
45162306a36Sopenharmony_ci			 */
45262306a36Sopenharmony_ci			vmci_handle_arr_remove_entry(context->queue_pair_array,
45362306a36Sopenharmony_ci						     temp_handle);
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci		temp_handle =
45662306a36Sopenharmony_ci		    vmci_handle_arr_get_entry(context->queue_pair_array, 0);
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/*
46062306a36Sopenharmony_ci	 * It is fine to destroy this without locking the callQueue, as
46162306a36Sopenharmony_ci	 * this is the only thread having a reference to the context.
46262306a36Sopenharmony_ci	 */
46362306a36Sopenharmony_ci	list_for_each_entry_safe(dq_entry, dq_entry_tmp,
46462306a36Sopenharmony_ci				 &context->datagram_queue, list_item) {
46562306a36Sopenharmony_ci		WARN_ON(dq_entry->dg_size != VMCI_DG_SIZE(dq_entry->dg));
46662306a36Sopenharmony_ci		list_del(&dq_entry->list_item);
46762306a36Sopenharmony_ci		kfree(dq_entry->dg);
46862306a36Sopenharmony_ci		kfree(dq_entry);
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	list_for_each_entry_safe(notifier, tmp,
47262306a36Sopenharmony_ci				 &context->notifier_list, node) {
47362306a36Sopenharmony_ci		list_del(&notifier->node);
47462306a36Sopenharmony_ci		kfree(notifier);
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	vmci_handle_arr_destroy(context->queue_pair_array);
47862306a36Sopenharmony_ci	vmci_handle_arr_destroy(context->doorbell_array);
47962306a36Sopenharmony_ci	vmci_handle_arr_destroy(context->pending_doorbell_array);
48062306a36Sopenharmony_ci	vmci_ctx_unset_notify(context);
48162306a36Sopenharmony_ci	if (context->cred)
48262306a36Sopenharmony_ci		put_cred(context->cred);
48362306a36Sopenharmony_ci	kfree(context);
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/*
48762306a36Sopenharmony_ci * Drops reference to VMCI context. If this is the last reference to
48862306a36Sopenharmony_ci * the context it will be deallocated. A context is created with
48962306a36Sopenharmony_ci * a reference count of one, and on destroy, it is removed from
49062306a36Sopenharmony_ci * the context list before its reference count is decremented. Thus,
49162306a36Sopenharmony_ci * if we reach zero, we are sure that nobody else are about to increment
49262306a36Sopenharmony_ci * it (they need the entry in the context list for that), and so there
49362306a36Sopenharmony_ci * is no need for locking.
49462306a36Sopenharmony_ci */
49562306a36Sopenharmony_civoid vmci_ctx_put(struct vmci_ctx *context)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	kref_put(&context->kref, ctx_free_ctx);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/*
50162306a36Sopenharmony_ci * Dequeues the next datagram and returns it to caller.
50262306a36Sopenharmony_ci * The caller passes in a pointer to the max size datagram
50362306a36Sopenharmony_ci * it can handle and the datagram is only unqueued if the
50462306a36Sopenharmony_ci * size is less than max_size. If larger max_size is set to
50562306a36Sopenharmony_ci * the size of the datagram to give the caller a chance to
50662306a36Sopenharmony_ci * set up a larger buffer for the guestcall.
50762306a36Sopenharmony_ci */
50862306a36Sopenharmony_ciint vmci_ctx_dequeue_datagram(struct vmci_ctx *context,
50962306a36Sopenharmony_ci			      size_t *max_size,
51062306a36Sopenharmony_ci			      struct vmci_datagram **dg)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct vmci_datagram_queue_entry *dq_entry;
51362306a36Sopenharmony_ci	struct list_head *list_item;
51462306a36Sopenharmony_ci	int rv;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	/* Dequeue the next datagram entry. */
51762306a36Sopenharmony_ci	spin_lock(&context->lock);
51862306a36Sopenharmony_ci	if (context->pending_datagrams == 0) {
51962306a36Sopenharmony_ci		ctx_clear_notify_call(context);
52062306a36Sopenharmony_ci		spin_unlock(&context->lock);
52162306a36Sopenharmony_ci		pr_devel("No datagrams pending\n");
52262306a36Sopenharmony_ci		return VMCI_ERROR_NO_MORE_DATAGRAMS;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	list_item = context->datagram_queue.next;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	dq_entry =
52862306a36Sopenharmony_ci	    list_entry(list_item, struct vmci_datagram_queue_entry, list_item);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* Check size of caller's buffer. */
53162306a36Sopenharmony_ci	if (*max_size < dq_entry->dg_size) {
53262306a36Sopenharmony_ci		*max_size = dq_entry->dg_size;
53362306a36Sopenharmony_ci		spin_unlock(&context->lock);
53462306a36Sopenharmony_ci		pr_devel("Caller's buffer should be at least (size=%u bytes)\n",
53562306a36Sopenharmony_ci			 (u32) *max_size);
53662306a36Sopenharmony_ci		return VMCI_ERROR_NO_MEM;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	list_del(list_item);
54062306a36Sopenharmony_ci	context->pending_datagrams--;
54162306a36Sopenharmony_ci	context->datagram_queue_size -= dq_entry->dg_size;
54262306a36Sopenharmony_ci	if (context->pending_datagrams == 0) {
54362306a36Sopenharmony_ci		ctx_clear_notify_call(context);
54462306a36Sopenharmony_ci		rv = VMCI_SUCCESS;
54562306a36Sopenharmony_ci	} else {
54662306a36Sopenharmony_ci		/*
54762306a36Sopenharmony_ci		 * Return the size of the next datagram.
54862306a36Sopenharmony_ci		 */
54962306a36Sopenharmony_ci		struct vmci_datagram_queue_entry *next_entry;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		list_item = context->datagram_queue.next;
55262306a36Sopenharmony_ci		next_entry =
55362306a36Sopenharmony_ci		    list_entry(list_item, struct vmci_datagram_queue_entry,
55462306a36Sopenharmony_ci			       list_item);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		/*
55762306a36Sopenharmony_ci		 * The following size_t -> int truncation is fine as
55862306a36Sopenharmony_ci		 * the maximum size of a (routable) datagram is 68KB.
55962306a36Sopenharmony_ci		 */
56062306a36Sopenharmony_ci		rv = (int)next_entry->dg_size;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci	spin_unlock(&context->lock);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* Caller must free datagram. */
56562306a36Sopenharmony_ci	*dg = dq_entry->dg;
56662306a36Sopenharmony_ci	dq_entry->dg = NULL;
56762306a36Sopenharmony_ci	kfree(dq_entry);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	return rv;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci/*
57362306a36Sopenharmony_ci * Reverts actions set up by vmci_setup_notify().  Unmaps and unlocks the
57462306a36Sopenharmony_ci * page mapped/locked by vmci_setup_notify().
57562306a36Sopenharmony_ci */
57662306a36Sopenharmony_civoid vmci_ctx_unset_notify(struct vmci_ctx *context)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct page *notify_page;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	spin_lock(&context->lock);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	notify_page = context->notify_page;
58362306a36Sopenharmony_ci	context->notify = &ctx_dummy_notify;
58462306a36Sopenharmony_ci	context->notify_page = NULL;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	spin_unlock(&context->lock);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (notify_page) {
58962306a36Sopenharmony_ci		kunmap(notify_page);
59062306a36Sopenharmony_ci		put_page(notify_page);
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci/*
59562306a36Sopenharmony_ci * Add remote_cid to list of contexts current contexts wants
59662306a36Sopenharmony_ci * notifications from/about.
59762306a36Sopenharmony_ci */
59862306a36Sopenharmony_ciint vmci_ctx_add_notification(u32 context_id, u32 remote_cid)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct vmci_ctx *context;
60162306a36Sopenharmony_ci	struct vmci_handle_list *notifier, *n;
60262306a36Sopenharmony_ci	int result;
60362306a36Sopenharmony_ci	bool exists = false;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	context = vmci_ctx_get(context_id);
60662306a36Sopenharmony_ci	if (!context)
60762306a36Sopenharmony_ci		return VMCI_ERROR_NOT_FOUND;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (VMCI_CONTEXT_IS_VM(context_id) && VMCI_CONTEXT_IS_VM(remote_cid)) {
61062306a36Sopenharmony_ci		pr_devel("Context removed notifications for other VMs not supported (src=0x%x, remote=0x%x)\n",
61162306a36Sopenharmony_ci			 context_id, remote_cid);
61262306a36Sopenharmony_ci		result = VMCI_ERROR_DST_UNREACHABLE;
61362306a36Sopenharmony_ci		goto out;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (context->priv_flags & VMCI_PRIVILEGE_FLAG_RESTRICTED) {
61762306a36Sopenharmony_ci		result = VMCI_ERROR_NO_ACCESS;
61862306a36Sopenharmony_ci		goto out;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	notifier = kmalloc(sizeof(struct vmci_handle_list), GFP_KERNEL);
62262306a36Sopenharmony_ci	if (!notifier) {
62362306a36Sopenharmony_ci		result = VMCI_ERROR_NO_MEM;
62462306a36Sopenharmony_ci		goto out;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	INIT_LIST_HEAD(&notifier->node);
62862306a36Sopenharmony_ci	notifier->handle = vmci_make_handle(remote_cid, VMCI_EVENT_HANDLER);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	spin_lock(&context->lock);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	if (context->n_notifiers < VMCI_MAX_CONTEXTS) {
63362306a36Sopenharmony_ci		list_for_each_entry(n, &context->notifier_list, node) {
63462306a36Sopenharmony_ci			if (vmci_handle_is_equal(n->handle, notifier->handle)) {
63562306a36Sopenharmony_ci				exists = true;
63662306a36Sopenharmony_ci				break;
63762306a36Sopenharmony_ci			}
63862306a36Sopenharmony_ci		}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		if (exists) {
64162306a36Sopenharmony_ci			kfree(notifier);
64262306a36Sopenharmony_ci			result = VMCI_ERROR_ALREADY_EXISTS;
64362306a36Sopenharmony_ci		} else {
64462306a36Sopenharmony_ci			list_add_tail_rcu(&notifier->node,
64562306a36Sopenharmony_ci					  &context->notifier_list);
64662306a36Sopenharmony_ci			context->n_notifiers++;
64762306a36Sopenharmony_ci			result = VMCI_SUCCESS;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci	} else {
65062306a36Sopenharmony_ci		kfree(notifier);
65162306a36Sopenharmony_ci		result = VMCI_ERROR_NO_MEM;
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	spin_unlock(&context->lock);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci out:
65762306a36Sopenharmony_ci	vmci_ctx_put(context);
65862306a36Sopenharmony_ci	return result;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci/*
66262306a36Sopenharmony_ci * Remove remote_cid from current context's list of contexts it is
66362306a36Sopenharmony_ci * interested in getting notifications from/about.
66462306a36Sopenharmony_ci */
66562306a36Sopenharmony_ciint vmci_ctx_remove_notification(u32 context_id, u32 remote_cid)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	struct vmci_ctx *context;
66862306a36Sopenharmony_ci	struct vmci_handle_list *notifier = NULL, *iter, *tmp;
66962306a36Sopenharmony_ci	struct vmci_handle handle;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	context = vmci_ctx_get(context_id);
67262306a36Sopenharmony_ci	if (!context)
67362306a36Sopenharmony_ci		return VMCI_ERROR_NOT_FOUND;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	handle = vmci_make_handle(remote_cid, VMCI_EVENT_HANDLER);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	spin_lock(&context->lock);
67862306a36Sopenharmony_ci	list_for_each_entry_safe(iter, tmp,
67962306a36Sopenharmony_ci				 &context->notifier_list, node) {
68062306a36Sopenharmony_ci		if (vmci_handle_is_equal(iter->handle, handle)) {
68162306a36Sopenharmony_ci			list_del_rcu(&iter->node);
68262306a36Sopenharmony_ci			context->n_notifiers--;
68362306a36Sopenharmony_ci			notifier = iter;
68462306a36Sopenharmony_ci			break;
68562306a36Sopenharmony_ci		}
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci	spin_unlock(&context->lock);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (notifier)
69062306a36Sopenharmony_ci		kvfree_rcu_mightsleep(notifier);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	vmci_ctx_put(context);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	return notifier ? VMCI_SUCCESS : VMCI_ERROR_NOT_FOUND;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic int vmci_ctx_get_chkpt_notifiers(struct vmci_ctx *context,
69862306a36Sopenharmony_ci					u32 *buf_size, void **pbuf)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	u32 *notifiers;
70162306a36Sopenharmony_ci	size_t data_size;
70262306a36Sopenharmony_ci	struct vmci_handle_list *entry;
70362306a36Sopenharmony_ci	int i = 0;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (context->n_notifiers == 0) {
70662306a36Sopenharmony_ci		*buf_size = 0;
70762306a36Sopenharmony_ci		*pbuf = NULL;
70862306a36Sopenharmony_ci		return VMCI_SUCCESS;
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	data_size = context->n_notifiers * sizeof(*notifiers);
71262306a36Sopenharmony_ci	if (*buf_size < data_size) {
71362306a36Sopenharmony_ci		*buf_size = data_size;
71462306a36Sopenharmony_ci		return VMCI_ERROR_MORE_DATA;
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	notifiers = kmalloc(data_size, GFP_ATOMIC); /* FIXME: want GFP_KERNEL */
71862306a36Sopenharmony_ci	if (!notifiers)
71962306a36Sopenharmony_ci		return VMCI_ERROR_NO_MEM;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	list_for_each_entry(entry, &context->notifier_list, node)
72262306a36Sopenharmony_ci		notifiers[i++] = entry->handle.context;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	*buf_size = data_size;
72562306a36Sopenharmony_ci	*pbuf = notifiers;
72662306a36Sopenharmony_ci	return VMCI_SUCCESS;
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_cistatic int vmci_ctx_get_chkpt_doorbells(struct vmci_ctx *context,
73062306a36Sopenharmony_ci					u32 *buf_size, void **pbuf)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	struct dbell_cpt_state *dbells;
73362306a36Sopenharmony_ci	u32 i, n_doorbells;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	n_doorbells = vmci_handle_arr_get_size(context->doorbell_array);
73662306a36Sopenharmony_ci	if (n_doorbells > 0) {
73762306a36Sopenharmony_ci		size_t data_size = n_doorbells * sizeof(*dbells);
73862306a36Sopenharmony_ci		if (*buf_size < data_size) {
73962306a36Sopenharmony_ci			*buf_size = data_size;
74062306a36Sopenharmony_ci			return VMCI_ERROR_MORE_DATA;
74162306a36Sopenharmony_ci		}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		dbells = kzalloc(data_size, GFP_ATOMIC);
74462306a36Sopenharmony_ci		if (!dbells)
74562306a36Sopenharmony_ci			return VMCI_ERROR_NO_MEM;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		for (i = 0; i < n_doorbells; i++)
74862306a36Sopenharmony_ci			dbells[i].handle = vmci_handle_arr_get_entry(
74962306a36Sopenharmony_ci						context->doorbell_array, i);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci		*buf_size = data_size;
75262306a36Sopenharmony_ci		*pbuf = dbells;
75362306a36Sopenharmony_ci	} else {
75462306a36Sopenharmony_ci		*buf_size = 0;
75562306a36Sopenharmony_ci		*pbuf = NULL;
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	return VMCI_SUCCESS;
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci/*
76262306a36Sopenharmony_ci * Get current context's checkpoint state of given type.
76362306a36Sopenharmony_ci */
76462306a36Sopenharmony_ciint vmci_ctx_get_chkpt_state(u32 context_id,
76562306a36Sopenharmony_ci			     u32 cpt_type,
76662306a36Sopenharmony_ci			     u32 *buf_size,
76762306a36Sopenharmony_ci			     void **pbuf)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	struct vmci_ctx *context;
77062306a36Sopenharmony_ci	int result;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	context = vmci_ctx_get(context_id);
77362306a36Sopenharmony_ci	if (!context)
77462306a36Sopenharmony_ci		return VMCI_ERROR_NOT_FOUND;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	spin_lock(&context->lock);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	switch (cpt_type) {
77962306a36Sopenharmony_ci	case VMCI_NOTIFICATION_CPT_STATE:
78062306a36Sopenharmony_ci		result = vmci_ctx_get_chkpt_notifiers(context, buf_size, pbuf);
78162306a36Sopenharmony_ci		break;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	case VMCI_WELLKNOWN_CPT_STATE:
78462306a36Sopenharmony_ci		/*
78562306a36Sopenharmony_ci		 * For compatibility with VMX'en with VM to VM communication, we
78662306a36Sopenharmony_ci		 * always return zero wellknown handles.
78762306a36Sopenharmony_ci		 */
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		*buf_size = 0;
79062306a36Sopenharmony_ci		*pbuf = NULL;
79162306a36Sopenharmony_ci		result = VMCI_SUCCESS;
79262306a36Sopenharmony_ci		break;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	case VMCI_DOORBELL_CPT_STATE:
79562306a36Sopenharmony_ci		result = vmci_ctx_get_chkpt_doorbells(context, buf_size, pbuf);
79662306a36Sopenharmony_ci		break;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	default:
79962306a36Sopenharmony_ci		pr_devel("Invalid cpt state (type=%d)\n", cpt_type);
80062306a36Sopenharmony_ci		result = VMCI_ERROR_INVALID_ARGS;
80162306a36Sopenharmony_ci		break;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	spin_unlock(&context->lock);
80562306a36Sopenharmony_ci	vmci_ctx_put(context);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	return result;
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci/*
81162306a36Sopenharmony_ci * Set current context's checkpoint state of given type.
81262306a36Sopenharmony_ci */
81362306a36Sopenharmony_ciint vmci_ctx_set_chkpt_state(u32 context_id,
81462306a36Sopenharmony_ci			     u32 cpt_type,
81562306a36Sopenharmony_ci			     u32 buf_size,
81662306a36Sopenharmony_ci			     void *cpt_buf)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	u32 i;
81962306a36Sopenharmony_ci	u32 current_id;
82062306a36Sopenharmony_ci	int result = VMCI_SUCCESS;
82162306a36Sopenharmony_ci	u32 num_ids = buf_size / sizeof(u32);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (cpt_type == VMCI_WELLKNOWN_CPT_STATE && num_ids > 0) {
82462306a36Sopenharmony_ci		/*
82562306a36Sopenharmony_ci		 * We would end up here if VMX with VM to VM communication
82662306a36Sopenharmony_ci		 * attempts to restore a checkpoint with wellknown handles.
82762306a36Sopenharmony_ci		 */
82862306a36Sopenharmony_ci		pr_warn("Attempt to restore checkpoint with obsolete wellknown handles\n");
82962306a36Sopenharmony_ci		return VMCI_ERROR_OBSOLETE;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (cpt_type != VMCI_NOTIFICATION_CPT_STATE) {
83362306a36Sopenharmony_ci		pr_devel("Invalid cpt state (type=%d)\n", cpt_type);
83462306a36Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	for (i = 0; i < num_ids && result == VMCI_SUCCESS; i++) {
83862306a36Sopenharmony_ci		current_id = ((u32 *)cpt_buf)[i];
83962306a36Sopenharmony_ci		result = vmci_ctx_add_notification(context_id, current_id);
84062306a36Sopenharmony_ci		if (result != VMCI_SUCCESS)
84162306a36Sopenharmony_ci			break;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci	if (result != VMCI_SUCCESS)
84462306a36Sopenharmony_ci		pr_devel("Failed to set cpt state (type=%d) (error=%d)\n",
84562306a36Sopenharmony_ci			 cpt_type, result);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	return result;
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci/*
85162306a36Sopenharmony_ci * Retrieves the specified context's pending notifications in the
85262306a36Sopenharmony_ci * form of a handle array. The handle arrays returned are the
85362306a36Sopenharmony_ci * actual data - not a copy and should not be modified by the
85462306a36Sopenharmony_ci * caller. They must be released using
85562306a36Sopenharmony_ci * vmci_ctx_rcv_notifications_release.
85662306a36Sopenharmony_ci */
85762306a36Sopenharmony_ciint vmci_ctx_rcv_notifications_get(u32 context_id,
85862306a36Sopenharmony_ci				   struct vmci_handle_arr **db_handle_array,
85962306a36Sopenharmony_ci				   struct vmci_handle_arr **qp_handle_array)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct vmci_ctx *context;
86262306a36Sopenharmony_ci	int result = VMCI_SUCCESS;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	context = vmci_ctx_get(context_id);
86562306a36Sopenharmony_ci	if (context == NULL)
86662306a36Sopenharmony_ci		return VMCI_ERROR_NOT_FOUND;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	spin_lock(&context->lock);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	*db_handle_array = context->pending_doorbell_array;
87162306a36Sopenharmony_ci	context->pending_doorbell_array =
87262306a36Sopenharmony_ci		vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT);
87362306a36Sopenharmony_ci	if (!context->pending_doorbell_array) {
87462306a36Sopenharmony_ci		context->pending_doorbell_array = *db_handle_array;
87562306a36Sopenharmony_ci		*db_handle_array = NULL;
87662306a36Sopenharmony_ci		result = VMCI_ERROR_NO_MEM;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci	*qp_handle_array = NULL;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	spin_unlock(&context->lock);
88162306a36Sopenharmony_ci	vmci_ctx_put(context);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	return result;
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci/*
88762306a36Sopenharmony_ci * Releases handle arrays with pending notifications previously
88862306a36Sopenharmony_ci * retrieved using vmci_ctx_rcv_notifications_get. If the
88962306a36Sopenharmony_ci * notifications were not successfully handed over to the guest,
89062306a36Sopenharmony_ci * success must be false.
89162306a36Sopenharmony_ci */
89262306a36Sopenharmony_civoid vmci_ctx_rcv_notifications_release(u32 context_id,
89362306a36Sopenharmony_ci					struct vmci_handle_arr *db_handle_array,
89462306a36Sopenharmony_ci					struct vmci_handle_arr *qp_handle_array,
89562306a36Sopenharmony_ci					bool success)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	struct vmci_ctx *context = vmci_ctx_get(context_id);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	spin_lock(&context->lock);
90062306a36Sopenharmony_ci	if (!success) {
90162306a36Sopenharmony_ci		struct vmci_handle handle;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci		/*
90462306a36Sopenharmony_ci		 * New notifications may have been added while we were not
90562306a36Sopenharmony_ci		 * holding the context lock, so we transfer any new pending
90662306a36Sopenharmony_ci		 * doorbell notifications to the old array, and reinstate the
90762306a36Sopenharmony_ci		 * old array.
90862306a36Sopenharmony_ci		 */
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		handle = vmci_handle_arr_remove_tail(
91162306a36Sopenharmony_ci					context->pending_doorbell_array);
91262306a36Sopenharmony_ci		while (!vmci_handle_is_invalid(handle)) {
91362306a36Sopenharmony_ci			if (!vmci_handle_arr_has_entry(db_handle_array,
91462306a36Sopenharmony_ci						       handle)) {
91562306a36Sopenharmony_ci				vmci_handle_arr_append_entry(
91662306a36Sopenharmony_ci						&db_handle_array, handle);
91762306a36Sopenharmony_ci			}
91862306a36Sopenharmony_ci			handle = vmci_handle_arr_remove_tail(
91962306a36Sopenharmony_ci					context->pending_doorbell_array);
92062306a36Sopenharmony_ci		}
92162306a36Sopenharmony_ci		vmci_handle_arr_destroy(context->pending_doorbell_array);
92262306a36Sopenharmony_ci		context->pending_doorbell_array = db_handle_array;
92362306a36Sopenharmony_ci		db_handle_array = NULL;
92462306a36Sopenharmony_ci	} else {
92562306a36Sopenharmony_ci		ctx_clear_notify_call(context);
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci	spin_unlock(&context->lock);
92862306a36Sopenharmony_ci	vmci_ctx_put(context);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (db_handle_array)
93162306a36Sopenharmony_ci		vmci_handle_arr_destroy(db_handle_array);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (qp_handle_array)
93462306a36Sopenharmony_ci		vmci_handle_arr_destroy(qp_handle_array);
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci/*
93862306a36Sopenharmony_ci * Registers that a new doorbell handle has been allocated by the
93962306a36Sopenharmony_ci * context. Only doorbell handles registered can be notified.
94062306a36Sopenharmony_ci */
94162306a36Sopenharmony_ciint vmci_ctx_dbell_create(u32 context_id, struct vmci_handle handle)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	struct vmci_ctx *context;
94462306a36Sopenharmony_ci	int result;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if (context_id == VMCI_INVALID_ID || vmci_handle_is_invalid(handle))
94762306a36Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	context = vmci_ctx_get(context_id);
95062306a36Sopenharmony_ci	if (context == NULL)
95162306a36Sopenharmony_ci		return VMCI_ERROR_NOT_FOUND;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	spin_lock(&context->lock);
95462306a36Sopenharmony_ci	if (!vmci_handle_arr_has_entry(context->doorbell_array, handle))
95562306a36Sopenharmony_ci		result = vmci_handle_arr_append_entry(&context->doorbell_array,
95662306a36Sopenharmony_ci						      handle);
95762306a36Sopenharmony_ci	else
95862306a36Sopenharmony_ci		result = VMCI_ERROR_DUPLICATE_ENTRY;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	spin_unlock(&context->lock);
96162306a36Sopenharmony_ci	vmci_ctx_put(context);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	return result;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci/*
96762306a36Sopenharmony_ci * Unregisters a doorbell handle that was previously registered
96862306a36Sopenharmony_ci * with vmci_ctx_dbell_create.
96962306a36Sopenharmony_ci */
97062306a36Sopenharmony_ciint vmci_ctx_dbell_destroy(u32 context_id, struct vmci_handle handle)
97162306a36Sopenharmony_ci{
97262306a36Sopenharmony_ci	struct vmci_ctx *context;
97362306a36Sopenharmony_ci	struct vmci_handle removed_handle;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (context_id == VMCI_INVALID_ID || vmci_handle_is_invalid(handle))
97662306a36Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	context = vmci_ctx_get(context_id);
97962306a36Sopenharmony_ci	if (context == NULL)
98062306a36Sopenharmony_ci		return VMCI_ERROR_NOT_FOUND;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	spin_lock(&context->lock);
98362306a36Sopenharmony_ci	removed_handle =
98462306a36Sopenharmony_ci	    vmci_handle_arr_remove_entry(context->doorbell_array, handle);
98562306a36Sopenharmony_ci	vmci_handle_arr_remove_entry(context->pending_doorbell_array, handle);
98662306a36Sopenharmony_ci	spin_unlock(&context->lock);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	vmci_ctx_put(context);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	return vmci_handle_is_invalid(removed_handle) ?
99162306a36Sopenharmony_ci	    VMCI_ERROR_NOT_FOUND : VMCI_SUCCESS;
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci/*
99562306a36Sopenharmony_ci * Unregisters all doorbell handles that were previously
99662306a36Sopenharmony_ci * registered with vmci_ctx_dbell_create.
99762306a36Sopenharmony_ci */
99862306a36Sopenharmony_ciint vmci_ctx_dbell_destroy_all(u32 context_id)
99962306a36Sopenharmony_ci{
100062306a36Sopenharmony_ci	struct vmci_ctx *context;
100162306a36Sopenharmony_ci	struct vmci_handle handle;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	if (context_id == VMCI_INVALID_ID)
100462306a36Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	context = vmci_ctx_get(context_id);
100762306a36Sopenharmony_ci	if (context == NULL)
100862306a36Sopenharmony_ci		return VMCI_ERROR_NOT_FOUND;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	spin_lock(&context->lock);
101162306a36Sopenharmony_ci	do {
101262306a36Sopenharmony_ci		struct vmci_handle_arr *arr = context->doorbell_array;
101362306a36Sopenharmony_ci		handle = vmci_handle_arr_remove_tail(arr);
101462306a36Sopenharmony_ci	} while (!vmci_handle_is_invalid(handle));
101562306a36Sopenharmony_ci	do {
101662306a36Sopenharmony_ci		struct vmci_handle_arr *arr = context->pending_doorbell_array;
101762306a36Sopenharmony_ci		handle = vmci_handle_arr_remove_tail(arr);
101862306a36Sopenharmony_ci	} while (!vmci_handle_is_invalid(handle));
101962306a36Sopenharmony_ci	spin_unlock(&context->lock);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	vmci_ctx_put(context);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	return VMCI_SUCCESS;
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci/*
102762306a36Sopenharmony_ci * Registers a notification of a doorbell handle initiated by the
102862306a36Sopenharmony_ci * specified source context. The notification of doorbells are
102962306a36Sopenharmony_ci * subject to the same isolation rules as datagram delivery. To
103062306a36Sopenharmony_ci * allow host side senders of notifications a finer granularity
103162306a36Sopenharmony_ci * of sender rights than those assigned to the sending context
103262306a36Sopenharmony_ci * itself, the host context is required to specify a different
103362306a36Sopenharmony_ci * set of privilege flags that will override the privileges of
103462306a36Sopenharmony_ci * the source context.
103562306a36Sopenharmony_ci */
103662306a36Sopenharmony_ciint vmci_ctx_notify_dbell(u32 src_cid,
103762306a36Sopenharmony_ci			  struct vmci_handle handle,
103862306a36Sopenharmony_ci			  u32 src_priv_flags)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	struct vmci_ctx *dst_context;
104162306a36Sopenharmony_ci	int result;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (vmci_handle_is_invalid(handle))
104462306a36Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	/* Get the target VM's VMCI context. */
104762306a36Sopenharmony_ci	dst_context = vmci_ctx_get(handle.context);
104862306a36Sopenharmony_ci	if (!dst_context) {
104962306a36Sopenharmony_ci		pr_devel("Invalid context (ID=0x%x)\n", handle.context);
105062306a36Sopenharmony_ci		return VMCI_ERROR_NOT_FOUND;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	if (src_cid != handle.context) {
105462306a36Sopenharmony_ci		u32 dst_priv_flags;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci		if (VMCI_CONTEXT_IS_VM(src_cid) &&
105762306a36Sopenharmony_ci		    VMCI_CONTEXT_IS_VM(handle.context)) {
105862306a36Sopenharmony_ci			pr_devel("Doorbell notification from VM to VM not supported (src=0x%x, dst=0x%x)\n",
105962306a36Sopenharmony_ci				 src_cid, handle.context);
106062306a36Sopenharmony_ci			result = VMCI_ERROR_DST_UNREACHABLE;
106162306a36Sopenharmony_ci			goto out;
106262306a36Sopenharmony_ci		}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci		result = vmci_dbell_get_priv_flags(handle, &dst_priv_flags);
106562306a36Sopenharmony_ci		if (result < VMCI_SUCCESS) {
106662306a36Sopenharmony_ci			pr_warn("Failed to get privilege flags for destination (handle=0x%x:0x%x)\n",
106762306a36Sopenharmony_ci				handle.context, handle.resource);
106862306a36Sopenharmony_ci			goto out;
106962306a36Sopenharmony_ci		}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		if (src_cid != VMCI_HOST_CONTEXT_ID ||
107262306a36Sopenharmony_ci		    src_priv_flags == VMCI_NO_PRIVILEGE_FLAGS) {
107362306a36Sopenharmony_ci			src_priv_flags = vmci_context_get_priv_flags(src_cid);
107462306a36Sopenharmony_ci		}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		if (vmci_deny_interaction(src_priv_flags, dst_priv_flags)) {
107762306a36Sopenharmony_ci			result = VMCI_ERROR_NO_ACCESS;
107862306a36Sopenharmony_ci			goto out;
107962306a36Sopenharmony_ci		}
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	if (handle.context == VMCI_HOST_CONTEXT_ID) {
108362306a36Sopenharmony_ci		result = vmci_dbell_host_context_notify(src_cid, handle);
108462306a36Sopenharmony_ci	} else {
108562306a36Sopenharmony_ci		spin_lock(&dst_context->lock);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci		if (!vmci_handle_arr_has_entry(dst_context->doorbell_array,
108862306a36Sopenharmony_ci					       handle)) {
108962306a36Sopenharmony_ci			result = VMCI_ERROR_NOT_FOUND;
109062306a36Sopenharmony_ci		} else {
109162306a36Sopenharmony_ci			if (!vmci_handle_arr_has_entry(
109262306a36Sopenharmony_ci					dst_context->pending_doorbell_array,
109362306a36Sopenharmony_ci					handle)) {
109462306a36Sopenharmony_ci				result = vmci_handle_arr_append_entry(
109562306a36Sopenharmony_ci					&dst_context->pending_doorbell_array,
109662306a36Sopenharmony_ci					handle);
109762306a36Sopenharmony_ci				if (result == VMCI_SUCCESS) {
109862306a36Sopenharmony_ci					ctx_signal_notify(dst_context);
109962306a36Sopenharmony_ci					wake_up(&dst_context->host_context.wait_queue);
110062306a36Sopenharmony_ci				}
110162306a36Sopenharmony_ci			} else {
110262306a36Sopenharmony_ci				result = VMCI_SUCCESS;
110362306a36Sopenharmony_ci			}
110462306a36Sopenharmony_ci		}
110562306a36Sopenharmony_ci		spin_unlock(&dst_context->lock);
110662306a36Sopenharmony_ci	}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci out:
110962306a36Sopenharmony_ci	vmci_ctx_put(dst_context);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	return result;
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cibool vmci_ctx_supports_host_qp(struct vmci_ctx *context)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	return context && context->user_version >= VMCI_VERSION_HOSTQP;
111762306a36Sopenharmony_ci}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci/*
112062306a36Sopenharmony_ci * Registers that a new queue pair handle has been allocated by
112162306a36Sopenharmony_ci * the context.
112262306a36Sopenharmony_ci */
112362306a36Sopenharmony_ciint vmci_ctx_qp_create(struct vmci_ctx *context, struct vmci_handle handle)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	int result;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	if (context == NULL || vmci_handle_is_invalid(handle))
112862306a36Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	if (!vmci_handle_arr_has_entry(context->queue_pair_array, handle))
113162306a36Sopenharmony_ci		result = vmci_handle_arr_append_entry(
113262306a36Sopenharmony_ci			&context->queue_pair_array, handle);
113362306a36Sopenharmony_ci	else
113462306a36Sopenharmony_ci		result = VMCI_ERROR_DUPLICATE_ENTRY;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	return result;
113762306a36Sopenharmony_ci}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci/*
114062306a36Sopenharmony_ci * Unregisters a queue pair handle that was previously registered
114162306a36Sopenharmony_ci * with vmci_ctx_qp_create.
114262306a36Sopenharmony_ci */
114362306a36Sopenharmony_ciint vmci_ctx_qp_destroy(struct vmci_ctx *context, struct vmci_handle handle)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	struct vmci_handle hndl;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (context == NULL || vmci_handle_is_invalid(handle))
114862306a36Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	hndl = vmci_handle_arr_remove_entry(context->queue_pair_array, handle);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	return vmci_handle_is_invalid(hndl) ?
115362306a36Sopenharmony_ci		VMCI_ERROR_NOT_FOUND : VMCI_SUCCESS;
115462306a36Sopenharmony_ci}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci/*
115762306a36Sopenharmony_ci * Determines whether a given queue pair handle is registered
115862306a36Sopenharmony_ci * with the given context.
115962306a36Sopenharmony_ci */
116062306a36Sopenharmony_cibool vmci_ctx_qp_exists(struct vmci_ctx *context, struct vmci_handle handle)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	if (context == NULL || vmci_handle_is_invalid(handle))
116362306a36Sopenharmony_ci		return false;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	return vmci_handle_arr_has_entry(context->queue_pair_array, handle);
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci/*
116962306a36Sopenharmony_ci * vmci_context_get_priv_flags() - Retrieve privilege flags.
117062306a36Sopenharmony_ci * @context_id: The context ID of the VMCI context.
117162306a36Sopenharmony_ci *
117262306a36Sopenharmony_ci * Retrieves privilege flags of the given VMCI context ID.
117362306a36Sopenharmony_ci */
117462306a36Sopenharmony_ciu32 vmci_context_get_priv_flags(u32 context_id)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	if (vmci_host_code_active()) {
117762306a36Sopenharmony_ci		u32 flags;
117862306a36Sopenharmony_ci		struct vmci_ctx *context;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci		context = vmci_ctx_get(context_id);
118162306a36Sopenharmony_ci		if (!context)
118262306a36Sopenharmony_ci			return VMCI_LEAST_PRIVILEGE_FLAGS;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci		flags = context->priv_flags;
118562306a36Sopenharmony_ci		vmci_ctx_put(context);
118662306a36Sopenharmony_ci		return flags;
118762306a36Sopenharmony_ci	}
118862306a36Sopenharmony_ci	return VMCI_NO_PRIVILEGE_FLAGS;
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_context_get_priv_flags);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci/*
119362306a36Sopenharmony_ci * vmci_is_context_owner() - Determimnes if user is the context owner
119462306a36Sopenharmony_ci * @context_id: The context ID of the VMCI context.
119562306a36Sopenharmony_ci * @uid:        The host user id (real kernel value).
119662306a36Sopenharmony_ci *
119762306a36Sopenharmony_ci * Determines whether a given UID is the owner of given VMCI context.
119862306a36Sopenharmony_ci */
119962306a36Sopenharmony_cibool vmci_is_context_owner(u32 context_id, kuid_t uid)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	bool is_owner = false;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	if (vmci_host_code_active()) {
120462306a36Sopenharmony_ci		struct vmci_ctx *context = vmci_ctx_get(context_id);
120562306a36Sopenharmony_ci		if (context) {
120662306a36Sopenharmony_ci			if (context->cred)
120762306a36Sopenharmony_ci				is_owner = uid_eq(context->cred->uid, uid);
120862306a36Sopenharmony_ci			vmci_ctx_put(context);
120962306a36Sopenharmony_ci		}
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	return is_owner;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_is_context_owner);
1215