Goffredo Baroncelli
2013-Feb-23 13:46 UTC
[PATCH V2][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
Hi all, I updates my previous patches [1] to add support for raid5/6. These patches update the btrfs fi df command and add two new commands: - btrfs filesystem disk-usage <path> - btrfs device disk-usage <path> The command "btrfs filesystem df" now shows only the disk usage/available. $ sudo btrfs filesystem df /mnt/btrfs1/ Disk size: 400.00GB Disk allocated: 8.04GB Disk unallocated: 391.97GB Used: 11.29MB Free (Estimated): 250.45GB (Max: 396.99GB, min: 201.00GB) Data to disk ratio: 63 % The "Free (Estimated)" tries to give an estimation of the free space on the basis of the chunks usage. Max and min are the maximum allowable space (if the next chunk are allocated as SINGLE) or the minimum one ( if the next chunks are allocated as DUP/RAID1/RAID10). The other two commands show the chunks in the disks. $ sudo btrfs filesystem disk-usage /mnt/btrfs1/ Data,Single: Size:8.00MB, Used:0.00 /dev/vdb 8.00MB Data,RAID6: Size:2.00GB, Used:11.25MB /dev/vdb 1.00GB /dev/vdc 1.00GB /dev/vdd 1.00GB /dev/vde 1.00GB Metadata,Single: Size:8.00MB, Used:0.00 /dev/vdb 8.00MB Metadata,RAID5: Size:3.00GB, Used:36.00KB /dev/vdb 1.00GB /dev/vdc 1.00GB /dev/vdd 1.00GB /dev/vde 1.00GB System,Single: Size:4.00MB, Used:0.00 /dev/vdb 4.00MB System,RAID5: Size:12.00MB, Used:4.00KB /dev/vdb 4.00MB /dev/vdc 4.00MB /dev/vdd 4.00MB /dev/vde 4.00MB Unallocated: /dev/vdb 97.98GB /dev/vdc 98.00GB /dev/vdd 98.00GB /dev/vde 98.00GB or in tabular format $ sudo ./btrfs filesystem disk-usage -t /mnt/btrfs1/ Data Data Metadata Metadata System System Single RAID6 Single RAID5 Single RAID5 Unallocated /dev/vdb 8.00MB 1.00GB 8.00MB 1.00GB 4.00MB 4.00MB 97.98GB /dev/vdc - 1.00GB - 1.00GB - 4.00MB 98.00GB /dev/vdd - 1.00GB - 1.00GB - 4.00MB 98.00GB /dev/vde - 1.00GB - 1.00GB - 4.00MB 98.00GB ====== ======= ======== ======== ====== ======= ==========Total 8.00MB 2.00GB 8.00MB 3.00GB 4.00MB 12.00MB 391.97GB Used 0.00 11.25MB 0.00 36.00KB 0.00 4.00KB These are the most complete output, where it is possible to know which disk a chunk uses and the usage of every chunk. Finally the last command shows which chunks a disk hosts: $ sudo ./btrfs device disk-usage /mnt/btrfs1/ /dev/vdb 100.00GB Data,Single: 8.00MB Data,RAID6: 1.00GB Metadata,Single: 8.00MB Metadata,RAID5: 1.00GB System,Single: 4.00MB System,RAID5: 4.00MB Unallocated: 97.98GB /dev/vdc 100.00GB Data,RAID6: 1.00GB Metadata,RAID5: 1.00GB System,RAID5: 4.00MB Unallocated: 98.00GB /dev/vdd 100.00GB Data,RAID6: 1.00GB Metadata,RAID5: 1.00GB System,RAID5: 4.00MB Unallocated: 98.00GB /dev/vde 100.00GB Data,RAID6: 1.00GB Metadata,RAID5: 1.00GB System,RAID5: 4.00MB Unallocated: 98.00GB More or less are the same information above, only grouped by disk. Unfortunately I don''t have any information about the chunk usage per disk basis. Comments are welcome. The code is pullable from http://cassiopea.homelinux.net/git/btrfs-progs-unstable.git branch df-du-raid56 BR G.Baroncelli [1] http://permalink.gmane.org/gmane.comp.file-systems.btrfs/21071 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. -- gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it> Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-23 13:46 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 | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ string_list.h | 23 +++++++++++++++++++++ 3 files changed, 88 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..8d8cc1f --- /dev/null +++ b/string_list.c @@ -0,0 +1,63 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* To store the strings */ +static void **strings_to_free; +static int count_string_to_free; + +/* + * Add a string to the dynamic allocated string list + */ +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..f974fbc --- /dev/null +++ b/string_list.h @@ -0,0 +1,23 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#ifndef STRING_LIST_H +#define STRING_LIST_H + +void string_list_add(char *s); +void string_list_free(); + +#endif -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-23 13:46 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 | 520 ++++++++++++++++++++++++++++++++++++++++++++++++++ cmds-fi-disk_usage.h | 25 +++ cmds-filesystem.c | 125 +----------- ctree.h | 17 +- utils.c | 14 ++ utils.h | 2 + 7 files changed, 579 insertions(+), 126 deletions(-) create mode 100644 cmds-fi-disk_usage.c create mode 100644 cmds-fi-disk_usage.h diff --git a/Makefile b/Makefile index 0d6c43a..bd792b6 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \ string_list.o cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \ cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \ - cmds-quota.o cmds-qgroup.o cmds-replace.o + cmds-quota.o cmds-qgroup.o cmds-replace.o cmds-fi-disk_usage.o CHECKFLAGS= -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \ -Wuninitialized -Wshadow -Wundef diff --git a/cmds-fi-disk_usage.c b/cmds-fi-disk_usage.c new file mode 100644 index 0000000..1e3589f --- /dev/null +++ b/cmds-fi-disk_usage.c @@ -0,0 +1,520 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <errno.h> + +#include "utils.h" +#include "kerncompat.h" +#include "ctree.h" +#include "string_list.h" + +#include "commands.h" + +#include "version.h" + +#define DF_HUMAN_UNIT (1<<0) + +/* + * To store the size information about the chunks: + * the chunks info are grouped by the tuple (type, devid, num_stripes), + * i.e. if two chunks are of the same type (RAID1, DUP...), are on the + * same disk, have the same stripes then their sizes are grouped + */ +struct chunk_info { + u64 type; + u64 size; + u64 devid; + int num_stripes; +}; + +/* + * Pretty print the size + */ +static char *df_pretty_sizes(u64 size, int mode) +{ + char *s; + + if (mode & DF_HUMAN_UNIT) { + s = pretty_sizes(size); + if (!s) + return NULL; + } else { + s = malloc(20); + if (!s) + return NULL; + sprintf(s, "%llu", size); + } + + string_list_add(s); + return s; +} + +/* + * Add the chunk info to the chunk_info list + */ +static int add_info_to_list(struct chunk_info **info_ptr, + int *info_count, + struct btrfs_chunk *chunk) +{ + + u64 type = btrfs_stack_chunk_type(chunk); + u64 size = btrfs_stack_chunk_length(chunk); + int num_stripes = btrfs_stack_chunk_num_stripes(chunk); + int j; + + for (j = 0 ; j < num_stripes ; j++) { + int i; + struct chunk_info *p = 0; + struct btrfs_stripe *stripe; + u64 devid; + + stripe = btrfs_stripe_nr(chunk, j); + devid = btrfs_stack_stripe_devid(stripe); + + for (i = 0 ; i < *info_count ; i++) + if ((*info_ptr)[i].type == type && + (*info_ptr)[i].devid == devid && + (*info_ptr)[i].num_stripes == num_stripes ) { + p = (*info_ptr) + i; + break; + } + + if (!p) { + int size = sizeof(struct btrfs_chunk) * (*info_count+1); + struct chunk_info *res = realloc(*info_ptr, size); + + if (!res) { + fprintf(stderr, "ERROR: not enough memory\n"); + return -1; + } + + *info_ptr = res; + p = res + *info_count; + (*info_count)++; + + p->devid = devid; + p->type = type; + p->size = 0; + p->num_stripes = num_stripes; + } + + p->size += size; + + } + + return 0; + +} + +/* + * Helper to sort the chunk type + */ +static int cmp_chunk_block_group(u64 f1, u64 f2) +{ + + u64 mask; + + if ((f1 & BTRFS_BLOCK_GROUP_TYPE_MASK) =+ (f2 & BTRFS_BLOCK_GROUP_TYPE_MASK)) + mask = BTRFS_BLOCK_GROUP_PROFILE_MASK; + else if (f2 & BTRFS_BLOCK_GROUP_SYSTEM) + return -1; + else if (f1 & BTRFS_BLOCK_GROUP_SYSTEM) + return +1; + else + mask = BTRFS_BLOCK_GROUP_TYPE_MASK; + + if ((f1 & mask) > (f2 & mask)) + return +1; + else if ((f1 & mask) < (f2 & mask)) + return -1; + else + return 0; +} + +/* + * Helper to sort the chunk + */ +static int cmp_chunk_info(const void *a, const void *b) +{ + return cmp_chunk_block_group( + ((struct chunk_info *)a)->type, + ((struct chunk_info *)b)->type); +} + +/* + * This function load all the chunk info from the ''fd'' filesystem + */ +static int load_chunk_info(int fd, + struct chunk_info **info_ptr, + int *info_count) +{ + + int ret; + struct btrfs_ioctl_search_args args; + struct btrfs_ioctl_search_key *sk = &args.key; + struct btrfs_ioctl_search_header *sh; + unsigned long off = 0; + int i, e; + + + memset(&args, 0, sizeof(args)); + + /* + * there may be more than one ROOT_ITEM key if there are + * snapshots pending deletion, we have to loop through + * them. + */ + + + sk->tree_id = BTRFS_CHUNK_TREE_OBJECTID; + + sk->min_objectid = 0; + sk->max_objectid = (u64)-1; + sk->max_type = 0; + sk->min_type = (u8)-1; + sk->min_offset = 0; + sk->max_offset = (u64)-1; + sk->min_transid = 0; + sk->max_transid = (u64)-1; + sk->nr_items = 4096; + + while (1) { + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); + e = errno; + if (ret < 0) { + fprintf(stderr, + "ERROR: can''t perform the search - %s\n", + strerror(e)); + return -99; + } + /* the ioctl returns the number of item it found in nr_items */ + + if (sk->nr_items == 0) + break; + + off = 0; + for (i = 0; i < sk->nr_items; i++) { + struct btrfs_chunk *item; + sh = (struct btrfs_ioctl_search_header *)(args.buf + + off); + + off += sizeof(*sh); + item = (struct btrfs_chunk *)(args.buf + off); + + if (add_info_to_list(info_ptr, info_count, item)) { + *info_ptr = 0; + free(*info_ptr); + return -100; + } + + off += sh->len; + + sk->min_objectid = sh->objectid; + sk->min_type = sh->type; + sk->min_offset = sh->offset+1; + + } + if (!sk->min_offset) /* overflow */ + sk->min_type++; + else + continue; + + if (!sk->min_type) + sk->min_objectid++; + else + continue; + + if (!sk->min_objectid) + break; + } + + qsort(*info_ptr, *info_count, sizeof(struct chunk_info), + cmp_chunk_info); + + return 0; + +} + +/* + * Helper to sort the struct btrfs_ioctl_space_info + */ +static int cmp_btrfs_ioctl_space_info(const void *a, const void *b) +{ + return cmp_chunk_block_group( + ((struct btrfs_ioctl_space_info *)a)->flags, + ((struct btrfs_ioctl_space_info *)b)->flags); +} + +/* + * This function load all the information about the space usage + */ +static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path) +{ + struct btrfs_ioctl_space_args *sargs = 0, *sargs_orig = 0; + int e, ret, count; + + sargs_orig = sargs = malloc(sizeof(struct btrfs_ioctl_space_args)); + if (!sargs) { + fprintf(stderr, "ERROR: not enough memory\n"); + return NULL; + } + + sargs->space_slots = 0; + sargs->total_spaces = 0; + + ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); + e = errno; + if (ret) { + fprintf(stderr, + "ERROR: couldn''t get space info on ''%s'' - %s\n", + path, strerror(e)); + free(sargs); + return NULL; + } + if (!sargs->total_spaces) { + free(sargs); + printf("No chunks found\n"); + return NULL; + } + + count = sargs->total_spaces; + + sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) + + (count * sizeof(struct btrfs_ioctl_space_info))); + if (!sargs) { + free(sargs_orig); + fprintf(stderr, "ERROR: not enough memory\n"); + return NULL; + } + + sargs->space_slots = count; + sargs->total_spaces = 0; + + ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); + e = errno; + + if (ret) { + fprintf(stderr, + "ERROR: couldn''t get space info on ''%s'' - %s\n", + path, strerror(e)); + free(sargs); + return NULL; + } + + qsort(&(sargs->spaces), count, sizeof(struct btrfs_ioctl_space_info), + cmp_btrfs_ioctl_space_info); + + return sargs; +} + +/* + * This function computes the space occuped by a *single* RAID5/RAID6 chunk. + * The computation is performed on the basis of the number of stripes + * which compose the chunk, which could be different from the number of disks + * if a disk is added later. + */ +static int get_raid56_used(int fd, u64 *raid5_used, u64 *raid6_used) +{ + struct chunk_info *info_ptr=0, *p; + int info_count=0; + int ret; + + *raid5_used = *raid6_used =0; + + ret = load_chunk_info(fd, &info_ptr, &info_count); + if( ret < 0) + return ret; + + for ( p = info_ptr; info_count ; info_count--, p++ ) { + if (p->type & BTRFS_BLOCK_GROUP_RAID5) + (*raid5_used) += p->size / (p->num_stripes -1); + if (p->type & BTRFS_BLOCK_GROUP_RAID6) + (*raid6_used) += p->size / (p->num_stripes -2); + } + + return 0; + +} + +static int _cmd_disk_free(int fd, char *path, int mode) +{ + struct btrfs_ioctl_space_args *sargs = 0; + int i; + int ret = 0; + int e, width; + u64 total_disk; /* filesystem size == sum of + disks sizes */ + u64 total_chunks; /* sum of chunks sizes on disk(s) */ + u64 total_used; /* logical space used */ + u64 total_free; /* logical space un-used */ + double K; + u64 raid5_used, raid6_used; + + if ((sargs = load_space_info(fd, path)) == NULL) { + ret = -1; + goto exit; + } + + total_disk = disk_size(path); + e = errno; + if (total_disk == 0) { + fprintf(stderr, + "ERROR: couldn''t get space info on ''%s'' - %s\n", + path, strerror(e)); + + ret = 19; + goto exit; + } + if (get_raid56_used(fd, &raid5_used, &raid6_used) < 0) { + fprintf(stderr, + "ERROR: couldn''t get space info on ''%s''\n", + path ); + ret = 20; + goto exit; + } + + total_chunks = total_used = total_free = 0; + + for (i = 0; i < sargs->total_spaces; i++) { + float ratio = 1; + u64 allocated; + u64 flags = sargs->spaces[i].flags; + + /* TODO: the raid5/raid6 ratio depends by the number + of disk used by every chunk. It is computed separately + */ + if (flags & BTRFS_BLOCK_GROUP_RAID0) + ratio = 1; + else if (flags & BTRFS_BLOCK_GROUP_RAID1) + ratio = 2; + else if (flags & BTRFS_BLOCK_GROUP_RAID5) + ratio = 0; + else if (flags & BTRFS_BLOCK_GROUP_RAID6) + ratio = 0; + else if (flags & BTRFS_BLOCK_GROUP_DUP) + ratio = 2; + else if (flags & BTRFS_BLOCK_GROUP_RAID10) + ratio = 2; + else + ratio = 1; + + allocated = sargs->spaces[i].total_bytes * ratio; + + total_chunks += allocated; + total_used += sargs->spaces[i].used_bytes; + total_free += (sargs->spaces[i].total_bytes - + sargs->spaces[i].used_bytes); + + } + + /* add the raid5/6 allocated space */ + total_chunks += raid5_used + raid6_used; + + K = ((double)total_used + (double)total_free) / (double)total_chunks; + + if (mode & DF_HUMAN_UNIT) + width = 9; + else + width = 18; + + printf("Disk size:\t\t%*s\n", width, + df_pretty_sizes(total_disk, mode)); + printf("Disk allocated:\t\t%*s\n", width, + df_pretty_sizes(total_chunks, mode)); + printf("Disk unallocated:\t%*s\n", width, + df_pretty_sizes(total_disk-total_chunks, mode)); + printf("Used:\t\t\t%*s\n", width, + df_pretty_sizes(total_used, mode)); + printf("Free (Estimated):\t%*s\t(Max: %s, min: %s)\n", + width, + df_pretty_sizes((u64)(K*total_disk-total_used), mode), + df_pretty_sizes(total_disk-total_chunks+total_free, mode), + df_pretty_sizes((total_disk-total_chunks)/2+total_free, mode)); + printf("Data to disk ratio:\t%*.0f %%\n", + width-2, K*100); + +exit: + + string_list_free(); + if (sargs) + free(sargs); + + return ret; +} + +const char * const cmd_filesystem_df_usage[] = { + "btrfs filesystem df [-b] <path> [<path>..]", + "Show space usage information for a mount point(s).", + "", + "-b\tSet byte as unit", + NULL +}; + +int cmd_filesystem_df(int argc, char **argv) +{ + + int flags = DF_HUMAN_UNIT; + int i, more_than_one = 0; + + optind = 1; + while (1) { + char c = getopt(argc, argv, "b"); + if (c < 0) + break; + + switch (c) { + case ''b'': + flags &= ~DF_HUMAN_UNIT; + break; + default: + usage(cmd_filesystem_df_usage); + } + } + + if (check_argc_min(argc - optind, 1)) { + usage(cmd_filesystem_df_usage); + return 21; + } + + for (i = optind; i < argc ; i++) { + int r, fd; + if (more_than_one) + printf("\n"); + + fd = open_file_or_dir(argv[i]); + if (fd < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", + argv[1]); + return 12; + } + r = _cmd_disk_free(fd, argv[i], flags); + close(fd); + + if (r) + return r; + more_than_one = 1; + + } + + return 0; +} + diff --git a/cmds-fi-disk_usage.h b/cmds-fi-disk_usage.h new file mode 100644 index 0000000..9f68bb3 --- /dev/null +++ b/cmds-fi-disk_usage.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2007 Oracle. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#ifndef __CMDS_FI_DISK_USAGE__ +#define __CMDS_FI_DISK_USAGE__ + +extern const char * const cmd_filesystem_df_usage[]; +int cmd_filesystem_df(int argc, char **argv); + +#endif diff --git a/cmds-filesystem.c b/cmds-filesystem.c index bdbd2ee..5301dc3 100644 --- a/cmds-filesystem.c +++ b/cmds-filesystem.c @@ -33,6 +33,7 @@ #include "commands.h" #include "btrfslabel.h" +#include "cmds-fi-disk_usage.h" static const char * const filesystem_cmd_group_usage[] = { "btrfs filesystem [<group>] <command> [<args>]", @@ -45,128 +46,6 @@ static const char * const cmd_df_usage[] = { NULL }; -static int cmd_df(int argc, char **argv) -{ - struct btrfs_ioctl_space_args *sargs, *sargs_orig; - u64 count = 0, i; - int ret; - int fd; - int e; - char *path; - - if (check_argc_exact(argc, 2)) - usage(cmd_df_usage); - - path = argv[1]; - - fd = open_file_or_dir(path); - if (fd < 0) { - fprintf(stderr, "ERROR: can''t access to ''%s''\n", path); - return 12; - } - - sargs_orig = sargs = malloc(sizeof(struct btrfs_ioctl_space_args)); - if (!sargs) - return -ENOMEM; - - sargs->space_slots = 0; - sargs->total_spaces = 0; - - ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); - e = errno; - if (ret) { - fprintf(stderr, "ERROR: couldn''t get space info on ''%s'' - %s\n", - path, strerror(e)); - close(fd); - free(sargs); - return ret; - } - if (!sargs->total_spaces) { - close(fd); - free(sargs); - return 0; - } - - count = sargs->total_spaces; - - sargs = realloc(sargs, sizeof(struct btrfs_ioctl_space_args) + - (count * sizeof(struct btrfs_ioctl_space_info))); - if (!sargs) { - close(fd); - free(sargs_orig); - return -ENOMEM; - } - - sargs->space_slots = count; - sargs->total_spaces = 0; - - ret = ioctl(fd, BTRFS_IOC_SPACE_INFO, sargs); - e = errno; - if (ret) { - fprintf(stderr, "ERROR: couldn''t get space info on ''%s'' - %s\n", - path, strerror(e)); - close(fd); - free(sargs); - return ret; - } - - for (i = 0; i < sargs->total_spaces; i++) { - char description[80]; - char *total_bytes; - char *used_bytes; - int written = 0; - u64 flags = sargs->spaces[i].flags; - - memset(description, 0, 80); - - if (flags & BTRFS_BLOCK_GROUP_DATA) { - if (flags & BTRFS_BLOCK_GROUP_METADATA) { - snprintf(description, 14, "%s", - "Data+Metadata"); - written += 13; - } else { - snprintf(description, 5, "%s", "Data"); - written += 4; - } - } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) { - snprintf(description, 7, "%s", "System"); - written += 6; - } else if (flags & BTRFS_BLOCK_GROUP_METADATA) { - snprintf(description, 9, "%s", "Metadata"); - written += 8; - } - - if (flags & BTRFS_BLOCK_GROUP_RAID0) { - snprintf(description+written, 8, "%s", ", RAID0"); - written += 7; - } else if (flags & BTRFS_BLOCK_GROUP_RAID1) { - snprintf(description+written, 8, "%s", ", RAID1"); - written += 7; - } else if (flags & BTRFS_BLOCK_GROUP_DUP) { - snprintf(description+written, 6, "%s", ", DUP"); - written += 5; - } else if (flags & BTRFS_BLOCK_GROUP_RAID10) { - snprintf(description+written, 9, "%s", ", RAID10"); - written += 8; - } else if (flags & BTRFS_BLOCK_GROUP_RAID5) { - snprintf(description+written, 9, "%s", ", RAID5"); - written += 7; - } else if (flags & BTRFS_BLOCK_GROUP_RAID6) { - snprintf(description+written, 9, "%s", ", RAID6"); - written += 7; - } - - total_bytes = pretty_sizes(sargs->spaces[i].total_bytes); - used_bytes = pretty_sizes(sargs->spaces[i].used_bytes); - printf("%s: total=%s, used=%s\n", description, total_bytes, - used_bytes); - } - close(fd); - free(sargs); - - return 0; -} - static int uuid_search(struct btrfs_fs_devices *fs_devices, char *search) { char uuidbuf[37]; @@ -517,7 +396,7 @@ static int cmd_label(int argc, char **argv) const struct cmd_group filesystem_cmd_group = { filesystem_cmd_group_usage, NULL, { - { "df", cmd_df, cmd_df_usage, NULL, 0 }, + { "df", cmd_filesystem_df, cmd_filesystem_df_usage, NULL, 0 }, { "show", cmd_show, cmd_show_usage, NULL, 0 }, { "sync", cmd_sync, cmd_sync_usage, NULL, 0 }, { "defragment", cmd_defrag, cmd_defrag_usage, NULL, 0 }, diff --git a/ctree.h b/ctree.h index 12f8fe3..a029368 100644 --- a/ctree.h +++ b/ctree.h @@ -798,9 +798,22 @@ struct btrfs_csum_item { #define BTRFS_BLOCK_GROUP_RAID1 (1ULL << 4) #define BTRFS_BLOCK_GROUP_DUP (1ULL << 5) #define BTRFS_BLOCK_GROUP_RAID10 (1ULL << 6) -#define BTRFS_BLOCK_GROUP_RAID5 (1ULL << 7) -#define BTRFS_BLOCK_GROUP_RAID6 (1ULL << 8) +#define BTRFS_BLOCK_GROUP_RAID5 (1ULL << 7) +#define BTRFS_BLOCK_GROUP_RAID6 (1ULL << 8) #define BTRFS_BLOCK_GROUP_RESERVED BTRFS_AVAIL_ALLOC_BIT_SINGLE +#define BTRFS_NR_RAID_TYPES 7 + +#define BTRFS_BLOCK_GROUP_TYPE_MASK (BTRFS_BLOCK_GROUP_DATA | \ + BTRFS_BLOCK_GROUP_SYSTEM | \ + BTRFS_BLOCK_GROUP_METADATA) + +#define BTRFS_BLOCK_GROUP_PROFILE_MASK (BTRFS_BLOCK_GROUP_RAID0 | \ + BTRFS_BLOCK_GROUP_RAID1 | \ + BTRFS_BLOCK_GROUP_RAID5 | \ + BTRFS_BLOCK_GROUP_RAID6 | \ + BTRFS_BLOCK_GROUP_DUP | \ + BTRFS_BLOCK_GROUP_RAID10) + /* used in struct btrfs_balance_args fields */ #define BTRFS_AVAIL_ALLOC_BIT_SINGLE (1ULL << 48) diff --git a/utils.c b/utils.c index f9ee812..029729c 100644 --- a/utils.c +++ b/utils.c @@ -38,6 +38,8 @@ #include <linux/major.h> #include <linux/kdev_t.h> #include <limits.h> +#include <sys/vfs.h> + #include "kerncompat.h" #include "radix-tree.h" #include "ctree.h" @@ -1386,3 +1388,15 @@ int get_fs_info(int fd, char *path, struct btrfs_ioctl_fs_info_args *fi_args, return 0; } + +u64 disk_size(char *path) +{ + struct statfs sfs; + + if (statfs(path, &sfs) < 0) + return 0; + else + return sfs.f_bsize * sfs.f_blocks; + +} + diff --git a/utils.h b/utils.h index bbcaf6a..7974d00 100644 --- a/utils.h +++ b/utils.h @@ -19,6 +19,7 @@ #ifndef __UTILS__ #define __UTILS__ +#include "kerncompat.h" #include "ctree.h" #define BTRFS_MKFS_SYSTEM_GROUP_SIZE (4 * 1024 * 1024) @@ -58,4 +59,5 @@ char *__strncpy__null(char *dest, const char *src, size_t n); /* Helper to always get proper size of the destination string */ #define strncpy_null(dest, src) __strncpy__null(dest, src, sizeof(dest)) +u64 disk_size(char *path); #endif -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-23 13:46 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-Feb-23 13:46 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..9784422 --- /dev/null +++ b/string_table.c @@ -0,0 +1,157 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> + +#include "string_table.h" + +/* + * This function create an array of char * which will represent a table + */ +struct string_table *table_create(int columns, int rows) +{ + struct string_table *p; + int size; + + + size = sizeof( struct string_table ) + + rows * columns* sizeof(char *); + p = calloc(1, size); + + if (!p) return NULL; + + p->ncols = columns; + p->nrows = rows; + + return p; +} + +/* + * This function is like a vprintf, but store the results in a cell of + * the table. + * If fmt starts with ''<'', the text is left aligned; if fmt starts with + * ''>'' the text is right aligned. If fmt is equal to ''='' the text will + * be replaced by a ''====='' dimensioned in the basis of the column width + */ +char *table_vprintf(struct string_table *tab, int column, int row, + char *fmt, va_list ap) +{ + int idx = tab->ncols*row+column; + char *msg = calloc(100, sizeof(char)); + + if (!msg) + return NULL; + + if (tab->cells[idx]) + free(tab->cells[idx]); + tab->cells[idx] = msg; + vsnprintf(msg, 99, fmt, ap); + + return msg; +} + + +/* + * This function is like a printf, but store the results in a cell of + * the table. + */ +char *table_printf(struct string_table *tab, int column, int row, + char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = table_vprintf(tab, column, row, fmt, ap); + va_end(ap); + + return ret; +} + +/* + * This function dumps the table. Every "=" string will be replaced by + * a "=======" length as the column + */ +void table_dump(struct string_table *tab) +{ + int sizes[tab->ncols]; + int i, j; + + for (i = 0 ; i < tab->ncols ; i++) { + sizes[i] = 0; + for (j = 0 ; j < tab->nrows ; j++) { + int idx = i + j*tab->ncols; + int s; + + if (!tab->cells[idx]) + continue; + + s = strlen(tab->cells[idx]) - 1; + if (s < 1 || tab->cells[idx][0] == ''='') + continue; + + if (s > sizes[i]) + sizes[i] = s; + } + } + + + for (j = 0 ; j < tab->nrows ; j++) { + for (i = 0 ; i < tab->ncols ; i++) { + + int idx = i + j*tab->ncols; + char *s = tab->cells[idx]; + + if (!s|| !strlen(s)) { + printf("%*s", sizes[i], ""); + } else if (s && s[0] == ''='') { + int k = sizes[i]; + while(k--) + putchar(''=''); + } else { + printf("%*s", + s[0] == ''<'' ? -sizes[i] : sizes[i], + s+1); + } + if (i != (tab->ncols - 1)) + putchar('' ''); + } + putchar(''\n''); + } + +} + +/* + * Deallocate a tabular and all its content + */ + +void table_free(struct string_table *tab) +{ + + int i, count; + + count = tab->ncols * tab->nrows; + + for (i=0 ; i < count ; i++) + if (tab->cells[i]) + free(tab->cells[i]); + + free(tab); + +} diff --git a/string_table.h b/string_table.h new file mode 100644 index 0000000..83c4425 --- /dev/null +++ b/string_table.h @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#ifndef STRING_TABLE_H +#define STRING_TABLE_H + +struct string_table { + + int ncols, nrows; + char *cells[]; + +}; + + +struct string_table *table_create(int columns, int rows); +char *table_printf(struct string_table *tab, int column, int row, + char *fmt, ...); +char *table_vprintf(struct string_table *tab, int column, int row, + char *fmt, va_list ap); +void table_dump(struct string_table *tab); +void table_free(struct string_table *); + +#endif -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-23 13:46 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 | 434 +++++++++++++++++++++++++++++++++++++++++++++++++- cmds-fi-disk_usage.h | 2 + cmds-filesystem.c | 2 + utils.c | 58 +++++++ utils.h | 3 + 5 files changed, 498 insertions(+), 1 deletion(-) diff --git a/cmds-fi-disk_usage.c b/cmds-fi-disk_usage.c index 1e3589f..eea4168 100644 --- a/cmds-fi-disk_usage.c +++ b/cmds-fi-disk_usage.c @@ -20,11 +20,13 @@ #include <unistd.h> #include <sys/ioctl.h> #include <errno.h> +#include <stdarg.h> #include "utils.h" #include "kerncompat.h" #include "ctree.h" #include "string_list.h" +#include "string_table.h" #include "commands.h" @@ -42,7 +44,14 @@ struct chunk_info { u64 type; u64 size; u64 devid; - int num_stripes; + u64 num_stripes; +}; + +/* to store information about the disks */ +struct disk_info { + u64 devid; + char path[BTRFS_DEVICE_PATH_NAME_MAX]; + u64 size; }; /* @@ -518,3 +527,426 @@ int cmd_filesystem_df(int argc, char **argv) return 0; } +/* + * Helper to sort the disk_info structure + */ +static int cmp_disk_info(const void *a, const void *b) +{ + return strcmp(((struct disk_info *)a)->path, + ((struct disk_info *)b)->path); +} + +/* + * This function load the disk_info structure and put them in an array + */ +static int load_disks_info(int fd, + struct disk_info **disks_info_ptr, + int *disks_info_count) +{ + + int ret, i, ndevs; + struct btrfs_ioctl_fs_info_args fi_args; + struct btrfs_ioctl_dev_info_args dev_info; + struct disk_info *info; + + *disks_info_count = 0; + *disks_info_ptr = 0; + + ret = ioctl(fd, BTRFS_IOC_FS_INFO, &fi_args); + if (ret < 0) { + fprintf(stderr, "ERROR: cannot get filesystem info\n"); + return -1; + } + + info = malloc(sizeof(struct disk_info) * fi_args.num_devices); + if (!info) { + fprintf(stderr, "ERROR: not enough memory\n"); + return -1; + } + + for (i = 0, ndevs = 0 ; i <= fi_args.max_id ; i++) { + + BUG_ON(ndevs >= fi_args.num_devices); + ret = get_device_info(fd, i, &dev_info); + + if (ret == -ENODEV) + continue; + if (ret) { + fprintf(stderr, + "ERROR: cannot get info about device devid=%d\n", + i); + free(info); + return -1; + } + + info[ndevs].devid = dev_info.devid; + strcpy(info[ndevs].path, (char *)dev_info.path); + info[ndevs].size = get_partition_size((char *)dev_info.path); + ++ndevs; + } + + BUG_ON(ndevs != fi_args.num_devices); + qsort(info, fi_args.num_devices, + sizeof(struct disk_info), cmp_disk_info); + + *disks_info_count = fi_args.num_devices; + *disks_info_ptr = info; + + return 0; + +} + +/* + * This function computes the size of a chunk in a disk + */ +static u64 calc_chunk_size(struct chunk_info *ci) +{ + if (ci->type & BTRFS_BLOCK_GROUP_RAID0) + return ci->size / ci->num_stripes; + else if (ci->type & BTRFS_BLOCK_GROUP_RAID1) + return ci->size ; + else if (ci->type & BTRFS_BLOCK_GROUP_DUP) + return ci->size ; + else if (ci->type & BTRFS_BLOCK_GROUP_RAID5) + return ci->size / (ci->num_stripes -1); + else if (ci->type & BTRFS_BLOCK_GROUP_RAID6) + return ci->size / (ci->num_stripes -2); + else if (ci->type & BTRFS_BLOCK_GROUP_RAID10) + return ci->size / ci->num_stripes; + return ci->size; +} + +/* + * This function print the results of the command btrfs fi disk-usage + * in tabular format + */ +static void _cmd_filesystem_disk_usage_tabular(int mode, + struct btrfs_ioctl_space_args *sargs, + struct chunk_info *chunks_info_ptr, + int chunks_info_count, + struct disk_info *disks_info_ptr, + int disks_info_count) +{ + int i; + u64 total_unused = 0; + struct string_table *matrix = 0; + int ncols, nrows; + + + ncols = sargs->total_spaces + 2; + nrows = 2 + 1 + disks_info_count + 1 + 2; + + matrix = table_create(ncols, nrows); + if (!matrix) { + fprintf(stderr, "ERROR: not enough memory\n"); + return; + } + + /* header */ + for (i = 0; i < sargs->total_spaces; i++) { + const char *description; + + u64 flags = sargs->spaces[i].flags; + description = btrfs_flags2description(flags); + + table_printf(matrix, 1+i, 0, "<%s", description); + } + + for (i = 0; i < sargs->total_spaces; i++) { + const char *r_mode; + + u64 flags = sargs->spaces[i].flags; + r_mode = btrfs_flags2profile(flags); + + table_printf(matrix, 1+i, 1, "<%s", r_mode); + } + + table_printf(matrix, 1+sargs->total_spaces, 1, "<Unallocated"); + + /* body */ + for (i = 0 ; i < disks_info_count ; i++) { + int k, col; + char *p; + + u64 total_allocated = 0, unused; + + p = strrchr(disks_info_ptr[i].path, ''/''); + if (!p) + p = disks_info_ptr[i].path; + else + p++; + + table_printf(matrix, 0, i+3, "<%s", + disks_info_ptr[i].path); + + for (col = 1, k = 0 ; k < sargs->total_spaces ; k++) { + u64 flags = sargs->spaces[k].flags; + int j; + + for (j = 0 ; j < chunks_info_count ; j++) { + u64 size = calc_chunk_size(chunks_info_ptr+j); + + if (chunks_info_ptr[j].type != flags || + chunks_info_ptr[j].devid !+ disks_info_ptr[i].devid) + continue; + + table_printf(matrix, col, i+3, + ">%s", df_pretty_sizes(size, mode)); + total_allocated += size; + col++; + break; + + } + if (j == chunks_info_count) { + table_printf(matrix, col, i+3, ">-"); + col++; + } + } + + unused = get_partition_size(disks_info_ptr[i].path) - + total_allocated; + + table_printf(matrix, sargs->total_spaces + 1, i + 3, + ">%s", df_pretty_sizes(unused, mode)); + total_unused += unused; + + } + + for (i = 0; i <= sargs->total_spaces; i++) + table_printf(matrix, i + 1, disks_info_count + 3, "="); + + + /* footer */ + table_printf(matrix, 0, disks_info_count + 4, "<Total"); + for (i = 0; i < sargs->total_spaces; i++) + table_printf(matrix, 1 + i, disks_info_count + 4, + ">%s", + df_pretty_sizes(sargs->spaces[i].total_bytes, mode)); + + table_printf(matrix, sargs->total_spaces+1, disks_info_count+4, + ">%s", df_pretty_sizes(total_unused, mode)); + + table_printf(matrix, 0, disks_info_count+5, "<Used"); + for (i = 0; i < sargs->total_spaces; i++) + table_printf(matrix, 1+i, disks_info_count+5, ">%s", + df_pretty_sizes(sargs->spaces[i].used_bytes, mode)); + + + table_dump(matrix); + table_free(matrix); + +} + +/* + * This function prints the unused space per every disk + */ +static void print_unused(struct chunk_info *info_ptr, + int info_count, + struct disk_info *disks_info_ptr, + int disks_info_count, + int mode) +{ + int i; + for (i = 0 ; i < disks_info_count ; i++) { + + int j; + u64 total = 0; + char *s; + + for (j = 0 ; j < info_count ; j++) + if (info_ptr[j].devid == disks_info_ptr[i].devid) + total += calc_chunk_size(info_ptr+j); + + s = df_pretty_sizes(disks_info_ptr[i].size - total, mode); + printf(" %s\t%10s\n", disks_info_ptr[i].path, s); + + } + +} + +/* + * This function prints the allocated chunk per every disk + */ +static void print_chunk_disks(u64 chunk_type, + struct chunk_info *chunks_info_ptr, + int chunks_info_count, + struct disk_info *disks_info_ptr, + int disks_info_count, + int mode) +{ + int i; + + for (i = 0 ; i < disks_info_count ; i++) { + + int j; + u64 total = 0; + char *s; + + for (j = 0 ; j < chunks_info_count ; j++) { + + if (chunks_info_ptr[j].type != chunk_type) + continue; + if (chunks_info_ptr[j].devid != disks_info_ptr[i].devid) + continue; + + total += calc_chunk_size(&(chunks_info_ptr[j])); + //total += chunks_info_ptr[j].size; + } + + if (total > 0) { + s = df_pretty_sizes(total, mode); + printf(" %s\t%10s\n", disks_info_ptr[i].path, s); + } + } +} + +/* + * This function print the results of the command btrfs fi disk-usage + * in linear format + */ +static void _cmd_filesystem_disk_usage_linear(int mode, + struct btrfs_ioctl_space_args *sargs, + struct chunk_info *info_ptr, + int info_count, + struct disk_info *disks_info_ptr, + int disks_info_count) +{ + int i; + + for (i = 0; i < sargs->total_spaces; i++) { + const char *description; + const char *r_mode; + + u64 flags = sargs->spaces[i].flags; + description= btrfs_flags2description(flags); + r_mode = btrfs_flags2profile(flags); + + printf("%s,%s: Size:%s, Used:%s\n", + description, + r_mode, + df_pretty_sizes(sargs->spaces[i].total_bytes , + mode), + df_pretty_sizes(sargs->spaces[i].used_bytes, + mode)); + + print_chunk_disks(flags, info_ptr, info_count, + disks_info_ptr, disks_info_count, + mode); + printf("\n"); + + } + + printf("Unallocated:\n"); + print_unused(info_ptr, info_count, + disks_info_ptr, disks_info_count, + mode); + + + +} + +static int _cmd_filesystem_disk_usage(int fd, char *path, int mode, int tabular) +{ + struct btrfs_ioctl_space_args *sargs = 0; + int info_count = 0; + struct chunk_info *info_ptr = 0; + struct disk_info *disks_info_ptr = 0; + int disks_info_count = 0; + int ret = 0; + + if (load_chunk_info(fd, &info_ptr, &info_count) || + load_disks_info(fd, &disks_info_ptr, &disks_info_count)) { + ret = -1; + goto exit; + } + + if ((sargs = load_space_info(fd, path)) == NULL) { + ret = -1; + goto exit; + } + + if (tabular) + _cmd_filesystem_disk_usage_tabular(mode, sargs, + info_ptr, info_count, + disks_info_ptr, disks_info_count); + else + _cmd_filesystem_disk_usage_linear(mode, sargs, + info_ptr, info_count, + disks_info_ptr, disks_info_count); + +exit: + + string_list_free(); + if (sargs) + free(sargs); + if (disks_info_ptr) + free(disks_info_ptr); + if (info_ptr) + free(info_ptr); + + return ret; +} + +const char * const cmd_filesystem_disk_usage_usage[] = { + "btrfs filesystem disk-usage [-b][-t] <path> [<path>..]", + "Show in which disk the chunks are allocated.", + "", + "-b\tSet byte as unit", + "-t\tShow data in tabular format", + NULL +}; + +int cmd_filesystem_disk_usage(int argc, char **argv) +{ + + int flags = DF_HUMAN_UNIT; + int i, more_than_one = 0; + int tabular = 0; + + optind = 1; + while (1) { + char c = getopt(argc, argv, "bt"); + if (c < 0) + break; + switch (c) { + case ''b'': + flags &= ~DF_HUMAN_UNIT; + break; + case ''t'': + tabular = 1; + break; + default: + usage(cmd_filesystem_disk_usage_usage); + } + } + + if (check_argc_min(argc - optind, 1)) { + usage(cmd_filesystem_disk_usage_usage); + return 21; + } + + for (i = optind; i < argc ; i++) { + int r, fd; + if (more_than_one) + printf("\n"); + + fd = open_file_or_dir(argv[i]); + if (fd < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", + argv[1]); + return 12; + } + r = _cmd_filesystem_disk_usage(fd, argv[i], flags, tabular); + close(fd); + + if (r) + return r; + more_than_one = 1; + + } + + return 0; +} + + diff --git a/cmds-fi-disk_usage.h b/cmds-fi-disk_usage.h index 9f68bb3..ae11570 100644 --- a/cmds-fi-disk_usage.h +++ b/cmds-fi-disk_usage.h @@ -21,5 +21,7 @@ extern const char * const cmd_filesystem_df_usage[]; int cmd_filesystem_df(int argc, char **argv); +extern const char * const cmd_filesystem_disk_usage_usage[]; +int cmd_filesystem_disk_usage(int argc, char **argv); #endif diff --git a/cmds-filesystem.c b/cmds-filesystem.c index 5301dc3..e9eb0c4 100644 --- a/cmds-filesystem.c +++ b/cmds-filesystem.c @@ -403,6 +403,8 @@ const struct cmd_group filesystem_cmd_group = { { "balance", cmd_balance, NULL, &balance_cmd_group, 1 }, { "resize", cmd_resize, cmd_resize_usage, NULL, 0 }, { "label", cmd_label, cmd_label_usage, NULL, 0 }, + { "disk-usage", cmd_filesystem_disk_usage, + cmd_filesystem_disk_usage_usage, NULL, 0 }, { 0, 0, 0, 0, 0 }, } }; diff --git a/utils.c b/utils.c index 029729c..60f05e4 100644 --- a/utils.c +++ b/utils.c @@ -1400,3 +1400,61 @@ u64 disk_size(char *path) } +u64 get_partition_size(char *dev) +{ + u64 result; + int fd = open(dev, O_RDONLY); + + if (fd < 0) + return 0; + if (ioctl(fd, BLKGETSIZE64, &result) < 0) { + close(fd); + return 0; + } + close(fd); + + return result; +} + +/* + * Convert a chunk type to a chunk description + */ +const char * btrfs_flags2description(u64 flags) +{ + if (flags & BTRFS_BLOCK_GROUP_DATA) { + if (flags & BTRFS_BLOCK_GROUP_METADATA) + return "Data+Metadata"; + else + return "Data"; + } else if (flags & BTRFS_BLOCK_GROUP_SYSTEM) { + return "System"; + } else if (flags & BTRFS_BLOCK_GROUP_METADATA) { + return "Metadata"; + } else { + return "Unknown"; + } +} + +/* + * Convert a chunk type to a chunk profile description + */ +const char * btrfs_flags2profile(u64 flags) +{ + if (flags & BTRFS_BLOCK_GROUP_RAID0) { + return "RAID0"; + } else if (flags & BTRFS_BLOCK_GROUP_RAID1) { + return "RAID1"; + } else if (flags & BTRFS_BLOCK_GROUP_RAID5) { + return "RAID5"; + } else if (flags & BTRFS_BLOCK_GROUP_RAID6) { + return "RAID6"; + } else if (flags & BTRFS_BLOCK_GROUP_DUP) { + return "DUP"; + } else if (flags & BTRFS_BLOCK_GROUP_RAID10) { + return "RAID10"; + } else { + return "Single"; + } +} + + diff --git a/utils.h b/utils.h index 7974d00..84fdd0e 100644 --- a/utils.h +++ b/utils.h @@ -60,4 +60,7 @@ char *__strncpy__null(char *dest, const char *src, size_t n); #define strncpy_null(dest, src) __strncpy__null(dest, src, sizeof(dest)) u64 disk_size(char *path); +u64 get_partition_size(char *dev); +const char * btrfs_flags2profile(u64 flags); +const char * btrfs_flags2description(u64 flags); #endif -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-23 13:46 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 | 4 ++ 3 files changed, 148 insertions(+) diff --git a/cmds-device.c b/cmds-device.c index 198ad68..0dbc02c 100644 --- a/cmds-device.c +++ b/cmds-device.c @@ -27,6 +27,7 @@ #include "ctree.h" #include "ioctl.h" #include "utils.h" +#include "cmds-fi-disk_usage.h" #include "commands.h" @@ -403,6 +404,8 @@ const struct cmd_group device_cmd_group = { { "scan", cmd_scan_dev, cmd_scan_dev_usage, NULL, 0 }, { "ready", cmd_ready_dev, cmd_ready_dev_usage, NULL, 0 }, { "stats", cmd_dev_stats, cmd_dev_stats_usage, NULL, 0 }, + { "disk-usage", cmd_device_disk_usage, + cmd_device_disk_usage_usage, NULL, 0 }, { 0, 0, 0, 0, 0 } } }; diff --git a/cmds-fi-disk_usage.c b/cmds-fi-disk_usage.c index eea4168..18350ce 100644 --- a/cmds-fi-disk_usage.c +++ b/cmds-fi-disk_usage.c @@ -949,4 +949,145 @@ int cmd_filesystem_disk_usage(int argc, char **argv) return 0; } +static void print_disk_chunks(int fd, + u64 devid, + u64 total_size, + struct chunk_info *chunks_info_ptr, + int chunks_info_count, + int mode) +{ + int i; + u64 allocated = 0; + char *s; + + for (i = 0 ; i < chunks_info_count ; i++) { + const char *description; + const char *r_mode; + u64 flags; + u64 size; + + if (chunks_info_ptr[i].devid != devid) + continue; + + flags = chunks_info_ptr[i].type; + + description = btrfs_flags2description(flags); + r_mode = btrfs_flags2profile(flags); + size = calc_chunk_size(chunks_info_ptr+i); + s = df_pretty_sizes(size, mode); + printf(" %s,%s:%*s%10s\n", + description, + r_mode, + (int)(20 - strlen(description) - strlen(r_mode)), "", + s); + + allocated += size; + + } + s = df_pretty_sizes(total_size - allocated, mode); + printf(" Unallocated: %*s%10s\n", + (int)(20 - strlen("Unallocated")), "", + s); + +} + +static int _cmd_device_disk_usage(int fd, char *path, int mode) +{ + int i; + int ret = 0; + int info_count = 0; + struct chunk_info *info_ptr = 0; + struct disk_info *disks_info_ptr = 0; + int disks_info_count = 0; + + if (load_chunk_info(fd, &info_ptr, &info_count) || + load_disks_info(fd, &disks_info_ptr, &disks_info_count)) { + ret = -1; + goto exit; + } + + for (i = 0 ; i < disks_info_count ; i++) { + char *s; + + s = df_pretty_sizes(disks_info_ptr[i].size, mode); + printf("%s\t%10s\n", disks_info_ptr[i].path, s); + + print_disk_chunks(fd, disks_info_ptr[i].devid, + disks_info_ptr[i].size, + info_ptr, info_count, + mode); + printf("\n"); + + } + + +exit: + + string_list_free(); + if (disks_info_ptr) + free(disks_info_ptr); + if (info_ptr) + free(info_ptr); + + return ret; +} + +const char * const cmd_device_disk_usage_usage[] = { + "btrfs device disk-usage [-b] <path> [<path>..]", + "Show which chunks are in a device.", + "", + "-b\tSet byte as unit", + NULL +}; + +int cmd_device_disk_usage(int argc, char **argv) +{ + + int flags = DF_HUMAN_UNIT; + int i, more_than_one = 0; + + optind = 1; + while (1) { + char c = getopt(argc, argv, "b"); + + if (c < 0) + break; + + switch (c) { + case ''b'': + flags &= ~DF_HUMAN_UNIT; + break; + default: + usage(cmd_device_disk_usage_usage); + } + } + + if (check_argc_min(argc - optind, 1)) { + usage(cmd_device_disk_usage_usage); + return 21; + } + + for (i = optind; i < argc ; i++) { + int r, fd; + if (more_than_one) + printf("\n"); + + fd = open_file_or_dir(argv[i]); + if (fd < 0) { + fprintf(stderr, "ERROR: can''t access to ''%s''\n", + argv[1]); + return 12; + } + r = _cmd_device_disk_usage(fd, argv[i], flags); + close(fd); + + if (r) + return r; + more_than_one = 1; + + } + + return 0; +} + diff --git a/cmds-fi-disk_usage.h b/cmds-fi-disk_usage.h index ae11570..c315004 100644 --- a/cmds-fi-disk_usage.h +++ b/cmds-fi-disk_usage.h @@ -21,7 +21,11 @@ extern const char * const cmd_filesystem_df_usage[]; int cmd_filesystem_df(int argc, char **argv); + extern const char * const cmd_filesystem_disk_usage_usage[]; int cmd_filesystem_disk_usage(int argc, char **argv); +extern const char * const cmd_device_disk_usage_usage[]; +int cmd_device_disk_usage(int argc, char **argv); + #endif -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-23 13:46 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
Eric Sandeen
2013-Feb-25 02:20 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
On 2/23/13 7:46 AM, Goffredo Baroncelli wrote:> 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.I have to say, I agree with Zach that there are more straightforward and less error-prone ways to accomplish this same result. You said:> your suggestion is not so easy applicableto Zach''s suggestion. Why do you consider it to be not applicable? Maybe I am missing some subtlety, but it''d help if you could explain the problem that you see. I think that the asymmetry in i.e. _cmd_disk_free where you do: + printf("Disk size:\t\t%*s\n", width, + df_pretty_sizes(total_disk, mode)); <a few times> and then: + string_list_free(); ... because obviously (?) "df_pretty_sizes" needs this cleanup ... is unexpected, magic, and error-prone. -Eric thanks, -Eric> Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> > --- > Makefile | 3 ++- > string_list.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > string_list.h | 23 +++++++++++++++++++++ > 3 files changed, 88 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..8d8cc1f > --- /dev/null > +++ b/string_list.c > @@ -0,0 +1,63 @@ > +/* > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public > + * License v2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + * > + * You should have received a copy of the GNU General Public > + * License along with this program; if not, write to the > + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, > + * Boston, MA 021110-1307, USA. > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > + > +/* To store the strings */ > +static void **strings_to_free; > +static int count_string_to_free; > + > +/* > + * Add a string to the dynamic allocated string list > + */ > +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..f974fbc > --- /dev/null > +++ b/string_list.h > @@ -0,0 +1,23 @@ > +/* > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public > + * License v2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + * > + * You should have received a copy of the GNU General Public > + * License along with this program; if not, write to the > + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, > + * Boston, MA 021110-1307, USA. > + */ > + > +#ifndef STRING_LIST_H > +#define STRING_LIST_H > + > +void string_list_add(char *s); > +void string_list_free(); > + > +#endif >-- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Zach Brown
2013-Feb-25 17:38 UTC
Re: [PATCH V2][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
> I updates my previous patches [1] to add support for raid5/6. > These patches update the btrfs fi df command and add two new commands: > - btrfs filesystem disk-usage <path> > - btrfs device disk-usage <path>This seems like a ton of code. Here''s a thought experiment: What''s the smallest possible change that could communicate the information that people don''t have today? - z -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-25 19:59 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
Hi Eric, On 02/25/2013 03:20 AM, Eric Sandeen wrote:> On 2/23/13 7:46 AM, Goffredo Baroncelli wrote: >> 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. > > I have to say, I agree with Zach that there are more straightforward > and less error-prone ways to accomplish this same result. > > You said: > >> your suggestion is not so easy applicable > > to Zach''s suggestion. Why do you consider it to be not applicable? > Maybe I am missing some subtlety, but it''d help if you could explain > the problem that you see.For example try on the following line (is a real example from the function _cmd_disk_free): printf("Disk size:\t\t%*s\n", width, df_pretty_sizes(total_disk, mode)); it would be translated (note the ''%*s''): if (mode == DF_HUMAN_UNIT) printf("Disk size:\t\t%*s%s\n", width-2, df_pretty_sizes_number(total_disk), df_pretty_sizes_unit(total_disk)); else printf("Disk size:\t\t%*lld\n", width, total_disk); 6 lines versus 2: does it make sense ?> > I think that the asymmetry in i.e. _cmd_disk_free where you do: > > + printf("Disk size:\t\t%*s\n", width, > + df_pretty_sizes(total_disk, mode)); > <a few times> > and then: > > + string_list_free(); > > ... because obviously (?) "df_pretty_sizes" needs this cleanup ... > > is unexpected, magic, and error-prone.df_pretty_sizes is not a generic function. There is a reason because it is _defined_static_. We could improve the function comment (or even rename the function itself) to point out it better. It is a facility to avoid to write a lot of equal lines. However I am open to any suggestion which: - let the code simple enough - avoid the duplication of lines. To avoid all this mess, we should add another conversion specifier with the function register_printf_function(). Unfortunately I was not able to find the equivalent one for sprintf(). BR G.Baroncelli> > -Eric > > > > thanks, > -Eric > >> Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it> >> --- >> Makefile | 3 ++- >> string_list.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> string_list.h | 23 +++++++++++++++++++++ >> 3 files changed, 88 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..8d8cc1f >> --- /dev/null >> +++ b/string_list.c >> @@ -0,0 +1,63 @@ >> +/* >> + * This program is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU General Public >> + * License v2 as published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public >> + * License along with this program; if not, write to the >> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, >> + * Boston, MA 021110-1307, USA. >> + */ >> + >> +#include <stdio.h> >> +#include <stdlib.h> >> +#include <string.h> >> +#include <unistd.h> >> + >> +/* To store the strings */ >> +static void **strings_to_free; >> +static int count_string_to_free; >> + >> +/* >> + * Add a string to the dynamic allocated string list >> + */ >> +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..f974fbc >> --- /dev/null >> +++ b/string_list.h >> @@ -0,0 +1,23 @@ >> +/* >> + * This program is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU General Public >> + * License v2 as published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public >> + * License along with this program; if not, write to the >> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, >> + * Boston, MA 021110-1307, USA. >> + */ >> + >> +#ifndef STRING_LIST_H >> +#define STRING_LIST_H >> + >> +void string_list_add(char *s); >> +void string_list_free(); >> + >> +#endif >> > >-- gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it> Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Zach Brown
2013-Feb-25 20:19 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
> printf("Disk size:\t\t%*s\n", width, > df_pretty_sizes(total_disk, mode)); > > it would be translated (note the ''%*s''): > > if (mode == DF_HUMAN_UNIT) > printf("Disk size:\t\t%*s%s\n", width-2, > df_pretty_sizes_number(total_disk), > df_pretty_sizes_unit(total_disk)); > else > printf("Disk size:\t\t%*lld\n", width, total_disk);So use mode as the argument in the second case, just like you did in the first. printf("Disk dize:\t\t"sz_fmt"\n", sz_arg(total_disk, mode, width)); #define sz_fmt "%*llu%s" #define sz_fmt(v,m,w) width(w, m), scaled(v, m), units(v, m) And again, the reason we jump through these mildly distasteful hoops is that *it gets rid of allocated strings entirely*. That bug in df_pretty_sizes() where it allocates a 20 byte buffer to store a string that can be 21 bytes long with its null? It''d just vanish. Not needed. The only code that is *sure* to be bug free is the code that doesn''t exist.> To avoid all this mess, we should add another conversion specifier with > the function register_printf_function(). Unfortunately I was not able to > find the equivalent one for sprintf().Doesn''t that prevent compile-time verification that the types of the format specifiers match the arguments? - z -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Goffredo Baroncelli
2013-Feb-25 21:00 UTC
Re: [PATCH 1/8] Add some helpers to manage the strings allocation/deallocation.
On 02/25/2013 09:19 PM, Zach Brown wrote:>> printf("Disk size:\t\t%*s\n", width, >> df_pretty_sizes(total_disk, mode)); >> >> it would be translated (note the ''%*s''): >> >> if (mode == DF_HUMAN_UNIT) >> printf("Disk size:\t\t%*s%s\n", width-2, >> df_pretty_sizes_number(total_disk), >> df_pretty_sizes_unit(total_disk)); >> else >> printf("Disk size:\t\t%*lld\n", width, total_disk); > > So use mode as the argument in the second case, just like you did in the > first. > > printf("Disk dize:\t\t"sz_fmt"\n", sz_arg(total_disk, mode, width));> > #define sz_fmt "%*llu%s" > #define sz_fmt(v,m,w) width(w, m), scaled(v, m), units(v, m)I suppose s/sz_fmt/sz_arg/> And again, the reason we jump through these mildly distasteful hoops is > that *it gets rid of allocated strings entirely*.I am not entirely convinced, however if you prepare a patch I will integrate it quickly> That bug in df_pretty_sizes() where it allocates a 20 byte buffer to > store a string that can be 21 bytes long with its null? It''d just > vanish. Not needed.Good catch>> To avoid all this mess, we should add another conversion specifier with >> the function register_printf_function(). Unfortunately I was not able to >> find the equivalent one for sprintf(). > > Doesn''t that prevent compile-time verification that the types of the > format specifiers match the arguments?Yes this was another cons...> > - z > -- > To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html >-- gpg @keyserver.linux.it: Goffredo Baroncelli (kreijackATinwind.it> Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Martin Steigerwald
2013-Feb-26 11:09 UTC
Re: [PATCH V2][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
Am Montag, 25. Februar 2013 schrieb Zach Brown:> > I updates my previous patches [1] to add support for raid5/6. > > These patches update the btrfs fi df command and add two new commands: > > - btrfs filesystem disk-usage <path> > > - btrfs device disk-usage <path> > > This seems like a ton of code. > > Here''s a thought experiment: What''s the smallest possible change that > could communicate the information that people don''t have today?The kind and amount of information output of these additions have been discussed several times before. I found the output provided quite useful. As others. Free space seems to be a complex matter in BTRFS and one conclusion was that its not easily possible to provide a single number to show how much space is free. I´d still like that for df, whose output is quite bogus in certain BTRFS setups at the moment and does not give applications a realistic estimate at all. One example is raid 1 with 10 GB each disk. Shows 20 GB free. An application which wants to write 15 GB will fail. Which can break installer scripts, package management, cache software or anything else which checks for free space. Thus I´d like df to default to *minimum* free. But what is it concretely, what you feel uncomfortable with? The Linux kernel is also a ton of code. I´d really like to see Goffredo improvements go in instead of them being discussed endlessly. So I´d like to feedback to be as concrete as possible, so Goffredo has a chance to work on it. Ciao, -- 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
Gareth Pye
2013-Feb-26 11:28 UTC
Re: [PATCH V2][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
On Tue, Feb 26, 2013 at 10:09 PM, Martin Steigerwald <Martin@lichtvoll.de> wrote:> I´d still like that for df, whose output is quite bogus in certain BTRFS > setups at the moment and does not give applications a realistic estimate at > all. One example is raid 1 with 10 GB each disk. Shows 20 GB free. An > application which wants to write 15 GB will fail. Which can break installer > scripts, package management, cache software or anything else which checks > for free space. Thus I´d like df to default to *minimum* free.Is that any better than the script failing to attempt to install because it needs 15G but because some of the storage is used in RAID1 then df shows 10G free but the 15G install would work fine. If you could force the tool to install where it know it doesn''t have sufficient space? -- Gareth Pye Level 2 Judge, Melbourne, Australia Australian MTG Forum: mtgau.com gareth@cerberos.id.au - www.rockpaperdynamite.wordpress.com "Dear God, I would like to file a bug report" -- 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-Feb-26 12:55 UTC
Re: [PATCH V2][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
Am Samstag, 23. Februar 2013 schrieb Goffredo Baroncelli:> Hi all,Hi Goffredo,> I updates my previous patches [1] to add support for raid5/6. > These patches update the btrfs fi df command and add two new commands: > - btrfs filesystem disk-usage <path> > - btrfs device disk-usage <path>Tested-By: Martin Steigerwald <martin@lichtvoll.de> Only for -d single and -m single, I have RAID-1 at workstation at work, but will take some time till I get to office again. No RAID-5/6 setup at the moment. So test obviously not complete yet. merkaba:~> /tmp/btrfs fi sh failed to read /dev/sr0 Label: ''home'' uuid: […] Total devices 1 FS bytes used 196.11GB devid 1 size 223.52GB used 204.02GB path /dev/dm-2 Label: ''debian'' uuid: […] Total devices 1 FS bytes used 10.69GB devid 1 size 18.62GB used 17.02GB path /dev/dm-0 Btrfs v0.19-367-g711b24a /: merkaba:~> /tmp/btrfs fi df / Disk size: 18.62GB Disk allocated: 17.02GB Disk unallocated: 1.61GB Used: 10.69GB Free (Estimated): 7.93GB (Max: 7.93GB, min: 7.13GB) Data to disk ratio: 100 % merkaba:~> /tmp/btrfs fi disk-usage / Data,Single: Size:16.01GB, Used:10.14GB /dev/dm-0 16.01GB Metadata,Single: Size:1.01GB, Used:565.34MB /dev/dm-0 1.01GB System,Single: Size:4.00MB, Used:4.00KB /dev/dm-0 4.00MB Unallocated: /dev/dm-0 1.61GB merkaba:~> /tmp/btrfs fi disk-usage -t / Data Metadata System Single Single Single Unallocated /dev/dm-0 16.01GB 1.01GB 4.00MB 1.61GB ======= ======== ====== ==========Total 16.01GB 1.01GB 4.00MB 1.61GB Used 10.14GB 565.34MB 4.00KB merkaba:~> /tmp/btrfs dev disk-usage / /dev/dm-0 18.62GB Data,Single: 16.01GB Metadata,Single: 1.01GB System,Single: 4.00MB Unallocated: 1.61GB /home (quite fresh fs): merkaba:~#129> /tmp/btrfs fi df /home Disk size: 223.52GB Disk allocated: 204.02GB Disk unallocated: 19.50GB Used: 196.11GB Free (Estimated): 27.40GB (Max: 27.40GB, min: 17.66GB) Data to disk ratio: 100 % merkaba:~> /tmp/btrfs fi disk-usage /home Data,Single: Size:202.01GB, Used:194.67GB /dev/dm-2 202.01GB Metadata,Single: Size:2.01GB, Used:1.44GB /dev/dm-2 2.01GB System,Single: Size:4.00MB, Used:48.00KB /dev/dm-2 4.00MB Unallocated: /dev/dm-2 19.50GB merkaba:~> /tmp/btrfs fi disk-usage -t /home Data Metadata System Single Single Single Unallocated /dev/dm-2 202.01GB 2.01GB 4.00MB 19.50GB ======== ======== ======= ==========Total 202.01GB 2.01GB 4.00MB 19.50GB Used 194.67GB 1.44GB 48.00KB merkaba:~> /tmp/btrfs dev disk-usage /home /dev/dm-2 223.52GB Data,Single: 202.01GB Metadata,Single: 2.01GB System,Single: 4.00MB Unallocated: 19.50GB 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-Feb-26 12:58 UTC
Re: [PATCH V2][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
Am Dienstag, 26. Februar 2013 schrieb Gareth Pye:> On Tue, Feb 26, 2013 at 10:09 PM, Martin Steigerwald > > <Martin@lichtvoll.de> wrote: > > I´d still like that for df, whose output is quite bogus in certain > > BTRFS setups at the moment and does not give applications a realistic > > estimate at all. One example is raid 1 with 10 GB each disk. Shows 20 > > GB free. An application which wants to write 15 GB will fail. Which > > can break installer scripts, package management, cache software or > > anything else which checks for free space. Thus I´d like df to default > > to *minimum* free. > > Is that any better than the script failing to attempt to install > because it needs 15G but because some of the storage is used in RAID1 > then df shows 10G free but the 15G install would work fine. If you > could force the tool to install where it know it doesn''t have > sufficient space?I do not quite understand your question. In RAID-1 with 10 GB and two disks, df will show 20 GB free. If the script needs 15 GB and checks for it it would run, but then fail. I would prefer that the script space check bails out in that case it is know that there is not enough space available anymore. So or so one can always argue that current free space is just a snapshot of the current moment and there is *never* a guarentee that there is enough space, cause another application may write to the filesystem at the same time. 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-Feb-26 13:15 UTC
Re: [PATCH V2][BTRFS-PROGS] Enhance btrfs fi df with raid5/6 support
Am Dienstag, 26. Februar 2013 schrieb Martin Steigerwald:> Am Dienstag, 26. Februar 2013 schrieb Gareth Pye: > > On Tue, Feb 26, 2013 at 10:09 PM, Martin Steigerwald > > > > <Martin@lichtvoll.de> wrote: > > > I´d still like that for df, whose output is quite bogus in certain > > > BTRFS setups at the moment and does not give applications a realistic > > > estimate at all. One example is raid 1 with 10 GB each disk. Shows 20 > > > GB free. An application which wants to write 15 GB will fail. Which > > > can break installer scripts, package management, cache software or > > > anything else which checks for free space. Thus I´d like df to > > > default to *minimum* free. > > > > Is that any better than the script failing to attempt to install > > because it needs 15G but because some of the storage is used in RAID1 > > then df shows 10G free but the 15G install would work fine. If you > > could force the tool to install where it know it doesn''t have > > sufficient space? > > I do not quite understand your question. In RAID-1 with 10 GB and two > disks, df will show 20 GB free. If the script needs 15 GB and checks for > it it would run, but then fail. I would prefer that the script space > check bails out in that case it is know that there is not enough space > available anymore.Well, okay, and exactly that is not known, cause if the files the installer script are installed are stored a single instead of RAID-1 there would be enough space. Anyway for the additions Goffredo made I strongly suggest reading the old discussions before starting to discuss stuff that has already been discussed back then. -- 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