diff --git a/bcache.c b/bcache.c index 52745519..f6fcb24c 100644 --- a/bcache.c +++ b/bcache.c @@ -829,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)); @@ -944,7 +945,7 @@ struct cache_sb *query_dev(char *dev, bool force_csum, return sb; } -static void dev_name(const char *ugly_path) { +static char *dev_name(const char *ugly_path) { char buf[32]; int i, end = strlen(ugly_path); @@ -958,7 +959,7 @@ static void dev_name(const 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) { @@ -974,6 +975,7 @@ static void list_cacheset_devs(char *cset_dir, char *cset_name, bool parse_dev_n while(true) { char buf[MAX_PATH]; int len; + char *tmp; if((cachedir = opendir(entry)) == NULL) break; @@ -983,10 +985,13 @@ static void list_cacheset_devs(char *cset_dir, char *cset_name, bool parse_dev_n if((len = readlink(entry, buf, sizeof(buf) - 1)) != -1) { buf[len] = '\0'; - if(parse_dev_name) - dev_name(buf); - else + if(parse_dev_name) { + tmp = dev_name(buf); + printf("/dev%s\n", tmp); + free(tmp); + } else { printf("\t%s\n", buf); + } } /* remove i from end and append i++ */ @@ -1274,7 +1279,7 @@ err: return err; } -char *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[MAX_PATH]; @@ -1299,14 +1304,114 @@ char *read_stat_dir(DIR *dir, char *stats_dir, char *stat_name, bool print_val) return NULL; } - while(fgets(buf, MAX_PATH, fp)); - - if(print_val) - printf("%s\n", buf); - else - printf("%s\n", stat_name); + 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((cache_dir = opendir(cache_path)) == NULL) + break; + + err = read_stat_dir(cache_dir, cache_path, + "bucket_size_bytes", buf); + if (err) + goto err; + else + 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; +} diff --git a/bcache.h b/bcache.h index dc79d066..4cfac71f 100644 --- a/bcache.h +++ b/bcache.h @@ -22,7 +22,7 @@ typedef __s64 s64; #define SB_START (SB_SECTOR * 512) #define MAX_PATH 256 - +#define MAX_DEVS MAX_CACHES_PER_SET #define max(x, y) ({ \ typeof(x) _max1 = (x); \ @@ -104,10 +104,11 @@ char *parse_array_to_list(char *const *); char *register_bcache(char *const *); char *unregister_bcache(char *const *); char *probe(char *, int); -char *read_stat_dir(DIR *, char *, char *, bool); +char *read_stat_dir(DIR *, char *, char *, char *); char *find_matching_uuid(char *, char *, const char*); char *add_devices(char *const *, char *); char *remove_device(const char *, bool); +char *bcache_get_capacity(const char *, const char *, bool); #define csum_set(i, type) \ ({ \ diff --git a/bcacheadm.c b/bcacheadm.c index b321c980..623ea830 100644 --- a/bcacheadm.c +++ b/bcacheadm.c @@ -34,7 +34,6 @@ #define PACKAGE_VERSION "1.0" #define PACKAGE_BUGREPORT "bugreport" -#define MAX_DEVS MAX_CACHES_PER_SET /* make-bcache globals */ @@ -85,6 +84,10 @@ bool list_devs = false; /* status globals */ bool status_all = false; +/* capacity globals */ +static const char *capacity_uuid = NULL; +bool capacity_devs = false; + /* stats globals */ bool stats_all = false; bool stats_list = false; @@ -232,6 +235,12 @@ static NihOption status_options[] = { NIH_OPTION_LAST }; +static NihOption capacity_options[] = { + {'u', "set", N_("cache_set UUID"), NULL, "UUID", &capacity_uuid, NULL}, + {'d', "devs", N_("dev UUID"), NULL, NULL, &capacity_devs, NULL}, + NIH_OPTION_LAST +}; + static NihOption stats_options[] = { {'a', "all", N_("all"), NULL, NULL, &stats_all, NULL}, {'l', "list", N_("list"), NULL, NULL, &stats_list, NULL}, @@ -595,6 +604,24 @@ int bcache_status(NihCommand *command, char *const *args) return 0; } +int bcache_capacity(NihCommand *command, char *const *args) +{ + char *err = "Must provide a cacheset uuid"; + if(!capacity_uuid) + goto err; + + err = bcache_get_capacity(cset_dir, capacity_uuid, capacity_devs); + if (err) + goto err; + + return 0; + +err: + printf("bcache_capacity failed with error: %s\n", err); + return -1; + +} + static char *stats_subdir(char* stats_dir) { char tmp[50] = "/"; @@ -631,6 +658,7 @@ int bcache_stats(NihCommand *command, char *const *args) DIR *dir = NULL; struct dirent *ent = NULL; char *err = NULL; + char buf[MAX_PATH]; if (stats_uuid) { snprintf(stats_dir, MAX_PATH, "%s/%s", cset_dir, stats_uuid); @@ -650,17 +678,21 @@ int bcache_stats(NihCommand *command, char *const *args) if(stats_list || stats_all) { while ((ent = readdir(dir)) != NULL) { - err = read_stat_dir(dir, stats_dir, ent->d_name, stats_all); + err = read_stat_dir(dir, stats_dir, ent->d_name, buf); if (err) goto err; + if(stats_list) + printf("%s\n", ent->d_name); + if(stats_all) + printf("\t%s\n", buf); } } - for (i = 0; args[i] != NULL; i++) { - err = read_stat_dir(dir, stats_dir, args[i], true); + err = read_stat_dir(dir, stats_dir, args[i], buf); if (err) goto err; + printf("%s\n", buf); } closedir(dir); @@ -714,6 +746,10 @@ static NihCommand commands[] = { "Finds the status of the most up to date superblock", N_("Finds the status of the most up to date superblock"), NULL, status_options, bcache_status}, + {"capacity", N_("capacity --set=UUID"), + "Shows the capacity of the cacheset", + N_("Shows the capacity of the cacheset"), + NULL, capacity_options, bcache_capacity}, {"stats", N_("stats "), "List various bcache statistics", N_("List various bcache statistics"),