[PATCH 1/1] bcache-tools: Add command line tool 'bcache' to improve user experience.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



show the bcache devices in both table-like format and tree-like format.
users can use this tools to manage device(regist,unregist,attach,detach,set-cachemode).
---
 Makefile      |  21 +-
 bcache-main.c | 601 ++++++++++++++++++++++++++++++++++++++++++++++
 bcache.c      |   2 +-
 lib.c         | 472 ++++++++++++++++++++++++++++++++++++
 lib.h         |  60 +++++
 list.h        | 749 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 make.c        | 461 ++++++++++++++++++++++++++++++++++++
 7 files changed, 2361 insertions(+), 5 deletions(-)
 create mode 100644 bcache-main.c
 create mode 100644 lib.c
 create mode 100644 lib.h
 create mode 100644 list.h
 create mode 100644 make.c

diff --git a/Makefile b/Makefile
index c824ae3..6c53995 100644
--- a/Makefile
+++ b/Makefile
@@ -3,12 +3,12 @@ PREFIX=/usr
 UDEVLIBDIR=/lib/udev
 DRACUTLIBDIR=/lib/dracut
 INSTALL=install
-CFLAGS+=-O2 -Wall -g
+#CFLAGS+=-O2 -Wall -g
 
-all: make-bcache probe-bcache bcache-super-show bcache-register
+all: make-bcache probe-bcache bcache-super-show bcache-register depend bcache
 
 install: make-bcache probe-bcache bcache-super-show
-	$(INSTALL) -m0755 make-bcache bcache-super-show	$(DESTDIR)${PREFIX}/sbin/
+	$(INSTALL) -m0755 make-bcache bcache-super-show	bcache $(DESTDIR)${PREFIX}/sbin/
 	$(INSTALL) -m0755 probe-bcache bcache-register		$(DESTDIR)$(UDEVLIBDIR)/
 	$(INSTALL) -m0644 69-bcache.rules	$(DESTDIR)$(UDEVLIBDIR)/rules.d/
 	$(INSTALL) -m0644 -- *.8 $(DESTDIR)${PREFIX}/share/man/man8/
@@ -18,7 +18,7 @@ install: make-bcache probe-bcache bcache-super-show
 #	$(INSTALL) -m0755 bcache-test $(DESTDIR)${PREFIX}/sbin/
 
 clean:
-	$(RM) -f make-bcache probe-bcache bcache-super-show bcache-test -- *.o
+	$(RM) -f bcache make-bcache probe-bcache bcache-super-show bcache-register bcache-test -- *.o
 
 bcache-test: LDLIBS += `pkg-config --libs openssl` -lm
 make-bcache: LDLIBS += `pkg-config --libs uuid blkid`
@@ -30,3 +30,16 @@ bcache-super-show: LDLIBS += `pkg-config --libs uuid`
 bcache-super-show: CFLAGS += -std=gnu99
 bcache-super-show: bcache.o
 bcache-register: bcache-register.o
