diff mbox

btrfs-progs: calculate disk space that a subvol could free

Message ID 1380300329-9123-1-git-send-email-anand.jain@oracle.com (mailing list archive)
State Under Review, archived
Headers show

Commit Message

Anand Jain Sept. 27, 2013, 4:45 p.m. UTC
It needs a lot more information about the snapshots if
snapshot's life cycle has to be all auto managed by
scripts _some day_.  this patch is a step towards that.

This patch provides the size which would be freed
if the subvol/snapshot is deleted.
preview:
---------------------
btrfs su show /btrfs/sv1
::
	Unshared space: 	89.09MiB
---------------------

v2: rename to 'unshared space' and edit commit text

Signed-off-by: Anand Jain <anand.jain@oracle.com>
---
 cmds-subvolume.c |   5 ++
 utils.c          | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 utils.h          |   1 +
 3 files changed, 160 insertions(+)

Comments

Zach Brown Sept. 27, 2013, 7:10 p.m. UTC | #1
> diff --git a/cmds-subvolume.c b/cmds-subvolume.c
> index de246ab..0f36cde 100644
> --- a/cmds-subvolume.c
> +++ b/cmds-subvolume.c
> @@ -809,6 +809,7 @@ static int cmd_subvol_show(int argc, char **argv)
>  	int fd = -1, mntfd = -1;
>  	int ret = 1;
>  	DIR *dirstream1 = NULL, *dirstream2 = NULL;
> +	u64 freeable_bytes;
>  
>  	if (check_argc_exact(argc, 2))
>  		usage(cmd_subvol_show_usage);
> @@ -878,6 +879,8 @@ static int cmd_subvol_show(int argc, char **argv)
>  		goto out;
>  	}
>  
> +	freeable_bytes = get_subvol_freeable_bytes(fd);
> +
>  	ret = 0;
>  	/* print the info */
>  	printf("%s\n", fullpath);
> @@ -915,6 +918,8 @@ static int cmd_subvol_show(int argc, char **argv)
>  	else
>  		printf("\tFlags: \t\t\t-\n");
>  
> +	printf("\tUnshared space: \t%s\n",
> +		pretty_size(freeable_bytes));

There's no reason to have a local variable:

	printf("\tUnshared space: \t%s\n",
		pretty_size(get_subvol_freeable_bytes(fd));


>  	printf("\tSnapshot(s):\n");
>  	filter_set = btrfs_list_alloc_filter_set();
> diff --git a/utils.c b/utils.c
> index ccb5199..ca30485 100644
> --- a/utils.c
> +++ b/utils.c
> @@ -2062,3 +2062,157 @@ int lookup_ino_rootid(int fd, u64 *rootid)
>  
>  	return 0;
>  }
> +
> +/* gets the ref count for given extent
> + * 0 = didn't find the item
> + * n = number of references
> +*/
> +u64 get_extent_refcnt(int fd, u64 disk_blk)
> +{
> +	int ret = 0, i, e;
> +	struct btrfs_ioctl_search_args args;
> +	struct btrfs_ioctl_search_key *sk = &args.key;
> +	struct btrfs_ioctl_search_header sh;
> +	unsigned long off = 0;
> +
> +	memset(&args, 0, sizeof(args));
> +
> +	sk->tree_id = BTRFS_EXTENT_TREE_OBJECTID;
> +
> +	sk->min_type = BTRFS_EXTENT_ITEM_KEY;
> +	sk->max_type = BTRFS_EXTENT_ITEM_KEY;
> +
> +	sk->min_objectid = disk_blk;
> +	sk->max_objectid = disk_blk;
> +
> +	sk->max_offset = (u64)-1;
> +	sk->max_transid = (u64)-1;
> +
> +	while (1) {
> +		sk->nr_items = 4096;
> +
> +		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
> +		e = errno;
> +		if (ret < 0) {
> +			fprintf(stderr, "ERROR: search failed - %s\n",
> +				strerror(e));
> +			return 0;
> +		}
> +		if (sk->nr_items == 0)
> +			break;
> +
> +		off = 0;
> +		for (i = 0; i < sk->nr_items; i++) {
> +			struct btrfs_extent_item *ei;
> +			u64 ref;
> +
> +			memcpy(&sh, args.buf + off, sizeof(sh));
> +			off += sizeof(sh);
> +
> +			if (sh.type != BTRFS_EXTENT_ITEM_KEY) {
> +				off += sh.len;
> +				continue;
> +			}
> +
> +			ei = (struct btrfs_extent_item *)(args.buf + off);
> +			ref = btrfs_stack_extent_refs(ei);
> +			return ref;
> +		}
> +		sk->min_objectid = sh.objectid;
> +		sk->min_offset = sh.offset;
> +		sk->min_type = sh.type;
> +		if (sk->min_offset < (u64)-1)
> +			sk->min_offset++;
> +		else if (sk->min_objectid < (u64)-1) {
> +			sk->min_objectid++;
> +			sk->min_offset = 0;
> +			sk->min_type = 0;
> +		} else
> +			break;
> +	}
> +	return 0;
> +}

