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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdlib.h> 27 #include <stdio.h> 28 29 #include <strings.h> 30 #include <sys/types.h> 31 #include <sys/stat.h> 32 #include <time.h> 33 #include <fcntl.h> 34 #include <unistd.h> 35 #include <errno.h> 36 #include <assert.h> 37 #include <umem.h> 38 #include <alloca.h> 39 #include <sys/processor.h> 40 #include <poll.h> 41 #include <pthread.h> 42 #include <signal.h> 43 #include <values.h> 44 #include <libscf.h> 45 46 #include <ctype.h> 47 48 #include "ldmsvcs_utils.h" 49 #include "ldom_alloc.h" 50 #include "ldom_utils.h" 51 52 #define ASSERT(cnd) \ 53 ((void) ((cnd) || ((void) fprintf(stderr, \ 54 "assertion failure in %s:%d: %s\n", \ 55 __FILE__, __LINE__, #cnd), 0))) 56 57 #define FDS_VLDC \ 58 "/devices/virtual-devices@100/channel-devices@200/" \ 59 "/virtual-channel-client@1:ldmfma" 60 61 /* allow timeouts in sec that are nearly forever but small enough for an int */ 62 #define LDM_TIMEOUT_CEILING (MAXINT / 2) 63 64 #define MIN(x, y) ((x) < (y) ? (x) : (y)) 65 66 /* 67 * functions in this file are for version 1.0 of FMA domain services 68 */ 69 static ds_ver_t ds_vers[] = { 70 { 1, 0 } 71 }; 72 73 #define DS_NUM_VER (sizeof (ds_vers) / sizeof (ds_ver_t)) 74 75 /* 76 * information for each channel 77 */ 78 struct ldmsvcs_info { 79 pthread_mutex_t mt; 80 pthread_cond_t cv; 81 fds_channel_t fds_chan; 82 fds_reg_svcs_t fmas_svcs; 83 int cv_twait; 84 }; 85 86 /* 87 * struct listdata_s and struct poller_s are used to maintain the state of 88 * the poller thread. this thread is used to manage incoming messages and 89 * pass those messages onto the correct requesting thread. see the "poller 90 * functions" section for more details. 91 */ 92 struct listdata_s { 93 enum { 94 UNUSED, 95 PENDING, 96 ARRIVED 97 } status; 98 uint64_t req_num; 99 int fd; 100 size_t datalen; 101 }; 102 103 static struct poller_s { 104 pthread_mutex_t mt; 105 pthread_cond_t cv; 106 pthread_t polling_tid; 107 int polling_thr_sig; 108 int doreset; 109 int doexit; 110 int nclients; 111 struct listdata_s **list; 112 int list_len; 113 int pending_count; 114 } pollbase = { 115 PTHREAD_MUTEX_INITIALIZER, 116 PTHREAD_COND_INITIALIZER, 117 0, 118 SIGTERM, 119 1, 120 0, 121 0, 122 NULL, 123 0, 124 0 125 }; 126 127 128 static struct ldmsvcs_info *channel_init(struct ldom_hdl *lhp); 129 static int channel_openreset(struct ldmsvcs_info *lsp); 130 static int read_msg(struct ldmsvcs_info *lsp); 131 132 static int 133 get_smf_int_val(char *prop_nm, int min, int max, int default_val) 134 { 135 scf_simple_prop_t *prop; /* SMF property */ 136 int64_t *valp; /* prop value ptr */ 137 int64_t val; /* prop value to return */ 138 139 val = default_val; 140 if ((prop = scf_simple_prop_get(NULL, LDM_SVC_NM, LDM_PROP_GROUP_NM, 141 prop_nm)) != NULL) { 142 if ((valp = scf_simple_prop_next_integer(prop)) != NULL) { 143 val = *valp; 144 if (val < min) 145 val = min; 146 else if (val > max) 147 val = max; 148 } 149 scf_simple_prop_free(prop); 150 } 151 return ((int)val); 152 } 153 154 static void 155 channel_close(struct ldmsvcs_info *lsp) 156 { 157 (void) pthread_mutex_lock(&lsp->mt); 158 159 if (lsp->fds_chan.state == CHANNEL_OPEN || 160 lsp->fds_chan.state == CHANNEL_READY) { 161 (void) close(lsp->fds_chan.fd); 162 lsp->cv_twait = get_smf_int_val(LDM_INIT_TO_PROP_NM, 163 0, LDM_TIMEOUT_CEILING, LDM_INIT_WAIT_TIME); 164 lsp->fds_chan.state = CHANNEL_CLOSED; 165 } 166 167 (void) pthread_mutex_unlock(&lsp->mt); 168 } 169 170 /* 171 * read size bytes of data from a streaming fd into buf 172 */ 173 static int 174 read_stream(int fd, void *buf, size_t size) 175 { 176 pollfd_t pollfd; 177 ssize_t rv; 178 size_t data_left; 179 ptrdiff_t currentp; 180 181 pollfd.events = POLLIN; 182 pollfd.revents = 0; 183 pollfd.fd = fd; 184 185 currentp = (ptrdiff_t)buf; 186 data_left = size; 187 188 /* 189 * data may come in bits and pieces 190 */ 191 do { 192 if ((rv = read(fd, (void *)currentp, data_left)) < 0) { 193 if (errno == EAGAIN && poll(&pollfd, 1, -1) > 0) 194 continue; /* retry */ 195 else 196 return (1); 197 } 198 199 data_left -= rv; 200 currentp += rv; 201 } while (data_left > 0); 202 203 return (0); 204 } 205 206 207 /* 208 * poller functions 209 * 210 * at init time, a thread is created for the purpose of monitoring incoming 211 * messages and doing one of the following: 212 * 213 * 1. doing the initial handshake and version negotiation 214 * 215 * 2. handing incoming data off to the requesting thread (which is an fmd 216 * module or scheme thread) 217 */ 218 static int 219 poller_handle_data(int fd, size_t payloadsize) 220 { 221 uint64_t *req_num; 222 void *pr; 223 size_t prlen; 224 int i; 225 226 prlen = sizeof (ds_data_handle_t) + sizeof (uint64_t); 227 228 if (payloadsize < prlen) 229 return (1); 230 231 pr = alloca(prlen); 232 233 if (read_stream(fd, pr, prlen) != 0) 234 return (1); 235 236 req_num = (uint64_t *)((ptrdiff_t)pr + sizeof (ds_data_handle_t)); 237 238 (void) pthread_mutex_lock(&pollbase.mt); 239 240 for (i = 0; i < pollbase.list_len; i++) { 241 if (pollbase.list[i]->req_num == *req_num) { 242 ASSERT(pollbase.list[i]->status == PENDING); 243 244 pollbase.list[i]->status = ARRIVED; 245 pollbase.list[i]->fd = fd; 246 pollbase.list[i]->datalen = payloadsize - prlen; 247 248 pollbase.pending_count--; 249 (void) pthread_cond_broadcast(&pollbase.cv); 250 break; 251 } 252 } 253 254 /* 255 * now wait for receiving thread to read in the data 256 */ 257 if (i < pollbase.list_len) { 258 while (pollbase.list[i]->status == ARRIVED) 259 (void) pthread_cond_wait(&pollbase.cv, &pollbase.mt); 260 } 261 262 (void) pthread_mutex_unlock(&pollbase.mt); 263 264 return (0); 265 } 266 267 268 /* 269 * note that this function is meant to handle only DS_DATA messages 270 */ 271 static int 272 poller_recv_data(struct ldom_hdl *lhp, uint64_t req_num, int index, 273 void **resp, size_t *resplen) 274 { 275 struct timespec twait; 276 int ier; 277 278 ier = 0; 279 twait.tv_sec = time(NULL) + lhp->lsinfo->cv_twait; 280 twait.tv_nsec = 0; 281 282 (void) pthread_mutex_lock(&pollbase.mt); 283 284 ASSERT(pollbase.list[index]->req_num == req_num); 285 286 while (pollbase.list[index]->status == PENDING && 287 pollbase.doreset == 0 && ier == 0) 288 ier = pthread_cond_timedwait(&pollbase.cv, &pollbase.mt, 289 &twait); 290 291 if (ier == 0) { 292 if (pollbase.doreset == 0) { 293 ASSERT(pollbase.list[index]->status == ARRIVED); 294 295 /* 296 * need to add req_num to beginning of resp 297 */ 298 *resplen = pollbase.list[index]->datalen + 299 sizeof (uint64_t); 300 *resp = lhp->allocp(*resplen); 301 *((uint64_t *)*resp) = req_num; 302 303 if (read_stream(pollbase.list[index]->fd, 304 (void *)((ptrdiff_t)*resp + sizeof (uint64_t)), 305 *resplen - sizeof (uint64_t)) != 0) 306 ier = ETIMEDOUT; 307 308 pollbase.list[index]->status = UNUSED; 309 pollbase.list[index]->req_num = 0; 310 (void) pthread_cond_broadcast(&pollbase.cv); 311 } else { 312 if (--(pollbase.pending_count) == 0) 313 (void) pthread_cond_broadcast(&pollbase.cv); 314 } 315 } 316 317 (void) pthread_mutex_unlock(&pollbase.mt); 318 319 ASSERT(ier == 0 || ier == ETIMEDOUT); 320 321 return (ier); 322 } 323 324 325 static void 326 poller_add_client(void) 327 { 328 (void) pthread_mutex_lock(&pollbase.mt); 329 pollbase.nclients++; 330 (void) pthread_mutex_unlock(&pollbase.mt); 331 } 332 333 334 static void 335 poller_remove_client(void) 336 { 337 (void) pthread_mutex_lock(&pollbase.mt); 338 pollbase.nclients--; 339 ASSERT(pollbase.nclients >= 0); 340 (void) pthread_mutex_unlock(&pollbase.mt); 341 } 342 343 344 static int 345 poller_add_pending(uint64_t req_num) 346 { 347 int newlen, index, i, j; 348 349 (void) pthread_mutex_lock(&pollbase.mt); 350 pollbase.pending_count++; 351 352 for (j = 0, index = -1; j < 2 && index == -1; j++) { 353 for (i = 0; i < pollbase.list_len; i++) { 354 if (pollbase.list[i]->status == UNUSED) { 355 pollbase.list[i]->status = PENDING; 356 pollbase.list[i]->req_num = req_num; 357 pollbase.list[i]->datalen = 0; 358 index = i; 359 break; 360 } 361 } 362 363 if (index == -1) { 364 struct listdata_s **newlist, **oldlist; 365 366 /* 367 * get to this point if list is not long enough. 368 * check for a runaway list. since requests are 369 * synchronous (clients send a request and need to 370 * wait for the result before returning) the size 371 * of the list cannot be much more than the number 372 * of clients. 373 */ 374 ASSERT(pollbase.list_len < pollbase.nclients + 1); 375 376 newlen = pollbase.list_len + 5; 377 newlist = ldom_alloc(newlen * 378 sizeof (struct listdata_s *)); 379 380 for (i = 0; i < pollbase.list_len; i++) 381 newlist[i] = pollbase.list[i]; 382 383 oldlist = pollbase.list; 384 pollbase.list = newlist; 385 ldom_free(oldlist, pollbase.list_len * 386 sizeof (struct listdata_s *)); 387 388 for (i = pollbase.list_len; i < newlen; i++) { 389 pollbase.list[i] = 390 ldom_alloc(sizeof (struct listdata_s)); 391 pollbase.list[i]->status = UNUSED; 392 } 393 394 pollbase.list_len = newlen; 395 } 396 } 397 398 (void) pthread_mutex_unlock(&pollbase.mt); 399 ASSERT(index != -1); 400 401 return (index); 402 } 403 404 405 static void 406 poller_delete_pending(uint64_t req_num, int index) 407 { 408 (void) pthread_mutex_lock(&pollbase.mt); 409 410 ASSERT(pollbase.list[index]->req_num == req_num); 411 pollbase.list[index]->status = UNUSED; 412 413 if (--(pollbase.pending_count) == 0 && pollbase.doreset == 1) 414 (void) pthread_cond_broadcast(&pollbase.cv); 415 416 (void) pthread_mutex_unlock(&pollbase.mt); 417 } 418 419 420 static void 421 poller_shutdown(boolean_t wait) 422 { 423 (void) pthread_mutex_lock(&pollbase.mt); 424 425 pollbase.doexit = 1; 426 427 (void) pthread_mutex_unlock(&pollbase.mt); 428 429 if (wait == B_TRUE) { 430 /* stop the poller thread and wait for it to end */ 431 (void) pthread_kill(pollbase.polling_tid, 432 pollbase.polling_thr_sig); 433 (void) pthread_join(pollbase.polling_tid, NULL); 434 } 435 } 436 437 438 /* 439 * perform the polling of incoming messages. manage any resets (usually 440 * due to one end of the connection being closed) as well as exit 441 * conditions. 442 */ 443 static void * 444 poller_loop(void *arg) 445 { 446 struct ldmsvcs_info *lsp; 447 pollfd_t pollfd; 448 int ier; 449 450 lsp = (struct ldmsvcs_info *)arg; 451 452 for (;;) { 453 (void) pthread_mutex_lock(&pollbase.mt); 454 455 if (pollbase.doexit) { 456 (void) pthread_mutex_unlock(&pollbase.mt); 457 break; 458 } 459 460 if (pollbase.doreset) { 461 int i; 462 463 while (pollbase.pending_count > 0) 464 (void) pthread_cond_wait(&pollbase.cv, 465 &pollbase.mt); 466 467 ASSERT(pollbase.pending_count == 0); 468 for (i = 0; i < pollbase.list_len; i++) 469 pollbase.list[i]->status = UNUSED; 470 471 pollbase.doreset = 0; 472 } 473 (void) pthread_mutex_unlock(&pollbase.mt); 474 475 if ((ier = channel_openreset(lsp)) == 1) { 476 continue; 477 } else if (ier == 2) { 478 /* 479 * start exit preparations 480 */ 481 poller_shutdown(B_FALSE); 482 continue; 483 } 484 485 pollfd.events = POLLIN; 486 pollfd.revents = 0; 487 pollfd.fd = lsp->fds_chan.fd; 488 489 if (poll(&pollfd, 1, -1) <= 0 || read_msg(lsp) != 0) { 490 /* 491 * read error and/or fd got closed 492 */ 493 (void) pthread_mutex_lock(&pollbase.mt); 494 pollbase.doreset = 1; 495 (void) pthread_mutex_unlock(&pollbase.mt); 496 497 channel_close(lsp); 498 } 499 } 500 501 return (NULL); 502 } 503 504 505 /* 506 * create the polling thread 507 */ 508 static int 509 poller_init(struct ldmsvcs_info *lsp) 510 { 511 int rc = 0; 512 513 (void) pthread_mutex_lock(&pollbase.mt); 514 515 if (pollbase.polling_tid == 0) { 516 pthread_attr_t *attr = NULL; 517 518 /* 519 * create a joinable polling thread for receiving messages 520 * The polling_thr_sig stores the signal number that is not 521 * currently masked. it is used to stop the poller thread. 522 */ 523 pollbase.polling_thr_sig = ldom_find_thr_sig(); 524 if (pthread_create(&pollbase.polling_tid, attr, 525 poller_loop, lsp) != 0) 526 rc = 1; 527 } 528 529 (void) pthread_mutex_unlock(&pollbase.mt); 530 531 return (rc); 532 } 533 534 /* 535 * Cleanup the polling thread 536 */ 537 static void 538 poller_fini(void) 539 { 540 int i; 541 542 /* stop the poller thread */ 543 poller_shutdown(B_TRUE); 544 545 (void) pthread_mutex_lock(&pollbase.mt); 546 547 /* Free up the list of outstanding requests */ 548 if (pollbase.list != NULL) { 549 for (i = 0; i < pollbase.list_len; i++) { 550 if (pollbase.list[i]) { 551 ldom_free(pollbase.list[i], 552 sizeof (struct listdata_s)); 553 } 554 } 555 ldom_free(pollbase.list, pollbase.list_len * 556 sizeof (struct listdata_s *)); 557 pollbase.list = NULL; 558 pollbase.list_len = 0; 559 } 560 561 (void) pthread_mutex_unlock(&pollbase.mt); 562 } 563 564 /* 565 * utilities for message handlers 566 */ 567 static int 568 fds_send(struct ldmsvcs_info *lsp, void *msg, size_t msglen) 569 { 570 static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER; 571 572 (void) pthread_mutex_lock(&mt); 573 574 if (write(lsp->fds_chan.fd, msg, msglen) != msglen) { 575 channel_close(lsp); 576 (void) pthread_mutex_unlock(&mt); 577 return (ETIMEDOUT); 578 } 579 580 (void) pthread_mutex_unlock(&mt); 581 return (0); 582 } 583 584 585 /* 586 * Find the max and min version supported 587 */ 588 static void 589 fds_min_max_versions(uint16_t *min_major, uint16_t *max_major) 590 { 591 int i; 592 593 *min_major = ds_vers[0].major; 594 *max_major = *min_major; 595 596 for (i = 1; i < DS_NUM_VER; i++) { 597 if (ds_vers[i].major < *min_major) 598 *min_major = ds_vers[i].major; 599 600 if (ds_vers[i].major > *max_major) 601 *max_major = ds_vers[i].major; 602 } 603 } 604 605 /* 606 * check whether the major and minor numbers requested by remote ds client 607 * can be satisfied. if the requested major is supported, true is 608 * returned, and the agreed minor is returned in new_minor. if the 609 * requested major is not supported, the routine returns false, and the 610 * closest major is returned in *new_major, upon which the ds client should 611 * renegotiate. the closest major is the just lower that the requested 612 * major number. 613 */ 614 static boolean_t 615 fds_negotiate_version(uint16_t req_major, uint16_t *new_majorp, 616 uint16_t *new_minorp) 617 { 618 int i = 0; 619 uint16_t major, lower_major; 620 uint16_t min_major, max_major; 621 boolean_t found_match = B_FALSE; 622 623 fds_min_max_versions(&min_major, &max_major); 624 625 /* 626 * if the minimum version supported is greater than the version 627 * requested, return the lowest version supported 628 */ 629 if (min_major > req_major) { 630 *new_majorp = min_major; 631 return (B_FALSE); 632 } 633 634 /* 635 * if the largest version supported is lower than the version 636 * requested, return the largest version supported 637 */ 638 if (max_major < req_major) { 639 *new_majorp = max_major; 640 return (B_FALSE); 641 } 642 643 /* 644 * now we know that the requested version lies between the min and 645 * max versions supported. check if the requested major can be 646 * found in supported versions. 647 */ 648 lower_major = min_major; 649 for (i = 0; i < DS_NUM_VER; i++) { 650 major = ds_vers[i].major; 651 if (major == req_major) { 652 found_match = B_TRUE; 653 *new_minorp = ds_vers[i].minor; 654 *new_majorp = major; 655 break; 656 } else if ((major < req_major) && (major > lower_major)) 657 lower_major = major; 658 } 659 660 /* 661 * If no match is found, return the closest available number 662 */ 663 if (!found_match) 664 *new_majorp = lower_major; 665 666 return (found_match); 667 } 668 669 670 /* 671 * return 0 if service is added; 1 if service is a duplicate 672 */ 673 static int 674 fds_svc_add(struct ldmsvcs_info *lsp, ds_reg_req_t *req, int minor) 675 { 676 fds_svc_t *svc; 677 int i, rc; 678 679 svc = NULL; 680 for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) { 681 if (strcmp(lsp->fmas_svcs.tbl[i]->name, req->svc_id) == 0) { 682 svc = lsp->fmas_svcs.tbl[i]; 683 break; 684 } 685 } 686 687 if (svc == NULL) 688 return (0); /* we don't need this service */ 689 690 (void) pthread_mutex_lock(&lsp->fmas_svcs.mt); 691 692 /* 693 * duplicate registration is OK --- we retain the previous entry 694 * (which has not been unregistered anyway) 695 */ 696 if (svc->state == DS_SVC_ACTIVE) { 697 rc = 1; 698 } else { 699 svc->state = DS_SVC_ACTIVE; 700 svc->hdl = req->svc_handle; 701 svc->ver.major = req->major_vers; 702 svc->ver.minor = minor; 703 704 rc = 0; 705 (void) pthread_cond_broadcast(&lsp->fmas_svcs.cv); 706 } 707 708 (void) pthread_mutex_unlock(&lsp->fmas_svcs.mt); 709 710 return (rc); 711 } 712 713 714 static void 715 fds_svc_reset(struct ldmsvcs_info *lsp, int index) 716 { 717 int i, start, end; 718 719 if (index >= 0) { 720 start = index; 721 end = index + 1; 722 } else { 723 start = 0; 724 end = lsp->fmas_svcs.nsvcs; 725 } 726 727 (void) pthread_mutex_lock(&lsp->fmas_svcs.mt); 728 729 for (i = start; i < end; i++) { 730 lsp->fmas_svcs.tbl[i]->hdl = 0; 731 lsp->fmas_svcs.tbl[i]->state = DS_SVC_INVAL; 732 lsp->fmas_svcs.tbl[i]->ver.major = 733 ds_vers[DS_NUM_VER - 1].major; 734 lsp->fmas_svcs.tbl[i]->ver.minor = 735 ds_vers[DS_NUM_VER - 1].minor; 736 } 737 738 (void) pthread_mutex_unlock(&lsp->fmas_svcs.mt); 739 } 740 741 742 static int 743 fds_svc_remove(struct ldmsvcs_info *lsp, ds_svc_hdl_t svc_handle) 744 { 745 int i; 746 747 for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) { 748 if (lsp->fmas_svcs.tbl[i]->hdl == svc_handle) { 749 fds_svc_reset(lsp, i); 750 return (0); 751 } 752 } 753 754 return (1); 755 } 756 757 758 /* 759 * message handlers 760 */ 761 /*ARGSUSED*/ 762 static void 763 ds_handle_msg_noop(struct ldmsvcs_info *lsp, void *buf, size_t len) 764 { 765 } 766 767 static void 768 ds_handle_init_req(struct ldmsvcs_info *lsp, void *buf, size_t len) 769 { 770 ds_init_req_t *req; 771 uint16_t new_major, new_minor; 772 size_t msglen; 773 774 req = (ds_init_req_t *)buf; 775 776 /* sanity check the incoming message */ 777 if (len != sizeof (ds_init_req_t)) { 778 channel_close(lsp); 779 return; 780 } 781 782 /* 783 * Check version info. ACK only if the major numbers exactly 784 * match. The service entity can retry with a new minor 785 * based on the response sent as part of the NACK. 786 */ 787 if (fds_negotiate_version(req->major_vers, &new_major, &new_minor)) { 788 ds_hdr_t *H; 789 ds_init_ack_t *R; 790 791 msglen = sizeof (ds_hdr_t) + sizeof (ds_init_ack_t); 792 H = alloca(msglen); 793 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t)); 794 795 H->msg_type = DS_INIT_ACK; 796 H->payload_len = sizeof (ds_init_ack_t); 797 R->minor_vers = MIN(new_minor, req->minor_vers); 798 799 if (fds_send(lsp, H, msglen) != 0) 800 return; 801 802 (void) pthread_mutex_lock(&lsp->mt); 803 ASSERT(lsp->fds_chan.state == CHANNEL_OPEN); 804 lsp->fds_chan.state = CHANNEL_READY; 805 806 /* 807 * Now the channel is ready after the handshake completes. 808 * Reset the timeout to a smaller value for receiving messages 809 * from the domain services. 810 */ 811 lsp->cv_twait = get_smf_int_val(LDM_RUNNING_TO_PROP_NM, 812 0, LDM_TIMEOUT_CEILING, LDM_RUNNING_WAIT_TIME); 813 814 (void) pthread_mutex_unlock(&lsp->mt); 815 } else { 816 ds_hdr_t *H; 817 ds_init_nack_t *R; 818 819 msglen = sizeof (ds_hdr_t) + sizeof (ds_init_nack_t); 820 H = alloca(msglen); 821 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t)); 822 823 H->msg_type = DS_INIT_NACK; 824 H->payload_len = sizeof (ds_init_nack_t); 825 R->major_vers = new_major; 826 827 (void) fds_send(lsp, H, msglen); 828 /* 829 * do not update state; remote end may attempt to initiate 830 * connection with a different version 831 */ 832 } 833 } 834 835 836 /*ARGSUSED*/ 837 static void 838 ds_handle_reg_req(struct ldmsvcs_info *lsp, void *buf, size_t len) 839 { 840 ds_reg_req_t *req; 841 char *msg; 842 uint16_t new_major, new_minor; 843 size_t msglen; 844 int dup_svcreg = 0; 845 846 req = (ds_reg_req_t *)buf; 847 msg = (char *)req->svc_id; 848 849 /* 850 * Service must be NULL terminated 851 */ 852 if (req->svc_id == NULL || strlen(req->svc_id) == 0 || 853 msg[strlen(req->svc_id)] != '\0') { 854 channel_close(lsp); 855 return; 856 } 857 858 if (fds_negotiate_version(req->major_vers, &new_major, &new_minor) && 859 (dup_svcreg = fds_svc_add(lsp, req, 860 MIN(new_minor, req->minor_vers))) == 0) { 861 862 /* 863 * Check version info. ACK only if the major numbers 864 * exactly match. The service entity can retry with a new 865 * minor based on the response sent as part of the NACK. 866 */ 867 ds_hdr_t *H; 868 ds_reg_ack_t *R; 869 870 msglen = sizeof (ds_hdr_t) + sizeof (ds_reg_ack_t); 871 H = alloca(msglen); 872 bzero(H, msglen); 873 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t)); 874 875 H->msg_type = DS_REG_ACK; 876 H->payload_len = sizeof (ds_reg_ack_t); 877 R->svc_handle = req->svc_handle; 878 R->minor_vers = MIN(new_minor, req->minor_vers); 879 880 (void) fds_send(lsp, H, msglen); 881 } else { 882 ds_hdr_t *H; 883 ds_reg_nack_t *R; 884 885 msglen = sizeof (ds_hdr_t) + sizeof (ds_reg_nack_t); 886 H = alloca(msglen); 887 bzero(H, msglen); 888 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t)); 889 890 H->msg_type = DS_REG_NACK; 891 H->payload_len = sizeof (ds_reg_nack_t); 892 R->svc_handle = req->svc_handle; 893 R->major_vers = new_major; 894 895 if (dup_svcreg) 896 R->result = DS_REG_DUP; 897 else 898 R->result = DS_REG_VER_NACK; 899 900 (void) fds_send(lsp, H, msglen); 901 } 902 } 903 904 905 /*ARGSUSED*/ 906 static void 907 ds_handle_unreg(struct ldmsvcs_info *lsp, void *buf, size_t len) 908 { 909 ds_unreg_req_t *req; 910 size_t msglen; 911 912 req = (ds_unreg_req_t *)buf; 913 914 if (fds_svc_remove(lsp, req->svc_handle) == 0) { 915 ds_hdr_t *H; 916 ds_unreg_ack_t *R; 917 918 msglen = sizeof (ds_hdr_t) + sizeof (ds_unreg_ack_t); 919 H = alloca(msglen); 920 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t)); 921 922 H->msg_type = DS_REG_ACK; 923 H->payload_len = sizeof (ds_unreg_ack_t); 924 R->svc_handle = req->svc_handle; 925 926 (void) fds_send(lsp, H, msglen); 927 } else { 928 ds_hdr_t *H; 929 ds_unreg_nack_t *R; 930 931 msglen = sizeof (ds_hdr_t) + sizeof (ds_unreg_nack_t); 932 H = alloca(msglen); 933 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t)); 934 935 H->msg_type = DS_REG_NACK; 936 H->payload_len = sizeof (ds_unreg_nack_t); 937 R->svc_handle = req->svc_handle; 938 939 (void) fds_send(lsp, H, msglen); 940 } 941 } 942 943 944 /* 945 * Message handler lookup table (v1.0 only for now) Future 946 * versions can add their own lookup table. 947 */ 948 typedef void (*ds_msg_handler_t)(struct ldmsvcs_info *lsp, 949 void *buf, size_t len); 950 951 static const ds_msg_handler_t ds_msg_handlers[] = { 952 ds_handle_init_req, /* DS_INIT_REQ */ 953 ds_handle_msg_noop, /* DS_INIT_ACK */ 954 ds_handle_msg_noop, /* DS_INIT_NACK */ 955 ds_handle_reg_req, /* DS_REG_REQ */ 956 ds_handle_msg_noop, /* DS_REG_ACK */ 957 ds_handle_msg_noop, /* DS_REG_NACK */ 958 ds_handle_unreg, /* DS_UNREG */ 959 ds_handle_msg_noop, /* DS_UNREG_ACK */ 960 ds_handle_msg_noop, /* DS_UNREG_NACK */ 961 ds_handle_msg_noop, /* DS_DATA */ 962 ds_handle_msg_noop /* DS_NACK */ 963 }; 964 965 966 /* 967 * message and service internal functions 968 */ 969 static void 970 fds_svc_alloc(struct ldmsvcs_info *lsp) 971 { 972 int i; 973 static char *name[] = { LDM_DS_NAME_CPU, LDM_DS_NAME_MEM, 974 LDM_DS_NAME_PRI, LDM_DS_NAME_IOD, NULL }; 975 976 (void) pthread_mutex_init(&lsp->fmas_svcs.mt, NULL); 977 (void) pthread_cond_init(&lsp->fmas_svcs.cv, NULL); 978 979 for (lsp->fmas_svcs.nsvcs = 0; name[lsp->fmas_svcs.nsvcs] != NULL; 980 lsp->fmas_svcs.nsvcs++) 981 ; 982 983 lsp->fmas_svcs.tbl = (fds_svc_t **)ldom_alloc(sizeof (fds_svc_t *) * 984 lsp->fmas_svcs.nsvcs); 985 986 for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) { 987 lsp->fmas_svcs.tbl[i] = 988 (fds_svc_t *)ldom_alloc(sizeof (fds_svc_t)); 989 bzero(lsp->fmas_svcs.tbl[i], sizeof (fds_svc_t)); 990 lsp->fmas_svcs.tbl[i]->name = name[i]; 991 } 992 } 993 994 995 static fds_svc_t * 996 fds_svc_lookup(struct ldmsvcs_info *lsp, char *name) 997 { 998 struct timespec twait; 999 fds_svc_t *svc; 1000 int i, ier; 1001 1002 if (pthread_mutex_lock(&lsp->fmas_svcs.mt) == EINVAL) 1003 return (NULL); /* uninitialized or destroyed mutex */ 1004 1005 svc = NULL; 1006 for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) { 1007 if (strcmp(lsp->fmas_svcs.tbl[i]->name, name) == 0) { 1008 svc = lsp->fmas_svcs.tbl[i]; 1009 break; 1010 } 1011 } 1012 1013 ASSERT(svc != NULL); 1014 1015 if (svc->state == DS_SVC_INACTIVE) { 1016 /* service is not registered */ 1017 ier = ETIMEDOUT; 1018 } else { 1019 ier = 0; 1020 twait.tv_sec = time(NULL) + lsp->cv_twait; 1021 twait.tv_nsec = 0; 1022 1023 while (svc->state != DS_SVC_ACTIVE && ier == 0 && 1024 lsp->fds_chan.state != CHANNEL_UNUSABLE) 1025 ier = pthread_cond_timedwait(&lsp->fmas_svcs.cv, 1026 &lsp->fmas_svcs.mt, &twait); 1027 1028 /* 1029 * By now, the ds service should have registered already. 1030 * If it does not, ldmd probably does not support this service. 1031 * Then mark the service state as inactive. 1032 */ 1033 if (ier == ETIMEDOUT) { 1034 svc->state = DS_SVC_INACTIVE; 1035 } 1036 } 1037 1038 (void) pthread_mutex_unlock(&lsp->fmas_svcs.mt); 1039 1040 if (ier == 0) 1041 return (svc); 1042 else 1043 return (NULL); 1044 } 1045 1046 1047 static uint64_t 1048 fds_svc_req_num(void) 1049 { 1050 static uint64_t req_num = 1; 1051 1052 return (req_num++); 1053 } 1054 1055 1056 /* 1057 * return 0 if successful, 1 if otherwise 1058 */ 1059 static int 1060 read_msg(struct ldmsvcs_info *lsp) 1061 { 1062 ds_hdr_t header; 1063 void *msg_buf; 1064 1065 /* 1066 * read the header 1067 */ 1068 if (read_stream(lsp->fds_chan.fd, &header, sizeof (ds_hdr_t)) != 0) 1069 return (1); 1070 1071 if (header.msg_type >= 1072 sizeof (ds_msg_handlers) / sizeof (ds_msg_handler_t)) 1073 return (1); 1074 1075 /* 1076 * handle data as a special case 1077 */ 1078 if (header.msg_type == 9) 1079 return (poller_handle_data(lsp->fds_chan.fd, 1080 header.payload_len)); 1081 1082 /* 1083 * all other types of messages should be small 1084 */ 1085 ASSERT(header.payload_len < 1024); 1086 msg_buf = alloca(header.payload_len); 1087 1088 /* 1089 * read the payload 1090 */ 1091 if (read_stream(lsp->fds_chan.fd, msg_buf, header.payload_len) != 0) 1092 return (1); 1093 1094 (*ds_msg_handlers[header.msg_type])(lsp, msg_buf, header.payload_len); 1095 1096 return (0); 1097 } 1098 1099 1100 /* 1101 * return values: 1102 * 0 - success 1103 * 1 - problem with opening the channel 1104 * 2 - channed not opened; request to exit has been detected 1105 */ 1106 static int 1107 channel_openreset(struct ldmsvcs_info *lsp) 1108 { 1109 int ier; 1110 1111 ier = pthread_mutex_lock(&lsp->mt); 1112 1113 if (ier == EINVAL || lsp->fds_chan.state == CHANNEL_EXIT || 1114 lsp->fds_chan.state == CHANNEL_UNUSABLE) { 1115 (void) pthread_mutex_unlock(&lsp->mt); 1116 return (2); 1117 } 1118 1119 if (lsp->fds_chan.state == CHANNEL_UNINITIALIZED || 1120 lsp->fds_chan.state == CHANNEL_CLOSED) { 1121 (void) pthread_cond_broadcast(&lsp->cv); 1122 1123 if ((lsp->fds_chan.fd = open(FDS_VLDC, O_RDWR)) < 0) { 1124 lsp->fds_chan.state = CHANNEL_UNUSABLE; 1125 lsp->cv_twait = get_smf_int_val(LDM_RUNNING_TO_PROP_NM, 1126 0, LDM_TIMEOUT_CEILING, LDM_RUNNING_WAIT_TIME); 1127 (void) pthread_mutex_unlock(&lsp->mt); 1128 (void) pthread_cond_broadcast(&lsp->fmas_svcs.cv); 1129 1130 return (2); 1131 } else { 1132 vldc_opt_op_t op; 1133 1134 op.op_sel = VLDC_OP_SET; 1135 op.opt_sel = VLDC_OPT_MODE; 1136 op.opt_val = LDC_MODE_RELIABLE; 1137 1138 if (ioctl(lsp->fds_chan.fd, VLDC_IOCTL_OPT_OP, 1139 &op) != 0) { 1140 (void) close(lsp->fds_chan.fd); 1141 (void) pthread_mutex_unlock(&lsp->mt); 1142 return (1); 1143 } 1144 } 1145 lsp->fds_chan.state = CHANNEL_OPEN; 1146 } 1147 1148 if (lsp->fds_chan.state == CHANNEL_OPEN) { 1149 /* 1150 * reset various channel parameters 1151 */ 1152 lsp->fds_chan.ver.major = 0; 1153 lsp->fds_chan.ver.minor = 0; 1154 fds_svc_reset(lsp, -1); 1155 } 1156 (void) pthread_mutex_unlock(&lsp->mt); 1157 1158 return (0); 1159 } 1160 1161 1162 static void 1163 channel_fini(void) 1164 { 1165 int i; 1166 struct ldmsvcs_info *lsp; 1167 1168 /* 1169 * End the poller thread 1170 */ 1171 poller_fini(); 1172 1173 if ((lsp = channel_init(NULL)) == NULL) 1174 return; 1175 1176 (void) pthread_mutex_lock(&lsp->mt); 1177 1178 lsp->fds_chan.state = CHANNEL_EXIT; 1179 (void) close(lsp->fds_chan.fd); 1180 1181 (void) pthread_mutex_unlock(&lsp->mt); 1182 1183 /* Free the ldom service structure */ 1184 for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) { 1185 ldom_free(lsp->fmas_svcs.tbl[i], sizeof (fds_svc_t)); 1186 } 1187 ldom_free(lsp->fmas_svcs.tbl, 1188 lsp->fmas_svcs.nsvcs * sizeof (fds_svc_t *)); 1189 ldom_free(lsp, sizeof (struct ldmsvcs_info)); 1190 } 1191 1192 1193 static struct ldmsvcs_info * 1194 channel_init(struct ldom_hdl *lhp) 1195 { 1196 static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER; 1197 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; 1198 static struct ldmsvcs_info *root = NULL; 1199 static int busy_init = 0; 1200 1201 struct timespec twait; 1202 int expired; 1203 1204 (void) pthread_mutex_lock(&mt); 1205 1206 while (busy_init == 1) 1207 (void) pthread_cond_wait(&cv, &mt); 1208 1209 if (root != NULL || (lhp == NULL && root == NULL)) { 1210 (void) pthread_mutex_unlock(&mt); 1211 return (root); 1212 } 1213 1214 /* 1215 * get to this point if we need to open the channel 1216 */ 1217 busy_init = 1; 1218 (void) pthread_mutex_unlock(&mt); 1219 1220 root = (struct ldmsvcs_info *) 1221 ldom_alloc(sizeof (struct ldmsvcs_info)); 1222 bzero(root, sizeof (struct ldmsvcs_info)); 1223 1224 root->fds_chan.state = CHANNEL_UNINITIALIZED; 1225 root->cv_twait = get_smf_int_val(LDM_INIT_TO_PROP_NM, 1226 0, LDM_TIMEOUT_CEILING, LDM_INIT_WAIT_TIME); 1227 1228 if (pthread_mutex_init(&root->mt, NULL) != 0 || 1229 pthread_cond_init(&root->cv, NULL) != 0) { 1230 ldom_free(root, sizeof (struct ldmsvcs_info)); 1231 return (NULL); 1232 } 1233 1234 fds_svc_alloc(root); 1235 fds_svc_reset(root, -1); 1236 1237 (void) poller_init(root); 1238 1239 expired = 0; 1240 twait.tv_sec = time(NULL) + 10; 1241 twait.tv_nsec = 0; 1242 1243 (void) pthread_mutex_lock(&root->mt); 1244 1245 /* 1246 * wait for channel to become uninitialized. this should be quick. 1247 */ 1248 while (root->fds_chan.state == CHANNEL_UNINITIALIZED && expired == 0) 1249 expired = pthread_cond_timedwait(&root->cv, &root->mt, &twait); 1250 1251 if (root->fds_chan.state == CHANNEL_UNUSABLE) 1252 expired = 1; 1253 1254 (void) pthread_mutex_unlock(&root->mt); 1255 1256 (void) pthread_mutex_lock(&mt); 1257 busy_init = 0; 1258 (void) pthread_mutex_unlock(&mt); 1259 (void) pthread_cond_broadcast(&cv); 1260 1261 (void) atexit(channel_fini); 1262 1263 if (expired == 0) 1264 return (root); 1265 else 1266 return (NULL); 1267 } 1268 1269 1270 static int 1271 sendrecv(struct ldom_hdl *lhp, uint64_t req_num, 1272 void *msg, size_t msglen, ds_svc_hdl_t *svc_hdl, char *svcname, 1273 void **resp, size_t *resplen) 1274 { 1275 struct ldmsvcs_info *lsp; 1276 fds_svc_t *svc; 1277 int maxretries, index, i, ier; 1278 1279 lsp = lhp->lsinfo; 1280 i = 0; 1281 maxretries = 1; 1282 1283 do { 1284 /* 1285 * if any of the calls in this loop fail, retry some number 1286 * of times before giving up. 1287 */ 1288 if ((svc = fds_svc_lookup(lsp, svcname)) == NULL) { 1289 (void) pthread_mutex_lock(&lsp->mt); 1290 1291 if (lsp->fds_chan.state != CHANNEL_READY) 1292 ier = ETIMEDOUT; /* channel not ready */ 1293 else 1294 ier = ENOTSUP; /* service not ready */ 1295 1296 (void) pthread_mutex_unlock(&lsp->mt); 1297 1298 continue; 1299 } else { 1300 ier = 0; 1301 *svc_hdl = svc->hdl; 1302 } 1303 1304 index = poller_add_pending(req_num); 1305 1306 if ((ier = fds_send(lsp, msg, msglen)) != 0 || 1307 (ier = poller_recv_data(lhp, req_num, index, resp, 1308 resplen)) != 0) 1309 poller_delete_pending(req_num, index); 1310 1311 } while (i++ < maxretries && ier != 0); 1312 1313 ASSERT(ier == 0 || ier == ETIMEDOUT || ier == ENOTSUP); 1314 1315 return (ier); 1316 } 1317 1318 1319 /* 1320 * input: 1321 * msg_type - requested operation: FMA_CPU_REQ_STATUS or FMA_CPU_REQ_OFFLINE 1322 * cpuid - physical cpu id 1323 * 1324 * normal return values: 1325 * P_OFFLINE - cpu is offline 1326 * P_ONLINE - cpu is online 1327 * 1328 * abnormal return values: 1329 * ETIMEDOUT - LDOM manager is not responding 1330 * ENOTSUP - LDOM service for cpu offlining/status is not available 1331 * ENOMSG - got an unexpected response from the LDOM cpu service 1332 */ 1333 static int 1334 cpu_request(struct ldom_hdl *lhp, uint32_t msg_type, uint32_t cpuid) 1335 { 1336 ds_hdr_t *H; 1337 ds_data_handle_t *D; 1338 fma_cpu_service_req_t *R; 1339 1340 char *svcname = LDM_DS_NAME_CPU; 1341 fma_cpu_resp_t *respmsg; 1342 void *resp; 1343 size_t resplen, reqmsglen; 1344 int rc; 1345 1346 if (lhp->lsinfo == NULL) 1347 return (ENOMSG); 1348 1349 reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) + 1350 sizeof (fma_cpu_service_req_t); 1351 1352 H = lhp->allocp(reqmsglen); 1353 D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t)); 1354 R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t)); 1355 1356 H->msg_type = DS_DATA; 1357 H->payload_len = sizeof (ds_data_handle_t) + 1358 sizeof (fma_cpu_service_req_t); 1359 1360 R->req_num = fds_svc_req_num(); 1361 R->msg_type = msg_type; 1362 R->cpu_id = cpuid; 1363 1364 if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen, 1365 &D->svc_handle, svcname, &resp, &resplen)) != 0) { 1366 lhp->freep(H, reqmsglen); 1367 return (rc); 1368 } 1369 1370 lhp->freep(H, reqmsglen); 1371 1372 ASSERT(resplen == sizeof (fma_cpu_resp_t)); 1373 respmsg = (fma_cpu_resp_t *)resp; 1374 1375 rc = ENOMSG; 1376 if (respmsg->result == FMA_CPU_RESP_OK) { 1377 if (respmsg->status == FMA_CPU_STAT_ONLINE) 1378 rc = P_ONLINE; 1379 else if (respmsg->status == FMA_CPU_STAT_OFFLINE) 1380 rc = P_OFFLINE; 1381 } else { 1382 if (msg_type == FMA_CPU_REQ_OFFLINE && 1383 respmsg->status == FMA_CPU_STAT_OFFLINE) 1384 rc = P_OFFLINE; 1385 } 1386 1387 lhp->freep(resp, resplen); 1388 1389 return (rc); 1390 } 1391 1392 1393 /* 1394 * input: 1395 * msg_type - requested operation: FMA_MEM_REQ_STATUS or FMA_MEM_REQ_RETIRE 1396 * pa - starting address of memory page 1397 * pgsize - memory page size in bytes 1398 * 1399 * normal return values for msg_type == FMA_MEM_REQ_STATUS: 1400 * 0 - page is retired 1401 * EAGAIN - page is scheduled for retirement 1402 * EIO - page not scheduled for retirement 1403 * EINVAL - error 1404 * 1405 * normal return values for msg_type == FMA_MEM_REQ_RETIRE: 1406 * 0 - success in retiring page 1407 * EIO - page is already retired 1408 * EAGAIN - page is scheduled for retirement 1409 * EINVAL - error 1410 * 1411 * abnormal return values (regardless of msg_type) 1412 * ETIMEDOUT - LDOM manager is not responding 1413 * ENOTSUP - LDOM service for cpu offlining/status is not available 1414 * ENOMSG - got an unexpected response from the LDOM cpu service 1415 */ 1416 static int 1417 mem_request(struct ldom_hdl *lhp, uint32_t msg_type, uint64_t pa, 1418 uint64_t pgsize) 1419 { 1420 ds_hdr_t *H; 1421 ds_data_handle_t *D; 1422 fma_mem_service_req_t *R; 1423 1424 char *svcname = LDM_DS_NAME_MEM; 1425 fma_mem_resp_t *respmsg; 1426 void *resp; 1427 size_t resplen, reqmsglen; 1428 int rc; 1429 1430 if (lhp->lsinfo == NULL) 1431 return (ENOMSG); 1432 1433 reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) + 1434 sizeof (fma_mem_service_req_t); 1435 1436 H = lhp->allocp(reqmsglen); 1437 bzero(H, reqmsglen); 1438 D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t)); 1439 R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t)); 1440 1441 H->msg_type = DS_DATA; 1442 H->payload_len = sizeof (ds_data_handle_t) + 1443 sizeof (fma_mem_service_req_t); 1444 1445 R->req_num = fds_svc_req_num(); 1446 R->msg_type = msg_type; 1447 R->real_addr = pa; 1448 R->length = pgsize; 1449 1450 if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen, 1451 &D->svc_handle, svcname, &resp, &resplen)) != 0) { 1452 lhp->freep(H, reqmsglen); 1453 return (rc); 1454 } 1455 1456 lhp->freep(H, reqmsglen); 1457 1458 ASSERT(resplen == sizeof (fma_mem_resp_t)); 1459 respmsg = (fma_mem_resp_t *)resp; 1460 1461 rc = ENOMSG; 1462 if (msg_type == FMA_MEM_REQ_STATUS) { 1463 if (respmsg->result == FMA_MEM_RESP_OK) { 1464 if (respmsg->status == FMA_MEM_STAT_RETIRED) 1465 rc = 0; /* page is retired */ 1466 else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED) 1467 rc = EIO; /* page is not scheduled */ 1468 } else if (respmsg->result == FMA_MEM_RESP_FAILURE) { 1469 if (respmsg->status == FMA_MEM_STAT_NOTRETIRED) 1470 rc = EAGAIN; /* page is scheduled */ 1471 else if (respmsg->status == FMA_MEM_STAT_ILLEGAL) 1472 rc = EINVAL; 1473 } 1474 } else if (msg_type == FMA_MEM_REQ_RETIRE) { 1475 if (respmsg->result == FMA_MEM_RESP_OK) { 1476 if (respmsg->status == FMA_MEM_STAT_RETIRED) 1477 rc = 0; /* is successfully retired */ 1478 } else if (respmsg->result == FMA_MEM_RESP_FAILURE) { 1479 if (respmsg->status == FMA_MEM_STAT_RETIRED) 1480 rc = EIO; /* is already retired */ 1481 else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED) 1482 rc = EAGAIN; /* is scheduled to retire */ 1483 else if (respmsg->status == FMA_MEM_STAT_ILLEGAL) 1484 rc = EINVAL; 1485 } 1486 } else if (msg_type == FMA_MEM_REQ_RESURRECT) { 1487 if (respmsg->result == FMA_MEM_RESP_OK) { 1488 if (respmsg->status == FMA_MEM_STAT_NOTRETIRED) 1489 rc = 0; /* is successfully unretired */ 1490 } if (respmsg->result == FMA_MEM_RESP_FAILURE) { 1491 if (respmsg->status == FMA_MEM_STAT_RETIRED) 1492 rc = EAGAIN; /* page couldn't be locked */ 1493 else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED) 1494 rc = EIO; /* page isn't retired already */ 1495 else if (respmsg->status == FMA_MEM_STAT_ILLEGAL) 1496 rc = EINVAL; 1497 } 1498 } 1499 1500 lhp->freep(resp, resplen); 1501 1502 return (rc); 1503 } 1504 1505 1506 /* 1507 * APIs 1508 */ 1509 int 1510 ldmsvcs_check_channel(void) 1511 { 1512 struct stat buf; 1513 1514 if (stat(FDS_VLDC, &buf) == 0) 1515 return (0); /* vldc exists */ 1516 else if (errno == ENOENT || errno == ENOTDIR) 1517 return (1); /* vldc does not exist */ 1518 else 1519 return (-1); /* miscellaneous error */ 1520 } 1521 1522 1523 /*ARGSUSED*/ 1524 void 1525 ldmsvcs_init(struct ldom_hdl *lhp) 1526 { 1527 if (ldmsvcs_check_channel() != 0) 1528 return; 1529 1530 lhp->lsinfo = channel_init(lhp); 1531 poller_add_client(); 1532 } 1533 1534 1535 /*ARGSUSED*/ 1536 void 1537 ldmsvcs_fini(struct ldom_hdl *lhp) 1538 { 1539 if (ldmsvcs_check_channel() != 0) 1540 return; 1541 1542 poller_remove_client(); 1543 } 1544 1545 1546 /*ARGSUSED*/ 1547 ssize_t 1548 ldmsvcs_get_core_md(struct ldom_hdl *lhp, uint64_t **buf) 1549 { 1550 ds_hdr_t *H; 1551 ds_data_handle_t *D; 1552 fma_req_pri_t *R; 1553 1554 char *svcname = LDM_DS_NAME_PRI; 1555 void *resp; 1556 size_t resplen, reqmsglen; 1557 ssize_t buflen; 1558 int rc; 1559 1560 if (lhp->lsinfo == NULL) 1561 return (-1); 1562 1563 reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) + 1564 sizeof (fma_req_pri_t); 1565 1566 H = lhp->allocp(reqmsglen); 1567 D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t)); 1568 R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t)); 1569 1570 H->msg_type = DS_DATA; 1571 H->payload_len = sizeof (ds_data_handle_t) + 1572 sizeof (fma_req_pri_t); 1573 1574 R->req_num = fds_svc_req_num(); 1575 1576 if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen, 1577 &D->svc_handle, svcname, &resp, &resplen)) != 0) { 1578 lhp->freep(H, reqmsglen); 1579 errno = rc; 1580 return (-1); 1581 } 1582 1583 lhp->freep(H, reqmsglen); 1584 1585 /* 1586 * resp should contain the req_num immediately followed by the PRI 1587 * (the latter may or may not be present). unfortunately, the 1588 * current compiler flags cause a warning for the following 1589 * definition 1590 * 1591 * typedef struct { 1592 * uint64_t req_num; 1593 * uint8_t pri[]; 1594 * } fma_pri_resp_t; 1595 * 1596 * so we do not use the struct here. 1597 */ 1598 if (resplen <= sizeof (uint64_t)) { 1599 lhp->freep(resp, resplen); 1600 if (resplen == sizeof (uint64_t)) 1601 return (0); 1602 else 1603 return (-1); 1604 } 1605 1606 buflen = resplen - sizeof (uint64_t); 1607 *buf = lhp->allocp(buflen); 1608 1609 bcopy((void *)((ptrdiff_t)resp + sizeof (uint64_t)), *buf, buflen); 1610 lhp->freep(resp, resplen); 1611 1612 return (buflen); 1613 } 1614 1615 1616 /* 1617 * see cpu_request() for a description of return values 1618 */ 1619 int 1620 ldmsvcs_cpu_req_status(struct ldom_hdl *lhp, uint32_t cpuid) 1621 { 1622 return (cpu_request(lhp, FMA_CPU_REQ_STATUS, cpuid)); 1623 } 1624 1625 1626 int 1627 ldmsvcs_cpu_req_offline(struct ldom_hdl *lhp, uint32_t cpuid) 1628 { 1629 return (cpu_request(lhp, FMA_CPU_REQ_OFFLINE, cpuid)); 1630 } 1631 1632 int 1633 ldmsvcs_cpu_req_online(struct ldom_hdl *lhp, uint32_t cpuid) 1634 { 1635 return (cpu_request(lhp, FMA_CPU_REQ_ONLINE, cpuid)); 1636 } 1637 1638 /* 1639 * see mem_request() for a description of return values 1640 */ 1641 int 1642 ldmsvcs_mem_req_status(struct ldom_hdl *lhp, uint64_t pa) 1643 { 1644 return (mem_request(lhp, FMA_MEM_REQ_STATUS, pa, getpagesize())); 1645 } 1646 1647 int 1648 ldmsvcs_mem_req_retire(struct ldom_hdl *lhp, uint64_t pa) 1649 { 1650 return (mem_request(lhp, FMA_MEM_REQ_RETIRE, pa, getpagesize())); 1651 } 1652 1653 int 1654 ldmsvcs_mem_req_unretire(struct ldom_hdl *lhp, uint64_t pa) 1655 { 1656 return (mem_request(lhp, FMA_MEM_REQ_RESURRECT, pa, getpagesize())); 1657 } 1658 1659 int 1660 ldmsvcs_io_req_id(struct ldom_hdl *lhp, uint64_t addr, uint_t type, 1661 uint64_t *virt_addr, char *name, int name_len, uint64_t *did) 1662 { 1663 1664 ds_hdr_t *H; 1665 ds_data_handle_t *D; 1666 fma_io_req_t *R; 1667 1668 char *svcname = LDM_DS_NAME_IOD; 1669 void *resp; 1670 fma_io_resp_t *iop; 1671 size_t resplen, reqmsglen; 1672 int offset; 1673 int rc; 1674 1675 if (lhp->lsinfo == NULL) 1676 return (-1); 1677 1678 reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) + 1679 sizeof (fma_io_req_t); 1680 1681 H = lhp->allocp(reqmsglen); 1682 D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t)); 1683 R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t)); 1684 1685 H->msg_type = DS_DATA; 1686 H->payload_len = sizeof (ds_data_handle_t) + sizeof (fma_io_req_t); 1687 1688 R->req_num = fds_svc_req_num(); 1689 R->msg_type = type; 1690 R->rsrc_address = addr; 1691 1692 rc = ENOMSG; 1693 if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen, 1694 &D->svc_handle, svcname, &resp, &resplen)) != 0) { 1695 lhp->freep(H, reqmsglen); 1696 return (rc); 1697 } 1698 lhp->freep(H, reqmsglen); 1699 1700 /* 1701 * resp should contain the req_num, status, virtual addr, domain id 1702 * and the domain name. The domain name may or may not be present. 1703 */ 1704 offset = sizeof (fma_io_resp_t); 1705 if (resplen < offset) { 1706 lhp->freep(resp, resplen); 1707 return (-1); 1708 } 1709 1710 iop = (fma_io_resp_t *)resp; 1711 switch (iop->result) { 1712 case FMA_IO_RESP_OK: 1713 /* success */ 1714 rc = 0; 1715 *virt_addr = iop->virt_rsrc_address; 1716 *did = iop->domain_id; 1717 if (name == NULL || name_len <= 0) 1718 break; 1719 *name = '\0'; 1720 if (resplen > offset) { 1721 (void) strncpy(name, (char *)((ptrdiff_t)resp + offset), 1722 name_len); 1723 } 1724 break; 1725 default: 1726 rc = -1; 1727 break; 1728 } 1729 1730 lhp->freep(resp, resplen); 1731 return (rc); 1732 } 1733 1734 /* end file */ 1735