2009-07-14 08:03:05

by Zhao Lei

[permalink] [raw]
Subject: [PATCH 1/2] Add function to convert between calendar time and broken-down time for universal use

There are many similar code in kernel for one object:
convert time between calendar time and broken-down time.

Here is some source I found:
fs/ncpfs/dir.c
fs/smbfs/proc.c
fs/fat/misc.c
fs/udf/udftime.c
fs/cifs/netmisc.c
net/netfilter/xt_time.c
drivers/scsi/ips.c
drivers/input/misc/hp_sdc_rtc.c
drivers/rtc/rtc-lib.c
arch/ia64/hp/sim/boot/fw-emu.c
arch/m68k/mac/misc.c
arch/powerpc/kernel/time.c
arch/parisc/include/asm/rtc.h
...

We can make a common function for this type of conversion,
At least we can get following benefit:
1: Make kernel simple and unify
2: Easy to fix bug in converting code
3: Reduce clone of code in future
For example, I'm trying to make ftrace display walltime,
this patch will make me easy.

This code is based on code from glibc-2.6

Signed-off-by: Zhao Lei <[email protected]>
---
include/linux/time.h | 9 +++
kernel/time/Makefile | 2 +-
kernel/time/timeconv.c | 180
++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 190 insertions(+), 1 deletions(-)
create mode 100644 kernel/time/timeconv.c

diff --git a/include/linux/time.h b/include/linux/time.h
index ea16c1a..0ae0e6e 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -151,6 +151,15 @@ extern void update_xtime_cache(u64 nsec);
struct tms;
extern void do_sys_times(struct tms *);

