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 2009 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_OUT_BUFSZ; 114 } 115 for (;;) { 116 if (outnvlp != NULL) { 117 outbuf = umem_alloc(outsz, UMEM_DEFAULT); 118 if (outbuf == NULL) { 119 err = errno; 120 break; 121 } 122 } 123 124 fid.fid_version = ver; 125 fid.fid_insz = insz; 126 fid.fid_inbuf = inbuf; 127 fid.fid_outsz = outsz; 128 fid.fid_outbuf = outbuf; 129 130 if (ioctl(hdl->agent_devfd, cmd, &fid) < 0) { 131 if (errno == ENAMETOOLONG && outsz != 0 && 132 outsz < (FM_IOC_OUT_MAXBUFSZ / 2)) { 133 umem_free(outbuf, outsz); 134 outsz *= 2; 135 outbuf = umem_alloc(outsz, UMEM_DEFAULT); 136 if (outbuf == NULL) { 137 err = errno; 138 break; 139 } 140 } else { 141 err = errno; 142 break; 143 } 144 } else if (outnvlp != NULL) { 145 err = nvlist_unpack(fid.fid_outbuf, fid.fid_outsz, 146 outnvlp, 0); 147 break; 148 } else { 149 break; 150 } 151 } 152 153 if (inbuf != NULL) 154 umem_free(inbuf, insz); 155 if (outbuf != NULL) 156 umem_free(outbuf, outsz); 157 158 return (err); 159 } 160 161 /* 162 * Open /dev/fm and return a handle. ver is the overall interface version. 163 */ 164 static fmd_agent_hdl_t * 165 fmd_agent_open_dev(int ver, int mode) 166 { 167 fmd_agent_hdl_t *hdl; 168 int fd, err; 169 nvlist_t *nvl; 170 171 if ((fd = open("/dev/fm", mode)) < 0) 172 return (NULL); /* errno is set for us */ 173 174 if ((hdl = umem_alloc(sizeof (fmd_agent_hdl_t), 175 UMEM_DEFAULT)) == NULL) { 176 err = errno; 177 (void) close(fd); 178 errno = err; 179 return (NULL); 180 } 181 182 hdl->agent_devfd = fd; 183 hdl->agent_version = ver; 184 185 /* 186 * Get the individual interface versions. 187 */ 188 if ((err = fmd_agent_nvl_ioctl(hdl, FM_IOC_VERSIONS, ver, NULL, &nvl)) 189 < 0) { 190 (void) close(fd); 191 umem_free(hdl, sizeof (fmd_agent_hdl_t)); 192 errno = err; 193 return (NULL); 194 } 195 196 hdl->agent_ioc_versions = nvl; 197 return (hdl); 198 } 199 200 fmd_agent_hdl_t * 201 fmd_agent_open(int ver) 202 { 203 if (ver > FMD_AGENT_VERSION) { 204 errno = ENOTSUP; 205 return (NULL); 206 } 207 return (fmd_agent_open_dev(ver, O_RDONLY)); 208 } 209 210 void 211 fmd_agent_close(fmd_agent_hdl_t *hdl) 212 { 213 (void) close(hdl->agent_devfd); 214 nvlist_free(hdl->agent_ioc_versions); 215 umem_free(hdl, sizeof (fmd_agent_hdl_t)); 216 } 217 218 /* 219 * Given a interface name, return the kernel interface version. 220 */ 221 int 222 fmd_agent_version(fmd_agent_hdl_t *hdl, const char *op, uint32_t *verp) 223 { 224 int err; 225 226 err = nvlist_lookup_uint32(hdl->agent_ioc_versions, 227 op, verp); 228 229 if (err != 0) { 230 errno = err; 231 return (-1); 232 } 233 return (0); 234 } 235 236 static int 237 fmd_agent_pageop_v1(fmd_agent_hdl_t *hdl, int cmd, nvlist_t *fmri) 238 { 239 int err; 240 nvlist_t *nvl = NULL; 241 242 if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0 || 243 (err = nvlist_add_nvlist(nvl, FM_PAGE_RETIRE_FMRI, fmri)) != 0 || 244 (err = fmd_agent_nvl_ioctl(hdl, cmd, 1, nvl, NULL)) != 0) 245 return (cleanup_set_errno(hdl, nvl, NULL, err)); 246 247 nvlist_free(nvl); 248 return (0); 249 } 250 251 static int 252 fmd_agent_pageop(fmd_agent_hdl_t *hdl, int cmd, nvlist_t *fmri) 253 { 254 uint32_t ver; 255 256 if (fmd_agent_version(hdl, FM_PAGE_OP_VERSION, &ver) == -1) 257 return (fmd_agent_seterrno(hdl, errno)); 258 259 switch (ver) { 260 case 1: 261 return (fmd_agent_pageop_v1(hdl, cmd, fmri)); 262 263 default: 264 return (fmd_agent_seterrno(hdl, ENOTSUP)); 265 } 266 } 267 268 int 269 fmd_agent_page_retire(fmd_agent_hdl_t *hdl, nvlist_t *fmri) 270 { 271 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_RETIRE, fmri); 272 int err = fmd_agent_errno(hdl); 273 274 /* 275 * FM_IOC_PAGE_RETIRE ioctl returns: 276 * 0 - success in retiring page 277 * -1, errno = EIO - page is already retired 278 * -1, errno = EAGAIN - page is scheduled for retirement 279 * -1, errno = EINVAL - page fmri is invalid 280 * -1, errno = any else - error 281 */ 282 if (rc == 0 || err == EIO || err == EINVAL) { 283 if (rc == 0) 284 (void) fmd_agent_seterrno(hdl, 0); 285 return (FMD_AGENT_RETIRE_DONE); 286 } 287 if (err == EAGAIN) 288 return (FMD_AGENT_RETIRE_ASYNC); 289 290 return (FMD_AGENT_RETIRE_FAIL); 291 } 292 293 int 294 fmd_agent_page_unretire(fmd_agent_hdl_t *hdl, nvlist_t *fmri) 295 { 296 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_UNRETIRE, fmri); 297 int err = fmd_agent_errno(hdl); 298 299 /* 300 * FM_IOC_PAGE_UNRETIRE ioctl returns: 301 * 0 - success in unretiring page 302 * -1, errno = EIO - page is already unretired 303 * -1, errno = EAGAIN - page couldn't be locked, still retired 304 * -1, errno = EINVAL - page fmri is invalid 305 * -1, errno = any else - error 306 */ 307 if (rc == 0 || err == EIO || err == EINVAL) { 308 if (rc == 0) 309 (void) fmd_agent_seterrno(hdl, 0); 310 return (FMD_AGENT_RETIRE_DONE); 311 } 312 313 return (FMD_AGENT_RETIRE_FAIL); 314 } 315 316 int 317 fmd_agent_page_isretired(fmd_agent_hdl_t *hdl, nvlist_t *fmri) 318 { 319 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_STATUS, fmri); 320 int err = fmd_agent_errno(hdl); 321 322 /* 323 * FM_IOC_PAGE_STATUS returns: 324 * 0 - page is retired 325 * -1, errno = EAGAIN - page is scheduled for retirement 326 * -1, errno = EIO - page not scheduled for retirement 327 * -1, errno = EINVAL - page fmri is invalid 328 * -1, errno = any else - error 329 */ 330 if (rc == 0 || err == EINVAL) { 331 if (rc == 0) 332 (void) fmd_agent_seterrno(hdl, 0); 333 return (FMD_AGENT_RETIRE_DONE); 334 } 335 if (err == EAGAIN) 336 return (FMD_AGENT_RETIRE_ASYNC); 337 338 return (FMD_AGENT_RETIRE_FAIL); 339 } 340