223 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * VMware VMCI Driver4 *5 * Copyright (C) 2012 VMware, Inc. All rights reserved.6 */7 8#include <linux/vmw_vmci_defs.h>9#include <linux/hash.h>10#include <linux/types.h>11#include <linux/rculist.h>12#include <linux/completion.h>13 14#include "vmci_resource.h"15#include "vmci_driver.h"16 17 18#define VMCI_RESOURCE_HASH_BITS 719#define VMCI_RESOURCE_HASH_BUCKETS (1 << VMCI_RESOURCE_HASH_BITS)20 21struct vmci_hash_table {22 spinlock_t lock;23 struct hlist_head entries[VMCI_RESOURCE_HASH_BUCKETS];24};25 26static struct vmci_hash_table vmci_resource_table = {27 .lock = __SPIN_LOCK_UNLOCKED(vmci_resource_table.lock),28};29 30static unsigned int vmci_resource_hash(struct vmci_handle handle)31{32 return hash_32(handle.resource, VMCI_RESOURCE_HASH_BITS);33}34 35/*36 * Gets a resource (if one exists) matching given handle from the hash table.37 */38static struct vmci_resource *vmci_resource_lookup(struct vmci_handle handle,39 enum vmci_resource_type type)40{41 struct vmci_resource *r, *resource = NULL;42 unsigned int idx = vmci_resource_hash(handle);43 44 rcu_read_lock();45 hlist_for_each_entry_rcu(r,46 &vmci_resource_table.entries[idx], node) {47 u32 cid = r->handle.context;48 u32 rid = r->handle.resource;49 50 if (r->type == type &&51 rid == handle.resource &&52 (cid == handle.context || cid == VMCI_INVALID_ID ||53 handle.context == VMCI_INVALID_ID)) {54 resource = r;55 break;56 }57 }58 rcu_read_unlock();59 60 return resource;61}62 63/*64 * Find an unused resource ID and return it. The first65 * VMCI_RESERVED_RESOURCE_ID_MAX are reserved so we start from66 * its value + 1.67 * Returns VMCI resource id on success, VMCI_INVALID_ID on failure.68 */69static u32 vmci_resource_find_id(u32 context_id,70 enum vmci_resource_type resource_type)71{72 static u32 resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;73 u32 old_rid = resource_id;74 u32 current_rid;75 76 /*77 * Generate a unique resource ID. Keep on trying until we wrap around78 * in the RID space.79 */80 do {81 struct vmci_handle handle;82 83 current_rid = resource_id;84 resource_id++;85 if (unlikely(resource_id == VMCI_INVALID_ID)) {86 /* Skip the reserved rids. */87 resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;88 }89 90 handle = vmci_make_handle(context_id, current_rid);91 if (!vmci_resource_lookup(handle, resource_type))92 return current_rid;93 } while (resource_id != old_rid);94 95 return VMCI_INVALID_ID;96}97 98 99int vmci_resource_add(struct vmci_resource *resource,100 enum vmci_resource_type resource_type,101 struct vmci_handle handle)102 103{104 unsigned int idx;105 int result;106 107 spin_lock(&vmci_resource_table.lock);108 109 if (handle.resource == VMCI_INVALID_ID) {110 handle.resource = vmci_resource_find_id(handle.context,111 resource_type);112 if (handle.resource == VMCI_INVALID_ID) {113 result = VMCI_ERROR_NO_HANDLE;114 goto out;115 }116 } else if (vmci_resource_lookup(handle, resource_type)) {117 result = VMCI_ERROR_ALREADY_EXISTS;118 goto out;119 }120 121 resource->handle = handle;122 resource->type = resource_type;123 INIT_HLIST_NODE(&resource->node);124 kref_init(&resource->kref);125 init_completion(&resource->done);126 127 idx = vmci_resource_hash(resource->handle);128 hlist_add_head_rcu(&resource->node, &vmci_resource_table.entries[idx]);129 130 result = VMCI_SUCCESS;131 132out:133 spin_unlock(&vmci_resource_table.lock);134 return result;135}136 137void vmci_resource_remove(struct vmci_resource *resource)138{139 struct vmci_handle handle = resource->handle;140 unsigned int idx = vmci_resource_hash(handle);141 struct vmci_resource *r;142 143 /* Remove resource from hash table. */144 spin_lock(&vmci_resource_table.lock);145 146 hlist_for_each_entry(r, &vmci_resource_table.entries[idx], node) {147 if (vmci_handle_is_equal(r->handle, resource->handle) &&148 resource->type == r->type) {149 hlist_del_init_rcu(&r->node);150 break;151 }152 }153 154 spin_unlock(&vmci_resource_table.lock);155 synchronize_rcu();156 157 vmci_resource_put(resource);158 wait_for_completion(&resource->done);159}160 161struct vmci_resource *162vmci_resource_by_handle(struct vmci_handle resource_handle,163 enum vmci_resource_type resource_type)164{165 struct vmci_resource *r, *resource = NULL;166 167 rcu_read_lock();168 169 r = vmci_resource_lookup(resource_handle, resource_type);170 if (r &&171 (resource_type == r->type ||172 resource_type == VMCI_RESOURCE_TYPE_ANY)) {173 resource = vmci_resource_get(r);174 }175 176 rcu_read_unlock();177 178 return resource;179}180 181/*182 * Get a reference to given resource.183 */184struct vmci_resource *vmci_resource_get(struct vmci_resource *resource)185{186 kref_get(&resource->kref);187 188 return resource;189}190 191static void vmci_release_resource(struct kref *kref)192{193 struct vmci_resource *resource =194 container_of(kref, struct vmci_resource, kref);195 196 /* Verify the resource has been unlinked from hash table */197 WARN_ON(!hlist_unhashed(&resource->node));198 199 /* Signal that container of this resource can now be destroyed */200 complete(&resource->done);201}202 203/*204 * Resource's release function will get called if last reference.205 * If it is the last reference, then we are sure that nobody else206 * can increment the count again (it's gone from the resource hash207 * table), so there's no need for locking here.208 */209int vmci_resource_put(struct vmci_resource *resource)210{211 /*212 * We propagate the information back to caller in case it wants to know213 * whether entry was freed.214 */215 return kref_put(&resource->kref, vmci_release_resource) ?216 VMCI_SUCCESS_ENTRY_DEAD : VMCI_SUCCESS;217}218 219struct vmci_handle vmci_resource_handle(struct vmci_resource *resource)220{221 return resource->handle;222}223