X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=bcache.c;h=a69a46fb0ad6dacd37fd141d6568b311aaf0d2c5;hb=ff6acc10b405cb06878d15b584cfa82a78ff95f0;hp=5d1162f12d3a5957dbc53d40b6c847ed939198f4;hpb=7d34cf342e5d11c068f51162bfe50dcdaa0aac59;p=bcachefs-tools-debian diff --git a/bcache.c b/bcache.c index 5d1162f..a69a46f 100644 --- 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)); @@ -718,6 +718,10 @@ static void print_encode(char *in) printf("%%%x", *pos); } +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) { char uuid[40]; @@ -751,7 +755,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); } @@ -825,6 +829,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)); @@ -864,96 +869,375 @@ void show_super_cache(struct cache_sb *sb, bool force_csum) show_cache_member(sb, sb->nr_this_dev); } -struct cache_sb *query_dev(char *dev, bool force_csum) +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, 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); + 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); } - if (sb->keys) { - bytes = sizeof(*sb) + sb->keys * sizeof(uint64_t); - sb = malloc(bytes); + close(fd); - if (pread(fd, sb, bytes, SB_START) != bytes) { - fprintf(stderr, "Couldn't read\n"); - exit(2); - } + if(uuid_only) { + show_uuid_only(sb, dev_uuid); + return sb; + } + + if(print_sb) { + if (!SB_IS_BDEV(sb)) + show_super_cache(sb, force_csum); + else + show_super_backingdev(sb, force_csum); } return sb; } -void print_dev_info(struct cache_sb *sb, bool force_csum) -{ - if (!SB_IS_BDEV(sb)) - show_super_cache(sb, force_csum); - else - show_super_backingdev(sb, force_csum); +static char *dev_name(const char *ugly_path) { + char buf[32]; + int i, end = strlen(ugly_path); + + //Chop off "/bcache", then look for the next '/' from the end + for (i = end - 8; ; i--) + if(ugly_path[i] == '/') + break; + + strcpy(buf, ugly_path + i); + buf[end - i - 7] = 0; + + // Is the dev guaranteed to be in /dev? + // This is needed for finding the superblock with a query-dev + return strdup(buf); } -int list_cachesets(char *cset_dir) +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[MAX_PATH]; + char *err = NULL; + + snprintf(entry, MAX_PATH, "%s%s", stats_dir, subdir); + snprintf(intbuf, 4, "%d", i); + strcat(entry, intbuf); + + while(true) { + char buf[MAX_PATH]; + int len; + + if((cachedir = opendir(entry)) == NULL) + break; + + if(stat(entry, &cache_stat)) + break; + + if((len = readlink(entry, buf, sizeof(buf) - 1)) != -1) { + char dev_uuid[40]; + buf[len] = '\0'; + 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 + free(sb); + + if(!strcmp(stats_dev_uuid, dev_uuid)) { + strcat(subdir, intbuf); + return NULL; + } + } + + /* remove i from end and append i++ */ + entry[strlen(entry)-strlen(intbuf)] = 0; + i++; + snprintf(intbuf, 4, "%d", i); + strcat(entry, intbuf); + } + + + err = "dev uuid doesn't exist in cache_set"; + return err; +} + +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[100]; + 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)) { printf("%s\n", ent->d_name); + + if(list_devs) { + list_cacheset_devs(cset_dir, ent->d_name, true); + } } } +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]; @@ -1011,53 +1295,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; +} - if(print_val) - printf("%s\t%s", stat_name, buf); +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((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; }