diff mbox

[2/3] rtc: armada38x: Convert to time64_t

Message ID 20161208171010.29446-3-gregory.clement@free-electrons.com (mailing list archive)
State New, archived
Headers show

Commit Message

Gregory CLEMENT Dec. 8, 2016, 5:10 p.m. UTC
It is one more step to remove the deprecated functions rtc_time_to_tm
and rtc_tm_to_time.

And as bonus it simplifies a little the driver.

Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 drivers/rtc/rtc-armada38x.c | 42 ++++++++++++++++++------------------------
 1 file changed, 18 insertions(+), 24 deletions(-)

Comments

Russell King (Oracle) Dec. 8, 2016, 6:08 p.m. UTC | #1
On Thu, Dec 08, 2016 at 06:10:09PM +0100, Gregory CLEMENT wrote:
> It is one more step to remove the deprecated functions rtc_time_to_tm
> and rtc_tm_to_time.

I fail to see any advantage to this patch, or in fact the y2038 patch
merged into rtc.

Let's first look at the original rtc_time_to_tm():

+void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
+{
+       days = time / 86400;
+       year = 1970 + days / 365;

time is an unsigned long, so is 32-bit or 64-bit.  Let's assume 32-bit.
The maximum value it can have is 2^32-1 - which gives a maximum days
value of 49710 (it always fits in an int or unsigned int as a positive
value on any arch supported by Linux.)

That gives a maximum value of 'year' as 2106.  So, actually
rtc_time_to_tm() doesn't have a y2038 problem, it has a y2106 problem.
So, the commentary in the original commit is actually wrong!


Okay, that aside, let's look at what your patch actually achieves.

Old code to set the time:

	unsigned long time;

	ret = rtc_tm_to_time(tm, &time);
	if (ret)
		goto out;
	/* write time to a 32-bit register */

New code:

	time64_t time;

	time = rtc_tm_to_time64(tm);
	/* write time to a 32-bit register */

I fail to see how this is any kind of improvement, or aids to solve the
y2106 problem (not y2038) here.

A better approach would have been to also provide a rtc_tm_to_time32()
following the function signature of rtc_tm_to_time(), but replacing the
unsigned long pointer with a u32 pointer, and (importantly) making it
fail if the time was unrepresentable as a u32 value.

Why u32 and not time_t?  time_t is a signed value, which suffers from
the y2038 problem.  u32 suffers from a y2106 problem, and is what we
have with the original rtc_tm_to_time() on 32-bit platforms.  So it's
safe _not_ to needlessly chop off a bit there which wasn't done in the
original code.

This would mean that drivers (such as armada38x) which are only capable
of storing a 32-bit time fail gracefully to set times beyond y2106.

The problem as I see it right now is that the y2038 patch which
deprecates rtc_time_to_tm() etc wasn't thought out thoroughly enough,
and is making the situation much worse by pushing the problem in many
drivers in a way that results in it being less visible.

I think this needs to be reconsidered, and it needs a much better
implementation - one which at least tells the user that their hardware
has broken when trying to set the time to something the hardware can't
support.
diff mbox

Patch

diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c
index a0859286a4c4..b8a74ffaae80 100644
--- a/drivers/rtc/rtc-armada38x.c
+++ b/drivers/rtc/rtc-armada38x.c
@@ -128,13 +128,14 @@  static unsigned long read_rtc_register_wa(struct armada38x_rtc *rtc, u8 rtc_reg)
 static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
-	unsigned long time, flags;
+	unsigned long flags;
+	time64_t time;
 
 	spin_lock_irqsave(&rtc->lock, flags);
-	time = read_rtc_register_wa(rtc, RTC_TIME);
+	time = (time64_t)read_rtc_register_wa(rtc, RTC_TIME);
 	spin_unlock_irqrestore(&rtc->lock, flags);
 
-	rtc_time_to_tm(time, tm);
+	rtc_time64_to_tm(time, tm);
 
 	return 0;
 }
@@ -142,37 +143,34 @@  static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
 static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
 {
 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
-	int ret = 0;
-	unsigned long time, flags;
-
-	ret = rtc_tm_to_time(tm, &time);
+	unsigned long flags;
+	time64_t time;
 
-	if (ret)
-		goto out;
+	time = rtc_tm_to_time64(tm);
 
 	spin_lock_irqsave(&rtc->lock, flags);
-	rtc_delayed_write(time, rtc, RTC_TIME);
+	rtc_delayed_write((u32)time, rtc, RTC_TIME);
 	spin_unlock_irqrestore(&rtc->lock, flags);
 
-out:
-	return ret;
+	return 0;
 }
 
 static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 {
 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
-	unsigned long time, flags;
+	unsigned long flags;
+	time64_t time;
 	u32 val;
 
 	spin_lock_irqsave(&rtc->lock, flags);
 
-	time = read_rtc_register_wa(rtc, RTC_ALARM1);
+	time = (time64_t)read_rtc_register_wa(rtc, RTC_ALARM1);
 	val = read_rtc_register_wa(rtc, RTC_IRQ1_CONF) & RTC_IRQ1_AL_EN;
 
 	spin_unlock_irqrestore(&rtc->lock, flags);
 
 	alrm->enabled = val ? 1 : 0;
-	rtc_time_to_tm(time,  &alrm->time);
+	rtc_time64_to_tm(time,  &alrm->time);
 
 	return 0;
 }
@@ -180,18 +178,15 @@  static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 {
 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
-	unsigned long time, flags;
-	int ret = 0;
+	unsigned long flags;
+	time64_t time;
 	u32 val;
 
-	ret = rtc_tm_to_time(&alrm->time, &time);
-
-	if (ret)
-		goto out;
+	time = rtc_tm_to_time64(&alrm->time);
 
 	spin_lock_irqsave(&rtc->lock, flags);
 
-	rtc_delayed_write(time, rtc, RTC_ALARM1);
+	rtc_delayed_write((u32)time, rtc, RTC_ALARM1);
 
 	if (alrm->enabled) {
 			rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF);
@@ -202,8 +197,7 @@  static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 
 	spin_unlock_irqrestore(&rtc->lock, flags);
 
-out:
-	return ret;
+	return 0;
 }
 
 static int armada38x_rtc_alarm_irq_enable(struct device *dev,