+
+CFLAGS+=`pkg-config --cflags blkid uuid smartcols`
+LDFLAGS+=`pkg-config --libs blkid uuid smartcols`
+
+SRCS=bcache-main.c bcache.c lib.c make.c
+depend: .depend
+.depend: $(SRCS)
+	rm -f ./.depend
+	$(CC) $(CFLAGS) -MM $^ > ./.depend;
+
+include .depend
+
+bcache: bcache-main.o bcache.o lib.o make.o
diff --git a/bcache-main.c b/bcache-main.c
new file mode 100644
index 0000000..1b58628
--- /dev/null
+++ b/bcache-main.c
@@ -0,0 +1,601 @@
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <regex.h>
+#include "bcache.h"
+#include "lib.h"
+#include "libsmartcols/libsmartcols.h"
+#include <locale.h>
+#include "list.h"
+
+
+//utils function
+static bool accepted_char(char c)
+{
+	if ('0' <= c && c <= '9')
+		return true;
+	if ('A' <= c && c <= 'Z')
+		return true;
+	if ('a' <= c && c <= 'z')
+		return true;
+	if (strchr(".-_", c))
+		return true;
+	return false;
+}
+
+static void print_encode(char *in)
+{
+	char *pos;
+	for (pos = in; *pos; pos++)
+		if (accepted_char(*pos))
+			putchar(*pos);
+		else
+			printf("%%%x", *pos);
+}
+
+bool bad_uuid(char *uuid)
+{
+	const char *pattern =
+	    "^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$";
+	regex_t reg;
+	int status;
+	regmatch_t regmatche;
+	if (regcomp(&reg, pattern, REG_EXTENDED) != 0) {
+		fprintf(stderr, "Error happen when check uuid format:%m");
+	}
+	status = regexec(&reg, uuid, 1, &regmatche, 0);
+	regfree(&reg);
+	if (status == REG_NOMATCH) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+bool bad_dev(char *devname)
+{
+	const char *pattern = "^/dev/[a-zA-Z0-9]*$";
+	regex_t reg;
+	int status;
+	regmatch_t regmatche;
+	if (regcomp(&reg, pattern, REG_EXTENDED) != 0) {
+		fprintf(stderr,
+			"Error happen when check device name format:%m");
+	}
+	status = regexec(&reg, devname, 1, &regmatche, 0);
+	regfree(&reg);
+	if (status == REG_NOMATCH) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+
+int ctlusage()
+{
+	fprintf(stderr,
+		"Usage:bcache-ctl [SUBCMD]\n"
+		"	show		show all bcache devices in this host\n"
+		"	tree		show active bcache devices in this host\n"
+		"	make		make regular device to bcache device\n"
+		"	regist 		regist device to kernel\n"
+		"	unregist	unregist device from kernel\n"
+		"	attach		attach backend device(data device) to cache device\n"
+		"	detach		detach backend device(data device) from cache device\n"
+		"	set-cachemode	set cachemode for backend device\n");
+	return EXIT_FAILURE;
+}
+
+int showusage()
+{
+	fprintf(stderr,
+		"Usage: show [option]"
+		"	show overall information about all devices\n"
+		"	-d --device {devname}	show the detail infomation about this device\n"
+		"	-m --more		show overall information about all devices with detail info \n"
+		"	-h --help		show help information \n");
+	return EXIT_FAILURE;
+}
+
+int treeusage()
+{
+	fprintf(stderr,
+		"Usage: tree	show active bcache devices in this host\n");
+	return EXIT_FAILURE;
+}
+
+int registusage()
+{
+	fprintf(stderr,
+		"Usage:regist devicename		regist device as bcache device to kernel\n");
+	return EXIT_FAILURE;
+}
+
+int unregistusage()
+{
+	fprintf(stderr,
+		"Usage:unregist devicename		unregist device from kernel\n");
+	return EXIT_FAILURE;
+}
+
+int attachusage()
+{
+	fprintf(stderr, "Usage:attach cset_uuid|cachedevice datadevice\n");
+	return EXIT_FAILURE;
+}
+
+int detachusage()
+{
+	fprintf(stderr, "Usage:detach devicename\n");
+	return EXIT_FAILURE;
+}
+
+int setcachemodeusage()
+{
+	fprintf(stderr, "Usage:set-cachemode devicename modetype\n");
+	return EXIT_FAILURE;
+}
+
+
+void free_dev(struct list_head *head)
+{
+	struct dev *dev;
+	list_for_each_entry(dev, head, dev_list) {
+		free(dev);
+	}
+}
+
+int show_bdevs_detail()
+{
+	struct list_head head;
+	struct dev *devs;
+	INIT_LIST_HEAD(&head);
+	int ret;
+
+	ret = list_bdevs(&head);
+	if (ret != 0) {
+		fprintf(stderr, "Failed to list devices\n");
+		return ret;
+	}
+	printf
+	    ("Name\t\tUuid\t\t\t\t\tCset_Uuid\t\t\t\tType\t\tState\t\tBname\t\tAttachToDev\tAttachToCset\n");
+	list_for_each_entry(devs, &head, dev_list) {
+		printf("%s\t%s\t%s\t%d", devs->name, devs->uuid,
+		       devs->cset, devs->version);
+		switch (devs->version) {
+			// These are handled the same by the kernel
+		case BCACHE_SB_VERSION_CDEV:
+		case BCACHE_SB_VERSION_CDEV_WITH_UUID:
+			printf(" (cache)");
+			break;
+
+			// The second adds data offset supporet
+		case BCACHE_SB_VERSION_BDEV:
+		case BCACHE_SB_VERSION_BDEV_WITH_OFFSET:
+			printf(" (data)");
+			break;
+
+		default:
+			printf(" (unknown)");
+			break;
+		}
+
+		printf("\t%-8s", devs->state);
+		printf("\t%-16s", devs->bname);
+
+		char attachdev[30];
+		if (strlen(devs->attachuuid) == 36) {
+			cset_to_devname(&head, devs->cset, attachdev);
+		} else if (devs->version == BCACHE_SB_VERSION_CDEV
+			   || devs->version ==
+			   BCACHE_SB_VERSION_CDEV_WITH_UUID) {
+			strcpy(attachdev, BCACHE_NO_SUPPORT);
+		} else {
+			strcpy(attachdev, BCACHE_ATTACH_ALONE);
+		}
+		printf("%-16s", attachdev);
+
+		printf("%s", devs->attachuuid);
+		putchar('\n');
+	}
+	free_dev(&head);
+	return 0;
+}
+
+
+int show_bdevs()
+{
+	struct list_head head;
+	struct dev *devs;
+	INIT_LIST_HEAD(&head);
+	int ret;
+	ret = list_bdevs(&head);
+	if (ret != 0) {
+		fprintf(stderr, "Failed to list devices\n");
+		return ret;
+	}
+
+	printf("Name\t\tType\t\tState\t\tBname\t\tAttachToDev\n");
+	list_for_each_entry(devs, &head, dev_list) {
+		printf("%s\t%d", devs->name, devs->version);
+		switch (devs->version) {
+			// These are handled the same by the kernel
+		case BCACHE_SB_VERSION_CDEV:
+		case BCACHE_SB_VERSION_CDEV_WITH_UUID:
+			printf(" (cache)");
+			break;
+
+			// The second adds data offset supporet
+		case BCACHE_SB_VERSION_BDEV:
+		case BCACHE_SB_VERSION_BDEV_WITH_OFFSET:
+			printf(" (data)");
+			break;
+
+		default:
+			printf(" (unknown)");
+			break;
+		}
+
+		printf("\t%-8s", devs->state);
+		printf("\t%-16s", devs->bname);
+
+		char attachdev[30];
+		if (strlen(devs->attachuuid) == 36) {
+			cset_to_devname(&head, devs->cset, attachdev);
+		} else if (devs->version == BCACHE_SB_VERSION_CDEV
+			   || devs->version ==
+			   BCACHE_SB_VERSION_CDEV_WITH_UUID) {
+			strcpy(attachdev, BCACHE_NO_SUPPORT);
+		} else {
+			strcpy(attachdev, BCACHE_ATTACH_ALONE);
+		}
+		printf("%s", attachdev);
+		putchar('\n');
+	}
+	free_dev(&head);
+	return 0;
+}
+
+int detail(char *devname)
+{
+	struct bdev bd;
+	struct cdev cd;
+	int type = 1;
+	int ret;
+	ret = detail_dev(devname, &bd, &cd, &type);
+	if (ret != 0) {
+		fprintf(stderr, "Failed to detail device\n");
+		return ret;
+	}
+	if (type == BCACHE_SB_VERSION_BDEV) {
+		printf("sb.magic\t\t%s\n", bd.base.magic);
+		printf("sb.first_sector\t\t%" PRIu64 "\n",
+		       bd.base.first_sector);
+		printf("sb.csum\t\t\t%" PRIX64 "\n", bd.base.csum);
+		printf("sb.version\t\t%" PRIu64, bd.base.version);
+		printf(" [backing device]\n");
+		putchar('\n');
+		printf("dev.label\t\t");
+		if (*bd.base.label) {
+			print_encode(bd.base.label);
+		} else {
+			printf("(empty)");
+		}
+		putchar('\n');
+		printf("dev.uuid\t\t%s\n", bd.base.uuid);
+		printf("dev.sectors_per_block\t%u\n"
+		       "dev.sectors_per_bucket\t%u\n",
+		       bd.base.sectors_per_block,
+		       bd.base.sectors_per_bucket);
+		printf("dev.data.first_sector\t%ju\n"
+		       "dev.data.cache_mode\t%ju",
+		       bd.first_sector, bd.cache_mode);
+		switch (bd.cache_mode) {
+		case CACHE_MODE_WRITETHROUGH:
+			printf(" [writethrough]\n");
+			break;
+		case CACHE_MODE_WRITEBACK:
+			printf(" [writeback]\n");
+			break;
+		case CACHE_MODE_WRITEAROUND:
+			printf(" [writearound]\n");
+			break;
+		case CACHE_MODE_NONE:
+			printf(" [no caching]\n");
+			break;
+		default:
+			putchar('\n');
+		}
+		printf("dev.data.cache_state\t%ju", bd.cache_state);
+		switch (bd.cache_state) {
+		case BDEV_STATE_NONE:
+			printf(" [detached]\n");
+			break;
+		case BDEV_STATE_CLEAN:
+			printf(" [clean]\n");
+			break;
+		case BDEV_STATE_DIRTY:
+			printf(" [dirty]\n");
+			break;
+		case BDEV_STATE_STALE:
+			printf(" [inconsistent]\n");
+			break;
+		default:
+			putchar('\n');
+		}
+
+		putchar('\n');
+		printf("cset.uuid\t\t%s\n", bd.base.cset);
+	} else if (type == BCACHE_SB_VERSION_CDEV
+		   || type == BCACHE_SB_VERSION_CDEV_WITH_UUID) {
+		printf("sb.magic\t\t%s\n", cd.base.magic);
+		printf("sb.first_sector\t\t%" PRIu64 "\n",
+		       cd.base.first_sector);
+		printf("sb.csum\t\t\t%" PRIX64 "\n", cd.base.csum);
+		printf("sb.version\t\t%" PRIu64, cd.base.version);
+		printf(" [cache device]\n");
+		putchar('\n');
+		printf("dev.label\t\t");
+		if (*cd.base.label) {
+			print_encode(cd.base.label);
+		} else {
+			printf("(empty)");
+		}
+		putchar('\n');
+		printf("dev.uuid\t\t%s\n", cd.base.uuid);
+		printf("dev.sectors_per_block\t%u\n"
+		       "dev.sectors_per_bucket\t%u\n",
+		       cd.base.sectors_per_block,
+		       cd.base.sectors_per_bucket);
+		printf("dev.cache.first_sector\t%u\n"
+		       "dev.cache.cache_sectors\t%ju\n"
+		       "dev.cache.total_sectors\t%ju\n"
+		       "dev.cache.ordered\t%s\n"
+		       "dev.cache.discard\t%s\n"
+		       "dev.cache.pos\t\t%u\n"
+		       "dev.cache.replacement\t%d",
+		       cd.first_sector,
+		       cd.cache_sectors,
+		       cd.total_sectors,
+		       cd.ordered ? "yes" : "no",
+		       cd.discard ? "yes" : "no", cd.pos, cd.replacement);
+		switch (cd.replacement) {
+		case CACHE_REPLACEMENT_LRU:
+			printf(" [lru]\n");
+			break;
+		case CACHE_REPLACEMENT_FIFO:
+			printf(" [fifo]\n");
+			break;
+		case CACHE_REPLACEMENT_RANDOM:
+			printf(" [random]\n");
+			break;
+		default:
+			putchar('\n');
+		}
+
+		putchar('\n');
+		printf("cset.uuid\t\t%s\n", cd.base.cset);
+	} else {
+		return 1;
+	}
+	return 0;
+}
+
+int tree()
+{
+	struct list_head head;
+	struct dev *devs, *tmp;
+	INIT_LIST_HEAD(&head);
+	int ret;
+	ret = list_bdevs(&head);
+	if (ret != 0) {
+		fprintf(stderr, "Failed to list devices\n");
+		return ret;
+	}
+	struct libscols_table *tb;
+	struct libscols_line *dad, *son;
+	enum { COL_CSET, COL_BNAME };
+	setlocale(LC_ALL, "");
+	tb = scols_new_table();
+	scols_table_new_column(tb, ".", 0.1, SCOLS_FL_TREE);
+	scols_table_new_column(tb, "", 2, SCOLS_FL_TRUNC);
+	list_for_each_entry(devs, &head, dev_list) {
+		if ((devs->version == BCACHE_SB_VERSION_CDEV
+		     || devs->version == BCACHE_SB_VERSION_CDEV_WITH_UUID)
+		    && strcmp(devs->state, BCACHE_BASIC_STATE_ACTIVE) == 0) {
+			dad = scols_table_new_line(tb, NULL);
+			scols_line_set_data(dad, COL_CSET, devs->name);
+			list_for_each_entry(tmp, &head, dev_list) {
+				if (strcmp(devs->cset, tmp->attachuuid) ==
+				    0) {
+					son =
+					    scols_table_new_line(tb, dad);
+					scols_line_set_data(son, COL_CSET,
+							    tmp->name);
+					scols_line_set_data(son, COL_BNAME,
+							    tmp->bname);
+				}
+			}
+		}
+	}
+	scols_print_table(tb);
+	scols_unref_table(tb);
+	free_dev(&head);
+	return 0;
+}
+
+int attach_both(char *cdev, char *backdev)
+{
+	struct bdev bd;
+	struct cdev cd;
+	int type = 1;
+	int ret;
+	char buf[100];
+	ret = detail_dev(backdev, &bd, &cd, &type);
+	if (ret < 0) {
+		return ret;
+	}
+	if (type != BCACHE_SB_VERSION_BDEV
+	    && type != BCACHE_SB_VERSION_BDEV_WITH_OFFSET) {
+		fprintf(stderr, "%s is not an backend device\n", backdev);
+		return 1;
+	}
+	if (strcmp(bd.base.attachuuid, BCACHE_BNAME_NOT_EXIST) != 0) {
+		fprintf(stderr,
+			"This device have attached to another cset\n");
+		return 1;
+	}
+
+	if (strlen(cdev) != 36) {
+		ret = detail_dev(cdev, &bd, &cd, &type);
+		if (type != BCACHE_SB_VERSION_CDEV
+		    && type != BCACHE_SB_VERSION_CDEV_WITH_UUID) {
+			fprintf(stderr, "%s is not an cache device", cdev);
+			return 1;
+		}
+		strcpy(buf, cd.base.cset);
+	} else {
+		strcpy(buf, cdev);
+	}
+	return attach(buf, backdev);
+}
+
+int main(int argc, char **argv)
+{
+	char *subcmd;
+	if (argc < 2) {
+		ctlusage();
+		return 1;
+	} else {
+		subcmd = argv[1];
+		argc--;
+		argv += 1;
+	}
+
+	if (strcmp(subcmd, "make") == 0) {
+		return make_bcache(argc, argv);
+
+	} else if (strcmp(subcmd, "show") == 0) {
+		int o = 0;
+		char *devname;
+		int more = 0;
+		int device = 0;
+		int help = 0;
+
+		static struct option long_options[] = {
+			{"more", no_argument, 0, 'm'},
+			{"help", no_argument, 0, 'h'},
+			{"device", required_argument, 0, 'd'},
+			{0, 0, 0, 0}
+		};
+		int option_index = 0;
+		while ((o =
+			getopt_long(argc, argv, "hmd:", long_options,
+				    &option_index)) != EOF) {
+			switch (o) {
+			case 'd':
+				devname = optarg;
+				device = 1;
+				break;
+			case 'm':
+				more = 1;
+				break;
+			case 'h':
+				help = 1;
+				break;
+			case '?':
+				return 1;
+			}
+		}
+		argc -= optind;
+		if (help || argc != 0) {
+			return showusage();
+		} else if (more) {
+			return show_bdevs_detail();
+		} else if (device) {
+			if (bad_dev(devname)) {
+				fprintf(stderr,
+					"Error:Wrong device name found\n");
+				return 1;
+			}
+			return detail(devname);
+		} else {
+			return show_bdevs();
+		}
+	} else if (strcmp(subcmd, "tree") == 0) {
+		if (argc != 1) {
+			return treeusage();
+		}
+		return tree();
+	} else if (strcmp(subcmd, "regist") == 0) {
+		if (argc != 2 || strcmp(argv[1], "-h") == 0) {
+			return registusage();
+		}
+		if (bad_dev(argv[1])) {
+			fprintf(stderr, "Error:Wrong device name found\n");
+			return 1;
+		}
+		return regist(argv[1]);
+	} else if (strcmp(subcmd, "unregist") == 0) {
+		if (argc != 2 || strcmp(argv[1], "-h") == 0) {
+			return unregistusage();
+		}
+		if (bad_dev(argv[1])) {
+			fprintf(stderr, "Error:Wrong device name found\n");
+			return 1;
+		}
+		struct bdev bd;
+		struct cdev cd;
+		int type = 1;
+		int ret;
+		ret = detail_dev(argv[1], &bd, &cd, &type);
+		if (ret != 0) {
+			return ret;
+		}
+		if (type == BCACHE_SB_VERSION_BDEV) {
+			return stop_backdev(argv[1]);
+		} else if (type == BCACHE_SB_VERSION_CDEV
+			   || type == BCACHE_SB_VERSION_CDEV_WITH_UUID) {
+			return unregist_cset(cd.base.cset);
+		}
+		return 1;
+	} else if (strcmp(subcmd, "attach") == 0) {
+		if (argc != 3 || strcmp(argv[1], "-h") == 0) {
+			return attachusage();
+		}
+		if (bad_dev(argv[1]) && bad_uuid(argv[1])
+		    || bad_dev(argv[2])) {
+			fprintf(stderr,
+				"Error:Wrong device name or cache_set uuid found\n");
+			return 1;
+		}
+		return attach_both(argv[1], argv[2]);
+	} else if (strcmp(subcmd, "detach") == 0) {
+		if (argc != 2 || strcmp(argv[1], "-h") == 0) {
+			return detachusage();
+		}
+		if (bad_dev(argv[1])) {
+			fprintf(stderr, "Error:Wrong device name found\n");
+			return 1;
+		}
+		return detach(argv[1]);
+	} else if (strcmp(subcmd, "set-cachemode") == 0) {
+		if (argc != 3) {
+			return setcachemodeusage();
+		}
+		if (bad_dev(argv[1])) {
+			fprintf(stderr, "Error:Wrong device name found\n");
+			return 1;
+		}
+		return set_backdev_cachemode(argv[1], argv[2]);
+	} else {
+		ctlusage();
+	}
+	return 0;
+}
diff --git a/bcache.c b/bcache.c
index 8f37445..8b4b986 100644
--- a/bcache.c
+++ b/bcache.c
@@ -115,7 +115,7 @@ static const uint64_t crc_table[256] = {
 	0x9AFCE626CE85B507ULL
 };
 
-inline uint64_t crc64(const void *_data, size_t len)
+uint64_t crc64(const void *_data, size_t len)
 {
 	uint64_t crc = 0xFFFFFFFFFFFFFFFFULL;
 	const unsigned char *data = _data;
diff --git a/lib.c b/lib.c
new file mode 100644
index 0000000..5c7e38f
--- /dev/null
+++ b/lib.c
@@ -0,0 +1,472 @@
+#include <stdbool.h>
+#include <blkid/blkid.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include "bcache.h"
+#include "lib.h"
+#include <uuid/uuid.h>
+#include <string.h>
+#include <malloc.h>
+
+
+/*
+ * utils function
+ */
+
+static void trim_prefix(char *dest, char *src, int num)
+{
+	strcpy(dest, src + num);
+}
+
+static void get_tail(char *dest, char *src, int n)
+{
+	int num, i;
+	num = strlen(src);
+	for (i = 0; i < n; i++) {
+		dest[i] = src[num - n + i];
+	}
+	dest[i] = '\0';
+}
+
+static void trim_tail(char *src, int n)
+{
+	int num;
+	num = strlen(src);
+	src[num - n] = '\0';
+}
+
+
+int get_backdev_state(char *devname, char *state)
+{
+	FILE *fd;
+	char path[100];
+	char buf[40];
+	trim_prefix(buf, devname, DEV_PREFIX_LEN);
+	sprintf(path, "/sys/block/%s/bcache/state", buf);
+	fd = fopen(path, "r");
+	if (fd == NULL) {
+		strcpy(state, BCACHE_BASIC_STATE_INACTIVE);
+		return 0;
+	}
+	int i = 0;
+	while ((state[i] = getc(fd)) != '\n') {
+		i++;
+	}
+	state[i] = '\0';
+	fclose(fd);
+	return 0;
+}
+
+int get_cachedev_state(char *cset_id, char *state)
+{
+	DIR *dir = NULL;
+	char path[100];
+	sprintf(path, "/sys/fs/bcache/%s/", cset_id);
+	dir = opendir(path);
+	if (dir == NULL) {
+		strcpy(state, BCACHE_BASIC_STATE_INACTIVE);
+	} else {
+		strcpy(state, BCACHE_BASIC_STATE_ACTIVE);
+	}
+	closedir(dir);
+	return 0;
+}
+
+int get_state(struct dev *dev, char *state)
+{
+	if (dev->version == BCACHE_SB_VERSION_CDEV
+	    || dev->version == BCACHE_SB_VERSION_CDEV_WITH_UUID) {
+		return get_cachedev_state(dev->cset, state);
+	} else if (dev->version == BCACHE_SB_VERSION_BDEV
+		   || dev->version == BCACHE_SB_VERSION_BDEV_WITH_OFFSET) {
+		return get_backdev_state(dev->name, state);
+	} else {
+		return 1;
+	}
+}
+
+
+int get_dev_bname(char *devname, char *bname)
+{
+	int ret;
+	char path[100];
+	char buf[40];
+	char link[100];
+	trim_prefix(buf, devname, DEV_PREFIX_LEN);
+	sprintf(path, "/sys/block/%s/bcache/dev", buf);
+	ret = readlink(path, link, sizeof(link));
+	if (ret < 0) {
+		strcpy(bname, BCACHE_BNAME_NOT_EXIST);
+	} else {
+		trim_tail(link, strlen(link) - ret);
+		strcpy(bname, link + 41);
+	}
+	return 0;
+}
+
+int get_bname(struct dev *dev, char *bname)
+{
+	if (dev->version == BCACHE_SB_VERSION_CDEV
+	    || dev->version == BCACHE_SB_VERSION_CDEV_WITH_UUID) {
+		strcpy(bname, BCACHE_NO_SUPPORT);
+	} else if (dev->version == BCACHE_SB_VERSION_BDEV
+		   || dev->version == BCACHE_SB_VERSION_BDEV_WITH_OFFSET) {
+		return get_dev_bname(dev->name, bname);
+	}
+	return 0;
+}
+
+int get_backdev_attachpoint(char *devname, char *point)
+{
+	int ret;
+	char path[100];
+	char buf[20];
+	char link[100];
+	char uuid[40];
+	trim_prefix(buf, devname, DEV_PREFIX_LEN);
+	sprintf(path, "/sys/block/%s/bcache/cache", buf);
+	ret = readlink(path, link, sizeof(link));
+	if (ret < 0) {
+		strcpy(point, BCACHE_BNAME_NOT_EXIST);
+	} else {
+		trim_tail(link, strlen(link) - ret);
+		get_tail(uuid, link, 36);
+		strcpy(point, uuid);
+	}
+	return 0;
+}
+
+int get_point(struct dev *dev, char *point)
+{
+	if (dev->version == BCACHE_SB_VERSION_CDEV
+	    || dev->version == BCACHE_SB_VERSION_CDEV_WITH_UUID) {
+		strcpy(point, BCACHE_NO_SUPPORT);
+	} else if (dev->version == BCACHE_SB_VERSION_BDEV
+		   || dev->version == BCACHE_SB_VERSION_BDEV_WITH_OFFSET) {
+		return get_backdev_attachpoint(dev->name, point);
+	}
+	return 0;
+}
+
+int cset_to_devname(struct list_head *head, char *cset, char *devname)
+{
+	struct dev *dev;
+	list_for_each_entry(dev, head, dev_list) {
+		if ((dev->version == BCACHE_SB_VERSION_CDEV
+		     || dev->version == BCACHE_SB_VERSION_CDEV_WITH_UUID)
+		    && strcmp(dev->cset, cset) == 0) {
+			strcpy(devname, dev->name);
+		}
+	}
+	return 0;
+}
+
+
+int detail_base(char *devname, struct cache_sb sb, struct dev *base)
+{
+	int ret;
+	strcpy(base->name, devname);
+	base->magic = "ok";
+	base->first_sector = SB_SECTOR;
+	base->csum = sb.csum;
+	base->version = sb.version;
+
+	strncpy(base->label, (char *) sb.label, SB_LABEL_SIZE);
+	base->label[SB_LABEL_SIZE] = '\0';
+
+	uuid_unparse(sb.uuid, base->uuid);
+	uuid_unparse(sb.set_uuid, base->cset);
+	base->sectors_per_block = sb.block_size;
+	base->sectors_per_bucket = sb.bucket_size;
+	if ((ret = get_state(base, base->state)) != 0) {
+		fprintf(stderr, "Failed to get state for %s\n", devname);
+		return ret;
+	}
+	if ((ret = get_bname(base, base->bname)) != 0) {
+		fprintf(stderr, "Failed to get bname for %s\n", devname);
+		return ret;
+	}
+	if ((ret = get_point(base, base->attachuuid)) != 0) {
+		fprintf(stderr, "Failed to get attachuuid for  %s\n",
+			devname);
+		return ret;
+	}
+	return 0;
+}
+
+int list_bdevs(struct list_head *head)
+{
+	DIR *dir;
+	struct dirent *ptr;
+	struct cache_sb sb;
+	dir = opendir("/sys/block");
+	if (dir == NULL) {
+		fprintf(stderr, "Unable to open dir /sys/block\n");
+		return 1;
+	}
+	while ((ptr = readdir(dir)) != NULL) {
+		if (strcmp(ptr->d_name, ".") == 0
+		    || strcmp(ptr->d_name, "..") == 0) {
+			continue;
+		}
+		char dev[20];
+		sprintf(dev, "/dev/%s", ptr->d_name);
+		int fd = open(dev, O_RDONLY);
+		if (fd == -1) {
+			continue;
+		}
+
+		if (pread(fd, &sb, sizeof(sb), SB_START) != sizeof(sb)) {
+			close(fd);
+			continue;
+		}
+		if (memcmp(sb.magic, bcache_magic, 16)) {
+			close(fd);
+			continue;
+		}
+		struct dev *tmp;
+		int ret;
+		tmp = (struct dev *) malloc(DEVLEN);
+		ret = detail_base(dev, sb, tmp);
+		if (ret != 0) {
+			fprintf(stderr,
+				"Failed to get information for %s\n", dev);
+			return 1;
+		} else {
+			list_add_tail(&tmp->dev_list, head);
+		}
+	}
+	closedir(dir);
+	return 0;
+}
+
+int detail_dev(char *devname, struct bdev *bd, struct cdev *cd, int *type)
+{
+	struct cache_sb sb;
+	uint64_t expected_csum;
+	int fd = open(devname, O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "Error: Can't open dev  %s\n", devname);
+		return 1;
+	}
+
+	if (pread(fd, &sb, sizeof(sb), SB_START) != sizeof(sb)) {
+		fprintf(stderr, "Couldn't read\n");
+		goto Fail;
+	}
+
+	if (memcmp(sb.magic, bcache_magic, 16)) {
+		fprintf(stderr,
+			"Bad magic,make sure this is an bcache device\n");
+		goto Fail;
+	}
+
+	if (!sb.offset == SB_SECTOR) {
+		fprintf(stderr, "Invalid superblock (bad sector)\n");
+		goto Fail;
+	}
+
+	expected_csum = csum_set(&sb);
+	if (!sb.csum == expected_csum) {
+		fprintf(stderr, "Csum is not match with expected one");
+		goto Fail;
+	}
+
+	*type = sb.version;
+	if (sb.version == BCACHE_SB_VERSION_BDEV) {
+		detail_base(devname, sb, &bd->base);
+		bd->first_sector = BDEV_DATA_START_DEFAULT;
+		bd->cache_mode = BDEV_CACHE_MODE(&sb);
+		bd->cache_state = BDEV_STATE(&sb);
+	} else if (sb.version == BCACHE_SB_VERSION_CDEV
+		   || sb.version == BCACHE_SB_VERSION_CDEV_WITH_UUID) {
+		detail_base(devname, sb, &cd->base);
+		cd->first_sector = sb.bucket_size * sb.first_bucket;
+		cd->cache_sectors =
+		    sb.bucket_size * (sb.nbuckets - sb.first_bucket);
+		cd->total_sectors = sb.bucket_size * sb.nbuckets;
+		cd->ordered = CACHE_SYNC(&sb);
+		cd->discard = CACHE_DISCARD(&sb);
+		cd->pos = sb.nr_this_dev;
+		cd->replacement = CACHE_REPLACEMENT(&sb);
+	} else {
+		fprintf(stderr, "Unknown bcache device type found");
+		goto Fail;
+	}
+	return 0;
+      Fail:
+	close(fd);
+	return 1;
+}
+
+int regist(char *devname)
+{
+	int fd;
+	fd = open("/sys/fs/bcache/register", O_WRONLY);
+	if (fd < 0) {
+		perror("Error opening /sys/fs/bcache/register");
+		fprintf(stderr,
+			"The bcache kernel module must be loaded\n");
+		return 1;
+	}
+	if (dprintf(fd, "%s\n", devname) < 0) {
+		fprintf(stderr, "Error registering %s with bcache: %m\n",
+			devname);
+		close(fd);
+		return 1;
+	}
+	close(fd);
+	return 0;
+}
+
+int unregist_cset(char *cset)
+{
+	int fd;
+	char path[100];
+	sprintf(path, "/sys/fs/bcache/%s/unregister", cset);
+	fd = open(path, O_WRONLY);
+	if (fd < 0) {
+		fprintf(stderr, "Can't open %s\n", path);
+		return 1;
+	}
+	if (dprintf(fd, "%d\n", 1) < 0) {
+		fprintf(stderr, "Failed to unregist this cache device");
+		close(fd);
+		return 1;
+	}
+	close(fd);
+	return 0;
+}
+
+int stop_backdev(char *devname)
+{
+	char path[100];
+	int fd;
+	char buf[20];
+	trim_prefix(buf, devname, DEV_PREFIX_LEN);
+	sprintf(path, "/sys/block/%s/bcache/stop", buf);
+	fd = open(path, O_WRONLY);
+	if (fd < 0) {
+		fprintf(stderr, "Can't open %s\n", path);
+		return 1;
+	}
+	if (dprintf(fd, "%s\n", "1") < 0) {
+		fprintf(stderr, "Error stop back device %s\n", devname);
+		close(fd);
+		return 1;
+	}
+	close(fd);
+	return 0;
+}
+
+int unregist_both(char *cset)
+{
+	int fd;
+	char path[100];
+	sprintf(path, "/sys/fs/bcache/%s/stop", cset);
+	fd = open(path, O_WRONLY);
+	if (fd < 0) {
+		fprintf(stderr, "Can't open %s\n", path);
+		return 1;
+	}
+	if (dprintf(fd, "%d\n", 1) < 0) {
+		fprintf(stderr, "Failed to stop cset and its backends %m");
+		close(fd);
+		return 1;
+	}
+	close(fd);
+	return 0;
+}
+
+int attach(char *cset, char *devname)
+{
+	int fd;
+	char buf[20];
+	trim_prefix(buf, devname, DEV_PREFIX_LEN);
+	char path[100];
+	sprintf(path, "/sys/block/%s/bcache/attach", buf);
+	fd = open(path, O_WRONLY);
+	if (fd < 0) {
+		fprintf(stderr, "Can't open %s:%m\n", path);
+		return 1;
+	}
+	if (dprintf(fd, "%s\n", cset) < 0) {
+		fprintf(stderr, "Failed to attache to cset %s\n", cset);
+		close(fd);
+		return 1;
+	}
+	close(fd);
+	return 0;
+}
+
+int detach(char *devname)
+{
+	int fd;
+	char buf[20];
+	trim_prefix(buf, devname, DEV_PREFIX_LEN);
+	char path[100];
+	sprintf(path, "/sys/block/%s/bcache/detach", buf);
+	fd = open(path, O_WRONLY);
+	if (fd < 0) {
+		fprintf(stderr,
+			"Can't open %s,Make sure the device name is correct\n",
+			path);
+		return 1;
+	}
+	if (dprintf(fd, "%d\n", 1) < 0) {
+		close(fd);
+		fprintf(stderr, "Error detach device %s:%m", devname);
+		return 1;
+	}
+	close(fd);
+	return 0;
+}
+
+int set_backdev_cachemode(char *devname, char *cachemode)
+{
+	int fd;
+	char path[100];
+	char buf[20];
+	trim_prefix(buf, devname, DEV_PREFIX_LEN);
+	sprintf(path, "/sys/block/%s/bcache/cache_mode", buf);
+	fd = open(path, O_WRONLY);
+	if (fd < 0) {
+		fprintf(stderr,
+			"Can't open %s,Make sure the device name is correct\n",
+			path);
+		return 1;
+	}
+	if (dprintf(fd, "%s\n", cachemode) < 0) {
+		printf("Failed to set cachemode for device %s:%m\n",
+		       devname);
+		close(fd);
+		return 1;
+	}
+	close(fd);
+	return 0;
+}
+
+int get_backdev_cachemode(char *devname, char *mode)
+{
+	int fd;
+	char path[100];
+	sprintf(path, "/sys/block/%s/bcache/cache_mode", devname);
+	fd = open(path, O_RDONLY);
+	if (fd < 0) {
+		perror("Error opening /sys/fs/bcache/register");
+		fprintf(stderr,
+			"The bcache kernel module must be loaded\n");
+		return 1;
+	}
+	printf("size in func is %d", sizeof(mode));
+	if (read(fd, mode, 100) < 0) {
+		fprintf(stderr, "Failed to fetch device cache mode\n");
+		close(fd);
+		return 1;
+	}
+	close(fd);
+	return 0;
+}
diff --git a/lib.h b/lib.h
new file mode 100644
index 0000000..6911132
--- /dev/null
+++ b/lib.h
@@ -0,0 +1,60 @@
+#include "list.h"
+
+struct dev {
+	char name[40];
+	char *magic;
+	uint64_t first_sector;
+	uint64_t csum;
+	int version;
+	char label[SB_LABEL_SIZE + 1];
+	char uuid[40];
+	int sectors_per_block;
+	int sectors_per_bucket;
+	char cset[40];
+	char state[40];
+	char bname[40];
+	char attachuuid[40];
+	struct list_head dev_list;
+};
+
+struct bdev {
+	struct dev base;
+	int first_sector;
+	int cache_mode;
+	int cache_state;
+};
+
+//typedef int bool;
+struct cdev {
+	struct dev base;
+	int first_sector;
+	int cache_sectors;
+	int total_sectors;
+	bool ordered;
+	bool discard;
+	int pos;
+	unsigned int replacement;
+};
+
+
+int list_bdevs(struct list_head *head);
+int detail_dev(char *devname, struct bdev *bd, struct cdev *cd, int *type);
+int regist(char *devname);
+int stop_backdev(char *devname);
+int unregist_cset(char *cset);
+int attach(char *cset, char *devname);
+int detach(char *devname);
+int set_backdev_cachemode(char *devname, char *cachemode);
+int cset_to_devname(struct list_head *head, char *cset, char *devname);
+
+
+#define DEVLEN sizeof(struct dev)
+
+#define BCACHE_NO_SUPPORT			"N/A"
+
+#define BCACHE_BASIC_STATE_ACTIVE	"active"
+#define BCACHE_BASIC_STATE_INACTIVE	"inactive"
+
+#define BCACHE_ATTACH_ALONE		"Alone"
+#define BCACHE_BNAME_NOT_EXIST		"Non-Exist"
+#define DEV_PREFIX_LEN	5
diff --git a/list.h b/list.h
new file mode 100644
index 0000000..c43f387
--- /dev/null
+++ b/list.h
@@ -0,0 +1,749 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#include <stdio.h>
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ *  * container_of - cast a member of a structure out to the containing structure
+ *   * @ptr:	the pointer to the member.
+ *    * @type:	the type of the container struct this is embedded in.
+ *     * @member:	the name of the member within the struct.
+ *      *
+ *       */
+#define container_of(ptr, type, member) ({			\
+	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+	(type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ *  * These are non-NULL pointers that will result in page faults
+ *   * under normal circumstances, used to verify that nobody uses
+ *    * non-initialized list entries.
+ *     */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+struct hlist_head {
+	struct hlist_node *first;
+};
+
+struct hlist_node {
+	struct hlist_node *next, **pprev;
+};
+
+/*
+ *  * Simple doubly linked list implementation.
+ *   *
+ *    * Some of the internal functions ("__xxx") are useful when
+ *     * manipulating whole lists rather than single entries, as
+ *      * sometimes we already know the next/prev entries and we can
+ *       * generate better code by using them directly rather than
+ *        * using the generic single-entry routines.
+ *         */
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+/*
+ *  * Insert a new entry between two known consecutive entries.
+ *   *
+ *    * This is only for internal list manipulation where we know
+ *     * the prev/next entries already!
+ *      */
+#ifndef CONFIG_DEBUG_LIST
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+#else
+extern void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next);
+#endif
+
+/**
+ *  * list_add - add a new entry
+ *   * @new: new entry to be added
+ *    * @head: list head to add it after
+ *     *
+ *      * Insert a new entry after the specified head.
+ *       * This is good for implementing stacks.
+ *        */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+
+/**
+ *  * list_add_tail - add a new entry
+ *   * @new: new entry to be added
+ *    * @head: list head to add it before
+ *     *
+ *      * Insert a new entry before the specified head.
+ *       * This is useful for implementing queues.
+ *        */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ *  * Delete a list entry by making the prev/next entries
+ *   * point to each other.
+ *    *
+ *     * This is only for internal list manipulation where we know
+ *      * the prev/next entries already!
+ *       */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ *  * list_del - deletes entry from list.
+ *   * @entry: the element to delete from the list.
+ *    * Note: list_empty() on entry does not return true after this, the entry is
+ *     * in an undefined state.
+ *      */
+#ifndef CONFIG_DEBUG_LIST
+static inline void __list_del_entry(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+}
+
+static inline void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->next = LIST_POISON1;
+	entry->prev = LIST_POISON2;
+}
+#else
+extern void __list_del_entry(struct list_head *entry);
+extern void list_del(struct list_head *entry);
+#endif
+
+/**
+ *  * list_replace - replace old entry by new one
+ *   * @old : the element to be replaced
+ *    * @new : the new element to insert
+ *     *
+ *      * If @old was empty, it will be overwritten.
+ *       */
+static inline void list_replace(struct list_head *old,
+				struct list_head *new)
+{
+	new->next = old->next;
+	new->next->prev = new;
+	new->prev = old->prev;
+	new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+					struct list_head *new)
+{
+	list_replace(old, new);
+	INIT_LIST_HEAD(old);
+}
+
+/**
+ *  * list_del_init - deletes entry from list and reinitialize it.
+ *   * @entry: the element to delete from the list.
+ *    */
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del_entry(entry);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ *  * list_move - delete from one list and add as another's head
+ *   * @list: the entry to move
+ *    * @head: the head that will precede our entry
+ *     */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+	__list_del_entry(list);
+	list_add(list, head);
+}
+
+/**
+ *  * list_move_tail - delete from one list and add as another's tail
+ *   * @list: the entry to move
+ *    * @head: the head that will follow our entry
+ *     */
+static inline void list_move_tail(struct list_head *list,
+				  struct list_head *head)
+{
+	__list_del_entry(list);
+	list_add_tail(list, head);
+}
+
+/**
+ *  * list_is_last - tests whether @list is the last entry in list @head
+ *   * @list: the entry to test
+ *    * @head: the head of the list
+ *     */
+static inline int list_is_last(const struct list_head *list,
+				const struct list_head *head)
+{
+	return list->next == head;
+}
+
+/**
+ *  * list_empty - tests whether a list is empty
+ *   * @head: the list to test.
+ *    */
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ *  * list_empty_careful - tests whether a list is empty and not being modified
+ *   * @head: the list to test
+ *    *
+ *     * Description:
+ *      * tests whether a list is empty _and_ checks that no other CPU might be
+ *       * in the process of modifying either member (next or prev)
+ *        *
+ *         * NOTE: using list_empty_careful() without synchronization
+ *          * can only be safe if the only activity that can happen
+ *           * to the list entry is list_del_init(). Eg. it cannot be used
+ *            * if another CPU could re-list_add() it.
+ *             */
+static inline int list_empty_careful(const struct list_head *head)
+{
+	struct list_head *next = head->next;
+	return (next == head) && (next == head->prev);
+}
+
+/**
+ *  * list_rotate_left - rotate the list to the left
+ *   * @head: the head of the list
+ *    */
+static inline void list_rotate_left(struct list_head *head)
+{
+	struct list_head *first;
+
+	if (!list_empty(head)) {
+		first = head->next;
+		list_move_tail(first, head);
+	}
+}
+
+/**
+ *  * list_is_singular - tests whether a list has just one entry.
+ *   * @head: the list to test.
+ *    */
+static inline int list_is_singular(const struct list_head *head)
+{
+	return !list_empty(head) && (head->next == head->prev);
+}
+
+static inline void __list_cut_position(struct list_head *list,
+		struct list_head *head, struct list_head *entry)
+{
+	struct list_head *new_first = entry->next;
+	list->next = head->next;
+	list->next->prev = list;
+	list->prev = entry;
+	entry->next = list;
+	head->next = new_first;
+	new_first->prev = head;
+}
+
+/**
+ *  * list_cut_position - cut a list into two
+ *   * @list: a new list to add all removed entries
+ *    * @head: a list with entries
+ *     * @entry: an entry within head, could be the head itself
+ *      *	and if so we won't cut the list
+ *       *
+ *        * This helper moves the initial part of @head, up to and
+ *         * including @entry, from @head to @list. You should
+ *          * pass on @entry an element you know is on @head. @list
+ *           * should be an empty list or a list you do not care about
+ *            * losing its data.
+ *             *
+ *              */
+static inline void list_cut_position(struct list_head *list,
+		struct list_head *head, struct list_head *entry)
+{
+	if (list_empty(head))
+		return;
+	if (list_is_singular(head) &&
+		(head->next != entry && head != entry))
+		return;
+	if (entry == head)
+		INIT_LIST_HEAD(list);
+	else
+		__list_cut_position(list, head, entry);
+}
+
+static inline void __list_splice(const struct list_head *list,
+				 struct list_head *prev,
+				 struct list_head *next)
+{
+	struct list_head *first = list->next;
+	struct list_head *last = list->prev;
+
+	first->prev = prev;
+	prev->next = first;
+
+	last->next = next;
+	next->prev = last;
+}
+
+/**
+ *  * list_splice - join two lists, this is designed for stacks
+ *   * @list: the new list to add.
+ *    * @head: the place to add it in the first list.
+ *     */
+static inline void list_splice(const struct list_head *list,
+				struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head, head->next);
+}
+
+/**
+ *  * list_splice_tail - join two lists, each list being a queue
+ *   * @list: the new list to add.
+ *    * @head: the place to add it in the first list.
+ *     */
+static inline void list_splice_tail(struct list_head *list,
+				struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head->prev, head);
+}
+
+/**
+ *  * list_splice_init - join two lists and reinitialise the emptied list.
+ *   * @list: the new list to add.
+ *    * @head: the place to add it in the first list.
+ *     *
+ *      * The list at @list is reinitialised
+ *       */
+static inline void list_splice_init(struct list_head *list,
+				    struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head, head->next);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ *  * list_splice_tail_init - join two lists and reinitialise the emptied list
+ *   * @list: the new list to add.
+ *    * @head: the place to add it in the first list.
+ *     *
+ *      * Each of the lists is a queue.
+ *       * The list at @list is reinitialised
+ *        */
+static inline void list_splice_tail_init(struct list_head *list,
+					 struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head->prev, head);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ *  * list_entry - get the struct for this entry
+ *   * @ptr:	the &struct list_head pointer.
+ *    * @type:	the type of the struct this is embedded in.
+ *     * @member:	the name of the list_struct within the struct.
+ *      */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ *  * list_first_entry - get the first element from a list
+ *   * @ptr:	the list head to take the element from.
+ *    * @type:	the type of the struct this is embedded in.
+ *     * @member:	the name of the list_struct within the struct.
+ *      *
+ *       * Note, that list is expected to be not empty.
+ *        */
+#define list_first_entry(ptr, type, member) \
+	list_entry((ptr)->next, type, member)
+
+/**
+ *  * list_for_each	-	iterate over a list
+ *   * @pos:	the &struct list_head to use as a loop cursor.
+ *    * @head:	the head for your list.
+ *     */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ *  * __list_for_each	-	iterate over a list
+ *   * @pos:	the &struct list_head to use as a loop cursor.
+ *    * @head:	the head for your list.
+ *     *
+ *      * This variant doesn't differ from list_for_each() any more.
+ *       * We don't do prefetching in either case.
+ *        */
+#define __list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ *  * list_for_each_prev	-	iterate over a list backwards
+ *   * @pos:	the &struct list_head to use as a loop cursor.
+ *    * @head:	the head for your list.
+ *     */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ *  * list_for_each_safe - iterate over a list safe against removal of list entry
+ *   * @pos:	the &struct list_head to use as a loop cursor.
+ *    * @n:		another &struct list_head to use as temporary storage
+ *     * @head:	the head for your list.
+ *      */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ *  * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ *   * @pos:	the &struct list_head to use as a loop cursor.
+ *    * @n:		another &struct list_head to use as temporary storage
+ *     * @head:	the head for your list.
+ *      */
+#define list_for_each_prev_safe(pos, n, head) \
+	for (pos = (head)->prev, n = pos->prev; \
+	     pos != (head); \
+	     pos = n, n = pos->prev)
+
+/**
+ *  * list_for_each_entry	-	iterate over list of given type
+ *   * @pos:	the type * to use as a loop cursor.
+ *    * @head:	the head for your list.
+ *     * @member:	the name of the list_struct within the struct.
+ *      */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member);	\
+	     &pos->member != (head); 	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ *  * list_for_each_entry_reverse - iterate backwards over list of given type.
+ *   * @pos:	the type * to use as a loop cursor.
+ *    * @head:	the head for your list.
+ *     * @member:	the name of the list_struct within the struct.
+ *      */
+#define list_for_each_entry_reverse(pos, head, member)			\
+	for (pos = list_entry((head)->prev, typeof(*pos), member);	\
+	     &pos->member != (head); 	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ *  * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ *   * @pos:	the type * to use as a start point
+ *    * @head:	the head of the list
+ *     * @member:	the name of the list_struct within the struct.
+ *      *
+ *       * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ *        */
+#define list_prepare_entry(pos, head, member) \
+	((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ *  * list_for_each_entry_continue - continue iteration over list of given type
+ *   * @pos:	the type * to use as a loop cursor.
+ *    * @head:	the head for your list.
+ *     * @member:	the name of the list_struct within the struct.
+ *      *
+ *       * Continue to iterate over list of given type, continuing after
+ *        * the current position.
+ *         */
+#define list_for_each_entry_continue(pos, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ *  * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ *   * @pos:	the type * to use as a loop cursor.
+ *    * @head:	the head for your list.
+ *     * @member:	the name of the list_struct within the struct.
+ *      *
+ *       * Start to iterate over list of given type backwards, continuing after
+ *        * the current position.
+ *         */
+#define list_for_each_entry_continue_reverse(pos, head, member)		\
+	for (pos = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head);	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ *  * list_for_each_entry_from - iterate over list of given type from the current point
+ *   * @pos:	the type * to use as a loop cursor.
+ *    * @head:	the head for your list.
+ *     * @member:	the name of the list_struct within the struct.
+ *      *
+ *       * Iterate over list of given type, continuing from current position.
+ *        */
+#define list_for_each_entry_from(pos, head, member) 			\
+	for (; &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ *  * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ *   * @pos:	the type * to use as a loop cursor.
+ *    * @n:		another type * to use as temporary storage
+ *     * @head:	the head for your list.
+ *      * @member:	the name of the list_struct within the struct.
+ *       */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ *  * list_for_each_entry_safe_continue - continue list iteration safe against removal
+ *   * @pos:	the type * to use as a loop cursor.
+ *    * @n:		another type * to use as temporary storage
+ *     * @head:	the head for your list.
+ *      * @member:	the name of the list_struct within the struct.
+ *       *
+ *        * Iterate over list of given type, continuing after current point,
+ *         * safe against removal of list entry.
+ *          */
+#define list_for_each_entry_safe_continue(pos, n, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member), 		\
+		n = list_entry(pos->member.next, typeof(*pos), member);		\
+	     &pos->member != (head);						\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ *  * list_for_each_entry_safe_from - iterate over list from current point safe against removal
+ *   * @pos:	the type * to use as a loop cursor.
+ *    * @n:		another type * to use as temporary storage
+ *     * @head:	the head for your list.
+ *      * @member:	the name of the list_struct within the struct.
+ *       *
+ *        * Iterate over list of given type from current point, safe against
+ *         * removal of list entry.
+ *          */
+#define list_for_each_entry_safe_from(pos, n, head, member) 			\
+	for (n = list_entry(pos->member.next, typeof(*pos), member);		\
+	     &pos->member != (head);						\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ *  * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
+ *   * @pos:	the type * to use as a loop cursor.
+ *    * @n:		another type * to use as temporary storage
+ *     * @head:	the head for your list.
+ *      * @member:	the name of the list_struct within the struct.
+ *       *
+ *        * Iterate backwards over list of given type, safe against removal
+ *         * of list entry.
+ *          */
+#define list_for_each_entry_safe_reverse(pos, n, head, member)		\
+	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
+		n = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+/**
+ *  * list_safe_reset_next - reset a stale list_for_each_entry_safe loop
+ *   * @pos:	the loop cursor used in the list_for_each_entry_safe loop
+ *    * @n:		temporary storage used in list_for_each_entry_safe
+ *     * @member:	the name of the list_struct within the struct.
+ *      *
+ *       * list_safe_reset_next is not safe to use in general if the list may be
+ *        * modified concurrently (eg. the lock is dropped in the loop body). An
+ *         * exception to this is if the cursor element (pos) is pinned in the list,
+ *          * and list_safe_reset_next is called after re-taking the lock and before
+ *           * completing the current iteration of the loop body.
+ *            */
+#define list_safe_reset_next(pos, n, member)				\
+	n = list_entry(pos->member.next, typeof(*pos), member)
+
+/*
+ *  * Double linked lists with a single pointer list head.
+ *   * Mostly useful for hash tables where the two pointer list head is
+ *    * too wasteful.
+ *     * You lose the ability to access the tail in O(1).
+ *      */
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+static inline void INIT_HLIST_NODE(struct hlist_node *h)
+{
+	h->next = NULL;
+	h->pprev = NULL;
+}
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+	return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+	return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+	struct hlist_node *next = n->next;
+	struct hlist_node **pprev = n->pprev;
+	*pprev = next;
+	if (next)
+		next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+	__hlist_del(n);
+	n->next = LIST_POISON1;
+	n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+	if (!hlist_unhashed(n)) {
+		__hlist_del(n);
+		INIT_HLIST_NODE(n);
+	}
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+	struct hlist_node *first = h->first;
+	n->next = first;
+	if (first)
+		first->pprev = &n->next;
+	h->first = n;
+	n->pprev = &h->first;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	n->pprev = next->pprev;
+	n->next = next;
+	next->pprev = &n->next;
+	*(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	next->next = n->next;
+	n->next = next;
+	next->pprev = &n->next;
+
+	if(next->next)
+		next->next->pprev  = &next->next;
+}
+
+/* after that we'll appear to be on some hlist and hlist_del will work */
+static inline void hlist_add_fake(struct hlist_node *n)
+{
+	n->pprev = &n->next;
+}
+
+/*
+ *  * Move a list from one list head to another. Fixup the pprev
+ *   * reference of the first entry if it exists.
+ *    */
+static inline void hlist_move_list(struct hlist_head *old,
+				   struct hlist_head *new)
+{
+	new->first = old->first;
+	if (new->first)
+		new->first->pprev = &new->first;
+	old->first = NULL;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+	for (pos = (head)->first; pos ; pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+	     pos = n)
+
+/**
+ *  * hlist_for_each_entry	- iterate over list of given type
+ *   * @tpos:	the type * to use as a loop cursor.
+ *    * @pos:	the &struct hlist_node to use as a loop cursor.
+ *     * @head:	the head for your list.
+ *      * @member:	the name of the hlist_node within the struct.
+ *       */
+#define hlist_for_each_entry(tpos, pos, head, member)			 \
+	for (pos = (head)->first;					 \
+	     pos &&							 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ *  * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
+ *   * @tpos:	the type * to use as a loop cursor.
+ *    * @pos:	the &struct hlist_node to use as a loop cursor.
+ *     * @member:	the name of the hlist_node within the struct.
+ *      */
+#define hlist_for_each_entry_continue(tpos, pos, member)		 \
+	for (pos = (pos)->next;						 \
+	     pos &&							 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ *  * hlist_for_each_entry_from - iterate over a hlist continuing from current point
+ *   * @tpos:	the type * to use as a loop cursor.
+ *    * @pos:	the &struct hlist_node to use as a loop cursor.
+ *     * @member:	the name of the hlist_node within the struct.
+ *      */
+#define hlist_for_each_entry_from(tpos, pos, member)			 \
+	for (; pos &&							 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ *  * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ *   * @tpos:	the type * to use as a loop cursor.
+ *    * @pos:	the &struct hlist_node to use as a loop cursor.
+ *     * @n:		another &struct hlist_node to use as temporary storage
+ *      * @head:	the head for your list.
+ *       * @member:	the name of the hlist_node within the struct.
+ *        */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) 		 \
+	for (pos = (head)->first;					 \
+	     pos && ({ n = pos->next; 1; }) && 				 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = n)
+
+#endif
diff --git a/make.c b/make.c
new file mode 100644
index 0000000..29551ec
--- /dev/null
+++ b/make.c
@@ -0,0 +1,461 @@
+/*
+ * Author: Kent Overstreet <kmo@xxxxxxxxxxxxx>
+ *
+ * GPLv2
+ */
+
+#define _FILE_OFFSET_BITS	64
+#define __USE_FILE_OFFSET64
+#define _XOPEN_SOURCE 600
+
+#include <blkid/blkid.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/fs.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+
+#include "bcache.h"
+
+#define max(x, y) ({				\
+	typeof(x) _max1 = (x);			\
+	typeof(y) _max2 = (y);			\
+	(void) (&_max1 == &_max2);		\
+	_max1 > _max2 ? _max1 : _max2; })
+
+uint64_t getblocks(int fd)
+{
+	uint64_t ret;
+	struct stat statbuf;
+	if (fstat(fd, &statbuf)) {
+		perror("stat error\n");
+		exit(EXIT_FAILURE);
+	}
+	ret = statbuf.st_size / 512;
+	if (S_ISBLK(statbuf.st_mode))
+		if (ioctl(fd, BLKGETSIZE, &ret)) {
+			perror("ioctl error");
+			exit(EXIT_FAILURE);
+		}
+	return ret;
+}
+
+uint64_t hatoi(const char *s)
+{
+	char *e;
+	long long i = strtoll(s, &e, 10);
+	switch (*e) {
+		case 't':
+		case 'T':
+			i *= 1024;
+		case 'g':
+		case 'G':
+			i *= 1024;
+		case 'm':
+		case 'M':
+			i *= 1024;
+		case 'k':
+		case 'K':
+			i *= 1024;
+	}
+	return i;
+}
+
+unsigned hatoi_validate(const char *s, const char *msg)
+{
+	uint64_t v = hatoi(s);
+
+	if (v & (v - 1)) {
+		fprintf(stderr, "%s must be a power of two\n", msg);
+		exit(EXIT_FAILURE);
+	}
+
+	v /= 512;
+
+	if (v > USHRT_MAX) {
+		fprintf(stderr, "%s too large\n", msg);
+		exit(EXIT_FAILURE);
+	}
+
+	if (!v) {
+		fprintf(stderr, "%s too small\n", msg);
+		exit(EXIT_FAILURE);
+	}
+
+	return v;
+}
+
+char *skip_spaces(const char *str)
+{
+	while (isspace(*str))
+		++str;
+	return (char *)str;
+}
+
+char *strim(char *s)
+{
+	size_t size;
+	char *end;
+
+	s = skip_spaces(s);
+	size = strlen(s);
+	if (!size)
+		return s;
+
+	end = s + size - 1;
+	while (end >= s && isspace(*end))
+		end--;
+	*(end + 1) = '\0';
+
+	return s;
+}
+
+ssize_t read_string_list(const char *buf, const char * const list[])
+{
+	size_t i;
+	char *s, *d = strdup(buf);
+	if (!d)
+		return -ENOMEM;
+
+	s = strim(d);
+
+	for (i = 0; list[i]; i++)
+		if (!strcmp(list[i], s))
+			break;
+
+	free(d);
+
+	if (!list[i])
+		return -EINVAL;
+
+	return i;
+}
+
+void usage()
+{
+	fprintf(stderr,
+		   "Usage: make-bcache [options] device\n"
+	       "	-C, --cache		Format a cache device\n"
+	       "	-B, --bdev		Format a backing device\n"
+	       "	-b, --bucket		bucket size\n"
+	       "	-w, --block		block size (hard sector size of SSD, often 2k)\n"
+	       "	-o, --data-offset	data offset in sectors\n"
+	       "	    --cset-uuid		UUID for the cache set\n"
+//	       "	-U			UUID\n"
+	       "	    --writeback		enable writeback\n"
+	       "	    --discard		enable discards\n"
+	       "	    --cache_replacement_policy=(lru|fifo)\n"
+	       "	-h, --help		display this help and exit\n");
+	exit(EXIT_FAILURE);
+}
+
+const char * const cache_replacement_policies[] = {
+	"lru",
+	"fifo",
+	"random",
+	NULL
+};
+
+static void write_sb(char *dev, unsigned block_size, unsigned bucket_size,
+		     bool writeback, bool discard, bool wipe_bcache,
+		     unsigned cache_replacement_policy,
+		     uint64_t data_offset,
+		     uuid_t set_uuid, bool bdev)
+{
+	int fd;
+	char uuid_str[40], set_uuid_str[40], zeroes[SB_START] = {0};
+	struct cache_sb sb;
+	blkid_probe pr;
+
+	if ((fd = open(dev, O_RDWR|O_EXCL)) == -1) {
+		fprintf(stderr, "Can't open dev %s: %s\n", dev, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	if (pread(fd, &sb, sizeof(sb), SB_START) != sizeof(sb))
+		exit(EXIT_FAILURE);
+
+	if (!memcmp(sb.magic, bcache_magic, 16) && !wipe_bcache) {
+		fprintf(stderr, "Already a bcache device on %s, "
+			"overwrite with --wipe-bcache\n", dev);
+		exit(EXIT_FAILURE);
+	}
+
+	if (!(pr = blkid_new_probe()))
+		exit(EXIT_FAILURE);
+	if (blkid_probe_set_device(pr, fd, 0, 0))
+		exit(EXIT_FAILURE);
+	/* enable ptable probing; superblock probing is enabled by default */
+	if (blkid_probe_enable_partitions(pr, true))
+		exit(EXIT_FAILURE);
+	if (!blkid_do_probe(pr)) {
+		/* XXX wipefs doesn't know how to remove partition tables */
+		fprintf(stderr, "Device %s already has a non-bcache superblock, "
+				"remove it using wipefs and wipefs -a\n", dev);
+		exit(EXIT_FAILURE);
+	}
+
+	memset(&sb, 0, sizeof(struct cache_sb));
+
+	sb.offset	= SB_SECTOR;
+	sb.version	= bdev
+		? BCACHE_SB_VERSION_BDEV
+		: BCACHE_SB_VERSION_CDEV;
+
+	memcpy(sb.magic, bcache_magic, 16);
+	uuid_generate(sb.uuid);
+	memcpy(sb.set_uuid, set_uuid, sizeof(sb.set_uuid));
+
+	sb.bucket_size	= bucket_size;
+	sb.block_size	= block_size;
+
+	uuid_unparse(sb.uuid, uuid_str);
+	uuid_unparse(sb.set_uuid, set_uuid_str);
+
+	if (SB_IS_BDEV(&sb)) {
+		SET_BDEV_CACHE_MODE(
+			&sb, writeback ? CACHE_MODE_WRITEBACK : CACHE_MODE_WRITETHROUGH);
+
+		if (data_offset != BDEV_DATA_START_DEFAULT) {
+			sb.version = BCACHE_SB_VERSION_BDEV_WITH_OFFSET;
+			sb.data_offset = data_offset;
+		}
+
+		printf("UUID:			%s\n"
+		       "Set UUID:		%s\n"
+		       "version:		%u\n"
+		       "block_size:		%u\n"
+		       "data_offset:		%ju\n",
+		       uuid_str, set_uuid_str,
+		       (unsigned) sb.version,
+		       sb.block_size,
+		       data_offset);
+	} else {
+		sb.nbuckets		= getblocks(fd) / sb.bucket_size;
+		sb.nr_in_set		= 1;
+		sb.first_bucket		= (23 / sb.bucket_size) + 1;
+
+		if (sb.nbuckets < 1 << 7) {
+			fprintf(stderr, "Not enough buckets: %ju, need %u\n",
+			       sb.nbuckets, 1 << 7);
+			exit(EXIT_FAILURE);
+		}
+
+		SET_CACHE_DISCARD(&sb, discard);
+		SET_CACHE_REPLACEMENT(&sb, cache_replacement_policy);
+
+		printf("UUID:			%s\n"
+		       "Set UUID:		%s\n"
+		       "version:		%u\n"
+		       "nbuckets:		%ju\n"
+		       "block_size:		%u\n"
+		       "bucket_size:		%u\n"
+		       "nr_in_set:		%u\n"
+		       "nr_this_dev:		%u\n"
+		       "first_bucket:		%u\n",
+		       uuid_str, set_uuid_str,
+		       (unsigned) sb.version,
+		       sb.nbuckets,
+		       sb.block_size,
+		       sb.bucket_size,
+		       sb.nr_in_set,
+		       sb.nr_this_dev,
+		       sb.first_bucket);
+	}
+
+	sb.csum = csum_set(&sb);
+
+	/* Zero start of disk */
+	if (pwrite(fd, zeroes, SB_START, 0) != SB_START) {
+		perror("write error\n");
+		exit(EXIT_FAILURE);
+	}
+	/* Write superblock */
+	if (pwrite(fd, &sb, sizeof(sb), SB_START) != sizeof(sb)) {
+		perror("write error\n");
+		exit(EXIT_FAILURE);
+	}
+
+	fsync(fd);
+	close(fd);
+}
+
+static unsigned get_blocksize(const char *path)
+{
+	struct stat statbuf;
+
+	if (stat(path, &statbuf)) {
+		fprintf(stderr, "Error statting %s: %s\n",
+			path, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	if (S_ISBLK(statbuf.st_mode)) {
+		/* check IO limits:
+		 * BLKALIGNOFF: alignment_offset
+		 * BLKPBSZGET: physical_block_size
+		 * BLKSSZGET: logical_block_size
+		 * BLKIOMIN: minimum_io_size
+		 * BLKIOOPT: optimal_io_size
+		 *
+		 * It may be tempting to use physical_block_size,
+		 * or even minimum_io_size.
+		 * But to be as transparent as possible,
+		 * we want to use logical_block_size.
+		 */
+		unsigned int logical_block_size;
+		int fd = open(path, O_RDONLY);
+
+		if (fd < 0) {
+			fprintf(stderr, "open(%s) failed: %m\n", path);
+			exit(EXIT_FAILURE);
+		}
+		if (ioctl(fd, BLKSSZGET, &logical_block_size)) {
+			fprintf(stderr, "ioctl(%s, BLKSSZGET) failed: %m\n", path);
+			exit(EXIT_FAILURE);
+		}
+		close(fd);
+		return logical_block_size / 512;
+
+	}
+	/* else: not a block device.
+	 * Why would we even want to write a bcache super block there? */
+
+	return statbuf.st_blksize / 512;
+}
+
+int make_bcache(int argc, char **argv)
+{
+	int c, bdev = -1;
+	unsigned i, ncache_devices = 0, nbacking_devices = 0;
+	char *cache_devices[argc];
+	char *backing_devices[argc];
+
+	unsigned block_size = 0, bucket_size = 1024;
+	int writeback = 0, discard = 0, wipe_bcache = 0;
+	unsigned cache_replacement_policy = 0;
+	uint64_t data_offset = BDEV_DATA_START_DEFAULT;
+	uuid_t set_uuid;
+
+	uuid_generate(set_uuid);
+
+	struct option opts[] = {
+		{ "cache",		0, NULL,	'C' },
+		{ "bdev",		0, NULL,	'B' },
+		{ "bucket",		1, NULL,	'b' },
+		{ "block",		1, NULL,	'w' },
+		{ "writeback",		0, &writeback,	1 },
+		{ "wipe-bcache",	0, &wipe_bcache,	1 },
+		{ "discard",		0, &discard,	1 },
+		{ "cache_replacement_policy", 1, NULL, 'p' },
+		{ "cache-replacement-policy", 1, NULL, 'p' },
+		{ "data_offset",	1, NULL,	'o' },
+		{ "data-offset",	1, NULL,	'o' },
+		{ "cset-uuid",		1, NULL,	'u' },
+		{ "help",		0, NULL,	'h' },
+		{ NULL,			0, NULL,	0 },
+	};
+
+	while ((c = getopt_long(argc, argv,
+				"-hCBUo:w:b:",
+				opts, NULL)) != -1)
+		switch (c) {
+		case 'C':
+			bdev = 0;
+			break;
+		case 'B':
+			bdev = 1;
+			break;
+		case 'b':
+			bucket_size = hatoi_validate(optarg, "bucket size");
+			break;
+		case 'w':
+			block_size = hatoi_validate(optarg, "block size");
+			break;
+#if 0
+		case 'U':
+			if (uuid_parse(optarg, sb.uuid)) {
+				fprintf(stderr, "Bad uuid\n");
+				exit(EXIT_FAILURE);
+			}
+			break;
+#endif
+		case 'p':
+			cache_replacement_policy = read_string_list(optarg,
+						    cache_replacement_policies);
+			break;
+		case 'o':
+			data_offset = atoll(optarg);
+			if (data_offset < BDEV_DATA_START_DEFAULT) {
+				fprintf(stderr, "Bad data offset; minimum %d sectors\n",
+				       BDEV_DATA_START_DEFAULT);
+				exit(EXIT_FAILURE);
+			}
+			break;
+		case 'u':
+			if (uuid_parse(optarg, set_uuid)) {
+				fprintf(stderr, "Bad uuid\n");
+				exit(EXIT_FAILURE);
+			}
+			break;
+		case 'h':
+			usage();
+			break;
+		case 1:
+			if (bdev == -1) {
+				fprintf(stderr, "Please specify -C or -B\n");
+				exit(EXIT_FAILURE);
+			}
+
+			if (bdev)
+				backing_devices[nbacking_devices++] = optarg;
+			else
+				cache_devices[ncache_devices++] = optarg;
+			break;
+		}
+
+	if (!ncache_devices && !nbacking_devices) {
+		fprintf(stderr, "Please supply a device\n");
+		usage();
+	}
+
+	if (bucket_size < block_size) {
+		fprintf(stderr, "Bucket size cannot be smaller than block size\n");
+		exit(EXIT_FAILURE);
+	}
+
+	if (!block_size) {
+		for (i = 0; i < ncache_devices; i++)
+			block_size = max(block_size,
+					 get_blocksize(cache_devices[i]));
+
+		for (i = 0; i < nbacking_devices; i++)
+			block_size = max(block_size,
+					 get_blocksize(backing_devices[i]));
+	}
+
+	for (i = 0; i < ncache_devices; i++)
+		write_sb(cache_devices[i], block_size, bucket_size,
+			 writeback, discard, wipe_bcache,
+			 cache_replacement_policy,
+			 data_offset, set_uuid, false);
+
+	for (i = 0; i < nbacking_devices; i++)
+		write_sb(backing_devices[i], block_size, bucket_size,
+			 writeback, discard, wipe_bcache,
+			 cache_replacement_policy,
+			 data_offset, set_uuid, true);
+
+	return 0;
+}
-- 
1.8.3.1




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux ARM Kernel]     [Linux Filesystem Development]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux