xref: /onnv-gate/usr/src/lib/fm/libldom/sparc/ldmsvcs_utils.c (revision 7850:168d612bb1e8)
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 /*
226111Scy152378  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
232072Svn83148  * Use is subject to license terms.
242072Svn83148  */
252072Svn83148 
262072Svn83148 #include <stdlib.h>
272072Svn83148 #include <stdio.h>
28*7850SVuong.Nguyen@Sun.COM 
292072Svn83148 #include <strings.h>
302072Svn83148 #include <sys/types.h>
312072Svn83148 #include <sys/stat.h>
322072Svn83148 #include <time.h>
332072Svn83148 #include <fcntl.h>
342072Svn83148 #include <unistd.h>
352072Svn83148 #include <errno.h>
362072Svn83148 #include <assert.h>
372072Svn83148 #include <umem.h>
382072Svn83148 #include <alloca.h>
392072Svn83148 #include <sys/processor.h>
402072Svn83148 #include <poll.h>
412072Svn83148 #include <pthread.h>
424358Srb144127 #include <values.h>
434358Srb144127 #include <libscf.h>
442072Svn83148 
45*7850SVuong.Nguyen@Sun.COM #include <ctype.h>
46*7850SVuong.Nguyen@Sun.COM 
472072Svn83148 #include "ldmsvcs_utils.h"
482072Svn83148 
492072Svn83148 #define	ASSERT(cnd) \
502072Svn83148 	((void) ((cnd) || ((void) fprintf(stderr, \
512072Svn83148 		"assertion failure in %s:%d: %s\n", \
522072Svn83148 		__FILE__, __LINE__, #cnd), 0)))
532072Svn83148 
542072Svn83148 #define	FDS_VLDC \
552072Svn83148 	"/devices/virtual-devices@100/channel-devices@200/" \
562072Svn83148 	"/virtual-channel-client@1:ldmfma"
572072Svn83148 
584358Srb144127 /* allow timeouts in sec that are nearly forever but small enough for an int */
594358Srb144127 #define	LDM_TIMEOUT_CEILING	(MAXINT / 2)
602072Svn83148 
612072Svn83148 #define	MIN(x, y)	((x) < (y) ? (x) : (y))
622072Svn83148 
632072Svn83148 /*
642072Svn83148  * functions in this file are for version 1.0 of FMA domain services
652072Svn83148  */
662072Svn83148 static ds_ver_t ds_vers[] = {
672072Svn83148 	{ 1, 0 }
682072Svn83148 };
692072Svn83148 
702072Svn83148 #define	DS_NUM_VER	(sizeof (ds_vers) / sizeof (ds_ver_t))
712072Svn83148 
722072Svn83148 /*
732072Svn83148  * information for each channel
742072Svn83148  */
752072Svn83148 struct ldmsvcs_info {
762072Svn83148 	pthread_mutex_t mt;
772072Svn83148 	pthread_cond_t cv;
782072Svn83148 	fds_channel_t fds_chan;
792072Svn83148 	fds_reg_svcs_t fmas_svcs;
802072Svn83148 	int cv_twait;
812072Svn83148 };
822072Svn83148 
832072Svn83148 /*
842072Svn83148  * struct listdata_s and struct poller_s are used to maintain the state of
852072Svn83148  * the poller thread.  this thread is used to manage incoming messages and
862072Svn83148  * pass those messages onto the correct requesting thread.  see the "poller
872072Svn83148  * functions" section for more details.
882072Svn83148  */
892072Svn83148 struct listdata_s {
902072Svn83148 	enum {
912072Svn83148 		UNUSED,
922072Svn83148 		PENDING,
932072Svn83148 		ARRIVED
942072Svn83148 	} status;
952072Svn83148 	uint64_t req_num;
962072Svn83148 	int fd;
972072Svn83148 	size_t datalen;
982072Svn83148 };
992072Svn83148 
1002072Svn83148 static struct poller_s {
1012072Svn83148 	pthread_mutex_t mt;
1022072Svn83148 	pthread_cond_t cv;
1032072Svn83148 	pthread_t polling_tid;
1042072Svn83148 	int doreset;
1052072Svn83148 	int doexit;
1062072Svn83148 	int nclients;
1072072Svn83148 	struct listdata_s **list;
1082072Svn83148 	int list_len;
1092072Svn83148 	int pending_count;
1102072Svn83148 } pollbase = {
1112072Svn83148 	PTHREAD_MUTEX_INITIALIZER,
1122072Svn83148 	PTHREAD_COND_INITIALIZER,
1132072Svn83148 	0,
1142072Svn83148 	1,
1152072Svn83148 	0,
1162072Svn83148 	0,
1172072Svn83148 	NULL,
1182072Svn83148 	0,
1192072Svn83148 	0
1202072Svn83148 };
1212072Svn83148 
1222072Svn83148 
1232072Svn83148 static struct ldmsvcs_info *channel_init(struct ldom_hdl *lhp);
1242072Svn83148 static int channel_openreset(struct ldmsvcs_info *lsp);
1252072Svn83148 static int read_msg(struct ldmsvcs_info *lsp);
1262072Svn83148 
1274358Srb144127 static int
1284358Srb144127 get_smf_int_val(char *prop_nm, int min, int max, int default_val)
1294358Srb144127 {
1304358Srb144127 	scf_simple_prop_t	*prop;		/* SMF property */
1314358Srb144127 	int64_t			*valp;		/* prop value ptr */
1324358Srb144127 	int64_t			val;		/* prop value to return */
1334358Srb144127 
1344358Srb144127 	val = default_val;
1354358Srb144127 	if ((prop = scf_simple_prop_get(NULL, LDM_SVC_NM, LDM_PROP_GROUP_NM,
1364358Srb144127 	    prop_nm)) != NULL) {
1374358Srb144127 		if ((valp = scf_simple_prop_next_integer(prop)) != NULL) {
1384358Srb144127 			val = *valp;
1394358Srb144127 			if (val < min)
1404358Srb144127 				val = min;
1414358Srb144127 			else if (val > max)
1424358Srb144127 				val = max;
1434358Srb144127 		}
1444358Srb144127 		scf_simple_prop_free(prop);
1454358Srb144127 	}
1464358Srb144127 	return ((int)val);
1474358Srb144127 }
1484358Srb144127 
1492072Svn83148 static void
1502072Svn83148 channel_close(struct ldmsvcs_info *lsp)
1512072Svn83148 {
1522072Svn83148 	(void) pthread_mutex_lock(&lsp->mt);
1532072Svn83148 
1543327Svn83148 	if (lsp->fds_chan.state == CHANNEL_OPEN ||
1554358Srb144127 	    lsp->fds_chan.state == CHANNEL_READY) {
1563327Svn83148 		(void) close(lsp->fds_chan.fd);
1574358Srb144127 		lsp->cv_twait = get_smf_int_val(LDM_INIT_TO_PROP_NM,
1584358Srb144127 		    0, LDM_TIMEOUT_CEILING, LDM_INIT_WAIT_TIME);
1593327Svn83148 		lsp->fds_chan.state = CHANNEL_CLOSED;
1603327Svn83148 	}
1612072Svn83148 
1622072Svn83148 	(void) pthread_mutex_unlock(&lsp->mt);
1632072Svn83148 }
1642072Svn83148 
1652072Svn83148 /*
1662072Svn83148  * read size bytes of data from a streaming fd into buf
1672072Svn83148  */
1682072Svn83148 static int
1692072Svn83148 read_stream(int fd, void *buf, size_t size)
1702072Svn83148 {
1712072Svn83148 	pollfd_t pollfd;
1722072Svn83148 	ssize_t rv;
1732072Svn83148 	size_t data_left;
1742072Svn83148 	ptrdiff_t currentp;
1752072Svn83148 
1762072Svn83148 	pollfd.events = POLLIN;
1772072Svn83148 	pollfd.revents = 0;
1782072Svn83148 	pollfd.fd = fd;
1792072Svn83148 
1802072Svn83148 	currentp = (ptrdiff_t)buf;
1812072Svn83148 	data_left = size;
1822072Svn83148 
1832072Svn83148 	/*
1842072Svn83148 	 * data may come in bits and pieces
1852072Svn83148 	 */
1862072Svn83148 	do {
1872072Svn83148 		if ((rv = read(fd, (void *)currentp, data_left)) < 0) {
1882072Svn83148 			if (errno == EAGAIN && poll(&pollfd, 1, -1) > 0)
1892072Svn83148 				continue;	/* retry */
1902072Svn83148 			else
1912072Svn83148 				return (1);
1922072Svn83148 		}
1932072Svn83148 
1942072Svn83148 		data_left -= rv;
1952072Svn83148 		currentp += rv;
1962072Svn83148 	} while (data_left > 0);
1972072Svn83148 
1982072Svn83148 	return (0);
1992072Svn83148 }
2002072Svn83148 
2012072Svn83148 
2022072Svn83148 /*
2032072Svn83148  * poller functions
2042072Svn83148  *
2052072Svn83148  * at init time, a thread is created for the purpose of monitoring incoming
2062072Svn83148  * messages and doing one of the following:
2072072Svn83148  *
2082072Svn83148  * 1. doing the initial handshake and version negotiation
2092072Svn83148  *
2102072Svn83148  * 2. handing incoming data off to the requesting thread (which is an fmd
2112072Svn83148  * module or scheme thread)
2122072Svn83148  */
2132072Svn83148 static int
2142072Svn83148 poller_handle_data(int fd, size_t payloadsize)
2152072Svn83148 {
2162072Svn83148 	uint64_t *req_num;
2172072Svn83148 	void *pr;
2182072Svn83148 	size_t prlen;
2192072Svn83148 	int i;
2202072Svn83148 
2212072Svn83148 	prlen = sizeof (ds_data_handle_t) + sizeof (uint64_t);
2222072Svn83148 
2232072Svn83148 	if (payloadsize < prlen)
2242072Svn83148 		return (1);
2252072Svn83148 
2262072Svn83148 	pr = alloca(prlen);
2272072Svn83148 
2282072Svn83148 	if (read_stream(fd, pr, prlen) != 0)
2292072Svn83148 		return (1);
2302072Svn83148 
2312072Svn83148 	req_num = (uint64_t *)((ptrdiff_t)pr + sizeof (ds_data_handle_t));
2322072Svn83148 
2332072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
2342072Svn83148 
2352072Svn83148 	for (i = 0; i < pollbase.list_len; i++) {
2362072Svn83148 		if (pollbase.list[i]->req_num == *req_num) {
2372072Svn83148 			ASSERT(pollbase.list[i]->status == PENDING);
2382072Svn83148 
2392072Svn83148 			pollbase.list[i]->status = ARRIVED;
2402072Svn83148 			pollbase.list[i]->fd = fd;
2412072Svn83148 			pollbase.list[i]->datalen = payloadsize - prlen;
2422072Svn83148 
2432072Svn83148 			pollbase.pending_count--;
2442072Svn83148 			(void) pthread_cond_broadcast(&pollbase.cv);
2452072Svn83148 			break;
2462072Svn83148 		}
2472072Svn83148 	}
2482072Svn83148 
2492072Svn83148 	/*
2502072Svn83148 	 * now wait for receiving thread to read in the data
2512072Svn83148 	 */
2522072Svn83148 	if (i < pollbase.list_len) {
2532072Svn83148 		while (pollbase.list[i]->status == ARRIVED)
2542072Svn83148 			(void) pthread_cond_wait(&pollbase.cv, &pollbase.mt);
2552072Svn83148 	}
2562072Svn83148 
2572072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
2582072Svn83148 
2592072Svn83148 	return (0);
2602072Svn83148 }
2612072Svn83148 
2622072Svn83148 
2632072Svn83148 /*
2642072Svn83148  * note that this function is meant to handle only DS_DATA messages
2652072Svn83148  */
2662072Svn83148 static int
2672072Svn83148 poller_recv_data(struct ldom_hdl *lhp, uint64_t req_num, int index,
2682072Svn83148 		void **resp, size_t *resplen)
2692072Svn83148 {
2702072Svn83148 	struct timespec twait;
2712072Svn83148 	int ier;
2722072Svn83148 
2732072Svn83148 	ier = 0;
2742072Svn83148 	twait.tv_sec = time(NULL) + lhp->lsinfo->cv_twait;
2752072Svn83148 	twait.tv_nsec = 0;
2762072Svn83148 
2772072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
2782072Svn83148 
2792072Svn83148 	ASSERT(pollbase.list[index]->req_num == req_num);
2802072Svn83148 
2812072Svn83148 	while (pollbase.list[index]->status == PENDING &&
2822072Svn83148 	    pollbase.doreset == 0 && ier == 0)
2832072Svn83148 		ier = pthread_cond_timedwait(&pollbase.cv, &pollbase.mt,
2844358Srb144127 		    &twait);
2852072Svn83148 
2862072Svn83148 	if (ier == 0) {
2872072Svn83148 		if (pollbase.doreset == 0) {
2882072Svn83148 			ASSERT(pollbase.list[index]->status == ARRIVED);
2892072Svn83148 
2902072Svn83148 			/*
2912072Svn83148 			 * need to add req_num to beginning of resp
2922072Svn83148 			 */
2932072Svn83148 			*resplen = pollbase.list[index]->datalen +
2944358Srb144127 			    sizeof (uint64_t);
2952072Svn83148 			*resp = lhp->allocp(*resplen);
2962072Svn83148 			*((uint64_t *)*resp) = req_num;
2972072Svn83148 
2982072Svn83148 			if (read_stream(pollbase.list[index]->fd,
2994358Srb144127 			    (void *)((ptrdiff_t)*resp + sizeof (uint64_t)),
3004358Srb144127 			    *resplen - sizeof (uint64_t)) != 0)
3012072Svn83148 				ier = ETIMEDOUT;
3022072Svn83148 
3032072Svn83148 			pollbase.list[index]->status = UNUSED;
3042072Svn83148 			pollbase.list[index]->req_num = 0;
3052072Svn83148 			(void) pthread_cond_broadcast(&pollbase.cv);
3062072Svn83148 		} else {
3072072Svn83148 			if (--(pollbase.pending_count) == 0)
3082072Svn83148 				(void) pthread_cond_broadcast(&pollbase.cv);
3092072Svn83148 		}
3102072Svn83148 	}
3112072Svn83148 
3122072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
3132072Svn83148 
3142072Svn83148 	ASSERT(ier == 0 || ier == ETIMEDOUT);
3152072Svn83148 
3162072Svn83148 	return (ier);
3172072Svn83148 }
3182072Svn83148 
3192072Svn83148 
3202072Svn83148 static void
3212072Svn83148 poller_add_client(void)
3222072Svn83148 {
3232072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
3242072Svn83148 	pollbase.nclients++;
3252072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
3262072Svn83148 }
3272072Svn83148 
3282072Svn83148 
3292072Svn83148 static void
3302072Svn83148 poller_remove_client(void)
3312072Svn83148 {
3322072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
3332072Svn83148 	pollbase.nclients--;
3342072Svn83148 	ASSERT(pollbase.nclients >= 0);
3352072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
3362072Svn83148 }
3372072Svn83148 
3382072Svn83148 
3392072Svn83148 static int
3402072Svn83148 poller_add_pending(struct ldom_hdl *lhp, uint64_t req_num)
3412072Svn83148 {
3422072Svn83148 	int newlen, index, i, j;
3432072Svn83148 
3442072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
3452072Svn83148 	pollbase.pending_count++;
3462072Svn83148 
3472072Svn83148 	for (j = 0, index = -1; j < 2 && index == -1; j++) {
3482072Svn83148 		for (i = 0; i < pollbase.list_len; i++) {
3492072Svn83148 			if (pollbase.list[i]->status == UNUSED) {
3502072Svn83148 				pollbase.list[i]->status = PENDING;
3512072Svn83148 				pollbase.list[i]->req_num = req_num;
3522072Svn83148 				pollbase.list[i]->datalen = 0;
3532072Svn83148 				index = i;
3542072Svn83148 				break;
3552072Svn83148 			}
3562072Svn83148 		}
3572072Svn83148 
3582072Svn83148 		if (index == -1) {
3592072Svn83148 			struct listdata_s **newlist, **oldlist;
3602072Svn83148 
3612072Svn83148 			/*
3622072Svn83148 			 * get to this point if list is not long enough.
3632072Svn83148 			 * check for a runaway list.  since requests are
3642072Svn83148 			 * synchronous (clients send a request and need to
3652072Svn83148 			 * wait for the result before returning) the size
3662072Svn83148 			 * of the list cannot be much more than the number
3672072Svn83148 			 * of clients.
3682072Svn83148 			 */
3692072Svn83148 			ASSERT(pollbase.list_len < pollbase.nclients + 1);
3702072Svn83148 
3712072Svn83148 			newlen = pollbase.list_len + 5;
3722072Svn83148 			newlist = lhp->allocp(newlen *
3734358Srb144127 			    sizeof (struct listdata_s));
3742072Svn83148 
3752072Svn83148 			for (i = 0; i < pollbase.list_len; i++)
3762072Svn83148 				newlist[i] = pollbase.list[i];
3772072Svn83148 
3782072Svn83148 			oldlist = pollbase.list;
3792072Svn83148 			pollbase.list = newlist;
3802072Svn83148 			lhp->freep(oldlist, pollbase.list_len *
3814358Srb144127 			    sizeof (struct listdata_s));
3822072Svn83148 
3832072Svn83148 			for (i = pollbase.list_len; i < newlen; i++) {
3842072Svn83148 				pollbase.list[i] =
3852072Svn83148 				    lhp->allocp(sizeof (struct listdata_s));
3862072Svn83148 				pollbase.list[i]->status = UNUSED;
3872072Svn83148 			}
3882072Svn83148 
3892072Svn83148 			pollbase.list_len = newlen;
3902072Svn83148 		}
3912072Svn83148 	}
3922072Svn83148 
3932072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
3942072Svn83148 	ASSERT(index != -1);
3952072Svn83148 
3962072Svn83148 	return (index);
3972072Svn83148 }
3982072Svn83148 
3992072Svn83148 
4002072Svn83148 static void
4012072Svn83148 poller_delete_pending(uint64_t req_num, int index)
4022072Svn83148 {
4032072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
4042072Svn83148 
4052072Svn83148 	ASSERT(pollbase.list[index]->req_num == req_num);
4062072Svn83148 	pollbase.list[index]->status = UNUSED;
4072072Svn83148 
4082072Svn83148 	if (--(pollbase.pending_count) == 0 && pollbase.doreset == 1)
4092072Svn83148 		(void) pthread_cond_broadcast(&pollbase.cv);
4102072Svn83148 
4112072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
4122072Svn83148 }
4132072Svn83148 
4142072Svn83148 
4152072Svn83148 static void
4162072Svn83148 poller_shutdown(void)
4172072Svn83148 {
4182072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
4192072Svn83148 
4202072Svn83148 	pollbase.doexit = 1;
4212072Svn83148 
4222072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
4232072Svn83148 }
4242072Svn83148 
4252072Svn83148 
4262072Svn83148 /*
4272072Svn83148  * perform the polling of incoming messages.  manage any resets (usually
4282072Svn83148  * due to one end of the connection being closed) as well as exit
4292072Svn83148  * conditions.
4302072Svn83148  */
4312072Svn83148 static void *
4322072Svn83148 poller_loop(void *arg)
4332072Svn83148 {
4342072Svn83148 	struct ldmsvcs_info *lsp;
4352072Svn83148 	pollfd_t pollfd;
4362072Svn83148 	int ier;
4372072Svn83148 
4382072Svn83148 	lsp = (struct ldmsvcs_info *)arg;
4392072Svn83148 
4402072Svn83148 	for (;;) {
4412072Svn83148 		(void) pthread_mutex_lock(&pollbase.mt);
4422072Svn83148 
4432072Svn83148 		if (pollbase.doexit) {
4442072Svn83148 			(void) pthread_mutex_unlock(&pollbase.mt);
4452072Svn83148 			break;
4462072Svn83148 		}
4472072Svn83148 
4482072Svn83148 		if (pollbase.doreset) {
4492072Svn83148 			int i;
4502072Svn83148 
4512072Svn83148 			while (pollbase.pending_count > 0)
4522072Svn83148 				(void) pthread_cond_wait(&pollbase.cv,
4534358Srb144127 				    &pollbase.mt);
4542072Svn83148 
4552072Svn83148 			ASSERT(pollbase.pending_count == 0);
4562072Svn83148 			for (i = 0; i < pollbase.list_len; i++)
4572072Svn83148 				pollbase.list[i]->status = UNUSED;
4582072Svn83148 
4592072Svn83148 			pollbase.doreset = 0;
4602072Svn83148 		}
4612072Svn83148 		(void) pthread_mutex_unlock(&pollbase.mt);
4622072Svn83148 
4632072Svn83148 		if ((ier = channel_openreset(lsp)) == 1) {
4642072Svn83148 			continue;
4652072Svn83148 		} else if (ier == 2) {
4662072Svn83148 			/*
4672072Svn83148 			 * start exit preparations
4682072Svn83148 			 */
4692072Svn83148 			poller_shutdown();
4702072Svn83148 			continue;
4712072Svn83148 		}
4722072Svn83148 
4732072Svn83148 		pollfd.events = POLLIN;
4742072Svn83148 		pollfd.revents = 0;
4752072Svn83148 		pollfd.fd = lsp->fds_chan.fd;
4762072Svn83148 
4772072Svn83148 		if (poll(&pollfd, 1, -1) <= 0 || read_msg(lsp) != 0) {
4782072Svn83148 			/*
4792072Svn83148 			 * read error and/or fd got closed
4802072Svn83148 			 */
4812072Svn83148 			(void) pthread_mutex_lock(&pollbase.mt);
4822072Svn83148 			pollbase.doreset = 1;
4832072Svn83148 			(void) pthread_mutex_unlock(&pollbase.mt);
4842072Svn83148 
4852072Svn83148 			channel_close(lsp);
4862072Svn83148 		}
4872072Svn83148 	}
4882072Svn83148 
4892072Svn83148 	return (NULL);
4902072Svn83148 }
4912072Svn83148 
4922072Svn83148 
4932072Svn83148 /*
4942072Svn83148  * create the polling thread
4952072Svn83148  */
4962072Svn83148 static int
4972072Svn83148 poller_init(struct ldmsvcs_info *lsp)
4982072Svn83148 {
4992072Svn83148 	int rc = 0;
5002072Svn83148 
5012072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
5022072Svn83148 
5032072Svn83148 	if (pollbase.polling_tid == 0) {
5042072Svn83148 		pthread_attr_t attr;
5052072Svn83148 
5062072Svn83148 		/*
5072072Svn83148 		 * create polling thread for receiving messages
5082072Svn83148 		 */
5092072Svn83148 		(void) pthread_attr_init(&attr);
5102072Svn83148 		(void) pthread_attr_setdetachstate(&attr,
5114358Srb144127 		    PTHREAD_CREATE_DETACHED);
5122072Svn83148 
5132072Svn83148 		if (pthread_create(&pollbase.polling_tid, &attr,
5144358Srb144127 		    poller_loop, lsp) != 0)
5152072Svn83148 			rc = 1;
5162072Svn83148 
5172072Svn83148 		(void) pthread_attr_destroy(&attr);
5182072Svn83148 	}
5192072Svn83148 
5202072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
5212072Svn83148 
5222072Svn83148 	return (rc);
5232072Svn83148 }
5242072Svn83148 
5252072Svn83148 
5262072Svn83148 /*
5272072Svn83148  * utilities for message handlers
5282072Svn83148  */
5292072Svn83148 static int
5302072Svn83148 fds_send(struct ldmsvcs_info *lsp, void *msg, size_t msglen)
5312072Svn83148 {
5322072Svn83148 	static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER;
5332072Svn83148 
5342072Svn83148 	(void) pthread_mutex_lock(&mt);
5352072Svn83148 
5362072Svn83148 	if (write(lsp->fds_chan.fd, msg, msglen) != msglen) {
5372072Svn83148 		channel_close(lsp);
5382072Svn83148 		(void) pthread_mutex_unlock(&mt);
5392072Svn83148 		return (ETIMEDOUT);
5402072Svn83148 	}
5412072Svn83148 
5422072Svn83148 	(void) pthread_mutex_unlock(&mt);
5432072Svn83148 	return (0);
5442072Svn83148 }
5452072Svn83148 
5462072Svn83148 
5472072Svn83148 /*
5482072Svn83148  * Find the max and min version supported
5492072Svn83148  */
5502072Svn83148 static void
5512072Svn83148 fds_min_max_versions(uint16_t *min_major, uint16_t *max_major)
5522072Svn83148 {
5532072Svn83148 	int i;
5542072Svn83148 
5552072Svn83148 	*min_major = ds_vers[0].major;
5562072Svn83148 	*max_major = *min_major;
5572072Svn83148 
5582072Svn83148 	for (i = 1; i < DS_NUM_VER; i++) {
5592072Svn83148 		if (ds_vers[i].major < *min_major)
5602072Svn83148 			*min_major = ds_vers[i].major;
5612072Svn83148 
5622072Svn83148 		if (ds_vers[i].major > *max_major)
5632072Svn83148 			*max_major = ds_vers[i].major;
5642072Svn83148 	}
5652072Svn83148 }
5662072Svn83148 
5672072Svn83148 /*
5682072Svn83148  * check whether the major and minor numbers requested by remote ds client
5692072Svn83148  * can be satisfied.  if the requested major is supported, true is
5702072Svn83148  * returned, and the agreed minor is returned in new_minor.  if the
5712072Svn83148  * requested major is not supported, the routine returns false, and the
5722072Svn83148  * closest major is returned in *new_major, upon which the ds client should
5732072Svn83148  * renegotiate.  the closest major is the just lower that the requested
5742072Svn83148  * major number.
5752072Svn83148  */
5762072Svn83148 static boolean_t
5772072Svn83148 fds_negotiate_version(uint16_t req_major, uint16_t *new_majorp,
5782072Svn83148     uint16_t *new_minorp)
5792072Svn83148 {
5802072Svn83148 	int i = 0;
5812072Svn83148 	uint16_t major, lower_major;
5822072Svn83148 	uint16_t min_major, max_major;
5832072Svn83148 	boolean_t found_match = B_FALSE;
5842072Svn83148 
5852072Svn83148 	fds_min_max_versions(&min_major, &max_major);
5862072Svn83148 
5872072Svn83148 	/*
5882072Svn83148 	 * if the minimum version supported is greater than the version
5892072Svn83148 	 * requested, return the lowest version supported
5902072Svn83148 	 */
5912072Svn83148 	if (min_major > req_major) {
5922072Svn83148 		*new_majorp = min_major;
5932072Svn83148 		return (B_FALSE);
5942072Svn83148 	}
5952072Svn83148 
5962072Svn83148 	/*
5972072Svn83148 	 * if the largest version supported is lower than the version
5982072Svn83148 	 * requested, return the largest version supported
5992072Svn83148 	 */
6002072Svn83148 	if (max_major < req_major) {
6012072Svn83148 		*new_majorp = max_major;
6022072Svn83148 		return (B_FALSE);
6032072Svn83148 	}
6042072Svn83148 
6052072Svn83148 	/*
6062072Svn83148 	 * now we know that the requested version lies between the min and
6072072Svn83148 	 * max versions supported.  check if the requested major can be
6082072Svn83148 	 * found in supported versions.
6092072Svn83148 	 */
6102072Svn83148 	lower_major = min_major;
6112072Svn83148 	for (i = 0; i < DS_NUM_VER; i++) {
6122072Svn83148 		major = ds_vers[i].major;
6132072Svn83148 		if (major == req_major) {
6142072Svn83148 			found_match = B_TRUE;
6152072Svn83148 			*new_minorp = ds_vers[i].minor;
6162072Svn83148 			*new_majorp = major;
6172072Svn83148 			break;
6182072Svn83148 		} else if ((major < req_major) && (major > lower_major))
6192072Svn83148 			lower_major = major;
6202072Svn83148 	}
6212072Svn83148 
6222072Svn83148 	/*
6232072Svn83148 	 * If  no match is found, return the closest available number
6242072Svn83148 	 */
6252072Svn83148 	if (!found_match)
6262072Svn83148 		*new_majorp = lower_major;
6272072Svn83148 
6282072Svn83148 	return (found_match);
6292072Svn83148 }
6302072Svn83148 
6312072Svn83148 
6322072Svn83148 /*
6332072Svn83148  * return 0 if service is added; 1 if service is a duplicate
6342072Svn83148  */
6352072Svn83148 static int
6362072Svn83148 fds_svc_add(struct ldmsvcs_info *lsp, ds_reg_req_t *req, int minor)
6372072Svn83148 {
6382072Svn83148 	fds_svc_t *svc;
6392072Svn83148 	int i, rc;
6402072Svn83148 
6412072Svn83148 	svc = NULL;
6422072Svn83148 	for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
6432072Svn83148 		if (strcmp(lsp->fmas_svcs.tbl[i]->name, req->svc_id) == 0) {
6442072Svn83148 			svc = lsp->fmas_svcs.tbl[i];
6452072Svn83148 			break;
6462072Svn83148 		}
6472072Svn83148 	}
6482072Svn83148 
6492072Svn83148 	if (svc == NULL)
6502072Svn83148 		return (0);	/* we don't need this service */
6512072Svn83148 
6522072Svn83148 	(void) pthread_mutex_lock(&lsp->fmas_svcs.mt);
6532072Svn83148 
6542072Svn83148 	/*
6552072Svn83148 	 * duplicate registration is OK --- we retain the previous entry
6562072Svn83148 	 * (which has not been unregistered anyway)
6572072Svn83148 	 */
6582072Svn83148 	if (svc->state == DS_SVC_ACTIVE) {
6592072Svn83148 		rc = 1;
6602072Svn83148 	} else {
6612072Svn83148 		svc->state = DS_SVC_ACTIVE;
6622072Svn83148 		svc->hdl = req->svc_handle;
6632072Svn83148 		svc->ver.major = req->major_vers;
6642072Svn83148 		svc->ver.minor = minor;
6652072Svn83148 
6662072Svn83148 		rc = 0;
6672072Svn83148 		(void) pthread_cond_broadcast(&lsp->fmas_svcs.cv);
6682072Svn83148 	}
6692072Svn83148 
6702072Svn83148 	(void) pthread_mutex_unlock(&lsp->fmas_svcs.mt);
6712072Svn83148 
6722072Svn83148 	return (rc);
6732072Svn83148 }
6742072Svn83148 
6752072Svn83148 
6762072Svn83148 static void
6772072Svn83148 fds_svc_reset(struct ldmsvcs_info *lsp, int index)
6782072Svn83148 {
6792072Svn83148 	int i, start, end;
6802072Svn83148 
6812072Svn83148 	if (index >= 0) {
6822072Svn83148 		start = index;
6832072Svn83148 		end = index + 1;
6842072Svn83148 	} else {
6852072Svn83148 		start = 0;
6862072Svn83148 		end = lsp->fmas_svcs.nsvcs;
6872072Svn83148 	}
6882072Svn83148 
6892072Svn83148 	(void) pthread_mutex_lock(&lsp->fmas_svcs.mt);
6902072Svn83148 
6912072Svn83148 	for (i = start; i < end; i++) {
6922072Svn83148 		lsp->fmas_svcs.tbl[i]->hdl = 0;
6932072Svn83148 		lsp->fmas_svcs.tbl[i]->state = DS_SVC_INVAL;
6942072Svn83148 		lsp->fmas_svcs.tbl[i]->ver.major =
6954358Srb144127 		    ds_vers[DS_NUM_VER - 1].major;
6962072Svn83148 		lsp->fmas_svcs.tbl[i]->ver.minor =
6974358Srb144127 		    ds_vers[DS_NUM_VER - 1].minor;
6982072Svn83148 	}
6992072Svn83148 
7002072Svn83148 	(void) pthread_mutex_unlock(&lsp->fmas_svcs.mt);
7012072Svn83148 }
7022072Svn83148 
7032072Svn83148 
7042072Svn83148 static int
7052072Svn83148 fds_svc_remove(struct ldmsvcs_info *lsp, ds_svc_hdl_t svc_handle)
7062072Svn83148 {
7072072Svn83148 	int i;
7082072Svn83148 
7092072Svn83148 	for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
7102072Svn83148 		if (lsp->fmas_svcs.tbl[i]->hdl == svc_handle) {
7112072Svn83148 			fds_svc_reset(lsp, i);
7122072Svn83148 			return (0);
7132072Svn83148 		}
7142072Svn83148 	}
7152072Svn83148 
7162072Svn83148 	return (1);
7172072Svn83148 }
7182072Svn83148 
7192072Svn83148 
7202072Svn83148 /*
7212072Svn83148  * message handlers
7222072Svn83148  */
7232072Svn83148 /*ARGSUSED*/
7242072Svn83148 static void
7252072Svn83148 ds_handle_msg_noop(struct ldmsvcs_info *lsp, void *buf, size_t len)
7262072Svn83148 {
7272072Svn83148 }
7282072Svn83148 
7292072Svn83148 static void
7302072Svn83148 ds_handle_init_req(struct ldmsvcs_info *lsp, void *buf, size_t len)
7312072Svn83148 {
7322072Svn83148 	ds_init_req_t *req;
7332072Svn83148 	uint16_t new_major, new_minor;
7342072Svn83148 	size_t msglen;
7352072Svn83148 
7362072Svn83148 	req = (ds_init_req_t *)buf;
7372072Svn83148 
7382072Svn83148 	/* sanity check the incoming message */
7392072Svn83148 	if (len != sizeof (ds_init_req_t)) {
7402072Svn83148 		channel_close(lsp);
7412072Svn83148 		return;
7422072Svn83148 	}
7432072Svn83148 
7442072Svn83148 	/*
7452072Svn83148 	 * Check version info. ACK only if the major numbers exactly
7462072Svn83148 	 * match. The service entity can retry with a new minor
7472072Svn83148 	 * based on the response sent as part of the NACK.
7482072Svn83148 	 */
7492072Svn83148 	if (fds_negotiate_version(req->major_vers, &new_major, &new_minor)) {
7502072Svn83148 		ds_hdr_t *H;
7512072Svn83148 		ds_init_ack_t *R;
7522072Svn83148 
7532072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_init_ack_t);
7542072Svn83148 		H = alloca(msglen);
7552072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
7562072Svn83148 
7572072Svn83148 		H->msg_type = DS_INIT_ACK;
7582072Svn83148 		H->payload_len = sizeof (ds_init_ack_t);
7592072Svn83148 		R->minor_vers = MIN(new_minor, req->minor_vers);
7602072Svn83148 
7612072Svn83148 		if (fds_send(lsp, H, msglen) != 0)
7622072Svn83148 			return;
7632072Svn83148 
7642072Svn83148 		(void) pthread_mutex_lock(&lsp->mt);
7652072Svn83148 		ASSERT(lsp->fds_chan.state == CHANNEL_OPEN);
7662072Svn83148 		lsp->fds_chan.state = CHANNEL_READY;
7673327Svn83148 
7683327Svn83148 		/*
7693327Svn83148 		 * Now the channel is ready after the handshake completes.
7703327Svn83148 		 * Reset the timeout to a smaller value for receiving messages
7713327Svn83148 		 * from the domain services.
7723327Svn83148 		 */
7734358Srb144127 		lsp->cv_twait = get_smf_int_val(LDM_RUNNING_TO_PROP_NM,
7744358Srb144127 		    0, LDM_TIMEOUT_CEILING, LDM_RUNNING_WAIT_TIME);
7753327Svn83148 
7762072Svn83148 		(void) pthread_mutex_unlock(&lsp->mt);
7772072Svn83148 	} else {
7782072Svn83148 		ds_hdr_t *H;
7792072Svn83148 		ds_init_nack_t *R;
7802072Svn83148 
7812072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_init_nack_t);
7822072Svn83148 		H = alloca(msglen);
7832072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
7842072Svn83148 
7852072Svn83148 		H->msg_type = DS_INIT_NACK;
7862072Svn83148 		H->payload_len = sizeof (ds_init_nack_t);
7872072Svn83148 		R->major_vers = new_major;
7882072Svn83148 
7892072Svn83148 		(void) fds_send(lsp, H, msglen);
7902072Svn83148 		/*
7912072Svn83148 		 * do not update state; remote end may attempt to initiate
7922072Svn83148 		 * connection with a different version
7932072Svn83148 		 */
7942072Svn83148 	}
7952072Svn83148 }
7962072Svn83148 
7972072Svn83148 
7982072Svn83148 /*ARGSUSED*/
7992072Svn83148 static void
8002072Svn83148 ds_handle_reg_req(struct ldmsvcs_info *lsp, void *buf, size_t len)
8012072Svn83148 {
8022072Svn83148 	ds_reg_req_t *req;
8032072Svn83148 	char *msg;
8042072Svn83148 	uint16_t new_major, new_minor;
8052072Svn83148 	size_t msglen;
8062072Svn83148 	int dup_svcreg = 0;
8072072Svn83148 
8082072Svn83148 	req = (ds_reg_req_t *)buf;
8092072Svn83148 	msg = (char *)req->svc_id;
8102072Svn83148 
8112072Svn83148 	/*
8122072Svn83148 	 * Service must be NULL terminated
8132072Svn83148 	 */
8142072Svn83148 	if (req->svc_id == NULL || strlen(req->svc_id) == 0 ||
8152072Svn83148 	    msg[strlen(req->svc_id)] != '\0') {
8162072Svn83148 		channel_close(lsp);
8172072Svn83148 		return;
8182072Svn83148 	}
8192072Svn83148 
8202072Svn83148 	if (fds_negotiate_version(req->major_vers, &new_major, &new_minor) &&
8212072Svn83148 	    (dup_svcreg = fds_svc_add(lsp, req,
8224358Srb144127 	    MIN(new_minor, req->minor_vers))) == 0) {
8232072Svn83148 
8242072Svn83148 		/*
8252072Svn83148 		 * Check version info. ACK only if the major numbers
8262072Svn83148 		 * exactly match. The service entity can retry with a new
8272072Svn83148 		 * minor based on the response sent as part of the NACK.
8282072Svn83148 		 */
8292072Svn83148 		ds_hdr_t *H;
8302072Svn83148 		ds_reg_ack_t *R;
8312072Svn83148 
8322072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_reg_ack_t);
8332072Svn83148 		H = alloca(msglen);
8342072Svn83148 		bzero(H, msglen);
8352072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
8362072Svn83148 
8372072Svn83148 		H->msg_type = DS_REG_ACK;
8382072Svn83148 		H->payload_len = sizeof (ds_reg_ack_t);
8392072Svn83148 		R->svc_handle = req->svc_handle;
8402072Svn83148 		R->minor_vers = MIN(new_minor, req->minor_vers);
8412072Svn83148 
8422072Svn83148 		(void) fds_send(lsp, H, msglen);
8432072Svn83148 	} else {
8442072Svn83148 		ds_hdr_t *H;
8452072Svn83148 		ds_reg_nack_t *R;
8462072Svn83148 
8472072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_reg_nack_t);
8482072Svn83148 		H = alloca(msglen);
8492072Svn83148 		bzero(H, msglen);
8502072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
8512072Svn83148 
8522072Svn83148 		H->msg_type = DS_REG_NACK;
8532072Svn83148 		H->payload_len = sizeof (ds_reg_nack_t);
8542072Svn83148 		R->svc_handle = req->svc_handle;
8552072Svn83148 		R->major_vers = new_major;
8562072Svn83148 
8572072Svn83148 		if (dup_svcreg)
8582072Svn83148 			R->result = DS_REG_DUP;
8592072Svn83148 		else
8602072Svn83148 			R->result = DS_REG_VER_NACK;
8612072Svn83148 
8622072Svn83148 		(void) fds_send(lsp, H, msglen);
8632072Svn83148 	}
8642072Svn83148 }
8652072Svn83148 
8662072Svn83148 
8672072Svn83148 /*ARGSUSED*/
8682072Svn83148 static void
8692072Svn83148 ds_handle_unreg(struct ldmsvcs_info *lsp, void *buf, size_t len)
8702072Svn83148 {
8712072Svn83148 	ds_unreg_req_t *req;
8722072Svn83148 	size_t msglen;
8732072Svn83148 
8742072Svn83148 	req = (ds_unreg_req_t *)buf;
8752072Svn83148 
8762072Svn83148 	if (fds_svc_remove(lsp, req->svc_handle) == 0) {
8772072Svn83148 		ds_hdr_t *H;
8782072Svn83148 		ds_unreg_ack_t *R;
8792072Svn83148 
8802072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_unreg_ack_t);
8812072Svn83148 		H = alloca(msglen);
8822072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
8832072Svn83148 
8842072Svn83148 		H->msg_type = DS_REG_ACK;
8852072Svn83148 		H->payload_len = sizeof (ds_unreg_ack_t);
8862072Svn83148 		R->svc_handle = req->svc_handle;
8872072Svn83148 
8882072Svn83148 		(void) fds_send(lsp, H, msglen);
8892072Svn83148 	} else {
8902072Svn83148 		ds_hdr_t *H;
8912072Svn83148 		ds_unreg_nack_t *R;
8922072Svn83148 
8932072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_unreg_nack_t);
8942072Svn83148 		H = alloca(msglen);
8952072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
8962072Svn83148 
8972072Svn83148 		H->msg_type = DS_REG_NACK;
8982072Svn83148 		H->payload_len = sizeof (ds_unreg_nack_t);
8992072Svn83148 		R->svc_handle = req->svc_handle;
9002072Svn83148 
9012072Svn83148 		(void) fds_send(lsp, H, msglen);
9022072Svn83148 	}
9032072Svn83148 }
9042072Svn83148 
9052072Svn83148 
9062072Svn83148 /*
9072072Svn83148  * Message handler lookup table (v1.0 only for now) Future
9082072Svn83148  * versions can add their own lookup table.
9092072Svn83148  */
9102072Svn83148 typedef void (*ds_msg_handler_t)(struct ldmsvcs_info *lsp,
9112072Svn83148 				void *buf, size_t len);
9122072Svn83148 
9132072Svn83148 static const ds_msg_handler_t ds_msg_handlers[] = {
9142072Svn83148 	ds_handle_init_req,		/* DS_INIT_REQ */
9152072Svn83148 	ds_handle_msg_noop,		/* DS_INIT_ACK */
9162072Svn83148 	ds_handle_msg_noop,		/* DS_INIT_NACK */
9172072Svn83148 	ds_handle_reg_req,		/* DS_REG_REQ */
9182072Svn83148 	ds_handle_msg_noop,		/* DS_REG_ACK */
9192072Svn83148 	ds_handle_msg_noop,		/* DS_REG_NACK */
9202072Svn83148 	ds_handle_unreg,		/* DS_UNREG */
9212072Svn83148 	ds_handle_msg_noop,		/* DS_UNREG_ACK */
9222072Svn83148 	ds_handle_msg_noop,		/* DS_UNREG_NACK */
9232072Svn83148 	ds_handle_msg_noop,		/* DS_DATA */
9242072Svn83148 	ds_handle_msg_noop		/* DS_NACK */
9252072Svn83148 };
9262072Svn83148 
9272072Svn83148 
9282072Svn83148 /*
9292072Svn83148  * message and service internal functions
9302072Svn83148  */
9312072Svn83148 static void
9322072Svn83148 fds_svc_alloc(struct ldom_hdl *lhp, struct ldmsvcs_info *lsp)
9332072Svn83148 {
9342072Svn83148 	int i;
935*7850SVuong.Nguyen@Sun.COM 	static char *name[] = { LDM_DS_NAME_CPU, LDM_DS_NAME_MEM,
936*7850SVuong.Nguyen@Sun.COM 			LDM_DS_NAME_PRI, LDM_DS_NAME_IOD, NULL };
9372072Svn83148 
9382072Svn83148 	(void) pthread_mutex_init(&lsp->fmas_svcs.mt, NULL);
9392072Svn83148 	(void) pthread_cond_init(&lsp->fmas_svcs.cv, NULL);
9402072Svn83148 
9412072Svn83148 	for (lsp->fmas_svcs.nsvcs = 0; name[lsp->fmas_svcs.nsvcs] != NULL;
9422072Svn83148 	    lsp->fmas_svcs.nsvcs++)
9432072Svn83148 		;
9442072Svn83148 
9452072Svn83148 	lsp->fmas_svcs.tbl = (fds_svc_t **)lhp->allocp(sizeof (fds_svc_t *) *
9464358Srb144127 	    lsp->fmas_svcs.nsvcs);
9472072Svn83148 
9482072Svn83148 	for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
9492072Svn83148 		lsp->fmas_svcs.tbl[i] =
9504358Srb144127 		    (fds_svc_t *)lhp->allocp(sizeof (fds_svc_t));
9512072Svn83148 		bzero(lsp->fmas_svcs.tbl[i], sizeof (fds_svc_t));
9522072Svn83148 		lsp->fmas_svcs.tbl[i]->name = name[i];
9532072Svn83148 	}
9542072Svn83148 }
9552072Svn83148 
9562072Svn83148 
9572072Svn83148 static fds_svc_t *
9582072Svn83148 fds_svc_lookup(struct ldmsvcs_info *lsp, char *name)
9592072Svn83148 {
9602072Svn83148 	struct timespec twait;
9612072Svn83148 	fds_svc_t *svc;
9622072Svn83148 	int i, ier;
9632072Svn83148 
9642072Svn83148 	if (pthread_mutex_lock(&lsp->fmas_svcs.mt) == EINVAL)
9652072Svn83148 		return (NULL);	/* uninitialized or destroyed mutex */
9662072Svn83148 
9672072Svn83148 	svc = NULL;
9682072Svn83148 	for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
9692072Svn83148 		if (strcmp(lsp->fmas_svcs.tbl[i]->name, name) == 0) {
9702072Svn83148 			svc = lsp->fmas_svcs.tbl[i];
9712072Svn83148 			break;
9722072Svn83148 		}
9732072Svn83148 	}
9742072Svn83148 
9752072Svn83148 	ASSERT(svc != NULL);
9762072Svn83148 
977*7850SVuong.Nguyen@Sun.COM 	if (svc->state == DS_SVC_INACTIVE) {
978*7850SVuong.Nguyen@Sun.COM 		/* service is not registered */
979*7850SVuong.Nguyen@Sun.COM 		ier = ETIMEDOUT;
980*7850SVuong.Nguyen@Sun.COM 	} else {
981*7850SVuong.Nguyen@Sun.COM 		ier = 0;
982*7850SVuong.Nguyen@Sun.COM 		twait.tv_sec = time(NULL) + lsp->cv_twait;
983*7850SVuong.Nguyen@Sun.COM 		twait.tv_nsec = 0;
9842072Svn83148 
985*7850SVuong.Nguyen@Sun.COM 		while (svc->state != DS_SVC_ACTIVE && ier == 0 &&
986*7850SVuong.Nguyen@Sun.COM 		    lsp->fds_chan.state != CHANNEL_UNUSABLE)
987*7850SVuong.Nguyen@Sun.COM 			ier = pthread_cond_timedwait(&lsp->fmas_svcs.cv,
988*7850SVuong.Nguyen@Sun.COM 			    &lsp->fmas_svcs.mt, &twait);
989*7850SVuong.Nguyen@Sun.COM 
990*7850SVuong.Nguyen@Sun.COM 		/*
991*7850SVuong.Nguyen@Sun.COM 		 * By now, the ds service should have registered already.
992*7850SVuong.Nguyen@Sun.COM 		 * If it does not, ldmd probably does not support this service.
993*7850SVuong.Nguyen@Sun.COM 		 * Then mark the service state as inactive.
994*7850SVuong.Nguyen@Sun.COM 		 */
995*7850SVuong.Nguyen@Sun.COM 		if (ier == ETIMEDOUT) {
996*7850SVuong.Nguyen@Sun.COM 			svc->state = DS_SVC_INACTIVE;
997*7850SVuong.Nguyen@Sun.COM 		}
998*7850SVuong.Nguyen@Sun.COM 	}
9992072Svn83148 
10002072Svn83148 	(void) pthread_mutex_unlock(&lsp->fmas_svcs.mt);
10012072Svn83148 
10022072Svn83148 	if (ier == 0)
10032072Svn83148 		return (svc);
10042072Svn83148 	else
10052072Svn83148 		return (NULL);
10062072Svn83148 }
10072072Svn83148 
10082072Svn83148 
10092072Svn83148 static uint64_t
10102072Svn83148 fds_svc_req_num(void)
10112072Svn83148 {
10122072Svn83148 	static uint64_t req_num = 1;
10132072Svn83148 
10142072Svn83148 	return (req_num++);
10152072Svn83148 }
10162072Svn83148 
10172072Svn83148 
10182072Svn83148 /*
10192072Svn83148  * return 0 if successful, 1 if otherwise
10202072Svn83148  */
10212072Svn83148 static int
10222072Svn83148 read_msg(struct ldmsvcs_info *lsp)
10232072Svn83148 {
10242072Svn83148 	ds_hdr_t header;
10252072Svn83148 	void *msg_buf;
10262072Svn83148 
10272072Svn83148 	/*
10282072Svn83148 	 * read the header
10292072Svn83148 	 */
10302072Svn83148 	if (read_stream(lsp->fds_chan.fd, &header, sizeof (ds_hdr_t)) != 0)
10312072Svn83148 		return (1);
10322072Svn83148 
10332072Svn83148 	if (header.msg_type >=
10342072Svn83148 	    sizeof (ds_msg_handlers) / sizeof (ds_msg_handler_t))
10354358Srb144127 		return (1);
10362072Svn83148 
10372072Svn83148 	/*
10382072Svn83148 	 * handle data as a special case
10392072Svn83148 	 */
10402072Svn83148 	if (header.msg_type == 9)
10412072Svn83148 		return (poller_handle_data(lsp->fds_chan.fd,
10424358Srb144127 		    header.payload_len));
10432072Svn83148 
10442072Svn83148 	/*
10452072Svn83148 	 * all other types of messages should be small
10462072Svn83148 	 */
10472072Svn83148 	ASSERT(header.payload_len < 1024);
10482072Svn83148 	msg_buf = alloca(header.payload_len);
10492072Svn83148 
10502072Svn83148 	/*
10512072Svn83148 	 * read the payload
10522072Svn83148 	 */
10532072Svn83148 	if (read_stream(lsp->fds_chan.fd, msg_buf, header.payload_len) != 0)
10542072Svn83148 		return (1);
10552072Svn83148 
10562072Svn83148 	(*ds_msg_handlers[header.msg_type])(lsp, msg_buf, header.payload_len);
10572072Svn83148 
10582072Svn83148 	return (0);
10592072Svn83148 }
10602072Svn83148 
10612072Svn83148 
10622072Svn83148 /*
10632072Svn83148  * return values:
10642072Svn83148  *  0 - success
10652072Svn83148  *  1 - problem with opening the channel
10662072Svn83148  *  2 - channed not opened; request to exit has been detected
10672072Svn83148  */
10682072Svn83148 static int
10692072Svn83148 channel_openreset(struct ldmsvcs_info *lsp)
10702072Svn83148 {
10712072Svn83148 	int ier;
10722072Svn83148 
10732072Svn83148 	ier = pthread_mutex_lock(&lsp->mt);
10742072Svn83148 
10752072Svn83148 	if (ier == EINVAL || lsp->fds_chan.state == CHANNEL_EXIT ||
10762072Svn83148 	    lsp->fds_chan.state == CHANNEL_UNUSABLE) {
10772072Svn83148 		(void) pthread_mutex_unlock(&lsp->mt);
10782072Svn83148 		return (2);
10792072Svn83148 	}
10802072Svn83148 
10812072Svn83148 	if (lsp->fds_chan.state == CHANNEL_UNINITIALIZED ||
10822072Svn83148 	    lsp->fds_chan.state == CHANNEL_CLOSED) {
10832072Svn83148 		(void) pthread_cond_broadcast(&lsp->cv);
10842072Svn83148 
10852072Svn83148 		if ((lsp->fds_chan.fd = open(FDS_VLDC, O_RDWR)) < 0) {
10862072Svn83148 			lsp->fds_chan.state = CHANNEL_UNUSABLE;
10874358Srb144127 			lsp->cv_twait = get_smf_int_val(LDM_RUNNING_TO_PROP_NM,
10884358Srb144127 			    0, LDM_TIMEOUT_CEILING, LDM_RUNNING_WAIT_TIME);
10892072Svn83148 			(void) pthread_mutex_unlock(&lsp->mt);
10902072Svn83148 			(void) pthread_cond_broadcast(&lsp->fmas_svcs.cv);
10912072Svn83148 
10922072Svn83148 			return (2);
10932072Svn83148 		} else {
10942072Svn83148 			vldc_opt_op_t op;
10952072Svn83148 
10962072Svn83148 			op.op_sel = VLDC_OP_SET;
10972072Svn83148 			op.opt_sel = VLDC_OPT_MODE;
10986408Sha137994 			op.opt_val = LDC_MODE_RELIABLE;
10992072Svn83148 
11002072Svn83148 			if (ioctl(lsp->fds_chan.fd, VLDC_IOCTL_OPT_OP,
11014358Srb144127 			    &op) != 0) {
11022072Svn83148 				(void) close(lsp->fds_chan.fd);
11032072Svn83148 				(void) pthread_mutex_unlock(&lsp->mt);
11042072Svn83148 				return (1);
11052072Svn83148 			}
11062072Svn83148 		}
11072072Svn83148 		lsp->fds_chan.state = CHANNEL_OPEN;
11082072Svn83148 	}
11092072Svn83148 
11102072Svn83148 	if (lsp->fds_chan.state == CHANNEL_OPEN) {
11112072Svn83148 		/*
11122072Svn83148 		 * reset various channel parameters
11132072Svn83148 		 */
11142072Svn83148 		lsp->fds_chan.ver.major = 0;
11152072Svn83148 		lsp->fds_chan.ver.minor = 0;
11162072Svn83148 		fds_svc_reset(lsp, -1);
11172072Svn83148 	}
11182072Svn83148 	(void) pthread_mutex_unlock(&lsp->mt);
11192072Svn83148 
11202072Svn83148 	return (0);
11212072Svn83148 }
11222072Svn83148 
11232072Svn83148 
11242072Svn83148 static void
11252072Svn83148 channel_fini(void)
11262072Svn83148 {
11272072Svn83148 	struct ldmsvcs_info *lsp;
11282072Svn83148 
11292072Svn83148 	/*
11302072Svn83148 	 * End the poller thread
11312072Svn83148 	 */
11322072Svn83148 	poller_shutdown();
11332072Svn83148 
11342072Svn83148 	if ((lsp = channel_init(NULL)) == NULL)
11352072Svn83148 		return;
11362072Svn83148 
11372072Svn83148 	(void) pthread_mutex_lock(&lsp->mt);
11382072Svn83148 
11392072Svn83148 	lsp->fds_chan.state = CHANNEL_EXIT;
11402072Svn83148 	(void) close(lsp->fds_chan.fd);
11412072Svn83148 
11422072Svn83148 	(void) pthread_mutex_unlock(&lsp->mt);
11432072Svn83148 }
11442072Svn83148 
11452072Svn83148 
11462072Svn83148 static struct ldmsvcs_info *
11472072Svn83148 channel_init(struct ldom_hdl *lhp)
11482072Svn83148 {
11492072Svn83148 	static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER;
11502072Svn83148 	static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
11512072Svn83148 	static struct ldmsvcs_info *root = NULL;
11522072Svn83148 	static int busy_init = 0;
11532072Svn83148 
11542072Svn83148 	struct timespec twait;
11552072Svn83148 	int expired;
11562072Svn83148 
11572072Svn83148 	(void) pthread_mutex_lock(&mt);
11582072Svn83148 
11592072Svn83148 	while (busy_init == 1)
11602072Svn83148 		(void) pthread_cond_wait(&cv, &mt);
11612072Svn83148 
11622072Svn83148 	if (root != NULL || (lhp == NULL && root == NULL)) {
11632072Svn83148 		(void) pthread_mutex_unlock(&mt);
11642072Svn83148 		return (root);
11652072Svn83148 	}
11662072Svn83148 
11672072Svn83148 	/*
11682072Svn83148 	 * get to this point if we need to open the channel
11692072Svn83148 	 */
11702072Svn83148 	busy_init = 1;
11712072Svn83148 	(void) pthread_mutex_unlock(&mt);
11722072Svn83148 
11732072Svn83148 	root = (struct ldmsvcs_info *)
11744358Srb144127 	    lhp->allocp(sizeof (struct ldmsvcs_info));
11752072Svn83148 	bzero(root, sizeof (struct ldmsvcs_info));
11762072Svn83148 
11772072Svn83148 	root->fds_chan.state = CHANNEL_UNINITIALIZED;
11784358Srb144127 	root->cv_twait = get_smf_int_val(LDM_INIT_TO_PROP_NM,
11794358Srb144127 	    0, LDM_TIMEOUT_CEILING, LDM_INIT_WAIT_TIME);
11802072Svn83148 
11812072Svn83148 	if (pthread_mutex_init(&root->mt, NULL) != 0 ||
11822072Svn83148 	    pthread_cond_init(&root->cv, NULL) != 0) {
11832072Svn83148 		lhp->freep(root, sizeof (struct ldmsvcs_info));
11842072Svn83148 		return (NULL);
11852072Svn83148 	}
11862072Svn83148 
11872072Svn83148 	fds_svc_alloc(lhp, root);
11882072Svn83148 	fds_svc_reset(root, -1);
11892072Svn83148 
11902072Svn83148 	(void) poller_init(root);
11912072Svn83148 
11922072Svn83148 	expired = 0;
11932072Svn83148 	twait.tv_sec = time(NULL) + 10;
11942072Svn83148 	twait.tv_nsec = 0;
11952072Svn83148 
11962072Svn83148 	(void) pthread_mutex_lock(&root->mt);
11972072Svn83148 
11982072Svn83148 	/*
11992072Svn83148 	 * wait for channel to become uninitialized.  this should be quick.
12002072Svn83148 	 */
12012072Svn83148 	while (root->fds_chan.state == CHANNEL_UNINITIALIZED && expired == 0)
12022072Svn83148 		expired = pthread_cond_timedwait(&root->cv, &root->mt, &twait);
12032072Svn83148 
12042072Svn83148 	if (root->fds_chan.state == CHANNEL_UNUSABLE)
12052072Svn83148 		expired = 1;
12062072Svn83148 
12072072Svn83148 	(void) pthread_mutex_unlock(&root->mt);
12082072Svn83148 
12092072Svn83148 	(void) pthread_mutex_lock(&mt);
12102072Svn83148 	busy_init = 0;
12112072Svn83148 	(void) pthread_mutex_unlock(&mt);
12122072Svn83148 	(void) pthread_cond_broadcast(&cv);
12132072Svn83148 
12142072Svn83148 	(void) atexit(channel_fini);
12152072Svn83148 
12162072Svn83148 	if (expired == 0)
12172072Svn83148 		return (root);
12182072Svn83148 	else
12192072Svn83148 		return (NULL);
12202072Svn83148 }
12212072Svn83148 
12222072Svn83148 
12232072Svn83148 static int
12242072Svn83148 sendrecv(struct ldom_hdl *lhp, uint64_t req_num,
12252072Svn83148 	void *msg, size_t msglen, ds_svc_hdl_t *svc_hdl, char *svcname,
12262072Svn83148 	void **resp, size_t *resplen)
12272072Svn83148 {
12282072Svn83148 	struct ldmsvcs_info *lsp;
12292072Svn83148 	fds_svc_t *svc;
12302072Svn83148 	int maxretries, index, i, ier;
12312072Svn83148 
12322072Svn83148 	lsp = lhp->lsinfo;
12332072Svn83148 	i = 0;
12342072Svn83148 	maxretries = 1;
12352072Svn83148 
12362072Svn83148 	do {
12372072Svn83148 		/*
12382072Svn83148 		 * if any of the calls in this loop fail, retry some number
12392072Svn83148 		 * of times before giving up.
12402072Svn83148 		 */
12412072Svn83148 		if ((svc = fds_svc_lookup(lsp, svcname)) == NULL) {
12422072Svn83148 			(void) pthread_mutex_lock(&lsp->mt);
12432072Svn83148 
12442072Svn83148 			if (lsp->fds_chan.state != CHANNEL_READY)
12452072Svn83148 				ier = ETIMEDOUT;	/* channel not ready */
12462072Svn83148 			else
12472072Svn83148 				ier = ENOTSUP;		/* service not ready */
12482072Svn83148 
12492072Svn83148 			(void) pthread_mutex_unlock(&lsp->mt);
12502072Svn83148 
12512072Svn83148 			continue;
12522072Svn83148 		} else {
12532072Svn83148 			ier = 0;
12542072Svn83148 			*svc_hdl = svc->hdl;
12552072Svn83148 		}
12562072Svn83148 
12572072Svn83148 		index = poller_add_pending(lhp, req_num);
12582072Svn83148 
12592072Svn83148 		if ((ier = fds_send(lsp, msg, msglen)) != 0 ||
12602072Svn83148 		    (ier = poller_recv_data(lhp, req_num, index, resp,
12614358Srb144127 		    resplen)) != 0)
12622072Svn83148 			poller_delete_pending(req_num, index);
12632072Svn83148 
12642072Svn83148 	} while (i++ < maxretries && ier != 0);
12652072Svn83148 
12662072Svn83148 	ASSERT(ier == 0 || ier == ETIMEDOUT || ier == ENOTSUP);
12672072Svn83148 
12682072Svn83148 	return (ier);
12692072Svn83148 }
12702072Svn83148 
12712072Svn83148 
12722072Svn83148 /*
12732072Svn83148  * input:
12742072Svn83148  *   msg_type - requested operation: FMA_CPU_REQ_STATUS or FMA_CPU_REQ_OFFLINE
12752072Svn83148  *   cpuid - physical cpu id
12762072Svn83148  *
12772072Svn83148  * normal return values:
12782072Svn83148  *   P_OFFLINE - cpu is offline
12792072Svn83148  *   P_ONLINE - cpu is online
12802072Svn83148  *
12812072Svn83148  * abnormal return values:
12822072Svn83148  *   ETIMEDOUT - LDOM manager is not responding
12832072Svn83148  *   ENOTSUP - LDOM service for cpu offlining/status is not available
12842072Svn83148  *   ENOMSG - got an unexpected response from the LDOM cpu service
12852072Svn83148  */
12862072Svn83148 static int
12872072Svn83148 cpu_request(struct ldom_hdl *lhp, uint32_t msg_type, uint32_t cpuid)
12882072Svn83148 {
12892072Svn83148 	ds_hdr_t *H;
12902072Svn83148 	ds_data_handle_t *D;
12912072Svn83148 	fma_cpu_service_req_t *R;
12922072Svn83148 
1293*7850SVuong.Nguyen@Sun.COM 	char *svcname = LDM_DS_NAME_CPU;
12942072Svn83148 	fma_cpu_resp_t *respmsg;
12952072Svn83148 	void *resp;
12962072Svn83148 	size_t resplen, reqmsglen;
12972072Svn83148 	int rc;
12982072Svn83148 
12992072Svn83148 	if (lhp->lsinfo == NULL)
13002072Svn83148 		return (ENOMSG);
13012072Svn83148 
13022072Svn83148 	reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
13034358Srb144127 	    sizeof (fma_cpu_service_req_t);
13042072Svn83148 
13052072Svn83148 	H = lhp->allocp(reqmsglen);
13062072Svn83148 	D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
13072072Svn83148 	R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
13082072Svn83148 
13092072Svn83148 	H->msg_type = DS_DATA;
13102072Svn83148 	H->payload_len = sizeof (ds_data_handle_t) +
13114358Srb144127 	    sizeof (fma_cpu_service_req_t);
13122072Svn83148 
13132072Svn83148 	R->req_num = fds_svc_req_num();
13142072Svn83148 	R->msg_type = msg_type;
13152072Svn83148 	R->cpu_id = cpuid;
13162072Svn83148 
13172072Svn83148 	if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
13184358Srb144127 	    &D->svc_handle, svcname, &resp, &resplen)) != 0) {
13192072Svn83148 		lhp->freep(H, reqmsglen);
13202072Svn83148 		return (rc);
13212072Svn83148 	}
13222072Svn83148 
13232072Svn83148 	lhp->freep(H, reqmsglen);
13242072Svn83148 
13252072Svn83148 	ASSERT(resplen == sizeof (fma_cpu_resp_t));
13262072Svn83148 	respmsg = (fma_cpu_resp_t *)resp;
13272072Svn83148 
13282072Svn83148 	rc = ENOMSG;
13292072Svn83148 	if (respmsg->result == FMA_CPU_RESP_OK) {
13302072Svn83148 		if (respmsg->status == FMA_CPU_STAT_ONLINE)
13312072Svn83148 			rc = P_ONLINE;
13322072Svn83148 		else if (respmsg->status == FMA_CPU_STAT_OFFLINE)
13332072Svn83148 			rc = P_OFFLINE;
13342072Svn83148 	} else {
13352072Svn83148 		if (msg_type == FMA_CPU_REQ_OFFLINE &&
13362072Svn83148 		    respmsg->status == FMA_CPU_STAT_OFFLINE)
13372072Svn83148 			rc = P_OFFLINE;
13382072Svn83148 	}
13392072Svn83148 
13402072Svn83148 	lhp->freep(resp, resplen);
13412072Svn83148 
13422072Svn83148 	return (rc);
13432072Svn83148 }
13442072Svn83148 
13452072Svn83148 
13462072Svn83148 /*
13472072Svn83148  * input:
13482072Svn83148  *   msg_type - requested operation: FMA_MEM_REQ_STATUS or FMA_MEM_REQ_RETIRE
13492072Svn83148  *   pa - starting address of memory page
13502072Svn83148  *   pgsize - memory page size in bytes
13512072Svn83148  *
13522072Svn83148  * normal return values for msg_type == FMA_MEM_REQ_STATUS:
13532072Svn83148  *   0 - page is retired
13542072Svn83148  *   EAGAIN - page is scheduled for retirement
13552072Svn83148  *   EIO - page not scheduled for retirement
13562072Svn83148  *   EINVAL - error
13572072Svn83148  *
13582072Svn83148  * normal return values for msg_type == FMA_MEM_REQ_RETIRE:
13592072Svn83148  *   0 - success in retiring page
13602072Svn83148  *   EIO - page is already retired
13612072Svn83148  *   EAGAIN - page is scheduled for retirement
13622072Svn83148  *   EINVAL - error
13632072Svn83148  *
13642072Svn83148  * abnormal return values (regardless of msg_type)
13652072Svn83148  *   ETIMEDOUT - LDOM manager is not responding
13662072Svn83148  *   ENOTSUP - LDOM service for cpu offlining/status is not available
13672072Svn83148  *   ENOMSG - got an unexpected response from the LDOM cpu service
13682072Svn83148  */
13692072Svn83148 static int
13702072Svn83148 mem_request(struct ldom_hdl *lhp, uint32_t msg_type, uint64_t pa,
13712072Svn83148 	    uint64_t pgsize)
13722072Svn83148 {
13732072Svn83148 	ds_hdr_t *H;
13742072Svn83148 	ds_data_handle_t *D;
13752072Svn83148 	fma_mem_service_req_t *R;
13762072Svn83148 
1377*7850SVuong.Nguyen@Sun.COM 	char *svcname = LDM_DS_NAME_MEM;
13782072Svn83148 	fma_mem_resp_t *respmsg;
13792072Svn83148 	void *resp;
13802072Svn83148 	size_t resplen, reqmsglen;
13812072Svn83148 	int rc;
13822072Svn83148 
13832072Svn83148 	if (lhp->lsinfo == NULL)
13842072Svn83148 		return (ENOMSG);
13852072Svn83148 
13862072Svn83148 	reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
13874358Srb144127 	    sizeof (fma_mem_service_req_t);
13882072Svn83148 
13892072Svn83148 	H = lhp->allocp(reqmsglen);
13902072Svn83148 	bzero(H, reqmsglen);
13912072Svn83148 	D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
13922072Svn83148 	R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
13932072Svn83148 
13942072Svn83148 	H->msg_type = DS_DATA;
13952072Svn83148 	H->payload_len = sizeof (ds_data_handle_t) +
13964358Srb144127 	    sizeof (fma_mem_service_req_t);
13972072Svn83148 
13982072Svn83148 	R->req_num = fds_svc_req_num();
13992072Svn83148 	R->msg_type = msg_type;
14002072Svn83148 	R->real_addr = pa;
14012072Svn83148 	R->length = pgsize;
14022072Svn83148 
14032072Svn83148 	if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
14044358Srb144127 	    &D->svc_handle, svcname, &resp, &resplen)) != 0) {
14052072Svn83148 		lhp->freep(H, reqmsglen);
14062072Svn83148 		return (rc);
14072072Svn83148 	}
14082072Svn83148 
14092072Svn83148 	lhp->freep(H, reqmsglen);
14102072Svn83148 
14112072Svn83148 	ASSERT(resplen == sizeof (fma_mem_resp_t));
14122072Svn83148 	respmsg = (fma_mem_resp_t *)resp;
14132072Svn83148 
14142072Svn83148 	rc = ENOMSG;
14152072Svn83148 	if (msg_type == FMA_MEM_REQ_STATUS) {
14162072Svn83148 		if (respmsg->result == FMA_MEM_RESP_OK) {
14172072Svn83148 			if (respmsg->status == FMA_MEM_STAT_RETIRED)
14184655Svn83148 				rc = 0;		/* page is retired */
14192072Svn83148 			else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14204655Svn83148 				rc = EIO;	/* page is not scheduled */
14212072Svn83148 		} else if (respmsg->result == FMA_MEM_RESP_FAILURE) {
14224655Svn83148 			if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14234655Svn83148 				rc = EAGAIN;	/* page is scheduled */
14244655Svn83148 			else if (respmsg->status == FMA_MEM_STAT_ILLEGAL)
14252072Svn83148 				rc = EINVAL;
14262072Svn83148 		}
14272072Svn83148 	} else if (msg_type == FMA_MEM_REQ_RETIRE) {
14282072Svn83148 		if (respmsg->result == FMA_MEM_RESP_OK) {
14292072Svn83148 			if (respmsg->status == FMA_MEM_STAT_RETIRED)
14304655Svn83148 				rc = 0;		/* is successfully retired */
14312072Svn83148 		} else if (respmsg->result == FMA_MEM_RESP_FAILURE) {
14322072Svn83148 			if (respmsg->status == FMA_MEM_STAT_RETIRED)
14334655Svn83148 				rc = EIO;	/* is already retired */
14344655Svn83148 			else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14354655Svn83148 				rc = EAGAIN;	/* is scheduled to retire */
14362072Svn83148 			else if (respmsg->status == FMA_MEM_STAT_ILLEGAL)
14372072Svn83148 				rc = EINVAL;
14382072Svn83148 		}
14397532SSean.Ye@Sun.COM 	} else if (msg_type == FMA_MEM_REQ_RESURRECT) {
14407532SSean.Ye@Sun.COM 		if (respmsg->result == FMA_MEM_RESP_OK) {
14417532SSean.Ye@Sun.COM 			if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14427532SSean.Ye@Sun.COM 				rc = 0;		/* is successfully unretired */
14437532SSean.Ye@Sun.COM 		} if (respmsg->result == FMA_MEM_RESP_FAILURE) {
14447532SSean.Ye@Sun.COM 			if (respmsg->status == FMA_MEM_STAT_RETIRED)
14457532SSean.Ye@Sun.COM 				rc = EAGAIN; 	/* page couldn't be locked */
14467532SSean.Ye@Sun.COM 			else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14477532SSean.Ye@Sun.COM 				rc = EIO;	/* page isn't retired already */
14487532SSean.Ye@Sun.COM 			else if (respmsg->status == FMA_MEM_STAT_ILLEGAL)
14497532SSean.Ye@Sun.COM 				rc = EINVAL;
14507532SSean.Ye@Sun.COM 		}
14512072Svn83148 	}
14522072Svn83148 
14532072Svn83148 	lhp->freep(resp, resplen);
14542072Svn83148 
14552072Svn83148 	return (rc);
14562072Svn83148 }
14572072Svn83148 
14582072Svn83148 
14592072Svn83148 /*
14602072Svn83148  * APIs
14612072Svn83148  */
14622072Svn83148 int
14632072Svn83148 ldmsvcs_check_channel(void)
14642072Svn83148 {
14652072Svn83148 	struct stat buf;
14662072Svn83148 
14672072Svn83148 	if (stat(FDS_VLDC, &buf) == 0)
14682072Svn83148 		return (0);	/* vldc exists */
14692072Svn83148 	else if (errno == ENOENT || errno == ENOTDIR)
14702072Svn83148 		return (1);	/* vldc does not exist */
14712072Svn83148 	else
14722072Svn83148 		return (-1);	/* miscellaneous error */
14732072Svn83148 }
14742072Svn83148 
14752072Svn83148 
14762072Svn83148 /*ARGSUSED*/
14772072Svn83148 void
14782072Svn83148 ldmsvcs_init(struct ldom_hdl *lhp)
14792072Svn83148 {
14802072Svn83148 	if (ldmsvcs_check_channel() != 0)
14812072Svn83148 		return;
14822072Svn83148 
14832072Svn83148 	lhp->lsinfo = channel_init(lhp);
14842072Svn83148 	poller_add_client();
14852072Svn83148 }
14862072Svn83148 
14872072Svn83148 
14882072Svn83148 /*ARGSUSED*/
14892072Svn83148 void
14902072Svn83148 ldmsvcs_fini(struct ldom_hdl *lhp)
14912072Svn83148 {
14922072Svn83148 	if (ldmsvcs_check_channel() != 0)
14932072Svn83148 		return;
14942072Svn83148 
14952072Svn83148 	poller_remove_client();
14962072Svn83148 }
14972072Svn83148 
14982072Svn83148 
14992072Svn83148 /*ARGSUSED*/
15002072Svn83148 ssize_t
15012072Svn83148 ldmsvcs_get_core_md(struct ldom_hdl *lhp, uint64_t **buf)
15022072Svn83148 {
15032072Svn83148 	ds_hdr_t *H;
15042072Svn83148 	ds_data_handle_t *D;
15052072Svn83148 	fma_req_pri_t *R;
15062072Svn83148 
1507*7850SVuong.Nguyen@Sun.COM 	char *svcname = LDM_DS_NAME_PRI;
15082072Svn83148 	void *resp;
15092072Svn83148 	size_t resplen, reqmsglen;
15102072Svn83148 	ssize_t buflen;
15112072Svn83148 	int rc;
15122072Svn83148 
15132072Svn83148 	if (lhp->lsinfo == NULL)
15142072Svn83148 		return (-1);
15152072Svn83148 
15162072Svn83148 	reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
15174358Srb144127 	    sizeof (fma_req_pri_t);
15182072Svn83148 
15192072Svn83148 	H = lhp->allocp(reqmsglen);
15202072Svn83148 	D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
15212072Svn83148 	R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
15222072Svn83148 
15232072Svn83148 	H->msg_type = DS_DATA;
15242072Svn83148 	H->payload_len = sizeof (ds_data_handle_t) +
15254358Srb144127 	    sizeof (fma_req_pri_t);
15262072Svn83148 
15272072Svn83148 	R->req_num = fds_svc_req_num();
15282072Svn83148 
15292072Svn83148 	if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
15304358Srb144127 	    &D->svc_handle, svcname, &resp, &resplen)) != 0) {
15312072Svn83148 		lhp->freep(H, reqmsglen);
15322072Svn83148 		errno = rc;
15332072Svn83148 		return (-1);
15342072Svn83148 	}
15352072Svn83148 
15362072Svn83148 	lhp->freep(H, reqmsglen);
15372072Svn83148 
15382072Svn83148 	/*
15392072Svn83148 	 * resp should contain the req_num immediately followed by the PRI
15402072Svn83148 	 * (the latter may or may not be present).  unfortunately, the
15412072Svn83148 	 * current compiler flags cause a warning for the following
15422072Svn83148 	 * definition
15432072Svn83148 	 *
15442072Svn83148 	 * typedef struct {
15452072Svn83148 	 *    uint64_t req_num;
15462072Svn83148 	 *    uint8_t pri[];
15472072Svn83148 	 *  } fma_pri_resp_t;
15482072Svn83148 	 *
15492072Svn83148 	 * so we do not use the struct here.
15502072Svn83148 	 */
15512072Svn83148 	if (resplen <= sizeof (uint64_t)) {
15522072Svn83148 		lhp->freep(resp, resplen);
15532072Svn83148 		if (resplen == sizeof (uint64_t))
15542072Svn83148 			return (0);
15552072Svn83148 		else
15562072Svn83148 			return (-1);
15572072Svn83148 	}
15582072Svn83148 
15592072Svn83148 	buflen = resplen - sizeof (uint64_t);
15602072Svn83148 	*buf = lhp->allocp(buflen);
15612072Svn83148 
15622072Svn83148 	bcopy((void *)((ptrdiff_t)resp + sizeof (uint64_t)), *buf, buflen);
15632072Svn83148 	lhp->freep(resp, resplen);
15642072Svn83148 
15652072Svn83148 	return (buflen);
15662072Svn83148 }
15672072Svn83148 
15682072Svn83148 
15692072Svn83148 /*
15702072Svn83148  * see cpu_request() for a description of return values
15712072Svn83148  */
15722072Svn83148 int
15732072Svn83148 ldmsvcs_cpu_req_status(struct ldom_hdl *lhp, uint32_t cpuid)
15742072Svn83148 {
15752072Svn83148 	return (cpu_request(lhp, FMA_CPU_REQ_STATUS, cpuid));
15762072Svn83148 }
15772072Svn83148 
15782072Svn83148 
15792072Svn83148 int
15802072Svn83148 ldmsvcs_cpu_req_offline(struct ldom_hdl *lhp, uint32_t cpuid)
15812072Svn83148 {
15822072Svn83148 	return (cpu_request(lhp, FMA_CPU_REQ_OFFLINE, cpuid));
15832072Svn83148 }
15842072Svn83148 
15856111Scy152378 int
15866111Scy152378 ldmsvcs_cpu_req_online(struct ldom_hdl *lhp, uint32_t cpuid)
15876111Scy152378 {
15886111Scy152378 	return (cpu_request(lhp, FMA_CPU_REQ_ONLINE, cpuid));
15896111Scy152378 }
15902072Svn83148 
15912072Svn83148 /*
15922072Svn83148  * see mem_request() for a description of return values
15932072Svn83148  */
15942072Svn83148 int
15952072Svn83148 ldmsvcs_mem_req_status(struct ldom_hdl *lhp, uint64_t pa)
15962072Svn83148 {
15972072Svn83148 	return (mem_request(lhp, FMA_MEM_REQ_STATUS, pa, getpagesize()));
15982072Svn83148 }
15992072Svn83148 
16002072Svn83148 int
16012072Svn83148 ldmsvcs_mem_req_retire(struct ldom_hdl *lhp, uint64_t pa)
16022072Svn83148 {
16032072Svn83148 	return (mem_request(lhp, FMA_MEM_REQ_RETIRE, pa, getpagesize()));
16042072Svn83148 }
16052072Svn83148 
16066111Scy152378 int
16076111Scy152378 ldmsvcs_mem_req_unretire(struct ldom_hdl *lhp, uint64_t pa)
16086111Scy152378 {
16096111Scy152378 	return (mem_request(lhp, FMA_MEM_REQ_RESURRECT, pa, getpagesize()));
16106111Scy152378 }
16116111Scy152378 
1612*7850SVuong.Nguyen@Sun.COM int
1613*7850SVuong.Nguyen@Sun.COM ldmsvcs_io_req_id(struct ldom_hdl *lhp, uint64_t addr, uint_t type,
1614*7850SVuong.Nguyen@Sun.COM     uint64_t *virt_addr, char *name, int name_len, uint64_t *did)
1615*7850SVuong.Nguyen@Sun.COM {
1616*7850SVuong.Nguyen@Sun.COM 
1617*7850SVuong.Nguyen@Sun.COM 	ds_hdr_t *H;
1618*7850SVuong.Nguyen@Sun.COM 	ds_data_handle_t *D;
1619*7850SVuong.Nguyen@Sun.COM 	fma_io_req_t *R;
1620*7850SVuong.Nguyen@Sun.COM 
1621*7850SVuong.Nguyen@Sun.COM 	char *svcname = LDM_DS_NAME_IOD;
1622*7850SVuong.Nguyen@Sun.COM 	void *resp;
1623*7850SVuong.Nguyen@Sun.COM 	fma_io_resp_t *iop;
1624*7850SVuong.Nguyen@Sun.COM 	size_t resplen, reqmsglen;
1625*7850SVuong.Nguyen@Sun.COM 	int offset;
1626*7850SVuong.Nguyen@Sun.COM 	int rc;
1627*7850SVuong.Nguyen@Sun.COM 
1628*7850SVuong.Nguyen@Sun.COM 	if (lhp->lsinfo == NULL)
1629*7850SVuong.Nguyen@Sun.COM 		return (-1);
1630*7850SVuong.Nguyen@Sun.COM 
1631*7850SVuong.Nguyen@Sun.COM 	reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
1632*7850SVuong.Nguyen@Sun.COM 	    sizeof (fma_io_req_t);
1633*7850SVuong.Nguyen@Sun.COM 
1634*7850SVuong.Nguyen@Sun.COM 	H = lhp->allocp(reqmsglen);
1635*7850SVuong.Nguyen@Sun.COM 	D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
1636*7850SVuong.Nguyen@Sun.COM 	R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
1637*7850SVuong.Nguyen@Sun.COM 
1638*7850SVuong.Nguyen@Sun.COM 	H->msg_type = DS_DATA;
1639*7850SVuong.Nguyen@Sun.COM 	H->payload_len = sizeof (ds_data_handle_t) + sizeof (fma_io_req_t);
1640*7850SVuong.Nguyen@Sun.COM 
1641*7850SVuong.Nguyen@Sun.COM 	R->req_num = fds_svc_req_num();
1642*7850SVuong.Nguyen@Sun.COM 	R->msg_type = type;
1643*7850SVuong.Nguyen@Sun.COM 	R->rsrc_address = addr;
1644*7850SVuong.Nguyen@Sun.COM 
1645*7850SVuong.Nguyen@Sun.COM 	rc = ENOMSG;
1646*7850SVuong.Nguyen@Sun.COM 	if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
1647*7850SVuong.Nguyen@Sun.COM 	    &D->svc_handle, svcname, &resp, &resplen)) != 0) {
1648*7850SVuong.Nguyen@Sun.COM 		lhp->freep(H, reqmsglen);
1649*7850SVuong.Nguyen@Sun.COM 		return (rc);
1650*7850SVuong.Nguyen@Sun.COM 	}
1651*7850SVuong.Nguyen@Sun.COM 	lhp->freep(H, reqmsglen);
1652*7850SVuong.Nguyen@Sun.COM 
1653*7850SVuong.Nguyen@Sun.COM 	/*
1654*7850SVuong.Nguyen@Sun.COM 	 * resp should contain the req_num, status, virtual addr, domain id
1655*7850SVuong.Nguyen@Sun.COM 	 * and the domain name. The domain name may or may not be present.
1656*7850SVuong.Nguyen@Sun.COM 	 */
1657*7850SVuong.Nguyen@Sun.COM 	offset = sizeof (fma_io_resp_t);
1658*7850SVuong.Nguyen@Sun.COM 	if (resplen < offset) {
1659*7850SVuong.Nguyen@Sun.COM 		lhp->freep(resp, resplen);
1660*7850SVuong.Nguyen@Sun.COM 		return (-1);
1661*7850SVuong.Nguyen@Sun.COM 	}
1662*7850SVuong.Nguyen@Sun.COM 
1663*7850SVuong.Nguyen@Sun.COM 	iop = (fma_io_resp_t *)resp;
1664*7850SVuong.Nguyen@Sun.COM 	switch (iop->result) {
1665*7850SVuong.Nguyen@Sun.COM 	case FMA_IO_RESP_OK:
1666*7850SVuong.Nguyen@Sun.COM 		/* success */
1667*7850SVuong.Nguyen@Sun.COM 		rc = 0;
1668*7850SVuong.Nguyen@Sun.COM 		*virt_addr = iop->virt_rsrc_address;
1669*7850SVuong.Nguyen@Sun.COM 		*did = iop->domain_id;
1670*7850SVuong.Nguyen@Sun.COM 		if (name == NULL || name_len <= 0)
1671*7850SVuong.Nguyen@Sun.COM 			break;
1672*7850SVuong.Nguyen@Sun.COM 		*name = '\0';
1673*7850SVuong.Nguyen@Sun.COM 		if (resplen > offset) {
1674*7850SVuong.Nguyen@Sun.COM 			(void) strncpy(name, (char *)((ptrdiff_t)resp + offset),
1675*7850SVuong.Nguyen@Sun.COM 			    name_len);
1676*7850SVuong.Nguyen@Sun.COM 		}
1677*7850SVuong.Nguyen@Sun.COM 		break;
1678*7850SVuong.Nguyen@Sun.COM 	default:
1679*7850SVuong.Nguyen@Sun.COM 		rc = -1;
1680*7850SVuong.Nguyen@Sun.COM 		break;
1681*7850SVuong.Nguyen@Sun.COM 	}
1682*7850SVuong.Nguyen@Sun.COM 
1683*7850SVuong.Nguyen@Sun.COM 	lhp->freep(resp, resplen);
1684*7850SVuong.Nguyen@Sun.COM 	return (rc);
1685*7850SVuong.Nguyen@Sun.COM }
1686*7850SVuong.Nguyen@Sun.COM 
16872072Svn83148 /* end file */
1688