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