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