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