]> xenbits.xensource.com Git - people/royger/freebsd.git/commitdiff
Add support for attaching aggregation labels to sysctl objects.
authored <ed@FreeBSD.org>
Wed, 14 Dec 2016 12:47:34 +0000 (12:47 +0000)
committered <ed@FreeBSD.org>
Wed, 14 Dec 2016 12:47:34 +0000 (12:47 +0000)
I'm currently working on writing a metrics exporter for the Prometheus
monitoring system to provide access to sysctl metrics. Prometheus and
sysctl have some structural differences:

- sysctl is a tree of string component names.
- Prometheus uses a flat namespace for its metrics, but allows you to
  attach labels with values to them, so that you can do aggregation.

An initial version of my exporter simply translated

    hw.acpi.thermal.tz1.temperature

to

    sysctl_hw_acpi_thermal_tz1_temperature_celcius

while we should ideally have

    sysctl_hw_acpi_thermal_temperature_celcius{thermal_zone="tz1"}

allowing you to graph all thermal zones on a system in one go.

The change presented in this commit adds support for accomplishing this,
by providing the ability to attach labels to nodes. In the example I
gave above, the label "thermal_zone" would be attached to "tz1". As this
is a feature that will only be used very rarely, I decided to not change
the KPI too aggressively.

Discussed on: hackers@
Reviewed by: cem
Differential Revision: https://reviews.freebsd.org/D8775

share/man/man9/Makefile
share/man/man9/sysctl.9
share/man/man9/sysctl_add_oid.9
sys/kern/kern_sysctl.c
sys/sys/sysctl.h

index c27540db6858b852e766eec641c0ad61e21c15d1..d11500ba48dca2668b944c3c8ecd4767de13f01a 100644 (file)
@@ -1712,6 +1712,7 @@ MLINKS+=sysctl.9 SYSCTL_DECL.9 \
        sysctl.9 SYSCTL_ADD_INT.9 \
        sysctl.9 SYSCTL_ADD_LONG.9 \
        sysctl.9 SYSCTL_ADD_NODE.9 \
+       sysctl.9 SYSCTL_ADD_NODE_WITH_LABEL.9 \
        sysctl.9 SYSCTL_ADD_OPAQUE.9 \
        sysctl.9 SYSCTL_ADD_PROC.9 \
        sysctl.9 SYSCTL_ADD_QUAD.9 \
@@ -1735,8 +1736,10 @@ MLINKS+=sysctl.9 SYSCTL_DECL.9 \
        sysctl.9 SYSCTL_NODE_CHILDREN.9 \
        sysctl.9 SYSCTL_PARENT.9 \
        sysctl.9 SYSCTL_INT.9 \
+       sysctl.9 SYSCTL_INT_WITH_LABEL.9 \
        sysctl.9 SYSCTL_LONG.9 \
        sysctl.9 SYSCTL_NODE.9 \
+       sysctl.9 SYSCTL_NODE_WITH_LABEL.9 \
        sysctl.9 SYSCTL_OPAQUE.9 \
        sysctl.9 SYSCTL_PROC.9 \
        sysctl.9 SYSCTL_QUAD.9 \
index 916ffa1f2fdc3cc958d9bdb7ee654aaf0661e270..dbb61f59f2c0dc1d3e880f5c59c71085d56885b4 100644 (file)
@@ -25,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd October 23, 2015
+.Dd December 13, 2016
 .Dt SYSCTL 9
 .Os
 .Sh NAME
@@ -33,6 +33,7 @@
 .Nm SYSCTL_ADD_INT ,
 .Nm SYSCTL_ADD_LONG ,
 .Nm SYSCTL_ADD_NODE ,
+.Nm SYSCTL_ADD_NODE_WITH_LABEL ,
 .Nm SYSCTL_ADD_OPAQUE ,
 .Nm SYSCTL_ADD_PROC ,
 .Nm SYSCTL_ADD_QUAD ,
 .Nm SYSCTL_NODE_CHILDREN ,
 .Nm SYSCTL_PARENT ,
 .Nm SYSCTL_INT ,
+.Nm SYSCTL_INT_WITH_LABEL ,
 .Nm SYSCTL_LONG ,
 .Nm SYSCTL_NODE ,
