diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index ed9e13c..2dad701 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -8296,6 +8296,12 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
 	if (err)
 		goto out_end_trans;
 
+	ret = btrfs_del_qgroup_items(trans, root);
+	if (ret) {
+		btrfs_abort_transaction(trans, root, ret);
+		goto out_end_trans;
+	}
+
 	ret = btrfs_del_root(trans, tree_root, &root->root_key);
 	if (ret) {
 		btrfs_abort_transaction(trans, tree_root, ret);
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 1569338..2ec2432 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -35,7 +35,6 @@
 #include "qgroup.h"
 
 /* TODO XXX FIXME
- *  - subvol delete -> delete when ref goes to 0? delete limits also?
  *  - reorganize keys
  *  - compressed
  *  - sync
@@ -99,6 +98,16 @@ struct btrfs_qgroup_list {
 	struct btrfs_qgroup *member;
 };
 
+/*
+ * used in remove_qgroup_relations() to track qgroup relations that
+ * need deleting
+ */
+struct relation_rec {
+	struct list_head list;
+	u64 src;
+	u64 dst;
+};
+
 #define ptr_to_u64(x) ((u64)(uintptr_t)x)
 #define u64_to_ptr(x) ((struct btrfs_qgroup *)(uintptr_t)x)
 
@@ -551,9 +560,15 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans,
 	key.type = BTRFS_QGROUP_INFO_KEY;
 	key.offset = qgroupid;
 
+	/*
+	 * Avoid a transaction abort by catching -EEXIST here. In that
+	 * case, we proceed by re-initializing the existing structure
+	 * on disk.
+	 */
+
 	ret = btrfs_insert_empty_item(trans, quota_root, path, &key,
 				      sizeof(*qgroup_info));
-	if (ret)
+	if (ret && ret != -EEXIST)
 		goto out;
 
 	leaf = path->nodes[0];
@@ -572,7 +587,7 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans,
 	key.type = BTRFS_QGROUP_LIMIT_KEY;
 	ret = btrfs_insert_empty_item(trans, quota_root, path, &key,
 				      sizeof(*qgroup_limit));
-	if (ret)
+	if (ret && ret != -EEXIST)
 		goto out;
 
 	leaf = path->nodes[0];
@@ -2817,3 +2832,96 @@ btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info)
 		btrfs_queue_work(fs_info->qgroup_rescan_workers,
 				 &fs_info->qgroup_rescan_work);
 }
+
+static struct relation_rec *
+qlist_to_relation_rec(struct btrfs_qgroup_list *qlist, struct list_head *all)
+{
+	u64 group, member;
+	struct relation_rec *rec;
+
+	BUILD_BUG_ON(sizeof(struct btrfs_qgroup_list) < sizeof(struct relation_rec));
+
+	list_del(&qlist->next_group);
+	list_del(&qlist->next_member);
+	group = qlist->group->qgroupid;
+	member = qlist->member->qgroupid;
+	rec = (struct relation_rec *)qlist;
+	rec->src = group;
+	rec->dst = member;
+
+	list_add(&rec->list, all);
+	return rec;
+}
+
+static int remove_qgroup_relations(struct btrfs_trans_handle *trans,
+				   struct btrfs_fs_info *fs_info, u64 qgroupid)
+{
+	int ret, err;
+	struct btrfs_root *quota_root = fs_info->quota_root;
+	struct relation_rec *rec;
+	struct btrfs_qgroup_list *qlist;
+	struct btrfs_qgroup *qgroup;
+	LIST_HEAD(relations);
+
+	spin_lock(&fs_info->qgroup_lock);
+	qgroup = find_qgroup_rb(fs_info, qgroupid);
+
+	while (!list_empty(&qgroup->groups)) {
+		qlist = list_first_entry(&qgroup->groups,
+					 struct btrfs_qgroup_list, next_group);
+		rec = qlist_to_relation_rec(qlist, &relations);
+	}
+
+	while (!list_empty(&qgroup->members)) {
+		qlist = list_first_entry(&qgroup->members,
+					 struct btrfs_qgroup_list, next_member);
+		rec = qlist_to_relation_rec(qlist, &relations);
+	}
+
+	spin_unlock(&fs_info->qgroup_lock);
+
+	ret = 0;
+	list_for_each_entry(rec, &relations, list) {
+		ret = del_qgroup_relation_item(trans, quota_root, rec->src, rec->dst);
+		err = del_qgroup_relation_item(trans, quota_root, rec->dst, rec->src);
+		if (err && !ret)
+			ret = err;
+		if (ret && ret != -ENOENT)
+			break;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+int btrfs_del_qgroup_items(struct btrfs_trans_handle *trans,
+			   struct btrfs_root *root)
+{
+	int ret;
+	struct btrfs_fs_info *fs_info = root->fs_info;
+	struct btrfs_root *quota_root = fs_info->quota_root;
+	u64 qgroupid = root->root_key.objectid;
+
+	if (!fs_info->quota_enabled)
+		return 0;
+
+	mutex_lock(&fs_info->qgroup_ioctl_lock);
+
+	ret = remove_qgroup_relations(trans, fs_info, qgroupid);
+	if (ret)
+		goto out_unlock;
+
+	spin_lock(&fs_info->qgroup_lock);
+	del_qgroup_rb(quota_root->fs_info, qgroupid);
+	spin_unlock(&fs_info->qgroup_lock);
+
+	ret = del_qgroup_item(trans, quota_root, qgroupid);
+	if (ret && ret != -ENOENT)
+		goto out_unlock;
+
+	ret = 0;
+out_unlock:
+	mutex_unlock(&fs_info->qgroup_ioctl_lock);
+
+	return ret;
+}
diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h
index 18cc68c..b22a2ce 100644
--- a/fs/btrfs/qgroup.h
+++ b/fs/btrfs/qgroup.h
@@ -98,6 +98,9 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
 int btrfs_qgroup_reserve(struct btrfs_root *root, u64 num_bytes);
 void btrfs_qgroup_free(struct btrfs_root *root, u64 num_bytes);
 
+int btrfs_del_qgroup_items(struct btrfs_trans_handle *trans,
+			   struct btrfs_root *root);
+
 void assert_qgroups_uptodate(struct btrfs_trans_handle *trans);
 
 #ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
