xref: /illumos-gate/usr/src/lib/libpri/common/pri.c (revision 9164a50bf932130cbb5097a16f6986873ce0e6e5)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <sys/param.h>
28 #include <fcntl.h>
29 #include <poll.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <errno.h>
33 
34 #include "sys/ds_pri.h"
35 #include "pri.h"
36 
37 static int pri_fd = -1;
38 
39 
40 
41 /*
42  * Library init function
43  * Returns: Success (0), Failure (-1)
44  */
45 int
46 pri_init(void)
47 {
48 	int fd;
49 
50 	if (pri_fd != -1)
51 		return (-1);
52 
53 	fd = open(DS_PRI_DRIVER, O_RDONLY);
54 	if (fd < 0)
55 		return (-1);
56 
57 	pri_fd = fd;
58 
59 	return (0);
60 }
61 
62 /*
63  * Library fini function
64  * Returns: N/A
65  */
66 void
67 pri_fini(void)
68 {
69 	if (pri_fd < 0)
70 		return;
71 
72 	(void) close(pri_fd);
73 	pri_fd = -1;
74 }
75 
76 /*
77  * PRI retrieval function.
78  * Description:
79  *	- Library routine to retrieve the Physical Resource Inventory (PRI)
80  *	- Utilized by sun4v platforms which support Logical Domains
81  *	- Interacts with the ds_pri pseudo driver to retrieve the
82  *	  PRI. ds_pri driver in turn gets the PRI from the
83  *	  Domain Services kernel module. Domain Services gets the
84  *	  PRI from the Service Processor via LDC (Logical Domain
85  *	  Channel).
86  *	- Consumers of this api include FMA, Zeus, and picld
87  *	- MT-Safe, Stateless
88  *
89  * Imports:
90  *	- ds_pri driver interfaces
91  *
92  * Arguments:
93  *	- wait: specifies whether caller wants to wait for a new PRI,
94  *		PRI_GET is no-wait, PRI_WAITGET is wait-forever
95  *	- token: opaque PRI token, accepted from and/or returned to caller,
96  *		see write-only or read-write semantics below
97  *	- buf: PRI buffer received from ds_pri driver, returned to caller
98  *	- allocp: caller provided pointer to memory allocator function
99  *	- freep: caller provided pointer to memory free function
100  *
101  * Calling Semantics:
102  *	- PRI_GET call ignores the token passed in, and returns
103  *	  immediately with current PRI and its token (if any)
104  *	- PRI_WAITGET call returns only upon the receipt of a new PRI
105  *	  whose token differs from the token passed in by the caller;
106  *	  the passed in token should come from a previous pri_get()
107  *	  call with return value >= 0; the new PRI buffer and its token
108  *	  are returned to the caller
109  *	- If wait time must be bounded, the caller can spawn a thread
110  *	  which makes a PRI_WAITGET call; caller can choose to kill the
111  *	  spawned thread after a finite time
112  *
113  * Usage Semantics:
114  *	- Caller can use the returned PRI buffer as an argument to
115  *	  to md_init_intern() to process it into a machine
116  *	  descriptor (md_t) format
117  *	- Caller can choose to supply the same allocator and free
118  *	  functions to the md_init_intern() call
119  *	- Once the caller is done using these data structures,
120  *	  the following actions need to be performed by the caller:
121  *		- md_fini(mdp) if called md_init_intern()
122  *		- freep(bufp, size)
123  *
124  * Returns:
125  *	>0 if PRI is returned successfully (size of PRI buffer)
126  *	0 if no PRI is available
127  *	-1 if there is an error (errno contains the error code
128  *	provided)
129  *
130  */
131 ssize_t
132 pri_get(uint8_t wait, uint64_t *token, uint64_t **buf,
133 		void *(*allocp)(size_t), void (*freep)(void *, size_t))
134 {
135 	uint64_t		*bufp;		/* buf holding PRI */
136 	size_t			size;		/* sizeof PRI */
137 	struct dspri_info	pri_info;	/* info about PRI */
138 	struct dspri_info	pri_info2;	/* for PRI delta check */
139 
140 	if (pri_fd < 0) {
141 		errno = EBADF;
142 		return (-1);
143 	}
144 
145 	if (wait == PRI_WAITGET) {
146 		/* wait until have new PRI with different token */
147 		if (ioctl(pri_fd, DSPRI_WAIT, token) < 0) {
148 			return (-1);
149 		}
150 	}
151 
152 	do {
153 		/* get info on current PRI */
154 		if (ioctl(pri_fd, DSPRI_GETINFO, &pri_info) < 0) {
155 			return (-1);
156 		}
157 
158 		size = (size_t)pri_info.size;
159 
160 		/* check to see if no PRI available yet */
161 		if (size == 0) {
162 			*token = pri_info.token;
163 			return (0);
164 		}
165 
166 		/* allocate a buffer and read the PRI into it */
167 		if ((bufp = (uint64_t *)allocp(size)) == NULL) {
168 			if (errno == 0)
169 				errno = ENOMEM;
170 			return (-1);
171 		}
172 		if (read(pri_fd, bufp, size) < 0) {
173 			freep(bufp, size);
174 			return (-1);
175 		}
176 
177 		/*
178 		 * Check whether PRI token changed between the time
179 		 * we did the DSPRI_GETINFO ioctl() and the actual
180 		 * read() from the ds_pri driver. The token delta check
181 		 * tries to catch the above race condition; be sure
182 		 * to not leak memory on retries.
183 		 */
184 		if (ioctl(pri_fd, DSPRI_GETINFO, &pri_info2) < 0) {
185 			freep(bufp, size);
186 			return (-1);
187 		}
188 		if (pri_info2.token != pri_info.token)
189 			freep(bufp, size);
190 
191 	} while (pri_info2.token != pri_info.token);
192 
193 	/* return the PRI, its token, and its size to the caller */
194 	*buf = bufp;
195 	*token = pri_info.token;
196 	return ((ssize_t)size);
197 }
198