8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / fm / libfmd_agent / common / fmd_agent.c
blob928978fa177c8355d17379915dccdf55e6f5ab4a
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * libfmd_agent contains the low-level operations that needed by the fmd
29 * agents, such as page operations (status/retire/unretire), cpu operations
30 * (status/online/offline), etc.
32 * Some operations are implemented by /dev/fm ioctls. Those ioctls are
33 * heavily versioned to allow userland patching without requiring a reboot
34 * to get a matched /dev/fm. All the ioctls use packed nvlist to interact
35 * between userland and kernel. (see fmd_agent_nvl_ioctl()).
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <unistd.h>
41 #include <strings.h>
42 #include <libnvpair.h>
43 #include <string.h>
44 #include <sys/types.h>
45 #include <sys/devfm.h>
46 #include <fmd_agent_impl.h>
48 int
49 fmd_agent_errno(fmd_agent_hdl_t *hdl)
51 return (hdl->agent_errno);
54 int
55 fmd_agent_seterrno(fmd_agent_hdl_t *hdl, int err)
57 hdl->agent_errno = err;
58 return (-1);
61 const char *
62 fmd_agent_strerr(int err)
64 return (strerror(err));
67 const char *
68 fmd_agent_errmsg(fmd_agent_hdl_t *hdl)
70 return (fmd_agent_strerr(hdl->agent_errno));
73 static int
74 cleanup_set_errno(fmd_agent_hdl_t *hdl, nvlist_t *innvl, nvlist_t *outnvl,
75 int err)
77 nvlist_free(innvl);
78 nvlist_free(outnvl);
79 return (fmd_agent_seterrno(hdl, err));
83 * Perform /dev/fm ioctl. The input and output data are represented by
84 * name-value lists (nvlists).
86 int
87 fmd_agent_nvl_ioctl(fmd_agent_hdl_t *hdl, int cmd, uint32_t ver,
88 nvlist_t *innvl, nvlist_t **outnvlp)
90 fm_ioc_data_t fid;
91 int err = 0;
92 char *inbuf = NULL, *outbuf = NULL;
93 size_t insz = 0, outsz = 0;
95 if (innvl != NULL) {
96 if ((err = nvlist_size(innvl, &insz, NV_ENCODE_NATIVE)) != 0)
97 return (err);
98 if (insz > FM_IOC_MAXBUFSZ)
99 return (ENAMETOOLONG);
100 if ((inbuf = umem_alloc(insz, UMEM_DEFAULT)) == NULL)
101 return (errno);
103 if ((err = nvlist_pack(innvl, &inbuf, &insz,
104 NV_ENCODE_NATIVE, 0)) != 0) {
105 umem_free(inbuf, insz);
106 return (err);
110 if (outnvlp != NULL) {
111 outsz = FM_IOC_OUT_BUFSZ;
113 for (;;) {
114 if (outnvlp != NULL) {
115 outbuf = umem_alloc(outsz, UMEM_DEFAULT);
116 if (outbuf == NULL) {
117 err = errno;
118 break;
122 fid.fid_version = ver;
123 fid.fid_insz = insz;
124 fid.fid_inbuf = inbuf;
125 fid.fid_outsz = outsz;
126 fid.fid_outbuf = outbuf;
128 if (ioctl(hdl->agent_devfd, cmd, &fid) < 0) {
129 if (errno == ENAMETOOLONG && outsz != 0 &&
130 outsz < (FM_IOC_OUT_MAXBUFSZ / 2)) {
131 umem_free(outbuf, outsz);
132 outsz *= 2;
133 outbuf = umem_alloc(outsz, UMEM_DEFAULT);
134 if (outbuf == NULL) {
135 err = errno;
136 break;
138 } else {
139 err = errno;
140 break;
142 } else if (outnvlp != NULL) {
143 err = nvlist_unpack(fid.fid_outbuf, fid.fid_outsz,
144 outnvlp, 0);
145 break;
146 } else {
147 break;
151 if (inbuf != NULL)
152 umem_free(inbuf, insz);
153 if (outbuf != NULL)
154 umem_free(outbuf, outsz);
156 return (err);
160 * Open /dev/fm and return a handle. ver is the overall interface version.
162 static fmd_agent_hdl_t *
163 fmd_agent_open_dev(int ver, int mode)
165 fmd_agent_hdl_t *hdl;
166 int fd, err;
167 nvlist_t *nvl;
169 if ((fd = open("/dev/fm", mode)) < 0)
170 return (NULL); /* errno is set for us */
172 if ((hdl = umem_alloc(sizeof (fmd_agent_hdl_t),
173 UMEM_DEFAULT)) == NULL) {
174 err = errno;
175 (void) close(fd);
176 errno = err;
177 return (NULL);
180 hdl->agent_devfd = fd;
181 hdl->agent_version = ver;
184 * Get the individual interface versions.
186 if ((err = fmd_agent_nvl_ioctl(hdl, FM_IOC_VERSIONS, ver, NULL, &nvl))
187 < 0) {
188 (void) close(fd);
189 umem_free(hdl, sizeof (fmd_agent_hdl_t));
190 errno = err;
191 return (NULL);
194 hdl->agent_ioc_versions = nvl;
195 return (hdl);
198 fmd_agent_hdl_t *
199 fmd_agent_open(int ver)
201 if (ver > FMD_AGENT_VERSION) {
202 errno = ENOTSUP;
203 return (NULL);
205 return (fmd_agent_open_dev(ver, O_RDONLY));
208 void
209 fmd_agent_close(fmd_agent_hdl_t *hdl)
211 (void) close(hdl->agent_devfd);
212 nvlist_free(hdl->agent_ioc_versions);
213 umem_free(hdl, sizeof (fmd_agent_hdl_t));
217 * Given a interface name, return the kernel interface version.
220 fmd_agent_version(fmd_agent_hdl_t *hdl, const char *op, uint32_t *verp)
222 int err;
224 err = nvlist_lookup_uint32(hdl->agent_ioc_versions,
225 op, verp);
227 if (err != 0) {
228 errno = err;
229 return (-1);
231 return (0);
234 static int
235 fmd_agent_pageop_v1(fmd_agent_hdl_t *hdl, int cmd, nvlist_t *fmri)
237 int err;
238 nvlist_t *nvl = NULL;
240 if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0 ||
241 (err = nvlist_add_nvlist(nvl, FM_PAGE_RETIRE_FMRI, fmri)) != 0 ||
242 (err = fmd_agent_nvl_ioctl(hdl, cmd, 1, nvl, NULL)) != 0)
243 return (cleanup_set_errno(hdl, nvl, NULL, err));
245 nvlist_free(nvl);
246 return (0);
249 static int
250 fmd_agent_pageop(fmd_agent_hdl_t *hdl, int cmd, nvlist_t *fmri)
252 uint32_t ver;
254 if (fmd_agent_version(hdl, FM_PAGE_OP_VERSION, &ver) == -1)
255 return (fmd_agent_seterrno(hdl, errno));
257 switch (ver) {
258 case 1:
259 return (fmd_agent_pageop_v1(hdl, cmd, fmri));
261 default:
262 return (fmd_agent_seterrno(hdl, ENOTSUP));
267 fmd_agent_page_retire(fmd_agent_hdl_t *hdl, nvlist_t *fmri)
269 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_RETIRE, fmri);
270 int err = fmd_agent_errno(hdl);
273 * FM_IOC_PAGE_RETIRE ioctl returns:
274 * 0 - success in retiring page
275 * -1, errno = EIO - page is already retired
276 * -1, errno = EAGAIN - page is scheduled for retirement
277 * -1, errno = EINVAL - page fmri is invalid
278 * -1, errno = any else - error
280 if (rc == 0 || err == EIO || err == EINVAL) {
281 if (rc == 0)
282 (void) fmd_agent_seterrno(hdl, 0);
283 return (FMD_AGENT_RETIRE_DONE);
285 if (err == EAGAIN)
286 return (FMD_AGENT_RETIRE_ASYNC);
288 return (FMD_AGENT_RETIRE_FAIL);
292 fmd_agent_page_unretire(fmd_agent_hdl_t *hdl, nvlist_t *fmri)
294 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_UNRETIRE, fmri);
295 int err = fmd_agent_errno(hdl);
298 * FM_IOC_PAGE_UNRETIRE ioctl returns:
299 * 0 - success in unretiring page
300 * -1, errno = EIO - page is already unretired
301 * -1, errno = EAGAIN - page couldn't be locked, still retired
302 * -1, errno = EINVAL - page fmri is invalid
303 * -1, errno = any else - error
305 if (rc == 0 || err == EIO || err == EINVAL) {
306 if (rc == 0)
307 (void) fmd_agent_seterrno(hdl, 0);
308 return (FMD_AGENT_RETIRE_DONE);
311 return (FMD_AGENT_RETIRE_FAIL);
315 fmd_agent_page_isretired(fmd_agent_hdl_t *hdl, nvlist_t *fmri)
317 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_STATUS, fmri);
318 int err = fmd_agent_errno(hdl);
321 * FM_IOC_PAGE_STATUS returns:
322 * 0 - page is retired
323 * -1, errno = EAGAIN - page is scheduled for retirement
324 * -1, errno = EIO - page not scheduled for retirement
325 * -1, errno = EINVAL - page fmri is invalid
326 * -1, errno = any else - error
328 if (rc == 0 || err == EINVAL) {
329 if (rc == 0)
330 (void) fmd_agent_seterrno(hdl, 0);
331 return (FMD_AGENT_RETIRE_DONE);
333 if (err == EAGAIN)
334 return (FMD_AGENT_RETIRE_ASYNC);
336 return (FMD_AGENT_RETIRE_FAIL);