bcachefs: Fix time handling
authorKent Overstreet <kent.overstreet@gmail.com>
Thu, 29 Apr 2021 02:51:42 +0000 (22:51 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:02 +0000 (17:09 -0400)
There were some overflows in the time conversion functions - fix this by
converting tv_sec and tv_nsec separately. Also, set sb->time_min and
sb->time_max.

Fixes xfstest generic/258.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/bcachefs.h
fs/bcachefs/fs.c
fs/bcachefs/super-io.c

index 323705f..c47e699 100644 (file)
@@ -605,11 +605,13 @@ struct bch_fs {
 
                u64             time_base_lo;
                u32             time_base_hi;
-               u32             time_precision;
+               unsigned        time_units_per_sec;
+               unsigned        nsec_per_time_unit;
                u64             features;
                u64             compat;
        }                       sb;
 
+
        struct bch_sb_handle    disk_sb;
 
        unsigned short          block_bits;     /* ilog2(block_size) */
@@ -872,19 +874,22 @@ static inline unsigned block_bytes(const struct bch_fs *c)
        return c->opts.block_size << 9;
 }
 
-static inline struct timespec64 bch2_time_to_timespec(struct bch_fs *c, u64 time)
+static inline struct timespec64 bch2_time_to_timespec(struct bch_fs *c, s64 time)
 {
-       return ns_to_timespec64(time * c->sb.time_precision + c->sb.time_base_lo);
+       struct timespec64 t;
+       s32 rem;
+
+       time += c->sb.time_base_lo;
+
+       t.tv_sec = div_s64_rem(time, c->sb.time_units_per_sec, &rem);
+       t.tv_nsec = rem * c->sb.nsec_per_time_unit;
+       return t;
 }
 
 static inline s64 timespec_to_bch2_time(struct bch_fs *c, struct timespec64 ts)
 {
-       s64 ns = timespec64_to_ns(&ts) - c->sb.time_base_lo;
-
-       if (c->sb.time_precision == 1)
-               return ns;
-
-       return div_s64(ns, c->sb.time_precision);
+       return (ts.tv_sec * c->sb.time_units_per_sec +
+               (int) ts.tv_nsec / c->sb.nsec_per_time_unit) - c->sb.time_base_lo;
 }
 
 static inline s64 bch2_current_time(struct bch_fs *c)
index 67e9a35..b00f352 100644 (file)
@@ -1565,7 +1565,9 @@ got_sb:
 #endif
        sb->s_xattr             = bch2_xattr_handlers;
        sb->s_magic             = BCACHEFS_STATFS_MAGIC;
-       sb->s_time_gran         = c->sb.time_precision;
+       sb->s_time_gran         = c->sb.nsec_per_time_unit;
+       sb->s_time_min          = div_s64(S64_MIN, c->sb.time_units_per_sec) + 1;
+       sb->s_time_max          = div_s64(S64_MAX, c->sb.time_units_per_sec);
        c->vfs_sb               = sb;
        strlcpy(sb->s_id, c->name, sizeof(sb->s_id));
 
index e0de6f0..4c7cea4 100644 (file)
@@ -373,9 +373,15 @@ static void bch2_sb_update(struct bch_fs *c)
        c->sb.clean             = BCH_SB_CLEAN(src);
        c->sb.encryption_type   = BCH_SB_ENCRYPTION_TYPE(src);
        c->sb.encoded_extent_max= 1 << BCH_SB_ENCODED_EXTENT_MAX_BITS(src);
-       c->sb.time_base_lo      = le64_to_cpu(src->time_base_lo);
+
+       c->sb.nsec_per_time_unit = le32_to_cpu(src->time_precision);
+       c->sb.time_units_per_sec = NSEC_PER_SEC / c->sb.nsec_per_time_unit;
+
+       /* XXX this is wrong, we need a 96 or 128 bit integer type */
+       c->sb.time_base_lo      = div_u64(le64_to_cpu(src->time_base_lo),
+                                         c->sb.nsec_per_time_unit);
        c->sb.time_base_hi      = le32_to_cpu(src->time_base_hi);
-       c->sb.time_precision    = le32_to_cpu(src->time_precision);
+
        c->sb.features          = le64_to_cpu(src->features[0]);
        c->sb.compat            = le64_to_cpu(src->compat[0]);