]>
Commit | Line | Data |
---|---|---|
1421c891 PS |
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 | #include <sys/zfs_context.h> | |
23 | #include <sys/spa_impl.h> | |
24 | ||
25 | /* | |
26 | * Keeps stats on last N reads per spa_t, disabled by default. | |
27 | */ | |
28 | int zfs_read_history = 0; | |
29 | ||
30 | /* | |
31 | * Include cache hits in history, disabled by default. | |
32 | */ | |
33 | int zfs_read_history_hits = 0; | |
34 | ||
0b1401ee BB |
35 | /* |
36 | * Keeps stats on the last N txgs, disabled by default. | |
37 | */ | |
38 | int zfs_txg_history = 0; | |
39 | ||
1421c891 PS |
40 | /* |
41 | * ========================================================================== | |
42 | * SPA Read History Routines | |
43 | * ========================================================================== | |
44 | */ | |
45 | ||
46 | /* | |
47 | * Read statistics - Information exported regarding each arc_read call | |
48 | */ | |
49 | typedef struct spa_read_history { | |
50 | uint64_t uid; /* unique identifier */ | |
51 | hrtime_t start; /* time read completed */ | |
52 | uint64_t objset; /* read from this objset */ | |
53 | uint64_t object; /* read of this object number */ | |
54 | uint64_t level; /* block's indirection level */ | |
55 | uint64_t blkid; /* read of this block id */ | |
56 | char origin[24]; /* read originated from here */ | |
57 | uint32_t aflags; /* ARC flags (cached, prefetch, etc.) */ | |
58 | pid_t pid; /* PID of task doing read */ | |
59 | char comm[16]; /* process name of task doing read */ | |
60 | list_node_t srh_link; | |
61 | } spa_read_history_t; | |
62 | ||
63 | static int | |
64 | spa_read_history_headers(char *buf, size_t size) | |
65 | { | |
66 | size = snprintf(buf, size - 1, "%-8s %-16s %-8s %-8s %-8s %-8s %-8s " | |
67 | "%-24s %-8s %-16s\n", "UID", "start", "objset", "object", | |
68 | "level", "blkid", "aflags", "origin", "pid", "process"); | |
69 | buf[size] = '\0'; | |
70 | ||
71 | return (0); | |
72 | } | |
73 | ||
74 | static int | |
75 | spa_read_history_data(char *buf, size_t size, void *data) | |
76 | { | |
77 | spa_read_history_t *srh = (spa_read_history_t *)data; | |
78 | ||
79 | size = snprintf(buf, size - 1, "%-8llu %-16llu 0x%-6llx " | |
80 | "%-8lli %-8lli %-8lli 0x%-6x %-24s %-8i %-16s\n", | |
81 | (u_longlong_t)srh->uid, srh->start, | |
82 | (longlong_t)srh->objset, (longlong_t)srh->object, | |
83 | (longlong_t)srh->level, (longlong_t)srh->blkid, | |
84 | srh->aflags, srh->origin, srh->pid, srh->comm); | |
85 | buf[size] = '\0'; | |
86 | ||
87 | return (0); | |
88 | } | |
89 | ||
90 | /* | |
91 | * Calculate the address for the next spa_stats_history_t entry. The | |
92 | * ssh->lock will be held until ksp->ks_ndata entries are processed. | |
93 | */ | |
94 | static void * | |
95 | spa_read_history_addr(kstat_t *ksp, loff_t n) | |
96 | { | |
97 | spa_t *spa = ksp->ks_private; | |
98 | spa_stats_history_t *ssh = &spa->spa_stats.read_history; | |
99 | ||
100 | ASSERT(MUTEX_HELD(&ssh->lock)); | |
101 | ||
102 | if (n == 0) | |
103 | ssh->private = list_tail(&ssh->list); | |
104 | else if (ssh->private) | |
105 | ssh->private = list_prev(&ssh->list, ssh->private); | |
106 | ||
107 | return (ssh->private); | |
108 | } | |
109 | ||
110 | /* | |
111 | * When the kstat is written discard all spa_read_history_t entires. The | |
112 | * ssh->lock will be held until ksp->ks_ndata entries are processed. | |
113 | */ | |
114 | static int | |
115 | spa_read_history_update(kstat_t *ksp, int rw) | |
116 | { | |
117 | spa_t *spa = ksp->ks_private; | |
118 | spa_stats_history_t *ssh = &spa->spa_stats.read_history; | |
119 | ||
120 | if (rw == KSTAT_WRITE) { | |
121 | spa_read_history_t *srh; | |
122 | ||
123 | while ((srh = list_remove_head(&ssh->list))) { | |
124 | ssh->size--; | |
d1d7e268 | 125 | kmem_free(srh, sizeof (spa_read_history_t)); |
1421c891 PS |
126 | } |
127 | ||
128 | ASSERT3U(ssh->size, ==, 0); | |
129 | } | |
130 | ||
131 | ksp->ks_ndata = ssh->size; | |
d1d7e268 | 132 | ksp->ks_data_size = ssh->size * sizeof (spa_read_history_t); |
1421c891 PS |
133 | |
134 | return (0); | |
135 | } | |
136 | ||
137 | static void | |
138 | spa_read_history_init(spa_t *spa) | |
139 | { | |
140 | spa_stats_history_t *ssh = &spa->spa_stats.read_history; | |
141 | char name[KSTAT_STRLEN]; | |
142 | kstat_t *ksp; | |
143 | ||
144 | mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL); | |
145 | list_create(&ssh->list, sizeof (spa_read_history_t), | |
146 | offsetof(spa_read_history_t, srh_link)); | |
147 | ||
148 | ssh->count = 0; | |
149 | ssh->size = 0; | |
150 | ssh->private = NULL; | |
151 | ||
152 | (void) snprintf(name, KSTAT_STRLEN, "zfs/%s", spa_name(spa)); | |
153 | name[KSTAT_STRLEN-1] = '\0'; | |
154 | ||
155 | ksp = kstat_create(name, 0, "reads", "misc", | |
156 | KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); | |
157 | ssh->kstat = ksp; | |
158 | ||
159 | if (ksp) { | |
160 | ksp->ks_lock = &ssh->lock; | |
161 | ksp->ks_data = NULL; | |
162 | ksp->ks_private = spa; | |
163 | ksp->ks_update = spa_read_history_update; | |
164 | kstat_set_raw_ops(ksp, spa_read_history_headers, | |
165 | spa_read_history_data, spa_read_history_addr); | |
166 | kstat_install(ksp); | |
167 | } | |
168 | } | |
169 | ||
170 | static void | |
171 | spa_read_history_destroy(spa_t *spa) | |
172 | { | |
173 | spa_stats_history_t *ssh = &spa->spa_stats.read_history; | |
174 | spa_read_history_t *srh; | |
175 | kstat_t *ksp; | |
176 | ||
177 | ksp = ssh->kstat; | |
178 | if (ksp) | |
179 | kstat_delete(ksp); | |
180 | ||
181 | mutex_enter(&ssh->lock); | |
182 | while ((srh = list_remove_head(&ssh->list))) { | |
183 | ssh->size--; | |
d1d7e268 | 184 | kmem_free(srh, sizeof (spa_read_history_t)); |
1421c891 PS |
185 | } |
186 | ||
187 | ASSERT3U(ssh->size, ==, 0); | |
188 | list_destroy(&ssh->list); | |
189 | mutex_exit(&ssh->lock); | |
190 | ||
191 | mutex_destroy(&ssh->lock); | |
192 | } | |
193 | ||
194 | void | |
195 | spa_read_history_add(spa_t *spa, const zbookmark_t *zb, uint32_t aflags) | |
196 | { | |
197 | spa_stats_history_t *ssh = &spa->spa_stats.read_history; | |
198 | spa_read_history_t *srh, *rm; | |
199 | ||
200 | ASSERT3P(spa, !=, NULL); | |
201 | ASSERT3P(zb, !=, NULL); | |
202 | ||
203 | if (zfs_read_history == 0 && ssh->size == 0) | |
204 | return; | |
205 | ||
206 | if (zfs_read_history_hits == 0 && (aflags & ARC_CACHED)) | |
207 | return; | |
208 | ||
d1d7e268 MK |
209 | srh = kmem_zalloc(sizeof (spa_read_history_t), KM_PUSHPAGE); |
210 | strlcpy(srh->origin, zb->zb_func, sizeof (srh->origin)); | |
211 | strlcpy(srh->comm, getcomm(), sizeof (srh->comm)); | |
1421c891 PS |
212 | srh->start = gethrtime(); |
213 | srh->objset = zb->zb_objset; | |
214 | srh->object = zb->zb_object; | |
215 | srh->level = zb->zb_level; | |
216 | srh->blkid = zb->zb_blkid; | |
217 | srh->aflags = aflags; | |
218 | srh->pid = getpid(); | |
219 | ||
220 | mutex_enter(&ssh->lock); | |
221 | ||
222 | srh->uid = ssh->count++; | |
223 | list_insert_head(&ssh->list, srh); | |
224 | ssh->size++; | |
225 | ||
226 | while (ssh->size > zfs_read_history) { | |
227 | ssh->size--; | |
228 | rm = list_remove_tail(&ssh->list); | |
d1d7e268 | 229 | kmem_free(rm, sizeof (spa_read_history_t)); |
1421c891 PS |
230 | } |
231 | ||
232 | mutex_exit(&ssh->lock); | |
233 | } | |
234 | ||
0b1401ee BB |
235 | /* |
236 | * ========================================================================== | |
237 | * SPA TXG History Routines | |
238 | * ========================================================================== | |
239 | */ | |
240 | ||
241 | /* | |
242 | * Txg statistics - Information exported regarding each txg sync | |
243 | */ | |
244 | ||
245 | typedef struct spa_txg_history { | |
246 | uint64_t txg; /* txg id */ | |
247 | txg_state_t state; /* active txg state */ | |
248 | uint64_t nread; /* number of bytes read */ | |
249 | uint64_t nwritten; /* number of bytes written */ | |
250 | uint64_t reads; /* number of read operations */ | |
251 | uint64_t writes; /* number of write operations */ | |
252 | uint64_t nreserved; /* number of bytes reserved */ | |
253 | hrtime_t times[TXG_STATE_COMMITTED]; /* completion times */ | |
254 | list_node_t sth_link; | |
255 | } spa_txg_history_t; | |
256 | ||
257 | static int | |
258 | spa_txg_history_headers(char *buf, size_t size) | |
259 | { | |
260 | size = snprintf(buf, size - 1, "%-8s %-16s %-5s %-12s %-12s %-12s " | |
261 | "%-8s %-8s %-12s %-12s %-12s\n", "txg", "birth", "state", | |
262 | "nreserved", "nread", "nwritten", "reads", "writes", | |
263 | "otime", "qtime", "stime"); | |
264 | buf[size] = '\0'; | |
265 | ||
266 | return (0); | |
267 | } | |
268 | ||
269 | static int | |
270 | spa_txg_history_data(char *buf, size_t size, void *data) | |
271 | { | |
272 | spa_txg_history_t *sth = (spa_txg_history_t *)data; | |
273 | uint64_t open = 0, quiesce = 0, sync = 0; | |
274 | char state; | |
275 | ||
276 | switch (sth->state) { | |
277 | case TXG_STATE_BIRTH: state = 'B'; break; | |
278 | case TXG_STATE_OPEN: state = 'O'; break; | |
279 | case TXG_STATE_QUIESCED: state = 'Q'; break; | |
280 | case TXG_STATE_SYNCED: state = 'S'; break; | |
281 | case TXG_STATE_COMMITTED: state = 'C'; break; | |
282 | default: state = '?'; break; | |
283 | } | |
284 | ||
285 | if (sth->times[TXG_STATE_OPEN]) | |
286 | open = sth->times[TXG_STATE_OPEN] - | |
287 | sth->times[TXG_STATE_BIRTH]; | |
288 | ||
289 | if (sth->times[TXG_STATE_QUIESCED]) | |
290 | quiesce = sth->times[TXG_STATE_QUIESCED] - | |
291 | sth->times[TXG_STATE_OPEN]; | |
292 | ||
293 | if (sth->times[TXG_STATE_SYNCED]) | |
294 | sync = sth->times[TXG_STATE_SYNCED] - | |
295 | sth->times[TXG_STATE_QUIESCED]; | |
296 | ||
297 | size = snprintf(buf, size - 1, "%-8llu %-16llu %-5c %-12llu " | |
298 | "%-12llu %-12llu %-8llu %-8llu %-12llu %-12llu %-12llu\n", | |
299 | (longlong_t)sth->txg, sth->times[TXG_STATE_BIRTH], state, | |
300 | (u_longlong_t)sth->nreserved, | |
301 | (u_longlong_t)sth->nread, (u_longlong_t)sth->nwritten, | |
302 | (u_longlong_t)sth->reads, (u_longlong_t)sth->writes, | |
303 | (u_longlong_t)open, (u_longlong_t)quiesce, (u_longlong_t)sync); | |
304 | buf[size] = '\0'; | |
305 | ||
306 | return (0); | |
307 | } | |
308 | ||
309 | /* | |
310 | * Calculate the address for the next spa_stats_history_t entry. The | |
311 | * ssh->lock will be held until ksp->ks_ndata entries are processed. | |
312 | */ | |
313 | static void * | |
314 | spa_txg_history_addr(kstat_t *ksp, loff_t n) | |
315 | { | |
316 | spa_t *spa = ksp->ks_private; | |
317 | spa_stats_history_t *ssh = &spa->spa_stats.txg_history; | |
318 | ||
319 | ASSERT(MUTEX_HELD(&ssh->lock)); | |
320 | ||
321 | if (n == 0) | |
322 | ssh->private = list_tail(&ssh->list); | |
323 | else if (ssh->private) | |
324 | ssh->private = list_prev(&ssh->list, ssh->private); | |
325 | ||
326 | return (ssh->private); | |
327 | } | |
328 | ||
329 | /* | |
330 | * When the kstat is written discard all spa_txg_history_t entires. The | |
331 | * ssh->lock will be held until ksp->ks_ndata entries are processed. | |
332 | */ | |
333 | static int | |
334 | spa_txg_history_update(kstat_t *ksp, int rw) | |
335 | { | |
336 | spa_t *spa = ksp->ks_private; | |
337 | spa_stats_history_t *ssh = &spa->spa_stats.txg_history; | |
338 | ||
339 | ASSERT(MUTEX_HELD(&ssh->lock)); | |
340 | ||
341 | if (rw == KSTAT_WRITE) { | |
342 | spa_txg_history_t *sth; | |
343 | ||
344 | while ((sth = list_remove_head(&ssh->list))) { | |
345 | ssh->size--; | |
d1d7e268 | 346 | kmem_free(sth, sizeof (spa_txg_history_t)); |
0b1401ee BB |
347 | } |
348 | ||
349 | ASSERT3U(ssh->size, ==, 0); | |
350 | } | |
351 | ||
352 | ksp->ks_ndata = ssh->size; | |
d1d7e268 | 353 | ksp->ks_data_size = ssh->size * sizeof (spa_txg_history_t); |
0b1401ee BB |
354 | |
355 | return (0); | |
356 | } | |
357 | ||
358 | static void | |
359 | spa_txg_history_init(spa_t *spa) | |
360 | { | |
361 | spa_stats_history_t *ssh = &spa->spa_stats.txg_history; | |
362 | char name[KSTAT_STRLEN]; | |
363 | kstat_t *ksp; | |
364 | ||
365 | mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL); | |
366 | list_create(&ssh->list, sizeof (spa_txg_history_t), | |
367 | offsetof(spa_txg_history_t, sth_link)); | |
368 | ||
369 | ssh->count = 0; | |
370 | ssh->size = 0; | |
371 | ssh->private = NULL; | |
372 | ||
373 | (void) snprintf(name, KSTAT_STRLEN, "zfs/%s", spa_name(spa)); | |
374 | name[KSTAT_STRLEN-1] = '\0'; | |
375 | ||
376 | ksp = kstat_create(name, 0, "txgs", "misc", | |
377 | KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); | |
378 | ssh->kstat = ksp; | |
379 | ||
380 | if (ksp) { | |
381 | ksp->ks_lock = &ssh->lock; | |
382 | ksp->ks_data = NULL; | |
383 | ksp->ks_private = spa; | |
384 | ksp->ks_update = spa_txg_history_update; | |
385 | kstat_set_raw_ops(ksp, spa_txg_history_headers, | |
386 | spa_txg_history_data, spa_txg_history_addr); | |
387 | kstat_install(ksp); | |
388 | } | |
389 | } | |
390 | ||
391 | static void | |
392 | spa_txg_history_destroy(spa_t *spa) | |
393 | { | |
394 | spa_stats_history_t *ssh = &spa->spa_stats.txg_history; | |
395 | spa_txg_history_t *sth; | |
396 | kstat_t *ksp; | |
397 | ||
398 | ksp = ssh->kstat; | |
399 | if (ksp) | |
400 | kstat_delete(ksp); | |
401 | ||
402 | mutex_enter(&ssh->lock); | |
403 | while ((sth = list_remove_head(&ssh->list))) { | |
404 | ssh->size--; | |
d1d7e268 | 405 | kmem_free(sth, sizeof (spa_txg_history_t)); |
0b1401ee BB |
406 | } |
407 | ||
408 | ASSERT3U(ssh->size, ==, 0); | |
409 | list_destroy(&ssh->list); | |
410 | mutex_exit(&ssh->lock); | |
411 | ||
412 | mutex_destroy(&ssh->lock); | |
413 | } | |
414 | ||
415 | /* | |
416 | * Add a new txg to historical record. | |
417 | */ | |
418 | void | |
419 | spa_txg_history_add(spa_t *spa, uint64_t txg) | |
420 | { | |
421 | spa_stats_history_t *ssh = &spa->spa_stats.txg_history; | |
422 | spa_txg_history_t *sth, *rm; | |
423 | ||
424 | if (zfs_txg_history == 0 && ssh->size == 0) | |
425 | return; | |
426 | ||
d1d7e268 | 427 | sth = kmem_zalloc(sizeof (spa_txg_history_t), KM_PUSHPAGE); |
0b1401ee BB |
428 | sth->txg = txg; |
429 | sth->state = TXG_STATE_OPEN; | |
430 | sth->times[TXG_STATE_BIRTH] = gethrtime(); | |
431 | ||
432 | mutex_enter(&ssh->lock); | |
433 | ||
434 | list_insert_head(&ssh->list, sth); | |
435 | ssh->size++; | |
436 | ||
437 | while (ssh->size > zfs_txg_history) { | |
438 | ssh->size--; | |
439 | rm = list_remove_tail(&ssh->list); | |
d1d7e268 | 440 | kmem_free(rm, sizeof (spa_txg_history_t)); |
0b1401ee BB |
441 | } |
442 | ||
443 | mutex_exit(&ssh->lock); | |
444 | } | |
445 | ||
446 | /* | |
447 | * Set txg state completion time and increment current state. | |
448 | */ | |
449 | int | |
450 | spa_txg_history_set(spa_t *spa, uint64_t txg, txg_state_t completed_state, | |
451 | hrtime_t completed_time) | |
452 | { | |
453 | spa_stats_history_t *ssh = &spa->spa_stats.txg_history; | |
454 | spa_txg_history_t *sth; | |
455 | int error = ENOENT; | |
456 | ||
457 | if (zfs_txg_history == 0) | |
458 | return (0); | |
459 | ||
460 | mutex_enter(&ssh->lock); | |
461 | for (sth = list_head(&ssh->list); sth != NULL; | |
d1d7e268 | 462 | sth = list_next(&ssh->list, sth)) { |
0b1401ee BB |
463 | if (sth->txg == txg) { |
464 | sth->times[completed_state] = completed_time; | |
465 | sth->state++; | |
466 | error = 0; | |
467 | break; | |
468 | } | |
469 | } | |
470 | mutex_exit(&ssh->lock); | |
471 | ||
472 | return (error); | |
473 | } | |
474 | ||
475 | /* | |
476 | * Set txg IO stats. | |
477 | */ | |
478 | int | |
479 | spa_txg_history_set_io(spa_t *spa, uint64_t txg, uint64_t nread, | |
480 | uint64_t nwritten, uint64_t reads, uint64_t writes, uint64_t nreserved) | |
481 | { | |
482 | spa_stats_history_t *ssh = &spa->spa_stats.txg_history; | |
483 | spa_txg_history_t *sth; | |
484 | int error = ENOENT; | |
485 | ||
486 | if (zfs_txg_history == 0) | |
487 | return (0); | |
488 | ||
489 | mutex_enter(&ssh->lock); | |
490 | for (sth = list_head(&ssh->list); sth != NULL; | |
d1d7e268 | 491 | sth = list_next(&ssh->list, sth)) { |
0b1401ee BB |
492 | if (sth->txg == txg) { |
493 | sth->nread = nread; | |
494 | sth->nwritten = nwritten; | |
495 | sth->reads = reads; | |
496 | sth->writes = writes; | |
497 | sth->nreserved = nreserved; | |
498 | error = 0; | |
499 | break; | |
500 | } | |
501 | } | |
502 | mutex_exit(&ssh->lock); | |
503 | ||
504 | return (error); | |
505 | } | |
506 | ||
2d37239a BB |
507 | /* |
508 | * ========================================================================== | |
509 | * SPA TX Assign Histogram Routines | |
510 | * ========================================================================== | |
511 | */ | |
512 | ||
513 | /* | |
514 | * Tx statistics - Information exported regarding dmu_tx_assign time. | |
515 | */ | |
516 | ||
517 | /* | |
518 | * When the kstat is written zero all buckets. When the kstat is read | |
519 | * count the number of trailing buckets set to zero and update ks_ndata | |
520 | * such that they are not output. | |
521 | */ | |
522 | static int | |
523 | spa_tx_assign_update(kstat_t *ksp, int rw) | |
524 | { | |
525 | spa_t *spa = ksp->ks_private; | |
526 | spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram; | |
527 | int i; | |
528 | ||
529 | if (rw == KSTAT_WRITE) { | |
530 | for (i = 0; i < ssh->count; i++) | |
531 | ((kstat_named_t *)ssh->private)[i].value.ui64 = 0; | |
532 | } | |
533 | ||
534 | for (i = ssh->count; i > 0; i--) | |
535 | if (((kstat_named_t *)ssh->private)[i-1].value.ui64 != 0) | |
536 | break; | |
537 | ||
538 | ksp->ks_ndata = i; | |
d1d7e268 | 539 | ksp->ks_data_size = i * sizeof (kstat_named_t); |
2d37239a BB |
540 | |
541 | return (0); | |
542 | } | |
543 | ||
544 | static void | |
545 | spa_tx_assign_init(spa_t *spa) | |
546 | { | |
547 | spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram; | |
548 | char name[KSTAT_STRLEN]; | |
549 | kstat_named_t *ks; | |
550 | kstat_t *ksp; | |
551 | int i; | |
552 | ||
553 | mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL); | |
554 | ||
555 | ssh->count = 42; /* power of two buckets for 1ns to 2,199s */ | |
d1d7e268 | 556 | ssh->size = ssh->count * sizeof (kstat_named_t); |
2d37239a BB |
557 | ssh->private = kmem_alloc(ssh->size, KM_SLEEP); |
558 | ||
559 | (void) snprintf(name, KSTAT_STRLEN, "zfs/%s", spa_name(spa)); | |
560 | name[KSTAT_STRLEN-1] = '\0'; | |
561 | ||
562 | for (i = 0; i < ssh->count; i++) { | |
563 | ks = &((kstat_named_t *)ssh->private)[i]; | |
564 | ks->data_type = KSTAT_DATA_UINT64; | |
565 | ks->value.ui64 = 0; | |
566 | (void) snprintf(ks->name, KSTAT_STRLEN, "%llu ns", | |
567 | (u_longlong_t)1 << i); | |
568 | } | |
569 | ||
570 | ksp = kstat_create(name, 0, "dmu_tx_assign", "misc", | |
571 | KSTAT_TYPE_NAMED, 0, KSTAT_FLAG_VIRTUAL); | |
572 | ssh->kstat = ksp; | |
573 | ||
574 | if (ksp) { | |
575 | ksp->ks_lock = &ssh->lock; | |
576 | ksp->ks_data = ssh->private; | |
577 | ksp->ks_ndata = ssh->count; | |
578 | ksp->ks_data_size = ssh->size; | |
579 | ksp->ks_private = spa; | |
580 | ksp->ks_update = spa_tx_assign_update; | |
581 | kstat_install(ksp); | |
582 | } | |
583 | } | |
584 | ||
585 | static void | |
586 | spa_tx_assign_destroy(spa_t *spa) | |
587 | { | |
588 | spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram; | |
589 | kstat_t *ksp; | |
590 | ||
591 | ksp = ssh->kstat; | |
592 | if (ksp) | |
593 | kstat_delete(ksp); | |
594 | ||
595 | kmem_free(ssh->private, ssh->size); | |
596 | mutex_destroy(&ssh->lock); | |
597 | } | |
598 | ||
599 | void | |
600 | spa_tx_assign_add_nsecs(spa_t *spa, uint64_t nsecs) | |
601 | { | |
602 | spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram; | |
603 | uint64_t idx = 0; | |
604 | ||
605 | while (((1 << idx) < nsecs) && (idx < ssh->size - 1)) | |
606 | idx++; | |
607 | ||
608 | atomic_inc_64(&((kstat_named_t *)ssh->private)[idx].value.ui64); | |
609 | } | |
610 | ||
330847ff MA |
611 | /* |
612 | * ========================================================================== | |
613 | * SPA IO History Routines | |
614 | * ========================================================================== | |
615 | */ | |
616 | static int | |
617 | spa_io_history_update(kstat_t *ksp, int rw) | |
618 | { | |
619 | if (rw == KSTAT_WRITE) | |
620 | memset(ksp->ks_data, 0, ksp->ks_data_size); | |
621 | ||
622 | return (0); | |
623 | } | |
624 | ||
625 | static void | |
626 | spa_io_history_init(spa_t *spa) | |
627 | { | |
628 | spa_stats_history_t *ssh = &spa->spa_stats.io_history; | |
629 | char name[KSTAT_STRLEN]; | |
630 | kstat_t *ksp; | |
631 | ||
632 | mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL); | |
633 | ||
634 | (void) snprintf(name, KSTAT_STRLEN, "zfs/%s", spa_name(spa)); | |
635 | name[KSTAT_STRLEN-1] = '\0'; | |
636 | ||
637 | ksp = kstat_create(name, 0, "io", "disk", KSTAT_TYPE_IO, 1, 0); | |
638 | ssh->kstat = ksp; | |
639 | ||
640 | if (ksp) { | |
641 | ksp->ks_lock = &ssh->lock; | |
642 | ksp->ks_private = spa; | |
643 | ksp->ks_update = spa_io_history_update; | |
644 | kstat_install(ksp); | |
645 | } | |
646 | } | |
647 | ||
648 | static void | |
649 | spa_io_history_destroy(spa_t *spa) | |
650 | { | |
651 | spa_stats_history_t *ssh = &spa->spa_stats.io_history; | |
652 | ||
653 | if (ssh->kstat) | |
654 | kstat_delete(ssh->kstat); | |
655 | ||
656 | mutex_destroy(&ssh->lock); | |
657 | } | |
658 | ||
1421c891 PS |
659 | void |
660 | spa_stats_init(spa_t *spa) | |
661 | { | |
662 | spa_read_history_init(spa); | |
0b1401ee | 663 | spa_txg_history_init(spa); |
2d37239a | 664 | spa_tx_assign_init(spa); |
330847ff | 665 | spa_io_history_init(spa); |
1421c891 PS |
666 | } |
667 | ||
668 | void | |
669 | spa_stats_destroy(spa_t *spa) | |
670 | { | |
2d37239a | 671 | spa_tx_assign_destroy(spa); |
0b1401ee | 672 | spa_txg_history_destroy(spa); |
1421c891 | 673 | spa_read_history_destroy(spa); |
330847ff | 674 | spa_io_history_destroy(spa); |
1421c891 PS |
675 | } |
676 | ||
677 | #if defined(_KERNEL) && defined(HAVE_SPL) | |
678 | module_param(zfs_read_history, int, 0644); | |
679 | MODULE_PARM_DESC(zfs_read_history, "Historic statistics for the last N reads"); | |
680 | ||
681 | module_param(zfs_read_history_hits, int, 0644); | |
682 | MODULE_PARM_DESC(zfs_read_history_hits, "Include cache hits in read history"); | |
0b1401ee BB |
683 | |
684 | module_param(zfs_txg_history, int, 0644); | |
685 | MODULE_PARM_DESC(zfs_txg_history, "Historic statistics for the last N txgs"); | |
1421c891 | 686 | #endif |