Sorry, and I have added this tag in another mail. Coly Li <colyli@xxxxxxx> 于2018年8月14日周二 下午8:17写道: > > On 2018/8/14 10:44 AM, Shaoxiong Li wrote: > > show the bcache devices in both table-like format and tree-like format. > > users can use this tools to manage device(regist,unregist,attach,detach,set-cachemode). > > Hi Shaoxing, > > Generally it is OK for me to take it for testing. But before I apply > this patch, could you please add your Signed-off-by tag? I cannnot do > this for you... > > 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 l