]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - bcache.c
Merge branch 'master' of ssh://gits.daterainc.com:2984/project/2013.MAIN/bcache-tools
[bcachefs-tools-debian] / bcache.c
index 72adccad3611939916ca40726698add3f5266e17..2bc32c035a20efb55cebbd4945178d869bd0f235 100644 (file)
--- a/bcache.c
+++ b/bcache.c
@@ -503,7 +503,7 @@ int dev_open(const char *dev, bool wipe_bcache)
        struct cache_sb sb;
        blkid_probe pr;
        int fd;
-       char err[256];
+       char err[MAX_PATH];
 
        if ((fd = open(dev, O_RDWR|O_EXCL)) == -1) {
                sprintf(err, "Can't open dev %s: %s\n", dev, strerror(errno));
@@ -559,16 +559,6 @@ static unsigned min_bucket_size(int num_bucket_sizes, unsigned *bucket_sizes)
        return min;
 }
 
-static unsigned node_size(unsigned bucket_size) {
-
-       if (bucket_size <= 256)
-               return bucket_size;
-       else if (bucket_size <= 512)
-               return bucket_size / 2;
-       else
-               return bucket_size / 4;
-}
-
 void write_cache_sbs(int *fds, struct cache_sb *sb,
                            unsigned block_size, unsigned *bucket_sizes,
                                int num_bucket_sizes)
@@ -596,7 +586,8 @@ void write_cache_sbs(int *fds, struct cache_sb *sb,
                        sb->bucket_size = bucket_sizes[0];
                else
                        sb->bucket_size = bucket_sizes[i];
-               SET_CACHE_BTREE_NODE_SIZE(sb, node_size(min_size));
+               SET_CACHE_BTREE_NODE_SIZE(sb, min_size);
+
 
                sb->uuid = m->uuid;
                sb->nbuckets            = getblocks(fds[i]) / sb->bucket_size;
@@ -718,10 +709,8 @@ static void print_encode(char *in)
                        printf("%%%x", *pos);
 }
 