These two fiddly functions only differ in the tree search and what they
do with each item.  So replace them with a function that takes a
description of the search and calls the caller's callback for each item.

typedef void (*item_func_t)(struct btrfs_key *key, void *data, void *arg);

int btrfs_for_each_item(int fd, min and max and junk,
			item_func_t func, void *arg);

u64 get_subvol_freeable_bytes(int fd)
{
	u64 size_bytes = 0;

	btrfs_for_each_item(fd, ...., sum_extents, &size_bytes);

	return size_bytes;
}

Etc.  You get the idea.

- 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
Anand Jain Sept. 28, 2013, 5:19 p.m. UTC | #2
On 09/28/2013 03:10 AM, Zach Brown wrote:
>> diff --git a/cmds-subvolume.c b/cmds-subvolume.c
>> index de246ab..0f36cde 100644
>> --- a/cmds-subvolume.c
>> +++ b/cmds-subvolume.c
>> @@ -809,6 +809,7 @@ static int cmd_subvol_show(int argc, char **argv)
>>   	int fd = -1, mntfd = -1;
>>   	int ret = 1;
>>   	DIR *dirstream1 = NULL, *dirstream2 = NULL;
>> +	u64 freeable_bytes;
>>
>>   	if (check_argc_exact(argc, 2))
>>   		usage(cmd_subvol_show_usage);
>> @@ -878,6 +879,8 @@ static int cmd_subvol_show(int argc, char **argv)
>>   		goto out;
>>   	}
>>
>> +	freeable_bytes = get_subvol_freeable_bytes(fd);
>> +
>>   	ret = 0;
>>   	/* print the info */
>>   	printf("%s\n", fullpath);
>> @@ -915,6 +918,8 @@ static int cmd_subvol_show(int argc, char **argv)
>>   	else
>>   		printf("\tFlags: \t\t\t-\n");
>>
>> +	printf("\tUnshared space: \t%s\n",
>> +		pretty_size(freeable_bytes));
>
> There's no reason to have a local variable:
>
> 	printf("\tUnshared space: \t%s\n",
> 		pretty_size(get_subvol_freeable_bytes(fd));
>
>
>>   	printf("\tSnapshot(s):\n");
>>   	filter_set = btrfs_list_alloc_filter_set();
>> diff --git a/utils.c b/utils.c
>> index ccb5199..ca30485 100644
>> --- a/utils.c
>> +++ b/utils.c
>> @@ -2062,3 +2062,157 @@ int lookup_ino_rootid(int fd, u64 *rootid)
>>
>>   	return 0;
>>   }
>> +
>> +/* gets the ref count for given extent
>> + * 0 = didn't find the item
>> + * n = number of references
>> +*/
>> +u64 get_extent_refcnt(int fd, u64 disk_blk)
>> +{
>> +	int ret = 0, i, e;
>> +	struct btrfs_ioctl_search_args args;
>> +	struct btrfs_ioctl_search_key *sk = &args.key;
>> +	struct btrfs_ioctl_search_header sh;
>> +	unsigned long off = 0;
>> +
>> +	memset(&args, 0, sizeof(args));
>> +
>> +	sk->tree_id = BTRFS_EXTENT_TREE_OBJECTID;
>> +
>> +	sk->min_type = BTRFS_EXTENT_ITEM_KEY;
>> +	sk->max_type = BTRFS_EXTENT_ITEM_KEY;
>> +
>> +	sk->min_objectid = disk_blk;
>> +	sk->max_objectid = disk_blk;
>> +
>> +	sk->max_offset = (u64)-1;
>> +	sk->max_transid = (u64)-1;
>> +
>> +	while (1) {
>> +		sk->nr_items = 4096;
>> +
>> +		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
>> +		e = errno;
>> +		if (ret < 0) {
>> +			fprintf(stderr, "ERROR: search failed - %s\n",
>> +				strerror(e));
>> +			return 0;
>> +		}
>> +		if (sk->nr_items == 0)
>> +			break;
>> +
>> +		off = 0;
>> +		for (i = 0; i < sk->nr_items; i++) {
>> +			struct btrfs_extent_item *ei;
>> +			u64 ref;
>> +
>> +			memcpy(&sh, args.buf + off, sizeof(sh));
>> +			off += sizeof(sh);
>> +
>> +			if (sh.type != BTRFS_EXTENT_ITEM_KEY) {
>> +				off += sh.len;
>> +				continue;
>> +			}
>> +
>> +			ei = (struct btrfs_extent_item *)(args.buf + off);
>> +			ref = btrfs_stack_extent_refs(ei);
>> +			return ref;
>> +		}
>> +		sk->min_objectid = sh.objectid;
>> +		sk->min_offset = sh.offset;
>> +		sk->min_type = sh.type;
>> +		if (sk->min_offset < (u64)-1)
>> +			sk->min_offset++;
>> +		else if (sk->min_objectid < (u64)-1) {
>> +			sk->min_objectid++;
>> +			sk->min_offset = 0;
>> +			sk->min_type = 0;
>> +		} else
>> +			break;
>> +	}
>> +	return 0;
>> +}
>
> These two fiddly functions only differ in the tree search and what they
> do with each item.  So replace them with a function that takes a
> description of the search and calls the caller's callback for each item.
>
> typedef void (*item_func_t)(struct btrfs_key *key, void *data, void *arg);
>
> int btrfs_for_each_item(int fd, min and max and junk,
> 			item_func_t func, void *arg);
>
> u64 get_subvol_freeable_bytes(int fd)
> {
> 	u64 size_bytes = 0;
>
> 	btrfs_for_each_item(fd, ...., sum_extents, &size_bytes);
>
> 	return size_bytes;
> }
>
> Etc.  You get the idea.


  Will fix them.  Thanks !

-Anand




--
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
Anand Jain Sept. 29, 2013, 3:02 p.m. UTC | #3
>>>
>>> +    printf("\tUnshared space: \t%s\n",
>>> +        pretty_size(freeable_bytes));
>>
>> There's no reason to have a local variable:
>>
>>     printf("\tUnshared space: \t%s\n",
>>         pretty_size(get_subvol_freeable_bytes(fd));
>>
>>

this is taken care.


>> These two fiddly functions only differ in the tree search and what they
>> do with each item.  So replace them with a function that takes a
>> description of the search and calls the caller's callback for each item.
>>
>> typedef void (*item_func_t)(struct btrfs_key *key, void *data, void
>> *arg);
>>
>> int btrfs_for_each_item(int fd, min and max and junk,
>>             item_func_t func, void *arg);
>>
>> u64 get_subvol_freeable_bytes(int fd)
>> {
>>     u64 size_bytes = 0;
>>
>>     btrfs_for_each_item(fd, ...., sum_extents, &size_bytes);
>>
>>     return size_bytes;
>> }
>>
>> Etc.  You get the idea.
>
>
>   Will fix them.  Thanks !
>
> -Anand

  this is to avoid duplicate codes, which indeed spans across
  most of the search functions that's in there in btrfs-progs.
  what you suggest is good, but its better to do that collectively.


Thanks, Anand
--
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. 1, 2013, 1:25 p.m. UTC | #4
On Sun, Sep 29, 2013 at 11:02:29PM +0800, Anand Jain wrote:
>  this is to avoid duplicate codes, which indeed spans across
>  most of the search functions that's in there in btrfs-progs.
>  what you suggest is good, but its better to do that collectively.

Good cleanup idea, added to wiki todo list.
--
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-subvolume.c b/cmds-subvolume.c
index de246ab..0f36cde 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -809,6 +809,7 @@  static int cmd_subvol_show(int argc, char **argv)
 	int fd = -1, mntfd = -1;
 	int ret = 1;
 	DIR *dirstream1 = NULL, *dirstream2 = NULL;
+	u64 freeable_bytes;
 
 	if (check_argc_exact(argc, 2))
 		usage(cmd_subvol_show_usage);
@@ -878,6 +879,8 @@  static int cmd_subvol_show(int argc, char **argv)
 		goto out;
 	}
 
