Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * VMware VMCI Driver
0004  *
0005  * Copyright (C) 2012 VMware, Inc. All rights reserved.
0006  */
0007 
0008 #include <linux/vmw_vmci_defs.h>
0009 #include <linux/hash.h>
0010 #include <linux/types.h>
0011 #include <linux/rculist.h>
0012 #include <linux/completion.h>
0013 
0014 #include "vmci_resource.h"
0015 #include "vmci_driver.h"
0016 
0017 
0018 #define VMCI_RESOURCE_HASH_BITS         7
0019 #define VMCI_RESOURCE_HASH_BUCKETS      (1 << VMCI_RESOURCE_HASH_BITS)
0020 
0021 struct vmci_hash_table {
0022     spinlock_t lock;
0023     struct hlist_head entries[VMCI_RESOURCE_HASH_BUCKETS];
0024 };
0025 
0026 static struct vmci_hash_table vmci_resource_table = {
0027     .lock = __SPIN_LOCK_UNLOCKED(vmci_resource_table.lock),
0028 };
0029 
0030 static unsigned int vmci_resource_hash(struct vmci_handle handle)
0031 {
0032     return hash_32(handle.resource, VMCI_RESOURCE_HASH_BITS);
0033 }
0034 
0035 /*
0036  * Gets a resource (if one exists) matching given handle from the hash table.
0037  */
0038 static struct vmci_resource *vmci_resource_lookup(struct vmci_handle handle,
0039                           enum vmci_resource_type type)
0040 {
0041     struct vmci_resource *r, *resource = NULL;
0042     unsigned int idx = vmci_resource_hash(handle);
0043 
0044     rcu_read_lock();
0045     hlist_for_each_entry_rcu(r,
0046                  &vmci_resource_table.entries[idx], node) {
0047         u32 cid = r->handle.context;
0048         u32 rid = r->handle.resource;
0049 
0050         if (r->type == type &&
0051             rid == handle.resource &&
0052             (cid == handle.context || cid == VMCI_INVALID_ID ||
0053              handle.context == VMCI_INVALID_ID)) {
0054             resource = r;
0055             break;
0056         }
0057     }
0058     rcu_read_unlock();
0059 
0060     return resource;
0061 }
0062 
0063 /*
0064  * Find an unused resource ID and return it. The first
0065  * VMCI_RESERVED_RESOURCE_ID_MAX are reserved so we start from
0066  * its value + 1.
0067  * Returns VMCI resource id on success, VMCI_INVALID_ID on failure.
0068  */
0069 static u32 vmci_resource_find_id(u32 context_id,
0070                  enum vmci_resource_type resource_type)
0071 {
0072     static u32 resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
0073     u32 old_rid = resource_id;
0074     u32 current_rid;
0075 
0076     /*
0077      * Generate a unique resource ID.  Keep on trying until we wrap around
0078      * in the RID space.
0079      */
0080     do {
0081         struct vmci_handle handle;
0082 
0083         current_rid = resource_id;
0084         resource_id++;
0085         if (unlikely(resource_id == VMCI_INVALID_ID)) {
0086             /* Skip the reserved rids. */
0087             resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1;
0088         }
0089 
0090         handle = vmci_make_handle(context_id, current_rid);
0091         if (!vmci_resource_lookup(handle, resource_type))
0092             return current_rid;
0093     } while (resource_id != old_rid);
0094 
0095     return VMCI_INVALID_ID;
0096 }
0097 
0098 
0099 int vmci_resource_add(struct vmci_resource *resource,
0100               enum vmci_resource_type resource_type,
0101               struct vmci_handle handle)
0102 
0103 {
0104     unsigned int idx;
0105     int result;
0106 
0107     spin_lock(&vmci_resource_table.lock);
0108 
0109     if (handle.resource == VMCI_INVALID_ID) {
0110         handle.resource = vmci_resource_find_id(handle.context,
0111             resource_type);
0112         if (handle.resource == VMCI_INVALID_ID) {
0113             result = VMCI_ERROR_NO_HANDLE;
0114             goto out;
0115         }
0116     } else if (vmci_resource_lookup(handle, resource_type)) {
0117         result = VMCI_ERROR_ALREADY_EXISTS;
0118         goto out;
0119     }
0120 
0121     resource->handle = handle;
0122     resource->type = resource_type;
0123     INIT_HLIST_NODE(&resource->node);
0124     kref_init(&resource->kref);
0125     init_completion(&resource->done);
0126 
0127     idx = vmci_resource_hash(resource->handle);
0128     hlist_add_head_rcu(&resource->node, &vmci_resource_table.entries[idx]);
0129 
0130     result = VMCI_SUCCESS;
0131 
0132 out:
0133     spin_unlock(&vmci_resource_table.lock);
0134     return result;
0135 }
0136 
0137 void vmci_resource_remove(struct vmci_resource *resource)
0138 {
0139     struct vmci_handle handle = resource->handle;
0140     unsigned int idx = vmci_resource_hash(handle);
0141     struct vmci_resource *r;
0142 
0143     /* Remove resource from hash table. */
0144     spin_lock(&vmci_resource_table.lock);
0145 
0146     hlist_for_each_entry(r, &vmci_resource_table.entries[idx], node) {
0147         if (vmci_handle_is_equal(r->handle, resource->handle)) {
0148             hlist_del_init_rcu(&r->node);
0149             break;
0150         }
0151     }
0152 
0153     spin_unlock(&vmci_resource_table.lock);
0154     synchronize_rcu();
0155 
0156     vmci_resource_put(resource);
0157     wait_for_completion(&resource->done);
0158 }
0159 
0160 struct vmci_resource *
0161 vmci_resource_by_handle(struct vmci_handle resource_handle,
0162             enum vmci_resource_type resource_type)
0163 {
0164     struct vmci_resource *r, *resource = NULL;
0165 
0166     rcu_read_lock();
0167 
0168     r = vmci_resource_lookup(resource_handle, resource_type);
0169     if (r &&
0170         (resource_type == r->type ||
0171          resource_type == VMCI_RESOURCE_TYPE_ANY)) {
0172         resource = vmci_resource_get(r);
0173     }
0174 
0175     rcu_read_unlock();
0176 
0177     return resource;
0178 }
0179 
0180 /*
0181  * Get a reference to given resource.
0182  */
0183 struct vmci_resource *vmci_resource_get(struct vmci_resource *resource)
0184 {
0185     kref_get(&resource->kref);
0186 
0187     return resource;
0188 }
0189 
0190 static void vmci_release_resource(struct kref *kref)
0191 {
0192     struct vmci_resource *resource =
0193         container_of(kref, struct vmci_resource, kref);
0194 
0195     /* Verify the resource has been unlinked from hash table */
0196     WARN_ON(!hlist_unhashed(&resource->node));
0197 
0198     /* Signal that container of this resource can now be destroyed */
0199     complete(&resource->done);
0200 }
0201 
0202 /*
0203  * Resource's release function will get called if last reference.
0204  * If it is the last reference, then we are sure that nobody else
0205  * can increment the count again (it's gone from the resource hash
0206  * table), so there's no need for locking here.
0207  */
0208 int vmci_resource_put(struct vmci_resource *resource)
0209 {
0210     /*
0211      * We propagate the information back to caller in case it wants to know
0212      * whether entry was freed.
0213      */
0214     return kref_put(&resource->kref, vmci_release_resource) ?
0215         VMCI_SUCCESS_ENTRY_DEAD : VMCI_SUCCESS;
0216 }
0217 
0218 struct vmci_handle vmci_resource_handle(struct vmci_resource *resource)
0219 {
0220     return resource->handle;
0221 }