1 // SPDX-License-Identifier: CDDL-1.0 2 /* 3 * CDDL HEADER START 4 * 5 * The contents of this file are subject to the terms of the 6 * Common Development and Distribution License (the "License"). 7 * You may not use this file except in compliance with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or https://opensource.org/licenses/CDDL-1.0. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2016 by Delphix. All rights reserved. 25 * Copyright 2017 Jason King 26 * Copyright (c) 2017, Intel Corporation. 27 */ 28 29 #include <assert.h> 30 #include <sys/zfs_context.h> 31 #include <sys/avl.h> 32 #include <string.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <sys/spa.h> 36 #include <sys/fs/zfs.h> 37 #include <sys/zfs_refcount.h> 38 #include <sys/zfs_ioctl.h> 39 #include <dlfcn.h> 40 #include <libzutil.h> 41 42 /* 43 * Routines needed by more than one client of libzpool. 44 */ 45 46 static void 47 show_vdev_stats(const char *desc, const char *ctype, nvlist_t *nv, int indent) 48 { 49 vdev_stat_t *vs; 50 vdev_stat_t *v0 = { 0 }; 51 uint64_t sec; 52 uint64_t is_log = 0; 53 nvlist_t **child; 54 uint_t c, children; 55 char used[6], avail[6]; 56 char rops[6], wops[6], rbytes[6], wbytes[6], rerr[6], werr[6], cerr[6]; 57 58 v0 = umem_zalloc(sizeof (*v0), UMEM_NOFAIL); 59 60 if (indent == 0 && desc != NULL) { 61 (void) printf(" " 62 " capacity operations bandwidth ---- errors ----\n"); 63 (void) printf("description " 64 "used avail read write read write read write cksum\n"); 65 } 66 67 if (desc != NULL) { 68 const char *suffix = ""; 69 const char *bias = NULL; 70 char bias_suffix[32]; 71 72 (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG, &is_log); 73 (void) nvlist_lookup_string(nv, ZPOOL_CONFIG_ALLOCATION_BIAS, 74 &bias); 75 if (nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS, 76 (uint64_t **)&vs, &c) != 0) 77 vs = v0; 78 79 if (bias != NULL) { 80 (void) snprintf(bias_suffix, sizeof (bias_suffix), 81 " (%s)", bias); 82 suffix = bias_suffix; 83 } else if (is_log) { 84 suffix = " (log)"; 85 } 86 87 sec = MAX(1, vs->vs_timestamp / NANOSEC); 88 89 nicenum(vs->vs_alloc, used, sizeof (used)); 90 nicenum(vs->vs_space - vs->vs_alloc, avail, sizeof (avail)); 91 nicenum(vs->vs_ops[ZIO_TYPE_READ] / sec, rops, sizeof (rops)); 92 nicenum(vs->vs_ops[ZIO_TYPE_WRITE] / sec, wops, sizeof (wops)); 93 nicenum(vs->vs_bytes[ZIO_TYPE_READ] / sec, rbytes, 94 sizeof (rbytes)); 95 nicenum(vs->vs_bytes[ZIO_TYPE_WRITE] / sec, wbytes, 96 sizeof (wbytes)); 97 nicenum(vs->vs_read_errors, rerr, sizeof (rerr)); 98 nicenum(vs->vs_write_errors, werr, sizeof (werr)); 99 nicenum(vs->vs_checksum_errors, cerr, sizeof (cerr)); 100 101 (void) printf("%*s%s%*s%*s%*s %5s %5s %5s %5s %5s %5s %5s\n", 102 indent, "", 103 desc, 104 (int)(indent+strlen(desc)-25-(vs->vs_space ? 0 : 12)), 105 suffix, 106 vs->vs_space ? 6 : 0, vs->vs_space ? used : "", 107 vs->vs_space ? 6 : 0, vs->vs_space ? avail : "", 108 rops, wops, rbytes, wbytes, rerr, werr, cerr); 109 } 110 umem_free(v0, sizeof (*v0)); 111 112 if (nvlist_lookup_nvlist_array(nv, ctype, &child, &children) != 0) 113 return; 114 115 for (c = 0; c < children; c++) { 116 nvlist_t *cnv = child[c]; 117 const char *cname = NULL; 118 char *tname; 119 uint64_t np; 120 int len; 121 if (nvlist_lookup_string(cnv, ZPOOL_CONFIG_PATH, &cname) && 122 nvlist_lookup_string(cnv, ZPOOL_CONFIG_TYPE, &cname)) 123 cname = "<unknown>"; 124 len = strlen(cname) + 2; 125 tname = umem_zalloc(len, UMEM_NOFAIL); 126 (void) strlcpy(tname, cname, len); 127 if (nvlist_lookup_uint64(cnv, ZPOOL_CONFIG_NPARITY, &np) == 0) 128 tname[strlen(tname)] = '0' + np; 129 show_vdev_stats(tname, ctype, cnv, indent + 2); 130 umem_free(tname, len); 131 } 132 } 133 134 void 135 show_pool_stats(spa_t *spa) 136 { 137 nvlist_t *config, *nvroot; 138 const char *name; 139 140 VERIFY(spa_get_stats(spa_name(spa), &config, NULL, 0) == 0); 141 142 VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 143 &nvroot) == 0); 144 VERIFY(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, 145 &name) == 0); 146 147 show_vdev_stats(name, ZPOOL_CONFIG_CHILDREN, nvroot, 0); 148 show_vdev_stats(NULL, ZPOOL_CONFIG_L2CACHE, nvroot, 0); 149 show_vdev_stats(NULL, ZPOOL_CONFIG_SPARES, nvroot, 0); 150 151 nvlist_free(config); 152 } 153 154 /* *k_out must be freed by the caller */ 155 static int 156 set_global_var_parse_kv(const char *arg, char **k_out, u_longlong_t *v_out) 157 { 158 int err; 159 VERIFY(arg); 160 char *d = strdup(arg); 161 162 char *save = NULL; 163 char *k = strtok_r(d, "=", &save); 164 char *v_str = strtok_r(NULL, "=", &save); 165 char *follow = strtok_r(NULL, "=", &save); 166 if (k == NULL || v_str == NULL || follow != NULL) { 167 err = EINVAL; 168 goto err_free; 169 } 170 171 u_longlong_t val = strtoull(v_str, NULL, 0); 172 if (val > UINT32_MAX) { 173 fprintf(stderr, "Value for global variable '%s' must " 174 "be a 32-bit unsigned integer, got '%s'\n", k, v_str); 175 err = EOVERFLOW; 176 goto err_free; 177 } 178 179 *k_out = strdup(k); 180 *v_out = val; 181 free(d); 182 return (0); 183 184 err_free: 185 free(d); 186 187 return (err); 188 } 189 190 /* 191 * Sets given global variable in libzpool to given unsigned 32-bit value. 192 * arg: "<variable>=<value>" 193 */ 194 int 195 set_global_var(char const *arg) 196 { 197 void *zpoolhdl; 198 char *varname; 199 u_longlong_t val; 200 int ret; 201 202 #ifndef _ZFS_LITTLE_ENDIAN 203 /* 204 * On big endian systems changing a 64-bit variable would set the high 205 * 32 bits instead of the low 32 bits, which could cause unexpected 206 * results. 207 */ 208 fprintf(stderr, "Setting global variables is only supported on " 209 "little-endian systems\n"); 210 ret = ENOTSUP; 211 goto out_ret; 212 #endif 213 214 if ((ret = set_global_var_parse_kv(arg, &varname, &val)) != 0) { 215 goto out_ret; 216 } 217 218 zpoolhdl = dlopen("libzpool.so", RTLD_LAZY); 219 if (zpoolhdl != NULL) { 220 uint32_t *var; 221 var = dlsym(zpoolhdl, varname); 222 if (var == NULL) { 223 fprintf(stderr, "Global variable '%s' does not exist " 224 "in libzpool.so\n", varname); 225 ret = EINVAL; 226 goto out_dlclose; 227 } 228 *var = (uint32_t)val; 229 230 } else { 231 fprintf(stderr, "Failed to open libzpool.so to set global " 232 "variable\n"); 233 ret = EIO; 234 goto out_free; 235 } 236 237 ret = 0; 238 239 out_dlclose: 240 dlclose(zpoolhdl); 241 out_free: 242 free(varname); 243 out_ret: 244 return (ret); 245 } 246 247 static nvlist_t * 248 refresh_config(void *unused, nvlist_t *tryconfig) 249 { 250 (void) unused; 251 return (spa_tryimport(tryconfig)); 252 } 253 254 #if defined(__FreeBSD__) 255 256 #include <sys/param.h> 257 #include <sys/sysctl.h> 258 #include <os/freebsd/zfs/sys/zfs_ioctl_compat.h> 259 260 static int 261 pool_active(void *unused, const char *name, uint64_t guid, boolean_t *isactive) 262 { 263 (void) unused, (void) guid; 264 zfs_iocparm_t zp; 265 zfs_cmd_t *zc = NULL; 266 #ifdef ZFS_LEGACY_SUPPORT 267 zfs_cmd_legacy_t *zcl = NULL; 268 #endif 269 unsigned long request; 270 int ret; 271 272 int fd = open(ZFS_DEV, O_RDWR | O_CLOEXEC); 273 if (fd < 0) 274 return (-1); 275 276 /* 277 * Use ZFS_IOC_POOL_STATS to check if the pool is active. We want to 278 * avoid adding a dependency on libzfs_core solely for this ioctl(), 279 * therefore we manually craft the stats command. Note that the command 280 * ID is identical between the openzfs and legacy ioctl() formats. 281 */ 282 int ver = ZFS_IOCVER_NONE; 283 size_t ver_size = sizeof (ver); 284 285 sysctlbyname("vfs.zfs.version.ioctl", &ver, &ver_size, NULL, 0); 286 287 switch (ver) { 288 case ZFS_IOCVER_OZFS: 289 zc = umem_zalloc(sizeof (zfs_cmd_t), UMEM_NOFAIL); 290 291 (void) strlcpy(zc->zc_name, name, sizeof (zc->zc_name)); 292 zp.zfs_cmd = (uint64_t)(uintptr_t)zc; 293 zp.zfs_cmd_size = sizeof (zfs_cmd_t); 294 zp.zfs_ioctl_version = ZFS_IOCVER_OZFS; 295 296 request = _IOWR('Z', ZFS_IOC_POOL_STATS, zfs_iocparm_t); 297 ret = ioctl(fd, request, &zp); 298 299 free((void *)(uintptr_t)zc->zc_nvlist_dst); 300 umem_free(zc, sizeof (zfs_cmd_t)); 301 302 break; 303 #ifdef ZFS_LEGACY_SUPPORT 304 case ZFS_IOCVER_LEGACY: 305 zcl = umem_zalloc(sizeof (zfs_cmd_legacy_t), UMEM_NOFAIL); 306 307 (void) strlcpy(zcl->zc_name, name, sizeof (zcl->zc_name)); 308 zp.zfs_cmd = (uint64_t)(uintptr_t)zcl; 309 zp.zfs_cmd_size = sizeof (zfs_cmd_legacy_t); 310 zp.zfs_ioctl_version = ZFS_IOCVER_LEGACY; 311 312 request = _IOWR('Z', ZFS_IOC_POOL_STATS, zfs_iocparm_t); 313 ret = ioctl(fd, request, &zp); 314 315 free((void *)(uintptr_t)zcl->zc_nvlist_dst); 316 umem_free(zcl, sizeof (zfs_cmd_legacy_t)); 317 318 break; 319 #endif 320 default: 321 fprintf(stderr, "unrecognized zfs ioctl version %d", ver); 322 exit(1); 323 } 324 325 (void) close(fd); 326 327 *isactive = (ret == 0); 328 329 return (0); 330 } 331 #else 332 static int 333 pool_active(void *unused, const char *name, uint64_t guid, 334 boolean_t *isactive) 335 { 336 (void) unused, (void) guid; 337 int fd = open(ZFS_DEV, O_RDWR | O_CLOEXEC); 338 if (fd < 0) 339 return (-1); 340 341 /* 342 * Use ZFS_IOC_POOL_STATS to check if a pool is active. 343 */ 344 zfs_cmd_t *zcp = umem_zalloc(sizeof (zfs_cmd_t), UMEM_NOFAIL); 345 (void) strlcpy(zcp->zc_name, name, sizeof (zcp->zc_name)); 346 347 int ret = ioctl(fd, ZFS_IOC_POOL_STATS, zcp); 348 349 free((void *)(uintptr_t)zcp->zc_nvlist_dst); 350 umem_free(zcp, sizeof (zfs_cmd_t)); 351 352 (void) close(fd); 353 354 *isactive = (ret == 0); 355 356 return (0); 357 } 358 #endif 359 360 pool_config_ops_t libzpool_config_ops = { 361 .pco_refresh_config = refresh_config, 362 .pco_pool_active = pool_active, 363 }; 364