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 nvlist_free(innvl); 78 nvlist_free(outnvl); 79 return (fmd_agent_seterrno(hdl, err)); 80 } 81 82 /* 83 * Perform /dev/fm ioctl. The input and output data are represented by 84 * name-value lists (nvlists). 85 */ 86 int 87 fmd_agent_nvl_ioctl(fmd_agent_hdl_t *hdl, int cmd, uint32_t ver, 88 nvlist_t *innvl, nvlist_t **outnvlp) 89 { 90 fm_ioc_data_t fid; 91 int err = 0; 92 char *inbuf = NULL, *outbuf = NULL; 93 size_t insz = 0, outsz = 0; 94 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); 102 103 if ((err = nvlist_pack(innvl, &inbuf, &insz, 104 NV_ENCODE_NATIVE, 0)) != 0) { 105 umem_free(inbuf, insz); 106 return (err); 107 } 108 } 109 110 if (outnvlp != NULL) { 111 outsz = FM_IOC_OUT_BUFSZ; 112 } 113 for (;;) { 114 if (outnvlp != NULL) { 115 outbuf = umem_alloc(outsz, UMEM_DEFAULT); 116 if (outbuf == NULL) { 117 err = errno; 118 break; 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 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; 137 } 138 } else { 139 err = errno; 140 break; 141 } 142 } else if (outnvlp != NULL) { 143 err = nvlist_unpack(fid.fid_outbuf, fid.fid_outsz, 144 outnvlp, 0); 145 break; 146 } else { 147 break; 148 } 149 } 150 151 if (inbuf != NULL) 152 umem_free(inbuf, insz); 153 if (outbuf != NULL) 154 umem_free(outbuf, outsz); 155 156 return (err); 157 } 158 159 /* 160 * Open /dev/fm and return a handle. ver is the overall interface version. 161 */ 162 static fmd_agent_hdl_t * 163 fmd_agent_open_dev(int ver, int mode) 164 { 165 fmd_agent_hdl_t *hdl; 166 int fd, err; 167 nvlist_t *nvl; 168 169 if ((fd = open("/dev/fm", mode)) < 0) 170 return (NULL); /* errno is set for us */ 171 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); 178 } 179 180 hdl->agent_devfd = fd; 181 hdl->agent_version = ver; 182 183 /* 184 * Get the individual interface versions. 185 */ 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); 192 } 193 194 hdl->agent_ioc_versions = nvl; 195 return (hdl); 196 } 197 198 fmd_agent_hdl_t * 199 fmd_agent_open(int ver) 200 { 201 if (ver > FMD_AGENT_VERSION) { 202 errno = ENOTSUP; 203 return (NULL); 204 } 205 return (fmd_agent_open_dev(ver, O_RDONLY)); 206 } 207 208 void 209 fmd_agent_close(fmd_agent_hdl_t *hdl) 210 { 211 (void) close(hdl->agent_devfd); 212 nvlist_free(hdl->agent_ioc_versions); 213 umem_free(hdl, sizeof (fmd_agent_hdl_t)); 214 } 215 216 /* 217 * Given a interface name, return the kernel interface version. 218 */ 219 int 220 fmd_agent_version(fmd_agent_hdl_t *hdl, const char *op, uint32_t *verp) 221 { 222 int err; 223 224 err = nvlist_lookup_uint32(hdl->agent_ioc_versions, 225 op, verp); 226 227 if (err != 0) { 228 errno = err; 229 return (-1); 230 } 231 return (0); 232 } 233 234 static int 235 fmd_agent_pageop_v1(fmd_agent_hdl_t *hdl, int cmd, nvlist_t *fmri) 236 { 237 int err; 238 nvlist_t *nvl = NULL; 239 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)); 244 245 nvlist_free(nvl); 246 return (0); 247 } 248 249 static int 250 fmd_agent_pageop(fmd_agent_hdl_t *hdl, int cmd, nvlist_t *fmri) 251 { 252 uint32_t ver; 253 254 if (fmd_agent_version(hdl, FM_PAGE_OP_VERSION, &ver) == -1) 255 return (fmd_agent_seterrno(hdl, errno)); 256 257 switch (ver) { 258 case 1: 259 return (fmd_agent_pageop_v1(hdl, cmd, fmri)); 260 261 default: 262 return (fmd_agent_seterrno(hdl, ENOTSUP)); 263 } 264 } 265 266 int 267 fmd_agent_page_retire(fmd_agent_hdl_t *hdl, nvlist_t *fmri) 268 { 269 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_RETIRE, fmri); 270 int err = fmd_agent_errno(hdl); 271 272 /* 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 279 */ 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); 284 } 285 if (err == EAGAIN) 286 return (FMD_AGENT_RETIRE_ASYNC); 287 288 return (FMD_AGENT_RETIRE_FAIL); 289 } 290 291 int 292 fmd_agent_page_unretire(fmd_agent_hdl_t *hdl, nvlist_t *fmri) 293 { 294 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_UNRETIRE, fmri); 295 int err = fmd_agent_errno(hdl); 296 297 /* 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 304 */ 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); 309 } 310 311 return (FMD_AGENT_RETIRE_FAIL); 312 } 313 314 int 315 fmd_agent_page_isretired(fmd_agent_hdl_t *hdl, nvlist_t *fmri) 316 { 317 int rc = fmd_agent_pageop(hdl, FM_IOC_PAGE_STATUS, fmri); 318 int err = fmd_agent_errno(hdl); 319 320 /* 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 327 */ 328 if (rc == 0 || err == EINVAL) { 329 if (rc == 0) 330 (void) fmd_agent_seterrno(hdl, 0); 331 return (FMD_AGENT_RETIRE_DONE); 332 } 333 if (err == EAGAIN) 334 return (FMD_AGENT_RETIRE_ASYNC); 335 336 return (FMD_AGENT_RETIRE_FAIL); 337 } 338