diff options
Diffstat (limited to 'module')
-rw-r--r-- | module/spl/Makefile.in | 1 | ||||
-rw-r--r-- | module/spl/spl-kstat.c | 100 | ||||
-rw-r--r-- | module/spl/spl-procfs-list.c | 256 | ||||
-rw-r--r-- | module/zfs/spa_stats.c | 600 | ||||
-rw-r--r-- | module/zfs/vdev_queue.c | 40 | ||||
-rw-r--r-- | module/zfs/zfs_debug.c | 132 |
6 files changed, 607 insertions, 522 deletions
diff --git a/module/spl/Makefile.in b/module/spl/Makefile.in index 97a431f22..3bcbf63cb 100644 --- a/module/spl/Makefile.in +++ b/module/spl/Makefile.in @@ -18,6 +18,7 @@ $(MODULE)-objs += spl-kobj.o $(MODULE)-objs += spl-kstat.o $(MODULE)-objs += spl-mutex.o $(MODULE)-objs += spl-proc.o +$(MODULE)-objs += spl-procfs-list.o $(MODULE)-objs += spl-rwlock.o $(MODULE)-objs += spl-taskq.o $(MODULE)-objs += spl-thread.o diff --git a/module/spl/spl-kstat.c b/module/spl/spl-kstat.c index c3fc2e4b2..8683693c8 100644 --- a/module/spl/spl-kstat.c +++ b/module/spl/spl-kstat.c @@ -530,6 +530,18 @@ __kstat_set_raw_ops(kstat_t *ksp, } EXPORT_SYMBOL(__kstat_set_raw_ops); +void +kstat_proc_entry_init(kstat_proc_entry_t *kpep, const char *module, + const char *name) +{ + kpep->kpe_owner = NULL; + kpep->kpe_proc = NULL; + INIT_LIST_HEAD(&kpep->kpe_list); + strncpy(kpep->kpe_module, module, KSTAT_STRLEN); + strncpy(kpep->kpe_name, name, KSTAT_STRLEN); +} +EXPORT_SYMBOL(kstat_proc_entry_init); + kstat_t * __kstat_create(const char *ks_module, int ks_instance, const char *ks_name, const char *ks_class, uchar_t ks_type, uint_t ks_ndata, @@ -556,13 +568,10 @@ __kstat_create(const char *ks_module, int ks_instance, const char *ks_name, ksp->ks_magic = KS_MAGIC; mutex_init(&ksp->ks_private_lock, NULL, MUTEX_DEFAULT, NULL); ksp->ks_lock = &ksp->ks_private_lock; - INIT_LIST_HEAD(&ksp->ks_list); ksp->ks_crtime = gethrtime(); ksp->ks_snaptime = ksp->ks_crtime; - strncpy(ksp->ks_module, ks_module, KSTAT_STRLEN); ksp->ks_instance = ks_instance; - strncpy(ksp->ks_name, ks_name, KSTAT_STRLEN); strncpy(ksp->ks_class, ks_class, KSTAT_STRLEN); ksp->ks_type = ks_type; ksp->ks_flags = ks_flags; @@ -573,6 +582,7 @@ __kstat_create(const char *ks_module, int ks_instance, const char *ks_name, ksp->ks_raw_ops.addr = NULL; ksp->ks_raw_buf = NULL; ksp->ks_raw_bufsize = 0; + kstat_proc_entry_init(&ksp->ks_proc, ks_module, ks_name); switch (ksp->ks_type) { case KSTAT_TYPE_RAW: @@ -614,14 +624,14 @@ __kstat_create(const char *ks_module, int ks_instance, const char *ks_name, EXPORT_SYMBOL(__kstat_create); static int -kstat_detect_collision(kstat_t *ksp) +kstat_detect_collision(kstat_proc_entry_t *kpep) { kstat_module_t *module; - kstat_t *tmp; + kstat_proc_entry_t *tmp; char *parent; char *cp; - parent = kmem_asprintf("%s", ksp->ks_module); + parent = kmem_asprintf("%s", kpep->kpe_module); if ((cp = strrchr(parent, '/')) == NULL) { strfree(parent); @@ -630,8 +640,8 @@ kstat_detect_collision(kstat_t *ksp) cp[0] = '\0'; if ((module = kstat_find_module(parent)) != NULL) { - list_for_each_entry(tmp, &module->ksm_kstat_list, ks_list) { - if (strncmp(tmp->ks_name, cp+1, KSTAT_STRLEN) == 0) { + list_for_each_entry(tmp, &module->ksm_kstat_list, kpe_list) { + if (strncmp(tmp->kpe_name, cp+1, KSTAT_STRLEN) == 0) { strfree(parent); return (EEXIST); } @@ -642,24 +652,30 @@ kstat_detect_collision(kstat_t *ksp) return (0); } +/* + * Add a file to the proc filesystem under the kstat namespace (i.e. + * /proc/spl/kstat/). The file need not necessarily be implemented as a + * kstat. + */ void -__kstat_install(kstat_t *ksp) +kstat_proc_entry_install(kstat_proc_entry_t *kpep, + const struct file_operations *file_ops, void *data) { kstat_module_t *module; - kstat_t *tmp; + kstat_proc_entry_t *tmp; - ASSERT(ksp); + ASSERT(kpep); mutex_enter(&kstat_module_lock); - module = kstat_find_module(ksp->ks_module); + module = kstat_find_module(kpep->kpe_module); if (module == NULL) { - if (kstat_detect_collision(ksp) != 0) { + if (kstat_detect_collision(kpep) != 0) { cmn_err(CE_WARN, "kstat_create('%s', '%s'): namespace" \ - " collision", ksp->ks_module, ksp->ks_name); + " collision", kpep->kpe_module, kpep->kpe_name); goto out; } - module = kstat_create_module(ksp->ks_module); + module = kstat_create_module(kpep->kpe_module); if (module == NULL) goto out; } @@ -668,44 +684,60 @@ __kstat_install(kstat_t *ksp) * Only one entry by this name per-module, on failure the module * shouldn't be deleted because we know it has at least one entry. */ - list_for_each_entry(tmp, &module->ksm_kstat_list, ks_list) { - if (strncmp(tmp->ks_name, ksp->ks_name, KSTAT_STRLEN) == 0) + list_for_each_entry(tmp, &module->ksm_kstat_list, kpe_list) { + if (strncmp(tmp->kpe_name, kpep->kpe_name, KSTAT_STRLEN) == 0) goto out; } - list_add_tail(&ksp->ks_list, &module->ksm_kstat_list); + list_add_tail(&kpep->kpe_list, &module->ksm_kstat_list); - mutex_enter(ksp->ks_lock); - ksp->ks_owner = module; - ksp->ks_proc = proc_create_data(ksp->ks_name, 0644, - module->ksm_proc, &proc_kstat_operations, (void *)ksp); - if (ksp->ks_proc == NULL) { - list_del_init(&ksp->ks_list); + kpep->kpe_owner = module; + kpep->kpe_proc = proc_create_data(kpep->kpe_name, 0644, + module->ksm_proc, file_ops, data); + if (kpep->kpe_proc == NULL) { + list_del_init(&kpep->kpe_list); if (list_empty(&module->ksm_kstat_list)) kstat_delete_module(module); } - mutex_exit(ksp->ks_lock); out: mutex_exit(&kstat_module_lock); + +} +EXPORT_SYMBOL(kstat_proc_entry_install); + +void +__kstat_install(kstat_t *ksp) +{ + ASSERT(ksp); + kstat_proc_entry_install(&ksp->ks_proc, &proc_kstat_operations, ksp); } EXPORT_SYMBOL(__kstat_install); void -__kstat_delete(kstat_t *ksp) +kstat_proc_entry_delete(kstat_proc_entry_t *kpep) { - kstat_module_t *module = ksp->ks_owner; + kstat_module_t *module = kpep->kpe_owner; + if (kpep->kpe_proc) + remove_proc_entry(kpep->kpe_name, module->ksm_proc); mutex_enter(&kstat_module_lock); - list_del_init(&ksp->ks_list); + list_del_init(&kpep->kpe_list); + + /* + * Remove top level module directory if it wasn't empty before, but now + * is. + */ + if (kpep->kpe_proc && list_empty(&module->ksm_kstat_list)) + kstat_delete_module(module); mutex_exit(&kstat_module_lock); - if (ksp->ks_proc) { - remove_proc_entry(ksp->ks_name, module->ksm_proc); +} +EXPORT_SYMBOL(kstat_proc_entry_delete); - /* Remove top level module directory if it's empty */ - if (list_empty(&module->ksm_kstat_list)) - kstat_delete_module(module); - } +void +__kstat_delete(kstat_t *ksp) +{ + kstat_proc_entry_delete(&ksp->ks_proc); if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL)) kmem_free(ksp->ks_data, ksp->ks_data_size); diff --git a/module/spl/spl-procfs-list.c b/module/spl/spl-procfs-list.c new file mode 100644 index 000000000..4902e0a56 --- /dev/null +++ b/module/spl/spl-procfs-list.c @@ -0,0 +1,256 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2018 by Delphix. All rights reserved. + */ + +#include <sys/list.h> +#include <sys/mutex.h> +#include <sys/procfs_list.h> +#include <linux/proc_fs.h> + +/* + * A procfs_list is a wrapper around a linked list which implements the seq_file + * interface, allowing the contents of the list to be exposed through procfs. + * The kernel already has some utilities to help implement the seq_file + * interface for linked lists (seq_list_*), but they aren't appropriate for use + * with lists that have many entries, because seq_list_start walks the list at + * the start of each read syscall to find where it left off, so reading a file + * ends up being quadratic in the number of entries in the list. + * + * This implementation avoids this penalty by maintaining a separate cursor into + * the list per instance of the file that is open. It also maintains some extra + * information in each node of the list to prevent reads of entries that have + * been dropped from the list. + * + * Callers should only add elements to the list using procfs_list_add, which + * adds an element to the tail of the list. Other operations can be performed + * directly on the wrapped list using the normal list manipulation functions, + * but elements should only be removed from the head of the list. + */ + +#define NODE_ID(procfs_list, obj) \ + (((procfs_list_node_t *)(((char *)obj) + \ + (procfs_list)->pl_node_offset))->pln_id) + +typedef struct procfs_list_cursor { + procfs_list_t *procfs_list; /* List into which this cursor points */ + void *cached_node; /* Most recently accessed node */ + loff_t cached_pos; /* Position of cached_node */ +} procfs_list_cursor_t; + +static int +procfs_list_seq_show(struct seq_file *f, void *p) +{ + procfs_list_cursor_t *cursor = f->private; + procfs_list_t *procfs_list = cursor->procfs_list; + + ASSERT(MUTEX_HELD(&procfs_list->pl_lock)); + if (p == SEQ_START_TOKEN) { + if (procfs_list->pl_show_header != NULL) + return (procfs_list->pl_show_header(f)); + else + return (0); + } + return (procfs_list->pl_show(f, p)); +} + +static void * +procfs_list_next_node(procfs_list_cursor_t *cursor, loff_t *pos) +{ + void *next_node; + procfs_list_t *procfs_list = cursor->procfs_list; + + if (cursor->cached_node == SEQ_START_TOKEN) + next_node = list_head(&procfs_list->pl_list); + else + next_node = list_next(&procfs_list->pl_list, + cursor->cached_node); + + if (next_node != NULL) { + cursor->cached_node = next_node; + cursor->cached_pos = NODE_ID(procfs_list, cursor->cached_node); + *pos = cursor->cached_pos; + } + return (next_node); +} + +static void * +procfs_list_seq_start(struct seq_file *f, loff_t *pos) +{ + procfs_list_cursor_t *cursor = f->private; + procfs_list_t *procfs_list = cursor->procfs_list; + + mutex_enter(&procfs_list->pl_lock); + + if (*pos == 0) { + cursor->cached_node = SEQ_START_TOKEN; + cursor->cached_pos = 0; + return (SEQ_START_TOKEN); + } + + /* + * Check if our cached pointer has become stale, which happens if the + * the message where we left off has been dropped from the list since + * the last read syscall completed. + */ + void *oldest_node = list_head(&procfs_list->pl_list); + if (cursor->cached_node != SEQ_START_TOKEN && (oldest_node == NULL || + NODE_ID(procfs_list, oldest_node) > cursor->cached_pos)) + return (ERR_PTR(-EIO)); + + /* + * If it isn't starting from the beginning of the file, the seq_file + * code will either pick up at the same position it visited last or the + * following one. + */ + if (*pos == cursor->cached_pos) { + return (cursor->cached_node); + } else { + ASSERT3U(*pos, ==, cursor->cached_pos + 1); + return (procfs_list_next_node(cursor, pos)); + } +} + +static void * +procfs_list_seq_next(struct seq_file *f, void *p, loff_t *pos) +{ + procfs_list_cursor_t *cursor = f->private; + ASSERT(MUTEX_HELD(&cursor->procfs_list->pl_lock)); + return (procfs_list_next_node(cursor, pos)); +} + +static void +procfs_list_seq_stop(struct seq_file *f, void *p) +{ + procfs_list_cursor_t *cursor = f->private; + procfs_list_t *procfs_list = cursor->procfs_list; + mutex_exit(&procfs_list->pl_lock); +} + +static struct seq_operations procfs_list_seq_ops = { + .show = procfs_list_seq_show, + .start = procfs_list_seq_start, + .next = procfs_list_seq_next, + .stop = procfs_list_seq_stop, +}; + +static int +procfs_list_open(struct inode *inode, struct file *filp) +{ + int rc = seq_open_private(filp, &procfs_list_seq_ops, + sizeof (procfs_list_cursor_t)); + if (rc != 0) + return (rc); + + struct seq_file *f = filp->private_data; + procfs_list_cursor_t *cursor = f->private; + cursor->procfs_list = PDE_DATA(inode); + cursor->cached_node = NULL; + cursor->cached_pos = 0; + + return (0); +} + +static ssize_t +procfs_list_write(struct file *filp, const char __user *buf, size_t len, + loff_t *ppos) +{ + struct seq_file *f = filp->private_data; + procfs_list_cursor_t *cursor = f->private; + procfs_list_t *procfs_list = cursor->procfs_list; + int rc; + + if (procfs_list->pl_clear != NULL && + (rc = procfs_list->pl_clear(procfs_list)) != 0) + return (-rc); + return (len); +} + +static struct file_operations procfs_list_operations = { + .owner = THIS_MODULE, + .open = procfs_list_open, + .write = procfs_list_write, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + +/* + * Initialize a procfs_list and create a file for it in the proc filesystem + * under the kstat namespace. + */ +void +procfs_list_install(const char *module, + const char *name, + procfs_list_t *procfs_list, + int (*show)(struct seq_file *f, void *p), + int (*show_header)(struct seq_file *f), + int (*clear)(procfs_list_t *procfs_list), + size_t procfs_list_node_off) +{ + mutex_init(&procfs_list->pl_lock, NULL, MUTEX_DEFAULT, NULL); + list_create(&procfs_list->pl_list, + procfs_list_node_off + sizeof (procfs_list_node_t), + procfs_list_node_off + offsetof(procfs_list_node_t, pln_link)); + procfs_list->pl_next_id = 1; /* Save id 0 for SEQ_START_TOKEN */ + procfs_list->pl_show = show; + procfs_list->pl_show_header = show_header; + procfs_list->pl_clear = clear; + procfs_list->pl_node_offset = procfs_list_node_off; + + kstat_proc_entry_init(&procfs_list->pl_kstat_entry, module, name); + kstat_proc_entry_install(&procfs_list->pl_kstat_entry, + &procfs_list_operations, procfs_list); +} +EXPORT_SYMBOL(procfs_list_install); + +/* Remove the proc filesystem file corresponding to the given list */ +void +procfs_list_uninstall(procfs_list_t *procfs_list) +{ + kstat_proc_entry_delete(&procfs_list->pl_kstat_entry); +} +EXPORT_SYMBOL(procfs_list_uninstall); + +void +procfs_list_destroy(procfs_list_t *procfs_list) +{ + ASSERT(list_is_empty(&procfs_list->pl_list)); + list_destroy(&procfs_list->pl_list); + mutex_destroy(&procfs_list->pl_lock); +} +EXPORT_SYMBOL(procfs_list_destroy); + +/* + * Add a new node to the tail of the list. While the standard list manipulation + * functions can be use for all other operation, adding elements to the list + * should only be done using this helper so that the id of the new node is set + * correctly. + */ +void +procfs_list_add(procfs_list_t *procfs_list, void *p) +{ + ASSERT(MUTEX_HELD(&procfs_list->pl_lock)); + NODE_ID(procfs_list, p) = procfs_list->pl_next_id++; + list_insert_tail(&procfs_list->pl_list, p); +} +EXPORT_SYMBOL(procfs_list_add); diff --git a/module/zfs/spa_stats.c b/module/zfs/spa_stats.c index fa1cf9e98..c02ef86b5 100644 --- a/module/zfs/spa_stats.c +++ b/module/zfs/spa_stats.c @@ -55,7 +55,6 @@ int zfs_multihost_history = 0; * Read statistics - Information exported regarding each arc_read call */ typedef struct spa_read_history { - uint64_t uid; /* unique identifier */ hrtime_t start; /* time read completed */ uint64_t objset; /* read from this objset */ uint64_t object; /* read of this object number */ @@ -65,13 +64,13 @@ typedef struct spa_read_history { uint32_t aflags; /* ARC flags (cached, prefetch, etc.) */ pid_t pid; /* PID of task doing read */ char comm[16]; /* process name of task doing read */ - list_node_t srh_link; + procfs_list_node_t srh_node; } spa_read_history_t; static int -spa_read_history_headers(char *buf, size_t size) +spa_read_history_show_header(struct seq_file *f) { - (void) snprintf(buf, size, "%-8s %-16s %-8s %-8s %-8s %-8s %-8s " + seq_printf(f, "%-8s %-16s %-8s %-8s %-8s %-8s %-8s " "%-24s %-8s %-16s\n", "UID", "start", "objset", "object", "level", "blkid", "aflags", "origin", "pid", "process"); @@ -79,13 +78,13 @@ spa_read_history_headers(char *buf, size_t size) } static int -spa_read_history_data(char *buf, size_t size, void *data) +spa_read_history_show(struct seq_file *f, void *data) { spa_read_history_t *srh = (spa_read_history_t *)data; - (void) snprintf(buf, size, "%-8llu %-16llu 0x%-6llx " + seq_printf(f, "%-8llu %-16llu 0x%-6llx " "%-8lli %-8lli %-8lli 0x%-6x %-24s %-8i %-16s\n", - (u_longlong_t)srh->uid, srh->start, + (u_longlong_t)srh->srh_node.pln_id, srh->start, (longlong_t)srh->objset, (longlong_t)srh->object, (longlong_t)srh->level, (longlong_t)srh->blkid, srh->aflags, srh->origin, srh->pid, srh->comm); @@ -93,120 +92,73 @@ spa_read_history_data(char *buf, size_t size, void *data) return (0); } -/* - * Calculate the address for the next spa_stats_history_t entry. The - * ssh->lock will be held until ksp->ks_ndata entries are processed. - */ -static void * -spa_read_history_addr(kstat_t *ksp, loff_t n) +/* Remove oldest elements from list until there are no more than 'size' left */ +static void +spa_read_history_truncate(spa_history_list_t *shl, unsigned int size) { - spa_t *spa = ksp->ks_private; - spa_stats_history_t *ssh = &spa->spa_stats.read_history; - - ASSERT(MUTEX_HELD(&ssh->lock)); - - if (n == 0) - ssh->private = list_tail(&ssh->list); - else if (ssh->private) - ssh->private = list_prev(&ssh->list, ssh->private); + spa_read_history_t *srh; + while (shl->size > size) { + srh = list_remove_head(&shl->procfs_list.pl_list); + ASSERT3P(srh, !=, NULL); + kmem_free(srh, sizeof (spa_read_history_t)); + shl->size--; + } - return (ssh->private); + if (size == 0) + ASSERT(list_is_empty(&shl->procfs_list.pl_list)); } -/* - * When the kstat is written discard all spa_read_history_t entries. The - * ssh->lock will be held until ksp->ks_ndata entries are processed. - */ static int -spa_read_history_update(kstat_t *ksp, int rw) +spa_read_history_clear(procfs_list_t *procfs_list) { - spa_t *spa = ksp->ks_private; - spa_stats_history_t *ssh = &spa->spa_stats.read_history; - - if (rw == KSTAT_WRITE) { - spa_read_history_t *srh; - - while ((srh = list_remove_head(&ssh->list))) { - ssh->size--; - kmem_free(srh, sizeof (spa_read_history_t)); - } - - ASSERT3U(ssh->size, ==, 0); - } - - ksp->ks_ndata = ssh->size; - ksp->ks_data_size = ssh->size * sizeof (spa_read_history_t); - + spa_history_list_t *shl = procfs_list->pl_private; + mutex_enter(&procfs_list->pl_lock); + spa_read_history_truncate(shl, 0); + mutex_exit(&procfs_list->pl_lock); return (0); } static void spa_read_history_init(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.read_history; - char *name; - kstat_t *ksp; + spa_history_list_t *shl = &spa->spa_stats.read_history; + char *module; - mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL); - list_create(&ssh->list, sizeof (spa_read_history_t), - offsetof(spa_read_history_t, srh_link)); + shl->size = 0; - ssh->count = 0; - ssh->size = 0; - ssh->private = NULL; + module = kmem_asprintf("zfs/%s", spa_name(spa)); - name = kmem_asprintf("zfs/%s", spa_name(spa)); + shl->procfs_list.pl_private = shl; + procfs_list_install(module, + "reads", + &shl->procfs_list, + spa_read_history_show, + spa_read_history_show_header, + spa_read_history_clear, + offsetof(spa_read_history_t, srh_node)); - ksp = kstat_create(name, 0, "reads", "misc", - KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); - ssh->kstat = ksp; - - if (ksp) { - ksp->ks_lock = &ssh->lock; - ksp->ks_data = NULL; - ksp->ks_private = spa; - ksp->ks_update = spa_read_history_update; - kstat_set_raw_ops(ksp, spa_read_history_headers, - spa_read_history_data, spa_read_history_addr); - kstat_install(ksp); - } - strfree(name); + strfree(module); } static void spa_read_history_destroy(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.read_history; - spa_read_history_t *srh; - kstat_t *ksp; - - ksp = ssh->kstat; - if (ksp) - kstat_delete(ksp); - - mutex_enter(&ssh->lock); - while ((srh = list_remove_head(&ssh->list))) { - ssh->size--; - kmem_free(srh, sizeof (spa_read_history_t)); - } - - ASSERT3U(ssh->size, ==, 0); - list_destroy(&ssh->list); - mutex_exit(&ssh->lock); - - mutex_destroy(&ssh->lock); + spa_history_list_t *shl = &spa->spa_stats.read_history; + procfs_list_uninstall(&shl->procfs_list); + spa_read_history_truncate(shl, 0); + procfs_list_destroy(&shl->procfs_list); } void spa_read_history_add(spa_t *spa, const zbookmark_phys_t *zb, uint32_t aflags) { - spa_stats_history_t *ssh = &spa->spa_stats.read_history; - spa_read_history_t *srh, *rm; + spa_history_list_t *shl = &spa->spa_stats.read_history; + spa_read_history_t *srh; ASSERT3P(spa, !=, NULL); ASSERT3P(zb, !=, NULL); - if (zfs_read_history == 0 && ssh->size == 0) + if (zfs_read_history == 0 && shl->size == 0) return; if (zfs_read_history_hits == 0 && (aflags & ARC_FLAG_CACHED)) @@ -222,19 +174,14 @@ spa_read_history_add(spa_t *spa, const zbookmark_phys_t *zb, uint32_t aflags) srh->aflags = aflags; srh->pid = getpid(); - mutex_enter(&ssh->lock); + mutex_enter(&shl->procfs_list.pl_lock); - srh->uid = ssh->count++; - list_insert_head(&ssh->list, srh); - ssh->size++; + procfs_list_add(&shl->procfs_list, srh); + shl->size++; - while (ssh->size > zfs_read_history) { - ssh->size--; - rm = list_remove_tail(&ssh->list); - kmem_free(rm, sizeof (spa_read_history_t)); - } + spa_read_history_truncate(shl, zfs_read_history); - mutex_exit(&ssh->lock); + mutex_exit(&shl->procfs_list.pl_lock); } /* @@ -256,22 +203,21 @@ typedef struct spa_txg_history { uint64_t writes; /* number of write operations */ uint64_t ndirty; /* number of dirty bytes */ hrtime_t times[TXG_STATE_COMMITTED]; /* completion times */ - list_node_t sth_link; + procfs_list_node_t sth_node; } spa_txg_history_t; static int -spa_txg_history_headers(char *buf, size_t size) +spa_txg_history_show_header(struct seq_file *f) { - (void) snprintf(buf, size, "%-8s %-16s %-5s %-12s %-12s %-12s " + seq_printf(f, "%-8s %-16s %-5s %-12s %-12s %-12s " "%-8s %-8s %-12s %-12s %-12s %-12s\n", "txg", "birth", "state", "ndirty", "nread", "nwritten", "reads", "writes", "otime", "qtime", "wtime", "stime"); - return (0); } static int -spa_txg_history_data(char *buf, size_t size, void *data) +spa_txg_history_show(struct seq_file *f, void *data) { spa_txg_history_t *sth = (spa_txg_history_t *)data; uint64_t open = 0, quiesce = 0, wait = 0, sync = 0; @@ -303,7 +249,7 @@ spa_txg_history_data(char *buf, size_t size, void *data) sync = sth->times[TXG_STATE_SYNCED] - sth->times[TXG_STATE_WAIT_FOR_SYNC]; - (void) snprintf(buf, size, "%-8llu %-16llu %-5c %-12llu " + seq_printf(f, "%-8llu %-16llu %-5c %-12llu " "%-12llu %-12llu %-8llu %-8llu %-12llu %-12llu %-12llu %-12llu\n", (longlong_t)sth->txg, sth->times[TXG_STATE_BIRTH], state, (u_longlong_t)sth->ndirty, @@ -315,110 +261,62 @@ spa_txg_history_data(char *buf, size_t size, void *data) return (0); } -/* - * Calculate the address for the next spa_stats_history_t entry. The - * ssh->lock will be held until ksp->ks_ndata entries are processed. - */ -static void * -spa_txg_history_addr(kstat_t *ksp, loff_t n) +/* Remove oldest elements from list until there are no more than 'size' left */ +static void +spa_txg_history_truncate(spa_history_list_t *shl, unsigned int size) { - spa_t *spa = ksp->ks_private; - spa_stats_history_t *ssh = &spa->spa_stats.txg_history; - - ASSERT(MUTEX_HELD(&ssh->lock)); + spa_txg_history_t *sth; + while (shl->size > size) { + sth = list_remove_head(&shl->procfs_list.pl_list); + ASSERT3P(sth, !=, NULL); + kmem_free(sth, sizeof (spa_txg_history_t)); + shl->size--; + } - if (n == 0) - ssh->private = list_tail(&ssh->list); - else if (ssh->private) - ssh->private = list_prev(&ssh->list, ssh->private); + if (size == 0) + ASSERT(list_is_empty(&shl->procfs_list.pl_list)); - return (ssh->private); } -/* - * When the kstat is written discard all spa_txg_history_t entries. The - * ssh->lock will be held until ksp->ks_ndata entries are processed. - */ static int -spa_txg_history_update(kstat_t *ksp, int rw) +spa_txg_history_clear(procfs_list_t *procfs_list) { - spa_t *spa = ksp->ks_private; - spa_stats_history_t *ssh = &spa->spa_stats.txg_history; - - ASSERT(MUTEX_HELD(&ssh->lock)); - - if (rw == KSTAT_WRITE) { - spa_txg_history_t *sth; - - while ((sth = list_remove_head(&ssh->list))) { - ssh->size--; - kmem_free(sth, sizeof (spa_txg_history_t)); - } - - ASSERT3U(ssh->size, ==, 0); - } - - ksp->ks_ndata = ssh->size; - ksp->ks_data_size = ssh->size * sizeof (spa_txg_history_t); - + spa_history_list_t *shl = procfs_list->pl_private; + mutex_enter(&procfs_list->pl_lock); + spa_txg_history_truncate(shl, 0); + mutex_exit(&procfs_list->pl_lock); return (0); } static void spa_txg_history_init(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.txg_history; - char *name; - kstat_t *ksp; + spa_history_list_t *shl = &spa->spa_stats.txg_history; + char *module; - mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL); - list_create(&ssh->list, sizeof (spa_txg_history_t), - offsetof(spa_txg_history_t, sth_link)); + shl->size = 0; - ssh->count = 0; - ssh->size = 0; - ssh->private = NULL; + module = kmem_asprintf("zfs/%s", spa_name(spa)); - name = kmem_asprintf("zfs/%s", spa_name(spa)); + shl->procfs_list.pl_private = shl; + procfs_list_install(module, + "txgs", + &shl->procfs_list, + spa_txg_history_show, + spa_txg_history_show_header, + spa_txg_history_clear, + offsetof(spa_txg_history_t, sth_node)); - ksp = kstat_create(name, 0, "txgs", "misc", - KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); - ssh->kstat = ksp; - - if (ksp) { - ksp->ks_lock = &ssh->lock; - ksp->ks_data = NULL; - ksp->ks_private = spa; - ksp->ks_update = spa_txg_history_update; - kstat_set_raw_ops(ksp, spa_txg_history_headers, - spa_txg_history_data, spa_txg_history_addr); - kstat_install(ksp); - } - strfree(name); + strfree(module); } static void spa_txg_history_destroy(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.txg_history; - spa_txg_history_t *sth; - kstat_t *ksp; - - ksp = ssh->kstat; - if (ksp) - kstat_delete(ksp); - - mutex_enter(&ssh->lock); - while ((sth = list_remove_head(&ssh->list))) { - ssh->size--; - kmem_free(sth, sizeof (spa_txg_history_t)); - } - - ASSERT3U(ssh->size, ==, 0); - list_destroy(&ssh->list); - mutex_exit(&ssh->lock); - - mutex_destroy(&ssh->lock); + spa_history_list_t *shl = &spa->spa_stats.txg_history; + procfs_list_uninstall(&shl->procfs_list); + spa_txg_history_truncate(shl, 0); + procfs_list_destroy(&shl->procfs_list); } /* @@ -427,10 +325,10 @@ spa_txg_history_destroy(spa_t *spa) void spa_txg_history_add(spa_t *spa, uint64_t txg, hrtime_t birth_time) { - spa_stats_history_t *ssh = &spa->spa_stats.txg_history; - spa_txg_history_t *sth, *rm; + spa_history_list_t *shl = &spa->spa_stats.txg_history; + spa_txg_history_t *sth; - if (zfs_txg_history == 0 && ssh->size == 0) + if (zfs_txg_history == 0 && shl->size == 0) return; sth = kmem_zalloc(sizeof (spa_txg_history_t), KM_SLEEP); @@ -438,18 +336,11 @@ spa_txg_history_add(spa_t *spa, uint64_t txg, hrtime_t birth_time) sth->state = TXG_STATE_OPEN; sth->times[TXG_STATE_BIRTH] = birth_time; - mutex_enter(&ssh->lock); - - list_insert_head(&ssh->list, sth); - ssh->size++; - - while (ssh->size > zfs_txg_history) { - ssh->size--; - rm = list_remove_tail(&ssh->list); - kmem_free(rm, sizeof (spa_txg_history_t)); - } - - mutex_exit(&ssh->lock); + mutex_enter(&shl->procfs_list.pl_lock); + procfs_list_add(&shl->procfs_list, sth); + shl->size++; + spa_txg_history_truncate(shl, zfs_txg_history); + mutex_exit(&shl->procfs_list.pl_lock); } /* @@ -459,16 +350,16 @@ int spa_txg_history_set(spa_t *spa, uint64_t txg, txg_state_t completed_state, hrtime_t completed_time) { - spa_stats_history_t *ssh = &spa->spa_stats.txg_history; + spa_history_list_t *shl = &spa->spa_stats.txg_history; spa_txg_history_t *sth; int error = ENOENT; if (zfs_txg_history == 0) return (0); - mutex_enter(&ssh->lock); - for (sth = list_head(&ssh->list); sth != NULL; - sth = list_next(&ssh->list, sth)) { + mutex_enter(&shl->procfs_list.pl_lock); + for (sth = list_tail(&shl->procfs_list.pl_list); sth != NULL; + sth = list_prev(&shl->procfs_list.pl_list, sth)) { if (sth->txg == txg) { sth->times[completed_state] = completed_time; sth->state++; @@ -476,7 +367,7 @@ spa_txg_history_set(spa_t *spa, uint64_t txg, txg_state_t completed_state, break; } } - mutex_exit(&ssh->lock); + mutex_exit(&shl->procfs_list.pl_lock); return (error); } @@ -488,16 +379,16 @@ 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) { - spa_stats_history_t *ssh = &spa->spa_stats.txg_history; + spa_history_list_t *shl = &spa->spa_stats.txg_history; spa_txg_history_t *sth; int error = ENOENT; if (zfs_txg_history == 0) return (0); - mutex_enter(&ssh->lock); - for (sth = list_head(&ssh->list); sth != NULL; - sth = list_next(&ssh->list, sth)) { + mutex_enter(&shl->procfs_list.pl_lock); + for (sth = list_tail(&shl->procfs_list.pl_list); sth != NULL; + sth = list_prev(&shl->procfs_list.pl_list, sth)) { if (sth->txg == txg) { sth->nread = nread; sth->nwritten = nwritten; @@ -508,7 +399,7 @@ spa_txg_history_set_io(spa_t *spa, uint64_t txg, uint64_t nread, break; } } - mutex_exit(&ssh->lock); + mutex_exit(&shl->procfs_list.pl_lock); return (error); } @@ -580,16 +471,16 @@ static int spa_tx_assign_update(kstat_t *ksp, int rw) { spa_t *spa = ksp->ks_private; - spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram; + spa_history_kstat_t *shk = &spa->spa_stats.tx_assign_histogram; int i; if (rw == KSTAT_WRITE) { - for (i = 0; i < ssh->count; i++) - ((kstat_named_t *)ssh->private)[i].value.ui64 = 0; + for (i = 0; i < shk->count; i++) + ((kstat_named_t *)shk->private)[i].value.ui64 = 0; } - for (i = ssh->count; i > 0; i--) - if (((kstat_named_t *)ssh->private)[i-1].value.ui64 != 0) + for (i = shk->count; i > 0; i--) + if (((kstat_named_t *)shk->private)[i-1].value.ui64 != 0) break; ksp->ks_ndata = i; @@ -601,22 +492,22 @@ spa_tx_assign_update(kstat_t *ksp, int rw) static void spa_tx_assign_init(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram; + spa_history_kstat_t *shk = &spa->spa_stats.tx_assign_histogram; char *name; kstat_named_t *ks; kstat_t *ksp; int i; - mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&shk->lock, NULL, MUTEX_DEFAULT, NULL); - ssh->count = 42; /* power of two buckets for 1ns to 2,199s */ - ssh->size = ssh->count * sizeof (kstat_named_t); - ssh->private = kmem_alloc(ssh->size, KM_SLEEP); + shk->count = 42; /* power of two buckets for 1ns to 2,199s */ + shk->size = shk->count * sizeof (kstat_named_t); + shk->private = kmem_alloc(shk->size, KM_SLEEP); name = kmem_asprintf("zfs/%s", spa_name(spa)); - for (i = 0; i < ssh->count; i++) { - ks = &((kstat_named_t *)ssh->private)[i]; + for (i = 0; i < shk->count; i++) { + ks = &((kstat_named_t *)shk->private)[i]; ks->data_type = KSTAT_DATA_UINT64; ks->value.ui64 = 0; (void) snprintf(ks->name, KSTAT_STRLEN, "%llu ns", @@ -625,13 +516,13 @@ spa_tx_assign_init(spa_t *spa) ksp = kstat_create(name, 0, "dmu_tx_assign", "misc", KSTAT_TYPE_NAMED, 0, KSTAT_FLAG_VIRTUAL); - ssh->kstat = ksp; + shk->kstat = ksp; if (ksp) { - ksp->ks_lock = &ssh->lock; - ksp->ks_data = ssh->private; - ksp->ks_ndata = ssh->count; - ksp->ks_data_size = ssh->size; + ksp->ks_lock = &shk->lock; + ksp->ks_data = shk->private; + ksp->ks_ndata = shk->count; + ksp->ks_data_size = shk->size; ksp->ks_private = spa; ksp->ks_update = spa_tx_assign_update; kstat_install(ksp); @@ -642,27 +533,27 @@ spa_tx_assign_init(spa_t *spa) static void spa_tx_assign_destroy(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram; + spa_history_kstat_t *shk = &spa->spa_stats.tx_assign_histogram; kstat_t *ksp; - ksp = ssh->kstat; + ksp = shk->kstat; if (ksp) kstat_delete(ksp); - kmem_free(ssh->private, ssh->size); - mutex_destroy(&ssh->lock); + kmem_free(shk->private, shk->size); + mutex_destroy(&shk->lock); } void spa_tx_assign_add_nsecs(spa_t *spa, uint64_t nsecs) { - spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram; + spa_history_kstat_t *shk = &spa->spa_stats.tx_assign_histogram; uint64_t idx = 0; - while (((1ULL << idx) < nsecs) && (idx < ssh->size - 1)) + while (((1ULL << idx) < nsecs) && (idx < shk->size - 1)) idx++; - atomic_inc_64(&((kstat_named_t *)ssh->private)[idx].value.ui64); + atomic_inc_64(&((kstat_named_t *)shk->private)[idx].value.ui64); } /* @@ -682,19 +573,19 @@ spa_io_history_update(kstat_t *ksp, int rw) static void spa_io_history_init(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.io_history; + spa_history_kstat_t *shk = &spa->spa_stats.io_history; char *name; kstat_t *ksp; - mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&shk->lock, NULL, MUTEX_DEFAULT, NULL); name = kmem_asprintf("zfs/%s", spa_name(spa)); ksp = kstat_create(name, 0, "io", "disk", KSTAT_TYPE_IO, 1, 0); - ssh->kstat = ksp; + shk->kstat = ksp; if (ksp) { - ksp->ks_lock = &ssh->lock; + ksp->ks_lock = &shk->lock; ksp->ks_private = spa; ksp->ks_update = spa_io_history_update; kstat_install(ksp); @@ -705,12 +596,12 @@ spa_io_history_init(spa_t *spa) static void spa_io_history_destroy(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.io_history; + spa_history_kstat_t *shk = &spa->spa_stats.io_history; - if (ssh->kstat) - kstat_delete(ssh->kstat); + if (shk->kstat) + kstat_delete(shk->kstat); - mutex_destroy(&ssh->lock); + mutex_destroy(&shk->lock); } /* @@ -733,7 +624,7 @@ spa_io_history_destroy(spa_t *spa) */ typedef struct spa_mmp_history { - uint64_t mmp_kstat_id; /* unique # for updates */ + uint64_t mmp_node_id; /* unique # for updates */ uint64_t txg; /* txg of last sync */ uint64_t timestamp; /* UTC time MMP write issued */ uint64_t mmp_delay; /* mmp_thread.mmp_delay at timestamp */ @@ -743,20 +634,20 @@ typedef struct spa_mmp_history { int io_error; /* error status of MMP write */ hrtime_t error_start; /* hrtime of start of error period */ hrtime_t duration; /* time from submission to completion */ - list_node_t smh_link; + procfs_list_node_t smh_node; } spa_mmp_history_t; static int -spa_mmp_history_headers(char *buf, size_t size) +spa_mmp_history_show_header(struct seq_file *f) { - (void) snprintf(buf, size, "%-10s %-10s %-10s %-6s %-10s %-12s %-24s " + seq_printf(f, "%-10s %-10s %-10s %-6s %-10s %-12s %-24s " "%-10s %s\n", "id", "txg", "timestamp", "error", "duration", "mmp_delay", "vdev_guid", "vdev_label", "vdev_path"); return (0); } static int -spa_mmp_history_data(char *buf, size_t size, void *data) +spa_mmp_history_show(struct seq_file *f, void *data) { spa_mmp_history_t *smh = (spa_mmp_history_t *)data; char skip_fmt[] = "%-10llu %-10llu %10llu %#6llx %10lld %12llu %-24llu " @@ -764,8 +655,8 @@ spa_mmp_history_data(char *buf, size_t size, void *data) char write_fmt[] = "%-10llu %-10llu %10llu %6lld %10lld %12llu %-24llu " "%-10lld %s\n"; - (void) snprintf(buf, size, (smh->error_start ? skip_fmt : write_fmt), - (u_longlong_t)smh->mmp_kstat_id, (u_longlong_t)smh->txg, + seq_printf(f, (smh->error_start ? skip_fmt : write_fmt), + (u_longlong_t)smh->mmp_node_id, (u_longlong_t)smh->txg, (u_longlong_t)smh->timestamp, (longlong_t)smh->io_error, (longlong_t)smh->duration, (u_longlong_t)smh->mmp_delay, (u_longlong_t)smh->vdev_guid, (u_longlong_t)smh->vdev_label, @@ -774,137 +665,86 @@ spa_mmp_history_data(char *buf, size_t size, void *data) return (0); } -/* - * Calculate the address for the next spa_stats_history_t entry. The - * ssh->lock will be held until ksp->ks_ndata entries are processed. - */ -static void * -spa_mmp_history_addr(kstat_t *ksp, loff_t n) +/* Remove oldest elements from list until there are no more than 'size' left */ +static void +spa_mmp_history_truncate(spa_history_list_t *shl, unsigned int size) { - spa_t *spa = ksp->ks_private; - spa_stats_history_t *ssh = &spa->spa_stats.mmp_history; - - ASSERT(MUTEX_HELD(&ssh->lock)); + spa_mmp_history_t *smh; + while (shl->size > size) { + smh = list_remove_head(&shl->procfs_list.pl_list); + if (smh->vdev_path) + strfree(smh->vdev_path); + kmem_free(smh, sizeof (spa_mmp_history_t)); + shl->size--; + } - if (n == 0) - ssh->private = list_tail(&ssh->list); - else if (ssh->private) - ssh->private = list_prev(&ssh->list, ssh->private); + if (size == 0) + ASSERT(list_is_empty(&shl->procfs_list.pl_list)); - return (ssh->private); } -/* - * When the kstat is written discard all spa_mmp_history_t entries. The - * ssh->lock will be held until ksp->ks_ndata entries are processed. - */ static int -spa_mmp_history_update(kstat_t *ksp, int rw) +spa_mmp_history_clear(procfs_list_t *procfs_list) { - spa_t *spa = ksp->ks_private; - spa_stats_history_t *ssh = &spa->spa_stats.mmp_history; - - ASSERT(MUTEX_HELD(&ssh->lock)); - - if (rw == KSTAT_WRITE) { - spa_mmp_history_t *smh; - - while ((smh = list_remove_head(&ssh->list))) { - ssh->size--; - if (smh->vdev_path) - strfree(smh->vdev_path); - kmem_free(smh, sizeof (spa_mmp_history_t)); - } - - ASSERT3U(ssh->size, ==, 0); - } - - ksp->ks_ndata = ssh->size; - ksp->ks_data_size = ssh->size * sizeof (spa_mmp_history_t); - + spa_history_list_t *shl = procfs_list->pl_private; + mutex_enter(&procfs_list->pl_lock); + spa_mmp_history_truncate(shl, 0); + mutex_exit(&procfs_list->pl_lock); return (0); } static void spa_mmp_history_init(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.mmp_history; - char *name; - kstat_t *ksp; + spa_history_list_t *shl = &spa->spa_stats.mmp_history; + char *module; - mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL); - list_create(&ssh->list, sizeof (spa_mmp_history_t), - offsetof(spa_mmp_history_t, smh_link)); + shl->size = 0; - ssh->count = 0; - ssh->size = 0; - ssh->private = NULL; - - name = kmem_asprintf("zfs/%s", spa_name(spa)); + module = kmem_asprintf("zfs/%s", spa_name(spa)); - ksp = kstat_create(name, 0, "multihost", "misc", - KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); - ssh->kstat = ksp; + shl->procfs_list.pl_private = shl; + procfs_list_install(module, + "multihost", + &shl->procfs_list, + spa_mmp_history_show, + spa_mmp_history_show_header, + spa_mmp_history_clear, + offsetof(spa_mmp_history_t, smh_node)); - if (ksp) { - ksp->ks_lock = &ssh->lock; - ksp->ks_data = NULL; - ksp->ks_private = spa; - ksp->ks_update = spa_mmp_history_update; - kstat_set_raw_ops(ksp, spa_mmp_history_headers, - spa_mmp_history_data, spa_mmp_history_addr); - kstat_install(ksp); - } - strfree(name); + strfree(module); } static void spa_mmp_history_destroy(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.mmp_history; - spa_mmp_history_t *smh; - kstat_t *ksp; - - ksp = ssh->kstat; - if (ksp) - kstat_delete(ksp); - - mutex_enter(&ssh->lock); - while ((smh = list_remove_head(&ssh->list))) { - ssh->size--; - if (smh->vdev_path) - strfree(smh->vdev_path); - kmem_free(smh, sizeof (spa_mmp_history_t)); - } - - ASSERT3U(ssh->size, ==, 0); - list_destroy(&ssh->list); - mutex_exit(&ssh->lock); - - mutex_destroy(&ssh->lock); + spa_history_list_t *shl = &spa->spa_stats.mmp_history; + procfs_list_uninstall(&shl->procfs_list); + spa_mmp_history_truncate(shl, 0); + procfs_list_destroy(&shl->procfs_list); } /* * Set duration in existing "skip" record to how long we have waited for a leaf * vdev to become available. * - * Important that we start search at the head of the list where new + * Important that we start search at the tail of the list where new * records are inserted, so this is normally an O(1) operation. */ int -spa_mmp_history_set_skip(spa_t *spa, uint64_t mmp_kstat_id) +spa_mmp_history_set_skip(spa_t *spa, uint64_t mmp_node_id) { - spa_stats_history_t *ssh = &spa->spa_stats.mmp_history; + spa_history_list_t *shl = &spa->spa_stats.mmp_history; spa_mmp_history_t *smh; int error = ENOENT; - if (zfs_multihost_history == 0 && ssh->size == 0) + if (zfs_multihost_history == 0 && shl->size == 0) return (0); - mutex_enter(&ssh->lock); - for (smh = list_head(&ssh->list); smh != NULL; - smh = list_next(&ssh->list, smh)) { - if (smh->mmp_kstat_id == mmp_kstat_id) { + mutex_enter(&shl->procfs_list.pl_lock); + for (smh = list_tail(&shl->procfs_list.pl_list); smh != NULL; + smh = list_prev(&shl->procfs_list.pl_list, smh)) { + if (smh->mmp_node_id == mmp_node_id) { ASSERT3U(smh->io_error, !=, 0); smh->duration = gethrtime() - smh->error_start; smh->vdev_guid++; @@ -912,7 +752,7 @@ spa_mmp_history_set_skip(spa_t *spa, uint64_t mmp_kstat_id) break; } } - mutex_exit(&ssh->lock); + mutex_exit(&shl->procfs_list.pl_lock); return (error); } @@ -922,20 +762,20 @@ spa_mmp_history_set_skip(spa_t *spa, uint64_t mmp_kstat_id) * See comment re: search order above spa_mmp_history_set_skip(). */ int -spa_mmp_history_set(spa_t *spa, uint64_t mmp_kstat_id, int io_error, +spa_mmp_history_set(spa_t *spa, uint64_t mmp_node_id, int io_error, hrtime_t duration) { - spa_stats_history_t *ssh = &spa->spa_stats.mmp_history; + spa_history_list_t *shl = &spa->spa_stats.mmp_history; spa_mmp_history_t *smh; int error = ENOENT; - if (zfs_multihost_history == 0 && ssh->size == 0) + if (zfs_multihost_history == 0 && shl->size == 0) return (0); - mutex_enter(&ssh->lock); - for (smh = list_head(&ssh->list); smh != NULL; - smh = list_next(&ssh->list, smh)) { - if (smh->mmp_kstat_id == mmp_kstat_id) { + mutex_enter(&shl->procfs_list.pl_lock); + for (smh = list_tail(&shl->procfs_list.pl_list); smh != NULL; + smh = list_prev(&shl->procfs_list.pl_list, smh)) { + if (smh->mmp_node_id == mmp_node_id) { ASSERT(smh->io_error == 0); smh->io_error = io_error; smh->duration = duration; @@ -943,7 +783,7 @@ spa_mmp_history_set(spa_t *spa, uint64_t mmp_kstat_id, int io_error, break; } } - mutex_exit(&ssh->lock); + mutex_exit(&shl->procfs_list.pl_lock); return (error); } @@ -953,16 +793,16 @@ spa_mmp_history_set(spa_t *spa, uint64_t mmp_kstat_id, int io_error, * error == 0 : a write was issued. * error != 0 : a write was not issued because no leaves were found. */ -void * +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_kstat_id, + uint64_t mmp_delay, vdev_t *vd, int label, uint64_t mmp_node_id, int error) { - spa_stats_history_t *ssh = &spa->spa_stats.mmp_history; - spa_mmp_history_t *smh, *rm; + spa_history_list_t *shl = &spa->spa_stats.mmp_history; + spa_mmp_history_t *smh; - if (zfs_multihost_history == 0 && ssh->size == 0) - return (NULL); + if (zfs_multihost_history == 0 && shl->size == 0) + return; smh = kmem_zalloc(sizeof (spa_mmp_history_t), KM_SLEEP); smh->txg = txg; @@ -974,7 +814,7 @@ spa_mmp_history_add(spa_t *spa, uint64_t txg, uint64_t timestamp, smh->vdev_path = strdup(vd->vdev_path); } smh->vdev_label = label; - smh->mmp_kstat_id = mmp_kstat_id; + smh->mmp_node_id = mmp_node_id; if (error) { smh->io_error = error; @@ -982,21 +822,11 @@ spa_mmp_history_add(spa_t *spa, uint64_t txg, uint64_t timestamp, smh->vdev_guid = 1; } - mutex_enter(&ssh->lock); - - list_insert_head(&ssh->list, smh); - ssh->size++; - - while (ssh->size > zfs_multihost_history) { - ssh->size--; - rm = list_remove_tail(&ssh->list); - if (rm->vdev_path) - strfree(rm->vdev_path); - kmem_free(rm, sizeof (spa_mmp_history_t)); - } - - mutex_exit(&ssh->lock); - return ((void *)smh); + mutex_enter(&shl->procfs_list.pl_lock); + procfs_list_add(&shl->procfs_list, smh); + shl->size++; + spa_mmp_history_truncate(shl, zfs_multihost_history); + mutex_exit(&shl->procfs_list.pl_lock); } static void * @@ -1023,19 +853,19 @@ spa_state_data(char *buf, size_t size, void *data) static void spa_state_init(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.state; + spa_history_kstat_t *shk = &spa->spa_stats.state; char *name; kstat_t *ksp; - mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&shk->lock, NULL, MUTEX_DEFAULT, NULL); name = kmem_asprintf("zfs/%s", spa_name(spa)); ksp = kstat_create(name, 0, "state", "misc", KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); - ssh->kstat = ksp; + shk->kstat = ksp; if (ksp) { - ksp->ks_lock = &ssh->lock; + ksp->ks_lock = &shk->lock; ksp->ks_data = NULL; ksp->ks_private = spa; ksp->ks_flags |= KSTAT_FLAG_NO_HEADERS; @@ -1049,12 +879,12 @@ spa_state_init(spa_t *spa) static void spa_health_destroy(spa_t *spa) { - spa_stats_history_t *ssh = &spa->spa_stats.state; - kstat_t *ksp = ssh->kstat; + spa_history_kstat_t *shk = &spa->spa_stats.state; + kstat_t *ksp = shk->kstat; if (ksp) kstat_delete(ksp); - mutex_destroy(&ssh->lock); + mutex_destroy(&shk->lock); } void diff --git a/module/zfs/vdev_queue.c b/module/zfs/vdev_queue.c index 30a883f85..89cdf7d81 100644 --- a/module/zfs/vdev_queue.c +++ b/module/zfs/vdev_queue.c @@ -429,16 +429,16 @@ static void vdev_queue_io_add(vdev_queue_t *vq, zio_t *zio) { spa_t *spa = zio->io_spa; - spa_stats_history_t *ssh = &spa->spa_stats.io_history; + spa_history_kstat_t *shk = &spa->spa_stats.io_history; ASSERT3U(zio->io_priority, <, ZIO_PRIORITY_NUM_QUEUEABLE); avl_add(vdev_queue_class_tree(vq, zio->io_priority), zio); avl_add(vdev_queue_type_tree(vq, zio->io_type), zio); - if (ssh->kstat != NULL) { - mutex_enter(&ssh->lock); - kstat_waitq_enter(ssh->kstat->ks_data); - mutex_exit(&ssh->lock); + if (shk->kstat != NULL) { + mutex_enter(&shk->lock); + kstat_waitq_enter(shk->kstat->ks_data); + mutex_exit(&shk->lock); } } @@ -446,16 +446,16 @@ static void vdev_queue_io_remove(vdev_queue_t *vq, zio_t *zio) { spa_t *spa = zio->io_spa; - spa_stats_history_t *ssh = &spa->spa_stats.io_history; + spa_history_kstat_t *shk = &spa->spa_stats.io_history; ASSERT3U(zio->io_priority, <, ZIO_PRIORITY_NUM_QUEUEABLE); avl_remove(vdev_queue_class_tree(vq, zio->io_priority), zio); avl_remove(vdev_queue_type_tree(vq, zio->io_type), zio); - if (ssh->kstat != NULL) { - mutex_enter(&ssh->lock); - kstat_waitq_exit(ssh->kstat->ks_data); - mutex_exit(&ssh->lock); + if (shk->kstat != NULL) { + mutex_enter(&shk->lock); + kstat_waitq_exit(shk->kstat->ks_data); + mutex_exit(&shk->lock); } } @@ -463,17 +463,17 @@ static void vdev_queue_pending_add(vdev_queue_t *vq, zio_t *zio) { spa_t *spa = zio->io_spa; - spa_stats_history_t *ssh = &spa->spa_stats.io_history; + spa_history_kstat_t *shk = &spa->spa_stats.io_history; ASSERT(MUTEX_HELD(&vq->vq_lock)); ASSERT3U(zio->io_priority, <, ZIO_PRIORITY_NUM_QUEUEABLE); vq->vq_class[zio->io_priority].vqc_active++; avl_add(&vq->vq_active_tree, zio); - if (ssh->kstat != NULL) { - mutex_enter(&ssh->lock); - kstat_runq_enter(ssh->kstat->ks_data); - mutex_exit(&ssh->lock); + if (shk->kstat != NULL) { + mutex_enter(&shk->lock); + kstat_runq_enter(shk->kstat->ks_data); + mutex_exit(&shk->lock); } } @@ -481,17 +481,17 @@ static void vdev_queue_pending_remove(vdev_queue_t *vq, zio_t *zio) { spa_t *spa = zio->io_spa; - spa_stats_history_t *ssh = &spa->spa_stats.io_history; + spa_history_kstat_t *shk = &spa->spa_stats.io_history; ASSERT(MUTEX_HELD(&vq->vq_lock)); ASSERT3U(zio->io_priority, <, ZIO_PRIORITY_NUM_QUEUEABLE); vq->vq_class[zio->io_priority].vqc_active--; avl_remove(&vq->vq_active_tree, zio); - if (ssh->kstat != NULL) { - kstat_io_t *ksio = ssh->kstat->ks_data; + if (shk->kstat != NULL) { + kstat_io_t *ksio = shk->kstat->ks_data; - mutex_enter(&ssh->lock); + mutex_enter(&shk->lock); kstat_runq_exit(ksio); if (zio->io_type == ZIO_TYPE_READ) { ksio->reads++; @@ -500,7 +500,7 @@ vdev_queue_pending_remove(vdev_queue_t *vq, zio_t *zio) ksio->writes++; ksio->nwritten += zio->io_size; } - mutex_exit(&ssh->lock); + mutex_exit(&shk->lock); } } diff --git a/module/zfs/zfs_debug.c b/module/zfs/zfs_debug.c index ca79893c9..b5f93fd9b 100644 --- a/module/zfs/zfs_debug.c +++ b/module/zfs/zfs_debug.c @@ -24,13 +24,17 @@ */ #include <sys/zfs_context.h> -#include <sys/kstat.h> -list_t zfs_dbgmsgs; +typedef struct zfs_dbgmsg { + procfs_list_node_t zdm_node; + time_t zdm_timestamp; + int zdm_size; + char zdm_msg[1]; /* variable length allocation */ +} zfs_dbgmsg_t; + +procfs_list_t zfs_dbgmsgs; int zfs_dbgmsg_size = 0; -kmutex_t zfs_dbgmsgs_lock; int zfs_dbgmsg_maxsize = 4<<20; /* 4MB */ -kstat_t *zfs_dbgmsg_kstat; /* * Internal ZFS debug messages are enabled by default. @@ -47,123 +51,71 @@ kstat_t *zfs_dbgmsg_kstat; int zfs_dbgmsg_enable = 1; static int -zfs_dbgmsg_headers(char *buf, size_t size) +zfs_dbgmsg_show_header(struct seq_file *f) { - (void) snprintf(buf, size, "%-12s %-8s\n", "timestamp", "message"); - + seq_printf(f, "%-12s %-8s\n", "timestamp", "message"); return (0); } static int -zfs_dbgmsg_data(char *buf, size_t size, void *data) +zfs_dbgmsg_show(struct seq_file *f, void *p) { - zfs_dbgmsg_t *zdm = (zfs_dbgmsg_t *)data; - - (void) snprintf(buf, size, "%-12llu %-s\n", + zfs_dbgmsg_t *zdm = (zfs_dbgmsg_t *)p; + seq_printf(f, "%-12llu %-s\n", (u_longlong_t)zdm->zdm_timestamp, zdm->zdm_msg); - return (0); } -static void * -zfs_dbgmsg_addr(kstat_t *ksp, loff_t n) -{ - zfs_dbgmsg_t *zdm = (zfs_dbgmsg_t *)ksp->ks_private; - - ASSERT(MUTEX_HELD(&zfs_dbgmsgs_lock)); - - if (n == 0) - ksp->ks_private = list_head(&zfs_dbgmsgs); - else if (zdm) - ksp->ks_private = list_next(&zfs_dbgmsgs, zdm); - - return (ksp->ks_private); -} - static void zfs_dbgmsg_purge(int max_size) { - zfs_dbgmsg_t *zdm; - int size; - - ASSERT(MUTEX_HELD(&zfs_dbgmsgs_lock)); - while (zfs_dbgmsg_size > max_size) { - zdm = list_remove_head(&zfs_dbgmsgs); + zfs_dbgmsg_t *zdm = list_remove_head(&zfs_dbgmsgs.pl_list); if (zdm == NULL) return; - size = zdm->zdm_size; + int size = zdm->zdm_size; kmem_free(zdm, size); zfs_dbgmsg_size -= size; } } static int -zfs_dbgmsg_update(kstat_t *ksp, int rw) +zfs_dbgmsg_clear(procfs_list_t *procfs_list) { - if (rw == KSTAT_WRITE) - zfs_dbgmsg_purge(0); - + mutex_enter(&zfs_dbgmsgs.pl_lock); + zfs_dbgmsg_purge(0); + mutex_exit(&zfs_dbgmsgs.pl_lock); return (0); } void zfs_dbgmsg_init(void) { - list_create(&zfs_dbgmsgs, sizeof (zfs_dbgmsg_t), + procfs_list_install("zfs", + "dbgmsg", + &zfs_dbgmsgs, + zfs_dbgmsg_show, + zfs_dbgmsg_show_header, + zfs_dbgmsg_clear, offsetof(zfs_dbgmsg_t, zdm_node)); - mutex_init(&zfs_dbgmsgs_lock, NULL, MUTEX_DEFAULT, NULL); - - zfs_dbgmsg_kstat = kstat_create("zfs", 0, "dbgmsg", "misc", - KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); - if (zfs_dbgmsg_kstat) { - zfs_dbgmsg_kstat->ks_lock = &zfs_dbgmsgs_lock; - zfs_dbgmsg_kstat->ks_ndata = UINT32_MAX; - zfs_dbgmsg_kstat->ks_private = NULL; - zfs_dbgmsg_kstat->ks_update = zfs_dbgmsg_update; - kstat_set_raw_ops(zfs_dbgmsg_kstat, zfs_dbgmsg_headers, - zfs_dbgmsg_data, zfs_dbgmsg_addr); - kstat_install(zfs_dbgmsg_kstat); - } } void zfs_dbgmsg_fini(void) { - if (zfs_dbgmsg_kstat) - kstat_delete(zfs_dbgmsg_kstat); + procfs_list_uninstall(&zfs_dbgmsgs); + zfs_dbgmsg_purge(0); + /* * TODO - decide how to make this permanent */ #ifdef _KERNEL - mutex_enter(&zfs_dbgmsgs_lock); - zfs_dbgmsg_purge(0); - mutex_exit(&zfs_dbgmsgs_lock); - mutex_destroy(&zfs_dbgmsgs_lock); + procfs_list_destroy(&zfs_dbgmsgs); #endif } void -__zfs_dbgmsg(char *buf) -{ - zfs_dbgmsg_t *zdm; - int size; - - size = sizeof (zfs_dbgmsg_t) + strlen(buf); - zdm = kmem_zalloc(size, KM_SLEEP); - zdm->zdm_size = size; - zdm->zdm_timestamp = gethrestime_sec(); - strcpy(zdm->zdm_msg, buf); - - mutex_enter(&zfs_dbgmsgs_lock); - list_insert_tail(&zfs_dbgmsgs, zdm); - zfs_dbgmsg_size += size; - zfs_dbgmsg_purge(MAX(zfs_dbgmsg_maxsize, 0)); - mutex_exit(&zfs_dbgmsgs_lock); -} - -void __set_error(const char *file, const char *func, int line, int err) { /* @@ -176,6 +128,22 @@ __set_error(const char *file, const char *func, int line, int err) } #ifdef _KERNEL +static void +__zfs_dbgmsg(char *buf) +{ + int size = sizeof (zfs_dbgmsg_t) + strlen(buf); + zfs_dbgmsg_t *zdm = kmem_zalloc(size, KM_SLEEP); + zdm->zdm_size = size; + zdm->zdm_timestamp = gethrestime_sec(); + strcpy(zdm->zdm_msg, buf); + + mutex_enter(&zfs_dbgmsgs.pl_lock); + procfs_list_add(&zfs_dbgmsgs, zdm); + zfs_dbgmsg_size += size; + zfs_dbgmsg_purge(MAX(zfs_dbgmsg_maxsize, 0)); + mutex_exit(&zfs_dbgmsgs.pl_lock); +} + void __dprintf(const char *file, const char *func, int line, const char *fmt, ...) { @@ -244,14 +212,12 @@ __dprintf(const char *file, const char *func, int line, const char *fmt, ...) void zfs_dbgmsg_print(const char *tag) { - zfs_dbgmsg_t *zdm; - (void) printf("ZFS_DBGMSG(%s):\n", tag); - mutex_enter(&zfs_dbgmsgs_lock); - for (zdm = list_head(&zfs_dbgmsgs); zdm; - zdm = list_next(&zfs_dbgmsgs, zdm)) + mutex_enter(&zfs_dbgmsgs.pl_lock); + for (zfs_dbgmsg_t *zdm = list_head(&zfs_dbgmsgs.pl_list); zdm != NULL; + zdm = list_next(&zfs_dbgmsgs.pl_list, zdm)) (void) printf("%s\n", zdm->zdm_msg); - mutex_exit(&zfs_dbgmsgs_lock); + mutex_exit(&zfs_dbgmsgs.pl_lock); } #endif /* _KERNEL */ |