+.Nm SYSCTL_NODE_WITH_LABEL ,
 .Nm SYSCTL_OPAQUE ,
 .Nm SYSCTL_PROC ,
 .Nm SYSCTL_QUAD ,
 .Fa "const char *descr"
 .Fc
 .Ft struct sysctl_oid *
+.Fo SYSCTL_ADD_NODE_WITH_LABEL
+.Fa "struct sysctl_ctx_list *ctx"
+.Fa "struct sysctl_oid_list *parent"
+.Fa "int number"
+.Fa "const char *name"
+.Fa "int ctlflags"
+.Fa "int (*handler)(SYSCTL_HANDLER_ARGS)"
+.Fa "const char *descr"
+.Fa "const char *label"
+.Fc
+.Ft struct sysctl_oid *
 .Fo SYSCTL_ADD_OPAQUE
 .Fa "struct sysctl_ctx_list *ctx"
 .Fa "struct sysctl_oid_list *parent"
 .Fa "struct sysctl_oid *oid"
 .Fc
 .Fn SYSCTL_INT parent number name ctlflags ptr val descr
+.Fn SYSCTL_INT_WITH_LABEL parent number name ctlflags ptr val descr label
 .Fn SYSCTL_LONG parent number name ctlflags ptr val descr
 .Fn SYSCTL_NODE parent number name ctlflags handler descr
+.Fn SYSCTL_NODE_WITH_LABEL parent number name ctlflags handler descr label
 .Fn SYSCTL_OPAQUE parent number name ctlflags ptr len format descr
 .Fn SYSCTL_PROC parent number name ctlflags arg1 arg2 handler format descr
 .Fn SYSCTL_QUAD parent number name ctlflags ptr val descr
@@ -459,6 +475,18 @@ structures
 .El
 .It Fa descr
 A pointer to a textual description of the OID.
+.It Fa label
+A pointer to an aggregation label for this component of the OID.
+To make it easier to export sysctl data to monitoring systems that
+support aggregations through labels (e.g., Prometheus),
+this argument can be used to attach a label name to an OID.
+The label acts as a hint that this component's name should not be part
+of the metric's name,
+but attached to the metric as a label instead.
+.Pp
+Labels should only be applied to siblings that are structurally similar
+and encode the same type of value,
+as aggregation is of no use otherwise.
 .El
 .Sh CREATING ROOT NODES
 Sysctl MIBs or OIDs are created in a hierarchical tree.
@@ -517,8 +545,10 @@ accessible, or needs to be processed before exporting.
 .Sh CREATING A STATIC SYSCTL
 Static sysctls are declared using one of the
 .Fn SYSCTL_INT ,
+.Fn SYSCTL_INT_WITH_LABEL ,
 .Fn SYSCTL_LONG ,
 .Fn SYSCTL_NODE ,
+.Fn SYSCTL_NODE_WITH_LABEL ,
 .Fn SYSCTL_OPAQUE ,
 .Fn SYSCTL_PROC ,
 .Fn SYSCTL_QUAD ,
@@ -543,6 +573,7 @@ Dynamic nodes are created using one of the
 .Fn SYSCTL_ADD_INT ,
 .Fn SYSCTL_ADD_LONG ,
 .Fn SYSCTL_ADD_NODE ,
+.Fn SYSCTL_ADD_NODE_WITH_LABEL ,
 .Fn SYSCTL_ADD_OPAQUE ,
 .Fn SYSCTL_ADD_PROC ,
 .Fn SYSCTL_ADD_QUAD ,
index e309f6529fdf08230e7b273e36851f1aee54adbd..d28aa799be8fd187f3e0b1e0f0ea2b04fee5d5e4 100644 (file)
@@ -27,7 +27,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd November 6, 2015
+.Dd December 13, 2016
 .Dt SYSCTL_ADD_OID 9
 .Os
 .Sh NAME
@@ -51,6 +51,7 @@
 .Fa "int (*handler) (SYSCTL_HANDLER_ARGS)"
 .Fa "const char *format"
 .Fa "const char *descr"
+.Fa "const char *label"
 .Fc
 .Ft int
 .Fo sysctl_move_oid
