[PATCH] vfs_btrfs: add qgroup-aware disk free support
Justin Maggard
jmaggard10 at gmail.com
Thu Jan 14 00:27:48 UTC 2016
Add support for qgroup-aware representation of disk free statistics to
vfs_btrfs. If max_rfer has been defined on the subvolume, use metrics
from qgroups to represent disk free.
This can be disabled by setting "btrfs:qgroup dfree" to false in smb.conf.
Signed-off-by: Justin Maggard <jmaggard at netgear.com>
---
source3/modules/vfs_btrfs.c | 170 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 170 insertions(+)
diff --git a/source3/modules/vfs_btrfs.c b/source3/modules/vfs_btrfs.c
index bd95637..435f08f 100644
--- a/source3/modules/vfs_btrfs.c
+++ b/source3/modules/vfs_btrfs.c
@@ -50,7 +50,13 @@ static uint32_t btrfs_fs_capabilities(struct vfs_handle_struct *handle,
#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
#define BTRFS_SUBVOL_NAME_MAX 4039
+#define BTRFS_INO_LOOKUP_PATH_MAX 4080
#define BTRFS_PATH_NAME_MAX 4087
+#define BTRFS_FIRST_FREE_OBJECTID 256ULL
+#define BTRFS_QUOTA_TREE_OBJECTID 8ULL
+#define BTRFS_QGROUP_INFO_KEY 242
+#define BTRFS_QGROUP_LIMIT_KEY 244
+#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key))
struct btrfs_ioctl_vol_args_v2 {
int64_t fd;
uint64_t transid;
@@ -70,11 +76,65 @@ struct btrfs_ioctl_clone_range_args {
uint64_t dest_offset;
};
+struct btrfs_ioctl_search_key {
+ uint64_t tree_id;
+ uint64_t min_objectid;
+ uint64_t max_objectid;
+ uint64_t min_offset;
+ uint64_t max_offset;
+ uint64_t min_transid;
+ uint64_t max_transid;
+ uint32_t min_type;
+ uint32_t max_type;
+ uint32_t nr_items;
+ uint32_t unused;
+ uint64_t unused1[4];
+};
+
+struct btrfs_ioctl_search_header {
+ uint64_t transid;
+ uint64_t objectid;
+ uint64_t offset;
+ uint32_t type;
+ uint32_t len;
+} __attribute__((may_alias));
+
+struct btrfs_ioctl_search_args {
+ struct btrfs_ioctl_search_key key;
+ char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
+};
+
+struct btrfs_ioctl_ino_lookup_args {
+ uint64_t treeid;
+ uint64_t objectid;
+ char name[BTRFS_INO_LOOKUP_PATH_MAX];
+};
+
+struct btrfs_qgroup_info_item {
+ uint64_t generation;
+ uint64_t referenced;
+ uint64_t referenced_compressed;
+ uint64_t exclusive;
+ uint64_t exclusive_compressed;
+} __attribute__ ((__packed__, may_alias));
+
+struct btrfs_qgroup_limit_item {
+ uint64_t flags;
+ uint64_t max_referenced;
+ uint64_t max_exclusive;
+ uint64_t rsv_referenced;
+ uint64_t rsv_exclusive;
+} __attribute__ ((__packed__, may_alias));
+
#define BTRFS_IOCTL_MAGIC 0x94
#define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
struct btrfs_ioctl_clone_range_args)
#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
+ struct btrfs_ioctl_search_args)
+#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \
+ struct btrfs_ioctl_ino_lookup_args)
#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
struct btrfs_ioctl_vol_args_v2)
@@ -665,6 +725,115 @@ static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
return NT_STATUS_OK;
}
+static uint64_t btrfs_get_path_rootid(int fd)
+{
+ int ret;
+ struct btrfs_ioctl_ino_lookup_args args;
+
+ memset(&args, 0, sizeof(args));
+ args.objectid = BTRFS_FIRST_FREE_OBJECTID;
+
+ ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args);
+ if (ret < 0) {
+ return (uint64_t)ret;
+ }
+ return args.treeid;
+}
+
+static int qgroup_search(int fd, uint64_t *used, uint64_t *quota, uint64_t qgroupid)
+{
+ struct btrfs_ioctl_search_args args;
+ struct btrfs_ioctl_search_key *sk = &args.key;
+ struct btrfs_ioctl_search_header *sh;
+ struct btrfs_qgroup_info_item *info;
+ struct btrfs_qgroup_limit_item *limit;
+ unsigned long off = 0;
+ unsigned int i;
+ int ret;
+
+ memset(&args, 0, sizeof(args));
+ sk->tree_id = BTRFS_QUOTA_TREE_OBJECTID;
+ sk->min_type = BTRFS_QGROUP_INFO_KEY;
+ sk->max_type = BTRFS_QGROUP_LIMIT_KEY;
+ sk->max_objectid = (uint64_t)-1;
+ sk->max_offset = (uint64_t)-1;
+ sk->max_transid = (uint64_t)-1;
+ sk->nr_items = 4096;
+
+ while (true) {
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+ if (ret < 0) {
+ return ret;
+ }
+ if (sk->nr_items == 0) {
+ break;
+ }
+ off = 0;
+ for (i = 0; i < sk->nr_items; i++) {
+ sh = (struct btrfs_ioctl_search_header *)(args.buf +
+ off);
+ off += sizeof(*sh);
+
+ if (sh->type == BTRFS_QGROUP_INFO_KEY) {
+ info = (struct btrfs_qgroup_info_item *)
+ (args.buf + off);
+ if (sh->offset == qgroupid) {
+ *used = BVAL(&info->referenced, 0);
+ }
+ } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
+ limit = (struct btrfs_qgroup_limit_item *)
+ (args.buf + off);
+ if (sh->offset == qgroupid) {
+ *quota = BVAL(&limit->max_referenced, 0);
+ }
+ } else {
+ return 0;
+ }
+
+ off += sh->len;
+ sk->min_type = sh->type;
+ sk->min_offset = sh->offset;
+ sk->min_objectid = sh->objectid;
+ }
+ sk->nr_items = 4096;
+ if (sk->min_offset < (uint64_t)-1) {
+ sk->min_offset++;
+ } else {
+ break;
+ }
+ }
+ return 0;
+}
+
+static uint64_t btrfs_disk_free(vfs_handle_struct *handle,
+ const char *path, uint64_t *bsize,
+ uint64_t *dfree, uint64_t *dsize)
+{
+ int ret = -1, fd;
+ uint64_t used = UINT64_MAX, quota = 0, id;
+
+ if (lp_parm_bool(-1, "btrfs", "qgroup dfree", true)) {
+ become_root();
+ if ((fd = open(path, 0)) >= 0) {
+ id = btrfs_get_path_rootid(fd);
+ if (id != (uint64_t)-1) {
+ ret = qgroup_search(fd, &used, "a, id);
+ }
+ close(fd);
+ }
+ unbecome_root();
+ }
+ if (ret || used == UINT64_MAX || !quota) {
+ return SMB_VFS_NEXT_DISK_FREE(handle, path,
+ bsize, dfree, dsize);
+ }
+ *bsize = 4096;
+ *dfree = (quota - used)/4096;
+ *dsize = quota/4096;
+ return (quota - used)/1024;
+}
+
+
static struct vfs_fn_pointers btrfs_fns = {
.fs_capabilities_fn = btrfs_fs_capabilities,
.copy_chunk_send_fn = btrfs_copy_chunk_send,
@@ -674,6 +843,7 @@ static struct vfs_fn_pointers btrfs_fns = {
.snap_check_path_fn = btrfs_snap_check_path,
.snap_create_fn = btrfs_snap_create,
.snap_delete_fn = btrfs_snap_delete,
+ .disk_free_fn = btrfs_disk_free,
};
NTSTATUS vfs_btrfs_init(void);
--
2.7.0
More information about the samba-technical
mailing list