xref: /freebsd/sys/contrib/ck/src/ck_ec_timeutil.h (revision cc68614da8232d8baaca0ae0d0dd8f890f06623e)
1 #ifndef CK_EC_TIMEUTIL_H
2 #define CK_EC_TIMEUTIL_H
3 #include <ck_cc.h>
4 #include <ck_ec.h>
5 #include <ck_limits.h>
6 #include <ck_stdint.h>
7 #include <sys/time.h>
8 
9 #define TIME_MAX ((time_t)((1ULL << ((sizeof(time_t) * CHAR_BIT) - 1)) - 1))
10 #define NSEC_MAX ((1000L * 1000 * 1000) - 1)
11 
12 /*
13  * Approximates (nsec * multiplier) >> shift. Clamps to UINT32_MAX on
14  * overflow.
15  */
16 CK_CC_UNUSED static uint32_t
17 wait_time_scale(uint32_t nsec,
18 		uint32_t multiplier,
19 		unsigned int shift)
20 {
21 	uint64_t temp = (uint64_t)nsec * multiplier;
22 	uint64_t max = (uint64_t)UINT32_MAX << shift;
23 
24 	if (temp >= max) {
25 		return UINT32_MAX;
26 	}
27 
28 	return temp >> shift;
29 }
30 
31 
32 /*
33  * Returns ts + ns. ns is clamped to at most 1 second. Clamps the
34  * return value to TIME_MAX, NSEC_MAX on overflow.
35  *
36  */
37 CK_CC_UNUSED static struct timespec timespec_add_ns(const struct timespec ts,
38 						    uint32_t ns)
39 {
40 	struct timespec ret = {
41 		.tv_sec = TIME_MAX,
42 		.tv_nsec = NSEC_MAX
43 	};
44 	time_t sec;
45 	uint32_t sum_ns;
46 
47 	if (ns > (uint32_t)NSEC_MAX) {
48 		if (ts.tv_sec >= TIME_MAX) {
49 			return ret;
50 		}
51 
52 		ret.tv_sec = ts.tv_sec + 1;
53 		ret.tv_nsec = ts.tv_nsec;
54 		return ret;
55 	}
56 
57 	sec = ts.tv_sec;
58 	sum_ns = ns + ts.tv_nsec;
59 	if (sum_ns > NSEC_MAX) {
60 		if (sec >= TIME_MAX) {
61 			return ret;
62 		}
63 
64 		sec++;
65 		sum_ns -= (NSEC_MAX + 1);
66 	}
67 
68 	ret.tv_sec = sec;
69 	ret.tv_nsec = sum_ns;
70 	return ret;
71 }
72 
73 
74 /*
75  * Returns ts + inc. If inc is negative, it is normalized to 0.
76  * Clamps the return value to TIME_MAX, NSEC_MAX on overflow.
77  */
78 CK_CC_UNUSED static struct timespec timespec_add(const struct timespec ts,
79 						 const struct timespec inc)
80 {
81 	/* Initial return value is clamped to infinite future. */
82 	struct timespec ret = {
83 		.tv_sec = TIME_MAX,
84 		.tv_nsec = NSEC_MAX
85 	};
86 	time_t sec;
87 	unsigned long nsec;
88 
89 	/* Non-positive delta is a no-op. Invalid nsec is another no-op. */
90 	if (inc.tv_sec < 0 || inc.tv_nsec < 0 || inc.tv_nsec > NSEC_MAX) {
91 		return ts;
92 	}
93 
94 	/* Detect overflow early. */
95 	if (inc.tv_sec > TIME_MAX - ts.tv_sec) {
96 		return ret;
97 	}
98 
99 	sec = ts.tv_sec + inc.tv_sec;
100 	/* This sum can't overflow if the inputs are valid.*/
101 	nsec = (unsigned long)ts.tv_nsec + inc.tv_nsec;
102 
103 	if (nsec > NSEC_MAX) {
104 		if (sec >= TIME_MAX) {
105 			return ret;
106 		}
107 
108 		sec++;
109 		nsec -= (NSEC_MAX + 1);
110 	}
111 
112 	ret.tv_sec = sec;
113 	ret.tv_nsec = nsec;
114 	return ret;
115 }
116 
117 /* Compares two timespecs. Returns -1 if x < y, 0 if x == y, and 1 if x > y. */
118 CK_CC_UNUSED static int timespec_cmp(const struct timespec x,
119 				     const struct timespec y)
120 {
121 	if (x.tv_sec != y.tv_sec) {
122 		return (x.tv_sec < y.tv_sec) ? -1 : 1;
123 	}
124 
125 	if (x.tv_nsec != y.tv_nsec) {
126 		return (x.tv_nsec < y.tv_nsec) ? -1 : 1;
127 	}
128 
129 	return 0;
130 }
131 
132 /*
133  * Overwrites now with the current CLOCK_MONOTONIC time, and returns
134  * true if the current time is greater than or equal to the deadline,
135  * or the clock is somehow broken.
136  */
137 CK_CC_UNUSED static bool check_deadline(struct timespec *now,
138 					const struct ck_ec_ops *ops,
139 					const struct timespec deadline)
140 {
141 	int r;
142 
143 	r = ops->gettime(ops, now);
144 	if (r != 0) {
145 		return true;
146 	}
147 
148 	return timespec_cmp(*now, deadline) >= 0;
149 }
150 #endif /* !CK_EC_TIMEUTIL_H */
151