+extern void gmtime(__kernel_time_t totalsecs,
+ unsigned int *year, unsigned int *mon, unsigned int *mday,
+ unsigned int *hour, unsigned int *min, unsigned int *sec,
+ unsigned int *wday, unsigned int *yday);
+extern void localtime(__kernel_time_t totalsecs,
+ unsigned int *year, unsigned int *mon, unsigned int *mday,
+ unsigned int *hour, unsigned int *min, unsigned int *sec,
+ unsigned int *wday, unsigned int *yday);
+
/**
* timespec_to_ns - Convert timespec to nanoseconds
* @ts: pointer to the timespec variable to be converted
diff --git a/kernel/time/Makefile b/kernel/time/Makefile
index 0b0a636..ee26662 100644
--- a/kernel/time/Makefile
+++ b/kernel/time/Makefile
@@ -1,4 +1,4 @@
-obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
timecompare.o
+obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
timecompare.o timeconv.o

obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o
obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o
diff --git a/kernel/time/timeconv.c b/kernel/time/timeconv.c
new file mode 100644
index 0000000..c710605
--- /dev/null
+++ b/kernel/time/timeconv.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation,
Inc.
+ * This file is part of the GNU C Library.
+ * Contributed by Paul Eggert ([email protected]).
+ *
+ * The GNU C Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The GNU C Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the GNU C Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite
330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Converts the calendar time to broken-down time representation
+ * Based on code from glibc-2.6
+ *
+ * 2009-7-14:
+ * Moved from glibc-2.6 to kernel by Zhaolei<[email protected]>
+ */
+
+#include <linux/time.h>
+#include <linux/module.h>
+
+/*
+ * Nonzero if YEAR is a leap year (every 4 years,
+ * except every 100th isn't, and every 400th is).
+ */
+static inline int __isleap(unsigned int year)
+{
+ return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
+}
+
+/* How many days come before each month (0-12). */
+static const unsigned short __mon_yday[2][13] =
+ {
+ /* Normal years. */
+ {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
+ /* Leap years. */
+ {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
+ };
+
+#define SECS_PER_HOUR (60 * 60)
+#define SECS_PER_DAY (SECS_PER_HOUR * 24)
+
+static void __offtime(__kernel_time_t totalsecs, int offset,
+ unsigned int *year, unsigned int *mon, unsigned int *mday,
+ unsigned int *hour, unsigned int *min, unsigned int *sec,
+ unsigned int *wday, unsigned int *yday)
+{
+ long days, rem, y;
+ const unsigned short *ip;
+
+ days = totalsecs / SECS_PER_DAY;
+ rem = totalsecs % SECS_PER_DAY;
+ rem += offset;
+ while (rem < 0) {
+ rem += SECS_PER_DAY;
+ --days;
+ }
+ while (rem >= SECS_PER_DAY) {
+ rem -= SECS_PER_DAY;
+ ++days;
+ }
+
+ if (hour)
+ *hour = rem / SECS_PER_HOUR;
+ rem %= SECS_PER_HOUR;
+ if (min)
+ *min = rem / 60;
+ if (sec)
+ *sec = rem % 60;
+
+ if (wday) {
+ /* January 1, 1970 was a Thursday. */
+ *wday = (4 + days) % 7;
+ if (*wday < 0)
+ *wday += 7;
+ }
+
+ y = 1970;
+
+#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
+#define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV(y, 100) + DIV(y, 400))
+
+ while (days < 0 || days >= (__isleap(y) ? 366 : 365)) {
+ /* Guess a corrected year, assuming 365 days per year. */
+ long yg = y + days / 365 - (days % 365 < 0);
+
+ /* Adjust DAYS and Y to match the guessed year. */
+ days -= ((yg - y) * 365
+ + LEAPS_THRU_END_OF(yg - 1)
+ - LEAPS_THRU_END_OF(y - 1));
+ y = yg;
+ }
+ if (year) {
+ *year = y - 1900;
+ if (*year != y - 1900) {
+ /* The year cannot be represented due to overflow. */
+ *year = -1;
+ }
+ }
+
+ if (yday)
+ *yday = days;
+
+ ip = __mon_yday[__isleap(y)];
+ for (y = 11; days < ip[y]; y--)
+ continue;
+ days -= ip[y];
+ if (mon)
+ *mon = y;
+ if (mday)
+ *mday = days + 1;
+}
+
+/**
+ * gmtime - converts the calendar time to UTC broken-down time
+ *
+ * @totalsecs The number of seconds elapsed since 00:00:00 on
January 1, 1970,
+ * Coordinated Universal Time (UTC).
+ * @year Store the number of years since 1900.
+ * @mon Store the number of months since January, in the range 0
to 11.
+ * @mday Store the day of the month, in the range 1 to 31.
+ * @hour Store the number of hours past midnight, in the range 0 to 23.
+ * @min Store the number of minutes after the hour,
+ * in the range 0 to 59.
+ * @sec Store the number of seconds after the minute, normally
in the
+ * range 0 to 59, but can be up to 60 to allow for leap seconds.
+ * @wday Store the number of days since Sunday, in the range 0 to 6.
+ * @yday Store the number of days since January 1, in the range 0 to
365.
+ *
+ * Similar to the gmtime() in glibc, broken-down time is expressed in
+ * Coordinated Universal Time (UTC).
+ */
+void gmtime(__kernel_time_t totalsecs,
+ unsigned int *year, unsigned int *mon, unsigned int *mday,
+ unsigned int *hour, unsigned int *min, unsigned int *sec,
+ unsigned int *wday, unsigned int *yday)
+{
+ __offtime(totalsecs, 0, year, mon, mday, hour, min, sec, wday, yday);
+}
+EXPORT_SYMBOL(gmtime);
+
+/**
+ * localtime - converts the calendar time to local broken-down time
+ *
+ * @totalsecs The number of seconds elapsed since 00:00:00 on
January 1, 1970,
+ * Coordinated Universal Time (UTC).
+ * @year Store the number of years since 1900.
+ * @mon Store the number of months since January, in the range 0
to 11.
+ * @mday Store the day of the month, in the range 1 to 31.
+ * @hour Store the number of hours past midnight, in the range 0 to 23.
+ * @min Store the number of minutes after the hour,
+ * in the range 0 to 59.
+ * @sec Store the number of seconds after the minute, normally
in the
+ * range 0 to 59, but can be up to 60 to allow for leap seconds.
+ * @wday Store the number of days since Sunday, in the range 0 to 6.
+ * @yday Store the number of days since January 1, in the range 0 to
365.
+ *
+ * Similar to the localtime() in glibc, broken-down time is expressed
+ * relative to sys_tz.
+ */
+void localtime(__kernel_time_t totalsecs,
+ unsigned int *year, unsigned int *mon, unsigned int *mday,
+ unsigned int *hour, unsigned int *min, unsigned int *sec,
+ unsigned int *wday, unsigned int *yday)
+{
+ __offtime(totalsecs, -sys_tz.tz_minuteswest * 60, year, mon, mday,
hour,
+ min, sec, wday, yday);
+}
+EXPORT_SYMBOL(localtime);
--
1.5.5.3


2009-07-14 08:04:30

by Zhao Lei

[permalink] [raw]
Subject: [PATCH 2/2] fs/fat: Use common localtime/gmtime in fat_time_unix2fat()

It is not necessary to write custom code for convert calendar time
to broken-down time.
localtime()/gmtime() is more generic to do that.

Signed-off-by: Zhao Lei <[email protected]>
---
fs/fat/misc.c | 62
++++++++++++++++++--------------------------------------
1 files changed, 20 insertions(+), 42 deletions(-)

diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index a6c2047..d9b6552 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/buffer_head.h>
+#include <linux/time.h>
#include "fat.h"

/*
@@ -155,10 +156,6 @@ extern struct timezone sys_tz;
#define SECS_PER_MIN 60
#define SECS_PER_HOUR (60 * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
-#define UNIX_SECS_1980 315532800L
-#if BITS_PER_LONG == 64
-#define UNIX_SECS_2108 4354819200L
-#endif
/* days between 1.1.70 and 1.1.80 (2 leap days) */
#define DAYS_DELTA (365 * 10 + 2)
/* 120 (2100 - 1980) isn't leap year */
@@ -211,58 +208,40 @@ void fat_time_fat2unix(struct msdos_sb_info *sbi,
struct timespec *ts,
void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts,
__le16 *time, __le16 *date, u8 *time_cs)
{
- time_t second = ts->tv_sec;
- time_t day, leap_day, month, year;
-
- if (!sbi->options.tz_utc)
- second -= sys_tz.tz_minuteswest * SECS_PER_MIN;
+ unsigned int year, mon, mday, hour, min, sec;
+ if (sbi->options.tz_utc) {
+ gmtime(ts->tv_sec,
+ &year, &mon, &mday, &hour, &min, &sec, NULL, NULL);
+ } else {
+ localtime(ts->tv_sec,
+ &year, &mon, &mday, &hour, &min, &sec, NULL, NULL);
+ }

- /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
- if (second < UNIX_SECS_1980) {
+ /* FAT can only support year between 1980 to 2107 */
+ if (year < 1980 - 1900) {
*time = 0;
*date = cpu_to_le16((0 << 9) | (1 << 5) | 1);
if (time_cs)
*time_cs = 0;
return;
}
-#if BITS_PER_LONG == 64
- if (second >= UNIX_SECS_2108) {
+ if (year > 2107 - 1900) {
*time = cpu_to_le16((23 << 11) | (59 << 5) | 29);
*date = cpu_to_le16((127 << 9) | (12 << 5) | 31);
if (time_cs)
*time_cs = 199;
return;
}
-#endif

- day = second / SECS_PER_DAY - DAYS_DELTA;
- year = day / 365;
- leap_day = (year + 3) / 4;
- if (year > YEAR_2100) /* 2100 isn't leap year */
- leap_day--;
- if (year * 365 + leap_day > day)
- year--;
- leap_day = (year + 3) / 4;
- if (year > YEAR_2100) /* 2100 isn't leap year */
- leap_day--;
- day -= year * 365 + leap_day;
+ /* from 1900 -> from 1980 */
+ year -= 80;
+ /* 0~11 -> 1~12 */
+ mon++;
+ /* 0~59 -> 0~29(2sec counts) */
+ sec >>= 1;

- if (IS_LEAP_YEAR(year) && day == days_in_year[3]) {
- month = 2;
- } else {
- if (IS_LEAP_YEAR(year) && day > days_in_year[3])
- day--;
- for (month = 1; month < 12; month++) {
- if (days_in_year[month + 1] > day)
- break;
- }
- }
- day -= days_in_year[month];
-
- *time = cpu_to_le16(((second / SECS_PER_HOUR) % 24) << 11
- | ((second / SECS_PER_MIN) % 60) << 5
- | (second % SECS_PER_MIN) >> 1);
- *date = cpu_to_le16((year << 9) | (month << 5) | (day + 1));
+ *time = cpu_to_le16(hour << 11 | min << 5 | sec);
+ *date = cpu_to_le16(year << 9 | mon << 5 | mday);
if (time_cs)
*time_cs = (ts->tv_sec & 1) * 100 + ts->tv_nsec / 10000000;
}
@@ -283,4 +262,3 @@ int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs)
}
return err;
}
-
--
1.5.5.3

2009-07-14 22:11:12

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 1/2] Add function to convert between calendar time and broken-down time for universal use

On Tue, 14 Jul 2009 16:03:12 +0800
Zhaolei <[email protected]> wrote:

> There are many similar code in kernel for one object:
> convert time between calendar time and broken-down time.
>
> Here is some source I found:
> fs/ncpfs/dir.c
> fs/smbfs/proc.c
> fs/fat/misc.c
> fs/udf/udftime.c
> fs/cifs/netmisc.c
> net/netfilter/xt_time.c
> drivers/scsi/ips.c
> drivers/input/misc/hp_sdc_rtc.c
> drivers/rtc/rtc-lib.c
> arch/ia64/hp/sim/boot/fw-emu.c
> arch/m68k/mac/misc.c
> arch/powerpc/kernel/time.c
> arch/parisc/include/asm/rtc.h
> ...
>
> We can make a common function for this type of conversion,
> At least we can get following benefit:
> 1: Make kernel simple and unify
> 2: Easy to fix bug in converting code
> 3: Reduce clone of code in future
> For example, I'm trying to make ftrace display walltime,
> this patch will make me easy.
>

