Re: [PATCH 2/2 v7] printk: Add monotonic, boottime, and realtime timestamps

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

 



On 08/23/2017 04:45 AM, Petr Mladek wrote:
> On Thu 2017-08-17 09:15:39, Prarit Bhargava wrote:
>> printk.time=1/CONFIG_PRINTK_TIME=1 adds a unmodified local hardware clock
>> timestamp to printk messages.  The local hardware clock loses time each
>> day making it difficult to determine exactly when an issue has occurred in
>> the kernel log, and making it difficult to determine how kernel and
>> hardware issues relate to each other in real time.
>>
>> Make printk output different timestamps by adding options for no
>> timestamp, the local hardware clock, the monotonic clock, the boottime
>> clock, and the real clock.  Allow a user to pick one of the clocks by
>> using the printk.time kernel parameter.  Output the type of clock in
>> /sys/module/printk/parameters/time so userspace programs can interpret the
>> timestamp.
>>
>> diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
>> index fc47863f629c..3c46217629fd 100644
>> --- a/kernel/printk/printk.c
>> +++ b/kernel/printk/printk.c
>> @@ -1202,14 +1205,144 @@ static inline void boot_delay_msec(int level)
>>  }
>>  #endif
>>  
>> -static bool printk_time = IS_ENABLED(CONFIG_PRINTK_TIME);
>> -module_param_named(time, printk_time, bool, S_IRUGO | S_IWUSR);
>> +static int printk_time = CONFIG_PRINTK_TIME_TYPE;
>> +static int printk_time_source;
>> +
>> +/**
>> + * enum timestamp_sources - Timestamp sources for printk() messages.
>> + * @PRINTK_TIME_UNDEFINED: Timestamp undefined.  This option is not selectable
>> + * from the configs, and is used as a reference in the code.
>> + * @PRINTK_TIME_DISABLE: No time stamp.
>> + * @PRINTK_TIME_LOCAL: Local hardware clock timestamp.
>> + * @PRINTK_TIME_BOOT: Boottime clock timestamp.
>> + * @PRINTK_TIME_MONO: Monotonic clock timestamp.
>> + * @PRINTK_TIME_REAL: Realtime clock timestamp.  On 32-bit
>> + * systems selecting the real clock printk timestamp may lead to unlikely
>> + * situations where a timestamp is wrong because the real time offset is read
>> + * without the protection of a sequence lock when printk_get_ts() is set to
>> + * printk_get_real_ns().
>> + */
>> +enum timestamp_sources {
> 
> I guess that this might be static. Also it is related to the
> PRINTK_TIME_TYPE Kconfig option. So the name should be
> enum printk_time_type or so.
> 
> 
>> +	PRINTK_TIME_UNDEFINED = 0,
> 
> I would use -1 for PRINTK_TIME_UNDEFINED or I would remove it
> completely, see below. Then we could use 0 for PRINTK_TIME_DISABLED
> which is more expected.

FIXED along with adjustment of configs (see below).

> 
> 
>> +	PRINTK_TIME_DISABLE = 1,
> 
> This should be PRINTK_TIME_DISABLED to be in sync with
> the string and style of the other flags.
>

FIXED.

> 
>> +	PRINTK_TIME_LOCAL = 2,
>> +	PRINTK_TIME_BOOT = 3,
>> +	PRINTK_TIME_MONO = 4,
>> +	PRINTK_TIME_REAL = 5,
>> +};
>> +
>> +static const char * const timestamp_sources_str[6] = {
>> +	"undefined",
> 
> Do we really need the string "undefined"? 

It is only included to keep timestamp_sources_str & the enum in sync.
FIXED & removed with adjustment of configs & enum.

<snip>

>> +/**
>> + * printk_get_real_ns: - Return a realtime timestamp for printk messages
>> + * On 32-bit systems selecting the real clock printk timestamp may lead to
>> + * unlikely situations where a timestamp is wrong because the real time offset
>> + * is read without the protection of a sequence lock.
>> + */
>> +static u64 printk_get_real_ns(void)
>> +{
>> +	return ktime_get_mono_fast_ns() + ktime_get_real_offset();
>> +}
>> +
>> +static u64 printk_set_timestamp(void)
> 
> I would rename this function to printk_set_ts_func() or so.
> It will be more clear the it sets the function pointer.
> 

FIXED.

> 
>> +{
>> +	switch (printk_time) {
>> +	case PRINTK_TIME_LOCAL:
>> +	case PRINTK_TIME_DISABLE:
>> +	default:
>> +		printk_get_ts = local_clock;
> 
> The PRINTK_TIME_DISABLE case is more complicated. We should set
> printk_get_ts only when it has not been set yet. Otherwise,
> we should keep storing the same type of time stamps into
> to the log buffer and keep it consistent.
> 

I think you've figured this out below.... but for other readers,
printk_time_set() does not call printk_set_timestamp with
printk_time = PRINTK_TIME_DISABLE.

> 
>> +		break;
>> +	case PRINTK_TIME_BOOT:
>> +		printk_get_ts = ktime_get_boot_fast_ns;
>> +		break;
>> +	case PRINTK_TIME_MONO:
>> +		printk_get_ts = ktime_get_mono_fast_ns;
>> +		break;
>> +	case PRINTK_TIME_REAL:
>> +		printk_get_ts = printk_get_real_ns;
>> +		break;
>> +	}
>> +	return printk_get_ts();
> 
> On one hand, this side effect helps to reuse the same code but it
> looks too hacky to me. Another possibility would be to create:
> 
> static u64 printk_get_first_ts()
> {
> 	printk_set_ts_function();
> 
> 	return printk_get_ts();
> }
> 
> and initialize:
> 
> static u64 (*printk_get_ts)(void) = printk_get_first_ts();
> 

FIXED.

> Another side effect is that we might not need PRINTK_TIME_UNDEFINED.
> 

I tried to do this, however, I still need PRINTK_TIME_UNDEFINED to indicate
that printk_time_source is not set.  PRINTK_TIME_UNDEFINED "reads" better IMO.
>From your comment it looks like you're okay with me leaving it defined, just
not as 0.  I have changed it to -1.  FIXED.

> 
>> +}
>> +
>> +static int printk_time_set(const char *val, const struct kernel_param *kp)
>> +{
>> +	char *param = strstrip((char *)val);
>> +	int _printk_time = PRINTK_TIME_UNDEFINED;
>> +	int ts;
>> +
>> +	if (strlen(param) == 1) {
>> +		/* Preserve legacy boolean settings */
>> +		if ((param[0] == '0') || (param[0] == 'n') ||
>> +		    (param[0] == 'N'))
>> +			_printk_time = PRINTK_TIME_DISABLE;
>> +		if ((param[0] == '1') || (param[0] == 'y') ||
>> +		    (param[0] == 'Y'))
>> +			_printk_time = PRINTK_TIME_LOCAL;
>> +	}
>> +	if (_printk_time == PRINTK_TIME_UNDEFINED) {
>> +		for (ts = 0; ts < ARRAY_SIZE(timestamp_sources_str); ts++) {
>> +			if (!strncmp(timestamp_sources_str[ts], param,
>> +				     strlen(param))) {
>> +				_printk_time = ts;
>> +				break;
>> +			}
>> +		}
>> +	}
>> +	if (_printk_time == PRINTK_TIME_UNDEFINED) {
>> +		pr_warn("printk: invalid timestamp option %s\n", param);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (printk_time_source == PRINTK_TIME_UNDEFINED)
>> +		printk_time_source = _printk_time;
>> +#ifndef PRINTK_TIME_DEBUG
>> +	else if ((printk_time_source != _printk_time) &&
>> +		 (_printk_time != PRINTK_TIME_DISABLE)) {
>> +		/*
>> +		 * Only allow enabling and disabling of the current printk_time
>> +		 * setting.  Changing it from one setting to another confuses
>> +		 * userspace.
>> +		 */
>> +		pr_warn("printk: timestamp can only be set to 0, disabled, or %s\n",
>> +			timestamp_sources_str[printk_time_source]);
>> +		return -EINVAL;
>> +	}
>> +#endif
> 
> Do we really need this complication? I guess that people will

This has been requested multiple times by Mark Salyzyn <salyzyn@xxxxxxxxxxx>.
It does not add any additional complexity beyond a (recently requested)
config option.  I like the option FWIW and I just forgot to include it (sorry
Mark) in earlier versions.  I like Mark's idea of adding 'b', r', etc.,
to the timestamps but have put it off for future effort... but your suggestion
below is a better approach [1].  After some discussion with Mark,
I'll see what I can do after this initial patchset is completed.

<snip>

> 
> 
> And this brings an important question. Do you have any plans
> to update userspace for this change (dmesg, crash, syslogd,
> other loggers)?

I mentioned this in an earlier version of these patches -- I have code for
dmesg.  crash, as I said before, doesn't need to be modified according to
Dave Anderson.  dmesg, from util-linux, requires *this* patchset to be in
Linus' tree before they will even look at a patch.

> 
> If I compile the kernel with CONFIG_PRINTK_TIME_LOCAL=y
> and boot with printk.time=realtime. Then I get
> the following output from dmesg --time-format=iso:
> 
> 2017-08-23T09:38:10,000000+0200 RCU: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=4
> 2017-08-23T09:38:10,000000+0200 NR_IRQS: 524544, nr_irqs: 456, preallocated irqs: 16
> 2017-08-23T09:38:10,000000+0200         Offload RCU callbacks from CPUs: .
> 2065-04-14T17:16:20,287498+0200 Console: colour dummy device 80x25
> 2065-04-14T17:16:20,287498+0200 console [tty0] enabled
> 2065-04-14T17:16:20,287498+0200 console [ttyS0] enabled
> 

Yes, you've pointed this out before and I have a solution for dmesg.

> I would like to see patches for at least one tool (dmesg)
> that would make it working properly. It will prove that
> the kernel side allows userspace to handle this
> a reasonable way.
> 
> 
>> +
>> +	printk_time = _printk_time;
>> +	if (printk_time_source > PRINTK_TIME_DISABLE)
> 
> Ah, this actually prevents replacing the printk_get_ts
> with local_clock. IMHO, it will be more clear and safe
> if we handle this in printk_set_timestamp() as suggested above.
> 

FIXED.

> 
>> +		printk_set_timestamp();
>>
>> +
>> +	pr_info("printk: timestamp set to %s\n",
>> +		timestamp_sources_str[printk_time]);
>> +	return 0;
>> +}
>> +
>> +static int printk_time_get(char *buffer, const struct kernel_param *kp)
>> +{
>> +	return scnprintf(buffer, PAGE_SIZE, "%s",
>> +			 timestamp_sources_str[printk_time]);
>> +}
> 
> I know that it would make the code more complicated. But
> I really like the approach used by /sys/power/disk or
> /sys/power/pm_test. They list all possible values
> and put the selected one into [] brackets.
> 

I like the idea too, however, I think that complicates things with userspace
(which as you point out is something that we really need to be concerned
with).  I am using /sys/modules/printk/parameters/time to indicate the
timestamp to userspace.

With my code, userspace would see "realtime" or "disabled".  With your
suggested change they would see "disabled [realtime]".  I think that
file needs to be a simple as possible and be a single entry for userspace
to consume.  IMHO.

> Well, this might be done in a separate patch.
> I am not 100% sure that this actually does not break some
> rules for sysfs.
> 

I don't see anything that says I must list every available option
but I only did a quick overview of the docs.

> 
>> +
>> +static struct kernel_param_ops printk_time_ops = {
>> +	.set = printk_time_set,
>> +	.get = printk_time_get,
> 
> It seems that an often used naming scheme is:
> 
>    .set = param_set_<param_name>
>    .get = param_get_<param_name>
> 
> Please, use it. It think that they make the meaning more obvious.

FIXED.

> 
>> +};
>> +module_param_cb(time, &printk_time_ops, NULL, 0644);
>>  
>>  static size_t print_time(u64 ts, char *buf)
>>  {
>>  	unsigned long rem_nsec;
>>  
>> -	if (!printk_time)
>> +	if (printk_time == PRINTK_TIME_DISABLE)
>>  		return 0;
>>  
>>  	rem_nsec = do_div(ts, 1000000000);
>> @@ -1643,7 +1776,7 @@ static bool cont_add(int facility, int level, enum log_flags flags, const char *
>>  		cont.facility = facility;
>>  		cont.level = level;
>>  		cont.owner = current;
>> -		cont.ts_nsec = local_clock();
>> +		cont.ts_nsec = printk_get_ts();
>>  		cont.flags = flags;
>>  	}
>>  
>> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
>> index 98fe715522e8..0262770d6bc5 100644
>> --- a/lib/Kconfig.debug
>> +++ b/lib/Kconfig.debug
>> @@ -8,12 +8,58 @@ config PRINTK_TIME
>>  	  messages to be added to the output of the syslog() system
>>  	  call and at the console.
>>  
>> +choice
>> +	prompt "printk default clock timestamp" if PRINTK_TIME
>> +	default PRINTK_TIME_LOCAL if PRINTK_TIME
>> +	help
>> +	  This option is selected by setting one of
>> +	  PRINTK_TIME_[DISABLE|LOCAL|BOOT|MONO|REAL] and causes time stamps of
>> +	  the printk() messages to be added to the output of the syslog()
>> +	  system call and at the console.
>> +
>>  	  The timestamp is always recorded internally, and exported
>>  	  to /dev/kmsg. This flag just specifies if the timestamp should
>>  	  be included, not that the timestamp is recorded.
>>  
>>  	  The behavior is also controlled by the kernel command line
>> -	  parameter printk.time=1. See Documentation/admin-guide/kernel-parameters.rst
>> +	  parameter printk.time. See
>> +	  Documentation/admin-guide/kernel-parameters.rst
>> +
>> +config PRINTK_TIME_LOCAL
>> +	bool "Local Clock"
>> +	help
>> +	  Selecting this option causes the time stamps of printk() to be
>> +	  stamped with the unadjusted hardware clock.
>> +
>> +config PRINTK_TIME_BOOT
>> +	bool "CLOCK_BOOTTIME"
>> +	help
>> +	  Selecting this option causes the time stamps of printk() to be
>> +	  stamped with the adjusted boottime clock.
>> +
>> +config PRINTK_TIME_MONO
>> +	bool "CLOCK_MONOTONIC"
>> +	help
>> +	  Selecting this option causes the time stamps of printk() to be
>> +	  stamped with the adjusted monotonic clock.
>> +
>> +config PRINTK_TIME_REAL
>> +	bool "CLOCK_REALTIME"
>> +	help
>> +	  Selecting this option causes the time stamps of printk() to be
>> +	  stamped with the adjusted realtime clock.
> 
> It might make sense to point out that this is in UTC. I would mention
> this also in kernel-parameters. The discussion about an older version
> of this patchset suggests that this is not widely known.
> 

FIXED in Kconfig & kernel-parameters.txt.

> 
>> +endchoice
>> +
>> +config PRINTK_TIME_TYPE
>> +	int
>> +	depends on PRINTK
>> +	range 1 5
>> +	default 1 if !PRINTK_TIME
>> +	default 2 if PRINTK_TIME_LOCAL
>> +	default 3 if PRINTK_TIME_BOOT
>> +	default 4 if PRINTK_TIME_MONO
>> +	default 5 if PRINTK_TIME_REAL
> 
> I am surprised that this even force PRINTK_TIME_TYPE to be
> in sync with the above choice. I would expect that "default"
> would still allow to set another value here and make
> it inconsistent. For example I tried to define:
> 

Aside: I have changed to 0-4 range to remove "undefined" from
timestamp_sources_str[], and modified additional code to reflect the change.

> CONFIG_PRINTK_TIME=y
> CONFIG_PRINTK_TIME_LOCAL=y
> CONFIG_PRINTK_TIME_TYPE=4
> 
> But "make" automatically fixed this to
> 
> CONFIG_PRINTK_TIME=y
> CONFIG_PRINTK_TIME_LOCAL=y
> CONFIG_PRINTK_TIME_TYPE=2
> 
> which is great. I hope that we could rely on this.
> 

Yes, I think this among the reasons why peterz & John recommended this method.

> 
> Best Regards,
> Petr
>

> PS: I agree that this functionality would be useful. My main
> concern is how to "always" display the time correctly, especially
> by userpace tools. It does not make sense to solve the other
> rather cosmetic probles until we have a good answer for
> the main one.
> 

[1]:  Mark ... this is an interesting idea we should pursue in the future,
as I think it would nicely solve your request for the timestamp info.

> Note that we could store the information about the time
> type for each line in struct printk_log for each line.
> There are two unused bits in log_flags (one is LOG_NOCONS).
> The question is how to pass it to userspace.
>

P.
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux