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 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 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. 31 * 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()). 36 */ 37 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> 47 48 int 49 fmd_agent_errno(fmd_agent_hdl_t *hdl) 50 { 51 return (hdl->agent_errno); 52 } 53 54 int 55 fmd_agent_seterrno(fmd_agent_hdl_t *hdl, int err) 56 { 57 hdl->agent_errno = err; 58 return (-1); 59 } 60 61 const char * 62 fmd_agent_strerr(int err) 63 { 64 return (strerror(err)); 65 } 66 67 const char * 68 fmd_agent_errmsg(fmd_agent_hdl_t *hdl) 69 { 70 return (fmd_agent_strerr(hdl->agent_errno)); 71 } 72 73 static int 74 cleanup_set_errno(fmd_agent_hdl_t *hdl, nvlist_t *innvl, nvlist_t *outnvl, 75 int err) 76 { 77 if (innvl != NULL) 78 nvlist_free(innvl); 79 if (outnvl != NULL) 80 nvlist_free(outnvl); 81 return (fmd_agent_seterrno(hdl, err)); 82 } 83 84 /* 85 * Perform /dev/fm ioctl. The input and output data are represented by 86 * name-value lists (nvlists). 87 */ 88 int 89 fmd_agent_nvl_ioctl(fmd_agent_hdl_t *hdl, int cmd, uint32_t ver, 90 nvlist_t *innvl, nvlist_t **outnvlp) 91 { 92 fm_ioc_data_t fid; 93 int err = 0; 94 char *inbuf = NULL, *outbuf = NULL; 95 size_t insz = 0, outsz = 0; 96 97 if (innvl != NULL) { 98 if ((err = nvlist_size(innvl, &insz, NV_ENCODE_NATIVE)) != 0) 99 return (err); 100 if (insz > FM_IOC_MAXBUFSZ) 101 return (ENAMETOOLONG); 102 if ((inbuf = umem_alloc(insz, UMEM_DEFAULT)) == NULL) 103 return (errno); 104 105 if ((err = nvlist_pack(innvl, &inbuf, &insz, 106 NV_ENCODE_NATIVE, 0)) != 0) { 107 umem_free(inbuf, insz); 108 return (err); 109 } 110 } 111 112 if (outnvlp != NULL) { 113 outsz = FM_IOC_MAXBUFSZ; 114 if ((outbuf = umem_alloc(outsz, UMEM_DEFAULT)) == NULL) { 115 err = errno; 116 if (inbuf != NULL) 117 umem_free(inbuf, insz); 118 return (err); 119 } 120 } 121 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; 127 128 if (ioctl(hdl->agent_devfd, cmd, &fid) < 0) 129 err = errno; 130 else if (outnvlp != NULL) 131 err = nvlist_unpack(fid.fid_outbuf, fid.fid_outsz, outnvlp, 0); 132 133 if (inbuf != NULL) 134 umem_free(inbuf, insz); 135 if (outbuf != NULL) 136 umem_free(outbuf, outsz); 137 138 return (err); 139 } 140 141 /* 142 * Open /dev/fm and return a handle. ver is the overall interface version. 143 */ 144 static fmd_agent_hdl_t * 145 fmd_agent_open_dev(int ver, int mode) 146 { 147 fmd_agent_hdl_t *hdl; 148 int fd, err; 149 nvlist_t *nvl; 150 151 if ((fd = open("/dev/fm", mode)) < 0) 152 return (NULL); /* errno is set for us */ 153 154 if ((hdl = umem_alloc(sizeof (fmd_agent_hdl_t), 155 UMEM_DEFAULT)) == NULL) { 156 err = errno; 157 (void) close(fd); 158 errno = err; 159 return (NULL); 160 } 161 162 hdl->agent_devfd = fd; 163 hdl->agent_version = ver; 164 165 /* 166 * Get the individual interface versions. 167 */ 168 if ((err = fmd_agent_nvl_ioctl(hdl, FM_IOC_VERSIONS, ver, NULL, &nvl)) 169 < 0) { 170 (void) close(fd); 171 umem_free(hdl, sizeof (fmd_agent_hdl_t)); 172 errno = err; 173 return (NULL); 174 } 175 176 hdl->agent_ioc_versions = nvl; 177 return (hdl); 178 } 179 180 fmd_agent_hdl_t * 181 fmd_agent_open(int ver) 182 { 183 if (ver > FMD_AGENT_VERSION) { 184 errno = ENOTSUP; 185 return (NULL); 186 } 187 return (fmd_agent_open_dev(ver, O_RDONLY)); 188 } 189 190 void 191 fmd_agent_close(fmd_agent_hdl_t *hdl) 192 { 193 (void) close(hdl->agent_devfd); 194 nvlist_free(hdl->agent_ioc_versions); 195 umem_free(hdl, sizeof (fmd_agent_hdl_t)); 196 } 197 198 /* 199 * Given a interface name, return the kernel interface version. 200 */ 201 int 202 fmd_agent_version(fmd_agent_hdl_t *hdl, const char *op, uint32_t *verp) 203 { 204 int err; 205 206 err = nvlist_lookup_uint32(hdl->agent_ioc_versions, 207 op, verp); 208 209 if (err != 0) { 210 errno = err; 211 return (-1); 212 } 213 return (0); 214 } 215 216 static int 217 fmd_agent_pageop_v1(fmd_agent_hdl_t *hdl, int cmd, nvlist_t *fmri) 218 { 219 int err; 220 nvlist_t *nvl = NULL; 221 222 if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0 || 223 (err = nvlist_add_nvlist(nvl, FM_PAGE_RETIRE_FMRI, fmri)) != 0 || 224 (err = fmd_agent_nvl_ioctl(hdl, cmd, 1, nvl, NULL)) != 0) 225 return (cleanup_set_errno(hdl, nvl, NULL, err)); 226 227 nvlist_free(nvl); 228 return (0); 229 } 230 231 static int 232 fmd_agent_pageop(fmd_agent_hdl_t *hdl, int cmd, nvlist_t *fmri) 233 { 234 uint32_t ver; 235 236 if (fmd_agent_version(hdl, FM_PAGE_OP_VERSION, &ver) == -1) 237 return (fmd_agent_seterrno(hdl, errno)); 238 239 switch (ver) { 240 case 1: 241 return (fmd_agent_pageop_v1(hdl, cmd, fmri)); 242 243 default: 244 return (fmd_agent_seterrno(hdl, ENOTSUP)); 245 } 246 } 247 248 int 249 fmd_agent_page_retire(fmd_agent_hdl_t *hdl, nvlist_t *fmri) 250 { 251 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_RETIRE, fmri); 252 int err = fmd_agent_errno(hdl); 253 254 /* 255 * FM_IOC_PAGE_RETIRE ioctl returns: 256 * 0 - success in retiring page 257 * -1, errno = EIO - page is already retired 258 * -1, errno = EAGAIN - page is scheduled for retirement 259 * -1, errno = any else - error 260 */ 261 if (rc == 0 || err == EIO || err == EINVAL) { 262 if (rc == 0) 263 (void) fmd_agent_seterrno(hdl, 0); 264 return (FMD_AGENT_RETIRE_DONE); 265 } 266 if (err == EAGAIN) 267 return (FMD_AGENT_RETIRE_ASYNC); 268 269 return (FMD_AGENT_RETIRE_FAIL); 270 } 271 272 int 273 fmd_agent_page_unretire(fmd_agent_hdl_t *hdl, nvlist_t *fmri) 274 { 275 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_UNRETIRE, fmri); 276 int err = fmd_agent_errno(hdl); 277 278 /* 279 * FM_IOC_PAGE_UNRETIRE ioctl returns: 280 * 0 - success in unretiring page 281 * -1, errno = EIO - page is already unretired 282 * -1, errno = EAGAIN - page couldn't be locked, still retired 283 * -1, errno = any else - error 284 */ 285 if (rc == 0 || err == EIO) { 286 if (rc == 0) 287 (void) fmd_agent_seterrno(hdl, 0); 288 return (FMD_AGENT_RETIRE_DONE); 289 } 290 291 return (FMD_AGENT_RETIRE_FAIL); 292 } 293 294 int 295 fmd_agent_page_isretired(fmd_agent_hdl_t *hdl, nvlist_t *fmri) 296 { 297 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_STATUS, fmri); 298 int err = fmd_agent_errno(hdl); 299 300 /* 301 * FM_IOC_PAGE_STATUS returns: 302 * 0 - page is retired 303 * -1, errno = EAGAIN - page is scheduled for retirement 304 * -1, errno = EIO - page not scheduled for retirement 305 * -1, errno = any else - error 306 */ 307 if (rc == 0 || err == EINVAL) { 308 if (rc == 0) 309 (void) fmd_agent_seterrno(hdl, 0); 310 return (FMD_AGENT_RETIRE_DONE); 311 } 312 if (err == EAGAIN) 313 return (FMD_AGENT_RETIRE_ASYNC); 314 315 return (FMD_AGENT_RETIRE_FAIL); 316 } 317