4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
21 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
22 * Use is subject to license terms.
25 #pragma ident "%Z%%M% %I% %E% SMI"
27 #include <librcm_impl.h>
30 static int query(char **, int, const char *, int, pid_t
, uint_t
, timespec_t
*,
31 int, rcm_info_t
**, int *);
32 static void cancel_query(int, const char *, pid_t
, uint_t
, int);
35 * The following ops are invoked when modules initiate librcm calls which
36 * require daemon processing. Cascaded RCM operations must come through
39 librcm_ops_t rcm_ops
= {
41 remove_resource_client
,
43 process_resource_suspend
,
44 notify_resource_resume
,
45 process_resource_offline
,
46 notify_resource_online
,
47 notify_resource_remove
,
48 request_capacity_change
,
49 notify_capacity_change
,
50 notify_resource_event
,
55 * Process a request or a notification on a subtree
59 common_resource_op(int cmd
, char *rsrcname
, pid_t pid
, uint_t flag
, int seq_num
,
60 timespec_t
*interval
, nvlist_t
*nvl
, rcm_info_t
**info
)
67 * Find the node (root of subtree) in the resource tree, invoke
68 * appropriate callbacks for all clients hanging off the subtree,
69 * and mark the subtree with the appropriate state.
71 * NOTE: It's possible the node doesn't exist, which means no RCM
72 * consumer registered for the resource. In this case we silently
75 error
= rsrc_node_find(rsrcname
, 0, &node
);
76 if ((error
== RCM_SUCCESS
) && (node
!= NULL
)) {
79 arg
.seq_num
= seq_num
;
80 arg
.interval
= interval
;
84 if ((cmd
== CMD_NOTIFY_CHANGE
) ||
85 (cmd
== CMD_REQUEST_CHANGE
) ||
87 error
= rsrc_client_action_list(node
->users
, cmd
, &arg
);
89 error
= rsrc_tree_action(node
, cmd
, &arg
);
91 } else if ((error
== RCM_SUCCESS
) && (flag
& RCM_RETIRE_REQUEST
)) {
93 * No matching node, so no client. This means there
94 * is no constraint (RCM wise) on this retire. Return
95 * RCM_NO_CONSTRAINT to indicate this
97 rcm_log_message(RCM_TRACE1
, "No client. Returning "
98 "RCM_NO_CONSTRAINT: %s\n", rsrcname
);
99 error
= RCM_NO_CONSTRAINT
;
106 * When a resource is removed, notify all clients who registered for this
107 * particular resource.
110 notify_resource_remove(char **rsrcnames
, pid_t pid
, uint_t flag
, int seq_num
,
115 int retval
= RCM_SUCCESS
;
117 for (i
= 0; rsrcnames
[i
] != NULL
; i
++) {
119 rcm_log_message(RCM_TRACE2
,
120 "notify_resource_remove(%s, %ld, 0x%x, %d)\n", rsrcnames
[i
],
124 * Mark state as issuing removal notification. Return failure
125 * if no DR request for this node exists.
127 error
= dr_req_update(rsrcnames
[i
], pid
, flag
,
128 RCM_STATE_REMOVING
, seq_num
, info
);
129 if (error
!= RCM_SUCCESS
) {
134 error
= common_resource_op(CMD_REMOVE
, rsrcnames
[i
], pid
, flag
,
135 seq_num
, NULL
, NULL
, info
);
138 * delete the request entry from DR list
140 dr_req_remove(rsrcnames
[i
], flag
);
142 if (error
!= RCM_SUCCESS
)
150 * Notify users that a resource has been resumed
153 notify_resource_resume(char **rsrcnames
, pid_t pid
, uint_t flag
, int seq_num
,
158 rcm_info_t
*state_info
;
159 rcm_info_tuple_t
*state_tuple
;
160 int retval
= RCM_SUCCESS
;
162 for (i
= 0; rsrcnames
[i
] != NULL
; i
++) {
167 /* Check resource state (was resource actually suspended?) */
168 if (get_resource_state(rsrcnames
[i
], pid
, &state_info
) ||
169 ((state_tuple
= rcm_info_next(state_info
, NULL
)) == NULL
) ||
170 (rcm_info_state(state_tuple
) == RCM_STATE_SUSPEND
))
171 flag
|= RCM_SUSPENDED
;
173 rcm_free_info(state_info
);
175 rcm_log_message(RCM_TRACE2
,
176 "notify_resource_resume(%s, %ld, 0x%x, %d)\n",
177 rsrcnames
[i
], pid
, flag
, seq_num
);
180 * Mark state as sending resumption notifications
182 error
= dr_req_update(rsrcnames
[i
], pid
, flag
,
183 RCM_STATE_RESUMING
, seq_num
, info
);
184 if (error
!= RCM_SUCCESS
) {
189 error
= common_resource_op(CMD_RESUME
, rsrcnames
[i
], pid
, flag
,
190 seq_num
, NULL
, NULL
, info
);
192 dr_req_remove(rsrcnames
[i
], flag
);
194 if (error
!= RCM_SUCCESS
)
202 * Notify users that an offlined device is again available
205 notify_resource_online(char **rsrcnames
, pid_t pid
, uint_t flag
, int seq_num
,
210 int retval
= RCM_SUCCESS
;
212 for (i
= 0; rsrcnames
[i
] != NULL
; i
++) {
214 rcm_log_message(RCM_TRACE2
,
215 "notify_resource_online(%s, %ld, 0x%x, %d)\n",
216 rsrcnames
[i
], pid
, flag
, seq_num
);
219 * Mark state as sending onlining notifications
221 error
= dr_req_update(rsrcnames
[i
], pid
, flag
,
222 RCM_STATE_ONLINING
, seq_num
, info
);
223 if (error
!= RCM_SUCCESS
) {
228 error
= common_resource_op(CMD_ONLINE
, rsrcnames
[i
], pid
, flag
,
229 seq_num
, NULL
, NULL
, info
);
231 dr_req_remove(rsrcnames
[i
], flag
);
233 if (error
!= RCM_SUCCESS
)
241 * For offline and suspend, need to get the logic correct here. There are
244 * 1. It is a door call and RCM_QUERY is not set:
245 * run a QUERY; if that succeeds, run the operation.
247 * 2. It is a door call and RCM_QUERY is set:
248 * run the QUERY only.
250 * 3. It is not a door call:
251 * run the call, but look at the flag to see if the
252 * lock should be kept.
256 * Request permission to suspend a resource
259 process_resource_suspend(char **rsrcnames
, pid_t pid
, uint_t flag
, int seq_num
,
260 timespec_t
*interval
, rcm_info_t
**info
)
263 int error
= RCM_SUCCESS
;
264 int is_doorcall
= ((seq_num
& SEQ_NUM_MASK
) == 0);
267 * Query the operation first. The return value of the query indicates
268 * if the operation should proceed and be implemented.
270 if (query(rsrcnames
, CMD_SUSPEND
, "suspend", RCM_STATE_SUSPEND_QUERYING
,
271 pid
, flag
, interval
, seq_num
, info
, &error
) == 0) {
276 * Implement the operation.
278 for (i
= 0; rsrcnames
[i
] != NULL
; i
++) {
280 /* Update the lock from a query state to the suspending state */
281 if ((error
= dr_req_update(rsrcnames
[i
], pid
, flag
,
282 RCM_STATE_SUSPENDING
, seq_num
, info
)) != RCM_SUCCESS
) {
284 rcm_log_message(RCM_DEBUG
,
285 "suspend %s denied with error %d\n", rsrcnames
[i
],
289 * When called from a module, don't return EAGAIN.
290 * This is to avoid recursion if module always retries.
292 if (!is_doorcall
&& error
== EAGAIN
) {
293 return (RCM_CONFLICT
);
299 /* Actually suspend the resource */
300 error
= common_resource_op(CMD_SUSPEND
, rsrcnames
[i
], pid
,
301 flag
, seq_num
, interval
, NULL
, info
);
302 if (error
!= RCM_SUCCESS
) {
303 (void) dr_req_update(rsrcnames
[i
], pid
, flag
,
304 RCM_STATE_SUSPEND_FAIL
, seq_num
, info
);
305 rcm_log_message(RCM_DEBUG
,
306 "suspend tree failed for %s\n", rsrcnames
[i
]);
310 rcm_log_message(RCM_TRACE3
, "suspend tree succeeded for %s\n",
313 /* Update the lock for the successful suspend */
314 (void) dr_req_update(rsrcnames
[i
], pid
, flag
,
315 RCM_STATE_SUSPEND
, seq_num
, info
);
318 return (RCM_SUCCESS
);
322 * Process a device removal request, reply is needed
325 process_resource_offline(char **rsrcnames
, pid_t pid
, uint_t flag
, int seq_num
,
329 int error
= RCM_SUCCESS
;
330 int is_doorcall
= ((seq_num
& SEQ_NUM_MASK
) == 0);
333 * Query the operation first. The return value of the query indicates
334 * if the operation should proceed and be implemented.
336 if (query(rsrcnames
, CMD_OFFLINE
, "offline", RCM_STATE_OFFLINE_QUERYING
,
337 pid
, flag
, NULL
, seq_num
, info
, &error
) == 0) {
342 * Implement the operation.
344 for (i
= 0; rsrcnames
[i
] != NULL
; i
++) {
346 error
= dr_req_update(rsrcnames
[i
], pid
, flag
,
347 RCM_STATE_OFFLINING
, seq_num
, info
);
348 if (error
!= RCM_SUCCESS
) {
349 rcm_log_message(RCM_DEBUG
,
350 "offline %s denied with error %d\n", rsrcnames
[i
],
354 * When called from a module, don't return EAGAIN.
355 * This is to avoid recursion if module always retries.
357 if (!is_doorcall
&& error
== EAGAIN
) {
358 return (RCM_CONFLICT
);
364 /* Actually offline the resource */
365 error
= common_resource_op(CMD_OFFLINE
, rsrcnames
[i
], pid
,
366 flag
, seq_num
, NULL
, NULL
, info
);
367 if (error
!= RCM_SUCCESS
) {
368 (void) dr_req_update(rsrcnames
[i
], pid
, flag
,
369 RCM_STATE_OFFLINE_FAIL
, seq_num
, info
);
370 rcm_log_message(RCM_DEBUG
,
371 "offline tree failed for %s\n", rsrcnames
[i
]);
375 rcm_log_message(RCM_TRACE3
, "offline tree succeeded for %s\n",
378 /* Update the lock for the successful offline */
379 (void) dr_req_update(rsrcnames
[i
], pid
, flag
,
380 RCM_STATE_OFFLINE
, seq_num
, info
);
383 return (RCM_SUCCESS
);
387 * Add a resource client who wishes to interpose on DR, events, or capacity.
391 add_resource_client(char *modname
, char *rsrcname
, pid_t pid
, uint_t flag
,
394 int error
= RCM_SUCCESS
;
395 client_t
*user
= NULL
;
396 rsrc_node_t
*node
= NULL
;
397 rcm_info_t
*info
= NULL
;
399 rcm_log_message(RCM_TRACE2
,
400 "add_resource_client(%s, %s, %ld, 0x%x)\n",
401 modname
, rsrcname
, pid
, flag
);
403 if (strcmp(rsrcname
, "/") == 0) {
405 * No need to register for / because it will never go away.
407 rcm_log_message(RCM_INFO
, gettext(
408 "registering for / by %s has been turned into a no-op\n"),
410 return (RCM_SUCCESS
);
414 * Hold the rcm_req_lock so no dr request may come in while the
415 * registration is in progress.
417 (void) mutex_lock(&rcm_req_lock
);
420 * Test if the requested registration is a noop, and return EALREADY
423 error
= rsrc_node_find(rsrcname
, RSRC_NODE_CREATE
, &node
);
424 if ((error
!= RCM_SUCCESS
) || (node
== NULL
)) {
425 (void) mutex_unlock(&rcm_req_lock
);
426 return (RCM_FAILURE
);
429 user
= rsrc_client_find(modname
, pid
, &node
->users
);
430 if ((user
!= NULL
) &&
431 ((user
->flag
& (flag
& RCM_REGISTER_MASK
)) != 0)) {
432 (void) mutex_unlock(&rcm_req_lock
);
433 if ((flag
& RCM_REGISTER_DR
) &&
434 (user
->state
== RCM_STATE_REMOVE
)) {
435 user
->state
= RCM_STATE_ONLINE
;
436 return (RCM_SUCCESS
);
441 /* If adding a new DR registration, reject if the resource is locked */
442 if (flag
& RCM_REGISTER_DR
) {
444 if (rsrc_check_lock_conflicts(rsrcname
, flag
, LOCK_FOR_USE
,
445 &info
) != RCM_SUCCESS
) {
447 * The resource is being DR'ed, so return failure
449 (void) mutex_unlock(&rcm_req_lock
);
452 * If caller doesn't care about info, free it
459 return (RCM_CONFLICT
);
463 /* The registration is new and allowable, so add it */
464 error
= rsrc_node_add_user(node
, rsrcname
, modname
, pid
, flag
);
465 (void) mutex_unlock(&rcm_req_lock
);
471 * Remove a resource client, who no longer wishes to interpose on either
472 * DR, events, or capacity.
475 remove_resource_client(char *modname
, char *rsrcname
, pid_t pid
, uint_t flag
)
480 rcm_log_message(RCM_TRACE2
,
481 "remove_resource_client(%s, %s, %ld, 0x%x)\n",
482 modname
, rsrcname
, pid
, flag
);
485 * Allow resource client to leave anytime, assume client knows what
486 * it is trying to do.
488 error
= rsrc_node_find(rsrcname
, 0, &node
);
489 if ((error
!= RCM_SUCCESS
) || (node
== NULL
)) {
490 rcm_log_message(RCM_WARNING
,
491 gettext("resource %s not found\n"), rsrcname
);
495 return (rsrc_node_remove_user(node
, modname
, pid
, flag
));
502 get_resource_info(char **rsrcnames
, uint_t flag
, int seq_num
, rcm_info_t
**info
)
504 int rv
= RCM_SUCCESS
;
506 if (flag
& RCM_DR_OPERATION
) {
507 *info
= rsrc_dr_info();
508 } else if (flag
& RCM_MOD_INFO
) {
509 *info
= rsrc_mod_info();
511 rv
= rsrc_usage_info(rsrcnames
, flag
, seq_num
, info
);
518 notify_resource_event(char *rsrcname
, id_t pid
, uint_t flag
, int seq_num
,
519 nvlist_t
*event_data
, rcm_info_t
**info
)
525 rcm_log_message(RCM_TRACE2
, "notify_resource_event(%s, %ld, 0x%x)\n",
526 rsrcname
, pid
, flag
);
528 error
= common_resource_op(CMD_EVENT
, rsrcname
, pid
, flag
, seq_num
,
529 NULL
, event_data
, info
);
535 request_capacity_change(char *rsrcname
, id_t pid
, uint_t flag
, int seq_num
,
536 nvlist_t
*nvl
, rcm_info_t
**info
)
539 int is_doorcall
= ((seq_num
& SEQ_NUM_MASK
) == 0);
541 rcm_log_message(RCM_TRACE2
,
542 "request_capacity_change(%s, %ld, 0x%x, %d)\n", rsrcname
, pid
,
545 if (is_doorcall
|| (flag
& RCM_QUERY
)) {
547 error
= common_resource_op(CMD_REQUEST_CHANGE
, rsrcname
, pid
,
548 flag
| RCM_QUERY
, seq_num
, NULL
, nvl
, info
);
550 if (error
!= RCM_SUCCESS
) {
551 rcm_log_message(RCM_DEBUG
,
552 "request state change query denied\n");
557 if (flag
& RCM_QUERY
)
558 return (RCM_SUCCESS
);
560 error
= common_resource_op(CMD_REQUEST_CHANGE
, rsrcname
, pid
, flag
,
561 seq_num
, NULL
, nvl
, info
);
563 if (error
!= RCM_SUCCESS
) {
564 rcm_log_message(RCM_DEBUG
, "request state change failed\n");
565 return (RCM_FAILURE
);
568 rcm_log_message(RCM_TRACE3
, "request state change succeeded\n");
574 notify_capacity_change(char *rsrcname
, id_t pid
, uint_t flag
, int seq_num
,
575 nvlist_t
*nvl
, rcm_info_t
**info
)
579 rcm_log_message(RCM_TRACE2
,
580 "notify_capacity_change(%s, %ld, 0x%x, %d)\n", rsrcname
, pid
,
583 error
= common_resource_op(CMD_NOTIFY_CHANGE
, rsrcname
, pid
, flag
,
584 seq_num
, NULL
, nvl
, info
);
586 if (error
!= RCM_SUCCESS
) {
587 rcm_log_message(RCM_DEBUG
, "notify state change failed\n");
588 return (RCM_FAILURE
);
591 rcm_log_message(RCM_TRACE3
, "notify state change succeeded\n");
597 get_resource_state(char *rsrcname
, pid_t pid
, rcm_info_t
**info
)
603 rcm_info_t
*dr_info
= NULL
;
604 rcm_info_tuple_t
*dr_info_tuple
= NULL
;
607 char *state_info
= gettext("State of resource");
609 rcm_log_message(RCM_TRACE2
, "get_resource_state(%s, %ld)\n",
613 * Check for locks, first.
615 dr_info
= rsrc_dr_info();
617 state
= RCM_STATE_UNKNOWN
;
618 if ((resolved
= resolve_name(rsrcname
)) == NULL
)
619 return (RCM_FAILURE
);
620 while (dr_info_tuple
= rcm_info_next(dr_info
, dr_info_tuple
)) {
621 s
= (char *)rcm_info_rsrc(dr_info_tuple
);
622 if (s
&& (strcmp(resolved
, s
) == 0)) {
623 state
= rcm_info_state(dr_info_tuple
);
628 rcm_free_info(dr_info
);
629 if (state
!= RCM_STATE_UNKNOWN
) {
630 rcm_log_message(RCM_TRACE2
,
631 "get_resource_state(%s)=%d\n", rsrcname
, state
);
632 add_busy_rsrc_to_list(rsrcname
, pid
, state
, 0, NULL
,
633 (char *)state_info
, NULL
, NULL
, info
);
634 return (RCM_SUCCESS
);
639 * No locks, so look for client states in the resource tree.
641 * NOTE: It's possible the node doesn't exist, which means no RCM
642 * consumer registered for the resource. In this case we silently
645 error
= rsrc_node_find(rsrcname
, 0, &node
);
646 state
= RCM_STATE_ONLINE
;
648 if ((error
== RCM_SUCCESS
) && (node
!= NULL
)) {
649 for (client
= node
->users
; client
; client
= client
->next
) {
650 if (client
->state
== RCM_STATE_OFFLINE_FAIL
||
651 client
->state
== RCM_STATE_OFFLINE_QUERY_FAIL
||
652 client
->state
== RCM_STATE_SUSPEND_FAIL
||
653 client
->state
== RCM_STATE_SUSPEND_QUERY_FAIL
) {
654 state
= client
->state
;
658 if (client
->state
!= RCM_STATE_ONLINE
&&
659 client
->state
!= RCM_STATE_REMOVE
)
660 state
= client
->state
;
664 if (error
== RCM_SUCCESS
) {
665 rcm_log_message(RCM_TRACE2
, "get_resource_state(%s)=%d\n",
667 add_busy_rsrc_to_list(rsrcname
, pid
, state
, 0, NULL
,
668 (char *)state_info
, NULL
, NULL
, info
);
675 * Perform a query of an offline or suspend.
677 * The return value of this function indicates whether the operation should
678 * be implemented (0 == No, 1 == Yes). Note that locks and client state
679 * changes will only persist if the caller is going to implement the operation.
682 query(char **rsrcnames
, int cmd
, const char *opname
, int querystate
, pid_t pid
,
683 uint_t flag
, timespec_t
*interval
, int seq_num
, rcm_info_t
**info
,
689 int is_doorcall
= ((seq_num
& SEQ_NUM_MASK
) == 0);
691 /* Only query for door calls, or when the RCM_QUERY flag is set */
692 if ((is_doorcall
== 0) && ((flag
& RCM_QUERY
) == 0)) {
696 /* Lock all the resources. Fail the query in the case of a conflict. */
697 for (i
= 0; rsrcnames
[i
] != NULL
; i
++) {
699 rcm_log_message(RCM_TRACE2
,
700 "process_resource_%s(%s, %ld, 0x%x, %d)\n",
701 opname
, rsrcnames
[i
], pid
, flag
, seq_num
);
703 error
= dr_req_add(rsrcnames
[i
], pid
, flag
, querystate
, seq_num
,
706 /* The query goes no further if a resource cannot be locked */
707 if (error
!= RCM_SUCCESS
) {
709 rcm_log_message(RCM_DEBUG
,
710 "%s query %s defined with error %d\n",
711 opname
, rsrcnames
[i
], error
);
714 * Replace EAGAIN with RCM_CONFLICT in the case of
715 * module callbacks; to avoid modules from trying
718 if ((is_doorcall
== 0) && (error
== EAGAIN
)) {
719 error
= RCM_CONFLICT
;
727 * All the resources were locked above, so use common_resource_op()
728 * to pass the query on to the clients. Accumulate the overall error
729 * value in 'final_error', before transferring it to 'error' at the end.
731 for (final_error
= RCM_SUCCESS
, i
= 0; rsrcnames
[i
] != NULL
; i
++) {
733 /* Log the query (for tracing purposes). */
734 rcm_log_message(RCM_TRACE2
, "querying resource %s\n",
737 /* Query the resource's clients through common_resource_op(). */
738 error
= common_resource_op(cmd
, rsrcnames
[i
], pid
,
739 flag
| RCM_QUERY
, seq_num
, interval
, NULL
, info
);
742 * If a query fails, don't stop iterating through the loop.
743 * Just ensure that 'final_error' is set (if not already),
744 * log the error, and continue looping.
746 * In the case of a user who manually intervenes and retries
747 * the operation, this will maximize the extent of the query
748 * so that they experience fewer such iterations overall.
750 if (error
!= RCM_SUCCESS
) {
752 /* Log each query that failed along the way */
753 rcm_log_message(RCM_DEBUG
, "%s %s query denied\n",
754 opname
, rsrcnames
[i
]);
756 if (final_error
!= RCM_FAILURE
) {
764 * Tell the calling function not to proceed any further with the
765 * implementation phase of the operation if the query failed, or
766 * if the user's intent was to only query the operation.
769 if ((error
!= RCM_SUCCESS
) || ((flag
& RCM_QUERY
) != 0)) {
772 * Since the operation won't be implemented, cancel the
773 * query (unlock resources and reverse client state changes).
775 * The cancellation routine cleans up everything for the entire
776 * operation, and thus it should only be called from the very
777 * root of the operation (e.g. when 'is_doorcall' is TRUE).
779 if (is_doorcall
!= 0) {
780 cancel_query(cmd
, opname
, pid
, flag
, seq_num
);
787 /* Otherwise, tell the caller to proceed with the implementation. */
788 *errorp
= RCM_SUCCESS
;
793 * Implementation of a query cancellation.
795 * The full scope of the query is already noted, so the scope of the operation
796 * does not need to be expanded in the same recursive manner that was used for
797 * the query itself. (Clients don't have to be called to cross namespaces.)
798 * Instead, the locks added to the DR request list during the query are scanned.
801 cancel_query(int cmd
, const char *opname
, pid_t pid
, uint_t flag
, int seq_num
)
803 char rsrc
[MAXPATHLEN
];
806 * Find every lock in the DR request list that is a part of this
807 * sequence. Call common_resource_op() with the QUERY_CANCEL flag to
808 * cancel each sub-operation, and then remove each lock from the list.
810 * The 'rsrc' buffer is required to retrieve the 'device' fields of
811 * matching DR request list entries in a way that's multi-thread safe.
813 while (dr_req_lookup(seq_num
, rsrc
) == RCM_SUCCESS
) {
815 rcm_log_message(RCM_TRACE2
, "%s query %s cancelled\n",
818 (void) common_resource_op(cmd
, rsrc
, pid
,
819 flag
| RCM_QUERY
| RCM_QUERY_CANCEL
, seq_num
, NULL
, NULL
,
822 (void) dr_req_remove(rsrc
, flag
);