+	freeable_bytes = get_subvol_freeable_bytes(fd);
+
 	ret = 0;
 	/* print the info */
 	printf("%s\n", fullpath);
@@ -915,6 +918,8 @@  static int cmd_subvol_show(int argc, char **argv)
 	else
 		printf("\tFlags: \t\t\t-\n");
 
+	printf("\tUnshared space: \t%s\n",
+		pretty_size(freeable_bytes));
 	/* print the snapshots of the given subvol if any*/
 	printf("\tSnapshot(s):\n");
 	filter_set = btrfs_list_alloc_filter_set();
diff --git a/utils.c b/utils.c
index ccb5199..ca30485 100644
--- a/utils.c
+++ b/utils.c
@@ -2062,3 +2062,157 @@  int lookup_ino_rootid(int fd, u64 *rootid)
 
 	return 0;
 }
+
+/* gets the ref count for given extent
+ * 0 = didn't find the item
+ * n = number of references
+*/
+u64 get_extent_refcnt(int fd, u64 disk_blk)
+{
+	int ret = 0, i, e;
+	struct btrfs_ioctl_search_args args;
+	struct btrfs_ioctl_search_key *sk = &args.key;
+	struct btrfs_ioctl_search_header sh;
+	unsigned long off = 0;
+
+	memset(&args, 0, sizeof(args));
+
+	sk->tree_id = BTRFS_EXTENT_TREE_OBJECTID;
+
+	sk->min_type = BTRFS_EXTENT_ITEM_KEY;
+	sk->max_type = BTRFS_EXTENT_ITEM_KEY;
+
+	sk->min_objectid = disk_blk;
+	sk->max_objectid = disk_blk;
+
+	sk->max_offset = (u64)-1;
+	sk->max_transid = (u64)-1;
+
+	while (1) {
+		sk->nr_items = 4096;
+
+		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+		e = errno;
+		if (ret < 0) {
+			fprintf(stderr, "ERROR: search failed - %s\n",
+				strerror(e));
+			return 0;
+		}
+		if (sk->nr_items == 0)
+			break;
+
+		off = 0;
+		for (i = 0; i < sk->nr_items; i++) {
+			struct btrfs_extent_item *ei;
+			u64 ref;
+
+			memcpy(&sh, args.buf + off, sizeof(sh));
+			off += sizeof(sh);
+
+			if (sh.type != BTRFS_EXTENT_ITEM_KEY) {
+				off += sh.len;
+				continue;
+			}
+
+			ei = (struct btrfs_extent_item *)(args.buf + off);
+			ref = btrfs_stack_extent_refs(ei);
+			return ref;
+		}
+		sk->min_objectid = sh.objectid;
+		sk->min_offset = sh.offset;
+		sk->min_type = sh.type;
+		if (sk->min_offset < (u64)-1)
+			sk->min_offset++;
+		else if (sk->min_objectid < (u64)-1) {
+			sk->min_objectid++;
+			sk->min_offset = 0;
+			sk->min_type = 0;
+		} else
+			break;
+	}
+	return 0;
+}
+
+u64 get_subvol_freeable_bytes(int fd)
+{
+	int ret = 0, i, e;
+	struct btrfs_ioctl_search_args args;
+	struct btrfs_ioctl_search_key *sk = &args.key;
+	struct btrfs_ioctl_search_header sh;
+	unsigned long off = 0;
+	u64 size_bytes = 0;
+
+	memset(&args, 0, sizeof(args));
+
+	sk->tree_id = 0;
+
+	sk->min_type = BTRFS_EXTENT_DATA_KEY;
+	sk->max_type = BTRFS_EXTENT_DATA_KEY;
+
+	sk->max_objectid = (u64) -1;
+	sk->max_offset = (u64)-1;
+	sk->max_transid = (u64)-1;
+
+	while (1) {
+		sk->nr_items = 4096;
+
+		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+		e = errno;
+		if (ret < 0) {
+			fprintf(stderr, "ERROR: search failed - %s\n",
+				strerror(e));
+			return 0;
+		}
+		if (sk->nr_items == 0)
+			break;
+
+		off = 0;
+		for (i = 0; i < sk->nr_items; i++) {
+			struct btrfs_file_extent_item *efi;
+			u64 disk_bytenr = 0;
+			u64 num_bytes = 0;
+			u64 refcnt;
+			u8 type;
+
+			memcpy(&sh, args.buf + off, sizeof(sh));
+			off += sizeof(sh);
+
+			if (sh.type != BTRFS_EXTENT_DATA_KEY) {
+				off += sh.len;
+				continue;
+			}
+
+			efi = (struct btrfs_file_extent_item *)(args.buf + off);
+			type = btrfs_stack_file_extent_type(efi);
+
+			if (type == BTRFS_FILE_EXTENT_INLINE) {
+				size_bytes +=
+					btrfs_stack_file_extent_ram_bytes(efi);
+				goto skip_extent_data;
+			}
+			disk_bytenr = btrfs_stack_file_extent_disk_bytenr(efi);
+			num_bytes = btrfs_stack_file_extent_num_bytes(efi);
+
+			if (disk_bytenr) {
+				refcnt = get_extent_refcnt(fd, disk_bytenr);
+				if (refcnt == 1)
+					size_bytes += num_bytes;
+			}
+skip_extent_data:
+			off += sh.len;
+		}
+		sk->min_objectid = sh.objectid;
+		sk->min_offset = sh.offset;
+		sk->min_type = sh.type;
+
+		if (sk->min_offset < (u64)-1)
+			sk->min_offset++;
+		else if (sk->min_objectid < (u64)-1) {
+			sk->min_objectid++;
+			sk->min_offset = 0;
+			sk->min_type = 0;
+		} else
+			break;
+	}
+	return size_bytes;
+}
diff --git a/utils.h b/utils.h
index 0f31db7..4ddcf09 100644
--- a/utils.h
+++ b/utils.h
@@ -91,5 +91,6 @@  int csum_tree_block(struct btrfs_root *root, struct extent_buffer *buf,
 int ask_user(char *question);
 int lookup_ino_rootid(int fd, u64 *rootid);
 int btrfs_scan_lblkid(int update_kernel);
+u64 get_subvol_freeable_bytes(int fd);
 
 #endif