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