rcm_impl.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <librcm_impl.h>
#include "rcm_impl.h"
int, rcm_info_t **, int *);
/*
* The following ops are invoked when modules initiate librcm calls which
* require daemon processing. Cascaded RCM operations must come through
* this path.
*/
librcm_ops_t rcm_ops = {
};
/*
* Process a request or a notification on a subtree
*/
/*ARGSUSED2*/
static int
{
int error;
/*
* Find the node (root of subtree) in the resource tree, invoke
* appropriate callbacks for all clients hanging off the subtree,
* and mark the subtree with the appropriate state.
*
* NOTE: It's possible the node doesn't exist, which means no RCM
* consumer registered for the resource. In this case we silently
* succeed.
*/
if ((cmd == CMD_NOTIFY_CHANGE) ||
(cmd == CMD_REQUEST_CHANGE) ||
} else {
}
}
return (error);
}
/*
* When a resource is removed, notify all clients who registered for this
* particular resource.
*/
int
rcm_info_t **info)
{
int i;
int error;
int retval = RCM_SUCCESS;
"notify_resource_remove(%s, %ld, 0x%x, %d)\n", rsrcnames[i],
/*
* Mark state as issuing removal notification. Return failure
* if no DR request for this node exists.
*/
if (error != RCM_SUCCESS) {
continue;
}
/*
* delete the request entry from DR list
*/
if (error != RCM_SUCCESS)
}
return (retval);
}
/*
* Notify users that a resource has been resumed
*/
int
rcm_info_t **info)
{
int i;
int error;
int retval = RCM_SUCCESS;
state_info = NULL;
state_tuple = NULL;
/* Check resource state (was resource actually suspended?) */
flag |= RCM_SUSPENDED;
if (state_info)
"notify_resource_resume(%s, %ld, 0x%x, %d)\n",
/*
* Mark state as sending resumption notifications
*/
if (error != RCM_SUCCESS) {
continue;
}
if (error != RCM_SUCCESS)
}
return (retval);
}
/*
* Notify users that an offlined device is again available
*/
int
rcm_info_t **info)
{
int i;
int error;
int retval = RCM_SUCCESS;
"notify_resource_online(%s, %ld, 0x%x, %d)\n",
/*
* Mark state as sending onlining notifications
*/
if (error != RCM_SUCCESS) {
continue;
}
if (error != RCM_SUCCESS)
}
return (retval);
}
/*
* For offline and suspend, need to get the logic correct here. There are
* several cases:
*
* 1. It is a door call and RCM_QUERY is not set:
* run a QUERY; if that succeeds, run the operation.
*
* 2. It is a door call and RCM_QUERY is set:
* run the QUERY only.
*
* 3. It is not a door call:
* run the call, but look at the flag to see if the
* lock should be kept.
*/
/*
* Request permission to suspend a resource
*/
int
{
int i;
int error = RCM_SUCCESS;
/*
* Query the operation first. The return value of the query indicates
* if the operation should proceed and be implemented.
*/
return (error);
}
/*
* Implement the operation.
*/
/* Update the lock from a query state to the suspending state */
"suspend %s denied with error %d\n", rsrcnames[i],
error);
/*
* When called from a module, don't return EAGAIN.
* This is to avoid recursion if module always retries.
*/
return (RCM_CONFLICT);
}
return (error);
}
/* Actually suspend the resource */
if (error != RCM_SUCCESS) {
"suspend tree failed for %s\n", rsrcnames[i]);
return (error);
}
rsrcnames[i]);
/* Update the lock for the successful suspend */
}
return (RCM_SUCCESS);
}
/*
* Process a device removal request, reply is needed
*/
int
rcm_info_t **info)
{
int i;
int error = RCM_SUCCESS;
/*
* Query the operation first. The return value of the query indicates
* if the operation should proceed and be implemented.
*/
return (error);
}
/*
* Implement the operation.
*/
if (error != RCM_SUCCESS) {
"offline %s denied with error %d\n", rsrcnames[i],
error);
/*
* When called from a module, don't return EAGAIN.
* This is to avoid recursion if module always retries.
*/
return (RCM_CONFLICT);
}
return (error);
}
/* Actually offline the resource */
if (error != RCM_SUCCESS) {
"offline tree failed for %s\n", rsrcnames[i]);
return (error);
}
rsrcnames[i]);
/* Update the lock for the successful offline */
}
return (RCM_SUCCESS);
}
/*
* Add a resource client who wishes to interpose on DR, events, or capacity.
* Reply needed.
*/
int
rcm_info_t **infop)
{
int error = RCM_SUCCESS;
"add_resource_client(%s, %s, %ld, 0x%x)\n",
/*
* No need to register for / because it will never go away.
*/
"registering for / by %s has been turned into a no-op\n"),
modname);
return (RCM_SUCCESS);
}
/*
* Hold the rcm_req_lock so no dr request may come in while the
* registration is in progress.
*/
(void) mutex_lock(&rcm_req_lock);
/*
* Test if the requested registration is a noop, and return EALREADY
* if it is.
*/
(void) mutex_unlock(&rcm_req_lock);
return (RCM_FAILURE);
}
(void) mutex_unlock(&rcm_req_lock);
if ((flag & RCM_REGISTER_DR) &&
return (RCM_SUCCESS);
}
return (EALREADY);
}
/* If adding a new DR registration, reject if the resource is locked */
if (flag & RCM_REGISTER_DR) {
&info) != RCM_SUCCESS) {
/*
* The resource is being DR'ed, so return failure
*/
(void) mutex_unlock(&rcm_req_lock);
/*
* If caller doesn't care about info, free it
*/
if (infop)
else
return (RCM_CONFLICT);
}
}
/* The registration is new and allowable, so add it */
(void) mutex_unlock(&rcm_req_lock);
return (error);
}
/*
* Remove a resource client, who no longer wishes to interpose on either
* DR, events, or capacity.
*/
int
{
int error;
"remove_resource_client(%s, %s, %ld, 0x%x)\n",
/*
* Allow resource client to leave anytime, assume client knows what
* it is trying to do.
*/
return (ENOENT);
}
}
/*
* Reply is needed
*/
int
{
int rv = RCM_SUCCESS;
if (flag & RCM_DR_OPERATION) {
*info = rsrc_dr_info();
} else if (flag & RCM_MOD_INFO) {
*info = rsrc_mod_info();
} else {
}
return (rv);
}
int
{
int error;
return (error);
}
int
{
int error;
if (error != RCM_SUCCESS) {
"request state change query denied\n");
return (error);
}
}
return (RCM_SUCCESS);
if (error != RCM_SUCCESS) {
return (RCM_FAILURE);
}
return (error);
}
int
{
int error;
if (error != RCM_SUCCESS) {
return (RCM_FAILURE);
}
return (error);
}
int
{
int error;
int state;
char *s;
char *resolved;
/*
* Check for locks, first.
*/
dr_info = rsrc_dr_info();
if (dr_info) {
return (RCM_FAILURE);
s = (char *)rcm_info_rsrc(dr_info_tuple);
break;
}
}
if (state != RCM_STATE_UNKNOWN) {
return (RCM_SUCCESS);
}
}
/*
* No locks, so look for client states in the resource tree.
*
* NOTE: It's possible the node doesn't exist, which means no RCM
* consumer registered for the resource. In this case we silently
* succeed.
*/
break;
}
}
}
if (error == RCM_SUCCESS) {
}
return (error);
}
/*
* Perform a query of an offline or suspend.
*
* The return value of this function indicates whether the operation should
* be implemented (0 == No, 1 == Yes). Note that locks and client state
* changes will only persist if the caller is going to implement the operation.
*/
static int
int *errorp)
{
int i;
int error;
int final_error;
/* Only query for door calls, or when the RCM_QUERY flag is set */
return (1);
}
/* Lock all the resources. Fail the query in the case of a conflict. */
"process_resource_%s(%s, %ld, 0x%x, %d)\n",
/* The query goes no further if a resource cannot be locked */
if (error != RCM_SUCCESS) {
"%s query %s defined with error %d\n",
/*
* Replace EAGAIN with RCM_CONFLICT in the case of
* module callbacks; to avoid modules from trying
* again infinitely.
*/
}
goto finished;
}
}
/*
* All the resources were locked above, so use common_resource_op()
* to pass the query on to the clients. Accumulate the overall error
* value in 'final_error', before transferring it to 'error' at the end.
*/
/* Log the query (for tracing purposes). */
rsrcnames[i]);
/* Query the resource's clients through common_resource_op(). */
/*
* If a query fails, don't stop iterating through the loop.
* Just ensure that 'final_error' is set (if not already),
* log the error, and continue looping.
*
* In the case of a user who manually intervenes and retries
* the operation, this will maximize the extent of the query
* so that they experience fewer such iterations overall.
*/
if (error != RCM_SUCCESS) {
/* Log each query that failed along the way */
if (final_error != RCM_FAILURE) {
final_error = error;
}
}
}
error = final_error;
/*
* Tell the calling function not to proceed any further with the
* implementation phase of the operation if the query failed, or
* if the user's intent was to only query the operation.
*/
/*
* Since the operation won't be implemented, cancel the
* query (unlock resources and reverse client state changes).
*
* The cancellation routine cleans up everything for the entire
* operation, and thus it should only be called from the very
* root of the operation (e.g. when 'is_doorcall' is TRUE).
*/
if (is_doorcall != 0) {
}
return (0);
}
/* Otherwise, tell the caller to proceed with the implementation. */
*errorp = RCM_SUCCESS;
return (1);
}
/*
* Implementation of a query cancellation.
*
* The full scope of the query is already noted, so the scope of the operation
* does not need to be expanded in the same recursive manner that was used for
* the query itself. (Clients don't have to be called to cross namespaces.)
* Instead, the locks added to the DR request list during the query are scanned.
*/
static void
{
char rsrc[MAXPATHLEN];
/*
* Find every lock in the DR request list that is a part of this
* sequence. Call common_resource_op() with the QUERY_CANCEL flag to
* cancel each sub-operation, and then remove each lock from the list.
*
* The 'rsrc' buffer is required to retrieve the 'device' fields of
* matching DR request list entries in a way that's multi-thread safe.
*/
NULL);
}
}