The objective is a good one. Have you verified that these new library
functions can be used by most/all of the above callers?

>
> Signed-off-by: Zhao Lei <[email protected]>
> ---
> include/linux/time.h | 9 +++
> kernel/time/Makefile | 2 +-
> kernel/time/timeconv.c | 180
> ++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 190 insertions(+), 1 deletions(-)
> create mode 100644 kernel/time/timeconv.c
>
> diff --git a/include/linux/time.h b/include/linux/time.h
> index ea16c1a..0ae0e6e 100644
> --- a/include/linux/time.h
> +++ b/include/linux/time.h
> @@ -151,6 +151,15 @@ extern void update_xtime_cache(u64 nsec);
> struct tms;
> extern void do_sys_times(struct tms *);
>
> +extern void gmtime(__kernel_time_t totalsecs,
> + unsigned int *year, unsigned int *mon, unsigned int *mday,
> + unsigned int *hour, unsigned int *min, unsigned int *sec,
> + unsigned int *wday, unsigned int *yday);
> +extern void localtime(__kernel_time_t totalsecs,
> + unsigned int *year, unsigned int *mon, unsigned int *mday,
> + unsigned int *hour, unsigned int *min, unsigned int *sec,
> + unsigned int *wday, unsigned int *yday);

I'd imagine that many callers would at least need some types changed -
replace `int' with `unsigned int', etc. Not a big problem.

> /**
> * timespec_to_ns - Convert timespec to nanoseconds
> * @ts: pointer to the timespec variable to be converted
> diff --git a/kernel/time/Makefile b/kernel/time/Makefile
> index 0b0a636..ee26662 100644
> --- a/kernel/time/Makefile
> +++ b/kernel/time/Makefile
> @@ -1,4 +1,4 @@
> -obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
> timecompare.o
> +obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
> timecompare.o timeconv.o
>
> obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o
> obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o
> diff --git a/kernel/time/timeconv.c b/kernel/time/timeconv.c
> new file mode 100644
> index 0000000..c710605
> --- /dev/null
> +++ b/kernel/time/timeconv.c
> @@ -0,0 +1,180 @@
> +/*
> + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation,
> Inc.

The patch is significantly wordwrapped by your email client.

The patch has no tabs in it at all - either some serious cleanup is
needed of your email client is performing tab-to-space conversion.

> + * This file is part of the GNU C Library.
> + * Contributed by Paul Eggert ([email protected]).
> + *
> + * The GNU C Library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Library General Public License as
> + * published by the Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version.
> + *
> + * The GNU C Library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Library General Public License for more details.
> + *
> + * You should have received a copy of the GNU Library General Public
> + * License along with the GNU C Library; see the file COPYING.LIB. If not,
> + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite
> 330,
> + * Boston, MA 02111-1307, USA.
> + */
> +
> +/*
> + * Converts the calendar time to broken-down time representation
> + * Based on code from glibc-2.6
> + *
> + * 2009-7-14:
> + * Moved from glibc-2.6 to kernel by Zhaolei<[email protected]>
> + */
> +
> +#include <linux/time.h>
> +#include <linux/module.h>
> +
> +/*
> + * Nonzero if YEAR is a leap year (every 4 years,
> + * except every 100th isn't, and every 400th is).
> + */
> +static inline int __isleap(unsigned int year)
> +{
> + return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
> +}

The explicit `inline' probably isn't beneficial here.

> +/* How many days come before each month (0-12). */
> +static const unsigned short __mon_yday[2][13] =
> + {
> + /* Normal years. */
> + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
> + /* Leap years. */
> + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
> + };
> +
> +#define SECS_PER_HOUR (60 * 60)
> +#define SECS_PER_DAY (SECS_PER_HOUR * 24)

I wonder if these whould be in a header. I guess not, if there aren't
any sites which can use this.

> +static void __offtime(__kernel_time_t totalsecs, int offset,
> + unsigned int *year, unsigned int *mon, unsigned int *mday,
> + unsigned int *hour, unsigned int *min, unsigned int *sec,
> + unsigned int *wday, unsigned int *yday)
> +{
> + long days, rem, y;
> + const unsigned short *ip;
> +
> + days = totalsecs / SECS_PER_DAY;
> + rem = totalsecs % SECS_PER_DAY;
> + rem += offset;
> + while (rem < 0) {
> + rem += SECS_PER_DAY;
> + --days;
> + }
> + while (rem >= SECS_PER_DAY) {
> + rem -= SECS_PER_DAY;
> + ++days;
> + }
> +
> + if (hour)
> + *hour = rem / SECS_PER_HOUR;
> + rem %= SECS_PER_HOUR;
> + if (min)
> + *min = rem / 60;
> + if (sec)
> + *sec = rem % 60;
> +
> + if (wday) {
> + /* January 1, 1970 was a Thursday. */
> + *wday = (4 + days) % 7;
> + if (*wday < 0)
> + *wday += 7;
> + }

The code should all be converted to standard kernel style, please.
Mainly the use of hard tabs.


> + y = 1970;
> +
> +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))

hm, I wonder what that does.

It would be clearer to convert this into a regular C function, along
with a comment whcih explains what it does.

> +#define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV(y, 100) + DIV(y, 400))

Ditto.

> + while (days < 0 || days >= (__isleap(y) ? 366 : 365)) {
> + /* Guess a corrected year, assuming 365 days per year. */
> + long yg = y + days / 365 - (days % 365 < 0);
> +
> + /* Adjust DAYS and Y to match the guessed year. */
> + days -= ((yg - y) * 365
> + + LEAPS_THRU_END_OF(yg - 1)
> + - LEAPS_THRU_END_OF(y - 1));
> + y = yg;
> + }
> + if (year) {
> + *year = y - 1900;
> + if (*year != y - 1900) {
> + /* The year cannot be represented due to overflow. */
> + *year = -1;
> + }
> + }
> +
> + if (yday)
> + *yday = days;
> +
> + ip = __mon_yday[__isleap(y)];
> + for (y = 11; days < ip[y]; y--)
> + continue;
> + days -= ip[y];
> + if (mon)
> + *mon = y;
> + if (mday)
> + *mday = days + 1;
> +}

>
> ...
>
> +void gmtime(__kernel_time_t totalsecs,
> + unsigned int *year, unsigned int *mon, unsigned int *mday,
> + unsigned int *hour, unsigned int *min, unsigned int *sec,
> + unsigned int *wday, unsigned int *yday)
> +{
> + __offtime(totalsecs, 0, year, mon, mday, hour, min, sec, wday, yday);
> +}
> +EXPORT_SYMBOL(gmtime);
>
> ...
>
> +void localtime(__kernel_time_t totalsecs,
> + unsigned int *year, unsigned int *mon, unsigned int *mday,
> + unsigned int *hour, unsigned int *min, unsigned int *sec,
> + unsigned int *wday, unsigned int *yday)
> +{
> + __offtime(totalsecs, -sys_tz.tz_minuteswest * 60, year, mon, mday,
> hour,
> + min, sec, wday, yday);
> +}
> +EXPORT_SYMBOL(localtime);

These are such simple wrappers around __offtime() that it might be
better to make them static inlines in time.h, so callers will end up
directly calling __offtime() rather than having to remarshal ten
function arguments.

2009-07-15 00:59:41

by Zhao Lei

[permalink] [raw]
Subject: Re: Re: [PATCH 1/2] Add function to convert between calendar time and broken-down time for universal use

Andrew Morton wrote:
> On Tue, 14 Jul 2009 16:03:12 +0800
> Zhaolei <[email protected]> wrote:
>
>
>> There are many similar code in kernel for one object:
>> convert time between calendar time and broken-down time.
>>
>> Here is some source I found:
>> fs/ncpfs/dir.c
>> fs/smbfs/proc.c
>> fs/fat/misc.c
>> fs/udf/udftime.c
>> fs/cifs/netmisc.c
>> net/netfilter/xt_time.c
>> drivers/scsi/ips.c
>> drivers/input/misc/hp_sdc_rtc.c
>> drivers/rtc/rtc-lib.c
>> arch/ia64/hp/sim/boot/fw-emu.c
>> arch/m68k/mac/misc.c
>> arch/powerpc/kernel/time.c
>> arch/parisc/include/asm/rtc.h
>> ...
>>
>> We can make a common function for this type of conversion,
>> At least we can get following benefit:
>> 1: Make kernel simple and unify
>> 2: Easy to fix bug in converting code
>> 3: Reduce clone of code in future
>> For example, I'm trying to make ftrace display walltime,
>> this patch will make me easy.
>>
>>
>
> The objective is a good one. Have you verified that these new library
> functions can be used by most/all of the above callers?
>

Hello, Andrew

Thanks for your review.

I checked some of them, and I think this new library can make them simple.
A example is my patch for fs/fat/misc.c.

>
>> Signed-off-by: Zhao Lei <[email protected]>
>> ---
>> include/linux/time.h | 9 +++
>> kernel/time/Makefile | 2 +-
>> kernel/time/timeconv.c | 180
>> ++++++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 190 insertions(+), 1 deletions(-)
>> create mode 100644 kernel/time/timeconv.c
>>
>> diff --git a/include/linux/time.h b/include/linux/time.h
>> index ea16c1a..0ae0e6e 100644
>> --- a/include/linux/time.h
>> +++ b/include/linux/time.h
>> @@ -151,6 +151,15 @@ extern void update_xtime_cache(u64 nsec);
>> struct tms;
>> extern void do_sys_times(struct tms *);
>>
>> +extern void gmtime(__kernel_time_t totalsecs,
>> + unsigned int *year, unsigned int *mon, unsigned int *mday,
>> + unsigned int *hour, unsigned int *min, unsigned int *sec,
>> + unsigned int *wday, unsigned int *yday);
>> +extern void localtime(__kernel_time_t totalsecs,
>> + unsigned int *year, unsigned int *mon, unsigned int *mday,
>> + unsigned int *hour, unsigned int *min, unsigned int *sec,
>> + unsigned int *wday, unsigned int *yday);
>>
>
> I'd imagine that many callers would at least need some types changed -
> replace `int' with `unsigned int', etc. Not a big problem.
>

