Goffredo Baroncelli
2013-Feb-18 21:04 UTC
[PATCH][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
Hi all, I updates my previous patches [1] to add support for raid5/6. These patches update the btrfs fi df command and add two new commands: - btrfs filesystem disk-usage <path> - btrfs device disk-usage <path> The command "btrfs filesystem df" now shows only the disk usage/available. $ sudo btrfs filesystem df /mnt/btrfs1/ Disk size: 400.00GB Disk allocated: 8.04GB Disk unallocated: 391.97GB Used: 11.29MB Free (Estimated): 250.45GB (Max: 396.99GB, min: 201.00GB) Data to disk ratio: 63 % The "Free (Estimated)" tries to give an estimation of the free space on the basis of the chunks usage. Max and min are the maximum allowable space (if the next chunk are allocated as SINGLE) or the minimum one ( if the next chunks are allocated as DUP/RAID1/RAID10). The other two commands show the chunks in the disks. $ sudo btrfs filesystem disk-usage /mnt/btrfs1/ Data,Single: Size:8.00MB, Used:0.00 /dev/vdb 8.00MB Data,RAID6: Size:2.00GB, Used:11.25MB /dev/vdb 1.00GB /dev/vdc 1.00GB /dev/vdd 1.00GB /dev/vde 1.00GB Metadata,Single: Size:8.00MB, Used:0.00 /dev/vdb 8.00MB Metadata,RAID5: Size:3.00GB, Used:36.00KB /dev/vdb 1.00GB /dev/vdc 1.00GB /dev/vdd 1.00GB /dev/vde 1.00GB System,Single: Size:4.00MB, Used:0.00 /dev/vdb 4.00MB System,RAID5: Size:12.00MB, Used:4.00KB /dev/vdb 4.00MB /dev/vdc 4.00MB /dev/vdd 4.00MB /dev/vde 4.00MB Unallocated: /dev/vdb 97.98GB /dev/vdc 98.00GB /dev/vdd 98.00GB /dev/vde 98.00GB or in tabular format $ sudo ./btrfs filesystem disk-usage -t /mnt/btrfs1/ Data Data Metadata Metadata System System Single RAID6 Single RAID5 Single RAID5 Unallocated /dev/vdb 8.00MB 1.00GB 8.00MB 1.00GB 4.00MB 4.00MB 97.98GB /dev/vdc - 1.00GB - 1.00GB - 4.00MB 98.00GB /dev/vdd - 1.00GB - 1.00GB - 4.00MB 98.00GB /dev/vde - 1.00GB - 1.00GB - 4.00MB 98.00GB ====== ======= ======== ======== ====== ======= ==========Total 8.00MB 2.00GB 8.00MB 3.00GB 4.00MB 12.00MB 391.97GB Used 0.00 11.25MB 0.00 36.00KB 0.00 4.00KB These are the most complete output, where it is possible to know which disk a chunk uses and the usage of every chunk. Finally the last command shows which chunks a disk hosts: $ sudo ./btrfs device disk-usage /mnt/btrfs1/ /dev/vdb 100.00GB Data,Single: 8.00MB Data,RAID6: 1.00GB Metadata,Single: 8.00MB Metadata,RAID5: 1.00GB System,Single: 4.00MB System,RAID5: 4.00MB Unallocated: 97.98GB /dev/vdc 100.00GB Data,RAID6: 1.00GB Metadata,RAID5: 1.00GB System,RAID5: 4.00MB Unallocated: 98.00GB /dev/vdd 100.00GB Data,RAID6: 1.00GB Metadata,RAID5: 1.00GB System,RAID5: 4.00MB Unallocated: 98.00GB /dev/vde 100.00GB Data,RAID6: 1.00GB Metadata,RAID5: 1.00GB System,RAID5: 4.00MB Unallocated: 98.00GB More or less are the same information above, only grouped by disk. Unfortunately I don''t have any information about the chunk usage per disk basis. Comments are welcome. The code is pullable from http://cassiopea.homelinux.net/git/btrfs-progs-unstable.git branch df-du-raid56 BR G.Baroncelli [1] http://permalink.gmane.org/gmane.comp.file-systems.btrfs/21071 -- gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it> Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-18 21:04 UTC
[PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
This patch adds some helpers to manage the strings allocation and deallocation. The function string_list_add(char *) adds the passed string to a list; the function string_list_free() frees all the strings together. Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> --- Makefile | 3 ++- string_list.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ string_list.h | 23 +++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 string_list.c create mode 100644 string_list.h diff --git a/Makefile b/Makefile index 596bf93..0d6c43a 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,8 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ root-tree.o dir-item.o file-item.o inode-item.o \ inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ volumes.o utils.o btrfs-list.o btrfslabel.o repair.o \ - send-stream.o send-utils.o qgroup.o raid6.o + send-stream.o send-utils.o qgroup.o raid6.o \ + string_list.o cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \ cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \ cmds-quota.o cmds-qgroup.o cmds-replace.o diff --git a/string_list.c b/string_list.c new file mode 100644 index 0000000..d5a28b9 --- /dev/null +++ b/string_list.c @@ -0,0 +1,62 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* To store the strings */ +static void **strings_to_free; +static int count_string_to_free; + +/* + * Add a string to the dynamic allocated string list + */ +void string_list_add(char *s) +{ + int size; + + size = sizeof(void *) * ++count_string_to_free; + strings_to_free = realloc(strings_to_free, size); + + /* if we don''t have enough memory, we have more serius + problem than that a wrong handling of not enough memory */ + if (!strings_to_free) { + fprintf(stderr, "add_string_to_free(): Not enough memory\n"); + strings_to_free = 0; + count_string_to_free = 0; + } + + strings_to_free[count_string_to_free-1] = s; +} + +/* + * Free the dynamic allocated strings list + */ +void string_list_free() +{ + int i; + for (i = 0 ; i < count_string_to_free ; i++) + free(strings_to_free[i]); + + free(strings_to_free); + + strings_to_free = 0; + count_string_to_free = 0; +} + + diff --git a/string_list.h b/string_list.h new file mode 100644 index 0000000..f974fbc --- /dev/null +++ b/string_list.h @@ -0,0 +1,23 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#ifndef STRING_LIST_H +#define STRING_LIST_H + +void string_list_add(char *s); +void string_list_free(); + +#endif -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-18 21:04 UTC
[PATCH 2/8] Enhance the command btrfs filesystem df.
Enhance the command "btrfs filesystem df" to show space usage information for a mount point(s). It shows also an estimation of the space available, on the basis of the current one used. Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> --- Makefile | 2 +- cmds-fi-disk_usage.c | 520 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmds-fi-disk_usage.h | 25 +++ cmds-filesystem.c | 125 +----------- ctree.h | 17 +- utils.c | 14 ++ utils.h | 2 + 7 files changed, 579 insertions(+), 126 deletions(-) create mode 100644 cmds-fi-disk_usage.c create mode 100644 cmds-fi-disk_usage.h diff --git a/Makefile b/Makefile index 0d6c43a..bd792b6 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ string_list.o cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \ cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \ - cmds-quota.o cmds-qgroup.o cmds-replace.o + cmds-quota.o cmds-qgroup.o cmds-replace.o cmds-fi-disk_usage.o CHECKFLAGS= -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ -Wuninitialized -Wshadow -Wundef diff --git a/cmds-fi-disk_usage.c b/cmds-fi-disk_usage.c new file mode 100644 index 0000000..1e3589f --- /dev/null +++ b/cmds-fi-disk_usage.c @@ -0,0 +1,520 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <errno.h> + +#include "utils.h" +#include "kerncompat.h" +#include "ctree.h" +#include "string_list.h" + +#include "commands.h" + +#include "version.h" + +#define DF_HUMAN_UNIT (1<<0) + +/* + * To store the size information about the chunks: + * the chunks info are grouped by the tuple (type, devid, num_stripes), + * i.e. if two chunks are of the same type (RAID1, DUP...), are on the + * same disk, have the same stripes then their sizes are grouped + */ +struct chunk_info { + u64 type; + u64 size; + u64 devid; + int num_stripes; +}; + +/* + * Pretty print the size + */ +static char *df_pretty_sizes(u64 size, int mode) +{ + char *s; + + if (mode & DF_HUMAN_UNIT) { + s = pretty_sizes(size); + if (!s) + return NULL; + } else { + s = malloc(20); + if (!s) + return NULL; + sprintf(s, "%llu", size); + } + + string_list_add(s); + return s; +} + +/* + * Add the chunk info to the chunk_info list + */ +static int add_info_to_list(struct chunk_info **info_ptr, + int *info_count, + struct btrfs_chunk *chunk) +{ + + u64 type = btrfs_stack_chunk_type(chunk); + u64 size = btrfs_stack_chunk_length(chunk); + int num_stripes = btrfs_stack_chunk_num_stripes(chunk); + int j; + + for (j = 0 ; j < num_stripes ; j++) { + int i; + struct chunk_info *p = 0; + struct btrfs_stripe *stripe; + u64 devid; + + stripe = btrfs_stripe_nr(chunk, j); + devid = btrfs_stack_stripe_devid(stripe); + + for (i = 0 ; i < *info_count ; i++) + if ((*info_ptr)[i].type == type && + (*info_ptr)[i].devid == devid && + (*info_ptr)[i].num_stripes == num_stripes ) { + p = (*info_ptr) + i; + break; + } + + if (!p) { + int size = sizeof(struct btrfs_chunk) * (*info_count+1); + struct chunk_info *res = realloc(*info_ptr, size); + + if (!res) { + fprintf(stderr, "ERROR: not enough memory\n"); + return -1; + } + + *info_ptr = res; + p = res + *info_count; + (*info_count)++; + + p->devid = devid; + p->type = type; + p->size = 0; + p->num_stripes = num_stripes; + } + + p->size += size; + + } + + return 0; + +} + +/* + * Helper to sort the chunk type + */ +static int cmp_chunk_block_group(u64 f1, u64 f2) +{ + + u64 mask; + + if ((f1 & BTRFS_BLOCK_GROUP_TYPE_MASK) =+ (f2 & BTRFS_BLOCK_GROUP_TYPE_MASK)) + mask = BTRFS_BLOCK_GROUP_PROFILE_MASK; + else if (f2 & BTRFS_BLOCK_GROUP_SYSTEM) + return -1; + else if (f1 & BTRFS_BLOCK_GROUP_SYSTEM) + return +1; + else + mask = BTRFS_BLOCK_GROUP_TYPE_MASK; + + if ((f1 & mask) > (f2 & mask)) + return +1; + else if ((f1 & mask) < (f2 & mask)) + return -1; + else + return 0; +} + +/* + * Helper to sort the chunk + */ +static int cmp_chunk_info(const void *a, const void *b) +{ + return cmp_chunk_block_group( + ((struct chunk_info *)a)->type, + ((struct chunk_info *)b)->type); +} + +/* + * This function load all the chunk info from the ''fd'' filesystem + */ +static int load_chunk_info(int fd, + struct chunk_info **info_ptr, + int *info_count) +{ + + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header *sh; + unsigned long off = 0; + int i, e; + + + memset(&args, 0, sizeof(args)); + + /* + * there may be more than one ROOT_ITEM key if there are + * snapshots pending deletion, we have to loop through + * them. + */ + + + sk->tree_id = BTRFS_CHUNK_TREE_OBJECTID; + + sk->min_objectid = 0; + sk->max_objectid = (u64)-1; + sk->max_type = 0; + sk->min_type = (u8)-1; + sk->min_offset = 0; + sk->max_offset = (u64)-1; + sk->min_transid = 0; + sk->max_transid = (u64)-1; + sk->nr_items = 4096; + + while (1) { + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + e = errno; + if (ret < 0) { + fprintf(stderr, + "ERROR: can''t perform the search - %s\n", + strerror(e)); + return -99; + } + /* the ioctl returns the number of item it found in nr_items */ + + if (sk->nr_items == 0) + break; + + off = 0; + for (i = 0; i < sk->nr_items; i++) { + struct btrfs_chunk *item; + sh = (struct btrfs_ioctl_search_header *)(args.buf + + off); + + off += sizeof(*sh); + item = (struct btrfs_chunk *)(args.buf + off); + + if (add_info_to_list(info_ptr, info_count, item)) { + *info_ptr = 0; + free(*info_ptr); + return -100; + } + + off += sh->len; + + sk->min_objectid = sh->objectid; + sk->min_type = sh->type; + sk->min_offset = sh->offset+1; + + } + if (!sk->min_offset) /* overflow */ + sk->min_type++; + else + continue; + + if (!sk->min_type) + sk->min_objectid++; + else + continue; + + if (!sk->min_objectid) + break; + } + + qsort(*info_ptr, *info_count, sizeof(struct chunk_info), + cmp_chunk_info); + + return 0; + +} + +/* + * Helper to sort the struct btrfs_ioctl_space_info + */ +static int cmp_btrfs_ioctl_space_info(const void *a, const void *b) +{ + return cmp_chunk_block_group( + ((struct btrfs_ioctl_space_info *)a)->flags, + ((struct btrfs_ioctl_space_info *)b)->flags); +} + +/* + * This function load all the information about the space usage + */ +static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path) +{ + struct btrfs_ioctl_space_args *sargs = 0, *sargs_orig = 0; + int e, ret, count; + + sargs_orig = sargs = malloc(sizeof(struct btrfs_ioctl_space_args)); + if (!sargs) { + fprintf(stderr, "ERROR: not enough memory\n"); + return NULL; + } + + sargs->space_slots = 0; + sargs->total_spaces = 0; + + ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); + e = errno; + if (ret) { + fprintf(stderr, + "ERROR: couldn''t get space info on ''%s'' - %s\n", + path, strerror(e)); + free(sargs); + return NULL; + } + if (!sargs->total_spaces) { + free(sargs); + printf("No chunks found\n"); + return NULL; + } + + count = sargs->total_spaces; + + sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) + + (count * sizeof(struct btrfs_ioctl_space_info))); + if (!sargs) { + free(sargs_orig); + fprintf(stderr, "ERROR: not enough memory\n"); + return NULL; + } + + sargs->space_slots = count; + sargs->total_spaces = 0; + + ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); + e = errno; + + if (ret) { + fprintf(stderr, + "ERROR: couldn''t get space info on ''%s'' - %s\n", + path, strerror(e)); + free(sargs); + return NULL; + } + + qsort(&(sargs->spaces), count, sizeof(struct btrfs_ioctl_space_info), + cmp_btrfs_ioctl_space_info); + + return sargs; +} + +/* + * This function computes the space occuped by a *single* RAID5/RAID6 chunk. + * The computation is performed on the basis of the number of stripes + * which compose the chunk, which could be different from the number of disks + * if a disk is added later. + */ +static int get_raid56_used(int fd, u64 *raid5_used, u64 *raid6_used) +{ + struct chunk_info *info_ptr=0, *p; + int info_count=0; + int ret; + + *raid5_used = *raid6_used =0; + + ret = load_chunk_info(fd, &info_ptr, &info_count); + if( ret < 0) + return ret; + + for ( p = info_ptr; info_count ; info_count--, p++ ) { + if (p->type & BTRFS_BLOCK_GROUP_RAID5) + (*raid5_used) += p->size / (p->num_stripes -1); + if (p->type & BTRFS_BLOCK_GROUP_RAID6) + (*raid6_used) += p->size / (p->num_stripes -2); + } + + return 0; + +} + +static int _cmd_disk_free(int fd, char *path, int mode) +{ + struct btrfs_ioctl_space_args *sargs = 0; + int i; + int ret = 0; + int e, width; + u64 total_disk; /* filesystem size == sum of + disks sizes */ + u64 total_chunks; /* sum of chunks sizes on disk(s) */ + u64 total_used; /* logical space used */ + u64 total_free; /* logical space un-used */ + double K; + u64 raid5_used, raid6_used; + + if ((sargs = load_space_info(fd, path)) == NULL) { + ret = -1; + goto exit; + } + + total_disk = disk_size(path); + e = errno; + if (total_disk == 0) { + fprintf(stderr, + "ERROR: couldn''t get space info on ''%s'' - %s\n", + path, strerror(e)); + + ret = 19; + goto exit; + } + if (get_raid56_used(fd, &raid5_used, &raid6_used) < 0) { + fprintf(stderr, + "ERROR: couldn''t get space info on ''%s''\n", + path ); + ret = 20; + goto exit; + } + + total_chunks = total_used = total_free = 0; + + for (i = 0; i < sargs->total_spaces; i++) { + float ratio = 1; + u64 allocated; + u64 flags = sargs->spaces[i].flags; + + /* TODO: the raid5/raid6 ratio depends by the number + of disk used by every chunk. It is computed separately + */ + if (flags & BTRFS_BLOCK_GROUP_RAID0) + ratio = 1; + else if (flags & BTRFS_BLOCK_GROUP_RAID1) + ratio = 2; + else if (flags & BTRFS_BLOCK_GROUP_RAID5) + ratio = 0; + else if (flags & BTRFS_BLOCK_GROUP_RAID6) + ratio = 0; + else if (flags & BTRFS_BLOCK_GROUP_DUP) + ratio = 2; + else if (flags & BTRFS_BLOCK_GROUP_RAID10) + ratio = 2; + else + ratio = 1; + + allocated = sargs->spaces[i].total_bytes * ratio; + + total_chunks += allocated; + total_used += sargs->spaces[i].used_bytes; + total_free += (sargs->spaces[i].total_bytes - + sargs->spaces[i].used_bytes); + + } + + /* add the raid5/6 allocated space */ + total_chunks += raid5_used + raid6_used; + + K = ((double)total_used + (double)total_free) / (double)total_chunks; + + if (mode & DF_HUMAN_UNIT) + width = 9; + else + width = 18; + + printf("Disk size:\t\t%*s\n", width, + df_pretty_sizes(total_disk, mode)); + printf("Disk allocated:\t\t%*s\n", width, + df_pretty_sizes(total_chunks, mode)); + printf("Disk unallocated:\t%*s\n", width, + df_pretty_sizes(total_disk-total_chunks, mode)); + printf("Used:\t\t\t%*s\n", width, + df_pretty_sizes(total_used, mode)); + printf("Free (Estimated):\t%*s\t(Max: %s, min: %s)\n", + width, + df_pretty_sizes((u64)(K*total_disk-total_used), mode), + df_pretty_sizes(total_disk-total_chunks+total_free, mode), + df_pretty_sizes((total_disk-total_chunks)/2+total_free, mode)); + printf("Data to disk ratio:\t%*.0f %%\n", + width-2, K*100); + +exit: + + string_list_free(); + if (sargs) + free(sargs); + + return ret; +} + +const char * const cmd_filesystem_df_usage[] = { + "btrfs filesystem df [-b] <path> [<path>..]", + "Show space usage information for a mount point(s).", + "", + "-b\tSet byte as unit", + NULL +}; + +int cmd_filesystem_df(int argc, char **argv) +{ + + int flags = DF_HUMAN_UNIT; + int i, more_than_one = 0; + + optind = 1; + while (1) { + char c = getopt(argc, argv, "b"); + if (c < 0) + break; + + switch (c) { + case ''b'': + flags &= ~DF_HUMAN_UNIT; + break; + default: + usage(cmd_filesystem_df_usage); + } + } + + if (check_argc_min(argc - optind, 1)) { + usage(cmd_filesystem_df_usage); + return 21; + } + + for (i = optind; i < argc ; i++) { + int r, fd; + if (more_than_one) + printf("\n"); + + fd = open_file_or_dir(argv[i]); + if (fd < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", + argv[1]); + return 12; + } + r = _cmd_disk_free(fd, argv[i], flags); + close(fd); + + if (r) + return r; + more_than_one = 1; + + } + + return 0; +} + diff --git a/cmds-fi-disk_usage.h b/cmds-fi-disk_usage.h new file mode 100644 index 0000000..9f68bb3 --- /dev/null +++ b/cmds-fi-disk_usage.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2007 Oracle. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#ifndef __CMDS_FI_DISK_USAGE__ +#define __CMDS_FI_DISK_USAGE__ + +extern const char * const cmd_filesystem_df_usage[]; +int cmd_filesystem_df(int argc, char **argv); + +#endif diff --git a/cmds-filesystem.c b/cmds-filesystem.c index bdbd2ee..5301dc3 100644 --- a/cmds-filesystem.c +++ b/cmds-filesystem.c @@ -33,6 +33,7 @@ #include "commands.h" #include "btrfslabel.h" +#include "cmds-fi-disk_usage.h" static const char * const filesystem_cmd_group_usage[] = { "btrfs filesystem [<group>] <command> [<args>]", @@ -45,128 +46,6 @@ static const char * const cmd_df_usage[] = { NULL }; -static int cmd_df(int argc, char **argv) -{ - struct btrfs_ioctl_space_args *sargs, *sargs_orig; - u64 count = 0, i; - int ret; - int fd; - int e; - char *path; - - if (check_argc_exact(argc, 2)) - usage(cmd_df_usage); - - path = argv[1]; - - fd = open_file_or_dir(path); - if (fd < 0) { - fprintf(stderr, "ERROR: can''t access to ''%s''\n", path); - return 12; - } - - sargs_orig = sargs = malloc(sizeof(struct btrfs_ioctl_space_args)); - if (!sargs) - return -ENOMEM; - - sargs->space_slots = 0; - sargs->total_spaces = 0; - - ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); - e = errno; - if (ret) { - fprintf(stderr, "ERROR: couldn''t get space info on ''%s'' - %s\n", - path, strerror(e)); - close(fd); - free(sargs); - return ret; - } - if (!sargs->total_spaces) { - close(fd); - free(sargs); - return 0; - } - - count = sargs->total_spaces; - - sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) + - (count * sizeof(struct btrfs_ioctl_space_info))); - if (!sargs) { - close(fd); - free(sargs_orig); - return -ENOMEM; - } - - sargs->space_slots = count; - sargs->total_spaces = 0; - - ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); - e = errno; - if (ret) { - fprintf(stderr, "ERROR: couldn''t get space info on ''%s'' - %s\n", - path, strerror(e)); - close(fd); - free(sargs); - return ret; - } - - for (i = 0; i < sargs->total_spaces; i++) { - char description[80]; - char *total_bytes; - char *used_bytes; - int written = 0; - u64 flags = sargs->spaces[i].flags; - - memset(description, 0, 80); - - if (flags & BTRFS_BLOCK_GROUP_DATA) { - if (flags & BTRFS_BLOCK_GROUP_METADATA) { - snprintf(description, 14, "%s", - "Data+Metadata"); - written += 13; - } else { - snprintf(description, 5, "%s", "Data"); - written += 4; - } - } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) { - snprintf(description, 7, "%s", "System"); - written += 6; - } else if (flags & BTRFS_BLOCK_GROUP_METADATA) { - snprintf(description, 9, "%s", "Metadata"); - written += 8; - } - - if (flags & BTRFS_BLOCK_GROUP_RAID0) { - snprintf(description+written, 8, "%s", ", RAID0"); - written += 7; - } else if (flags & BTRFS_BLOCK_GROUP_RAID1) { - snprintf(description+written, 8, "%s", ", RAID1"); - written += 7; - } else if (flags & BTRFS_BLOCK_GROUP_DUP) { - snprintf(description+written, 6, "%s", ", DUP"); - written += 5; - } else if (flags & BTRFS_BLOCK_GROUP_RAID10) { - snprintf(description+written, 9, "%s", ", RAID10"); - written += 8; - } else if (flags & BTRFS_BLOCK_GROUP_RAID5) { - snprintf(description+written, 9, "%s", ", RAID5"); - written += 7; - } else if (flags & BTRFS_BLOCK_GROUP_RAID6) { - snprintf(description+written, 9, "%s", ", RAID6"); - written += 7; - } - - total_bytes = pretty_sizes(sargs->spaces[i].total_bytes); - used_bytes = pretty_sizes(sargs->spaces[i].used_bytes); - printf("%s: total=%s, used=%s\n", description, total_bytes, - used_bytes); - } - close(fd); - free(sargs); - - return 0; -} - static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search) { char uuidbuf[37]; @@ -517,7 +396,7 @@ static int cmd_label(int argc, char **argv) const struct cmd_group filesystem_cmd_group = { filesystem_cmd_group_usage, NULL, { - { "df", cmd_df, cmd_df_usage, NULL, 0 }, + { "df", cmd_filesystem_df, cmd_filesystem_df_usage, NULL, 0 }, { "show", cmd_show, cmd_show_usage, NULL, 0 }, { "sync", cmd_sync, cmd_sync_usage, NULL, 0 }, { "defragment", cmd_defrag, cmd_defrag_usage, NULL, 0 }, diff --git a/ctree.h b/ctree.h index 12f8fe3..a029368 100644 --- a/ctree.h +++ b/ctree.h @@ -798,9 +798,22 @@ struct btrfs_csum_item { #define BTRFS_BLOCK_GROUP_RAID1 (1ULL << 4) #define BTRFS_BLOCK_GROUP_DUP (1ULL << 5) #define BTRFS_BLOCK_GROUP_RAID10 (1ULL << 6) -#define BTRFS_BLOCK_GROUP_RAID5 (1ULL << 7) -#define BTRFS_BLOCK_GROUP_RAID6 (1ULL << 8) +#define BTRFS_BLOCK_GROUP_RAID5 (1ULL << 7) +#define BTRFS_BLOCK_GROUP_RAID6 (1ULL << 8) #define BTRFS_BLOCK_GROUP_RESERVED BTRFS_AVAIL_ALLOC_BIT_SINGLE +#define BTRFS_NR_RAID_TYPES 7 + +#define BTRFS_BLOCK_GROUP_TYPE_MASK (BTRFS_BLOCK_GROUP_DATA | \ + BTRFS_BLOCK_GROUP_SYSTEM | \ + BTRFS_BLOCK_GROUP_METADATA) + +#define BTRFS_BLOCK_GROUP_PROFILE_MASK (BTRFS_BLOCK_GROUP_RAID0 | \ + BTRFS_BLOCK_GROUP_RAID1 | \ + BTRFS_BLOCK_GROUP_RAID5 | \ + BTRFS_BLOCK_GROUP_RAID6 | \ + BTRFS_BLOCK_GROUP_DUP | \ + BTRFS_BLOCK_GROUP_RAID10) + /* used in struct btrfs_balance_args fields */ #define BTRFS_AVAIL_ALLOC_BIT_SINGLE (1ULL << 48) diff --git a/utils.c b/utils.c index f9ee812..029729c 100644 --- a/utils.c +++ b/utils.c @@ -38,6 +38,8 @@ #include <linux/major.h> #include <linux/kdev_t.h> #include <limits.h> +#include <sys/vfs.h> + #include "kerncompat.h" #include "radix-tree.h" #include "ctree.h" @@ -1386,3 +1388,15 @@ int get_fs_info(int fd, char *path, struct btrfs_ioctl_fs_info_args *fi_args, return 0; } + +u64 disk_size(char *path) +{ + struct statfs sfs; + + if (statfs(path, &sfs) < 0) + return 0; + else + return sfs.f_bsize * sfs.f_blocks; + +} + diff --git a/utils.h b/utils.h index bbcaf6a..7974d00 100644 --- a/utils.h +++ b/utils.h @@ -19,6 +19,7 @@ #ifndef __UTILS__ #define __UTILS__ +#include "kerncompat.h" #include "ctree.h" #define BTRFS_MKFS_SYSTEM_GROUP_SIZE (4 * 1024 * 1024) @@ -58,4 +59,5 @@ char *__strncpy__null(char *dest, const char *src, size_t n); /* Helper to always get proper size of the destination string */ #define strncpy_null(dest, src) __strncpy__null(dest, src, sizeof(dest)) +u64 disk_size(char *path); #endif -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-18 21:04 UTC
[PATCH 3/8] Create the man page entry for the command btrfs fi df
Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> --- man/btrfs.8.in | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/man/btrfs.8.in b/man/btrfs.8.in index 94f4ffe..e2f86ea 100644 --- a/man/btrfs.8.in +++ b/man/btrfs.8.in @@ -31,6 +31,8 @@ btrfs \- control a btrfs filesystem .PP \fBbtrfs\fP \fBfilesystem label\fP\fI <dev> [newlabel]\fP .PP +\fBbtrfs\fP \fBfilesystem df\fP\fI [-b] \fIpath [path..]\fR\fP +.PP \fBbtrfs\fP \fBfilesystem balance\fP\fI <path> \fP .PP \fBbtrfs\fP \fBdevice scan\fP\fI [--all-devices|<device> [<device>...]]\fP @@ -266,6 +268,53 @@ NOTE: Currently there are the following limitations: - the filesystem should not have more than one device. .TP +\fBfilesystem df\fP [-b] \fIpath [path..]\fR + +Show space usage information for a mount point. + +\fB-b\fP Set byte as unit + +The command \fBbtrfs filesystem df\fP is used to query how many space on the +disk(s) are used and an estimation of the free +space of the filesystem. +The output of the command \fBbtrfs filesystem df\fP shows: + +.RS +.IP \fBDisk\ size\fP +the total size of the disks which compose the filesystem. + +.IP \fBDisk\ allocated\fP +the size of the area of the disks used by the chunks. + +.IP \fBDisk\ unallocated\fP +the size of the area of the disks which is free (i.e. +the differences of the values above). + +.IP \fBUsed\fP +the portion of the logical space used by the file and metadata. + +.IP \fBFree\ (estimated)\fP +the estimated free space available: i.e. how many space can be used +by the user. The evaluation +cannot be rigorous because it depends by the allocation policy (DUP, Single, +RAID1...) of the metadata and data chunks. If every chunk is stored as +"Single" the sum of the \fBfree (estimated)\fP space and the \fBused\fP +space is equal to the \fBdisk size\fP. +Otherwise if all the chunk are mirrored (raid1 or raid10) or duplicated +the sum of the \fBfree (estimated)\fP space and the \fBused\fP space is +half of the \fBdisk size\fP. Normally the \fBfree (estimated)\fP is between +these two limits. + +.IP \fBData\ to\ disk\ ratio\fP +the ratio betwen the \fBlogical size\fP (i.e. the space available by +the chunks) and the \fBdisk allocated\fP (by the chunks). Normally it is +lower than 100% because the metadata is duplicated for security reasons. +If all the data and metadata are duplicated (or have a profile like RAID1) +the \fBData\ to\ disk\ ratio\fP could be 50%. + +.RE +.TP + \fBfilesystem show\fR [--all-devices|<uuid>|<label>]\fR Show the btrfs filesystem with some additional info. If no \fIUUID\fP or \fIlabel\fP is passed, \fBbtrfs\fR show info of all the btrfs filesystem. -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-18 21:04 UTC
[PATCH 4/8] Add helpers functions to handle the printing of data in tabular format.
This patch adds some functions to manage the printing of the data in tabular format. The function struct string_table *table_create(int columns, int rows) creates an (empty) table. The functions char *table_printf(struct string_table *tab, int column, int row, char *fmt, ...) char *table_vprintf(struct string_table *tab, int column, int row, char *fmt, va_list ap) populate the table with text. To align the text to the left, the text shall be prefixed with ''<'', otherwise the text shall be prefixed by a ''>''. If the first character is a ''='', the the text is replace by a sequence of ''='' to fill the column width. The function void table_free(struct string_table *) frees all the data associated to the table. The function void table_dump(struct string_table *tab) prints the table on stdout. Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> --- Makefile | 2 +- string_table.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ string_table.h | 36 +++++++++++++ 3 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 string_table.c create mode 100644 string_table.h diff --git a/Makefile b/Makefile index bd792b6..fd1b312 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \ volumes.o utils.o btrfs-list.o btrfslabel.o repair.o \ send-stream.o send-utils.o qgroup.o raid6.o \ - string_list.o + string_list.o string_table.o cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \ cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \ cmds-quota.o cmds-qgroup.o cmds-replace.o cmds-fi-disk_usage.o diff --git a/string_table.c b/string_table.c new file mode 100644 index 0000000..9784422 --- /dev/null +++ b/string_table.c @@ -0,0 +1,157 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> + +#include "string_table.h" + +/* + * This function create an array of char * which will represent a table + */ +struct string_table *table_create(int columns, int rows) +{ + struct string_table *p; + int size; + + + size = sizeof( struct string_table ) + + rows * columns* sizeof(char *); + p = calloc(1, size); + + if (!p) return NULL; + + p->ncols = columns; + p->nrows = rows; + + return p; +} + +/* + * This function is like a vprintf, but store the results in a cell of + * the table. + * If fmt starts with ''<'', the text is left aligned; if fmt starts with + * ''>'' the text is right aligned. If fmt is equal to ''='' the text will + * be replaced by a ''====='' dimensioned in the basis of the column width + */ +char *table_vprintf(struct string_table *tab, int column, int row, + char *fmt, va_list ap) +{ + int idx = tab->ncols*row+column; + char *msg = calloc(100, sizeof(char)); + + if (!msg) + return NULL; + + if (tab->cells[idx]) + free(tab->cells[idx]); + tab->cells[idx] = msg; + vsnprintf(msg, 99, fmt, ap); + + return msg; +} + + +/* + * This function is like a printf, but store the results in a cell of + * the table. + */ +char *table_printf(struct string_table *tab, int column, int row, + char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = table_vprintf(tab, column, row, fmt, ap); + va_end(ap); + + return ret; +} + +/* + * This function dumps the table. Every "=" string will be replaced by + * a "=======" length as the column + */ +void table_dump(struct string_table *tab) +{ + int sizes[tab->ncols]; + int i, j; + + for (i = 0 ; i < tab->ncols ; i++) { + sizes[i] = 0; + for (j = 0 ; j < tab->nrows ; j++) { + int idx = i + j*tab->ncols; + int s; + + if (!tab->cells[idx]) + continue; + + s = strlen(tab->cells[idx]) - 1; + if (s < 1 || tab->cells[idx][0] == ''='') + continue; + + if (s > sizes[i]) + sizes[i] = s; + } + } + + + for (j = 0 ; j < tab->nrows ; j++) { + for (i = 0 ; i < tab->ncols ; i++) { + + int idx = i + j*tab->ncols; + char *s = tab->cells[idx]; + + if (!s|| !strlen(s)) { + printf("%*s", sizes[i], ""); + } else if (s && s[0] == ''='') { + int k = sizes[i]; + while(k--) + putchar(''=''); + } else { + printf("%*s", + s[0] == ''<'' ? -sizes[i] : sizes[i], + s+1); + } + if (i != (tab->ncols - 1)) + putchar('' ''); + } + putchar(''\n''); + } + +} + +/* + * Deallocate a tabular and all its content + */ + +void table_free(struct string_table *tab) +{ + + int i, count; + + count = tab->ncols * tab->nrows; + + for (i=0 ; i < count ; i++) + if (tab->cells[i]) + free(tab->cells[i]); + + free(tab); + +} diff --git a/string_table.h b/string_table.h new file mode 100644 index 0000000..83c4425 --- /dev/null +++ b/string_table.h @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#ifndef STRING_TABLE_H +#define STRING_TABLE_H + +struct string_table { + + int ncols, nrows; + char *cells[]; + +}; + + +struct string_table *table_create(int columns, int rows); +char *table_printf(struct string_table *tab, int column, int row, + char *fmt, ...); +char *table_vprintf(struct string_table *tab, int column, int row, + char *fmt, va_list ap); +void table_dump(struct string_table *tab); +void table_free(struct string_table *); + +#endif -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-18 21:04 UTC
[PATCH 5/8] Add command btrfs filesystem disk-usage
Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> --- cmds-fi-disk_usage.c | 434 +++++++++++++++++++++++++++++++++++++++++++++++++- cmds-fi-disk_usage.h | 2 + cmds-filesystem.c | 2 + utils.c | 58 +++++++ utils.h | 3 + 5 files changed, 498 insertions(+), 1 deletion(-) diff --git a/cmds-fi-disk_usage.c b/cmds-fi-disk_usage.c index 1e3589f..eea4168 100644 --- a/cmds-fi-disk_usage.c +++ b/cmds-fi-disk_usage.c @@ -20,11 +20,13 @@ #include <unistd.h> #include <sys/ioctl.h> #include <errno.h> +#include <stdarg.h> #include "utils.h" #include "kerncompat.h" #include "ctree.h" #include "string_list.h" +#include "string_table.h" #include "commands.h" @@ -42,7 +44,14 @@ struct chunk_info { u64 type; u64 size; u64 devid; - int num_stripes; + u64 num_stripes; +}; + +/* to store information about the disks */ +struct disk_info { + u64 devid; + char path[BTRFS_DEVICE_PATH_NAME_MAX]; + u64 size; }; /* @@ -518,3 +527,426 @@ int cmd_filesystem_df(int argc, char **argv) return 0; } +/* + * Helper to sort the disk_info structure + */ +static int cmp_disk_info(const void *a, const void *b) +{ + return strcmp(((struct disk_info *)a)->path, + ((struct disk_info *)b)->path); +} + +/* + * This function load the disk_info structure and put them in an array + */ +static int load_disks_info(int fd, + struct disk_info **disks_info_ptr, + int *disks_info_count) +{ + + int ret, i, ndevs; + struct btrfs_ioctl_fs_info_args fi_args; + struct btrfs_ioctl_dev_info_args dev_info; + struct disk_info *info; + + *disks_info_count = 0; + *disks_info_ptr = 0; + + ret = ioctl(fd, BTRFS_IOC_FS_INFO, &fi_args); + if (ret < 0) { + fprintf(stderr, "ERROR: cannot get filesystem info\n"); + return -1; + } + + info = malloc(sizeof(struct disk_info) * fi_args.num_devices); + if (!info) { + fprintf(stderr, "ERROR: not enough memory\n"); + return -1; + } + + for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) { + + BUG_ON(ndevs >= fi_args.num_devices); + ret = get_device_info(fd, i, &dev_info); + + if (ret == -ENODEV) + continue; + if (ret) { + fprintf(stderr, + "ERROR: cannot get info about device devid=%d\n", + i); + free(info); + return -1; + } + + info[ndevs].devid = dev_info.devid; + strcpy(info[ndevs].path, (char *)dev_info.path); + info[ndevs].size = get_partition_size((char *)dev_info.path); + ++ndevs; + } + + BUG_ON(ndevs != fi_args.num_devices); + qsort(info, fi_args.num_devices, + sizeof(struct disk_info), cmp_disk_info); + + *disks_info_count = fi_args.num_devices; + *disks_info_ptr = info; + + return 0; + +} + +/* + * This function computes the size of a chunk in a disk + */ +static u64 calc_chunk_size(struct chunk_info *ci) +{ + if (ci->type & BTRFS_BLOCK_GROUP_RAID0) + return ci->size / ci->num_stripes; + else if (ci->type & BTRFS_BLOCK_GROUP_RAID1) + return ci->size ; + else if (ci->type & BTRFS_BLOCK_GROUP_DUP) + return ci->size ; + else if (ci->type & BTRFS_BLOCK_GROUP_RAID5) + return ci->size / (ci->num_stripes -1); + else if (ci->type & BTRFS_BLOCK_GROUP_RAID6) + return ci->size / (ci->num_stripes -2); + else if (ci->type & BTRFS_BLOCK_GROUP_RAID10) + return ci->size / ci->num_stripes; + return ci->size; +} + +/* + * This function print the results of the command btrfs fi disk-usage + * in tabular format + */ +static void _cmd_filesystem_disk_usage_tabular(int mode, + struct btrfs_ioctl_space_args *sargs, + struct chunk_info *chunks_info_ptr, + int chunks_info_count, + struct disk_info *disks_info_ptr, + int disks_info_count) +{ + int i; + u64 total_unused = 0; + struct string_table *matrix = 0; + int ncols, nrows; + + + ncols = sargs->total_spaces + 2; + nrows = 2 + 1 + disks_info_count + 1 + 2; + + matrix = table_create(ncols, nrows); + if (!matrix) { + fprintf(stderr, "ERROR: not enough memory\n"); + return; + } + + /* header */ + for (i = 0; i < sargs->total_spaces; i++) { + const char *description; + + u64 flags = sargs->spaces[i].flags; + description = btrfs_flags2description(flags); + + table_printf(matrix, 1+i, 0, "<%s", description); + } + + for (i = 0; i < sargs->total_spaces; i++) { + const char *r_mode; + + u64 flags = sargs->spaces[i].flags; + r_mode = btrfs_flags2profile(flags); + + table_printf(matrix, 1+i, 1, "<%s", r_mode); + } + + table_printf(matrix, 1+sargs->total_spaces, 1, "<Unallocated"); + + /* body */ + for (i = 0 ; i < disks_info_count ; i++) { + int k, col; + char *p; + + u64 total_allocated = 0, unused; + + p = strrchr(disks_info_ptr[i].path, ''/''); + if (!p) + p = disks_info_ptr[i].path; + else + p++; + + table_printf(matrix, 0, i+3, "<%s", + disks_info_ptr[i].path); + + for (col = 1, k = 0 ; k < sargs->total_spaces ; k++) { + u64 flags = sargs->spaces[k].flags; + int j; + + for (j = 0 ; j < chunks_info_count ; j++) { + u64 size = calc_chunk_size(chunks_info_ptr+j); + + if (chunks_info_ptr[j].type != flags || + chunks_info_ptr[j].devid !+ disks_info_ptr[i].devid) + continue; + + table_printf(matrix, col, i+3, + ">%s", df_pretty_sizes(size, mode)); + total_allocated += size; + col++; + break; + + } + if (j == chunks_info_count) { + table_printf(matrix, col, i+3, ">-"); + col++; + } + } + + unused = get_partition_size(disks_info_ptr[i].path) - + total_allocated; + + table_printf(matrix, sargs->total_spaces + 1, i + 3, + ">%s", df_pretty_sizes(unused, mode)); + total_unused += unused; + + } + + for (i = 0; i <= sargs->total_spaces; i++) + table_printf(matrix, i + 1, disks_info_count + 3, "="); + + + /* footer */ + table_printf(matrix, 0, disks_info_count + 4, "<Total"); + for (i = 0; i < sargs->total_spaces; i++) + table_printf(matrix, 1 + i, disks_info_count + 4, + ">%s", + df_pretty_sizes(sargs->spaces[i].total_bytes, mode)); + + table_printf(matrix, sargs->total_spaces+1, disks_info_count+4, + ">%s", df_pretty_sizes(total_unused, mode)); + + table_printf(matrix, 0, disks_info_count+5, "<Used"); + for (i = 0; i < sargs->total_spaces; i++) + table_printf(matrix, 1+i, disks_info_count+5, ">%s", + df_pretty_sizes(sargs->spaces[i].used_bytes, mode)); + + + table_dump(matrix); + table_free(matrix); + +} + +/* + * This function prints the unused space per every disk + */ +static void print_unused(struct chunk_info *info_ptr, + int info_count, + struct disk_info *disks_info_ptr, + int disks_info_count, + int mode) +{ + int i; + for (i = 0 ; i < disks_info_count ; i++) { + + int j; + u64 total = 0; + char *s; + + for (j = 0 ; j < info_count ; j++) + if (info_ptr[j].devid == disks_info_ptr[i].devid) + total += calc_chunk_size(info_ptr+j); + + s = df_pretty_sizes(disks_info_ptr[i].size - total, mode); + printf(" %s\t%10s\n", disks_info_ptr[i].path, s); + + } + +} + +/* + * This function prints the allocated chunk per every disk + */ +static void print_chunk_disks(u64 chunk_type, + struct chunk_info *chunks_info_ptr, + int chunks_info_count, + struct disk_info *disks_info_ptr, + int disks_info_count, + int mode) +{ + int i; + + for (i = 0 ; i < disks_info_count ; i++) { + + int j; + u64 total = 0; + char *s; + + for (j = 0 ; j < chunks_info_count ; j++) { + + if (chunks_info_ptr[j].type != chunk_type) + continue; + if (chunks_info_ptr[j].devid != disks_info_ptr[i].devid) + continue; + + total += calc_chunk_size(&(chunks_info_ptr[j])); + //total += chunks_info_ptr[j].size; + } + + if (total > 0) { + s = df_pretty_sizes(total, mode); + printf(" %s\t%10s\n", disks_info_ptr[i].path, s); + } + } +} + +/* + * This function print the results of the command btrfs fi disk-usage + * in linear format + */ +static void _cmd_filesystem_disk_usage_linear(int mode, + struct btrfs_ioctl_space_args *sargs, + struct chunk_info *info_ptr, + int info_count, + struct disk_info *disks_info_ptr, + int disks_info_count) +{ + int i; + + for (i = 0; i < sargs->total_spaces; i++) { + const char *description; + const char *r_mode; + + u64 flags = sargs->spaces[i].flags; + description= btrfs_flags2description(flags); + r_mode = btrfs_flags2profile(flags); + + printf("%s,%s: Size:%s, Used:%s\n", + description, + r_mode, + df_pretty_sizes(sargs->spaces[i].total_bytes , + mode), + df_pretty_sizes(sargs->spaces[i].used_bytes, + mode)); + + print_chunk_disks(flags, info_ptr, info_count, + disks_info_ptr, disks_info_count, + mode); + printf("\n"); + + } + + printf("Unallocated:\n"); + print_unused(info_ptr, info_count, + disks_info_ptr, disks_info_count, + mode); + + + +} + +static int _cmd_filesystem_disk_usage(int fd, char *path, int mode, int tabular) +{ + struct btrfs_ioctl_space_args *sargs = 0; + int info_count = 0; + struct chunk_info *info_ptr = 0; + struct disk_info *disks_info_ptr = 0; + int disks_info_count = 0; + int ret = 0; + + if (load_chunk_info(fd, &info_ptr, &info_count) || + load_disks_info(fd, &disks_info_ptr, &disks_info_count)) { + ret = -1; + goto exit; + } + + if ((sargs = load_space_info(fd, path)) == NULL) { + ret = -1; + goto exit; + } + + if (tabular) + _cmd_filesystem_disk_usage_tabular(mode, sargs, + info_ptr, info_count, + disks_info_ptr, disks_info_count); + else + _cmd_filesystem_disk_usage_linear(mode, sargs, + info_ptr, info_count, + disks_info_ptr, disks_info_count); + +exit: + + string_list_free(); + if (sargs) + free(sargs); + if (disks_info_ptr) + free(disks_info_ptr); + if (info_ptr) + free(info_ptr); + + return ret; +} + +const char * const cmd_filesystem_disk_usage_usage[] = { + "btrfs filesystem disk-usage [-b][-t] <path> [<path>..]", + "Show in which disk the chunks are allocated.", + "", + "-b\tSet byte as unit", + "-t\tShow data in tabular format", + NULL +}; + +int cmd_filesystem_disk_usage(int argc, char **argv) +{ + + int flags = DF_HUMAN_UNIT; + int i, more_than_one = 0; + int tabular = 0; + + optind = 1; + while (1) { + char c = getopt(argc, argv, "bt"); + if (c < 0) + break; + switch (c) { + case ''b'': + flags &= ~DF_HUMAN_UNIT; + break; + case ''t'': + tabular = 1; + break; + default: + usage(cmd_filesystem_disk_usage_usage); + } + } + + if (check_argc_min(argc - optind, 1)) { + usage(cmd_filesystem_disk_usage_usage); + return 21; + } + + for (i = optind; i < argc ; i++) { + int r, fd; + if (more_than_one) + printf("\n"); + + fd = open_file_or_dir(argv[i]); + if (fd < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", + argv[1]); + return 12; + } + r = _cmd_filesystem_disk_usage(fd, argv[i], flags, tabular); + close(fd); + + if (r) + return r; + more_than_one = 1; + + } + + return 0; +} + + diff --git a/cmds-fi-disk_usage.h b/cmds-fi-disk_usage.h index 9f68bb3..ae11570 100644 --- a/cmds-fi-disk_usage.h +++ b/cmds-fi-disk_usage.h @@ -21,5 +21,7 @@ extern const char * const cmd_filesystem_df_usage[]; int cmd_filesystem_df(int argc, char **argv); +extern const char * const cmd_filesystem_disk_usage_usage[]; +int cmd_filesystem_disk_usage(int argc, char **argv); #endif diff --git a/cmds-filesystem.c b/cmds-filesystem.c index 5301dc3..e9eb0c4 100644 --- a/cmds-filesystem.c +++ b/cmds-filesystem.c @@ -403,6 +403,8 @@ const struct cmd_group filesystem_cmd_group = { { "balance", cmd_balance, NULL, &balance_cmd_group, 1 }, { "resize", cmd_resize, cmd_resize_usage, NULL, 0 }, { "label", cmd_label, cmd_label_usage, NULL, 0 }, + { "disk-usage", cmd_filesystem_disk_usage, + cmd_filesystem_disk_usage_usage, NULL, 0 }, { 0, 0, 0, 0, 0 }, } }; diff --git a/utils.c b/utils.c index 029729c..60f05e4 100644 --- a/utils.c +++ b/utils.c @@ -1400,3 +1400,61 @@ u64 disk_size(char *path) } +u64 get_partition_size(char *dev) +{ + u64 result; + int fd = open(dev, O_RDONLY); + + if (fd < 0) + return 0; + if (ioctl(fd, BLKGETSIZE64, &result) < 0) { + close(fd); + return 0; + } + close(fd); + + return result; +} + +/* + * Convert a chunk type to a chunk description + */ +const char * btrfs_flags2description(u64 flags) +{ + if (flags & BTRFS_BLOCK_GROUP_DATA) { + if (flags & BTRFS_BLOCK_GROUP_METADATA) + return "Data+Metadata"; + else + return "Data"; + } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) { + return "System"; + } else if (flags & BTRFS_BLOCK_GROUP_METADATA) { + return "Metadata"; + } else { + return "Unknown"; + } +} + +/* + * Convert a chunk type to a chunk profile description + */ +const char * btrfs_flags2profile(u64 flags) +{ + if (flags & BTRFS_BLOCK_GROUP_RAID0) { + return "RAID0"; + } else if (flags & BTRFS_BLOCK_GROUP_RAID1) { + return "RAID1"; + } else if (flags & BTRFS_BLOCK_GROUP_RAID5) { + return "RAID5"; + } else if (flags & BTRFS_BLOCK_GROUP_RAID6) { + return "RAID6"; + } else if (flags & BTRFS_BLOCK_GROUP_DUP) { + return "DUP"; + } else if (flags & BTRFS_BLOCK_GROUP_RAID10) { + return "RAID10"; + } else { + return "Single"; + } +} + + diff --git a/utils.h b/utils.h index 7974d00..84fdd0e 100644 --- a/utils.h +++ b/utils.h @@ -60,4 +60,7 @@ char *__strncpy__null(char *dest, const char *src, size_t n); #define strncpy_null(dest, src) __strncpy__null(dest, src, sizeof(dest)) u64 disk_size(char *path); +u64 get_partition_size(char *dev); +const char * btrfs_flags2profile(u64 flags); +const char * btrfs_flags2description(u64 flags); #endif -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-18 21:04 UTC
[PATCH 6/8] Create entry in man page for btrfs filesystem disk-usage
Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> --- man/btrfs.8.in | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/man/btrfs.8.in b/man/btrfs.8.in index e2f86ea..50dc510 100644 --- a/man/btrfs.8.in +++ b/man/btrfs.8.in @@ -29,6 +29,9 @@ btrfs \- control a btrfs filesystem .PP \fBbtrfs\fP \fBfilesystem resize\fP\fI [devid:][+/\-]<size>[gkm]|[devid:]max <filesystem>\fP .PP +\fBbtrfs\fP \fBfilesystem filesystem disk-usage [-t][-b]\fP\fI <path> +[path..]\fP +.PP \fBbtrfs\fP \fBfilesystem label\fP\fI <dev> [newlabel]\fP .PP \fBbtrfs\fP \fBfilesystem df\fP\fI [-b] \fIpath [path..]\fR\fP @@ -251,6 +254,16 @@ it with the new desired size. When recreating the partition make sure to use the same starting disk cylinder as before. .TP +\fBfilesystem disk-usage\fP [-t][-b] \fIpath [path..]\fR + +Show in which disk the chunks are allocated. + +\fB-b\fP Set byte as unit + +\fB-t\fP Show data in tabular format + +.TP + \fBfilesystem label\fP\fI <dev> [newlabel]\fP Show or update the label of a filesystem. \fI<dev>\fR is used to identify the filesystem. -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> --- cmds-device.c | 3 ++ cmds-fi-disk_usage.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmds-fi-disk_usage.h | 4 ++ 3 files changed, 148 insertions(+) diff --git a/cmds-device.c b/cmds-device.c index 198ad68..0dbc02c 100644 --- a/cmds-device.c +++ b/cmds-device.c @@ -27,6 +27,7 @@ #include "ctree.h" #include "ioctl.h" #include "utils.h" +#include "cmds-fi-disk_usage.h" #include "commands.h" @@ -403,6 +404,8 @@ const struct cmd_group device_cmd_group = { { "scan", cmd_scan_dev, cmd_scan_dev_usage, NULL, 0 }, { "ready", cmd_ready_dev, cmd_ready_dev_usage, NULL, 0 }, { "stats", cmd_dev_stats, cmd_dev_stats_usage, NULL, 0 }, + { "disk-usage", cmd_device_disk_usage, + cmd_device_disk_usage_usage, NULL, 0 }, { 0, 0, 0, 0, 0 } } }; diff --git a/cmds-fi-disk_usage.c b/cmds-fi-disk_usage.c index eea4168..18350ce 100644 --- a/cmds-fi-disk_usage.c +++ b/cmds-fi-disk_usage.c @@ -949,4 +949,145 @@ int cmd_filesystem_disk_usage(int argc, char **argv) return 0; } +static void print_disk_chunks(int fd, + u64 devid, + u64 total_size, + struct chunk_info *chunks_info_ptr, + int chunks_info_count, + int mode) +{ + int i; + u64 allocated = 0; + char *s; + + for (i = 0 ; i < chunks_info_count ; i++) { + const char *description; + const char *r_mode; + u64 flags; + u64 size; + + if (chunks_info_ptr[i].devid != devid) + continue; + + flags = chunks_info_ptr[i].type; + + description = btrfs_flags2description(flags); + r_mode = btrfs_flags2profile(flags); + size = calc_chunk_size(chunks_info_ptr+i); + s = df_pretty_sizes(size, mode); + printf(" %s,%s:%*s%10s\n", + description, + r_mode, + (int)(20 - strlen(description) - strlen(r_mode)), "", + s); + + allocated += size; + + } + s = df_pretty_sizes(total_size - allocated, mode); + printf(" Unallocated: %*s%10s\n", + (int)(20 - strlen("Unallocated")), "", + s); + +} + +static int _cmd_device_disk_usage(int fd, char *path, int mode) +{ + int i; + int ret = 0; + int info_count = 0; + struct chunk_info *info_ptr = 0; + struct disk_info *disks_info_ptr = 0; + int disks_info_count = 0; + + if (load_chunk_info(fd, &info_ptr, &info_count) || + load_disks_info(fd, &disks_info_ptr, &disks_info_count)) { + ret = -1; + goto exit; + } + + for (i = 0 ; i < disks_info_count ; i++) { + char *s; + + s = df_pretty_sizes(disks_info_ptr[i].size, mode); + printf("%s\t%10s\n", disks_info_ptr[i].path, s); + + print_disk_chunks(fd, disks_info_ptr[i].devid, + disks_info_ptr[i].size, + info_ptr, info_count, + mode); + printf("\n"); + + } + + +exit: + + string_list_free(); + if (disks_info_ptr) + free(disks_info_ptr); + if (info_ptr) + free(info_ptr); + + return ret; +} + +const char * const cmd_device_disk_usage_usage[] = { + "btrfs device disk-usage [-b] <path> [<path>..]", + "Show which chunks are in a device.", + "", + "-b\tSet byte as unit", + NULL +}; + +int cmd_device_disk_usage(int argc, char **argv) +{ + + int flags = DF_HUMAN_UNIT; + int i, more_than_one = 0; + + optind = 1; + while (1) { + char c = getopt(argc, argv, "b"); + + if (c < 0) + break; + + switch (c) { + case ''b'': + flags &= ~DF_HUMAN_UNIT; + break; + default: + usage(cmd_device_disk_usage_usage); + } + } + + if (check_argc_min(argc - optind, 1)) { + usage(cmd_device_disk_usage_usage); + return 21; + } + + for (i = optind; i < argc ; i++) { + int r, fd; + if (more_than_one) + printf("\n"); + + fd = open_file_or_dir(argv[i]); + if (fd < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", + argv[1]); + return 12; + } + r = _cmd_device_disk_usage(fd, argv[i], flags); + close(fd); + + if (r) + return r; + more_than_one = 1; + + } + + return 0; +} + diff --git a/cmds-fi-disk_usage.h b/cmds-fi-disk_usage.h index ae11570..c315004 100644 --- a/cmds-fi-disk_usage.h +++ b/cmds-fi-disk_usage.h @@ -21,7 +21,11 @@ extern const char * const cmd_filesystem_df_usage[]; int cmd_filesystem_df(int argc, char **argv); + extern const char * const cmd_filesystem_disk_usage_usage[]; int cmd_filesystem_disk_usage(int argc, char **argv); +extern const char * const cmd_device_disk_usage_usage[]; +int cmd_device_disk_usage(int argc, char **argv); + #endif -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-18 21:04 UTC
[PATCH 8/8] Create a new entry in btrfs man page for btrfs device disk-usage.
Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> --- man/btrfs.8.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/man/btrfs.8.in b/man/btrfs.8.in index 50dc510..e60c81f 100644 --- a/man/btrfs.8.in +++ b/man/btrfs.8.in @@ -46,6 +46,8 @@ btrfs \- control a btrfs filesystem .PP \fBbtrfs\fP \fBdevice delete\fP\fI <device> [<device>...] <path> \fP .PP +\fBbtrfs\fP \fBdevice disk-usage\fP\fI [-b] <path> [<path>...] \fP +.PP \fBbtrfs\fP \fBreplace start\fP \fI[-Bfr] <srcdev>|<devid> <targetdev> <path>\fP .PP \fBbtrfs\fP \fBreplace status\fP \fI[-1] <path>\fP @@ -360,6 +362,12 @@ Add device(s) to the filesystem identified by \fI<path>\fR. Remove device(s) from a filesystem identified by \fI<path>\fR. .TP +\fBdevice disk-usage\fR\fI [-b] <path> [<path>..] <path>\fR +Show which chunks are in a device. + +\fB-b\fP set byte as unit. +.TP + \fBdevice scan\fR \fI[--all-devices|<device> [<device>...]\fR If one or more devices are passed, these are scanned for a btrfs filesystem. If no devices are passed, \fBbtrfs\fR scans all the block devices listed -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Zach Brown
2013-Feb-18 23:08 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
On Mon, Feb 18, 2013 at 10:04:26PM +0100, Goffredo Baroncelli wrote:> This patch adds some helpers to manage the strings allocation and > deallocation. > The function string_list_add(char *) adds the passed string to a list; > the function string_list_free() frees all the strings together.Please don''t do this. To verify that a given pointer isn''t freed before it''s used we''d have to make sure that there are no string_list_free() calls in the interim that would hit their pointer on this global list. As far as I can tell, this is only used for the pretty units? Instead of printf("%s", leaked_string(raw)); how about printf("%llu%s", scaled_value(raw), static_unit_str(raw)); That''d avoid the need to pass back arbitrary allocated strings and this code could go away.> + if (!strings_to_free) { > + fprintf(stderr, "add_string_to_free(): Not enough memory\n"); > + strings_to_free = 0;if (a == 0) a = 0?> + count_string_to_free = 0; > + } > + > + strings_to_free[count_string_to_free-1] = s;NULL[-1] = s? - z -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-19 07:07 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
On 02/19/2013 12:08 AM, Zach Brown wrote:> On Mon, Feb 18, 2013 at 10:04:26PM +0100, Goffredo Baroncelli wrote: >> This patch adds some helpers to manage the strings allocation and >> deallocation. >> The function string_list_add(char *) adds the passed string to a list; >> the function string_list_free() frees all the strings together. > > Please don''t do this. To verify that a given pointer isn''t freed before > it''s used we''d have to make sure that there are no string_list_free() > calls in the interim that would hit their pointer on this global list.The idea is that the code shouldn''t care about do deallocate the strings until finishing its jobs. At the end it calls string_list_free(). Of course, if after string_list_free() some dynamically allocated strings are used then bad things could happen. Ideally string_list_free() should be called at the end of the main. I don''t think that btrfs-progs allocates an huge quantity of string, so this could be an acceptable behaviour.> > As far as I can tell, this is only used for the pretty units? Instead > of > printf("%s", leaked_string(raw)); > > how about > > printf("%llu%s", scaled_value(raw), static_unit_str(raw)); > > That''d avoid the need to pass back arbitrary allocated strings and this > code could go away.Sorry I don''t understand the differences between {leaked, scaled, raw}_string. Could you elaborate a bit ?> >> + if (!strings_to_free) { >> + fprintf(stderr, "add_string_to_free(): Not enough memory\n"); >> + strings_to_free = 0; > > if (a == 0) a = 0? > >> + count_string_to_free = 0; >> + } >> + >> + strings_to_free[count_string_to_free-1] = s; > > NULL[-1] = s?Right, I will correct soon.> > - z > -- > To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html >-- gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it> Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Zach Brown
2013-Feb-19 17:21 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
> Of course, if after string_list_free() some dynamically allocated > strings are used then bad things could happen.Right. So let''s not make that even possible by not having the code at all.> Sorry I don''t understand the differences between {leaked, scaled, > raw}_string. Could you elaborate a bit ?The code I saw returned an allocated string that the caller has to worry about. Crummy code just ignores the problem. You added the global list of strings to free at some point in the future. I''m suggesting it not allocate at all so that there''s nothing to free. Instead of: printf("%s", pretty(value)); char *pretty(u64 value) { static char *units[] = { "KB", "MB", /* etc */ }; char *str = malloc(20); /* should be 21 */ sprintf(str, "%llu%s", scale(value), units[scale_index(value)); global_list_stuff(str); return str; } Do: printf("%llu%s", scale(value), unit_string(value)); char *unit(u64 value) { static char *units[] = { "KB", "MB", /* etc */ }; return units[scale_index(value)); } (all rough code, obviously) Then there''s nothing for the caller to worry about. Right? - z -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-19 17:44 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
On 02/19/2013 06:21 PM, Zach Brown wrote:>> Of course, if after string_list_free() some dynamically allocated >> strings are used then bad things could happen. > > Right. So let''s not make that even possible by not having the code at > all. > >> Sorry I don''t understand the differences between {leaked, scaled, >> raw}_string. Could you elaborate a bit ? > > The code I saw returned an allocated string that the caller has to worry > about. Crummy code just ignores the problem. You added the global list > of strings to free at some point in the future. > > I''m suggesting it not allocate at all so that there''s nothing to free. > > Instead of: > > printf("%s", pretty(value)); > > char *pretty(u64 value) { > static char *units[] = { "KB", "MB", /* etc */ }; > char *str = malloc(20); /* should be 21 */ > sprintf(str, "%llu%s", > scale(value), units[scale_index(value)); > global_list_stuff(str); > return str; > } > > Do: > > printf("%llu%s", scale(value), unit_string(value)); > > char *unit(u64 value) > { > static char *units[] = { "KB", "MB", /* etc */ }; > return units[scale_index(value)); > } > > (all rough code, obviously) > > Then there''s nothing for the caller to worry about.Sorry but this is very dangerous and it leads to very subtle bug: what happens if someone wrote: printf("%d%s - %d%s\n", scale(123), unit_string(123), scale(123), unit_string(456) );> > Right? > > - z >-- gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it> Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Zach Brown
2013-Feb-19 17:59 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
> Sorry but this is very dangerous and it leads to very subtle bug: what > happens if someone wrote:"very dangerous"? I disagree. If you''re worried about the value and units getting out of sync then you offer some helper macros:> printf("%d%s - %d%s\n", scale(123), unit_string(123), > scale(123), unit_string(456) );#define pty_fmt "%llu%s" #define pty_arg(v) scale(v), unit_string(v) printf(pty_fmt, pty_arg(v)); The kernel used to print ipv4 addresses like this before it grew its own %pI4 format specifier. - z -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-19 21:28 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
On 02/19/2013 06:59 PM, Zach Brown wrote:>> Sorry but this is very dangerous and it leads to very subtle bug: what >> happens if someone wrote: > > "very dangerous"? I disagree. > > If you''re worried about the value and units getting out of sync then you > offer some helper macros:No my complaint was related to another thing; but I was wrong. Anyway your suggestion is not so easy applicable, and (IMHO) I don''t see any difficulty to remember to call string_list_free() only at the end of the code.> >> printf("%d%s - %d%s\n", scale(123), unit_string(123), >> scale(123), unit_string(456) ); > > #define pty_fmt "%llu%s" > #define pty_arg(v) scale(v), unit_string(v) > > printf(pty_fmt, pty_arg(v)); > > The kernel used to print ipv4 addresses like this before it grew its own > %pI4 format specifier. > > - z >-- gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it> Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Zach Brown
2013-Feb-19 21:40 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
> No my complaint was related to another thing; but I was wrong. Anyway > your suggestion is not so easy applicable, and (IMHO) I don''t see any > difficulty to remember to call string_list_free() only at the end of the > code.Understood, and I disagree. It''s simply not needed. - z -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html