1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 * 21 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 22 * Use is subject to license terms. 23 */ 24 25 #include <librcm_impl.h> 26 #include "rcm_impl.h" 27 28 static int query(char **, int, const char *, int, pid_t, uint_t, timespec_t *, 29 int, rcm_info_t **, int *); 30 static void cancel_query(int, const char *, pid_t, uint_t, int); 31 32 /* 33 * The following ops are invoked when modules initiate librcm calls which 34 * require daemon processing. Cascaded RCM operations must come through 35 * this path. 36 */ 37 librcm_ops_t rcm_ops = { 38 add_resource_client, 39 remove_resource_client, 40 get_resource_info, 41 process_resource_suspend, 42 notify_resource_resume, 43 process_resource_offline, 44 notify_resource_online, 45 notify_resource_remove, 46 request_capacity_change, 47 notify_capacity_change, 48 notify_resource_event, 49 get_resource_state 50 }; 51 52 /* 53 * Process a request or a notification on a subtree 54 */ 55 /*ARGSUSED2*/ 56 static int 57 common_resource_op(int cmd, char *rsrcname, pid_t pid, uint_t flag, int seq_num, 58 timespec_t *interval, nvlist_t *nvl, rcm_info_t **info) 59 { 60 int error; 61 rsrc_node_t *node; 62 tree_walk_arg_t arg; 63 64 /* 65 * Find the node (root of subtree) in the resource tree, invoke 66 * appropriate callbacks for all clients hanging off the subtree, 67 * and mark the subtree with the appropriate state. 68 * 69 * NOTE: It's possible the node doesn't exist, which means no RCM 70 * consumer registered for the resource. In this case we silently 71 * succeed. 72 */ 73 error = rsrc_node_find(rsrcname, 0, &node); 74 if ((error == RCM_SUCCESS) && (node != NULL)) { 75 arg.flag = flag; 76 arg.info = info; 77 arg.seq_num = seq_num; 78 arg.interval = interval; 79 arg.nvl = nvl; 80 arg.cmd = cmd; 81 82 if ((cmd == CMD_NOTIFY_CHANGE) || 83 (cmd == CMD_REQUEST_CHANGE) || 84 (cmd == CMD_EVENT)) { 85 error = rsrc_client_action_list(node->users, cmd, &arg); 86 } else { 87 error = rsrc_tree_action(node, cmd, &arg); 88 } 89 } else if ((error == RCM_SUCCESS) && (flag & RCM_RETIRE_REQUEST)) { 90 /* 91 * No matching node, so no client. This means there 92 * is no constraint (RCM wise) on this retire. Return 93 * RCM_NO_CONSTRAINT to indicate this 94 */ 95 rcm_log_message(RCM_TRACE1, "No client. Returning " 96 "RCM_NO_CONSTRAINT: %s\n", rsrcname); 97 error = RCM_NO_CONSTRAINT; 98 } 99 100 return (error); 101 } 102 103 /* 104 * When a resource is removed, notify all clients who registered for this 105 * particular resource. 106 */ 107 int 108 notify_resource_remove(char **rsrcnames, pid_t pid, uint_t flag, int seq_num, 109 rcm_info_t **info) 110 { 111 int i; 112 int error; 113 int retval = RCM_SUCCESS; 114 115 for (i = 0; rsrcnames[i] != NULL; i++) { 116 117 rcm_log_message(RCM_TRACE2, 118 "notify_resource_remove(%s, %ld, 0x%x, %d)\n", rsrcnames[i], 119 pid, flag, seq_num); 120 121 /* 122 * Mark state as issuing removal notification. Return failure 123 * if no DR request for this node exists. 124 */ 125 error = dr_req_update(rsrcnames[i], pid, flag, 126 RCM_STATE_REMOVING, seq_num, info); 127 if (error != RCM_SUCCESS) { 128 retval = error; 129 continue; 130 } 131 132 error = common_resource_op(CMD_REMOVE, rsrcnames[i], pid, flag, 133 seq_num, NULL, NULL, info); 134 135 /* 136 * delete the request entry from DR list 137 */ 138 dr_req_remove(rsrcnames[i], flag); 139 140 if (error != RCM_SUCCESS) 141 retval = error; 142 } 143 144 return (retval); 145 } 146 147 /* 148 * Notify users that a resource has been resumed 149 */ 150 int 151 notify_resource_resume(char **rsrcnames, pid_t pid, uint_t flag, int seq_num, 152 rcm_info_t **info) 153 { 154 int i; 155 int error; 156 rcm_info_t *state_info; 157 rcm_info_tuple_t *state_tuple; 158 int retval = RCM_SUCCESS; 159 160 for (i = 0; rsrcnames[i] != NULL; i++) { 161 162 state_info = NULL; 163 state_tuple = NULL; 164 165 /* Check resource state (was resource actually suspended?) */ 166 if (get_resource_state(rsrcnames[i], pid, &state_info) || 167 ((state_tuple = rcm_info_next(state_info, NULL)) == NULL) || 168 (rcm_info_state(state_tuple) == RCM_STATE_SUSPEND)) 169 flag |= RCM_SUSPENDED; 170 if (state_info) 171 rcm_free_info(state_info); 172 173 rcm_log_message(RCM_TRACE2, 174 "notify_resource_resume(%s, %ld, 0x%x, %d)\n", 175 rsrcnames[i], pid, flag, seq_num); 176 177 /* 178 * Mark state as sending resumption notifications 179 */ 180 error = dr_req_update(rsrcnames[i], pid, flag, 181 RCM_STATE_RESUMING, seq_num, info); 182 if (error != RCM_SUCCESS) { 183 retval = error; 184 continue; 185 } 186 187 error = common_resource_op(CMD_RESUME, rsrcnames[i], pid, flag, 188 seq_num, NULL, NULL, info); 189 190 dr_req_remove(rsrcnames[i], flag); 191 192 if (error != RCM_SUCCESS) 193 retval = error; 194 } 195 196 return (retval); 197 } 198 199 /* 200 * Notify users that an offlined device is again available 201 */ 202 int 203 notify_resource_online(char **rsrcnames, pid_t pid, uint_t flag, int seq_num, 204 rcm_info_t **info) 205 { 206 int i; 207 int error; 208 int retval = RCM_SUCCESS; 209 210 for (i = 0; rsrcnames[i] != NULL; i++) { 211 212 rcm_log_message(RCM_TRACE2, 213 "notify_resource_online(%s, %ld, 0x%x, %d)\n", 214 rsrcnames[i], pid, flag, seq_num); 215 216 /* 217 * Mark state as sending onlining notifications 218 */ 219 error = dr_req_update(rsrcnames[i], pid, flag, 220 RCM_STATE_ONLINING, seq_num, info); 221 if (error != RCM_SUCCESS) { 222 retval = error; 223 continue; 224 } 225 226 error = common_resource_op(CMD_ONLINE, rsrcnames[i], pid, flag, 227 seq_num, NULL, NULL, info); 228 229 dr_req_remove(rsrcnames[i], flag); 230 231 if (error != RCM_SUCCESS) 232 retval = error; 233 } 234 235 return (retval); 236 } 237 238 /* 239 * For offline and suspend, need to get the logic correct here. There are 240 * several cases: 241 * 242 * 1. It is a door call and RCM_QUERY is not set: 243 * run a QUERY; if that succeeds, run the operation. 244 * 245 * 2. It is a door call and RCM_QUERY is set: 246 * run the QUERY only. 247 * 248 * 3. It is not a door call: 249 * run the call, but look at the flag to see if the 250 * lock should be kept. 251 */ 252 253 /* 254 * Request permission to suspend a resource 255 */ 256 int 257 process_resource_suspend(char **rsrcnames, pid_t pid, uint_t flag, int seq_num, 258 timespec_t *interval, rcm_info_t **info) 259 { 260 int i; 261 int error = RCM_SUCCESS; 262 int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0); 263 264 /* 265 * Query the operation first. The return value of the query indicates 266 * if the operation should proceed and be implemented. 267 */ 268 if (query(rsrcnames, CMD_SUSPEND, "suspend", RCM_STATE_SUSPEND_QUERYING, 269 pid, flag, interval, seq_num, info, &error) == 0) { 270 return (error); 271 } 272 273 /* 274 * Implement the operation. 275 */ 276 for (i = 0; rsrcnames[i] != NULL; i++) { 277 278 /* Update the lock from a query state to the suspending state */ 279 if ((error = dr_req_update(rsrcnames[i], pid, flag, 280 RCM_STATE_SUSPENDING, seq_num, info)) != RCM_SUCCESS) { 281 282 rcm_log_message(RCM_DEBUG, 283 "suspend %s denied with error %d\n", rsrcnames[i], 284 error); 285 286 /* 287 * When called from a module, don't return EAGAIN. 288 * This is to avoid recursion if module always retries. 289 */ 290 if (!is_doorcall && error == EAGAIN) { 291 return (RCM_CONFLICT); 292 } 293 294 return (error); 295 } 296 297 /* Actually suspend the resource */ 298 error = common_resource_op(CMD_SUSPEND, rsrcnames[i], pid, 299 flag, seq_num, interval, NULL, info); 300 if (error != RCM_SUCCESS) { 301 (void) dr_req_update(rsrcnames[i], pid, flag, 302 RCM_STATE_SUSPEND_FAIL, seq_num, info); 303 rcm_log_message(RCM_DEBUG, 304 "suspend tree failed for %s\n", rsrcnames[i]); 305 return (error); 306 } 307 308 rcm_log_message(RCM_TRACE3, "suspend tree succeeded for %s\n", 309 rsrcnames[i]); 310 311 /* Update the lock for the successful suspend */ 312 (void) dr_req_update(rsrcnames[i], pid, flag, 313 RCM_STATE_SUSPEND, seq_num, info); 314 } 315 316 return (RCM_SUCCESS); 317 } 318 319 /* 320 * Process a device removal request, reply is needed 321 */ 322 int 323 process_resource_offline(char **rsrcnames, pid_t pid, uint_t flag, int seq_num, 324 rcm_info_t **info) 325 { 326 int i; 327 int error = RCM_SUCCESS; 328 int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0); 329 330 /* 331 * Query the operation first. The return value of the query indicates 332 * if the operation should proceed and be implemented. 333 */ 334 if (query(rsrcnames, CMD_OFFLINE, "offline", RCM_STATE_OFFLINE_QUERYING, 335 pid, flag, NULL, seq_num, info, &error) == 0) { 336 return (error); 337 } 338 339 /* 340 * Implement the operation. 341 */ 342 for (i = 0; rsrcnames[i] != NULL; i++) { 343 344 error = dr_req_update(rsrcnames[i], pid, flag, 345 RCM_STATE_OFFLINING, seq_num, info); 346 if (error != RCM_SUCCESS) { 347 rcm_log_message(RCM_DEBUG, 348 "offline %s denied with error %d\n", rsrcnames[i], 349 error); 350 351 /* 352 * When called from a module, don't return EAGAIN. 353 * This is to avoid recursion if module always retries. 354 */ 355 if (!is_doorcall && error == EAGAIN) { 356 return (RCM_CONFLICT); 357 } 358 359 return (error); 360 } 361 362 /* Actually offline the resource */ 363 error = common_resource_op(CMD_OFFLINE, rsrcnames[i], pid, 364 flag, seq_num, NULL, NULL, info); 365 if (error != RCM_SUCCESS) { 366 (void) dr_req_update(rsrcnames[i], pid, flag, 367 RCM_STATE_OFFLINE_FAIL, seq_num, info); 368 rcm_log_message(RCM_DEBUG, 369 "offline tree failed for %s\n", rsrcnames[i]); 370 return (error); 371 } 372 373 rcm_log_message(RCM_TRACE3, "offline tree succeeded for %s\n", 374 rsrcnames[i]); 375 376 /* Update the lock for the successful offline */ 377 (void) dr_req_update(rsrcnames[i], pid, flag, 378 RCM_STATE_OFFLINE, seq_num, info); 379 } 380 381 return (RCM_SUCCESS); 382 } 383 384 /* 385 * Add a resource client who wishes to interpose on DR, events, or capacity. 386 * Reply needed. 387 */ 388 int 389 add_resource_client(char *modname, char *rsrcname, pid_t pid, uint_t flag, 390 rcm_info_t **infop) 391 { 392 int error = RCM_SUCCESS; 393 client_t *user = NULL; 394 rsrc_node_t *node = NULL; 395 rcm_info_t *info = NULL; 396 397 rcm_log_message(RCM_TRACE2, 398 "add_resource_client(%s, %s, %ld, 0x%x)\n", 399 modname, rsrcname, pid, flag); 400 401 if (strcmp(rsrcname, "/") == 0) { 402 /* 403 * No need to register for / because it will never go away. 404 */ 405 rcm_log_message(RCM_INFO, gettext( 406 "registering for / by %s has been turned into a no-op\n"), 407 modname); 408 return (RCM_SUCCESS); 409 } 410 411 /* 412 * Hold the rcm_req_lock so no dr request may come in while the 413 * registration is in progress. 414 */ 415 (void) mutex_lock(&rcm_req_lock); 416 417 /* 418 * Test if the requested registration is a noop, and return EALREADY 419 * if it is. 420 */ 421 error = rsrc_node_find(rsrcname, RSRC_NODE_CREATE, &node); 422 if ((error != RCM_SUCCESS) || (node == NULL)) { 423 (void) mutex_unlock(&rcm_req_lock); 424 return (RCM_FAILURE); 425 } 426 427 user = rsrc_client_find(modname, pid, &node->users); 428 if ((user != NULL) && 429 ((user->flag & (flag & RCM_REGISTER_MASK)) != 0)) { 430 (void) mutex_unlock(&rcm_req_lock); 431 if ((flag & RCM_REGISTER_DR) && 432 (user->state == RCM_STATE_REMOVE)) { 433 user->state = RCM_STATE_ONLINE; 434 return (RCM_SUCCESS); 435 } 436 return (EALREADY); 437 } 438 439 /* If adding a new DR registration, reject if the resource is locked */ 440 if (flag & RCM_REGISTER_DR) { 441 442 if (rsrc_check_lock_conflicts(rsrcname, flag, LOCK_FOR_USE, 443 &info) != RCM_SUCCESS) { 444 /* 445 * The resource is being DR'ed, so return failure 446 */ 447 (void) mutex_unlock(&rcm_req_lock); 448 449 /* 450 * If caller doesn't care about info, free it 451 */ 452 if (infop) 453 *infop = info; 454 else 455 rcm_free_info(info); 456 457 return (RCM_CONFLICT); 458 } 459 } 460 461 /* The registration is new and allowable, so add it */ 462 error = rsrc_node_add_user(node, rsrcname, modname, pid, flag); 463 (void) mutex_unlock(&rcm_req_lock); 464 465 return (error); 466 } 467 468 /* 469 * Remove a resource client, who no longer wishes to interpose on either 470 * DR, events, or capacity. 471 */ 472 int 473 remove_resource_client(char *modname, char *rsrcname, pid_t pid, uint_t flag) 474 { 475 int error; 476 rsrc_node_t *node; 477 478 rcm_log_message(RCM_TRACE2, 479 "remove_resource_client(%s, %s, %ld, 0x%x)\n", 480 modname, rsrcname, pid, flag); 481 482 /* 483 * Allow resource client to leave anytime, assume client knows what 484 * it is trying to do. 485 */ 486 error = rsrc_node_find(rsrcname, 0, &node); 487 if ((error != RCM_SUCCESS) || (node == NULL)) { 488 rcm_log_message(RCM_WARNING, 489 gettext("resource %s not found\n"), rsrcname); 490 return (ENOENT); 491 } 492 493 return (rsrc_node_remove_user(node, modname, pid, flag)); 494 } 495 496 /* 497 * Reply is needed 498 */ 499 int 500 get_resource_info(char **rsrcnames, uint_t flag, int seq_num, rcm_info_t **info) 501 { 502 int rv = RCM_SUCCESS; 503 504 if (flag & RCM_DR_OPERATION) { 505 *info = rsrc_dr_info(); 506 } else if (flag & RCM_MOD_INFO) { 507 *info = rsrc_mod_info(); 508 } else { 509 rv = rsrc_usage_info(rsrcnames, flag, seq_num, info); 510 } 511 512 return (rv); 513 } 514 515 int 516 notify_resource_event(char *rsrcname, id_t pid, uint_t flag, int seq_num, 517 nvlist_t *event_data, rcm_info_t **info) 518 { 519 int error; 520 521 assert(flag == 0); 522 523 rcm_log_message(RCM_TRACE2, "notify_resource_event(%s, %ld, 0x%x)\n", 524 rsrcname, pid, flag); 525 526 error = common_resource_op(CMD_EVENT, rsrcname, pid, flag, seq_num, 527 NULL, event_data, info); 528 529 return (error); 530 } 531 532 int 533 request_capacity_change(char *rsrcname, id_t pid, uint_t flag, int seq_num, 534 nvlist_t *nvl, rcm_info_t **info) 535 { 536 int error; 537 int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0); 538 539 rcm_log_message(RCM_TRACE2, 540 "request_capacity_change(%s, %ld, 0x%x, %d)\n", rsrcname, pid, 541 flag, seq_num); 542 543 if (is_doorcall || (flag & RCM_QUERY)) { 544 545 error = common_resource_op(CMD_REQUEST_CHANGE, rsrcname, pid, 546 flag | RCM_QUERY, seq_num, NULL, nvl, info); 547 548 if (error != RCM_SUCCESS) { 549 rcm_log_message(RCM_DEBUG, 550 "request state change query denied\n"); 551 return (error); 552 } 553 } 554 555 if (flag & RCM_QUERY) 556 return (RCM_SUCCESS); 557 558 error = common_resource_op(CMD_REQUEST_CHANGE, rsrcname, pid, flag, 559 seq_num, NULL, nvl, info); 560 561 if (error != RCM_SUCCESS) { 562 rcm_log_message(RCM_DEBUG, "request state change failed\n"); 563 return (RCM_FAILURE); 564 } 565 566 rcm_log_message(RCM_TRACE3, "request state change succeeded\n"); 567 568 return (error); 569 } 570 571 int 572 notify_capacity_change(char *rsrcname, id_t pid, uint_t flag, int seq_num, 573 nvlist_t *nvl, rcm_info_t **info) 574 { 575 int error; 576 577 rcm_log_message(RCM_TRACE2, 578 "notify_capacity_change(%s, %ld, 0x%x, %d)\n", rsrcname, pid, 579 flag, seq_num); 580 581 error = common_resource_op(CMD_NOTIFY_CHANGE, rsrcname, pid, flag, 582 seq_num, NULL, nvl, info); 583 584 if (error != RCM_SUCCESS) { 585 rcm_log_message(RCM_DEBUG, "notify state change failed\n"); 586 return (RCM_FAILURE); 587 } 588 589 rcm_log_message(RCM_TRACE3, "notify state change succeeded\n"); 590 591 return (error); 592 } 593 594 int 595 get_resource_state(char *rsrcname, pid_t pid, rcm_info_t **info) 596 { 597 int error; 598 int state; 599 char *s; 600 char *resolved; 601 rcm_info_t *dr_info = NULL; 602 rcm_info_tuple_t *dr_info_tuple = NULL; 603 rsrc_node_t *node; 604 client_t *client; 605 char *state_info = gettext("State of resource"); 606 607 rcm_log_message(RCM_TRACE2, "get_resource_state(%s, %ld)\n", 608 rsrcname, pid); 609 610 /* 611 * Check for locks, first. 612 */ 613 dr_info = rsrc_dr_info(); 614 if (dr_info) { 615 state = RCM_STATE_UNKNOWN; 616 if ((resolved = resolve_name(rsrcname)) == NULL) 617 return (RCM_FAILURE); 618 while (dr_info_tuple = rcm_info_next(dr_info, dr_info_tuple)) { 619 s = (char *)rcm_info_rsrc(dr_info_tuple); 620 if (s && (strcmp(resolved, s) == 0)) { 621 state = rcm_info_state(dr_info_tuple); 622 break; 623 } 624 } 625 free(resolved); 626 rcm_free_info(dr_info); 627 if (state != RCM_STATE_UNKNOWN) { 628 rcm_log_message(RCM_TRACE2, 629 "get_resource_state(%s)=%d\n", rsrcname, state); 630 add_busy_rsrc_to_list(rsrcname, pid, state, 0, NULL, 631 (char *)state_info, NULL, NULL, info); 632 return (RCM_SUCCESS); 633 } 634 } 635 636 /* 637 * No locks, so look for client states in the resource tree. 638 * 639 * NOTE: It's possible the node doesn't exist, which means no RCM 640 * consumer registered for the resource. In this case we silently 641 * succeed. 642 */ 643 error = rsrc_node_find(rsrcname, 0, &node); 644 state = RCM_STATE_ONLINE; 645 646 if ((error == RCM_SUCCESS) && (node != NULL)) { 647 for (client = node->users; client; client = client->next) { 648 if (client->state == RCM_STATE_OFFLINE_FAIL || 649 client->state == RCM_STATE_OFFLINE_QUERY_FAIL || 650 client->state == RCM_STATE_SUSPEND_FAIL || 651 client->state == RCM_STATE_SUSPEND_QUERY_FAIL) { 652 state = client->state; 653 break; 654 } 655 656 if (client->state != RCM_STATE_ONLINE && 657 client->state != RCM_STATE_REMOVE) 658 state = client->state; 659 } 660 } 661 662 if (error == RCM_SUCCESS) { 663 rcm_log_message(RCM_TRACE2, "get_resource_state(%s)=%d\n", 664 rsrcname, state); 665 add_busy_rsrc_to_list(rsrcname, pid, state, 0, NULL, 666 (char *)state_info, NULL, NULL, info); 667 } 668 669 return (error); 670 } 671 672 /* 673 * Perform a query of an offline or suspend. 674 * 675 * The return value of this function indicates whether the operation should 676 * be implemented (0 == No, 1 == Yes). Note that locks and client state 677 * changes will only persist if the caller is going to implement the operation. 678 */ 679 static int 680 query(char **rsrcnames, int cmd, const char *opname, int querystate, pid_t pid, 681 uint_t flag, timespec_t *interval, int seq_num, rcm_info_t **info, 682 int *errorp) 683 { 684 int i; 685 int error; 686 int final_error; 687 int is_doorcall = ((seq_num & SEQ_NUM_MASK) == 0); 688 689 /* Only query for door calls, or when the RCM_QUERY flag is set */ 690 if ((is_doorcall == 0) && ((flag & RCM_QUERY) == 0)) { 691 return (1); 692 } 693 694 /* Lock all the resources. Fail the query in the case of a conflict. */ 695 for (i = 0; rsrcnames[i] != NULL; i++) { 696 697 rcm_log_message(RCM_TRACE2, 698 "process_resource_%s(%s, %ld, 0x%x, %d)\n", 699 opname, rsrcnames[i], pid, flag, seq_num); 700 701 error = dr_req_add(rsrcnames[i], pid, flag, querystate, seq_num, 702 NULL, info); 703 704 /* The query goes no further if a resource cannot be locked */ 705 if (error != RCM_SUCCESS) { 706 707 rcm_log_message(RCM_DEBUG, 708 "%s query %s defined with error %d\n", 709 opname, rsrcnames[i], error); 710 711 /* 712 * Replace EAGAIN with RCM_CONFLICT in the case of 713 * module callbacks; to avoid modules from trying 714 * again infinitely. 715 */ 716 if ((is_doorcall == 0) && (error == EAGAIN)) { 717 error = RCM_CONFLICT; 718 } 719 720 goto finished; 721 } 722 } 723 724 /* 725 * All the resources were locked above, so use common_resource_op() 726 * to pass the query on to the clients. Accumulate the overall error 727 * value in 'final_error', before transferring it to 'error' at the end. 728 */ 729 for (final_error = RCM_SUCCESS, i = 0; rsrcnames[i] != NULL; i++) { 730 731 /* Log the query (for tracing purposes). */ 732 rcm_log_message(RCM_TRACE2, "querying resource %s\n", 733 rsrcnames[i]); 734 735 /* Query the resource's clients through common_resource_op(). */ 736 error = common_resource_op(cmd, rsrcnames[i], pid, 737 flag | RCM_QUERY, seq_num, interval, NULL, info); 738 739 /* 740 * If a query fails, don't stop iterating through the loop. 741 * Just ensure that 'final_error' is set (if not already), 742 * log the error, and continue looping. 743 * 744 * In the case of a user who manually intervenes and retries 745 * the operation, this will maximize the extent of the query 746 * so that they experience fewer such iterations overall. 747 */ 748 if (error != RCM_SUCCESS) { 749 750 /* Log each query that failed along the way */ 751 rcm_log_message(RCM_DEBUG, "%s %s query denied\n", 752 opname, rsrcnames[i]); 753 754 if (final_error != RCM_FAILURE) { 755 final_error = error; 756 } 757 } 758 } 759 error = final_error; 760 761 /* 762 * Tell the calling function not to proceed any further with the 763 * implementation phase of the operation if the query failed, or 764 * if the user's intent was to only query the operation. 765 */ 766 finished: 767 if ((error != RCM_SUCCESS) || ((flag & RCM_QUERY) != 0)) { 768 769 /* 770 * Since the operation won't be implemented, cancel the 771 * query (unlock resources and reverse client state changes). 772 * 773 * The cancellation routine cleans up everything for the entire 774 * operation, and thus it should only be called from the very 775 * root of the operation (e.g. when 'is_doorcall' is TRUE). 776 */ 777 if (is_doorcall != 0) { 778 cancel_query(cmd, opname, pid, flag, seq_num); 779 } 780 781 *errorp = error; 782 return (0); 783 } 784 785 /* Otherwise, tell the caller to proceed with the implementation. */ 786 *errorp = RCM_SUCCESS; 787 return (1); 788 } 789 790 /* 791 * Implementation of a query cancellation. 792 * 793 * The full scope of the query is already noted, so the scope of the operation 794 * does not need to be expanded in the same recursive manner that was used for 795 * the query itself. (Clients don't have to be called to cross namespaces.) 796 * Instead, the locks added to the DR request list during the query are scanned. 797 */ 798 static void 799 cancel_query(int cmd, const char *opname, pid_t pid, uint_t flag, int seq_num) 800 { 801 char rsrc[MAXPATHLEN]; 802 803 /* 804 * Find every lock in the DR request list that is a part of this 805 * sequence. Call common_resource_op() with the QUERY_CANCEL flag to 806 * cancel each sub-operation, and then remove each lock from the list. 807 * 808 * The 'rsrc' buffer is required to retrieve the 'device' fields of 809 * matching DR request list entries in a way that's multi-thread safe. 810 */ 811 while (dr_req_lookup(seq_num, rsrc) == RCM_SUCCESS) { 812 813 rcm_log_message(RCM_TRACE2, "%s query %s cancelled\n", 814 opname, rsrc); 815 816 (void) common_resource_op(cmd, rsrc, pid, 817 flag | RCM_QUERY | RCM_QUERY_CANCEL, seq_num, NULL, NULL, 818 NULL); 819 820 (void) dr_req_remove(rsrc, flag); 821 } 822 } 823