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]
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()).
42 #include <libnvpair.h>
44 #include <sys/types.h>
45 #include <sys/devfm.h>
46 #include <fmd_agent_impl.h>
49 fmd_agent_errno(fmd_agent_hdl_t
*hdl
)
51 return (hdl
->agent_errno
);
55 fmd_agent_seterrno(fmd_agent_hdl_t
*hdl
, int err
)
57 hdl
->agent_errno
= err
;
62 fmd_agent_strerr(int err
)
64 return (strerror(err
));
68 fmd_agent_errmsg(fmd_agent_hdl_t
*hdl
)
70 return (fmd_agent_strerr(hdl
->agent_errno
));
74 cleanup_set_errno(fmd_agent_hdl_t
*hdl
, nvlist_t
*innvl
, nvlist_t
*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).
87 fmd_agent_nvl_ioctl(fmd_agent_hdl_t
*hdl
, int cmd
, uint32_t ver
,
88 nvlist_t
*innvl
, nvlist_t
**outnvlp
)
92 char *inbuf
= NULL
, *outbuf
= NULL
;
93 size_t insz
= 0, outsz
= 0;
96 if ((err
= nvlist_size(innvl
, &insz
, NV_ENCODE_NATIVE
)) != 0)
98 if (insz
> FM_IOC_MAXBUFSZ
)
99 return (ENAMETOOLONG
);
100 if ((inbuf
= umem_alloc(insz
, UMEM_DEFAULT
)) == NULL
)
103 if ((err
= nvlist_pack(innvl
, &inbuf
, &insz
,
104 NV_ENCODE_NATIVE
, 0)) != 0) {
105 umem_free(inbuf
, insz
);
110 if (outnvlp
!= NULL
) {
111 outsz
= FM_IOC_OUT_BUFSZ
;
114 if (outnvlp
!= NULL
) {
115 outbuf
= umem_alloc(outsz
, UMEM_DEFAULT
);
116 if (outbuf
== NULL
) {
122 fid
.fid_version
= ver
;
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
);
133 outbuf
= umem_alloc(outsz
, UMEM_DEFAULT
);
134 if (outbuf
== NULL
) {
142 } else if (outnvlp
!= NULL
) {
143 err
= nvlist_unpack(fid
.fid_outbuf
, fid
.fid_outsz
,
152 umem_free(inbuf
, insz
);
154 umem_free(outbuf
, outsz
);
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
;
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
) {
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
))
189 umem_free(hdl
, sizeof (fmd_agent_hdl_t
));
194 hdl
->agent_ioc_versions
= nvl
;
199 fmd_agent_open(int ver
)
201 if (ver
> FMD_AGENT_VERSION
) {
205 return (fmd_agent_open_dev(ver
, O_RDONLY
));
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
)
224 err
= nvlist_lookup_uint32(hdl
->agent_ioc_versions
,
235 fmd_agent_pageop_v1(fmd_agent_hdl_t
*hdl
, int cmd
, nvlist_t
*fmri
)
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
));
250 fmd_agent_pageop(fmd_agent_hdl_t
*hdl
, int cmd
, nvlist_t
*fmri
)
254 if (fmd_agent_version(hdl
, FM_PAGE_OP_VERSION
, &ver
) == -1)
255 return (fmd_agent_seterrno(hdl
, errno
));
259 return (fmd_agent_pageop_v1(hdl
, cmd
, fmri
));
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
) {
282 (void) fmd_agent_seterrno(hdl
, 0);
283 return (FMD_AGENT_RETIRE_DONE
);
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
) {
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
) {
330 (void) fmd_agent_seterrno(hdl
, 0);
331 return (FMD_AGENT_RETIRE_DONE
);
334 return (FMD_AGENT_RETIRE_ASYNC
);
336 return (FMD_AGENT_RETIRE_FAIL
);