pri.c revision 4109:20d5b06dbce1
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#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <stdio.h>
29#include <sys/param.h>
30#include <fcntl.h>
31#include <poll.h>
32#include <string.h>
33#include <unistd.h>
34#include <errno.h>
35
36#include "sys/ds_pri.h"
37#include "pri.h"
38
39static int pri_fd = -1;
40
41
42
43/*
44 * Library init function
45 * Returns: Success (0), Failure (-1)
46 */
47int
48pri_init(void)
49{
50	int fd;
51
52	if (pri_fd != -1)
53		return (-1);
54
55	fd = open(DS_PRI_DRIVER, O_RDONLY);
56	if (fd < 0)
57		return (-1);
58
59	pri_fd = fd;
60
61	return (0);
62}
63
64/*
65 * Library fini function
66 * Returns: N/A
67 */
68void
69pri_fini(void)
70{
71	if (pri_fd < 0)
72		return;
73
74	(void) close(pri_fd);
75	pri_fd = -1;
76}
77
78/*
79 * PRI retrieval function.
80 * Description:
81 *	- Library routine to retrieve the Physical Resource Inventory (PRI)
82 *	- Utilized by sun4v platforms which support Logical Domains
83 *	- Interacts with the ds_pri pseudo driver to retrieve the
84 *	  PRI. ds_pri driver in turn gets the PRI from the
85 *	  Domain Services kernel module. Domain Services gets the
86 *	  PRI from the Service Processor via LDC (Logical Domain
87 *	  Channel).
88 *	- Consumers of this api include FMA, Zeus, and picld
89 *	- MT-Safe, Stateless
90 *
91 * Imports:
92 *	- ds_pri driver interfaces
93 *
94 * Arguments:
95 *	- wait: specifies whether caller wants to wait for a new PRI,
96 *		PRI_GET is no-wait, PRI_WAITGET is wait-forever
97 *	- token: opaque PRI token, accepted from and/or returned to caller,
98 *		see write-only or read-write semantics below
99 *	- buf: PRI buffer received from ds_pri driver, returned to caller
100 *	- allocp: caller provided pointer to memory allocator function
101 *	- freep: caller provided pointer to memory free function
102 *
103 * Calling Semantics:
104 *	- PRI_GET call ignores the token passed in, and returns
105 *	  immediately with current PRI and its token (if any)
106 *	- PRI_WAITGET call returns only upon the receipt of a new PRI
107 *	  whose token differs from the token passed in by the caller;
108 *	  the passed in token should come from a previous pri_get()
109 *	  call with return value >= 0; the new PRI buffer and its token
110 *	  are returned to the caller
111 *	- If wait time must be bounded, the caller can spawn a thread
112 *	  which makes a PRI_WAITGET call; caller can choose to kill the
113 *	  spawned thread after a finite time
114 *
115 * Usage Semantics:
116 *	- Caller can use the returned PRI buffer as an argument to
117 *	  to md_init_intern() to process it into a machine
118 *	  descriptor (md_t) format
119 *	- Caller can choose to supply the same allocator and free
120 *	  functions to the md_init_intern() call
121 *	- Once the caller is done using these data structures,
122 *	  the following actions need to be performed by the caller:
123 *		- md_fini(mdp) if called md_init_intern()
124 *		- freep(bufp, size)
125 *
126 * Returns:
127 *	>0 if PRI is returned successfully (size of PRI buffer)
128 *	0 if no PRI is available
129 *	-1 if there is an error (errno contains the error code
130 *	provided)
131 *
132 */
133ssize_t
134pri_get(uint8_t wait, uint64_t *token, uint64_t **buf,
135		void *(*allocp)(size_t), void (*freep)(void *, size_t))
136{
137	uint64_t		*bufp;		/* buf holding PRI */
138	size_t			size;		/* sizeof PRI */
139	struct dspri_info	pri_info;	/* info about PRI */
140	struct dspri_info	pri_info2;	/* for PRI delta check */
141
142	if (pri_fd < 0) {
143		errno = EBADF;
144		return (-1);
145	}
146
147	if (wait == PRI_WAITGET) {
148		/* wait until have new PRI with different token */
149		if (ioctl(pri_fd, DSPRI_WAIT, token) < 0) {
150			return (-1);
151		}
152	}
153
154	do {
155		/* get info on current PRI */
156		if (ioctl(pri_fd, DSPRI_GETINFO, &pri_info) < 0) {
157			return (-1);
158		}
159
160		size = (size_t)pri_info.size;
161
162		/* check to see if no PRI available yet */
163		if (size == 0) {
164			*token = pri_info.token;
165			return (0);
166		}
167
168		/* allocate a buffer and read the PRI into it */
169		if ((bufp = (uint64_t *)allocp(size)) == NULL) {
170			if (errno == 0)
171				errno = ENOMEM;
172			return (-1);
173		}
174		if (read(pri_fd, bufp, size) < 0) {
175			freep(bufp, size);
176			return (-1);
177		}
178
179		/*
180		 * Check whether PRI token changed between the time
181		 * we did the DSPRI_GETINFO ioctl() and the actual
182		 * read() from the ds_pri driver. The token delta check
183		 * tries to catch the above race condition; be sure
184		 * to not leak memory on retries.
185		 */
186		if (ioctl(pri_fd, DSPRI_GETINFO, &pri_info2) < 0) {
187			freep(bufp, size);
188			return (-1);
189		}
190		if (pri_info2.token != pri_info.token)
191			freep(bufp, size);
192
193	} while (pri_info2.token != pri_info.token);
194
195	/* return the PRI, its token, and its size to the caller */
196	*buf = bufp;
197	*token = pri_info.token;
198	return ((ssize_t)size);
199}
200