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

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

 



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(&reg, pattern, REG_EXTENDED) != 0) {
> > +             fprintf(stderr, "Error happen when check uuid format:%m");
> > +     }
> > +     status = regexec(&reg, uuid, 1, &regmatche, 0);
> > +     regfree(&reg);
> > +     if (status == REG_NOMATCH) {
> > +             return true;
> > +     } else {
> > +             return false;
> > +     }
> > +}
> > +
> > +bool bad_dev(char *devname)
> > +{
> > +     const char *pattern = "^/dev/[a-zA-Z0-9]*$";
> > +     regex_t reg;
> > +     int status;
> > +     regmatch_t regmatche;
> > +     if (regcomp(&reg, pattern, REG_EXTENDED) != 0) {
> > +             fprintf(stderr,
> > +                     "Error happen when check device name format:%m");
> > +     }
> > +     status = regexec(&reg, devname, 1, &regmatche, 0);
> > +     regfree(&reg);
> > +     if (status == REG_NOMATCH) {
> > +             return true;
> > +     } else {
> > +             return false;
> > +     }
> > +}
> > +
> > +
> > +int ctlusage()
> > +{
> > +     fprintf(stderr,
> > +             "Usage:bcache-ctl [SUBCMD]\n"
> > +             "       show            show all bcache devices in this host\n"
> > +             "       tree            show active bcache devices in this host\n"
> > +             "       make            make regular device to bcache device\n"
> > +             "       regist          regist device to kernel\n"
> > +             "       unregist        unregist device from kernel\n"
> > +             "       attach          attach backend device(data device) to cache device\n"
> > +             "       detach          detach backend device(data device) from cache device\n"
> > +             "       set-cachemode   set cachemode for backend device\n");
> > +     return EXIT_FAILURE;
> > +}
> > +
> > +int showusage()
> > +{
> > +     fprintf(stderr,
> > +             "Usage: show [option]"
> > +             "       show overall information about all devices\n"
> > +             "       -d --device {devname}   show the detail infomation about this device\n"
> > +             "       -m --more               show overall information about all devices with detail info \n"
> > +             "       -h --help               show help information \n");
> > +     return EXIT_FAILURE;
> > +}
> > +
> > +int treeusage()
> > +{
> > +     fprintf(stderr,
> > +             "Usage: tree    show active bcache devices in this host\n");
> > +     return EXIT_FAILURE;
> > +}
> > +
> > +int registusage()
> > +{
> > +     fprintf(stderr,
> > +             "Usage:regist devicename                regist device as bcache device to kernel\n");
> > +     return EXIT_FAILURE;
> > +}
> > +
> > +int unregistusage()
> > +{
> > +     fprintf(stderr,
> > +             "Usage:unregist devicename              unregist device from kernel\n");
> > +     return EXIT_FAILURE;
> > +}
> > +
> > +int attachusage()
> > +{
> > +     fprintf(stderr, "Usage:attach cset_uuid|cachedevice datadevice\n");
> > +     return EXIT_FAILURE;
> > +}
> > +
> > +int detachusage()
> > +{
> > +     fprintf(stderr, "Usage:detach devicename\n");
> > +     return EXIT_FAILURE;
> > +}
> > +
> > +int setcachemodeusage()
> > +{
> > +     fprintf(stderr, "Usage:set-cachemode devicename modetype\n");
> > +     return EXIT_FAILURE;
> > +}
> > +
> > +
> > +void free_dev(struct list_head *head)
> > +{
> > +     struct dev *dev;
> > +     list_for_each_entry(dev, head, dev_list) {
> > +             free(dev);
> > +     }
> > +}
> > +
> > +int show_bdevs_detail()
> > +{
> > +     struct list_head head;
> > +     struct dev *devs;
> > +     INIT_LIST_HEAD(&head);
> > +     int ret;
> > +
> > +     ret = list_bdevs(&head);
> > +     if (ret != 0) {
> > +             fprintf(stderr, "Failed to list devices\n");
> > +             return ret;
> > +     }
> > +     printf
> > +         ("Name\t\tUuid\t\t\t\t\tCset_Uuid\t\t\t\tType\t\tState\t\tBname\t\tAttachToDev\tAttachToCset\n");
> > +     list_for_each_entry(devs, &head, dev_list) {
> > +             printf("%s\t%s\t%s\t%d", devs->name, devs->uuid,
> > +                    devs->cset, devs->version);
> > +             switch (devs->version) {
> > +                     // These are handled the same by the kernel
> > +             case BCACHE_SB_VERSION_CDEV:
> > +             case BCACHE_SB_VERSION_CDEV_WITH_UUID:
> > +                     printf(" (cache)");
> > +                     break;
> > +
> > +                     // The second adds data offset supporet
> > +             case BCACHE_SB_VERSION_BDEV:
> > +             case BCACHE_SB_VERSION_BDEV_WITH_OFFSET:
> > +                     printf(" (data)");
> > +                     break;
> > +
> > +             default:
> > +                     printf(" (unknown)");
> > +                     break;
> > +             }
> > +
> > +             printf("\t%-8s", devs->state);
> > +             printf("\t%-16s", devs->bname);
> > +
> > +             char attachdev[30];
> > +             if (strlen(devs->attachuuid) == 36) {
> > +                     cset_to_devname(&head, devs->cset, attachdev);
> > +             } else if (devs->version == BCACHE_SB_VERSION_CDEV
> > +                        || devs->version ==
> > +                        BCACHE_SB_VERSION_CDEV_WITH_UUID) {
> > +                     strcpy(attachdev, BCACHE_NO_SUPPORT);
> > +             } else {
> > +                     strcpy(attachdev, BCACHE_ATTACH_ALONE);
> > +             }
> > +             printf("%-16s", attachdev);
> > +
> > +             printf("%s", devs->attachuuid);
> > +             putchar('\n');
> > +     }
> > +     free_dev(&head);
> > +     return 0;
> > +}
> > +
> > +
> > +int show_bdevs()
> > +{
> > +     struct list_head head;
> > +     struct dev *devs;
> > +     INIT_LIST_HEAD(&head);
> > +     int ret;
> > +     ret = list_bdevs(&head);
> > +     if (ret != 0) {
> > +             fprintf(stderr, "Failed to list devices\n");
> > +             return ret;
> > +     }
> > +
> > +     printf("Name\t\tType\t\tState\t\tBname\t\tAttachToDev\n");
> > +     list_for_each_entry(devs, &head, dev_list) {
> > +             printf("%s\t%d", devs->name, devs->version);
> > +             switch (devs->version) {
> > +                     // These are handled the same by the kernel
> > +             case BCACHE_SB_VERSION_CDEV:
> > +             case BCACHE_SB_VERSION_CDEV_WITH_UUID:
> > +                     printf(" (cache)");
> > +                     break;
> > +
> > +                     // The second adds data offset supporet
> > +             case BCACHE_SB_VERSION_BDEV:
> > +             case BCACHE_SB_VERSION_BDEV_WITH_OFFSET:
> > +                     printf(" (data)");
> > +                     break;
> > +
> > +             default:
> > +                     printf(" (unknown)");
> > +                     break;
> > +             }
> > +
> > +             printf("\t%-8s", devs->state);
> > +             printf("\t%-16s", devs->bname);
> > +
> > +             char attachdev[30];
> > +             if (strlen(devs->attachuuid) == 36) {
> > +                     cset_to_devname(&head, devs->cset, attachdev);
> > +             } else if (devs->version == BCACHE_SB_VERSION_CDEV
> > +                        || devs->version ==
> > +                        BCACHE_SB_VERSION_CDEV_WITH_UUID) {
> > +                     strcpy(attachdev, BCACHE_NO_SUPPORT);
> > +             } else {
> > +                     strcpy(attachdev, BCACHE_ATTACH_ALONE);
> > +             }
> > +             printf("%s", attachdev);
> > +             putchar('\n');
> > +     }
> > +     free_dev(&head);
> > +     return 0;
> > +}
> > +
> > +int detail(char *devname)
> > +{
> > +     struct bdev bd;
> > +     struct cdev cd;
> > +     int type = 1;
> > +     int ret;
> > +     ret = detail_dev(devname, &bd, &cd, &type);
> > +     if (ret != 0) {
> > +             fprintf(stderr, "Failed to detail device\n");
> > +             return ret;
> > +     }
> > +     if (type == BCACHE_SB_VERSION_BDEV) {
> > +             printf("sb.magic\t\t%s\n", bd.base.magic);
> > +             printf("sb.first_sector\t\t%" PRIu64 "\n",
> > +                    bd.base.first_sector);
> > +             printf("sb.csum\t\t\t%" PRIX64 "\n", bd.base.csum);
> > +             printf("sb.version\t\t%" PRIu64, bd.base.version);
> > +             printf(" [backing device]\n");
> > +             putchar('\n');
> > +             printf("dev.label\t\t");
> > +             if (*bd.base.label) {
> > +                     print_encode(bd.base.label);
> > +             } else {
> > +                     printf("(empty)");
> > +             }
> > +             putchar('\n');
> > +             printf("dev.uuid\t\t%s\n", bd.base.uuid);
> > +             printf("dev.sectors_per_block\t%u\n"
> > +                    "dev.sectors_per_bucket\t%u\n",
> > +                    bd.base.sectors_per_block,
> > +                    bd.base.sectors_per_bucket);
> > +             printf("dev.data.first_sector\t%ju\n"
> > +                    "dev.data.cache_mode\t%ju",
> > +                    bd.first_sector, bd.cache_mode);
> > +             switch (bd.cache_mode) {
> > +             case CACHE_MODE_WRITETHROUGH:
> > +                     printf(" [writethrough]\n");
> > +                     break;
> > +             case CACHE_MODE_WRITEBACK:
> > +                     printf(" [writeback]\n");
> > +                     break;
> > +             case CACHE_MODE_WRITEAROUND:
> > +                     printf(" [writearound]\n");
> > +                     break;
> > +             case CACHE_MODE_NONE:
> > +                     printf(" [no caching]\n");
> > +                     break;
> > +             default:
> > +                     putchar('\n');
> > +             }
> > +             printf("dev.data.cache_state\t%ju", bd.cache_state);
> > +             switch (bd.cache_state) {
> > +             case BDEV_STATE_NONE:
> > +                     printf(" [detached]\n");
> > +                     break;
> > +             case BDEV_STATE_CLEAN:
> > +                     printf(" [clean]\n");
> > +                     break;
> > +             case BDEV_STATE_DIRTY:
> > +                     printf(" [dirty]\n");
> > +                     break;
> > +             case BDEV_STATE_STALE:
> > +                     printf(" [inconsistent]\n");
> > +                     break;
> > +             default:
> > +                     putchar('\n');
> > +             }
> > +
> > +             putchar('\n');
> > +             printf("cset.uuid\t\t%s\n", bd.base.cset);
> > +     } else if (type == BCACHE_SB_VERSION_CDEV
> > +                || type == BCACHE_SB_VERSION_CDEV_WITH_UUID) {
> > +             printf("sb.magic\t\t%s\n", cd.base.magic);
> > +             printf("sb.first_sector\t\t%" PRIu64 "\n",
> > +                    cd.base.first_sector);
> > +             printf("sb.csum\t\t\t%" PRIX64 "\n", cd.base.csum);
> > +             printf("sb.version\t\t%" PRIu64, cd.base.version);
> > +             printf(" [cache device]\n");
> > +             putchar('\n');
> > +             printf("dev.label\t\t");
> > +             if (*cd.base.label) {
> > +                     print_encode(cd.base.label);
> > +             } else {
> > +                     printf("(empty)");
> > +             }
> > +             putchar('\n');
> > +             printf("dev.uuid\t\t%s\n", cd.base.uuid);
> > +             printf("dev.sectors_per_block\t%u\n"
> > +                    "dev.sectors_per_bucket\t%u\n",
> > +                    cd.base.sectors_per_block,
> > +                    cd.base.sectors_per_bucket);
> > +             printf("dev.cache.first_sector\t%u\n"
> > +                    "dev.cache.cache_sectors\t%ju\n"
> > +                    "dev.cache.total_sectors\t%ju\n"
> > +                    "dev.cache.ordered\t%s\n"
> > +                    "dev.cache.discard\t%s\n"
> > +                    "dev.cache.pos\t\t%u\n"
> > +                    "dev.cache.replacement\t%d",
> > +                    cd.first_sector,
> > +                    cd.cache_sectors,
> > +                    cd.total_sectors,
> > +                    cd.ordered ? "yes" : "no",
> > +                    cd.discard ? "yes" : "no", cd.pos, cd.replacement);
> > +             switch (cd.replacement) {
> > +             case CACHE_REPLACEMENT_LRU:
> > +                     printf(" [lru]\n");
> > +                     break;
> > +             case CACHE_REPLACEMENT_FIFO:
> > +                     printf(" [fifo]\n");
> > +                     break;
> > +             case CACHE_REPLACEMENT_RANDOM:
> > +                     printf(" [random]\n");
> > +                     break;
> > +             default:
> > +                     putchar('\n');
> > +             }
> > +
> > +             putchar('\n');
> > +             printf("cset.uuid\t\t%s\n", cd.base.cset);
> > +     } else {
> > +             return 1;
> > +     }
> > +     return 0;
> > +}
> > +
> > +int tree()
> > +{
> > +     struct list_head head;
> > +     struct dev *devs, *tmp;
> > +     INIT_LIST_HEAD(&head);
> > +     int ret;
> > +     ret = list_bdevs(&head);
> > +     if (ret != 0) {
> > +             fprintf(stderr, "Failed to list devices\n");
> > +             return ret;
> > +     }
> > +     struct libscols_table *tb;
> > +     struct libscols_line *dad, *son;
> > +     enum { COL_CSET, COL_BNAME };
> > +     setlocale(LC_ALL, "");
> > +     tb = scols_new_table();
> > +     scols_table_new_column(tb, ".", 0.1, SCOLS_FL_TREE);
> > +     scols_table_new_column(tb, "", 2, SCOLS_FL_TRUNC);
> > +     list_for_each_entry(devs, &head, dev_list) {
> > +             if ((devs->version == BCACHE_SB_VERSION_CDEV
> > +                  || devs->version == BCACHE_SB_VERSION_CDEV_WITH_UUID)
> > +                 && strcmp(devs->state, BCACHE_BASIC_STATE_ACTIVE) == 0) {
> > +                     dad = scols_table_new_line(tb, NULL);
> > +                     scols_line_set_data(dad, COL_CSET, devs->name);
> > +                     list_for_each_entry(tmp, &head, dev_list) {
> > +                             if (strcmp(devs->cset, tmp->attachuuid) ==
> > +                                 0) {
> > +                                     son =
> > +                                         scols_table_new_line(tb, dad);
> > +                                     scols_line_set_data(son, COL_CSET,
> > +                                                         tmp->name);
> > +                                     scols_line_set_data(son, COL_BNAME,
> > +                                                         tmp->bname);
> > +                             }
> > +                     }
> > +             }
> > +     }
> > +     scols_print_table(tb);
> > +     scols_unref_table(tb);
> > +     free_dev(&head);
> > +     return 0;
> > +}
> > +
> > +int attach_both(char *cdev, char *backdev)
> > +{
> > +     struct bdev bd;
> > +     struct cdev cd;
> > +     int type = 1;
> > +     int ret;
> > +     char buf[100];
> > +     ret = detail_dev(backdev, &bd, &cd, &type);
> > +     if (ret < 0) {
> > +             return ret;
> > +     }
> > +     if (type != BCACHE_SB_VERSION_BDEV
> > +         && type != BCACHE_SB_VERSION_BDEV_WITH_OFFSET) {
> > +             fprintf(stderr, "%s is not an backend device\n", backdev);
> > +             return 1;
> > +     }
> > +     if (strcmp(bd.base.attachuuid, BCACHE_BNAME_NOT_EXIST) != 0) {
> > +             fprintf(stderr,
> > +                     "This device have attached to another cset\n");
> > +             return 1;
> > +     }
> > +
> > +     if (strlen(cdev) != 36) {
> > +             ret = detail_dev(cdev, &bd, &cd, &type);
> > +             if (type != BCACHE_SB_VERSION_CDEV
> > +                 && type != BCACHE_SB_VERSION_CDEV_WITH_UUID) {
> > +                     fprintf(stderr, "%s is not an cache device", cdev);
> > +                     return 1;
> > +             }
> > +             strcpy(buf, cd.base.cset);
> > +     } else {
> > +             strcpy(buf, cdev);
> > +     }
> > +     return attach(buf, backdev);
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > +     char *subcmd;
> > +     if (argc < 2) {
> > +             ctlusage();
> > +             return 1;
> > +     } else {
> > +             subcmd = argv[1];
> > +             argc--;
> > +             argv += 1;
> > +     }
> > +
> > +     if (strcmp(subcmd, "make") == 0) {
> > +             return make_bcache(argc, argv);
> > +
> > +     } else if (strcmp(subcmd, "show") == 0) {
> > +             int o = 0;
> > +             char *devname;
> > +             int more = 0;
> > +             int device = 0;
> > +             int help = 0;
> > +
> > +             static struct option long_options[] = {
> > +                     {"more", no_argument, 0, 'm'},
> > +                     {"help", no_argument, 0, 'h'},
> > +                     {"device", required_argument, 0, 'd'},
> > +                     {0, 0, 0, 0}
> > +             };
> > +             int option_index = 0;
> > +             while ((o =
> > +                     getopt_long(argc, argv, "hmd:", long_options,
> > +                                 &option_index)) != EOF) {
> > +                     switch (o) {
> > +                     case 'd':
> > +                             devname = optarg;
> > +                             device = 1;
> > +                             break;
> > +                     case 'm':
> > +                             more = 1;
> > +                             break;
> > +                     case 'h':
> > +                             help = 1;
> > +                             break;
> > +                     case '?':
> > +                             return 1;
> > +                     }
> > +             }
> > +             argc -= optind;
> > +             if (help || argc != 0) {
> > +                     return showusage();
> > +             } else if (more) {
> > +                     return show_bdevs_detail();
> > +             } else if (device) {
> > +                     if (bad_dev(devname)) {
> > +                             fprintf(stderr,
> > +                                     "Error:Wrong device name found\n");
> > +                             return 1;
> > +                     }
> > +                     return detail(devname);
> > +             } else {
> > +                     return show_bdevs();
> > +             }
> > +     } else if (strcmp(subcmd, "tree") == 0) {
> > +             if (argc != 1) {
> > +                     return treeusage();
> > +             }
> > +             return tree();
> > +     } else if (strcmp(subcmd, "regist") == 0) {
> > +             if (argc != 2 || strcmp(argv[1], "-h") == 0) {
> > +                     return registusage();
> > +             }
> > +             if (bad_dev(argv[1])) {
> > +                     fprintf(stderr, "Error:Wrong device name found\n");
> > +                     return 1;
> > +             }
> > +             return regist(argv[1]);
> > +     } else if (strcmp(subcmd, "unregist") == 0) {
> > +             if (argc != 2 || strcmp(argv[1], "-h") == 0) {
> > +                     return unregistusage();
> > +             }
> > +             if (bad_dev(argv[1])) {
> > +                     fprintf(stderr, "Error:Wrong device name found\n");
> > +                     return 1;
> > +             }
> > +             struct bdev bd;
> > +             struct cdev cd;
> > +             int type = 1;
> > +             int ret;
> > +             ret = detail_dev(argv[1], &bd, &cd, &type);
> > +             if (ret != 0) {
> > +                     return ret;
> > +             }
> > +             if (type == BCACHE_SB_VERSION_BDEV) {
> > +                     return stop_backdev(argv[1]);
> > +             } else if (type == BCACHE_SB_VERSION_CDEV
> > +                        || type == BCACHE_SB_VERSION_CDEV_WITH_UUID) {
> > +                     return unregist_cset(cd.base.cset);
> > +             }
> > +             return 1;
> > +     } else if (strcmp(subcmd, "attach") == 0) {
> > +             if (argc != 3 || strcmp(argv[1], "-h") == 0) {
> > +                     return attachusage();
> > +             }
> > +             if (bad_dev(argv[1]) && bad_uuid(argv[1])
> > +                 || bad_dev(argv[2])) {
> > +                     fprintf(stderr,
> > +                             "Error:Wrong device name or cache_set uuid found\n");
> > +                     return 1;
> > +             }
> > +             return attach_both(argv[1], argv[2]);
> > +     } else if (strcmp(subcmd, "detach") == 0) {
> > +             if (argc != 2 || strcmp(argv[1], "-h") == 0) {
> > +                     return detachusage();
> > +             }
> > +             if (bad_dev(argv[1])) {
> > +                     fprintf(stderr, "Error:Wrong device name found\n");
> > +                     return 1;
> > +             }
> > +             return detach(argv[1]);
> > +     } else if (strcmp(subcmd, "set-cachemode") == 0) {
> > +             if (argc != 3) {
> > +                     return setcachemodeusage();
> > +             }
> > +             if (bad_dev(argv[1])) {
> > +                     fprintf(stderr, "Error:Wrong device name found\n");
> > +                     return 1;
> > +             }
> > +             return set_backdev_cachemode(argv[1], argv[2]);
> > +     } else {
> > +             ctlusage();
> > +     }
> > +     return 0;
> > +}
> > diff --git a/bcache.c b/bcache.c
> > index 8f37445..8b4b986 100644
> > --- a/bcache.c
> > +++ b/bcache.c
> > @@ -115,7 +115,7 @@ static const uint64_t crc_table[256] = {
> >       0x9AFCE626CE85B507ULL
> >  };
> >
> > -inline uint64_t crc64(const void *_data, size_t len)
> > +uint64_t crc64(const void *_data, size_t len)
> >  {
> >       uint64_t crc = 0xFFFFFFFFFFFFFFFFULL;
> >       const unsigned char *data = _data;
> > diff --git a/lib.c b/lib.c
> > new file mode 100644
> > index 0000000..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





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

  Powered by Linux