source: mainline/uspace/srv/vfs/vfs_node.c@ 45226285

Last change on this file since 45226285 was 0db0df2, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 months ago

Hash table improvements

Implement hash_table_foreach macro, analogous to list_foreach.

Remove superfluous argument to hash_table_find_next().
(If the user needs to recheck the part of the list already
checked by hash_table_find(), they can just rerun that function.)

Add hash argument to hash_table_ops_t::key_equal.
The big change here is that users with big keys can store the hash
value alongside key in their entries, and for the low low cost of
sizeof(size_t) bytes eliminate a bunch of expensive key comparisons.

Also added a hash function for strings and arbitrary data.
Found this one by asking ChatGPT, because the latency of accesses
to my book collection is currently a couple of hours.

+ Some drive-by unused #include removal.

  • Property mode set to 100644
File size: 8.4 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup vfs
30 * @{
31 */
32
33/**
34 * @file vfs_node.c
35 * @brief Various operations on VFS nodes have their home in this file.
36 */
37
38#include "vfs.h"
39#include <stdlib.h>
40#include <str.h>
41#include <fibril_synch.h>
42#include <adt/hash_table.h>
43#include <adt/hash.h>
44#include <assert.h>
45#include <async.h>
46#include <errno.h>
47#include <macros.h>
48
49/** Mutex protecting the VFS node hash table. */
50FIBRIL_MUTEX_INITIALIZE(nodes_mutex);
51
52#define NODES_BUCKETS_LOG 8
53#define NODES_BUCKETS (1 << NODES_BUCKETS_LOG)
54
55/** VFS node hash table containing all active, in-memory VFS nodes. */
56hash_table_t nodes;
57
58#define KEY_FS_HANDLE 0
59#define KEY_DEV_HANDLE 1
60#define KEY_INDEX 2
61
62static size_t nodes_key_hash(const void *);
63static size_t nodes_hash(const ht_link_t *);
64static bool nodes_key_equal(const void *, size_t, const ht_link_t *);
65static vfs_triplet_t node_triplet(vfs_node_t *node);
66
67/** VFS node hash table operations. */
68const hash_table_ops_t nodes_ops = {
69 .hash = nodes_hash,
70 .key_hash = nodes_key_hash,
71 .key_equal = nodes_key_equal,
72 .equal = NULL,
73 .remove_callback = NULL,
74};
75
76/** Initialize the VFS node hash table.
77 *
78 * @return Return true on success, false on failure.
79 */
80bool vfs_nodes_init(void)
81{
82 return hash_table_create(&nodes, 0, 0, &nodes_ops);
83}
84
85static inline void _vfs_node_addref(vfs_node_t *node)
86{
87 node->refcnt++;
88}
89
90/** Increment reference count of a VFS node.
91 *
92 * @param node VFS node that will have its refcnt incremented.
93 */
94void vfs_node_addref(vfs_node_t *node)
95{
96 fibril_mutex_lock(&nodes_mutex);
97 _vfs_node_addref(node);
98 fibril_mutex_unlock(&nodes_mutex);
99}
100
101/** Decrement reference count of a VFS node.
102 *
103 * This function handles the case when the reference count drops to zero.
104 *
105 * @param node VFS node that will have its refcnt decremented.
106 */
107void vfs_node_delref(vfs_node_t *node)
108{
109 bool free_node = false;
110
111 fibril_mutex_lock(&nodes_mutex);
112
113 node->refcnt--;
114 if (node->refcnt == 0) {
115 /*
116 * We are dropping the last reference to this node.
117 * Remove it from the VFS node hash table.
118 */
119
120 hash_table_remove_item(&nodes, &node->nh_link);
121 free_node = true;
122 }
123
124 fibril_mutex_unlock(&nodes_mutex);
125
126 if (free_node) {
127 /*
128 * VFS_OUT_DESTROY will free up the file's resources if there
129 * are no more hard links.
130 */
131
132 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
133 async_msg_2(exch, VFS_OUT_DESTROY, (sysarg_t) node->service_id,
134 (sysarg_t)node->index);
135 vfs_exchange_release(exch);
136
137 free(node);
138 }
139}
140
141/** Forget node.
142 *
143 * This function will remove the node from the node hash table and deallocate
144 * its memory, regardless of the node's reference count.
145 *
146 * @param node Node to be forgotten.
147 */
148void vfs_node_forget(vfs_node_t *node)
149{
150 fibril_mutex_lock(&nodes_mutex);
151 hash_table_remove_item(&nodes, &node->nh_link);
152 fibril_mutex_unlock(&nodes_mutex);
153 free(node);
154}
155
156/** Find VFS node.
157 *
158 * This function will try to lookup the given triplet in the VFS node hash
159 * table. In case the triplet is not found there, a new VFS node is created.
160 * In any case, the VFS node will have its reference count incremented. Every
161 * node returned by this call should be eventually put back by calling
162 * vfs_node_put() on it.
163 *
164 * @param result Populated lookup result structure.
165 *
166 * @return VFS node corresponding to the given triplet.
167 */
168vfs_node_t *vfs_node_get(vfs_lookup_res_t *result)
169{
170 vfs_node_t *node;
171
172 fibril_mutex_lock(&nodes_mutex);
173 ht_link_t *tmp = hash_table_find(&nodes, &result->triplet);
174 if (!tmp) {
175 node = (vfs_node_t *) malloc(sizeof(vfs_node_t));
176 if (!node) {
177 fibril_mutex_unlock(&nodes_mutex);
178 return NULL;
179 }
180 memset(node, 0, sizeof(vfs_node_t));
181 node->fs_handle = result->triplet.fs_handle;
182 node->service_id = result->triplet.service_id;
183 node->index = result->triplet.index;
184 node->size = result->size;
185 node->type = result->type;
186 fibril_rwlock_initialize(&node->contents_rwlock);
187 hash_table_insert(&nodes, &node->nh_link);
188 } else {
189 node = hash_table_get_inst(tmp, vfs_node_t, nh_link);
190 }
191
192 _vfs_node_addref(node);
193 fibril_mutex_unlock(&nodes_mutex);
194
195 return node;
196}
197
198vfs_node_t *vfs_node_peek(vfs_lookup_res_t *result)
199{
200 vfs_node_t *node = NULL;
201
202 fibril_mutex_lock(&nodes_mutex);
203 ht_link_t *tmp = hash_table_find(&nodes, &result->triplet);
204 if (tmp) {
205 node = hash_table_get_inst(tmp, vfs_node_t, nh_link);
206 _vfs_node_addref(node);
207 }
208 fibril_mutex_unlock(&nodes_mutex);
209
210 return node;
211}
212
213/** Return VFS node when no longer needed by the caller.
214 *
215 * This function will remove the reference on the VFS node created by
216 * vfs_node_get(). This function can only be called as a closing bracket to the
217 * preceding vfs_node_get() call.
218 *
219 * @param node VFS node being released.
220 */
221void vfs_node_put(vfs_node_t *node)
222{
223 vfs_node_delref(node);
224}
225
226struct refcnt_data {
227 /** Sum of all reference counts for this file system instance. */
228 unsigned refcnt;
229 fs_handle_t fs_handle;
230 service_id_t service_id;
231};
232
233static bool refcnt_visitor(ht_link_t *item, void *arg)
234{
235 vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
236 struct refcnt_data *rd = (void *) arg;
237
238 if ((node->fs_handle == rd->fs_handle) &&
239 (node->service_id == rd->service_id))
240 rd->refcnt += node->refcnt;
241
242 return true;
243}
244
245unsigned
246vfs_nodes_refcount_sum_get(fs_handle_t fs_handle, service_id_t service_id)
247{
248 struct refcnt_data rd = {
249 .refcnt = 0,
250 .fs_handle = fs_handle,
251 .service_id = service_id
252 };
253
254 fibril_mutex_lock(&nodes_mutex);
255 hash_table_apply(&nodes, refcnt_visitor, &rd);
256 fibril_mutex_unlock(&nodes_mutex);
257
258 return rd.refcnt;
259}
260
261/** Perform a remote node open operation.
262 *
263 * @return EOK on success or an error code from errno.h.
264 *
265 */
266errno_t vfs_open_node_remote(vfs_node_t *node)
267{
268 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
269
270 ipc_call_t answer;
271 aid_t req = async_send_2(exch, VFS_OUT_OPEN_NODE,
272 (sysarg_t) node->service_id, (sysarg_t) node->index, &answer);
273
274 vfs_exchange_release(exch);
275
276 errno_t rc;
277 async_wait_for(req, &rc);
278
279 return rc;
280}
281
282static size_t nodes_key_hash(const void *key)
283{
284 const vfs_triplet_t *tri = key;
285 size_t hash = hash_combine(tri->fs_handle, tri->index);
286 return hash_combine(hash, tri->service_id);
287}
288
289static size_t nodes_hash(const ht_link_t *item)
290{
291 vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
292 vfs_triplet_t tri = node_triplet(node);
293 return nodes_key_hash(&tri);
294}
295
296static bool nodes_key_equal(const void *key, size_t hash, const ht_link_t *item)
297{
298 const vfs_triplet_t *tri = key;
299 vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
300 return node->fs_handle == tri->fs_handle &&
301 node->service_id == tri->service_id && node->index == tri->index;
302}
303
304static inline vfs_triplet_t node_triplet(vfs_node_t *node)
305{
306 vfs_triplet_t tri = {
307 .fs_handle = node->fs_handle,
308 .service_id = node->service_id,
309 .index = node->index
310 };
311
312 return tri;
313}
314
315bool vfs_node_has_children(vfs_node_t *node)
316{
317 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
318 errno_t rc = async_req_2_0(exch, VFS_OUT_IS_EMPTY, node->service_id,
319 node->index);
320 vfs_exchange_release(exch);
321 return rc == ENOTEMPTY;
322}
323
324/**
325 * @}
326 */
Note: See TracBrowser for help on using the repository browser.