diff mbox

[5/8] Add command btrfs filesystem disk-usage

Message ID 52FD1AD6.50500@libero.it (mailing list archive)
State New, archived
Headers show

Commit Message

Goffredo Baroncelli Feb. 13, 2014, 7:19 p.m. UTC
Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it>
---
 cmds-fi-disk_usage.c | 428 +++++++++++++++++++++++++++++++++++++++++++++++++++
 cmds-fi-disk_usage.h |   3 +
 cmds-filesystem.c    |   3 +
 utils.c              |  63 ++++++++
 utils.h              |   3 +
 5 files changed, 500 insertions(+)

Comments

Roman Mamedov Feb. 13, 2014, 7:28 p.m. UTC | #1
On Thu, 13 Feb 2014 20:19:50 +0100
Goffredo Baroncelli <kreijack@libero.it> wrote:

> Signed-off-by: Goffredo Baroncelli <kreijack@inwind.it>
> ---
>  cmds-fi-disk_usage.c | 428 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  cmds-fi-disk_usage.h |   3 +
>  cmds-filesystem.c    |   3 +
>  utils.c              |  63 ++++++++
>  utils.h              |   3 +
>  5 files changed, 500 insertions(+)
> 
> diff --git a/cmds-fi-disk_usage.c b/cmds-fi-disk_usage.c
> index 4012c78..16b3ab2 100644
> --- a/cmds-fi-disk_usage.c
> +++ b/cmds-fi-disk_usage.c
> @@ -20,10 +20,12 @@
>  #include <unistd.h>
>  #include <sys/ioctl.h>
>  #include <errno.h>
> +#include <stdarg.h>
>  
>  #include "utils.h"
>  #include "kerncompat.h"
>  #include "ctree.h"
> +#include "string_table.h"
>  
>  #include "commands.h"
>  
> @@ -44,6 +46,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
>   * PAY ATTENTION: it return a statically buffer
> @@ -514,3 +523,422 @@ 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 = group_type_str(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 = group_profile_str(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;
> +			u64 devid = disks_info_ptr[i].devid;
> +			int	j;
> +			u64 size = 0;
> +
> +			for (j = 0 ; j < chunks_info_count ; j++) {
> +				if (chunks_info_ptr[j].type != flags )
> +						continue;
> +				if (chunks_info_ptr[j].devid != devid)
> +						continue;
> +
> +				size += calc_chunk_size(chunks_info_ptr+j);
> +			}
> +
> +			if (size)
> +				table_printf(matrix, col, i+3,
> +					">%s", df_pretty_sizes(size, mode));
> +			else
> +				table_printf(matrix, col, i+3, ">-");
> +
> +			total_allocated += size;
> +			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;
> +
> +		for (j = 0 ; j < info_count ; j++)
> +			if (info_ptr[j].devid == disks_info_ptr[i].devid)
> +				total += calc_chunk_size(info_ptr+j);
> +
> +		printf("   %s\t%10s\n",
> +			disks_info_ptr[i].path,
> +			df_pretty_sizes(disks_info_ptr[i].size - total, mode));
> +
> +	}
> +
> +}
> +
> +/*
> + *  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;
> +
> +		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)
> +			printf("   %s\t%10s\n",
> +				disks_info_ptr[i].path,
> +				df_pretty_sizes(total, mode));
> +	}
> +}
> +
> +/*
> + *  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= group_type_str(flags);
> +		r_mode = group_profile_str(flags);
> +
> +		printf("%s,%s: Size:%s, ",
> +			description,
> +			r_mode,
> +			df_pretty_sizes(sargs->spaces[i].total_bytes ,
> +			    mode));
> +		printf("Used:%s\n",
> +			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:
> +
> +	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;
> +		DIR	*dirstream = NULL;
> +		if (more_than_one)
> +			printf("\n");
> +
> +		fd = open_file_or_dir(argv[i], &dirstream);
> +		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_file_or_dir(fd, dirstream);
> +
> +		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 fc85eef..d4cab63 100644
> --- a/cmds-filesystem.c
> +++ b/cmds-filesystem.c
> @@ -798,6 +798,9 @@ 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 },
> +

Same here, I'd suggest "du" or "usage".
As currently in the patch it's not shorthand-friendly, you basically propose
that people wishing a shorter invocation use it as "btrfs fi di" or "btrfs fi
disk", which seems awkward and not intuitive.
Goffredo Baroncelli Feb. 13, 2014, 7:49 p.m. UTC | #2
Hi Roman

On 02/13/2014 08:28 PM, Roman Mamedov wrote:
> On Thu, 13 Feb 2014 20:19:50 +0100
> Goffredo Baroncelli <kreijack@libero.it> wrote:
> 
[...]
>> diff --git a/cmds-filesystem.c b/cmds-filesystem.c
>> index fc85eef..d4cab63 100644
>> --- a/cmds-filesystem.c
>> +++ b/cmds-filesystem.c
>> @@ -798,6 +798,9 @@ 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 },
>> +
> 
> Same here, I'd suggest "du" or "usage".
> As currently in the patch it's not shorthand-friendly, you basically propose
> that people wishing a shorter invocation use it as "btrfs fi di" or "btrfs fi
> disk", which seems awkward and not intuitive.

Thanks for the comments, however I don't like du not usage; but you are right 
when you don't like "disk-usage". What about "btrfs filesystem chunk-usage" ? The short form would be 

	btrfs fi chunk

which is coherent with the data displayed

Regards
Goffredo

P.S.
Please the next time, cut the email lines which are not relevant to your
replay
Duncan Feb. 13, 2014, 8:22 p.m. UTC | #3
Goffredo Baroncelli posted on Thu, 13 Feb 2014 20:49:08 +0100 as
excerpted:

> Thanks for the comments, however I don't like du not usage; but you are
> right when you don't like "disk-usage". What about "btrfs filesystem
> chunk-usage" ? The short form would be
> 
> 	btrfs fi chunk
> 
> which is coherent with the data displayed

That makes much more sense from a different perspective as well.  "Disk" 
sounds so 20th-century spinning rust..., and at least to me, "du" isn't 
as intuitive or commonly used as "df", so while "df" seems natural to me, 
while I understand where "du" comes from, it still makes me go "huh?" as 
I wouldn't expect to see it in the btrfs context.

So btrfs filesystem chunk(-usage), please. =:^)
Roman Mamedov Feb. 13, 2014, 9 p.m. UTC | #4
On Thu, 13 Feb 2014 20:49:08 +0100
Goffredo Baroncelli <kreijack@libero.it> wrote:

> Thanks for the comments, however I don't like du not usage; but you are right 
> when you don't like "disk-usage". What about "btrfs filesystem chunk-usage" ?

Personally I don't see the point of being super-pedantic here, i.e. "look this
is not just filesystem usage, this is filesystem CHUNK usage"... Consistency
of having a matching "dev usage" and "fi usage" would have been nicer.
Goffredo Baroncelli Feb. 14, 2014, 5:57 p.m. UTC | #5
On 02/13/2014 10:00 PM, Roman Mamedov wrote:
> On Thu, 13 Feb 2014 20:49:08 +0100
> Goffredo Baroncelli <kreijack@libero.it> wrote:
> 
>> Thanks for the comments, however I don't like du not usage; but you are right 
>> when you don't like "disk-usage". What about "btrfs filesystem chunk-usage" ?
> 
> Personally I don't see the point of being super-pedantic here, i.e. "look this
> is not just filesystem usage, this is filesystem CHUNK usage"... Consistency
> of having a matching "dev usage" and "fi usage" would have been nicer.


What about "btrfs filesystem chunk-usage" ?
Roman Mamedov Feb. 14, 2014, 6:11 p.m. UTC | #6
On Fri, 14 Feb 2014 18:57:03 +0100
Goffredo Baroncelli <kreijack@libero.it> wrote:

> On 02/13/2014 10:00 PM, Roman Mamedov wrote:
> > On Thu, 13 Feb 2014 20:49:08 +0100
> > Goffredo Baroncelli <kreijack@libero.it> wrote:
> > 
> >> Thanks for the comments, however I don't like du not usage; but you are right 
> >> when you don't like "disk-usage". What about "btrfs filesystem chunk-usage" ?
> > 
> > Personally I don't see the point of being super-pedantic here, i.e. "look this
> > is not just filesystem usage, this is filesystem CHUNK usage"... Consistency
> > of having a matching "dev usage" and "fi usage" would have been nicer.
> 
> 
> What about "btrfs filesystem chunk-usage" ? 

Uhm? Had to reread this several times, but it looks like you're repeating
exactly the same question that I was already answering in the quoted part.

To clarify even more, personally I'd like if there would have been "btrfs dev
usage" and "btrfs fi usage". Do not see the need to specifically make the 2nd
one "chunk-usage" instead of simply "usage".
Goffredo Baroncelli Feb. 14, 2014, 6:27 p.m. UTC | #7
On 02/14/2014 07:11 PM, Roman Mamedov wrote:
> On Fri, 14 Feb 2014 18:57:03 +0100
> Goffredo Baroncelli <kreijack@libero.it> wrote:
> 
>> On 02/13/2014 10:00 PM, Roman Mamedov wrote:
>>> On Thu, 13 Feb 2014 20:49:08 +0100
>>> Goffredo Baroncelli <kreijack@libero.it> wrote:
>>>
>>>> Thanks for the comments, however I don't like du not usage; but you are right 
>>>> when you don't like "disk-usage". What about "btrfs filesystem chunk-usage" ?
>>>
>>> Personally I don't see the point of being super-pedantic here, i.e. "look this
>>> is not just filesystem usage, this is filesystem CHUNK usage"... Consistency
>>> of having a matching "dev usage" and "fi usage" would have been nicer.
>>
>>
>> What about "btrfs filesystem chunk-usage" ? 
> 
> Uhm? Had to reread this several times, but it looks like you're repeating
> exactly the same question that I was already answering in the quoted part.
> 
> To clarify even more, personally I'd like if there would have been "btrfs dev
> usage" and "btrfs fi usage". Do not see the need to specifically make the 2nd
> one "chunk-usage" instead of simply "usage".

I don't like "usage" because it to me seems to be too much generic.
Because both "btrfs filesystem disk-usage" and "btrfs device disk-usage"
report about chunk (and/or block group) infos, I am investigating 
about 
- btrfs filesystem chunk-usage
- btrfs device chunk-usage

Regards
GB
Hugo Mills Feb. 14, 2014, 6:34 p.m. UTC | #8
On Fri, Feb 14, 2014 at 07:27:57PM +0100, Goffredo Baroncelli wrote:
> On 02/14/2014 07:11 PM, Roman Mamedov wrote:
> > On Fri, 14 Feb 2014 18:57:03 +0100
> > Goffredo Baroncelli <kreijack@libero.it> wrote:
> > 
> >> On 02/13/2014 10:00 PM, Roman Mamedov wrote:
> >>> On Thu, 13 Feb 2014 20:49:08 +0100
> >>> Goffredo Baroncelli <kreijack@libero.it> wrote:
> >>>
> >>>> Thanks for the comments, however I don't like du not usage; but you are right 
> >>>> when you don't like "disk-usage". What about "btrfs filesystem chunk-usage" ?
> >>>
> >>> Personally I don't see the point of being super-pedantic here, i.e. "look this
> >>> is not just filesystem usage, this is filesystem CHUNK usage"... Consistency
> >>> of having a matching "dev usage" and "fi usage" would have been nicer.
> >>
> >>
> >> What about "btrfs filesystem chunk-usage" ? 
> > 
> > Uhm? Had to reread this several times, but it looks like you're repeating
> > exactly the same question that I was already answering in the quoted part.
> > 
> > To clarify even more, personally I'd like if there would have been "btrfs dev
> > usage" and "btrfs fi usage". Do not see the need to specifically make the 2nd
> > one "chunk-usage" instead of simply "usage".
> 
> I don't like "usage" because it to me seems to be too much generic.
> Because both "btrfs filesystem disk-usage" and "btrfs device disk-usage"
> report about chunk (and/or block group) infos, I am investigating 
> about 
> - btrfs filesystem chunk-usage
> - btrfs device chunk-usage

   Most people aren't going to know (or care) what a chunk is. I'm
much happier with Roman's suggestion of btrfs {fi,dev} usage.

   Hugo.
Chris Murphy Feb. 15, 2014, 10:23 p.m. UTC | #9
On Feb 14, 2014, at 11:34 AM, Hugo Mills <hugo@carfax.org.uk> wrote:

> On Fri, Feb 14, 2014 at 07:27:57PM +0100, Goffredo Baroncelli wrote:
>> On 02/14/2014 07:11 PM, Roman Mamedov wrote:
>>> On Fri, 14 Feb 2014 18:57:03 +0100
>>> Goffredo Baroncelli <kreijack@libero.it> wrote:
>>> 
>>>> On 02/13/2014 10:00 PM, Roman Mamedov wrote:
>>>>> On Thu, 13 Feb 2014 20:49:08 +0100
>>>>> Goffredo Baroncelli <kreijack@libero.it> wrote:
>>>>> 
>>>>>> Thanks for the comments, however I don't like du not usage; but you are right 
>>>>>> when you don't like "disk-usage". What about "btrfs filesystem chunk-usage" ?
>>>>> 
>>>>> Personally I don't see the point of being super-pedantic here, i.e. "look this
>>>>> is not just filesystem usage, this is filesystem CHUNK usage"... Consistency
>>>>> of having a matching "dev usage" and "fi usage" would have been nicer.
>>>> 
>>>> 
>>>> What about "btrfs filesystem chunk-usage" ? 
>>> 
>>> Uhm? Had to reread this several times, but it looks like you're repeating
>>> exactly the same question that I was already answering in the quoted part.
>>> 
>>> To clarify even more, personally I'd like if there would have been "btrfs dev
>>> usage" and "btrfs fi usage". Do not see the need to specifically make the 2nd
>>> one "chunk-usage" instead of simply "usage".
>> 
>> I don't like "usage" because it to me seems to be too much generic.
>> Because both "btrfs filesystem disk-usage" and "btrfs device disk-usage"
>> report about chunk (and/or block group) infos, I am investigating 
>> about 
>> - btrfs filesystem chunk-usage
>> - btrfs device chunk-usage
> 
>   Most people aren't going to know (or care) what a chunk is. I'm
> much happier with Roman's suggestion of btrfs {fi,dev} usage.

Or btrfs filesystem examine, or btrfs filesystem detail, which are semi-consistent with mdadm for obtaining similar data.


Chris Murphy--
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 Feb. 17, 2014, 6:09 p.m. UTC | #10
On 02/15/2014 11:23 PM, Chris Murphy wrote:
> 
> On Feb 14, 2014, at 11:34 AM, Hugo Mills <hugo@carfax.org.uk> wrote:
> 
>> On Fri, Feb 14, 2014 at 07:27:57PM +0100, Goffredo Baroncelli wrote:
>>> On 02/14/2014 07:11 PM, Roman Mamedov wrote:
>>>> On Fri, 14 Feb 2014 18:57:03 +0100
>>>> Goffredo Baroncelli <kreijack@libero.it> wrote:
>>>>
>>>>> On 02/13/2014 10:00 PM, Roman Mamedov wrote:
>>>>>> On Thu, 13 Feb 2014 20:49:08 +0100
>>>>>> Goffredo Baroncelli <kreijack@libero.it> wrote:
>>>>>>
>>>>>>> Thanks for the comments, however I don't like du not usage; but you are right 
>>>>>>> when you don't like "disk-usage". What about "btrfs filesystem chunk-usage" ?
>>>>>>
>>>>>> Personally I don't see the point of being super-pedantic here, i.e. "look this
>>>>>> is not just filesystem usage, this is filesystem CHUNK usage"... Consistency
>>>>>> of having a matching "dev usage" and "fi usage" would have been nicer.
>>>>>
>>>>>
>>>>> What about "btrfs filesystem chunk-usage" ? 
>>>>
>>>> Uhm? Had to reread this several times, but it looks like you're repeating
>>>> exactly the same question that I was already answering in the quoted part.
>>>>
>>>> To clarify even more, personally I'd like if there would have been "btrfs dev
>>>> usage" and "btrfs fi usage". Do not see the need to specifically make the 2nd
>>>> one "chunk-usage" instead of simply "usage".
>>>
>>> I don't like "usage" because it to me seems to be too much generic.
>>> Because both "btrfs filesystem disk-usage" and "btrfs device disk-usage"
>>> report about chunk (and/or block group) infos, I am investigating 
>>> about 
>>> - btrfs filesystem chunk-usage
>>> - btrfs device chunk-usage
>>
>>   Most people aren't going to know (or care) what a chunk is. I'm
>> much happier with Roman's suggestion of btrfs {fi,dev} usage.
> 
> Or btrfs filesystem examine, or btrfs filesystem detail, which are
> semi-consistent with mdadm for obtaining similar data.
> 

I have to agree with Chris: looking at the output of "btrfs fi disk-usage"

$ 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   

it is hard to tell that this can be named "filesystem usage". I think that 
"details" or "examine" is a better name.

Regarding "btrfs device usage", it seems to me more coherent. But as 
reported before consistency also matters, so now I am inclined to use
"detail" (or examine) also for "btrfs device"

> 
> Chris Murphy
> 
Regards
Goffredo
David Sterba Feb. 20, 2014, 5:31 p.m. UTC | #11
On Mon, Feb 17, 2014 at 07:09:55PM +0100, Goffredo Baroncelli wrote:
> I have to agree with Chris: looking at the output of "btrfs fi disk-usage"
> 
> $ 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   
> 
> it is hard to tell that this can be named "filesystem usage". I think that 
> "details" or "examine" is a better name.
> 
> Regarding "btrfs device usage", it seems to me more coherent. But as 
> reported before consistency also matters, so now I am inclined to use
> "detail" (or examine) also for "btrfs device"

I'm for 'btrfs filesystem usage' and 'btrfs device usage', IMHO
intuitive, easy to type and remember.
--
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
diff mbox

Patch

diff --git a/cmds-fi-disk_usage.c b/cmds-fi-disk_usage.c
index 4012c78..16b3ab2 100644
--- a/cmds-fi-disk_usage.c
+++ b/cmds-fi-disk_usage.c
@@ -20,10 +20,12 @@ 
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <errno.h>
+#include <stdarg.h>
 
 #include "utils.h"
 #include "kerncompat.h"
 #include "ctree.h"
+#include "string_table.h"
 
 #include "commands.h"
 
@@ -44,6 +46,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
  * PAY ATTENTION: it return a statically buffer
@@ -514,3 +523,422 @@  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 = group_type_str(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 = group_profile_str(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;
+			u64 devid = disks_info_ptr[i].devid;
+			int	j;
+			u64 size = 0;
+
+			for (j = 0 ; j < chunks_info_count ; j++) {
+				if (chunks_info_ptr[j].type != flags )
+						continue;
+				if (chunks_info_ptr[j].devid != devid)
+						continue;
+
+				size += calc_chunk_size(chunks_info_ptr+j);
+			}
+
+			if (size)
+				table_printf(matrix, col, i+3,
+					">%s", df_pretty_sizes(size, mode));
+			else
+				table_printf(matrix, col, i+3, ">-");
+
+			total_allocated += size;
+			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;
+
+		for (j = 0 ; j < info_count ; j++)
+			if (info_ptr[j].devid == disks_info_ptr[i].devid)
+				total += calc_chunk_size(info_ptr+j);
+
+		printf("   %s\t%10s\n",
+			disks_info_ptr[i].path,
+			df_pretty_sizes(disks_info_ptr[i].size - total, mode));
+
+	}
+
+}
+
+/*
+ *  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;
+
+		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)
+			printf("   %s\t%10s\n",
+				disks_info_ptr[i].path,
+				df_pretty_sizes(total, mode));
+	}
+}
+
+/*
+ *  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= group_type_str(flags);
+		r_mode = group_profile_str(flags);
+
+		printf("%s,%s: Size:%s, ",
+			description,
+			r_mode,
+			df_pretty_sizes(sargs->spaces[i].total_bytes ,
+			    mode));
+		printf("Used:%s\n",
+			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:
+
+	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;
+		DIR	*dirstream = NULL;
+		if (more_than_one)
+			printf("\n");
+
+		fd = open_file_or_dir(argv[i], &dirstream);
+		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_file_or_dir(fd, dirstream);
+
+		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 fc85eef..d4cab63 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -798,6 +798,9 @@  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 },
+
 		NULL_CMD_STRUCT
 	}
 };
diff --git a/utils.c b/utils.c
index 69295b5..8b27552 100644
--- a/utils.c
+++ b/utils.c
@@ -2121,3 +2121,66 @@  u64 disk_size(char *path)
 		return sfs.f_bsize * sfs.f_blocks;
 
 }
+
+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 *group_type_str(u64 flag)
+{
+	switch (flag & BTRFS_BLOCK_GROUP_TYPE_MASK) {
+	case BTRFS_BLOCK_GROUP_DATA:
+		return "Data";
+	case BTRFS_BLOCK_GROUP_SYSTEM:
+		return "System";
+	case BTRFS_BLOCK_GROUP_METADATA:
+		return "Metadata";
+	case BTRFS_BLOCK_GROUP_DATA|BTRFS_BLOCK_GROUP_METADATA:
+		return "Data+Metadata";
+	default:
+		return "unknown";
+	}
+}
+
+/*
+ *  Convert a chunk type to a chunk profile description
+ */
+const char *group_profile_str(u64 flag)
+{
+	switch (flag & BTRFS_BLOCK_GROUP_PROFILE_MASK) {
+	case 0:
+		return "single";
+	case BTRFS_BLOCK_GROUP_RAID0:
+		return "RAID0";
+	case BTRFS_BLOCK_GROUP_RAID1:
+		return "RAID1";
+	case BTRFS_BLOCK_GROUP_RAID5:
+		return "RAID5";
+	case BTRFS_BLOCK_GROUP_RAID6:
+		return "RAID6";
+	case BTRFS_BLOCK_GROUP_DUP:
+		return "DUP";
+	case BTRFS_BLOCK_GROUP_RAID10:
+		return "RAID10";
+	default:
+		return "unknown";
+	}
+}
diff --git a/utils.h b/utils.h
index 58fb4a7..8469d4a 100644
--- a/utils.h
+++ b/utils.h
@@ -99,4 +99,7 @@  int get_btrfs_mount(const char *dev, char *mp, size_t mp_size);
 u64 disk_size(char *path);
 int get_device_info(int fd, u64 devid,
 		struct btrfs_ioctl_dev_info_args *di_args);
+u64 get_partition_size(char *dev);
+const char * group_type_str(u64 flags);
+const char * group_profile_str(u64 flags);
 #endif