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

Last change on this file 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
RevLine 
[ec01adf]1/*
[eb27ce5a]2 * Copyright (c) 2008 Jakub Jermar
[ec01adf]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
[b1834a01]29/** @addtogroup vfs
[ec01adf]30 * @{
[1b20da0]31 */
[ec01adf]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"
[b818cff]39#include <stdlib.h>
[19f857a]40#include <str.h>
[1e4cada]41#include <fibril_synch.h>
[d9c8c81]42#include <adt/hash_table.h>
[062d900]43#include <adt/hash.h>
[7fff5eab]44#include <assert.h>
[f17667a]45#include <async.h>
46#include <errno.h>
[677745a]47#include <macros.h>
[b818cff]48
[553492be]49/** Mutex protecting the VFS node hash table. */
50FIBRIL_MUTEX_INITIALIZE(nodes_mutex);
[b818cff]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
[5e801dc]62static size_t nodes_key_hash(const void *);
[062d900]63static size_t nodes_hash(const ht_link_t *);
[0db0df2]64static bool nodes_key_equal(const void *, size_t, const ht_link_t *);
[062d900]65static vfs_triplet_t node_triplet(vfs_node_t *node);
[b818cff]66
67/** VFS node hash table operations. */
[61eb2ce2]68const hash_table_ops_t nodes_ops = {
[b818cff]69 .hash = nodes_hash,
[062d900]70 .key_hash = nodes_key_hash,
71 .key_equal = nodes_key_equal,
[4e00f87]72 .equal = NULL,
73 .remove_callback = NULL,
[b818cff]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{
[062d900]82 return hash_table_create(&nodes, 0, 0, &nodes_ops);
[b818cff]83}
84
85static inline void _vfs_node_addref(vfs_node_t *node)
86{
87 node->refcnt++;
88}
[ec01adf]89
[320c884]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{
[553492be]96 fibril_mutex_lock(&nodes_mutex);
[b818cff]97 _vfs_node_addref(node);
[553492be]98 fibril_mutex_unlock(&nodes_mutex);
[320c884]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{
[b7c62a9]109 bool free_node = false;
[a35b458]110
[553492be]111 fibril_mutex_lock(&nodes_mutex);
[a35b458]112
[b7c62a9]113 node->refcnt--;
114 if (node->refcnt == 0) {
[f17667a]115 /*
116 * We are dropping the last reference to this node.
117 * Remove it from the VFS node hash table.
118 */
[a35b458]119
[062d900]120 hash_table_remove_item(&nodes, &node->nh_link);
[b7c62a9]121 free_node = true;
[b818cff]122 }
[a35b458]123
[553492be]124 fibril_mutex_unlock(&nodes_mutex);
[a35b458]125
[b7c62a9]126 if (free_node) {
[79ae36dd]127 /*
[61042de]128 * VFS_OUT_DESTROY will free up the file's resources if there
129 * are no more hard links.
[f17667a]130 */
[a35b458]131
[79ae36dd]132 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
[61042de]133 async_msg_2(exch, VFS_OUT_DESTROY, (sysarg_t) node->service_id,
134 (sysarg_t)node->index);
[79ae36dd]135 vfs_exchange_release(exch);
[b7c62a9]136
[f17667a]137 free(node);
[b7c62a9]138 }
[320c884]139}
140
[c4aca2c]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);
[062d900]151 hash_table_remove_item(&nodes, &node->nh_link);
[c4aca2c]152 fibril_mutex_unlock(&nodes_mutex);
153 free(node);
154}
155
[320c884]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 *
[eb27ce5a]164 * @param result Populated lookup result structure.
[320c884]165 *
166 * @return VFS node corresponding to the given triplet.
167 */
[eb27ce5a]168vfs_node_t *vfs_node_get(vfs_lookup_res_t *result)
[ec01adf]169{
[b818cff]170 vfs_node_t *node;
171
[553492be]172 fibril_mutex_lock(&nodes_mutex);
[062d900]173 ht_link_t *tmp = hash_table_find(&nodes, &result->triplet);
[b818cff]174 if (!tmp) {
175 node = (vfs_node_t *) malloc(sizeof(vfs_node_t));
176 if (!node) {
[553492be]177 fibril_mutex_unlock(&nodes_mutex);
[b818cff]178 return NULL;
179 }
180 memset(node, 0, sizeof(vfs_node_t));
[eb27ce5a]181 node->fs_handle = result->triplet.fs_handle;
[15f3c3f]182 node->service_id = result->triplet.service_id;
[eb27ce5a]183 node->index = result->triplet.index;
184 node->size = result->size;
[b17186d]185 node->type = result->type;
[230260ac]186 fibril_rwlock_initialize(&node->contents_rwlock);
[062d900]187 hash_table_insert(&nodes, &node->nh_link);
[b818cff]188 } else {
[062d900]189 node = hash_table_get_inst(tmp, vfs_node_t, nh_link);
[b818cff]190 }
[7fff5eab]191
[b818cff]192 _vfs_node_addref(node);
[553492be]193 fibril_mutex_unlock(&nodes_mutex);
[b818cff]194
195 return node;
[320c884]196}
197
[5bcd5b7]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);
[4f9ab1e]206 _vfs_node_addref(node);
[5bcd5b7]207 }
208 fibril_mutex_unlock(&nodes_mutex);
209
210 return node;
211}
212
[320c884]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);
[ec01adf]224}
225
[319f4fb]226struct refcnt_data {
227 /** Sum of all reference counts for this file system instance. */
228 unsigned refcnt;
229 fs_handle_t fs_handle;
[15f3c3f]230 service_id_t service_id;
[319f4fb]231};
232
[062d900]233static bool refcnt_visitor(ht_link_t *item, void *arg)
[319f4fb]234{
[062d900]235 vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
[319f4fb]236 struct refcnt_data *rd = (void *) arg;
237
238 if ((node->fs_handle == rd->fs_handle) &&
[15f3c3f]239 (node->service_id == rd->service_id))
[319f4fb]240 rd->refcnt += node->refcnt;
[a35b458]241
[062d900]242 return true;
[319f4fb]243}
244
245unsigned
[15f3c3f]246vfs_nodes_refcount_sum_get(fs_handle_t fs_handle, service_id_t service_id)
[319f4fb]247{
248 struct refcnt_data rd = {
249 .refcnt = 0,
250 .fs_handle = fs_handle,
[15f3c3f]251 .service_id = service_id
[319f4fb]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
[44451ee]261/** Perform a remote node open operation.
262 *
263 * @return EOK on success or an error code from errno.h.
264 *
265 */
[b7fd2a0]266errno_t vfs_open_node_remote(vfs_node_t *node)
[44451ee]267{
268 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
[a35b458]269
[44451ee]270 ipc_call_t answer;
271 aid_t req = async_send_2(exch, VFS_OUT_OPEN_NODE,
[42a619b]272 (sysarg_t) node->service_id, (sysarg_t) node->index, &answer);
[a35b458]273
[44451ee]274 vfs_exchange_release(exch);
275
[b7fd2a0]276 errno_t rc;
[44451ee]277 async_wait_for(req, &rc);
[a35b458]278
[44451ee]279 return rc;
280}
281
[5e801dc]282static size_t nodes_key_hash(const void *key)
[062d900]283{
[5e801dc]284 const vfs_triplet_t *tri = key;
[062d900]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
[0db0df2]296static bool nodes_key_equal(const void *key, size_t hash, const ht_link_t *item)
[062d900]297{
[5e801dc]298 const vfs_triplet_t *tri = key;
[062d900]299 vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
[61042de]300 return node->fs_handle == tri->fs_handle &&
301 node->service_id == tri->service_id && node->index == tri->index;
[062d900]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 };
[a35b458]311
[062d900]312 return tri;
313}
314
[5bcd5b7]315bool vfs_node_has_children(vfs_node_t *node)
316{
317 async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
[b7fd2a0]318 errno_t rc = async_req_2_0(exch, VFS_OUT_IS_EMPTY, node->service_id,
[61042de]319 node->index);
[5bcd5b7]320 vfs_exchange_release(exch);
321 return rc == ENOTEMPTY;
322}
323
[ec01adf]324/**
325 * @}
[05b9912]326 */
Note: See TracBrowser for help on using the repository browser.