diff mbox series

[33/38] backports: add mul_u64_u64_div_u64()

Message ID 20221011230356.8a84958f6acb.Ia106b487910574e59b56065f04f9448655011b0a@changeid (mailing list archive)
State New, archived
Headers show
Series backports updates | expand

Commit Message

Johannes Berg Oct. 11, 2022, 9:04 p.m. UTC
From: Johannes Berg <johannes.berg@intel.com>

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 backport/backport-include/linux/math64.h | 11 ++++++
 backport/compat/Makefile                 |  1 +
 backport/compat/backport-5.9.c           | 45 ++++++++++++++++++++++++
 3 files changed, 57 insertions(+)
 create mode 100644 backport/backport-include/linux/math64.h
 create mode 100644 backport/compat/backport-5.9.c
diff mbox series

Patch

diff --git a/backport/backport-include/linux/math64.h b/backport/backport-include/linux/math64.h
new file mode 100644
index 000000000000..6aea9c9eec77
--- /dev/null
+++ b/backport/backport-include/linux/math64.h
@@ -0,0 +1,11 @@ 
+#ifndef __BACKPORT_LINUX_MATH64_H
+#define __BACKPORT_LINUX_MATH64_H
+#include_next <linux/math64.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_LESS(5,9,0)
+#define mul_u64_u64_div_u64 LINUX_BACKPORT(mul_u64_u64_div_u64)
+extern u64 mul_u64_u64_div_u64(u64 a, u64 b, u64 c);
+#endif /* < 5.9 */
+
+#endif /* __BACKPORT_LINUX_MATH64_H */
diff --git a/backport/compat/Makefile b/backport/compat/Makefile
index b9f1dee9ae07..c488fcdc727f 100644
--- a/backport/compat/Makefile
+++ b/backport/compat/Makefile
@@ -17,6 +17,7 @@  compat-$(CPTCFG_KERNEL_4_18) += backport-4.18.o
 compat-$(CPTCFG_KERNEL_5_2) += backport-5.2.o backport-genetlink.o
 compat-$(CPTCFG_KERNEL_5_3) += backport-5.3.o
 compat-$(CPTCFG_KERNEL_5_5) += backport-5.5.o
+compat-$(CPTCFG_KERNEL_5_9) += backport-5.9.o
 compat-$(CPTCFG_KERNEL_5_10) += backport-5.10.o
 compat-$(CPTCFG_KERNEL_5_11) += backport-5.11.o
 compat-$(CPTCFG_KERNEL_5_13) += backport-5.13.o
diff --git a/backport/compat/backport-5.9.c b/backport/compat/backport-5.9.c
new file mode 100644
index 000000000000..54d1802efd31
--- /dev/null
+++ b/backport/compat/backport-5.9.c
@@ -0,0 +1,45 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+
+u64 mul_u64_u64_div_u64(u64 a, u64 b, u64 c)
+{
+	u64 res = 0, div, rem;
+	int shift;
+
+	/* can a * b overflow ? */
+	if (ilog2(a) + ilog2(b) > 62) {
+		/*
+		 * (b * a) / c is equal to
+		 *
+		 *      (b / c) * a +
+		 *      (b % c) * a / c
+		 *
+		 * if nothing overflows. Can the 1st multiplication
+		 * overflow? Yes, but we do not care: this can only
+		 * happen if the end result can't fit in u64 anyway.
+		 *
+		 * So the code below does
+		 *
+		 *      res = (b / c) * a;
+		 *      b = b % c;
+		 */
+		div = div64_u64_rem(b, c, &rem);
+		res = div * a;
+		b = rem;
+
+		shift = ilog2(a) + ilog2(b) - 62;
+		if (shift > 0) {
+			/* drop precision */
+			b >>= shift;
+			c >>= shift;
+			if (!c)
+				return res;
+		}
+	}
+
+	return res + div64_u64(a * b, c);
+}
+EXPORT_SYMBOL_GPL(mul_u64_u64_div_u64);