diff --git a/sys/compat/linuxkpi/common/include/linux/math64.h b/sys/compat/linuxkpi/common/include/linux/math64.h --- a/sys/compat/linuxkpi/common/include/linux/math64.h +++ b/sys/compat/linuxkpi/common/include/linux/math64.h @@ -31,6 +31,7 @@ #define _LINUXKPI_LINUX_MATH64_H #include +#include #define do_div(n, base) ({ \ uint32_t __base = (base); \ @@ -108,6 +109,41 @@ return ((x / div) * y + (rem * y) / div); } +static inline uint64_t +mul_u64_u64_div_u64(uint64_t x, uint64_t y, uint64_t z) +{ + uint64_t res; + + res = 0; + /* Overflow on x * y possible? */ + if ((ilog2(x) + ilog2(y)) > 62) { + uint64_t remainder; + int d; + + /* + * Do: (y / z) * x; y = y % z. + * If we overflow the result will never fit. Otherwise the final + * div64_u64() will do the x * y / z missing we already prepared + * for the final result of (y % z) * x / z in addition to res. + */ + res = div64_u64_rem(y, z, &remainder); + res *= x; + y = remainder; + + /* Re-check with new y. */ + d = ilog2(x) + ilog2(y) - 62; + if (d > 0) { + /* We cannot help anymore. Reduce precision. */ + y >>= d; + z >>= d; + /* Avoid div0. */ + if (z == 0) + return (res); + } + } + return (res + div64_u64(x * y, z)); +} + static inline uint64_t mul_u64_u32_shr(uint64_t x, uint32_t y, unsigned int shift) {