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