xref: /onnv-gate/usr/src/lib/fm/libfmd_agent/common/fmd_agent.c (revision 11264:99b4beb4e9f6)
17532SSean.Ye@Sun.COM /*
27532SSean.Ye@Sun.COM  * CDDL HEADER START
37532SSean.Ye@Sun.COM  *
47532SSean.Ye@Sun.COM  * The contents of this file are subject to the terms of the
57532SSean.Ye@Sun.COM  * Common Development and Distribution License (the "License").
67532SSean.Ye@Sun.COM  * You may not use this file except in compliance with the License.
77532SSean.Ye@Sun.COM  *
87532SSean.Ye@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97532SSean.Ye@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107532SSean.Ye@Sun.COM  * See the License for the specific language governing permissions
117532SSean.Ye@Sun.COM  * and limitations under the License.
127532SSean.Ye@Sun.COM  *
137532SSean.Ye@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147532SSean.Ye@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157532SSean.Ye@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167532SSean.Ye@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177532SSean.Ye@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187532SSean.Ye@Sun.COM  *
197532SSean.Ye@Sun.COM  * CDDL HEADER END
207532SSean.Ye@Sun.COM  */
217532SSean.Ye@Sun.COM 
227532SSean.Ye@Sun.COM /*
23*11264SAdrian.Frost@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247532SSean.Ye@Sun.COM  * Use is subject to license terms.
257532SSean.Ye@Sun.COM  */
267532SSean.Ye@Sun.COM 
277532SSean.Ye@Sun.COM /*
287532SSean.Ye@Sun.COM  * libfmd_agent contains the low-level operations that needed by the fmd
297532SSean.Ye@Sun.COM  * agents, such as page operations (status/retire/unretire), cpu operations
307532SSean.Ye@Sun.COM  * (status/online/offline), etc.
317532SSean.Ye@Sun.COM  *
327532SSean.Ye@Sun.COM  * Some operations are implemented by /dev/fm ioctls.  Those ioctls are
337532SSean.Ye@Sun.COM  * heavily versioned to allow userland patching without requiring a reboot
347532SSean.Ye@Sun.COM  * to get a matched /dev/fm.   All the ioctls use packed nvlist to interact
357532SSean.Ye@Sun.COM  * between userland and kernel.  (see fmd_agent_nvl_ioctl()).
367532SSean.Ye@Sun.COM  */
377532SSean.Ye@Sun.COM 
387532SSean.Ye@Sun.COM #include <fcntl.h>
397532SSean.Ye@Sun.COM #include <errno.h>
407532SSean.Ye@Sun.COM #include <unistd.h>
417532SSean.Ye@Sun.COM #include <strings.h>
427532SSean.Ye@Sun.COM #include <libnvpair.h>
437532SSean.Ye@Sun.COM #include <string.h>
447532SSean.Ye@Sun.COM #include <sys/types.h>
457532SSean.Ye@Sun.COM #include <sys/devfm.h>
467532SSean.Ye@Sun.COM #include <fmd_agent_impl.h>
477532SSean.Ye@Sun.COM 
487532SSean.Ye@Sun.COM int
fmd_agent_errno(fmd_agent_hdl_t * hdl)497532SSean.Ye@Sun.COM fmd_agent_errno(fmd_agent_hdl_t *hdl)
507532SSean.Ye@Sun.COM {
517532SSean.Ye@Sun.COM 	return (hdl->agent_errno);
527532SSean.Ye@Sun.COM }
537532SSean.Ye@Sun.COM 
547532SSean.Ye@Sun.COM int
fmd_agent_seterrno(fmd_agent_hdl_t * hdl,int err)557532SSean.Ye@Sun.COM fmd_agent_seterrno(fmd_agent_hdl_t *hdl, int err)
567532SSean.Ye@Sun.COM {
577532SSean.Ye@Sun.COM 	hdl->agent_errno = err;
587532SSean.Ye@Sun.COM 	return (-1);
597532SSean.Ye@Sun.COM }
607532SSean.Ye@Sun.COM 
617532SSean.Ye@Sun.COM const char *
fmd_agent_strerr(int err)627532SSean.Ye@Sun.COM fmd_agent_strerr(int err)
637532SSean.Ye@Sun.COM {
647532SSean.Ye@Sun.COM 	return (strerror(err));
657532SSean.Ye@Sun.COM }
667532SSean.Ye@Sun.COM 
677532SSean.Ye@Sun.COM const char *
fmd_agent_errmsg(fmd_agent_hdl_t * hdl)687532SSean.Ye@Sun.COM fmd_agent_errmsg(fmd_agent_hdl_t *hdl)
697532SSean.Ye@Sun.COM {
707532SSean.Ye@Sun.COM 	return (fmd_agent_strerr(hdl->agent_errno));
717532SSean.Ye@Sun.COM }
727532SSean.Ye@Sun.COM 
737532SSean.Ye@Sun.COM static int
cleanup_set_errno(fmd_agent_hdl_t * hdl,nvlist_t * innvl,nvlist_t * outnvl,int err)747532SSean.Ye@Sun.COM cleanup_set_errno(fmd_agent_hdl_t *hdl, nvlist_t *innvl, nvlist_t *outnvl,
757532SSean.Ye@Sun.COM     int err)
767532SSean.Ye@Sun.COM {
777532SSean.Ye@Sun.COM 	if (innvl != NULL)
787532SSean.Ye@Sun.COM 		nvlist_free(innvl);
797532SSean.Ye@Sun.COM 	if (outnvl != NULL)
807532SSean.Ye@Sun.COM 		nvlist_free(outnvl);
817532SSean.Ye@Sun.COM 	return (fmd_agent_seterrno(hdl, err));
827532SSean.Ye@Sun.COM }
837532SSean.Ye@Sun.COM 
847532SSean.Ye@Sun.COM /*
857532SSean.Ye@Sun.COM  * Perform /dev/fm ioctl.  The input and output data are represented by
867532SSean.Ye@Sun.COM  * name-value lists (nvlists).
877532SSean.Ye@Sun.COM  */
887532SSean.Ye@Sun.COM int
fmd_agent_nvl_ioctl(fmd_agent_hdl_t * hdl,int cmd,uint32_t ver,nvlist_t * innvl,nvlist_t ** outnvlp)897532SSean.Ye@Sun.COM fmd_agent_nvl_ioctl(fmd_agent_hdl_t *hdl, int cmd, uint32_t ver,
907532SSean.Ye@Sun.COM     nvlist_t *innvl, nvlist_t **outnvlp)
917532SSean.Ye@Sun.COM {
927532SSean.Ye@Sun.COM 	fm_ioc_data_t fid;
937532SSean.Ye@Sun.COM 	int err = 0;
947532SSean.Ye@Sun.COM 	char *inbuf = NULL, *outbuf = NULL;
957532SSean.Ye@Sun.COM 	size_t insz = 0, outsz = 0;
967532SSean.Ye@Sun.COM 
977532SSean.Ye@Sun.COM 	if (innvl != NULL) {
987532SSean.Ye@Sun.COM 		if ((err = nvlist_size(innvl, &insz, NV_ENCODE_NATIVE)) != 0)
997532SSean.Ye@Sun.COM 			return (err);
1007532SSean.Ye@Sun.COM 		if (insz > FM_IOC_MAXBUFSZ)
1017532SSean.Ye@Sun.COM 			return (ENAMETOOLONG);
1027532SSean.Ye@Sun.COM 		if ((inbuf = umem_alloc(insz, UMEM_DEFAULT)) == NULL)
1037532SSean.Ye@Sun.COM 			return (errno);
1047532SSean.Ye@Sun.COM 
1057532SSean.Ye@Sun.COM 		if ((err = nvlist_pack(innvl, &inbuf, &insz,
1067532SSean.Ye@Sun.COM 		    NV_ENCODE_NATIVE, 0)) != 0) {
1077532SSean.Ye@Sun.COM 			umem_free(inbuf, insz);
1087532SSean.Ye@Sun.COM 			return (err);
1097532SSean.Ye@Sun.COM 		}
1107532SSean.Ye@Sun.COM 	}
1117532SSean.Ye@Sun.COM 
1127532SSean.Ye@Sun.COM 	if (outnvlp != NULL) {
113*11264SAdrian.Frost@Sun.COM 		outsz = FM_IOC_OUT_BUFSZ;
114*11264SAdrian.Frost@Sun.COM 	}
115*11264SAdrian.Frost@Sun.COM 	for (;;) {
116*11264SAdrian.Frost@Sun.COM 		if (outnvlp != NULL) {
117*11264SAdrian.Frost@Sun.COM 			outbuf = umem_alloc(outsz, UMEM_DEFAULT);
118*11264SAdrian.Frost@Sun.COM 			if (outbuf == NULL) {
119*11264SAdrian.Frost@Sun.COM 				err = errno;
120*11264SAdrian.Frost@Sun.COM 				break;
121*11264SAdrian.Frost@Sun.COM 			}
122*11264SAdrian.Frost@Sun.COM 		}
123*11264SAdrian.Frost@Sun.COM 
124*11264SAdrian.Frost@Sun.COM 		fid.fid_version = ver;
125*11264SAdrian.Frost@Sun.COM 		fid.fid_insz = insz;
126*11264SAdrian.Frost@Sun.COM 		fid.fid_inbuf = inbuf;
127*11264SAdrian.Frost@Sun.COM 		fid.fid_outsz = outsz;
128*11264SAdrian.Frost@Sun.COM 		fid.fid_outbuf = outbuf;
129*11264SAdrian.Frost@Sun.COM 
130*11264SAdrian.Frost@Sun.COM 		if (ioctl(hdl->agent_devfd, cmd, &fid) < 0) {
131*11264SAdrian.Frost@Sun.COM 			if (errno == ENAMETOOLONG && outsz != 0 &&
132*11264SAdrian.Frost@Sun.COM 			    outsz < (FM_IOC_OUT_MAXBUFSZ / 2)) {
133*11264SAdrian.Frost@Sun.COM 				umem_free(outbuf, outsz);
134*11264SAdrian.Frost@Sun.COM 				outsz *= 2;
135*11264SAdrian.Frost@Sun.COM 				outbuf = umem_alloc(outsz, UMEM_DEFAULT);
136*11264SAdrian.Frost@Sun.COM 				if (outbuf == NULL) {
137*11264SAdrian.Frost@Sun.COM 					err = errno;
138*11264SAdrian.Frost@Sun.COM 					break;
139*11264SAdrian.Frost@Sun.COM 				}
140*11264SAdrian.Frost@Sun.COM 			} else {
141*11264SAdrian.Frost@Sun.COM 				err = errno;
142*11264SAdrian.Frost@Sun.COM 				break;
143*11264SAdrian.Frost@Sun.COM 			}
144*11264SAdrian.Frost@Sun.COM 		} else if (outnvlp != NULL) {
145*11264SAdrian.Frost@Sun.COM 			err = nvlist_unpack(fid.fid_outbuf, fid.fid_outsz,
146*11264SAdrian.Frost@Sun.COM 			    outnvlp, 0);
147*11264SAdrian.Frost@Sun.COM 			break;
148*11264SAdrian.Frost@Sun.COM 		} else {
149*11264SAdrian.Frost@Sun.COM 			break;
1507532SSean.Ye@Sun.COM 		}
1517532SSean.Ye@Sun.COM 	}
1527532SSean.Ye@Sun.COM 
1537532SSean.Ye@Sun.COM 	if (inbuf != NULL)
1547532SSean.Ye@Sun.COM 		umem_free(inbuf, insz);
1557532SSean.Ye@Sun.COM 	if (outbuf != NULL)
1567532SSean.Ye@Sun.COM 		umem_free(outbuf, outsz);
1577532SSean.Ye@Sun.COM 
1587532SSean.Ye@Sun.COM 	return (err);
1597532SSean.Ye@Sun.COM }
1607532SSean.Ye@Sun.COM 
1617532SSean.Ye@Sun.COM /*
1627532SSean.Ye@Sun.COM  * Open /dev/fm and return a handle.  ver is the overall interface version.
1637532SSean.Ye@Sun.COM  */
1647532SSean.Ye@Sun.COM static fmd_agent_hdl_t *
fmd_agent_open_dev(int ver,int mode)1657532SSean.Ye@Sun.COM fmd_agent_open_dev(int ver, int mode)
1667532SSean.Ye@Sun.COM {
1677532SSean.Ye@Sun.COM 	fmd_agent_hdl_t *hdl;
1687532SSean.Ye@Sun.COM 	int fd, err;
1697532SSean.Ye@Sun.COM 	nvlist_t *nvl;
1707532SSean.Ye@Sun.COM 
1717532SSean.Ye@Sun.COM 	if ((fd = open("/dev/fm", mode)) < 0)
1727532SSean.Ye@Sun.COM 		return (NULL); /* errno is set for us */
1737532SSean.Ye@Sun.COM 
1747532SSean.Ye@Sun.COM 	if ((hdl = umem_alloc(sizeof (fmd_agent_hdl_t),
1757532SSean.Ye@Sun.COM 	    UMEM_DEFAULT)) == NULL) {
1767532SSean.Ye@Sun.COM 		err = errno;
1777532SSean.Ye@Sun.COM 		(void) close(fd);
1787532SSean.Ye@Sun.COM 		errno = err;
1797532SSean.Ye@Sun.COM 		return (NULL);
1807532SSean.Ye@Sun.COM 	}
1817532SSean.Ye@Sun.COM 
1827532SSean.Ye@Sun.COM 	hdl->agent_devfd = fd;
1837532SSean.Ye@Sun.COM 	hdl->agent_version = ver;
1847532SSean.Ye@Sun.COM 
1857532SSean.Ye@Sun.COM 	/*
1867532SSean.Ye@Sun.COM 	 * Get the individual interface versions.
1877532SSean.Ye@Sun.COM 	 */
1887532SSean.Ye@Sun.COM 	if ((err = fmd_agent_nvl_ioctl(hdl, FM_IOC_VERSIONS, ver, NULL, &nvl))
1897532SSean.Ye@Sun.COM 	    < 0) {
1907532SSean.Ye@Sun.COM 		(void) close(fd);
1917532SSean.Ye@Sun.COM 		umem_free(hdl, sizeof (fmd_agent_hdl_t));
1927532SSean.Ye@Sun.COM 		errno = err;
1937532SSean.Ye@Sun.COM 		return (NULL);
1947532SSean.Ye@Sun.COM 	}
1957532SSean.Ye@Sun.COM 
1967532SSean.Ye@Sun.COM 	hdl->agent_ioc_versions = nvl;
1977532SSean.Ye@Sun.COM 	return (hdl);
1987532SSean.Ye@Sun.COM }
1997532SSean.Ye@Sun.COM 
2007532SSean.Ye@Sun.COM fmd_agent_hdl_t *
fmd_agent_open(int ver)2017532SSean.Ye@Sun.COM fmd_agent_open(int ver)
2027532SSean.Ye@Sun.COM {
2037532SSean.Ye@Sun.COM 	if (ver > FMD_AGENT_VERSION) {
2047532SSean.Ye@Sun.COM 		errno = ENOTSUP;
2057532SSean.Ye@Sun.COM 		return (NULL);
2067532SSean.Ye@Sun.COM 	}
2077532SSean.Ye@Sun.COM 	return (fmd_agent_open_dev(ver, O_RDONLY));
2087532SSean.Ye@Sun.COM }
2097532SSean.Ye@Sun.COM 
2107532SSean.Ye@Sun.COM void
fmd_agent_close(fmd_agent_hdl_t * hdl)2117532SSean.Ye@Sun.COM fmd_agent_close(fmd_agent_hdl_t *hdl)
2127532SSean.Ye@Sun.COM {
2137532SSean.Ye@Sun.COM 	(void) close(hdl->agent_devfd);
2147532SSean.Ye@Sun.COM 	nvlist_free(hdl->agent_ioc_versions);
2157532SSean.Ye@Sun.COM 	umem_free(hdl, sizeof (fmd_agent_hdl_t));
2167532SSean.Ye@Sun.COM }
2177532SSean.Ye@Sun.COM 
2187532SSean.Ye@Sun.COM /*
2197532SSean.Ye@Sun.COM  * Given a interface name, return the kernel interface version.
2207532SSean.Ye@Sun.COM  */
2217532SSean.Ye@Sun.COM int
fmd_agent_version(fmd_agent_hdl_t * hdl,const char * op,uint32_t * verp)2227532SSean.Ye@Sun.COM fmd_agent_version(fmd_agent_hdl_t *hdl, const char *op, uint32_t *verp)
2237532SSean.Ye@Sun.COM {
2247532SSean.Ye@Sun.COM 	int err;
2257532SSean.Ye@Sun.COM 
2267532SSean.Ye@Sun.COM 	err = nvlist_lookup_uint32(hdl->agent_ioc_versions,
2277532SSean.Ye@Sun.COM 	    op, verp);
2287532SSean.Ye@Sun.COM 
2297532SSean.Ye@Sun.COM 	if (err != 0) {
2307532SSean.Ye@Sun.COM 		errno = err;
2317532SSean.Ye@Sun.COM 		return (-1);
2327532SSean.Ye@Sun.COM 	}
2337532SSean.Ye@Sun.COM 	return (0);
2347532SSean.Ye@Sun.COM }
2357532SSean.Ye@Sun.COM 
2367532SSean.Ye@Sun.COM static int
fmd_agent_pageop_v1(fmd_agent_hdl_t * hdl,int cmd,nvlist_t * fmri)2377532SSean.Ye@Sun.COM fmd_agent_pageop_v1(fmd_agent_hdl_t *hdl, int cmd, nvlist_t *fmri)
2387532SSean.Ye@Sun.COM {
2397532SSean.Ye@Sun.COM 	int err;
2407532SSean.Ye@Sun.COM 	nvlist_t *nvl = NULL;
2417532SSean.Ye@Sun.COM 
2427532SSean.Ye@Sun.COM 	if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0 ||
2437532SSean.Ye@Sun.COM 	    (err = nvlist_add_nvlist(nvl, FM_PAGE_RETIRE_FMRI, fmri)) != 0 ||
2447532SSean.Ye@Sun.COM 	    (err = fmd_agent_nvl_ioctl(hdl, cmd, 1, nvl, NULL)) != 0)
2457532SSean.Ye@Sun.COM 		return (cleanup_set_errno(hdl, nvl, NULL, err));
2467532SSean.Ye@Sun.COM 
2477532SSean.Ye@Sun.COM 	nvlist_free(nvl);
2487532SSean.Ye@Sun.COM 	return (0);
2497532SSean.Ye@Sun.COM }
2507532SSean.Ye@Sun.COM 
2517532SSean.Ye@Sun.COM static int
fmd_agent_pageop(fmd_agent_hdl_t * hdl,int cmd,nvlist_t * fmri)2527532SSean.Ye@Sun.COM fmd_agent_pageop(fmd_agent_hdl_t *hdl, int cmd, nvlist_t *fmri)
2537532SSean.Ye@Sun.COM {
2547532SSean.Ye@Sun.COM 	uint32_t ver;
2557532SSean.Ye@Sun.COM 
2567532SSean.Ye@Sun.COM 	if (fmd_agent_version(hdl, FM_PAGE_OP_VERSION, &ver) == -1)
2577532SSean.Ye@Sun.COM 		return (fmd_agent_seterrno(hdl, errno));
2587532SSean.Ye@Sun.COM 
2597532SSean.Ye@Sun.COM 	switch (ver) {
2607532SSean.Ye@Sun.COM 	case 1:
2617532SSean.Ye@Sun.COM 		return (fmd_agent_pageop_v1(hdl, cmd, fmri));
2627532SSean.Ye@Sun.COM 
2637532SSean.Ye@Sun.COM 	default:
2647532SSean.Ye@Sun.COM 		return (fmd_agent_seterrno(hdl, ENOTSUP));
2657532SSean.Ye@Sun.COM 	}
2667532SSean.Ye@Sun.COM }
2677532SSean.Ye@Sun.COM 
2687532SSean.Ye@Sun.COM int
fmd_agent_page_retire(fmd_agent_hdl_t * hdl,nvlist_t * fmri)2697532SSean.Ye@Sun.COM fmd_agent_page_retire(fmd_agent_hdl_t *hdl, nvlist_t *fmri)
2707532SSean.Ye@Sun.COM {
2717532SSean.Ye@Sun.COM 	int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_RETIRE, fmri);
2727532SSean.Ye@Sun.COM 	int err = fmd_agent_errno(hdl);
2737532SSean.Ye@Sun.COM 
2747532SSean.Ye@Sun.COM 	/*
2757532SSean.Ye@Sun.COM 	 * FM_IOC_PAGE_RETIRE ioctl returns:
2767532SSean.Ye@Sun.COM 	 *   0 - success in retiring page
2777532SSean.Ye@Sun.COM 	 *   -1, errno = EIO - page is already retired
2787532SSean.Ye@Sun.COM 	 *   -1, errno = EAGAIN - page is scheduled for retirement
2798074SSean.Ye@Sun.COM 	 *   -1, errno = EINVAL - page fmri is invalid
2807532SSean.Ye@Sun.COM 	 *   -1, errno = any else - error
2817532SSean.Ye@Sun.COM 	 */
2827532SSean.Ye@Sun.COM 	if (rc == 0 || err == EIO || err == EINVAL) {
2837532SSean.Ye@Sun.COM 		if (rc == 0)
2847532SSean.Ye@Sun.COM 			(void) fmd_agent_seterrno(hdl, 0);
2857532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_DONE);
2867532SSean.Ye@Sun.COM 	}
2877532SSean.Ye@Sun.COM 	if (err == EAGAIN)
2887532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_ASYNC);
2897532SSean.Ye@Sun.COM 
2907532SSean.Ye@Sun.COM 	return (FMD_AGENT_RETIRE_FAIL);
2917532SSean.Ye@Sun.COM }
2927532SSean.Ye@Sun.COM 
2937532SSean.Ye@Sun.COM int
fmd_agent_page_unretire(fmd_agent_hdl_t * hdl,nvlist_t * fmri)2947532SSean.Ye@Sun.COM fmd_agent_page_unretire(fmd_agent_hdl_t *hdl, nvlist_t *fmri)
2957532SSean.Ye@Sun.COM {
2967532SSean.Ye@Sun.COM 	int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_UNRETIRE, fmri);
2977532SSean.Ye@Sun.COM 	int err = fmd_agent_errno(hdl);
2987532SSean.Ye@Sun.COM 
2997532SSean.Ye@Sun.COM 	/*
3007532SSean.Ye@Sun.COM 	 * FM_IOC_PAGE_UNRETIRE ioctl returns:
3017532SSean.Ye@Sun.COM 	 *   0 - success in unretiring page
3027532SSean.Ye@Sun.COM 	 *   -1, errno = EIO - page is already unretired
3037532SSean.Ye@Sun.COM 	 *   -1, errno = EAGAIN - page couldn't be locked, still retired
3048074SSean.Ye@Sun.COM 	 *   -1, errno = EINVAL - page fmri is invalid
3057532SSean.Ye@Sun.COM 	 *   -1, errno = any else - error
3067532SSean.Ye@Sun.COM 	 */
3078074SSean.Ye@Sun.COM 	if (rc == 0 || err == EIO || err == EINVAL) {
3087532SSean.Ye@Sun.COM 		if (rc == 0)
3097532SSean.Ye@Sun.COM 			(void) fmd_agent_seterrno(hdl, 0);
3107532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_DONE);
3117532SSean.Ye@Sun.COM 	}
3127532SSean.Ye@Sun.COM 
3137532SSean.Ye@Sun.COM 	return (FMD_AGENT_RETIRE_FAIL);
3147532SSean.Ye@Sun.COM }
3157532SSean.Ye@Sun.COM 
3167532SSean.Ye@Sun.COM int
fmd_agent_page_isretired(fmd_agent_hdl_t * hdl,nvlist_t * fmri)3177532SSean.Ye@Sun.COM fmd_agent_page_isretired(fmd_agent_hdl_t *hdl, nvlist_t *fmri)
3187532SSean.Ye@Sun.COM {
3197532SSean.Ye@Sun.COM 	int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_STATUS, fmri);
3207532SSean.Ye@Sun.COM 	int err = fmd_agent_errno(hdl);
3217532SSean.Ye@Sun.COM 
3227532SSean.Ye@Sun.COM 	/*
3237532SSean.Ye@Sun.COM 	 * FM_IOC_PAGE_STATUS returns:
3247532SSean.Ye@Sun.COM 	 *   0 - page is retired
3257532SSean.Ye@Sun.COM 	 *   -1, errno = EAGAIN - page is scheduled for retirement
3267532SSean.Ye@Sun.COM 	 *   -1, errno = EIO - page not scheduled for retirement
3278074SSean.Ye@Sun.COM 	 *   -1, errno = EINVAL - page fmri is invalid
3287532SSean.Ye@Sun.COM 	 *   -1, errno = any else - error
3297532SSean.Ye@Sun.COM 	 */
3307532SSean.Ye@Sun.COM 	if (rc == 0 || err == EINVAL) {
3317532SSean.Ye@Sun.COM 		if (rc == 0)
3327532SSean.Ye@Sun.COM 			(void) fmd_agent_seterrno(hdl, 0);
3337532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_DONE);
3347532SSean.Ye@Sun.COM 	}
3357532SSean.Ye@Sun.COM 	if (err == EAGAIN)
3367532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_ASYNC);
3377532SSean.Ye@Sun.COM 
3387532SSean.Ye@Sun.COM 	return (FMD_AGENT_RETIRE_FAIL);
3397532SSean.Ye@Sun.COM }
340