Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/sys/spa.h
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,7 @@ typedef struct spa_stats {
spa_history_kstat_t state; /* pool state */
spa_history_kstat_t guid; /* pool guid */
spa_history_kstat_t iostats;
spa_history_kstat_t fragmentation[6];
} spa_stats_t;

typedef enum txg_state {
Expand Down
105 changes: 105 additions & 0 deletions module/zfs/spa_stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <sys/vdev_impl.h>
#include <sys/spa.h>
#include <zfs_comutil.h>
#include <sys/metaslab_impl.h>

/*
* Keeps stats on last N reads per spa_t, disabled by default.
Expand Down Expand Up @@ -303,6 +304,108 @@ spa_txg_history_init(spa_t *spa)
offsetof(spa_txg_history_t, sth_node));
}

typedef struct {
spa_t *spa;
int metaclass;
} freespace_histogram_stat_t;

static void *
spa_frag_addr(kstat_t *ksp, loff_t n)
{
if (n == 0) {
freespace_histogram_stat_t *stat = ksp->ks_private;
switch (stat->metaclass) {
case 0:
return (spa_normal_class(stat->spa));
case 1:
return (spa_log_class(stat->spa));
case 2:
return (spa_embedded_log_class(stat->spa));
case 3:
return (spa_special_class(stat->spa));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A 6th metaslab class called spa_special_embedded_log_class was added by be1e991. Please add this one in too.

case 4:
return (spa_dedup_class(stat->spa));
case 5:
return (spa_special_embedded_log_class(stat->spa));
}
}
return (NULL);
}

static int
spa_frag_data(char *buf, size_t size, void *data)
{
metaslab_class_t *mc = data;
size_t offset = 0;
// without this, it becomes a data-leak, and un-initialized strings
// can be returned if all buckets are 0
buf[0] = 0;

mutex_enter(&mc->mc_lock);
for (int i = 0; i < ZFS_RANGE_TREE_HISTOGRAM_SIZE; i++) {
if (mc->mc_histogram[i] > 0) {
snprintf(buf + offset, size - offset, "%d %llu\n", i,
(u_longlong_t)mc->mc_histogram[i]);
offset = strlen(buf);
}
}
mutex_exit(&mc->mc_lock);

return (0);
}

static void
spa_fragmentation_init(spa_t *spa)
{
char *dirname;
const char *statnames[] = {
"fragmentation_normal",
"fragmentation_log",
"fragmentation_embedded_log",
"fragmentation_special",
"fragmentation_dedup",
"fragmentation_special_embedded_log"
};
for (int i = 0; i < 6; i++) {
spa_history_kstat_t *shk = &spa->spa_stats.fragmentation[i];
kstat_t *ksp;

dirname = kmem_asprintf("zfs/%s", spa_name(spa));
ksp = kstat_create(dirname, 0, statnames[i], "misc",
KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL);
kmem_strfree(dirname);

shk->kstat = ksp;
if (ksp) {
freespace_histogram_stat_t *stat = kmem_zalloc(
sizeof (freespace_histogram_stat_t), KM_SLEEP);
stat->spa = spa;
stat->metaclass = i;
ksp->ks_private = stat;
ksp->ks_flags |= KSTAT_FLAG_NO_HEADERS;
kstat_set_raw_ops(ksp, NULL, spa_frag_data,
spa_frag_addr);
kstat_install(ksp);
}
}
}

static void
spa_fragmentation_destroy(spa_t *spa)
{
for (int i = 0; i < 6; i++) {
spa_history_kstat_t *shk = &spa->spa_stats.fragmentation[i];
kstat_t *ksp = shk->kstat;
if (ksp) {
if (ksp->ks_private) {
kmem_free(ksp->ks_private,
sizeof (freespace_histogram_stat_t));
}
kstat_delete(ksp);
}
}
}

static void
spa_txg_history_destroy(spa_t *spa)
{
Expand Down Expand Up @@ -1047,11 +1150,13 @@ spa_stats_init(spa_t *spa)
spa_state_init(spa);
spa_guid_init(spa);
spa_iostats_init(spa);
spa_fragmentation_init(spa);
}

void
spa_stats_destroy(spa_t *spa)
{
spa_fragmentation_destroy(spa);
spa_iostats_destroy(spa);
spa_health_destroy(spa);
spa_tx_assign_destroy(spa);
Expand Down
Loading