diff mbox

[v2,1/3] console: allow log level threshold adjustments

Message ID 56D9C9F802000078000D992D@prv-mh.provo.novell.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Beulich March 4, 2016, 4:46 p.m. UTC
... 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@suse.com>
---
v2: Add sysctl.
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@suse.com>
---
v2: Add sysctl.

--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -460,6 +460,10 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
         ret = tmem_control(&op->u.tmem_op);
         break;
 
+    case XEN_SYSCTL_loglvl_op:
+        ret = console_loglvl_op(&op->u.loglvl);
+        break;
+
     default:
         ret = arch_do_sysctl(op, u_sysctl);
         copyback = 0;
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -168,7 +168,7 @@ static void __init parse_guest_loglvl(ch
     _parse_loglvl(s, &xenlog_guest_lower_thresh, &xenlog_guest_upper_thresh);
 }
 
-static char * __init loglvl_str(int lvl)
+static char *loglvl_str(int lvl)
 {
     switch ( lvl )
     {
@@ -181,6 +181,119 @@ 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)
+{
+    ++*lower_thresh_adj;
+    do_adj_thresh(key);
+}
+
+static void do_dec_thresh(unsigned char key, struct cpu_user_regs *regs)
+{
+    if ( *lower_thresh_adj )
+        --*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 struct xen_sysctl_loglvl_thresh *op,
+                         int *lower, int *upper, const char *which)
+{
+    if ( op->lower_thresh < 0 && op->upper_thresh < 0 )
+        return;
+
+    if ( op->lower_thresh >= 0 )
+        *lower = op->lower_thresh;
+
+    if ( op->upper_thresh >= 0 )
+        *upper = op->upper_thresh;
+
+    if ( *upper < *lower )
+    {
+        if ( op->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)
+{
+    switch ( op->cmd )
+    {
+    default:
+        return -EOPNOTSUPP;
+
+    case XEN_SYSCTL_LOGLVL_set:
+        if ( (op->host.lower_thresh >= 0 && op->host.upper_thresh >= 0 &&
+              op->host.lower_thresh > op->host.upper_thresh) ||
+             (op->guest.lower_thresh >= 0 && op->guest.upper_thresh >= 0 &&
+              op->guest.lower_thresh > op->guest.upper_thresh) )
+            return -EINVAL;
+
+        do_loglvl_op(&op->host, &xenlog_lower_thresh,
+                     &xenlog_upper_thresh, "standard");
+        do_loglvl_op(&op->guest, &xenlog_guest_lower_thresh,
+                     &xenlog_guest_upper_thresh, "guest");
+
+        /* fall through */
+    case XEN_SYSCTL_LOGLVL_get:
+        op->host.lower_thresh = xenlog_lower_thresh;
+        op->host.upper_thresh = xenlog_upper_thresh;
+
+        op->guest.lower_thresh = xenlog_guest_lower_thresh;
+        op->guest.upper_thresh = xenlog_guest_upper_thresh;
+
+        break;
+    }
+
+    return 0;
+}
 /*
  * ********************************************************
  * *************** ACCESS TO CONSOLE RING *****************
@@ -833,6 +946,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();
--- a/xen/include/public/sysctl.h
+++ b/xen/include/public/sysctl.h
@@ -766,6 +766,17 @@ struct xen_sysctl_tmem_op {
 typedef struct xen_sysctl_tmem_op xen_sysctl_tmem_op_t;
 DEFINE_XEN_GUEST_HANDLE(xen_sysctl_tmem_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 {
+        /* Negative values mean "no adjustment". */
+        int32_t lower_thresh, upper_thresh;
+    } host, guest;
+};
+
 struct xen_sysctl {
     uint32_t cmd;
 #define XEN_SYSCTL_readconsole                    1
@@ -791,6 +802,7 @@ struct xen_sysctl {
 #define XEN_SYSCTL_pcitopoinfo                   22
 #define XEN_SYSCTL_psr_cat_op                    23
 #define XEN_SYSCTL_tmem_op                       24
+#define XEN_SYSCTL_loglvl_op                     25
     uint32_t interface_version; /* XEN_SYSCTL_INTERFACE_VERSION */
     union {
         struct xen_sysctl_readconsole       readconsole;
@@ -816,6 +828,7 @@ struct xen_sysctl {
         struct xen_sysctl_psr_cmt_op        psr_cmt_op;
         struct xen_sysctl_psr_cat_op        psr_cat_op;
         struct xen_sysctl_tmem_op           tmem_op;
+        struct xen_sysctl_loglvl_op         loglvl;
         uint8_t                             pad[128];
     } u;
 };
--- 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);

Comments

Konrad Rzeszutek Wilk March 4, 2016, 8:55 p.m. UTC | #1
> +static void do_inc_thresh(unsigned char key, struct cpu_user_regs *regs)
> +{
> +    ++*lower_thresh_adj;
> +    do_adj_thresh(key);
> +}
> +
> +static void do_dec_thresh(unsigned char key, struct cpu_user_regs *regs)
> +{
> +    if ( *lower_thresh_adj )
> +        --*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 struct xen_sysctl_loglvl_thresh *op,
> +                         int *lower, int *upper, const char *which)
> +{
> +    if ( op->lower_thresh < 0 && op->upper_thresh < 0 )
> +        return;
> +
> +    if ( op->lower_thresh >= 0 )
> +        *lower = op->lower_thresh;
> +
> +    if ( op->upper_thresh >= 0 )
> +        *upper = op->upper_thresh;
> +

..snip..
> +    case XEN_SYSCTL_LOGLVL_set:
> +        if ( (op->host.lower_thresh >= 0 && op->host.upper_thresh >= 0 &&
> +              op->host.lower_thresh > op->host.upper_thresh) ||
> +             (op->guest.lower_thresh >= 0 && op->guest.upper_thresh >= 0 &&
> +              op->guest.lower_thresh > op->guest.upper_thresh) )
> +            return -EINVAL;
> +
> +        do_loglvl_op(&op->host, &xenlog_lower_thresh,
> +                     &xenlog_upper_thresh, "standard");


The keyboard and the sysctl both allow the user to go beyound the XENLOG_
values we have. That is you could set the lower and upper threshold to be
at 9 (or more) say. It will have the same effect as XENLOG_DEBUG (which is 4)
as printk_prefix_check seems to have a simple < check.

But perhaps to be correct only accept only proper values? Not allow
the system admin to set the level to say 31415?
Jan Beulich March 7, 2016, 10:44 a.m. UTC | #2
>>> On 04.03.16 at 21:55, <konrad.wilk@oracle.com> wrote:
>> +    case XEN_SYSCTL_LOGLVL_set:
>> +        if ( (op->host.lower_thresh >= 0 && op->host.upper_thresh >= 0 &&
>> +              op->host.lower_thresh > op->host.upper_thresh) ||
>> +             (op->guest.lower_thresh >= 0 && op->guest.upper_thresh >= 0 &&
>> +              op->guest.lower_thresh > op->guest.upper_thresh) )
>> +            return -EINVAL;
>> +
>> +        do_loglvl_op(&op->host, &xenlog_lower_thresh,
>> +                     &xenlog_upper_thresh, "standard");
> 
> 
> The keyboard and the sysctl both allow the user to go beyound the XENLOG_
> values we have. That is you could set the lower and upper threshold to be
> at 9 (or more) say. It will have the same effect as XENLOG_DEBUG (which is 
> 4)
> as printk_prefix_check seems to have a simple < check.
> 
> But perhaps to be correct only accept only proper values? Not allow
> the system admin to set the level to say 31415?

Since there's no bad side effect from doing so I opted for not
adding respective extra checks, keeping the code easier to read.

Jan
Konrad Rzeszutek Wilk March 7, 2016, 2:41 p.m. UTC | #3
On Mon, Mar 7, 2016 at 5:44 AM, Jan Beulich <JBeulich@suse.com> wrote:
>>>> On 04.03.16 at 21:55, <konrad.wilk@oracle.com> wrote:
>>> +    case XEN_SYSCTL_LOGLVL_set:
>>> +        if ( (op->host.lower_thresh >= 0 && op->host.upper_thresh >= 0 &&
>>> +              op->host.lower_thresh > op->host.upper_thresh) ||
>>> +             (op->guest.lower_thresh >= 0 && op->guest.upper_thresh >= 0 &&
>>> +              op->guest.lower_thresh > op->guest.upper_thresh) )
>>> +            return -EINVAL;
>>> +
>>> +        do_loglvl_op(&op->host, &xenlog_lower_thresh,
>>> +                     &xenlog_upper_thresh, "standard");
>>
>>
>> The keyboard and the sysctl both allow the user to go beyound the XENLOG_
>> values we have. That is you could set the lower and upper threshold to be
>> at 9 (or more) say. It will have the same effect as XENLOG_DEBUG (which is
>> 4)
>> as printk_prefix_check seems to have a simple < check.
>>
>> But perhaps to be correct only accept only proper values? Not allow
>> the system admin to set the level to say 31415?
>
> Since there's no bad side effect from doing so I opted for not
> adding respective extra checks, keeping the code easier to read.
>

Fair enough. Could you perhaps just add that in the commit description?

Also I noticed that this patch is missing an XSM check in flask_sysctl
- could that be added please?
> Jan
>
>
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.xen.org
> http://lists.xen.org/xen-devel
Jan Beulich March 7, 2016, 3:19 p.m. UTC | #4
>>> On 07.03.16 at 15:41, <konrad@kernel.org> wrote:
> On Mon, Mar 7, 2016 at 5:44 AM, Jan Beulich <JBeulich@suse.com> wrote:
>>>>> On 04.03.16 at 21:55, <konrad.wilk@oracle.com> wrote:
>>>> +    case XEN_SYSCTL_LOGLVL_set:
>>>> +        if ( (op->host.lower_thresh >= 0 && op->host.upper_thresh >= 0 &&
>>>> +              op->host.lower_thresh > op->host.upper_thresh) ||
>>>> +             (op->guest.lower_thresh >= 0 && op->guest.upper_thresh >= 0 &&
>>>> +              op->guest.lower_thresh > op->guest.upper_thresh) )
>>>> +            return -EINVAL;
>>>> +
>>>> +        do_loglvl_op(&op->host, &xenlog_lower_thresh,
>>>> +                     &xenlog_upper_thresh, "standard");
>>>
>>>
>>> The keyboard and the sysctl both allow the user to go beyound the XENLOG_
>>> values we have. That is you could set the lower and upper threshold to be
>>> at 9 (or more) say. It will have the same effect as XENLOG_DEBUG (which is
>>> 4)
>>> as printk_prefix_check seems to have a simple < check.
>>>
>>> But perhaps to be correct only accept only proper values? Not allow
>>> the system admin to set the level to say 31415?
>>
>> Since there's no bad side effect from doing so I opted for not
>> adding respective extra checks, keeping the code easier to read.
>>
> 
> Fair enough. Could you perhaps just add that in the commit description?

Sure.

> Also I noticed that this patch is missing an XSM check in flask_sysctl
> - could that be added please?

Of course; it's pretty ugly that one doesn't notice the lack thereof
via a build failure.

Jan
diff mbox

Patch

--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -460,6 +460,10 @@  long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
         ret = tmem_control(&op->u.tmem_op);
         break;
 
+    case XEN_SYSCTL_loglvl_op:
+        ret = console_loglvl_op(&op->u.loglvl);
+        break;
+
     default:
         ret = arch_do_sysctl(op, u_sysctl);
         copyback = 0;
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -168,7 +168,7 @@  static void __init parse_guest_loglvl(ch
     _parse_loglvl(s, &xenlog_guest_lower_thresh, &xenlog_guest_upper_thresh);
 }
 
-static char * __init loglvl_str(int lvl)
+static char *loglvl_str(int lvl)
 {
     switch ( lvl )
     {
@@ -181,6 +181,119 @@  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)
+{
+    ++*lower_thresh_adj;
+    do_adj_thresh(key);
+}
+
+static void do_dec_thresh(unsigned char key, struct cpu_user_regs *regs)
+{
+    if ( *lower_thresh_adj )
+        --*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 struct xen_sysctl_loglvl_thresh *op,
+                         int *lower, int *upper, const char *which)
+{
+    if ( op->lower_thresh < 0 && op->upper_thresh < 0 )
+        return;
+
+    if ( op->lower_thresh >= 0 )
+        *lower = op->lower_thresh;
+
+    if ( op->upper_thresh >= 0 )
+        *upper = op->upper_thresh;
+
+    if ( *upper < *lower )
+    {
+        if ( op->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)
+{
+    switch ( op->cmd )
+    {
+    default:
+        return -EOPNOTSUPP;
+
+    case XEN_SYSCTL_LOGLVL_set:
+        if ( (op->host.lower_thresh >= 0 && op->host.upper_thresh >= 0 &&
+              op->host.lower_thresh > op->host.upper_thresh) ||
+             (op->guest.lower_thresh >= 0 && op->guest.upper_thresh >= 0 &&
+              op->guest.lower_thresh > op->guest.upper_thresh) )
+            return -EINVAL;
+
+        do_loglvl_op(&op->host, &xenlog_lower_thresh,
+                     &xenlog_upper_thresh, "standard");
+        do_loglvl_op(&op->guest, &xenlog_guest_lower_thresh,
+                     &xenlog_guest_upper_thresh, "guest");
+
+        /* fall through */
+    case XEN_SYSCTL_LOGLVL_get:
+        op->host.lower_thresh = xenlog_lower_thresh;
+        op->host.upper_thresh = xenlog_upper_thresh;
+
+        op->guest.lower_thresh = xenlog_guest_lower_thresh;
+        op->guest.upper_thresh = xenlog_guest_upper_thresh;
+
+        break;
+    }
+
+    return 0;
+}
 /*
  * ********************************************************
  * *************** ACCESS TO CONSOLE RING *****************
@@ -833,6 +946,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();
--- a/xen/include/public/sysctl.h
+++ b/xen/include/public/sysctl.h
@@ -766,6 +766,17 @@  struct xen_sysctl_tmem_op {
 typedef struct xen_sysctl_tmem_op xen_sysctl_tmem_op_t;
 DEFINE_XEN_GUEST_HANDLE(xen_sysctl_tmem_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 {
+        /* Negative values mean "no adjustment". */
+        int32_t lower_thresh, upper_thresh;
+    } host, guest;
+};
+
 struct xen_sysctl {
     uint32_t cmd;
 #define XEN_SYSCTL_readconsole                    1
@@ -791,6 +802,7 @@  struct xen_sysctl {
 #define XEN_SYSCTL_pcitopoinfo                   22
 #define XEN_SYSCTL_psr_cat_op                    23
 #define XEN_SYSCTL_tmem_op                       24
+#define XEN_SYSCTL_loglvl_op                     25
     uint32_t interface_version; /* XEN_SYSCTL_INTERFACE_VERSION */
     union {
         struct xen_sysctl_readconsole       readconsole;
@@ -816,6 +828,7 @@  struct xen_sysctl {
         struct xen_sysctl_psr_cmt_op        psr_cmt_op;
         struct xen_sysctl_psr_cat_op        psr_cat_op;
         struct xen_sysctl_tmem_op           tmem_op;
+        struct xen_sysctl_loglvl_op         loglvl;
         uint8_t                             pad[128];
     } u;
 };
--- 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);