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