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
get_smf_int_val(char * prop_nm,int min,int max,int default_val)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
channel_close(struct ldmsvcs_info * lsp)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
read_stream(int fd,void * buf,size_t size)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
poller_handle_data(int fd,size_t payloadsize)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
poller_recv_data(struct ldom_hdl * lhp,uint64_t req_num,int index,void ** resp,size_t * resplen)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
poller_add_client(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
poller_remove_client(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
poller_add_pending(uint64_t req_num)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
poller_delete_pending(uint64_t req_num,int index)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
poller_shutdown(boolean_t wait)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 *
poller_loop(void * arg)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
poller_init(struct ldmsvcs_info * lsp)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
poller_fini(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
fds_send(struct ldmsvcs_info * lsp,void * msg,size_t msglen)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
fds_min_max_versions(uint16_t * min_major,uint16_t * max_major)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
fds_negotiate_version(uint16_t req_major,uint16_t * new_majorp,uint16_t * new_minorp)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
fds_svc_add(struct ldmsvcs_info * lsp,ds_reg_req_t * req,int minor)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
fds_svc_reset(struct ldmsvcs_info * lsp,int index)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
fds_svc_remove(struct ldmsvcs_info * lsp,ds_svc_hdl_t svc_handle)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
ds_handle_msg_noop(struct ldmsvcs_info * lsp,void * buf,size_t len)776 ds_handle_msg_noop(struct ldmsvcs_info *lsp, void *buf, size_t len)
777 {
778 }
779
780 static void
ds_handle_init_req(struct ldmsvcs_info * lsp,void * buf,size_t len)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
ds_handle_reg_req(struct ldmsvcs_info * lsp,void * buf,size_t len)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
ds_handle_unreg(struct ldmsvcs_info * lsp,void * buf,size_t len)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
fds_svc_alloc(struct ldmsvcs_info * lsp)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 *
fds_svc_lookup(struct ldmsvcs_info * lsp,char * name)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
fds_svc_req_num(void)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
read_msg(struct ldmsvcs_info * lsp)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
channel_openreset(struct ldmsvcs_info * lsp)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
channel_fini(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 *
channel_init(struct ldom_hdl * lhp)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
sendrecv(struct ldom_hdl * lhp,uint64_t req_num,void * msg,size_t msglen,ds_svc_hdl_t * svc_hdl,char * svcname,void ** resp,size_t * resplen)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
cpu_request(struct ldom_hdl * lhp,uint32_t msg_type,uint32_t cpuid)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
mem_request(struct ldom_hdl * lhp,uint32_t msg_type,uint64_t pa,uint64_t pgsize)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
ldmsvcs_check_channel(void)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
ldmsvcs_init(struct ldom_hdl * lhp)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
ldmsvcs_fini(struct ldom_hdl * lhp)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
ldmsvcs_get_core_md(struct ldom_hdl * lhp,uint64_t ** buf)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
ldmsvcs_cpu_req_status(struct ldom_hdl * lhp,uint32_t cpuid)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
ldmsvcs_cpu_req_offline(struct ldom_hdl * lhp,uint32_t cpuid)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
ldmsvcs_cpu_req_online(struct ldom_hdl * lhp,uint32_t cpuid)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
ldmsvcs_mem_req_status(struct ldom_hdl * lhp,uint64_t pa)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
ldmsvcs_mem_req_retire(struct ldom_hdl * lhp,uint64_t pa)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
ldmsvcs_mem_req_unretire(struct ldom_hdl * lhp,uint64_t pa)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
ldmsvcs_io_req_id(struct ldom_hdl * lhp,uint64_t addr,uint_t type,uint64_t * virt_addr,char * name,int name_len,uint64_t * did)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