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