xref: /illumos-gate/usr/src/lib/libzfs/common/libzfs_status.c (revision d0698e0d179f97729cacdbc2f13446a6b0a3f22a)
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  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * This file contains the functions which analyze the status of a pool.  This
27  * include both the status of an active pool, as well as the status exported
28  * pools.  Returns one of the ZPOOL_STATUS_* defines describing the status of
29  * the pool.  This status is independent (to a certain degree) from the state of
30  * the pool.  A pool's state describes only whether or not it is capable of
31  * providing the necessary fault tolerance for data.  The status describes the
32  * overall status of devices.  A pool that is online can still have a device
33  * that is experiencing errors.
34  *
35  * Only a subset of the possible faults can be detected using 'zpool status',
36  * and not all possible errors correspond to a FMA message ID.  The explanation
37  * is left up to the caller, depending on whether it is a live pool or an
38  * import.
39  */
40 
41 #include <libzfs.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include "libzfs_impl.h"
45 
46 /*
47  * Message ID table.  This must be kept in sync with the ZPOOL_STATUS_* defines
48  * in libzfs.h.  Note that there are some status results which go past the end
49  * of this table, and hence have no associated message ID.
50  */
51 static char *zfs_msgid_table[] = {
52 	"ZFS-8000-14",
53 	"ZFS-8000-2Q",
54 	"ZFS-8000-3C",
55 	"ZFS-8000-4J",
56 	"ZFS-8000-5E",
57 	"ZFS-8000-6X",
58 	"ZFS-8000-72",
59 	"ZFS-8000-8A",
60 	"ZFS-8000-9P",
61 	"ZFS-8000-A5",
62 	"ZFS-8000-EY",
63 	"ZFS-8000-HC",
64 	"ZFS-8000-JQ",
65 	"ZFS-8000-K4",
66 };
67 
68 #define	NMSGID	(sizeof (zfs_msgid_table) / sizeof (zfs_msgid_table[0]))
69 
70 /* ARGSUSED */
71 static int
72 vdev_missing(uint64_t state, uint64_t aux, uint64_t errs)
73 {
74 	return (state == VDEV_STATE_CANT_OPEN &&
75 	    aux == VDEV_AUX_OPEN_FAILED);
76 }
77 
78 /* ARGSUSED */
79 static int
80 vdev_faulted(uint64_t state, uint64_t aux, uint64_t errs)
81 {
82 	return (state == VDEV_STATE_FAULTED);
83 }
84 
85 /* ARGSUSED */
86 static int
87 vdev_errors(uint64_t state, uint64_t aux, uint64_t errs)
88 {
89 	return (state == VDEV_STATE_DEGRADED || errs != 0);
90 }
91 
92 /* ARGSUSED */
93 static int
94 vdev_broken(uint64_t state, uint64_t aux, uint64_t errs)
95 {
96 	return (state == VDEV_STATE_CANT_OPEN);
97 }
98 
99 /* ARGSUSED */
100 static int
101 vdev_offlined(uint64_t state, uint64_t aux, uint64_t errs)
102 {
103 	return (state == VDEV_STATE_OFFLINE);
104 }
105 
106 /* ARGSUSED */
107 static int
108 vdev_removed(uint64_t state, uint64_t aux, uint64_t errs)
109 {
110 	return (state == VDEV_STATE_REMOVED);
111 }
112 
113 /*
114  * Detect if any leaf devices that have seen errors or could not be opened.
115  */
116 static boolean_t
117 find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t))
118 {
119 	nvlist_t **child;
120 	vdev_stat_t *vs;
121 	uint_t c, children;
122 	char *type;
123 
124 	/*
125 	 * Ignore problems within a 'replacing' vdev, since we're presumably in
126 	 * the process of repairing any such errors, and don't want to call them
127 	 * out again.  We'll pick up the fact that a resilver is happening
128 	 * later.
129 	 */
130 	verify(nvlist_lookup_string(vdev, ZPOOL_CONFIG_TYPE, &type) == 0);
131 	if (strcmp(type, VDEV_TYPE_REPLACING) == 0)
132 		return (B_FALSE);
133 
134 	if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, &child,
135 	    &children) == 0) {
136 		for (c = 0; c < children; c++)
137 			if (find_vdev_problem(child[c], func))
138 				return (B_TRUE);
139 	} else {
140 		verify(nvlist_lookup_uint64_array(vdev, ZPOOL_CONFIG_VDEV_STATS,
141 		    (uint64_t **)&vs, &c) == 0);
142 
143 		if (func(vs->vs_state, vs->vs_aux,
144 		    vs->vs_read_errors +
145 		    vs->vs_write_errors +
146 		    vs->vs_checksum_errors))
147 			return (B_TRUE);
148 	}
149 
150 	return (B_FALSE);
151 }
152 
153 /*
154  * Active pool health status.
155  *
156  * To determine the status for a pool, we make several passes over the config,
157  * picking the most egregious error we find.  In order of importance, we do the
158  * following:
159  *
160  *	- Check for a complete and valid configuration
161  *	- Look for any faulted or missing devices in a non-replicated config
162  *	- Check for any data errors
163  *	- Check for any faulted or missing devices in a replicated config
164  *	- Look for any devices showing errors
165  *	- Check for any resilvering devices
166  *
167  * There can obviously be multiple errors within a single pool, so this routine
168  * only picks the most damaging of all the current errors to report.
169  */
170 static zpool_status_t
171 check_status(nvlist_t *config, boolean_t isimport)
172 {
173 	nvlist_t *nvroot;
174 	vdev_stat_t *vs;
175 	pool_scan_stat_t *ps = NULL;
176 	uint_t vsc, psc;
177 	uint64_t nerr;
178 	uint64_t version;
179 	uint64_t stateval;
180 	uint64_t suspended;
181 	uint64_t hostid = 0;
182 
183 	verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
184 	    &version) == 0);
185 	verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
186 	    &nvroot) == 0);
187 	verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
188 	    (uint64_t **)&vs, &vsc) == 0);
189 	verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
190 	    &stateval) == 0);
191 
192 	/*
193 	 * Currently resilvering a vdev
194 	 */
195 	(void) nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_SCAN_STATS,
196 	    (uint64_t **)&ps, &psc);
197 	if (ps && ps->pss_func == POOL_SCAN_RESILVER &&
198 	    ps->pss_state == DSS_SCANNING)
199 		return (ZPOOL_STATUS_RESILVERING);
200 
201 	/*
202 	 * Pool last accessed by another system.
203 	 */
204 	(void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, &hostid);
205 	if (hostid != 0 && (unsigned long)hostid != gethostid() &&
206 	    stateval == POOL_STATE_ACTIVE)
207 		return (ZPOOL_STATUS_HOSTID_MISMATCH);
208 
209 	/*
210 	 * Newer on-disk version.
211 	 */
212 	if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
213 	    vs->vs_aux == VDEV_AUX_VERSION_NEWER)
214 		return (ZPOOL_STATUS_VERSION_NEWER);
215 
216 	/*
217 	 * Check that the config is complete.
218 	 */
219 	if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
220 	    vs->vs_aux == VDEV_AUX_BAD_GUID_SUM)
221 		return (ZPOOL_STATUS_BAD_GUID_SUM);
222 
223 	/*
224 	 * Check whether the pool has suspended due to failed I/O.
225 	 */
226 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED,
227 	    &suspended) == 0) {
228 		if (suspended == ZIO_FAILURE_MODE_CONTINUE)
229 			return (ZPOOL_STATUS_IO_FAILURE_CONTINUE);
230 		return (ZPOOL_STATUS_IO_FAILURE_WAIT);
231 	}
232 
233 	/*
234 	 * Could not read a log.
235 	 */
236 	if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
237 	    vs->vs_aux == VDEV_AUX_BAD_LOG) {
238 		return (ZPOOL_STATUS_BAD_LOG);
239 	}
240 
241 	/*
242 	 * Bad devices in non-replicated config.
243 	 */
244 	if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
245 	    find_vdev_problem(nvroot, vdev_faulted))
246 		return (ZPOOL_STATUS_FAULTED_DEV_NR);
247 
248 	if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
249 	    find_vdev_problem(nvroot, vdev_missing))
250 		return (ZPOOL_STATUS_MISSING_DEV_NR);
251 
252 	if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
253 	    find_vdev_problem(nvroot, vdev_broken))
254 		return (ZPOOL_STATUS_CORRUPT_LABEL_NR);
255 
256 	/*
257 	 * Corrupted pool metadata
258 	 */
259 	if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
260 	    vs->vs_aux == VDEV_AUX_CORRUPT_DATA)
261 		return (ZPOOL_STATUS_CORRUPT_POOL);
262 
263 	/*
264 	 * Persistent data errors.
265 	 */
266 	if (!isimport) {
267 		if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT,
268 		    &nerr) == 0 && nerr != 0)
269 			return (ZPOOL_STATUS_CORRUPT_DATA);
270 	}
271 
272 	/*
273 	 * Missing devices in a replicated config.
274 	 */
275 	if (find_vdev_problem(nvroot, vdev_faulted))
276 		return (ZPOOL_STATUS_FAULTED_DEV_R);
277 	if (find_vdev_problem(nvroot, vdev_missing))
278 		return (ZPOOL_STATUS_MISSING_DEV_R);
279 	if (find_vdev_problem(nvroot, vdev_broken))
280 		return (ZPOOL_STATUS_CORRUPT_LABEL_R);
281 
282 	/*
283 	 * Devices with errors
284 	 */
285 	if (!isimport && find_vdev_problem(nvroot, vdev_errors))
286 		return (ZPOOL_STATUS_FAILING_DEV);
287 
288 	/*
289 	 * Offlined devices
290 	 */
291 	if (find_vdev_problem(nvroot, vdev_offlined))
292 		return (ZPOOL_STATUS_OFFLINE_DEV);
293 
294 	/*
295 	 * Removed device
296 	 */
297 	if (find_vdev_problem(nvroot, vdev_removed))
298 		return (ZPOOL_STATUS_REMOVED_DEV);
299 
300 	/*
301 	 * Outdated, but usable, version
302 	 */
303 	if (version < SPA_VERSION)
304 		return (ZPOOL_STATUS_VERSION_OLDER);
305 
306 	return (ZPOOL_STATUS_OK);
307 }
308 
309 zpool_status_t
310 zpool_get_status(zpool_handle_t *zhp, char **msgid)
311 {
312 	zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE);
313 
314 	if (ret >= NMSGID)
315 		*msgid = NULL;
316 	else
317 		*msgid = zfs_msgid_table[ret];
318 
319 	return (ret);
320 }
321 
322 zpool_status_t
323 zpool_import_status(nvlist_t *config, char **msgid)
324 {
325 	zpool_status_t ret = check_status(config, B_TRUE);
326 
327 	if (ret >= NMSGID)
328 		*msgid = NULL;
329 	else
330 		*msgid = zfs_msgid_table[ret];
331 
332 	return (ret);
333 }
334 
335 static void
336 dump_ddt_stat(const ddt_stat_t *dds, int h)
337 {
338 	char refcnt[6];
339 	char blocks[6], lsize[6], psize[6], dsize[6];
340 	char ref_blocks[6], ref_lsize[6], ref_psize[6], ref_dsize[6];
341 
342 	if (dds == NULL || dds->dds_blocks == 0)
343 		return;
344 
345 	if (h == -1)
346 		(void) strcpy(refcnt, "Total");
347 	else
348 		zfs_nicenum(1ULL << h, refcnt, sizeof (refcnt));
349 
350 	zfs_nicenum(dds->dds_blocks, blocks, sizeof (blocks));
351 	zfs_nicenum(dds->dds_lsize, lsize, sizeof (lsize));
352 	zfs_nicenum(dds->dds_psize, psize, sizeof (psize));
353 	zfs_nicenum(dds->dds_dsize, dsize, sizeof (dsize));
354 	zfs_nicenum(dds->dds_ref_blocks, ref_blocks, sizeof (ref_blocks));
355 	zfs_nicenum(dds->dds_ref_lsize, ref_lsize, sizeof (ref_lsize));
356 	zfs_nicenum(dds->dds_ref_psize, ref_psize, sizeof (ref_psize));
357 	zfs_nicenum(dds->dds_ref_dsize, ref_dsize, sizeof (ref_dsize));
358 
359 	(void) printf("%6s   %6s   %5s   %5s   %5s   %6s   %5s   %5s   %5s\n",
360 	    refcnt,
361 	    blocks, lsize, psize, dsize,
362 	    ref_blocks, ref_lsize, ref_psize, ref_dsize);
363 }
364 
365 /*
366  * Print the DDT histogram and the column totals.
367  */
368 void
369 zpool_dump_ddt(const ddt_stat_t *dds_total, const ddt_histogram_t *ddh)
370 {
371 	int h;
372 
373 	(void) printf("\n");
374 
375 	(void) printf("bucket   "
376 	    "           allocated             "
377 	    "          referenced          \n");
378 	(void) printf("______   "
379 	    "______________________________   "
380 	    "______________________________\n");
381 
382 	(void) printf("%6s   %6s   %5s   %5s   %5s   %6s   %5s   %5s   %5s\n",
383 	    "refcnt",
384 	    "blocks", "LSIZE", "PSIZE", "DSIZE",
385 	    "blocks", "LSIZE", "PSIZE", "DSIZE");
386 
387 	(void) printf("%6s   %6s   %5s   %5s   %5s   %6s   %5s   %5s   %5s\n",
388 	    "------",
389 	    "------", "-----", "-----", "-----",
390 	    "------", "-----", "-----", "-----");
391 
392 	for (h = 0; h < 64; h++)
393 		dump_ddt_stat(&ddh->ddh_stat[h], h);
394 
395 	dump_ddt_stat(dds_total, -1);
396 
397 	(void) printf("\n");
398 }
399