Goffredo Baroncelli
2013-Mar-10 12:17 UTC
[PATCH V3][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
Hi all, This is the third attempt of my patches related to show how the data are stored in a btrfs filesystem. I rebased all the patches on the latest mason git. I tried to address the Zach concern abou the using of the string_list_add() in the df_pretty_sizes(): string_list_add() is removed from the df_pretty_sizes() and I created the new function sla_pretty_sizes() which calls df_pretty_sizes() and string_list_add(). Unfortunately I noticed a regression which passed all the reviews until now: the command btrfs fi df previous didn''t require the root capability, now with my patches it is required, because I need to know some info about the chunks so I need to use the "BTRFS_IOC_TREE_SEARCH". I think that there are the following possibilities: 1) accept this regresssion 2) remove the command "btrfs fi df" and leave only "btrfs fi disk-usage" and "btrfs dev disk-usage" 3) adding a new ioctl which could be used without root capability. Of course this ioctl would return only a subset of the BTRFS_IOC_TREE_SEARCH info I think that the 3) would be the "long term" solution. I am not happy about the 1), so as "short term solution" I think that we should go with the 2). What do you think ? Below the description of the patches. -- 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 Changelog: v1 2013/02/18 First issue v2 2013/02/23 Fix uncorrct "not enough memory" handling in patch #1. Thanks to Zac to highlight it. v3 2013/03/10 - Rebased on the latest mason git repository - Small fix in the comment - Increase the buffer in the function df_pretty_sizes() to avoid buffer pverwflow (thanks to Zach to highlight it) - adding the sla_pretty_sizes() function to remove the call of string_list_add() from df_pretty_sizes. -- 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-Mar-10 12:17 UTC
[PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
From: Goffredo Baroncelli <kreijack@inwind.it> 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 | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ string_list.h | 23 ++++++++++++++++++++ 3 files changed, 90 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..f840048 --- /dev/null +++ b/string_list.c @@ -0,0 +1,65 @@ +/* + * 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 "string_list.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 + */ +char *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"); + count_string_to_free = 0; + return NULL; + } + + strings_to_free[count_string_to_free-1] = s; + return 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..fdc027d --- /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 + +char *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-Mar-10 12:17 UTC
[PATCH 2/8] Enhance the command btrfs filesystem df.
From: Goffredo Baroncelli <kreijack@inwind.it> 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 | 530 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmds-fi-disk_usage.h | 25 +++ cmds-filesystem.c | 125 +----------- ctree.h | 17 +- utils.c | 14 ++ utils.h | 2 + 7 files changed, 589 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..50b2fae --- /dev/null +++ b/cmds-fi-disk_usage.c @@ -0,0 +1,530 @@ +/* + * 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; + u64 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(21); + if (!s) + return NULL; + sprintf(s, "%llu", size); + } + + return s; +} + +/* + * This function is like the one above, only it passes the string buffer + * to the string_list_add function to be able to free all the strings togheter + * with the string_list_free() function + */ +static char *sla_pretty_sizes(u64 size, int mode) +{ + return string_list_add(df_pretty_sizes(size,mode)); +} + +/* + * 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, + sla_pretty_sizes(total_disk, mode)); + printf("Disk allocated:\t\t%*s\n", width, + sla_pretty_sizes(total_chunks, mode)); + printf("Disk unallocated:\t%*s\n", width, + sla_pretty_sizes(total_disk-total_chunks, mode)); + printf("Used:\t\t\t%*s\n", width, + sla_pretty_sizes(total_used, mode)); + printf("Free (Estimated):\t%*s\t(Max: %s, min: %s)\n", + width, + sla_pretty_sizes((u64)(K*total_disk-total_used), mode), + sla_pretty_sizes(total_disk-total_chunks+total_free, mode), + sla_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-Mar-10 12:17 UTC
[PATCH 3/8] Create the man page entry for the command btrfs fi df
From: Goffredo Baroncelli <kreijack@inwind.it> 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-Mar-10 12:17 UTC
[PATCH 4/8] Add helpers functions to handle the printing of data in tabular format.
From: Goffredo Baroncelli <kreijack@inwind.it> 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..7efff87 --- /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 on 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-Mar-10 12:17 UTC
[PATCH 5/8] Add command btrfs filesystem disk-usage
From: Goffredo Baroncelli <kreijack@inwind.it> Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> --- cmds-fi-disk_usage.c | 432 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmds-fi-disk_usage.h | 3 + cmds-filesystem.c | 2 + utils.c | 58 +++++++ utils.h | 3 + 5 files changed, 498 insertions(+) diff --git a/cmds-fi-disk_usage.c b/cmds-fi-disk_usage.c index 50b2fae..cb680e6 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" @@ -45,6 +47,13 @@ struct chunk_info { u64 num_stripes; }; +/* to store information about the disks */ +struct disk_info { + u64 devid; + char path[BTRFS_DEVICE_PATH_NAME_MAX]; + u64 size; +}; + /* * Pretty print the size */ @@ -528,3 +537,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", sla_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", sla_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", + sla_pretty_sizes(sargs->spaces[i].total_bytes, mode)); + + table_printf(matrix, sargs->total_spaces+1, disks_info_count+4, + ">%s", sla_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", + sla_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 = sla_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 = sla_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, + sla_pretty_sizes(sargs->spaces[i].total_bytes , + mode), + sla_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..c7459b1 100644 --- a/cmds-fi-disk_usage.h +++ b/cmds-fi-disk_usage.h @@ -22,4 +22,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-Mar-10 12:17 UTC
[PATCH 6/8] Create entry in man page for btrfs filesystem disk-usage
From: Goffredo Baroncelli <kreijack@inwind.it> 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
From: Goffredo Baroncelli <kreijack@inwind.it> Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> --- cmds-device.c | 3 ++ cmds-fi-disk_usage.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmds-fi-disk_usage.h | 3 ++ 3 files changed, 147 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 cb680e6..f64e483 100644 --- a/cmds-fi-disk_usage.c +++ b/cmds-fi-disk_usage.c @@ -959,4 +959,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 = sla_pretty_sizes(size, mode); + printf(" %s,%s:%*s%10s\n", + description, + r_mode, + (int)(20 - strlen(description) - strlen(r_mode)), "", + s); + + allocated += size; + + } + s = sla_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 = sla_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 c7459b1..c315004 100644 --- a/cmds-fi-disk_usage.h +++ b/cmds-fi-disk_usage.h @@ -25,4 +25,7 @@ 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-Mar-10 12:17 UTC
[PATCH 8/8] Create a new entry in btrfs man page for btrfs device disk-usage.
From: Goffredo Baroncelli <kreijack@inwind.it> 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
Martin Steigerwald
2013-Mar-10 13:16 UTC
Re: [PATCH V3][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
Am Sonntag, 10. März 2013 schrieb Goffredo Baroncelli:> Hi all,Hi Goffredo,> This is the third attempt of my patches related to show how the data > are stored in a btrfs filesystem. I rebased all the patches on the latest > mason git. I tried to address the Zach concern abou the using of > the string_list_add() in the df_pretty_sizes(): string_list_add() is > removed from the df_pretty_sizes() and I created the new function > sla_pretty_sizes() which calls df_pretty_sizes() and string_list_add().Thanks for your new round of patches.> Unfortunately I noticed a regression which passed all the reviews until > now: the command btrfs fi df previous didn''t require the root > capability, now with my patches it is required, because I need to know > some info about the chunks so I need to use the "BTRFS_IOC_TREE_SEARCH". > > I think that there are the following possibilities: > 1) accept this regresssion > 2) remove the command "btrfs fi df" and leave only "btrfs fi disk-usage" > and "btrfs dev disk-usage" > 3) adding a new ioctl which could be used without root capability. Of > course this ioctl would return only a subset of the > BTRFS_IOC_TREE_SEARCH info > > I think that the 3) would be the "long term" solution. I am not happy > about the 1), so as "short term solution" I think that we should go with > the 2). What do you think ?Uhm, but exactly the new btrfs fi df contains a good overview:> Below the description of the patches. > > -- > > 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).What information fi df can´t display without root permissions? Maybe its okay to just omit it for now if being run as user or display a "run as root" hint instead? Thanks, -- Martin ''Helios'' Steigerwald - http://www.Lichtvoll.de GPG: 03B0 0D6C 0040 0710 4AFA B82F 991B EAAC A599 84C7 -- 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
Martin Steigerwald
2013-Mar-10 13:19 UTC
Re: [PATCH V3][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
Am Sonntag, 10. März 2013 schrieb Martin Steigerwald:> Am Sonntag, 10. März 2013 schrieb Goffredo Baroncelli: > > Hi all, > > Hi Goffredo, > > > This is the third attempt of my patches related to show how the data > > are stored in a btrfs filesystem. I rebased all the patches on the > > latest mason git. I tried to address the Zach concern abou the using > > of the string_list_add() in the df_pretty_sizes(): string_list_add() > > is removed from the df_pretty_sizes() and I created the new function > > sla_pretty_sizes() which calls df_pretty_sizes() and > > string_list_add(). > > Thanks for your new round of patches.My MTA returned on my first answer to you: <goffredo.baroncelli@yahoo.com>: host mta5.am0.yahoodns.net[74.6.136.244] said: 554 delivery error: dd This user doesn''t have a yahoo.com account (goffredo.baroncelli@yahoo.com) [-5] - mta1233.mail.sk1.yahoo.com (in reply to end of DATA command) Replying to list cause obviously I cannot reach your personal mail address right now. Thanks, -- Martin ''Helios'' Steigerwald - http://www.Lichtvoll.de GPG: 03B0 0D6C 0040 0710 4AFA B82F 991B EAAC A599 84C7 -- 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
Wang Shilong
2013-Mar-10 14:34 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
Hello,> From: Goffredo Baroncelli <kreijack@inwind.it> > > 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 | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > string_list.h | 23 ++++++++++++++++++++ > 3 files changed, 90 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..f840048 > --- /dev/null > +++ b/string_list.c > @@ -0,0 +1,65 @@ > +/* > + * 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 "string_list.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 > + */ > +char *string_list_add(char *s) > +{ > + int size; > +I''d prefer to have a check here firstly, like: if (!s) return s; Since this function is called directly without any check about ''char *s'' in your next patch.. Thanks, Wang> + 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"); > + count_string_to_free = 0; > + return NULL; > + } > + > + strings_to_free[count_string_to_free-1] = s; > + return 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..fdc027d > --- /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 > + > +char *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-- 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-Mar-10 14:51 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
On 03/10/2013 03:34 PM, Wang Shilong wrote:> Hello,[...]>> + >> +/* >> + * Add a string to the dynamic allocated string list >> + */ >> +char *string_list_add(char *s) >> +{ >> + int size; >> + > > I''d prefer to have a check here firstly, like: > if (!s) > return s; > > Since this function is called directly without any check about ''char *s'' > in your next patch.. > > Thanks, > WangThanks for the review. However this check is not mandatory. Even if s == null, we store a null pointer which is meaningless but not dangerous because it is legal to free() a null pointer. Form man 3 free: void free(void *ptr); [...] If ptr is NULL, no operation is performed. In a next submit I will add your suggestions.> >> + 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"); >> + count_string_to_free = 0; >> + return NULL; >> + } >> + >> + strings_to_free[count_string_to_free-1] = s; >> + return 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..fdc027d >> --- /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 >> + >> +char *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 > >-- 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
Wang Shilong
2013-Mar-10 15:00 UTC
Re: [PATCH 2/8] Enhance the command btrfs filesystem df.
Hello,> From: Goffredo Baroncelli <kreijack@inwind.it> > > 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 | 530 ++++++++++++++++++++++++++++++++++++++++++++++++++ > cmds-fi-disk_usage.h | 25 +++ > cmds-filesystem.c | 125 +----------- > ctree.h | 17 +- > utils.c | 14 ++ > utils.h | 2 + > 7 files changed, 589 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..50b2fae > --- /dev/null > +++ b/cmds-fi-disk_usage.c > @@ -0,0 +1,530 @@ > +/* > + * 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; > + u64 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(21);I don''t really like ''21'' here...> + if (!s) > + return NULL; > + sprintf(s, "%llu", size); > + } > + > + return s; > +} > + > +/* > + * This function is like the one above, only it passes the string buffer > + * to the string_list_add function to be able to free all the strings togheter > + * with the string_list_free() function > + */ > +static char *sla_pretty_sizes(u64 size, int mode) > +{ > + return string_list_add(df_pretty_sizes(size,mode)); > +} > + > +/* > + * 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);why not u64 for num_stripes here?> + int j; > + > + for (j = 0 ; j < num_stripes ; j++) { > + int i; > + struct chunk_info *p = 0; > + struct btrfs_stripe *stripe; > + u64 devid;It is better to move all these declarations to the start of the function… and there are many places..you declare or assign several values in the one line, i think it is not good coding styles ^_^> + > + 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");a memory leak here.. free(*info_count)> + 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, > + sla_pretty_sizes(total_disk, mode)); > + printf("Disk allocated:\t\t%*s\n", width, > + sla_pretty_sizes(total_chunks, mode)); > + printf("Disk unallocated:\t%*s\n", width, > + sla_pretty_sizes(total_disk-total_chunks, mode)); > + printf("Used:\t\t\t%*s\n", width, > + sla_pretty_sizes(total_used, mode)); > + printf("Free (Estimated):\t%*s\t(Max: %s, min: %s)\n", > + width, > + sla_pretty_sizes((u64)(K*total_disk-total_used), mode), > + sla_pretty_sizes(total_disk-total_chunks+total_free, mode), > + sla_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-- 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-Mar-10 15:52 UTC
Re: [PATCH V3][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
Hi Martin, On 03/10/2013 02:16 PM, Martin Steigerwald wrote:> Am Sonntag, 10. März 2013 schrieb Goffredo Baroncelli: >> Hi all, > > Hi Goffredo, > >> This is the third attempt of my patches related to show how the data >> are stored in a btrfs filesystem. I rebased all the patches on the latest >> mason git. I tried to address the Zach concern abou the using of >> the string_list_add() in the df_pretty_sizes(): string_list_add() is >> removed from the df_pretty_sizes() and I created the new function >> sla_pretty_sizes() which calls df_pretty_sizes() and string_list_add(). > > Thanks for your new round of patches. > >> Unfortunately I noticed a regression which passed all the reviews until >> now: the command btrfs fi df previous didn''t require the root >> capability, now with my patches it is required, because I need to know >> some info about the chunks so I need to use the "BTRFS_IOC_TREE_SEARCH". >> >> I think that there are the following possibilities: >> 1) accept this regresssion >> 2) remove the command "btrfs fi df" and leave only "btrfs fi disk-usage" >> and "btrfs dev disk-usage" >> 3) adding a new ioctl which could be used without root capability. Of >> course this ioctl would return only a subset of the >> BTRFS_IOC_TREE_SEARCH info >> >> I think that the 3) would be the "long term" solution. I am not happy >> about the 1), so as "short term solution" I think that we should go with >> the 2). What do you think ? > > Uhm, but exactly the new btrfs fi df contains a good overview: > >> Below the description of the patches. >> >> -- >> >> 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). > > What information fi df can´t display without root permissions? Maybe its > okay to just omit it for now if being run as user or display a "run as root" > hint instead?I need the root permission to know how many stripes the raid5/6 chunks are allocated. For the RAID1/RAID10/RAID0 the computation of the disk usage by the stripes was easy. I need to multiply the field " total_bytes" of the struct btrfs_ioctl_space_args by a factor depending by the kind of RAID (for example for RAID1 this factor is two: the disk space used is two times the space available). Instead for RAID5/6 this value depends by the number of disk at "the time of the chunk creation". If the chunk was created in a RAID5 with 4 disks, the ratio space available/disk space used is 3/4. If I add another disk and don''t perform a balance, for the old chunk the ration is 3/4 for a new chunk the ratio become 4/5. To got this information I need to retrieve the chunk info using the BTRFS_IOC_TREE_SEARCH ioctl. GB -- 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-Mar-10 15:59 UTC
Re: [PATCH V3][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
On 03/10/2013 02:19 PM, Martin Steigerwald wrote:> Am Sonntag, 10. März 2013 schrieb Martin Steigerwald: >> Am Sonntag, 10. März 2013 schrieb Goffredo Baroncelli: >>> Hi all, >> >> Hi Goffredo, >> >>> This is the third attempt of my patches related to show how the data >>> are stored in a btrfs filesystem. I rebased all the patches on the >>> latest mason git. I tried to address the Zach concern abou the using >>> of the string_list_add() in the df_pretty_sizes(): string_list_add() >>> is removed from the df_pretty_sizes() and I created the new function >>> sla_pretty_sizes() which calls df_pretty_sizes() and >>> string_list_add(). >> >> Thanks for your new round of patches. > > My MTA returned on my first answer to you: > > <goffredo.baroncelli@yahoo.com>: host mta5.am0.yahoodns.net[74.6.136.244] > said: > 554 delivery error: dd This user doesn''t have a yahoo.com account > (goffredo.baroncelli@yahoo.com) [-5] - mta1233.mail.sk1.yahoo.com (in > reply > to end of DATA command) > > Replying to list cause obviously I cannot reach your personal mail address > right now.It seems that yahoo doesn''t recognise my email "goffredo.baroncelli@yahoo.com"; the strange thing is that I use this user for the authentication !> > Thanks,GB -- 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
Bart Noordervliet
2013-Mar-21 15:27 UTC
Re: [PATCH V3][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
Hi Goffredo, On Sun, Mar 10, 2013 at 1:17 PM, Goffredo Baroncelli <goffredo.baroncelli@yahoo.com> wrote:> Unfortunately I noticed a regression which passed all the reviews until now: > the command btrfs fi df previous didn''t require the root capability, > now with my patches it is required, because I need to know some info > about the chunks so I need to use the "BTRFS_IOC_TREE_SEARCH". > > I think that there are the following possibilities: > 1) accept this regresssion > 2) remove the command "btrfs fi df" and leave only "btrfs fi disk-usage" and > "btrfs dev disk-usage" > 3) adding a new ioctl which could be used without root capability. Of course > this ioctl would return only a subset of the BTRFS_IOC_TREE_SEARCH infoI''d like to vote for option 1) here. "btrfs fi df" gives useful, condensed information that I can''t get from the others at first glance. I''d argue that many read-only, informational functions of the btrfs utility already require root privileges even where that might not be strictly necessary from a security point of view. I''d also like to bump this thread in the hope that, if no one else steps up with new, specific problems, we can now merge this patchset. We can keep bikeshedding about the display details forever, but I really think this is a significant improvement over what we had previously. Regards, Bart -- 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