Search Linux Wireless

[RFC PATCH v3] average: change non-init state and add check for added values

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The uninitialized state 0 involves the danger of reaching that state in
normal operation. Since the weight_rcp value needs to be bigger than one
(otherwise no averaging takes place), the final shifting in add ensures,
that the internal state never reach ULONG_MAX. Therefore use this value
to signal, that the ewma has no initial value.

The add function needs to check, that val is not to big, otherwise the new
value can overflow the internal state, which results in unexpected outputs.

Move the compile time checks into a separate macro, as they are used
multiple times and noise up the functions.

Signed-off-by: Benjamin Beichler <benjamin.beichler@xxxxxxxxxxxxxx>
---
 include/linux/average.h | 82 ++++++++++++++++++++++-------------------
 1 file changed, 45 insertions(+), 37 deletions(-)

diff --git a/include/linux/average.h b/include/linux/average.h
index a1a8f09631ce..4fb170ed0225 100644
--- a/include/linux/average.h
+++ b/include/linux/average.h
@@ -22,50 +22,58 @@
  * The third argument, the weight reciprocal, determines how the
  * new values will be weighed vs. the old state, new values will
  * get weight 1/weight_rcp and old values 1-1/weight_rcp. Note
- * that this parameter must be a power of two for efficiency.
+ * that this parameter must be a power of two for efficiency. A
+ * weight of 1 is not allowed, because of the internal init function,
+ * but nevertheless it makes no sense, since it means no averaging at all.
  */
 
-#define DECLARE_EWMA(name, _precision, _weight_rcp)			\
-	struct ewma_##name {						\
-		unsigned long internal;					\
-	};								\
-	static inline void ewma_##name##_init(struct ewma_##name *e)	\
-	{								\
+#define EWMA_BUILD_TIME_CHECKS(_precision, _weight_rcp)			\
+	do {								\
 		BUILD_BUG_ON(!__builtin_constant_p(_precision));	\
 		BUILD_BUG_ON(!__builtin_constant_p(_weight_rcp));	\
-		/*							\
-		 * Even if you want to feed it just 0/1 you should have	\
-		 * some bits for the non-fractional part...		\
-		 */							\
-		BUILD_BUG_ON((_precision) > 30);			\
-		BUILD_BUG_ON_NOT_POWER_OF_2(_weight_rcp);		\
-		e->internal = 0;					\
-	}								\
-	static inline unsigned long					\
-	ewma_##name##_read(struct ewma_##name *e)			\
-	{								\
-		BUILD_BUG_ON(!__builtin_constant_p(_precision));	\
-		BUILD_BUG_ON(!__builtin_constant_p(_weight_rcp));	\
-		BUILD_BUG_ON((_precision) > 30);			\
-		BUILD_BUG_ON_NOT_POWER_OF_2(_weight_rcp);		\
-		return e->internal >> (_precision);			\
-	}								\
-	static inline void ewma_##name##_add(struct ewma_##name *e,	\
-					     unsigned long val)		\
-	{								\
-		unsigned long internal = READ_ONCE(e->internal);	\
-		unsigned long weight_rcp = ilog2(_weight_rcp);		\
-		unsigned long precision = _precision;			\
 									\
-		BUILD_BUG_ON(!__builtin_constant_p(_precision));	\
-		BUILD_BUG_ON(!__builtin_constant_p(_weight_rcp));	\
 		BUILD_BUG_ON((_precision) > 30);			\
 		BUILD_BUG_ON_NOT_POWER_OF_2(_weight_rcp);		\
-									\
-		WRITE_ONCE(e->internal, internal ?			\
-			(((internal << weight_rcp) - internal) +	\
-				(val << precision)) >> weight_rcp :	\
-			(val << precision));				\
+		BUILD_BUG_ON(_weight_rcp <= 1);				\
+	} while (0)
+
+#define DECLARE_EWMA(name, _precision, _weight_rcp)				\
+	struct ewma_##name {							\
+		unsigned long internal;						\
+	};									\
+	static inline void ewma_##name##_init(struct ewma_##name *e)		\
+	{									\
+		EWMA_BUILD_TIME_CHECKS(_precision, _weight_rcp);		\
+		e->internal = ULONG_MAX;					\
+	}									\
+		static inline void ewma_##name##_init_val(struct ewma_##name *e	\
+							  unsigned long val)	\
+	{									\
+		EWMA_BUILD_TIME_CHECKS(_precision, _weight_rcp);		\
+		e->internal = val;						\
+	}									\
+	static inline unsigned long						\
+	ewma_##name##_read(struct ewma_##name *e)				\
+	{									\
+		EWMA_BUILD_TIME_CHECKS(_precision, _weight_rcp);		\
+		return unlikely(e->internal == ULONG_MAX) ?			\
+				0 : e->internal >> (_precision);		\
+	}									\
+	static inline void ewma_##name##_add(struct ewma_##name *e,		\
+					     unsigned long val)			\
+	{									\
+		unsigned long internal = READ_ONCE(e->internal);		\
+		unsigned long weight_rcp = ilog2(_weight_rcp);			\
+		unsigned long precision = _precision;				\
+		unsigned long max_val = ULONG_MAX >> (precision + weight_rcp);	\
+										\
+		EWMA_BUILD_TIME_CHECKS(_precision, _weight_rcp);		\
+		WARN_ONCE(val > max_val, "Value (%lu) is too large for ewma_##name##_add, this will result in unexpected values of the ewma filter. Max valid value is %lu",\
+			  val, max_val);					\
+		WRITE_ONCE(e->internal, unlikely(internal != ULONG_MAX) ?	\
+			(((internal << weight_rcp) - internal) +		\
+				(val << precision)) >> weight_rcp :		\
+			(val << precision));					\
 	}
 
 #endif /* _LINUX_AVERAGE_H */
-- 
2.25.1



[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux