source: mainline/uspace/srv/devman/fun.c

Last change on this file was 8300c72, checked in by Jiri Svoboda <jiri@…>, 4 months ago

Quiesce devices before proceeding with shutdown.

Only implemented for e1k, uhci and xhci.

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