diff options
author | George Zhang <georgezhang@vmware.com> | 2013-01-08 18:55:07 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-01-08 19:15:56 -0500 |
commit | bc63dedb7d46a7d690c6b6edf69136b88af06cc6 (patch) | |
tree | f8b1a1eece8bdd545ab8dbd5570977a641eb6196 /drivers/misc/vmw_vmci/vmci_resource.c | |
parent | 06164d2b72aa752ce4633184b3e0d97601017135 (diff) |
VMCI: resource object implementation.
VMCI resource tracks all used resources within the vmci code.
Signed-off-by: George Zhang <georgezhang@vmware.com>
Acked-by: Andy king <acking@vmware.com>
Acked-by: Dmitry Torokhov <dtor@vmware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/vmw_vmci/vmci_resource.c')
-rw-r--r-- | drivers/misc/vmw_vmci/vmci_resource.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/drivers/misc/vmw_vmci/vmci_resource.c b/drivers/misc/vmw_vmci/vmci_resource.c new file mode 100644 index 000000000000..a196f84a4fd2 --- /dev/null +++ b/drivers/misc/vmw_vmci/vmci_resource.c | |||
@@ -0,0 +1,229 @@ | |||
1 | /* | ||
2 | * VMware VMCI Driver | ||
3 | * | ||
4 | * Copyright (C) 2012 VMware, Inc. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation version 2 and no later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
13 | * for more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/vmw_vmci_defs.h> | ||
17 | #include <linux/hash.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/rculist.h> | ||
20 | |||
21 | #include "vmci_resource.h" | ||
22 | #include "vmci_driver.h" | ||
23 | |||
24 | |||
25 | #define VMCI_RESOURCE_HASH_BITS 7 | ||
26 | #define VMCI_RESOURCE_HASH_BUCKETS (1 << VMCI_RESOURCE_HASH_BITS) | ||
27 | |||
28 | struct vmci_hash_table { | ||
29 | spinlock_t lock; | ||
30 | struct hlist_head entries[VMCI_RESOURCE_HASH_BUCKETS]; | ||
31 | }; | ||
32 | |||
33 | static struct vmci_hash_table vmci_resource_table = { | ||
34 | .lock = __SPIN_LOCK_UNLOCKED(vmci_resource_table.lock), | ||
35 | }; | ||
36 | |||
37 | static unsigned int vmci_resource_hash(struct vmci_handle handle) | ||
38 | { | ||
39 | return hash_32(handle.resource, VMCI_RESOURCE_HASH_BITS); | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * Gets a resource (if one exists) matching given handle from the hash table. | ||
44 | */ | ||
45 | static struct vmci_resource *vmci_resource_lookup(struct vmci_handle handle, | ||
46 | enum vmci_resource_type type) | ||
47 | { | ||
48 | struct vmci_resource *r, *resource = NULL; | ||
49 | struct hlist_node *node; | ||
50 | unsigned int idx = vmci_resource_hash(handle); | ||
51 | |||
52 | rcu_read_lock(); | ||
53 | hlist_for_each_entry_rcu(r, node, | ||
54 | &vmci_resource_table.entries[idx], node) { | ||
55 | u32 cid = r->handle.context; | ||
56 | u32 rid = r->handle.resource; | ||
57 | |||
58 | if (r->type == type && | ||
59 | rid == handle.resource && | ||
60 | (cid == handle.context || cid == VMCI_INVALID_ID)) { | ||
61 | resource = r; | ||
62 | break; | ||
63 | } | ||
64 | } | ||
65 | rcu_read_unlock(); | ||
66 | |||
67 | return resource; | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Find an unused resource ID and return it. The first | ||
72 | * VMCI_RESERVED_RESOURCE_ID_MAX are reserved so we start from | ||
73 | * its value + 1. | ||
74 | * Returns VMCI resource id on success, VMCI_INVALID_ID on failure. | ||
75 | */ | ||
76 | static u32 vmci_resource_find_id(u32 context_id, | ||
77 | enum vmci_resource_type resource_type) | ||
78 | { | ||
79 | static u32 resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1; | ||
80 | u32 old_rid = resource_id; | ||
81 | u32 current_rid; | ||
82 | |||
83 | /* | ||
84 | * Generate a unique resource ID. Keep on trying until we wrap around | ||
85 | * in the RID space. | ||
86 | */ | ||
87 | do { | ||
88 | struct vmci_handle handle; | ||
89 | |||
90 | current_rid = resource_id; | ||
91 | resource_id++; | ||
92 | if (unlikely(resource_id == VMCI_INVALID_ID)) { | ||
93 | /* Skip the reserved rids. */ | ||
94 | resource_id = VMCI_RESERVED_RESOURCE_ID_MAX + 1; | ||
95 | } | ||
96 | |||
97 | handle = vmci_make_handle(context_id, current_rid); | ||
98 | if (!vmci_resource_lookup(handle, resource_type)) | ||
99 | return current_rid; | ||
100 | } while (resource_id != old_rid); | ||
101 | |||
102 | return VMCI_INVALID_ID; | ||
103 | } | ||
104 | |||
105 | |||
106 | int vmci_resource_add(struct vmci_resource *resource, | ||
107 | enum vmci_resource_type resource_type, | ||
108 | struct vmci_handle handle) | ||
109 | |||
110 | { | ||
111 | unsigned int idx; | ||
112 | int result; | ||
113 | |||
114 | spin_lock(&vmci_resource_table.lock); | ||
115 | |||
116 | if (handle.resource == VMCI_INVALID_ID) { | ||
117 | handle.resource = vmci_resource_find_id(handle.context, | ||
118 | resource_type); | ||
119 | if (handle.resource == VMCI_INVALID_ID) { | ||
120 | result = VMCI_ERROR_NO_HANDLE; | ||
121 | goto out; | ||
122 | } | ||
123 | } else if (vmci_resource_lookup(handle, resource_type)) { | ||
124 | result = VMCI_ERROR_ALREADY_EXISTS; | ||
125 | goto out; | ||
126 | } | ||
127 | |||
128 | resource->handle = handle; | ||
129 | resource->type = resource_type; | ||
130 | INIT_HLIST_NODE(&resource->node); | ||
131 | kref_init(&resource->kref); | ||
132 | init_completion(&resource->done); | ||
133 | |||
134 | idx = vmci_resource_hash(resource->handle); | ||
135 | hlist_add_head_rcu(&resource->node, &vmci_resource_table.entries[idx]); | ||
136 | |||
137 | result = VMCI_SUCCESS; | ||
138 | |||
139 | out: | ||
140 | spin_unlock(&vmci_resource_table.lock); | ||
141 | return result; | ||
142 | } | ||
143 | |||
144 | void vmci_resource_remove(struct vmci_resource *resource) | ||
145 | { | ||
146 | struct vmci_handle handle = resource->handle; | ||
147 | unsigned int idx = vmci_resource_hash(handle); | ||
148 | struct vmci_resource *r; | ||
149 | struct hlist_node *node; | ||
150 | |||
151 | /* Remove resource from hash table. */ | ||
152 | spin_lock(&vmci_resource_table.lock); | ||
153 | |||
154 | hlist_for_each_entry(r, node, &vmci_resource_table.entries[idx], node) { | ||
155 | if (vmci_handle_is_equal(r->handle, resource->handle)) { | ||
156 | hlist_del_init_rcu(&r->node); | ||
157 | break; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | spin_unlock(&vmci_resource_table.lock); | ||
162 | synchronize_rcu(); | ||
163 | |||
164 | vmci_resource_put(resource); | ||
165 | wait_for_completion(&resource->done); | ||
166 | } | ||
167 | |||
168 | struct vmci_resource * | ||
169 | vmci_resource_by_handle(struct vmci_handle resource_handle, | ||
170 | enum vmci_resource_type resource_type) | ||
171 | { | ||
172 | struct vmci_resource *r, *resource = NULL; | ||
173 | |||
174 | rcu_read_lock(); | ||
175 | |||
176 | r = vmci_resource_lookup(resource_handle, resource_type); | ||
177 | if (r && | ||
178 | (resource_type == r->type || | ||
179 | resource_type == VMCI_RESOURCE_TYPE_ANY)) { | ||
180 | resource = vmci_resource_get(r); | ||
181 | } | ||
182 | |||
183 | rcu_read_unlock(); | ||
184 | |||
185 | return resource; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * Get a reference to given resource. | ||
190 | */ | ||
191 | struct vmci_resource *vmci_resource_get(struct vmci_resource *resource) | ||
192 | { | ||
193 | kref_get(&resource->kref); | ||
194 | |||
195 | return resource; | ||
196 | } | ||
197 | |||
198 | static void vmci_release_resource(struct kref *kref) | ||
199 | { | ||
200 | struct vmci_resource *resource = | ||
201 | container_of(kref, struct vmci_resource, kref); | ||
202 | |||
203 | /* Verify the resource has been unlinked from hash table */ | ||
204 | WARN_ON(!hlist_unhashed(&resource->node)); | ||
205 | |||
206 | /* Signal that container of this resource can now be destroyed */ | ||
207 | complete(&resource->done); | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Resource's release function will get called if last reference. | ||
212 | * If it is the last reference, then we are sure that nobody else | ||
213 | * can increment the count again (it's gone from the resource hash | ||
214 | * table), so there's no need for locking here. | ||
215 | */ | ||
216 | int vmci_resource_put(struct vmci_resource *resource) | ||
217 | { | ||
218 | /* | ||
219 | * We propagate the information back to caller in case it wants to know | ||
220 | * whether entry was freed. | ||
221 | */ | ||
222 | return kref_put(&resource->kref, vmci_release_resource) ? | ||
223 | VMCI_SUCCESS_ENTRY_DEAD : VMCI_SUCCESS; | ||
224 | } | ||
225 | |||
226 | struct vmci_handle vmci_resource_handle(struct vmci_resource *resource) | ||
227 | { | ||
228 | return resource->handle; | ||
229 | } | ||