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