#include <librcm_impl.h>
#include "rcm_impl.h"
static int query(char **, int, const char *, int, pid_t, uint_t, timespec_t *,
int, rcm_info_t **, int *);
static void cancel_query(int, const char *, pid_t, uint_t, int);
librcm_ops_t rcm_ops = {
add_resource_client,
remove_resource_client,
get_resource_info,
process_resource_suspend,
notify_resource_resume,
process_resource_offline,
notify_resource_online,
notify_resource_remove,
request_capacity_change,
notify_capacity_change,
notify_resource_event,
get_resource_state
};
static int
common_resource_op(int cmd, char *rsrcname, pid_t pid, uint_t flag, int seq_num,
timespec_t *interval, nvlist_t *nvl, rcm_info_t **info)
{
int error;
rsrc_node_t *node;
tree_walk_arg_t arg;
error = rsrc_node_find(rsrcname, 0, &node);
if ((error == RCM_SUCCESS) && (node != NULL)) {
arg.flag = flag;
arg.info = info;
arg.seq_num = seq_num;
arg.interval = interval;
arg.nvl = nvl;
arg.cmd = cmd;
if ((cmd == CMD_NOTIFY_CHANGE) ||
(cmd == CMD_REQUEST_CHANGE) ||
(cmd == CMD_EVENT)) {
error = rsrc_client_action_list(node->users, cmd, &arg);
} else {
error = rsrc_tree_action(node, cmd, &arg);
}
} else if ((error == RCM_SUCCESS) && (flag & RCM_RETIRE_REQUEST)) {
rcm_log_message(RCM_TRACE1, "No client. Returning "
"RCM_NO_CONSTRAINT: %s\n", rsrcname);
error = RCM_NO_CONSTRAINT;
}
return (error);
}
int
notify_resource_remove(char **rsrcnames, pid_t pid, uint_t flag, int seq_num,
rcm_info_t **info)
{
int i;
int error;
int retval = RCM_SUCCESS;
for (i = 0; rsrcnames[i] != NULL; i++) {
rcm_log_message(RCM_TRACE2,
"notify_resource_remove(%s, %ld, 0x%x, %d)\n", rsrcnames[i],
pid, flag, seq_num);
error = dr_req_update(rsrcnames[i], pid, flag,
RCM_STATE_REMOVING, seq_num, info);
if (error != RCM_SUCCESS) {
retval = error;
continue;
}
error = common_resource_op(CMD_REMOVE, rsrcnames[i], pid, flag,
seq_num, NULL, NULL, info);
dr_req_remove(rsrcnames[i], flag);
if (error != RCM_SUCCESS)
retval = error;
}
return (retval);
}
int
notify_resource_resume(char **rsrcnames, pid_t pid, uint_t flag, int seq_num,
rcm_info_t **info)
{
int i;
int error;
rcm_info_t *state_info;
rcm_info_tuple_t *state_tuple;
int retval = RCM_SUCCESS;
for (i = 0; rsrcnames[i] != NULL; i++) {
state_info = NULL;
state_tuple = NULL;
if (get_resource_state(rsrcnames[i], pid, &state_info) ||
((state_tuple = rcm_info_next(state_info, NULL)) == NULL) ||
(rcm_info_state(state_tuple) == RCM_STATE_SUSPEND))
flag |= RCM_SUSPENDED;
if (state_info)
rcm_free_info(state_info);
rcm_log_message(RCM_TRACE2,
"notify_resource_resume(%s, %ld, 0x%x, %d)\n",
rsrcnames[i], pid, flag, seq_num);
error = dr_req_update(rsrcnames[i], pid, flag,
RCM_STATE_RESUMING, seq_num, info);
if (error != RCM_SUCCESS) {
retval = error;
continue;
}
error = common_resource_op(CMD_RESUME, rsrcnames[i], pid, flag,
seq_num, NULL, NULL, info);
dr_req_remove(rsrcnames[i], flag);
if (error != RCM_SUCCESS)
retval = error;
}
return (retval);
}
int
notify_resource_online(char **rsrcnames, pid_t pid, uint_t flag, int seq_num,
rcm_info_t **info)
{
int i;
int error;
int retval = RCM_SUCCESS;
for (i = 0; rsrcnames[i] != NULL; i++) {
rcm_log_message(RCM_TRACE2,
"notify_resource_online(%s, %ld, 0x%x, %d)\n",
rsrcnames[i], pid, flag, seq_num);
error = dr_req_update(rsrcnames[i], pid, flag,
RCM_STATE_ONLINING, seq_num, info);
if (error != RCM_SUCCESS) {
retval = error;
continue;
}
error = common_resource_op(CMD_ONLINE, rsrcnames[i], pid, flag,
seq_num, NULL, NULL, info);
dr_req_remove(rsrcnames[i], flag);
if (error != RCM_SUCCESS)
retval = error;
}
return (retval);
}
int
process_resource_suspend(char **rsrcnames, pid_t pid, uint_t flag, int seq_num,
timespec_t *interval, rcm_info_t **info)
{
int i;
int error = RCM_SUCCESS;
int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0);
if (query(rsrcnames, CMD_SUSPEND, "suspend", RCM_STATE_SUSPEND_QUERYING,
pid, flag, interval, seq_num, info, &error) == 0) {
return (error);
}
for (i = 0; rsrcnames[i] != NULL; i++) {
if ((error = dr_req_update(rsrcnames[i], pid, flag,
RCM_STATE_SUSPENDING, seq_num, info)) != RCM_SUCCESS) {
rcm_log_message(RCM_DEBUG,
"suspend %s denied with error %d\n", rsrcnames[i],
error);
if (!is_doorcall && error == EAGAIN) {
return (RCM_CONFLICT);
}
return (error);
}
error = common_resource_op(CMD_SUSPEND, rsrcnames[i], pid,
flag, seq_num, interval, NULL, info);
if (error != RCM_SUCCESS) {
(void) dr_req_update(rsrcnames[i], pid, flag,
RCM_STATE_SUSPEND_FAIL, seq_num, info);
rcm_log_message(RCM_DEBUG,
"suspend tree failed for %s\n", rsrcnames[i]);
return (error);
}
rcm_log_message(RCM_TRACE3, "suspend tree succeeded for %s\n",
rsrcnames[i]);
(void) dr_req_update(rsrcnames[i], pid, flag,
RCM_STATE_SUSPEND, seq_num, info);
}
return (RCM_SUCCESS);
}
int
process_resource_offline(char **rsrcnames, pid_t pid, uint_t flag, int seq_num,
rcm_info_t **info)
{
int i;
int error = RCM_SUCCESS;
int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0);
if (query(rsrcnames, CMD_OFFLINE, "offline", RCM_STATE_OFFLINE_QUERYING,
pid, flag, NULL, seq_num, info, &error) == 0) {
return (error);
}
for (i = 0; rsrcnames[i] != NULL; i++) {
error = dr_req_update(rsrcnames[i], pid, flag,
RCM_STATE_OFFLINING, seq_num, info);
if (error != RCM_SUCCESS) {
rcm_log_message(RCM_DEBUG,
"offline %s denied with error %d\n", rsrcnames[i],
error);
if (!is_doorcall && error == EAGAIN) {
return (RCM_CONFLICT);
}
return (error);
}
error = common_resource_op(CMD_OFFLINE, rsrcnames[i], pid,
flag, seq_num, NULL, NULL, info);
if (error != RCM_SUCCESS) {
(void) dr_req_update(rsrcnames[i], pid, flag,
RCM_STATE_OFFLINE_FAIL, seq_num, info);
rcm_log_message(RCM_DEBUG,
"offline tree failed for %s\n", rsrcnames[i]);
return (error);
}
rcm_log_message(RCM_TRACE3, "offline tree succeeded for %s\n",
rsrcnames[i]);
(void) dr_req_update(rsrcnames[i], pid, flag,
RCM_STATE_OFFLINE, seq_num, info);
}
return (RCM_SUCCESS);
}
int
add_resource_client(char *modname, char *rsrcname, pid_t pid, uint_t flag,
rcm_info_t **infop)
{
int error = RCM_SUCCESS;
client_t *user = NULL;
rsrc_node_t *node = NULL;
rcm_info_t *info = NULL;
rcm_log_message(RCM_TRACE2,
"add_resource_client(%s, %s, %ld, 0x%x)\n",
modname, rsrcname, pid, flag);
if (strcmp(rsrcname, "/") == 0) {
rcm_log_message(RCM_INFO, gettext(
"registering for / by %s has been turned into a no-op\n"),
modname);
return (RCM_SUCCESS);
}
(void) mutex_lock(&rcm_req_lock);
error = rsrc_node_find(rsrcname, RSRC_NODE_CREATE, &node);
if ((error != RCM_SUCCESS) || (node == NULL)) {
(void) mutex_unlock(&rcm_req_lock);
return (RCM_FAILURE);
}
user = rsrc_client_find(modname, pid, &node->users);
if ((user != NULL) &&
((user->flag & (flag & RCM_REGISTER_MASK)) != 0)) {
(void) mutex_unlock(&rcm_req_lock);
if ((flag & RCM_REGISTER_DR) &&
(user->state == RCM_STATE_REMOVE)) {
user->state = RCM_STATE_ONLINE;
return (RCM_SUCCESS);
}
return (EALREADY);
}
if (flag & RCM_REGISTER_DR) {
if (rsrc_check_lock_conflicts(rsrcname, flag, LOCK_FOR_USE,
&info) != RCM_SUCCESS) {
(void) mutex_unlock(&rcm_req_lock);
if (infop)
*infop = info;
else
rcm_free_info(info);
return (RCM_CONFLICT);
}
}
error = rsrc_node_add_user(node, rsrcname, modname, pid, flag);
(void) mutex_unlock(&rcm_req_lock);
return (error);
}
int
remove_resource_client(char *modname, char *rsrcname, pid_t pid, uint_t flag)
{
int error;
rsrc_node_t *node;
rcm_log_message(RCM_TRACE2,
"remove_resource_client(%s, %s, %ld, 0x%x)\n",
modname, rsrcname, pid, flag);
error = rsrc_node_find(rsrcname, 0, &node);
if ((error != RCM_SUCCESS) || (node == NULL)) {
rcm_log_message(RCM_WARNING,
gettext("resource %s not found\n"), rsrcname);
return (ENOENT);
}
return (rsrc_node_remove_user(node, modname, pid, flag));
}
int
get_resource_info(char **rsrcnames, uint_t flag, int seq_num, rcm_info_t **info)
{
int rv = RCM_SUCCESS;
if (flag & RCM_DR_OPERATION) {
*info = rsrc_dr_info();
} else if (flag & RCM_MOD_INFO) {
*info = rsrc_mod_info();
} else {
rv = rsrc_usage_info(rsrcnames, flag, seq_num, info);
}
return (rv);
}
int
notify_resource_event(char *rsrcname, id_t pid, uint_t flag, int seq_num,
nvlist_t *event_data, rcm_info_t **info)
{
int error;
assert(flag == 0);
rcm_log_message(RCM_TRACE2, "notify_resource_event(%s, %ld, 0x%x)\n",
rsrcname, pid, flag);
error = common_resource_op(CMD_EVENT, rsrcname, pid, flag, seq_num,
NULL, event_data, info);
return (error);
}
int
request_capacity_change(char *rsrcname, id_t pid, uint_t flag, int seq_num,
nvlist_t *nvl, rcm_info_t **info)
{
int error;
int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0);
rcm_log_message(RCM_TRACE2,
"request_capacity_change(%s, %ld, 0x%x, %d)\n", rsrcname, pid,
flag, seq_num);
if (is_doorcall || (flag & RCM_QUERY)) {
error = common_resource_op(CMD_REQUEST_CHANGE, rsrcname, pid,
flag | RCM_QUERY, seq_num, NULL, nvl, info);
if (error != RCM_SUCCESS) {
rcm_log_message(RCM_DEBUG,
"request state change query denied\n");
return (error);
}
}
if (flag & RCM_QUERY)
return (RCM_SUCCESS);
error = common_resource_op(CMD_REQUEST_CHANGE, rsrcname, pid, flag,
seq_num, NULL, nvl, info);
if (error != RCM_SUCCESS) {
rcm_log_message(RCM_DEBUG, "request state change failed\n");
return (RCM_FAILURE);
}
rcm_log_message(RCM_TRACE3, "request state change succeeded\n");
return (error);
}
int
notify_capacity_change(char *rsrcname, id_t pid, uint_t flag, int seq_num,
nvlist_t *nvl, rcm_info_t **info)
{
int error;
rcm_log_message(RCM_TRACE2,
"notify_capacity_change(%s, %ld, 0x%x, %d)\n", rsrcname, pid,
flag, seq_num);
error = common_resource_op(CMD_NOTIFY_CHANGE, rsrcname, pid, flag,
seq_num, NULL, nvl, info);
if (error != RCM_SUCCESS) {
rcm_log_message(RCM_DEBUG, "notify state change failed\n");
return (RCM_FAILURE);
}
rcm_log_message(RCM_TRACE3, "notify state change succeeded\n");
return (error);
}
int
get_resource_state(char *rsrcname, pid_t pid, rcm_info_t **info)
{
int error;
int state;
char *s;
char *resolved;
rcm_info_t *dr_info = NULL;
rcm_info_tuple_t *dr_info_tuple = NULL;
rsrc_node_t *node;
client_t *client;
char *state_info = gettext("State of resource");
rcm_log_message(RCM_TRACE2, "get_resource_state(%s, %ld)\n",
rsrcname, pid);
dr_info = rsrc_dr_info();
if (dr_info) {
state = RCM_STATE_UNKNOWN;
if ((resolved = resolve_name(rsrcname)) == NULL)
return (RCM_FAILURE);
while (dr_info_tuple = rcm_info_next(dr_info, dr_info_tuple)) {
s = (char *)rcm_info_rsrc(dr_info_tuple);
if (s && (strcmp(resolved, s) == 0)) {
state = rcm_info_state(dr_info_tuple);
break;
}
}
free(resolved);
rcm_free_info(dr_info);
if (state != RCM_STATE_UNKNOWN) {
rcm_log_message(RCM_TRACE2,
"get_resource_state(%s)=%d\n", rsrcname, state);
add_busy_rsrc_to_list(rsrcname, pid, state, 0, NULL,
(char *)state_info, NULL, NULL, info);
return (RCM_SUCCESS);
}
}
error = rsrc_node_find(rsrcname, 0, &node);
state = RCM_STATE_ONLINE;
if ((error == RCM_SUCCESS) && (node != NULL)) {
for (client = node->users; client; client = client->next) {
if (client->state == RCM_STATE_OFFLINE_FAIL ||
client->state == RCM_STATE_OFFLINE_QUERY_FAIL ||
client->state == RCM_STATE_SUSPEND_FAIL ||
client->state == RCM_STATE_SUSPEND_QUERY_FAIL) {
state = client->state;
break;
}
if (client->state != RCM_STATE_ONLINE &&
client->state != RCM_STATE_REMOVE)
state = client->state;
}
}
if (error == RCM_SUCCESS) {
rcm_log_message(RCM_TRACE2, "get_resource_state(%s)=%d\n",
rsrcname, state);
add_busy_rsrc_to_list(rsrcname, pid, state, 0, NULL,
(char *)state_info, NULL, NULL, info);
}
return (error);
}
static int
query(char **rsrcnames, int cmd, const char *opname, int querystate, pid_t pid,
uint_t flag, timespec_t *interval, int seq_num, rcm_info_t **info,
int *errorp)
{
int i;
int error;
int final_error;
int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0);
if ((is_doorcall == 0) && ((flag & RCM_QUERY) == 0)) {
return (1);
}
for (i = 0; rsrcnames[i] != NULL; i++) {
rcm_log_message(RCM_TRACE2,
"process_resource_%s(%s, %ld, 0x%x, %d)\n",
opname, rsrcnames[i], pid, flag, seq_num);
error = dr_req_add(rsrcnames[i], pid, flag, querystate, seq_num,
NULL, info);
if (error != RCM_SUCCESS) {
rcm_log_message(RCM_DEBUG,
"%s query %s defined with error %d\n",
opname, rsrcnames[i], error);
if ((is_doorcall == 0) && (error == EAGAIN)) {
error = RCM_CONFLICT;
}
goto finished;
}
}
for (final_error = RCM_SUCCESS, i = 0; rsrcnames[i] != NULL; i++) {
rcm_log_message(RCM_TRACE2, "querying resource %s\n",
rsrcnames[i]);
error = common_resource_op(cmd, rsrcnames[i], pid,
flag | RCM_QUERY, seq_num, interval, NULL, info);
if (error != RCM_SUCCESS) {
rcm_log_message(RCM_DEBUG, "%s %s query denied\n",
opname, rsrcnames[i]);
if (final_error != RCM_FAILURE) {
final_error = error;
}
}
}
error = final_error;
finished:
if ((error != RCM_SUCCESS) || ((flag & RCM_QUERY) != 0)) {
if (is_doorcall != 0) {
cancel_query(cmd, opname, pid, flag, seq_num);
}
*errorp = error;
return (0);
}
*errorp = RCM_SUCCESS;
return (1);
}
static void
cancel_query(int cmd, const char *opname, pid_t pid, uint_t flag, int seq_num)
{
char rsrc[MAXPATHLEN];
while (dr_req_lookup(seq_num, rsrc) == RCM_SUCCESS) {
rcm_log_message(RCM_TRACE2, "%s query %s cancelled\n",
opname, rsrc);
(void) common_resource_op(cmd, rsrc, pid,
flag | RCM_QUERY | RCM_QUERY_CANCEL, seq_num, NULL, NULL,
NULL);
(void) dr_req_remove(rsrc, flag);
}
}