index 2f4085a1a584b6e22ac3ba7c4e2a26989ad2f886..6d9980d65c15b59c5fd694fb4083564076e3967b 100644 (file)
@@ -678,6 +678,9 @@ sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse)
                        if (oidp->oid_descr)
                                free(__DECONST(char *, oidp->oid_descr),
                                    M_SYSCTLOID);
+                       if (oidp->oid_label)
+                               free(__DECONST(char *, oidp->oid_label),
+                                   M_SYSCTLOID);
                        free(__DECONST(char *, oidp->oid_name), M_SYSCTLOID);
                        free(oidp, M_SYSCTLOID);
                }
@@ -691,7 +694,8 @@ sysctl_remove_oid_locked(struct sysctl_oid *oidp, int del, int recurse)
 struct sysctl_oid *
 sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent,
        int number, const char *name, int kind, void *arg1, intmax_t arg2,
-       int (*handler)(SYSCTL_HANDLER_ARGS), const char *fmt, const char *descr)
+       int (*handler)(SYSCTL_HANDLER_ARGS), const char *fmt, const char *descr,
+       const char *label)
 {
        struct sysctl_oid *oidp;
 
@@ -728,6 +732,8 @@ sysctl_add_oid(struct sysctl_ctx_list *clist, struct sysctl_oid_list *parent,
        oidp->oid_fmt = fmt;
        if (descr != NULL)
                oidp->oid_descr = strdup(descr, M_SYSCTLOID);
+       if (label != NULL)
+               oidp->oid_label = strdup(label, M_SYSCTLOID);
        /* Update the context, if used */
        if (clist != NULL)
                sysctl_ctx_entry_add(clist, oidp);
@@ -1176,6 +1182,31 @@ sysctl_sysctl_oiddescr(SYSCTL_HANDLER_ARGS)
 static SYSCTL_NODE(_sysctl, 5, oiddescr, CTLFLAG_RD|CTLFLAG_MPSAFE|CTLFLAG_CAPRD,
     sysctl_sysctl_oiddescr, "");
 
+static int
+sysctl_sysctl_oidlabel(SYSCTL_HANDLER_ARGS)
+{
+       struct sysctl_oid *oid;
+       struct rm_priotracker tracker;
+       int error;
+
+       SYSCTL_RLOCK(&tracker);
+       error = sysctl_find_oid(arg1, arg2, &oid, NULL, req);
+       if (error)
+               goto out;
+
+       if (oid->oid_label == NULL) {
+               error = ENOENT;
+               goto out;
+       }
+       error = SYSCTL_OUT(req, oid->oid_label, strlen(oid->oid_label) + 1);
+ out:
+       SYSCTL_RUNLOCK(&tracker);
+       return (error);
+}
+
+static SYSCTL_NODE(_sysctl, 6, oidlabel,
+    CTLFLAG_RD | CTLFLAG_MPSAFE | CTLFLAG_CAPRD, sysctl_sysctl_oidlabel, "");
+
 /*
  * Default "handler" functions.
  */
index bc6c542f95678216997340d35a269dfb3048f70f..5f949f5a40c6919d5e66a3cb54c2691c909d7260 100644 (file)
@@ -188,6 +188,7 @@ struct sysctl_oid {
        int              oid_refcnt;
        u_int            oid_running;
        const char      *oid_descr;
+       const char      *oid_label;
 };
 
 #define        SYSCTL_IN(r, p, l)      (r->newfunc)(r, p, l)
@@ -252,7 +253,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
 #endif
 
 /* This macro is only for internal use */
-#define        SYSCTL_OID_RAW(id, parent_child_head, nbr, name, kind, a1, a2, handler, fmt, descr) \
+#define        SYSCTL_OID_RAW(id, parent_child_head, nbr, name, kind, a1, a2, handler, fmt, descr, label) \
        struct sysctl_oid id = {                                        \
                .oid_parent = (parent_child_head),                      \
                .oid_children = SLIST_HEAD_INITIALIZER(&id.oid_children), \
@@ -263,46 +264,58 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
                .oid_name = (name),                                     \
                .oid_handler = (handler),                               \
                .oid_fmt = (fmt),                                       \
-               .oid_descr = __DESCR(descr)                             \
+               .oid_descr = __DESCR(descr),                            \
+               .oid_label = (label),                                   \
        };                                                              \
        DATA_SET(sysctl_set, id)
 
 /* This constructs a static "raw" MIB oid. */
 #define        SYSCTL_OID(parent, nbr, name, kind, a1, a2, handler, fmt, descr) \
-    static SYSCTL_OID_RAW(sysctl__##parent##_##name, \
-       SYSCTL_CHILDREN(&sysctl__##parent), \
-       nbr, #name, kind, a1, a2, handler, fmt, descr)
+       SYSCTL_OID_WITH_LABEL(parent, nbr, name, kind, a1, a2,          \
+           handler, fmt, descr, NULL)
+
+#define        SYSCTL_OID_WITH_LABEL(parent, nbr, name, kind, a1, a2, handler, fmt, descr, label) \
+    static SYSCTL_OID_RAW(sysctl__##parent##_##name,                   \
+       SYSCTL_CHILDREN(&sysctl__##parent),                             \
+       nbr, #name, kind, a1, a2, handler, fmt, descr, label)
 
 /* This constructs a global "raw" MIB oid. */
-#define        SYSCTL_OID_GLOBAL(parent, nbr, name, kind, a1, a2, handler, fmt, descr) \
+#define        SYSCTL_OID_GLOBAL(parent, nbr, name, kind, a1, a2, handler, fmt, descr, label) \
     SYSCTL_OID_RAW(sysctl__##parent##_##name, \
        SYSCTL_CHILDREN(&sysctl__##parent),     \
-       nbr, #name, kind, a1, a2, handler, fmt, descr)
+       nbr, #name, kind, a1, a2, handler, fmt, descr, label)
 
 #define        SYSCTL_ADD_OID(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, descr) \
-       sysctl_add_oid(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, __DESCR(descr))
+       sysctl_add_oid(ctx, parent, nbr, name, kind, a1, a2, handler, fmt, __DESCR(descr), NULL)
 
 /* This constructs a root node from which other nodes can hang. */
 #define        SYSCTL_ROOT_NODE(nbr, name, access, handler, descr)     \
        SYSCTL_OID_RAW(sysctl___##name, &sysctl__children,      \
            nbr, #name, CTLTYPE_NODE|(access), NULL, 0,         \
-           handler, "N", descr);                               \
+           handler, "N", descr, NULL);                         \
        CTASSERT(((access) & CTLTYPE) == 0 ||                   \
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_NODE)
 
 /* This constructs a node from which other oids can hang. */
-#define        SYSCTL_NODE(parent, nbr, name, access, handler, descr)          \
+#define        SYSCTL_NODE(parent, nbr, name, access, handler, descr) \
+       SYSCTL_NODE_WITH_LABEL(parent, nbr, name, access, handler, descr, NULL)
+
+#define        SYSCTL_NODE_WITH_LABEL(parent, nbr, name, access, handler, descr, label) \
        SYSCTL_OID_GLOBAL(parent, nbr, name, CTLTYPE_NODE|(access),     \
-           NULL, 0, handler, "N", descr);                              \
+           NULL, 0, handler, "N", descr, label);                       \
        CTASSERT(((access) & CTLTYPE) == 0 ||                           \
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_NODE)
 
 #define        SYSCTL_ADD_NODE(ctx, parent, nbr, name, access, handler, descr) \
+       SYSCTL_ADD_NODE_WITH_LABEL(ctx, parent, nbr, name, access, \
+           handler, descr, NULL)
+
+#define        SYSCTL_ADD_NODE_WITH_LABEL(ctx, parent, nbr, name, access, handler, descr, label) \
 ({                                                                     \
        CTASSERT(((access) & CTLTYPE) == 0 ||                           \
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_NODE);        \
        sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_NODE|(access),   \
-           NULL, 0, handler, "N", __DESCR(descr));                     \
+           NULL, 0, handler, "N", __DESCR(descr), label);              \
 })
 
 #define        SYSCTL_ADD_ROOT_NODE(ctx, nbr, name, access, handler, descr)    \
@@ -311,7 +324,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_NODE);        \
        sysctl_add_oid(ctx, &sysctl__children, nbr, name,               \
            CTLTYPE_NODE|(access),                                      \
-           NULL, 0, handler, "N", __DESCR(descr));                     \
+           NULL, 0, handler, "N", __DESCR(descr), NULL);               \
 })
 
 /* Oid for a string.  len can be 0 to indicate '\0' termination. */
