[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v2 2/5] xen/console: allow log level threshold adjustments



... from serial console and via sysctl so that one doesn't always need
to reboot to see more / less messages.

Note that upper thresholds driven from the serial console are sticky,
i.e. while they get adjusted upwards when the lower threshold would
otherwise end up above the upper one, they don't get adjusted when
reducing the lower one. Full flexibility is available only via the
sysctl interface.

Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>

Rework the sysctl interface to pass in / out strings directly. Provide
some helper functions to transform from log level numbers to strings and
vice verse. Lower and upper bounds are checked. Add XSM hook.

Signed-off-by: Wei Liu <wei.liu2@xxxxxxxxxx>
---
Cc: Jan Beulich <jbeulich@xxxxxxxx>
Cc: Daniel De GRaaf <dgdegra@xxxxxxxxxxxxx>
Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
---
 tools/flask/policy/modules/dom0.te  |   2 +-
 xen/common/sysctl.c                 |   5 +
 xen/drivers/char/console.c          | 291 +++++++++++++++++++++++++++++++++++-
 xen/include/public/sysctl.h         |  41 +++++
 xen/include/xen/console.h           |   2 +
 xen/xsm/flask/hooks.c               |   3 +
 xen/xsm/flask/policy/access_vectors |   2 +
 7 files changed, 343 insertions(+), 3 deletions(-)

diff --git a/tools/flask/policy/modules/dom0.te 
b/tools/flask/policy/modules/dom0.te
index 2d982d9..4e45e10 100644
--- a/tools/flask/policy/modules/dom0.te
+++ b/tools/flask/policy/modules/dom0.te
@@ -15,7 +15,7 @@ allow dom0_t xen_t:xen {
 };
 allow dom0_t xen_t:xen2 {
        resource_op psr_cmt_op psr_cat_op pmu_ctrl get_symbol
-       get_cpu_levelling_caps get_cpu_featureset livepatch_op
+       get_cpu_levelling_caps get_cpu_featureset livepatch_op loglvl
 };
 
 # Allow dom0 to use all XENVER_ subops that have checks.
diff --git a/xen/common/sysctl.c b/xen/common/sysctl.c
index 55f2077..cf8fa68 100644
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -467,6 +467,11 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) 
u_sysctl)
             copyback = 1;
         break;
 
+    case XEN_SYSCTL_loglvl_op:
+        ret = console_loglvl_op(&op->u.loglvl);
+        copyback = 1;
+        break;
+
     default:
         ret = arch_do_sysctl(op, u_sysctl);
         copyback = 0;
diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index 6620a1c..df847c4 100644
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -144,7 +144,7 @@ struct log_level {
     unsigned int num;
 };
 
-static struct log_level __initdata log_levels[] = {
+static struct log_level log_levels[] = {
     { "none",    0 },
     { "error",   1 },
     { "warning", 2 },
@@ -152,6 +152,9 @@ static struct log_level __initdata log_levels[] = {
     { "debug",   4 },
     { "all",     4 },
 };
+#define LOG_LEVEL_MIN 0
+#define LOG_LEVEL_MAX 4
+#define LOG_LEVEL_STRLEN_MAX 7  /* "warning" is the longest one */
 
 #define ___parse_loglvl(s, ps, lvlstr, lvlnum)          \
     if ( !strncmp((s), (lvlstr), strlen(lvlstr)) ) {    \
@@ -188,7 +191,41 @@ static void __init parse_guest_loglvl(char *s)
     _parse_loglvl(s, &xenlog_guest_lower_thresh, &xenlog_guest_upper_thresh);
 }
 
-static char * __init loglvl_str(int lvl)
+/*
+ * A variant used to parse log level during runtime. Returns -1 if it
+ * fails to parse.
+ */
+static int parse_log_level(const char *s)
+{
+    unsigned int i;
+
+    for ( i = 0; i < ARRAY_SIZE(log_levels); i++ )
+    {
+        if ( !strcmp(log_levels[i].str, s) )
+            return log_levels[i].num;
+    }
+
+    return -1;
+}
+
+static const char *loglvl_to_str(int lvl)
+{
+    unsigned int i;
+
+    if ( lvl < LOG_LEVEL_MIN || lvl > LOG_LEVEL_MAX )
+        return "???";
+
+    /* Multiple log levels can use the same number. Return the most
+     * comprehensive log level string.
+     */
+    for ( i = ARRAY_SIZE(log_levels) - 1; i >= 0; i-- )
+    {
+        if ( log_levels[i].num == lvl )
+            return log_levels[i].str;
+    }
+}
+
+static char *loglvl_str(int lvl)
 {
     switch ( lvl )
     {
@@ -201,6 +238,250 @@ static char * __init loglvl_str(int lvl)
     return "???";
 }
 
+static int *__read_mostly upper_thresh_adj = &xenlog_upper_thresh;
+static int *__read_mostly lower_thresh_adj = &xenlog_lower_thresh;
+static const char *__read_mostly thresh_adj = "standard";
+
+static void do_toggle_guest(unsigned char key, struct cpu_user_regs *regs)
+{
+    if ( upper_thresh_adj == &xenlog_upper_thresh )
+    {
+        upper_thresh_adj = &xenlog_guest_upper_thresh;
+        lower_thresh_adj = &xenlog_guest_lower_thresh;
+        thresh_adj = "guest";
+    }
+    else
+    {
+        upper_thresh_adj = &xenlog_upper_thresh;
+        lower_thresh_adj = &xenlog_lower_thresh;
+        thresh_adj = "standard";
+    }
+    printk("'%c' pressed -> %s log level adjustments enabled\n",
+           key, thresh_adj);
+}
+
+static void do_adj_thresh(unsigned char key)
+{
+    if ( *upper_thresh_adj < *lower_thresh_adj )
+        *upper_thresh_adj = *lower_thresh_adj;
+    printk("'%c' pressed -> %s log level: %s (rate limited %s)\n",
+           key, thresh_adj, loglvl_str(*lower_thresh_adj),
+           loglvl_str(*upper_thresh_adj));
+}
+
+static void do_inc_thresh(unsigned char key, struct cpu_user_regs *regs)
+{
+    if ( *lower_thresh_adj == LOG_LEVEL_MAX )
+        return;
+    ++*lower_thresh_adj;
+    do_adj_thresh(key);
+}
+
+static void do_dec_thresh(unsigned char key, struct cpu_user_regs *regs)
+{
+    if ( *lower_thresh_adj == LOG_LEVEL_MIN )
+        return;
+    --*lower_thresh_adj;
+    do_adj_thresh(key);
+}
+
+static void __putstr(const char *);
+static void printk_start_of_line(const char *);
+
+static void do_loglvl_op(const int lower_thresh, const int upper_thresh,
+                         int *lower, int *upper, const char *which)
+{
+    if ( lower_thresh < 0 && upper_thresh < 0 )
+        return;
+
+    if ( lower_thresh >= 0 )
+        *lower = lower_thresh;
+
+    if ( upper_thresh >= 0 )
+        *upper = upper_thresh;
+
+    if ( *upper < *lower )
+    {
+        if ( upper_thresh < 0 )
+            *upper = *lower;
+        else
+            *lower = *upper;
+    }
+
+    if ( printk_ratelimit() )
+    {
+        spin_lock_irq(&console_lock);
+        printk_start_of_line("(XEN) ");
+        __putstr(which);
+        __putstr(" log level: ");
+        __putstr(loglvl_str(*lower));
+        __putstr(" (rate limited ");
+        __putstr(loglvl_str(*upper));
+        __putstr(")\n");
+        spin_unlock_irq(&console_lock);
+    }
+}
+
+int console_loglvl_op(struct xen_sysctl_loglvl_op *op)
+{
+    int ret;
+
+    switch ( op->cmd )
+    {
+    default:
+        ret = -EOPNOTSUPP;
+        break;
+
+    case XEN_SYSCTL_LOGLVL_set:
+    {
+        char *buf;
+        unsigned int buf_size = 0;
+        int host_lower_thresh, host_upper_thresh;
+        int guest_lower_thresh, guest_upper_thresh;
+
+        buf_size = op->host.lower_thresh_len;
+        if ( buf_size < op->host.upper_thresh_len )
+            buf_size = op->host.upper_thresh_len;
+        if ( buf_size < op->guest.lower_thresh_len )
+            buf_size = op->guest.lower_thresh_len;
+        if ( buf_size < op->guest.upper_thresh_len )
+            buf_size = op->guest.upper_thresh_len;
+
+        buf = xmalloc_array(char, buf_size);
+        if ( !buf )
+        {
+            ret = -ENOMEM;
+            goto set_done;
+        }
+
+#define parse(hg, lu)                                                   \
+        hg##_##lu##_thresh = -1;                                        \
+        if ( op->hg.lu ##_thresh_len )                                  \
+        {                                                               \
+            if ( copy_from_guest(buf, op->hg.lu ##_thresh,              \
+                                 op->hg.lu ##_thresh_len) )             \
+            {                                                           \
+                ret = -EFAULT;                                          \
+                goto set_done;                                          \
+            }                                                           \
+                                                                        \
+            buf[buf_size-1] = 0;                                        \
+                                                                        \
+            ret = parse_log_level(buf);                                 \
+            if ( ret == -1 )                                            \
+            {                                                           \
+                ret = -EINVAL;                                          \
+                goto set_done;                                          \
+            }                                                           \
+                                                                        \
+            hg##_##lu##_thresh = ret;                                   \
+        }
+
+        parse(host,  lower);
+        parse(host,  upper);
+        parse(guest, lower);
+        parse(guest, upper);
+
+#undef parse
+
+        if ( (host_lower_thresh >= 0 && host_upper_thresh >= 0 &&
+              host_lower_thresh > host_upper_thresh) ||
+             (guest_lower_thresh >= 0 && guest_upper_thresh >= 0 &&
+              guest_lower_thresh > guest_upper_thresh) )
+        {
+            ret = -EINVAL;
+            goto set_done;
+        }
+
+        do_loglvl_op(host_lower_thresh, host_upper_thresh,
+                     &xenlog_lower_thresh, &xenlog_upper_thresh,
+                     "standard");
+
+        do_loglvl_op(guest_lower_thresh, guest_upper_thresh,
+                     &xenlog_guest_lower_thresh, &xenlog_guest_upper_thresh,
+                     "guest");
+
+        ret = 0;
+    set_done:
+        xfree(buf);
+        break;
+    }
+    case XEN_SYSCTL_LOGLVL_get:
+    {
+        unsigned int host_lower_thresh_len =
+            strlen(loglvl_to_str(xenlog_lower_thresh)) + 1;
+        unsigned int host_upper_thresh_len =
+            strlen(loglvl_to_str(xenlog_upper_thresh)) + 1;
+        unsigned int guest_lower_thresh_len =
+            strlen(loglvl_to_str(xenlog_guest_lower_thresh)) + 1;
+        unsigned int guest_upper_thresh_len =
+            strlen(loglvl_to_str(xenlog_guest_upper_thresh)) + 1;
+        char scratch[LOG_LEVEL_STRLEN_MAX+1];
+
+        if ( (op->host.lower_thresh_len &&
+              op->host.lower_thresh_len < host_lower_thresh_len) ||
+             (op->host.upper_thresh_len &&
+              op->host.upper_thresh_len < host_upper_thresh_len) ||
+             (op->guest.lower_thresh_len &&
+              op->guest.lower_thresh_len < guest_lower_thresh_len) ||
+             (op->guest.upper_thresh_len
+              && op->guest.upper_thresh_len < guest_upper_thresh_len)
+            )
+        {
+            ret = -ENOBUFS;
+            goto get_done;
+        }
+
+        ret = -EFAULT;
+
+        if ( op->host.lower_thresh_len )
+        {
+            snprintf(scratch, sizeof(scratch), "%s",
+                     loglvl_to_str(xenlog_lower_thresh));
+            if ( copy_to_guest(op->host.lower_thresh, scratch,
+                               strlen(scratch)+1) )
+                goto get_done;
+        }
+
+        if ( op->host.upper_thresh_len )
+        {
+            snprintf(scratch, sizeof(scratch), "%s",
+                     loglvl_to_str(xenlog_upper_thresh));
+            if ( copy_to_guest(op->host.upper_thresh, scratch,
+                               strlen(scratch)+1) )
+                goto get_done;
+        }
+
+        if ( op->guest.lower_thresh_len )
+        {
+            snprintf(scratch, sizeof(scratch), "%s",
+                     loglvl_to_str(xenlog_guest_lower_thresh));
+            if ( copy_to_guest(op->guest.lower_thresh, scratch,
+                               strlen(scratch)+1) )
+                goto get_done;
+        }
+
+        if ( op->guest.upper_thresh_len )
+        {
+            snprintf(scratch, sizeof(scratch), "%s",
+                     loglvl_to_str(xenlog_guest_upper_thresh));
+            if ( copy_to_guest(op->guest.upper_thresh, scratch,
+                               strlen(scratch)+1) )
+                goto get_done;
+        }
+
+        ret = 0;
+    get_done:
+        op->host.lower_thresh_len = host_lower_thresh_len;
+        op->host.upper_thresh_len = host_upper_thresh_len;
+        op->guest.lower_thresh_len = guest_lower_thresh_len;
+        op->guest.upper_thresh_len = guest_upper_thresh_len;
+        break;
+    }
+    }
+
+    return ret;
+}
 /*
  * ********************************************************
  * *************** ACCESS TO CONSOLE RING *****************
@@ -829,6 +1110,12 @@ void __init console_endboot(void)
 
     register_keyhandler('w', dump_console_ring_key,
                         "synchronously dump console ring buffer (dmesg)", 0);
+    register_irq_keyhandler('+', &do_inc_thresh,
+                            "increase log level threshold", 0);
+    register_irq_keyhandler('-', &do_dec_thresh,
+                            "decrease log level threshold", 0);
+    register_irq_keyhandler('G', &do_toggle_guest,
+                            "toggle host/guest log level adjustment", 0);
 
     /* Serial input is directed to DOM0 by default. */
     switch_serial_input();
diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h
index 8197c14..124a393 100644
--- a/xen/include/public/sysctl.h
+++ b/xen/include/public/sysctl.h
@@ -1031,6 +1031,45 @@ struct xen_sysctl_livepatch_op {
 typedef struct xen_sysctl_livepatch_op xen_sysctl_livepatch_op_t;
 DEFINE_XEN_GUEST_HANDLE(xen_sysctl_livepatch_op_t);
 
+/* XEN_SYSCTL_loglvl_op */
+#define XEN_SYSCTL_LOGLVL_get 0
+#define XEN_SYSCTL_LOGLVL_set 1
+struct xen_sysctl_loglvl_op {
+    uint32_t cmd;        /* XEN_SYSCTL_LOGLVL_* */
+    struct xen_sysctl_loglvl_thresh {
+        /*
+         * IN/OUT variables.
+         *
+         * SET command:
+         *
+         * {lower,upper}_thresh: string representation of lower or
+         * upper threshold of log level.
+         *
+         * {lower,upper}_thresh_len: len == 0 indicates toolstack is
+         * not interested in setting the threshold. len should always
+         * count the trailing 0.
+         *
+         * GET command:
+         *
+         * {lower,upper}_thresh: points to buffer for hypervisor to
+         * fill in.
+         *
+         * {lower,upper}_thresh_len: len == 0 indicates toolstack is
+         * not interested in getting the threshold. For any len != 0,
+         * if len is too small, hypervisor returns -ENOBUFS.  The
+         * actual length of the string representation of threshold
+         * (including trailing 0) is always copied back to to these
+         * fields.
+         */
+        XEN_GUEST_HANDLE_64(char) lower_thresh;
+        XEN_GUEST_HANDLE_64(char) upper_thresh;
+        uint32_t lower_thresh_len;
+        uint32_t upper_thresh_len;
+    } host, guest;
+};
+typedef struct xen_sysctl_loglvl_op xen_sysctl_loglvl_op_t;
+DEFINE_XEN_GUEST_HANDLE(xen_sysctl_loglvl_op_t);
+
 struct xen_sysctl {
     uint32_t cmd;
 #define XEN_SYSCTL_readconsole                    1
@@ -1059,6 +1098,7 @@ struct xen_sysctl {
 #define XEN_SYSCTL_get_cpu_levelling_caps        25
 #define XEN_SYSCTL_get_cpu_featureset            26
 #define XEN_SYSCTL_livepatch_op                  27
+#define XEN_SYSCTL_loglvl_op                     28
     uint32_t interface_version; /* XEN_SYSCTL_INTERFACE_VERSION */
     union {
         struct xen_sysctl_readconsole       readconsole;
@@ -1087,6 +1127,7 @@ struct xen_sysctl {
         struct xen_sysctl_cpu_levelling_caps cpu_levelling_caps;
         struct xen_sysctl_cpu_featureset    cpu_featureset;
         struct xen_sysctl_livepatch_op      livepatch;
+        struct xen_sysctl_loglvl_op         loglvl;
         uint8_t                             pad[128];
     } u;
 };
diff --git a/xen/include/xen/console.h b/xen/include/xen/console.h
index ea06fd8..ac57f78 100644
--- a/xen/include/xen/console.h
+++ b/xen/include/xen/console.h
@@ -12,6 +12,8 @@
 
 struct xen_sysctl_readconsole;
 long read_console_ring(struct xen_sysctl_readconsole *op);
+struct xen_sysctl_loglvl_op;
+int console_loglvl_op(struct xen_sysctl_loglvl_op *);
 
 void console_init_preirq(void);
 void console_init_ring(void);
diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c
index 2692a6f..de6aaa8 100644
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -823,6 +823,9 @@ static int flask_sysctl(int cmd)
         return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
                                     XEN2__LIVEPATCH_OP, NULL);
 
+    case XEN_SYSCTL_loglvl_op:
+        return domain_has_xen(current->domain, XEN2__LOGLVL);
+
     default:
         return avc_unknown_permission("sysctl", cmd);
     }
diff --git a/xen/xsm/flask/policy/access_vectors 
b/xen/xsm/flask/policy/access_vectors
index 49c9a9e..f9d37a2 100644
--- a/xen/xsm/flask/policy/access_vectors
+++ b/xen/xsm/flask/policy/access_vectors
@@ -99,6 +99,8 @@ class xen2
     get_cpu_featureset
 # XEN_SYSCTL_livepatch_op
     livepatch_op
+# XEN_SYSCTL_loglvl_op
+    loglvl
 }
 
 # Classes domain and domain2 consist of operations that a domain performs on
-- 
2.1.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.