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