DF-2632 / naturalprobe.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | /* * DF-2632 — natural-name negative-control probe. * * (a) replicates hammer2_dirhash() in userland (hammer2_subr.c:178-229) * and reports the max number of natural names sharing one 64K * collision window (key & ~HAMMER2_DIRHASH_LOMASK, LOMASK=0x7FFF); * (b) creates the natural-named files on the hammer2 mount with periodic * sync(), proving (or refuting) that natural names cannot make any * window dense enough to trip the flusher overlap panic. * * usage: naturalprobe <dir> <count-sequential> <count-random> [batch] */ #include <fcntl.h> #include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> static uint32_t tbl[256]; static void mk_tbl(void) { uint32_t i, j, c; for (i = 0; i < 256; ++i) { c = i; for (j = 0; j < 8; ++j) c = (c >> 1) ^ (0x82F63B78 & (-(c & 1))); tbl[i] = c; } } static uint32_t icrc32(const char *b, size_t n) { uint32_t r = 0xFFFFFFFF; size_t i; for (i = 0; i < n; ++i) r = (r >> 8) ^ tbl[(r ^ (uint8_t)b[i]) & 0xFF]; return r ^ 0xFFFFFFFF; } /* replica of hammer2_dirhash() for delimiter-free names */ static uint64_t dirhash(const char *name, size_t len) { uint64_t key = 0; uint32_t crcx; crcx = icrc32(name, len); /* single segment: m32 == crc */ crcx |= 0x80000000U; key |= (uint64_t)crcx << 32; crcx = icrc32(name, len); crcx = crcx ^ (crcx << 16); key |= crcx & 0xFFFF0000U; key |= 0x8000U; return key; } struct went { uint64_t win; long cnt; }; static int cmp_win(const void *a, const void *b) { uint64_t x = ((const struct went *)a)->win; uint64_t y = ((const struct went *)b)->win; return (x < y) ? -1 : (x > y) ? 1 : 0; } static uint64_t xorshift64(uint64_t *s) { uint64_t x = *s; x ^= x << 13; x ^= x >> 7; x ^= x << 17; *s = x; return x; } int main(int argc, char **argv) { const char *dir; long nseq, nrand, batch = 512; char name[64], path[512]; struct went *w; long nw = 0, i, created = 0, errors = 0, maxwin = 0, dupwin = 0; uint64_t seed = 0x9E3779B97F4A7C15ULL; FILE *dev; if (argc < 4 || argc > 5) { fprintf(stderr, "usage: %s <dir> <nseq> <nrand> [batch]\n", argv[0]); return 2; } dir = argv[1]; nseq = atol(argv[2]); nrand = atol(argv[3]); if (argc == 5) batch = atol(argv[4]); mk_tbl(); if (icrc32("123456789", 9) != 0xE3069283u) { fprintf(stderr, "crc32c self-test FAILED\n"); return 2; } /* real randomness for the random-name half */ dev = fopen("/dev/urandom", "r"); if (dev) { if (fread(&seed, sizeof(seed), 1, dev) != 1) seed = 0x12345678DEADBEEFULL; fclose(dev); } w = calloc(nseq + nrand, sizeof(*w)); if (!w) { perror("calloc"); return 2; } for (i = 0; i < nseq; ++i) { snprintf(name, sizeof(name), "f%07ld", i); w[nw++].win = dirhash(name, strlen(name)) & ~0x7FFFULL; } for (i = 0; i < nrand; ++i) { static const char hex[] = "0123456789abcdef"; uint64_t r = xorshift64(&seed); int k; for (k = 0; k < 32; ++k) { name[k] = hex[(r >> (k % 60)) & 15]; if ((k & 15) == 15) r = xorshift64(&seed); } name[32] = 0; w[nw++].win = dirhash(name, strlen(name)) & ~0x7FFFULL; } qsort(w, nw, sizeof(*w), cmp_win); for (i = 0; i < nw; ) { long j = i; while (j < nw && w[j].win == w[i].win) ++j; if (j - i > maxwin) maxwin = j - i; if (j - i > 1) ++dupwin; i = j; } printf("names=%ld windows=%ld max-per-window=%ld windows-with->1=%ld\n", nw, nw, maxwin, dupwin); fflush(stdout); free(w); setvbuf(stdout, NULL, _IONBF, 0); seed ^= 0xA5A5A5A5A5A5A5A5ULL; for (i = 0; i < nseq; ++i) { snprintf(name, sizeof(name), "f%07ld", i); snprintf(path, sizeof(path), "%s/%s", dir, name); if (open(path, O_CREAT | O_EXCL | O_WRONLY, 0644) < 0) ++errors; else ++created; if (created % batch == 0 && created) { printf("created=%ld errors=%ld\n", created, errors); sync(); } } for (i = 0; i < nrand; ++i) { static const char hex[] = "0123456789abcdef"; uint64_t r = xorshift64(&seed); int k; for (k = 0; k < 32; ++k) { name[k] = hex[(r >> (k % 60)) & 15]; if ((k & 15) == 15) r = xorshift64(&seed); } name[32] = 0; snprintf(path, sizeof(path), "%s/%s", dir, name); if (open(path, O_CREAT | O_EXCL | O_WRONLY, 0644) < 0) ++errors; else ++created; if (created % batch == 0 && created) { printf("created=%ld errors=%ld\n", created, errors); sync(); } } sync(); printf("DONE created=%ld errors=%ld\n", created, errors); return (errors == 0) ? 0 : 1; } |