On 2018/8/14 9:58 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). > > Signed-off-by: Shaoxiong Li <dahefanteng@xxxxxxxxx> Hi Shaoxiong, I add this patch to my bcache-tools testing tree, it can be find here, https://git.kernel.org/pub/scm/linux/kernel/git/colyli/bcache-tools.git/commit/?h=for-next It is a start to test 'bcache' program. Thanks. Coly Li > --- > Makefile | 21 +- > bcache-main.c | 608 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > bcache.c | 2 +- > lib.c | 479 +++++++++++++++++++++++++++++++++++++++++++++ > lib.h | 67 +++++++ > list.h | 519 +++++++++++++++++++++++++++++++++++++++++++++++++ > make.c | 463 ++++++++++++++++++++++++++++++++++++++++++++ > 7 files changed, 2154 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..7b0ca0a > --- /dev/null > +++ b/bcache-main.c > @@ -0,0 +1,608 @@ > +/* > + * Author: Shaoxiong Li <dahefanteng@xxxxxxxxx> > + * > + * GPLv2 > + */ > + > + > +#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(®, pattern, REG_EXTENDED) != 0) { > + fprintf(stderr, "Error happen when check uuid format:%m"); > + } > + status = regexec(®, uuid, 1, ®matche, 0); > + regfree(®); > + 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(®, pattern, REG_EXTENDED) != 0) { > + fprintf(stderr, > + "Error happen when check device name format:%m"); > + } > + status = regexec(®, devname, 1, ®matche, 0); > + regfree(®); > + 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..c4cef4d > --- /dev/null > +++ b/lib.c > @@ -0,0 +1,479 @@ > +/* > + * Author: Shaoxiong Li <dahefanteng@xxxxxxxxx> > + * > + * GPLv2 > + */ > + > + > +#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..f8fd218 > --- /dev/null > +++ b/lib.h > @@ -0,0 +1,67 @@ > +/* > + * Author: Shaoxiong Li <dahefanteng@xxxxxxxxx> > + * > + * GPLv2 > + */ > + > + > +#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..224dd03 > --- /dev/null > +++ b/list.h > @@ -0,0 +1,519 @@ > +/** > + * Copy from http://www.mcs.anl.gov/~kazutomo/list/list.h.And eventually > + * copy from kernel as the author said below: > + * > + * I grub it from linux kernel source code and fix it for user space > + * program. Of course, this is a GPL licensed header file. > + * > + * Here is a recipe to cook list.h for user space program > + * > + * 1. copy list.h from linux/include/list.h > + * 2. remove > + * - #ifdef __KERNE__ and its #endif > + * - all #include line > + * - prefetch() and rcu related functions > + * 3. add macro offsetof() and container_of > + * > + * - kazutomo@xxxxxxxxxxx > + */ > +#ifndef _LINUX_LIST_H > +#define _LINUX_LIST_H > + > +/** > + * @name from other kernel headers > + */ > +/*@{*/ > + > +/** > + * Get offset of a member > + */ > +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) > + > +/** > + * Casts a member of a structure out to the containing structure > + * @param ptr the pointer to the member. > + * @param type the type of the container struct this is embedded in. > + * @param 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) > + > +/** > + * 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. > + */ > +struct list_head { > + struct list_head *next, *prev; > +}; > + > +#define LIST_HEAD_INIT(name) { &(name), &(name) } > + > +#define LIST_HEAD(name) \ > + struct list_head name = LIST_HEAD_INIT(name) > + > +#define INIT_LIST_HEAD(ptr) do { \ > + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ > +} while (0) > + > +/* > + * Insert a new entry between two known consecutive entries. > + * > + * This is only for internal list manipulation where we know > + * the prev/next entries already! > + */ > +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; > +} > + > +/** > + * 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. > + */ > +static inline void list_del(struct list_head *entry) > +{ > + __list_del(entry->prev, entry->next); > + entry->next = LIST_POISON1; > + entry->prev = LIST_POISON2; > +} > + > + > + > +/** > + * 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->prev, entry->next); > + 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(list->prev, list->next); > + 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(list->prev, list->next); > + list_add_tail(list, 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; > +} > + > +static inline void __list_splice(struct list_head *list, > + struct list_head *head) > +{ > + struct list_head *first = list->next; > + struct list_head *last = list->prev; > + struct list_head *at = head->next; > + > + first->prev = head; > + head->next = first; > + > + last->next = at; > + at->prev = last; > +} > + > +/** > + * list_splice - join two lists > + * @list: the new list to add. > + * @head: the place to add it in the first list. > + */ > +static inline void list_splice(struct list_head *list, struct list_head *head) > +{ > + if (!list_empty(list)) > + __list_splice(list, 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); > + 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_for_each - iterate over a list > + * @pos: the &struct list_head to use as a loop counter. > + * @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 counter. > + * @head: the head for your list. > + * > + * This variant differs from list_for_each() in that it's the > + * simplest possible list iteration code, no prefetching is done. > + * Use this for code that knows the list to be very short (empty > + * or 1 entry) most of the time. > + */ > +#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 counter. > + * @head: the head for your list. > + */ > +#define list_for_each_prev(pos, head) \ > + for (pos = (head)->prev; prefetch(pos->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 counter. > + * @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_entry - iterate over list of given type > + * @pos: the type * to use as a loop counter. > + * @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 counter. > + * @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 as a start point 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. > + */ > +#define list_prepare_entry(pos, head, member) \ > + ((pos) ? : list_entry(head, typeof(*pos), member)) > + > +/** > + * list_for_each_entry_continue - iterate over list of given type > + * continuing after existing point > + * @pos: the type * to use as a loop counter. > + * @head: the head for your list. > + * @member: the name of the list_struct within the struct. > + */ > +#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_safe - iterate over list of given type safe against removal of list entry > + * @pos: the type * to use as a loop counter. > + * @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 - iterate over list of given type > + * continuing after existing point safe against removal of list entry > + * @pos: the type * to use as a loop counter. > + * @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_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_reverse - iterate backwards over list of given type safe against > + * removal of list entry > + * @pos: the type * to use as a loop counter. > + * @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_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)) > + > + > + > + > +/* > + * 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). > + */ > + > +struct hlist_head { > + struct hlist_node *first; > +}; > + > +struct hlist_node { > + struct hlist_node *next, **pprev; > +}; > + > +#define HLIST_HEAD_INIT { .first = NULL } > +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } > +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) > +#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->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 (n->pprev) { > + __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; > +} > + > + > + > +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) > + > +#define hlist_for_each(pos, head) \ > + for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \ > + 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 counter. > + * @pos: the &struct hlist_node to use as a loop counter. > + * @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 && ({ prefetch(pos->next); 1;}) && \ > + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ > + pos = pos->next) > + > +/** > + * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point > + * @tpos: the type * to use as a loop counter. > + * @pos: the &struct hlist_node to use as a loop counter. > + * @member: the name of the hlist_node within the struct. > + */ > +#define hlist_for_each_entry_continue(tpos, pos, member) \ > + for (pos = (pos)->next; \ > + pos && ({ prefetch(pos->next); 1;}) && \ > + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ > + pos = pos->next) > + > +/** > + * hlist_for_each_entry_from - iterate over a hlist continuing from existing point > + * @tpos: the type * to use as a loop counter. > + * @pos: the &struct hlist_node to use as a loop counter. > + * @member: the name of the hlist_node within the struct. > + */ > +#define hlist_for_each_entry_from(tpos, pos, member) \ > + for (; pos && ({ prefetch(pos->next); 1;}) && \ > + ({ 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 counter. > + * @pos: the &struct hlist_node to use as a loop counter. > + * @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..b0fd380 > --- /dev/null > +++ b/make.c > @@ -0,0 +1,463 @@ > +/* > + * Author: Kent Overstreet <kmo@xxxxxxxxxxxxx> > + * > + * GPLv2 > + * > + * TODO: It's OK to merge this file with 'make-bcache.c' after the 'bcache' tool has been well tested. > + */ > + > +#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; > +} >