xref: /onnv-gate/usr/src/lib/fm/libldom/sparc/ldmsvcs_utils.c (revision 8634:cf404c445bbc)
12072Svn83148 /*
22072Svn83148  * CDDL HEADER START
32072Svn83148  *
42072Svn83148  * The contents of this file are subject to the terms of the
52072Svn83148  * Common Development and Distribution License (the "License").
62072Svn83148  * You may not use this file except in compliance with the License.
72072Svn83148  *
82072Svn83148  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92072Svn83148  * or http://www.opensolaris.org/os/licensing.
102072Svn83148  * See the License for the specific language governing permissions
112072Svn83148  * and limitations under the License.
122072Svn83148  *
132072Svn83148  * When distributing Covered Code, include this CDDL HEADER in each
142072Svn83148  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152072Svn83148  * If applicable, add the following below this CDDL HEADER, with the
162072Svn83148  * fields enclosed by brackets "[]" replaced with your own identifying
172072Svn83148  * information: Portions Copyright [yyyy] [name of copyright owner]
182072Svn83148  *
192072Svn83148  * CDDL HEADER END
202072Svn83148  */
212072Svn83148 /*
22*8634SVuong.Nguyen@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
232072Svn83148  * Use is subject to license terms.
242072Svn83148  */
252072Svn83148 
262072Svn83148 #include <stdlib.h>
272072Svn83148 #include <stdio.h>
287850SVuong.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>
42*8634SVuong.Nguyen@Sun.COM #include <signal.h>
434358Srb144127 #include <values.h>
444358Srb144127 #include <libscf.h>
452072Svn83148 
467850SVuong.Nguyen@Sun.COM #include <ctype.h>
477850SVuong.Nguyen@Sun.COM 
482072Svn83148 #include "ldmsvcs_utils.h"
49*8634SVuong.Nguyen@Sun.COM #include "ldom_alloc.h"
502072Svn83148 
512072Svn83148 #define	ASSERT(cnd) \
522072Svn83148 	((void) ((cnd) || ((void) fprintf(stderr, \
532072Svn83148 		"assertion failure in %s:%d: %s\n", \
542072Svn83148 		__FILE__, __LINE__, #cnd), 0)))
552072Svn83148 
562072Svn83148 #define	FDS_VLDC \
572072Svn83148 	"/devices/virtual-devices@100/channel-devices@200/" \
582072Svn83148 	"/virtual-channel-client@1:ldmfma"
592072Svn83148 
604358Srb144127 /* allow timeouts in sec that are nearly forever but small enough for an int */
614358Srb144127 #define	LDM_TIMEOUT_CEILING	(MAXINT / 2)
622072Svn83148 
632072Svn83148 #define	MIN(x, y)	((x) < (y) ? (x) : (y))
642072Svn83148 
652072Svn83148 /*
662072Svn83148  * functions in this file are for version 1.0 of FMA domain services
672072Svn83148  */
682072Svn83148 static ds_ver_t ds_vers[] = {
692072Svn83148 	{ 1, 0 }
702072Svn83148 };
712072Svn83148 
722072Svn83148 #define	DS_NUM_VER	(sizeof (ds_vers) / sizeof (ds_ver_t))
732072Svn83148 
742072Svn83148 /*
752072Svn83148  * information for each channel
762072Svn83148  */
772072Svn83148 struct ldmsvcs_info {
782072Svn83148 	pthread_mutex_t mt;
792072Svn83148 	pthread_cond_t cv;
802072Svn83148 	fds_channel_t fds_chan;
812072Svn83148 	fds_reg_svcs_t fmas_svcs;
822072Svn83148 	int cv_twait;
832072Svn83148 };
842072Svn83148 
852072Svn83148 /*
862072Svn83148  * struct listdata_s and struct poller_s are used to maintain the state of
872072Svn83148  * the poller thread.  this thread is used to manage incoming messages and
882072Svn83148  * pass those messages onto the correct requesting thread.  see the "poller
892072Svn83148  * functions" section for more details.
902072Svn83148  */
912072Svn83148 struct listdata_s {
922072Svn83148 	enum {
932072Svn83148 		UNUSED,
942072Svn83148 		PENDING,
952072Svn83148 		ARRIVED
962072Svn83148 	} status;
972072Svn83148 	uint64_t req_num;
982072Svn83148 	int fd;
992072Svn83148 	size_t datalen;
1002072Svn83148 };
1012072Svn83148 
1022072Svn83148 static struct poller_s {
1032072Svn83148 	pthread_mutex_t mt;
1042072Svn83148 	pthread_cond_t cv;
1052072Svn83148 	pthread_t polling_tid;
1062072Svn83148 	int doreset;
1072072Svn83148 	int doexit;
1082072Svn83148 	int nclients;
1092072Svn83148 	struct listdata_s **list;
1102072Svn83148 	int list_len;
1112072Svn83148 	int pending_count;
1122072Svn83148 } pollbase = {
1132072Svn83148 	PTHREAD_MUTEX_INITIALIZER,
1142072Svn83148 	PTHREAD_COND_INITIALIZER,
1152072Svn83148 	0,
1162072Svn83148 	1,
1172072Svn83148 	0,
1182072Svn83148 	0,
1192072Svn83148 	NULL,
1202072Svn83148 	0,
1212072Svn83148 	0
1222072Svn83148 };
1232072Svn83148 
1242072Svn83148 
1252072Svn83148 static struct ldmsvcs_info *channel_init(struct ldom_hdl *lhp);
1262072Svn83148 static int channel_openreset(struct ldmsvcs_info *lsp);
1272072Svn83148 static int read_msg(struct ldmsvcs_info *lsp);
1282072Svn83148 
1294358Srb144127 static int
1304358Srb144127 get_smf_int_val(char *prop_nm, int min, int max, int default_val)
1314358Srb144127 {
1324358Srb144127 	scf_simple_prop_t	*prop;		/* SMF property */
1334358Srb144127 	int64_t			*valp;		/* prop value ptr */
1344358Srb144127 	int64_t			val;		/* prop value to return */
1354358Srb144127 
1364358Srb144127 	val = default_val;
1374358Srb144127 	if ((prop = scf_simple_prop_get(NULL, LDM_SVC_NM, LDM_PROP_GROUP_NM,
1384358Srb144127 	    prop_nm)) != NULL) {
1394358Srb144127 		if ((valp = scf_simple_prop_next_integer(prop)) != NULL) {
1404358Srb144127 			val = *valp;
1414358Srb144127 			if (val < min)
1424358Srb144127 				val = min;
1434358Srb144127 			else if (val > max)
1444358Srb144127 				val = max;
1454358Srb144127 		}
1464358Srb144127 		scf_simple_prop_free(prop);
1474358Srb144127 	}
1484358Srb144127 	return ((int)val);
1494358Srb144127 }
1504358Srb144127 
1512072Svn83148 static void
1522072Svn83148 channel_close(struct ldmsvcs_info *lsp)
1532072Svn83148 {
1542072Svn83148 	(void) pthread_mutex_lock(&lsp->mt);
1552072Svn83148 
1563327Svn83148 	if (lsp->fds_chan.state == CHANNEL_OPEN ||
1574358Srb144127 	    lsp->fds_chan.state == CHANNEL_READY) {
1583327Svn83148 		(void) close(lsp->fds_chan.fd);
1594358Srb144127 		lsp->cv_twait = get_smf_int_val(LDM_INIT_TO_PROP_NM,
1604358Srb144127 		    0, LDM_TIMEOUT_CEILING, LDM_INIT_WAIT_TIME);
1613327Svn83148 		lsp->fds_chan.state = CHANNEL_CLOSED;
1623327Svn83148 	}
1632072Svn83148 
1642072Svn83148 	(void) pthread_mutex_unlock(&lsp->mt);
1652072Svn83148 }
1662072Svn83148 
1672072Svn83148 /*
1682072Svn83148  * read size bytes of data from a streaming fd into buf
1692072Svn83148  */
1702072Svn83148 static int
1712072Svn83148 read_stream(int fd, void *buf, size_t size)
1722072Svn83148 {
1732072Svn83148 	pollfd_t pollfd;
1742072Svn83148 	ssize_t rv;
1752072Svn83148 	size_t data_left;
1762072Svn83148 	ptrdiff_t currentp;
1772072Svn83148 
1782072Svn83148 	pollfd.events = POLLIN;
1792072Svn83148 	pollfd.revents = 0;
1802072Svn83148 	pollfd.fd = fd;
1812072Svn83148 
1822072Svn83148 	currentp = (ptrdiff_t)buf;
1832072Svn83148 	data_left = size;
1842072Svn83148 
1852072Svn83148 	/*
1862072Svn83148 	 * data may come in bits and pieces
1872072Svn83148 	 */
1882072Svn83148 	do {
1892072Svn83148 		if ((rv = read(fd, (void *)currentp, data_left)) < 0) {
1902072Svn83148 			if (errno == EAGAIN && poll(&pollfd, 1, -1) > 0)
1912072Svn83148 				continue;	/* retry */
1922072Svn83148 			else
1932072Svn83148 				return (1);
1942072Svn83148 		}
1952072Svn83148 
1962072Svn83148 		data_left -= rv;
1972072Svn83148 		currentp += rv;
1982072Svn83148 	} while (data_left > 0);
1992072Svn83148 
2002072Svn83148 	return (0);
2012072Svn83148 }
2022072Svn83148 
2032072Svn83148 
2042072Svn83148 /*
2052072Svn83148  * poller functions
2062072Svn83148  *
2072072Svn83148  * at init time, a thread is created for the purpose of monitoring incoming
2082072Svn83148  * messages and doing one of the following:
2092072Svn83148  *
2102072Svn83148  * 1. doing the initial handshake and version negotiation
2112072Svn83148  *
2122072Svn83148  * 2. handing incoming data off to the requesting thread (which is an fmd
2132072Svn83148  * module or scheme thread)
2142072Svn83148  */
2152072Svn83148 static int
2162072Svn83148 poller_handle_data(int fd, size_t payloadsize)
2172072Svn83148 {
2182072Svn83148 	uint64_t *req_num;
2192072Svn83148 	void *pr;
2202072Svn83148 	size_t prlen;
2212072Svn83148 	int i;
2222072Svn83148 
2232072Svn83148 	prlen = sizeof (ds_data_handle_t) + sizeof (uint64_t);
2242072Svn83148 
2252072Svn83148 	if (payloadsize < prlen)
2262072Svn83148 		return (1);
2272072Svn83148 
2282072Svn83148 	pr = alloca(prlen);
2292072Svn83148 
2302072Svn83148 	if (read_stream(fd, pr, prlen) != 0)
2312072Svn83148 		return (1);
2322072Svn83148 
2332072Svn83148 	req_num = (uint64_t *)((ptrdiff_t)pr + sizeof (ds_data_handle_t));
2342072Svn83148 
2352072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
2362072Svn83148 
2372072Svn83148 	for (i = 0; i < pollbase.list_len; i++) {
2382072Svn83148 		if (pollbase.list[i]->req_num == *req_num) {
2392072Svn83148 			ASSERT(pollbase.list[i]->status == PENDING);
2402072Svn83148 
2412072Svn83148 			pollbase.list[i]->status = ARRIVED;
2422072Svn83148 			pollbase.list[i]->fd = fd;
2432072Svn83148 			pollbase.list[i]->datalen = payloadsize - prlen;
2442072Svn83148 
2452072Svn83148 			pollbase.pending_count--;
2462072Svn83148 			(void) pthread_cond_broadcast(&pollbase.cv);
2472072Svn83148 			break;
2482072Svn83148 		}
2492072Svn83148 	}
2502072Svn83148 
2512072Svn83148 	/*
2522072Svn83148 	 * now wait for receiving thread to read in the data
2532072Svn83148 	 */
2542072Svn83148 	if (i < pollbase.list_len) {
2552072Svn83148 		while (pollbase.list[i]->status == ARRIVED)
2562072Svn83148 			(void) pthread_cond_wait(&pollbase.cv, &pollbase.mt);
2572072Svn83148 	}
2582072Svn83148 
2592072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
2602072Svn83148 
2612072Svn83148 	return (0);
2622072Svn83148 }
2632072Svn83148 
2642072Svn83148 
2652072Svn83148 /*
2662072Svn83148  * note that this function is meant to handle only DS_DATA messages
2672072Svn83148  */
2682072Svn83148 static int
2692072Svn83148 poller_recv_data(struct ldom_hdl *lhp, uint64_t req_num, int index,
2702072Svn83148 		void **resp, size_t *resplen)
2712072Svn83148 {
2722072Svn83148 	struct timespec twait;
2732072Svn83148 	int ier;
2742072Svn83148 
2752072Svn83148 	ier = 0;
2762072Svn83148 	twait.tv_sec = time(NULL) + lhp->lsinfo->cv_twait;
2772072Svn83148 	twait.tv_nsec = 0;
2782072Svn83148 
2792072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
2802072Svn83148 
2812072Svn83148 	ASSERT(pollbase.list[index]->req_num == req_num);
2822072Svn83148 
2832072Svn83148 	while (pollbase.list[index]->status == PENDING &&
2842072Svn83148 	    pollbase.doreset == 0 && ier == 0)
2852072Svn83148 		ier = pthread_cond_timedwait(&pollbase.cv, &pollbase.mt,
2864358Srb144127 		    &twait);
2872072Svn83148 
2882072Svn83148 	if (ier == 0) {
2892072Svn83148 		if (pollbase.doreset == 0) {
2902072Svn83148 			ASSERT(pollbase.list[index]->status == ARRIVED);
2912072Svn83148 
2922072Svn83148 			/*
2932072Svn83148 			 * need to add req_num to beginning of resp
2942072Svn83148 			 */
2952072Svn83148 			*resplen = pollbase.list[index]->datalen +
2964358Srb144127 			    sizeof (uint64_t);
2972072Svn83148 			*resp = lhp->allocp(*resplen);
2982072Svn83148 			*((uint64_t *)*resp) = req_num;
2992072Svn83148 
3002072Svn83148 			if (read_stream(pollbase.list[index]->fd,
3014358Srb144127 			    (void *)((ptrdiff_t)*resp + sizeof (uint64_t)),
3024358Srb144127 			    *resplen - sizeof (uint64_t)) != 0)
3032072Svn83148 				ier = ETIMEDOUT;
3042072Svn83148 
3052072Svn83148 			pollbase.list[index]->status = UNUSED;
3062072Svn83148 			pollbase.list[index]->req_num = 0;
3072072Svn83148 			(void) pthread_cond_broadcast(&pollbase.cv);
3082072Svn83148 		} else {
3092072Svn83148 			if (--(pollbase.pending_count) == 0)
3102072Svn83148 				(void) pthread_cond_broadcast(&pollbase.cv);
3112072Svn83148 		}
3122072Svn83148 	}
3132072Svn83148 
3142072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
3152072Svn83148 
3162072Svn83148 	ASSERT(ier == 0 || ier == ETIMEDOUT);
3172072Svn83148 
3182072Svn83148 	return (ier);
3192072Svn83148 }
3202072Svn83148 
3212072Svn83148 
3222072Svn83148 static void
3232072Svn83148 poller_add_client(void)
3242072Svn83148 {
3252072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
3262072Svn83148 	pollbase.nclients++;
3272072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
3282072Svn83148 }
3292072Svn83148 
3302072Svn83148 
3312072Svn83148 static void
3322072Svn83148 poller_remove_client(void)
3332072Svn83148 {
3342072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
3352072Svn83148 	pollbase.nclients--;
3362072Svn83148 	ASSERT(pollbase.nclients >= 0);
3372072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
3382072Svn83148 }
3392072Svn83148 
3402072Svn83148 
3412072Svn83148 static int
342*8634SVuong.Nguyen@Sun.COM poller_add_pending(uint64_t req_num)
3432072Svn83148 {
3442072Svn83148 	int newlen, index, i, j;
3452072Svn83148 
3462072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
3472072Svn83148 	pollbase.pending_count++;
3482072Svn83148 
3492072Svn83148 	for (j = 0, index = -1; j < 2 && index == -1; j++) {
3502072Svn83148 		for (i = 0; i < pollbase.list_len; i++) {
3512072Svn83148 			if (pollbase.list[i]->status == UNUSED) {
3522072Svn83148 				pollbase.list[i]->status = PENDING;
3532072Svn83148 				pollbase.list[i]->req_num = req_num;
3542072Svn83148 				pollbase.list[i]->datalen = 0;
3552072Svn83148 				index = i;
3562072Svn83148 				break;
3572072Svn83148 			}
3582072Svn83148 		}
3592072Svn83148 
3602072Svn83148 		if (index == -1) {
3612072Svn83148 			struct listdata_s **newlist, **oldlist;
3622072Svn83148 
3632072Svn83148 			/*
3642072Svn83148 			 * get to this point if list is not long enough.
3652072Svn83148 			 * check for a runaway list.  since requests are
3662072Svn83148 			 * synchronous (clients send a request and need to
3672072Svn83148 			 * wait for the result before returning) the size
3682072Svn83148 			 * of the list cannot be much more than the number
3692072Svn83148 			 * of clients.
3702072Svn83148 			 */
3712072Svn83148 			ASSERT(pollbase.list_len < pollbase.nclients + 1);
3722072Svn83148 
3732072Svn83148 			newlen = pollbase.list_len + 5;
374*8634SVuong.Nguyen@Sun.COM 			newlist = ldom_alloc(newlen *
375*8634SVuong.Nguyen@Sun.COM 			    sizeof (struct listdata_s *));
3762072Svn83148 
3772072Svn83148 			for (i = 0; i < pollbase.list_len; i++)
3782072Svn83148 				newlist[i] = pollbase.list[i];
3792072Svn83148 
3802072Svn83148 			oldlist = pollbase.list;
3812072Svn83148 			pollbase.list = newlist;
382*8634SVuong.Nguyen@Sun.COM 			ldom_free(oldlist, pollbase.list_len *
383*8634SVuong.Nguyen@Sun.COM 			    sizeof (struct listdata_s *));
3842072Svn83148 
3852072Svn83148 			for (i = pollbase.list_len; i < newlen; i++) {
3862072Svn83148 				pollbase.list[i] =
387*8634SVuong.Nguyen@Sun.COM 				    ldom_alloc(sizeof (struct listdata_s));
3882072Svn83148 				pollbase.list[i]->status = UNUSED;
3892072Svn83148 			}
3902072Svn83148 
3912072Svn83148 			pollbase.list_len = newlen;
3922072Svn83148 		}
3932072Svn83148 	}
3942072Svn83148 
3952072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
3962072Svn83148 	ASSERT(index != -1);
3972072Svn83148 
3982072Svn83148 	return (index);
3992072Svn83148 }
4002072Svn83148 
4012072Svn83148 
4022072Svn83148 static void
4032072Svn83148 poller_delete_pending(uint64_t req_num, int index)
4042072Svn83148 {
4052072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
4062072Svn83148 
4072072Svn83148 	ASSERT(pollbase.list[index]->req_num == req_num);
4082072Svn83148 	pollbase.list[index]->status = UNUSED;
4092072Svn83148 
4102072Svn83148 	if (--(pollbase.pending_count) == 0 && pollbase.doreset == 1)
4112072Svn83148 		(void) pthread_cond_broadcast(&pollbase.cv);
4122072Svn83148 
4132072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
4142072Svn83148 }
4152072Svn83148 
4162072Svn83148 
4172072Svn83148 static void
418*8634SVuong.Nguyen@Sun.COM poller_shutdown(boolean_t wait)
4192072Svn83148 {
4202072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
4212072Svn83148 
4222072Svn83148 	pollbase.doexit = 1;
4232072Svn83148 
4242072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
425*8634SVuong.Nguyen@Sun.COM 
426*8634SVuong.Nguyen@Sun.COM 	if (wait == B_TRUE) {
427*8634SVuong.Nguyen@Sun.COM 		/* stop the poller thread  and wait for it to end */
428*8634SVuong.Nguyen@Sun.COM 		(void) pthread_kill(pollbase.polling_tid, SIGTERM);
429*8634SVuong.Nguyen@Sun.COM 		(void) pthread_join(pollbase.polling_tid, NULL);
430*8634SVuong.Nguyen@Sun.COM 	}
4312072Svn83148 }
4322072Svn83148 
4332072Svn83148 
4342072Svn83148 /*
4352072Svn83148  * perform the polling of incoming messages.  manage any resets (usually
4362072Svn83148  * due to one end of the connection being closed) as well as exit
4372072Svn83148  * conditions.
4382072Svn83148  */
4392072Svn83148 static void *
4402072Svn83148 poller_loop(void *arg)
4412072Svn83148 {
4422072Svn83148 	struct ldmsvcs_info *lsp;
4432072Svn83148 	pollfd_t pollfd;
4442072Svn83148 	int ier;
4452072Svn83148 
4462072Svn83148 	lsp = (struct ldmsvcs_info *)arg;
4472072Svn83148 
4482072Svn83148 	for (;;) {
4492072Svn83148 		(void) pthread_mutex_lock(&pollbase.mt);
4502072Svn83148 
4512072Svn83148 		if (pollbase.doexit) {
4522072Svn83148 			(void) pthread_mutex_unlock(&pollbase.mt);
4532072Svn83148 			break;
4542072Svn83148 		}
4552072Svn83148 
4562072Svn83148 		if (pollbase.doreset) {
4572072Svn83148 			int i;
4582072Svn83148 
4592072Svn83148 			while (pollbase.pending_count > 0)
4602072Svn83148 				(void) pthread_cond_wait(&pollbase.cv,
4614358Srb144127 				    &pollbase.mt);
4622072Svn83148 
4632072Svn83148 			ASSERT(pollbase.pending_count == 0);
4642072Svn83148 			for (i = 0; i < pollbase.list_len; i++)
4652072Svn83148 				pollbase.list[i]->status = UNUSED;
4662072Svn83148 
4672072Svn83148 			pollbase.doreset = 0;
4682072Svn83148 		}
4692072Svn83148 		(void) pthread_mutex_unlock(&pollbase.mt);
4702072Svn83148 
4712072Svn83148 		if ((ier = channel_openreset(lsp)) == 1) {
4722072Svn83148 			continue;
4732072Svn83148 		} else if (ier == 2) {
4742072Svn83148 			/*
4752072Svn83148 			 * start exit preparations
4762072Svn83148 			 */
477*8634SVuong.Nguyen@Sun.COM 			poller_shutdown(B_FALSE);
4782072Svn83148 			continue;
4792072Svn83148 		}
4802072Svn83148 
4812072Svn83148 		pollfd.events = POLLIN;
4822072Svn83148 		pollfd.revents = 0;
4832072Svn83148 		pollfd.fd = lsp->fds_chan.fd;
4842072Svn83148 
4852072Svn83148 		if (poll(&pollfd, 1, -1) <= 0 || read_msg(lsp) != 0) {
4862072Svn83148 			/*
4872072Svn83148 			 * read error and/or fd got closed
4882072Svn83148 			 */
4892072Svn83148 			(void) pthread_mutex_lock(&pollbase.mt);
4902072Svn83148 			pollbase.doreset = 1;
4912072Svn83148 			(void) pthread_mutex_unlock(&pollbase.mt);
4922072Svn83148 
4932072Svn83148 			channel_close(lsp);
4942072Svn83148 		}
4952072Svn83148 	}
4962072Svn83148 
4972072Svn83148 	return (NULL);
4982072Svn83148 }
4992072Svn83148 
5002072Svn83148 
5012072Svn83148 /*
5022072Svn83148  * create the polling thread
5032072Svn83148  */
5042072Svn83148 static int
5052072Svn83148 poller_init(struct ldmsvcs_info *lsp)
5062072Svn83148 {
5072072Svn83148 	int rc = 0;
5082072Svn83148 
5092072Svn83148 	(void) pthread_mutex_lock(&pollbase.mt);
5102072Svn83148 
5112072Svn83148 	if (pollbase.polling_tid == 0) {
512*8634SVuong.Nguyen@Sun.COM 		pthread_attr_t *attr = NULL;
5132072Svn83148 
5142072Svn83148 		/*
515*8634SVuong.Nguyen@Sun.COM 		 * create a joinable polling thread for receiving messages
5162072Svn83148 		 */
517*8634SVuong.Nguyen@Sun.COM 		if (pthread_create(&pollbase.polling_tid, attr,
5184358Srb144127 		    poller_loop, lsp) != 0)
5192072Svn83148 			rc = 1;
5202072Svn83148 	}
5212072Svn83148 
5222072Svn83148 	(void) pthread_mutex_unlock(&pollbase.mt);
5232072Svn83148 
5242072Svn83148 	return (rc);
5252072Svn83148 }
5262072Svn83148 
527*8634SVuong.Nguyen@Sun.COM /*
528*8634SVuong.Nguyen@Sun.COM  * Cleanup the polling thread
529*8634SVuong.Nguyen@Sun.COM  */
530*8634SVuong.Nguyen@Sun.COM static void
531*8634SVuong.Nguyen@Sun.COM poller_fini(void)
532*8634SVuong.Nguyen@Sun.COM {
533*8634SVuong.Nguyen@Sun.COM 	int i;
534*8634SVuong.Nguyen@Sun.COM 
535*8634SVuong.Nguyen@Sun.COM 	/* stop the poller thread */
536*8634SVuong.Nguyen@Sun.COM 	poller_shutdown(B_TRUE);
537*8634SVuong.Nguyen@Sun.COM 
538*8634SVuong.Nguyen@Sun.COM 	(void) pthread_mutex_lock(&pollbase.mt);
539*8634SVuong.Nguyen@Sun.COM 
540*8634SVuong.Nguyen@Sun.COM 	/* Free up the list of outstanding requests */
541*8634SVuong.Nguyen@Sun.COM 	if (pollbase.list != NULL) {
542*8634SVuong.Nguyen@Sun.COM 		for (i = 0; i < pollbase.list_len; i++) {
543*8634SVuong.Nguyen@Sun.COM 			if (pollbase.list[i]) {
544*8634SVuong.Nguyen@Sun.COM 				ldom_free(pollbase.list[i],
545*8634SVuong.Nguyen@Sun.COM 				    sizeof (struct listdata_s));
546*8634SVuong.Nguyen@Sun.COM 			}
547*8634SVuong.Nguyen@Sun.COM 		}
548*8634SVuong.Nguyen@Sun.COM 		ldom_free(pollbase.list, pollbase.list_len *
549*8634SVuong.Nguyen@Sun.COM 		    sizeof (struct listdata_s *));
550*8634SVuong.Nguyen@Sun.COM 		pollbase.list = NULL;
551*8634SVuong.Nguyen@Sun.COM 		pollbase.list_len = 0;
552*8634SVuong.Nguyen@Sun.COM 	}
553*8634SVuong.Nguyen@Sun.COM 
554*8634SVuong.Nguyen@Sun.COM 	(void) pthread_mutex_unlock(&pollbase.mt);
555*8634SVuong.Nguyen@Sun.COM }
5562072Svn83148 
5572072Svn83148 /*
5582072Svn83148  * utilities for message handlers
5592072Svn83148  */
5602072Svn83148 static int
5612072Svn83148 fds_send(struct ldmsvcs_info *lsp, void *msg, size_t msglen)
5622072Svn83148 {
5632072Svn83148 	static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER;
5642072Svn83148 
5652072Svn83148 	(void) pthread_mutex_lock(&mt);
5662072Svn83148 
5672072Svn83148 	if (write(lsp->fds_chan.fd, msg, msglen) != msglen) {
5682072Svn83148 		channel_close(lsp);
5692072Svn83148 		(void) pthread_mutex_unlock(&mt);
5702072Svn83148 		return (ETIMEDOUT);
5712072Svn83148 	}
5722072Svn83148 
5732072Svn83148 	(void) pthread_mutex_unlock(&mt);
5742072Svn83148 	return (0);
5752072Svn83148 }
5762072Svn83148 
5772072Svn83148 
5782072Svn83148 /*
5792072Svn83148  * Find the max and min version supported
5802072Svn83148  */
5812072Svn83148 static void
5822072Svn83148 fds_min_max_versions(uint16_t *min_major, uint16_t *max_major)
5832072Svn83148 {
5842072Svn83148 	int i;
5852072Svn83148 
5862072Svn83148 	*min_major = ds_vers[0].major;
5872072Svn83148 	*max_major = *min_major;
5882072Svn83148 
5892072Svn83148 	for (i = 1; i < DS_NUM_VER; i++) {
5902072Svn83148 		if (ds_vers[i].major < *min_major)
5912072Svn83148 			*min_major = ds_vers[i].major;
5922072Svn83148 
5932072Svn83148 		if (ds_vers[i].major > *max_major)
5942072Svn83148 			*max_major = ds_vers[i].major;
5952072Svn83148 	}
5962072Svn83148 }
5972072Svn83148 
5982072Svn83148 /*
5992072Svn83148  * check whether the major and minor numbers requested by remote ds client
6002072Svn83148  * can be satisfied.  if the requested major is supported, true is
6012072Svn83148  * returned, and the agreed minor is returned in new_minor.  if the
6022072Svn83148  * requested major is not supported, the routine returns false, and the
6032072Svn83148  * closest major is returned in *new_major, upon which the ds client should
6042072Svn83148  * renegotiate.  the closest major is the just lower that the requested
6052072Svn83148  * major number.
6062072Svn83148  */
6072072Svn83148 static boolean_t
6082072Svn83148 fds_negotiate_version(uint16_t req_major, uint16_t *new_majorp,
6092072Svn83148     uint16_t *new_minorp)
6102072Svn83148 {
6112072Svn83148 	int i = 0;
6122072Svn83148 	uint16_t major, lower_major;
6132072Svn83148 	uint16_t min_major, max_major;
6142072Svn83148 	boolean_t found_match = B_FALSE;
6152072Svn83148 
6162072Svn83148 	fds_min_max_versions(&min_major, &max_major);
6172072Svn83148 
6182072Svn83148 	/*
6192072Svn83148 	 * if the minimum version supported is greater than the version
6202072Svn83148 	 * requested, return the lowest version supported
6212072Svn83148 	 */
6222072Svn83148 	if (min_major > req_major) {
6232072Svn83148 		*new_majorp = min_major;
6242072Svn83148 		return (B_FALSE);
6252072Svn83148 	}
6262072Svn83148 
6272072Svn83148 	/*
6282072Svn83148 	 * if the largest version supported is lower than the version
6292072Svn83148 	 * requested, return the largest version supported
6302072Svn83148 	 */
6312072Svn83148 	if (max_major < req_major) {
6322072Svn83148 		*new_majorp = max_major;
6332072Svn83148 		return (B_FALSE);
6342072Svn83148 	}
6352072Svn83148 
6362072Svn83148 	/*
6372072Svn83148 	 * now we know that the requested version lies between the min and
6382072Svn83148 	 * max versions supported.  check if the requested major can be
6392072Svn83148 	 * found in supported versions.
6402072Svn83148 	 */
6412072Svn83148 	lower_major = min_major;
6422072Svn83148 	for (i = 0; i < DS_NUM_VER; i++) {
6432072Svn83148 		major = ds_vers[i].major;
6442072Svn83148 		if (major == req_major) {
6452072Svn83148 			found_match = B_TRUE;
6462072Svn83148 			*new_minorp = ds_vers[i].minor;
6472072Svn83148 			*new_majorp = major;
6482072Svn83148 			break;
6492072Svn83148 		} else if ((major < req_major) && (major > lower_major))
6502072Svn83148 			lower_major = major;
6512072Svn83148 	}
6522072Svn83148 
6532072Svn83148 	/*
6542072Svn83148 	 * If  no match is found, return the closest available number
6552072Svn83148 	 */
6562072Svn83148 	if (!found_match)
6572072Svn83148 		*new_majorp = lower_major;
6582072Svn83148 
6592072Svn83148 	return (found_match);
6602072Svn83148 }
6612072Svn83148 
6622072Svn83148 
6632072Svn83148 /*
6642072Svn83148  * return 0 if service is added; 1 if service is a duplicate
6652072Svn83148  */
6662072Svn83148 static int
6672072Svn83148 fds_svc_add(struct ldmsvcs_info *lsp, ds_reg_req_t *req, int minor)
6682072Svn83148 {
6692072Svn83148 	fds_svc_t *svc;
6702072Svn83148 	int i, rc;
6712072Svn83148 
6722072Svn83148 	svc = NULL;
6732072Svn83148 	for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
6742072Svn83148 		if (strcmp(lsp->fmas_svcs.tbl[i]->name, req->svc_id) == 0) {
6752072Svn83148 			svc = lsp->fmas_svcs.tbl[i];
6762072Svn83148 			break;
6772072Svn83148 		}
6782072Svn83148 	}
6792072Svn83148 
6802072Svn83148 	if (svc == NULL)
6812072Svn83148 		return (0);	/* we don't need this service */
6822072Svn83148 
6832072Svn83148 	(void) pthread_mutex_lock(&lsp->fmas_svcs.mt);
6842072Svn83148 
6852072Svn83148 	/*
6862072Svn83148 	 * duplicate registration is OK --- we retain the previous entry
6872072Svn83148 	 * (which has not been unregistered anyway)
6882072Svn83148 	 */
6892072Svn83148 	if (svc->state == DS_SVC_ACTIVE) {
6902072Svn83148 		rc = 1;
6912072Svn83148 	} else {
6922072Svn83148 		svc->state = DS_SVC_ACTIVE;
6932072Svn83148 		svc->hdl = req->svc_handle;
6942072Svn83148 		svc->ver.major = req->major_vers;
6952072Svn83148 		svc->ver.minor = minor;
6962072Svn83148 
6972072Svn83148 		rc = 0;
6982072Svn83148 		(void) pthread_cond_broadcast(&lsp->fmas_svcs.cv);
6992072Svn83148 	}
7002072Svn83148 
7012072Svn83148 	(void) pthread_mutex_unlock(&lsp->fmas_svcs.mt);
7022072Svn83148 
7032072Svn83148 	return (rc);
7042072Svn83148 }
7052072Svn83148 
7062072Svn83148 
7072072Svn83148 static void
7082072Svn83148 fds_svc_reset(struct ldmsvcs_info *lsp, int index)
7092072Svn83148 {
7102072Svn83148 	int i, start, end;
7112072Svn83148 
7122072Svn83148 	if (index >= 0) {
7132072Svn83148 		start = index;
7142072Svn83148 		end = index + 1;
7152072Svn83148 	} else {
7162072Svn83148 		start = 0;
7172072Svn83148 		end = lsp->fmas_svcs.nsvcs;
7182072Svn83148 	}
7192072Svn83148 
7202072Svn83148 	(void) pthread_mutex_lock(&lsp->fmas_svcs.mt);
7212072Svn83148 
7222072Svn83148 	for (i = start; i < end; i++) {
7232072Svn83148 		lsp->fmas_svcs.tbl[i]->hdl = 0;
7242072Svn83148 		lsp->fmas_svcs.tbl[i]->state = DS_SVC_INVAL;
7252072Svn83148 		lsp->fmas_svcs.tbl[i]->ver.major =
7264358Srb144127 		    ds_vers[DS_NUM_VER - 1].major;
7272072Svn83148 		lsp->fmas_svcs.tbl[i]->ver.minor =
7284358Srb144127 		    ds_vers[DS_NUM_VER - 1].minor;
7292072Svn83148 	}
7302072Svn83148 
7312072Svn83148 	(void) pthread_mutex_unlock(&lsp->fmas_svcs.mt);
7322072Svn83148 }
7332072Svn83148 
7342072Svn83148 
7352072Svn83148 static int
7362072Svn83148 fds_svc_remove(struct ldmsvcs_info *lsp, ds_svc_hdl_t svc_handle)
7372072Svn83148 {
7382072Svn83148 	int i;
7392072Svn83148 
7402072Svn83148 	for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
7412072Svn83148 		if (lsp->fmas_svcs.tbl[i]->hdl == svc_handle) {
7422072Svn83148 			fds_svc_reset(lsp, i);
7432072Svn83148 			return (0);
7442072Svn83148 		}
7452072Svn83148 	}
7462072Svn83148 
7472072Svn83148 	return (1);
7482072Svn83148 }
7492072Svn83148 
7502072Svn83148 
7512072Svn83148 /*
7522072Svn83148  * message handlers
7532072Svn83148  */
7542072Svn83148 /*ARGSUSED*/
7552072Svn83148 static void
7562072Svn83148 ds_handle_msg_noop(struct ldmsvcs_info *lsp, void *buf, size_t len)
7572072Svn83148 {
7582072Svn83148 }
7592072Svn83148 
7602072Svn83148 static void
7612072Svn83148 ds_handle_init_req(struct ldmsvcs_info *lsp, void *buf, size_t len)
7622072Svn83148 {
7632072Svn83148 	ds_init_req_t *req;
7642072Svn83148 	uint16_t new_major, new_minor;
7652072Svn83148 	size_t msglen;
7662072Svn83148 
7672072Svn83148 	req = (ds_init_req_t *)buf;
7682072Svn83148 
7692072Svn83148 	/* sanity check the incoming message */
7702072Svn83148 	if (len != sizeof (ds_init_req_t)) {
7712072Svn83148 		channel_close(lsp);
7722072Svn83148 		return;
7732072Svn83148 	}
7742072Svn83148 
7752072Svn83148 	/*
7762072Svn83148 	 * Check version info. ACK only if the major numbers exactly
7772072Svn83148 	 * match. The service entity can retry with a new minor
7782072Svn83148 	 * based on the response sent as part of the NACK.
7792072Svn83148 	 */
7802072Svn83148 	if (fds_negotiate_version(req->major_vers, &new_major, &new_minor)) {
7812072Svn83148 		ds_hdr_t *H;
7822072Svn83148 		ds_init_ack_t *R;
7832072Svn83148 
7842072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_init_ack_t);
7852072Svn83148 		H = alloca(msglen);
7862072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
7872072Svn83148 
7882072Svn83148 		H->msg_type = DS_INIT_ACK;
7892072Svn83148 		H->payload_len = sizeof (ds_init_ack_t);
7902072Svn83148 		R->minor_vers = MIN(new_minor, req->minor_vers);
7912072Svn83148 
7922072Svn83148 		if (fds_send(lsp, H, msglen) != 0)
7932072Svn83148 			return;
7942072Svn83148 
7952072Svn83148 		(void) pthread_mutex_lock(&lsp->mt);
7962072Svn83148 		ASSERT(lsp->fds_chan.state == CHANNEL_OPEN);
7972072Svn83148 		lsp->fds_chan.state = CHANNEL_READY;
7983327Svn83148 
7993327Svn83148 		/*
8003327Svn83148 		 * Now the channel is ready after the handshake completes.
8013327Svn83148 		 * Reset the timeout to a smaller value for receiving messages
8023327Svn83148 		 * from the domain services.
8033327Svn83148 		 */
8044358Srb144127 		lsp->cv_twait = get_smf_int_val(LDM_RUNNING_TO_PROP_NM,
8054358Srb144127 		    0, LDM_TIMEOUT_CEILING, LDM_RUNNING_WAIT_TIME);
8063327Svn83148 
8072072Svn83148 		(void) pthread_mutex_unlock(&lsp->mt);
8082072Svn83148 	} else {
8092072Svn83148 		ds_hdr_t *H;
8102072Svn83148 		ds_init_nack_t *R;
8112072Svn83148 
8122072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_init_nack_t);
8132072Svn83148 		H = alloca(msglen);
8142072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
8152072Svn83148 
8162072Svn83148 		H->msg_type = DS_INIT_NACK;
8172072Svn83148 		H->payload_len = sizeof (ds_init_nack_t);
8182072Svn83148 		R->major_vers = new_major;
8192072Svn83148 
8202072Svn83148 		(void) fds_send(lsp, H, msglen);
8212072Svn83148 		/*
8222072Svn83148 		 * do not update state; remote end may attempt to initiate
8232072Svn83148 		 * connection with a different version
8242072Svn83148 		 */
8252072Svn83148 	}
8262072Svn83148 }
8272072Svn83148 
8282072Svn83148 
8292072Svn83148 /*ARGSUSED*/
8302072Svn83148 static void
8312072Svn83148 ds_handle_reg_req(struct ldmsvcs_info *lsp, void *buf, size_t len)
8322072Svn83148 {
8332072Svn83148 	ds_reg_req_t *req;
8342072Svn83148 	char *msg;
8352072Svn83148 	uint16_t new_major, new_minor;
8362072Svn83148 	size_t msglen;
8372072Svn83148 	int dup_svcreg = 0;
8382072Svn83148 
8392072Svn83148 	req = (ds_reg_req_t *)buf;
8402072Svn83148 	msg = (char *)req->svc_id;
8412072Svn83148 
8422072Svn83148 	/*
8432072Svn83148 	 * Service must be NULL terminated
8442072Svn83148 	 */
8452072Svn83148 	if (req->svc_id == NULL || strlen(req->svc_id) == 0 ||
8462072Svn83148 	    msg[strlen(req->svc_id)] != '\0') {
8472072Svn83148 		channel_close(lsp);
8482072Svn83148 		return;
8492072Svn83148 	}
8502072Svn83148 
8512072Svn83148 	if (fds_negotiate_version(req->major_vers, &new_major, &new_minor) &&
8522072Svn83148 	    (dup_svcreg = fds_svc_add(lsp, req,
8534358Srb144127 	    MIN(new_minor, req->minor_vers))) == 0) {
8542072Svn83148 
8552072Svn83148 		/*
8562072Svn83148 		 * Check version info. ACK only if the major numbers
8572072Svn83148 		 * exactly match. The service entity can retry with a new
8582072Svn83148 		 * minor based on the response sent as part of the NACK.
8592072Svn83148 		 */
8602072Svn83148 		ds_hdr_t *H;
8612072Svn83148 		ds_reg_ack_t *R;
8622072Svn83148 
8632072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_reg_ack_t);
8642072Svn83148 		H = alloca(msglen);
8652072Svn83148 		bzero(H, msglen);
8662072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
8672072Svn83148 
8682072Svn83148 		H->msg_type = DS_REG_ACK;
8692072Svn83148 		H->payload_len = sizeof (ds_reg_ack_t);
8702072Svn83148 		R->svc_handle = req->svc_handle;
8712072Svn83148 		R->minor_vers = MIN(new_minor, req->minor_vers);
8722072Svn83148 
8732072Svn83148 		(void) fds_send(lsp, H, msglen);
8742072Svn83148 	} else {
8752072Svn83148 		ds_hdr_t *H;
8762072Svn83148 		ds_reg_nack_t *R;
8772072Svn83148 
8782072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_reg_nack_t);
8792072Svn83148 		H = alloca(msglen);
8802072Svn83148 		bzero(H, msglen);
8812072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
8822072Svn83148 
8832072Svn83148 		H->msg_type = DS_REG_NACK;
8842072Svn83148 		H->payload_len = sizeof (ds_reg_nack_t);
8852072Svn83148 		R->svc_handle = req->svc_handle;
8862072Svn83148 		R->major_vers = new_major;
8872072Svn83148 
8882072Svn83148 		if (dup_svcreg)
8892072Svn83148 			R->result = DS_REG_DUP;
8902072Svn83148 		else
8912072Svn83148 			R->result = DS_REG_VER_NACK;
8922072Svn83148 
8932072Svn83148 		(void) fds_send(lsp, H, msglen);
8942072Svn83148 	}
8952072Svn83148 }
8962072Svn83148 
8972072Svn83148 
8982072Svn83148 /*ARGSUSED*/
8992072Svn83148 static void
9002072Svn83148 ds_handle_unreg(struct ldmsvcs_info *lsp, void *buf, size_t len)
9012072Svn83148 {
9022072Svn83148 	ds_unreg_req_t *req;
9032072Svn83148 	size_t msglen;
9042072Svn83148 
9052072Svn83148 	req = (ds_unreg_req_t *)buf;
9062072Svn83148 
9072072Svn83148 	if (fds_svc_remove(lsp, req->svc_handle) == 0) {
9082072Svn83148 		ds_hdr_t *H;
9092072Svn83148 		ds_unreg_ack_t *R;
9102072Svn83148 
9112072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_unreg_ack_t);
9122072Svn83148 		H = alloca(msglen);
9132072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
9142072Svn83148 
9152072Svn83148 		H->msg_type = DS_REG_ACK;
9162072Svn83148 		H->payload_len = sizeof (ds_unreg_ack_t);
9172072Svn83148 		R->svc_handle = req->svc_handle;
9182072Svn83148 
9192072Svn83148 		(void) fds_send(lsp, H, msglen);
9202072Svn83148 	} else {
9212072Svn83148 		ds_hdr_t *H;
9222072Svn83148 		ds_unreg_nack_t *R;
9232072Svn83148 
9242072Svn83148 		msglen = sizeof (ds_hdr_t) + sizeof (ds_unreg_nack_t);
9252072Svn83148 		H = alloca(msglen);
9262072Svn83148 		R = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
9272072Svn83148 
9282072Svn83148 		H->msg_type = DS_REG_NACK;
9292072Svn83148 		H->payload_len = sizeof (ds_unreg_nack_t);
9302072Svn83148 		R->svc_handle = req->svc_handle;
9312072Svn83148 
9322072Svn83148 		(void) fds_send(lsp, H, msglen);
9332072Svn83148 	}
9342072Svn83148 }
9352072Svn83148 
9362072Svn83148 
9372072Svn83148 /*
9382072Svn83148  * Message handler lookup table (v1.0 only for now) Future
9392072Svn83148  * versions can add their own lookup table.
9402072Svn83148  */
9412072Svn83148 typedef void (*ds_msg_handler_t)(struct ldmsvcs_info *lsp,
9422072Svn83148 				void *buf, size_t len);
9432072Svn83148 
9442072Svn83148 static const ds_msg_handler_t ds_msg_handlers[] = {
9452072Svn83148 	ds_handle_init_req,		/* DS_INIT_REQ */
9462072Svn83148 	ds_handle_msg_noop,		/* DS_INIT_ACK */
9472072Svn83148 	ds_handle_msg_noop,		/* DS_INIT_NACK */
9482072Svn83148 	ds_handle_reg_req,		/* DS_REG_REQ */
9492072Svn83148 	ds_handle_msg_noop,		/* DS_REG_ACK */
9502072Svn83148 	ds_handle_msg_noop,		/* DS_REG_NACK */
9512072Svn83148 	ds_handle_unreg,		/* DS_UNREG */
9522072Svn83148 	ds_handle_msg_noop,		/* DS_UNREG_ACK */
9532072Svn83148 	ds_handle_msg_noop,		/* DS_UNREG_NACK */
9542072Svn83148 	ds_handle_msg_noop,		/* DS_DATA */
9552072Svn83148 	ds_handle_msg_noop		/* DS_NACK */
9562072Svn83148 };
9572072Svn83148 
9582072Svn83148 
9592072Svn83148 /*
9602072Svn83148  * message and service internal functions
9612072Svn83148  */
9622072Svn83148 static void
963*8634SVuong.Nguyen@Sun.COM fds_svc_alloc(struct ldmsvcs_info *lsp)
9642072Svn83148 {
9652072Svn83148 	int i;
9667850SVuong.Nguyen@Sun.COM 	static char *name[] = { LDM_DS_NAME_CPU, LDM_DS_NAME_MEM,
9677850SVuong.Nguyen@Sun.COM 			LDM_DS_NAME_PRI, LDM_DS_NAME_IOD, NULL };
9682072Svn83148 
9692072Svn83148 	(void) pthread_mutex_init(&lsp->fmas_svcs.mt, NULL);
9702072Svn83148 	(void) pthread_cond_init(&lsp->fmas_svcs.cv, NULL);
9712072Svn83148 
9722072Svn83148 	for (lsp->fmas_svcs.nsvcs = 0; name[lsp->fmas_svcs.nsvcs] != NULL;
9732072Svn83148 	    lsp->fmas_svcs.nsvcs++)
9742072Svn83148 		;
9752072Svn83148 
976*8634SVuong.Nguyen@Sun.COM 	lsp->fmas_svcs.tbl = (fds_svc_t **)ldom_alloc(sizeof (fds_svc_t *) *
9774358Srb144127 	    lsp->fmas_svcs.nsvcs);
9782072Svn83148 
9792072Svn83148 	for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
9802072Svn83148 		lsp->fmas_svcs.tbl[i] =
981*8634SVuong.Nguyen@Sun.COM 		    (fds_svc_t *)ldom_alloc(sizeof (fds_svc_t));
9822072Svn83148 		bzero(lsp->fmas_svcs.tbl[i], sizeof (fds_svc_t));
9832072Svn83148 		lsp->fmas_svcs.tbl[i]->name = name[i];
9842072Svn83148 	}
9852072Svn83148 }
9862072Svn83148 
9872072Svn83148 
9882072Svn83148 static fds_svc_t *
9892072Svn83148 fds_svc_lookup(struct ldmsvcs_info *lsp, char *name)
9902072Svn83148 {
9912072Svn83148 	struct timespec twait;
9922072Svn83148 	fds_svc_t *svc;
9932072Svn83148 	int i, ier;
9942072Svn83148 
9952072Svn83148 	if (pthread_mutex_lock(&lsp->fmas_svcs.mt) == EINVAL)
9962072Svn83148 		return (NULL);	/* uninitialized or destroyed mutex */
9972072Svn83148 
9982072Svn83148 	svc = NULL;
9992072Svn83148 	for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
10002072Svn83148 		if (strcmp(lsp->fmas_svcs.tbl[i]->name, name) == 0) {
10012072Svn83148 			svc = lsp->fmas_svcs.tbl[i];
10022072Svn83148 			break;
10032072Svn83148 		}
10042072Svn83148 	}
10052072Svn83148 
10062072Svn83148 	ASSERT(svc != NULL);
10072072Svn83148 
10087850SVuong.Nguyen@Sun.COM 	if (svc->state == DS_SVC_INACTIVE) {
10097850SVuong.Nguyen@Sun.COM 		/* service is not registered */
10107850SVuong.Nguyen@Sun.COM 		ier = ETIMEDOUT;
10117850SVuong.Nguyen@Sun.COM 	} else {
10127850SVuong.Nguyen@Sun.COM 		ier = 0;
10137850SVuong.Nguyen@Sun.COM 		twait.tv_sec = time(NULL) + lsp->cv_twait;
10147850SVuong.Nguyen@Sun.COM 		twait.tv_nsec = 0;
10152072Svn83148 
10167850SVuong.Nguyen@Sun.COM 		while (svc->state != DS_SVC_ACTIVE && ier == 0 &&
10177850SVuong.Nguyen@Sun.COM 		    lsp->fds_chan.state != CHANNEL_UNUSABLE)
10187850SVuong.Nguyen@Sun.COM 			ier = pthread_cond_timedwait(&lsp->fmas_svcs.cv,
10197850SVuong.Nguyen@Sun.COM 			    &lsp->fmas_svcs.mt, &twait);
10207850SVuong.Nguyen@Sun.COM 
10217850SVuong.Nguyen@Sun.COM 		/*
10227850SVuong.Nguyen@Sun.COM 		 * By now, the ds service should have registered already.
10237850SVuong.Nguyen@Sun.COM 		 * If it does not, ldmd probably does not support this service.
10247850SVuong.Nguyen@Sun.COM 		 * Then mark the service state as inactive.
10257850SVuong.Nguyen@Sun.COM 		 */
10267850SVuong.Nguyen@Sun.COM 		if (ier == ETIMEDOUT) {
10277850SVuong.Nguyen@Sun.COM 			svc->state = DS_SVC_INACTIVE;
10287850SVuong.Nguyen@Sun.COM 		}
10297850SVuong.Nguyen@Sun.COM 	}
10302072Svn83148 
10312072Svn83148 	(void) pthread_mutex_unlock(&lsp->fmas_svcs.mt);
10322072Svn83148 
10332072Svn83148 	if (ier == 0)
10342072Svn83148 		return (svc);
10352072Svn83148 	else
10362072Svn83148 		return (NULL);
10372072Svn83148 }
10382072Svn83148 
10392072Svn83148 
10402072Svn83148 static uint64_t
10412072Svn83148 fds_svc_req_num(void)
10422072Svn83148 {
10432072Svn83148 	static uint64_t req_num = 1;
10442072Svn83148 
10452072Svn83148 	return (req_num++);
10462072Svn83148 }
10472072Svn83148 
10482072Svn83148 
10492072Svn83148 /*
10502072Svn83148  * return 0 if successful, 1 if otherwise
10512072Svn83148  */
10522072Svn83148 static int
10532072Svn83148 read_msg(struct ldmsvcs_info *lsp)
10542072Svn83148 {
10552072Svn83148 	ds_hdr_t header;
10562072Svn83148 	void *msg_buf;
10572072Svn83148 
10582072Svn83148 	/*
10592072Svn83148 	 * read the header
10602072Svn83148 	 */
10612072Svn83148 	if (read_stream(lsp->fds_chan.fd, &header, sizeof (ds_hdr_t)) != 0)
10622072Svn83148 		return (1);
10632072Svn83148 
10642072Svn83148 	if (header.msg_type >=
10652072Svn83148 	    sizeof (ds_msg_handlers) / sizeof (ds_msg_handler_t))
10664358Srb144127 		return (1);
10672072Svn83148 
10682072Svn83148 	/*
10692072Svn83148 	 * handle data as a special case
10702072Svn83148 	 */
10712072Svn83148 	if (header.msg_type == 9)
10722072Svn83148 		return (poller_handle_data(lsp->fds_chan.fd,
10734358Srb144127 		    header.payload_len));
10742072Svn83148 
10752072Svn83148 	/*
10762072Svn83148 	 * all other types of messages should be small
10772072Svn83148 	 */
10782072Svn83148 	ASSERT(header.payload_len < 1024);
10792072Svn83148 	msg_buf = alloca(header.payload_len);
10802072Svn83148 
10812072Svn83148 	/*
10822072Svn83148 	 * read the payload
10832072Svn83148 	 */
10842072Svn83148 	if (read_stream(lsp->fds_chan.fd, msg_buf, header.payload_len) != 0)
10852072Svn83148 		return (1);
10862072Svn83148 
10872072Svn83148 	(*ds_msg_handlers[header.msg_type])(lsp, msg_buf, header.payload_len);
10882072Svn83148 
10892072Svn83148 	return (0);
10902072Svn83148 }
10912072Svn83148 
10922072Svn83148 
10932072Svn83148 /*
10942072Svn83148  * return values:
10952072Svn83148  *  0 - success
10962072Svn83148  *  1 - problem with opening the channel
10972072Svn83148  *  2 - channed not opened; request to exit has been detected
10982072Svn83148  */
10992072Svn83148 static int
11002072Svn83148 channel_openreset(struct ldmsvcs_info *lsp)
11012072Svn83148 {
11022072Svn83148 	int ier;
11032072Svn83148 
11042072Svn83148 	ier = pthread_mutex_lock(&lsp->mt);
11052072Svn83148 
11062072Svn83148 	if (ier == EINVAL || lsp->fds_chan.state == CHANNEL_EXIT ||
11072072Svn83148 	    lsp->fds_chan.state == CHANNEL_UNUSABLE) {
11082072Svn83148 		(void) pthread_mutex_unlock(&lsp->mt);
11092072Svn83148 		return (2);
11102072Svn83148 	}
11112072Svn83148 
11122072Svn83148 	if (lsp->fds_chan.state == CHANNEL_UNINITIALIZED ||
11132072Svn83148 	    lsp->fds_chan.state == CHANNEL_CLOSED) {
11142072Svn83148 		(void) pthread_cond_broadcast(&lsp->cv);
11152072Svn83148 
11162072Svn83148 		if ((lsp->fds_chan.fd = open(FDS_VLDC, O_RDWR)) < 0) {
11172072Svn83148 			lsp->fds_chan.state = CHANNEL_UNUSABLE;
11184358Srb144127 			lsp->cv_twait = get_smf_int_val(LDM_RUNNING_TO_PROP_NM,
11194358Srb144127 			    0, LDM_TIMEOUT_CEILING, LDM_RUNNING_WAIT_TIME);
11202072Svn83148 			(void) pthread_mutex_unlock(&lsp->mt);
11212072Svn83148 			(void) pthread_cond_broadcast(&lsp->fmas_svcs.cv);
11222072Svn83148 
11232072Svn83148 			return (2);
11242072Svn83148 		} else {
11252072Svn83148 			vldc_opt_op_t op;
11262072Svn83148 
11272072Svn83148 			op.op_sel = VLDC_OP_SET;
11282072Svn83148 			op.opt_sel = VLDC_OPT_MODE;
11296408Sha137994 			op.opt_val = LDC_MODE_RELIABLE;
11302072Svn83148 
11312072Svn83148 			if (ioctl(lsp->fds_chan.fd, VLDC_IOCTL_OPT_OP,
11324358Srb144127 			    &op) != 0) {
11332072Svn83148 				(void) close(lsp->fds_chan.fd);
11342072Svn83148 				(void) pthread_mutex_unlock(&lsp->mt);
11352072Svn83148 				return (1);
11362072Svn83148 			}
11372072Svn83148 		}
11382072Svn83148 		lsp->fds_chan.state = CHANNEL_OPEN;
11392072Svn83148 	}
11402072Svn83148 
11412072Svn83148 	if (lsp->fds_chan.state == CHANNEL_OPEN) {
11422072Svn83148 		/*
11432072Svn83148 		 * reset various channel parameters
11442072Svn83148 		 */
11452072Svn83148 		lsp->fds_chan.ver.major = 0;
11462072Svn83148 		lsp->fds_chan.ver.minor = 0;
11472072Svn83148 		fds_svc_reset(lsp, -1);
11482072Svn83148 	}
11492072Svn83148 	(void) pthread_mutex_unlock(&lsp->mt);
11502072Svn83148 
11512072Svn83148 	return (0);
11522072Svn83148 }
11532072Svn83148 
11542072Svn83148 
11552072Svn83148 static void
11562072Svn83148 channel_fini(void)
11572072Svn83148 {
1158*8634SVuong.Nguyen@Sun.COM 	int i;
11592072Svn83148 	struct ldmsvcs_info *lsp;
11602072Svn83148 
11612072Svn83148 	/*
11622072Svn83148 	 * End the poller thread
11632072Svn83148 	 */
1164*8634SVuong.Nguyen@Sun.COM 	poller_fini();
11652072Svn83148 
11662072Svn83148 	if ((lsp = channel_init(NULL)) == NULL)
11672072Svn83148 		return;
11682072Svn83148 
11692072Svn83148 	(void) pthread_mutex_lock(&lsp->mt);
11702072Svn83148 
11712072Svn83148 	lsp->fds_chan.state = CHANNEL_EXIT;
11722072Svn83148 	(void) close(lsp->fds_chan.fd);
11732072Svn83148 
11742072Svn83148 	(void) pthread_mutex_unlock(&lsp->mt);
1175*8634SVuong.Nguyen@Sun.COM 
1176*8634SVuong.Nguyen@Sun.COM 	/* Free the ldom service structure */
1177*8634SVuong.Nguyen@Sun.COM 	for (i = 0; i < lsp->fmas_svcs.nsvcs; i++) {
1178*8634SVuong.Nguyen@Sun.COM 		ldom_free(lsp->fmas_svcs.tbl[i], sizeof (fds_svc_t));
1179*8634SVuong.Nguyen@Sun.COM 	}
1180*8634SVuong.Nguyen@Sun.COM 	ldom_free(lsp->fmas_svcs.tbl,
1181*8634SVuong.Nguyen@Sun.COM 	    lsp->fmas_svcs.nsvcs * sizeof (fds_svc_t *));
1182*8634SVuong.Nguyen@Sun.COM 	ldom_free(lsp, sizeof (struct ldmsvcs_info));
11832072Svn83148 }
11842072Svn83148 
11852072Svn83148 
11862072Svn83148 static struct ldmsvcs_info *
11872072Svn83148 channel_init(struct ldom_hdl *lhp)
11882072Svn83148 {
11892072Svn83148 	static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER;
11902072Svn83148 	static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
11912072Svn83148 	static struct ldmsvcs_info *root = NULL;
11922072Svn83148 	static int busy_init = 0;
11932072Svn83148 
11942072Svn83148 	struct timespec twait;
11952072Svn83148 	int expired;
11962072Svn83148 
11972072Svn83148 	(void) pthread_mutex_lock(&mt);
11982072Svn83148 
11992072Svn83148 	while (busy_init == 1)
12002072Svn83148 		(void) pthread_cond_wait(&cv, &mt);
12012072Svn83148 
12022072Svn83148 	if (root != NULL || (lhp == NULL && root == NULL)) {
12032072Svn83148 		(void) pthread_mutex_unlock(&mt);
12042072Svn83148 		return (root);
12052072Svn83148 	}
12062072Svn83148 
12072072Svn83148 	/*
12082072Svn83148 	 * get to this point if we need to open the channel
12092072Svn83148 	 */
12102072Svn83148 	busy_init = 1;
12112072Svn83148 	(void) pthread_mutex_unlock(&mt);
12122072Svn83148 
12132072Svn83148 	root = (struct ldmsvcs_info *)
1214*8634SVuong.Nguyen@Sun.COM 	    ldom_alloc(sizeof (struct ldmsvcs_info));
12152072Svn83148 	bzero(root, sizeof (struct ldmsvcs_info));
12162072Svn83148 
12172072Svn83148 	root->fds_chan.state = CHANNEL_UNINITIALIZED;
12184358Srb144127 	root->cv_twait = get_smf_int_val(LDM_INIT_TO_PROP_NM,
12194358Srb144127 	    0, LDM_TIMEOUT_CEILING, LDM_INIT_WAIT_TIME);
12202072Svn83148 
12212072Svn83148 	if (pthread_mutex_init(&root->mt, NULL) != 0 ||
12222072Svn83148 	    pthread_cond_init(&root->cv, NULL) != 0) {
1223*8634SVuong.Nguyen@Sun.COM 		ldom_free(root, sizeof (struct ldmsvcs_info));
12242072Svn83148 		return (NULL);
12252072Svn83148 	}
12262072Svn83148 
1227*8634SVuong.Nguyen@Sun.COM 	fds_svc_alloc(root);
12282072Svn83148 	fds_svc_reset(root, -1);
12292072Svn83148 
12302072Svn83148 	(void) poller_init(root);
12312072Svn83148 
12322072Svn83148 	expired = 0;
12332072Svn83148 	twait.tv_sec = time(NULL) + 10;
12342072Svn83148 	twait.tv_nsec = 0;
12352072Svn83148 
12362072Svn83148 	(void) pthread_mutex_lock(&root->mt);
12372072Svn83148 
12382072Svn83148 	/*
12392072Svn83148 	 * wait for channel to become uninitialized.  this should be quick.
12402072Svn83148 	 */
12412072Svn83148 	while (root->fds_chan.state == CHANNEL_UNINITIALIZED && expired == 0)
12422072Svn83148 		expired = pthread_cond_timedwait(&root->cv, &root->mt, &twait);
12432072Svn83148 
12442072Svn83148 	if (root->fds_chan.state == CHANNEL_UNUSABLE)
12452072Svn83148 		expired = 1;
12462072Svn83148 
12472072Svn83148 	(void) pthread_mutex_unlock(&root->mt);
12482072Svn83148 
12492072Svn83148 	(void) pthread_mutex_lock(&mt);
12502072Svn83148 	busy_init = 0;
12512072Svn83148 	(void) pthread_mutex_unlock(&mt);
12522072Svn83148 	(void) pthread_cond_broadcast(&cv);
12532072Svn83148 
12542072Svn83148 	(void) atexit(channel_fini);
12552072Svn83148 
12562072Svn83148 	if (expired == 0)
12572072Svn83148 		return (root);
12582072Svn83148 	else
12592072Svn83148 		return (NULL);
12602072Svn83148 }
12612072Svn83148 
12622072Svn83148 
12632072Svn83148 static int
12642072Svn83148 sendrecv(struct ldom_hdl *lhp, uint64_t req_num,
12652072Svn83148 	void *msg, size_t msglen, ds_svc_hdl_t *svc_hdl, char *svcname,
12662072Svn83148 	void **resp, size_t *resplen)
12672072Svn83148 {
12682072Svn83148 	struct ldmsvcs_info *lsp;
12692072Svn83148 	fds_svc_t *svc;
12702072Svn83148 	int maxretries, index, i, ier;
12712072Svn83148 
12722072Svn83148 	lsp = lhp->lsinfo;
12732072Svn83148 	i = 0;
12742072Svn83148 	maxretries = 1;
12752072Svn83148 
12762072Svn83148 	do {
12772072Svn83148 		/*
12782072Svn83148 		 * if any of the calls in this loop fail, retry some number
12792072Svn83148 		 * of times before giving up.
12802072Svn83148 		 */
12812072Svn83148 		if ((svc = fds_svc_lookup(lsp, svcname)) == NULL) {
12822072Svn83148 			(void) pthread_mutex_lock(&lsp->mt);
12832072Svn83148 
12842072Svn83148 			if (lsp->fds_chan.state != CHANNEL_READY)
12852072Svn83148 				ier = ETIMEDOUT;	/* channel not ready */
12862072Svn83148 			else
12872072Svn83148 				ier = ENOTSUP;		/* service not ready */
12882072Svn83148 
12892072Svn83148 			(void) pthread_mutex_unlock(&lsp->mt);
12902072Svn83148 
12912072Svn83148 			continue;
12922072Svn83148 		} else {
12932072Svn83148 			ier = 0;
12942072Svn83148 			*svc_hdl = svc->hdl;
12952072Svn83148 		}
12962072Svn83148 
1297*8634SVuong.Nguyen@Sun.COM 		index = poller_add_pending(req_num);
12982072Svn83148 
12992072Svn83148 		if ((ier = fds_send(lsp, msg, msglen)) != 0 ||
13002072Svn83148 		    (ier = poller_recv_data(lhp, req_num, index, resp,
13014358Srb144127 		    resplen)) != 0)
13022072Svn83148 			poller_delete_pending(req_num, index);
13032072Svn83148 
13042072Svn83148 	} while (i++ < maxretries && ier != 0);
13052072Svn83148 
13062072Svn83148 	ASSERT(ier == 0 || ier == ETIMEDOUT || ier == ENOTSUP);
13072072Svn83148 
13082072Svn83148 	return (ier);
13092072Svn83148 }
13102072Svn83148 
13112072Svn83148 
13122072Svn83148 /*
13132072Svn83148  * input:
13142072Svn83148  *   msg_type - requested operation: FMA_CPU_REQ_STATUS or FMA_CPU_REQ_OFFLINE
13152072Svn83148  *   cpuid - physical cpu id
13162072Svn83148  *
13172072Svn83148  * normal return values:
13182072Svn83148  *   P_OFFLINE - cpu is offline
13192072Svn83148  *   P_ONLINE - cpu is online
13202072Svn83148  *
13212072Svn83148  * abnormal return values:
13222072Svn83148  *   ETIMEDOUT - LDOM manager is not responding
13232072Svn83148  *   ENOTSUP - LDOM service for cpu offlining/status is not available
13242072Svn83148  *   ENOMSG - got an unexpected response from the LDOM cpu service
13252072Svn83148  */
13262072Svn83148 static int
13272072Svn83148 cpu_request(struct ldom_hdl *lhp, uint32_t msg_type, uint32_t cpuid)
13282072Svn83148 {
13292072Svn83148 	ds_hdr_t *H;
13302072Svn83148 	ds_data_handle_t *D;
13312072Svn83148 	fma_cpu_service_req_t *R;
13322072Svn83148 
13337850SVuong.Nguyen@Sun.COM 	char *svcname = LDM_DS_NAME_CPU;
13342072Svn83148 	fma_cpu_resp_t *respmsg;
13352072Svn83148 	void *resp;
13362072Svn83148 	size_t resplen, reqmsglen;
13372072Svn83148 	int rc;
13382072Svn83148 
13392072Svn83148 	if (lhp->lsinfo == NULL)
13402072Svn83148 		return (ENOMSG);
13412072Svn83148 
13422072Svn83148 	reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
13434358Srb144127 	    sizeof (fma_cpu_service_req_t);
13442072Svn83148 
13452072Svn83148 	H = lhp->allocp(reqmsglen);
13462072Svn83148 	D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
13472072Svn83148 	R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
13482072Svn83148 
13492072Svn83148 	H->msg_type = DS_DATA;
13502072Svn83148 	H->payload_len = sizeof (ds_data_handle_t) +
13514358Srb144127 	    sizeof (fma_cpu_service_req_t);
13522072Svn83148 
13532072Svn83148 	R->req_num = fds_svc_req_num();
13542072Svn83148 	R->msg_type = msg_type;
13552072Svn83148 	R->cpu_id = cpuid;
13562072Svn83148 
13572072Svn83148 	if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
13584358Srb144127 	    &D->svc_handle, svcname, &resp, &resplen)) != 0) {
13592072Svn83148 		lhp->freep(H, reqmsglen);
13602072Svn83148 		return (rc);
13612072Svn83148 	}
13622072Svn83148 
13632072Svn83148 	lhp->freep(H, reqmsglen);
13642072Svn83148 
13652072Svn83148 	ASSERT(resplen == sizeof (fma_cpu_resp_t));
13662072Svn83148 	respmsg = (fma_cpu_resp_t *)resp;
13672072Svn83148 
13682072Svn83148 	rc = ENOMSG;
13692072Svn83148 	if (respmsg->result == FMA_CPU_RESP_OK) {
13702072Svn83148 		if (respmsg->status == FMA_CPU_STAT_ONLINE)
13712072Svn83148 			rc = P_ONLINE;
13722072Svn83148 		else if (respmsg->status == FMA_CPU_STAT_OFFLINE)
13732072Svn83148 			rc = P_OFFLINE;
13742072Svn83148 	} else {
13752072Svn83148 		if (msg_type == FMA_CPU_REQ_OFFLINE &&
13762072Svn83148 		    respmsg->status == FMA_CPU_STAT_OFFLINE)
13772072Svn83148 			rc = P_OFFLINE;
13782072Svn83148 	}
13792072Svn83148 
13802072Svn83148 	lhp->freep(resp, resplen);
13812072Svn83148 
13822072Svn83148 	return (rc);
13832072Svn83148 }
13842072Svn83148 
13852072Svn83148 
13862072Svn83148 /*
13872072Svn83148  * input:
13882072Svn83148  *   msg_type - requested operation: FMA_MEM_REQ_STATUS or FMA_MEM_REQ_RETIRE
13892072Svn83148  *   pa - starting address of memory page
13902072Svn83148  *   pgsize - memory page size in bytes
13912072Svn83148  *
13922072Svn83148  * normal return values for msg_type == FMA_MEM_REQ_STATUS:
13932072Svn83148  *   0 - page is retired
13942072Svn83148  *   EAGAIN - page is scheduled for retirement
13952072Svn83148  *   EIO - page not scheduled for retirement
13962072Svn83148  *   EINVAL - error
13972072Svn83148  *
13982072Svn83148  * normal return values for msg_type == FMA_MEM_REQ_RETIRE:
13992072Svn83148  *   0 - success in retiring page
14002072Svn83148  *   EIO - page is already retired
14012072Svn83148  *   EAGAIN - page is scheduled for retirement
14022072Svn83148  *   EINVAL - error
14032072Svn83148  *
14042072Svn83148  * abnormal return values (regardless of msg_type)
14052072Svn83148  *   ETIMEDOUT - LDOM manager is not responding
14062072Svn83148  *   ENOTSUP - LDOM service for cpu offlining/status is not available
14072072Svn83148  *   ENOMSG - got an unexpected response from the LDOM cpu service
14082072Svn83148  */
14092072Svn83148 static int
14102072Svn83148 mem_request(struct ldom_hdl *lhp, uint32_t msg_type, uint64_t pa,
14112072Svn83148 	    uint64_t pgsize)
14122072Svn83148 {
14132072Svn83148 	ds_hdr_t *H;
14142072Svn83148 	ds_data_handle_t *D;
14152072Svn83148 	fma_mem_service_req_t *R;
14162072Svn83148 
14177850SVuong.Nguyen@Sun.COM 	char *svcname = LDM_DS_NAME_MEM;
14182072Svn83148 	fma_mem_resp_t *respmsg;
14192072Svn83148 	void *resp;
14202072Svn83148 	size_t resplen, reqmsglen;
14212072Svn83148 	int rc;
14222072Svn83148 
14232072Svn83148 	if (lhp->lsinfo == NULL)
14242072Svn83148 		return (ENOMSG);
14252072Svn83148 
14262072Svn83148 	reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
14274358Srb144127 	    sizeof (fma_mem_service_req_t);
14282072Svn83148 
14292072Svn83148 	H = lhp->allocp(reqmsglen);
14302072Svn83148 	bzero(H, reqmsglen);
14312072Svn83148 	D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
14322072Svn83148 	R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
14332072Svn83148 
14342072Svn83148 	H->msg_type = DS_DATA;
14352072Svn83148 	H->payload_len = sizeof (ds_data_handle_t) +
14364358Srb144127 	    sizeof (fma_mem_service_req_t);
14372072Svn83148 
14382072Svn83148 	R->req_num = fds_svc_req_num();
14392072Svn83148 	R->msg_type = msg_type;
14402072Svn83148 	R->real_addr = pa;
14412072Svn83148 	R->length = pgsize;
14422072Svn83148 
14432072Svn83148 	if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
14444358Srb144127 	    &D->svc_handle, svcname, &resp, &resplen)) != 0) {
14452072Svn83148 		lhp->freep(H, reqmsglen);
14462072Svn83148 		return (rc);
14472072Svn83148 	}
14482072Svn83148 
14492072Svn83148 	lhp->freep(H, reqmsglen);
14502072Svn83148 
14512072Svn83148 	ASSERT(resplen == sizeof (fma_mem_resp_t));
14522072Svn83148 	respmsg = (fma_mem_resp_t *)resp;
14532072Svn83148 
14542072Svn83148 	rc = ENOMSG;
14552072Svn83148 	if (msg_type == FMA_MEM_REQ_STATUS) {
14562072Svn83148 		if (respmsg->result == FMA_MEM_RESP_OK) {
14572072Svn83148 			if (respmsg->status == FMA_MEM_STAT_RETIRED)
14584655Svn83148 				rc = 0;		/* page is retired */
14592072Svn83148 			else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14604655Svn83148 				rc = EIO;	/* page is not scheduled */
14612072Svn83148 		} else if (respmsg->result == FMA_MEM_RESP_FAILURE) {
14624655Svn83148 			if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14634655Svn83148 				rc = EAGAIN;	/* page is scheduled */
14644655Svn83148 			else if (respmsg->status == FMA_MEM_STAT_ILLEGAL)
14652072Svn83148 				rc = EINVAL;
14662072Svn83148 		}
14672072Svn83148 	} else if (msg_type == FMA_MEM_REQ_RETIRE) {
14682072Svn83148 		if (respmsg->result == FMA_MEM_RESP_OK) {
14692072Svn83148 			if (respmsg->status == FMA_MEM_STAT_RETIRED)
14704655Svn83148 				rc = 0;		/* is successfully retired */
14712072Svn83148 		} else if (respmsg->result == FMA_MEM_RESP_FAILURE) {
14722072Svn83148 			if (respmsg->status == FMA_MEM_STAT_RETIRED)
14734655Svn83148 				rc = EIO;	/* is already retired */
14744655Svn83148 			else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14754655Svn83148 				rc = EAGAIN;	/* is scheduled to retire */
14762072Svn83148 			else if (respmsg->status == FMA_MEM_STAT_ILLEGAL)
14772072Svn83148 				rc = EINVAL;
14782072Svn83148 		}
14797532SSean.Ye@Sun.COM 	} else if (msg_type == FMA_MEM_REQ_RESURRECT) {
14807532SSean.Ye@Sun.COM 		if (respmsg->result == FMA_MEM_RESP_OK) {
14817532SSean.Ye@Sun.COM 			if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14827532SSean.Ye@Sun.COM 				rc = 0;		/* is successfully unretired */
14837532SSean.Ye@Sun.COM 		} if (respmsg->result == FMA_MEM_RESP_FAILURE) {
14847532SSean.Ye@Sun.COM 			if (respmsg->status == FMA_MEM_STAT_RETIRED)
14857532SSean.Ye@Sun.COM 				rc = EAGAIN; 	/* page couldn't be locked */
14867532SSean.Ye@Sun.COM 			else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
14877532SSean.Ye@Sun.COM 				rc = EIO;	/* page isn't retired already */
14887532SSean.Ye@Sun.COM 			else if (respmsg->status == FMA_MEM_STAT_ILLEGAL)
14897532SSean.Ye@Sun.COM 				rc = EINVAL;
14907532SSean.Ye@Sun.COM 		}
14912072Svn83148 	}
14922072Svn83148 
14932072Svn83148 	lhp->freep(resp, resplen);
14942072Svn83148 
14952072Svn83148 	return (rc);
14962072Svn83148 }
14972072Svn83148 
14982072Svn83148 
14992072Svn83148 /*
15002072Svn83148  * APIs
15012072Svn83148  */
15022072Svn83148 int
15032072Svn83148 ldmsvcs_check_channel(void)
15042072Svn83148 {
15052072Svn83148 	struct stat buf;
15062072Svn83148 
15072072Svn83148 	if (stat(FDS_VLDC, &buf) == 0)
15082072Svn83148 		return (0);	/* vldc exists */
15092072Svn83148 	else if (errno == ENOENT || errno == ENOTDIR)
15102072Svn83148 		return (1);	/* vldc does not exist */
15112072Svn83148 	else
15122072Svn83148 		return (-1);	/* miscellaneous error */
15132072Svn83148 }
15142072Svn83148 
15152072Svn83148 
15162072Svn83148 /*ARGSUSED*/
15172072Svn83148 void
15182072Svn83148 ldmsvcs_init(struct ldom_hdl *lhp)
15192072Svn83148 {
15202072Svn83148 	if (ldmsvcs_check_channel() != 0)
15212072Svn83148 		return;
15222072Svn83148 
15232072Svn83148 	lhp->lsinfo = channel_init(lhp);
15242072Svn83148 	poller_add_client();
15252072Svn83148 }
15262072Svn83148 
15272072Svn83148 
15282072Svn83148 /*ARGSUSED*/
15292072Svn83148 void
15302072Svn83148 ldmsvcs_fini(struct ldom_hdl *lhp)
15312072Svn83148 {
15322072Svn83148 	if (ldmsvcs_check_channel() != 0)
15332072Svn83148 		return;
15342072Svn83148 
15352072Svn83148 	poller_remove_client();
15362072Svn83148 }
15372072Svn83148 
15382072Svn83148 
15392072Svn83148 /*ARGSUSED*/
15402072Svn83148 ssize_t
15412072Svn83148 ldmsvcs_get_core_md(struct ldom_hdl *lhp, uint64_t **buf)
15422072Svn83148 {
15432072Svn83148 	ds_hdr_t *H;
15442072Svn83148 	ds_data_handle_t *D;
15452072Svn83148 	fma_req_pri_t *R;
15462072Svn83148 
15477850SVuong.Nguyen@Sun.COM 	char *svcname = LDM_DS_NAME_PRI;
15482072Svn83148 	void *resp;
15492072Svn83148 	size_t resplen, reqmsglen;
15502072Svn83148 	ssize_t buflen;
15512072Svn83148 	int rc;
15522072Svn83148 
15532072Svn83148 	if (lhp->lsinfo == NULL)
15542072Svn83148 		return (-1);
15552072Svn83148 
15562072Svn83148 	reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
15574358Srb144127 	    sizeof (fma_req_pri_t);
15582072Svn83148 
15592072Svn83148 	H = lhp->allocp(reqmsglen);
15602072Svn83148 	D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
15612072Svn83148 	R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
15622072Svn83148 
15632072Svn83148 	H->msg_type = DS_DATA;
15642072Svn83148 	H->payload_len = sizeof (ds_data_handle_t) +
15654358Srb144127 	    sizeof (fma_req_pri_t);
15662072Svn83148 
15672072Svn83148 	R->req_num = fds_svc_req_num();
15682072Svn83148 
15692072Svn83148 	if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
15704358Srb144127 	    &D->svc_handle, svcname, &resp, &resplen)) != 0) {
15712072Svn83148 		lhp->freep(H, reqmsglen);
15722072Svn83148 		errno = rc;
15732072Svn83148 		return (-1);
15742072Svn83148 	}
15752072Svn83148 
15762072Svn83148 	lhp->freep(H, reqmsglen);
15772072Svn83148 
15782072Svn83148 	/*
15792072Svn83148 	 * resp should contain the req_num immediately followed by the PRI
15802072Svn83148 	 * (the latter may or may not be present).  unfortunately, the
15812072Svn83148 	 * current compiler flags cause a warning for the following
15822072Svn83148 	 * definition
15832072Svn83148 	 *
15842072Svn83148 	 * typedef struct {
15852072Svn83148 	 *    uint64_t req_num;
15862072Svn83148 	 *    uint8_t pri[];
15872072Svn83148 	 *  } fma_pri_resp_t;
15882072Svn83148 	 *
15892072Svn83148 	 * so we do not use the struct here.
15902072Svn83148 	 */
15912072Svn83148 	if (resplen <= sizeof (uint64_t)) {
15922072Svn83148 		lhp->freep(resp, resplen);
15932072Svn83148 		if (resplen == sizeof (uint64_t))
15942072Svn83148 			return (0);
15952072Svn83148 		else
15962072Svn83148 			return (-1);
15972072Svn83148 	}
15982072Svn83148 
15992072Svn83148 	buflen = resplen - sizeof (uint64_t);
16002072Svn83148 	*buf = lhp->allocp(buflen);
16012072Svn83148 
16022072Svn83148 	bcopy((void *)((ptrdiff_t)resp + sizeof (uint64_t)), *buf, buflen);
16032072Svn83148 	lhp->freep(resp, resplen);
16042072Svn83148 
16052072Svn83148 	return (buflen);
16062072Svn83148 }
16072072Svn83148 
16082072Svn83148 
16092072Svn83148 /*
16102072Svn83148  * see cpu_request() for a description of return values
16112072Svn83148  */
16122072Svn83148 int
16132072Svn83148 ldmsvcs_cpu_req_status(struct ldom_hdl *lhp, uint32_t cpuid)
16142072Svn83148 {
16152072Svn83148 	return (cpu_request(lhp, FMA_CPU_REQ_STATUS, cpuid));
16162072Svn83148 }
16172072Svn83148 
16182072Svn83148 
16192072Svn83148 int
16202072Svn83148 ldmsvcs_cpu_req_offline(struct ldom_hdl *lhp, uint32_t cpuid)
16212072Svn83148 {
16222072Svn83148 	return (cpu_request(lhp, FMA_CPU_REQ_OFFLINE, cpuid));
16232072Svn83148 }
16242072Svn83148 
16256111Scy152378 int
16266111Scy152378 ldmsvcs_cpu_req_online(struct ldom_hdl *lhp, uint32_t cpuid)
16276111Scy152378 {
16286111Scy152378 	return (cpu_request(lhp, FMA_CPU_REQ_ONLINE, cpuid));
16296111Scy152378 }
16302072Svn83148 
16312072Svn83148 /*
16322072Svn83148  * see mem_request() for a description of return values
16332072Svn83148  */
16342072Svn83148 int
16352072Svn83148 ldmsvcs_mem_req_status(struct ldom_hdl *lhp, uint64_t pa)
16362072Svn83148 {
16372072Svn83148 	return (mem_request(lhp, FMA_MEM_REQ_STATUS, pa, getpagesize()));
16382072Svn83148 }
16392072Svn83148 
16402072Svn83148 int
16412072Svn83148 ldmsvcs_mem_req_retire(struct ldom_hdl *lhp, uint64_t pa)
16422072Svn83148 {
16432072Svn83148 	return (mem_request(lhp, FMA_MEM_REQ_RETIRE, pa, getpagesize()));
16442072Svn83148 }
16452072Svn83148 
16466111Scy152378 int
16476111Scy152378 ldmsvcs_mem_req_unretire(struct ldom_hdl *lhp, uint64_t pa)
16486111Scy152378 {
16496111Scy152378 	return (mem_request(lhp, FMA_MEM_REQ_RESURRECT, pa, getpagesize()));
16506111Scy152378 }
16516111Scy152378 
16527850SVuong.Nguyen@Sun.COM int
16537850SVuong.Nguyen@Sun.COM ldmsvcs_io_req_id(struct ldom_hdl *lhp, uint64_t addr, uint_t type,
16547850SVuong.Nguyen@Sun.COM     uint64_t *virt_addr, char *name, int name_len, uint64_t *did)
16557850SVuong.Nguyen@Sun.COM {
16567850SVuong.Nguyen@Sun.COM 
16577850SVuong.Nguyen@Sun.COM 	ds_hdr_t *H;
16587850SVuong.Nguyen@Sun.COM 	ds_data_handle_t *D;
16597850SVuong.Nguyen@Sun.COM 	fma_io_req_t *R;
16607850SVuong.Nguyen@Sun.COM 
16617850SVuong.Nguyen@Sun.COM 	char *svcname = LDM_DS_NAME_IOD;
16627850SVuong.Nguyen@Sun.COM 	void *resp;
16637850SVuong.Nguyen@Sun.COM 	fma_io_resp_t *iop;
16647850SVuong.Nguyen@Sun.COM 	size_t resplen, reqmsglen;
16657850SVuong.Nguyen@Sun.COM 	int offset;
16667850SVuong.Nguyen@Sun.COM 	int rc;
16677850SVuong.Nguyen@Sun.COM 
16687850SVuong.Nguyen@Sun.COM 	if (lhp->lsinfo == NULL)
16697850SVuong.Nguyen@Sun.COM 		return (-1);
16707850SVuong.Nguyen@Sun.COM 
16717850SVuong.Nguyen@Sun.COM 	reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
16727850SVuong.Nguyen@Sun.COM 	    sizeof (fma_io_req_t);
16737850SVuong.Nguyen@Sun.COM 
16747850SVuong.Nguyen@Sun.COM 	H = lhp->allocp(reqmsglen);
16757850SVuong.Nguyen@Sun.COM 	D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
16767850SVuong.Nguyen@Sun.COM 	R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
16777850SVuong.Nguyen@Sun.COM 
16787850SVuong.Nguyen@Sun.COM 	H->msg_type = DS_DATA;
16797850SVuong.Nguyen@Sun.COM 	H->payload_len = sizeof (ds_data_handle_t) + sizeof (fma_io_req_t);
16807850SVuong.Nguyen@Sun.COM 
16817850SVuong.Nguyen@Sun.COM 	R->req_num = fds_svc_req_num();
16827850SVuong.Nguyen@Sun.COM 	R->msg_type = type;
16837850SVuong.Nguyen@Sun.COM 	R->rsrc_address = addr;
16847850SVuong.Nguyen@Sun.COM 
16857850SVuong.Nguyen@Sun.COM 	rc = ENOMSG;
16867850SVuong.Nguyen@Sun.COM 	if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
16877850SVuong.Nguyen@Sun.COM 	    &D->svc_handle, svcname, &resp, &resplen)) != 0) {
16887850SVuong.Nguyen@Sun.COM 		lhp->freep(H, reqmsglen);
16897850SVuong.Nguyen@Sun.COM 		return (rc);
16907850SVuong.Nguyen@Sun.COM 	}
16917850SVuong.Nguyen@Sun.COM 	lhp->freep(H, reqmsglen);
16927850SVuong.Nguyen@Sun.COM 
16937850SVuong.Nguyen@Sun.COM 	/*
16947850SVuong.Nguyen@Sun.COM 	 * resp should contain the req_num, status, virtual addr, domain id
16957850SVuong.Nguyen@Sun.COM 	 * and the domain name. The domain name may or may not be present.
16967850SVuong.Nguyen@Sun.COM 	 */
16977850SVuong.Nguyen@Sun.COM 	offset = sizeof (fma_io_resp_t);
16987850SVuong.Nguyen@Sun.COM 	if (resplen < offset) {
16997850SVuong.Nguyen@Sun.COM 		lhp->freep(resp, resplen);
17007850SVuong.Nguyen@Sun.COM 		return (-1);
17017850SVuong.Nguyen@Sun.COM 	}
17027850SVuong.Nguyen@Sun.COM 
17037850SVuong.Nguyen@Sun.COM 	iop = (fma_io_resp_t *)resp;
17047850SVuong.Nguyen@Sun.COM 	switch (iop->result) {
17057850SVuong.Nguyen@Sun.COM 	case FMA_IO_RESP_OK:
17067850SVuong.Nguyen@Sun.COM 		/* success */
17077850SVuong.Nguyen@Sun.COM 		rc = 0;
17087850SVuong.Nguyen@Sun.COM 		*virt_addr = iop->virt_rsrc_address;
17097850SVuong.Nguyen@Sun.COM 		*did = iop->domain_id;
17107850SVuong.Nguyen@Sun.COM 		if (name == NULL || name_len <= 0)
17117850SVuong.Nguyen@Sun.COM 			break;
17127850SVuong.Nguyen@Sun.COM 		*name = '\0';
17137850SVuong.Nguyen@Sun.COM 		if (resplen > offset) {
17147850SVuong.Nguyen@Sun.COM 			(void) strncpy(name, (char *)((ptrdiff_t)resp + offset),
17157850SVuong.Nguyen@Sun.COM 			    name_len);
17167850SVuong.Nguyen@Sun.COM 		}
17177850SVuong.Nguyen@Sun.COM 		break;
17187850SVuong.Nguyen@Sun.COM 	default:
17197850SVuong.Nguyen@Sun.COM 		rc = -1;
17207850SVuong.Nguyen@Sun.COM 		break;
17217850SVuong.Nguyen@Sun.COM 	}
17227850SVuong.Nguyen@Sun.COM 
17237850SVuong.Nguyen@Sun.COM 	lhp->freep(resp, resplen);
17247850SVuong.Nguyen@Sun.COM 	return (rc);
17257850SVuong.Nguyen@Sun.COM }
17267850SVuong.Nguyen@Sun.COM 
17272072Svn83148 /* end file */
1728