-static void show_uuid_only(struct cache_sb *sb) {
-       char uuid[40];
-       uuid_unparse(sb->uuid.b, uuid);
-       printf("%s\n", uuid);
+static void show_uuid_only(struct cache_sb *sb, char *dev_uuid) {
+       uuid_unparse(sb->uuid.b, dev_uuid);
 }
 
 static void show_super_common(struct cache_sb *sb, bool force_csum)
@@ -757,7 +746,7 @@ static void show_super_common(struct cache_sb *sb, bool force_csum)
                printf(" [match]\n");
        } else {
                printf(" [expected %" PRIX64 "]\n", expected_csum);
-               if (!force_csum) {
+               if (force_csum) {
                        fprintf(stderr, "Corrupt superblock (bad csum)\n");
                        exit(2);
                }
@@ -831,6 +820,7 @@ static void show_cache_member(struct cache_sb *sb, unsigned i)
        struct cache_member *m = ((struct cache_member *) sb->d) + i;
 
        printf("cache.state\t%s\n",             cache_state[CACHE_STATE(m)]);
+
        printf("cache.tier\t%llu\n",            CACHE_TIER(m));
 
        printf("cache.replication_set\t%llu\n", CACHE_REPLICATION_SET(m));
@@ -870,35 +860,78 @@ void show_super_cache(struct cache_sb *sb, bool force_csum)
        show_cache_member(sb, sb->nr_this_dev);
 }
 
+static int __sysfs_attr_type(char *attr, const char **attr_arr) {
+       int i, j;
+       for(i = 0; attr_arr[i] != NULL; i++)
+               if(!strcmp(attr, attr_arr[i]))
+                       return 1;
+       return 0;
+}
+
+enum sysfs_attr sysfs_attr_type(char *attr) {
+       int ret;
+       if(__sysfs_attr_type(attr, set_attrs))
+               return SET_ATTR;
+       if(__sysfs_attr_type(attr, cache_attrs))
+               return CACHE_ATTR;
+       if(__sysfs_attr_type(attr, internal_attrs))
+               return INTERNAL_ATTR;
+
+       printf("No attribute called %s, try --list to see options\n", attr);
+
+       return -1;
+}
+
+static void __sysfs_attr_list(const char **attr_arr) {
+       int i, j;
+       for (i = 0; attr_arr[i] != NULL; i++)
+               printf("%s\n", attr_arr[i]);
+}
+
+void sysfs_attr_list() {
+       __sysfs_attr_list(set_attrs);
+       __sysfs_attr_list(cache_attrs);
+       __sysfs_attr_list(internal_attrs);
+}
+
 struct cache_sb *query_dev(char *dev, bool force_csum,
-               bool print_sb, bool uuid_only)
+               bool print_sb, bool uuid_only, char *dev_uuid)
 {
-       struct cache_sb sb_stack, *sb = &sb_stack;
-       size_t bytes = sizeof(*sb);
+       size_t bytes = 4096;
+       struct cache_sb *sb = aligned_alloc(bytes, bytes);
 
-       int fd = open(dev, O_RDONLY);
+       int fd = open(dev, O_RDONLY|O_DIRECT);
        if (fd < 0) {
                printf("Can't open dev %s: %s\n", dev, strerror(errno));
-               exit(2);
+               return NULL;
        }
 
-       if (pread(fd, sb, bytes, SB_START) != bytes) {
-               fprintf(stderr, "Couldn't read\n");
-               exit(2);
-       }
-
-       if (sb->keys) {
-               bytes = sizeof(*sb) + sb->keys * sizeof(uint64_t);
-               sb = malloc(bytes);
-
-               if (pread(fd, sb, bytes, SB_START) != bytes) {
-                       fprintf(stderr, "Couldn't read\n");
-                       exit(2);
+       while (true) {
+               int ret = pread(fd, sb, bytes, SB_START);
+               if (ret < 0) {
+                       fprintf(stderr, "Couldn't read superblock: %s\n",
+                                       strerror(errno));
+                       close(fd);
+                       free(sb);
+                       return NULL;
+               } else if (bytes > sizeof(sb) + sb->keys * sizeof(u64)) {
+                       /* We read the whole superblock */
+                       break;
                }
+
+               /*
+                * otherwise double the size of our dest
+                * and read again
+                */
+               free(sb);
+               bytes *= 2;
+               sb = aligned_alloc(4096, bytes);
        }
 
+       close(fd);
+
        if(uuid_only) {
-               show_uuid_only(sb);
+               show_uuid_only(sb, dev_uuid);
                return sb;
        }
 
@@ -912,7 +945,7 @@ struct cache_sb *query_dev(char *dev, bool force_csum,
        return sb;
 }
 
-static void dev_name(char *ugly_path) {
+static char *dev_name(const char *ugly_path) {
        char buf[32];
        int i, end = strlen(ugly_path);
 
@@ -926,24 +959,77 @@ static void dev_name(char *ugly_path) {
 
        // Is the dev guaranteed to be in /dev?
        // This is needed for finding the superblock with a query-dev
-       printf("/dev%s\n", buf);
+       return strdup(buf);
 }
 
 static void list_cacheset_devs(char *cset_dir, char *cset_name, bool parse_dev_name) {
+       int i = 0;
+       DIR *cachedir, *dir;
+       struct stat cache_stat;
+       char entry[MAX_PATH];
+       struct dirent *ent;
+       snprintf(entry, MAX_PATH, "%s/%s", cset_dir, cset_name);
+
+       if((dir = opendir(entry)) != NULL) {
+               while((ent = readdir(dir)) != NULL) {
+                       char buf[MAX_PATH];
+                       int len;
+                       char *tmp;
+
+                       /*
+                        * We are looking for all cache# directories
+                        * do a strlen < 9 to skip over other entries
+                        * that also start with "cache"
+                        */
+                       if(strncmp(ent->d_name, "cache", 5) ||
+                                       !(strlen(ent->d_name) < 9))
+                               continue;
+
+                       snprintf(entry, MAX_PATH, "%s/%s/%s",
+                                       cset_dir,
+                                       cset_name,
+                                       ent->d_name);
+
+                       if((cachedir = opendir(entry)) == NULL)
+                               continue;
+
+                       if(stat(entry, &cache_stat))
+                               continue;
+
+                       if((len = readlink(entry, buf, sizeof(buf) - 1)) !=
+                                       -1) {
+                               buf[len] = '\0';
+                               if(parse_dev_name) {
+                                       tmp = dev_name(buf);
+                                       printf("/dev%s\n", tmp);
+                                       free(tmp);
+                               } else {
+                                       printf("\t%s\n", buf);
+                               }
+                       }
+               }
+       }
+}
+
+char *find_matching_uuid(char *stats_dir, char *subdir, const char *stats_dev_uuid) {
+       /* Do a query-dev --uuid only to get the uuid
+        * repeat on each dev until we find a matching one
+        * append that cache# to subdir and return
+        */
+
        int i = 0;
        DIR *cachedir;
        struct stat cache_stat;
        char intbuf[4];
-       char entry[256];
+       char entry[MAX_PATH];
+       char *err = NULL;
 
-       strcpy(entry, cset_dir);
-       strcat(entry, "/");
-       strcat(entry, cset_name);
-       strcat(entry, "/cache0");
+       snprintf(entry, MAX_PATH, "%s%s", stats_dir, subdir);
        snprintf(intbuf, 4, "%d", i);
+       strcat(entry, intbuf);
 
        while(true) {
-               char buf[256];
+               char buf[MAX_PATH];
                int len;
 
                if((cachedir = opendir(entry)) == NULL)
@@ -953,11 +1039,35 @@ static void list_cacheset_devs(char *cset_dir, char *cset_name, bool parse_dev_n
                        break;
 
                if((len = readlink(entry, buf, sizeof(buf) - 1)) != -1) {
+                       char dev_uuid[40];
                        buf[len] = '\0';
-                       if(parse_dev_name)
-                               dev_name(buf);
+                       int i, end = strlen(buf);
+                       char tmp[32], devname[32];
+                       struct cache_sb *sb;
+
+                       /* Chop off "/bcache", then look for the
+                        * next '/' from the end
+                        */
+                       for (i = end - 8; ; i--)
+                               if(buf[i] == '/')
+                                       break;
+
+                       strcpy(tmp, buf + i);
+                       tmp[end - i - 7] = 0;
+                       strcpy(devname, "/dev");
+                       strcat(devname, tmp);
+
+                       err = "Unable to open superblock";
+                       sb = query_dev(devname, false, false, true, dev_uuid);
+                       if(!sb)
+                               return err;
                        else
-                               printf("\t%s\n", buf);
+                               free(sb);
+
+                       if(!strcmp(stats_dev_uuid, dev_uuid)) {
+                               strcat(subdir, intbuf);
+                               return NULL;
+                       }
                }
 
                /* remove i from end and append i++ */
@@ -966,32 +1076,35 @@ static void list_cacheset_devs(char *cset_dir, char *cset_name, bool parse_dev_n
                snprintf(intbuf, 4, "%d", i);
                strcat(entry, intbuf);
        }
+
+
+       err = "dev uuid doesn't exist in cache_set";
+       return err;
 }
 
-int list_cachesets(char *cset_dir, bool list_devs)
+char *list_cachesets(char *cset_dir, bool list_devs)
 {
        struct dirent *ent;
-       DIR *dir = opendir(cset_dir);
+       DIR *dir;
+       char *err = NULL;
+
+       dir = opendir(cset_dir);
        if (!dir) {
-               fprintf(stderr, "Failed to open dir %s\n", cset_dir);
-               return 1;
+               err = "Failed to open cacheset dir";
+               goto err;
        }
 
        while ((ent = readdir(dir)) != NULL) {
                struct stat statbuf;
-               char entry[256];
-               struct dirent *cache_ent;
+               char entry[MAX_PATH];
 
                if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
                        continue;
 
-               strcpy(entry, cset_dir);
-               strcat(entry, "/");
-               strcat(entry, ent->d_name);
-
+               snprintf(entry, MAX_PATH, "%s/%s", cset_dir, ent->d_name);
                if(stat(entry, &statbuf) == -1) {
-                       fprintf(stderr, "Failed to stat %s\n", entry);
-                       return 1;
+                       err = "Failed to stat cacheset subdir";
+                       goto err;
                }
 
                if (S_ISDIR(statbuf.st_mode)) {
@@ -1003,31 +1116,119 @@ int list_cachesets(char *cset_dir, bool list_devs)
                }
        }
 
+err:
        closedir(dir);
-
-       return 0;
+       return err;
 }
 
-int register_bcache(char *devs)
+char *register_bcache(char *const *devs)
 {
        int ret, bcachefd;
+       char *err = NULL;
 
        bcachefd = open("/dev/bcache", O_RDWR);
        if (bcachefd < 0) {
-               perror("Can't open bcache device\n");
-               exit(EXIT_FAILURE);
+               err = "Can't open bcache device";
+               goto err;
        }
 
        ret = ioctl(bcachefd, BCH_IOCTL_REGISTER, devs);
        if (ret < 0) {
-               fprintf(stderr, "ioctl register error: %s\n", strerror(ret));
-               exit(EXIT_FAILURE);
+               char tmp[64];
+               snprintf(tmp, 64, "ioctl register error: %s\n",
+                               strerror(ret));
+               err = strdup(tmp);
+               goto err;
        }
-       return 0;
 
+err:
+       if (bcachefd)
+               close(bcachefd);
+       return err;
 }
 
-int probe(char *dev, int udev)
+char *unregister_bcache(char *const *devs)
+{
+       int ret, bcachefd;
+       char *err = NULL;
+
+       bcachefd = open("/dev/bcache", O_RDWR);
+       if (bcachefd < 0) {
+               err = "Can't open bcache device";
+               goto err;
+       }
+
+       ret = ioctl(bcachefd, BCH_IOCTL_UNREGISTER, devs);
+       if (ret < 0) {
+               char tmp[64];
+               snprintf(tmp, 64, "ioctl unregister error: %s\n",
+                               strerror(ret));
+               err = strdup(tmp);
+               goto err;
+       }
+
+err:
+       close(bcachefd);
+       return err;
+}
+
+char *add_devices(char *const *devs, char *uuid)
+{
+       int ret, bcachefd;
+       char *err = NULL;
+
+       bcachefd = open("/dev/bcache", O_RDWR);
+       if (bcachefd < 0) {
+               err = "Can't open bcache device";
+               goto err;
+       }
+
+       struct bch_ioctl_add_disks ia;
+       ia.devs = devs;
+       ia.uuid = uuid;
+
+       ret = ioctl(bcachefd, BCH_IOCTL_ADD_DISKS, &ia);
+       if (ret < 0) {
+               char tmp[128];
+               snprintf(tmp, 128, "ioctl add disk error: %s\n",
+                               strerror(ret));
+               err = strdup(tmp);
+       }
+
+err:
+       close(bcachefd);
+       return err;
+}
+
+char *remove_device(const char *dev, bool force)
+{
+       int ret, bcachefd;
+       char *err = NULL;
+
+       bcachefd = open("/dev/bcache", O_RDWR);
+       if (bcachefd < 0) {
+               err = "Can't open bcache device";
+               goto err;
+       }
+
+       struct bch_ioctl_rm_disk ir;
+       ir.dev = dev;
+       ir.force = force ? 1 : 0;
+
+       ret = ioctl(bcachefd, BCH_IOCTL_RM_DISK, &ir);
+       if (ret < 0) {
+               char tmp[128];
+               snprintf(tmp, 128, "ioctl add disk error: %s\n",
+                               strerror(ret));
+               err = strdup(tmp);
+       }
+
+err:
+       close(bcachefd);
+       return err;
+}
+
+char *probe(char *dev, int udev)
 {
        struct cache_sb sb;
        char uuid[40];
@@ -1085,53 +1286,143 @@ int probe(char *dev, int udev)
 
        return 0;
 
-       err:
-               fprintf(stderr, "Probe exit with error: %s", err);
-               return -1;
-}
-
-void sb_state(struct cache_sb *sb, char *dev)
-{
-       struct cache_member *m = ((struct cache_member *) sb->d) +
-               sb->nr_this_dev;
-
-       printf("device %s\n", dev);
-       printf("\tcache state\t%s\n",   cache_state[CACHE_STATE(m)]);
-       printf("\tcache_tier\t%llu\n", CACHE_TIER(m));
-       printf("\tseq#: \t%llu\n", sb->seq);
-
+err:
+       return err;
 }
 
-void read_stat_dir(DIR *dir, char *stats_dir, char *stat_name, bool print_val)
+char *read_stat_dir(DIR *dir, char *stats_dir, char *stat_name, char *ret)
 {
        struct stat statbuf;
-       char entry[150];
+       char entry[MAX_PATH];
+       char *err = NULL;
 
-       strcpy(entry, stats_dir);
-       strcat(entry, "/");
-       strcat(entry, stat_name);
+       snprintf(entry, MAX_PATH, "%s/%s", stats_dir, stat_name);
        if(stat(entry, &statbuf) == -1) {
-               fprintf(stderr, "Failed to stat %s\n", entry);
-               return;
+               char tmp[MAX_PATH];
+               snprintf(tmp, MAX_PATH, "Failed to stat %s\n", entry);
+               err = strdup(tmp);
+               goto err;
        }
 
        if (S_ISREG(statbuf.st_mode)) {
-               char buf[100];
+               char buf[MAX_PATH];
                FILE *fp = NULL;
 
                fp = fopen(entry, "r");
                if(!fp) {
                        /* If we can't open the file, this is probably because
                         * of permissions, just move to the next file */
-                       return;
+                       return NULL;
                }
 
-               while(fgets(buf, 100, fp));
+               while(fgets(ret, MAX_PATH, fp));
+               fclose(fp);
+       }
+err:
+       return err;
+}
+
+char *bcache_get_capacity(const char *cset_dir, const char *capacity_uuid,
+               bool show_devs)
+{
+       char *err = NULL;
+       char bucket_size_path[MAX_PATH];
+       char nbuckets_path[MAX_PATH];
+       char avail_buckets_path[MAX_PATH];
+       char cache_path[MAX_PATH];
+
+       double bucket_sizes[MAX_DEVS];
+       double nbuckets[MAX_DEVS];
+       double avail_buckets[MAX_DEVS];
+       char *dev_names[MAX_DEVS];
+       int dev_count = 0, i;
+       char intbuf[4];
+       double total_cap = 0, total_free = 0;
+       int precision = 2;
+
+       snprintf(intbuf, 4, "%d", i);
+       snprintf(bucket_size_path, MAX_PATH, "%s/%s/%s/%s", cset_dir,
+                       capacity_uuid, "cache0", "bucket_size_bytes");
+       snprintf(nbuckets_path, MAX_PATH, "%s/%s/%s/%s", cset_dir,
+                       capacity_uuid, "cache0", "nbuckets");
+       snprintf(avail_buckets_path, MAX_PATH, "%s/%s/%s/%s", cset_dir,
+                       capacity_uuid, "cache0", "available_buckets");
+       snprintf(cache_path, MAX_PATH, "%s/%s/%s", cset_dir, capacity_uuid,
+                       "cache0");
+
+       while(true) {
+               char buf[MAX_PATH];
+               int len;
+               DIR *cache_dir;
 
-               if(print_val)
-                       printf("%s\t%s", stat_name, buf);
+               if((cache_dir = opendir(cache_path)) == NULL)
+                       break;
+
+               err = read_stat_dir(cache_dir, cache_path,
+                               "bucket_size_bytes", buf);
+               if (err)
+                       goto err;
                else
-                       printf("%s\n", stat_name);
-               fclose(fp);
+                       bucket_sizes[dev_count] = atof(buf);
+
+               err = read_stat_dir(cache_dir, cache_path,
+                               "nbuckets", buf);
+               if (err)
+                       goto err;
+               else
+                       nbuckets[dev_count] = atof(buf);
+
+               err = read_stat_dir(cache_dir, cache_path,
+                               "available_buckets", buf);
+               if (err)
+                       goto err;
+               else
+                       avail_buckets[dev_count] = atof(buf);
+
+               if((len = readlink(cache_path, buf, sizeof(buf) - 1)) != -1) {
+                       buf[len] = '\0';
+                       dev_names[dev_count] = dev_name(buf);
+               }
+
+               /* remove i/stat and append i++/stat */
+               bucket_size_path[strlen(cache_path) - strlen(intbuf)] = 0;
+               nbuckets_path[strlen(cache_path) - strlen(intbuf)] = 0;
+               avail_buckets_path[strlen(cache_path) - strlen(intbuf)] = 0;
+               cache_path[strlen(cache_path) - strlen(intbuf)] = 0;
+
+               dev_count++;
+
+               snprintf(intbuf, 4, "%d", dev_count);
+               strcat(cache_path, intbuf);
+               strcat(bucket_size_path, intbuf);
+               strcat(nbuckets_path, intbuf);
+               strcat(avail_buckets_path, intbuf);
        }
+
+       printf("%-15s%-25s%-25s\n", "Device Name", "Capacity (512 Blocks)", "Free (512 Blocks)");
+
+       if (show_devs) {
+               for (i = 0; i < dev_count; i++) {
+                       printf("%s%-11s%-25.*f%-25.*f\n", "/dev", dev_names[i],
+                               precision,
+                               (bucket_sizes[i] * nbuckets[i]) / 512,
+                               precision,
+                               (bucket_sizes[i] * avail_buckets[i]) / 512);
+               }
+       }
+
+       for (i = 0; i < dev_count; i++) {
+               total_cap += (bucket_sizes[i] * nbuckets[i]) / 512;
+               total_free += (bucket_sizes[i] * avail_buckets[i]) / 512;
+
+       }
+
+       printf("%-15s%-25.*f%-25.*f\n", "Total", precision, total_cap,
+                       precision, total_free);
+
+err:
+       for (i = 0; i < dev_count; i++)
+               if (dev_names[i])
+                       free(dev_names[i]);
+       return err;
 }