]> git.sesse.net Git - bcachefs-tools-debian/blob - tools-util.c
Faster crc32c
[bcachefs-tools-debian] / tools-util.c
1 #include <assert.h>
2 #include <ctype.h>
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <linux/fs.h>
7 #include <math.h>
8 #include <stdbool.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/ioctl.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15
16 #include <blkid.h>
17 #include <uuid/uuid.h>
18
19 #include "bcachefs_ioctl.h"
20 #include "linux/sort.h"
21 #include "tools-util.h"
22 #include "util.h"
23
24 void die(const char *fmt, ...)
25 {
26         va_list args;
27
28         va_start(args, fmt);
29         vfprintf(stderr, fmt, args);
30         va_end(args);
31         fputc('\n', stderr);
32
33         exit(EXIT_FAILURE);
34 }
35
36 char *mprintf(const char *fmt, ...)
37 {
38         va_list args;
39         char *str;
40         int ret;
41
42         va_start(args, fmt);
43         ret = vasprintf(&str, fmt, args);
44         va_end(args);
45
46         if (ret < 0)
47                 die("insufficient memory");
48
49         return str;
50 }
51
52 void *xcalloc(size_t count, size_t size)
53 {
54         void *p = calloc(count, size);
55
56         if (!p)
57                 die("insufficient memory");
58
59         return p;
60 }
61
62 void *xmalloc(size_t size)
63 {
64         void *p = malloc(size);
65
66         if (!p)
67                 die("insufficient memory");
68
69         memset(p, 0, size);
70         return p;
71 }
72
73 void xpread(int fd, void *buf, size_t count, off_t offset)
74 {
75         ssize_t r = pread(fd, buf, count, offset);
76
77         if (r != count)
78                 die("read error (ret %zi)", r);
79 }
80
81 void xpwrite(int fd, const void *buf, size_t count, off_t offset)
82 {
83         ssize_t r = pwrite(fd, buf, count, offset);
84
85         if (r != count)
86                 die("write error (ret %zi err %m)", r);
87 }
88
89 struct stat xfstatat(int dirfd, const char *path, int flags)
90 {
91         struct stat stat;
92         if (fstatat(dirfd, path, &stat, flags))
93                 die("stat error: %m");
94         return stat;
95 }
96
97 struct stat xfstat(int fd)
98 {
99         struct stat stat;
100         if (fstat(fd, &stat))
101                 die("stat error: %m");
102         return stat;
103 }
104
105 /* Integer stuff: */
106
107 struct units_buf __pr_units(u64 v, enum units units)
108 {
109         struct units_buf ret;
110
111         switch (units) {
112         case BYTES:
113                 snprintf(ret.b, sizeof(ret.b), "%llu", v << 9);
114                 break;
115         case SECTORS:
116                 snprintf(ret.b, sizeof(ret.b), "%llu", v);
117                 break;
118         case HUMAN_READABLE:
119                 v <<= 9;
120
121                 if (v >= 1024) {
122                         int exp = log(v) / log(1024);
123                         snprintf(ret.b, sizeof(ret.b), "%.1f%c",
124                                  v / pow(1024, exp),
125                                  "KMGTPE"[exp-1]);
126                 } else {
127                         snprintf(ret.b, sizeof(ret.b), "%llu", v);
128                 }
129
130                 break;
131         }
132
133         return ret;
134 }
135
136 /* Argument parsing stuff: */
137
138 /* File parsing (i.e. sysfs) */
139
140 char *read_file_str(int dirfd, const char *path)
141 {
142         int fd = xopenat(dirfd, path, O_RDONLY);
143         ssize_t len = xfstat(fd).st_size;
144
145         char *buf = xmalloc(len + 1);
146
147         len = read(fd, buf, len);
148         if (len < 0)
149                 die("read error: %m");
150
151         buf[len] = '\0';
152         if (len && buf[len - 1] == '\n')
153                 buf[len - 1] = '\0';
154
155         close(fd);
156
157         return buf;
158 }
159
160 u64 read_file_u64(int dirfd, const char *path)
161 {
162         char *buf = read_file_str(dirfd, path);
163         u64 v;
164         if (kstrtou64(buf, 10, &v))
165                 die("read_file_u64: error parsing %s (got %s)", path, buf);
166         free(buf);
167         return v;
168 }
169
170 /* String list options: */
171
172 ssize_t read_string_list_or_die(const char *opt, const char * const list[],
173                                 const char *msg)
174 {
175         ssize_t v = bch2_read_string_list(opt, list);
176         if (v < 0)
177                 die("Bad %s %s", msg, opt);
178
179         return v;
180 }
181
182 /* Returns size of file or block device: */
183 u64 get_size(const char *path, int fd)
184 {
185         struct stat statbuf = xfstat(fd);
186
187         if (!S_ISBLK(statbuf.st_mode))
188                 return statbuf.st_size;
189
190         u64 ret;
191         xioctl(fd, BLKGETSIZE64, &ret);
192         return ret;
193 }
194
195 /* Returns blocksize in units of 512 byte sectors: */
196 unsigned get_blocksize(const char *path, int fd)
197 {
198         struct stat statbuf = xfstat(fd);
199
200         if (!S_ISBLK(statbuf.st_mode))
201                 return statbuf.st_blksize >> 9;
202
203         unsigned ret;
204         xioctl(fd, BLKPBSZGET, &ret);
205         return ret >> 9;
206 }
207
208 /* Open a block device, do magic blkid stuff to probe for existing filesystems: */
209 int open_for_format(const char *dev, bool force)
210 {
211         blkid_probe pr;
212         const char *fs_type = NULL, *fs_label = NULL;
213         size_t fs_type_len, fs_label_len;
214
215         int fd = xopen(dev, O_RDWR|O_EXCL);
216
217         if (force)
218                 return fd;
219
220         if (!(pr = blkid_new_probe()))
221                 die("blkid error 1");
222         if (blkid_probe_set_device(pr, fd, 0, 0))
223                 die("blkid error 2");
224         if (blkid_probe_enable_partitions(pr, true))
225                 die("blkid error 3");
226         if (blkid_do_fullprobe(pr) < 0)
227                 die("blkid error 4");
228
229         blkid_probe_lookup_value(pr, "TYPE", &fs_type, &fs_type_len);
230         blkid_probe_lookup_value(pr, "LABEL", &fs_label, &fs_label_len);
231
232         if (fs_type) {
233                 if (fs_label)
234                         printf("%s contains a %s filesystem labelled '%s'\n",
235                                dev, fs_type, fs_label);
236                 else
237                         printf("%s contains a %s filesystem\n",
238                                dev, fs_type);
239                 fputs("Proceed anyway?", stdout);
240                 if (!ask_yn())
241                         exit(EXIT_FAILURE);
242         }
243
244         blkid_free_probe(pr);
245         return fd;
246 }
247
248 /* Global control device: */
249 int bcachectl_open(void)
250 {
251         return xopen("/dev/bcachefs-ctl", O_RDWR);
252 }
253
254 /* Filesystem handles (ioctl, sysfs dir): */
255
256 #define SYSFS_BASE "/sys/fs/bcachefs/"
257
258 struct bcache_handle bcache_fs_open(const char *path)
259 {
260         struct bcache_handle ret;
261         uuid_t tmp;
262
263         if (!uuid_parse(path, tmp)) {
264                 /* It's a UUID, look it up in sysfs: */
265                 char *sysfs = mprintf("%s%s", SYSFS_BASE, path);
266                 ret.sysfs_fd = xopen(sysfs, O_RDONLY);
267
268                 char *minor = read_file_str(ret.sysfs_fd, "minor");
269                 char *ctl = mprintf("/dev/bcachefs%s-ctl", minor);
270                 ret.ioctl_fd = xopen(ctl, O_RDWR);
271
272                 free(sysfs);
273                 free(minor);
274                 free(ctl);
275         } else {
276                 /* It's a path: */
277                 ret.ioctl_fd = xopen(path, O_RDONLY);
278
279                 struct bch_ioctl_query_uuid uuid;
280                 xioctl(ret.ioctl_fd, BCH_IOCTL_QUERY_UUID, &uuid);
281
282                 char uuid_str[40];
283                 uuid_unparse(uuid.uuid.b, uuid_str);
284
285                 char *sysfs = mprintf("%s%s", SYSFS_BASE, uuid_str);
286                 ret.sysfs_fd = xopen(sysfs, O_RDONLY);
287                 free(sysfs);
288         }
289
290         return ret;
291 }
292
293 bool ask_yn(void)
294 {
295         const char *short_yes = "yY";
296         char *buf = NULL;
297         size_t buflen = 0;
298         bool ret;
299
300         fputs(" (y,n) ", stdout);
301         fflush(stdout);
302
303         if (getline(&buf, &buflen, stdin) < 0)
304                 die("error reading from standard input");
305
306         ret = strchr(short_yes, buf[0]);
307         free(buf);
308         return ret;
309 }
310
311 static int range_cmp(const void *_l, const void *_r)
312 {
313         const struct range *l = _l, *r = _r;
314
315         if (l->start < r->start)
316                 return -1;
317         if (l->start > r->start)
318                 return  1;
319         return 0;
320 }
321
322 void ranges_sort_merge(ranges *r)
323 {
324         struct range *t, *i;
325         ranges tmp = { NULL };
326
327         sort(&darray_item(*r, 0), darray_size(*r),
328              sizeof(darray_item(*r, 0)), range_cmp, NULL);
329
330         /* Merge contiguous ranges: */
331         darray_foreach(i, *r) {
332                 t = tmp.size ?  &tmp.item[tmp.size - 1] : NULL;
333
334                 if (t && t->end >= i->start)
335                         t->end = max(t->end, i->end);
336                 else
337                         darray_append(tmp, *i);
338         }
339
340         darray_free(*r);
341         *r = tmp;
342 }
343
344 void ranges_roundup(ranges *r, unsigned block_size)
345 {
346         struct range *i;
347
348         darray_foreach(i, *r) {
349                 i->start = round_down(i->start, block_size);
350                 i->end  = round_up(i->end, block_size);
351         }
352 }
353
354 void ranges_rounddown(ranges *r, unsigned block_size)
355 {
356         struct range *i;
357
358         darray_foreach(i, *r) {
359                 i->start = round_up(i->start, block_size);
360                 i->end  = round_down(i->end, block_size);
361                 i->end  = max(i->end, i->start);
362         }
363 }
364
365 struct fiemap_extent fiemap_iter_next(struct fiemap_iter *iter)
366 {
367         struct fiemap_extent e;
368
369         BUG_ON(iter->idx > iter->f.fm_mapped_extents);
370
371         if (iter->idx == iter->f.fm_mapped_extents) {
372                 xioctl(iter->fd, FS_IOC_FIEMAP, &iter->f);
373
374                 if (!iter->f.fm_mapped_extents)
375                         return (struct fiemap_extent) { .fe_length = 0 };
376
377                 iter->idx = 0;
378         }
379
380         e = iter->f.fm_extents[iter->idx++];
381         BUG_ON(!e.fe_length);
382
383         iter->f.fm_start = e.fe_logical + e.fe_length;
384
385         return e;
386 }
387
388 const char *strcmp_prefix(const char *a, const char *a_prefix)
389 {
390         while (*a_prefix && *a == *a_prefix) {
391                 a++;
392                 a_prefix++;
393         }
394         return *a_prefix ? NULL : a;
395 }
396
397 unsigned hatoi_validate(const char *s, const char *msg)
398 {
399         u64 v;
400
401         if (bch2_strtoull_h(s, &v))
402                 die("bad %s %s", msg, s);
403
404         if (v & (v - 1))
405                 die("%s must be a power of two", msg);
406
407         v /= 512;
408
409         if (v > USHRT_MAX)
410                 die("%s too large\n", msg);
411
412         if (!v)
413                 die("%s too small\n", msg);
414
415         return v;
416 }
417
418 /* crc32c */
419
420 static u32 crc32c_default(u32 crc, const void *buf, size_t size)
421 {
422         static const u32 crc32c_tab[] = {
423                 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
424                 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
425                 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
426                 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
427                 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
428                 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
429                 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
430                 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
431                 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
432                 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
433                 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
434                 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
435                 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
436                 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
437                 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
438                 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
439                 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
440                 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
441                 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
442                 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
443                 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
444                 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
445                 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
446                 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
447                 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
448                 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
449                 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
450                 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
451                 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
452                 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
453                 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
454                 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
455                 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
456                 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
457                 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
458                 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
459                 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
460                 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
461                 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
462                 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
463                 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
464                 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
465                 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
466                 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
467                 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
468                 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
469                 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
470                 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
471                 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
472                 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
473                 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
474                 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
475                 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
476                 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
477                 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
478                 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
479                 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
480                 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
481                 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
482                 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
483                 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
484                 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
485                 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
486                 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351
487         };
488         const u8 *p = buf;
489
490         while (size--)
491                 crc = crc32c_tab[(crc ^ *p++) & 0xFFL] ^ (crc >> 8);
492
493         return crc;
494 }
495
496 #include <linux/compiler.h>
497
498 #ifdef __x86_64__
499
500 #ifdef CONFIG_X86_64
501 #define REX_PRE "0x48, "
502 #else
503 #define REX_PRE
504 #endif
505
506 static u32 crc32c_sse42(u32 crc, const void *buf, size_t size)
507 {
508         while (size >= sizeof(long)) {
509                 const unsigned long *d = buf;
510
511                 __asm__ __volatile__(
512                         ".byte 0xf2, " REX_PRE "0xf, 0x38, 0xf1, 0xf1;"
513                         :"=S"(crc)
514                         :"0"(crc), "c"(*d)
515                 );
516                 buf     += sizeof(long);
517                 size    -= sizeof(long);
518         }
519
520         while (size) {
521                 const u8 *d = buf;
522
523                 __asm__ __volatile__(
524                         ".byte 0xf2, 0xf, 0x38, 0xf0, 0xf1"
525                         :"=S"(crc)
526                         :"0"(crc), "c"(*d)
527                 );
528                 buf     += 1;
529                 size    -= 1;
530         }
531
532         return crc;
533 }
534
535 static void *resolve_crc32c(void)
536 {
537         __builtin_cpu_init();
538
539 #ifdef __x86_64__
540         if (__builtin_cpu_supports("sse4.2"))
541                 return crc32c_sse42;
542 #endif
543         return crc32c_default;
544 }
545
546 /*
547  * ifunc is buggy and I don't know what breaks it (LTO?)
548  */
549 #ifdef HAVE_WORKING_IFUNC
550
551 u32 crc32c(u32, const void *, size_t)
552         __attribute__((ifunc("resolve_crc32c")));
553
554 #else
555
556 u32 crc32c(u32 crc, const void *buf, size_t size)
557 {
558         static u32 (*real_crc32c)(u32, const void *, size_t);
559
560         if (unlikely(!real_crc32c))
561                 real_crc32c = resolve_crc32c();
562
563         return real_crc32c(crc, buf, size);
564 }
565
566 #endif /* HAVE_WORKING_IFUNC */
567
568 #endif