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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <fcntl.h> 28 #include <unistd.h> 29 #include <stdlib.h> 30 #include <strings.h> 31 #include <errno.h> 32 #include <sys/types.h> 33 #include <sys/sysevent.h> 34 #include <libsysevent.h> 35 #include <sys/vlds.h> 36 #include "libds.h" 37 38 #define PTRTOUINT64(ptr) ((uint64_t)((uintptr_t)(ptr))) 39 static char vlds_device[] = 40 "/devices/virtual-devices@100/channel-devices@200/" 41 "virtual-domain-service@0:vlds"; 42 43 typedef struct dslibentry { 44 ds_hdl_t dsl_hdl; 45 uint32_t dsl_flags; 46 char *dsl_service; 47 ds_ops_t dsl_ops; 48 } dslibentry_t; 49 50 #define MIN_DSLIB_ENTRIES 64 51 static dslibentry_t *dslibtab; 52 static int ndslib; 53 54 /* 55 * Lock to protect the dslibtab table. We only need to protect this 56 * table for those functions which actually look at or modify the table: 57 * service registration (ds_svc_reg/ds_clnt_reg), service unregistration 58 * (ds_hdl_unreg) or during callbacks (ds_recv) 59 */ 60 static mutex_t dslib_lock; 61 62 static int ds_fd = -1; 63 64 static char *ds_sid_name = "vlds"; 65 66 static evchan_t *ds_evchan; 67 68 /* 69 * Static functions internal to dslib. 70 */ 71 static dslibentry_t *ds_hdl_to_dslibentry(ds_hdl_t hdl); 72 static dslibentry_t *ds_lookup_dslibentry(char *service, boolean_t is_client); 73 static dslibentry_t *ds_register_dslibentry(ds_hdl_t hdl, char *service, 74 boolean_t is_client); 75 static void ds_free_dslibentry(dslibentry_t *dsp, int force_unreg); 76 static int ds_recv(sysevent_t *sep, void *arg); 77 static void ds_string_arg(vlds_string_t *dsp, char *str); 78 static int ds_register(ds_capability_t *cap, ds_ops_t *ops, uint_t flags); 79 80 static dslibentry_t * 81 ds_hdl_to_dslibentry(ds_hdl_t hdl) 82 { 83 int i; 84 dslibentry_t *dsp; 85 86 for (i = 0, dsp = dslibtab; i < ndslib; i++, dsp++) { 87 if (hdl == dsp->dsl_hdl) 88 return (dsp); 89 } 90 return (NULL); 91 } 92 93 static dslibentry_t * 94 ds_new_dslibentry(void) 95 { 96 int newndslib; 97 dslibentry_t *dsp; 98 99 if ((dsp = ds_hdl_to_dslibentry(NULL)) != NULL) 100 return (dsp); 101 102 /* double the size */ 103 newndslib = ndslib << 1; 104 if ((dslibtab = realloc(dslibtab, newndslib * sizeof (dslibentry_t))) 105 == NULL) 106 return (NULL); 107 dsp = &dslibtab[ndslib]; 108 (void) memset(dsp, 0, (newndslib - ndslib) * sizeof (dslibentry_t)); 109 ndslib = newndslib; 110 return (dsp); 111 } 112 113 static dslibentry_t * 114 ds_lookup_dslibentry(char *service, boolean_t is_client) 115 { 116 int i; 117 dslibentry_t *dsp; 118 uint_t is_client_flag = is_client ? VLDS_REG_CLIENT : 0; 119 120 for (i = 0, dsp = dslibtab; i < ndslib; i++, dsp++) { 121 if (dsp->dsl_hdl != NULL && 122 strcmp(dsp->dsl_service, service) == 0 && 123 (dsp->dsl_flags & VLDS_REG_CLIENT) == is_client_flag) { 124 return (dsp); 125 } 126 } 127 return (NULL); 128 } 129 130 static dslibentry_t * 131 ds_register_dslibentry(ds_hdl_t hdl, char *service, boolean_t is_client) 132 { 133 dslibentry_t *dsp, *orig_dsp; 134 uint_t nhdls; 135 136 if ((dsp = ds_hdl_to_dslibentry(hdl)) != NULL) 137 return (dsp); 138 139 if ((orig_dsp = ds_lookup_dslibentry(service, is_client)) == NULL) { 140 return (NULL); 141 } 142 143 /* 144 * Find out if we have 1 or 2 or more handles. Having one implies 145 * that we can reuse the current one handle for this service. 146 * Having two or more implies we need to allocate a new handle. 147 */ 148 if (ds_hdl_lookup(service, is_client, NULL, 2, &nhdls) != 0) 149 return (NULL); 150 151 if (nhdls == 1) { 152 /* reuse the original structure entry */ 153 dsp = orig_dsp; 154 } else if (nhdls == 2) { 155 /* allocate a new structure entry */ 156 if ((dsp = ds_new_dslibentry()) == NULL) 157 return (NULL); 158 *dsp = *orig_dsp; 159 dsp->dsl_service = strdup(orig_dsp->dsl_service); 160 } else { 161 /* can't happen... */ 162 return (NULL); 163 } 164 dsp->dsl_hdl = hdl; 165 return (dsp); 166 } 167 168 /* 169 * Want to leave an entry in the dslib table even though all the 170 * handles may have been unregistered for it. 171 */ 172 static void 173 ds_free_dslibentry(dslibentry_t *dsp, int force_unreg) 174 { 175 uint_t nhdls; 176 177 /* 178 * Find out if we have 1 or 2 or more handles. Having one implies 179 * that we want to leave the entry alone unless this is a ds_unreg_hdl 180 * (force_unreg is true). 181 */ 182 if (ds_hdl_lookup(dsp->dsl_service, 183 (dsp->dsl_flags & VLDS_REG_CLIENT) != 0, NULL, 2, &nhdls) != 0) { 184 /* should never happen */ 185 return; 186 } 187 188 if ((nhdls == 1 && force_unreg) || nhdls == 2) { 189 dsp->dsl_hdl = NULL; 190 if (dsp->dsl_service) { 191 free(dsp->dsl_service); 192 } 193 (void) memset(dsp, 0, sizeof (dslibentry_t)); 194 } 195 } 196 197 /*ARGSUSED*/ 198 static int 199 ds_recv(sysevent_t *sep, void *arg) 200 { 201 nvlist_t *nvl; 202 uint64_t hdl; 203 ds_ver_t ver; 204 ds_domain_hdl_t dhdl; 205 uchar_t *bufp; 206 boolean_t is_client; 207 uint_t buflen; 208 char *subclass; 209 char *servicep; 210 dslibentry_t *dsp; 211 ds_cb_arg_t cb_arg; 212 213 subclass = sysevent_get_subclass_name(sep); 214 if (sysevent_get_attr_list(sep, &nvl) != 0) { 215 return (0); 216 } 217 218 if (nvlist_lookup_uint64(nvl, VLDS_HDL, &hdl) == 0) { 219 if (strcmp(subclass, ESC_VLDS_REGISTER) == 0) { 220 void (*reg_cb)(ds_hdl_t, ds_cb_arg_t, ds_ver_t *, 221 ds_domain_hdl_t) = NULL; 222 223 if (nvlist_lookup_string(nvl, VLDS_SERVICE_ID, 224 &servicep) == 0 && 225 nvlist_lookup_boolean_value(nvl, VLDS_ISCLIENT, 226 &is_client) == 0) { 227 (void) mutex_lock(&dslib_lock); 228 if ((dsp = ds_register_dslibentry(hdl, 229 servicep, is_client)) != NULL) { 230 reg_cb = dsp->dsl_ops.ds_reg_cb; 231 cb_arg = dsp->dsl_ops.cb_arg; 232 } 233 (void) mutex_unlock(&dslib_lock); 234 if (reg_cb != NULL && 235 nvlist_lookup_uint64(nvl, VLDS_DOMAIN_HDL, 236 &dhdl) == 0 && 237 nvlist_lookup_uint16(nvl, VLDS_VER_MAJOR, 238 &ver.major) == 0 && 239 nvlist_lookup_uint16(nvl, VLDS_VER_MINOR, 240 &ver.minor) == 0) { 241 (reg_cb)((ds_hdl_t)hdl, cb_arg, &ver, 242 dhdl); 243 } 244 } 245 } else if (strcmp(subclass, ESC_VLDS_UNREGISTER) == 0) { 246 void (*unreg_cb)(ds_hdl_t, ds_cb_arg_t) = NULL; 247 248 (void) mutex_lock(&dslib_lock); 249 if ((dsp = ds_hdl_to_dslibentry(hdl)) != NULL) { 250 unreg_cb = dsp->dsl_ops.ds_unreg_cb; 251 cb_arg = dsp->dsl_ops.cb_arg; 252 ds_free_dslibentry(dsp, 0); 253 } 254 (void) mutex_unlock(&dslib_lock); 255 if (unreg_cb != NULL) { 256 (unreg_cb)((ds_hdl_t)hdl, cb_arg); 257 } 258 } else if (strcmp(subclass, ESC_VLDS_DATA) == 0) { 259 void (*data_cb)(ds_hdl_t, ds_cb_arg_t, void *, 260 size_t) = NULL; 261 262 (void) mutex_lock(&dslib_lock); 263 if ((dsp = ds_hdl_to_dslibentry(hdl)) != NULL) { 264 data_cb = dsp->dsl_ops.ds_data_cb; 265 cb_arg = dsp->dsl_ops.cb_arg; 266 } 267 (void) mutex_unlock(&dslib_lock); 268 if (data_cb != NULL && 269 nvlist_lookup_byte_array(nvl, VLDS_DATA, &bufp, 270 &buflen) == 0) { 271 (data_cb)((ds_hdl_t)hdl, cb_arg, bufp, buflen); 272 } 273 } 274 } 275 nvlist_free(nvl); 276 return (0); 277 } 278 279 static void 280 ds_string_arg(vlds_string_t *dsp, char *str) 281 { 282 if (str == NULL) { 283 dsp->vlds_strp = NULL; 284 dsp->vlds_strlen = 0; 285 } else { 286 dsp->vlds_strp = PTRTOUINT64(str); 287 dsp->vlds_strlen = strlen(str) + 1; 288 } 289 } 290 291 static int 292 ds_init_sysev(void) 293 { 294 char evchan_name[MAX_CHNAME_LEN]; 295 296 (void) sprintf(evchan_name, VLDS_SYSEV_CHAN_FMT, (int)getpid()); 297 if (sysevent_evc_bind(evchan_name, &ds_evchan, 0) != 0) { 298 return (errno); 299 } 300 if (sysevent_evc_subscribe(ds_evchan, ds_sid_name, EC_VLDS, 301 ds_recv, NULL, 0) != 0) { 302 sysevent_evc_unbind(ds_evchan); 303 ds_evchan = NULL; 304 return (errno); 305 } 306 return (0); 307 } 308 309 int 310 ds_init(void) 311 { 312 if (ds_fd >= 0) 313 return (0); 314 315 if ((ds_fd = open(vlds_device, 0)) < 0) 316 return (errno); 317 318 if (dslibtab == NULL) { 319 if ((dslibtab = malloc(sizeof (dslibentry_t) * ndslib)) == NULL) 320 return (errno = ENOMEM); 321 ndslib = MIN_DSLIB_ENTRIES; 322 (void) memset(dslibtab, 0, sizeof (dslibentry_t) * ndslib); 323 } 324 325 (void) mutex_init(&dslib_lock, USYNC_THREAD, NULL); 326 return (0); 327 } 328 329 static int 330 ds_register(ds_capability_t *cap, ds_ops_t *ops, uint_t flags) 331 { 332 dslibentry_t *dsp; 333 vlds_svc_reg_arg_t vlds_arg; 334 vlds_cap_t vlds_cap; 335 vlds_ver_t vlds_vers[VLDS_MAX_VERS]; 336 uint64_t hdl_arg; 337 ds_hdl_t hdl; 338 uint_t nhdls; 339 int i; 340 341 if (cap == NULL || ops == NULL || cap->svc_id == NULL || 342 cap->vers == NULL || (flags & (~VLDS_REG_CLIENT)) != 0) { 343 return (errno = EINVAL); 344 } 345 346 if (cap->nvers > VLDS_MAX_VERS) { 347 return (errno = EINVAL); 348 } 349 350 if (ds_fd < 0 && (errno = ds_init()) != 0) { 351 return (errno); 352 } 353 354 if (ds_hdl_lookup(cap->svc_id, (flags & VLDS_REG_CLIENT), NULL, 1, 355 &nhdls) == 0 && nhdls == 1) { 356 return (errno = EALREADY); 357 } 358 359 (void) mutex_lock(&dslib_lock); 360 if ((dsp = ds_new_dslibentry()) == NULL) { 361 (void) mutex_unlock(&dslib_lock); 362 return (errno = ENOMEM); 363 } 364 365 /* Setup device driver capability structure. */ 366 367 /* service string */ 368 ds_string_arg(&vlds_cap.vlds_service, cap->svc_id); 369 370 /* version array */ 371 for (i = 0; i < cap->nvers; i++) { 372 vlds_vers[i].vlds_major = cap->vers[i].major; 373 vlds_vers[i].vlds_minor = cap->vers[i].minor; 374 } 375 vlds_cap.vlds_versp = PTRTOUINT64(vlds_vers); 376 vlds_cap.vlds_nver = cap->nvers; 377 378 /* 379 * Format args for VLDS_SVC_REG ioctl. 380 */ 381 382 vlds_arg.vlds_capp = PTRTOUINT64(&vlds_cap); 383 384 /* op flags */ 385 if (ops->ds_reg_cb != NULL) 386 flags |= VLDS_REGCB_VALID; 387 if (ops->ds_unreg_cb != NULL) 388 flags |= VLDS_UNREGCB_VALID; 389 if (ops->ds_data_cb != NULL) 390 flags |= VLDS_DATACB_VALID; 391 vlds_arg.vlds_reg_flags = flags; 392 393 /* returned handle */ 394 vlds_arg.vlds_hdlp = PTRTOUINT64(&hdl_arg); 395 396 if (ioctl(ds_fd, VLDS_SVC_REG, &vlds_arg) < 0) { 397 (void) mutex_unlock(&dslib_lock); 398 return (errno); 399 } 400 401 /* 402 * Setup user callback sysevent channel. 403 */ 404 if ((flags & VLDS_ANYCB_VALID) != 0 && ds_evchan == NULL && 405 ds_init_sysev() != 0) { 406 (void) mutex_unlock(&dslib_lock); 407 (void) ioctl(ds_fd, VLDS_UNREG_HDL, &vlds_arg); 408 return (errno); 409 } 410 411 hdl = hdl_arg; 412 413 /* 414 * Set entry values in dslibtab. 415 */ 416 dsp->dsl_hdl = hdl; 417 dsp->dsl_flags = flags; 418 dsp->dsl_service = strdup(cap->svc_id); 419 dsp->dsl_ops = *ops; 420 (void) mutex_unlock(&dslib_lock); 421 return (0); 422 } 423 424 /* 425 * Registers a service provider. Kicks off the handshake with other 426 * domain(s) to announce servce. Callback events are as described above. 427 */ 428 int 429 ds_svc_reg(ds_capability_t *cap, ds_ops_t *ops) 430 { 431 return (ds_register(cap, ops, 0)); 432 } 433 434 /* 435 * Registers interest in a service from a specific domain. When that 436 * service is registered, the register callback is invoked. When that 437 * service is unregistered, the unregister callback is invoked. When 438 * data is received, the receive data callback is invoked. 439 */ 440 int 441 ds_clnt_reg(ds_capability_t *cap, ds_ops_t *ops) 442 { 443 return (ds_register(cap, ops, VLDS_REG_CLIENT)); 444 } 445 446 /* 447 * Given a service name and type, returns the existing handle(s), if 448 * one or more exist. This could be used to poll for the connection being 449 * registered or unregistered, rather than using the register/unregister 450 * callbacks. 451 */ 452 int 453 ds_hdl_lookup(char *service, boolean_t is_client, ds_hdl_t *hdlsp, 454 uint_t maxhdls, uint_t *nhdlsp) 455 { 456 vlds_hdl_lookup_arg_t vlds_arg; 457 uint64_t nhdls_arg; 458 459 errno = 0; 460 if (ds_fd < 0) { 461 return (errno = EBADF); 462 } 463 464 if (service == NULL) { 465 return (errno = EINVAL); 466 } 467 468 ds_string_arg(&vlds_arg.vlds_service, service); 469 vlds_arg.vlds_isclient = is_client ? VLDS_REG_CLIENT : 0; 470 vlds_arg.vlds_hdlsp = PTRTOUINT64(hdlsp); 471 vlds_arg.vlds_maxhdls = maxhdls; 472 vlds_arg.vlds_nhdlsp = PTRTOUINT64(&nhdls_arg); 473 474 if (ioctl(ds_fd, VLDS_HDL_LOOKUP, &vlds_arg) < 0) { 475 return (errno); 476 } 477 478 *nhdlsp = nhdls_arg; 479 return (0); 480 } 481 482 /* 483 * Given a handle, return its associated domain. 484 */ 485 int 486 ds_domain_lookup(ds_hdl_t hdl, ds_domain_hdl_t *dhdlp) 487 { 488 vlds_dmn_lookup_arg_t vlds_arg; 489 uint64_t dhdl_arg; 490 491 if (ds_fd < 0) { 492 return (errno = EBADF); 493 } 494 495 vlds_arg.vlds_hdl = hdl; 496 vlds_arg.vlds_dhdlp = PTRTOUINT64(&dhdl_arg); 497 498 if (ioctl(ds_fd, VLDS_DMN_LOOKUP, &vlds_arg) < 0) { 499 return (errno); 500 } 501 502 if (dhdlp) { 503 *dhdlp = dhdl_arg; 504 } 505 506 return (0); 507 } 508 509 /* 510 * Unregisters either a service or an interest in that service 511 * indicated by the supplied handle. 512 */ 513 int 514 ds_unreg_hdl(ds_hdl_t hdl) 515 { 516 dslibentry_t *dsp; 517 vlds_unreg_hdl_arg_t vlds_arg; 518 519 (void) mutex_lock(&dslib_lock); 520 if ((dsp = ds_hdl_to_dslibentry(hdl)) != NULL) { 521 ds_free_dslibentry(dsp, 1); 522 } 523 (void) mutex_unlock(&dslib_lock); 524 525 if (ds_fd >= 0) { 526 vlds_arg.vlds_hdl = hdl; 527 (void) ioctl(ds_fd, VLDS_UNREG_HDL, &vlds_arg); 528 } 529 530 return (0); 531 } 532 533 /* 534 * Send data to the appropriate service provider or client 535 * indicated by the provided handle. The sender will block 536 * until the message has been sent. There is no guarantee 537 * that multiple calls to ds_send_msg by the same thread 538 * will result in the data showing up at the receiver in 539 * the same order as sent. If multiple messages are required, 540 * it will be up to the sender and receiver to implement a 541 * protocol. 542 */ 543 int 544 ds_send_msg(ds_hdl_t hdl, void *buf, size_t buflen) 545 { 546 vlds_send_msg_arg_t vlds_arg; 547 548 if (ds_fd < 0) { 549 return (errno = EBADF); 550 } 551 552 vlds_arg.vlds_hdl = hdl; 553 vlds_arg.vlds_bufp = PTRTOUINT64(buf); 554 vlds_arg.vlds_buflen = buflen; 555 556 if (ioctl(ds_fd, VLDS_SEND_MSG, &vlds_arg) < 0) { 557 return (errno); 558 } 559 560 return (0); 561 } 562 563 /* 564 * Receive data from the appropriate service provider or client 565 * indicated by the provided handle. The sender will block 566 * until a message has been received. 567 */ 568 int 569 ds_recv_msg(ds_hdl_t hdl, void *buf, size_t buflen, size_t *msglen) 570 { 571 vlds_recv_msg_arg_t vlds_arg; 572 uint64_t msglen_arg; 573 574 if (ds_fd < 0) { 575 return (errno = EBADF); 576 } 577 578 vlds_arg.vlds_hdl = hdl; 579 vlds_arg.vlds_bufp = PTRTOUINT64(buf); 580 vlds_arg.vlds_buflen = buflen; 581 vlds_arg.vlds_msglenp = PTRTOUINT64(&msglen_arg); 582 583 if (ioctl(ds_fd, VLDS_RECV_MSG, &vlds_arg) < 0) { 584 if (errno == EFBIG && msglen) { 585 *msglen = msglen_arg; 586 } 587 return (errno); 588 } 589 590 if (msglen) { 591 *msglen = msglen_arg; 592 } 593 594 return (0); 595 } 596 597 int 598 ds_isready(ds_hdl_t hdl, boolean_t *is_ready) 599 { 600 vlds_hdl_isready_arg_t vlds_arg; 601 uint64_t is_ready_arg; 602 603 if (ds_fd < 0) { 604 return (errno = EBADF); 605 } 606 607 vlds_arg.vlds_hdl = hdl; 608 vlds_arg.vlds_isreadyp = PTRTOUINT64(&is_ready_arg); 609 610 if (ioctl(ds_fd, VLDS_HDL_ISREADY, &vlds_arg) < 0) { 611 return (errno); 612 } 613 614 *is_ready = (is_ready_arg != 0); 615 return (0); 616 } 617 618 /* 619 * Given a domain name, return its associated domain handle. 620 */ 621 int 622 ds_dom_name_to_hdl(char *domain_name, ds_domain_hdl_t *dhdlp) 623 { 624 vlds_dom_nam2hdl_arg_t vlds_arg; 625 uint64_t dhdl_arg; 626 627 if (ds_fd < 0) { 628 return (errno = EBADF); 629 } 630 631 ds_string_arg(&vlds_arg.vlds_domain_name, domain_name); 632 vlds_arg.vlds_dhdlp = PTRTOUINT64(&dhdl_arg); 633 634 if (ioctl(ds_fd, VLDS_DOM_NAM2HDL, &vlds_arg) < 0) { 635 return (errno); 636 } 637 638 if (dhdlp) { 639 *dhdlp = dhdl_arg; 640 } 641 642 return (0); 643 } 644 645 /* 646 * Given a domain handle, return its associated domain name. 647 */ 648 int 649 ds_dom_hdl_to_name(ds_domain_hdl_t dhdl, char *domain_name, uint_t maxnamlen) 650 { 651 vlds_dom_hdl2nam_arg_t vlds_arg; 652 653 if (ds_fd < 0) { 654 return (errno = EBADF); 655 } 656 657 vlds_arg.vlds_dhdl = dhdl; 658 vlds_arg.vlds_domain_name.vlds_strp = PTRTOUINT64(domain_name); 659 vlds_arg.vlds_domain_name.vlds_strlen = maxnamlen; 660 661 if (ioctl(ds_fd, VLDS_DOM_HDL2NAM, &vlds_arg) < 0) { 662 return (errno); 663 } 664 665 return (0); 666 } 667 668 void 669 ds_unreg_svc(char *service, boolean_t is_client) 670 { 671 ds_hdl_t hdl; 672 uint_t nhdls; 673 674 while (ds_hdl_lookup(service, is_client, &hdl, 1, &nhdls) == 0 && 675 nhdls == 1) { 676 (void) ds_unreg_hdl(hdl); 677 } 678 } 679 680 void 681 ds_fini(void) 682 { 683 int i; 684 dslibentry_t *dsp; 685 686 if (ds_fd >= 0) { 687 (void) close(ds_fd); 688 ds_fd = -1; 689 } 690 if (ds_evchan) { 691 (void) sysevent_evc_unsubscribe(ds_evchan, ds_sid_name); 692 (void) sysevent_evc_unbind(ds_evchan); 693 ds_evchan = NULL; 694 } 695 if (ndslib > 0) { 696 (void) mutex_lock(&dslib_lock); 697 for (i = 0, dsp = dslibtab; i < ndslib; i++, dsp++) { 698 if (dsp->dsl_hdl == NULL) 699 continue; 700 if (dsp->dsl_service) { 701 free(dsp->dsl_service); 702 } 703 } 704 free(dslibtab); 705 ndslib = 0; 706 dslibtab = NULL; 707 (void) mutex_unlock(&dslib_lock); 708 (void) mutex_destroy(&dslib_lock); 709 } 710 } 711