source: mainline/uspace/srv/devman/fun.c@ 832cbe7

Last change on this file since 832cbe7 was 3083c74, checked in by Jakub Jermar <jakub@…>, 7 years ago

Fix devman device reference counting

After commit 498ced1, create_dev_node() returns a dev pointer with an
implicit reference. Adding an extra reference for creation thus adds a
reference that will never be dropped and the device object will be
leaked.

This commit removes the extra reference.

  • Property mode set to 100644
File size: 10.7 KB
RevLine 
[38e52c92]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>
[02e5e34]35#include <loc.h>
[38e52c92]36
[02e5e34]37#include "dev.h"
[38e52c92]38#include "devman.h"
[02e5e34]39#include "devtree.h"
40#include "driver.h"
[38e52c92]41#include "fun.h"
[02e5e34]42#include "main.h"
43#include "loc.h"
[38e52c92]44
45static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *);
46
47/* Function nodes */
48
49/** Create a new function node.
50 *
51 * @return A function node structure.
52 */
53fun_node_t *create_fun_node(void)
54{
55 fun_node_t *fun;
56
57 fun = calloc(1, sizeof(fun_node_t));
58 if (fun == NULL)
59 return NULL;
[a35b458]60
[38e52c92]61 fun->state = FUN_INIT;
[498ced1]62 refcount_init(&fun->refcnt);
[38e52c92]63 fibril_mutex_initialize(&fun->busy_lock);
64 link_initialize(&fun->dev_functions);
65 list_initialize(&fun->match_ids.ids);
[a35b458]66
[38e52c92]67 return fun;
68}
69
70/** Delete a function node.
71 *
72 * @param fun The device node structure.
73 */
74void delete_fun_node(fun_node_t *fun)
75{
76 assert(fun->dev == NULL);
77 assert(fun->child == NULL);
[a35b458]78
[38e52c92]79 clean_match_ids(&fun->match_ids);
80 free(fun->name);
81 free(fun->pathname);
82 free(fun);
83}
84
85/** Increase function node reference count.
86 *
87 * @param fun Function node
88 */
89void fun_add_ref(fun_node_t *fun)
90{
[498ced1]91 refcount_up(&fun->refcnt);
[38e52c92]92}
93
94/** Decrease function node reference count.
95 *
96 * When the count drops to zero the function node is freed.
97 *
98 * @param fun Function node
99 */
100void fun_del_ref(fun_node_t *fun)
101{
[498ced1]102 if (refcount_down(&fun->refcnt))
[38e52c92]103 delete_fun_node(fun);
104}
105
106/** Make function busy for reconfiguration operations. */
107void fun_busy_lock(fun_node_t *fun)
108{
109 fibril_mutex_lock(&fun->busy_lock);
110}
111
112/** Mark end of reconfiguration operation. */
113void fun_busy_unlock(fun_node_t *fun)
114{
115 fibril_mutex_unlock(&fun->busy_lock);
116}
117
118/** Find the function node with the specified handle.
119 *
120 * @param tree The device tree where we look for the device node.
121 * @param handle The handle of the function.
122 * @return The function node.
123 */
124fun_node_t *find_fun_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
125{
126 fun_node_t *fun;
[a35b458]127
[38e52c92]128 assert(fibril_rwlock_is_locked(&tree->rwlock));
[a35b458]129
[38e52c92]130 ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
131 if (link == NULL)
132 return NULL;
[a35b458]133
[38e52c92]134 fun = hash_table_get_inst(link, fun_node_t, devman_fun);
[a35b458]135
[38e52c92]136 return fun;
137}
138
139/** Find the function node with the specified handle.
140 *
141 * @param tree The device tree where we look for the device node.
142 * @param handle The handle of the function.
143 * @return The function node.
144 */
145fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle)
146{
147 fun_node_t *fun = NULL;
[a35b458]148
[38e52c92]149 fibril_rwlock_read_lock(&tree->rwlock);
[a35b458]150
[38e52c92]151 fun = find_fun_node_no_lock(tree, handle);
152 if (fun != NULL)
153 fun_add_ref(fun);
[a35b458]154
[38e52c92]155 fibril_rwlock_read_unlock(&tree->rwlock);
[a35b458]156
[38e52c92]157 return fun;
158}
159
160/** Create and set device's full path in device tree.
161 *
162 * @param tree Device tree
163 * @param node The device's device node.
164 * @param parent The parent device node.
165 * @return True on success, false otherwise (insufficient
166 * resources etc.).
167 */
168bool set_fun_path(dev_tree_t *tree, fun_node_t *fun, fun_node_t *parent)
169{
170 assert(fibril_rwlock_is_write_locked(&tree->rwlock));
171 assert(fun->name != NULL);
[a35b458]172
[38e52c92]173 size_t pathsize = (str_size(fun->name) + 1);
174 if (parent != NULL)
175 pathsize += str_size(parent->pathname) + 1;
[a35b458]176
[38e52c92]177 fun->pathname = (char *) malloc(pathsize);
178 if (fun->pathname == NULL) {
179 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate device path.");
180 return false;
181 }
[a35b458]182
[38e52c92]183 if (parent != NULL) {
184 str_cpy(fun->pathname, pathsize, parent->pathname);
185 str_append(fun->pathname, pathsize, "/");
186 str_append(fun->pathname, pathsize, fun->name);
187 } else {
188 str_cpy(fun->pathname, pathsize, fun->name);
189 }
[a35b458]190
[38e52c92]191 return true;
192}
193
194/** Find function node with a specified path in the device tree.
[1b20da0]195 *
[38e52c92]196 * @param path The path of the function node in the device tree.
197 * @param tree The device tree.
198 * @return The function node if it is present in the tree, NULL
199 * otherwise.
200 */
201fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
202{
203 assert(path != NULL);
204
205 bool is_absolute = path[0] == '/';
206 if (!is_absolute) {
207 return NULL;
208 }
209
210 fibril_rwlock_read_lock(&tree->rwlock);
[a35b458]211
[38e52c92]212 fun_node_t *fun = tree->root_node;
213 fun_add_ref(fun);
214 /*
215 * Relative path to the function from its parent (but with '/' at the
216 * beginning)
217 */
218 char *rel_path = path;
219 char *next_path_elem = NULL;
220 bool cont = (rel_path[1] != '\0');
[a35b458]221
[38e52c92]222 while (cont && fun != NULL) {
223 next_path_elem = get_path_elem_end(rel_path + 1);
224 if (next_path_elem[0] == '/') {
225 cont = true;
226 next_path_elem[0] = 0;
227 } else {
228 cont = false;
229 }
[a35b458]230
[38e52c92]231 fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
232 fun_del_ref(fun);
233 fun = cfun;
[a35b458]234
[38e52c92]235 if (cont) {
236 /* Restore the original path. */
237 next_path_elem[0] = '/';
238 }
239 rel_path = next_path_elem;
240 }
[a35b458]241
[38e52c92]242 fibril_rwlock_read_unlock(&tree->rwlock);
[a35b458]243
[38e52c92]244 return fun;
245}
246
247/** Find function with a specified name belonging to given device.
248 *
249 * Device tree rwlock should be held at least for reading.
250 *
251 * @param tree Device tree
252 * @param dev Device the function belongs to.
253 * @param name Function name (not path).
254 * @return Function node.
255 * @retval NULL No function with given name.
256 */
257fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *dev,
258 const char *name)
259{
260 assert(name != NULL);
261 assert(fibril_rwlock_is_locked(&tree->rwlock));
262
[08bc23d]263 list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
[38e52c92]264 if (str_cmp(name, fun->name) == 0) {
265 fun_add_ref(fun);
266 return fun;
267 }
268 }
269
270 return NULL;
271}
272
273/** Find child function node with a specified name.
274 *
275 * Device tree rwlock should be held at least for reading.
276 *
277 * @param tree Device tree
278 * @param parent The parent function node.
279 * @param name The name of the child function.
280 * @return The child function node.
281 */
282static fun_node_t *find_node_child(dev_tree_t *tree, fun_node_t *pfun,
283 const char *name)
284{
285 return find_fun_node_in_device(tree, pfun->child, name);
286}
287
[b7fd2a0]288static errno_t assign_driver_fibril(void *arg)
[02e5e34]289{
290 dev_node_t *dev_node = (dev_node_t *) arg;
291 assign_driver(dev_node, &drivers_list, &device_tree);
292
293 /* Delete one reference we got from the caller. */
294 dev_del_ref(dev_node);
295 return EOK;
296}
297
[b7fd2a0]298errno_t fun_online(fun_node_t *fun)
[02e5e34]299{
300 dev_node_t *dev;
[a35b458]301
[02e5e34]302 fibril_rwlock_write_lock(&device_tree.rwlock);
[a35b458]303
[02e5e34]304 if (fun->state == FUN_ON_LINE) {
305 fibril_rwlock_write_unlock(&device_tree.rwlock);
306 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.",
307 fun->pathname);
308 return EOK;
309 }
[a35b458]310
[02e5e34]311 if (fun->ftype == fun_inner) {
312 dev = create_dev_node();
313 if (dev == NULL) {
314 fibril_rwlock_write_unlock(&device_tree.rwlock);
315 return ENOMEM;
316 }
[a35b458]317
[02e5e34]318 insert_dev_node(&device_tree, dev, fun);
319 }
[a35b458]320
[02e5e34]321 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
[a35b458]322
[02e5e34]323 if (fun->ftype == fun_inner) {
324 dev = fun->child;
325 assert(dev != NULL);
[a35b458]326
[02e5e34]327 /* Give one reference over to assign_driver_fibril(). */
328 dev_add_ref(dev);
[a35b458]329
[02e5e34]330 /*
331 * Try to find a suitable driver and assign it to the device. We do
332 * not want to block the current fibril that is used for processing
333 * incoming calls: we will launch a separate fibril to handle the
334 * driver assigning. That is because assign_driver can actually include
335 * task spawning which could take some time.
336 */
337 fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
338 if (assign_fibril == 0) {
339 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
340 "assigning driver.");
341 /* XXX Cleanup */
342 fibril_rwlock_write_unlock(&device_tree.rwlock);
343 return ENOMEM;
344 }
345 fibril_add_ready(assign_fibril);
346 } else
347 loc_register_tree_function(fun, &device_tree);
[a35b458]348
[ba0eac5]349 fun->state = FUN_ON_LINE;
[02e5e34]350 fibril_rwlock_write_unlock(&device_tree.rwlock);
[a35b458]351
[02e5e34]352 return EOK;
353}
354
[b7fd2a0]355errno_t fun_offline(fun_node_t *fun)
[02e5e34]356{
[b7fd2a0]357 errno_t rc;
[a35b458]358
[02e5e34]359 fibril_rwlock_write_lock(&device_tree.rwlock);
[a35b458]360
[02e5e34]361 if (fun->state == FUN_OFF_LINE) {
362 fibril_rwlock_write_unlock(&device_tree.rwlock);
363 log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
364 fun->pathname);
365 return EOK;
366 }
[a35b458]367
[02e5e34]368 if (fun->ftype == fun_inner) {
369 log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
370 fun->pathname);
[a35b458]371
[02e5e34]372 if (fun->child != NULL) {
373 dev_node_t *dev = fun->child;
374 device_state_t dev_state;
[a35b458]375
[02e5e34]376 dev_add_ref(dev);
377 dev_state = dev->state;
[a35b458]378
[02e5e34]379 fibril_rwlock_write_unlock(&device_tree.rwlock);
380
381 /* If device is owned by driver, ask driver to give it up. */
382 if (dev_state == DEVICE_USABLE) {
383 rc = driver_dev_remove(&device_tree, dev);
384 if (rc != EOK) {
385 dev_del_ref(dev);
386 return ENOTSUP;
387 }
388 }
[a35b458]389
[02e5e34]390 /* Verify that driver removed all functions */
391 fibril_rwlock_read_lock(&device_tree.rwlock);
392 if (!list_empty(&dev->functions)) {
393 fibril_rwlock_read_unlock(&device_tree.rwlock);
394 dev_del_ref(dev);
395 return EIO;
396 }
[a35b458]397
[02e5e34]398 driver_t *driver = dev->drv;
399 fibril_rwlock_read_unlock(&device_tree.rwlock);
[a35b458]400
[02e5e34]401 if (driver)
402 detach_driver(&device_tree, dev);
[a35b458]403
[02e5e34]404 fibril_rwlock_write_lock(&device_tree.rwlock);
405 remove_dev_node(&device_tree, dev);
[a35b458]406
[02e5e34]407 /* Delete ref created when node was inserted */
408 dev_del_ref(dev);
409 /* Delete ref created by dev_add_ref(dev) above */
410 dev_del_ref(dev);
411 }
412 } else {
413 /* Unregister from location service */
[96ef672]414 rc = loc_unregister_tree_function(fun, &device_tree);
[02e5e34]415 if (rc != EOK) {
416 fibril_rwlock_write_unlock(&device_tree.rwlock);
417 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
418 return EIO;
419 }
[a35b458]420
[02e5e34]421 fun->service_id = 0;
422 }
[a35b458]423
[02e5e34]424 fun->state = FUN_OFF_LINE;
425 fibril_rwlock_write_unlock(&device_tree.rwlock);
[a35b458]426
[02e5e34]427 return EOK;
428}
429
[38e52c92]430/** @}
431 */
Note: See TracBrowser for help on using the repository browser.