Add a JSON equivalent to zpool-status(8)
This is a squashed commit of the commits from 03a64568f318c696b9e4be19429e72b446c97462 to 1c64f0c8832b34bfa82645125351d6c62815ae21 developed by Fred Weigel. Usage: cat /proc/spl/kstat/zfs/POOLNAME/stats The following changes has been applied during the rebase of the patches on top of the 2.1.5 branch: - Drop ZFS_IOC_ADD_LOG. This ioctl was introduced to support introducing messages into the ZFS kernel log. It was used for debugging during development. The implementation of this debugging feature made `zpool addlog` output messages to /proc/spl/kstat/zfs/dbgmsg. The messages could later be retrieved with `zdbgmsg show`. - Change the fmgw.c entry in lib/libzpool/Makefile.am to json_stats.c. The fmgw.c file has already been renamed to json_stats.c in other places. Co-authored-by: Mateusz Piotrowski <mateusz.piotrowski@klarasystems.com> (cherry picked from commit 75f3395d7fc0c93c02c8a8e792515f3e821aa05a)
This commit is contained in:
parent
18ae26747c
commit
747c7bbcf6
|
@ -68,3 +68,6 @@ venv
|
|||
*.so
|
||||
*.so.debug
|
||||
*.so.full
|
||||
|
||||
cscope.out
|
||||
*,v
|
||||
|
|
|
@ -129,6 +129,9 @@ static int zpool_do_wait(int, char **);
|
|||
static zpool_compat_status_t zpool_do_load_compat(
|
||||
const char *, boolean_t *);
|
||||
|
||||
/* fmgw - added to allow injection into zfs kernel log */
|
||||
static int zpool_do_addlog(int, char **);
|
||||
|
||||
/*
|
||||
* These libumem hooks provide a reasonable set of defaults for the allocator's
|
||||
* debugging facilities.
|
||||
|
@ -162,6 +165,7 @@ typedef enum {
|
|||
HELP_IOSTAT,
|
||||
HELP_LABELCLEAR,
|
||||
HELP_LIST,
|
||||
HELP_ADDLOG,
|
||||
HELP_OFFLINE,
|
||||
HELP_ONLINE,
|
||||
HELP_REPLACE,
|
||||
|
@ -290,6 +294,7 @@ static zpool_command_t command_table[] = {
|
|||
{ "labelclear", zpool_do_labelclear, HELP_LABELCLEAR },
|
||||
{ NULL },
|
||||
{ "checkpoint", zpool_do_checkpoint, HELP_CHECKPOINT },
|
||||
{ "addlog", zpool_do_addlog, HELP_ADDLOG },
|
||||
{ NULL },
|
||||
{ "list", zpool_do_list, HELP_LIST },
|
||||
{ "iostat", zpool_do_iostat, HELP_IOSTAT },
|
||||
|
@ -380,6 +385,9 @@ get_usage(zpool_help_t idx)
|
|||
return (gettext("\tlist [-gHLpPv] [-o property[,...]] "
|
||||
"[-T d|u] [pool] ... \n"
|
||||
"\t [interval [count]]\n"));
|
||||
case HELP_ADDLOG:
|
||||
return (gettext("\taddlog [-m message]\n"
|
||||
"\tmessage <= 254 characters\n"));
|
||||
case HELP_OFFLINE:
|
||||
return (gettext("\toffline [-f] [-t] <pool> <device> ...\n"));
|
||||
case HELP_ONLINE:
|
||||
|
@ -3430,6 +3438,46 @@ zpool_do_checkpoint(int argc, char **argv)
|
|||
return (err);
|
||||
}
|
||||
|
||||
static int zpool_addlog(char *msg)
|
||||
{
|
||||
int ret;
|
||||
zfs_cmd_t zc = {"\0"};
|
||||
|
||||
if (msg == NULL)
|
||||
msg = (char *)"---EMPTY---";
|
||||
(void) strlcpy(zc.zc_name, msg, sizeof (zc.zc_name));
|
||||
ret = zfs_ioctl(g_zfs, ZFS_IOC_ADD_LOG, &zc);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int zpool_do_addlog(int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
char *msg = NULL;
|
||||
int err = 0;
|
||||
|
||||
while ((c = getopt(argc, argv, "m:")) != -1) {
|
||||
switch (c) {
|
||||
case 'm':
|
||||
msg = optarg;
|
||||
break;
|
||||
case ':':
|
||||
(void) fprintf(stderr,
|
||||
gettext("missing argument for "
|
||||
"'%c' option\n"), optopt);
|
||||
usage(B_FALSE);
|
||||
break;
|
||||
case '?':
|
||||
(void) fprintf(stderr,
|
||||
gettext("invalid option '%c'\n"),
|
||||
optopt);
|
||||
usage(B_FALSE);
|
||||
}
|
||||
}
|
||||
err = zpool_addlog(msg);
|
||||
return (err);
|
||||
}
|
||||
|
||||
#define CHECKPOINT_OPT 1024
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* jprint.h
|
||||
*/
|
||||
|
||||
/* If in ZFS <sys/zfs_context.h> provides what is needed */
|
||||
|
||||
/* If standalone */
|
||||
/* #include <stdbool.h> */
|
||||
/* #define boolean_t bool */
|
||||
/* #define B_TRUE true */
|
||||
/* #define B_FALSE false */
|
||||
/* #include <stddef.h> */
|
||||
/* #include <stdint.h> */
|
||||
/* #include <stdio.h> */
|
||||
/* #include <stdarg.h> */
|
||||
/* #include <string.h> */
|
||||
/* #include <inttypes.h> */
|
||||
|
||||
/* maximum stack nesting */
|
||||
#define JP_MAX_STACK 32
|
||||
|
||||
enum jp_type {
|
||||
JP_OBJECT = 1,
|
||||
JP_ARRAY
|
||||
};
|
||||
|
||||
struct jp_stack {
|
||||
enum jp_type type;
|
||||
int nelem;
|
||||
};
|
||||
|
||||
typedef struct jprint {
|
||||
char *buffer; /* pointer to application's buffer */
|
||||
size_t buflen; /* length of buffer */
|
||||
char *bufp; /* current write position in buffer */
|
||||
char tmpbuf[32]; /* local buffer for conversions */
|
||||
int error; /* error code */
|
||||
int ncall; /* API call number on which error occurred */
|
||||
struct jp_stack /* stack of array/object nodes */
|
||||
stack[JP_MAX_STACK];
|
||||
int stackp;
|
||||
} jprint_t;
|
||||
|
||||
/* error return codes */
|
||||
#define JPRINT_OK 0 /* no error */
|
||||
#define JPRINT_BUF_FULL 1 /* output buffer full */
|
||||
#define JPRINT_NEST_ERROR 2 /* nesting error */
|
||||
#define JPRINT_STACK_FULL 3 /* array/object nesting */
|
||||
#define JPRINT_STACK_EMPTY 4 /* stack underflow error */
|
||||
#define JPRINT_OPEN 5 /* not all objects closed */
|
||||
#define JPRINT_FMT 6 /* format error */
|
||||
#define JPRINT_NO_DOUBLE 7 /* %g support not included */
|
||||
|
||||
const char *jp_errorstring(int err);
|
||||
int jp_error(jprint_t *jp);
|
||||
void jp_open(jprint_t *jp, char *buffer, size_t buflen);
|
||||
int jp_close(jprint_t *jp);
|
||||
int jp_errorpos(jprint_t *jp);
|
||||
int jp_printf(jprint_t *jp, const char *fmt, ...);
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* json_stats.h
|
||||
*/
|
||||
|
||||
void json_stats_destroy(spa_t *spa);
|
||||
void json_stats_init(spa_t *spa);
|
||||
|
|
@ -791,6 +791,9 @@ extern void spa_scan_stat_init(spa_t *spa);
|
|||
extern int spa_scan_get_stats(spa_t *spa, pool_scan_stat_t *ps);
|
||||
extern int bpobj_enqueue_alloc_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx);
|
||||
extern int bpobj_enqueue_free_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx);
|
||||
extern void spa_add_spares(spa_t *spa, nvlist_t *config);
|
||||
extern void spa_add_l2cache(spa_t *spa, nvlist_t *config);
|
||||
extern void spa_add_feature_stats(spa_t *spa, nvlist_t *config);
|
||||
|
||||
#define SPA_ASYNC_CONFIG_UPDATE 0x01
|
||||
#define SPA_ASYNC_REMOVE 0x02
|
||||
|
@ -921,6 +924,11 @@ typedef struct spa_stats {
|
|||
spa_history_kstat_t iostats;
|
||||
} spa_stats_t;
|
||||
|
||||
typedef struct json_stats {
|
||||
kmutex_t lock;
|
||||
spa_history_kstat_t kstat;
|
||||
} json_stats_t;
|
||||
|
||||
typedef enum txg_state {
|
||||
TXG_STATE_BIRTH = 0,
|
||||
TXG_STATE_OPEN = 1,
|
||||
|
|
|
@ -406,6 +406,7 @@ struct spa {
|
|||
uint64_t spa_autotrim; /* automatic background trim? */
|
||||
uint64_t spa_errata; /* errata issues detected */
|
||||
spa_stats_t spa_stats; /* assorted spa statistics */
|
||||
json_stats_t spa_json_stats; /* json stats */
|
||||
spa_keystore_t spa_keystore; /* loaded crypto keys */
|
||||
|
||||
/* arc_memory_throttle() parameters during low memory condition */
|
||||
|
|
|
@ -93,6 +93,8 @@ KERNEL_C = \
|
|||
hkdf.c \
|
||||
fm.c \
|
||||
gzip.c \
|
||||
jprint.c \
|
||||
json_stats.c \
|
||||
lzjb.c \
|
||||
lz4.c \
|
||||
metaslab.c \
|
||||
|
|
|
@ -43,5 +43,4 @@ endif
|
|||
|
||||
subdir-asflags-y := $(ZFS_MODULE_CFLAGS) $(ZFS_MODULE_CPPFLAGS)
|
||||
subdir-ccflags-y := $(ZFS_MODULE_CFLAGS) $(ZFS_MODULE_CPPFLAGS)
|
||||
|
||||
endif
|
||||
|
|
|
@ -243,6 +243,8 @@ SRCS+= abd.c \
|
|||
spa_log_spacemap.c \
|
||||
spa_misc.c \
|
||||
spa_stats.c \
|
||||
json_stats.c \
|
||||
jprint.c \
|
||||
space_map.c \
|
||||
space_reftree.c \
|
||||
txg.c \
|
||||
|
|
|
@ -722,6 +722,7 @@ dsl_scan_setup_sync(void *arg, dmu_tx_t *tx)
|
|||
dsl_pool_t *dp = scn->scn_dp;
|
||||
spa_t *spa = dp->dp_spa;
|
||||
|
||||
|
||||
ASSERT(!dsl_scan_is_running(scn));
|
||||
ASSERT(*funcp > POOL_SCAN_NONE && *funcp < POOL_SCAN_FUNCS);
|
||||
bzero(&scn->scn_phys, sizeof (scn->scn_phys));
|
||||
|
@ -1963,11 +1964,13 @@ dsl_scan_visitbp(blkptr_t *bp, const zbookmark_phys_t *zb,
|
|||
dsl_pool_t *dp = scn->scn_dp;
|
||||
blkptr_t *bp_toread = NULL;
|
||||
|
||||
if (dsl_scan_check_suspend(scn, zb))
|
||||
if (dsl_scan_check_suspend(scn, zb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dsl_scan_check_resume(scn, dnp, zb))
|
||||
if (dsl_scan_check_resume(scn, dnp, zb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
scn->scn_visited_this_txg++;
|
||||
|
||||
|
|
|
@ -0,0 +1,446 @@
|
|||
/*
|
||||
* jprint.c
|
||||
*/
|
||||
|
||||
/* If in ZFS */
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/jprint.h>
|
||||
|
||||
/* If standalone */
|
||||
/* #include "jprint.h" */
|
||||
|
||||
/* Do not support %g format. Just %d and %l for integers (if set) */
|
||||
#define NO_DOUBLE 1
|
||||
|
||||
/* Use %g instead of %e for double format */
|
||||
#define USE_G 1
|
||||
|
||||
/* Formats for int64_t and uint64_t */
|
||||
#ifndef PRId64
|
||||
#define PRId64 "lld" /* %D, int64_t */
|
||||
#define PRIu64 "llu" /* %U, uint64_t*/
|
||||
#endif
|
||||
|
||||
/* literal key length maximum */
|
||||
#define KEYLEN 255
|
||||
|
||||
/* return error position (call number of jp_printf) */
|
||||
int
|
||||
jp_errorpos(jprint_t *jp)
|
||||
{
|
||||
return (jp->ncall);
|
||||
}
|
||||
|
||||
/* return string for error code */
|
||||
const char *
|
||||
jp_errorstring(int err)
|
||||
{
|
||||
switch (err) {
|
||||
case JPRINT_OK: return "jprint ok";
|
||||
case JPRINT_BUF_FULL: return "jprint buffer full";
|
||||
case JPRINT_NEST_ERROR: return "jprint nest error";
|
||||
case JPRINT_STACK_FULL: return "jprint stack full";
|
||||
case JPRINT_STACK_EMPTY: return "jprint stack empty";
|
||||
case JPRINT_OPEN: return "jprint open";
|
||||
case JPRINT_FMT: return "jprint format";
|
||||
case JPRINT_NO_DOUBLE: return "jprint no double support";
|
||||
default: return "jprint unknown error";
|
||||
}
|
||||
return "jprint unknown error";
|
||||
}
|
||||
|
||||
/* return error from jprint_t */
|
||||
int
|
||||
jp_error(jprint_t *jp)
|
||||
{
|
||||
return (jp->error);
|
||||
}
|
||||
|
||||
/* open json using buffer of length buflen */
|
||||
void
|
||||
jp_open(jprint_t *jp, char *buffer, size_t buflen)
|
||||
{
|
||||
jp->buffer = jp->bufp = buffer;
|
||||
jp->buflen = buflen;
|
||||
jp->error = JPRINT_OK;
|
||||
jp->ncall = 0;
|
||||
jp->stackp = -1;
|
||||
*buffer = '\0';
|
||||
}
|
||||
|
||||
|
||||
/* close json (return out of memory error) */
|
||||
int
|
||||
jp_close(jprint_t *jp)
|
||||
{
|
||||
if (jp->error != JPRINT_OK)
|
||||
return (jp->error);
|
||||
if (jp->stackp != -1)
|
||||
jp->error = JPRINT_OPEN;
|
||||
return (jp->error);
|
||||
}
|
||||
|
||||
|
||||
/* put character to json */
|
||||
static void
|
||||
jp_putc(jprint_t *jp, char c)
|
||||
{
|
||||
if (jp->error == JPRINT_OK) {
|
||||
if ((jp->bufp - jp->buffer + 1) >= jp->buflen)
|
||||
jp->error = JPRINT_BUF_FULL;
|
||||
else {
|
||||
*jp->bufp++ = c;
|
||||
*jp->bufp = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* put string to json */
|
||||
static void
|
||||
jp_puts(jprint_t *jp, char *s)
|
||||
{
|
||||
while (*s && (jp->error == JPRINT_OK))
|
||||
jp_putc(jp, *s++);
|
||||
}
|
||||
|
||||
|
||||
/* put quoted string to json */
|
||||
static void
|
||||
jp_putsq(jprint_t *jp, char *s)
|
||||
{
|
||||
static const char *hex = "0123456789ABCDEF";
|
||||
int c;
|
||||
|
||||
if (s == NULL) {
|
||||
jp_puts(jp, (char *)"null");
|
||||
return;
|
||||
}
|
||||
jp_putc(jp, '\"');
|
||||
while (*s && (jp->error == JPRINT_OK)) {
|
||||
c = (int)*s++;
|
||||
/* formfeed, newline, return, tab, backspace */
|
||||
if (c == 12)
|
||||
jp_puts(jp, (char *)"\\f");
|
||||
else if (c == 10)
|
||||
jp_puts(jp, (char *)"\\n");
|
||||
else if (c == 13)
|
||||
jp_puts(jp, (char *)"\\r");
|
||||
else if (c == 9)
|
||||
jp_puts(jp, (char *)"\\t");
|
||||
else if (c == 8)
|
||||
jp_puts(jp, (char *)"\\b");
|
||||
/*
|
||||
* all characters from 0x00 to 0x1f, and 0x7f are
|
||||
* escaped as: \u00xx
|
||||
*/
|
||||
else if (((0 <= c) && (c <= 0x1f)) || (c == 0x7f)) {
|
||||
jp_puts(jp, (char *)"\\u00");
|
||||
jp_putc(jp, hex[(c >> 4) & 0x0f]);
|
||||
jp_putc(jp, hex[c & 0x0f]);
|
||||
/* * " \ / */
|
||||
} else if (c == '"')
|
||||
jp_puts(jp, (char *)"\\\"");
|
||||
else if (c == '\\')
|
||||
jp_puts(jp, (char *)"\\\\");
|
||||
else if (c == '/')
|
||||
jp_puts(jp, (char *)"\\/");
|
||||
/*
|
||||
* all other printable characters ' ' to '~', and
|
||||
* any utf-8 sequences (high bit set):
|
||||
* 1xxxxxxx 10xxxxxx ...
|
||||
* is a utf-8 sequence (10xxxxxx may occur 1 to 3 times).
|
||||
* Note that this is simply distinguished here as high
|
||||
* bit set.
|
||||
*/
|
||||
else
|
||||
jp_putc(jp, (char)c);
|
||||
}
|
||||
jp_putc(jp, '\"');
|
||||
}
|
||||
|
||||
|
||||
/* put out key if object open. error if nothing open */
|
||||
static int
|
||||
jp_key(jprint_t *jp, char *key)
|
||||
{
|
||||
if (jp->error != JPRINT_OK)
|
||||
goto err;
|
||||
/* at top level, no frame exists yet, no error */
|
||||
if (jp->stackp == -1)
|
||||
goto err;
|
||||
/* stackp has been "popped" too many times */
|
||||
if (jp->stackp < -1) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
goto err;
|
||||
}
|
||||
/* put comma separator in (both object and array) */
|
||||
if (++jp->stack[jp->stackp].nelem > 1)
|
||||
jp_putc(jp, ',');
|
||||
/* if its in an object, put out the key and separator */
|
||||
if (jp->stack[jp->stackp].type == JP_OBJECT) {
|
||||
jp_putsq(jp, key);
|
||||
jp_putc(jp, ':');
|
||||
}
|
||||
err:
|
||||
return (jp->error);
|
||||
}
|
||||
|
||||
|
||||
/* printf to json */
|
||||
int
|
||||
jp_printf(jprint_t *jp, const char *fmt, ...)
|
||||
{
|
||||
char key[KEYLEN + 1];
|
||||
int k, i;
|
||||
va_list ap;
|
||||
int n;
|
||||
unsigned int u;
|
||||
int64_t n64;
|
||||
uint64_t u64;
|
||||
boolean_t b;
|
||||
char *s;
|
||||
char *start = jp->bufp;
|
||||
|
||||
if (jp->error != JPRINT_OK)
|
||||
return (-1);
|
||||
++jp->ncall;
|
||||
va_start(ap, fmt);
|
||||
key[k = 0] = '\0';
|
||||
while (*fmt && (jp->error == JPRINT_OK)) {
|
||||
switch (*fmt) {
|
||||
case '%':
|
||||
++fmt;
|
||||
switch (*fmt) {
|
||||
case 'k': /* next parameter is key */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
s = va_arg(ap, char *);
|
||||
if (strlen(s) <= KEYLEN)
|
||||
strcpy(key, s);
|
||||
else
|
||||
jp->error = JPRINT_FMT;
|
||||
break;
|
||||
case 'd': /* next parameter is int */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
n = va_arg(ap, int);
|
||||
i = snprintf(
|
||||
jp->tmpbuf, sizeof (jp->tmpbuf),
|
||||
"%d", n);
|
||||
if (jp_key(jp, key) == JPRINT_OK) {
|
||||
if ((i >= sizeof (jp->tmpbuf)) ||
|
||||
(i < 0))
|
||||
jp_puts(jp, (char *)"####");
|
||||
else
|
||||
jp_puts(jp, jp->tmpbuf);
|
||||
}
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case 'u': /* next parameter is unsigned int */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
u = va_arg(ap, unsigned int);
|
||||
i = snprintf(
|
||||
jp->tmpbuf, sizeof (jp->tmpbuf),
|
||||
"%u", u);
|
||||
if (jp_key(jp, key) == JPRINT_OK) {
|
||||
if ((i >= sizeof (jp->tmpbuf)) ||
|
||||
(i < 0))
|
||||
jp_puts(jp, (char *)"####");
|
||||
else
|
||||
jp_puts(jp, jp->tmpbuf);
|
||||
}
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case 'U': /* next parameter is uint64_t */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
u64 = va_arg(ap, uint64_t);
|
||||
i = snprintf(
|
||||
jp->tmpbuf, sizeof (jp->tmpbuf),
|
||||
"%" PRIu64, u64);
|
||||
if (jp_key(jp, key) == JPRINT_OK) {
|
||||
if ((i >= sizeof (jp->tmpbuf)) ||
|
||||
(i < 0))
|
||||
jp_puts(jp, (char *)"####");
|
||||
else
|
||||
jp_puts(jp, jp->tmpbuf);
|
||||
}
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case 'D': /* next parameter is int64_t */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
n64 = va_arg(ap, int64_t);
|
||||
i = snprintf(
|
||||
jp->tmpbuf, sizeof (jp->tmpbuf),
|
||||
"%" PRId64, n64);
|
||||
if (jp_key(jp, key) == JPRINT_OK) {
|
||||
if ((i >= sizeof (jp->tmpbuf)) ||
|
||||
(i < 0))
|
||||
jp_puts(jp, (char *)"####");
|
||||
else
|
||||
jp_puts(jp, jp->tmpbuf);
|
||||
}
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case 's': /* next parameter is string */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
s = va_arg(ap, char *);
|
||||
if (jp_key(jp, key) == JPRINT_OK)
|
||||
jp_putsq(jp, s);
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case 'g': /* next parameter is double */
|
||||
#if NO_DOUBLE
|
||||
jp->error = JPRINT_NO_DOUBLE;
|
||||
#else
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
double x;
|
||||
x = va_arg(ap, double);
|
||||
if (jp_key(jp, key) == JPRINT_OK) {
|
||||
#if USE_G
|
||||
/*
|
||||
* if we have functional %g format,
|
||||
* use it.
|
||||
*/
|
||||
i = snprintf(
|
||||
jp->tmpbuf, sizeof (jp->tmpbuf),
|
||||
"%g", x);
|
||||
#else
|
||||
/*
|
||||
* double has 15 places:
|
||||
* 1.<14 digits>e-308
|
||||
*/
|
||||
i = snprintf(
|
||||
jp->tmpbuf, sizeof (jp->tmpbuf),
|
||||
"%21.14e", x);
|
||||
#endif
|
||||
if ((i >= sizeof (jp->tmpbuf)) ||
|
||||
(i < 0))
|
||||
jp_puts(jp, (char *)"####");
|
||||
else
|
||||
jp_puts(jp, jp->tmpbuf);
|
||||
}
|
||||
#endif
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case 'b': /* next parameter is boolean */
|
||||
if (jp->stackp < 0) {
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
break;
|
||||
}
|
||||
if (jp_key(jp, key) == JPRINT_OK) {
|
||||
b = (boolean_t)va_arg(ap, int);
|
||||
s = b ? (char *)"true" :
|
||||
(char *)"false";
|
||||
jp_puts(jp, s);
|
||||
}
|
||||
key[k = 0] = '\0';
|
||||
break;
|
||||
case '%': /* literal % */
|
||||
if (k < KEYLEN) {
|
||||
key[k++] = '%';
|
||||
key[k] = '\0';
|
||||
} else
|
||||
jp->error = JPRINT_FMT;
|
||||
break;
|
||||
default:
|
||||
jp->error = JPRINT_FMT;
|
||||
}
|
||||
break;
|
||||
case '{': /* open object */
|
||||
if (jp->stackp >= (JP_MAX_STACK - 1))
|
||||
jp->error = JPRINT_STACK_FULL;
|
||||
else {
|
||||
(void) jp_key(jp, key);
|
||||
++jp->stackp;
|
||||
jp->stack[jp->stackp].type = JP_OBJECT;
|
||||
jp->stack[jp->stackp].nelem = 0;
|
||||
jp_putc(jp, '{');
|
||||
}
|
||||
break;
|
||||
case '}': /* close object */
|
||||
if (jp->stackp < 0)
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
else if (jp->stack[jp->stackp].type != JP_OBJECT)
|
||||
jp->error = JPRINT_NEST_ERROR;
|
||||
else {
|
||||
--jp->stackp;
|
||||
jp_putc(jp, '}');
|
||||
}
|
||||
break;
|
||||
case '[': /* open array */
|
||||
if (jp->stackp >= (JP_MAX_STACK - 1))
|
||||
jp->error = JPRINT_STACK_FULL;
|
||||
else {
|
||||
(void) jp_key(jp, key);
|
||||
++jp->stackp;
|
||||
jp->stack[jp->stackp].type = JP_ARRAY;
|
||||
jp->stack[jp->stackp].nelem = 0;
|
||||
jp_putc(jp, '[');
|
||||
}
|
||||
break;
|
||||
case ']': /* close array */
|
||||
if (jp->stackp < 0)
|
||||
jp->error = JPRINT_STACK_EMPTY;
|
||||
else if (jp->stack[jp->stackp].type != JP_ARRAY)
|
||||
jp->error = JPRINT_NEST_ERROR;
|
||||
else {
|
||||
--jp->stackp;
|
||||
jp_putc(jp, ']');
|
||||
}
|
||||
break;
|
||||
|
||||
case ',': /* ,: space tab are ignored */
|
||||
case ':':
|
||||
case ' ':
|
||||
case '\t':
|
||||
break;
|
||||
case '\\':
|
||||
/* allow inclusion of ,: space tab to key */
|
||||
if (fmt[1] == '\0')
|
||||
jp->error = JPRINT_FMT;
|
||||
else {
|
||||
++fmt;
|
||||
if (k < KEYLEN) {
|
||||
key[k++] = *fmt;
|
||||
key[k] = '\0';
|
||||
} else
|
||||
jp->error = JPRINT_FMT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (k < KEYLEN) {
|
||||
key[k++] = *fmt;
|
||||
key[k] = '\0';
|
||||
} else
|
||||
jp->error = JPRINT_FMT;
|
||||
break;
|
||||
}
|
||||
++fmt;
|
||||
}
|
||||
va_end(ap);
|
||||
if (jp->error != JPRINT_OK)
|
||||
return (-1);
|
||||
|
||||
return (int)(jp->bufp - start);
|
||||
}
|
||||
|
|
@ -0,0 +1,467 @@
|
|||
/*
|
||||
* json_stats.c
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/spa_impl.h>
|
||||
#include <sys/vdev_impl.h>
|
||||
#include <sys/spa.h>
|
||||
#include <zfs_comutil.h>
|
||||
#include <sys/jprint.h>
|
||||
#include <sys/json_stats.h>
|
||||
#include <sys/nvpair_impl.h>
|
||||
|
||||
/* */
|
||||
void json_stats_destroy(spa_t *spa)
|
||||
{
|
||||
spa_history_kstat_t *shk = &spa->spa_json_stats.kstat;
|
||||
kstat_t *ksp = shk->kstat;
|
||||
|
||||
if (ksp) {
|
||||
if (ksp->ks_data)
|
||||
kmem_free(ksp->ks_data, sizeof (spa_iostats_t));
|
||||
kstat_delete(ksp);
|
||||
}
|
||||
mutex_destroy(&shk->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return string for datatype -- this guides us in implementing
|
||||
* json translation. This is only because I find it easier to read...
|
||||
*/
|
||||
static const char *datatype_string(data_type_t t) {
|
||||
switch (t) {
|
||||
case DATA_TYPE_UNKNOWN: return "DATA_TYPE_UNKNOWN";
|
||||
case DATA_TYPE_BOOLEAN: return "DATA_TYPE_BOOLEAN";
|
||||
case DATA_TYPE_BYTE: return "DATA_TYPE_BYTE";
|
||||
case DATA_TYPE_INT16: return "DATA_TYPE_INT16";
|
||||
case DATA_TYPE_UINT16: return "DATA_TYPE_UINT16";
|
||||
case DATA_TYPE_INT32: return "DATA_TYPE_INT32";
|
||||
case DATA_TYPE_UINT32: return "DATA_TYPE_UINT32";
|
||||
case DATA_TYPE_INT64: return "DATA_TYPE_INT64";
|
||||
case DATA_TYPE_UINT64: return "DATA_TYPE_UINT64";
|
||||
case DATA_TYPE_STRING: return "DATA_TYPE_STRING";
|
||||
case DATA_TYPE_BYTE_ARRAY: return "DATA_TYPE_BYTE_ARRAY";
|
||||
case DATA_TYPE_INT16_ARRAY: return "DATA_TYPE_INT16_ARRAY";
|
||||
case DATA_TYPE_UINT16_ARRAY: return "DATA_TYPE_UINT16_ARRAY";
|
||||
case DATA_TYPE_INT32_ARRAY: return "DATA_TYPE_INT32_ARRAY";
|
||||
case DATA_TYPE_UINT32_ARRAY: return "DATA_TYPE_UINT32_ARRAY";
|
||||
case DATA_TYPE_INT64_ARRAY: return "DATA_TYPE_INT64_ARRAY";
|
||||
case DATA_TYPE_UINT64_ARRAY: return "DATA_TYPE_UINT64_ARRAY";
|
||||
case DATA_TYPE_STRING_ARRAY: return "DATA_TYPE_STRING_ARRAY";
|
||||
case DATA_TYPE_HRTIME: return "DATA_TYPE_HRTIME";
|
||||
case DATA_TYPE_NVLIST: return "DATA_TYPE_NVLIST";
|
||||
case DATA_TYPE_NVLIST_ARRAY: return "DATA_TYPE_NVLIST_ARRAY";
|
||||
case DATA_TYPE_BOOLEAN_VALUE: return "DATA_TYPE_BOOLEAN_VALUE";
|
||||
case DATA_TYPE_INT8: return "DATA_TYPE_INT8";
|
||||
case DATA_TYPE_UINT8: return "DATA_TYPE_UINT8";
|
||||
case DATA_TYPE_BOOLEAN_ARRAY: return "DATA_TYPE_BOOLEAN_ARRAY";
|
||||
case DATA_TYPE_INT8_ARRAY: return "DATA_TYPE_INT8_ARRAY";
|
||||
case DATA_TYPE_UINT8_ARRAY: return "DATA_TYPE_UINT8_ARRAY";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This code is NOT abstracted -- just some duplication, until I
|
||||
* actually understand what is needed.
|
||||
*/
|
||||
static void nvlist_to_json(nvlist_t *nvl, jprint_t *jp) {
|
||||
const nvpriv_t *priv;
|
||||
const i_nvp_t *curr;
|
||||
|
||||
if ((priv = (const nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
|
||||
return;
|
||||
|
||||
for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) {
|
||||
const nvpair_t *nvp = &curr->nvi_nvp;
|
||||
const char *name = (const char *)NVP_NAME(nvp);
|
||||
data_type_t type = NVP_TYPE(nvp);
|
||||
void *p = NVP_VALUE(nvp);
|
||||
|
||||
if (jp_error(jp) != JPRINT_OK)
|
||||
return;
|
||||
|
||||
switch (type) {
|
||||
|
||||
/*
|
||||
* Array types
|
||||
*/
|
||||
case DATA_TYPE_UINT64_ARRAY:
|
||||
uint64_t *u = (uint64_t *)p;
|
||||
jp_printf(jp, "%k: [", name);
|
||||
for (int i = 0; i < NVP_NELEM(nvp); ++i) {
|
||||
if (jp_error(jp) != JPRINT_OK)
|
||||
break;
|
||||
jp_printf(jp, "%U", u[i]);
|
||||
}
|
||||
jp_printf(jp, "]");
|
||||
break;
|
||||
case DATA_TYPE_NVLIST_ARRAY:
|
||||
nvlist_t **a = (nvlist_t **)p;
|
||||
jp_printf(jp, "%k: [", name);
|
||||
for (int i = 0; i < NVP_NELEM(nvp); ++i) {
|
||||
if (jp_error(jp) != JPRINT_OK)
|
||||
break;
|
||||
jp_printf(jp, "{");
|
||||
nvlist_to_json(a[i], jp);
|
||||
jp_printf(jp, "}");
|
||||
}
|
||||
jp_printf(jp, "]");
|
||||
break;
|
||||
/*
|
||||
* Primitive types
|
||||
*/
|
||||
case DATA_TYPE_UINT64:
|
||||
jp_printf(jp, "%k: %U", name, *(uint64_t *)p);
|
||||
break;
|
||||
case DATA_TYPE_INT64:
|
||||
jp_printf(jp, "%k: %D", name, *(int64_t *)p);
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
jp_printf(jp, "%k: %s", name, (char *)p);
|
||||
break;
|
||||
case DATA_TYPE_BOOLEAN:
|
||||
jp_printf(jp, "%k: %b", name, B_TRUE);
|
||||
break;
|
||||
case DATA_TYPE_BOOLEAN_VALUE:
|
||||
jp_printf(jp, "%k: %b", name, *(boolean_t *)p);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Object types
|
||||
*/
|
||||
case DATA_TYPE_NVLIST:
|
||||
/*
|
||||
* We are going to suppress vdev_tree, and generate
|
||||
* later -- needs fixing... quick hack for wasabi
|
||||
*/
|
||||
if ((jp->stackp == 0) && (strcmp(name, "vdev_tree") == 0))
|
||||
break;
|
||||
jp_printf(jp, "%k: {", name);
|
||||
nvlist_to_json((nvlist_t *)p , jp);
|
||||
jp_printf(jp, "}");
|
||||
break;
|
||||
|
||||
/*
|
||||
* Default -- tell us what we are missing
|
||||
*/
|
||||
default:
|
||||
jp_printf(jp, "%k: %s", name, datatype_string(type));
|
||||
zfs_dbgmsg("name = %s type = %d %s", name,
|
||||
(int)type, datatype_string(type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void vdev_to_json(vdev_t *v, pool_scan_stat_t *ps, jprint_t *jp)
|
||||
{
|
||||
uint64_t i, n;
|
||||
vdev_t **a;
|
||||
const char *s;
|
||||
jp_printf(jp, "type: %s", v->vdev_ops->vdev_op_type);
|
||||
jp_printf(jp, "id: %U", v->vdev_id);
|
||||
jp_printf(jp, "guid: %U", v->vdev_guid);
|
||||
if (strcmp(v->vdev_ops->vdev_op_type, "root") != 0) {
|
||||
jp_printf(jp, "asize: %U", v->vdev_asize);
|
||||
jp_printf(jp, "ashift: %U", v->vdev_ashift);
|
||||
jp_printf(jp, "whole_disk: %b",
|
||||
(v->vdev_wholedisk == 0) ? B_FALSE : B_TRUE);
|
||||
jp_printf(jp, "offline: %b",
|
||||
(v->vdev_offline == 0) ? B_FALSE : B_TRUE);
|
||||
jp_printf(jp, "faulted: %b",
|
||||
(v->vdev_faulted == 0) ? B_FALSE : B_TRUE);
|
||||
jp_printf(jp, "degraded: %b",
|
||||
(v->vdev_degraded == 0) ? B_FALSE : B_TRUE);
|
||||
jp_printf(jp, "removed: %b",
|
||||
(v->vdev_removed == 0) ? B_FALSE : B_TRUE);
|
||||
jp_printf(jp, "not_present: %b",
|
||||
(v->vdev_not_present == 0) ? B_FALSE : B_TRUE);
|
||||
jp_printf(jp, "is_log: %b",
|
||||
(v->vdev_islog == 0) ? B_FALSE : B_TRUE);
|
||||
|
||||
jp_printf(jp, "path: %s", v->vdev_path);
|
||||
if (v->vdev_devid != NULL)
|
||||
jp_printf(jp, "devid: %s", v->vdev_devid);
|
||||
if (v->vdev_physpath != NULL)
|
||||
jp_printf(jp, "physpath: %s", v->vdev_physpath);
|
||||
if (v->vdev_enc_sysfs_path != NULL)
|
||||
jp_printf(jp, "enc_sysfs_path: %s",
|
||||
v->vdev_enc_sysfs_path);
|
||||
switch (v->vdev_stat.vs_state) {
|
||||
case VDEV_STATE_UNKNOWN: s = "HEALTHY"; break;
|
||||
case VDEV_STATE_CLOSED: s = "CLOSED"; break;
|
||||
case VDEV_STATE_OFFLINE: s = "OFFLINE"; break;
|
||||
case VDEV_STATE_REMOVED: s = "REMOVED"; break;
|
||||
case VDEV_STATE_CANT_OPEN: s = "CAN'T OPEN"; break;
|
||||
case VDEV_STATE_FAULTED: s = "FAULTED"; break;
|
||||
case VDEV_STATE_DEGRADED: s = "DEGRADED"; break;
|
||||
case VDEV_STATE_HEALTHY: s = "HEALTHY"; break;
|
||||
default: s = "?";
|
||||
}
|
||||
jp_printf(jp, "state: %s", s);
|
||||
/* Try for some of the extended status annotations that
|
||||
* zpool status provides.
|
||||
*/
|
||||
/* (removing) */
|
||||
jp_printf(jp, "vs_scan_removing: %b",
|
||||
v->vdev_stat.vs_scan_removing != 0);
|
||||
/* (non-allocating) */
|
||||
jp_printf(jp, "vs_noalloc: %b",
|
||||
v->vdev_stat.vs_noalloc != 0);
|
||||
/* (awaiting resilver) */
|
||||
jp_printf(jp, "vs_resilver_deferred: %b",
|
||||
v->vdev_stat.vs_resilver_deferred);
|
||||
if ((v->vdev_state == VDEV_STATE_UNKNOWN) ||
|
||||
(v->vdev_state == VDEV_STATE_HEALTHY)) {
|
||||
if (v->vdev_stat.vs_scan_processed != 0) {
|
||||
if (ps &&
|
||||
(ps->pss_state == DSS_SCANNING)) {
|
||||
jp_printf(jp, "resilver_repair: %s",
|
||||
(ps->pss_func == POOL_SCAN_RESILVER) ?
|
||||
"resilvering" : "repairing");
|
||||
} else if (ps &&
|
||||
v->vdev_stat.vs_resilver_deferred) {
|
||||
jp_printf(jp, "resilver_repair: %s",
|
||||
"awaiting resilver");
|
||||
}
|
||||
}
|
||||
}
|
||||
jp_printf(jp, "initialize_state: {");
|
||||
s = "VDEV_INITIALIZE_NONE";
|
||||
if (v->vdev_stat.vs_initialize_state == VDEV_INITIALIZE_ACTIVE)
|
||||
s = "VDEV_INITIALIZE_ACTIVE";
|
||||
if (v->vdev_stat.vs_initialize_state == VDEV_INITIALIZE_SUSPENDED)
|
||||
s = "VDEV_INITIALIZE_SUSPENDED";
|
||||
if (v->vdev_stat.vs_initialize_state == VDEV_INITIALIZE_COMPLETE)
|
||||
s = "VDEV_INITIALIZE_COMPLETE";
|
||||
jp_printf(jp, "vs_initialize_state: %s", s);
|
||||
jp_printf(jp, "vs_initialize_bytes_done: %U",
|
||||
v->vdev_stat.vs_initialize_bytes_done);
|
||||
jp_printf(jp, "vs_initialize_bytes_est: %U",
|
||||
v->vdev_stat.vs_initialize_bytes_est);
|
||||
jp_printf(jp, "vs_initialize_action_time: %U",
|
||||
v->vdev_stat.vs_initialize_action_time);
|
||||
jp_printf(jp, "}");
|
||||
|
||||
jp_printf(jp, "trim_state: {");
|
||||
s = "VDEV_UNTRIMMED";
|
||||
if (v->vdev_stat.vs_trim_state == VDEV_TRIM_ACTIVE)
|
||||
s = "VDEV_TRIM_ACTIVE";
|
||||
if (v->vdev_stat.vs_trim_state == VDEV_TRIM_SUSPENDED)
|
||||
s = "VDEV_TRIM_SUSPENDED";
|
||||
if (v->vdev_stat.vs_trim_state == VDEV_TRIM_COMPLETE)
|
||||
s = "VDEV_TRIM_COMPLETE";
|
||||
if (v->vdev_stat.vs_trim_notsup)
|
||||
s = "VDEV_TRIM_UNSUPPORTED";
|
||||
jp_printf(jp, "vs_trim_state: %s", s);
|
||||
if (!v->vdev_stat.vs_trim_notsup) {
|
||||
jp_printf(jp, "vs_trim_action_time: %U",
|
||||
v->vdev_stat.vs_trim_action_time);
|
||||
jp_printf(jp, "vs_trim_bytes_done: %U",
|
||||
v->vdev_stat.vs_trim_bytes_done);
|
||||
jp_printf(jp, "vs_trim_bytes_est: %U",
|
||||
v->vdev_stat.vs_trim_bytes_est);
|
||||
}
|
||||
jp_printf(jp, "}");
|
||||
|
||||
jp_printf(jp, "read_errors: %U",
|
||||
v->vdev_stat.vs_read_errors);
|
||||
jp_printf(jp, "write_errors: %U",
|
||||
v->vdev_stat.vs_write_errors);
|
||||
jp_printf(jp, "checksum_errors: %U",
|
||||
v->vdev_stat.vs_checksum_errors);
|
||||
jp_printf(jp, "slow_ios: %U",
|
||||
v->vdev_stat.vs_slow_ios);
|
||||
jp_printf(jp, "trim_errors: %U",
|
||||
v->vdev_stat.vs_trim_errors);
|
||||
}
|
||||
/*
|
||||
* Please note that children of children will translate to
|
||||
* json... we will not put out the number, but that is not
|
||||
* needed in json anyway.
|
||||
*/
|
||||
n = v->vdev_children;
|
||||
a = v->vdev_child;
|
||||
if (n != 0) {
|
||||
jp_printf(jp, "children: [");
|
||||
for (i = 0; i < n; ++i) {
|
||||
jp_printf(jp, "{");
|
||||
vdev_to_json(a[i], ps, jp);
|
||||
jp_printf(jp, "}");
|
||||
}
|
||||
jp_printf(jp, "]");
|
||||
}
|
||||
}
|
||||
|
||||
static void iterate_vdevs(spa_t *spa, pool_scan_stat_t *ps, jprint_t *jp)
|
||||
{
|
||||
vdev_t *v = spa->spa_root_vdev;
|
||||
if (v == NULL) {
|
||||
jp_printf(jp, "error: %s", "NO ROOT VDEV");
|
||||
return;
|
||||
}
|
||||
jp_printf(jp, "vdev_tree: {");
|
||||
vdev_to_json(v, ps, jp);
|
||||
jp_printf(jp, "}");
|
||||
}
|
||||
|
||||
static int json_data(char *buf, size_t size, void *data) {
|
||||
spa_t *spa = (spa_t *)data;
|
||||
int error = 0;
|
||||
int ps_error = 0;
|
||||
jprint_t jp;
|
||||
nvlist_t *nvl, *pnvl;
|
||||
uint64_t loadtimes[2];
|
||||
pool_scan_stat_t ps;
|
||||
const char *s = NULL;
|
||||
|
||||
if (nvlist_dup(spa->spa_config, &nvl, 0) != 0) {
|
||||
/*
|
||||
* fmgw - fixme, what to do here?!?
|
||||
*/
|
||||
zfs_dbgmsg("json_data: nvlist_dup failed");
|
||||
return (0);
|
||||
}
|
||||
fnvlist_add_nvlist(nvl, ZPOOL_CONFIG_LOAD_INFO, spa->spa_load_info);
|
||||
|
||||
spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
|
||||
|
||||
ps_error = spa_scan_get_stats(spa, &ps);
|
||||
|
||||
error = spa_prop_get(spa, &pnvl);
|
||||
fnvlist_add_nvlist(nvl, "spa_props", pnvl);
|
||||
|
||||
loadtimes[0] = spa->spa_loaded_ts.tv_sec;
|
||||
loadtimes[1] = spa->spa_loaded_ts.tv_nsec;
|
||||
fnvlist_add_uint64_array(nvl, ZPOOL_CONFIG_LOADED_TIME, loadtimes, 2);
|
||||
fnvlist_add_uint64(nvl, ZPOOL_CONFIG_ERRCOUNT,
|
||||
spa_get_errlog_size(spa));
|
||||
fnvlist_add_boolean_value(nvl, "is_suspended", spa_suspended(spa));
|
||||
fnvlist_add_uint64(nvl, ZPOOL_CONFIG_SUSPENDED, spa->spa_failmode);
|
||||
fnvlist_add_uint64(nvl, ZPOOL_CONFIG_SUSPENDED_REASON,
|
||||
spa->spa_suspended);
|
||||
|
||||
jp_open(&jp, buf, size);
|
||||
jp_printf(&jp, "{");
|
||||
|
||||
jp_printf(&jp, "stats_version: %d", 1);
|
||||
jp_printf(&jp, "scan_error: %d", ps_error);
|
||||
if (ps_error == 0) {
|
||||
jp_printf(&jp, "scan_stats: {");
|
||||
switch (ps.pss_func) {
|
||||
case POOL_SCAN_NONE: s = "NONE"; break;
|
||||
case POOL_SCAN_SCRUB: s = "SCRUB"; break;
|
||||
case POOL_SCAN_RESILVER: s = "RESILVER"; break;
|
||||
case POOL_SCAN_FUNCS: s = "?";
|
||||
}
|
||||
jp_printf(&jp, "func: %s", s);
|
||||
switch (ps.pss_state) {
|
||||
case DSS_NONE: s = "NONE"; break;
|
||||
case DSS_SCANNING: s = "SCANNING"; break;
|
||||
case DSS_FINISHED: s = "FINISHED"; break;
|
||||
case DSS_CANCELED: s = "CANCELED"; break;
|
||||
case DSS_NUM_STATES: s = "?";
|
||||
}
|
||||
jp_printf(&jp, "state: %s", s);
|
||||
jp_printf(&jp, "start_time: %U", ps.pss_start_time);
|
||||
jp_printf(&jp, "end_time: %U", ps.pss_end_time);
|
||||
jp_printf(&jp, "to_examine: %U", ps.pss_to_examine);
|
||||
jp_printf(&jp, "examined: %U", ps.pss_examined);
|
||||
jp_printf(&jp, "processed: %U", ps.pss_processed);
|
||||
jp_printf(&jp, "errors: %U", ps.pss_errors);
|
||||
|
||||
jp_printf(&jp, "pass_exam: %U", ps.pss_pass_exam);
|
||||
jp_printf(&jp, "pass_start: %U", ps.pss_pass_start);
|
||||
jp_printf(&jp, "pass_scrub_pause: %U", ps.pss_pass_scrub_pause);
|
||||
jp_printf(&jp, "pass_scrub_scrub_spent_paused: %U",
|
||||
ps.pss_pass_scrub_spent_paused);
|
||||
jp_printf(&jp, "pass_issued: %U", ps.pss_pass_issued);
|
||||
jp_printf(&jp, "issued: %U", ps.pss_issued);
|
||||
jp_printf(&jp, "}");
|
||||
}
|
||||
s = "?";
|
||||
switch (spa->spa_state) {
|
||||
case POOL_STATE_ACTIVE: s = "ACTIVE"; break;
|
||||
case POOL_STATE_EXPORTED: s = "EXPORTED"; break;
|
||||
case POOL_STATE_DESTROYED: s = "DESTROYED"; break;
|
||||
case POOL_STATE_SPARE: s = "SPARE"; break;
|
||||
case POOL_STATE_L2CACHE: s = "L2CACHE"; break;
|
||||
case POOL_STATE_UNINITIALIZED: s = "UNINITIALIZED"; break;
|
||||
case POOL_STATE_UNAVAIL: s = "UNAVAIL"; break;
|
||||
case POOL_STATE_POTENTIALLY_ACTIVE: s = "POTENTIALLY ACTIVE"; break;
|
||||
}
|
||||
jp_printf(&jp, "pool_state: %s", s);
|
||||
|
||||
spa_add_spares(spa, nvl);
|
||||
spa_add_l2cache(spa, nvl);
|
||||
spa_add_feature_stats(spa, nvl);
|
||||
|
||||
/* iterate and transfer nvl to json */
|
||||
nvlist_to_json(nvl, &jp);
|
||||
|
||||
iterate_vdevs(spa, &ps, &jp);
|
||||
|
||||
/*
|
||||
* close the root object
|
||||
*/
|
||||
jp_printf(&jp, "}");
|
||||
|
||||
spa_config_exit(spa, SCL_CONFIG, FTAG);
|
||||
nvlist_free(nvl);
|
||||
|
||||
error = jp_close(&jp);
|
||||
if (error == JPRINT_BUF_FULL) {
|
||||
error = SET_ERROR(ENOMEM);
|
||||
} else if (error != 0) {
|
||||
/*
|
||||
* Another error from jprint, format an error message
|
||||
* but this is not ever to happen (this would be a
|
||||
* defect elsewhere).
|
||||
*
|
||||
* If this does happen, we simply put the string where
|
||||
* the json should go... this is expected to trigger
|
||||
* a json decode error, and report "upstream"
|
||||
*/
|
||||
snprintf(buf, size,
|
||||
"jprint error %s (%d) callno %d, size %ld\n",
|
||||
jp_errorstring(error), error, jp_errorpos(&jp), size);
|
||||
error = 0;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
||||
static void *json_addr(kstat_t *ksp, loff_t n)
|
||||
{
|
||||
if (n == 0)
|
||||
return (ksp->ks_private);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
void json_stats_init(spa_t *spa)
|
||||
{
|
||||
spa_history_kstat_t *shk = &spa->spa_json_stats.kstat;
|
||||
char *name;
|
||||
kstat_t *ksp;
|
||||
|
||||
mutex_init(&shk->lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
name = kmem_asprintf("zfs/%s", spa_name(spa));
|
||||
ksp = kstat_create(name, 0, "stats", "misc",
|
||||
KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL);
|
||||
shk->kstat = ksp;
|
||||
if (ksp) {
|
||||
ksp->ks_lock = &shk->lock;
|
||||
ksp->ks_data = NULL;
|
||||
ksp->ks_private = spa;
|
||||
ksp->ks_flags |= KSTAT_FLAG_NO_HEADERS;
|
||||
kstat_set_raw_ops(ksp, NULL, json_data, json_addr);
|
||||
kstat_install(ksp);
|
||||
}
|
||||
|
||||
kmem_strfree(name);
|
||||
}
|
||||
|
|
@ -5285,7 +5285,7 @@ spa_inject_delref(spa_t *spa)
|
|||
/*
|
||||
* Add spares device information to the nvlist.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
spa_add_spares(spa_t *spa, nvlist_t *config)
|
||||
{
|
||||
nvlist_t **spares;
|
||||
|
@ -5334,7 +5334,7 @@ spa_add_spares(spa_t *spa, nvlist_t *config)
|
|||
/*
|
||||
* Add l2cache device information to the nvlist, including vdev stats.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
spa_add_l2cache(spa_t *spa, nvlist_t *config)
|
||||
{
|
||||
nvlist_t **l2cache;
|
||||
|
@ -5448,7 +5448,7 @@ spa_feature_stats_from_cache(spa_t *spa, nvlist_t *features)
|
|||
* ensures we don't block here on I/O on a suspended pool so 'zpool
|
||||
* clear' can resume the pool.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
spa_add_feature_stats(spa_t *spa, nvlist_t *config)
|
||||
{
|
||||
nvlist_t *features;
|
||||
|
@ -9098,6 +9098,7 @@ spa_sync_iterate_to_convergence(spa_t *spa, dmu_tx_t *tx)
|
|||
&spa->spa_deferred_bpobj, tx);
|
||||
}
|
||||
|
||||
|
||||
ddt_sync(spa, txg);
|
||||
dsl_scan_sync(dp, tx);
|
||||
svr_sync(spa, tx);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <sys/vdev_impl.h>
|
||||
#include <sys/spa.h>
|
||||
#include <zfs_comutil.h>
|
||||
#include <sys/json_stats.h>
|
||||
|
||||
/*
|
||||
* Keeps stats on last N reads per spa_t, disabled by default.
|
||||
|
@ -951,11 +952,13 @@ spa_stats_init(spa_t *spa)
|
|||
spa_mmp_history_init(spa);
|
||||
spa_state_init(spa);
|
||||
spa_iostats_init(spa);
|
||||
json_stats_init(spa);
|
||||
}
|
||||
|
||||
void
|
||||
spa_stats_destroy(spa_t *spa)
|
||||
{
|
||||
json_stats_destroy(spa);
|
||||
spa_iostats_destroy(spa);
|
||||
spa_health_destroy(spa);
|
||||
spa_tx_assign_destroy(spa);
|
||||
|
|
|
@ -221,6 +221,9 @@
|
|||
#include <sys/lua/lauxlib.h>
|
||||
#include <sys/zfs_ioctl_impl.h>
|
||||
|
||||
|
||||
int fmgw_debug = 0;
|
||||
|
||||
kmutex_t zfsdev_state_lock;
|
||||
zfsdev_state_t *zfsdev_state_list;
|
||||
|
||||
|
@ -1592,6 +1595,8 @@ zfs_ioc_pool_configs(zfs_cmd_t *zc)
|
|||
nvlist_t *configs;
|
||||
int error;
|
||||
|
||||
if (fmgw_debug != 0)
|
||||
zfs_dbgmsg("fmgw -- calling spa_all_configs, which takes spa_namespace_lock");
|
||||
if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL)
|
||||
return (SET_ERROR(EEXIST));
|
||||
|
||||
|
@ -1618,6 +1623,8 @@ zfs_ioc_pool_stats(zfs_cmd_t *zc)
|
|||
int error;
|
||||
int ret = 0;
|
||||
|
||||
if (fmgw_debug != 0)
|
||||
zfs_dbgmsg("fmgw -- calling spa_get_stats");
|
||||
error = spa_get_stats(zc->zc_name, &config, zc->zc_value,
|
||||
sizeof (zc->zc_value));
|
||||
|
||||
|
@ -1685,7 +1692,7 @@ zfs_ioc_pool_scan(zfs_cmd_t *zc)
|
|||
|
||||
if (zc->zc_flags == POOL_SCRUB_PAUSE)
|
||||
error = spa_scrub_pause_resume(spa, POOL_SCRUB_PAUSE);
|
||||
else if (zc->zc_cookie == POOL_SCAN_NONE)
|
||||
else if (zc->zc_cookie == POOL_SCAN_NONE)
|
||||
error = spa_scan_stop(spa);
|
||||
else
|
||||
error = spa_scan(spa, zc->zc_cookie);
|
||||
|
@ -1695,6 +1702,44 @@ zfs_ioc_pool_scan(zfs_cmd_t *zc)
|
|||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* This interface lets us put messages into zfs_dbgmsg() log. We
|
||||
* also look at the message; if the first character is *, we take
|
||||
* it as a private command. *D0..F sets fmgw_debug bits to 0..F
|
||||
*/
|
||||
static int
|
||||
zfs_ioc_addlog(zfs_cmd_t *zc)
|
||||
{
|
||||
char *s = "(NULL)";
|
||||
s = zc->zc_name;
|
||||
if ((s[0] == '*') && (s[1] == 'D')) {
|
||||
switch (s[2]) {
|
||||
case '0': fmgw_debug = 0x0; break;
|
||||
case '1': fmgw_debug = 0x1; break;
|
||||
case '2': fmgw_debug = 0x2; break;
|
||||
case '3': fmgw_debug = 0x3; break;
|
||||
case '4': fmgw_debug = 0x4; break;
|
||||
case '5': fmgw_debug = 0x5; break;
|
||||
case '6': fmgw_debug = 0x6; break;
|
||||
case '7': fmgw_debug = 0x7; break;
|
||||
case '8': fmgw_debug = 0x8; break;
|
||||
case '9': fmgw_debug = 0x9; break;
|
||||
case 'A': fmgw_debug = 0xa; break;
|
||||
case 'B': fmgw_debug = 0xb; break;
|
||||
case 'C': fmgw_debug = 0xc; break;
|
||||
case 'D': fmgw_debug = 0xd; break;
|
||||
case 'E': fmgw_debug = 0xe; break;
|
||||
case 'F': fmgw_debug = 0xf; break;
|
||||
default: break;
|
||||
}
|
||||
} else if (s[0] == '*') {
|
||||
zfs_dbgmsg("bad command %c", s[1]);
|
||||
} else {
|
||||
zfs_dbgmsg("%s", s);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_ioc_pool_freeze(zfs_cmd_t *zc)
|
||||
{
|
||||
|
@ -2087,6 +2132,8 @@ zfs_ioc_objset_stats(zfs_cmd_t *zc)
|
|||
objset_t *os;
|
||||
int error;
|
||||
|
||||
if (fmgw_debug != 0)
|
||||
zfs_dbgmsg("fmgw");
|
||||
error = dmu_objset_hold(zc->zc_name, FTAG, &os);
|
||||
if (error == 0) {
|
||||
error = zfs_ioc_objset_stats_impl(zc, os);
|
||||
|
@ -2214,6 +2261,8 @@ zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
|
|||
char *p;
|
||||
size_t orig_len = strlen(zc->zc_name);
|
||||
|
||||
if (fmgw_debug != 0)
|
||||
zfs_dbgmsg("fmgw");
|
||||
top:
|
||||
if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os))) {
|
||||
if (error == ENOENT)
|
||||
|
@ -2957,6 +3006,8 @@ zfs_ioc_pool_get_props(zfs_cmd_t *zc)
|
|||
int error;
|
||||
nvlist_t *nvp = NULL;
|
||||
|
||||
if (fmgw_debug != 0)
|
||||
zfs_dbgmsg("fmgw");
|
||||
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
|
||||
/*
|
||||
* If the pool is faulted, there may be properties we can still
|
||||
|
@ -3515,6 +3566,8 @@ zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl)
|
|||
spa_t *spa;
|
||||
int error;
|
||||
|
||||
if (fmgw_debug != 0)
|
||||
zfs_dbgmsg("fmgw");
|
||||
/*
|
||||
* The poolname in the ioctl is not set, we get it from the TSD,
|
||||
* which was set at the end of the last successful ioctl that allows
|
||||
|
@ -7112,6 +7165,12 @@ zfs_ioctl_init(void)
|
|||
zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze,
|
||||
zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_READONLY);
|
||||
|
||||
/* fmgw - we sneak this in here... just as awful as zfs_ioc_pool_freeze
|
||||
* which we are modeled on.
|
||||
*/
|
||||
zfs_ioctl_register_legacy(ZFS_IOC_ADD_LOG, zfs_ioc_addlog,
|
||||
zfs_secpolicy_none, NO_NAME, B_FALSE, POOL_CHECK_READONLY);
|
||||
|
||||
zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create,
|
||||
zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE);
|
||||
zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SCAN,
|
||||
|
@ -7458,6 +7517,8 @@ zfsdev_ioctl_common(uint_t vecnum, zfs_cmd_t *zc, int flag)
|
|||
if (vec->zvec_func == NULL && vec->zvec_legacy_func == NULL)
|
||||
return (SET_ERROR(ZFS_ERR_IOC_CMD_UNAVAIL));
|
||||
|
||||
// if (fmgw_debug != 0)
|
||||
// zfs_dbgmsg("enter ioctl, %d %x %s", vecnum, vecnum, fmgw_zioctl(vecnum + ZFS_IOC_FIRST));
|
||||
zc->zc_iflags = flag & FKIOCTL;
|
||||
max_nvlist_src_size = zfs_max_nvlist_src_size_os();
|
||||
if (zc->zc_nvlist_src_size > max_nvlist_src_size) {
|
||||
|
|
Loading…
Reference in New Issue