/* * Copyright (c) 2020 iXsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include typedef struct procfs_list_iter { procfs_list_t *pli_pl; void *pli_elt; } pli_t; void seq_printf(struct seq_file *f, const char *fmt, ...) { va_list adx; va_start(adx, fmt); (void) vsnprintf(f->sf_buf, f->sf_size, fmt, adx); va_end(adx); } static int procfs_list_update(kstat_t *ksp, int rw) { procfs_list_t *pl = ksp->ks_private; if (rw == KSTAT_WRITE) pl->pl_clear(pl); return (0); } static int procfs_list_data(char *buf, size_t size, void *data) { pli_t *p; void *elt; procfs_list_t *pl; struct seq_file f; p = data; pl = p->pli_pl; elt = p->pli_elt; free(p, M_TEMP); f.sf_buf = buf; f.sf_size = size; return (pl->pl_show(&f, elt)); } static void * procfs_list_addr(kstat_t *ksp, loff_t n) { procfs_list_t *pl = ksp->ks_private; void *elt = ksp->ks_private1; pli_t *p = NULL; if (n == 0) ksp->ks_private1 = list_head(&pl->pl_list); else if (elt) ksp->ks_private1 = list_next(&pl->pl_list, elt); if (ksp->ks_private1) { p = malloc(sizeof (*p), M_TEMP, M_WAITOK); p->pli_pl = pl; p->pli_elt = ksp->ks_private1; } return (p); } void procfs_list_install(const char *module, const char *submodule, const char *name, mode_t mode, 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) { kstat_t *procfs_kstat; 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_show = show; procfs_list->pl_show_header = show_header; procfs_list->pl_clear = clear; procfs_list->pl_next_id = 1; procfs_list->pl_node_offset = procfs_list_node_off; procfs_kstat = kstat_create(module, 0, name, submodule, KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); if (procfs_kstat) { procfs_kstat->ks_lock = &procfs_list->pl_lock; procfs_kstat->ks_ndata = UINT32_MAX; procfs_kstat->ks_private = procfs_list; procfs_kstat->ks_update = procfs_list_update; kstat_set_seq_raw_ops(procfs_kstat, show_header, procfs_list_data, procfs_list_addr); kstat_install(procfs_kstat); procfs_list->pl_private = procfs_kstat; } } void procfs_list_uninstall(procfs_list_t *procfs_list) {} void procfs_list_destroy(procfs_list_t *procfs_list) { ASSERT(list_is_empty(&procfs_list->pl_list)); kstat_delete(procfs_list->pl_private); list_destroy(&procfs_list->pl_list); mutex_destroy(&procfs_list->pl_lock); } #define NODE_ID(procfs_list, obj) \ (((procfs_list_node_t *)(((char *)obj) + \ (procfs_list)->pl_node_offset))->pln_id) 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); }