Re: [PATCH 19/24] Btrfs: qgroup implementation and prototypes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Jan,

(2012/05/21 1:06), Jan Schmidt wrote:
> From: Arne Jansen<sensille@xxxxxxx>
> 
> Signed-off-by: Arne Jansen<sensille@xxxxxxx>
> Signed-off-by: Jan Schmidt<list.btrfs@xxxxxxxxxxxxx>
> ---
>   fs/btrfs/Makefile |    2 +-
>   fs/btrfs/ctree.h  |   33 ++
>   fs/btrfs/ioctl.h  |   24 +
>   fs/btrfs/qgroup.c | 1531 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>   4 files changed, 1589 insertions(+), 1 deletions(-)
>   create mode 100644 fs/btrfs/qgroup.c
> 
> diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
> index 0c4fa2b..0bc4d3a 100644
> --- a/fs/btrfs/Makefile
> +++ b/fs/btrfs/Makefile
> @@ -8,7 +8,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
>   	   extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \
>   	   export.o tree-log.o free-space-cache.o zlib.o lzo.o \
>   	   compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \
> -	   reada.o backref.o ulist.o
> +	   reada.o backref.o ulist.o qgroup.o
> 
>   btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
>   btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 2b6f003..0630412 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -3284,6 +3284,39 @@ void btrfs_get_tree_mod_seq(struct btrfs_fs_info *fs_info,
>   void btrfs_put_tree_mod_seq(struct btrfs_fs_info *fs_info,
>   			    struct seq_list *elem);
> 
> +/* qgroup.c */
> +int btrfs_quota_enable(struct btrfs_trans_handle *trans,
> +		       struct btrfs_fs_info *fs_info);
> +int btrfs_quota_disable(struct btrfs_trans_handle *trans,
> +			struct btrfs_fs_info *fs_info);
> +int btrfs_quota_rescan(struct btrfs_fs_info *fs_info);
> +int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
> +			      struct btrfs_fs_info *fs_info, u64 src, u64 dst);
> +int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans,
> +			      struct btrfs_fs_info *fs_info, u64 src, u64 dst);
> +int btrfs_create_qgroup(struct btrfs_trans_handle *trans,
> +			struct btrfs_fs_info *fs_info, u64 qgroupid,
> +			char *name);
> +int btrfs_remove_qgroup(struct btrfs_trans_handle *trans,
> +			      struct btrfs_fs_info *fs_info, u64 qgroupid);
> +int btrfs_limit_qgroup(struct btrfs_trans_handle *trans,
> +		       struct btrfs_fs_info *fs_info, u64 qgroupid,
> +		       struct btrfs_qgroup_limit *limit);
> +int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info);
> +void btrfs_free_qgroup_config(struct btrfs_fs_info *fs_info);
> +struct btrfs_delayed_extent_op;
> +int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans,
> +			    struct btrfs_fs_info *fs_info,
> +			    struct btrfs_delayed_ref_node *node,
> +			    struct btrfs_delayed_extent_op *extent_op);
> +int btrfs_run_qgroups(struct btrfs_trans_handle *trans,
> +		      struct btrfs_fs_info *fs_info);
> +int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
> +			 struct btrfs_fs_info *fs_info, u64 srcid, u64 objectid,
> +			 struct btrfs_qgroup_inherit *inherit);
> +int btrfs_qgroup_reserve(struct btrfs_root *root, u64 num_bytes);
> +void btrfs_qgroup_free(struct btrfs_root *root, u64 num_bytes);
> +
>   static inline int is_fstree(u64 rootid)
>   {
>   	if (rootid == BTRFS_FS_TREE_OBJECTID ||
> diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h
> index 086e6bd..44c34a5 100644
> --- a/fs/btrfs/ioctl.h
> +++ b/fs/btrfs/ioctl.h
> @@ -35,6 +35,30 @@ struct btrfs_ioctl_vol_args {
>   #define BTRFS_FSID_SIZE 16
>   #define BTRFS_UUID_SIZE 16
> 
> +#define BTRFS_QGROUP_INHERIT_SET_LIMITS	(1ULL<<  0)
> +
> +struct btrfs_qgroup_limit {
> +	__u64	flags;
> +	__u64	max_rfer;
> +	__u64	max_excl;
> +	__u64	rsv_rfer;
> +	__u64	rsv_excl;
> +};
> +
> +struct btrfs_qgroup_inherit {
> +	__u64	flags;
> +	__u64	num_qgroups;
> +	__u64	num_ref_copies;
> +	__u64	num_excl_copies;
> +	struct btrfs_qgroup_limit lim;
> +	__u64	qgroups[0];
> +};
> +
> +struct btrfs_ioctl_qgroup_limit_args {
> +	__u64	qgroupid;
> +	struct btrfs_qgroup_limit lim;
> +};
> +
>   #define BTRFS_SUBVOL_NAME_MAX 4039
>   struct btrfs_ioctl_vol_args_v2 {
>   	__s64 fd;
> diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
> new file mode 100644
> index 0000000..678fe45
> --- /dev/null
> +++ b/fs/btrfs/qgroup.c
> @@ -0,0 +1,1531 @@
> +/*
> + * Copyright (C) 2011 STRATO.  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.
> + */
> +
> +#include<linux/sched.h>
> +#include<linux/pagemap.h>
> +#include<linux/writeback.h>
> +#include<linux/blkdev.h>
> +#include<linux/rbtree.h>
> +#include<linux/slab.h>
> +#include<linux/workqueue.h>
> +
> +#include "ctree.h"
> +#include "transaction.h"
> +#include "disk-io.h"
> +#include "locking.h"
> +#include "ulist.h"
> +#include "ioctl.h"
> +#include "backref.h"
> +
> +/* TODO XXX FIXME
> + *  - subvol delete ->  delete when ref goes to 0? delete limits also?
> + *  - reorganize keys
> + *  - compressed
> + *  - sync
> + *  - rescan
> + *  - copy also limits on subvol creation
> + *  - limit
> + *  - caches fuer ulists
> + *  - performance benchmarks
> + *  - check all ioctl parameters
> + */
> +
> +/*
> + * one struct for each qgroup, organized in fs_info->qgroup_tree.
> + */
> +struct btrfs_qgroup {
> +	u64 qgroupid;
> +
> +	/*
> +	 * state
> +	 */
> +	u64 rfer;	/* referenced */
> +	u64 rfer_cmpr;	/* referenced compressed */
> +	u64 excl;	/* exclusive */
> +	u64 excl_cmpr;	/* exclusive compressed */
> +
> +	/*
> +	 * limits
> +	 */
> +	u64 lim_flags;	/* which limits are set */
> +	u64 max_rfer;
> +	u64 max_excl;
> +	u64 rsv_rfer;
> +	u64 rsv_excl;
> +
> +	/*
> +	 * reservation tracking
> +	 */
> +	u64 reserved;
> +
> +	/*
> +	 * lists
> +	 */
> +	struct list_head groups;  /* groups this group is member of */
> +	struct list_head members; /* groups that are members of this group */
> +	struct list_head dirty;   /* dirty groups */
> +	struct rb_node node;	  /* tree of qgroups */
> +
> +	/*
> +	 * temp variables for accounting operations
> +	 */
> +	u64 tag;
> +	u64 refcnt;
> +};
> +
> +/*
> + * glue structure to represent the relations between qgroups.
> + */
> +struct btrfs_qgroup_list {
> +	struct list_head next_group;
> +	struct list_head next_member;
> +	struct btrfs_qgroup *group;
> +	struct btrfs_qgroup *member;
> +};
> +
> +/* must be called with qgroup_lock held */
> +static struct btrfs_qgroup *find_qgroup_rb(struct btrfs_fs_info *fs_info,
> +					   u64 qgroupid)
> +{
> +	struct rb_node *n = fs_info->qgroup_tree.rb_node;
> +	struct btrfs_qgroup *qgroup;
> +
> +	while (n) {
> +		qgroup = rb_entry(n, struct btrfs_qgroup, node);
> +		if (qgroup->qgroupid<  qgroupid)
> +			n = n->rb_left;
> +		else if (qgroup->qgroupid>  qgroupid)
> +			n = n->rb_right;
> +		else
> +			return qgroup;
> +	}
> +	return NULL;
> +}
> +
> +/* must be called with qgroup_lock held */
> +static struct btrfs_qgroup *add_qgroup_rb(struct btrfs_fs_info *fs_info,
> +					  u64 qgroupid)
> +{
> +	struct rb_node **p =&fs_info->qgroup_tree.rb_node;
> +	struct rb_node *parent = NULL;
> +	struct btrfs_qgroup *qgroup;
> +
> +	while (*p) {
> +		parent = *p;
> +		qgroup = rb_entry(parent, struct btrfs_qgroup, node);
> +
> +		if (qgroup->qgroupid<  qgroupid)
> +			p =&(*p)->rb_left;
> +		else if (qgroup->qgroupid>  qgroupid)
> +			p =&(*p)->rb_right;
> +		else
> +			return qgroup;
> +	}
> +
> +	qgroup = kzalloc(sizeof(*qgroup), GFP_ATOMIC);
> +	if (!qgroup)
> +		return ERR_PTR(-ENOMEM);
> +
> +	qgroup->qgroupid = qgroupid;
> +	INIT_LIST_HEAD(&qgroup->groups);
> +	INIT_LIST_HEAD(&qgroup->members);
> +	INIT_LIST_HEAD(&qgroup->dirty);
> +
> +	rb_link_node(&qgroup->node, parent, p);
> +	rb_insert_color(&qgroup->node,&fs_info->qgroup_tree);
> +
> +	return qgroup;
> +}
> +
> +/* must be called with qgroup_lock held */
> +static int del_qgroup_rb(struct btrfs_fs_info *fs_info, u64 qgroupid)
> +{
> +	struct btrfs_qgroup *qgroup = find_qgroup_rb(fs_info, qgroupid);
> +	struct btrfs_qgroup_list *list;
> +
> +	if (!qgroup)
> +		return -ENOENT;
> +
> +	rb_erase(&qgroup->node,&fs_info->qgroup_tree);
> +	list_del(&qgroup->dirty);
> +
> +	while (!list_empty(&qgroup->groups)) {
> +		list = list_first_entry(&qgroup->groups,
> +					struct btrfs_qgroup_list, next_group);
> +		list_del(&list->next_group);
> +		list_del(&list->next_member);
> +		kfree(list);
> +	}
> +
> +	while (!list_empty(&qgroup->members)) {
> +		list = list_first_entry(&qgroup->members,
> +					struct btrfs_qgroup_list, next_member);
> +		list_del(&list->next_group);
> +		list_del(&list->next_member);
> +		kfree(list);
> +	}
> +	kfree(qgroup);
> +
> +	return 0;
> +}
> +
> +/* must be called with qgroup_lock held */
> +static int add_relation_rb(struct btrfs_fs_info *fs_info,
> +			   u64 memberid, u64 parentid)
> +{
> +	struct btrfs_qgroup *member;
> +	struct btrfs_qgroup *parent;
> +	struct btrfs_qgroup_list *list;
> +
> +	member = find_qgroup_rb(fs_info, memberid);
> +	parent = find_qgroup_rb(fs_info, parentid);
> +	if (!member || !parent)
> +		return -ENOENT;
> +
> +	list = kzalloc(sizeof(*list), GFP_ATOMIC);
> +	if (!list)
> +		return -ENOMEM;
> +
> +	list->group = parent;
> +	list->member = member;
> +	list_add_tail(&list->next_group,&member->groups);
> +	list_add_tail(&list->next_member,&parent->members);
> +
> +	return 0;
> +}
> +
> +/* must be called with qgroup_lock held */
> +static int del_relation_rb(struct btrfs_fs_info *fs_info,
> +			   u64 memberid, u64 parentid)
> +{
> +	struct btrfs_qgroup *member;
> +	struct btrfs_qgroup *parent;
> +	struct btrfs_qgroup_list *list;
> +
> +	member = find_qgroup_rb(fs_info, memberid);
> +	parent = find_qgroup_rb(fs_info, parentid);
> +	if (!member || !parent)
> +		return -ENOENT;
> +
> +	list_for_each_entry(list,&member->groups, next_group) {
> +		if (list->group == parent) {
> +			list_del(&list->next_group);
> +			list_del(&list->next_member);
> +			kfree(list);
> +			return 0;
> +		}
> +	}
> +	return -ENOENT;
> +}
> +
> +/*
> + * The full config is read in one go, only called from open_ctree()
> + * It doesn't use any locking, as at this point we're still single-threaded
> + */
> +int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
> +{
> +	struct btrfs_key key;
> +	struct btrfs_key found_key;
> +	struct btrfs_root *quota_root = fs_info->quota_root;
> +	struct btrfs_path *path = NULL;
> +	struct extent_buffer *l;
> +	int slot;
> +	int ret = 0;
> +	u64 flags = 0;
> +
> +	if (!fs_info->quota_enabled)
> +		return 0;
> +
> +	path = btrfs_alloc_path();
> +	if (!path) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	/* default this to quota off, in case no status key is found */
> +	fs_info->qgroup_flags = 0;
> +
> +	/*
> +	 * pass 1: read status, all qgroup infos and limits
> +	 */
> +	key.objectid = 0;
> +	key.type = 0;
> +	key.offset = 0;
> +	ret = btrfs_search_slot_for_read(quota_root,&key, path, 1, 1);
> +	if (ret)
> +		goto out;
> +
> +	while (1) {
> +		struct btrfs_qgroup *qgroup;
> +
> +		slot = path->slots[0];
> +		l = path->nodes[0];
> +		btrfs_item_key_to_cpu(l,&found_key, slot);
> +
> +		if (found_key.type == BTRFS_QGROUP_STATUS_KEY) {
> +			struct btrfs_qgroup_status_item *ptr;
> +
> +			ptr = btrfs_item_ptr(l, slot,
> +					     struct btrfs_qgroup_status_item);
> +
> +			if (btrfs_qgroup_status_version(l, ptr) !=
> +			    BTRFS_QGROUP_STATUS_VERSION) {
> +				printk(KERN_ERR
> +				 "btrfs: old qgroup version, quota disabled\n");
> +				goto out;
> +			}
> +			if (btrfs_qgroup_status_generation(l, ptr) !=
> +			    fs_info->generation) {
> +				flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
> +				printk(KERN_ERR
> +					"btrfs: qgroup generation mismatch, "
> +					"marked as inconsistent\n");
> +			}
> +			fs_info->qgroup_flags = btrfs_qgroup_status_flags(l,
> +									  ptr);
> +			/* FIXME read scan element */
> +			goto next1;
> +		}
> +
> +		if (found_key.type != BTRFS_QGROUP_INFO_KEY&&
> +		    found_key.type != BTRFS_QGROUP_LIMIT_KEY)
> +			goto next1;
> +
> +		qgroup = find_qgroup_rb(fs_info, found_key.offset);
> +		if ((qgroup&&  found_key.type == BTRFS_QGROUP_INFO_KEY) ||
> +		    (!qgroup&&  found_key.type == BTRFS_QGROUP_LIMIT_KEY)) {
> +			printk(KERN_ERR "btrfs: inconsitent qgroup config\n");
> +			flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
> +		}
> +		if (!qgroup) {
> +			qgroup = add_qgroup_rb(fs_info, found_key.offset);
> +			if (IS_ERR(qgroup)) {
> +				ret = PTR_ERR(qgroup);
> +				goto out;
> +			}
> +		}
> +		switch (found_key.type) {
> +		case BTRFS_QGROUP_INFO_KEY: {
> +			struct btrfs_qgroup_info_item *ptr;
> +
> +			ptr = btrfs_item_ptr(l, slot,
> +					     struct btrfs_qgroup_info_item);
> +			qgroup->rfer = btrfs_qgroup_info_rfer(l, ptr);
> +			qgroup->rfer_cmpr = btrfs_qgroup_info_rfer_cmpr(l, ptr);
> +			qgroup->excl = btrfs_qgroup_info_excl(l, ptr);
> +			qgroup->excl_cmpr = btrfs_qgroup_info_excl_cmpr(l, ptr);
> +			/* generation currently unused */
> +			break;
> +		}
> +		case BTRFS_QGROUP_LIMIT_KEY: {
> +			struct btrfs_qgroup_limit_item *ptr;
> +
> +			ptr = btrfs_item_ptr(l, slot,
> +					     struct btrfs_qgroup_limit_item);
> +			qgroup->lim_flags = btrfs_qgroup_limit_flags(l, ptr);
> +			qgroup->max_rfer = btrfs_qgroup_limit_max_rfer(l, ptr);
> +			qgroup->max_excl = btrfs_qgroup_limit_max_excl(l, ptr);
> +			qgroup->rsv_rfer = btrfs_qgroup_limit_rsv_rfer(l, ptr);
> +			qgroup->rsv_excl = btrfs_qgroup_limit_rsv_excl(l, ptr);
> +			break;
> +		}
> +		}
> +next1:
> +		ret = btrfs_next_item(quota_root, path);
> +		if (ret<  0)
> +			goto out;
> +		if (ret)
> +			break;
> +	}
> +	btrfs_release_path(path);
> +
> +	/*
> +	 * pass 2: read all qgroup relations
> +	 */
> +	key.objectid = 0;
> +	key.type = BTRFS_QGROUP_RELATION_KEY;
> +	key.offset = 0;
> +	ret = btrfs_search_slot_for_read(quota_root,&key, path, 1, 0);
> +	if (ret)
> +		goto out;
> +	while (1) {
> +		slot = path->slots[0];
> +		l = path->nodes[0];
> +		btrfs_item_key_to_cpu(l,&found_key, slot);
> +
> +		if (found_key.type != BTRFS_QGROUP_RELATION_KEY)
> +			goto next2;
> +
> +		if (found_key.objectid>  found_key.offset) {
> +			/* parent<- member, not needed to build config */
> +			/* FIXME should we omit the key completely? */
> +			goto next2;
> +		}
> +
> +		ret = add_relation_rb(fs_info, found_key.objectid,
> +				      found_key.offset);
> +		if (ret)
> +			goto out;
> +next2:
> +		ret = btrfs_next_item(quota_root, path);
> +		if (ret<  0)
> +			goto out;
> +		if (ret)
> +			break;
> +	}
> +out:
> +	fs_info->qgroup_flags |= flags;
> +	if (!(fs_info->qgroup_flags&  BTRFS_QGROUP_STATUS_FLAG_ON)) {
> +		fs_info->quota_enabled = 0;
> +		fs_info->pending_quota_state = 0;
> +	}
> +	btrfs_free_path(path);
> +
> +	return ret<  0 ? ret : 0;
> +}
> +
> +/*
> + * This is only called from close_ctree() or open_ctree(), both in single-
> + * treaded paths. Clean up the in-memory structures. No locking needed.
> + */
> +void btrfs_free_qgroup_config(struct btrfs_fs_info *fs_info)
> +{
> +	struct rb_node *n;
> +	struct btrfs_qgroup *qgroup;
> +	struct btrfs_qgroup_list *list;
> +
> +	while ((n = rb_first(&fs_info->qgroup_tree))) {
> +		qgroup = rb_entry(n, struct btrfs_qgroup, node);
> +		rb_erase(n,&fs_info->qgroup_tree);
> +
> +		WARN_ON(!list_empty(&qgroup->dirty));
> +
> +		while (!list_empty(&qgroup->groups)) {
> +			list = list_first_entry(&qgroup->groups,
> +						struct btrfs_qgroup_list,
> +						next_group);
> +			list_del(&list->next_group);
> +			list_del(&list->next_member);
> +			kfree(list);
> +		}
> +
> +		while (!list_empty(&qgroup->members)) {
> +			list = list_first_entry(&qgroup->members,
> +						struct btrfs_qgroup_list,
> +						next_member);
> +			list_del(&list->next_group);
> +			list_del(&list->next_member);
> +			kfree(list);
> +		}
> +		kfree(qgroup);
> +	}
> +}
> +
> +static int add_qgroup_relation_item(struct btrfs_trans_handle *trans,
> +				    struct btrfs_root *quota_root,
> +				    u64 src, u64 dst)
> +{
> +	int ret;
> +	struct btrfs_path *path;
> +	struct btrfs_key key;
> +
> +	path = btrfs_alloc_path();
> +	if (!path)
> +		return -ENOMEM;
> +
> +	key.objectid = src;
> +	key.type = BTRFS_QGROUP_RELATION_KEY;
> +	key.offset = dst;
> +
> +	ret = btrfs_insert_empty_item(trans, quota_root, path,&key, 0);
> +
> +	btrfs_mark_buffer_dirty(path->nodes[0]);
> +
> +	btrfs_free_path(path);
> +	return ret;
> +}
> +
> +static int del_qgroup_relation_item(struct btrfs_trans_handle *trans,
> +				    struct btrfs_root *quota_root,
> +				    u64 src, u64 dst)
> +{
> +	int ret;
> +	struct btrfs_path *path;
> +	struct btrfs_key key;
> +
> +	path = btrfs_alloc_path();
> +	if (!path)
> +		return -ENOMEM;
> +
> +	key.objectid = src;
> +	key.type = BTRFS_QGROUP_RELATION_KEY;
> +	key.offset = dst;
> +
> +	ret = btrfs_search_slot(trans, quota_root,&key, path, -1, 1);
> +	if (ret<  0)
> +		goto out;
> +
> +	if (ret>  0) {
> +		ret = -ENOENT;
> +		goto out;
> +	}
> +
> +	ret = btrfs_del_item(trans, quota_root, path);
> +out:
> +	btrfs_free_path(path);
> +	return ret;
> +}
> +
> +static int add_qgroup_item(struct btrfs_trans_handle *trans,
> +			   struct btrfs_root *quota_root, u64 qgroupid)
> +{
> +	int ret;
> +	struct btrfs_path *path;
> +	struct btrfs_qgroup_info_item *qgroup_info;
> +	struct btrfs_qgroup_limit_item *qgroup_limit;
> +	struct extent_buffer *leaf;
> +	struct btrfs_key key;
> +
> +	path = btrfs_alloc_path();
> +	if (!path)
> +		return -ENOMEM;
> +
> +	key.objectid = 0;
> +	key.type = BTRFS_QGROUP_INFO_KEY;
> +	key.offset = qgroupid;
> +
> +	ret = btrfs_insert_empty_item(trans, quota_root, path,&key,
> +				      sizeof(*qgroup_info));
> +	if (ret)
> +		goto out;
> +
> +	leaf = path->nodes[0];
> +	qgroup_info = btrfs_item_ptr(leaf, path->slots[0],
> +				 struct btrfs_qgroup_info_item);
> +	btrfs_set_qgroup_info_generation(leaf, qgroup_info, trans->transid);
> +	btrfs_set_qgroup_info_rfer(leaf, qgroup_info, 0);
> +	btrfs_set_qgroup_info_rfer_cmpr(leaf, qgroup_info, 0);
> +	btrfs_set_qgroup_info_excl(leaf, qgroup_info, 0);
> +	btrfs_set_qgroup_info_excl_cmpr(leaf, qgroup_info, 0);
> +
> +	btrfs_mark_buffer_dirty(leaf);
> +
> +	btrfs_release_path(path);
> +
> +	key.type = BTRFS_QGROUP_LIMIT_KEY;
> +	ret = btrfs_insert_empty_item(trans, quota_root, path,&key,
> +				      sizeof(*qgroup_limit));
> +	if (ret)
> +		goto out;
> +
> +	leaf = path->nodes[0];
> +	qgroup_limit = btrfs_item_ptr(leaf, path->slots[0],
> +				  struct btrfs_qgroup_limit_item);
> +	btrfs_set_qgroup_limit_flags(leaf, qgroup_limit, 0);
> +	btrfs_set_qgroup_limit_max_rfer(leaf, qgroup_limit, 0);
> +	btrfs_set_qgroup_limit_max_excl(leaf, qgroup_limit, 0);
> +	btrfs_set_qgroup_limit_rsv_rfer(leaf, qgroup_limit, 0);
> +	btrfs_set_qgroup_limit_rsv_excl(leaf, qgroup_limit, 0);
> +
> +	btrfs_mark_buffer_dirty(leaf);
> +
> +	ret = 0;
> +out:
> +	btrfs_free_path(path);
> +	return ret;
> +}
> +
> +static int del_qgroup_item(struct btrfs_trans_handle *trans,
> +			   struct btrfs_root *quota_root, u64 qgroupid)
> +{
> +	int ret;
> +	struct btrfs_path *path;
> +	struct btrfs_key key;
> +
> +	path = btrfs_alloc_path();
> +	if (!path)
> +		return -ENOMEM;
> +
> +	key.objectid = 0;
> +	key.type = BTRFS_QGROUP_INFO_KEY;
> +	key.offset = qgroupid;
> +	ret = btrfs_search_slot(trans, quota_root,&key, path, -1, 1);
> +	if (ret<  0)
> +		goto out;
> +
> +	if (ret>  0) {
> +		ret = -ENOENT;
> +		goto out;
> +	}
> +
> +	ret = btrfs_del_item(trans, quota_root, path);
> +	if (ret)
> +		goto out;
> +
> +	btrfs_release_path(path);
> +
> +	key.type = BTRFS_QGROUP_LIMIT_KEY;
> +	ret = btrfs_search_slot(trans, quota_root,&key, path, -1, 1);
> +	if (ret<  0)
> +		goto out;
> +
> +	if (ret>  0) {
> +		ret = -ENOENT;
> +		goto out;
> +	}
> +
> +	ret = btrfs_del_item(trans, quota_root, path);
> +
> +out:
> +	btrfs_free_path(path);
> +	return ret;
> +}
> +
> +static int update_qgroup_limit_item(struct btrfs_trans_handle *trans,
> +				    struct btrfs_root *root, u64 qgroupid,
> +				    u64 flags, u64 max_rfer, u64 max_excl,
> +				    u64 rsv_rfer, u64 rsv_excl)
> +{
> +	struct btrfs_path *path;
> +	struct btrfs_key key;
> +	struct extent_buffer *l;
> +	struct btrfs_qgroup_limit_item *qgroup_limit;
> +	int ret;
> +	int slot;
> +
> +	key.objectid = 0;
> +	key.type = BTRFS_QGROUP_LIMIT_KEY;
> +	key.offset = qgroupid;
> +
> +	path = btrfs_alloc_path();
> +	BUG_ON(!path);
> +	ret = btrfs_search_slot(trans, root,&key, path, 0, 1);
> +	if (ret>  0)
> +		ret = -ENOENT;
> +
> +	if (ret)
> +		goto out;
> +
> +	l = path->nodes[0];
> +	slot = path->slots[0];
> +	qgroup_limit = btrfs_item_ptr(l, path->slots[0],
> +				      struct btrfs_qgroup_limit_item);
> +	btrfs_set_qgroup_limit_flags(l, qgroup_limit, flags);
> +	btrfs_set_qgroup_limit_max_rfer(l, qgroup_limit, max_rfer);
> +	btrfs_set_qgroup_limit_max_excl(l, qgroup_limit, max_excl);
> +	btrfs_set_qgroup_limit_rsv_rfer(l, qgroup_limit, rsv_rfer);
> +	btrfs_set_qgroup_limit_rsv_excl(l, qgroup_limit, rsv_excl);
> +
> +	btrfs_mark_buffer_dirty(l);
> +
> +out:
> +	btrfs_free_path(path);
> +	return ret;
> +}
> +
> +static int update_qgroup_info_item(struct btrfs_trans_handle *trans,
> +				   struct btrfs_root *root,
> +				   struct btrfs_qgroup *qgroup)
> +{
> +	struct btrfs_path *path;
> +	struct btrfs_key key;
> +	struct extent_buffer *l;
> +	struct btrfs_qgroup_info_item *qgroup_info;
> +	int ret;
> +	int slot;
> +
> +	key.objectid = 0;
> +	key.type = BTRFS_QGROUP_INFO_KEY;
> +	key.offset = qgroup->qgroupid;
> +
> +	path = btrfs_alloc_path();
> +	BUG_ON(!path);
> +	ret = btrfs_search_slot(trans, root,&key, path, 0, 1);
> +	if (ret>  0)
> +		ret = -ENOENT;
> +
> +	if (ret)
> +		goto out;
> +
> +	l = path->nodes[0];
> +	slot = path->slots[0];
> +	qgroup_info = btrfs_item_ptr(l, path->slots[0],
> +				 struct btrfs_qgroup_info_item);
> +	btrfs_set_qgroup_info_generation(l, qgroup_info, trans->transid);
> +	btrfs_set_qgroup_info_rfer(l, qgroup_info, qgroup->rfer);
> +	btrfs_set_qgroup_info_rfer_cmpr(l, qgroup_info, qgroup->rfer_cmpr);
> +	btrfs_set_qgroup_info_excl(l, qgroup_info, qgroup->excl);
> +	btrfs_set_qgroup_info_excl_cmpr(l, qgroup_info, qgroup->excl_cmpr);
> +
> +	btrfs_mark_buffer_dirty(l);
> +
> +out:
> +	btrfs_free_path(path);
> +	return ret;
> +}
> +
> +static int update_qgroup_status_item(struct btrfs_trans_handle *trans,
> +				     struct btrfs_fs_info *fs_info,
> +				    struct btrfs_root *root)
> +{
> +	struct btrfs_path *path;
> +	struct btrfs_key key;
> +	struct extent_buffer *l;
> +	struct btrfs_qgroup_status_item *ptr;
> +	int ret;
> +	int slot;
> +
> +	key.objectid = 0;
> +	key.type = BTRFS_QGROUP_STATUS_KEY;
> +	key.offset = 0;
> +
> +	path = btrfs_alloc_path();
> +	BUG_ON(!path);
> +	ret = btrfs_search_slot(trans, root,&key, path, 0, 1);
> +	if (ret>  0)
> +		ret = -ENOENT;
> +
> +	if (ret)
> +		goto out;
> +
> +	l = path->nodes[0];
> +	slot = path->slots[0];
> +	ptr = btrfs_item_ptr(l, slot, struct btrfs_qgroup_status_item);
> +	btrfs_set_qgroup_status_flags(l, ptr, fs_info->qgroup_flags);
> +	btrfs_set_qgroup_status_generation(l, ptr, trans->transid);
> +	/* XXX scan */
> +
> +	btrfs_mark_buffer_dirty(l);
> +
> +out:
> +	btrfs_free_path(path);
> +	return ret;
> +}
> +
> +/*
> + * called with qgroup_lock held
> + */
> +static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans,
> +				  struct btrfs_root *root)
> +{
> +	struct btrfs_path *path;
> +	struct btrfs_key key;
> +	int ret;
> +
> +	if (!root)
> +		return -EINVAL;
> +
> +	path = btrfs_alloc_path();
> +	if (!path)
> +		return -ENOMEM;
> +
> +	while (1) {
> +		key.objectid = 0;
> +		key.offset = 0;
> +		key.type = 0;
> +
> +		path->leave_spinning = 1;
> +		ret = btrfs_search_slot(trans, root,&key, path, -1, 1);
> +		if (ret>  0) {
> +			if (path->slots[0] == 0)
> +				break;
> +			path->slots[0]--;
> +		} else if (ret<  0) {
> +			break;
> +		}
> +
> +		ret = btrfs_del_item(trans, root, path);
> +		if (ret)
> +			goto out;
> +		btrfs_release_path(path);
> +	}
> +	ret = 0;
> +out:
> +	root->fs_info->pending_quota_state = 0;
> +	btrfs_free_path(path);
> +	return ret;
> +}
> +
> +int btrfs_quota_enable(struct btrfs_trans_handle *trans,
> +		       struct btrfs_fs_info *fs_info)
> +{
> +	struct btrfs_root *quota_root;
> +	struct btrfs_path *path = NULL;
> +	struct btrfs_qgroup_status_item *ptr;
> +	struct extent_buffer *leaf;
> +	struct btrfs_key key;
> +	int ret = 0;
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +	if (fs_info->quota_root) {
> +		fs_info->pending_quota_state = 1;
> +		spin_unlock(&fs_info->qgroup_lock);
> +		goto out;
> +	}
> +	spin_unlock(&fs_info->qgroup_lock);
> +
> +	/*
> +	 * initially create the quota tree
> +	 */
> +	quota_root = btrfs_create_tree(trans, fs_info,
> +				       BTRFS_QUOTA_TREE_OBJECTID);
> +	if (IS_ERR(quota_root)) {
> +		ret =  PTR_ERR(quota_root);
> +		goto out;
> +	}
> +
> +	path = btrfs_alloc_path();
> +	if (!path)
> +		return -ENOMEM;
> +
> +	key.objectid = 0;
> +	key.type = BTRFS_QGROUP_STATUS_KEY;
> +	key.offset = 0;
> +
> +	ret = btrfs_insert_empty_item(trans, quota_root, path,&key,
> +				      sizeof(*ptr));
> +	if (ret)
> +		goto out;
> +
> +	leaf = path->nodes[0];
> +	ptr = btrfs_item_ptr(leaf, path->slots[0],
> +				 struct btrfs_qgroup_status_item);
> +	btrfs_set_qgroup_status_generation(leaf, ptr, trans->transid);
> +	btrfs_set_qgroup_status_version(leaf, ptr, BTRFS_QGROUP_STATUS_VERSION);
> +	fs_info->qgroup_flags = BTRFS_QGROUP_STATUS_FLAG_ON |
> +				BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
> +	btrfs_set_qgroup_status_flags(leaf, ptr, fs_info->qgroup_flags);
> +	btrfs_set_qgroup_status_scan(leaf, ptr, 0);
> +
> +	btrfs_mark_buffer_dirty(leaf);
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +	fs_info->quota_root = quota_root;
> +	fs_info->pending_quota_state = 1;
> +	spin_unlock(&fs_info->qgroup_lock);
> +out:
> +	btrfs_free_path(path);
> +	return ret;
> +}
> +
> +int btrfs_quota_disable(struct btrfs_trans_handle *trans,
> +			struct btrfs_fs_info *fs_info)
> +{
> +	struct btrfs_root *tree_root = fs_info->tree_root;
> +	struct btrfs_root *quota_root;
> +	int ret = 0;
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +	fs_info->pending_quota_state = 0;
> +	quota_root = fs_info->quota_root;
> +	fs_info->quota_root = NULL;
> +	btrfs_free_qgroup_config(fs_info);
> +	spin_unlock(&fs_info->qgroup_lock);
> +
> +	if (!quota_root)
> +		return -EINVAL;
> +
> +	ret = btrfs_clean_quota_tree(trans, quota_root);
> +	if (ret)
> +		goto out;
> +
> +	ret = btrfs_del_root(trans, tree_root,&quota_root->root_key);
> +	if (ret)
> +		goto out;
> +
> +	list_del(&quota_root->dirty_list);
> +
> +	btrfs_tree_lock(quota_root->node);
> +	clean_tree_block(trans, tree_root, quota_root->node);
> +	btrfs_tree_unlock(quota_root->node);
> +	btrfs_free_tree_block(trans, quota_root, quota_root->node, 0, 1);
> +
> +	free_extent_buffer(quota_root->node);
> +	free_extent_buffer(quota_root->commit_root);
> +	kfree(quota_root);
> +out:
> +	return ret;
> +}
> +
> +int btrfs_quota_rescan(struct btrfs_fs_info *fs_info)
> +{
> +	/* FIXME */
> +	return 0;
> +}
> +
> +int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
> +			      struct btrfs_fs_info *fs_info, u64 src, u64 dst)
> +{
> +	struct btrfs_root *quota_root;
> +	int ret = 0;
> +
> +	quota_root = fs_info->quota_root;
> +	if (!quota_root)
> +		return -EINVAL;
> +
> +	ret = add_qgroup_relation_item(trans, quota_root, src, dst);
> +	if (ret)
> +		return ret;
> +
> +	ret = add_qgroup_relation_item(trans, quota_root, dst, src);
> +	if (ret) {
> +		del_qgroup_relation_item(trans, quota_root, src, dst);
> +		return ret;
> +	}
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +	ret = add_relation_rb(quota_root->fs_info, src, dst);
> +	spin_unlock(&fs_info->qgroup_lock);
> +
> +	return ret;
> +}
> +
> +int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans,
> +			      struct btrfs_fs_info *fs_info, u64 src, u64 dst)
> +{
> +	struct btrfs_root *quota_root;
> +	int ret = 0;
> +	int err;
> +
> +	quota_root = fs_info->quota_root;
> +	if (!quota_root)
> +		return -EINVAL;
> +
> +	ret = del_qgroup_relation_item(trans, quota_root, src, dst);
> +	err = del_qgroup_relation_item(trans, quota_root, dst, src);
> +	if (err&&  !ret)
> +		ret = err;
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +	del_relation_rb(fs_info, src, dst);
> +
> +	spin_unlock(&fs_info->qgroup_lock);
> +
> +	return ret;
> +}
> +
> +int btrfs_create_qgroup(struct btrfs_trans_handle *trans,
> +			struct btrfs_fs_info *fs_info, u64 qgroupid, char *name)
> +{
> +	struct btrfs_root *quota_root;
> +	struct btrfs_qgroup *qgroup;
> +	int ret = 0;
> +
> +	quota_root = fs_info->quota_root;
> +	if (!quota_root)
> +		return -EINVAL;
> +
> +	ret = add_qgroup_item(trans, quota_root, qgroupid);
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +	qgroup = add_qgroup_rb(fs_info, qgroupid);
> +	spin_unlock(&fs_info->qgroup_lock);
> +
> +	if (IS_ERR(qgroup))
> +		ret = PTR_ERR(qgroup);
> +
> +	return ret;
> +}
> +
> +int btrfs_remove_qgroup(struct btrfs_trans_handle *trans,
> +			struct btrfs_fs_info *fs_info, u64 qgroupid)
> +{
> +	struct btrfs_root *quota_root;
> +	int ret = 0;
> +
> +	quota_root = fs_info->quota_root;
> +	if (!quota_root)
> +		return -EINVAL;
> +
> +	ret = del_qgroup_item(trans, quota_root, qgroupid);
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +	del_qgroup_rb(quota_root->fs_info, qgroupid);
> +
> +	spin_unlock(&fs_info->qgroup_lock);
> +
> +	return ret;
> +}
> +
> +int btrfs_limit_qgroup(struct btrfs_trans_handle *trans,
> +		       struct btrfs_fs_info *fs_info, u64 qgroupid,
> +		       struct btrfs_qgroup_limit *limit)
> +{
> +	struct btrfs_root *quota_root = fs_info->quota_root;
> +	struct btrfs_qgroup *qgroup;
> +	int ret = 0;
> +
> +	if (!quota_root)
> +		return -EINVAL;
> +
> +	ret = update_qgroup_limit_item(trans, quota_root, qgroupid,
> +				       limit->flags, limit->max_rfer,
> +				       limit->max_excl, limit->rsv_rfer,
> +				       limit->rsv_excl);
> +	if (ret) {
> +		fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
> +		printk(KERN_INFO "unable to update quota limit for %llu\n",
> +		       (unsigned long long)qgroupid);
> +	}
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +
> +	qgroup = find_qgroup_rb(fs_info, qgroupid);
> +	if (!qgroup) {
> +		ret = -ENOENT;
> +		goto unlock;
> +	}
> +	qgroup->lim_flags = limit->flags;
> +	qgroup->max_rfer = limit->max_rfer;
> +	qgroup->max_excl = limit->max_excl;
> +	qgroup->rsv_rfer = limit->rsv_rfer;
> +	qgroup->rsv_excl = limit->rsv_excl;
> +
> +unlock:
> +	spin_unlock(&fs_info->qgroup_lock);
> +
> +	return ret;
> +}
> +
> +static void qgroup_dirty(struct btrfs_fs_info *fs_info,
> +			 struct btrfs_qgroup *qgroup)
> +{
> +	if (list_empty(&qgroup->dirty))
> +		list_add(&qgroup->dirty,&fs_info->dirty_qgroups);
> +}
> +
> +/*
> + * btrfs_qgroup_record_ref is called for every ref that is added to or deleted
> + * from the fs. First, all roots referencing the extent are searched, and
> + * then the space is accounted accordingly to the different roots. The
> + * accounting algorithm works in 3 steps documented inline.
> + */
> +int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans,
> +			     struct btrfs_fs_info *fs_info,
> +			     struct btrfs_delayed_ref_node *node,
> +			     struct btrfs_delayed_extent_op *extent_op)
> +{
> +	struct btrfs_key ins;
> +	struct btrfs_root *quota_root;
> +	u64 ref_root;
> +	struct btrfs_qgroup *qgroup;
> +	struct ulist_node *unode;
> +	struct ulist *roots = NULL;
> +	struct ulist *tmp = NULL;
> +	u64 seq;
> +	int ret = 0;
> +	int sgn;
> +
> +	if (!fs_info->quota_enabled)
> +		return 0;
> +
> +	BUG_ON(!fs_info->quota_root);
> +
> +	ins.objectid = node->bytenr;
> +	ins.offset = node->num_bytes;
> +	ins.type = BTRFS_EXTENT_ITEM_KEY;
> +
> +	if (node->type == BTRFS_TREE_BLOCK_REF_KEY ||
> +	    node->type == BTRFS_SHARED_BLOCK_REF_KEY) {
> +		struct btrfs_delayed_tree_ref *ref;
> +		ref = btrfs_delayed_node_to_tree_ref(node);
> +		ref_root = ref->root;
> +	} else if (node->type == BTRFS_EXTENT_DATA_REF_KEY ||
> +		   node->type == BTRFS_SHARED_DATA_REF_KEY) {
> +		struct btrfs_delayed_data_ref *ref;
> +		ref = btrfs_delayed_node_to_data_ref(node);
> +		ref_root = ref->root;
> +	} else {
> +		BUG();
> +	}
> +
> +	if (!is_fstree(ref_root)) {
> +		/*
> +		 * non-fs-trees are not being accounted
> +		 */
> +		return 0;
> +	}
> +
> +	switch (node->action) {
> +	case BTRFS_ADD_DELAYED_REF:
> +	case BTRFS_ADD_DELAYED_EXTENT:
> +		sgn = 1;
> +		break;
> +	case BTRFS_DROP_DELAYED_REF:
> +		sgn = -1;
> +		break;
> +	case BTRFS_UPDATE_DELAYED_HEAD:
> +		return 0;
> +	default:
> +		BUG();
> +	}
> +
> +	ret = btrfs_find_all_roots(trans, fs_info, node->bytenr,
> +				   node->num_bytes,
> +				   sgn>  0 ? node->seq - 1 : node->seq,&roots);
> +	if (IS_ERR(roots)) {
> +		ret = PTR_ERR(roots);
> +		goto out;
> +	}
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +	quota_root = fs_info->quota_root;
> +	if (!quota_root)
> +		goto out;
> +
> +	qgroup = find_qgroup_rb(fs_info, ref_root);
> +	if (!qgroup)
> +		goto out;
> +
> +	/*
> +	 * step 1: for each old ref, visit all nodes once and inc refcnt
> +	 */
> +	unode = NULL;
> +	tmp = ulist_alloc(GFP_ATOMIC);
> +	if (!tmp) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +	seq = fs_info->qgroup_seq;
> +	fs_info->qgroup_seq += roots->nnodes + 1; /* max refcnt */
> +
> +	while ((unode = ulist_next(roots, unode))) {
> +		struct ulist_node *tmp_unode;
> +		struct btrfs_qgroup *qg;
> +
> +		qg = find_qgroup_rb(fs_info, unode->val);
> +		if (!qg)
> +			continue;
> +
> +		ulist_reinit(tmp);
> +						/* XXX id not needed */
> +		ulist_add(tmp, qg->qgroupid, (unsigned long)qg, GFP_ATOMIC);
> +		tmp_unode = NULL;
> +		while ((tmp_unode = ulist_next(tmp, tmp_unode))) {
> +			struct btrfs_qgroup_list *glist;
> +
> +			qg = (struct btrfs_qgroup *)tmp_unode->aux;
> +			if (qg->refcnt<  seq)
> +				qg->refcnt = seq + 1;
> +			else
> +				++qg->refcnt;
> +
> +			list_for_each_entry(glist,&qg->groups, next_group) {
> +				ulist_add(tmp, glist->group->qgroupid,
> +					  (unsigned long)glist->group,
> +					  GFP_ATOMIC);
> +			}
> +		}
> +	}
> +
> +	/*
> +	 * step 2: walk from the new root
> +	 */
> +	ulist_reinit(tmp);
> +	ulist_add(tmp, qgroup->qgroupid, (unsigned long)qgroup, GFP_ATOMIC);
> +	unode = NULL;
> +	while ((unode = ulist_next(tmp, unode))) {
> +		struct btrfs_qgroup *qg;
> +		struct btrfs_qgroup_list *glist;
> +
> +		qg = (struct btrfs_qgroup *)unode->aux;
> +		if (qg->refcnt<  seq) {
> +			/* not visited by step 1 */
> +			qg->rfer += sgn * node->num_bytes;
> +			qg->rfer_cmpr += sgn * node->num_bytes;
> +			if (roots->nnodes == 0) {
> +				qg->excl += sgn * node->num_bytes;
> +				qg->excl_cmpr += sgn * node->num_bytes;
> +			}
> +			qgroup_dirty(fs_info, qg);
> +		}
> +		WARN_ON(qg->tag>= seq);
> +		qg->tag = seq;
> +
> +		list_for_each_entry(glist,&qg->groups, next_group) {
> +			ulist_add(tmp, glist->group->qgroupid,
> +				  (unsigned long)glist->group, GFP_ATOMIC);
> +		}
> +	}
> +
> +	/*
> +	 * step 3: walk again from old refs
> +	 */
> +	while ((unode = ulist_next(roots, unode))) {
> +		struct btrfs_qgroup *qg;
> +		struct ulist_node *tmp_unode;
> +
> +		qg = find_qgroup_rb(fs_info, unode->val);
> +		if (!qg)
> +			continue;
> +
> +		ulist_reinit(tmp);
> +		ulist_add(tmp, qg->qgroupid, (unsigned long)qg, GFP_ATOMIC);
> +		tmp_unode = NULL;
> +		while ((tmp_unode = ulist_next(tmp, tmp_unode))) {
> +			struct btrfs_qgroup_list *glist;
> +
> +			qg = (struct btrfs_qgroup *)tmp_unode->aux;
> +			if (qg->tag == seq)
> +				continue;
> +
> +			if (qg->refcnt - seq == roots->nnodes) {
> +				qg->excl -= sgn * node->num_bytes;
> +				qg->excl_cmpr -= sgn * node->num_bytes;
> +				qgroup_dirty(fs_info, qg);
> +			}
> +
> +			list_for_each_entry(glist,&qg->groups, next_group) {
> +				ulist_add(tmp, glist->group->qgroupid,
> +					  (unsigned long)glist->group,
> +					  GFP_ATOMIC);
> +			}
> +		}
> +	}
> +	ret = 0;
> +out:
> +	spin_unlock(&fs_info->qgroup_lock);
> +	ulist_free(roots);
> +	ulist_free(tmp);
> +
> +	return ret;
> +}
> +
> +/*
> + * called from commit_transaction. Writes all changed qgroups to disk.
> + */
> +int btrfs_run_qgroups(struct btrfs_trans_handle *trans,
> +		      struct btrfs_fs_info *fs_info)
> +{
> +	struct btrfs_root *quota_root = fs_info->quota_root;
> +	int ret = 0;
> +
> +	if (!quota_root)
> +		goto out;
> +
> +	fs_info->quota_enabled = fs_info->pending_quota_state;
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +	while (!list_empty(&fs_info->dirty_qgroups)) {
> +		struct btrfs_qgroup *qgroup;
> +		qgroup = list_first_entry(&fs_info->dirty_qgroups,
> +					  struct btrfs_qgroup, dirty);
> +		list_del_init(&qgroup->dirty);
> +		spin_unlock(&fs_info->qgroup_lock);
> +		ret = update_qgroup_info_item(trans, quota_root, qgroup);
> +		if (ret)
> +			fs_info->qgroup_flags |=
> +					BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
> +		spin_lock(&fs_info->qgroup_lock);
> +	}
> +	if (fs_info->quota_enabled)
> +		fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_ON;
> +	else
> +		fs_info->qgroup_flags&= ~BTRFS_QGROUP_STATUS_FLAG_ON;
> +	spin_unlock(&fs_info->qgroup_lock);
> +
> +	ret = update_qgroup_status_item(trans, fs_info, quota_root);
> +	if (ret)
> +		fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
> +
> +out:
> +
> +	return ret;
> +}
> +
> +/*
> + * copy the acounting information between qgroups. This is necessary when a
> + * snapshot or a subvolume is created
> + */
> +int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
> +			 struct btrfs_fs_info *fs_info, u64 srcid, u64 objectid,
> +			 struct btrfs_qgroup_inherit *inherit)
> +{
> +	int ret = 0;
> +	int i;
> +	u64 *i_qgroups;
> +	struct btrfs_root *quota_root = fs_info->quota_root;
> +	struct btrfs_qgroup *srcgroup;
> +	struct btrfs_qgroup *dstgroup;
> +	u32 level_size = 0;
> +
> +	if (!fs_info->quota_enabled)
> +		return 0;
> +
> +	if (!quota_root)
> +		ret = -EINVAL;

Is this "return -EINVAL" ?

> +
> +	/*
> +	 * create a tracking group for the subvol itself
> +	 */
> +	ret = add_qgroup_item(trans, quota_root, objectid);
> +	if (ret)
> +		goto out;
> +
> +	if (inherit&&  inherit->flags&  BTRFS_QGROUP_INHERIT_SET_LIMITS) {
> +		ret = update_qgroup_limit_item(trans, quota_root, objectid,
> +					       inherit->lim.flags,
> +					       inherit->lim.max_rfer,
> +					       inherit->lim.max_excl,
> +					       inherit->lim.rsv_rfer,
> +					       inherit->lim.rsv_excl);
> +		if (ret)
> +			goto out;
> +	}
> +
> +	if (srcid) {
> +		struct btrfs_root *srcroot;
> +		struct btrfs_key srckey;
> +		int srcroot_level;
> +
> +		srckey.objectid = srcid;
> +		srckey.type = BTRFS_ROOT_ITEM_KEY;
> +		srckey.offset = (u64)-1;
> +		srcroot = btrfs_read_fs_root_no_name(fs_info,&srckey);
> +		if (IS_ERR(srcroot)) {
> +			ret = PTR_ERR(srcroot);
> +			goto out;
> +		}
> +
> +		rcu_read_lock();
> +		srcroot_level = btrfs_header_level(srcroot->node);
> +		level_size = btrfs_level_size(srcroot, srcroot_level);
> +		rcu_read_unlock();
> +	}
> +
> +	/*
> +	 * add qgroup to all inherited groups
> +	 */
> +	if (inherit) {
> +		i_qgroups = (u64 *)(inherit + 1);
> +		for (i = 0; i<  inherit->num_qgroups; ++i) {
> +			ret = add_qgroup_relation_item(trans, quota_root,
> +						       objectid, *i_qgroups);
> +			if (ret)
> +				goto out;
> +			ret = add_qgroup_relation_item(trans, quota_root,
> +						       *i_qgroups, objectid);
> +			if (ret)
> +				goto out;
> +			++i_qgroups;
> +		}
> +	}
> +
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +
> +	dstgroup = add_qgroup_rb(fs_info, objectid);
> +	if (!dstgroup)
> +		goto unlock;
> +
> +	if (srcid) {
> +		srcgroup = find_qgroup_rb(fs_info, srcid);
> +		if (!srcgroup)
> +			goto unlock;
> +		dstgroup->rfer = srcgroup->rfer - level_size;
> +		dstgroup->rfer_cmpr = srcgroup->rfer_cmpr - level_size;
> +		srcgroup->excl = level_size;
> +		srcgroup->excl_cmpr = level_size;
> +		qgroup_dirty(fs_info, dstgroup);
> +		qgroup_dirty(fs_info, srcgroup);
> +	}
> +
> +	if (!inherit)
> +		goto unlock;
> +
> +	i_qgroups = (u64 *)(inherit + 1);
> +	for (i = 0; i<  inherit->num_qgroups; ++i) {
> +		ret = add_relation_rb(quota_root->fs_info, objectid,
> +				      *i_qgroups);
> +		if (ret)
> +			goto unlock;
> +		++i_qgroups;
> +	}
> +
> +	for (i = 0; i<   inherit->num_ref_copies; ++i) {
> +		struct btrfs_qgroup *src;
> +		struct btrfs_qgroup *dst;
> +
> +		src = find_qgroup_rb(fs_info, i_qgroups[0]);
> +		dst = find_qgroup_rb(fs_info, i_qgroups[1]);
> +
> +		if (!src || !dst) {
> +			ret = -EINVAL;
> +			goto unlock;
> +		}
> +
> +		dst->rfer = src->rfer - level_size;
> +		dst->rfer_cmpr = src->rfer_cmpr - level_size;
> +		i_qgroups += 2;
> +	}
> +	for (i = 0; i<   inherit->num_excl_copies; ++i) {
> +		struct btrfs_qgroup *src;
> +		struct btrfs_qgroup *dst;
> +
> +		src = find_qgroup_rb(fs_info, i_qgroups[0]);
> +		dst = find_qgroup_rb(fs_info, i_qgroups[1]);
> +
> +		if (!src || !dst) {
> +			ret = -EINVAL;
> +			goto unlock;
> +		}
> +
> +		dst->excl = src->excl + level_size;
> +		dst->excl_cmpr = src->excl_cmpr + level_size;
> +		i_qgroups += 2;
> +	}
> +
> +unlock:
> +	spin_unlock(&fs_info->qgroup_lock);
> +out:
> +	return 0;

        return ret; ?

Thanks,
Tsutomu

> +}
> +
> +/*
> + * reserve some space for a qgroup and all its parents. The reservation takes
> + * place with start_transaction or dealloc_reserve, similar to ENOSPC
> + * accounting. If not enough space is available, EDQUOT is returned.
> + * We assume that the requested space is new for all qgroups.
> + */
> +int btrfs_qgroup_reserve(struct btrfs_root *root, u64 num_bytes)
> +{
> +	struct btrfs_root *quota_root;
> +	struct btrfs_qgroup *qgroup;
> +	struct btrfs_fs_info *fs_info = root->fs_info;
> +	u64 ref_root = root->root_key.objectid;
> +	int ret = 0;
> +	struct ulist *ulist = NULL;
> +	struct ulist_node *unode;
> +
> +	if (!is_fstree(ref_root))
> +		return 0;
> +
> +	if (num_bytes == 0)
> +		return 0;
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +	quota_root = fs_info->quota_root;
> +	if (!quota_root)
> +		goto out;
> +
> +	qgroup = find_qgroup_rb(fs_info, ref_root);
> +	if (!qgroup)
> +		goto out;
> +
> +	/*
> +	 * in a first step, we check all affected qgroups if any limits would
> +	 * be exceeded
> +	 */
> +	ulist = ulist_alloc(GFP_ATOMIC);
> +	ulist_add(ulist, qgroup->qgroupid, (unsigned long)qgroup, GFP_ATOMIC);
> +	unode = NULL;
> +	while ((unode = ulist_next(ulist, unode))) {
> +		struct btrfs_qgroup *qg;
> +		struct btrfs_qgroup_list *glist;
> +
> +		qg = (struct btrfs_qgroup *)unode->aux;
> +
> +		if ((qg->lim_flags&  BTRFS_QGROUP_LIMIT_MAX_RFER)&&
> +		    qg->reserved + qg->rfer + num_bytes>
> +		    qg->max_rfer)
> +			ret = -EDQUOT;
> +
> +		if ((qg->lim_flags&  BTRFS_QGROUP_LIMIT_MAX_EXCL)&&
> +		    qg->reserved + qg->excl + num_bytes>
> +		    qg->max_excl)
> +			ret = -EDQUOT;
> +
> +		list_for_each_entry(glist,&qg->groups, next_group) {
> +			ulist_add(ulist, glist->group->qgroupid,
> +				  (unsigned long)glist->group, GFP_ATOMIC);
> +		}
> +	}
> +	if (ret)
> +		goto out;
> +
> +	/*
> +	 * no limits exceeded, now record the reservation into all qgroups
> +	 */
> +	unode = NULL;
> +	while ((unode = ulist_next(ulist, unode))) {
> +		struct btrfs_qgroup *qg;
> +
> +		qg = (struct btrfs_qgroup *)unode->aux;
> +
> +		qg->reserved += num_bytes;
> +#if 0
> +		qgroup_dirty(fs_info, qg);/* XXX not necesarry */
> +#endif
> +	}
> +
> +out:
> +	spin_unlock(&fs_info->qgroup_lock);
> +	ulist_free(ulist);
> +
> +	return ret;
> +}
> +
> +void btrfs_qgroup_free(struct btrfs_root *root, u64 num_bytes)
> +{
> +	struct btrfs_root *quota_root;
> +	struct btrfs_qgroup *qgroup;
> +	struct btrfs_fs_info *fs_info = root->fs_info;
> +	struct ulist *ulist = NULL;
> +	struct ulist_node *unode;
> +	u64 ref_root = root->root_key.objectid;
> +
> +	if (!is_fstree(ref_root))
> +		return;
> +
> +	if (num_bytes == 0)
> +		return;
> +
> +	spin_lock(&fs_info->qgroup_lock);
> +
> +	quota_root = fs_info->quota_root;
> +	if (!quota_root)
> +		goto out;
> +
> +	qgroup = find_qgroup_rb(fs_info, ref_root);
> +	if (!qgroup)
> +		goto out;
> +
> +	ulist = ulist_alloc(GFP_ATOMIC);
> +	ulist_add(ulist, qgroup->qgroupid, (unsigned long)qgroup, GFP_ATOMIC);
> +	unode = NULL;
> +	while ((unode = ulist_next(ulist, unode))) {
> +		struct btrfs_qgroup *qg;
> +		struct btrfs_qgroup_list *glist;
> +
> +		qg = (struct btrfs_qgroup *)unode->aux;
> +
> +		qg->reserved -= num_bytes;
> +#if 0
> +qgroup_dirty(fs_info, qg);
> +#endif
> +
> +		list_for_each_entry(glist,&qg->groups, next_group) {
> +			ulist_add(ulist, glist->group->qgroupid,
> +				  (unsigned long)glist->group, GFP_ATOMIC);
> +		}
> +	}
> +
> +out:
> +	spin_unlock(&fs_info->qgroup_lock);
> +	ulist_free(ulist);
> +}


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


[Index of Archives]     [Linux Filesystem Development]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux