12072Svn83148 /*
22072Svn83148 * CDDL HEADER START
32072Svn83148 *
42072Svn83148 * The contents of this file are subject to the terms of the
52072Svn83148 * Common Development and Distribution License (the "License").
62072Svn83148 * You may not use this file except in compliance with the License.
72072Svn83148 *
82072Svn83148 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92072Svn83148 * or http://www.opensolaris.org/os/licensing.
102072Svn83148 * See the License for the specific language governing permissions
112072Svn83148 * and limitations under the License.
122072Svn83148 *
132072Svn83148 * When distributing Covered Code, include this CDDL HEADER in each
142072Svn83148 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152072Svn83148 * If applicable, add the following below this CDDL HEADER, with the
162072Svn83148 * fields enclosed by brackets "[]" replaced with your own identifying
172072Svn83148 * information: Portions Copyright [yyyy] [name of copyright owner]
182072Svn83148 *
192072Svn83148 * CDDL HEADER END
202072Svn83148 */
212072Svn83148 /*
22*12516SVuong.Nguyen@Sun.COM * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
232072Svn83148 */
242072Svn83148
252072Svn83148 #include <stdlib.h>
262072Svn83148 #include <stdio.h>
277850SVuong.Nguyen@Sun.COM
282072Svn83148 #include <strings.h>
292072Svn83148 #include <sys/types.h>
302072Svn83148 #include <sys/stat.h>
312072Svn83148 #include <time.h>
322072Svn83148 #include <fcntl.h>
332072Svn83148 #include <unistd.h>
342072Svn83148 #include <errno.h>
352072Svn83148 #include <assert.h>
362072Svn83148 #include <umem.h>
372072Svn83148 #include <alloca.h>
382072Svn83148 #include <sys/processor.h>
392072Svn83148 #include <poll.h>
402072Svn83148 #include <pthread.h>
418634SVuong.Nguyen@Sun.COM #include <signal.h>
424358Srb144127 #include <values.h>
434358Srb144127 #include <libscf.h>
442072Svn83148
457850SVuong.Nguyen@Sun.COM #include <ctype.h>
467850SVuong.Nguyen@Sun.COM
472072Svn83148 #include "ldmsvcs_utils.h"
488634SVuong.Nguyen@Sun.COM #include "ldom_alloc.h"
4911831SVuong.Nguyen@Sun.COM #include "ldom_utils.h"
502072Svn83148
512072Svn83148 #define ASSERT(cnd) \
522072Svn83148 ((void) ((cnd) || ((void) fprintf(stderr, \
532072Svn83148 "assertion failure in %s:%d: %s\n", \
542072Svn83148 __FILE__, __LINE__, #cnd), 0)))
552072Svn83148
562072Svn83148 #define FDS_VLDC \
572072Svn83148 "/devices/virtual-devices@100/channel-devices@200/" \
582072Svn83148 "/virtual-channel-client@1:ldmfma"
592072Svn83148
604358Srb144127 /* allow timeouts in sec that are nearly forever but small enough for an int */
614358Srb144127 #define LDM_TIMEOUT_CEILING (MAXINT / 2)
622072Svn83148
632072Svn83148 #define MIN(x, y) ((x) < (y) ? (x) : (y))
642072Svn83148
652072Svn83148 /*
662072Svn83148 * functions in this file are for version 1.0 of FMA domain services
672072Svn83148 */
682072Svn83148 static ds_ver_t ds_vers[] = {
692072Svn83148 { 1, 0 }
702072Svn83148 };
712072Svn83148
722072Svn83148 #define DS_NUM_VER (sizeof (ds_vers) / sizeof (ds_ver_t))
732072Svn83148
742072Svn83148 /*
752072Svn83148 * information for each channel
762072Svn83148 */
772072Svn83148 struct ldmsvcs_info {
782072Svn83148 pthread_mutex_t mt;
792072Svn83148 pthread_cond_t cv;
802072Svn83148 fds_channel_t fds_chan;
812072Svn83148 fds_reg_svcs_t fmas_svcs;
822072Svn83148 int cv_twait;
832072Svn83148 };
842072Svn83148
852072Svn83148 /*
862072Svn83148 * struct listdata_s and struct poller_s are used to maintain the state of
872072Svn83148 * the poller thread. this thread is used to manage incoming messages and
882072Svn83148 * pass those messages onto the correct requesting thread. see the "poller
892072Svn83148 * functions" section for more details.
902072Svn83148 */
912072Svn83148 struct listdata_s {
922072Svn83148 enum {
932072Svn83148 UNUSED,
942072Svn83148 PENDING,
952072Svn83148 ARRIVED
962072Svn83148 } status;
972072Svn83148 uint64_t req_num;
982072Svn83148 int fd;
992072Svn83148 size_t datalen;
1002072Svn83148 };
1012072Svn83148
1022072Svn83148 static struct poller_s {
1032072Svn83148 pthread_mutex_t mt;
1042072Svn83148 pthread_cond_t cv;
1052072Svn83148 pthread_t polling_tid;
106*12516SVuong.Nguyen@Sun.COM int notify_pipe[2];
1072072Svn83148 int doreset;
1082072Svn83148 int doexit;
1092072Svn83148 int nclients;
1102072Svn83148 struct listdata_s **list;
1112072Svn83148 int list_len;
1122072Svn83148 int pending_count;
1132072Svn83148 } pollbase = {
1142072Svn83148 PTHREAD_MUTEX_INITIALIZER,
1152072Svn83148 PTHREAD_COND_INITIALIZER,
1162072Svn83148 0,
117*12516SVuong.Nguyen@Sun.COM {-1, -1},
1182072Svn83148 1,
1192072Svn83148 0,
1202072Svn83148 0,
1212072Svn83148 NULL,
1222072Svn83148 0,
1232072Svn83148 0
1242072Svn83148 };
1252072Svn83148
1262072Svn83148
1272072Svn83148 static struct ldmsvcs_info *channel_init(struct ldom_hdl *lhp);
1282072Svn83148 static int channel_openreset(struct ldmsvcs_info *lsp);
1292072Svn83148 static int read_msg(struct ldmsvcs_info *lsp);
1302072Svn83148
1314358Srb144127 static int
get_smf_int_val(char * prop_nm,int min,int max,int default_val)1324358Srb144127 get_smf_int_val(char *prop_nm, int min, int max, int default_val)
1334358Srb144127 {
1344358Srb144127 scf_simple_prop_t *prop; /* SMF property */
1354358Srb144127 int64_t *valp; /* prop value ptr */
1364358Srb144127 int64_t val; /* prop value to return */
1374358Srb144127
1384358Srb144127 val = default_val;
1394358Srb144127 if ((prop = scf_simple_prop_get(NULL, LDM_SVC_NM, LDM_PROP_GROUP_NM,
1404358Srb144127 prop_nm)) != NULL) {
1414358Srb144127 if ((valp = scf_simple_prop_next_integer(prop)) != NULL) {
1424358Srb144127 val = *valp;
1434358Srb144127 if (val < min)
1444358Srb144127 val = min;
1454358Srb144127 else if (val > max)
1464358Srb144127 val = max;
1474358Srb144127 }
1484358Srb144127 scf_simple_prop_free(prop);
1494358Srb144127 }
1504358Srb144127 return ((int)val);
1514358Srb144127 }
1524358Srb144127
1532072Svn83148 static void
channel_close(struct ldmsvcs_info * lsp)1542072Svn83148 channel_close(struct ldmsvcs_info *lsp)
1552072Svn83148 {
1562072Svn83148 (void) pthread_mutex_lock(&lsp->mt);
1572072Svn83148
1583327Svn83148 if (lsp->fds_chan.state == CHANNEL_OPEN ||
1594358Srb144127 lsp->fds_chan.state == CHANNEL_READY) {
1603327Svn83148 (void) close(lsp->fds_chan.fd);
1614358Srb144127 lsp->cv_twait = get_smf_int_val(LDM_INIT_TO_PROP_NM,
1624358Srb144127 0, LDM_TIMEOUT_CEILING, LDM_INIT_WAIT_TIME);
1633327Svn83148 lsp->fds_chan.state = CHANNEL_CLOSED;
1643327Svn83148 }
1652072Svn83148
1662072Svn83148 (void) pthread_mutex_unlock(&lsp->mt);
1672072Svn83148 }
1682072Svn83148
1692072Svn83148 /*
1702072Svn83148 * read size bytes of data from a streaming fd into buf
1712072Svn83148 */
1722072Svn83148 static int
read_stream(int fd,void * buf,size_t size)1732072Svn83148 read_stream(int fd, void *buf, size_t size)
1742072Svn83148 {
1752072Svn83148 pollfd_t pollfd;
1762072Svn83148 ssize_t rv;
1772072Svn83148 size_t data_left;
1782072Svn83148 ptrdiff_t currentp;
1792072Svn83148
1802072Svn83148 pollfd.events = POLLIN;
1812072Svn83148 pollfd.revents = 0;
1822072Svn83148 pollfd.fd = fd;
1832072Svn83148
1842072Svn83148 currentp = (ptrdiff_t)buf;
1852072Svn83148 data_left = size;
1862072Svn83148
1872072Svn83148 /*
1882072Svn83148 * data may come in bits and pieces
1892072Svn83148 */
1902072Svn83148 do {
1912072Svn83148 if ((rv = read(fd, (void *)currentp, data_left)) < 0) {
192*12516SVuong.Nguyen@Sun.COM if (errno == EAGAIN && poll(&pollfd, 1, 3000) > 0)
1932072Svn83148 continue; /* retry */
1942072Svn83148 else
1952072Svn83148 return (1);
1962072Svn83148 }
1972072Svn83148
1982072Svn83148 data_left -= rv;
1992072Svn83148 currentp += rv;
2002072Svn83148 } while (data_left > 0);
2012072Svn83148
2022072Svn83148 return (0);
2032072Svn83148 }
2042072Svn83148
2052072Svn83148
2062072Svn83148 /*
2072072Svn83148 * poller functions
2082072Svn83148 *
2092072Svn83148 * at init time, a thread is created for the purpose of monitoring incoming
2102072Svn83148 * messages and doing one of the following:
2112072Svn83148 *
2122072Svn83148 * 1. doing the initial handshake and version negotiation
2132072Svn83148 *
2142072Svn83148 * 2. handing incoming data off to the requesting thread (which is an fmd
2152072Svn83148 * module or scheme thread)
2162072Svn83148 */
2172072Svn83148 static int
poller_handle_data(int fd,size_t payloadsize)2182072Svn83148 poller_handle_data(int fd, size_t payloadsize)
2192072Svn83148 {
2202072Svn83148 uint64_t *req_num;
2212072Svn83148 void *pr;
2222072Svn83148 size_t prlen;
2232072Svn83148 int i;
2242072Svn83148
2252072Svn83148 prlen = sizeof (ds_data_handle_t) + sizeof (uint64_t);
2262072Svn83148
2272072Svn83148 if (payloadsize < prlen)
2282072Svn83148 return (1);
2292072Svn83148
2302072Svn83148 pr = alloca(prlen);
2312072Svn83148
2322072Svn83148 if (read_stream(fd, pr, prlen) != 0)
2332072Svn83148 return (1);
2342072Svn83148
2352072Svn83148 req_num = (uint64_t *)((ptrdiff_t)pr + sizeof (ds_data_handle_t));
2362072Svn83148
2372072Svn83148 (void) pthread_mutex_lock(&pollbase.mt);
2382072Svn83148
2392072Svn83148 for (i = 0; i < pollbase.list_len; i++) {
2402072Svn83148 if (pollbase.list[i]->req_num == *req_num) {
2412072Svn83148 ASSERT(pollbase.list[i]->status == PENDING);
2422072Svn83148
2432072Svn83148 pollbase.list[i]->status = ARRIVED;
2442072Svn83148 pollbase.list[i]->fd = fd;
2452072Svn83148 pollbase.list[i]->datalen = payloadsize - prlen;
2462072Svn83148
2472072Svn83148 pollbase.pending_count--;
2482072Svn83148 (void) pthread_cond_broadcast(&pollbase.cv);
2492072Svn83148 break;
2502072Svn83148 }
2512072Svn83148 }
2522072Svn83148
2532072Svn83148 /*
2542072Svn83148 * now wait for receiving thread to read in the data
2552072Svn83148 */
2562072Svn83148 if (i < pollbase.list_len) {
2572072Svn83148 while (pollbase.list[i]->status == ARRIVED)
2582072Svn83148 (void) pthread_cond_wait(&pollbase.cv, &pollbase.mt);
2592072Svn83148 }
2602072Svn83148
2612072Svn83148 (void) pthread_mutex_unlock(&pollbase.mt);
2622072Svn83148
2632072Svn83148 return (0);
2642072Svn83148 }
2652072Svn83148
2662072Svn83148
2672072Svn83148 /*
2682072Svn83148 * note that this function is meant to handle only DS_DATA messages
2692072Svn83148 */
2702072Svn83148 static int
poller_recv_data(struct ldom_hdl * lhp,uint64_t req_num,int index,void ** resp,size_t * resplen)2712072Svn83148 poller_recv_data(struct ldom_hdl *lhp, uint64_t req_num, int index,
2722072Svn83148 void **resp, size_t *resplen)
2732072Svn83148 {
2742072Svn83148 struct timespec twait;
2752072Svn83148 int ier;
2762072Svn83148
2772072Svn83148 ier = 0;
2782072Svn83148 twait.tv_sec = time(NULL) + lhp->lsinfo->cv_twait;
2792072Svn83148 twait.tv_nsec = 0;
2802072Svn83148
2812072Svn83148 (void) pthread_mutex_lock(&pollbase.mt);
2822072Svn83148
2832072Svn83148 ASSERT(pollbase.list[index]->req_num == req_num);
2842072Svn83148
2852072Svn83148 while (pollbase.list[index]->status == PENDING &&
2862072Svn83148 pollbase.doreset == 0 && ier == 0)
2872072Svn83148 ier = pthread_cond_timedwait(&pollbase.cv, &pollbase.mt,
2884358Srb144127 &twait);
2892072Svn83148
2902072Svn83148 if (ier == 0) {
2912072Svn83148 if (pollbase.doreset == 0) {
2922072Svn83148 ASSERT(pollbase.list[index]->status == ARRIVED);
2932072Svn83148
2942072Svn83148 /*
2952072Svn83148 * need to add req_num to beginning of resp
2962072Svn83148 */
2972072Svn83148 *resplen = pollbase.list[index]->datalen +
2984358Srb144127 sizeof (uint64_t);
2992072Svn83148 *resp = lhp->allocp(*resplen);
3002072Svn83148 *((uint64_t *)*resp) = req_num;
3012072Svn83148
3022072Svn83148 if (read_stream(pollbase.list[index]->fd,
3034358Srb144127 (void *)((ptrdiff_t)*resp + sizeof (uint64_t)),
3044358Srb144127 *resplen - sizeof (uint64_t)) != 0)
3052072Svn83148 ier = ETIMEDOUT;
3062072Svn83148
3072072Svn83148 pollbase.list[index]->status = UNUSED;
3082072Svn83148 pollbase.list[index]->req_num = 0;
3092072Svn83148 (void) pthread_cond_broadcast(&pollbase.cv);
3102072Svn83148 } else {
3112072Svn83148 if (--(pollbase.pending_count) == 0)
3122072Svn83148 (void) pthread_cond_broadcast(&pollbase.cv);
3132072Svn83148 }
3142072Svn83148 }
3152072Svn83148
3162072Svn83148 (void) pthread_mutex_unlock(&pollbase.mt);
3172072Svn83148
3182072Svn83148 ASSERT(ier == 0 || ier == ETIMEDOUT);
3192072Svn83148
3202072Svn83148 return (ier);
3212072Svn83148 }
3222072Svn83148
3232072Svn83148
3242072Svn83148 static void
poller_add_client(void)3252072Svn83148 poller_add_client(void)
3262072Svn83148 {
3272072Svn83148 (void) pthread_mutex_lock(&pollbase.mt);
3282072Svn83148 pollbase.nclients++;
3292072Svn83148 (void) pthread_mutex_unlock(&pollbase.mt);
3302072Svn83148 }
3312072Svn83148
3322072Svn83148
3332072Svn83148 static void
poller_remove_client(void)3342072Svn83148 poller_remove_client(void)
3352072Svn83148 {
3362072Svn83148 (void) pthread_mutex_lock(&pollbase.mt);
3372072Svn83148 pollbase.nclients--;
3382072Svn83148 ASSERT(pollbase.nclients >= 0);
3392072Svn83148 (void) pthread_mutex_unlock(&pollbase.mt);
3402072Svn83148 }
3412072Svn83148
3422072Svn83148
3432072Svn83148 static int
poller_add_pending(uint64_t req_num)3448634SVuong.Nguyen@Sun.COM poller_add_pending(uint64_t req_num)
3452072Svn83148 {
3462072Svn83148 int newlen, index, i, j;
3472072Svn83148
3482072Svn83148 (void) pthread_mutex_lock(&pollbase.mt);
3492072Svn83148 pollbase.pending_count++;
3502072Svn83148
3512072Svn83148 for (j = 0, index = -1; j < 2 && index == -1; j++) {
3522072Svn83148 for (i = 0; i < pollbase.list_len; i++) {
3532072Svn83148 if (pollbase.list[i]->status == UNUSED) {
3542072Svn83148 pollbase.list[i]->status = PENDING;
3552072Svn83148 pollbase.list[i]->req_num = req_num;
3562072Svn83148 pollbase.list[i]->datalen = 0;
3572072Svn83148 index = i;
3582072Svn83148 break;
3592072Svn83148 }
3602072Svn83148 }
3612072Svn83148
3622072Svn83148 if (index == -1) {
3632072Svn83148 struct listdata_s **newlist, **oldlist;
3642072Svn83148
3652072Svn83148 /*
3662072Svn83148 * get to this point if list is not long enough.
3672072Svn83148 * check for a runaway list. since requests are
3682072Svn83148 * synchronous (clients send a request and need to
3692072Svn83148 * wait for the result before returning) the size
3702072Svn83148 * of the list cannot be much more than the number
3712072Svn83148 * of clients.
3722072Svn83148 */
3732072Svn83148 ASSERT(pollbase.list_len < pollbase.nclients + 1);
3742072Svn83148
3752072Svn83148 newlen = pollbase.list_len + 5;
3768634SVuong.Nguyen@Sun.COM newlist = ldom_alloc(newlen *
3778634SVuong.Nguyen@Sun.COM sizeof (struct listdata_s *));
3782072Svn83148
3792072Svn83148 for (i = 0; i < pollbase.list_len; i++)
3802072Svn83148 newlist[i] = pollbase.list[i];
3812072Svn83148
3822072Svn83148 oldlist = pollbase.list;
3832072Svn83148 pollbase.list = newlist;
3848634SVuong.Nguyen@Sun.COM ldom_free(oldlist, pollbase.list_len *
3858634SVuong.Nguyen@Sun.COM sizeof (struct listdata_s *));
3862072Svn83148
3872072Svn83148 for (i = pollbase.list_len; i < newlen; i++) {
3882072Svn83148 pollbase.list[i] =
3898634SVuong.Nguyen@Sun.COM ldom_alloc(sizeof (struct listdata_s));
3902072Svn83148 pollbase.list[i]->status = UNUSED;
3912072Svn83148 }
3922072Svn83148
3932072Svn83148 pollbase.list_len = newlen;
3942072Svn83148 }
3952072Svn83148 }
3962072Svn83148
3972072Svn83148 (void) pthread_mutex_unlock(&pollbase.mt);
3982072Svn83148 ASSERT(index != -1);
3992072Svn83148
4002072Svn83148 return (index);
4012072Svn83148 }
4022072Svn83148
4032072Svn83148
4042072Svn83148 static void
poller_delete_pending(uint64_t req_num,int index)4052072Svn83148 poller_delete_pending(uint64_t req_num, int index)
4062072Svn83148 {
4072072Svn83148 (void) pthread_mutex_lock(&pollbase.mt);
4082072Svn83148
4092072Svn83148 ASSERT(pollbase.list[index]->req_num == req_num);
4102072Svn83148 pollbase.list[index]->status = UNUSED;
4112072Svn83148
4122072Svn83148 if (--(pollbase.pending_count) == 0 && pollbase.doreset == 1)
4132072Svn83148 (void) pthread_cond_broadcast(&pollbase.cv);
4142072Svn83148
4152072Svn83148 (void) pthread_mutex_unlock(&pollbase.mt);
4162072Svn83148 }
4172072Svn83148
4182072Svn83148
4192072Svn83148 static void
poller_shutdown(boolean_t wait)4208634SVuong.Nguyen@Sun.COM poller_shutdown(boolean_t wait)
4212072Svn83148 {
4222072Svn83148 (void) pthread_mutex_lock(&pollbase.mt);
4232072Svn83148
4242072Svn83148 pollbase.doexit = 1;
4252072Svn83148
4262072Svn83148 (void) pthread_mutex_unlock(&pollbase.mt);
4278634SVuong.Nguyen@Sun.COM
4288634SVuong.Nguyen@Sun.COM if (wait == B_TRUE) {
429*12516SVuong.Nguyen@Sun.COM /*
430*12516SVuong.Nguyen@Sun.COM * Write a byte to the pipe to notify the poller thread to exit.
431*12516SVuong.Nguyen@Sun.COM * Then wait for it to exit.
432*12516SVuong.Nguyen@Sun.COM */
433*12516SVuong.Nguyen@Sun.COM (void) write(pollbase.notify_pipe[0], "1", 1);
4348634SVuong.Nguyen@Sun.COM (void) pthread_join(pollbase.polling_tid, NULL);
4358634SVuong.Nguyen@Sun.COM }
4362072Svn83148 }
4372072Svn83148
4382072Svn83148
4392072Svn83148 /*
4402072Svn83148 * perform the polling of incoming messages. manage any resets (usually
4412072Svn83148 * due to one end of the connection being closed) as well as exit
4422072Svn83148 * conditions.
4432072Svn83148 */
4442072Svn83148 static void *
poller_loop(void * arg)4452072Svn83148 poller_loop(void *arg)
4462072Svn83148 {
4472072Svn83148 struct ldmsvcs_info *lsp;
448*12516SVuong.Nguyen@Sun.COM pollfd_t pollfd[2];
449*12516SVuong.Nguyen@Sun.COM struct pollfd *pipe_fd = &pollfd[0];
450*12516SVuong.Nguyen@Sun.COM struct pollfd *recv_fd = &pollfd[1];
4512072Svn83148 int ier;
4522072Svn83148
4532072Svn83148 lsp = (struct ldmsvcs_info *)arg;
4542072Svn83148
4552072Svn83148 for (;;) {
4562072Svn83148 (void) pthread_mutex_lock(&pollbase.mt);
4572072Svn83148
4582072Svn83148 if (pollbase.doexit) {
4592072Svn83148 (void) pthread_mutex_unlock(&pollbase.mt);
4602072Svn83148 break;
4612072Svn83148 }
4622072Svn83148
4632072Svn83148 if (pollbase.doreset) {
4642072Svn83148 int i;
4652072Svn83148
4662072Svn83148 while (pollbase.pending_count > 0)
4672072Svn83148 (void) pthread_cond_wait(&pollbase.cv,
4684358Srb144127 &pollbase.mt);
4692072Svn83148
4702072Svn83148 ASSERT(pollbase.pending_count == 0);
4712072Svn83148 for (i = 0; i < pollbase.list_len; i++)
4722072Svn83148 pollbase.list[i]->status = UNUSED;
4732072Svn83148
4742072Svn83148 pollbase.doreset = 0;
4752072Svn83148 }
476*12516SVuong.Nguyen@Sun.COM
4772072Svn83148 (void) pthread_mutex_unlock(&pollbase.mt);
4782072Svn83148
4792072Svn83148 if ((ier = channel_openreset(lsp)) == 1) {
4802072Svn83148 continue;
4812072Svn83148 } else if (ier == 2) {
4822072Svn83148 /*
4832072Svn83148 * start exit preparations
4842072Svn83148 */
4858634SVuong.Nguyen@Sun.COM poller_shutdown(B_FALSE);
4862072Svn83148 continue;
4872072Svn83148 }
4882072Svn83148
489*12516SVuong.Nguyen@Sun.COM pipe_fd->fd = pollbase.notify_pipe[1]; /* notification pipe */
490*12516SVuong.Nguyen@Sun.COM pipe_fd->events = POLLIN;
491*12516SVuong.Nguyen@Sun.COM pipe_fd->revents = 0;
492*12516SVuong.Nguyen@Sun.COM recv_fd->fd = lsp->fds_chan.fd; /* FMA LDC */
493*12516SVuong.Nguyen@Sun.COM recv_fd->events = POLLIN;
494*12516SVuong.Nguyen@Sun.COM recv_fd->revents = 0;
4952072Svn83148
496*12516SVuong.Nguyen@Sun.COM if (poll(pollfd, 2, -1) <= 0) {
497*12516SVuong.Nguyen@Sun.COM /* fd got closed */
4982072Svn83148 (void) pthread_mutex_lock(&pollbase.mt);
4992072Svn83148 pollbase.doreset = 1;
5002072Svn83148 (void) pthread_mutex_unlock(&pollbase.mt);
501*12516SVuong.Nguyen@Sun.COM channel_close(lsp);
502*12516SVuong.Nguyen@Sun.COM } else if (pipe_fd->revents & POLLIN) {
503*12516SVuong.Nguyen@Sun.COM /* Receive a notification to exit */
504*12516SVuong.Nguyen@Sun.COM channel_close(lsp);
505*12516SVuong.Nguyen@Sun.COM pthread_exit((void *)NULL);
506*12516SVuong.Nguyen@Sun.COM } else if (read_msg(lsp) != 0) {
507*12516SVuong.Nguyen@Sun.COM /* fail to read a message from the LDOM manager */
508*12516SVuong.Nguyen@Sun.COM (void) pthread_mutex_lock(&pollbase.mt);
509*12516SVuong.Nguyen@Sun.COM pollbase.doreset = 1;
510*12516SVuong.Nguyen@Sun.COM (void) pthread_mutex_unlock(&pollbase.mt);
5112072Svn83148 channel_close(lsp);
5122072Svn83148 }
5132072Svn83148 }
5142072Svn83148
5152072Svn83148 return (NULL);
5162072Svn83148 }
5172072Svn83148
5182072Svn83148
5192072Svn83148 /*
5202072Svn83148 * create the polling thread
5212072Svn83148 */
5222072Svn83148 static int
poller_init(struct ldmsvcs_info * lsp)5232072Svn83148 poller_init(struct ldmsvcs_info *lsp)
5242072Svn83148 {
5252072Svn83148 int rc = 0;
5262072Svn83148
5272072Svn83148 (void) pthread_mutex_lock(&pollbase.mt);
5282072Svn83148
5292072Svn83148 if (pollbase.polling_tid == 0) {
5308634SVuong.Nguyen@Sun.COM pthread_attr_t *attr = NULL;
5312072Svn83148
5322072Svn83148 /*
5338634SVuong.Nguyen@Sun.COM * create a joinable polling thread for receiving messages
534*12516SVuong.Nguyen@Sun.COM * The notify pipe is for stopping the thread
5352072Svn83148 */
536*12516SVuong.Nguyen@Sun.COM (void) notify_setup(pollbase.notify_pipe);
5378634SVuong.Nguyen@Sun.COM if (pthread_create(&pollbase.polling_tid, attr,
5384358Srb144127 poller_loop, lsp) != 0)
5392072Svn83148 rc = 1;
5402072Svn83148 }
5412072Svn83148
5422072Svn83148 (void) pthread_mutex_unlock(&pollbase.mt);
5432072Svn83148
5442072Svn83148 return (rc);
5452072Svn83148 }
5462072Svn83148
5478634SVuong.Nguyen@Sun.COM /*
5488634SVuong.Nguyen@Sun.COM * Cleanup the polling thread
5498634SVuong.Nguyen@Sun.COM */
5508634SVuong.Nguyen@Sun.COM static void
poller_fini(void)5518634SVuong.Nguyen@Sun.COM poller_fini(void)
5528634SVuong.Nguyen@Sun.COM {
5538634SVuong.Nguyen@Sun.COM int i;
5548634SVuong.Nguyen@Sun.COM
5558634SVuong.Nguyen@Sun.COM /* stop the poller thread */
5568634SVuong.Nguyen@Sun.COM poller_shutdown(B_TRUE);
5578634SVuong.Nguyen@Sun.COM
5588634SVuong.Nguyen@Sun.COM (void) pthread_mutex_lock(&pollbase.mt);
5598634SVuong.Nguyen@Sun.COM
5608634SVuong.Nguyen@Sun.COM /* Free up the list of outstanding requests */
5618634SVuong.Nguyen@Sun.COM if (pollbase.list != NULL) {
5628634SVuong.Nguyen@Sun.COM for (i = 0; i < pollbase.list_len; i++) {
5638634SVuong.Nguyen@Sun.COM if (pollbase.list[i]) {
5648634SVuong.Nguyen@Sun.COM ldom_free(pollbase.list[i],
5658634SVuong.Nguyen@Sun.COM sizeof (struct listdata_s));
5668634SVuong.Nguyen@Sun.COM }
5678634SVuong.Nguyen@Sun.COM }
5688634SVuong.Nguyen@Sun.COM ldom_free(pollbase.list, pollbase.list_len *
5698634SVuong.Nguyen@Sun.COM sizeof (struct listdata_s *));
5708634SVuong.Nguyen@Sun.COM pollbase.list = NULL;
5718634SVuong.Nguyen@Sun.COM pollbase.list_len = 0;
5728634SVuong.Nguyen@Sun.COM }
5738634SVuong.Nguyen@Sun.COM
5748634SVuong.Nguyen@Sun.COM (void) pthread_mutex_unlock(&pollbase.mt);
5758634SVuong.Nguyen@Sun.COM }
5762072Svn83148
5772072Svn83148 /*
5782072Svn83148 * utilities for message handlers
5792072Svn83148 */
5802072Svn83148 static int
fds_send(struct ldmsvcs_info * lsp,void * msg,size_t msglen)5812072Svn83148 fds_send(struct ldmsvcs_info *lsp, void *msg, size_t msglen)
5822072Svn83148 {
5832072Svn83148 static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER;
5842072Svn83148
5852072Svn83148 (void) pthread_mutex_lock(&mt);
5862072Svn83148
5872072Svn83148 if (write(lsp->fds_chan.fd, msg, msglen) != msglen) {
5882072Svn83148 channel_close(lsp);
5892072Svn83148 (void) pthread_mutex_unlock(&mt);
5902072Svn83148 return (ETIMEDOUT);
5912072Svn83148 }
5922072Svn83148
5932072Svn83148 (void) pthread_mutex_unlock(&mt);
5942072Svn83148 return (0);
5952072Svn83148 }
5962072Svn83148
5972072Svn83148
5982072Svn83148 /*
5992072Svn83148 * Find the max and min version supported
6002072Svn83148 */
6012072Svn83148 static void
fds_min_max_versions(uint16_t * min_major,uint16_t * max_major)6022072Svn83148 fds_min_max_versions(uint16_t *min_major, uint16_t *max_major)
6032072Svn83148 {
6042072Svn83148 int i;
6052072Svn83148
6062072Svn83148 *min_major = ds_vers[0].major;
6072072Svn83148 *max_major = *min_major;
6082072Svn83148
6092072Svn83148 for (i = 1; i < DS_NUM_VER; i++) {
6102072Svn83148 if (ds_vers[i].major < *min_major)
6112072Svn83148 *min_major = ds_vers[i].major;
6122072Svn83148
6132072Svn83148 if (ds_vers[i].major > *max_major)
6142072Svn83148 *max_major = ds_vers[i].major;
6152072Svn83148 }
6162072Svn83148 }
6172072Svn83148
6182072Svn83148 /*
6192072Svn83148 * check whether the major and minor numbers requested by remote ds client
6202072Svn83148 * can be satisfied. if the requested major is supported, true is
6212072Svn83148 * returned, and the agreed minor is returned in new_minor. if the
6222072Svn83148 * requested major is not supported, the routine returns false, and the
6232072Svn83148 * closest major is returned in *new_major, upon which the ds client should
6242072Svn83148 * renegotiate. the closest major is the just lower that the requested
6252072Svn83148 * major number.
6262072Svn83148 */
6272072Svn83148 static boolean_t
fds_negotiate_version(uint16_t req_major,uint16_t * new_majorp,uint16_t * new_minorp)6282072Svn83148 fds_negotiate_version(uint16_t req_major, uint16_t *new_majorp,
6292072Svn83148 uint16_t *new_minorp)
6302072Svn83148 {
6312072Svn83148 int i = 0;
6322072Svn83148 uint16_t major, lower_major;
6332072Svn83148 uint16_t min_major, max_major;
6342072Svn83148 boolean_t found_match = B_FALSE;
6352072Svn83148
6362072Svn83148 fds_min_max_versions(&min_major, &max_major);
6372072Svn83148
6382072Svn83148 /*
6392072Svn83148 * if the minimum version supported is greater than the version
6402072Svn83148 * requested, return the lowest version supported
6412072Svn83148 */
6422072Svn83148 if (min_major > req_major) {
6432072Svn83148 *new_majorp = min_major;
6442072Svn83148 return (B_FALSE);
6452072Svn83148 }
6462072Svn83148
6472072Svn83148 /*
6482072Svn83148 * if the largest version supported is lower than the version
6492072Svn83148 * requested, return the largest version supported
6502072Svn83148 */
6512072Svn83148 if (max_major < req_major) {
6522072Svn83148 *new_majorp = max_major;
6532072Svn83148 return (B_FALSE);
6542072Svn83148 }
6552072Svn83148
6562072Svn83148 /*
6572072Svn83148 * now we know that the requested version lies between the min and
6582072Svn83148 * max versions supported. check if the requested major can be
6592072Svn83148 * found in supported versions.
6602072Svn83148 */
6612072Svn83148 lower_major = min_major;
6622072Svn83148 for (i = 0; i < DS_NUM_VER; i++) {
6632072Svn83148 major = ds_vers[i].major;
6642072Svn83148 if (major == req_major) {
6652072Svn83148 found_match = B_TRUE;
6662072Svn83148 *new_minorp = ds_vers[i].minor;
6672072Svn83148 *new_majorp = major;
6682072Svn83148 break;
6692072Svn83148 } else if ((major < req_major) && (major > lower_major))
6702072Svn83148 lower_major = major;
6712072Svn83148 }
6722072Svn83148
6732072Svn83148 /*
6742072Svn83148 * If no match is found, return the closest available number
6752072Svn83148 */
6762072Svn83148 if (!found_match)
6772072Svn83148 *new_majorp = lower_major;
6782072Svn83148
6792072Svn83148 return (found_match);
6802072Svn83148 }
6812072Svn83148
6822072Svn83148
6832072Svn83148 /*
6842072Svn83148 * return 0 if service is added; 1 if service is a duplicate
6852072Svn83148 */
6862072Svn83148 static int
fds_svc_add(struct ldmsvcs_info * lsp,ds_reg_req_t * req,int minor)6872072Svn83148 fds_svc_add(struct ldmsvcs_info *lsp, ds_reg_req_t *req, int minor)
6882072Svn83148 {
6892072Svn83148 fds_svc_t *svc;
6902072Svn83148 int i, rc;
6912072Svn83148
6922072Svn83148 svc = NULL;
6932072Svn83148 for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
6942072Svn83148 if (strcmp(lsp->fmas_svcs.tbl[i]->name, req->svc_id) == 0) {
6952072Svn83148 svc = lsp->fmas_svcs.tbl[i];
6962072Svn83148 break;
6972072Svn83148 }
6982072Svn83148 }
6992072Svn83148
7002072Svn83148 if (svc == NULL)
7012072Svn83148 return (0); /* we don't need this service */
7022072Svn83148
7032072Svn83148 (void) pthread_mutex_lock(&lsp->fmas_svcs.mt);
7042072Svn83148
7052072Svn83148 /*
7062072Svn83148 * duplicate registration is OK --- we retain the previous entry
7072072Svn83148 * (which has not been unregistered anyway)
7082072Svn83148 */
7092072Svn83148 if (svc->state == DS_SVC_ACTIVE) {
7102072Svn83148 rc = 1;
7112072Svn83148 } else {
7122072Svn83148 svc->state = DS_SVC_ACTIVE;
7132072Svn83148 svc->hdl = req->svc_handle;
7142072Svn83148 svc->ver.major = req->major_vers;
7152072Svn83148 svc->ver.minor = minor;
7162072Svn83148
7172072Svn83148 rc = 0;
7182072Svn83148 (void) pthread_cond_broadcast(&lsp->fmas_svcs.cv);
7192072Svn83148 }
7202072Svn83148
7212072Svn83148 (void) pthread_mutex_unlock(&lsp->fmas_svcs.mt);
7222072Svn83148
7232072Svn83148 return (rc);
7242072Svn83148 }
7252072Svn83148
7262072Svn83148
7272072Svn83148 static void
fds_svc_reset(struct ldmsvcs_info * lsp,int index)7282072Svn83148 fds_svc_reset(struct ldmsvcs_info *lsp, int index)
7292072Svn83148 {
7302072Svn83148 int i, start, end;
7312072Svn83148
7322072Svn83148 if (index >= 0) {
7332072Svn83148 start = index;
7342072Svn83148 end = index + 1;
7352072Svn83148 } else {
7362072Svn83148 start = 0;
7372072Svn83148 end = lsp->fmas_svcs.nsvcs;
7382072Svn83148 }
7392072Svn83148
7402072Svn83148 (void) pthread_mutex_lock(&lsp->fmas_svcs.mt);
7412072Svn83148
7422072Svn83148 for (i = start; i < end; i++) {
7432072Svn83148 lsp->fmas_svcs.tbl[i]->hdl = 0;
7442072Svn83148 lsp->fmas_svcs.tbl[i]->state = DS_SVC_INVAL;
7452072Svn83148 lsp->fmas_svcs.tbl[i]->ver.major =
7464358Srb144127 ds_vers[DS_NUM_VER - 1].major;
7472072Svn83148 lsp->fmas_svcs.tbl[i]->ver.minor =
7484358Srb144127 ds_vers[DS_NUM_VER - 1].minor;
7492072Svn83148 }
7502072Svn83148
7512072Svn83148 (void) pthread_mutex_unlock(&lsp->fmas_svcs.mt);
7522072Svn83148 }
7532072Svn83148
7542072Svn83148
7552072Svn83148 static int
fds_svc_remove(struct ldmsvcs_info * lsp,ds_svc_hdl_t svc_handle)7562072Svn83148 fds_svc_remove(struct ldmsvcs_info *lsp, ds_svc_hdl_t svc_handle)
7572072Svn83148 {
7582072Svn83148 int i;
7592072Svn83148
7602072Svn83148 for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
7612072Svn83148 if (lsp->fmas_svcs.tbl[i]->hdl == svc_handle) {
7622072Svn83148 fds_svc_reset(lsp, i);
7632072Svn83148 return (0);
7642072Svn83148 }
7652072Svn83148 }
7662072Svn83148
7672072Svn83148 return (1);
7682072Svn83148 }
7692072Svn83148
7702072Svn83148
7712072Svn83148 /*
7722072Svn83148 * message handlers
7732072Svn83148 */
7742072Svn83148 /*ARGSUSED*/
7752072Svn83148 static void
ds_handle_msg_noop(struct ldmsvcs_info * lsp,void * buf,size_t len)7762072Svn83148 ds_handle_msg_noop(struct ldmsvcs_info *lsp, void *buf, size_t len)
7772072Svn83148 {
7782072Svn83148 }
7792072Svn83148
7802072Svn83148 static void
ds_handle_init_req(struct ldmsvcs_info * lsp,void * buf,size_t len)7812072Svn83148 ds_handle_init_req(struct ldmsvcs_info *lsp, void *buf, size_t len)
7822072Svn83148 {
7832072Svn83148 ds_init_req_t *req;
7842072Svn83148 uint16_t new_major, new_minor;
7852072Svn83148 size_t msglen;
7862072Svn83148
7872072Svn83148 req = (ds_init_req_t *)buf;
7882072Svn83148
7892072Svn83148 /* sanity check the incoming message */
7902072Svn83148 if (len != sizeof (ds_init_req_t)) {
7912072Svn83148 channel_close(lsp);
7922072Svn83148 return;
7932072Svn83148 }
7942072Svn83148
7952072Svn83148 /*
7962072Svn83148 * Check version info. ACK only if the major numbers exactly
7972072Svn83148 * match. The service entity can retry with a new minor
7982072Svn83148 * based on the response sent as part of the NACK.
7992072Svn83148 */
8002072Svn83148 if (fds_negotiate_version(req->major_vers, &new_major, &new_minor)) {
8012072Svn83148 ds_hdr_t *H;
8022072Svn83148 ds_init_ack_t *R;
8032072Svn83148
8042072Svn83148 msglen = sizeof (ds_hdr_t) + sizeof (ds_init_ack_t);
8052072Svn83148 H = alloca(msglen);
8062072Svn83148 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
8072072Svn83148
8082072Svn83148 H->msg_type = DS_INIT_ACK;
8092072Svn83148 H->payload_len = sizeof (ds_init_ack_t);
8102072Svn83148 R->minor_vers = MIN(new_minor, req->minor_vers);
8112072Svn83148
8122072Svn83148 if (fds_send(lsp, H, msglen) != 0)
8132072Svn83148 return;
8142072Svn83148
8152072Svn83148 (void) pthread_mutex_lock(&lsp->mt);
8162072Svn83148 ASSERT(lsp->fds_chan.state == CHANNEL_OPEN);
8172072Svn83148 lsp->fds_chan.state = CHANNEL_READY;
8183327Svn83148
8193327Svn83148 /*
8203327Svn83148 * Now the channel is ready after the handshake completes.
8213327Svn83148 * Reset the timeout to a smaller value for receiving messages
8223327Svn83148 * from the domain services.
8233327Svn83148 */
8244358Srb144127 lsp->cv_twait = get_smf_int_val(LDM_RUNNING_TO_PROP_NM,
8254358Srb144127 0, LDM_TIMEOUT_CEILING, LDM_RUNNING_WAIT_TIME);
8263327Svn83148
8272072Svn83148 (void) pthread_mutex_unlock(&lsp->mt);
8282072Svn83148 } else {
8292072Svn83148 ds_hdr_t *H;
8302072Svn83148 ds_init_nack_t *R;
8312072Svn83148
8322072Svn83148 msglen = sizeof (ds_hdr_t) + sizeof (ds_init_nack_t);
8332072Svn83148 H = alloca(msglen);
8342072Svn83148 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
8352072Svn83148
8362072Svn83148 H->msg_type = DS_INIT_NACK;
8372072Svn83148 H->payload_len = sizeof (ds_init_nack_t);
8382072Svn83148 R->major_vers = new_major;
8392072Svn83148
8402072Svn83148 (void) fds_send(lsp, H, msglen);
8412072Svn83148 /*
8422072Svn83148 * do not update state; remote end may attempt to initiate
8432072Svn83148 * connection with a different version
8442072Svn83148 */
8452072Svn83148 }
8462072Svn83148 }
8472072Svn83148
8482072Svn83148
8492072Svn83148 /*ARGSUSED*/
8502072Svn83148 static void
ds_handle_reg_req(struct ldmsvcs_info * lsp,void * buf,size_t len)8512072Svn83148 ds_handle_reg_req(struct ldmsvcs_info *lsp, void *buf, size_t len)
8522072Svn83148 {
8532072Svn83148 ds_reg_req_t *req;
8542072Svn83148 char *msg;
8552072Svn83148 uint16_t new_major, new_minor;
8562072Svn83148 size_t msglen;
8572072Svn83148 int dup_svcreg = 0;
8582072Svn83148
8592072Svn83148 req = (ds_reg_req_t *)buf;
8602072Svn83148 msg = (char *)req->svc_id;
8612072Svn83148
8622072Svn83148 /*
8632072Svn83148 * Service must be NULL terminated
8642072Svn83148 */
8652072Svn83148 if (req->svc_id == NULL || strlen(req->svc_id) == 0 ||
8662072Svn83148 msg[strlen(req->svc_id)] != '\0') {
8672072Svn83148 channel_close(lsp);
8682072Svn83148 return;
8692072Svn83148 }
8702072Svn83148
8712072Svn83148 if (fds_negotiate_version(req->major_vers, &new_major, &new_minor) &&
8722072Svn83148 (dup_svcreg = fds_svc_add(lsp, req,
8734358Srb144127 MIN(new_minor, req->minor_vers))) == 0) {
8742072Svn83148
8752072Svn83148 /*
8762072Svn83148 * Check version info. ACK only if the major numbers
8772072Svn83148 * exactly match. The service entity can retry with a new
8782072Svn83148 * minor based on the response sent as part of the NACK.
8792072Svn83148 */
8802072Svn83148 ds_hdr_t *H;
8812072Svn83148 ds_reg_ack_t *R;
8822072Svn83148
8832072Svn83148 msglen = sizeof (ds_hdr_t) + sizeof (ds_reg_ack_t);
8842072Svn83148 H = alloca(msglen);
8852072Svn83148 bzero(H, msglen);
8862072Svn83148 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
8872072Svn83148
8882072Svn83148 H->msg_type = DS_REG_ACK;
8892072Svn83148 H->payload_len = sizeof (ds_reg_ack_t);
8902072Svn83148 R->svc_handle = req->svc_handle;
8912072Svn83148 R->minor_vers = MIN(new_minor, req->minor_vers);
8922072Svn83148
8932072Svn83148 (void) fds_send(lsp, H, msglen);
8942072Svn83148 } else {
8952072Svn83148 ds_hdr_t *H;
8962072Svn83148 ds_reg_nack_t *R;
8972072Svn83148
8982072Svn83148 msglen = sizeof (ds_hdr_t) + sizeof (ds_reg_nack_t);
8992072Svn83148 H = alloca(msglen);
9002072Svn83148 bzero(H, msglen);
9012072Svn83148 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
9022072Svn83148
9032072Svn83148 H->msg_type = DS_REG_NACK;
9042072Svn83148 H->payload_len = sizeof (ds_reg_nack_t);
9052072Svn83148 R->svc_handle = req->svc_handle;
9062072Svn83148 R->major_vers = new_major;
9072072Svn83148
9082072Svn83148 if (dup_svcreg)
9092072Svn83148 R->result = DS_REG_DUP;
9102072Svn83148 else
9112072Svn83148 R->result = DS_REG_VER_NACK;
9122072Svn83148
9132072Svn83148 (void) fds_send(lsp, H, msglen);
9142072Svn83148 }
9152072Svn83148 }
9162072Svn83148
9172072Svn83148
9182072Svn83148 /*ARGSUSED*/
9192072Svn83148 static void
ds_handle_unreg(struct ldmsvcs_info * lsp,void * buf,size_t len)9202072Svn83148 ds_handle_unreg(struct ldmsvcs_info *lsp, void *buf, size_t len)
9212072Svn83148 {
9222072Svn83148 ds_unreg_req_t *req;
9232072Svn83148 size_t msglen;
9242072Svn83148
9252072Svn83148 req = (ds_unreg_req_t *)buf;
9262072Svn83148
9272072Svn83148 if (fds_svc_remove(lsp, req->svc_handle) == 0) {
9282072Svn83148 ds_hdr_t *H;
9292072Svn83148 ds_unreg_ack_t *R;
9302072Svn83148
9312072Svn83148 msglen = sizeof (ds_hdr_t) + sizeof (ds_unreg_ack_t);
9322072Svn83148 H = alloca(msglen);
9332072Svn83148 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
9342072Svn83148
9352072Svn83148 H->msg_type = DS_REG_ACK;
9362072Svn83148 H->payload_len = sizeof (ds_unreg_ack_t);
9372072Svn83148 R->svc_handle = req->svc_handle;
9382072Svn83148
9392072Svn83148 (void) fds_send(lsp, H, msglen);
9402072Svn83148 } else {
9412072Svn83148 ds_hdr_t *H;
9422072Svn83148 ds_unreg_nack_t *R;
9432072Svn83148
9442072Svn83148 msglen = sizeof (ds_hdr_t) + sizeof (ds_unreg_nack_t);
9452072Svn83148 H = alloca(msglen);
9462072Svn83148 R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
9472072Svn83148
9482072Svn83148 H->msg_type = DS_REG_NACK;
9492072Svn83148 H->payload_len = sizeof (ds_unreg_nack_t);
9502072Svn83148 R->svc_handle = req->svc_handle;
9512072Svn83148
9522072Svn83148 (void) fds_send(lsp, H, msglen);
9532072Svn83148 }
9542072Svn83148 }
9552072Svn83148
9562072Svn83148
9572072Svn83148 /*
9582072Svn83148 * Message handler lookup table (v1.0 only for now) Future
9592072Svn83148 * versions can add their own lookup table.
9602072Svn83148 */
9612072Svn83148 typedef void (*ds_msg_handler_t)(struct ldmsvcs_info *lsp,
9622072Svn83148 void *buf, size_t len);
9632072Svn83148
9642072Svn83148 static const ds_msg_handler_t ds_msg_handlers[] = {
9652072Svn83148 ds_handle_init_req, /* DS_INIT_REQ */
9662072Svn83148 ds_handle_msg_noop, /* DS_INIT_ACK */
9672072Svn83148 ds_handle_msg_noop, /* DS_INIT_NACK */
9682072Svn83148 ds_handle_reg_req, /* DS_REG_REQ */
9692072Svn83148 ds_handle_msg_noop, /* DS_REG_ACK */
9702072Svn83148 ds_handle_msg_noop, /* DS_REG_NACK */
9712072Svn83148 ds_handle_unreg, /* DS_UNREG */
9722072Svn83148 ds_handle_msg_noop, /* DS_UNREG_ACK */
9732072Svn83148 ds_handle_msg_noop, /* DS_UNREG_NACK */
9742072Svn83148 ds_handle_msg_noop, /* DS_DATA */
9752072Svn83148 ds_handle_msg_noop /* DS_NACK */
9762072Svn83148 };
9772072Svn83148
9782072Svn83148
9792072Svn83148 /*
9802072Svn83148 * message and service internal functions
9812072Svn83148 */
9822072Svn83148 static void
fds_svc_alloc(struct ldmsvcs_info * lsp)9838634SVuong.Nguyen@Sun.COM fds_svc_alloc(struct ldmsvcs_info *lsp)
9842072Svn83148 {
9852072Svn83148 int i;
9867850SVuong.Nguyen@Sun.COM static char *name[] = { LDM_DS_NAME_CPU, LDM_DS_NAME_MEM,
9877850SVuong.Nguyen@Sun.COM LDM_DS_NAME_PRI, LDM_DS_NAME_IOD, NULL };
9882072Svn83148
9892072Svn83148 (void) pthread_mutex_init(&lsp->fmas_svcs.mt, NULL);
9902072Svn83148 (void) pthread_cond_init(&lsp->fmas_svcs.cv, NULL);
9912072Svn83148
9922072Svn83148 for (lsp->fmas_svcs.nsvcs = 0; name[lsp->fmas_svcs.nsvcs] != NULL;
9932072Svn83148 lsp->fmas_svcs.nsvcs++)
9942072Svn83148 ;
9952072Svn83148
9968634SVuong.Nguyen@Sun.COM lsp->fmas_svcs.tbl = (fds_svc_t **)ldom_alloc(sizeof (fds_svc_t *) *
9974358Srb144127 lsp->fmas_svcs.nsvcs);
9982072Svn83148
9992072Svn83148 for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
10002072Svn83148 lsp->fmas_svcs.tbl[i] =
10018634SVuong.Nguyen@Sun.COM (fds_svc_t *)ldom_alloc(sizeof (fds_svc_t));
10022072Svn83148 bzero(lsp->fmas_svcs.tbl[i], sizeof (fds_svc_t));
10032072Svn83148 lsp->fmas_svcs.tbl[i]->name = name[i];
10042072Svn83148 }
10052072Svn83148 }
10062072Svn83148
10072072Svn83148
10082072Svn83148 static fds_svc_t *
fds_svc_lookup(struct ldmsvcs_info * lsp,char * name)10092072Svn83148 fds_svc_lookup(struct ldmsvcs_info *lsp, char *name)
10102072Svn83148 {
10112072Svn83148 struct timespec twait;
10122072Svn83148 fds_svc_t *svc;
10132072Svn83148 int i, ier;
10142072Svn83148
10152072Svn83148 if (pthread_mutex_lock(&lsp->fmas_svcs.mt) == EINVAL)
10162072Svn83148 return (NULL); /* uninitialized or destroyed mutex */
10172072Svn83148
10182072Svn83148 svc = NULL;
10192072Svn83148 for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
10202072Svn83148 if (strcmp(lsp->fmas_svcs.tbl[i]->name, name) == 0) {
10212072Svn83148 svc = lsp->fmas_svcs.tbl[i];
10222072Svn83148 break;
10232072Svn83148 }
10242072Svn83148 }
10252072Svn83148
10262072Svn83148 ASSERT(svc != NULL);
10272072Svn83148
10287850SVuong.Nguyen@Sun.COM if (svc->state == DS_SVC_INACTIVE) {
10297850SVuong.Nguyen@Sun.COM /* service is not registered */
10307850SVuong.Nguyen@Sun.COM ier = ETIMEDOUT;
10317850SVuong.Nguyen@Sun.COM } else {
10327850SVuong.Nguyen@Sun.COM ier = 0;
10337850SVuong.Nguyen@Sun.COM twait.tv_sec = time(NULL) + lsp->cv_twait;
10347850SVuong.Nguyen@Sun.COM twait.tv_nsec = 0;
10352072Svn83148
10367850SVuong.Nguyen@Sun.COM while (svc->state != DS_SVC_ACTIVE && ier == 0 &&
10377850SVuong.Nguyen@Sun.COM lsp->fds_chan.state != CHANNEL_UNUSABLE)
10387850SVuong.Nguyen@Sun.COM ier = pthread_cond_timedwait(&lsp->fmas_svcs.cv,
10397850SVuong.Nguyen@Sun.COM &lsp->fmas_svcs.mt, &twait);
10407850SVuong.Nguyen@Sun.COM
10417850SVuong.Nguyen@Sun.COM /*
10427850SVuong.Nguyen@Sun.COM * By now, the ds service should have registered already.
10437850SVuong.Nguyen@Sun.COM * If it does not, ldmd probably does not support this service.
10447850SVuong.Nguyen@Sun.COM * Then mark the service state as inactive.
10457850SVuong.Nguyen@Sun.COM */
10467850SVuong.Nguyen@Sun.COM if (ier == ETIMEDOUT) {
10477850SVuong.Nguyen@Sun.COM svc->state = DS_SVC_INACTIVE;
10487850SVuong.Nguyen@Sun.COM }
10497850SVuong.Nguyen@Sun.COM }
10502072Svn83148
10512072Svn83148 (void) pthread_mutex_unlock(&lsp->fmas_svcs.mt);
10522072Svn83148
10532072Svn83148 if (ier == 0)
10542072Svn83148 return (svc);
10552072Svn83148 else
10562072Svn83148 return (NULL);
10572072Svn83148 }
10582072Svn83148
10592072Svn83148
10602072Svn83148 static uint64_t
fds_svc_req_num(void)10612072Svn83148 fds_svc_req_num(void)
10622072Svn83148 {
10632072Svn83148 static uint64_t req_num = 1;
10642072Svn83148
10652072Svn83148 return (req_num++);
10662072Svn83148 }
10672072Svn83148
10682072Svn83148
10692072Svn83148 /*
10702072Svn83148 * return 0 if successful, 1 if otherwise
10712072Svn83148 */
10722072Svn83148 static int
read_msg(struct ldmsvcs_info * lsp)10732072Svn83148 read_msg(struct ldmsvcs_info *lsp)
10742072Svn83148 {
10752072Svn83148 ds_hdr_t header;
10762072Svn83148 void *msg_buf;
10772072Svn83148
10782072Svn83148 /*
10792072Svn83148 * read the header
10802072Svn83148 */
10812072Svn83148 if (read_stream(lsp->fds_chan.fd, &header, sizeof (ds_hdr_t)) != 0)
10822072Svn83148 return (1);
10832072Svn83148
10842072Svn83148 if (header.msg_type >=
10852072Svn83148 sizeof (ds_msg_handlers) / sizeof (ds_msg_handler_t))
10864358Srb144127 return (1);
10872072Svn83148
10882072Svn83148 /*
10892072Svn83148 * handle data as a special case
10902072Svn83148 */
10912072Svn83148 if (header.msg_type == 9)
10922072Svn83148 return (poller_handle_data(lsp->fds_chan.fd,
10934358Srb144127 header.payload_len));
10942072Svn83148
10952072Svn83148 /*
10962072Svn83148 * all other types of messages should be small
10972072Svn83148 */
10982072Svn83148 ASSERT(header.payload_len < 1024);
10992072Svn83148 msg_buf = alloca(header.payload_len);
11002072Svn83148
11012072Svn83148 /*
11022072Svn83148 * read the payload
11032072Svn83148 */
11042072Svn83148 if (read_stream(lsp->fds_chan.fd, msg_buf, header.payload_len) != 0)
11052072Svn83148 return (1);
11062072Svn83148
11072072Svn83148 (*ds_msg_handlers[header.msg_type])(lsp, msg_buf, header.payload_len);
11082072Svn83148
11092072Svn83148 return (0);
11102072Svn83148 }
11112072Svn83148
11122072Svn83148
11132072Svn83148 /*
11142072Svn83148 * return values:
11152072Svn83148 * 0 - success
11162072Svn83148 * 1 - problem with opening the channel
11172072Svn83148 * 2 - channed not opened; request to exit has been detected
11182072Svn83148 */
11192072Svn83148 static int
channel_openreset(struct ldmsvcs_info * lsp)11202072Svn83148 channel_openreset(struct ldmsvcs_info *lsp)
11212072Svn83148 {
11222072Svn83148 int ier;
11232072Svn83148
11242072Svn83148 ier = pthread_mutex_lock(&lsp->mt);
11252072Svn83148
11262072Svn83148 if (ier == EINVAL || lsp->fds_chan.state == CHANNEL_EXIT ||
11272072Svn83148 lsp->fds_chan.state == CHANNEL_UNUSABLE) {
11282072Svn83148 (void) pthread_mutex_unlock(&lsp->mt);
11292072Svn83148 return (2);
11302072Svn83148 }
11312072Svn83148
11322072Svn83148 if (lsp->fds_chan.state == CHANNEL_UNINITIALIZED ||
11332072Svn83148 lsp->fds_chan.state == CHANNEL_CLOSED) {
11342072Svn83148 (void) pthread_cond_broadcast(&lsp->cv);
11352072Svn83148
11362072Svn83148 if ((lsp->fds_chan.fd = open(FDS_VLDC, O_RDWR)) < 0) {
11372072Svn83148 lsp->fds_chan.state = CHANNEL_UNUSABLE;
11384358Srb144127 lsp->cv_twait = get_smf_int_val(LDM_RUNNING_TO_PROP_NM,
11394358Srb144127 0, LDM_TIMEOUT_CEILING, LDM_RUNNING_WAIT_TIME);
11402072Svn83148 (void) pthread_mutex_unlock(&lsp->mt);
11412072Svn83148 (void) pthread_cond_broadcast(&lsp->fmas_svcs.cv);
11422072Svn83148
11432072Svn83148 return (2);
11442072Svn83148 } else {
11452072Svn83148 vldc_opt_op_t op;
11462072Svn83148
11472072Svn83148 op.op_sel = VLDC_OP_SET;
11482072Svn83148 op.opt_sel = VLDC_OPT_MODE;
11496408Sha137994 op.opt_val = LDC_MODE_RELIABLE;
11502072Svn83148
11512072Svn83148 if (ioctl(lsp->fds_chan.fd, VLDC_IOCTL_OPT_OP,
11524358Srb144127 &op) != 0) {
11532072Svn83148 (void) close(lsp->fds_chan.fd);
11542072Svn83148 (void) pthread_mutex_unlock(&lsp->mt);
11552072Svn83148 return (1);
11562072Svn83148 }
11572072Svn83148 }
11582072Svn83148 lsp->fds_chan.state = CHANNEL_OPEN;
11592072Svn83148 }
11602072Svn83148
11612072Svn83148 if (lsp->fds_chan.state == CHANNEL_OPEN) {
11622072Svn83148 /*
11632072Svn83148 * reset various channel parameters
11642072Svn83148 */
11652072Svn83148 lsp->fds_chan.ver.major = 0;
11662072Svn83148 lsp->fds_chan.ver.minor = 0;
11672072Svn83148 fds_svc_reset(lsp, -1);
11682072Svn83148 }
11692072Svn83148 (void) pthread_mutex_unlock(&lsp->mt);
11702072Svn83148
11712072Svn83148 return (0);
11722072Svn83148 }
11732072Svn83148
11742072Svn83148
11752072Svn83148 static void
channel_fini(void)11762072Svn83148 channel_fini(void)
11772072Svn83148 {
11788634SVuong.Nguyen@Sun.COM int i;
11792072Svn83148 struct ldmsvcs_info *lsp;
11802072Svn83148
11812072Svn83148 /*
11822072Svn83148 * End the poller thread
11832072Svn83148 */
11848634SVuong.Nguyen@Sun.COM poller_fini();
11852072Svn83148
11862072Svn83148 if ((lsp = channel_init(NULL)) == NULL)
11872072Svn83148 return;
11882072Svn83148
11892072Svn83148 (void) pthread_mutex_lock(&lsp->mt);
11902072Svn83148
11912072Svn83148 lsp->fds_chan.state = CHANNEL_EXIT;
11922072Svn83148 (void) close(lsp->fds_chan.fd);
11932072Svn83148
11942072Svn83148 (void) pthread_mutex_unlock(&lsp->mt);
11958634SVuong.Nguyen@Sun.COM
11968634SVuong.Nguyen@Sun.COM /* Free the ldom service structure */
11978634SVuong.Nguyen@Sun.COM for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
11988634SVuong.Nguyen@Sun.COM ldom_free(lsp->fmas_svcs.tbl[i], sizeof (fds_svc_t));
11998634SVuong.Nguyen@Sun.COM }
12008634SVuong.Nguyen@Sun.COM ldom_free(lsp->fmas_svcs.tbl,
12018634SVuong.Nguyen@Sun.COM lsp->fmas_svcs.nsvcs * sizeof (fds_svc_t *));
12028634SVuong.Nguyen@Sun.COM ldom_free(lsp, sizeof (struct ldmsvcs_info));
12032072Svn83148 }
12042072Svn83148
12052072Svn83148
12062072Svn83148 static struct ldmsvcs_info *
channel_init(struct ldom_hdl * lhp)12072072Svn83148 channel_init(struct ldom_hdl *lhp)
12082072Svn83148 {
12092072Svn83148 static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER;
12102072Svn83148 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
12112072Svn83148 static struct ldmsvcs_info *root = NULL;
12122072Svn83148 static int busy_init = 0;
12132072Svn83148
12142072Svn83148 struct timespec twait;
12152072Svn83148 int expired;
12162072Svn83148
12172072Svn83148 (void) pthread_mutex_lock(&mt);
12182072Svn83148
12192072Svn83148 while (busy_init == 1)
12202072Svn83148 (void) pthread_cond_wait(&cv, &mt);
12212072Svn83148
12222072Svn83148 if (root != NULL || (lhp == NULL && root == NULL)) {
12232072Svn83148 (void) pthread_mutex_unlock(&mt);
12242072Svn83148 return (root);
12252072Svn83148 }
12262072Svn83148
12272072Svn83148 /*
12282072Svn83148 * get to this point if we need to open the channel
12292072Svn83148 */
12302072Svn83148 busy_init = 1;
12312072Svn83148 (void) pthread_mutex_unlock(&mt);
12322072Svn83148
12332072Svn83148 root = (struct ldmsvcs_info *)
12348634SVuong.Nguyen@Sun.COM ldom_alloc(sizeof (struct ldmsvcs_info));
12352072Svn83148 bzero(root, sizeof (struct ldmsvcs_info));
12362072Svn83148
12372072Svn83148 root->fds_chan.state = CHANNEL_UNINITIALIZED;
12384358Srb144127 root->cv_twait = get_smf_int_val(LDM_INIT_TO_PROP_NM,
12394358Srb144127 0, LDM_TIMEOUT_CEILING, LDM_INIT_WAIT_TIME);
12402072Svn83148
12412072Svn83148 if (pthread_mutex_init(&root->mt, NULL) != 0 ||
12422072Svn83148 pthread_cond_init(&root->cv, NULL) != 0) {
12438634SVuong.Nguyen@Sun.COM ldom_free(root, sizeof (struct ldmsvcs_info));
12442072Svn83148 return (NULL);
12452072Svn83148 }
12462072Svn83148
12478634SVuong.Nguyen@Sun.COM fds_svc_alloc(root);
12482072Svn83148 fds_svc_reset(root, -1);
12492072Svn83148
12502072Svn83148 (void) poller_init(root);
12512072Svn83148
12522072Svn83148 expired = 0;
12532072Svn83148 twait.tv_sec = time(NULL) + 10;
12542072Svn83148 twait.tv_nsec = 0;
12552072Svn83148
12562072Svn83148 (void) pthread_mutex_lock(&root->mt);
12572072Svn83148
12582072Svn83148 /*
12592072Svn83148 * wait for channel to become uninitialized. this should be quick.
12602072Svn83148 */
12612072Svn83148 while (root->fds_chan.state == CHANNEL_UNINITIALIZED && expired == 0)
12622072Svn83148 expired = pthread_cond_timedwait(&root->cv, &root->mt, &twait);
12632072Svn83148
12642072Svn83148 if (root->fds_chan.state == CHANNEL_UNUSABLE)
12652072Svn83148 expired = 1;
12662072Svn83148
12672072Svn83148 (void) pthread_mutex_unlock(&root->mt);
12682072Svn83148
12692072Svn83148 (void) pthread_mutex_lock(&mt);
12702072Svn83148 busy_init = 0;
12712072Svn83148 (void) pthread_mutex_unlock(&mt);
12722072Svn83148 (void) pthread_cond_broadcast(&cv);
12732072Svn83148
12742072Svn83148 (void) atexit(channel_fini);
12752072Svn83148
12762072Svn83148 if (expired == 0)
12772072Svn83148 return (root);
12782072Svn83148 else
12792072Svn83148 return (NULL);
12802072Svn83148 }
12812072Svn83148
12822072Svn83148
12832072Svn83148 static int
sendrecv(struct ldom_hdl * lhp,uint64_t req_num,void * msg,size_t msglen,ds_svc_hdl_t * svc_hdl,char * svcname,void ** resp,size_t * resplen)12842072Svn83148 sendrecv(struct ldom_hdl *lhp, uint64_t req_num,
12852072Svn83148 void *msg, size_t msglen, ds_svc_hdl_t *svc_hdl, char *svcname,
12862072Svn83148 void **resp, size_t *resplen)
12872072Svn83148 {
12882072Svn83148 struct ldmsvcs_info *lsp;
12892072Svn83148 fds_svc_t *svc;
12902072Svn83148 int maxretries, index, i, ier;
12912072Svn83148
12922072Svn83148 lsp = lhp->lsinfo;
12932072Svn83148 i = 0;
12942072Svn83148 maxretries = 1;
12952072Svn83148
12962072Svn83148 do {
12972072Svn83148 /*
12982072Svn83148 * if any of the calls in this loop fail, retry some number
12992072Svn83148 * of times before giving up.
13002072Svn83148 */
13012072Svn83148 if ((svc = fds_svc_lookup(lsp, svcname)) == NULL) {
13022072Svn83148 (void) pthread_mutex_lock(&lsp->mt);
13032072Svn83148
13042072Svn83148 if (lsp->fds_chan.state != CHANNEL_READY)
13052072Svn83148 ier = ETIMEDOUT; /* channel not ready */
13062072Svn83148 else
13072072Svn83148 ier = ENOTSUP; /* service not ready */
13082072Svn83148
13092072Svn83148 (void) pthread_mutex_unlock(&lsp->mt);
13102072Svn83148
13112072Svn83148 continue;
13122072Svn83148 } else {
13132072Svn83148 ier = 0;
13142072Svn83148 *svc_hdl = svc->hdl;
13152072Svn83148 }
13162072Svn83148
13178634SVuong.Nguyen@Sun.COM index = poller_add_pending(req_num);
13182072Svn83148
13192072Svn83148 if ((ier = fds_send(lsp, msg, msglen)) != 0 ||
13202072Svn83148 (ier = poller_recv_data(lhp, req_num, index, resp,
13214358Srb144127 resplen)) != 0)
13222072Svn83148 poller_delete_pending(req_num, index);
13232072Svn83148
13242072Svn83148 } while (i++ < maxretries && ier != 0);
13252072Svn83148
13262072Svn83148 ASSERT(ier == 0 || ier == ETIMEDOUT || ier == ENOTSUP);
13272072Svn83148
13282072Svn83148 return (ier);
13292072Svn83148 }
13302072Svn83148
13312072Svn83148
13322072Svn83148 /*
13332072Svn83148 * input:
13342072Svn83148 * msg_type - requested operation: FMA_CPU_REQ_STATUS or FMA_CPU_REQ_OFFLINE
13352072Svn83148 * cpuid - physical cpu id
13362072Svn83148 *
13372072Svn83148 * normal return values:
13382072Svn83148 * P_OFFLINE - cpu is offline
13392072Svn83148 * P_ONLINE - cpu is online
13402072Svn83148 *
13412072Svn83148 * abnormal return values:
13422072Svn83148 * ETIMEDOUT - LDOM manager is not responding
13432072Svn83148 * ENOTSUP - LDOM service for cpu offlining/status is not available
13442072Svn83148 * ENOMSG - got an unexpected response from the LDOM cpu service
13452072Svn83148 */
13462072Svn83148 static int
cpu_request(struct ldom_hdl * lhp,uint32_t msg_type,uint32_t cpuid)13472072Svn83148 cpu_request(struct ldom_hdl *lhp, uint32_t msg_type, uint32_t cpuid)
13482072Svn83148 {
13492072Svn83148 ds_hdr_t *H;
13502072Svn83148 ds_data_handle_t *D;
13512072Svn83148 fma_cpu_service_req_t *R;
13522072Svn83148
13537850SVuong.Nguyen@Sun.COM char *svcname = LDM_DS_NAME_CPU;
13542072Svn83148 fma_cpu_resp_t *respmsg;
13552072Svn83148 void *resp;
13562072Svn83148 size_t resplen, reqmsglen;
13572072Svn83148 int rc;
13582072Svn83148
13592072Svn83148 if (lhp->lsinfo == NULL)
13602072Svn83148 return (ENOMSG);
13612072Svn83148
13622072Svn83148 reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
13634358Srb144127 sizeof (fma_cpu_service_req_t);
13642072Svn83148
13652072Svn83148 H = lhp->allocp(reqmsglen);
13662072Svn83148 D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
13672072Svn83148 R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
13682072Svn83148
13692072Svn83148 H->msg_type = DS_DATA;
13702072Svn83148 H->payload_len = sizeof (ds_data_handle_t) +
13714358Srb144127 sizeof (fma_cpu_service_req_t);
13722072Svn83148
13732072Svn83148 R->req_num = fds_svc_req_num();
13742072Svn83148 R->msg_type = msg_type;
13752072Svn83148 R->cpu_id = cpuid;
13762072Svn83148
13772072Svn83148 if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
13784358Srb144127 &D->svc_handle, svcname, &resp, &resplen)) != 0) {
13792072Svn83148 lhp->freep(H, reqmsglen);
13802072Svn83148 return (rc);
13812072Svn83148 }
13822072Svn83148
13832072Svn83148 lhp->freep(H, reqmsglen);
13842072Svn83148
13852072Svn83148 ASSERT(resplen == sizeof (fma_cpu_resp_t));
13862072Svn83148 respmsg = (fma_cpu_resp_t *)resp;
13872072Svn83148
13882072Svn83148 rc = ENOMSG;
13892072Svn83148 if (respmsg->result == FMA_CPU_RESP_OK) {
13902072Svn83148 if (respmsg->status == FMA_CPU_STAT_ONLINE)
13912072Svn83148 rc = P_ONLINE;
13922072Svn83148 else if (respmsg->status == FMA_CPU_STAT_OFFLINE)
13932072Svn83148 rc = P_OFFLINE;
13942072Svn83148 } else {
13952072Svn83148 if (msg_type == FMA_CPU_REQ_OFFLINE &&
13962072Svn83148 respmsg->status == FMA_CPU_STAT_OFFLINE)
13972072Svn83148 rc = P_OFFLINE;
13982072Svn83148 }
13992072Svn83148
14002072Svn83148 lhp->freep(resp, resplen);
14012072Svn83148
14022072Svn83148 return (rc);
14032072Svn83148 }
14042072Svn83148
14052072Svn83148
14062072Svn83148 /*
14072072Svn83148 * input:
14082072Svn83148 * msg_type - requested operation: FMA_MEM_REQ_STATUS or FMA_MEM_REQ_RETIRE
14092072Svn83148 * pa - starting address of memory page
14102072Svn83148 * pgsize - memory page size in bytes
14112072Svn83148 *
14122072Svn83148 * normal return values for msg_type == FMA_MEM_REQ_STATUS:
14132072Svn83148 * 0 - page is retired
14142072Svn83148 * EAGAIN - page is scheduled for retirement
14152072Svn83148 * EIO - page not scheduled for retirement
14162072Svn83148 * EINVAL - error
14172072Svn83148 *
14182072Svn83148 * normal return values for msg_type == FMA_MEM_REQ_RETIRE:
14192072Svn83148 * 0 - success in retiring page
14202072Svn83148 * EIO - page is already retired
14212072Svn83148 * EAGAIN - page is scheduled for retirement
14222072Svn83148 * EINVAL - error
14232072Svn83148 *
14242072Svn83148 * abnormal return values (regardless of msg_type)
14252072Svn83148 * ETIMEDOUT - LDOM manager is not responding
14262072Svn83148 * ENOTSUP - LDOM service for cpu offlining/status is not available
14272072Svn83148 * ENOMSG - got an unexpected response from the LDOM cpu service
14282072Svn83148 */
14292072Svn83148 static int
mem_request(struct ldom_hdl * lhp,uint32_t msg_type,uint64_t pa,uint64_t pgsize)14302072Svn83148 mem_request(struct ldom_hdl *lhp, uint32_t msg_type, uint64_t pa,
14312072Svn83148 uint64_t pgsize)
14322072Svn83148 {
14332072Svn83148 ds_hdr_t *H;
14342072Svn83148 ds_data_handle_t *D;
14352072Svn83148 fma_mem_service_req_t *R;
14362072Svn83148
14377850SVuong.Nguyen@Sun.COM char *svcname = LDM_DS_NAME_MEM;
14382072Svn83148 fma_mem_resp_t *respmsg;
14392072Svn83148 void *resp;
14402072Svn83148 size_t resplen, reqmsglen;
14412072Svn83148 int rc;
14422072Svn83148
14432072Svn83148 if (lhp->lsinfo == NULL)
14442072Svn83148 return (ENOMSG);
14452072Svn83148
14462072Svn83148 reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
14474358Srb144127 sizeof (fma_mem_service_req_t);
14482072Svn83148
14492072Svn83148 H = lhp->allocp(reqmsglen);
14502072Svn83148 bzero(H, reqmsglen);
14512072Svn83148 D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
14522072Svn83148 R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
14532072Svn83148
14542072Svn83148 H->msg_type = DS_DATA;
14552072Svn83148 H->payload_len = sizeof (ds_data_handle_t) +
14564358Srb144127 sizeof (fma_mem_service_req_t);
14572072Svn83148
14582072Svn83148 R->req_num = fds_svc_req_num();
14592072Svn83148 R->msg_type = msg_type;
14602072Svn83148 R->real_addr = pa;
14612072Svn83148 R->length = pgsize;
14622072Svn83148
14632072Svn83148 if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
14644358Srb144127 &D->svc_handle, svcname, &resp, &resplen)) != 0) {
14652072Svn83148 lhp->freep(H, reqmsglen);
14662072Svn83148 return (rc);
14672072Svn83148 }
14682072Svn83148
14692072Svn83148 lhp->freep(H, reqmsglen);
14702072Svn83148
14712072Svn83148 ASSERT(resplen == sizeof (fma_mem_resp_t));
14722072Svn83148 respmsg = (fma_mem_resp_t *)resp;
14732072Svn83148
14742072Svn83148 rc = ENOMSG;
14752072Svn83148 if (msg_type == FMA_MEM_REQ_STATUS) {
14762072Svn83148 if (respmsg->result == FMA_MEM_RESP_OK) {
14772072Svn83148 if (respmsg->status == FMA_MEM_STAT_RETIRED)
14784655Svn83148 rc = 0; /* page is retired */
14792072Svn83148 else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14804655Svn83148 rc = EIO; /* page is not scheduled */
14812072Svn83148 } else if (respmsg->result == FMA_MEM_RESP_FAILURE) {
14824655Svn83148 if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14834655Svn83148 rc = EAGAIN; /* page is scheduled */
14844655Svn83148 else if (respmsg->status == FMA_MEM_STAT_ILLEGAL)
14852072Svn83148 rc = EINVAL;
14862072Svn83148 }
14872072Svn83148 } else if (msg_type == FMA_MEM_REQ_RETIRE) {
14882072Svn83148 if (respmsg->result == FMA_MEM_RESP_OK) {
14892072Svn83148 if (respmsg->status == FMA_MEM_STAT_RETIRED)
14904655Svn83148 rc = 0; /* is successfully retired */
14912072Svn83148 } else if (respmsg->result == FMA_MEM_RESP_FAILURE) {
14922072Svn83148 if (respmsg->status == FMA_MEM_STAT_RETIRED)
14934655Svn83148 rc = EIO; /* is already retired */
14944655Svn83148 else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14954655Svn83148 rc = EAGAIN; /* is scheduled to retire */
14962072Svn83148 else if (respmsg->status == FMA_MEM_STAT_ILLEGAL)
14972072Svn83148 rc = EINVAL;
14982072Svn83148 }
14997532SSean.Ye@Sun.COM } else if (msg_type == FMA_MEM_REQ_RESURRECT) {
15007532SSean.Ye@Sun.COM if (respmsg->result == FMA_MEM_RESP_OK) {
15017532SSean.Ye@Sun.COM if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
15027532SSean.Ye@Sun.COM rc = 0; /* is successfully unretired */
15037532SSean.Ye@Sun.COM } if (respmsg->result == FMA_MEM_RESP_FAILURE) {
15047532SSean.Ye@Sun.COM if (respmsg->status == FMA_MEM_STAT_RETIRED)
15057532SSean.Ye@Sun.COM rc = EAGAIN; /* page couldn't be locked */
15067532SSean.Ye@Sun.COM else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
15077532SSean.Ye@Sun.COM rc = EIO; /* page isn't retired already */
15087532SSean.Ye@Sun.COM else if (respmsg->status == FMA_MEM_STAT_ILLEGAL)
15097532SSean.Ye@Sun.COM rc = EINVAL;
15107532SSean.Ye@Sun.COM }
15112072Svn83148 }
15122072Svn83148
15132072Svn83148 lhp->freep(resp, resplen);
15142072Svn83148
15152072Svn83148 return (rc);
15162072Svn83148 }
15172072Svn83148
15182072Svn83148
15192072Svn83148 /*
15202072Svn83148 * APIs
15212072Svn83148 */
15222072Svn83148 int
ldmsvcs_check_channel(void)15232072Svn83148 ldmsvcs_check_channel(void)
15242072Svn83148 {
15252072Svn83148 struct stat buf;
15262072Svn83148
15272072Svn83148 if (stat(FDS_VLDC, &buf) == 0)
15282072Svn83148 return (0); /* vldc exists */
15292072Svn83148 else if (errno == ENOENT || errno == ENOTDIR)
15302072Svn83148 return (1); /* vldc does not exist */
15312072Svn83148 else
15322072Svn83148 return (-1); /* miscellaneous error */
15332072Svn83148 }
15342072Svn83148
15352072Svn83148
15362072Svn83148 /*ARGSUSED*/
15372072Svn83148 void
ldmsvcs_init(struct ldom_hdl * lhp)15382072Svn83148 ldmsvcs_init(struct ldom_hdl *lhp)
15392072Svn83148 {
15402072Svn83148 if (ldmsvcs_check_channel() != 0)
15412072Svn83148 return;
15422072Svn83148
15432072Svn83148 lhp->lsinfo = channel_init(lhp);
15442072Svn83148 poller_add_client();
15452072Svn83148 }
15462072Svn83148
15472072Svn83148
15482072Svn83148 /*ARGSUSED*/
15492072Svn83148 void
ldmsvcs_fini(struct ldom_hdl * lhp)15502072Svn83148 ldmsvcs_fini(struct ldom_hdl *lhp)
15512072Svn83148 {
15522072Svn83148 if (ldmsvcs_check_channel() != 0)
15532072Svn83148 return;
15542072Svn83148
15552072Svn83148 poller_remove_client();
15562072Svn83148 }
15572072Svn83148
15582072Svn83148
15592072Svn83148 /*ARGSUSED*/
15602072Svn83148 ssize_t
ldmsvcs_get_core_md(struct ldom_hdl * lhp,uint64_t ** buf)15612072Svn83148 ldmsvcs_get_core_md(struct ldom_hdl *lhp, uint64_t **buf)
15622072Svn83148 {
15632072Svn83148 ds_hdr_t *H;
15642072Svn83148 ds_data_handle_t *D;
15652072Svn83148 fma_req_pri_t *R;
15662072Svn83148
15677850SVuong.Nguyen@Sun.COM char *svcname = LDM_DS_NAME_PRI;
15682072Svn83148 void *resp;
15692072Svn83148 size_t resplen, reqmsglen;
15702072Svn83148 ssize_t buflen;
15712072Svn83148 int rc;
15722072Svn83148
15732072Svn83148 if (lhp->lsinfo == NULL)
15742072Svn83148 return (-1);
15752072Svn83148
15762072Svn83148 reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
15774358Srb144127 sizeof (fma_req_pri_t);
15782072Svn83148
15792072Svn83148 H = lhp->allocp(reqmsglen);
15802072Svn83148 D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
15812072Svn83148 R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
15822072Svn83148
15832072Svn83148 H->msg_type = DS_DATA;
15842072Svn83148 H->payload_len = sizeof (ds_data_handle_t) +
15854358Srb144127 sizeof (fma_req_pri_t);
15862072Svn83148
15872072Svn83148 R->req_num = fds_svc_req_num();
15882072Svn83148
15892072Svn83148 if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
15904358Srb144127 &D->svc_handle, svcname, &resp, &resplen)) != 0) {
15912072Svn83148 lhp->freep(H, reqmsglen);
15922072Svn83148 errno = rc;
15932072Svn83148 return (-1);
15942072Svn83148 }
15952072Svn83148
15962072Svn83148 lhp->freep(H, reqmsglen);
15972072Svn83148
15982072Svn83148 /*
15992072Svn83148 * resp should contain the req_num immediately followed by the PRI
16002072Svn83148 * (the latter may or may not be present). unfortunately, the
16012072Svn83148 * current compiler flags cause a warning for the following
16022072Svn83148 * definition
16032072Svn83148 *
16042072Svn83148 * typedef struct {
16052072Svn83148 * uint64_t req_num;
16062072Svn83148 * uint8_t pri[];
16072072Svn83148 * } fma_pri_resp_t;
16082072Svn83148 *
16092072Svn83148 * so we do not use the struct here.
16102072Svn83148 */
16112072Svn83148 if (resplen <= sizeof (uint64_t)) {
16122072Svn83148 lhp->freep(resp, resplen);
16132072Svn83148 if (resplen == sizeof (uint64_t))
16142072Svn83148 return (0);
16152072Svn83148 else
16162072Svn83148 return (-1);
16172072Svn83148 }
16182072Svn83148
16192072Svn83148 buflen = resplen - sizeof (uint64_t);
16202072Svn83148 *buf = lhp->allocp(buflen);
16212072Svn83148
16222072Svn83148 bcopy((void *)((ptrdiff_t)resp + sizeof (uint64_t)), *buf, buflen);
16232072Svn83148 lhp->freep(resp, resplen);
16242072Svn83148
16252072Svn83148 return (buflen);
16262072Svn83148 }
16272072Svn83148
16282072Svn83148
16292072Svn83148 /*
16302072Svn83148 * see cpu_request() for a description of return values
16312072Svn83148 */
16322072Svn83148 int
ldmsvcs_cpu_req_status(struct ldom_hdl * lhp,uint32_t cpuid)16332072Svn83148 ldmsvcs_cpu_req_status(struct ldom_hdl *lhp, uint32_t cpuid)
16342072Svn83148 {
16352072Svn83148 return (cpu_request(lhp, FMA_CPU_REQ_STATUS, cpuid));
16362072Svn83148 }
16372072Svn83148
16382072Svn83148
16392072Svn83148 int
ldmsvcs_cpu_req_offline(struct ldom_hdl * lhp,uint32_t cpuid)16402072Svn83148 ldmsvcs_cpu_req_offline(struct ldom_hdl *lhp, uint32_t cpuid)
16412072Svn83148 {
16422072Svn83148 return (cpu_request(lhp, FMA_CPU_REQ_OFFLINE, cpuid));
16432072Svn83148 }
16442072Svn83148
16456111Scy152378 int
ldmsvcs_cpu_req_online(struct ldom_hdl * lhp,uint32_t cpuid)16466111Scy152378 ldmsvcs_cpu_req_online(struct ldom_hdl *lhp, uint32_t cpuid)
16476111Scy152378 {
16486111Scy152378 return (cpu_request(lhp, FMA_CPU_REQ_ONLINE, cpuid));
16496111Scy152378 }
16502072Svn83148
16512072Svn83148 /*
16522072Svn83148 * see mem_request() for a description of return values
16532072Svn83148 */
16542072Svn83148 int
ldmsvcs_mem_req_status(struct ldom_hdl * lhp,uint64_t pa)16552072Svn83148 ldmsvcs_mem_req_status(struct ldom_hdl *lhp, uint64_t pa)
16562072Svn83148 {
16572072Svn83148 return (mem_request(lhp, FMA_MEM_REQ_STATUS, pa, getpagesize()));
16582072Svn83148 }
16592072Svn83148
16602072Svn83148 int
ldmsvcs_mem_req_retire(struct ldom_hdl * lhp,uint64_t pa)16612072Svn83148 ldmsvcs_mem_req_retire(struct ldom_hdl *lhp, uint64_t pa)
16622072Svn83148 {
16632072Svn83148 return (mem_request(lhp, FMA_MEM_REQ_RETIRE, pa, getpagesize()));
16642072Svn83148 }
16652072Svn83148
16666111Scy152378 int
ldmsvcs_mem_req_unretire(struct ldom_hdl * lhp,uint64_t pa)16676111Scy152378 ldmsvcs_mem_req_unretire(struct ldom_hdl *lhp, uint64_t pa)
16686111Scy152378 {
16696111Scy152378 return (mem_request(lhp, FMA_MEM_REQ_RESURRECT, pa, getpagesize()));
16706111Scy152378 }
16716111Scy152378
16727850SVuong.Nguyen@Sun.COM int
ldmsvcs_io_req_id(struct ldom_hdl * lhp,uint64_t addr,uint_t type,uint64_t * virt_addr,char * name,int name_len,uint64_t * did)16737850SVuong.Nguyen@Sun.COM ldmsvcs_io_req_id(struct ldom_hdl *lhp, uint64_t addr, uint_t type,
16747850SVuong.Nguyen@Sun.COM uint64_t *virt_addr, char *name, int name_len, uint64_t *did)
16757850SVuong.Nguyen@Sun.COM {
16767850SVuong.Nguyen@Sun.COM
16777850SVuong.Nguyen@Sun.COM ds_hdr_t *H;
16787850SVuong.Nguyen@Sun.COM ds_data_handle_t *D;
16797850SVuong.Nguyen@Sun.COM fma_io_req_t *R;
16807850SVuong.Nguyen@Sun.COM
16817850SVuong.Nguyen@Sun.COM char *svcname = LDM_DS_NAME_IOD;
16827850SVuong.Nguyen@Sun.COM void *resp;
16837850SVuong.Nguyen@Sun.COM fma_io_resp_t *iop;
16847850SVuong.Nguyen@Sun.COM size_t resplen, reqmsglen;
16857850SVuong.Nguyen@Sun.COM int offset;
16867850SVuong.Nguyen@Sun.COM int rc;
16877850SVuong.Nguyen@Sun.COM
16887850SVuong.Nguyen@Sun.COM if (lhp->lsinfo == NULL)
16897850SVuong.Nguyen@Sun.COM return (-1);
16907850SVuong.Nguyen@Sun.COM
16917850SVuong.Nguyen@Sun.COM reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
16927850SVuong.Nguyen@Sun.COM sizeof (fma_io_req_t);
16937850SVuong.Nguyen@Sun.COM
16947850SVuong.Nguyen@Sun.COM H = lhp->allocp(reqmsglen);
16957850SVuong.Nguyen@Sun.COM D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
16967850SVuong.Nguyen@Sun.COM R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
16977850SVuong.Nguyen@Sun.COM
16987850SVuong.Nguyen@Sun.COM H->msg_type = DS_DATA;
16997850SVuong.Nguyen@Sun.COM H->payload_len = sizeof (ds_data_handle_t) + sizeof (fma_io_req_t);
17007850SVuong.Nguyen@Sun.COM
17017850SVuong.Nguyen@Sun.COM R->req_num = fds_svc_req_num();
17027850SVuong.Nguyen@Sun.COM R->msg_type = type;
17037850SVuong.Nguyen@Sun.COM R->rsrc_address = addr;
17047850SVuong.Nguyen@Sun.COM
17057850SVuong.Nguyen@Sun.COM rc = ENOMSG;
17067850SVuong.Nguyen@Sun.COM if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
17077850SVuong.Nguyen@Sun.COM &D->svc_handle, svcname, &resp, &resplen)) != 0) {
17087850SVuong.Nguyen@Sun.COM lhp->freep(H, reqmsglen);
17097850SVuong.Nguyen@Sun.COM return (rc);
17107850SVuong.Nguyen@Sun.COM }
17117850SVuong.Nguyen@Sun.COM lhp->freep(H, reqmsglen);
17127850SVuong.Nguyen@Sun.COM
17137850SVuong.Nguyen@Sun.COM /*
17147850SVuong.Nguyen@Sun.COM * resp should contain the req_num, status, virtual addr, domain id
17157850SVuong.Nguyen@Sun.COM * and the domain name. The domain name may or may not be present.
17167850SVuong.Nguyen@Sun.COM */
17177850SVuong.Nguyen@Sun.COM offset = sizeof (fma_io_resp_t);
17187850SVuong.Nguyen@Sun.COM if (resplen < offset) {
17197850SVuong.Nguyen@Sun.COM lhp->freep(resp, resplen);
17207850SVuong.Nguyen@Sun.COM return (-1);
17217850SVuong.Nguyen@Sun.COM }
17227850SVuong.Nguyen@Sun.COM
17237850SVuong.Nguyen@Sun.COM iop = (fma_io_resp_t *)resp;
17247850SVuong.Nguyen@Sun.COM switch (iop->result) {
17257850SVuong.Nguyen@Sun.COM case FMA_IO_RESP_OK:
17267850SVuong.Nguyen@Sun.COM /* success */
17277850SVuong.Nguyen@Sun.COM rc = 0;
17287850SVuong.Nguyen@Sun.COM *virt_addr = iop->virt_rsrc_address;
17297850SVuong.Nguyen@Sun.COM *did = iop->domain_id;
17307850SVuong.Nguyen@Sun.COM if (name == NULL || name_len <= 0)
17317850SVuong.Nguyen@Sun.COM break;
17327850SVuong.Nguyen@Sun.COM *name = '\0';
17337850SVuong.Nguyen@Sun.COM if (resplen > offset) {
17347850SVuong.Nguyen@Sun.COM (void) strncpy(name, (char *)((ptrdiff_t)resp + offset),
17357850SVuong.Nguyen@Sun.COM name_len);
17367850SVuong.Nguyen@Sun.COM }
17377850SVuong.Nguyen@Sun.COM break;
17387850SVuong.Nguyen@Sun.COM default:
17397850SVuong.Nguyen@Sun.COM rc = -1;
17407850SVuong.Nguyen@Sun.COM break;
17417850SVuong.Nguyen@Sun.COM }
17427850SVuong.Nguyen@Sun.COM
17437850SVuong.Nguyen@Sun.COM lhp->freep(resp, resplen);
17447850SVuong.Nguyen@Sun.COM return (rc);
17457850SVuong.Nguyen@Sun.COM }
17467850SVuong.Nguyen@Sun.COM
17472072Svn83148 /* end file */
1748