]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
bnxt: fix bnxt_hwrm_fw_set_time for y2038
authorArnd Bergmann <arnd@arndb.de>
Wed, 7 Mar 2018 21:30:59 +0000 (16:30 -0500)
committerJack Vogel <jack.vogel@oracle.com>
Fri, 9 Mar 2018 05:02:15 +0000 (21:02 -0800)
Orabug: 2764835527648339

On 32-bit architectures, rtc_time_to_tm() returns incorrect results
in 2038 or later, and do_gettimeofday() is broken for the same reason.

This changes the code to use ktime_get_real_seconds() and time64_to_tm()
instead, both of them are 2038-safe, and we can also get rid of the
CONFIG_RTC_LIB dependency that way.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
(cherry picked from commit 7dfaa7bc99498da1c6c4a48bee8d2d5265161a8c)
Signed-off-by: Brian Maly <brian.maly@oracle.com>
Reviewed-by: Jack Vogel <jack.vogel@oracle.com>
drivers/net/ethernet/broadcom/bnxt/bnxt.c
drivers/net/ethernet/broadcom/bnxt/bnxt_compat.h

index 2a5d480837999af00ca26b3dbeaaa6d8ff97ce84..37e52dd87f8ffc909177d9725a7fd5a25278d850 100644 (file)
@@ -4884,16 +4884,14 @@ hwrm_ver_get_exit:
 
 int bnxt_hwrm_fw_set_time(struct bnxt *bp)
 {
-#if IS_ENABLED(CONFIG_RTC_LIB)
        struct hwrm_fw_set_time_input req = {0};
-       struct rtc_time tm;
-       struct timeval tv;
+       struct tm tm;
+       time64_t now = ktime_get_real_seconds();
 
        if (bp->hwrm_spec_code < 0x10400)
                return -EOPNOTSUPP;
 
-       do_gettimeofday(&tv);
-       rtc_time_to_tm(tv.tv_sec, &tm);
+       time64_to_tm(now, 0, &tm);
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FW_SET_TIME, -1, -1);
        req.year = cpu_to_le16(1900 + tm.tm_year);
        req.month = 1 + tm.tm_mon;
@@ -4902,9 +4900,6 @@ int bnxt_hwrm_fw_set_time(struct bnxt *bp)
        req.minute = tm.tm_min;
        req.second = tm.tm_sec;
        return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
-#else
-       return -EOPNOTSUPP;
-#endif
 }
 
 static int bnxt_hwrm_port_qstats(struct bnxt *bp)
index 4bc0a935b9d63a4364d1dc3067c0aa25b5a61db8..98f5ef72a716a01c9c29c31713423697077c023b 100644 (file)
@@ -8,6 +8,7 @@
  * the Free Software Foundation.
  */
 
+#include <linux/time.h>
 #include <linux/pci.h>
 #include <linux/version.h>
 #include <linux/ethtool.h>
 #ifndef SWITCHDEV_SET_OPS
 #define SWITCHDEV_SET_OPS(netdev, ops) ((netdev)->switchdev_ops = (ops))
 #endif
+
+/*
+ *  * Nonzero if YEAR is a leap year (every 4 years,
+ *   * except every 100th isn't, and every 400th is).
+ *    */
+static int __isleap(long 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)
+
+/**
+ *  * time64_to_tm - 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.
+ *       * @result      pointer to struct tm variable to receive broken-down time
+ *        */
+void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
+{
+        long days, rem, y;
+        int remainder;
+        const unsigned short *ip;
+
+        days = div_s64_rem(totalsecs, SECS_PER_DAY, &remainder);
+        rem = remainder;
+        rem += offset;
+        while (rem < 0) {
+                rem += SECS_PER_DAY;
+                --days;
+        }
+        while (rem >= SECS_PER_DAY) {
+                rem -= SECS_PER_DAY;
+                ++days;
+        }
+
+        result->tm_hour = rem / SECS_PER_HOUR;
+        rem %= SECS_PER_HOUR;
+        result->tm_min = rem / 60;
+        result->tm_sec = rem % 60;
+
+        /* January 1, 1970 was a Thursday. */
+        result->tm_wday = (4 + days) % 7;
+        if (result->tm_wday < 0)
+                result->tm_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;
+        }
+
+        result->tm_year = y - 1900;
+
+        result->tm_yday = days;
+
+        ip = __mon_yday[__isleap(y)];
+        for (y = 11; days < ip[y]; y--)
+                continue;
+        days -= ip[y];
+        result->tm_mon = y;
+        result->tm_mday = days + 1;
+}
+