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 22 /* 23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * DR control module for LDoms 28 */ 29 30 #include <sys/sysmacros.h> 31 #include <sys/modctl.h> 32 #include <sys/conf.h> 33 #include <sys/ddi.h> 34 #include <sys/sunddi.h> 35 #include <sys/ddi_impldefs.h> 36 #include <sys/stat.h> 37 #include <sys/door.h> 38 #include <sys/open.h> 39 #include <sys/note.h> 40 #include <sys/ldoms.h> 41 #include <sys/dr_util.h> 42 #include <sys/drctl.h> 43 #include <sys/drctl_impl.h> 44 45 46 static int drctl_attach(dev_info_t *, ddi_attach_cmd_t); 47 static int drctl_detach(dev_info_t *, ddi_detach_cmd_t); 48 static int drctl_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 49 50 static int drctl_open(dev_t *, int, int, cred_t *); 51 static int drctl_close(dev_t, int, int, cred_t *); 52 static int drctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 53 54 static void *pack_message(int, int, int, void *, size_t *, size_t *); 55 static int send_message(void *, size_t, drctl_resp_t **, size_t *); 56 57 58 /* 59 * Configuration data structures 60 */ 61 static struct cb_ops drctl_cb_ops = { 62 drctl_open, /* open */ 63 drctl_close, /* close */ 64 nodev, /* strategy */ 65 nodev, /* print */ 66 nodev, /* dump */ 67 nodev, /* read */ 68 nodev, /* write */ 69 drctl_ioctl, /* ioctl */ 70 nodev, /* devmap */ 71 nodev, /* mmap */ 72 nodev, /* segmap */ 73 nochpoll, /* poll */ 74 ddi_prop_op, /* prop_op */ 75 NULL, /* streamtab */ 76 D_MP | D_NEW, /* driver compatibility flag */ 77 CB_REV, /* cb_ops revision */ 78 nodev, /* async read */ 79 nodev /* async write */ 80 }; 81 82 83 static struct dev_ops drctl_ops = { 84 DEVO_REV, /* devo_rev */ 85 0, /* refcnt */ 86 drctl_getinfo, /* info */ 87 nulldev, /* identify */ 88 nulldev, /* probe */ 89 drctl_attach, /* attach */ 90 drctl_detach, /* detach */ 91 nodev, /* reset */ 92 &drctl_cb_ops, /* driver operations */ 93 NULL, /* bus operations */ 94 NULL, /* power */ 95 ddi_quiesce_not_needed, /* quiesce */ 96 }; 97 98 static struct modldrv modldrv = { 99 &mod_driverops, /* type of module - driver */ 100 "DR Control pseudo driver", 101 &drctl_ops 102 }; 103 104 static struct modlinkage modlinkage = { 105 MODREV_1, 106 &modldrv, 107 NULL 108 }; 109 110 111 /* 112 * Locking strategy 113 * 114 * One of the reasons for this module's existence is to serialize 115 * DR requests which might be coming from different sources. Only 116 * one operation is allowed to be in progress at any given time. 117 * 118 * A single lock word (the 'drc_busy' element below) is NULL 119 * when there is no operation in progress. When a client of this 120 * module initiates an operation it grabs the mutex 'drc_lock' in 121 * order to examine the lock word ('drc_busy'). If no other 122 * operation is in progress, the lock word will be NULL. If so, 123 * a cookie which uniquely identifies the requestor is stored in 124 * the lock word, and the mutex is released. Attempts by other 125 * clients to initiate an operation will fail. 126 * 127 * When the lock-holding client's operation is completed, the 128 * client will call a "finalize" function in this module, providing 129 * the cookie passed with the original request. Since the cookie 130 * matches, the operation will succeed and the lock word will be 131 * cleared. At this point, an new operation may be initiated. 132 */ 133 134 /* 135 * Driver private data 136 */ 137 static struct drctl_unit { 138 kmutex_t drc_lock; /* global driver lock */ 139 dev_info_t *drc_dip; /* dev_info pointer */ 140 kcondvar_t drc_busy_cv; /* block for !busy */ 141 drctl_cookie_t drc_busy; /* NULL if free else a unique */ 142 /* identifier for caller */ 143 int drc_cmd; /* the cmd underway (or -1) */ 144 int drc_flags; /* saved flag from above cmd */ 145 int drc_inst; /* our single instance */ 146 uint_t drc_state; /* driver state */ 147 } drctl_state; 148 149 static struct drctl_unit *drctlp = &drctl_state; 150 151 int 152 _init(void) 153 { 154 int rv; 155 156 drctlp->drc_inst = -1; 157 mutex_init(&drctlp->drc_lock, NULL, MUTEX_DRIVER, NULL); 158 cv_init(&drctlp->drc_busy_cv, NULL, CV_DRIVER, NULL); 159 160 if ((rv = mod_install(&modlinkage)) != 0) 161 mutex_destroy(&drctlp->drc_lock); 162 163 return (rv); 164 } 165 166 167 int 168 _fini(void) 169 { 170 int rv; 171 172 if ((rv = mod_remove(&modlinkage)) != 0) 173 return (rv); 174 cv_destroy(&drctlp->drc_busy_cv); 175 mutex_destroy(&drctlp->drc_lock); 176 return (0); 177 } 178 179 180 int 181 _info(struct modinfo *modinfop) 182 { 183 return (mod_info(&modlinkage, modinfop)); 184 } 185 186 187 /* 188 * Do the attach work 189 */ 190 static int 191 drctl_do_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 192 { 193 _NOTE(ARGUNUSED(cmd)) 194 195 char *str = "drctl_do_attach"; 196 int retval = DDI_SUCCESS; 197 198 if (drctlp->drc_inst != -1) { 199 cmn_err(CE_WARN, "%s: an instance is already attached!", str); 200 return (DDI_FAILURE); 201 } 202 drctlp->drc_inst = ddi_get_instance(dip); 203 204 retval = ddi_create_minor_node(dip, "drctl", S_IFCHR, 205 drctlp->drc_inst, DDI_PSEUDO, 0); 206 if (retval != DDI_SUCCESS) { 207 cmn_err(CE_WARN, "%s: can't create minor node", str); 208 drctlp->drc_inst = -1; 209 return (retval); 210 } 211 212 drctlp->drc_dip = dip; 213 ddi_report_dev(dip); 214 215 return (retval); 216 } 217 218 219 static int 220 drctl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 221 { 222 switch (cmd) { 223 case DDI_ATTACH: 224 return (drctl_do_attach(dip, cmd)); 225 226 default: 227 return (DDI_FAILURE); 228 } 229 } 230 231 232 /* ARGSUSED */ 233 static int 234 drctl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 235 { 236 switch (cmd) { 237 case DDI_DETACH: 238 drctlp->drc_inst = -1; 239 ddi_remove_minor_node(dip, "drctl"); 240 return (DDI_SUCCESS); 241 242 default: 243 return (DDI_FAILURE); 244 } 245 } 246 247 static int 248 drctl_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 249 { 250 _NOTE(ARGUNUSED(dip, cmd, arg, resultp)) 251 252 return (0); 253 } 254 255 static int 256 drctl_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 257 { 258 _NOTE(ARGUNUSED(devp, flag, cred_p)) 259 260 if (otyp != OTYP_CHR) 261 return (EINVAL); 262 263 return (0); 264 } 265 266 static int 267 drctl_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 268 { 269 _NOTE(ARGUNUSED(dev, flag, otyp, cred_p)) 270 271 return (0); 272 } 273 274 /* 275 * Create a reponse structure which includes an array of drctl_rsrc_t 276 * structures in which each status element is set to the 'status' 277 * arg. There is no error text, so set the 'offset' elements to 0. 278 */ 279 static drctl_resp_t * 280 drctl_generate_resp(drctl_rsrc_t *res, 281 int count, size_t *rsize, drctl_status_t status) 282 { 283 int i; 284 size_t size; 285 drctl_rsrc_t *rsrc; 286 drctl_resp_t *resp; 287 288 size = offsetof(drctl_resp_t, resp_resources) + (count * sizeof (*res)); 289 resp = kmem_alloc(size, KM_SLEEP); 290 DR_DBG_KMEM("%s: alloc addr %p size %ld\n", 291 __func__, (void *)resp, size); 292 293 resp->resp_type = DRCTL_RESP_OK; 294 rsrc = resp->resp_resources; 295 296 bcopy(res, rsrc, count * sizeof (*res)); 297 298 for (i = 0; i < count; i++) { 299 rsrc[i].status = status; 300 rsrc[i].offset = 0; 301 } 302 303 *rsize = size; 304 305 return (resp); 306 } 307 308 /* 309 * Generate an error response message. 310 */ 311 static drctl_resp_t * 312 drctl_generate_err_resp(char *msg, size_t *size) 313 { 314 drctl_resp_t *resp; 315 316 ASSERT(msg != NULL); 317 ASSERT(size != NULL); 318 319 *size = offsetof(drctl_resp_t, resp_err_msg) + strlen(msg) + 1; 320 resp = kmem_alloc(*size, KM_SLEEP); 321 DR_DBG_KMEM("%s: alloc addr %p size %ld\n", 322 __func__, (void *)resp, *size); 323 324 resp->resp_type = DRCTL_RESP_ERR; 325 (void) strcpy(resp->resp_err_msg, msg); 326 327 return (resp); 328 } 329 330 /* 331 * Since response comes from userland, verify that it is at least the 332 * minimum size based on the size of the original request. Verify 333 * that any offsets to error strings are within the string area of 334 * the response and, force the string area to be null-terminated. 335 */ 336 static int 337 verify_response(int cmd, 338 int count, drctl_resp_t *resp, size_t sent_len, size_t resp_len) 339 { 340 drctl_rsrc_t *rsrc = resp->resp_resources; 341 size_t rcvd_len = resp_len - (offsetof(drctl_resp_t, resp_resources)); 342 int is_cpu = 0; 343 int i; 344 345 switch (cmd) { 346 case DRCTL_CPU_CONFIG_REQUEST: 347 case DRCTL_CPU_UNCONFIG_REQUEST: 348 if (rcvd_len < sent_len) 349 return (EIO); 350 is_cpu = 1; 351 break; 352 case DRCTL_IO_UNCONFIG_REQUEST: 353 case DRCTL_IO_CONFIG_REQUEST: 354 if (count != 1) 355 return (EIO); 356 break; 357 case DRCTL_MEM_CONFIG_REQUEST: 358 case DRCTL_MEM_UNCONFIG_REQUEST: 359 break; 360 default: 361 return (EIO); 362 } 363 364 for (i = 0; i < count; i++) 365 if ((rsrc[i].offset > 0) && 366 /* string can't be inside the bounds of original request */ 367 (((rsrc[i].offset < sent_len) && is_cpu) || 368 /* string must start inside the message */ 369 (rsrc[i].offset >= rcvd_len))) 370 return (EIO); 371 372 /* If there are any strings, terminate the string area. */ 373 if (rcvd_len > sent_len) 374 *((char *)rsrc + rcvd_len - 1) = '\0'; 375 376 return (0); 377 } 378 379 static int 380 drctl_config_common(int cmd, int flags, drctl_rsrc_t *res, 381 int count, drctl_resp_t **rbuf, size_t *rsize, size_t *rq_size) 382 { 383 int rv = 0; 384 size_t size; 385 char *bufp; 386 387 switch (cmd) { 388 case DRCTL_CPU_CONFIG_REQUEST: 389 case DRCTL_CPU_CONFIG_NOTIFY: 390 case DRCTL_CPU_UNCONFIG_REQUEST: 391 case DRCTL_CPU_UNCONFIG_NOTIFY: 392 case DRCTL_IO_UNCONFIG_REQUEST: 393 case DRCTL_IO_UNCONFIG_NOTIFY: 394 case DRCTL_IO_CONFIG_REQUEST: 395 case DRCTL_IO_CONFIG_NOTIFY: 396 case DRCTL_MEM_CONFIG_REQUEST: 397 case DRCTL_MEM_CONFIG_NOTIFY: 398 case DRCTL_MEM_UNCONFIG_REQUEST: 399 case DRCTL_MEM_UNCONFIG_NOTIFY: 400 rv = 0; 401 break; 402 default: 403 rv = ENOTSUP; 404 break; 405 } 406 407 if (rv != 0) { 408 DR_DBG_CTL("%s: invalid cmd %d\n", __func__, cmd); 409 return (rv); 410 } 411 412 /* 413 * If the operation is a FORCE, we don't send a message to 414 * the daemon. But, the upstream clients still expect a 415 * response, so generate a response with all ops 'allowed'. 416 */ 417 if (flags == DRCTL_FLAG_FORCE) { 418 if (rbuf != NULL) 419 *rbuf = drctl_generate_resp(res, 420 count, rsize, DRCTL_STATUS_ALLOW); 421 return (0); 422 } 423 424 bufp = pack_message(cmd, flags, count, (void *)res, &size, rq_size); 425 DR_DBG_CTL("%s: from pack_message, bufp = %p size %ld\n", 426 __func__, (void *)bufp, size); 427 428 if (bufp == NULL || size == 0) 429 return (EINVAL); 430 431 return (send_message(bufp, size, rbuf, rsize)); 432 } 433 434 /* 435 * Prepare for a reconfig operation. 436 */ 437 int 438 drctl_config_init(int cmd, int flags, drctl_rsrc_t *res, 439 int count, drctl_resp_t **rbuf, size_t *rsize, drctl_cookie_t ck) 440 { 441 static char inval_msg[] = "Invalid command format received.\n"; 442 static char unsup_msg[] = "Unsuppported command received.\n"; 443 static char unk_msg [] = "Failure reason unknown.\n"; 444 static char rsp_msg [] = "Invalid response from " 445 "reconfiguration daemon.\n"; 446 static char drd_msg [] = "Cannot communicate with reconfiguration " 447 "daemon (drd) in target domain.\n" 448 "drd(8) SMF service may not be enabled.\n"; 449 static char busy_msg [] = "Busy executing earlier command; " 450 "please try again later.\n"; 451 size_t rq_size; 452 char *ermsg; 453 int rv; 454 455 if (ck == 0) { 456 *rbuf = drctl_generate_err_resp(inval_msg, rsize); 457 458 return (EINVAL); 459 } 460 461 mutex_enter(&drctlp->drc_lock); 462 if (drctlp->drc_busy != NULL) { 463 mutex_exit(&drctlp->drc_lock); 464 *rbuf = drctl_generate_err_resp(busy_msg, rsize); 465 466 return (EBUSY); 467 } 468 469 DR_DBG_CTL("%s: cmd %d flags %d res %p count %d\n", 470 __func__, cmd, flags, (void *)res, count); 471 472 /* Mark the link busy. Below we will fill in the actual cookie. */ 473 drctlp->drc_busy = (drctl_cookie_t)-1; 474 mutex_exit(&drctlp->drc_lock); 475 476 rv = drctl_config_common(cmd, flags, res, count, rbuf, rsize, &rq_size); 477 if (rv == 0) { 478 /* 479 * If the upcall to the daemon returned successfully, we 480 * still need to validate the format of the returned msg. 481 */ 482 if ((rv = verify_response(cmd, 483 count, *rbuf, rq_size, *rsize)) != 0) { 484 DR_DBG_KMEM("%s: free addr %p size %ld\n", 485 __func__, (void *)*rbuf, *rsize); 486 kmem_free(*rbuf, *rsize); 487 *rbuf = drctl_generate_err_resp(rsp_msg, rsize); 488 drctlp->drc_busy = NULL; 489 cv_broadcast(&drctlp->drc_busy_cv); 490 } else { /* message format is valid */ 491 drctlp->drc_busy = ck; 492 drctlp->drc_cmd = cmd; 493 drctlp->drc_flags = flags; 494 } 495 } else { 496 switch (rv) { 497 case ENOTSUP: 498 ermsg = unsup_msg; 499 break; 500 case EIO: 501 ermsg = drd_msg; 502 break; 503 default: 504 ermsg = unk_msg; 505 break; 506 } 507 508 *rbuf = drctl_generate_err_resp(ermsg, rsize); 509 510 drctlp->drc_cmd = -1; 511 drctlp->drc_flags = 0; 512 drctlp->drc_busy = NULL; 513 cv_broadcast(&drctlp->drc_busy_cv); 514 } 515 return (rv); 516 } 517 518 /* 519 * Complete a reconfig operation. 520 */ 521 int 522 drctl_config_fini(drctl_cookie_t ck, drctl_rsrc_t *res, int count) 523 { 524 int rv; 525 int notify_cmd; 526 int flags; 527 size_t rq_size; 528 529 mutex_enter(&drctlp->drc_lock); 530 if (drctlp->drc_busy != ck) { 531 mutex_exit(&drctlp->drc_lock); 532 return (EBUSY); 533 } 534 mutex_exit(&drctlp->drc_lock); 535 536 flags = drctlp->drc_flags; 537 /* 538 * Flip the saved _REQUEST command to its corresponding 539 * _NOTIFY command. 540 */ 541 switch (drctlp->drc_cmd) { 542 case DRCTL_CPU_CONFIG_REQUEST: 543 notify_cmd = DRCTL_CPU_CONFIG_NOTIFY; 544 break; 545 546 case DRCTL_CPU_UNCONFIG_REQUEST: 547 notify_cmd = DRCTL_CPU_UNCONFIG_NOTIFY; 548 break; 549 550 case DRCTL_IO_UNCONFIG_REQUEST: 551 notify_cmd = DRCTL_IO_UNCONFIG_NOTIFY; 552 break; 553 554 case DRCTL_IO_CONFIG_REQUEST: 555 notify_cmd = DRCTL_IO_CONFIG_NOTIFY; 556 break; 557 558 case DRCTL_MEM_CONFIG_REQUEST: 559 notify_cmd = DRCTL_MEM_CONFIG_NOTIFY; 560 break; 561 562 case DRCTL_MEM_UNCONFIG_REQUEST: 563 notify_cmd = DRCTL_MEM_UNCONFIG_NOTIFY; 564 break; 565 566 default: 567 /* none of the above should have been accepted in _init */ 568 ASSERT(0); 569 cmn_err(CE_CONT, 570 "drctl_config_fini: bad cmd %d\n", drctlp->drc_cmd); 571 rv = EINVAL; 572 goto done; 573 } 574 575 rv = drctl_config_common(notify_cmd, 576 flags, res, count, NULL, 0, &rq_size); 577 578 done: 579 drctlp->drc_cmd = -1; 580 drctlp->drc_flags = 0; 581 drctlp->drc_busy = NULL; 582 cv_broadcast(&drctlp->drc_busy_cv); 583 return (rv); 584 } 585 586 static int 587 drctl_ioctl(dev_t dev, 588 int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p) 589 { 590 _NOTE(ARGUNUSED(dev, mode, cred_p, rval_p)) 591 592 int rv; 593 594 switch (cmd) { 595 case DRCTL_IOCTL_CONNECT_SERVER: 596 rv = i_drctl_ioctl(cmd, arg); 597 break; 598 default: 599 rv = ENOTSUP; 600 } 601 602 *rval_p = (rv == 0) ? 0 : -1; 603 604 return (rv); 605 } 606 607 /* 608 * Accept a preformatted request from caller and send a message to 609 * the daemon. A pointer to the daemon's response buffer is passed 610 * back in obufp, its size in osize. 611 */ 612 static int 613 send_message(void *msg, size_t size, drctl_resp_t **obufp, size_t *osize) 614 { 615 drctl_resp_t *bufp; 616 drctl_rsrc_t *rsrcs; 617 size_t rsrcs_size; 618 int rv; 619 620 rv = i_drctl_send(msg, size, (void **)&rsrcs, &rsrcs_size); 621 622 if ((rv == 0) && ((rsrcs == NULL) ||(rsrcs_size == 0))) 623 rv = EINVAL; 624 625 if (rv == 0) { 626 if (obufp != NULL) { 627 ASSERT(osize != NULL); 628 629 *osize = 630 offsetof(drctl_resp_t, resp_resources) + rsrcs_size; 631 bufp = 632 kmem_alloc(*osize, KM_SLEEP); 633 DR_DBG_KMEM("%s: alloc addr %p size %ld\n", 634 __func__, (void *)bufp, *osize); 635 bufp->resp_type = DRCTL_RESP_OK; 636 bcopy(rsrcs, bufp->resp_resources, rsrcs_size); 637 *obufp = bufp; 638 } 639 640 DR_DBG_KMEM("%s: free addr %p size %ld\n", 641 __func__, (void *)rsrcs, rsrcs_size); 642 kmem_free(rsrcs, rsrcs_size); 643 } 644 645 DR_DBG_KMEM("%s:free addr %p size %ld\n", __func__, msg, size); 646 kmem_free(msg, size); 647 648 return (rv); 649 } 650 651 static void * 652 pack_message(int cmd, 653 int flags, int count, void *data, size_t *osize, size_t *data_size) 654 { 655 drd_msg_t *msgp = NULL; 656 size_t hdr_size = offsetof(drd_msg_t, data); 657 658 switch (cmd) { 659 case DRCTL_CPU_CONFIG_REQUEST: 660 case DRCTL_CPU_CONFIG_NOTIFY: 661 case DRCTL_CPU_UNCONFIG_REQUEST: 662 case DRCTL_CPU_UNCONFIG_NOTIFY: 663 *data_size = count * sizeof (drctl_rsrc_t); 664 break; 665 case DRCTL_MEM_CONFIG_REQUEST: 666 case DRCTL_MEM_CONFIG_NOTIFY: 667 case DRCTL_MEM_UNCONFIG_REQUEST: 668 case DRCTL_MEM_UNCONFIG_NOTIFY: 669 *data_size = count * sizeof (drctl_rsrc_t); 670 break; 671 case DRCTL_IO_CONFIG_REQUEST: 672 case DRCTL_IO_CONFIG_NOTIFY: 673 case DRCTL_IO_UNCONFIG_REQUEST: 674 case DRCTL_IO_UNCONFIG_NOTIFY: 675 *data_size = sizeof (drctl_rsrc_t) + 676 strlen(((drctl_rsrc_t *)data)->res_dev_path); 677 break; 678 default: 679 cmn_err(CE_WARN, 680 "drctl: pack_message received invalid cmd %d", cmd); 681 break; 682 } 683 684 if (data_size) { 685 *osize = hdr_size + *data_size; 686 msgp = kmem_alloc(*osize, KM_SLEEP); 687 DR_DBG_KMEM("%s: alloc addr %p size %ld\n", 688 __func__, (void *)msgp, *osize); 689 msgp->cmd = cmd; 690 msgp->count = count; 691 msgp->flags = flags; 692 bcopy(data, msgp->data, *data_size); 693 } 694 695 return (msgp); 696 } 697 698 /* 699 * Depending on the should_block argument, either wait for ongoing DR 700 * operations to finish and then block subsequent operations, or if a DR 701 * operation is already in progress, return EBUSY immediately without 702 * blocking subsequent DR operations. 703 */ 704 static int 705 drctl_block_conditional(boolean_t should_block) 706 { 707 mutex_enter(&drctlp->drc_lock); 708 /* If DR in progress and should_block is false, return */ 709 if (!should_block && drctlp->drc_busy != NULL) { 710 mutex_exit(&drctlp->drc_lock); 711 return (EBUSY); 712 } 713 714 /* Wait for any in progress DR operation to complete */ 715 while (drctlp->drc_busy != NULL) 716 (void) cv_wait_sig(&drctlp->drc_busy_cv, &drctlp->drc_lock); 717 718 /* Mark the link busy */ 719 drctlp->drc_busy = (drctl_cookie_t)-1; 720 drctlp->drc_cmd = DRCTL_DRC_BLOCK; 721 drctlp->drc_flags = 0; 722 mutex_exit(&drctlp->drc_lock); 723 return (0); 724 } 725 726 /* 727 * Wait for ongoing DR operations to finish, block subsequent operations. 728 */ 729 void 730 drctl_block(void) 731 { 732 (void) drctl_block_conditional(B_TRUE); 733 } 734 735 /* 736 * If a DR operation is already in progress, return EBUSY immediately 737 * without blocking subsequent DR operations. 738 */ 739 int 740 drctl_tryblock(void) 741 { 742 return (drctl_block_conditional(B_FALSE)); 743 } 744 745 /* 746 * Unblock DR operations 747 */ 748 void 749 drctl_unblock(void) 750 { 751 /* Mark the link free */ 752 mutex_enter(&drctlp->drc_lock); 753 drctlp->drc_cmd = -1; 754 drctlp->drc_flags = 0; 755 drctlp->drc_busy = NULL; 756 cv_broadcast(&drctlp->drc_busy_cv); 757 mutex_exit(&drctlp->drc_lock); 758 } 759