source: mainline/uspace/srv/devman/devtree.c@ ca48672

Last change on this file since ca48672 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.8 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
3 * Copyright (c) 2010 Lenka Trochtova
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup devman
31 * @{
32 */
33
34#include <io/log.h>
35
36#include "dev.h"
37#include "devtree.h"
38#include "devman.h"
39#include "driver.h"
40#include "fun.h"
41
42/* hash table operations */
43
44static inline size_t handle_key_hash(const void *key)
45{
46 const devman_handle_t *handle = key;
47 return *handle;
48}
49
50static size_t devman_devices_hash(const ht_link_t *item)
51{
52 dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
53 return handle_key_hash(&dev->handle);
54}
55
56static size_t devman_functions_hash(const ht_link_t *item)
57{
58 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
59 return handle_key_hash(&fun->handle);
60}
61
62static bool devman_devices_key_equal(const void *key, size_t hash, const ht_link_t *item)
63{
64 const devman_handle_t *handle = key;
65 dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
66 return dev->handle == *handle;
67}
68
69static bool devman_functions_key_equal(const void *key, size_t hash, const ht_link_t *item)
70{
71 const devman_handle_t *handle = key;
72 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
73 return fun->handle == *handle;
74}
75
76static inline size_t service_id_key_hash(const void *key)
77{
78 const service_id_t *service_id = key;
79 return *service_id;
80}
81
82static size_t loc_functions_hash(const ht_link_t *item)
83{
84 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
85 return service_id_key_hash(&fun->service_id);
86}
87
88static bool loc_functions_key_equal(const void *key, size_t hash, const ht_link_t *item)
89{
90 const service_id_t *service_id = key;
91 fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
92 return fun->service_id == *service_id;
93}
94
95static const hash_table_ops_t devman_devices_ops = {
96 .hash = devman_devices_hash,
97 .key_hash = handle_key_hash,
98 .key_equal = devman_devices_key_equal,
99 .equal = NULL,
100 .remove_callback = NULL
101};
102
103static const hash_table_ops_t devman_functions_ops = {
104 .hash = devman_functions_hash,
105 .key_hash = handle_key_hash,
106 .key_equal = devman_functions_key_equal,
107 .equal = NULL,
108 .remove_callback = NULL
109};
110
111static const hash_table_ops_t loc_devices_ops = {
112 .hash = loc_functions_hash,
113 .key_hash = service_id_key_hash,
114 .key_equal = loc_functions_key_equal,
115 .equal = NULL,
116 .remove_callback = NULL
117};
118
119/** Create root device and function node in the device tree.
120 *
121 * @param tree The device tree.
122 * @return True on success, false otherwise.
123 */
124bool create_root_nodes(dev_tree_t *tree)
125{
126 fun_node_t *fun;
127 dev_node_t *dev;
128
129 log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
130
131 fibril_rwlock_write_lock(&tree->rwlock);
132
133 /*
134 * Create root function. This is a pseudo function to which
135 * the root device node is attached. It allows us to match
136 * the root device driver in a standard manner, i.e. against
137 * the parent function.
138 */
139
140 fun = create_fun_node();
141 if (fun == NULL) {
142 fibril_rwlock_write_unlock(&tree->rwlock);
143 return false;
144 }
145
146 if (!insert_fun_node(tree, fun, str_dup(""), NULL)) {
147 fun_del_ref(fun); /* fun is destroyed */
148 fibril_rwlock_write_unlock(&tree->rwlock);
149 return false;
150 }
151
152 match_id_t *id = create_match_id();
153 id->id = str_dup("root");
154 id->score = 100;
155 add_match_id(&fun->match_ids, id);
156 tree->root_node = fun;
157
158 /*
159 * Create root device node.
160 */
161 dev = create_dev_node();
162 if (dev == NULL) {
163 fibril_rwlock_write_unlock(&tree->rwlock);
164 return false;
165 }
166
167 insert_dev_node(tree, dev, fun);
168
169 fibril_rwlock_write_unlock(&tree->rwlock);
170
171 return dev != NULL;
172}
173
174/** Initialize the device tree.
175 *
176 * Create root device node of the tree and assign driver to it.
177 *
178 * @param tree The device tree.
179 * @param drivers_list the list of available drivers.
180 * @return True on success, false otherwise.
181 */
182bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
183{
184 log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
185
186 tree->current_handle = 0;
187
188 hash_table_create(&tree->devman_devices, 0, 0, &devman_devices_ops);
189 hash_table_create(&tree->devman_functions, 0, 0, &devman_functions_ops);
190 hash_table_create(&tree->loc_functions, 0, 0, &loc_devices_ops);
191
192 fibril_rwlock_initialize(&tree->rwlock);
193
194 /* Create root function and root device and add them to the device tree. */
195 if (!create_root_nodes(tree))
196 return false;
197
198 /* Find suitable driver and start it. */
199 dev_node_t *rdev = tree->root_node->child;
200 dev_add_ref(rdev);
201 bool rc = assign_driver(rdev, drivers_list, tree);
202 dev_del_ref(rdev);
203
204 return rc;
205}
206
207/** Insert new device into device tree.
208 *
209 * @param tree The device tree.
210 * @param dev The newly added device node.
211 * @param pfun The parent function node.
212 *
213 * @return True on success, false otherwise (insufficient resources
214 * etc.).
215 */
216bool insert_dev_node(dev_tree_t *tree, dev_node_t *dev, fun_node_t *pfun)
217{
218 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
219
220 log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
221 dev, pfun, pfun->pathname);
222
223 /* Add the node to the handle-to-node map. */
224 dev->handle = ++tree->current_handle;
225 hash_table_insert(&tree->devman_devices, &dev->devman_dev);
226
227 /* Add the node to the list of its parent's children. */
228 dev->pfun = pfun;
229 pfun->child = dev;
230
231 return true;
232}
233
234/** Remove device from device tree.
235 *
236 * @param tree Device tree
237 * @param dev Device node
238 */
239void remove_dev_node(dev_tree_t *tree, dev_node_t *dev)
240{
241 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
242
243 log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
244
245 /* Remove node from the handle-to-node map. */
246 hash_table_remove(&tree->devman_devices, &dev->handle);
247
248 /* Unlink from parent function. */
249 dev->pfun->child = NULL;
250 dev->pfun = NULL;
251
252 dev->state = DEVICE_REMOVED;
253}
254
255/** Insert new function into device tree.
256 *
257 * @param tree The device tree.
258 * @param fun The newly added function node.
259 * @param fun_name The name of the newly added function.
260 * @param dev Owning device node.
261 *
262 * @return True on success, false otherwise (insufficient resources
263 * etc.).
264 */
265bool insert_fun_node(dev_tree_t *tree, fun_node_t *fun, char *fun_name,
266 dev_node_t *dev)
267{
268 fun_node_t *pfun;
269
270 assert(fun_name != NULL);
271 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
272
273 /*
274 * The root function is a special case, it does not belong to any
275 * device so for the root function dev == NULL.
276 */
277 pfun = (dev != NULL) ? dev->pfun : NULL;
278
279 fun->name = fun_name;
280 if (!set_fun_path(tree, fun, pfun)) {
281 return false;
282 }
283
284 /* Add the node to the handle-to-node map. */
285 fun->handle = ++tree->current_handle;
286 hash_table_insert(&tree->devman_functions, &fun->devman_fun);
287
288 /* Add the node to the list of its parent's children. */
289 fun->dev = dev;
290 if (dev != NULL)
291 list_append(&fun->dev_functions, &dev->functions);
292
293 return true;
294}
295
296/** Remove function from device tree.
297 *
298 * @param tree Device tree
299 * @param node Function node to remove
300 */
301void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
302{
303 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
304
305 /* Remove the node from the handle-to-node map. */
306 hash_table_remove(&tree->devman_functions, &fun->handle);
307
308 /* Remove the node from the list of its parent's children. */
309 if (fun->dev != NULL)
310 list_remove(&fun->dev_functions);
311
312 fun->dev = NULL;
313 fun->state = FUN_REMOVED;
314}
315
316/** Wait for device tree to stabilize.
317 *
318 * Blocks until the entire device tree had a chance to finish attaching
319 * all devices.
320 *
321 * @param tree Device tree
322 */
323void dev_tree_wait_stable(dev_tree_t *tree)
324{
325 dev_wait_stable(tree->root_node->child);
326}
327
328/** @}
329 */
Note: See TracBrowser for help on using the repository browser.