2 * Author: Kent Overstreet <kmo@daterainc.com>
7 #define _FILE_OFFSET_BITS 64
8 #define __USE_FILE_OFFSET64
9 #define _XOPEN_SOURCE 600
22 #include <sys/ioctl.h>
23 #include <sys/types.h>
26 #include <uuid/uuid.h>
30 #define max(x, y) ({ \
31 typeof(x) _max1 = (x); \
32 typeof(y) _max2 = (y); \
33 (void) (&_max1 == &_max2); \
34 _max1 > _max2 ? _max1 : _max2; })
36 uint64_t getblocks(int fd)
40 if (fstat(fd, &statbuf)) {
41 perror("stat error\n");
44 ret = statbuf.st_size / 512;
45 if (S_ISBLK(statbuf.st_mode))
46 if (ioctl(fd, BLKGETSIZE, &ret)) {
47 perror("ioctl error");
53 uint64_t hatoi(const char *s)
56 long long i = strtoll(s, &e, 10);
74 unsigned hatoi_validate(const char *s, const char *msg)
76 uint64_t v = hatoi(s);
79 printf("%s must be a power of two\n", msg);
86 printf("%s too large\n", msg);
91 printf("%s too small\n", msg);
98 char *skip_spaces(const char *str)
100 while (isspace(*str))
116 while (end >= s && isspace(*end))
123 ssize_t read_string_list(const char *buf, const char * const list[])
126 char *s, *d = strdup(buf);
132 for (i = 0; list[i]; i++)
133 if (!strcmp(list[i], s))
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"
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");
161 const char * const cache_replacement_policies[] = {
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)
175 char uuid_str[40], set_uuid_str[40];
178 if ((fd = open(dev, O_RDWR|O_EXCL)) == -1) {
179 printf("Can't open dev %s: %s\n", dev, strerror(errno));
183 memset(&sb, 0, sizeof(struct cache_sb));
185 sb.offset = SB_SECTOR;
187 ? BCACHE_SB_VERSION_BDEV
188 : BCACHE_SB_VERSION_CDEV;
190 memcpy(sb.magic, bcache_magic, 16);
191 uuid_generate(sb.uuid);
192 memcpy(sb.set_uuid, set_uuid, sizeof(sb.set_uuid));
194 sb.bucket_size = bucket_size;
195 sb.block_size = block_size;
197 uuid_unparse(sb.uuid, uuid_str);
198 uuid_unparse(sb.set_uuid, set_uuid_str);
200 if (SB_IS_BDEV(&sb)) {
202 &sb, writeback ? CACHE_MODE_WRITEBACK : CACHE_MODE_WRITETHROUGH);
204 if (data_offset != BDEV_DATA_START_DEFAULT) {
205 sb.version = BCACHE_SB_VERSION_BDEV_WITH_OFFSET;
206 sb.data_offset = data_offset;
213 "data_offset: %ju\n",
214 uuid_str, set_uuid_str,
215 (unsigned) sb.version,
219 sb.nbuckets = getblocks(fd) / sb.bucket_size;
221 sb.first_bucket = (23 / sb.bucket_size) + 1;
223 if (sb.nbuckets < 1 << 7) {
224 printf("Not enough buckets: %ju, need %u\n",
225 sb.nbuckets, 1 << 7);
229 SET_CACHE_DISCARD(&sb, discard);
230 SET_CACHE_REPLACEMENT(&sb, cache_replacement_policy);
240 "first_bucket: %u\n",
241 uuid_str, set_uuid_str,
242 (unsigned) sb.version,
251 sb.csum = csum_set(&sb);
253 if (pwrite(fd, &sb, sizeof(sb), SB_SECTOR << 9) != sizeof(sb)) {
254 perror("write error\n");
262 static unsigned get_blocksize(const char *path)
266 if (stat(path, &statbuf)) {
267 fprintf(stderr, "Error statting %s: %s\n",
268 path, strerror(errno));
272 if (S_ISBLK(statbuf.st_mode)) {
274 * BLKALIGNOFF: alignment_offset
275 * BLKPBSZGET: physical_block_size
276 * BLKSSZGET: logical_block_size
277 * BLKIOMIN: minimum_io_size
278 * BLKIOOPT: optimal_io_size
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.
285 unsigned int logical_block_size;
286 int fd = open(path, O_RDONLY);
289 fprintf(stderr, "open(%s) failed: %m\n", path);
292 if (ioctl(fd, BLKSSZGET, &logical_block_size)) {
293 fprintf(stderr, "ioctl(%s, BLKSSZGET) failed: %m\n", path);
297 return logical_block_size / 512;
300 /* else: not a block device.
301 * Why would we even want to write a bcache super block there? */
303 return statbuf.st_blksize / 512;
306 int main(int argc, char **argv)
309 unsigned i, ncache_devices = 0, nbacking_devices = 0;
310 char *cache_devices[argc];
311 char *backing_devices[argc];
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;
319 uuid_generate(set_uuid);
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 },
335 while ((c = getopt_long(argc, argv,
346 bucket_size = hatoi_validate(optarg, "bucket size");
349 block_size = hatoi_validate(optarg, "block size");
353 if (uuid_parse(optarg, sb.uuid)) {
354 printf("Bad uuid\n");
360 cache_replacement_policy = read_string_list(optarg,
361 cache_replacement_policies);
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);
372 if (uuid_parse(optarg, set_uuid)) {
373 printf("Bad uuid\n");
382 printf("Please specify -C or -B\n");
387 backing_devices[nbacking_devices++] = optarg;
389 cache_devices[ncache_devices++] = optarg;
393 if (!ncache_devices && !nbacking_devices) {
394 printf("Please supply a device\n");
398 if (bucket_size < block_size) {
399 printf("Bucket size cannot be smaller than block size\n");
404 for (i = 0; i < ncache_devices; i++)
405 block_size = max(block_size,
406 get_blocksize(cache_devices[i]));
408 for (i = 0; i < nbacking_devices; i++)
409 block_size = max(block_size,
410 get_blocksize(backing_devices[i]));
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);
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);