Re: [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]

 



On 2018/8/13 9:19 PM, Shaoxiong Li wrote:
> 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).

Hi Shaoxiong,

In general I am supportive to your idea, and I'd like to help to test
your patch. Here are some simple suggestions after a quick glance on the
patch,

1, The code comment indent of functions in list.h looks strange, could
you please to check it ?
2, If a source code file is write from scratch by yourself, you may add
your copy right information.
3, If a source code file is copied from somewhere else (e.g. kernel), it
is good to specify where the code comes from and what its license is.

Thanks.

Coly Li

> ---
>  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;
> +}
> 





[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