@@ -327,7 +340,8 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
        CTASSERT(((access) & CTLTYPE) == 0 ||                           \
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_STRING);      \
        sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_STRING|(access), \
-           __arg, len, sysctl_handle_string, "A", __DESCR(descr));     \
+           __arg, len, sysctl_handle_string, "A", __DESCR(descr),      \
+           NULL); \
 })
 
 /* Oid for a bool.  If ptr is NULL, val is returned. */
@@ -345,7 +359,8 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
        CTASSERT(((access) & CTLTYPE) == 0);                            \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_U8 | CTLFLAG_MPSAFE | (access),                     \
-           __ptr, val, sysctl_handle_bool, "CU", __DESCR(descr));      \
+           __ptr, val, sysctl_handle_bool, "CU", __DESCR(descr),       \
+           NULL);                                                      \
 })
 
 /* Oid for a signed 8-bit int.  If ptr is NULL, val is returned. */
@@ -365,7 +380,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S8);          \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_S8 | CTLFLAG_MPSAFE | (access),                     \
-           __ptr, val, sysctl_handle_8, "C", __DESCR(descr));  \
+           __ptr, val, sysctl_handle_8, "C", __DESCR(descr), NULL);    \
 })
 
 /* Oid for an unsigned 8-bit int.  If ptr is NULL, val is returned. */
@@ -385,7 +400,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U8);          \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_U8 | CTLFLAG_MPSAFE | (access),                     \
-           __ptr, val, sysctl_handle_8, "CU", __DESCR(descr)); \
+           __ptr, val, sysctl_handle_8, "CU", __DESCR(descr), NULL);   \
 })
 
 /* Oid for a signed 16-bit int.  If ptr is NULL, val is returned. */
@@ -405,7 +420,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S16);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_S16 | CTLFLAG_MPSAFE | (access),                    \
-           __ptr, val, sysctl_handle_16, "S", __DESCR(descr)); \
+           __ptr, val, sysctl_handle_16, "S", __DESCR(descr), NULL);   \
 })
 
 /* Oid for an unsigned 16-bit int.  If ptr is NULL, val is returned. */
@@ -425,7 +440,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U16);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_U16 | CTLFLAG_MPSAFE | (access),                    \
-           __ptr, val, sysctl_handle_16, "SU", __DESCR(descr));        \
+           __ptr, val, sysctl_handle_16, "SU", __DESCR(descr), NULL);  \
 })
 
 /* Oid for a signed 32-bit int.  If ptr is NULL, val is returned. */
@@ -445,7 +460,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S32);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_S32 | CTLFLAG_MPSAFE | (access),                    \
-           __ptr, val, sysctl_handle_32, "I", __DESCR(descr)); \
+           __ptr, val, sysctl_handle_32, "I", __DESCR(descr), NULL);   \
 })
 
 /* Oid for an unsigned 32-bit int.  If ptr is NULL, val is returned. */
@@ -465,7 +480,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U32);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_U32 | CTLFLAG_MPSAFE | (access),                    \
-           __ptr, val, sysctl_handle_32, "IU", __DESCR(descr));        \
+           __ptr, val, sysctl_handle_32, "IU", __DESCR(descr), NULL);  \
 })
 
 /* Oid for a signed 64-bit int.  If ptr is NULL, val is returned. */
@@ -485,7 +500,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S64);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_S64 | CTLFLAG_MPSAFE | (access),                    \
-           __ptr, val, sysctl_handle_64, "Q", __DESCR(descr)); \
+           __ptr, val, sysctl_handle_64, "Q", __DESCR(descr), NULL);   \
 })
 
 /* Oid for an unsigned 64-bit int.  If ptr is NULL, val is returned. */
@@ -505,16 +520,19 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U64);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_U64 | CTLFLAG_MPSAFE | (access),                    \
-           __ptr, val, sysctl_handle_64, "QU", __DESCR(descr));        \
+           __ptr, val, sysctl_handle_64, "QU", __DESCR(descr), NULL);  \
 })
 
 /* Oid for an int.  If ptr is SYSCTL_NULL_INT_PTR, val is returned. */
 #define        SYSCTL_NULL_INT_PTR ((int *)NULL)
-#define        SYSCTL_INT(parent, nbr, name, access, ptr, val, descr)  \
-       SYSCTL_OID(parent, nbr, name,                           \
-           CTLTYPE_INT | CTLFLAG_MPSAFE | (access),            \
-           ptr, val, sysctl_handle_int, "I", descr);           \
-       CTASSERT((((access) & CTLTYPE) == 0 ||                  \
+#define        SYSCTL_INT(parent, nbr, name, access, ptr, val, descr) \
+       SYSCTL_INT_WITH_LABEL(parent, nbr, name, access, ptr, val, descr, NULL)
+
+#define        SYSCTL_INT_WITH_LABEL(parent, nbr, name, access, ptr, val, descr, label) \
+       SYSCTL_OID_WITH_LABEL(parent, nbr, name,                        \
+           CTLTYPE_INT | CTLFLAG_MPSAFE | (access),                    \
+           ptr, val, sysctl_handle_int, "I", descr, label);            \
+       CTASSERT((((access) & CTLTYPE) == 0 ||                          \
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_INT) && \
            sizeof(int) == sizeof(*(ptr)))
 
@@ -525,7 +543,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_INT);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_INT | CTLFLAG_MPSAFE | (access),                    \
-           __ptr, val, sysctl_handle_int, "I", __DESCR(descr));        \
+           __ptr, val, sysctl_handle_int, "I", __DESCR(descr), NULL);  \
 })
 
 /* Oid for an unsigned int.  If ptr is NULL, val is returned. */
@@ -545,7 +563,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_UINT);        \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_UINT | CTLFLAG_MPSAFE | (access),                   \
-           __ptr, val, sysctl_handle_int, "IU", __DESCR(descr));       \
+           __ptr, val, sysctl_handle_int, "IU", __DESCR(descr), NULL); \
 })
 
 /* Oid for a long.  The pointer must be non NULL. */
@@ -565,7 +583,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_LONG);        \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_LONG | CTLFLAG_MPSAFE | (access),                   \
-           __ptr, 0, sysctl_handle_long, "L", __DESCR(descr));         \
+           __ptr, 0, sysctl_handle_long, "L", __DESCR(descr), NULL);   \
 })
 
 /* Oid for an unsigned long.  The pointer must be non NULL. */
@@ -585,7 +603,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_ULONG);       \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_ULONG | CTLFLAG_MPSAFE | (access),                  \
-           __ptr, 0, sysctl_handle_long, "LU", __DESCR(descr));        \
+           __ptr, 0, sysctl_handle_long, "LU", __DESCR(descr), NULL);  \
 })
 
 /* Oid for a quad.  The pointer must be non NULL. */
@@ -605,7 +623,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_S64);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_S64 | CTLFLAG_MPSAFE | (access),                    \
-           __ptr, 0, sysctl_handle_64, "Q", __DESCR(descr));           \
+           __ptr, 0, sysctl_handle_64, "Q", __DESCR(descr), NULL);     \
 })
 
 #define        SYSCTL_NULL_UQUAD_PTR ((uint64_t *)NULL)
@@ -624,7 +642,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U64);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_U64 | CTLFLAG_MPSAFE | (access),                    \
-           __ptr, 0, sysctl_handle_64, "QU", __DESCR(descr));          \
+           __ptr, 0, sysctl_handle_64, "QU", __DESCR(descr), NULL);    \
 })
 
 /* Oid for a CPU dependent variable */
@@ -638,12 +656,12 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
                __ret = sysctl_add_oid(ctx, parent, nbr, name,          \
                    CTLTYPE_U64 | CTLFLAG_MPSAFE | (access),            \
                    (ptr), 0, sysctl_handle_64, "QU",                   \
-                   __DESCR(descr));                                    \
+                   __DESCR(descr), NULL);                              \
        } else {                                                        \
                __ret = sysctl_add_oid(ctx, parent, nbr, name,          \
                    CTLTYPE_UINT | CTLFLAG_MPSAFE | (access),           \
                    (ptr), 0, sysctl_handle_int, "IU",                  \
-                   __DESCR(descr));                                    \
+                   __DESCR(descr), NULL);                              \
        }                                                               \
        __ret;                                                          \
 })
@@ -665,7 +683,8 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_U64);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_U64 | CTLFLAG_MPSAFE | (access),                    \
-           __ptr, 0, sysctl_handle_counter_u64, "QU", __DESCR(descr)); \
+           __ptr, 0, sysctl_handle_counter_u64, "QU", __DESCR(descr),  \
+           NULL);                                                      \
 })
 
 /* Oid for an array of counter(9)s.  The pointer and length must be non zero. */
@@ -687,7 +706,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_OPAQUE | CTLFLAG_MPSAFE | (access),                 \
            __ptr, len, sysctl_handle_counter_u64_array, "S",           \
-           __DESCR(descr));                                            \
+           __DESCR(descr), NULL);                                      \
 })
 
 /* Oid for an opaque object.  Specified by a pointer and a length. */
@@ -702,7 +721,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
        CTASSERT(((access) & CTLTYPE) == 0 ||                           \
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE);      \
        sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_OPAQUE|(access), \
-           ptr, len, sysctl_handle_opaque, fmt, __DESCR(descr));       \
+           ptr, len, sysctl_handle_opaque, fmt, __DESCR(descr), NULL); \
 })
 
 /* Oid for a struct.  Specified by a pointer and a type. */
@@ -719,7 +738,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_OPAQUE);      \
        sysctl_add_oid(ctx, parent, nbr, name, CTLTYPE_OPAQUE|(access), \
            (ptr), sizeof(struct type),                                 \
-           sysctl_handle_opaque, "S," #type, __DESCR(descr));          \
+           sysctl_handle_opaque, "S," #type, __DESCR(descr), NULL);    \
 })
 
 /* Oid for a procedure.  Specified by a pointer and an arg. */
@@ -732,7 +751,7 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
 ({                                                                     \
        CTASSERT(((access) & CTLTYPE) != 0);                            \
        sysctl_add_oid(ctx, parent, nbr, name, (access),                \
-           (ptr), (arg), (handler), (fmt), __DESCR(descr));            \
+           (ptr), (arg), (handler), (fmt), __DESCR(descr), NULL);      \
 })
 
 /* Oid to handle limits on uma(9) zone specified by pointer. */
@@ -750,7 +769,8 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_INT);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_INT | CTLFLAG_MPSAFE | (access),                    \
-           __ptr, 0, sysctl_handle_uma_zone_max, "I", __DESCR(descr)); \
+           __ptr, 0, sysctl_handle_uma_zone_max, "I", __DESCR(descr),  \
+           NULL);                                                      \
 })
 
 /* Oid to obtain current use of uma(9) zone specified by pointer. */
@@ -768,7 +788,8 @@ TAILQ_HEAD(sysctl_ctx_list, sysctl_ctx_entry);
            ((access) & SYSCTL_CT_ASSERT_MASK) == CTLTYPE_INT);         \
        sysctl_add_oid(ctx, parent, nbr, name,                          \
            CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RD | (access),       \
-           __ptr, 0, sysctl_handle_uma_zone_cur, "I", __DESCR(descr)); \
+           __ptr, 0, sysctl_handle_uma_zone_cur, "I", __DESCR(descr),  \
+           NULL);                                                      \
 })
 
 /*
@@ -993,7 +1014,7 @@ extern char        kern_ident[];
 struct sysctl_oid *sysctl_add_oid(struct sysctl_ctx_list *clist,
            struct sysctl_oid_list *parent, int nbr, const char *name, int kind,
            void *arg1, intmax_t arg2, int (*handler)(SYSCTL_HANDLER_ARGS),
-           const char *fmt, const char *descr);
+           const char *fmt, const char *descr, const char *label);
 int    sysctl_remove_name(struct sysctl_oid *parent, const char *name, int del,
            int recurse);
 void   sysctl_rename_oid(struct sysctl_oid *oidp, const char *name);