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