xref: /titanic_41/usr/src/lib/fm/libldom/sparc/ldmsvcs_utils.c (revision fe598cdcd847f8359013532d5c691bb6190378c0)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <strings.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <time.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <assert.h>
38 #include <umem.h>
39 #include <alloca.h>
40 #include <sys/processor.h>
41 #include <poll.h>
42 #include <pthread.h>
43 #include <values.h>
44 #include <libscf.h>
45 
46 #include "ldmsvcs_utils.h"
47 
48 #define	ASSERT(cnd) \
49 	((void) ((cnd) || ((void) fprintf(stderr, \
50 		"assertion failure in %s:%d: %s\n", \
51 		__FILE__, __LINE__, #cnd), 0)))
52 
53 #define	FDS_VLDC \
54 	"/devices/virtual-devices@100/channel-devices@200/" \
55 	"/virtual-channel-client@1:ldmfma"
56 
57 /* allow timeouts in sec that are nearly forever but small enough for an int */
58 #define	LDM_TIMEOUT_CEILING	(MAXINT / 2)
59 
60 #define	MIN(x, y)	((x) < (y) ? (x) : (y))
61 
62 /*
63  * functions in this file are for version 1.0 of FMA domain services
64  */
65 static ds_ver_t ds_vers[] = {
66 	{ 1, 0 }
67 };
68 
69 #define	DS_NUM_VER	(sizeof (ds_vers) / sizeof (ds_ver_t))
70 
71 /*
72  * information for each channel
73  */
74 struct ldmsvcs_info {
75 	pthread_mutex_t mt;
76 	pthread_cond_t cv;
77 	fds_channel_t fds_chan;
78 	fds_reg_svcs_t fmas_svcs;
79 	int cv_twait;
80 };
81 
82 /*
83  * struct listdata_s and struct poller_s are used to maintain the state of
84  * the poller thread.  this thread is used to manage incoming messages and
85  * pass those messages onto the correct requesting thread.  see the "poller
86  * functions" section for more details.
87  */
88 struct listdata_s {
89 	enum {
90 		UNUSED,
91 		PENDING,
92 		ARRIVED
93 	} status;
94 	uint64_t req_num;
95 	int fd;
96 	size_t datalen;
97 };
98 
99 static struct poller_s {
100 	pthread_mutex_t mt;
101 	pthread_cond_t cv;
102 	pthread_t polling_tid;
103 	int doreset;
104 	int doexit;
105 	int nclients;
106 	struct listdata_s **list;
107 	int list_len;
108 	int pending_count;
109 } pollbase = {
110 	PTHREAD_MUTEX_INITIALIZER,
111 	PTHREAD_COND_INITIALIZER,
112 	0,
113 	1,
114 	0,
115 	0,
116 	NULL,
117 	0,
118 	0
119 };
120 
121 
122 static struct ldmsvcs_info *channel_init(struct ldom_hdl *lhp);
123 static int channel_openreset(struct ldmsvcs_info *lsp);
124 static int read_msg(struct ldmsvcs_info *lsp);
125 
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 	char *name[] = { "fma-phys-cpu-service", "fma-phys-mem-service",
936 			"fma-pri-service", 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 	ier = 0;
978 	twait.tv_sec = time(NULL) + lsp->cv_twait;
979 	twait.tv_nsec = 0;
980 
981 	while (svc->state != DS_SVC_ACTIVE && ier == 0 &&
982 	    lsp->fds_chan.state != CHANNEL_UNUSABLE)
983 		ier = pthread_cond_timedwait(&lsp->fmas_svcs.cv,
984 		    &lsp->fmas_svcs.mt, &twait);
985 
986 	(void) pthread_mutex_unlock(&lsp->fmas_svcs.mt);
987 
988 	if (ier == 0)
989 		return (svc);
990 	else
991 		return (NULL);
992 }
993 
994 
995 static uint64_t
996 fds_svc_req_num(void)
997 {
998 	static uint64_t req_num = 1;
999 
1000 	return (req_num++);
1001 }
1002 
1003 
1004 /*
1005  * return 0 if successful, 1 if otherwise
1006  */
1007 static int
1008 read_msg(struct ldmsvcs_info *lsp)
1009 {
1010 	ds_hdr_t header;
1011 	void *msg_buf;
1012 
1013 	/*
1014 	 * read the header
1015 	 */
1016 	if (read_stream(lsp->fds_chan.fd, &header, sizeof (ds_hdr_t)) != 0)
1017 		return (1);
1018 
1019 	if (header.msg_type >=
1020 	    sizeof (ds_msg_handlers) / sizeof (ds_msg_handler_t))
1021 		return (1);
1022 
1023 	/*
1024 	 * handle data as a special case
1025 	 */
1026 	if (header.msg_type == 9)
1027 		return (poller_handle_data(lsp->fds_chan.fd,
1028 		    header.payload_len));
1029 
1030 	/*
1031 	 * all other types of messages should be small
1032 	 */
1033 	ASSERT(header.payload_len < 1024);
1034 	msg_buf = alloca(header.payload_len);
1035 
1036 	/*
1037 	 * read the payload
1038 	 */
1039 	if (read_stream(lsp->fds_chan.fd, msg_buf, header.payload_len) != 0)
1040 		return (1);
1041 
1042 	(*ds_msg_handlers[header.msg_type])(lsp, msg_buf, header.payload_len);
1043 
1044 	return (0);
1045 }
1046 
1047 
1048 /*
1049  * return values:
1050  *  0 - success
1051  *  1 - problem with opening the channel
1052  *  2 - channed not opened; request to exit has been detected
1053  */
1054 static int
1055 channel_openreset(struct ldmsvcs_info *lsp)
1056 {
1057 	int ier;
1058 
1059 	ier = pthread_mutex_lock(&lsp->mt);
1060 
1061 	if (ier == EINVAL || lsp->fds_chan.state == CHANNEL_EXIT ||
1062 	    lsp->fds_chan.state == CHANNEL_UNUSABLE) {
1063 		(void) pthread_mutex_unlock(&lsp->mt);
1064 		return (2);
1065 	}
1066 
1067 	if (lsp->fds_chan.state == CHANNEL_UNINITIALIZED ||
1068 	    lsp->fds_chan.state == CHANNEL_CLOSED) {
1069 		(void) pthread_cond_broadcast(&lsp->cv);
1070 
1071 		if ((lsp->fds_chan.fd = open(FDS_VLDC, O_RDWR)) < 0) {
1072 			lsp->fds_chan.state = CHANNEL_UNUSABLE;
1073 			lsp->cv_twait = get_smf_int_val(LDM_RUNNING_TO_PROP_NM,
1074 			    0, LDM_TIMEOUT_CEILING, LDM_RUNNING_WAIT_TIME);
1075 			(void) pthread_mutex_unlock(&lsp->mt);
1076 			(void) pthread_cond_broadcast(&lsp->fmas_svcs.cv);
1077 
1078 			return (2);
1079 		} else {
1080 			vldc_opt_op_t op;
1081 
1082 			op.op_sel = VLDC_OP_SET;
1083 			op.opt_sel = VLDC_OPT_MODE;
1084 			op.opt_val = LDC_MODE_STREAM;
1085 
1086 			if (ioctl(lsp->fds_chan.fd, VLDC_IOCTL_OPT_OP,
1087 			    &op) != 0) {
1088 				(void) close(lsp->fds_chan.fd);
1089 				(void) pthread_mutex_unlock(&lsp->mt);
1090 				return (1);
1091 			}
1092 		}
1093 		lsp->fds_chan.state = CHANNEL_OPEN;
1094 	}
1095 
1096 	if (lsp->fds_chan.state == CHANNEL_OPEN) {
1097 		/*
1098 		 * reset various channel parameters
1099 		 */
1100 		lsp->fds_chan.ver.major = 0;
1101 		lsp->fds_chan.ver.minor = 0;
1102 		fds_svc_reset(lsp, -1);
1103 	}
1104 	(void) pthread_mutex_unlock(&lsp->mt);
1105 
1106 	return (0);
1107 }
1108 
1109 
1110 static void
1111 channel_fini(void)
1112 {
1113 	struct ldmsvcs_info *lsp;
1114 
1115 	/*
1116 	 * End the poller thread
1117 	 */
1118 	poller_shutdown();
1119 
1120 	if ((lsp = channel_init(NULL)) == NULL)
1121 		return;
1122 
1123 	(void) pthread_mutex_lock(&lsp->mt);
1124 
1125 	lsp->fds_chan.state = CHANNEL_EXIT;
1126 	(void) close(lsp->fds_chan.fd);
1127 
1128 	(void) pthread_mutex_unlock(&lsp->mt);
1129 }
1130 
1131 
1132 static struct ldmsvcs_info *
1133 channel_init(struct ldom_hdl *lhp)
1134 {
1135 	static pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER;
1136 	static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
1137 	static struct ldmsvcs_info *root = NULL;
1138 	static int busy_init = 0;
1139 
1140 	struct timespec twait;
1141 	int expired;
1142 
1143 	(void) pthread_mutex_lock(&mt);
1144 
1145 	while (busy_init == 1)
1146 		(void) pthread_cond_wait(&cv, &mt);
1147 
1148 	if (root != NULL || (lhp == NULL && root == NULL)) {
1149 		(void) pthread_mutex_unlock(&mt);
1150 		return (root);
1151 	}
1152 
1153 	/*
1154 	 * get to this point if we need to open the channel
1155 	 */
1156 	busy_init = 1;
1157 	(void) pthread_mutex_unlock(&mt);
1158 
1159 	root = (struct ldmsvcs_info *)
1160 	    lhp->allocp(sizeof (struct ldmsvcs_info));
1161 	bzero(root, sizeof (struct ldmsvcs_info));
1162 
1163 	root->fds_chan.state = CHANNEL_UNINITIALIZED;
1164 	root->cv_twait = get_smf_int_val(LDM_INIT_TO_PROP_NM,
1165 	    0, LDM_TIMEOUT_CEILING, LDM_INIT_WAIT_TIME);
1166 
1167 	if (pthread_mutex_init(&root->mt, NULL) != 0 ||
1168 	    pthread_cond_init(&root->cv, NULL) != 0) {
1169 		lhp->freep(root, sizeof (struct ldmsvcs_info));
1170 		return (NULL);
1171 	}
1172 
1173 	fds_svc_alloc(lhp, root);
1174 	fds_svc_reset(root, -1);
1175 
1176 	(void) poller_init(root);
1177 
1178 	expired = 0;
1179 	twait.tv_sec = time(NULL) + 10;
1180 	twait.tv_nsec = 0;
1181 
1182 	(void) pthread_mutex_lock(&root->mt);
1183 
1184 	/*
1185 	 * wait for channel to become uninitialized.  this should be quick.
1186 	 */
1187 	while (root->fds_chan.state == CHANNEL_UNINITIALIZED && expired == 0)
1188 		expired = pthread_cond_timedwait(&root->cv, &root->mt, &twait);
1189 
1190 	if (root->fds_chan.state == CHANNEL_UNUSABLE)
1191 		expired = 1;
1192 
1193 	(void) pthread_mutex_unlock(&root->mt);
1194 
1195 	(void) pthread_mutex_lock(&mt);
1196 	busy_init = 0;
1197 	(void) pthread_mutex_unlock(&mt);
1198 	(void) pthread_cond_broadcast(&cv);
1199 
1200 	(void) atexit(channel_fini);
1201 
1202 	if (expired == 0)
1203 		return (root);
1204 	else
1205 		return (NULL);
1206 }
1207 
1208 
1209 static int
1210 sendrecv(struct ldom_hdl *lhp, uint64_t req_num,
1211 	void *msg, size_t msglen, ds_svc_hdl_t *svc_hdl, char *svcname,
1212 	void **resp, size_t *resplen)
1213 {
1214 	struct ldmsvcs_info *lsp;
1215 	fds_svc_t *svc;
1216 	int maxretries, index, i, ier;
1217 
1218 	lsp = lhp->lsinfo;
1219 	i = 0;
1220 	maxretries = 1;
1221 
1222 	do {
1223 		/*
1224 		 * if any of the calls in this loop fail, retry some number
1225 		 * of times before giving up.
1226 		 */
1227 		if ((svc = fds_svc_lookup(lsp, svcname)) == NULL) {
1228 			(void) pthread_mutex_lock(&lsp->mt);
1229 
1230 			if (lsp->fds_chan.state != CHANNEL_READY)
1231 				ier = ETIMEDOUT;	/* channel not ready */
1232 			else
1233 				ier = ENOTSUP;		/* service not ready */
1234 
1235 			(void) pthread_mutex_unlock(&lsp->mt);
1236 
1237 			continue;
1238 		} else {
1239 			ier = 0;
1240 			*svc_hdl = svc->hdl;
1241 		}
1242 
1243 		index = poller_add_pending(lhp, req_num);
1244 
1245 		if ((ier = fds_send(lsp, msg, msglen)) != 0 ||
1246 		    (ier = poller_recv_data(lhp, req_num, index, resp,
1247 		    resplen)) != 0)
1248 			poller_delete_pending(req_num, index);
1249 
1250 	} while (i++ < maxretries && ier != 0);
1251 
1252 	ASSERT(ier == 0 || ier == ETIMEDOUT || ier == ENOTSUP);
1253 
1254 	return (ier);
1255 }
1256 
1257 
1258 /*
1259  * input:
1260  *   msg_type - requested operation: FMA_CPU_REQ_STATUS or FMA_CPU_REQ_OFFLINE
1261  *   cpuid - physical cpu id
1262  *
1263  * normal return values:
1264  *   P_OFFLINE - cpu is offline
1265  *   P_ONLINE - cpu is online
1266  *
1267  * abnormal return values:
1268  *   ETIMEDOUT - LDOM manager is not responding
1269  *   ENOTSUP - LDOM service for cpu offlining/status is not available
1270  *   ENOMSG - got an unexpected response from the LDOM cpu service
1271  */
1272 static int
1273 cpu_request(struct ldom_hdl *lhp, uint32_t msg_type, uint32_t cpuid)
1274 {
1275 	ds_hdr_t *H;
1276 	ds_data_handle_t *D;
1277 	fma_cpu_service_req_t *R;
1278 
1279 	char *svcname = "fma-phys-cpu-service";
1280 	fma_cpu_resp_t *respmsg;
1281 	void *resp;
1282 	size_t resplen, reqmsglen;
1283 	int rc;
1284 
1285 	if (lhp->lsinfo == NULL)
1286 		return (ENOMSG);
1287 
1288 	reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
1289 	    sizeof (fma_cpu_service_req_t);
1290 
1291 	H = lhp->allocp(reqmsglen);
1292 	D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
1293 	R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
1294 
1295 	H->msg_type = DS_DATA;
1296 	H->payload_len = sizeof (ds_data_handle_t) +
1297 	    sizeof (fma_cpu_service_req_t);
1298 
1299 	R->req_num = fds_svc_req_num();
1300 	R->msg_type = msg_type;
1301 	R->cpu_id = cpuid;
1302 
1303 	if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
1304 	    &D->svc_handle, svcname, &resp, &resplen)) != 0) {
1305 		lhp->freep(H, reqmsglen);
1306 		return (rc);
1307 	}
1308 
1309 	lhp->freep(H, reqmsglen);
1310 
1311 	ASSERT(resplen == sizeof (fma_cpu_resp_t));
1312 	respmsg = (fma_cpu_resp_t *)resp;
1313 
1314 	rc = ENOMSG;
1315 	if (respmsg->result == FMA_CPU_RESP_OK) {
1316 		if (respmsg->status == FMA_CPU_STAT_ONLINE)
1317 			rc = P_ONLINE;
1318 		else if (respmsg->status == FMA_CPU_STAT_OFFLINE)
1319 			rc = P_OFFLINE;
1320 	} else {
1321 		if (msg_type == FMA_CPU_REQ_OFFLINE &&
1322 		    respmsg->status == FMA_CPU_STAT_OFFLINE)
1323 			rc = P_OFFLINE;
1324 	}
1325 
1326 	lhp->freep(resp, resplen);
1327 
1328 	return (rc);
1329 }
1330 
1331 
1332 /*
1333  * input:
1334  *   msg_type - requested operation: FMA_MEM_REQ_STATUS or FMA_MEM_REQ_RETIRE
1335  *   pa - starting address of memory page
1336  *   pgsize - memory page size in bytes
1337  *
1338  * normal return values for msg_type == FMA_MEM_REQ_STATUS:
1339  *   0 - page is retired
1340  *   EAGAIN - page is scheduled for retirement
1341  *   EIO - page not scheduled for retirement
1342  *   EINVAL - error
1343  *
1344  * normal return values for msg_type == FMA_MEM_REQ_RETIRE:
1345  *   0 - success in retiring page
1346  *   EIO - page is already retired
1347  *   EAGAIN - page is scheduled for retirement
1348  *   EINVAL - error
1349  *
1350  * abnormal return values (regardless of msg_type)
1351  *   ETIMEDOUT - LDOM manager is not responding
1352  *   ENOTSUP - LDOM service for cpu offlining/status is not available
1353  *   ENOMSG - got an unexpected response from the LDOM cpu service
1354  */
1355 static int
1356 mem_request(struct ldom_hdl *lhp, uint32_t msg_type, uint64_t pa,
1357 	    uint64_t pgsize)
1358 {
1359 	ds_hdr_t *H;
1360 	ds_data_handle_t *D;
1361 	fma_mem_service_req_t *R;
1362 
1363 	char *svcname = "fma-phys-mem-service";
1364 	fma_mem_resp_t *respmsg;
1365 	void *resp;
1366 	size_t resplen, reqmsglen;
1367 	int rc;
1368 
1369 	if (lhp->lsinfo == NULL)
1370 		return (ENOMSG);
1371 
1372 	reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
1373 	    sizeof (fma_mem_service_req_t);
1374 
1375 	H = lhp->allocp(reqmsglen);
1376 	bzero(H, reqmsglen);
1377 	D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
1378 	R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
1379 
1380 	H->msg_type = DS_DATA;
1381 	H->payload_len = sizeof (ds_data_handle_t) +
1382 	    sizeof (fma_mem_service_req_t);
1383 
1384 	R->req_num = fds_svc_req_num();
1385 	R->msg_type = msg_type;
1386 	R->real_addr = pa;
1387 	R->length = pgsize;
1388 
1389 	if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
1390 	    &D->svc_handle, svcname, &resp, &resplen)) != 0) {
1391 		lhp->freep(H, reqmsglen);
1392 		return (rc);
1393 	}
1394 
1395 	lhp->freep(H, reqmsglen);
1396 
1397 	ASSERT(resplen == sizeof (fma_mem_resp_t));
1398 	respmsg = (fma_mem_resp_t *)resp;
1399 
1400 	rc = ENOMSG;
1401 	if (msg_type == FMA_MEM_REQ_STATUS) {
1402 		if (respmsg->result == FMA_MEM_RESP_OK) {
1403 			if (respmsg->status == FMA_MEM_STAT_RETIRED)
1404 				rc = 0;		/* page is retired */
1405 			else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
1406 				rc = EIO;	/* page is not scheduled */
1407 		} else if (respmsg->result == FMA_MEM_RESP_FAILURE) {
1408 			if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
1409 				rc = EAGAIN;	/* page is scheduled */
1410 			else if (respmsg->status == FMA_MEM_STAT_ILLEGAL)
1411 				rc = EINVAL;
1412 		}
1413 	} else if (msg_type == FMA_MEM_REQ_RETIRE) {
1414 		if (respmsg->result == FMA_MEM_RESP_OK) {
1415 			if (respmsg->status == FMA_MEM_STAT_RETIRED)
1416 				rc = 0;		/* is successfully retired */
1417 		} else if (respmsg->result == FMA_MEM_RESP_FAILURE) {
1418 			if (respmsg->status == FMA_MEM_STAT_RETIRED)
1419 				rc = EIO;	/* is already retired */
1420 			else if (respmsg->status == FMA_MEM_STAT_NOTRETIRED)
1421 				rc = EAGAIN;	/* is scheduled to retire */
1422 			else if (respmsg->status == FMA_MEM_STAT_ILLEGAL)
1423 				rc = EINVAL;
1424 		}
1425 	}
1426 
1427 	lhp->freep(resp, resplen);
1428 
1429 	return (rc);
1430 }
1431 
1432 
1433 /*
1434  * APIs
1435  */
1436 int
1437 ldmsvcs_check_channel(void)
1438 {
1439 	struct stat buf;
1440 
1441 	if (stat(FDS_VLDC, &buf) == 0)
1442 		return (0);	/* vldc exists */
1443 	else if (errno == ENOENT || errno == ENOTDIR)
1444 		return (1);	/* vldc does not exist */
1445 	else
1446 		return (-1);	/* miscellaneous error */
1447 }
1448 
1449 
1450 /*ARGSUSED*/
1451 void
1452 ldmsvcs_init(struct ldom_hdl *lhp)
1453 {
1454 	if (ldmsvcs_check_channel() != 0)
1455 		return;
1456 
1457 	lhp->lsinfo = channel_init(lhp);
1458 	poller_add_client();
1459 }
1460 
1461 
1462 /*ARGSUSED*/
1463 void
1464 ldmsvcs_fini(struct ldom_hdl *lhp)
1465 {
1466 	if (ldmsvcs_check_channel() != 0)
1467 		return;
1468 
1469 	poller_remove_client();
1470 }
1471 
1472 
1473 /*ARGSUSED*/
1474 ssize_t
1475 ldmsvcs_get_core_md(struct ldom_hdl *lhp, uint64_t **buf)
1476 {
1477 	ds_hdr_t *H;
1478 	ds_data_handle_t *D;
1479 	fma_req_pri_t *R;
1480 
1481 	char *svcname = "fma-pri-service";
1482 	void *resp;
1483 	size_t resplen, reqmsglen;
1484 	ssize_t buflen;
1485 	int rc;
1486 
1487 	if (lhp->lsinfo == NULL)
1488 		return (-1);
1489 
1490 	reqmsglen = sizeof (ds_hdr_t) + sizeof (ds_data_handle_t) +
1491 	    sizeof (fma_req_pri_t);
1492 
1493 	H = lhp->allocp(reqmsglen);
1494 	D = (void *)((ptrdiff_t)H + sizeof (ds_hdr_t));
1495 	R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
1496 
1497 	H->msg_type = DS_DATA;
1498 	H->payload_len = sizeof (ds_data_handle_t) +
1499 	    sizeof (fma_req_pri_t);
1500 
1501 	R->req_num = fds_svc_req_num();
1502 
1503 	if ((rc = sendrecv(lhp, R->req_num, H, reqmsglen,
1504 	    &D->svc_handle, svcname, &resp, &resplen)) != 0) {
1505 		lhp->freep(H, reqmsglen);
1506 		errno = rc;
1507 		return (-1);
1508 	}
1509 
1510 	lhp->freep(H, reqmsglen);
1511 
1512 	/*
1513 	 * resp should contain the req_num immediately followed by the PRI
1514 	 * (the latter may or may not be present).  unfortunately, the
1515 	 * current compiler flags cause a warning for the following
1516 	 * definition
1517 	 *
1518 	 * typedef struct {
1519 	 *    uint64_t req_num;
1520 	 *    uint8_t pri[];
1521 	 *  } fma_pri_resp_t;
1522 	 *
1523 	 * so we do not use the struct here.
1524 	 */
1525 	if (resplen <= sizeof (uint64_t)) {
1526 		lhp->freep(resp, resplen);
1527 		if (resplen == sizeof (uint64_t))
1528 			return (0);
1529 		else
1530 			return (-1);
1531 	}
1532 
1533 	buflen = resplen - sizeof (uint64_t);
1534 	*buf = lhp->allocp(buflen);
1535 
1536 	bcopy((void *)((ptrdiff_t)resp + sizeof (uint64_t)), *buf, buflen);
1537 	lhp->freep(resp, resplen);
1538 
1539 	return (buflen);
1540 }
1541 
1542 
1543 /*
1544  * see cpu_request() for a description of return values
1545  */
1546 int
1547 ldmsvcs_cpu_req_status(struct ldom_hdl *lhp, uint32_t cpuid)
1548 {
1549 	return (cpu_request(lhp, FMA_CPU_REQ_STATUS, cpuid));
1550 }
1551 
1552 
1553 int
1554 ldmsvcs_cpu_req_offline(struct ldom_hdl *lhp, uint32_t cpuid)
1555 {
1556 	return (cpu_request(lhp, FMA_CPU_REQ_OFFLINE, cpuid));
1557 }
1558 
1559 
1560 /*
1561  * see mem_request() for a description of return values
1562  */
1563 int
1564 ldmsvcs_mem_req_status(struct ldom_hdl *lhp, uint64_t pa)
1565 {
1566 	return (mem_request(lhp, FMA_MEM_REQ_STATUS, pa, getpagesize()));
1567 }
1568 
1569 
1570 int
1571 ldmsvcs_mem_req_retire(struct ldom_hdl *lhp, uint64_t pa)
1572 {
1573 	return (mem_request(lhp, FMA_MEM_REQ_RETIRE, pa, getpagesize()));
1574 }
1575 
1576 /* end file */
1577