diff mbox

[3/3] btrfs-progs: Add command to check if balance op is req

Message ID 1476750915-3105-4-git-send-email-divya.indi@oracle.com (mailing list archive)
State New, archived
Headers show

Commit Message

Divya Indi Oct. 18, 2016, 12:35 a.m. UTC
Add new subcommand to btrfs inspect-internal

btrfs inspect-internal balance_check <path>
Checks whether 'btrfs balance' can help creating more space (Only
considers data block groups).

Signed-off-by: Divya Indi <divya.indi@oracle.com>
Reviewed-by: Ashish Samant <ashish.samant@oracle.com>
Reviewed-by: Liu Bo <bo.li.liu@oracle.com>
---
 cmds-inspect.c |  147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 147 insertions(+), 0 deletions(-)

Comments

Qu Wenruo Oct. 18, 2016, 1:42 a.m. UTC | #1
At 10/18/2016 08:35 AM, Divya Indi wrote:
> Add new subcommand to btrfs inspect-internal
>
> btrfs inspect-internal balance_check <path>
> Checks whether 'btrfs balance' can help creating more space (Only
> considers data block groups).

I didn't think it's good to add a new subcommand just for that.

Why not output such relocation sugguestion for you previous bg-analyze 
subcommand?
(It's better to make it a parameter to trigger such output)

Thanks,
Qu
>
> Signed-off-by: Divya Indi <divya.indi@oracle.com>
> Reviewed-by: Ashish Samant <ashish.samant@oracle.com>
> Reviewed-by: Liu Bo <bo.li.liu@oracle.com>
> ---
>  cmds-inspect.c |  147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 147 insertions(+), 0 deletions(-)
>
> diff --git a/cmds-inspect.c b/cmds-inspect.c
> index 0e2f15a..5baaa49 100644
> --- a/cmds-inspect.c
> +++ b/cmds-inspect.c
> @@ -267,6 +267,151 @@ static const char * const cmd_inspect_inode_resolve_usage[] = {
>  	NULL
>  };
>
> +static const char * const cmd_inspect_balance_check_usage[] = {
> +	"btrfs inspect-internal balance_check <path>",
> +	"To check whether 'btrfs balance' can help creating more space",
> +	"",
> +	"",
> +	NULL
> +};
> +
> +static int cmd_inspect_balance_check(int argc, char **argv)
> +{
> +	struct btrfs_ioctl_search_args args;
> +	struct btrfs_ioctl_search_args bg_args;
> +	struct btrfs_ioctl_search_key *sk;
> +	struct btrfs_ioctl_search_key *bg_sk;
> +	struct btrfs_ioctl_search_header *header;
> +	struct btrfs_ioctl_search_header *bg_header;
> +	struct btrfs_block_group_item *bg;
> +	struct btrfs_chunk *chunk;
> +	unsigned long off = 0;
> +	unsigned long bg_off = 0;
> +	DIR *dirstream = NULL;
> +	int fd;
> +	int i;
> +	u64 total_free = 0;
> +	u64 min_used = (u64)-1;
> +	u64 free_of_min_used = 0;
> +	u64 bg_of_min_used = 0;
> +	u64 flags;
> +	u64 used;
> +	int ret = 0;
> +	int nr_data_bgs = 0;
> +
> +	if (check_argc_exact(argc, 2))
> +		usage(cmd_inspect_balance_check_usage);
> +
> +	fd = btrfs_open_dir(argv[optind], &dirstream, 1);
> +	if (fd < 0)
> +		return 1;
> +
> +	memset(&args, 0, sizeof(args));
> +	sk = &args.key;
> +	sk->min_offset = sk->min_transid = 0;
> +	sk->max_offset = sk->max_transid = (u64)-1;
> +
> +	printf("%20s%20s%20s\n", "Start", "Len", "Used");
> +	while (1) {
> +		ret = get_chunks(fd, &args);
> +		if (ret < 0)
> +			goto out;
> +
> +		/*
> +		 * it should not happen.
> +		 */
> +		if (sk->nr_items == 0)
> +			break;
> +
> +		off = 0;
> +		memset(&bg_args, 0, sizeof(bg_args));
> +		bg_sk = &bg_args.key;
> +
> +		/* For every chunk, look up 1 exact match for block group in
> +		 * the extent tree. */
> +		bg_sk->tree_id = BTRFS_EXTENT_TREE_OBJECTID;
> +		bg_sk->min_type = BTRFS_BLOCK_GROUP_ITEM_KEY;
> +		bg_sk->max_type = BTRFS_BLOCK_GROUP_ITEM_KEY;
> +		bg_sk->min_transid =  0;
> +		bg_sk->max_transid = (u64)-1;
> +
> +		for (i = 0; i < sk->nr_items; i++) {
> +			header = (struct btrfs_ioctl_search_header *)(args.buf
> +								      + off);
> +			off += sizeof(*header);
> +			if (header->type == BTRFS_CHUNK_ITEM_KEY) {
> +				chunk = (struct btrfs_chunk *)
> +					(args.buf + off);
> +				ret = get_bg_info(fd, &bg_args, header->offset,
> +						  chunk->length);
> +				if (ret < 0)
> +					goto out;
> +
> +				/*
> +				 * it should not happen.
> +				 */
> +				if (bg_sk->nr_items == 0)
> +					continue;
> +
> +				bg_off = 0;
> +				bg_header = (struct btrfs_ioctl_search_header *)
> +					    (bg_args.buf + bg_off);
> +				bg_off += sizeof(*bg_header);
> +				bg = (struct btrfs_block_group_item *)
> +				     (bg_args.buf + bg_off);
> +
> +				flags = btrfs_block_group_flags(bg);
> +				if (flags & BTRFS_BLOCK_GROUP_DATA) {
> +					used = btrfs_block_group_used(bg);
> +					nr_data_bgs++;
> +					printf("%20llu%20s%20s\n",
> +						bg_header->objectid,
> +						pretty_size(bg_header->offset),
> +						pretty_size(used));
> +					total_free += bg_header->offset - used;
> +					if (min_used >= used) {
> +						min_used = used;
> +						free_of_min_used =
> +							bg_header->offset - used;
> +						bg_of_min_used =
> +							bg_header->objectid;
> +					}
> +				}
> +			}
> +
> +			off += header->len;
> +			sk->min_offset = header->offset + header->len;
> +		}
> +		sk->nr_items = 4096;
> +
> +	}
> +
> +	if (nr_data_bgs <= 1) {
> +		printf("Data block groups in fs = %d, no need to do balance.\n",
> +				nr_data_bgs);
> +		ret = 1;
> +		goto out;
> +	}
> +
> +	printf("Total data bgs: %d\nTotal free space: %s\n"
> +	       "For min used bg %llu used = %s free = %s\n",
> +		nr_data_bgs, pretty_size(total_free), bg_of_min_used,
> +		pretty_size(min_used), pretty_size(free_of_min_used));
> +
> +	if (total_free - free_of_min_used > min_used) {
> +		printf("run 'btrfs balance start -dvrange=%llu..%llu <mountpoint>'\n",
> +				bg_of_min_used, bg_of_min_used + 1);
> +		ret = 0;
> +	} else {
> +		printf("Please don't balance data block groups, no free space\n");
> +		ret = 1;
> +	}
> +
> +out:
> +	close_file_or_dir(fd, dirstream);
> +	return ret;
> +}
> +
>  static int cmd_inspect_inode_resolve(int argc, char **argv)
>  {
>  	int fd;
> @@ -816,6 +961,8 @@ const struct cmd_group inspect_cmd_group = {
>  			cmd_inspect_min_dev_size_usage, NULL, 0 },
>  		{ "bg_analysis", cmd_inspect_bg_analysis,
>  			cmd_inspect_bg_analysis_usage, NULL, 0 },
> +		{ "balance_check", cmd_inspect_balance_check,
> +			cmd_inspect_balance_check_usage, NULL, 0 },
>  		{ "dump-tree", cmd_inspect_dump_tree,
>  				cmd_inspect_dump_tree_usage, NULL, 0 },
>  		{ "dump-super", cmd_inspect_dump_super,
>


--
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
Divya Indi Oct. 19, 2016, 5:08 p.m. UTC | #2
On 10/17/2016 06:42 PM, Qu Wenruo wrote:
>
>
> At 10/18/2016 08:35 AM, Divya Indi wrote:
>> Add new subcommand to btrfs inspect-internal
>>
>> btrfs inspect-internal balance_check <path>
>> Checks whether 'btrfs balance' can help creating more space (Only
>> considers data block groups).
>
> I didn't think it's good to add a new subcommand just for that.
>
> Why not output such relocation sugguestion for you previous bg-analyze 
> subcommand?
> (It's better to make it a parameter to trigger such output)
>
> Thanks,
> Qu
Or maybe as an option to btrfs balance start?
Eg: btrfs balance start --check-only <path>
>>
>> Signed-off-by: Divya Indi <divya.indi@oracle.com>
>> Reviewed-by: Ashish Samant <ashish.samant@oracle.com>
>> Reviewed-by: Liu Bo <bo.li.liu@oracle.com>
>> ---
>>  cmds-inspect.c |  147 
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 files changed, 147 insertions(+), 0 deletions(-)
>>
>> diff --git a/cmds-inspect.c b/cmds-inspect.c
>> index 0e2f15a..5baaa49 100644
>> --- a/cmds-inspect.c
>> +++ b/cmds-inspect.c
>> @@ -267,6 +267,151 @@ static const char * const 
>> cmd_inspect_inode_resolve_usage[] = {
>>      NULL
>>  };
>>
>> +static const char * const cmd_inspect_balance_check_usage[] = {
>> +    "btrfs inspect-internal balance_check <path>",
>> +    "To check whether 'btrfs balance' can help creating more space",
>> +    "",
>> +    "",
>> +    NULL
>> +};
>> +
>> +static int cmd_inspect_balance_check(int argc, char **argv)
>> +{
>> +    struct btrfs_ioctl_search_args args;
>> +    struct btrfs_ioctl_search_args bg_args;
>> +    struct btrfs_ioctl_search_key *sk;
>> +    struct btrfs_ioctl_search_key *bg_sk;
>> +    struct btrfs_ioctl_search_header *header;
>> +    struct btrfs_ioctl_search_header *bg_header;
>> +    struct btrfs_block_group_item *bg;
>> +    struct btrfs_chunk *chunk;
>> +    unsigned long off = 0;
>> +    unsigned long bg_off = 0;
>> +    DIR *dirstream = NULL;
>> +    int fd;
>> +    int i;
>> +    u64 total_free = 0;
>> +    u64 min_used = (u64)-1;
>> +    u64 free_of_min_used = 0;
>> +    u64 bg_of_min_used = 0;
>> +    u64 flags;
>> +    u64 used;
>> +    int ret = 0;
>> +    int nr_data_bgs = 0;
>> +
>> +    if (check_argc_exact(argc, 2))
>> +        usage(cmd_inspect_balance_check_usage);
>> +
>> +    fd = btrfs_open_dir(argv[optind], &dirstream, 1);
>> +    if (fd < 0)
>> +        return 1;
>> +
>> +    memset(&args, 0, sizeof(args));
>> +    sk = &args.key;
>> +    sk->min_offset = sk->min_transid = 0;
>> +    sk->max_offset = sk->max_transid = (u64)-1;
>> +
>> +    printf("%20s%20s%20s\n", "Start", "Len", "Used");
>> +    while (1) {
>> +        ret = get_chunks(fd, &args);
>> +        if (ret < 0)
>> +            goto out;
>> +
>> +        /*
>> +         * it should not happen.
>> +         */
>> +        if (sk->nr_items == 0)
>> +            break;
>> +
>> +        off = 0;
>> +        memset(&bg_args, 0, sizeof(bg_args));
>> +        bg_sk = &bg_args.key;
>> +
>> +        /* For every chunk, look up 1 exact match for block group in
>> +         * the extent tree. */
>> +        bg_sk->tree_id = BTRFS_EXTENT_TREE_OBJECTID;
>> +        bg_sk->min_type = BTRFS_BLOCK_GROUP_ITEM_KEY;
>> +        bg_sk->max_type = BTRFS_BLOCK_GROUP_ITEM_KEY;
>> +        bg_sk->min_transid =  0;
>> +        bg_sk->max_transid = (u64)-1;
>> +
>> +        for (i = 0; i < sk->nr_items; i++) {
>> +            header = (struct btrfs_ioctl_search_header *)(args.buf
>> +                                      + off);
>> +            off += sizeof(*header);
>> +            if (header->type == BTRFS_CHUNK_ITEM_KEY) {
>> +                chunk = (struct btrfs_chunk *)
>> +                    (args.buf + off);
>> +                ret = get_bg_info(fd, &bg_args, header->offset,
>> +                          chunk->length);
>> +                if (ret < 0)
>> +                    goto out;
>> +
>> +                /*
>> +                 * it should not happen.
>> +                 */
>> +                if (bg_sk->nr_items == 0)
>> +                    continue;
>> +
>> +                bg_off = 0;
>> +                bg_header = (struct btrfs_ioctl_search_header *)
>> +                        (bg_args.buf + bg_off);
>> +                bg_off += sizeof(*bg_header);
>> +                bg = (struct btrfs_block_group_item *)
>> +                     (bg_args.buf + bg_off);
>> +
>> +                flags = btrfs_block_group_flags(bg);
>> +                if (flags & BTRFS_BLOCK_GROUP_DATA) {
>> +                    used = btrfs_block_group_used(bg);
>> +                    nr_data_bgs++;
>> +                    printf("%20llu%20s%20s\n",
>> +                        bg_header->objectid,
>> +                        pretty_size(bg_header->offset),
>> +                        pretty_size(used));
>> +                    total_free += bg_header->offset - used;
>> +                    if (min_used >= used) {
>> +                        min_used = used;
>> +                        free_of_min_used =
>> +                            bg_header->offset - used;
>> +                        bg_of_min_used =
>> +                            bg_header->objectid;
>> +                    }
>> +                }
>> +            }
>> +
>> +            off += header->len;
>> +            sk->min_offset = header->offset + header->len;
>> +        }
>> +        sk->nr_items = 4096;
>> +
>> +    }
>> +
>> +    if (nr_data_bgs <= 1) {
>> +        printf("Data block groups in fs = %d, no need to do 
>> balance.\n",
>> +                nr_data_bgs);
>> +        ret = 1;
>> +        goto out;
>> +    }
>> +
>> +    printf("Total data bgs: %d\nTotal free space: %s\n"
>> +           "For min used bg %llu used = %s free = %s\n",
>> +        nr_data_bgs, pretty_size(total_free), bg_of_min_used,
>> +        pretty_size(min_used), pretty_size(free_of_min_used));
>> +
>> +    if (total_free - free_of_min_used > min_used) {
>> +        printf("run 'btrfs balance start -dvrange=%llu..%llu 
>> <mountpoint>'\n",
>> +                bg_of_min_used, bg_of_min_used + 1);
>> +        ret = 0;
>> +    } else {
>> +        printf("Please don't balance data block groups, no free 
>> space\n");
>> +        ret = 1;
>> +    }
>> +
>> +out:
>> +    close_file_or_dir(fd, dirstream);
>> +    return ret;
>> +}
>> +
>>  static int cmd_inspect_inode_resolve(int argc, char **argv)
>>  {
>>      int fd;
>> @@ -816,6 +961,8 @@ const struct cmd_group inspect_cmd_group = {
>>              cmd_inspect_min_dev_size_usage, NULL, 0 },
>>          { "bg_analysis", cmd_inspect_bg_analysis,
>>              cmd_inspect_bg_analysis_usage, NULL, 0 },
>> +        { "balance_check", cmd_inspect_balance_check,
>> +            cmd_inspect_balance_check_usage, NULL, 0 },
>>          { "dump-tree", cmd_inspect_dump_tree,
>>                  cmd_inspect_dump_tree_usage, NULL, 0 },
>>          { "dump-super", cmd_inspect_dump_super,
>>
>
>

--
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
David Sterba Oct. 28, 2016, 3:20 p.m. UTC | #3
On Wed, Oct 19, 2016 at 10:08:23AM -0700, Divya Indi wrote:
> On 10/17/2016 06:42 PM, Qu Wenruo wrote:
> > At 10/18/2016 08:35 AM, Divya Indi wrote:
> >> Add new subcommand to btrfs inspect-internal
> >>
> >> btrfs inspect-internal balance_check <path>
> >> Checks whether 'btrfs balance' can help creating more space (Only
> >> considers data block groups).
> >
> > I didn't think it's good to add a new subcommand just for that.
> >
> > Why not output such relocation sugguestion for you previous bg-analyze 
> > subcommand?
> > (It's better to make it a parameter to trigger such output)
> Or maybe as an option to btrfs balance start?
> Eg: btrfs balance start --check-only <path>

I tend to agree with this approach. The usecase, with some random sample
balance options:

 $ btrfs balance start --analyze -dusage=10 -musage=5 /path

returns if the command would be able to make any progress according to
the given filters and then

 $ btrfs balance start -dusage=10 -musage=5 /path

would actually do that. This is inherently racy, as the analysis will
happen in userspace and the filesystem block group layout could change
in the meantime.

This also implies that the filters are implemented in the userspace,
duplicating the kernel code. This is not neccesarily bad, as this would
work even on older kernels (with new progs), compared to a new analysis
mode of balance implemented in kernel.
--
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
Graham Cobb Oct. 28, 2016, 4:29 p.m. UTC | #4
On 28/10/16 16:20, David Sterba wrote:
> I tend to agree with this approach. The usecase, with some random sample
> balance options:
> 
>  $ btrfs balance start --analyze -dusage=10 -musage=5 /path

Wouldn't a "balance analyze" command be better than "balance start
--analyze"? I would have guessed the latter started the balance but
printed some analysis as well (before or, probably more usefully,
afterwards).

There might, of course, be some point in a (future)

$ btrfs balance start --if-needed -dusage=10 -musage=5 /path

command.

--
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
Qu Wenruo Oct. 30, 2016, 2:10 p.m. UTC | #5
On 10/28/2016 11:20 PM, David Sterba wrote:
> On Wed, Oct 19, 2016 at 10:08:23AM -0700, Divya Indi wrote:
>> On 10/17/2016 06:42 PM, Qu Wenruo wrote:
>>> At 10/18/2016 08:35 AM, Divya Indi wrote:
>>>> Add new subcommand to btrfs inspect-internal
>>>>
>>>> btrfs inspect-internal balance_check <path>
>>>> Checks whether 'btrfs balance' can help creating more space (Only
>>>> considers data block groups).
>>>
>>> I didn't think it's good to add a new subcommand just for that.
>>>
>>> Why not output such relocation sugguestion for you previous bg-analyze
>>> subcommand?
>>> (It's better to make it a parameter to trigger such output)
>> Or maybe as an option to btrfs balance start?
>> Eg: btrfs balance start --check-only <path>
>
> I tend to agree with this approach. The usecase, with some random sample
> balance options:
>
>  $ btrfs balance start --analyze -dusage=10 -musage=5 /path

This use case is better than my original idea.
It is really useful then.

+1 for this approach.

Thanks,
Qu
>
> returns if the command would be able to make any progress according to
> the given filters and then
>
>  $ btrfs balance start -dusage=10 -musage=5 /path
>
> would actually do that. This is inherently racy, as the analysis will
> happen in userspace and the filesystem block group layout could change
> in the meantime.
>
> This also implies that the filters are implemented in the userspace,
> duplicating the kernel code. This is not neccesarily bad, as this would
> work even on older kernels (with new progs), compared to a new analysis
> mode of balance implemented in kernel.
--
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
David Sterba Oct. 31, 2016, 4:33 p.m. UTC | #6
On Fri, Oct 28, 2016 at 05:29:45PM +0100, Graham Cobb wrote:
> On 28/10/16 16:20, David Sterba wrote:
> > I tend to agree with this approach. The usecase, with some random sample
> > balance options:
> > 
> >  $ btrfs balance start --analyze -dusage=10 -musage=5 /path
> 
> Wouldn't a "balance analyze" command be better than "balance start
> --analyze"? I would have guessed the latter started the balance but
> printed some analysis as well (before or, probably more usefully,
> afterwards).

Right, separate command seems better.
--
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
Divya Indi Nov. 2, 2016, 12:39 a.m. UTC | #7
On 10/31/2016 09:33 AM, David Sterba wrote:
> On Fri, Oct 28, 2016 at 05:29:45PM +0100, Graham Cobb wrote:
>> On 28/10/16 16:20, David Sterba wrote:
>>> I tend to agree with this approach. The usecase, with some random sample
>>> balance options:
>>>
>>>   $ btrfs balance start --analyze -dusage=10 -musage=5 /path
>> Wouldn't a "balance analyze" command be better than "balance start
>> --analyze"? I would have guessed the latter started the balance but
>> printed some analysis as well (before or, probably more usefully,
>> afterwards).
> Right, separate command seems better.
What about btrfs inspect-internal bg_analysis (new name: 
show-block-groups)? It can still be a subcommand for inspect-internal?
So, wel have 2 new sub commands:
1) btrfs balance analyze
2) btrfs inspect-internal show-block-groups
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/cmds-inspect.c b/cmds-inspect.c
index 0e2f15a..5baaa49 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -267,6 +267,151 @@  static const char * const cmd_inspect_inode_resolve_usage[] = {
 	NULL
 };
 
+static const char * const cmd_inspect_balance_check_usage[] = {
+	"btrfs inspect-internal balance_check <path>",
+	"To check whether 'btrfs balance' can help creating more space",
+	"",
+	"",
+	NULL
+};
+
+static int cmd_inspect_balance_check(int argc, char **argv)
+{
+	struct btrfs_ioctl_search_args args;
+	struct btrfs_ioctl_search_args bg_args;
+	struct btrfs_ioctl_search_key *sk;
+	struct btrfs_ioctl_search_key *bg_sk;
+	struct btrfs_ioctl_search_header *header;
+	struct btrfs_ioctl_search_header *bg_header;
+	struct btrfs_block_group_item *bg;
+	struct btrfs_chunk *chunk;
+	unsigned long off = 0;
+	unsigned long bg_off = 0;
+	DIR *dirstream = NULL;
+	int fd;
+	int i;
+	u64 total_free = 0;
+	u64 min_used = (u64)-1;
+	u64 free_of_min_used = 0;
+	u64 bg_of_min_used = 0;
+	u64 flags;
+	u64 used;
+	int ret = 0;
+	int nr_data_bgs = 0;
+
+	if (check_argc_exact(argc, 2))
+		usage(cmd_inspect_balance_check_usage);
+
+	fd = btrfs_open_dir(argv[optind], &dirstream, 1);
+	if (fd < 0)
+		return 1;
+
+	memset(&args, 0, sizeof(args));
+	sk = &args.key;
+	sk->min_offset = sk->min_transid = 0;
+	sk->max_offset = sk->max_transid = (u64)-1;
+
+	printf("%20s%20s%20s\n", "Start", "Len", "Used");
+	while (1) {
+		ret = get_chunks(fd, &args);
+		if (ret < 0)
+			goto out;
+
+		/*
+		 * it should not happen.
+		 */
+		if (sk->nr_items == 0)
+			break;
+
+		off = 0;
+		memset(&bg_args, 0, sizeof(bg_args));
+		bg_sk = &bg_args.key;
+
+		/* For every chunk, look up 1 exact match for block group in
+		 * the extent tree. */
+		bg_sk->tree_id = BTRFS_EXTENT_TREE_OBJECTID;
+		bg_sk->min_type = BTRFS_BLOCK_GROUP_ITEM_KEY;
+		bg_sk->max_type = BTRFS_BLOCK_GROUP_ITEM_KEY;
+		bg_sk->min_transid =  0;
+		bg_sk->max_transid = (u64)-1;
+
+		for (i = 0; i < sk->nr_items; i++) {
+			header = (struct btrfs_ioctl_search_header *)(args.buf
+								      + off);
+			off += sizeof(*header);
+			if (header->type == BTRFS_CHUNK_ITEM_KEY) {
+				chunk = (struct btrfs_chunk *)
+					(args.buf + off);
+				ret = get_bg_info(fd, &bg_args, header->offset,
+						  chunk->length);
+				if (ret < 0)
+					goto out;
+
+				/*
+				 * it should not happen.
+				 */
+				if (bg_sk->nr_items == 0)
+					continue;
+
+				bg_off = 0;
+				bg_header = (struct btrfs_ioctl_search_header *)
+					    (bg_args.buf + bg_off);
+				bg_off += sizeof(*bg_header);
+				bg = (struct btrfs_block_group_item *)
+				     (bg_args.buf + bg_off);
+
+				flags = btrfs_block_group_flags(bg);
+				if (flags & BTRFS_BLOCK_GROUP_DATA) {
+					used = btrfs_block_group_used(bg);
+					nr_data_bgs++;
+					printf("%20llu%20s%20s\n",
+						bg_header->objectid,
+						pretty_size(bg_header->offset),
+						pretty_size(used));
+					total_free += bg_header->offset - used;
+					if (min_used >= used) {
+						min_used = used;
+						free_of_min_used =
+							bg_header->offset - used;
+						bg_of_min_used =
+							bg_header->objectid;
+					}
+				}
+			}
+
+			off += header->len;
+			sk->min_offset = header->offset + header->len;
+		}
+		sk->nr_items = 4096;
+
+	}
+
+	if (nr_data_bgs <= 1) {
+		printf("Data block groups in fs = %d, no need to do balance.\n",
+				nr_data_bgs);
+		ret = 1;
+		goto out;
+	}
+
+	printf("Total data bgs: %d\nTotal free space: %s\n"
+	       "For min used bg %llu used = %s free = %s\n",
+		nr_data_bgs, pretty_size(total_free), bg_of_min_used,
+		pretty_size(min_used), pretty_size(free_of_min_used));
+
+	if (total_free - free_of_min_used > min_used) {
+		printf("run 'btrfs balance start -dvrange=%llu..%llu <mountpoint>'\n",
+				bg_of_min_used, bg_of_min_used + 1);
+		ret = 0;
+	} else {
+		printf("Please don't balance data block groups, no free space\n");
+		ret = 1;
+	}
+
+out:
+	close_file_or_dir(fd, dirstream);
+	return ret;
+}
+
 static int cmd_inspect_inode_resolve(int argc, char **argv)
 {
 	int fd;
@@ -816,6 +961,8 @@  const struct cmd_group inspect_cmd_group = {
 			cmd_inspect_min_dev_size_usage, NULL, 0 },
 		{ "bg_analysis", cmd_inspect_bg_analysis,
 			cmd_inspect_bg_analysis_usage, NULL, 0 },
+		{ "balance_check", cmd_inspect_balance_check,
+			cmd_inspect_balance_check_usage, NULL, 0 },
 		{ "dump-tree", cmd_inspect_dump_tree,
 				cmd_inspect_dump_tree_usage, NULL, 0 },
 		{ "dump-super", cmd_inspect_dump_super,