xref: /freebsd/sys/contrib/openzfs/module/zfs/spa_stats.c (revision 22649d4dba730d46244fd2dff4fd174903c8379f)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * This file and its contents are supplied under the terms of the
4  * Common Development and Distribution License ("CDDL"), version 1.0.
5  * You may only use this file in accordance with the terms of version
6  * 1.0 of the CDDL.
7  *
8  * A full copy of the text of the CDDL should have accompanied this
9  * source.  A copy of the CDDL is also available via the Internet at
10  * https://opensource.org/license/CDDL-1.0.
11  */
12 
13 /*
14  * Copyright (c) 2024-2026, Klara, Inc.
15  * Copyright (c) 2026, TrueNAS.
16  */
17 
18 #include <sys/zfs_context.h>
19 #include <sys/spa_impl.h>
20 #include <sys/vdev_impl.h>
21 #include <sys/spa.h>
22 #include <zfs_comutil.h>
23 
24 /*
25  * Keeps stats on last N reads per spa_t, disabled by default.
26  */
27 static uint_t zfs_read_history = B_FALSE;
28 
29 /*
30  * Include cache hits in history, disabled by default.
31  */
32 static int zfs_read_history_hits = B_FALSE;
33 
34 /*
35  * Keeps stats on the last 100 txgs by default.
36  */
37 static uint_t zfs_txg_history = 100;
38 
39 /*
40  * Keeps stats on the last N MMP updates, disabled by default.
41  */
42 static uint_t zfs_multihost_history = B_FALSE;
43 
44 /*
45  * ==========================================================================
46  * SPA Read History Routines
47  * ==========================================================================
48  */
49 
50 /*
51  * Read statistics - Information exported regarding each arc_read call
52  */
53 typedef struct spa_read_history {
54 	hrtime_t	start;		/* time read completed */
55 	uint64_t	objset;		/* read from this objset */
56 	uint64_t	object;		/* read of this object number */
57 	uint64_t	level;		/* block's indirection level */
58 	uint64_t	blkid;		/* read of this block id */
59 	char		origin[24];	/* read originated from here */
60 	uint32_t	aflags;		/* ARC flags (cached, prefetch, etc.) */
61 	pid_t		pid;		/* PID of task doing read */
62 	char		comm[16];	/* process name of task doing read */
63 	procfs_list_node_t	srh_node;
64 } spa_read_history_t;
65 
66 static int
spa_read_history_show_header(struct seq_file * f)67 spa_read_history_show_header(struct seq_file *f)
68 {
69 	seq_printf(f, "%-8s %-16s %-8s %-8s %-8s %-8s %-8s "
70 	    "%-24s %-8s %-16s\n", "UID", "start", "objset", "object",
71 	    "level", "blkid", "aflags", "origin", "pid", "process");
72 
73 	return (0);
74 }
75 
76 static int
spa_read_history_show(struct seq_file * f,void * data)77 spa_read_history_show(struct seq_file *f, void *data)
78 {
79 	spa_read_history_t *srh = (spa_read_history_t *)data;
80 
81 	seq_printf(f, "%-8llu %-16llu 0x%-6llx "
82 	    "%-8lli %-8lli %-8lli 0x%-6x %-24s %-8i %-16s\n",
83 	    (u_longlong_t)srh->srh_node.pln_id, srh->start,
84 	    (longlong_t)srh->objset, (longlong_t)srh->object,
85 	    (longlong_t)srh->level, (longlong_t)srh->blkid,
86 	    srh->aflags, srh->origin, srh->pid, srh->comm);
87 
88 	return (0);
89 }
90 
91 /* Remove oldest elements from list until there are no more than 'size' left */
92 static void
spa_read_history_truncate(spa_history_list_t * shl,unsigned int size)93 spa_read_history_truncate(spa_history_list_t *shl, unsigned int size)
94 {
95 	spa_read_history_t *srh;
96 	while (shl->size > size) {
97 		srh = list_remove_head(&shl->procfs_list.pl_list);
98 		ASSERT3P(srh, !=, NULL);
99 		kmem_free(srh, sizeof (spa_read_history_t));
100 		shl->size--;
101 	}
102 
103 	if (size == 0)
104 		ASSERT(list_is_empty(&shl->procfs_list.pl_list));
105 }
106 
107 static int
spa_read_history_clear(procfs_list_t * procfs_list)108 spa_read_history_clear(procfs_list_t *procfs_list)
109 {
110 	spa_history_list_t *shl = procfs_list->pl_private;
111 	mutex_enter(&procfs_list->pl_lock);
112 	spa_read_history_truncate(shl, 0);
113 	mutex_exit(&procfs_list->pl_lock);
114 	return (0);
115 }
116 
117 static void
spa_read_history_init(spa_t * spa)118 spa_read_history_init(spa_t *spa)
119 {
120 	spa_history_list_t *shl = &spa->spa_stats.read_history;
121 
122 	shl->size = 0;
123 	shl->procfs_list.pl_private = shl;
124 	procfs_list_install("zfs",
125 	    spa_name(spa),
126 	    "reads",
127 	    0600,
128 	    &shl->procfs_list,
129 	    spa_read_history_show,
130 	    spa_read_history_show_header,
131 	    spa_read_history_clear,
132 	    offsetof(spa_read_history_t, srh_node));
133 }
134 
135 static void
spa_read_history_destroy(spa_t * spa)136 spa_read_history_destroy(spa_t *spa)
137 {
138 	spa_history_list_t *shl = &spa->spa_stats.read_history;
139 	procfs_list_uninstall(&shl->procfs_list);
140 	spa_read_history_truncate(shl, 0);
141 	procfs_list_destroy(&shl->procfs_list);
142 }
143 
144 void
spa_read_history_add(spa_t * spa,const zbookmark_phys_t * zb,uint32_t aflags)145 spa_read_history_add(spa_t *spa, const zbookmark_phys_t *zb, uint32_t aflags)
146 {
147 	spa_history_list_t *shl = &spa->spa_stats.read_history;
148 	spa_read_history_t *srh;
149 
150 	ASSERT3P(spa, !=, NULL);
151 	ASSERT3P(zb,  !=, NULL);
152 
153 	if (zfs_read_history == 0 && shl->size == 0)
154 		return;
155 
156 	if (zfs_read_history_hits == 0 && (aflags & ARC_FLAG_CACHED))
157 		return;
158 
159 	srh = kmem_zalloc(sizeof (spa_read_history_t), KM_SLEEP);
160 	strlcpy(srh->comm, getcomm(), sizeof (srh->comm));
161 	srh->start  = gethrtime();
162 	srh->objset = zb->zb_objset;
163 	srh->object = zb->zb_object;
164 	srh->level  = zb->zb_level;
165 	srh->blkid  = zb->zb_blkid;
166 	srh->aflags = aflags;
167 	srh->pid    = getpid();
168 
169 	mutex_enter(&shl->procfs_list.pl_lock);
170 
171 	procfs_list_add(&shl->procfs_list, srh);
172 	shl->size++;
173 
174 	spa_read_history_truncate(shl, zfs_read_history);
175 
176 	mutex_exit(&shl->procfs_list.pl_lock);
177 }
178 
179 /*
180  * ==========================================================================
181  * SPA TXG History Routines
182  * ==========================================================================
183  */
184 
185 /*
186  * Txg statistics - Information exported regarding each txg sync
187  */
188 
189 typedef struct spa_txg_history {
190 	uint64_t	txg;		/* txg id */
191 	txg_state_t	state;		/* active txg state */
192 	uint64_t	nread;		/* number of bytes read */
193 	uint64_t	nwritten;	/* number of bytes written */
194 	uint64_t	reads;		/* number of read operations */
195 	uint64_t	writes;		/* number of write operations */
196 	uint64_t	ndirty;		/* number of dirty bytes */
197 	hrtime_t	times[TXG_STATE_COMMITTED]; /* completion times */
198 	procfs_list_node_t	sth_node;
199 } spa_txg_history_t;
200 
201 static int
spa_txg_history_show_header(struct seq_file * f)202 spa_txg_history_show_header(struct seq_file *f)
203 {
204 	seq_printf(f, "%-8s %-16s %-5s %-12s %-12s %-12s "
205 	    "%-8s %-8s %-12s %-12s %-12s %-12s\n", "txg", "birth", "state",
206 	    "ndirty", "nread", "nwritten", "reads", "writes",
207 	    "otime", "qtime", "wtime", "stime");
208 	return (0);
209 }
210 
211 static int
spa_txg_history_show(struct seq_file * f,void * data)212 spa_txg_history_show(struct seq_file *f, void *data)
213 {
214 	spa_txg_history_t *sth = (spa_txg_history_t *)data;
215 	uint64_t open = 0, quiesce = 0, wait = 0, sync = 0;
216 	char state;
217 
218 	switch (sth->state) {
219 		case TXG_STATE_BIRTH:		state = 'B';	break;
220 		case TXG_STATE_OPEN:		state = 'O';	break;
221 		case TXG_STATE_QUIESCED:	state = 'Q';	break;
222 		case TXG_STATE_WAIT_FOR_SYNC:	state = 'W';	break;
223 		case TXG_STATE_SYNCED:		state = 'S';	break;
224 		case TXG_STATE_COMMITTED:	state = 'C';	break;
225 		default:			state = '?';	break;
226 	}
227 
228 	if (sth->times[TXG_STATE_OPEN])
229 		open = sth->times[TXG_STATE_OPEN] -
230 		    sth->times[TXG_STATE_BIRTH];
231 
232 	if (sth->times[TXG_STATE_QUIESCED])
233 		quiesce = sth->times[TXG_STATE_QUIESCED] -
234 		    sth->times[TXG_STATE_OPEN];
235 
236 	if (sth->times[TXG_STATE_WAIT_FOR_SYNC])
237 		wait = sth->times[TXG_STATE_WAIT_FOR_SYNC] -
238 		    sth->times[TXG_STATE_QUIESCED];
239 
240 	if (sth->times[TXG_STATE_SYNCED])
241 		sync = sth->times[TXG_STATE_SYNCED] -
242 		    sth->times[TXG_STATE_WAIT_FOR_SYNC];
243 
244 	seq_printf(f, "%-8llu %-16llu %-5c %-12llu "
245 	    "%-12llu %-12llu %-8llu %-8llu %-12llu %-12llu %-12llu %-12llu\n",
246 	    (longlong_t)sth->txg, sth->times[TXG_STATE_BIRTH], state,
247 	    (u_longlong_t)sth->ndirty,
248 	    (u_longlong_t)sth->nread, (u_longlong_t)sth->nwritten,
249 	    (u_longlong_t)sth->reads, (u_longlong_t)sth->writes,
250 	    (u_longlong_t)open, (u_longlong_t)quiesce, (u_longlong_t)wait,
251 	    (u_longlong_t)sync);
252 
253 	return (0);
254 }
255 
256 /* Remove oldest elements from list until there are no more than 'size' left */
257 static void
spa_txg_history_truncate(spa_history_list_t * shl,unsigned int size)258 spa_txg_history_truncate(spa_history_list_t *shl, unsigned int size)
259 {
260 	spa_txg_history_t *sth;
261 	while (shl->size > size) {
262 		sth = list_remove_head(&shl->procfs_list.pl_list);
263 		ASSERT3P(sth, !=, NULL);
264 		kmem_free(sth, sizeof (spa_txg_history_t));
265 		shl->size--;
266 	}
267 
268 	if (size == 0)
269 		ASSERT(list_is_empty(&shl->procfs_list.pl_list));
270 
271 }
272 
273 static int
spa_txg_history_clear(procfs_list_t * procfs_list)274 spa_txg_history_clear(procfs_list_t *procfs_list)
275 {
276 	spa_history_list_t *shl = procfs_list->pl_private;
277 	mutex_enter(&procfs_list->pl_lock);
278 	spa_txg_history_truncate(shl, 0);
279 	mutex_exit(&procfs_list->pl_lock);
280 	return (0);
281 }
282 
283 static void
spa_txg_history_init(spa_t * spa)284 spa_txg_history_init(spa_t *spa)
285 {
286 	spa_history_list_t *shl = &spa->spa_stats.txg_history;
287 
288 	shl->size = 0;
289 	shl->procfs_list.pl_private = shl;
290 	procfs_list_install("zfs",
291 	    spa_name(spa),
292 	    "txgs",
293 	    0644,
294 	    &shl->procfs_list,
295 	    spa_txg_history_show,
296 	    spa_txg_history_show_header,
297 	    spa_txg_history_clear,
298 	    offsetof(spa_txg_history_t, sth_node));
299 }
300 
301 static void
spa_txg_history_destroy(spa_t * spa)302 spa_txg_history_destroy(spa_t *spa)
303 {
304 	spa_history_list_t *shl = &spa->spa_stats.txg_history;
305 	procfs_list_uninstall(&shl->procfs_list);
306 	spa_txg_history_truncate(shl, 0);
307 	procfs_list_destroy(&shl->procfs_list);
308 }
309 
310 /*
311  * Add a new txg to historical record.
312  */
313 void
spa_txg_history_add(spa_t * spa,uint64_t txg,hrtime_t birth_time)314 spa_txg_history_add(spa_t *spa, uint64_t txg, hrtime_t birth_time)
315 {
316 	spa_history_list_t *shl = &spa->spa_stats.txg_history;
317 	spa_txg_history_t *sth;
318 
319 	if (zfs_txg_history == 0 && shl->size == 0)
320 		return;
321 
322 	sth = kmem_zalloc(sizeof (spa_txg_history_t), KM_SLEEP);
323 	sth->txg = txg;
324 	sth->state = TXG_STATE_OPEN;
325 	sth->times[TXG_STATE_BIRTH] = birth_time;
326 
327 	mutex_enter(&shl->procfs_list.pl_lock);
328 	procfs_list_add(&shl->procfs_list, sth);
329 	shl->size++;
330 	spa_txg_history_truncate(shl, zfs_txg_history);
331 	mutex_exit(&shl->procfs_list.pl_lock);
332 }
333 
334 /*
335  * Set txg state completion time and increment current state.
336  */
337 int
spa_txg_history_set(spa_t * spa,uint64_t txg,txg_state_t completed_state,hrtime_t completed_time)338 spa_txg_history_set(spa_t *spa, uint64_t txg, txg_state_t completed_state,
339     hrtime_t completed_time)
340 {
341 	spa_history_list_t *shl = &spa->spa_stats.txg_history;
342 	spa_txg_history_t *sth;
343 	int error = ENOENT;
344 
345 	if (zfs_txg_history == 0)
346 		return (0);
347 
348 	mutex_enter(&shl->procfs_list.pl_lock);
349 	for (sth = list_tail(&shl->procfs_list.pl_list); sth != NULL;
350 	    sth = list_prev(&shl->procfs_list.pl_list, sth)) {
351 		if (sth->txg == txg) {
352 			sth->times[completed_state] = completed_time;
353 			sth->state++;
354 			error = 0;
355 			break;
356 		}
357 	}
358 	mutex_exit(&shl->procfs_list.pl_lock);
359 
360 	return (error);
361 }
362 
363 /*
364  * Set txg IO stats.
365  */
366 static int
spa_txg_history_set_io(spa_t * spa,uint64_t txg,uint64_t nread,uint64_t nwritten,uint64_t reads,uint64_t writes,uint64_t ndirty)367 spa_txg_history_set_io(spa_t *spa, uint64_t txg, uint64_t nread,
368     uint64_t nwritten, uint64_t reads, uint64_t writes, uint64_t ndirty)
369 {
370 	spa_history_list_t *shl = &spa->spa_stats.txg_history;
371 	spa_txg_history_t *sth;
372 	int error = ENOENT;
373 
374 	if (zfs_txg_history == 0)
375 		return (0);
376 
377 	mutex_enter(&shl->procfs_list.pl_lock);
378 	for (sth = list_tail(&shl->procfs_list.pl_list); sth != NULL;
379 	    sth = list_prev(&shl->procfs_list.pl_list, sth)) {
380 		if (sth->txg == txg) {
381 			sth->nread = nread;
382 			sth->nwritten = nwritten;
383 			sth->reads = reads;
384 			sth->writes = writes;
385 			sth->ndirty = ndirty;
386 			error = 0;
387 			break;
388 		}
389 	}
390 	mutex_exit(&shl->procfs_list.pl_lock);
391 
392 	return (error);
393 }
394 
395 txg_stat_t *
spa_txg_history_init_io(spa_t * spa,uint64_t txg,dsl_pool_t * dp)396 spa_txg_history_init_io(spa_t *spa, uint64_t txg, dsl_pool_t *dp)
397 {
398 	txg_stat_t *ts;
399 
400 	if (zfs_txg_history == 0)
401 		return (NULL);
402 
403 	ts = kmem_alloc(sizeof (txg_stat_t), KM_SLEEP);
404 
405 	spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
406 	vdev_get_stats(spa->spa_root_vdev, &ts->vs1);
407 	spa_config_exit(spa, SCL_CONFIG, FTAG);
408 
409 	ts->txg = txg;
410 	ts->ndirty = dp->dp_dirty_pertxg[txg & TXG_MASK];
411 
412 	spa_txg_history_set(spa, txg, TXG_STATE_WAIT_FOR_SYNC, gethrtime());
413 
414 	return (ts);
415 }
416 
417 void
spa_txg_history_fini_io(spa_t * spa,txg_stat_t * ts)418 spa_txg_history_fini_io(spa_t *spa, txg_stat_t *ts)
419 {
420 	if (ts == NULL)
421 		return;
422 
423 	if (zfs_txg_history == 0) {
424 		kmem_free(ts, sizeof (txg_stat_t));
425 		return;
426 	}
427 
428 	spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
429 	vdev_get_stats(spa->spa_root_vdev, &ts->vs2);
430 	spa_config_exit(spa, SCL_CONFIG, FTAG);
431 
432 	spa_txg_history_set(spa, ts->txg, TXG_STATE_SYNCED, gethrtime());
433 	spa_txg_history_set_io(spa, ts->txg,
434 	    ts->vs2.vs_bytes[ZIO_TYPE_READ] - ts->vs1.vs_bytes[ZIO_TYPE_READ],
435 	    ts->vs2.vs_bytes[ZIO_TYPE_WRITE] - ts->vs1.vs_bytes[ZIO_TYPE_WRITE],
436 	    ts->vs2.vs_ops[ZIO_TYPE_READ] - ts->vs1.vs_ops[ZIO_TYPE_READ],
437 	    ts->vs2.vs_ops[ZIO_TYPE_WRITE] - ts->vs1.vs_ops[ZIO_TYPE_WRITE],
438 	    ts->ndirty);
439 
440 	kmem_free(ts, sizeof (txg_stat_t));
441 }
442 
443 /*
444  * ==========================================================================
445  * SPA TX Assign Histogram Routines
446  * ==========================================================================
447  */
448 
449 /*
450  * Tx statistics - Information exported regarding dmu_tx_assign time.
451  */
452 
453 /*
454  * When the kstat is written zero all buckets.  When the kstat is read
455  * count the number of trailing buckets set to zero and update ks_ndata
456  * such that they are not output.
457  */
458 static int
spa_tx_assign_update(kstat_t * ksp,int rw)459 spa_tx_assign_update(kstat_t *ksp, int rw)
460 {
461 	spa_t *spa = ksp->ks_private;
462 	spa_history_kstat_t *shk = &spa->spa_stats.tx_assign_histogram;
463 	int i;
464 
465 	if (rw == KSTAT_WRITE) {
466 		for (i = 0; i < shk->count; i++)
467 			((kstat_named_t *)shk->priv)[i].value.ui64 = 0;
468 	}
469 
470 	for (i = shk->count; i > 0; i--)
471 		if (((kstat_named_t *)shk->priv)[i-1].value.ui64 != 0)
472 			break;
473 
474 	ksp->ks_ndata = i;
475 	ksp->ks_data_size = i * sizeof (kstat_named_t);
476 
477 	return (0);
478 }
479 
480 static void
spa_tx_assign_init(spa_t * spa)481 spa_tx_assign_init(spa_t *spa)
482 {
483 	spa_history_kstat_t *shk = &spa->spa_stats.tx_assign_histogram;
484 	char *name;
485 	kstat_named_t *ks;
486 	kstat_t *ksp;
487 	int i;
488 
489 	mutex_init(&shk->lock, NULL, MUTEX_DEFAULT, NULL);
490 
491 	shk->count = 42; /* power of two buckets for 1ns to 2,199s */
492 	shk->size = shk->count * sizeof (kstat_named_t);
493 	shk->priv = kmem_alloc(shk->size, KM_SLEEP);
494 
495 	name = kmem_asprintf("zfs/%s", spa_name(spa));
496 
497 	for (i = 0; i < shk->count; i++) {
498 		ks = &((kstat_named_t *)shk->priv)[i];
499 		ks->data_type = KSTAT_DATA_UINT64;
500 		ks->value.ui64 = 0;
501 		(void) snprintf(ks->name, KSTAT_STRLEN, "%llu ns",
502 		    (u_longlong_t)1 << i);
503 	}
504 
505 	ksp = kstat_create(name, 0, "dmu_tx_assign", "misc",
506 	    KSTAT_TYPE_NAMED, 0, KSTAT_FLAG_VIRTUAL);
507 	shk->kstat = ksp;
508 
509 	if (ksp) {
510 		ksp->ks_lock = &shk->lock;
511 		ksp->ks_data = shk->priv;
512 		ksp->ks_ndata = shk->count;
513 		ksp->ks_data_size = shk->size;
514 		ksp->ks_private = spa;
515 		ksp->ks_update = spa_tx_assign_update;
516 		kstat_install(ksp);
517 	}
518 	kmem_strfree(name);
519 }
520 
521 static void
spa_tx_assign_destroy(spa_t * spa)522 spa_tx_assign_destroy(spa_t *spa)
523 {
524 	spa_history_kstat_t *shk = &spa->spa_stats.tx_assign_histogram;
525 	kstat_t *ksp;
526 
527 	ksp = shk->kstat;
528 	if (ksp)
529 		kstat_delete(ksp);
530 
531 	kmem_free(shk->priv, shk->size);
532 	mutex_destroy(&shk->lock);
533 }
534 
535 void
spa_tx_assign_add_nsecs(spa_t * spa,uint64_t nsecs)536 spa_tx_assign_add_nsecs(spa_t *spa, uint64_t nsecs)
537 {
538 	spa_history_kstat_t *shk = &spa->spa_stats.tx_assign_histogram;
539 	uint64_t idx = 0;
540 
541 	while (((1ULL << idx) < nsecs) && (idx < shk->size - 1))
542 		idx++;
543 
544 	atomic_inc_64(&((kstat_named_t *)shk->priv)[idx].value.ui64);
545 }
546 
547 /*
548  * ==========================================================================
549  * SPA MMP History Routines
550  * ==========================================================================
551  */
552 
553 /*
554  * MMP statistics - Information exported regarding attempted MMP writes
555  *   For MMP writes issued, fields used as per comments below.
556  *   For MMP writes skipped, an entry represents a span of time when
557  *      writes were skipped for same reason (error from mmp_random_leaf).
558  *      Differences are:
559  *      timestamp	time first write skipped, if >1 skipped in a row
560  *      mmp_delay	delay value at timestamp
561  *      vdev_guid	number of writes skipped
562  *      io_error	one of enum mmp_error
563  *      duration	time span (ns) of skipped writes
564  */
565 
566 typedef struct spa_mmp_history {
567 	uint64_t	mmp_node_id;	/* unique # for updates */
568 	uint64_t	txg;		/* txg of last sync */
569 	uint64_t	timestamp;	/* UTC time MMP write issued */
570 	uint64_t	mmp_delay;	/* mmp_thread.mmp_delay at timestamp */
571 	uint64_t	vdev_guid;	/* unique ID of leaf vdev */
572 	char		*vdev_path;
573 	int		vdev_label;	/* vdev label */
574 	int		io_error;	/* error status of MMP write */
575 	hrtime_t	error_start;	/* hrtime of start of error period */
576 	hrtime_t	duration;	/* time from submission to completion */
577 	procfs_list_node_t	smh_node;
578 } spa_mmp_history_t;
579 
580 static int
spa_mmp_history_show_header(struct seq_file * f)581 spa_mmp_history_show_header(struct seq_file *f)
582 {
583 	seq_printf(f, "%-10s %-10s %-10s %-6s %-10s %-12s %-24s "
584 	    "%-10s %s\n", "id", "txg", "timestamp", "error", "duration",
585 	    "mmp_delay", "vdev_guid", "vdev_label", "vdev_path");
586 	return (0);
587 }
588 
589 static int
spa_mmp_history_show(struct seq_file * f,void * data)590 spa_mmp_history_show(struct seq_file *f, void *data)
591 {
592 	spa_mmp_history_t *smh = (spa_mmp_history_t *)data;
593 	char skip_fmt[] = "%-10llu %-10llu %10llu %#6llx %10lld %12llu %-24llu "
594 	    "%-10lld %s\n";
595 	char write_fmt[] = "%-10llu %-10llu %10llu %6lld %10lld %12llu %-24llu "
596 	    "%-10lld %s\n";
597 
598 	seq_printf(f, (smh->error_start ? skip_fmt : write_fmt),
599 	    (u_longlong_t)smh->mmp_node_id, (u_longlong_t)smh->txg,
600 	    (u_longlong_t)smh->timestamp, (longlong_t)smh->io_error,
601 	    (longlong_t)smh->duration, (u_longlong_t)smh->mmp_delay,
602 	    (u_longlong_t)smh->vdev_guid, (u_longlong_t)smh->vdev_label,
603 	    (smh->vdev_path ? smh->vdev_path : "-"));
604 
605 	return (0);
606 }
607 
608 /* Remove oldest elements from list until there are no more than 'size' left */
609 static void
spa_mmp_history_truncate(spa_history_list_t * shl,unsigned int size)610 spa_mmp_history_truncate(spa_history_list_t *shl, unsigned int size)
611 {
612 	spa_mmp_history_t *smh;
613 	while (shl->size > size) {
614 		smh = list_remove_head(&shl->procfs_list.pl_list);
615 		if (smh->vdev_path)
616 			kmem_strfree(smh->vdev_path);
617 		kmem_free(smh, sizeof (spa_mmp_history_t));
618 		shl->size--;
619 	}
620 
621 	if (size == 0)
622 		ASSERT(list_is_empty(&shl->procfs_list.pl_list));
623 
624 }
625 
626 static int
spa_mmp_history_clear(procfs_list_t * procfs_list)627 spa_mmp_history_clear(procfs_list_t *procfs_list)
628 {
629 	spa_history_list_t *shl = procfs_list->pl_private;
630 	mutex_enter(&procfs_list->pl_lock);
631 	spa_mmp_history_truncate(shl, 0);
632 	mutex_exit(&procfs_list->pl_lock);
633 	return (0);
634 }
635 
636 static void
spa_mmp_history_init(spa_t * spa)637 spa_mmp_history_init(spa_t *spa)
638 {
639 	spa_history_list_t *shl = &spa->spa_stats.mmp_history;
640 
641 	shl->size = 0;
642 
643 	shl->procfs_list.pl_private = shl;
644 	procfs_list_install("zfs",
645 	    spa_name(spa),
646 	    "multihost",
647 	    0644,
648 	    &shl->procfs_list,
649 	    spa_mmp_history_show,
650 	    spa_mmp_history_show_header,
651 	    spa_mmp_history_clear,
652 	    offsetof(spa_mmp_history_t, smh_node));
653 }
654 
655 static void
spa_mmp_history_destroy(spa_t * spa)656 spa_mmp_history_destroy(spa_t *spa)
657 {
658 	spa_history_list_t *shl = &spa->spa_stats.mmp_history;
659 	procfs_list_uninstall(&shl->procfs_list);
660 	spa_mmp_history_truncate(shl, 0);
661 	procfs_list_destroy(&shl->procfs_list);
662 }
663 
664 /*
665  * Set duration in existing "skip" record to how long we have waited for a leaf
666  * vdev to become available.
667  *
668  * Important that we start search at the tail of the list where new
669  * records are inserted, so this is normally an O(1) operation.
670  */
671 int
spa_mmp_history_set_skip(spa_t * spa,uint64_t mmp_node_id)672 spa_mmp_history_set_skip(spa_t *spa, uint64_t mmp_node_id)
673 {
674 	spa_history_list_t *shl = &spa->spa_stats.mmp_history;
675 	spa_mmp_history_t *smh;
676 	int error = ENOENT;
677 
678 	if (zfs_multihost_history == 0 && shl->size == 0)
679 		return (0);
680 
681 	mutex_enter(&shl->procfs_list.pl_lock);
682 	for (smh = list_tail(&shl->procfs_list.pl_list); smh != NULL;
683 	    smh = list_prev(&shl->procfs_list.pl_list, smh)) {
684 		if (smh->mmp_node_id == mmp_node_id) {
685 			ASSERT3U(smh->io_error, !=, 0);
686 			smh->duration = gethrtime() - smh->error_start;
687 			smh->vdev_guid++;
688 			error = 0;
689 			break;
690 		}
691 	}
692 	mutex_exit(&shl->procfs_list.pl_lock);
693 
694 	return (error);
695 }
696 
697 /*
698  * Set MMP write duration and error status in existing record.
699  * See comment re: search order above spa_mmp_history_set_skip().
700  */
701 int
spa_mmp_history_set(spa_t * spa,uint64_t mmp_node_id,int io_error,hrtime_t duration)702 spa_mmp_history_set(spa_t *spa, uint64_t mmp_node_id, int io_error,
703     hrtime_t duration)
704 {
705 	spa_history_list_t *shl = &spa->spa_stats.mmp_history;
706 	spa_mmp_history_t *smh;
707 	int error = ENOENT;
708 
709 	if (zfs_multihost_history == 0 && shl->size == 0)
710 		return (0);
711 
712 	mutex_enter(&shl->procfs_list.pl_lock);
713 	for (smh = list_tail(&shl->procfs_list.pl_list); smh != NULL;
714 	    smh = list_prev(&shl->procfs_list.pl_list, smh)) {
715 		if (smh->mmp_node_id == mmp_node_id) {
716 			ASSERT0(smh->io_error);
717 			smh->io_error = io_error;
718 			smh->duration = duration;
719 			error = 0;
720 			break;
721 		}
722 	}
723 	mutex_exit(&shl->procfs_list.pl_lock);
724 
725 	return (error);
726 }
727 
728 /*
729  * Add a new MMP historical record.
730  * error == 0 : a write was issued.
731  * error != 0 : a write was not issued because no leaves were found.
732  */
733 void
spa_mmp_history_add(spa_t * spa,uint64_t txg,uint64_t timestamp,uint64_t mmp_delay,vdev_t * vd,int label,uint64_t mmp_node_id,int error)734 spa_mmp_history_add(spa_t *spa, uint64_t txg, uint64_t timestamp,
735     uint64_t mmp_delay, vdev_t *vd, int label, uint64_t mmp_node_id,
736     int error)
737 {
738 	spa_history_list_t *shl = &spa->spa_stats.mmp_history;
739 	spa_mmp_history_t *smh;
740 
741 	if (zfs_multihost_history == 0 && shl->size == 0)
742 		return;
743 
744 	smh = kmem_zalloc(sizeof (spa_mmp_history_t), KM_SLEEP);
745 	smh->txg = txg;
746 	smh->timestamp = timestamp;
747 	smh->mmp_delay = mmp_delay;
748 	if (vd) {
749 		smh->vdev_guid = vd->vdev_guid;
750 		if (vd->vdev_path)
751 			smh->vdev_path = kmem_strdup(vd->vdev_path);
752 	}
753 	smh->vdev_label = label;
754 	smh->mmp_node_id = mmp_node_id;
755 
756 	if (error) {
757 		smh->io_error = error;
758 		smh->error_start = gethrtime();
759 		smh->vdev_guid = 1;
760 	}
761 
762 	mutex_enter(&shl->procfs_list.pl_lock);
763 	procfs_list_add(&shl->procfs_list, smh);
764 	shl->size++;
765 	spa_mmp_history_truncate(shl, zfs_multihost_history);
766 	mutex_exit(&shl->procfs_list.pl_lock);
767 }
768 
769 static void *
spa_state_addr(kstat_t * ksp,loff_t n)770 spa_state_addr(kstat_t *ksp, loff_t n)
771 {
772 	if (n == 0)
773 		return (ksp->ks_private);	/* return the spa_t */
774 	return (NULL);
775 }
776 
777 static int
spa_state_data(char * buf,size_t size,void * data)778 spa_state_data(char *buf, size_t size, void *data)
779 {
780 	spa_t *spa = (spa_t *)data;
781 	(void) snprintf(buf, size, "%s\n", spa_state_to_name(spa));
782 	return (0);
783 }
784 
785 /*
786  * Return the state of the pool in /proc/spl/kstat/zfs/<pool>/state.
787  *
788  * This is a lock-less read of the pool's state (unlike using 'zpool', which
789  * can potentially block for seconds).  Because it doesn't block, it can useful
790  * as a pool heartbeat value.
791  */
792 static void
spa_state_init(spa_t * spa)793 spa_state_init(spa_t *spa)
794 {
795 	spa_history_kstat_t *shk = &spa->spa_stats.state;
796 	char *name;
797 	kstat_t *ksp;
798 
799 	mutex_init(&shk->lock, NULL, MUTEX_DEFAULT, NULL);
800 
801 	name = kmem_asprintf("zfs/%s", spa_name(spa));
802 	ksp = kstat_create(name, 0, "state", "misc",
803 	    KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL);
804 
805 	shk->kstat = ksp;
806 	if (ksp) {
807 		ksp->ks_lock = &shk->lock;
808 		ksp->ks_data = NULL;
809 		ksp->ks_private = spa;
810 		ksp->ks_flags |= KSTAT_FLAG_NO_HEADERS;
811 		kstat_set_raw_ops(ksp, NULL, spa_state_data, spa_state_addr);
812 		kstat_install(ksp);
813 	}
814 
815 	kmem_strfree(name);
816 }
817 
818 static int
spa_guid_data(char * buf,size_t size,void * data)819 spa_guid_data(char *buf, size_t size, void *data)
820 {
821 	spa_t *spa = (spa_t *)data;
822 	(void) snprintf(buf, size, "%llu\n", (u_longlong_t)spa_guid(spa));
823 	return (0);
824 }
825 
826 static void
spa_guid_init(spa_t * spa)827 spa_guid_init(spa_t *spa)
828 {
829 	spa_history_kstat_t *shk = &spa->spa_stats.guid;
830 	char *name;
831 	kstat_t *ksp;
832 
833 	mutex_init(&shk->lock, NULL, MUTEX_DEFAULT, NULL);
834 
835 	name = kmem_asprintf("zfs/%s", spa_name(spa));
836 
837 	ksp = kstat_create(name, 0, "guid", "misc",
838 	    KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL);
839 
840 	shk->kstat = ksp;
841 	if (ksp) {
842 		ksp->ks_lock = &shk->lock;
843 		ksp->ks_data = NULL;
844 		ksp->ks_private = spa;
845 		ksp->ks_flags |= KSTAT_FLAG_NO_HEADERS;
846 		kstat_set_raw_ops(ksp, NULL, spa_guid_data, spa_state_addr);
847 		kstat_install(ksp);
848 	}
849 
850 	kmem_strfree(name);
851 }
852 
853 static void
spa_health_destroy(spa_t * spa)854 spa_health_destroy(spa_t *spa)
855 {
856 	spa_history_kstat_t *shk = &spa->spa_stats.state;
857 	kstat_t *ksp = shk->kstat;
858 	if (ksp)
859 		kstat_delete(ksp);
860 
861 	mutex_destroy(&shk->lock);
862 }
863 
864 static void
spa_guid_destroy(spa_t * spa)865 spa_guid_destroy(spa_t *spa)
866 {
867 	spa_history_kstat_t *shk = &spa->spa_stats.guid;
868 	kstat_t *ksp = shk->kstat;
869 	if (ksp)
870 		kstat_delete(ksp);
871 
872 	mutex_destroy(&shk->lock);
873 }
874 
875 static const spa_iostats_t spa_iostats_template = {
876 	{ "trim_extents_written",		KSTAT_DATA_UINT64 },
877 	{ "trim_bytes_written",			KSTAT_DATA_UINT64 },
878 	{ "trim_extents_skipped",		KSTAT_DATA_UINT64 },
879 	{ "trim_bytes_skipped",			KSTAT_DATA_UINT64 },
880 	{ "trim_extents_failed",		KSTAT_DATA_UINT64 },
881 	{ "trim_bytes_failed",			KSTAT_DATA_UINT64 },
882 	{ "autotrim_extents_written",		KSTAT_DATA_UINT64 },
883 	{ "autotrim_bytes_written",		KSTAT_DATA_UINT64 },
884 	{ "autotrim_extents_skipped",		KSTAT_DATA_UINT64 },
885 	{ "autotrim_bytes_skipped",		KSTAT_DATA_UINT64 },
886 	{ "autotrim_extents_failed",		KSTAT_DATA_UINT64 },
887 	{ "autotrim_bytes_failed",		KSTAT_DATA_UINT64 },
888 	{ "simple_trim_extents_written",	KSTAT_DATA_UINT64 },
889 	{ "simple_trim_bytes_written",		KSTAT_DATA_UINT64 },
890 	{ "simple_trim_extents_skipped",	KSTAT_DATA_UINT64 },
891 	{ "simple_trim_bytes_skipped",		KSTAT_DATA_UINT64 },
892 	{ "simple_trim_extents_failed",		KSTAT_DATA_UINT64 },
893 	{ "simple_trim_bytes_failed",		KSTAT_DATA_UINT64 },
894 	{ "arc_read_count",			KSTAT_DATA_UINT64 },
895 	{ "arc_read_bytes",			KSTAT_DATA_UINT64 },
896 	{ "arc_write_count",			KSTAT_DATA_UINT64 },
897 	{ "arc_write_bytes",			KSTAT_DATA_UINT64 },
898 	{ "direct_read_count",			KSTAT_DATA_UINT64 },
899 	{ "direct_read_bytes",			KSTAT_DATA_UINT64 },
900 	{ "direct_write_count",			KSTAT_DATA_UINT64 },
901 	{ "direct_write_bytes",			KSTAT_DATA_UINT64 },
902 };
903 
904 #define	SPA_IOSTATS_ADD(stat, val) \
905     atomic_add_64(&iostats->stat.value.ui64, (val));
906 
907 void
spa_iostats_trim_add(spa_t * spa,trim_type_t type,uint64_t extents_written,uint64_t bytes_written,uint64_t extents_skipped,uint64_t bytes_skipped,uint64_t extents_failed,uint64_t bytes_failed)908 spa_iostats_trim_add(spa_t *spa, trim_type_t type,
909     uint64_t extents_written, uint64_t bytes_written,
910     uint64_t extents_skipped, uint64_t bytes_skipped,
911     uint64_t extents_failed, uint64_t bytes_failed)
912 {
913 	spa_history_kstat_t *shk = &spa->spa_stats.iostats;
914 	kstat_t *ksp = shk->kstat;
915 	spa_iostats_t *iostats;
916 
917 	if (ksp == NULL)
918 		return;
919 
920 	iostats = ksp->ks_data;
921 	if (type == TRIM_TYPE_MANUAL) {
922 		SPA_IOSTATS_ADD(trim_extents_written, extents_written);
923 		SPA_IOSTATS_ADD(trim_bytes_written, bytes_written);
924 		SPA_IOSTATS_ADD(trim_extents_skipped, extents_skipped);
925 		SPA_IOSTATS_ADD(trim_bytes_skipped, bytes_skipped);
926 		SPA_IOSTATS_ADD(trim_extents_failed, extents_failed);
927 		SPA_IOSTATS_ADD(trim_bytes_failed, bytes_failed);
928 	} else if (type == TRIM_TYPE_AUTO) {
929 		SPA_IOSTATS_ADD(autotrim_extents_written, extents_written);
930 		SPA_IOSTATS_ADD(autotrim_bytes_written, bytes_written);
931 		SPA_IOSTATS_ADD(autotrim_extents_skipped, extents_skipped);
932 		SPA_IOSTATS_ADD(autotrim_bytes_skipped, bytes_skipped);
933 		SPA_IOSTATS_ADD(autotrim_extents_failed, extents_failed);
934 		SPA_IOSTATS_ADD(autotrim_bytes_failed, bytes_failed);
935 	} else {
936 		SPA_IOSTATS_ADD(simple_trim_extents_written, extents_written);
937 		SPA_IOSTATS_ADD(simple_trim_bytes_written, bytes_written);
938 		SPA_IOSTATS_ADD(simple_trim_extents_skipped, extents_skipped);
939 		SPA_IOSTATS_ADD(simple_trim_bytes_skipped, bytes_skipped);
940 		SPA_IOSTATS_ADD(simple_trim_extents_failed, extents_failed);
941 		SPA_IOSTATS_ADD(simple_trim_bytes_failed, bytes_failed);
942 	}
943 }
944 
945 void
spa_iostats_read_add(spa_t * spa,uint64_t size,uint64_t iops,dmu_flags_t flags)946 spa_iostats_read_add(spa_t *spa, uint64_t size, uint64_t iops,
947     dmu_flags_t flags)
948 {
949 	spa_history_kstat_t *shk = &spa->spa_stats.iostats;
950 	kstat_t *ksp = shk->kstat;
951 
952 	if (ksp == NULL)
953 		return;
954 
955 	spa_iostats_t *iostats = ksp->ks_data;
956 	if (flags & DMU_DIRECTIO) {
957 		SPA_IOSTATS_ADD(direct_read_count, iops);
958 		SPA_IOSTATS_ADD(direct_read_bytes, size);
959 	} else {
960 		SPA_IOSTATS_ADD(arc_read_count, iops);
961 		SPA_IOSTATS_ADD(arc_read_bytes, size);
962 	}
963 }
964 
965 void
spa_iostats_write_add(spa_t * spa,uint64_t size,uint64_t iops,dmu_flags_t flags)966 spa_iostats_write_add(spa_t *spa, uint64_t size, uint64_t iops,
967     dmu_flags_t flags)
968 {
969 	spa_history_kstat_t *shk = &spa->spa_stats.iostats;
970 	kstat_t *ksp = shk->kstat;
971 
972 	if (ksp == NULL)
973 		return;
974 
975 	spa_iostats_t *iostats = ksp->ks_data;
976 	if (flags & DMU_DIRECTIO) {
977 		SPA_IOSTATS_ADD(direct_write_count, iops);
978 		SPA_IOSTATS_ADD(direct_write_bytes, size);
979 	} else {
980 		SPA_IOSTATS_ADD(arc_write_count, iops);
981 		SPA_IOSTATS_ADD(arc_write_bytes, size);
982 	}
983 }
984 
985 static int
spa_iostats_update(kstat_t * ksp,int rw)986 spa_iostats_update(kstat_t *ksp, int rw)
987 {
988 	if (rw == KSTAT_WRITE) {
989 		memcpy(ksp->ks_data, &spa_iostats_template,
990 		    sizeof (spa_iostats_t));
991 	}
992 
993 	return (0);
994 }
995 
996 static void
spa_iostats_init(spa_t * spa)997 spa_iostats_init(spa_t *spa)
998 {
999 	spa_history_kstat_t *shk = &spa->spa_stats.iostats;
1000 
1001 	mutex_init(&shk->lock, NULL, MUTEX_DEFAULT, NULL);
1002 
1003 	char *name = kmem_asprintf("zfs/%s", spa_name(spa));
1004 	kstat_t *ksp = kstat_create(name, 0, "iostats", "misc",
1005 	    KSTAT_TYPE_NAMED, sizeof (spa_iostats_t) / sizeof (kstat_named_t),
1006 	    KSTAT_FLAG_VIRTUAL);
1007 
1008 	shk->kstat = ksp;
1009 	if (ksp) {
1010 		int size = sizeof (spa_iostats_t);
1011 		ksp->ks_lock = &shk->lock;
1012 		ksp->ks_private = spa;
1013 		ksp->ks_update = spa_iostats_update;
1014 		ksp->ks_data = kmem_alloc(size, KM_SLEEP);
1015 		memcpy(ksp->ks_data, &spa_iostats_template, size);
1016 		kstat_install(ksp);
1017 	}
1018 
1019 	kmem_strfree(name);
1020 }
1021 
1022 static void
spa_iostats_destroy(spa_t * spa)1023 spa_iostats_destroy(spa_t *spa)
1024 {
1025 	spa_history_kstat_t *shk = &spa->spa_stats.iostats;
1026 	kstat_t *ksp = shk->kstat;
1027 	if (ksp) {
1028 		kmem_free(ksp->ks_data, sizeof (spa_iostats_t));
1029 		kstat_delete(ksp);
1030 	}
1031 
1032 	mutex_destroy(&shk->lock);
1033 }
1034 
1035 /*
1036  * Log spacemap stats.
1037  */
1038 typedef struct spa_log_sm_stats {
1039 	kstat_named_t	unflushed_memused;
1040 	kstat_named_t	unflushed_nmetaslabs;
1041 	kstat_named_t	unflushed_blocklimit;
1042 	kstat_named_t	unflushed_nblocks;
1043 } spa_log_sm_stats_t;
1044 
1045 static spa_log_sm_stats_t spa_log_sm_stats_template = {
1046 	{ "unflushed_memused",		KSTAT_DATA_UINT64 },
1047 	{ "unflushed_nmetaslabs",	KSTAT_DATA_UINT64 },
1048 	{ "unflushed_blocklimit",	KSTAT_DATA_UINT64 },
1049 	{ "unflushed_nblocks",		KSTAT_DATA_UINT64 }
1050 };
1051 
1052 #define	SPA_LOG_SM_STATS_SET(stat, val) \
1053     atomic_store_64(&log_sm_stats->stat.value.ui64, (val));
1054 
1055 void
spa_log_sm_stats_update(spa_t * spa)1056 spa_log_sm_stats_update(spa_t *spa)
1057 {
1058 	spa_history_kstat_t *shk = &spa->spa_stats.log_spacemaps;
1059 	kstat_t *ksp = shk->kstat;
1060 
1061 	if (ksp == NULL)
1062 		return;
1063 
1064 	spa_log_sm_stats_t *log_sm_stats = ksp->ks_data;
1065 
1066 	SPA_LOG_SM_STATS_SET(unflushed_memused,
1067 	    spa->spa_unflushed_stats.sus_memused);
1068 	SPA_LOG_SM_STATS_SET(unflushed_nmetaslabs,
1069 	    spa->spa_unflushed_stats.sus_nmetaslabs);
1070 	SPA_LOG_SM_STATS_SET(unflushed_blocklimit,
1071 	    spa->spa_unflushed_stats.sus_blocklimit);
1072 	SPA_LOG_SM_STATS_SET(unflushed_nblocks,
1073 	    spa->spa_unflushed_stats.sus_nblocks);
1074 }
1075 
1076 static void
spa_log_sm_stats_init(spa_t * spa)1077 spa_log_sm_stats_init(spa_t *spa)
1078 {
1079 	spa_history_kstat_t *shk = &spa->spa_stats.log_spacemaps;
1080 
1081 	mutex_init(&shk->lock, NULL, MUTEX_DEFAULT, NULL);
1082 
1083 	char *name = kmem_asprintf("zfs/%s", spa_name(spa));
1084 	kstat_t *ksp = kstat_create(name, 0, "log_spacemaps", "misc",
1085 	    KSTAT_TYPE_NAMED,
1086 	    sizeof (spa_log_sm_stats_t) / sizeof (kstat_named_t),
1087 	    KSTAT_FLAG_VIRTUAL);
1088 
1089 	shk->kstat = ksp;
1090 	if (ksp) {
1091 		ksp->ks_lock = &shk->lock;
1092 		ksp->ks_data =
1093 		    kmem_alloc(sizeof (spa_log_sm_stats_t), KM_SLEEP);
1094 		memcpy(ksp->ks_data, &spa_log_sm_stats_template,
1095 		    sizeof (spa_log_sm_stats_t));
1096 		kstat_install(ksp);
1097 	}
1098 
1099 	kmem_strfree(name);
1100 }
1101 
1102 static void
spa_log_sm_stats_destroy(spa_t * spa)1103 spa_log_sm_stats_destroy(spa_t *spa)
1104 {
1105 	spa_history_kstat_t *shk = &spa->spa_stats.log_spacemaps;
1106 	kstat_t *ksp = shk->kstat;
1107 	if (ksp) {
1108 		kmem_free(ksp->ks_data, sizeof (spa_log_sm_stats_t));
1109 		kstat_delete(ksp);
1110 	}
1111 
1112 	mutex_destroy(&shk->lock);
1113 }
1114 
1115 void
spa_stats_init(spa_t * spa)1116 spa_stats_init(spa_t *spa)
1117 {
1118 	spa_read_history_init(spa);
1119 	spa_txg_history_init(spa);
1120 	spa_tx_assign_init(spa);
1121 	spa_mmp_history_init(spa);
1122 	spa_state_init(spa);
1123 	spa_guid_init(spa);
1124 	spa_iostats_init(spa);
1125 	spa_log_sm_stats_init(spa);
1126 }
1127 
1128 void
spa_stats_destroy(spa_t * spa)1129 spa_stats_destroy(spa_t *spa)
1130 {
1131 	spa_log_sm_stats_destroy(spa);
1132 	spa_iostats_destroy(spa);
1133 	spa_health_destroy(spa);
1134 	spa_tx_assign_destroy(spa);
1135 	spa_txg_history_destroy(spa);
1136 	spa_read_history_destroy(spa);
1137 	spa_mmp_history_destroy(spa);
1138 	spa_guid_destroy(spa);
1139 }
1140 
1141 ZFS_MODULE_PARAM(zfs, zfs_, read_history, UINT, ZMOD_RW,
1142 	"Historical statistics for the last N reads");
1143 
1144 ZFS_MODULE_PARAM(zfs, zfs_, read_history_hits, INT, ZMOD_RW,
1145 	"Include cache hits in read history");
1146 
1147 ZFS_MODULE_PARAM(zfs_txg, zfs_txg_, history, UINT, ZMOD_RW,
1148 	"Historical statistics for the last N txgs");
1149 
1150 ZFS_MODULE_PARAM(zfs_multihost, zfs_multihost_, history, UINT, ZMOD_RW,
1151 	"Historical statistics for last N multihost writes");
1152