]> git.sesse.net Git - bcachefs-tools-debian/blob - make-bcache.c
Merge git://evilpiepirate.org/~kent/bcache-tools
[bcachefs-tools-debian] / make-bcache.c
1 /*
2  * Author: Kent Overstreet <kmo@daterainc.com>
3  *
4  * GPLv2
5  */
6
7 #define _FILE_OFFSET_BITS       64
8 #define __USE_FILE_OFFSET64
9 #define _XOPEN_SOURCE 600
10
11 #include <ctype.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <getopt.h>
15 #include <limits.h>
16 #include <linux/fs.h>
17 #include <stdbool.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/ioctl.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <uuid/uuid.h>
27
28 #include "bcache.h"
29
30 #define max(x, y) ({                            \
31         typeof(x) _max1 = (x);                  \
32         typeof(y) _max2 = (y);                  \
33         (void) (&_max1 == &_max2);              \
34         _max1 > _max2 ? _max1 : _max2; })
35
36 uint64_t getblocks(int fd)
37 {
38         uint64_t ret;
39         struct stat statbuf;
40         if (fstat(fd, &statbuf)) {
41                 perror("stat error\n");
42                 exit(EXIT_FAILURE);
43         }
44         ret = statbuf.st_size / 512;
45         if (S_ISBLK(statbuf.st_mode))
46                 if (ioctl(fd, BLKGETSIZE, &ret)) {
47                         perror("ioctl error");
48                         exit(EXIT_FAILURE);
49                 }
50         return ret;
51 }
52
53 uint64_t hatoi(const char *s)
54 {
55         char *e;
56         long long i = strtoll(s, &e, 10);
57         switch (*e) {
58                 case 't':
59                 case 'T':
60                         i *= 1024;
61                 case 'g':
62                 case 'G':
63                         i *= 1024;
64                 case 'm':
65                 case 'M':
66                         i *= 1024;
67                 case 'k':
68                 case 'K':
69                         i *= 1024;
70         }
71         return i;
72 }
73
74 unsigned hatoi_validate(const char *s, const char *msg)
75 {
76         uint64_t v = hatoi(s);
77
78         if (v & (v - 1)) {
79                 printf("%s must be a power of two\n", msg);
80                 exit(EXIT_FAILURE);
81         }
82
83         v /= 512;
84
85         if (v > USHRT_MAX) {
86                 printf("%s too large\n", msg);
87                 exit(EXIT_FAILURE);
88         }
89
90         if (!v) {
91                 printf("%s too small\n", msg);
92                 exit(EXIT_FAILURE);
93         }
94
95         return v;
96 }
97
98 char *skip_spaces(const char *str)
99 {
100         while (isspace(*str))
101                 ++str;
102         return (char *)str;
103 }
104
105 char *strim(char *s)
106 {
107         size_t size;
108         char *end;
109
110         s = skip_spaces(s);
111         size = strlen(s);
112         if (!size)
113                 return s;
114
115         end = s + size - 1;
116         while (end >= s && isspace(*end))
117                 end--;
118         *(end + 1) = '\0';
119
120         return s;
121 }
122
123 ssize_t read_string_list(const char *buf, const char * const list[])
124 {
125         size_t i;
126         char *s, *d = strdup(buf);
127         if (!d)
128                 return -ENOMEM;
129
130         s = strim(d);
131
132         for (i = 0; list[i]; i++)
133                 if (!strcmp(list[i], s))
134                         break;
135
136         free(d);
137
138         if (!list[i])
139                 return -EINVAL;
140
141         return i;
142 }
143
144 void usage()
145 {
146         printf("Usage: make-bcache [options] device\n"
147                "        -C, --cache             Format a cache device\n"
148                "        -B, --bdev              Format a backing device\n"
149                "        -b, --bucket            bucket size\n"
150                "        -w, --block             block size (hard sector size of SSD, often 2k)\n"
151                "        -o, --data-offset       data offset in sectors\n"
152                "            --cset-uuid         UUID for the cache set\n"
153 //             "        -U                      UUID\n"
154                "            --writeback         enable writeback\n"
155                "            --discard           enable discards\n"
156                "            --cache_replacement_policy=(lru|fifo)\n"
157                "        -h, --help              display this help and exit\n");
158         exit(EXIT_FAILURE);
159 }
160
161 const char * const cache_replacement_policies[] = {
162         "lru",
163         "fifo",
164         "random",
165         NULL
166 };
167
168 static void write_sb(char *dev, unsigned block_size, unsigned bucket_size,
169                      bool writeback, bool discard,
170                      unsigned cache_replacement_policy,
171                      uint64_t data_offset,
172                      uuid_t set_uuid, bool bdev)
173 {
174         int fd;
175         char uuid_str[40], set_uuid_str[40];
176         struct cache_sb sb;
177
178         if ((fd = open(dev, O_RDWR|O_EXCL)) == -1) {
179                 printf("Can't open dev %s: %s\n", dev, strerror(errno));
180                 exit(EXIT_FAILURE);
181         }
182
183         memset(&sb, 0, sizeof(struct cache_sb));
184
185         sb.offset       = SB_SECTOR;
186         sb.version      = bdev
187                 ? BCACHE_SB_VERSION_BDEV
188                 : BCACHE_SB_VERSION_CDEV;
189
190         memcpy(sb.magic, bcache_magic, 16);
191         uuid_generate(sb.uuid);
192         memcpy(sb.set_uuid, set_uuid, sizeof(sb.set_uuid));
193
194         sb.bucket_size  = bucket_size;
195         sb.block_size   = block_size;
196
197         uuid_unparse(sb.uuid, uuid_str);
198         uuid_unparse(sb.set_uuid, set_uuid_str);
199
200         if (SB_IS_BDEV(&sb)) {
201                 SET_BDEV_CACHE_MODE(
202                         &sb, writeback ? CACHE_MODE_WRITEBACK : CACHE_MODE_WRITETHROUGH);
203
204                 if (data_offset != BDEV_DATA_START_DEFAULT) {
205                         sb.version = BCACHE_SB_VERSION_BDEV_WITH_OFFSET;
206                         sb.data_offset = data_offset;
207                 }
208
209                 printf("UUID:                   %s\n"
210                        "Set UUID:               %s\n"
211                        "version:                %u\n"
212                        "block_size:             %u\n"
213                        "data_offset:            %ju\n",
214                        uuid_str, set_uuid_str,
215                        (unsigned) sb.version,
216                        sb.block_size,
217                        data_offset);
218         } else {
219                 sb.nbuckets             = getblocks(fd) / sb.bucket_size;
220                 sb.nr_in_set            = 1;
221                 sb.first_bucket         = (23 / sb.bucket_size) + 1;
222
223                 if (sb.nbuckets < 1 << 7) {
224                         printf("Not enough buckets: %ju, need %u\n",
225                                sb.nbuckets, 1 << 7);
226                         exit(EXIT_FAILURE);
227                 }
228
229                 SET_CACHE_DISCARD(&sb, discard);
230                 SET_CACHE_REPLACEMENT(&sb, cache_replacement_policy);
231
232                 printf("UUID:                   %s\n"
233                        "Set UUID:               %s\n"
234                        "version:                %u\n"
235                        "nbuckets:               %ju\n"
236                        "block_size:             %u\n"
237                        "bucket_size:            %u\n"
238                        "nr_in_set:              %u\n"
239                        "nr_this_dev:            %u\n"
240                        "first_bucket:           %u\n",
241                        uuid_str, set_uuid_str,
242                        (unsigned) sb.version,
243                        sb.nbuckets,
244                        sb.block_size,
245                        sb.bucket_size,
246                        sb.nr_in_set,
247                        sb.nr_this_dev,
248                        sb.first_bucket);
249         }
250
251         sb.csum = csum_set(&sb);
252
253         if (pwrite(fd, &sb, sizeof(sb), SB_SECTOR << 9) != sizeof(sb)) {
254                 perror("write error\n");
255                 exit(EXIT_FAILURE);
256         }
257
258         fsync(fd);
259         close(fd);
260 }
261
262 static unsigned get_blocksize(const char *path)
263 {
264         struct stat statbuf;
265
266         if (stat(path, &statbuf)) {
267                 fprintf(stderr, "Error statting %s: %s\n",
268                         path, strerror(errno));
269                 exit(EXIT_FAILURE);
270         }
271
272         if (S_ISBLK(statbuf.st_mode)) {
273                 /* check IO limits:
274                  * BLKALIGNOFF: alignment_offset
275                  * BLKPBSZGET: physical_block_size
276                  * BLKSSZGET: logical_block_size
277                  * BLKIOMIN: minimum_io_size
278                  * BLKIOOPT: optimal_io_size
279                  *
280                  * It may be tempting to use physical_block_size,
281                  * or even minimum_io_size.
282                  * But to be as transparent as possible,
283                  * we want to use logical_block_size.
284                  */
285                 unsigned int logical_block_size;
286                 int fd = open(path, O_RDONLY);
287
288                 if (fd < 0) {
289                         fprintf(stderr, "open(%s) failed: %m\n", path);
290                         exit(EXIT_FAILURE);
291                 }
292                 if (ioctl(fd, BLKSSZGET, &logical_block_size)) {
293                         fprintf(stderr, "ioctl(%s, BLKSSZGET) failed: %m\n", path);
294                         exit(EXIT_FAILURE);
295                 }
296                 close(fd);
297                 return logical_block_size / 512;
298
299         }
300         /* else: not a block device.
301          * Why would we even want to write a bcache super block there? */
302
303         return statbuf.st_blksize / 512;
304 }
305
306 int main(int argc, char **argv)
307 {
308         int c, bdev = -1;
309         unsigned i, ncache_devices = 0, nbacking_devices = 0;
310         char *cache_devices[argc];
311         char *backing_devices[argc];
312
313         unsigned block_size = 0, bucket_size = 1024;
314         int writeback = 0, discard = 0;
315         unsigned cache_replacement_policy = 0;
316         uint64_t data_offset = BDEV_DATA_START_DEFAULT;
317         uuid_t set_uuid;
318
319         uuid_generate(set_uuid);
320
321         struct option opts[] = {
322                 { "cache",              0, NULL,        'C' },
323                 { "bdev",               0, NULL,        'B' },
324                 { "bucket",             1, NULL,        'b' },
325                 { "block",              1, NULL,        'w' },
326                 { "writeback",          0, &writeback,  1 },
327                 { "discard",            0, &discard,    1 },
328                 { "cache_replacement_policy", 1, NULL, 'p' },
329                 { "data_offset",        1, NULL,        'o' },
330                 { "cset-uuid",          1, NULL,        'u' },
331                 { "help",               0, NULL,        'h' },
332                 { NULL,                 0, NULL,        0 },
333         };
334
335         while ((c = getopt_long(argc, argv,
336                                 "-hCBU:w:b:",
337                                 opts, NULL)) != -1)
338                 switch (c) {
339                 case 'C':
340                         bdev = 0;
341                         break;
342                 case 'B':
343                         bdev = 1;
344                         break;
345                 case 'b':
346                         bucket_size = hatoi_validate(optarg, "bucket size");
347                         break;
348                 case 'w':
349                         block_size = hatoi_validate(optarg, "block size");
350                         break;
351 #if 0
352                 case 'U':
353                         if (uuid_parse(optarg, sb.uuid)) {
354                                 printf("Bad uuid\n");
355                                 exit(EXIT_FAILURE);
356                         }
357                         break;
358 #endif
359                 case 'p':
360                         cache_replacement_policy = read_string_list(optarg,
361                                                     cache_replacement_policies);
362                         break;
363                 case 'o':
364                         data_offset = atoll(optarg);
365                         if (data_offset < BDEV_DATA_START_DEFAULT) {
366                                 printf("Bad data offset; minimum %d sectors\n",
367                                        BDEV_DATA_START_DEFAULT);
368                                 exit(EXIT_FAILURE);
369                         }
370                         break;
371                 case 'u':
372                         if (uuid_parse(optarg, set_uuid)) {
373                                 printf("Bad uuid\n");
374                                 exit(EXIT_FAILURE);
375                         }
376                         break;
377                 case 'h':
378                         usage();
379                         break;
380                 case 1:
381                         if (bdev == -1) {
382                                 printf("Please specify -C or -B\n");
383                                 exit(EXIT_FAILURE);
384                         }
385
386                         if (bdev)
387                                 backing_devices[nbacking_devices++] = optarg;
388                         else
389                                 cache_devices[ncache_devices++] = optarg;
390                         break;
391                 }
392
393         if (!ncache_devices && !nbacking_devices) {
394                 printf("Please supply a device\n");
395                 usage();
396         }
397
398         if (bucket_size < block_size) {
399                 printf("Bucket size cannot be smaller than block size\n");
400                 exit(EXIT_FAILURE);
401         }
402
403         if (!block_size) {
404                 for (i = 0; i < ncache_devices; i++)
405                         block_size = max(block_size,
406                                          get_blocksize(cache_devices[i]));
407
408                 for (i = 0; i < nbacking_devices; i++)
409                         block_size = max(block_size,
410                                          get_blocksize(backing_devices[i]));
411         }
412
413         for (i = 0; i < ncache_devices; i++)
414                 write_sb(cache_devices[i], block_size, bucket_size,
415                          writeback, discard, cache_replacement_policy,
416                          data_offset, set_uuid, false);
417
418         for (i = 0; i < nbacking_devices; i++)
419                 write_sb(backing_devices[i], block_size, bucket_size,
420                          writeback, discard, cache_replacement_policy,
421                          data_offset, set_uuid, true);
422
423         return 0;
424 }