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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f89204ee was f89204ee, checked in by Jakub Jermar <jakub@…>, 7 years ago

Fix devman function reference counting

After commit 498ced18a4, create_fun_node() returns a fun pointer with an
implicit reference. Adding an extra reference for creation thus adds a
reference that will never be dropped and the function object will be
leaked.

This commit fixes the reference counting issue and also adds the missing
check to the call to insert_fun_node().

  • Property mode set to 100644
File size: 8.5 KB
RevLine 
[59dc181]1/*
2 * Copyright (c) 2010 Lenka Trochtova
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 devman
30 * @{
31 */
32
33#include <errno.h>
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(void *key)
45{
[1433ecda]46 devman_handle_t handle = *(devman_handle_t *)key;
[59dc181]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(void *key, const ht_link_t *item)
63{
[1433ecda]64 devman_handle_t handle = *(devman_handle_t *)key;
[59dc181]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(void *key, const ht_link_t *item)
70{
[1433ecda]71 devman_handle_t handle = *(devman_handle_t *)key;
[59dc181]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(void *key)
77{
[1433ecda]78 service_id_t service_id = *(service_id_t *)key;
[59dc181]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(void *key, const ht_link_t *item)
89{
[1433ecda]90 service_id_t service_id = *(service_id_t *)key;
[59dc181]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 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 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 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;
[a35b458]128
[59dc181]129 log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
[a35b458]130
[59dc181]131 fibril_rwlock_write_lock(&tree->rwlock);
[a35b458]132
[59dc181]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 */
[a35b458]139
[59dc181]140 fun = create_fun_node();
141 if (fun == NULL) {
142 fibril_rwlock_write_unlock(&tree->rwlock);
143 return false;
144 }
[a35b458]145
[f89204ee]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 }
[a35b458]151
[59dc181]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;
[a35b458]157
[59dc181]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 }
[a35b458]166
[59dc181]167 dev_add_ref(dev);
168 insert_dev_node(tree, dev, fun);
[a35b458]169
[59dc181]170 fibril_rwlock_write_unlock(&tree->rwlock);
[a35b458]171
[59dc181]172 return dev != NULL;
173}
174
175/** Initialize the device tree.
176 *
177 * Create root device node of the tree and assign driver to it.
178 *
179 * @param tree The device tree.
180 * @param drivers_list the list of available drivers.
181 * @return True on success, false otherwise.
182 */
183bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
184{
185 log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
[a35b458]186
[59dc181]187 tree->current_handle = 0;
[a35b458]188
[59dc181]189 hash_table_create(&tree->devman_devices, 0, 0, &devman_devices_ops);
190 hash_table_create(&tree->devman_functions, 0, 0, &devman_functions_ops);
191 hash_table_create(&tree->loc_functions, 0, 0, &loc_devices_ops);
[a35b458]192
[59dc181]193 fibril_rwlock_initialize(&tree->rwlock);
[a35b458]194
[59dc181]195 /* Create root function and root device and add them to the device tree. */
196 if (!create_root_nodes(tree))
197 return false;
[a35b458]198
[59dc181]199 /* Find suitable driver and start it. */
200 dev_node_t *rdev = tree->root_node->child;
201 dev_add_ref(rdev);
[1569a9b]202 bool rc = assign_driver(rdev, drivers_list, tree);
[59dc181]203 dev_del_ref(rdev);
[a35b458]204
[59dc181]205 return rc;
206}
207
208/** Insert new device into device tree.
209 *
210 * @param tree The device tree.
211 * @param dev The newly added device node.
212 * @param pfun The parent function node.
213 *
214 * @return True on success, false otherwise (insufficient resources
215 * etc.).
216 */
217bool insert_dev_node(dev_tree_t *tree, dev_node_t *dev, fun_node_t *pfun)
218{
219 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
[a35b458]220
[59dc181]221 log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
222 dev, pfun, pfun->pathname);
223
224 /* Add the node to the handle-to-node map. */
225 dev->handle = ++tree->current_handle;
226 hash_table_insert(&tree->devman_devices, &dev->devman_dev);
227
228 /* Add the node to the list of its parent's children. */
229 dev->pfun = pfun;
230 pfun->child = dev;
[a35b458]231
[59dc181]232 return true;
233}
234
235/** Remove device from device tree.
236 *
237 * @param tree Device tree
238 * @param dev Device node
239 */
240void remove_dev_node(dev_tree_t *tree, dev_node_t *dev)
241{
242 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
[a35b458]243
[59dc181]244 log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
[a35b458]245
[59dc181]246 /* Remove node from the handle-to-node map. */
247 hash_table_remove(&tree->devman_devices, &dev->handle);
[a35b458]248
[59dc181]249 /* Unlink from parent function. */
250 dev->pfun->child = NULL;
251 dev->pfun = NULL;
[a35b458]252
[59dc181]253 dev->state = DEVICE_REMOVED;
254}
255
256/** Insert new function into device tree.
257 *
258 * @param tree The device tree.
[1b20da0]259 * @param fun The newly added function node.
[59dc181]260 * @param fun_name The name of the newly added function.
261 * @param dev Owning device node.
262 *
263 * @return True on success, false otherwise (insufficient resources
264 * etc.).
265 */
266bool insert_fun_node(dev_tree_t *tree, fun_node_t *fun, char *fun_name,
267 dev_node_t *dev)
268{
269 fun_node_t *pfun;
[a35b458]270
[59dc181]271 assert(fun_name != NULL);
272 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
[a35b458]273
[59dc181]274 /*
275 * The root function is a special case, it does not belong to any
276 * device so for the root function dev == NULL.
277 */
278 pfun = (dev != NULL) ? dev->pfun : NULL;
[a35b458]279
[59dc181]280 fun->name = fun_name;
281 if (!set_fun_path(tree, fun, pfun)) {
282 return false;
283 }
[a35b458]284
[59dc181]285 /* Add the node to the handle-to-node map. */
286 fun->handle = ++tree->current_handle;
287 hash_table_insert(&tree->devman_functions, &fun->devman_fun);
288
289 /* Add the node to the list of its parent's children. */
290 fun->dev = dev;
291 if (dev != NULL)
292 list_append(&fun->dev_functions, &dev->functions);
[a35b458]293
[59dc181]294 return true;
295}
296
297/** Remove function from device tree.
298 *
299 * @param tree Device tree
300 * @param node Function node to remove
301 */
302void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
303{
304 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
[a35b458]305
[59dc181]306 /* Remove the node from the handle-to-node map. */
307 hash_table_remove(&tree->devman_functions, &fun->handle);
[a35b458]308
[59dc181]309 /* Remove the node from the list of its parent's children. */
310 if (fun->dev != NULL)
311 list_remove(&fun->dev_functions);
[a35b458]312
[59dc181]313 fun->dev = NULL;
314 fun->state = FUN_REMOVED;
315}
316
317/** @}
318 */
Note: See TracBrowser for help on using the repository browser.