| 1 | |
| 2 | |
| 3 | * Copyright (C) 2021 Maciej W. Rozycki
|
| 4 |
|
| 5 |
|
| 6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
| 7 |
|
| 8 | #include <linux/init.h>
|
| 9 | #include <linux/ktime.h>
|
| 10 | #include <linux/module.h>
|
| 11 | #include <linux/printk.h>
|
| 12 | #include <linux/time64.h>
|
| 13 | #include <linux/types.h>
|
| 14 |
|
| 15 | #include <asm/div64.h>
|
| 16 |
|
| 17 | #define TEST_DIV64_N_ITER 1024
|
| 18 |
|
| 19 | static const u64 test_div64_dividends[] = {
|
| 20 | 0x00000000ab275080,
|
| 21 | 0x0000000fe73c1959,
|
| 22 | 0x000000e54c0a74b1,
|
| 23 | 0x00000d4398ff1ef9,
|
| 24 | 0x0000a18c2ee1c097,
|
| 25 | 0x00079fb80b072e4a,
|
| 26 | 0x0072db27380dd689,
|
| 27 | 0x0842f488162e2284,
|
| 28 | 0xf66745411d8ab063,
|
| 29 | 0xfffffffffffffffb,
|
| 30 | 0xfffffffffffffffc,
|
| 31 | 0xffffffffffffffff,
|
| 32 | };
|
| 33 | #define SIZE_DIV64_DIVIDENDS ARRAY_SIZE(test_div64_dividends)
|
| 34 |
|
| 35 | #define TEST_DIV64_DIVISOR_0 0x00000009
|
| 36 | #define TEST_DIV64_DIVISOR_1 0x0000007c
|
| 37 | #define TEST_DIV64_DIVISOR_2 0x00000204
|
| 38 | #define TEST_DIV64_DIVISOR_3 0x0000cb5b
|
| 39 | #define TEST_DIV64_DIVISOR_4 0x00010000
|
| 40 | #define TEST_DIV64_DIVISOR_5 0x0008a880
|
| 41 | #define TEST_DIV64_DIVISOR_6 0x003fd3ae
|
| 42 | #define TEST_DIV64_DIVISOR_7 0x0b658fac
|
| 43 | #define TEST_DIV64_DIVISOR_8 0x80000001
|
| 44 | #define TEST_DIV64_DIVISOR_9 0xdc08b349
|
| 45 | #define TEST_DIV64_DIVISOR_A 0xfffffffe
|
| 46 | #define TEST_DIV64_DIVISOR_B 0xffffffff
|
| 47 |
|
| 48 | static const u32 test_div64_divisors[] = {
|
| 49 | TEST_DIV64_DIVISOR_0,
|
| 50 | TEST_DIV64_DIVISOR_1,
|
| 51 | TEST_DIV64_DIVISOR_2,
|
| 52 | TEST_DIV64_DIVISOR_3,
|
| 53 | TEST_DIV64_DIVISOR_4,
|
| 54 | TEST_DIV64_DIVISOR_5,
|
| 55 | TEST_DIV64_DIVISOR_6,
|
| 56 | TEST_DIV64_DIVISOR_7,
|
| 57 | TEST_DIV64_DIVISOR_8,
|
| 58 | TEST_DIV64_DIVISOR_9,
|
| 59 | TEST_DIV64_DIVISOR_A,
|
| 60 | TEST_DIV64_DIVISOR_B,
|
| 61 | };
|
| 62 | #define SIZE_DIV64_DIVISORS ARRAY_SIZE(test_div64_divisors)
|
| 63 |
|
| 64 | static const struct {
|
| 65 | u64 quotient;
|
| 66 | u32 remainder;
|
| 67 | } test_div64_results[SIZE_DIV64_DIVIDENDS][SIZE_DIV64_DIVISORS] = {
|
| 68 | {
|
| 69 | { 0x0000000013045e47, 0x00000001 },
|
| 70 | { 0x000000000161596c, 0x00000030 },
|
| 71 | { 0x000000000054e9d4, 0x00000130 },
|
| 72 | { 0x000000000000d776, 0x0000278e },
|
| 73 | { 0x000000000000ab27, 0x00005080 },
|
| 74 | { 0x00000000000013c4, 0x0004ce80 },
|
| 75 | { 0x00000000000002ae, 0x001e143c },
|
| 76 | { 0x000000000000000f, 0x0033e56c },
|
| 77 | { 0x0000000000000001, 0x2b27507f },
|
| 78 | { 0x0000000000000000, 0xab275080 },
|
| 79 | { 0x0000000000000000, 0xab275080 },
|
| 80 | { 0x0000000000000000, 0xab275080 },
|
| 81 | }, {
|
| 82 | { 0x00000001c45c02d1, 0x00000000 },
|
| 83 | { 0x0000000020d5213c, 0x00000049 },
|
| 84 | { 0x0000000007e3d65f, 0x000001dd },
|
| 85 | { 0x0000000000140531, 0x000065ee },
|
| 86 | { 0x00000000000fe73c, 0x00001959 },
|
| 87 | { 0x000000000001d637, 0x0004e5d9 },
|
| 88 | { 0x0000000000003fc9, 0x000713bb },
|
| 89 | { 0x0000000000000165, 0x029abe7d },
|
| 90 | { 0x000000000000001f, 0x673c193a },
|
| 91 | { 0x0000000000000012, 0x6e9f7e37 },
|
| 92 | { 0x000000000000000f, 0xe73c1977 },
|
| 93 | { 0x000000000000000f, 0xe73c1968 },
|
| 94 | }, {
|
| 95 | { 0x000000197a3a0cf7, 0x00000002 },
|
| 96 | { 0x00000001d9632e5c, 0x00000021 },
|
| 97 | { 0x0000000071c28039, 0x000001cd },
|
| 98 | { 0x000000000120a844, 0x0000b885 },
|
| 99 | { 0x0000000000e54c0a, 0x000074b1 },
|
| 100 | { 0x00000000001a7bb3, 0x00072331 },
|
| 101 | { 0x00000000000397ad, 0x0002c61b },
|
| 102 | { 0x000000000000141e, 0x06ea2e89 },
|
| 103 | { 0x00000000000001ca, 0x4c0a72e7 },
|
| 104 | { 0x000000000000010a, 0xab002ad7 },
|
| 105 | { 0x00000000000000e5, 0x4c0a767b },
|
| 106 | { 0x00000000000000e5, 0x4c0a7596 },
|
| 107 | }, {
|
| 108 | { 0x0000017949e37538, 0x00000001 },
|
| 109 | { 0x0000001b62441f37, 0x00000055 },
|
| 110 | { 0x0000000694a3391d, 0x00000085 },
|
| 111 | { 0x0000000010b2a5d2, 0x0000a753 },
|
| 112 | { 0x000000000d4398ff, 0x00001ef9 },
|
| 113 | { 0x0000000001882ec6, 0x0005cbf9 },
|
| 114 | { 0x000000000035333b, 0x0017abdf },
|
| 115 | { 0x00000000000129f1, 0x0ab4520d },
|
| 116 | { 0x0000000000001a87, 0x18ff0472 },
|
| 117 | { 0x0000000000000f6e, 0x8ac0ce9b },
|
| 118 | { 0x0000000000000d43, 0x98ff397f },
|
| 119 | { 0x0000000000000d43, 0x98ff2c3c },
|
| 120 | }, {
|
| 121 | { 0x000011f321a74e49, 0x00000006 },
|
| 122 | { 0x0000014d8481d211, 0x0000005b },
|
| 123 | { 0x0000005025cbd92d, 0x000001e3 },
|
| 124 | { 0x00000000cb5e71e3, 0x000043e6 },
|
| 125 | { 0x00000000a18c2ee1, 0x0000c097 },
|
| 126 | { 0x0000000012a88828, 0x00036c97 },
|
| 127 | { 0x000000000287f16f, 0x002c2a25 },
|
| 128 | { 0x00000000000e2cc7, 0x02d581e3 },
|
| 129 | { 0x0000000000014318, 0x2ee07d7f },
|
| 130 | { 0x000000000000bbf4, 0x1ba08c03 },
|
| 131 | { 0x000000000000a18c, 0x2ee303af },
|
| 132 | { 0x000000000000a18c, 0x2ee26223 },
|
| 133 | }, {
|
| 134 | { 0x0000d8db8f72935d, 0x00000005 },
|
| 135 | { 0x00000fbd5aed7a2e, 0x00000002 },
|
| 136 | { 0x000003c84b6ea64a, 0x00000122 },
|
| 137 | { 0x0000000998fa8829, 0x000044b7 },
|
| 138 | { 0x000000079fb80b07, 0x00002e4a },
|
| 139 | { 0x00000000e16b20fa, 0x0002a14a },
|
| 140 | { 0x000000001e940d22, 0x00353b2e },
|
| 141 | { 0x0000000000ab40ac, 0x06fba6ba },
|
| 142 | { 0x00000000000f3f70, 0x0af7eeda },
|
| 143 | { 0x000000000008debd, 0x72d98365 },
|
| 144 | { 0x0000000000079fb8, 0x0b166dba },
|
| 145 | { 0x0000000000079fb8, 0x0b0ece02 },
|
| 146 | }, {
|
| 147 | { 0x000cc3045b8fc281, 0x00000000 },
|
| 148 | { 0x0000ed1f48b5c9fc, 0x00000079 },
|
| 149 | { 0x000038fb9c63406a, 0x000000e1 },
|
| 150 | { 0x000000909705b825, 0x00000a62 },
|
| 151 | { 0x00000072db27380d, 0x0000d689 },
|
| 152 | { 0x0000000d43fce827, 0x00082b09 },
|
| 153 | { 0x00000001ccaba11a, 0x0037e8dd },
|
| 154 | { 0x000000000a13f729, 0x0566dffd },
|
| 155 | { 0x0000000000e5b64e, 0x3728203b },
|
| 156 | { 0x000000000085a14b, 0x23d36726 },
|
| 157 | { 0x000000000072db27, 0x38f38cd7 },
|
| 158 | { 0x000000000072db27, 0x3880b1b0 },
|
| 159 | }, {
|
| 160 | { 0x00eafeb9c993592b, 0x00000001 },
|
| 161 | { 0x00110e5befa9a991, 0x00000048 },
|
| 162 | { 0x00041947b4a1d36a, 0x000000dc },
|
| 163 | { 0x00000a6679327311, 0x0000c079 },
|
| 164 | { 0x00000842f488162e, 0x00002284 },
|
| 165 | { 0x000000f4459740fc, 0x00084484 },
|
| 166 | { 0x0000002122c47bf9, 0x002ca446 },
|
| 167 | { 0x00000000b9936290, 0x004979c4 },
|
| 168 | { 0x000000001085e910, 0x05a83974 },
|
| 169 | { 0x00000000099ca89d, 0x9db446bf },
|
| 170 | { 0x000000000842f488, 0x26b40b94 },
|
| 171 | { 0x000000000842f488, 0x1e71170c },
|
| 172 | }, {
|
| 173 | { 0x1b60cece589da1d2, 0x00000001 },
|
| 174 | { 0x01fcb42be1453f5b, 0x0000004f },
|
| 175 | { 0x007a3f2457df0749, 0x0000013f },
|
| 176 | { 0x0001363130e3ec7b, 0x000017aa },
|
| 177 | { 0x0000f66745411d8a, 0x0000b063 },
|
| 178 | { 0x00001c757dfab350, 0x00048863 },
|
| 179 | { 0x000003dc4979c652, 0x00224ea7 },
|
| 180 | { 0x000000159edc3144, 0x06409ab3 },
|
| 181 | { 0x00000001ecce8a7e, 0x30bc25e5 },
|
| 182 | { 0x000000011eadfee3, 0xa99c48a8 },
|
| 183 | { 0x00000000f6674543, 0x0a593ae9 },
|
| 184 | { 0x00000000f6674542, 0x13f1f5a5 },
|
| 185 | }, {
|
| 186 | { 0x1c71c71c71c71c71, 0x00000002 },
|
| 187 | { 0x0210842108421084, 0x0000000b },
|
| 188 | { 0x007f01fc07f01fc0, 0x000000fb },
|
| 189 | { 0x00014245eabf1f9a, 0x0000a63d },
|
| 190 | { 0x0000ffffffffffff, 0x0000fffb },
|
| 191 | { 0x00001d913cecc509, 0x0007937b },
|
| 192 | { 0x00000402c70c678f, 0x0005bfc9 },
|
| 193 | { 0x00000016766cb70b, 0x045edf97 },
|
| 194 | { 0x00000001fffffffb, 0x80000000 },
|
| 195 | { 0x0000000129d84b3a, 0xa2e8fe71 },
|
| 196 | { 0x0000000100000001, 0xfffffffd },
|
| 197 | { 0x0000000100000000, 0xfffffffb },
|
| 198 | }, {
|
| 199 | { 0x1c71c71c71c71c71, 0x00000003 },
|
| 200 | { 0x0210842108421084, 0x0000000c },
|
| 201 | { 0x007f01fc07f01fc0, 0x000000fc },
|
| 202 | { 0x00014245eabf1f9a, 0x0000a63e },
|
| 203 | { 0x0000ffffffffffff, 0x0000fffc },
|
| 204 | { 0x00001d913cecc509, 0x0007937c },
|
| 205 | { 0x00000402c70c678f, 0x0005bfca },
|
| 206 | { 0x00000016766cb70b, 0x045edf98 },
|
| 207 | { 0x00000001fffffffc, 0x00000000 },
|
| 208 | { 0x0000000129d84b3a, 0xa2e8fe72 },
|
| 209 | { 0x0000000100000002, 0x00000000 },
|
| 210 | { 0x0000000100000000, 0xfffffffc },
|
| 211 | }, {
|
| 212 | { 0x1c71c71c71c71c71, 0x00000006 },
|
| 213 | { 0x0210842108421084, 0x0000000f },
|
| 214 | { 0x007f01fc07f01fc0, 0x000000ff },
|
| 215 | { 0x00014245eabf1f9a, 0x0000a641 },
|
| 216 | { 0x0000ffffffffffff, 0x0000ffff },
|
| 217 | { 0x00001d913cecc509, 0x0007937f },
|
| 218 | { 0x00000402c70c678f, 0x0005bfcd },
|
| 219 | { 0x00000016766cb70b, 0x045edf9b },
|
| 220 | { 0x00000001fffffffc, 0x00000003 },
|
| 221 | { 0x0000000129d84b3a, 0xa2e8fe75 },
|
| 222 | { 0x0000000100000002, 0x00000003 },
|
| 223 | { 0x0000000100000001, 0x00000000 },
|
| 224 | },
|
| 225 | };
|
| 226 |
|
| 227 | static inline bool test_div64_verify(u64 quotient, u32 remainder, int i, int j)
|
| 228 | {
|
| 229 | return (quotient == test_div64_results[i][j].quotient &&
|
| 230 | remainder == test_div64_results[i][j].remainder);
|
| 231 | }
|
| 232 |
|
| 233 | |
| 234 | * This needs to be a macro, because we don't want to rely on the compiler
|
| 235 | * to do constant propagation, and `do_div' may take a different path for
|
| 236 | * constants, so we do want to verify that as well.
|
| 237 |
|
| 238 | #define test_div64_one(dividend, divisor, i, j) ({ \
|
| 239 | bool result = true; \
|
| 240 | u64 quotient; \
|
| 241 | u32 remainder; \
|
| 242 | \
|
| 243 | quotient = dividend; \
|
| 244 | remainder = do_div(quotient, divisor); \
|
| 245 | if (!test_div64_verify(quotient, remainder, i, j)) { \
|
| 246 | pr_err("ERROR: %016llx / %08x => %016llx,%08x\n", \
|
| 247 | dividend, divisor, quotient, remainder); \
|
| 248 | pr_err("ERROR: expected value => %016llx,%08x\n",\
|
| 249 | test_div64_results[i][j].quotient, \
|
| 250 | test_div64_results[i][j].remainder); \
|
| 251 | result = false; \
|
| 252 | } \
|
| 253 | result; \
|
| 254 | })
|
| 255 |
|
| 256 | |
| 257 | * Run calculation for the same divisor value expressed as a constant
|
| 258 | * and as a variable, so as to verify the implementation for both cases
|
| 259 | * should they be handled by different code execution paths.
|
| 260 |
|
| 261 | static bool __init test_div64(void)
|
| 262 | {
|
| 263 | u64 dividend;
|
| 264 | int i, j;
|
| 265 |
|
| 266 | for (i = 0; i < SIZE_DIV64_DIVIDENDS; i++) {
|
| 267 | dividend = test_div64_dividends[i];
|
| 268 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_0, i, 0))
|
| 269 | return false;
|
| 270 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_1, i, 1))
|
| 271 | return false;
|
| 272 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_2, i, 2))
|
| 273 | return false;
|
| 274 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_3, i, 3))
|
| 275 | return false;
|
| 276 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_4, i, 4))
|
| 277 | return false;
|
| 278 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_5, i, 5))
|
| 279 | return false;
|
| 280 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_6, i, 6))
|
| 281 | return false;
|
| 282 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_7, i, 7))
|
| 283 | return false;
|
| 284 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_8, i, 8))
|
| 285 | return false;
|
| 286 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_9, i, 9))
|
| 287 | return false;
|
| 288 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_A, i, 10))
|
| 289 | return false;
|
| 290 | if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_B, i, 11))
|
| 291 | return false;
|
| 292 | for (j = 0; j < SIZE_DIV64_DIVISORS; j++) {
|
| 293 | if (!test_div64_one(dividend, test_div64_divisors[j],
|
| 294 | i, j))
|
| 295 | return false;
|
| 296 | }
|
| 297 | }
|
| 298 | return true;
|
| 299 | }
|
| 300 |
|
| 301 | static int __init test_div64_init(void)
|
| 302 | {
|
| 303 | struct timespec64 ts, ts0, ts1;
|
| 304 | int i;
|
| 305 |
|
| 306 | pr_info("Starting 64bit/32bit division and modulo test\n");
|
| 307 | ktime_get_ts64(&ts0);
|
| 308 |
|
| 309 | for (i = 0; i < TEST_DIV64_N_ITER; i++)
|
| 310 | if (!test_div64())
|
| 311 | break;
|
| 312 |
|
| 313 | ktime_get_ts64(&ts1);
|
| 314 | ts = timespec64_sub(ts1, ts0);
|
| 315 | pr_info("Completed 64bit/32bit division and modulo test, "
|
| 316 | "%llu.%09lus elapsed\n", ts.tv_sec, ts.tv_nsec);
|
| 317 |
|
| 318 | return 0;
|
| 319 | }
|
| 320 |
|
| 321 | static void __exit test_div64_exit(void)
|
| 322 | {
|
| 323 | }
|
| 324 |
|
| 325 | module_init(test_div64_init);
|
| 326 | module_exit(test_div64_exit);
|
| 327 |
|
| 328 | MODULE_AUTHOR("Maciej W. Rozycki <[email protected]>");
|
| 329 | MODULE_LICENSE("GPL");
|
| 330 | MODULE_DESCRIPTION("64bit/32bit division and modulo test module");
|