Actually, I considered to use int instead, but at last I use unsigned int to
make it same with mktime() which is already in kernel.

>
>> /**
>> * timespec_to_ns - Convert timespec to nanoseconds
>> * @ts: pointer to the timespec variable to be converted
>> diff --git a/kernel/time/Makefile b/kernel/time/Makefile
>> index 0b0a636..ee26662 100644
>> --- a/kernel/time/Makefile
>> +++ b/kernel/time/Makefile
>> @@ -1,4 +1,4 @@
>> -obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
>> timecompare.o
>> +obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
>> timecompare.o timeconv.o
>>
>> obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o
>> obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o
>> diff --git a/kernel/time/timeconv.c b/kernel/time/timeconv.c
>> new file mode 100644
>> index 0000000..c710605
>> --- /dev/null
>> +++ b/kernel/time/timeconv.c
>> @@ -0,0 +1,180 @@
>> +/*
>> + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation,
>> Inc.
>>
>
> The patch is significantly wordwrapped by your email client.
>
> The patch has no tabs in it at all - either some serious cleanup is
> needed of your email client is performing tab-to-space conversion.
>

Sorry...I reinstalled a email client......
I'll fix it and resend this patch.


>> + * This file is part of the GNU C Library.
>> + * Contributed by Paul Eggert ([email protected]).
>> + *
>> + * The GNU C Library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Library General Public License as
>> + * published by the Free Software Foundation; either version 2 of the
>> + * License, or (at your option) any later version.
>> + *
>> + * The GNU C Library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> + * Library General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Library General Public
>> + * License along with the GNU C Library; see the file COPYING.LIB. If not,
>> + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite
>> 330,
>> + * Boston, MA 02111-1307, USA.
>> + */
>> +
>> +/*
>> + * Converts the calendar time to broken-down time representation
>> + * Based on code from glibc-2.6
>> + *
>> + * 2009-7-14:
>> + * Moved from glibc-2.6 to kernel by Zhaolei<[email protected]>
>> + */
>> +
>> +#include <linux/time.h>
>> +#include <linux/module.h>
>> +
>> +/*
>> + * Nonzero if YEAR is a leap year (every 4 years,
>> + * except every 100th isn't, and every 400th is).
>> + */
>> +static inline int __isleap(unsigned int year)
>> +{
>> + return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
>> +}
>>
>
> The explicit `inline' probably isn't beneficial here.
>

OK, I'll remove it.

>
>> +/* How many days come before each month (0-12). */
>> +static const unsigned short __mon_yday[2][13] =
>> + {
>> + /* Normal years. */
>> + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
>> + /* Leap years. */
>> + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
>> + };
>> +
>> +#define SECS_PER_HOUR (60 * 60)
>> +#define SECS_PER_DAY (SECS_PER_HOUR * 24)
>>
>
> I wonder if these whould be in a header. I guess not, if there aren't
> any sites which can use this.
>

Agree, IMHO, it is not necessary to move it into header.

>
>> +static void __offtime(__kernel_time_t totalsecs, int offset,
>> + unsigned int *year, unsigned int *mon, unsigned int *mday,
>> + unsigned int *hour, unsigned int *min, unsigned int *sec,
>> + unsigned int *wday, unsigned int *yday)
>> +{
>> + long days, rem, y;
>> + const unsigned short *ip;
>> +
>> + days = totalsecs / SECS_PER_DAY;
>> + rem = totalsecs % SECS_PER_DAY;
>> + rem += offset;
>> + while (rem < 0) {
>> + rem += SECS_PER_DAY;
>> + --days;
>> + }
>> + while (rem >= SECS_PER_DAY) {
>> + rem -= SECS_PER_DAY;
>> + ++days;
>> + }
>> +
>> + if (hour)
>> + *hour = rem / SECS_PER_HOUR;
>> + rem %= SECS_PER_HOUR;
>> + if (min)
>> + *min = rem / 60;
>> + if (sec)
>> + *sec = rem % 60;
>> +
>> + if (wday) {
>> + /* January 1, 1970 was a Thursday. */
>> + *wday = (4 + days) % 7;
>> + if (*wday < 0)
>> + *wday += 7;
>> + }
>>
>
> The code should all be converted to standard kernel style, please.
> Mainly the use of hard tabs.
>

Sorry, I'll resend this patch.

>> + y = 1970;
>> +
>> +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
>>
>
> hm, I wonder what that does.
>
> It would be clearer to convert this into a regular C function, along
> with a comment whcih explains what it does.
>
>
>> +#define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV(y, 100) + DIV(y, 400))
>>
>
> Ditto.
>

It is because I try to keep similar style with code in glibc so that updates
in glibc can easy to applied here.
But actually it looks ugly, I'll fix it.


>
>> + while (days < 0 || days >= (__isleap(y) ? 366 : 365)) {
>> + /* Guess a corrected year, assuming 365 days per year. */
>> + long yg = y + days / 365 - (days % 365 < 0);
>> +
>> + /* Adjust DAYS and Y to match the guessed year. */
>> + days -= ((yg - y) * 365
>> + + LEAPS_THRU_END_OF(yg - 1)
>> + - LEAPS_THRU_END_OF(y - 1));
>> + y = yg;
>> + }
>> + if (year) {
>> + *year = y - 1900;
>> + if (*year != y - 1900) {
>> + /* The year cannot be represented due to overflow. */
>> + *year = -1;
>> + }
>> + }
>> +
>> + if (yday)
>> + *yday = days;
>> +
>> + ip = __mon_yday[__isleap(y)];
>> + for (y = 11; days < ip[y]; y--)
>> + continue;
>> + days -= ip[y];
>> + if (mon)
>> + *mon = y;
>> + if (mday)
>> + *mday = days + 1;
>> +}
>>
>
>
>> ...
>>
>> +void gmtime(__kernel_time_t totalsecs,
>> + unsigned int *year, unsigned int *mon, unsigned int *mday,
>> + unsigned int *hour, unsigned int *min, unsigned int *sec,
>> + unsigned int *wday, unsigned int *yday)
>> +{
>> + __offtime(totalsecs, 0, year, mon, mday, hour, min, sec, wday, yday);
>> +}
>> +EXPORT_SYMBOL(gmtime);
>>
>> ...
>>
>> +void localtime(__kernel_time_t totalsecs,
>> + unsigned int *year, unsigned int *mon, unsigned int *mday,
>> + unsigned int *hour, unsigned int *min, unsigned int *sec,
>> + unsigned int *wday, unsigned int *yday)
>> +{
>> + __offtime(totalsecs, -sys_tz.tz_minuteswest * 60, year, mon, mday,
>> hour,
>> + min, sec, wday, yday);
>> +}
>> +EXPORT_SYMBOL(localtime);
>>
>
> These are such simple wrappers around __offtime() that it might be
> better to make them static inlines in time.h, so callers will end up
> directly calling __offtime() rather than having to remarshal ten
> function arguments.
>

Good idea, will fix.

Thanks
Zhaolei

2009-07-15 07:23:18

by Zhao Lei

[permalink] [raw]
Subject: [PATCH v2 0/2] Add function to convert between calendar time and broken-down time for universal use

Hello, Andrew

I modified this patch based on your review.
Here is changes:
1: Fix "no tabs" error caused by email client.
2: Remove explicit 'inline' in __isleap()
3: Move DIV() and LEAPS_THRU_END_OF(y) into regular C function along with
comment
4: Move gmtime() and localtime() into header file.

Thanks
Zhaolei

2009-07-15 07:24:23

by Zhao Lei

[permalink] [raw]
Subject: [PATCH v2 1/2] Add function to convert between calendar time and broken-down time for universal use

There are many similar code in kernel for one object:
convert time between calendar time and broken-down time.

Here is some source I found:
fs/ncpfs/dir.c
fs/smbfs/proc.c
fs/fat/misc.c
fs/udf/udftime.c
fs/cifs/netmisc.c
net/netfilter/xt_time.c
drivers/scsi/ips.c
drivers/input/misc/hp_sdc_rtc.c
drivers/rtc/rtc-lib.c
arch/ia64/hp/sim/boot/fw-emu.c
arch/m68k/mac/misc.c
arch/powerpc/kernel/time.c
arch/parisc/include/asm/rtc.h
...

We can make a common function for this type of convert,
At least we can get following benefit:
1: Make kernel simple and unify
2: Easy to fix bug in converting code
3: Reduce clone of code in future
For example, I'm trying to make ftrace display walltime,
this patch will make me easy.

This code is based on code from glibc-2.6

Signed-off-by: Zhao Lei <[email protected]>
---
include/linux/time.h | 62 +++++++++++++++++++
kernel/time/Makefile | 2 +-
kernel/time/timeconv.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 216 insertions(+), 1 deletions(-)
create mode 100644 kernel/time/timeconv.c

diff --git a/include/linux/time.h b/include/linux/time.h
index ea16c1a..658f8e1 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -151,6 +151,68 @@ extern void update_xtime_cache(u64 nsec);
struct tms;
extern void do_sys_times(struct tms *);

+extern void __offtime(__kernel_time_t totalsecs, int offset,
+ unsigned int *year, unsigned int *mon, unsigned int *mday,
+ unsigned int *hour, unsigned int *min, unsigned int *sec,
+ unsigned int *wday, unsigned int *yday);
+
+/**
+ * gmtime - converts the calendar time to UTC broken-down time
+ *
+ * @totalsecs The number of seconds elapsed since 00:00:00 on January 1, 1970,
+ * Coordinated Universal Time (UTC).
+ * @year Store the number of years since 1900.
+ * @mon Store the number of months since January, in the range 0 to 11.
+ * @mday Store the day of the month, in the range 1 to 31.
+ * @hour Store the number of hours past midnight, in the range 0 to 23.
+ * @min Store the number of minutes after the hour,
+ * in the range 0 to 59.
+ * @sec Store the number of seconds after the minute, normally in the
+ * range 0 to 59, but can be up to 60 to allow for leap seconds.
+ * @wday Store the number of days since Sunday, in the range 0 to 6.
+ * @yday Store the number of days since January 1, in the range 0 to 365.
+ *
+ * Similar to the gmtime() in glibc, broken-down time is expressed in
+ * Coordinated Universal Time (UTC).
+ */
+static inline void gmtime(__kernel_time_t totalsecs,
+ unsigned int *year, unsigned int *mon,
+ unsigned int *mday, unsigned int *hour,
+ unsigned int *min, unsigned int *sec,
+ unsigned int *wday, unsigned int *yday)
+{
+ __offtime(totalsecs, 0, year, mon, mday, hour, min, sec, wday, yday);
+}
+
+/**
+ * localtime - converts the calendar time to local broken-down time
+ *
+ * @totalsecs The number of seconds elapsed since 00:00:00 on January 1, 1970,
+ * Coordinated Universal Time (UTC).
+ * @year Store the number of years since 1900.
+ * @mon Store the number of months since January, in the range 0 to 11.
+ * @mday Store the day of the month, in the range 1 to 31.
+ * @hour Store the number of hours past midnight, in the range 0 to 23.
+ * @min Store the number of minutes after the hour,
+ * in the range 0 to 59.
+ * @sec Store the number of seconds after the minute, normally in the
+ * range 0 to 59, but can be up to 60 to allow for leap seconds.
+ * @wday Store the number of days since Sunday, in the range 0 to 6.
+ * @yday Store the number of days since January 1, in the range 0 to 365.
+ *
+ * Similar to the localtime() in glibc, broken-down time is expressed
+ * relative to sys_tz.
+ */
+static inline void localtime(__kernel_time_t totalsecs,
+ unsigned int *year, unsigned int *mon,
+ unsigned int *mday, unsigned int *hour,
+ unsigned int *min, unsigned int *sec,
+ unsigned int *wday, unsigned int *yday)
+{
+ __offtime(totalsecs, -sys_tz.tz_minuteswest * 60, year, mon, mday, hour,
+ min, sec, wday, yday);
+}
+
/**
* timespec_to_ns - Convert timespec to nanoseconds
* @ts: pointer to the timespec variable to be converted
diff --git a/kernel/time/Makefile b/kernel/time/Makefile
index 0b0a636..ee26662 100644
--- a/kernel/time/Makefile
+++ b/kernel/time/Makefile
@@ -1,4 +1,4 @@
-obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o timecompare.o
+obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o timecompare.o timeconv.o

obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o
obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o
diff --git a/kernel/time/timeconv.c b/kernel/time/timeconv.c
new file mode 100644
index 0000000..b28fcec
--- /dev/null
+++ b/kernel/time/timeconv.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
+ * This file is part of the GNU C Library.
+ * Contributed by Paul Eggert ([email protected]).
+ *
+ * The GNU C Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The GNU C Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the GNU C Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Converts the calendar time to broken-down time representation
+ * Based on code from glibc-2.6
+ *
+ * 2009-7-14:
+ * Moved from glibc-2.6 to kernel by Zhaolei<[email protected]>
+ */
+
+#include <linux/time.h>
+#include <linux/module.h>
+
+/*
+ * Nonzero if YEAR is a leap year (every 4 years,
+ * except every 100th isn't, and every 400th is).
+ */
+static int __isleap(unsigned int year)
+{
+ return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
+}
+
+/* do a mathdiv for long type */
+static long math_div(long a, long b)
+{
+ return a / b - (a % b < 0);
+}
+
+/* How many leap years between y1 and y2, y1 must less or equal to y2 */
+static long leaps_between(long y1, long y2)
+{
+ long leaps1 = math_div(y1 - 1, 4) - math_div(y1 - 1, 100)
+ + math_div(y1 - 1, 400);
+ long leaps2 = math_div(y2 - 1, 4) - math_div(y2 - 1, 100)
+ + math_div(y2 - 1, 400);
+ return leaps2 - leaps1;
+}
+
+/* How many days come before each month (0-12). */
+static const unsigned short __mon_yday[2][13] = {
+ /* Normal years. */
+ {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
+ /* Leap years. */
+ {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
+};
+
+#define SECS_PER_HOUR (60 * 60)
+#define SECS_PER_DAY (SECS_PER_HOUR * 24)
+
+/**
+ * __offtime - converts the calendar time to local broken-down time
+ *
+ * @totalsecs The number of seconds elapsed since 00:00:00 on January 1, 1970,
+ * Coordinated Universal Time (UTC).
+ * @offset Offset seconds adding to totalsecs.
+ * @year Store the number of years since 1900.
+ * @mon Store the number of months since January, in the range 0 to 11.
+ * @mday Store the day of the month, in the range 1 to 31.
+ * @hour Store the number of hours past midnight, in the range 0 to 23.
+ * @min Store the number of minutes after the hour,
+ * in the range 0 to 59.
+ * @sec Store the number of seconds after the minute, normally in the
+ * range 0 to 59, but can be up to 60 to allow for leap seconds.
+ * @wday Store the number of days since Sunday, in the range 0 to 6.
+ * @yday Store the number of days since January 1, in the range 0 to 365.
+ *
+ * This function is for internal use, call gmtime() and localtime() instead.
+ */
+void __offtime(__kernel_time_t totalsecs, int offset,
+ unsigned int *year, unsigned int *mon, unsigned int *mday,
+ unsigned int *hour, unsigned int *min, unsigned int *sec,
+ unsigned int *wday, unsigned int *yday)
+{
+ long days, rem, y;
+ const unsigned short *ip;
+
+ days = totalsecs / SECS_PER_DAY;
+ rem = totalsecs % SECS_PER_DAY;
+ rem += offset;
+ while (rem < 0) {
+ rem += SECS_PER_DAY;
+ --days;
+ }
+ while (rem >= SECS_PER_DAY) {
+ rem -= SECS_PER_DAY;
+ ++days;
+ }
+
+ if (hour)
+ *hour = rem / SECS_PER_HOUR;
+ rem %= SECS_PER_HOUR;
+ if (min)
+ *min = rem / 60;
+ if (sec)
+ *sec = rem % 60;
+
+ if (wday) {
+ /* January 1, 1970 was a Thursday. */
+ *wday = (4 + days) % 7;
+ if (*wday < 0)
+ *wday += 7;
+ }
+
+ y = 1970;
+
+ while (days < 0 || days >= (__isleap(y) ? 366 : 365)) {
+ /* Guess a corrected year, assuming 365 days per year. */
+ long yg = y + math_div(days, 365);
+
+ /* Adjust DAYS and Y to match the guessed year. */
+ days -= (yg - y) * 365 + leaps_between(y, yg);
+ y = yg;
+ }
+ if (year) {
+ *year = y - 1900;
+ if (*year != y - 1900) {
+ /* The year cannot be represented due to overflow. */
+ *year = -1;
+ }
+ }
+
+ if (yday)
+ *yday = days;
+
+ ip = __mon_yday[__isleap(y)];
+ for (y = 11; days < ip[y]; y--)
+ continue;
+ days -= ip[y];
+ if (mon)
+ *mon = y;
+ if (mday)
+ *mday = days + 1;
+}
+EXPORT_SYMBOL(__offtime);
--
1.5.5.3

2009-07-15 07:25:10

by Zhao Lei

[permalink] [raw]
Subject: [PATCH v2 2/2] Use common localtime/gmtime in fat_time_unix2fat()

It is not necessary to write custom code for convert calendar time
to broken-down time.
localtime()/gmtime() is more generic to do that.

Signed-off-by: Zhao Lei <[email protected]>
---
fs/fat/misc.c | 62 ++++++++++++++++++--------------------------------------
1 files changed, 20 insertions(+), 42 deletions(-)

diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index a6c2047..d9b6552 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/buffer_head.h>
+#include <linux/time.h>
#include "fat.h"

/*
@@ -155,10 +156,6 @@ extern struct timezone sys_tz;
#define SECS_PER_MIN 60
#define SECS_PER_HOUR (60 * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
-#define UNIX_SECS_1980 315532800L
-#if BITS_PER_LONG == 64
-#define UNIX_SECS_2108 4354819200L
-#endif
/* days between 1.1.70 and 1.1.80 (2 leap days) */
#define DAYS_DELTA (365 * 10 + 2)
/* 120 (2100 - 1980) isn't leap year */
@@ -211,58 +208,40 @@ void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts,
__le16 *time, __le16 *date, u8 *time_cs)
{
- time_t second = ts->tv_sec;
- time_t day, leap_day, month, year;
-
- if (!sbi->options.tz_utc)
- second -= sys_tz.tz_minuteswest * SECS_PER_MIN;
+ unsigned int year, mon, mday, hour, min, sec;
+ if (sbi->options.tz_utc) {
+ gmtime(ts->tv_sec,
+ &year, &mon, &mday, &hour, &min, &sec, NULL, NULL);
+ } else {
+ localtime(ts->tv_sec,
+ &year, &mon, &mday, &hour, &min, &sec, NULL, NULL);
+ }

- /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
- if (second < UNIX_SECS_1980) {
+ /* FAT can only support year between 1980 to 2107 */
+ if (year < 1980 - 1900) {
*time = 0;
*date = cpu_to_le16((0 << 9) | (1 << 5) | 1);
if (time_cs)
*time_cs = 0;
return;
}
-#if BITS_PER_LONG == 64
- if (second >= UNIX_SECS_2108) {
+ if (year > 2107 - 1900) {
*time = cpu_to_le16((23 << 11) | (59 << 5) | 29);
*date = cpu_to_le16((127 << 9) | (12 << 5) | 31);
if (time_cs)
*time_cs = 199;
return;
}
-#endif

- day = second / SECS_PER_DAY - DAYS_DELTA;
- year = day / 365;
- leap_day = (year + 3) / 4;
- if (year > YEAR_2100) /* 2100 isn't leap year */
- leap_day--;
- if (year * 365 + leap_day > day)
- year--;
- leap_day = (year + 3) / 4;
- if (year > YEAR_2100) /* 2100 isn't leap year */
- leap_day--;
- day -= year * 365 + leap_day;
+ /* from 1900 -> from 1980 */
+ year -= 80;
+ /* 0~11 -> 1~12 */
+ mon++;
+ /* 0~59 -> 0~29(2sec counts) */
+ sec >>= 1;

- if (IS_LEAP_YEAR(year) && day == days_in_year[3]) {
- month = 2;
- } else {
- if (IS_LEAP_YEAR(year) && day > days_in_year[3])
- day--;
- for (month = 1; month < 12; month++) {
- if (days_in_year[month + 1] > day)
- break;
- }
- }
- day -= days_in_year[month];
-
- *time = cpu_to_le16(((second / SECS_PER_HOUR) % 24) << 11
- | ((second / SECS_PER_MIN) % 60) << 5
- | (second % SECS_PER_MIN) >> 1);
- *date = cpu_to_le16((year << 9) | (month << 5) | (day + 1));
+ *time = cpu_to_le16(hour << 11 | min << 5 | sec);
+ *date = cpu_to_le16(year << 9 | mon << 5 | mday);
if (time_cs)
*time_cs = (ts->tv_sec & 1) * 100 + ts->tv_nsec / 10000000;
}
@@ -283,4 +262,3 @@ int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs)
}
return err;
}
-
--
1.5.5.3

2009-07-18 10:03:38

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH 1/2] Add function to convert between calendar time and broken-down time for universal use


* Zhaolei <[email protected]> wrote:

> --- /dev/null
> +++ b/kernel/time/timeconv.c
> @@ -0,0 +1,180 @@
> +/*
> + * Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation,
> Inc.
> + * This file is part of the GNU C Library.
> + * Contributed by Paul Eggert ([email protected]).
> + *
> + * The GNU C Library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Library General Public License as
> + * published by the Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version.
> + *
> + * The GNU C Library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Library General Public License for more details.
> + *
> + * You should have received a copy of the GNU Library General Public
> + * License along with the GNU C Library; see the file COPYING.LIB. If not,
> + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite
> 330,
> + * Boston, MA 02111-1307, USA.
> + */
> +
> +/*
> + * Converts the calendar time to broken-down time representation
> + * Based on code from glibc-2.6
> + *
> + * 2009-7-14:
> + * Moved from glibc-2.6 to kernel by Zhaolei<[email protected]>
> + */
> +
> +#include <linux/time.h>
> +#include <linux/module.h>
> +
> +/*
> + * Nonzero if YEAR is a leap year (every 4 years,
> + * except every 100th isn't, and every 400th is).
> + */
> +static inline int __isleap(unsigned int year)
> +{
> + return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0);
> +}
> +
> +/* How many days come before each month (0-12). */
> +static const unsigned short __mon_yday[2][13] =
> + {
> + /* Normal years. */
> + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
> + /* Leap years. */
> + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
> + };
> +
> +#define SECS_PER_HOUR (60 * 60)
> +#define SECS_PER_DAY (SECS_PER_HOUR * 24)
> +
> +static void __offtime(__kernel_time_t totalsecs, int offset,
> + unsigned int *year, unsigned int *mon, unsigned int *mday,
> + unsigned int *hour, unsigned int *min, unsigned int *sec,
> + unsigned int *wday, unsigned int *yday)
> +{
> + long days, rem, y;
> + const unsigned short *ip;
> +
> + days = totalsecs / SECS_PER_DAY;
> + rem = totalsecs % SECS_PER_DAY;
> + rem += offset;
> + while (rem < 0) {
> + rem += SECS_PER_DAY;
> + --days;
> + }
> + while (rem >= SECS_PER_DAY) {
> + rem -= SECS_PER_DAY;
> + ++days;
> + }
> +
> + if (hour)
> + *hour = rem / SECS_PER_HOUR;
> + rem %= SECS_PER_HOUR;
> + if (min)
> + *min = rem / 60;
> + if (sec)
> + *sec = rem % 60;
> +
> + if (wday) {
> + /* January 1, 1970 was a Thursday. */
> + *wday = (4 + days) % 7;
> + if (*wday < 0)
> + *wday += 7;
> + }
> +
> + y = 1970;
> +
> +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
> +#define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV(y, 100) + DIV(y, 400))
> +
> + while (days < 0 || days >= (__isleap(y) ? 366 : 365)) {
> + /* Guess a corrected year, assuming 365 days per year. */
> + long yg = y + days / 365 - (days % 365 < 0);
> +
> + /* Adjust DAYS and Y to match the guessed year. */
> + days -= ((yg - y) * 365
> + + LEAPS_THRU_END_OF(yg - 1)
> + - LEAPS_THRU_END_OF(y - 1));
> + y = yg;
> + }
> + if (year) {
> + *year = y - 1900;
> + if (*year != y - 1900) {
> + /* The year cannot be represented due to overflow. */
> + *year = -1;
> + }
> + }
> +
> + if (yday)
> + *yday = days;
> +
> + ip = __mon_yday[__isleap(y)];
> + for (y = 11; days < ip[y]; y--)
> + continue;
> + days -= ip[y];
> + if (mon)
> + *mon = y;
> + if (mday)
> + *mday = days + 1;
> +}
> +
> +/**
> + * gmtime - converts the calendar time to UTC broken-down time
> + *
> + * @totalsecs The number of seconds elapsed since 00:00:00 on
> January 1, 1970,
> + * Coordinated Universal Time (UTC).
> + * @year Store the number of years since 1900.
> + * @mon Store the number of months since January, in the range 0
> to 11.
> + * @mday Store the day of the month, in the range 1 to 31.
> + * @hour Store the number of hours past midnight, in the range 0 to 23.
> + * @min Store the number of minutes after the hour,
> + * in the range 0 to 59.
> + * @sec Store the number of seconds after the minute, normally
> in the
> + * range 0 to 59, but can be up to 60 to allow for leap seconds.
> + * @wday Store the number of days since Sunday, in the range 0 to 6.
> + * @yday Store the number of days since January 1, in the range 0 to
> 365.
> + *
> + * Similar to the gmtime() in glibc, broken-down time is expressed in
> + * Coordinated Universal Time (UTC).
> + */
> +void gmtime(__kernel_time_t totalsecs,
> + unsigned int *year, unsigned int *mon, unsigned int *mday,
> + unsigned int *hour, unsigned int *min, unsigned int *sec,
> + unsigned int *wday, unsigned int *yday)
> +{
> + __offtime(totalsecs, 0, year, mon, mday, hour, min, sec, wday, yday);
> +}
> +EXPORT_SYMBOL(gmtime);
> +
> +/**
> + * localtime - converts the calendar time to local broken-down time
> + *
> + * @totalsecs The number of seconds elapsed since 00:00:00 on
> January 1, 1970,
> + * Coordinated Universal Time (UTC).
> + * @year Store the number of years since 1900.
> + * @mon Store the number of months since January, in the range 0
> to 11.
> + * @mday Store the day of the month, in the range 1 to 31.
> + * @hour Store the number of hours past midnight, in the range 0 to 23.
> + * @min Store the number of minutes after the hour,
> + * in the range 0 to 59.
> + * @sec Store the number of seconds after the minute, normally
> in the
> + * range 0 to 59, but can be up to 60 to allow for leap seconds.
> + * @wday Store the number of days since Sunday, in the range 0 to 6.
> + * @yday Store the number of days since January 1, in the range 0 to
> 365.
> + *
> + * Similar to the localtime() in glibc, broken-down time is expressed
> + * relative to sys_tz.
> + */
> +void localtime(__kernel_time_t totalsecs,
> + unsigned int *year, unsigned int *mon, unsigned int *mday,
> + unsigned int *hour, unsigned int *min, unsigned int *sec,
> + unsigned int *wday, unsigned int *yday)
> +{
> + __offtime(totalsecs, -sys_tz.tz_minuteswest * 60, year, mon, mday,
> hour,
> + min, sec, wday, yday);
> +}
> +EXPORT_SYMBOL(localtime);

Makes sense - but this code should be converted to the kernel coding
style. See other code in kernel/time/*.c for examples.

I'm also wondering whether including LGPL glibc code in the kernel
like this is license-appropriate - there seems to be few precedents
of it. It _should_ be OK, but i've Cc:-ed involved folks just to
make sure everyone agrees ...

Since you'll modify it anyway to match up the style, the license
itself could be restricted to the kernel's GPLv2 subset, that's OK
as per LGPL.

Ingo

2009-07-18 11:51:36

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 1/2] Add function to convert between calendar time and broken-down time for universal use


> > +extern void gmtime(__kernel_time_t totalsecs,
> > + unsigned int *year, unsigned int *mon, unsigned int *mday,
> > + unsigned int *hour, unsigned int *min, unsigned int *sec,
> > + unsigned int *wday, unsigned int *yday);
> > +extern void localtime(__kernel_time_t totalsecs,
> > + unsigned int *year, unsigned int *mon, unsigned int *mday,
> > + unsigned int *hour, unsigned int *min, unsigned int *sec,
> > + unsigned int *wday, unsigned int *yday);


Should year/mon/.../yday be passed up as a structure?

--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2009-07-18 12:11:38

by H. Peter Anvin

[permalink] [raw]
Subject: Re: [PATCH 1/2] Add function to convert between calendar time and broken-down time for universal use

Ingo Molnar wrote:
>
> I'm also wondering whether including LGPL glibc code in the kernel
> like this is license-appropriate - there seems to be few precedents
> of it. It _should_ be OK, but i've Cc:-ed involved folks just to
> make sure everyone agrees ...
>

Doesn't seem any more odd to me than BSD/GPL dual-licensing, which we
already have tons of.

-hpa

2009-07-18 12:26:36

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH 1/2] Add function to convert between calendar time and broken-down time for universal use

Zhaolei <[email protected]> writes:
> +
> +static void __offtime(__kernel_time_t totalsecs, int offset,
> + unsigned int *year, unsigned int *mon, unsigned int *mday,
> + unsigned int *hour, unsigned int *min, unsigned int *sec,
> + unsigned int *wday, unsigned int *yday)
> +{
> + long days, rem, y;
> + const unsigned short *ip;
> +
> + days = totalsecs / SECS_PER_DAY;

Does this handle leap seconds? Doesn't seem to.

-Andi

--
[email protected] -- Speaking for myself only.

2009-07-18 12:41:48

by Ulrich Drepper

[permalink] [raw]
Subject: Re: [PATCH 1/2] Add function to convert between calendar time and broken-down time for universal use

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Ingo Molnar wrote:
> I'm also wondering whether including LGPL glibc code in the kernel
> like this is license-appropriate

You can declare the modified code GPLed, no problem.

- --
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)

iEYEARECAAYFAkphwuUACgkQ2ijCOnn/RHTUFACcD0BZA8AakHERAjnkFOAbYDE0
A7YAnRUrer6z+mRmwXgBBT3/jMUGDBV9
=t0FC
-----END PGP SIGNATURE-----

2009-07-20 02:41:13

by Zhao Lei

[permalink] [raw]
Subject: Re: [PATCH 1/2] Add function to convert between calendar time and broken-down time for universal use

* From: "Andi Kleen" <[email protected]>
>> +
>> +static void __offtime(__kernel_time_t totalsecs, int offset,
>> + unsigned int *year, unsigned int *mon, unsigned int *mday,
>> + unsigned int *hour, unsigned int *min, unsigned int *sec,
>> + unsigned int *wday, unsigned int *yday)
>> +{
>> + long days, rem, y;
>> + const unsigned short *ip;
>> +
>> + days = totalsecs / SECS_PER_DAY;
>
> Does this handle leap seconds? Doesn't seem to.

Hello, Andi

Thank for your attention.

It seems that we don't need to deal with leap seconds in conversion because
unix time is not counting leap seconds..

There is some detail in http://en.wikipedia.org/wiki/Unix_time

Thanks
Zhaolei
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?