Arrange the code for a better user experience. show the bcache devices in both table-like format and tree-like format. users can use this tools to manage device(regist,unregist,attach,detach,set-cachemode). --- Makefile | 47 +-- README | 30 +- bcache-ctl.c | 603 ++++++++++++++++++++++++++++++++++++++ bcache-super-show.8 | 11 - bcache-super-show.c | 257 ----------------- bcache.c | 2 +- lib.c | 472 ++++++++++++++++++++++++++++++ lib.h | 60 ++++ list.h | 749 ++++++++++++++++++++++++++++++++++++++++++++++++ make-bcache.c | 2 +- make-bcache.h | 1 + package/bcache-ctl.spec | 55 ++++ package/rpmbuild.sh | 29 ++ 13 files changed, 2024 insertions(+), 294 deletions(-) create mode 100644 bcache-ctl.c delete mode 100644 bcache-super-show.8 delete mode 100644 bcache-super-show.c create mode 100644 lib.c create mode 100644 lib.h create mode 100644 list.h create mode 100644 make-bcache.h create mode 100755 package/bcache-ctl.spec create mode 100755 package/rpmbuild.sh diff --git a/Makefile b/Makefile index c824ae3..9924691 100644 --- a/Makefile +++ b/Makefile @@ -1,32 +1,37 @@ - +.PHONY: clean PREFIX=/usr UDEVLIBDIR=/lib/udev DRACUTLIBDIR=/lib/dracut INSTALL=install -CFLAGS+=-O2 -Wall -g -all: make-bcache probe-bcache bcache-super-show bcache-register +CFLAGS+=`pkg-config --cflags blkid uuid smartcols` +LDFLAGS+=`pkg-config --libs blkid uuid smartcols` -install: make-bcache probe-bcache bcache-super-show - $(INSTALL) -m0755 make-bcache bcache-super-show $(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/ - $(INSTALL) -D -m0755 initramfs/hook $(DESTDIR)/usr/share/initramfs-tools/hooks/bcache - $(INSTALL) -D -m0755 initcpio/install $(DESTDIR)/usr/lib/initcpio/install/bcache - $(INSTALL) -D -m0755 dracut/module-setup.sh $(DESTDIR)$(DRACUTLIBDIR)/modules.d/90bcache/module-setup.sh -# $(INSTALL) -m0755 bcache-test $(DESTDIR)${PREFIX}/sbin/ +all:depend bcache-ctl probe-bcache bcache-register -clean: - $(RM) -f make-bcache probe-bcache bcache-super-show bcache-test -- *.o -bcache-test: LDLIBS += `pkg-config --libs openssl` -lm -make-bcache: LDLIBS += `pkg-config --libs uuid blkid` -make-bcache: CFLAGS += `pkg-config --cflags uuid blkid` -make-bcache: bcache.o +SRCS=bcache-ctl.c bcache.c lib.c make-bcache.c +depend: .depend +.depend: $(SRCS) + rm -f ./.depend + $(CC) $(CFLAGS) -MM $^ > ./.depend; + +include .depend + +bcache-ctl: bcache-ctl.o bcache.o lib.o make-bcache.o probe-bcache: LDLIBS += `pkg-config --libs uuid blkid` probe-bcache: CFLAGS += `pkg-config --cflags uuid blkid` -bcache-super-show: LDLIBS += `pkg-config --libs uuid` -bcache-super-show: CFLAGS += -std=gnu99 -bcache-super-show: bcache.o bcache-register: bcache-register.o + + +install: + $(INSTALL) -m0755 bcache-ctl $(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/ + $(INSTALL) -D -m0755 initramfs/hook $(DESTDIR)/usr/share/initramfs-tools/hooks/bcache + $(INSTALL) -D -m0755 initcpio/install $(DESTDIR)/usr/lib/initcpio/install/bcache + $(INSTALL) -D -m0755 dracut/module-setup.sh $(DESTDIR)$(DRACUTLIBDIR)/modules.d/90bcache/module-setup.sh + +clean: + rm -f *.o .depend bcache-ctl probe-bcache bcache-register diff --git a/README b/README index 0232f70..9b50ca9 100644 --- a/README +++ b/README @@ -7,7 +7,11 @@ Documentation/bcache.txt. Included: -make-bcache +bcache-ctl + +bcache-ctl has a few subcommands + +bcache-ctl make Formats a block device for use with bcache. A device can be formatted for use as a cache or as a backing device (requires yet to be implemented kernel support). The most important option is for specifying the bucket size. @@ -17,8 +21,28 @@ performance. The bucket size is intended to be equal to the size of your SSD's erase blocks, which seems to be 128k-512k for most SSDs; feel free to experiment. -bcache-super-show -Prints the bcache superblock of a cache device or a backing device. +bcache-ctl show +show overall information about all devices +-d Prints the bcache superblock of a cache device or a backing device. +-m show overall information about all devices with detail info + +bcache-ctl tree +show active bcache devices and their relations in a tree-like format + +bcache-ctl regist +regist device to the kernel + +bcache-ctl unregist +unregist device from the kernel + +bcache-ctl attach +attach backend device(data device) to cache device + +bcache-ctl detach +detach backend device(data device) from cache device + +bcache-ctl set-cachemode +set cachemode for backend device(data device) Udev rules diff --git a/bcache-ctl.c b/bcache-ctl.c new file mode 100644 index 0000000..364d093 --- /dev/null +++ b/bcache-ctl.c @@ -0,0 +1,603 @@ +#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 "make-bcache.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"); + char state[20]; + 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"); + char state[20]; + 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", devname); + 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 { + printf("type is %d", type); + } +} + +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); +} + +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 ret; + 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-super-show.8 b/bcache-super-show.8 deleted file mode 100644 index 7d15a93..0000000 --- a/bcache-super-show.8 +++ /dev/null @@ -1,11 +0,0 @@ -.TH bcache-super-show 8 -.SH NAME -bcache-super-show \- Print the bcache superblock -.SH SYNOPSIS -.B bcache-super-show -[\fB \-f] -.I device -.SH OPTIONS -.TP -.BR \-f -Keep going if the superblock crc is invalid diff --git a/bcache-super-show.c b/bcache-super-show.c deleted file mode 100644 index 26cc40e..0000000 --- a/bcache-super-show.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Author: Gabriel de Perthuis <g2p.code@xxxxxxxxx> - * - * GPLv2 - */ - - -#define _FILE_OFFSET_BITS 64 -#define __USE_FILE_OFFSET64 -#define _XOPEN_SOURCE 500 - -#include <errno.h> -#include <fcntl.h> -#include <inttypes.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" - - -static void usage() -{ - fprintf(stderr, "Usage: bcache-super-show [-f] <device>\n"); -} - - -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) -{ - for (char* pos = in; *pos; pos++) - if (accepted_char(*pos)) - putchar(*pos); - else - printf("%%%x", *pos); -} - - -int main(int argc, char **argv) -{ - bool force_csum = false; - int o; - extern char *optarg; - struct cache_sb sb; - char uuid[40]; - uint64_t expected_csum; - - while ((o = getopt(argc, argv, "f")) != EOF) - switch (o) { - case 'f': - force_csum = 1; - break; - - default: - usage(); - exit(1); - } - - argv += optind; - argc -= optind; - - if (argc != 1) { - usage(); - exit(1); - } - - int fd = open(argv[0], O_RDONLY); - if (fd < 0) { - printf("Can't open dev %s: %s\n", argv[0], strerror(errno)); - exit(2); - } - - if (pread(fd, &sb, sizeof(sb), SB_START) != sizeof(sb)) { - fprintf(stderr, "Couldn't read\n"); - exit(2); - } - - printf("sb.magic\t\t"); - if (!memcmp(sb.magic, bcache_magic, 16)) { - printf("ok\n"); - } else { - printf("bad magic\n"); - fprintf(stderr, "Invalid superblock (bad magic)\n"); - exit(2); - } - - printf("sb.first_sector\t\t%" PRIu64, sb.offset); - if (sb.offset == SB_SECTOR) { - printf(" [match]\n"); - } else { - printf(" [expected %ds]\n", SB_SECTOR); - fprintf(stderr, "Invalid superblock (bad sector)\n"); - exit(2); - } - - printf("sb.csum\t\t\t%" PRIX64, sb.csum); - expected_csum = csum_set(&sb); - if (sb.csum == expected_csum) { - printf(" [match]\n"); - } else { - printf(" [expected %" PRIX64 "]\n", expected_csum); - if (!force_csum) { - fprintf(stderr, "Corrupt superblock (bad csum)\n"); - exit(2); - } - } - - printf("sb.version\t\t%" PRIu64, sb.version); - switch (sb.version) { - // These are handled the same by the kernel - case BCACHE_SB_VERSION_CDEV: - case BCACHE_SB_VERSION_CDEV_WITH_UUID: - printf(" [cache device]\n"); - break; - - // The second adds data offset support - case BCACHE_SB_VERSION_BDEV: - case BCACHE_SB_VERSION_BDEV_WITH_OFFSET: - printf(" [backing device]\n"); - break; - - default: - printf(" [unknown]\n"); - // exit code? - return 0; - } - - putchar('\n'); - - char label[SB_LABEL_SIZE + 1]; - strncpy(label, (char*)sb.label, SB_LABEL_SIZE); - label[SB_LABEL_SIZE] = '\0'; - printf("dev.label\t\t"); - if (*label) - print_encode(label); - else - printf("(empty)"); - putchar('\n'); - - uuid_unparse(sb.uuid, uuid); - printf("dev.uuid\t\t%s\n", uuid); - - printf("dev.sectors_per_block\t%u\n" - "dev.sectors_per_bucket\t%u\n", - sb.block_size, - sb.bucket_size); - - if (!SB_IS_BDEV(&sb)) { - // total_sectors includes the superblock; - 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%ju", - sb.bucket_size * sb.first_bucket, - sb.bucket_size * (sb.nbuckets - sb.first_bucket), - sb.bucket_size * sb.nbuckets, - CACHE_SYNC(&sb) ? "yes" : "no", - CACHE_DISCARD(&sb) ? "yes" : "no", - sb.nr_this_dev, - CACHE_REPLACEMENT(&sb)); - switch (CACHE_REPLACEMENT(&sb)) { - 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'); - } - - } else { - uint64_t first_sector; - if (sb.version == BCACHE_SB_VERSION_BDEV) { - first_sector = BDEV_DATA_START_DEFAULT; - } else { - if (sb.keys == 1 || sb.d[0]) { - fprintf(stderr, - "Possible experimental format detected, bailing\n"); - exit(3); - } - first_sector = sb.data_offset; - } - - printf("dev.data.first_sector\t%ju\n" - "dev.data.cache_mode\t%ju", - first_sector, - BDEV_CACHE_MODE(&sb)); - switch (BDEV_CACHE_MODE(&sb)) { - 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", - BDEV_STATE(&sb)); - switch (BDEV_STATE(&sb)) { - 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'); - - uuid_unparse(sb.set_uuid, uuid); - printf("cset.uuid\t\t%s\n", uuid); - - 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..0a27d3a --- /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); + } +} + + +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; + uint64_t expected_csum; + 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; + blkid_probe pr; + 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, *current; + 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-bcache.c b/make-bcache.c index c626eae..3140a3a 100644 --- a/make-bcache.c +++ b/make-bcache.c @@ -335,7 +335,7 @@ static unsigned get_blocksize(const char *path) return statbuf.st_blksize / 512; } -int main(int argc, char **argv) +int make_bcache(int argc, char **argv) { int c, bdev = -1; unsigned i, ncache_devices = 0, nbacking_devices = 0; diff --git a/make-bcache.h b/make-bcache.h new file mode 100644 index 0000000..3681737 --- /dev/null +++ b/make-bcache.h @@ -0,0 +1 @@ +extern int make_bcache(int argc, char **argv); diff --git a/package/bcache-ctl.spec b/package/bcache-ctl.spec new file mode 100755 index 0000000..ae58b7b --- /dev/null +++ b/package/bcache-ctl.spec @@ -0,0 +1,55 @@ +%global debug_package %{nil} +%global __strip /bin/true + +Name: bcache-ctl +Version: %{ver} +Release: %{rel}%{?dist} + +Summary: This is an command line tool for bcache,which used to manage bcache devices. + +Group: Storage +License: GPL +URL: https://github.com/dahefanteng/bcache-ctl +Source0: %{name}-%{version}-%{rel}.tar.gz +BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) + +%description + + +%prep +%setup -q -n %{name}-%{version}-%{rel} + + +%build +make + +%install +rm -rf %{buildroot} +install -D -m 755 %{_builddir}/bcache-ctl-%{version}-%{rel}/bcache-ctl %{buildroot}%{_bindir}/bcache-ctl +install -D -m0755 %{_builddir}/bcache-ctl-%{version}-%{rel}/probe-bcache %{buildroot}/lib/udev/probe-bcache +install -D -m0755 %{_builddir}/bcache-ctl-%{version}-%{rel}/bcache-register %{buildroot}/lib/udev/bcache-register +install -D -m0644 %{_builddir}/bcache-ctl-%{version}-%{rel}/69-bcache.rules %{buildroot}/lib/udev/rules.d/69-bcache.rules +install -D -m0644 %{_builddir}/bcache-ctl-%{version}-%{rel}/probe-bcache.8 %{buildroot}/usr/share/man/man8/probe-bcache.8 +install -D -m0755 %{_builddir}/bcache-ctl-%{version}-%{rel}/initramfs/hook %{buildroot}/usr/share/initramfs-tools/hooks/bcache +install -D -m0755 %{_builddir}/bcache-ctl-%{version}-%{rel}/initcpio/install %{buildroot}/usr/lib/initcpio/install/bcache +#conflict with dracut? +#install -D -m0755 %{_builddir}/bcache-ctl-%{version}-%{rel}/dracut/module-setup.sh %{buildroot}/lib/dracut/modules.d/90bcache/module-setup.sh + +%post + +%preun + +%clean +rm -rf %{buildroot} + +%files +%defattr(-,root,root,-) +/usr/bin/bcache-ctl +/lib/udev/probe-bcache +/lib/udev/bcache-register +/lib/udev/rules.d/69-bcache.rules +/usr/share/man/man8/probe-bcache.8.gz +/usr/share/initramfs-tools/hooks/bcache +/usr/lib/initcpio/install/bcache +#/lib/dracut/modules.d/90bcache/module-setup.sh +%changelog diff --git a/package/rpmbuild.sh b/package/rpmbuild.sh new file mode 100755 index 0000000..6eb8612 --- /dev/null +++ b/package/rpmbuild.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +PACKAGENAME=bcache-ctl +echo Building RPMs.. +GITROOT=`git rev-parse --show-toplevel` +cd $GITROOT +VER=1.0 +REL=`git rev-parse --short HEAD`git +REL=`git log --oneline|wc -l`.$REL +RPMTOPDIR=$GITROOT/rpm-build +echo "Ver: $VER, Release: $REL" + + +rm -rf $RPMTOPDIR +# Create tarball +mkdir -p $RPMTOPDIR/{SOURCES,SPECS} +git archive --format=tar --prefix=${PACKAGENAME}-${VER}-${REL}/ HEAD | gzip -c > $RPMTOPDIR/SOURCES/${PACKAGENAME}-${VER}-${REL}.tar.gz + +# Convert git log to RPM's ChangeLog format (shown with rpm -qp --changelog <rpm file>) +sed -e "s/%{ver}/$VER/" -e "s/%{rel}/$REL/" $GITROOT/package/${PACKAGENAME}.spec > $RPMTOPDIR/SPECS/${PACKAGENAME}.spec +git log -n 10 --format="* %cd %aN%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $RPMTOPDIR/SPECS/${PACKAGENAME}.spec +# Build SRC and binary RPMs +rpmbuild \ +--define "_topdir $RPMTOPDIR" \ +--define "_rpmdir $PWD" \ +--define "_srcrpmdir $PWD" \ +--define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \ +-ba $RPMTOPDIR/SPECS/${PACKAGENAME}.spec + -- 1.8.3.1