diff options
author | Jason King <jason.king@joyent.com> | 2018-10-13 02:59:13 -0500 |
---|---|---|
committer | Jason King <jason.king@joyent.com> | 2020-12-08 19:13:02 +0000 |
commit | cb289e196ef1c6759f345d5aac19c09ad077b61a (patch) | |
tree | 78cb0b143d2122f4fdb627bac34aee2329ed02a3 | |
parent | e63721360d4e789727cc6455cab43b8673370ba1 (diff) | |
download | illumos-joyent-cb289e196ef1c6759f345d5aac19c09ad077b61a.tar.gz |
More work
-rw-r--r-- | usr/src/cmd/intrd/intrd.c | 763 |
1 files changed, 640 insertions, 123 deletions
diff --git a/usr/src/cmd/intrd/intrd.c b/usr/src/cmd/intrd/intrd.c index 01d26c7bc2..192033bc91 100644 --- a/usr/src/cmd/intrd/intrd.c +++ b/usr/src/cmd/intrd/intrd.c @@ -12,136 +12,490 @@ /* * Copyright 2018, Joyent, Inc. */ - -#include <sys/kstat.h> -#include <kstat.h> +#define __EXTENSIONS__ +#include <err.h> +#include <errno.h> #include <inttypes.h> -#include <sys/debug.h> -#include <sys/list.h> -#include <string.h> -#include <stdlib.h> +#include <kstat.h> +#include <libcustr.h> +#include <limits.h> #include <stddef.h> -#include <err.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <syslog.h> +#include <unistd.h> +#include <umem.h> +#include <sys/debug.h> +#include <sys/errno.h> +#include <sys/kstat.h> + +typedef struct config { + uint_t cfg_interval; + uint_t cfg_idle_interval; + uint_t cfg_retry_interval; + // idle_load + uint_t cfg_avginterval; + double cfg_tooslow; + double cfg_unsafe_load; + double cfg_mindelta; +} config_t; typedef struct ivec { - list_node_t ivec_node; - hrtime_t ivec_time; hrtime_t ivec_crtime; + int ivec_instance; + int ivec_cpuid; uint64_t ivec_cookie; uint64_t ivec_pil; uint64_t ivec_ino; - uint64_t ivec_num_ino; + uint64_t ivec_time; char *ivec_buspath; - char *ivec_name; + char ivec_name[16]; /* sizeof kstat_named_t.value.c */ + char ivec_type[16]; /* sizeof kstat_named_t.value.c */ } ivec_t; typedef struct cpustat { - list_node_t cs_node; - uint64_t cs_cpuid; hrtime_t cs_crtime; - uint64_t cs_total; - list_t cs_ivecs; + int cs_cpuid; + boolean_t cs_online; + uint64_t cs_cpu_nsec_idle; + uint64_t cs_cpu_nsec_user; + uint64_t cs_cpu_nsec_kernel; + uint64_t cs_cpu_nsec_dtrace; + uint64_t cs_cpu_nsec_intr; } cpustat_t; -typedef struct istat { - list_t is_cpu; - size_t is_ncpu; - hrtime_t is_snaptime; -} istat_t; - -typedef struct delta_cpu { - list_node_t dc_node; - uint64_t dc_intrs; - uint64_t dc_total; - hrtime_t dc_bigintr; - double dc_intrload; -} delta_cpu_t; - -typedef struct delta { - boolean_t d_missing; - hrtime_t d_minsnap; - hrtime_t d_maxsnap; - double d_avg_intrload; - uint64_t d_avg_intrnsec; - list_t d_cpus; - size_t d_ncpus; -} delta_t; - -static istat_t *istat_new(void); -static cpustat_t *cpustat_new(void); -static ivec_t *ivec_new(void); +typedef struct stats { + cpustat_t *sts_cpu; + size_t sts_ncpu; + ivec_t *sts_ivec; + size_t sts_nivec; + boolean_t sts_changed; + hrtime_t sts_mintime; + hrtime_t sts_maxtime; +} stats_t; + +enum { + KITER_NEXT = 0, + KITER_DONE, + KITER_STOP +}; + +typedef int (*kstat_itercb_t)(kstat_ctl_t *restrict, kstat_t *restrict, + void *restrict); +static int kstat_iter(kstat_ctl_t *restrict, kstat_itercb_t, void *restrict); + +static int cpustat_cmp(const void *, const void *); +static int ivec_cmp(const void *, const void *); static char *xstrdup(const char *); +static stats_t *stats_dup(const stats_t *); +static void stats_free(stats_t *); -cpustat_t *get_cpustat(istat_t *, uint64_t); +static stats_t *calc_delta(const stats_t *restrict, const stats_t *restrict); +static stats_t *get_stats(const config_t *restrict, kstat_ctl_t *restrict, + uint_t); +static void loop(const config_t *restrict, kstat_ctl_t *restrict); -static delta_t *generate_delta(istat_t *restrict, istat_t *restrict); -static istat_t *get_stat(kstat_ctl_t *); +static volatile boolean_t quit; + +#ifdef DEBUG +const char * +_umem_debug_init(void) +{ + return ("default,verbose"); +} + +const char * +_umem_logging_init(void) +{ + return ("fail,contents"); +} +#endif + +static int +nomem(void) +{ + (void) fprintf(stderr, "Out of memory\n"); + return (UMEM_CALLBACK_EXIT(255)); +} int main(int argc, char **argv) { kstat_ctl_t *kcp; + config_t cfg = { 0 }; + + umem_nofail_callback(nomem); if ((kcp = kstat_open()) == NULL) err(EXIT_FAILURE, "could not open /dev/kstat"); + loop(&cfg, kcp); + kstat_close(kcp); return (0); } -static delta_t * -generate_delta(istat_t *restrict is, istat_t *restrict isnew) +static void +loop(const config_t *restrict cfg, kstat_ctl_t *restrict kcp) { - delta_t *d = calloc(1, sizeof (*d)); - cpustat_t *cs, *csnew; + const size_t deltas_sz = cfg->cfg_avginterval / cfg->cfg_interval + 1; - if (d == NULL) + stats_t *stats[2] = { 0 }; + stats_t *stp = NULL; + stats_t *delta = NULL; + stats_t **deltas = NULL; + size_t ndeltas = 0; + int gen = 0; + + if ((deltas = calloc(deltas_sz, sizeof (stats_t *))) == NULL) err(EXIT_FAILURE, "calloc failed"); - d->d_minsnap = is->is_snaptime; - d->d_maxsnap = isnew->is_snaptime; - if (d->d_minsnap > d->d_maxsnap) { - syslog(LOG_WARNING, "stats aren't ascending"); - free(d); - return (NULL); + while (!quit) { + uint_t interval = cfg->cfg_interval; + + if ((stp = get_stats(cfg, kcp, interval)) == NULL) { + /* + * Something (cpu, new device, etc) was added while + * we were reading our stats, or we took too long to + * read our stats. Reset and try again. + */ + sleep(cfg->cfg_retry_interval); + continue; + } + + stats_free(stats[gen]); + stats[gen] = stp; + delta = calc_delta(stats[gen], stats[gen ^ 1]); + gen ^= 1; + + if (delta == NULL || delta->sts_changed) + continue; + + sleep(interval); } - if (is->is_ncpu != isnew->is_ncpu) { - syslog(LOG_DEBUG, "number of CPUs has changed"); - free(d); + stats_free(stats[0]); + stats_free(stats[1]); +} + +static stats_t * +calc_delta(const stats_t *restrict st, const stats_t *restrict stprev) +{ + stats_t *delta = NULL; + cpustat_t *cs, *csd; + ivec_t *iv, *ivd; + size_t i; + + if (st->sts_ncpu != stprev->sts_ncpu) + return (NULL); + if (st->sts_nivec != stprev->sts_nivec) return (NULL); + + delta = stats_dup(st); + + cs = stprev->sts_cpu; + csd = delta->sts_cpu; + +#define CS_SUB(field, d, c) \ + if ((d)->cs_ ## field < (c)->cs_ ## field) { \ + syslog(LOG_WARNING, "%s kstat is decreasing", #field); \ + goto fail; \ + } \ + (d)->cs_ ## field -= (c)->cs_ ## field; + + for (i = 0; i < st->sts_ncpu; i++, cs++, csd++) { + VERIFY(cs->cs_online); + VERIFY(csd->cs_online); + + if (csd->cs_cpuid != cs->cs_cpuid) + goto fail; + + if (csd->cs_crtime < cs->cs_crtime) { + syslog(LOG_WARNING, "kstat time is not increasing"); + goto fail; + } + + csd->cs_crtime -= cs->cs_crtime; + CS_SUB(cpu_nsec_idle, csd, cs); + CS_SUB(cpu_nsec_user, csd, cs); + CS_SUB(cpu_nsec_kernel, csd, cs); + CS_SUB(cpu_nsec_dtrace, csd, cs); + CS_SUB(cpu_nsec_intr, csd, cs); + } +#undef CS_SUB + + iv = stprev->sts_ivec; + ivd = delta->sts_ivec; + for (i = 0; i < st->sts_nivec; i++, iv++, ivd++) { + if (ivd->ivec_instance != iv->ivec_instance || + ivd->ivec_cpuid != iv->ivec_cpuid || + ivd->ivec_ino != iv->ivec_ino || + strcmp(ivd->ivec_buspath, iv->ivec_buspath) != 0) + goto fail; + + if (ivd->ivec_crtime < iv->ivec_crtime) { + goto fail; + } + if (ivd->ivec_time < iv->ivec_time) { + goto fail; + } + ivd->ivec_time -= iv->ivec_time; } - for (cs = list_head(&is->is_cpu); cs != NULL; - cs = list_next(&is->is_cpu, cs)) { - csnew = get_cpustat(isnew, cs->cs_cpuid); - if (csnew == NULL) { - free(d); - return (NULL); + return (delta); + +fail: + stats_free(delta); + return (NULL); +} + +static stats_t * +sum_deltas(const stats_t **deltas, size_t n) +{ + return (NULL); +} + +static void +consolidate_ivecs(stats_t *stp) +{} + +struct count_info { + size_t ci_ncpuinfo; + size_t ci_ncpu; + size_t ci_nivec; +}; + +static int +count_kstats(kstat_ctl_t *restrict kcp, kstat_t *restrict ksp, void *arg) +{ + struct count_info *count = arg; + + if (strcmp(ksp->ks_module, "cpu_info") == 0) + count->ci_ncpuinfo++; + else if (strcmp(ksp->ks_module, "cpu") == 0 && + strcmp(ksp->ks_name, "sys") == 0) + count->ci_ncpu++; + else if (strcmp(ksp->ks_module, "ivec") == 0 && + strcmp(ksp->ks_name, "npe") == 0) + count->ci_nivec++; + + return (KITER_NEXT); +} + +struct read_info { + stats_t *ri_statsp; + size_t ri_count; +}; + +static int +do_cpu_info(kstat_ctl_t *restrict kcp, kstat_t *restrict ksp, void *arg) +{ + /* + * Cache the index of the cpu state field in the cpu_info kstat. + */ + static uint_t hint = -1; + + struct read_info *rip = arg; + + if (strcmp(ksp->ks_module, "cpu_info") != 0) + return (KITER_NEXT); + + cpustat_t *cs = rip->ri_statsp->sts_cpu + rip->ri_count++; + + if (kstat_read(kcp, ksp, NULL) == -1) { + if (errno == ENXIO) { + /* + * Our kstat has been removed since the update; + * just ignore and continue. + */ + return (KITER_NEXT); } + err(EXIT_FAILURE, "unable to read kstat %s:%d", + ksp->ks_name, ksp->ks_instance); + } - if (csnew->cs_total < cs->cs_total) { - syslog(LOG_WARNING, "deltas are not ascending"); - free(d); - return (NULL); + VERIFY3S(ksp->ks_type, ==, KSTAT_TYPE_NAMED); + cs->cs_cpuid = ksp->ks_instance; + + kstat_named_t *nm = KSTAT_NAMED_PTR(ksp); + + if (hint == -1) { + for (uint_t i = 0; i < ksp->ks_ndata; i++) { + if (strcmp(nm[i].name, "state") != 0) + continue; + + hint = i; + break; } + } + + VERIFY3S(ksp->ks_instance, >=, 0); + if (strcmp(KSTAT_NAMED_STR_PTR(&nm[hint]), "on-line") == 0) + cs->cs_online = B_TRUE; - if ((d->d_total = csnew->cs_total - cs->cs_total) == 0) - d->d_total = 1; + return (KITER_NEXT); +} +static int +do_cpu(kstat_ctl_t *restrict kcp, kstat_t *restrict ksp, void *restrict arg) +{ + static int idle = -1; + static int kernel = -1; + static int user = -1; + static int dtrace = -1; + static int intr = -1; + + stats_t *stp = arg; + cpustat_t *cs = NULL; + + if (strcmp(ksp->ks_module, "cpu") != 0) + return (KITER_NEXT); + if (strcmp(ksp->ks_name, "sys") != 0) + return (KITER_NEXT); + + for (uint_t i = 0; i < stp->sts_ncpu; i++) { + if (stp->sts_cpu[i].cs_cpuid == ksp->ks_instance) { + cs = &stp->sts_cpu[i]; + break; + } } + + if (cs == NULL) { + stp->sts_changed = B_TRUE; + return (KITER_DONE); + } + + if (kstat_read(kcp, ksp, NULL) == -1) { + if (errno == ENXIO) { + stp->sts_changed = B_TRUE; + return (KITER_DONE); + } + err(EXIT_FAILURE, "unable to read kstat %s:%d", + ksp->ks_name, ksp->ks_instance); + } + + VERIFY3S(ksp->ks_type, ==, KSTAT_TYPE_NAMED); + + kstat_named_t *nm = KSTAT_NAMED_PTR(ksp); + + if (idle == -1) { + for (uint_t i = 0; i < ksp->ks_ndata; i++) { + if (strcmp(nm[i].name, "cpu_nsec_dtrace") == 0) + dtrace = i; + else if (strcmp(nm[i].name, "cpu_nsec_idle") == 0) + idle = i; + else if (strcmp(nm[i].name, "cpu_nsec_intr") == 0) + intr = i; + else if (strcmp(nm[i].name, "cpu_nsec_kernel") == 0) + kernel = i; + else if (strcmp(nm[i].name, "cpu_nsec_user") == 0) + user = i; + } + } + + VERIFY3S(dtrace, >, -1); + VERIFY3S(idle, >, -1); + VERIFY3S(intr, >, -1); + VERIFY3S(kernel, >, -1); + VERIFY3S(user, >, -1); + + cs->cs_crtime = ksp->ks_crtime; + cs->cs_cpu_nsec_idle = nm[idle].value.ui64; + cs->cs_cpu_nsec_user = nm[user].value.ui64; + cs->cs_cpu_nsec_intr = nm[intr].value.ui64; + cs->cs_cpu_nsec_kernel = nm[kernel].value.ui64; + cs->cs_cpu_nsec_dtrace = nm[dtrace].value.ui64; + + return (KITER_NEXT); } -enum { - KSM_UNKNOWN, - KSM_CPUINFO, - KSM_PCIINTR -}; +static int +get_ivecs(kstat_ctl_t *restrict kcp, kstat_t *restrict ksp, void *restrict arg) +{ + static int cookie = -1; + static int cpu = -1; + static int buspath = -1; + static int ino = -1; + static int pil = -1; + static int type = -1; + static int name = -1; + static int f_time = -1; + + struct read_info *rip = arg; + + if (strcmp(ksp->ks_module, "pci_intrs") != 0) + return (KITER_NEXT); + if (strcmp(ksp->ks_name, "npe") != 0) + return (KITER_NEXT); + + ivec_t *ivp = rip->ri_statsp->sts_ivec + rip->ri_count++; + + if (kstat_read(kcp, ksp, NULL) == -1) { + if (errno = ENXIO) { + rip->ri_statsp->sts_changed = B_TRUE; + return (KITER_DONE); + } + err(EXIT_FAILURE, "unable to read kstat %s:%d", + ksp->ks_name, ksp->ks_instance); + } + + VERIFY3S(ksp->ks_type, ==, KSTAT_TYPE_NAMED); + + kstat_named_t *nm = KSTAT_NAMED_PTR(ksp); + + if (cookie == -1) { + for (uint_t i = 0; i < ksp->ks_ndata; i++) { + if (strcmp(nm[i].name, "buspath") == 0) + buspath = i; + else if (strcmp(nm[i].name, "cpu") == 0) + cpu = i; + else if (strcmp(nm[i].name, "cookie") == 0) + cookie = i; + else if (strcmp(nm[i].name, "ino") == 0) + ino = i; + else if (strcmp(nm[i].name, "pil") == 0) + pil = i; + else if (strcmp(nm[i].name, "type") == 0) + type = i; + else if (strcmp(nm[i].name, "name") == 0) + name = i; + else if (strcmp(nm[i].name, "time") == 0) + f_time = i; + } + } -static istat_t * -get_stat(kstat_ctl_t *kcp) + VERIFY3S(cookie, >, -1); + VERIFY3S(cpu, >, -1); + VERIFY3S(buspath, >, -1); + VERIFY3S(ino, >, -1); + VERIFY3S(pil, >, -1); + VERIFY3S(type, >, -1); + VERIFY3S(name, >, -1); + VERIFY3S(f_time, >, -1); + + ivp->ivec_instance = ksp->ks_instance; + ivp->ivec_crtime = ksp->ks_snaptime; + ivp->ivec_cookie = nm[cookie].value.ui64; + ivp->ivec_pil = nm[pil].value.ui64; + ivp->ivec_ino = nm[ino].value.ui64; + VERIFY3U(nm[ino].value.ui64, <=, INT_MAX); + ivp->ivec_cpuid = (int)nm[cpu].value.ui64; + ivp->ivec_time = nm[f_time].value.ui64; + ivp->ivec_buspath = xstrdup(KSTAT_NAMED_STR_PTR(&nm[buspath])); + (void) strlcpy(ivp->ivec_name, nm[name].value.c, + sizeof (ivp->ivec_name)); + (void) strlcpy(ivp->ivec_type, nm[type].value.c, + sizeof (ivp->ivec_type)); + + return (KITER_NEXT); +} + +static stats_t * +get_stats(const config_t *restrict cfg, kstat_ctl_t *restrict kcp, + uint_t interval) { - istat_t *is; + stats_t *sts; kstat_t *ksp; kid_t kid; @@ -151,82 +505,245 @@ get_stat(kstat_ctl_t *kcp) if (kid == -1) err(EXIT_FAILURE, "failed to update kstat chain"); - is = istat_new(); + struct count_info count = { 0 }; + + (void) kstat_iter(kcp, count_kstats, &count); + if (count.ci_ncpuinfo != count.ci_ncpu) { + return (NULL); + } + + if ((sts = calloc(1, sizeof (*sts))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + + if ((sts->sts_cpu = calloc(count.ci_ncpu, sizeof (cpustat_t))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + sts->sts_ncpu = count.ci_ncpu; + + if ((sts->sts_ivec = calloc(count.ci_nivec, sizeof (ivec_t))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + sts->sts_nivec = count.ci_nivec; + + sts->sts_mintime = INT64_MAX; + sts->sts_maxtime = INT64_MIN; + + struct read_info read_info = { + .ri_statsp = sts, + .ri_count = 0, + }; + (void) kstat_iter(kcp, do_cpu_info, &read_info); + (void) kstat_iter(kcp, do_cpu, &read_info); + + read_info.ri_count = 0; + (void) kstat_iter(kcp, get_ivecs, &read_info); + + if (sts->sts_changed) { + stats_free(sts); + return (NULL); + } - for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) { - int which = KSM_UNKNOWN; + qsort(sts->sts_cpu, sts->sts_ncpu, sizeof (cpustat_t), cpustat_cmp); + qsort(sts->sts_ivec, sts->sts_nivec, sizeof (ivec_t), ivec_cmp); - if (strcmp(ksp->ks_module, "cpu_info") == 0) - which = KSM_CPUINFO; - else if (strcmp(ksp->ks_module, "pci_intrs") == 0) - which = KSM_PCIINTR; + size_t i; - if (which == KSM_UNKNOWN) + /* + * Offline CPUs should be sorted to the end of the list. If we + * encounter an offline CPU, truncate the list at that point and + * stop. + */ + for (i = 0; i < sts->sts_ncpu; i++) { + if (sts->sts_cpu[i].cs_online) continue; + if (i == 0) { + syslog(LOG_WARNING, "all cpus are reporting offline"); + stats_free(sts); + return (NULL); + } + + sts->sts_cpu = realloc_array(sts->sts_cpu, i, + sizeof (cpustat_t)); + VERIFY3P(sts->sts_cpu, !=, NULL); + sts->sts_ncpu = i; + break; + } + + /* + * Similarly for ivecs, disabled ones are sorted last. If we + * encounter a disabled ivec, truncate the list at that point. + */ + for (i = 0; i < sts->sts_nivec; i++) { + if (strcmp(sts->sts_ivec[i].ivec_type, "disabled") != 0) + continue; + + if (i == 0) { + syslog(LOG_WARNING, "all interrupts are reporting " + "disabled"); + stats_free(sts); + return (NULL); + } + + sts->sts_nivec = realloc_array(sts->sts_ivec, i, + sizeof (ivec_t)); + VERIFY3P(sts->sts_ivec, !=, NULL); + sts->sts_nivec = i; + break; + } + + for (i = 0; i < sts->sts_ncpu; i++) { + if (sts->sts_cpu[i].cs_crtime < sts->sts_mintime) + sts->sts_mintime = sts->sts_cpu[i].cs_crtime; + if (sts->sts_cpu[i].cs_crtime > sts->sts_maxtime) + sts->sts_maxtime = sts->sts_cpu[i].cs_crtime; + } + for (i = 0; i < sts->sts_nivec; i++) { + if (sts->sts_ivec[i].ivec_crtime < sts->sts_mintime) + sts->sts_mintime = sts->sts_ivec[i].ivec_crtime; + if (sts->sts_ivec[i].ivec_crtime > sts->sts_maxtime) + sts->sts_maxtime = sts->sts_ivec[i].ivec_crtime; + } + hrtime_t timediff = sts->sts_maxtime - sts->sts_mintime; + if ((double)timediff / interval > cfg->cfg_tooslow) { + stats_free(sts); + return (NULL); } + + return (sts); } -cpustat_t * -get_cpustat(istat_t *is, uint64_t id) +static int +kstat_iter(kstat_ctl_t *restrict kcp, kstat_itercb_t cb, void *restrict arg) { - cpustat_t *cs; + int ret = KITER_DONE; - for (cs = list_head(&is->is_cpu); cs != NULL; - cs = list_next(&is->is_cpu, cs)) { - if (cs->cs_cpuid == id) - return (cs); + for (kstat_t *ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) { + if ((ret = cb(kcp, ksp, arg)) != KITER_NEXT) + return (ret); } - return (NULL); + return (ret); +} + +static char * +xstrdup(const char *s) +{ + char *p = strdup(s); + + if (p == NULL) + err(EXIT_FAILURE, "strdup failed"); + return (p); } -static istat_t * -istat_new(void) +static stats_t * +stats_dup(const stats_t *src) { - istat_t *is = calloc(1, sizeof (*is)); + stats_t *stp; - if (is == NULL) + if ((stp = calloc(1, sizeof (*stp))) == NULL) err(EXIT_FAILURE, "calloc failed"); - list_create(&is->is_cpu, sizeof (cpustat_t), - offsetof (cpustat_t, cs_node)); + stp->sts_ncpu = src->sts_ncpu; + stp->sts_nivec = src->sts_nivec; + stp->sts_cpu = calloc(stp->sts_ncpu, sizeof (cpustat_t)); + stp->sts_ivec = calloc(stp->sts_nivec, sizeof (ivec_t)); + if (stp->sts_cpu == NULL || stp->sts_ivec == NULL) + err(EXIT_FAILURE, "calloc failed"); - return (is); + bcopy(src->sts_cpu, stp->sts_cpu, src->sts_ncpu * sizeof (cpustat_t)); + bcopy(src->sts_ivec, stp->sts_ivec, src->sts_nivec * sizeof (ivec_t)); + + ivec_t *iv; + size_t i; + for (i = 0, iv = stp->sts_ivec; i < stp->sts_nivec; i++, iv++) { + iv->ivec_buspath = strdup(src->sts_ivec[i].ivec_buspath); + if (iv->ivec_buspath == NULL) + err(EXIT_FAILURE, "strdup failed"); + } + + return (stp); } -static cpustat_t * -cpustat_new(void) +static void +stats_free(stats_t *stp) { - cpustat_t *cs = calloc(1, sizeof (*cs)); + if (stp == NULL) + return; + free(stp->sts_cpu); + free(stp->sts_ivec); + free(stp); +} - if (cs == NULL) - err(EXIT_FAILURE, "calloc failed"); +/* + * sort by: online/!online, cpuid + */ +static int +cpustat_cmp(const void *a, const void *b) +{ + const cpustat_t *l = a; + const cpustat_t *r = b; + + if (l->cs_online != r->cs_online) { + if (!l->cs_online) + return (1); + if (!r->cs_online) + return (-1); + } - list_create(&cs->cs_ivecs, sizeof (ivec_t), - offsetof(ivec_t, ivec_node)); + if (l->cs_cpuid < r->cs_cpuid) + return (-1); + if (l->cs_cpuid > r->cs_cpuid) + return (1); - return (cs); + return (0); } -static ivec_t * -ivec_new(void) +/* + * sort by: !disabled/disabled, instance + */ +static int +ivec_cmp(const void *a, const void *b) { - ivec_t *iv = calloc(1, sizeof (*iv)); + const ivec_t *l = a; + const ivec_t *r = b; + + if (strcmp(l->ivec_type, r->ivec_type) != 0) { + if (strcmp(l->ivec_type, "disabled") == 0) + return (1); + if (strcmp(r->ivec_type, "disabled") == 0) + return (-1); + } - if (iv == NULL) - err(EXIT_FAILURE, "calloc failed"); + if (l->ivec_instance < r->ivec_instance) + return (-1); + if (l->ivec_instance > r->ivec_instance) + return (1); - return (iv); + return (0); } -static char * -xstrdup(const char *s) +/* + * sort by cpu, buspath, interrupt number (ino) + */ +static int +ivec_cmp2(const void *a, const void *b) { - char *p = strdup(s); + const ivec_t *l = a; + const ivec_t *r = b; + int ret; - if (p == NULL) - err(EXIT_FAILURE, "strdup failed"); - return (p); + if (l->ivec_cpuid < r->ivec_cpuid) + return (-1); + if (l->ivec_cpuid > r->ivec_cpuid) + return (1); + + if ((ret = strcmp(l->ivec_buspath, r->ivec_buspath)) != 0) + return ((ret != 1) ? -1 : 1); + + if (l->ivec_ino < r->ivec_ino) + return (-1); + if (l->ivec_ino > r->ivec_ino) + return (1); + + return (0); } |