diff --git a/Makefile b/Makefile
index 86c73590d..74862ff32 100644
--- a/Makefile
+++ b/Makefile
@@ -257,7 +257,7 @@ convert_objects = convert/main.o convert/common.o convert/source-fs.o \
 mkfs_objects = mkfs/main.o mkfs/common.o mkfs/rootdir.o
 image_objects = image/main.o image/sanitize.o
 tune_objects = tune/main.o tune/seeding.o tune/change-uuid.o tune/change-metadata-uuid.o \
-	       tune/convert-bgt.o tune/change-csum.o check/clear-cache.o
+	       tune/convert-bgt.o tune/change-csum.o tune/quota.o check/clear-cache.o
 all_objects = $(objects) $(cmds_objects) $(libbtrfs_objects) $(convert_objects) \
 	      $(mkfs_objects) $(image_objects) $(tune_objects) $(libbtrfsutil_objects)
 
diff --git a/tune/main.c b/tune/main.c
index e38c1f6d3..b694d0da0 100644
--- a/tune/main.c
+++ b/tune/main.c
@@ -103,6 +103,7 @@ static const char * const tune_usage[] = {
 	OPTLINE("-x", "enable skinny metadata extent refs (mkfs: skinny-metadata)"),
 	OPTLINE("-n", "enable no-holes feature (mkfs: no-holes, more efficient sparse file representation)"),
 	OPTLINE("-S <0|1>", "set/unset seeding status of a device"),
+	OPTLINE("-q", "enable simple quotas on the file system. (mkfs: squota)"),
 	OPTLINE("--convert-to-block-group-tree", "convert filesystem to track block groups in "
 			"the separate block-group-tree instead of extent tree (sets the incompat bit)"),
 	OPTLINE("--convert-from-block-group-tree",
@@ -147,6 +148,7 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
 	char *new_fsid_str = NULL;
 	int ret;
 	u64 super_flags = 0;
+	int quota = 0;
 	int fd = -1;
 
 	btrfs_config_init();
@@ -169,7 +171,7 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
 #endif
 			{ NULL, 0, NULL, 0 }
 		};
-		int c = getopt_long(argc, argv, "S:rxfuU:nmM:", long_options, NULL);
+		int c = getopt_long(argc, argv, "S:rxqfuU:nmM:", long_options, NULL);
 
 		if (c < 0)
 			break;
@@ -184,6 +186,9 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
 		case 'x':
 			super_flags |= BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA;
 			break;
+		case 'q':
+			quota = 1;
+			break;
 		case 'n':
 			super_flags |= BTRFS_FEATURE_INCOMPAT_NO_HOLES;
 			break;
@@ -241,7 +246,7 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
 	}
 	if (!super_flags && !seeding_flag && !(random_fsid || new_fsid_str) &&
 	    !change_metadata_uuid && csum_type == -1 && !to_bg_tree &&
-	    !to_extent_tree && !to_fst) {
+	    !to_extent_tree && !to_fst && !quota) {
 		error("at least one option should be specified");
 		usage(&tune_cmd, 1);
 		return 1;
@@ -420,6 +425,10 @@ int BOX_MAIN(btrfstune)(int argc, char *argv[])
 		total++;
 	}
 
+	if (quota) {
+		ret = enable_quota(root->fs_info, true);
+	}
+
 	if (success == total) {
 		ret = 0;
 	} else {
diff --git a/tune/quota.c b/tune/quota.c
new file mode 100644
index 000000000..b7086c59e
--- /dev/null
+++ b/tune/quota.c
@@ -0,0 +1,172 @@
+#include <errno.h>
+
+#include "common/messages.h"
+#include "kernel-shared/ctree.h"
+#include "kernel-shared/disk-io.h"
+#include "kernel-shared/transaction.h"
+#include "kernel-shared/uapi/btrfs_tree.h"
+#include "tune/tune.h"
+
+static int create_qgroup(struct btrfs_fs_info *fs_info,
+			 struct btrfs_trans_handle *trans,
+			 u64 qgroupid)
+{
+	struct btrfs_path path;
+	struct btrfs_root *quota_root = fs_info->quota_root;
+	struct btrfs_key key;
+	int ret;
+
+	if (qgroupid >> BTRFS_QGROUP_LEVEL_SHIFT) {
+		error("qgroup level other than 0 is not supported yet");
+		return -ENOTTY;
+	}
+
+	key.objectid = 0;
+	key.type = BTRFS_QGROUP_INFO_KEY;
+	key.offset = qgroupid;
+
+	btrfs_init_path(&path);
+	ret = btrfs_insert_empty_item(trans, quota_root, &path, &key,
+				      sizeof(struct btrfs_qgroup_info_item));
+	btrfs_release_path(&path);
+	if (ret < 0)
+		return ret;
+
+	key.objectid = 0;
+	key.type = BTRFS_QGROUP_LIMIT_KEY;
+	key.offset = qgroupid;
+	ret = btrfs_insert_empty_item(trans, quota_root, &path, &key,
+				      sizeof(struct btrfs_qgroup_limit_item));
+	btrfs_release_path(&path);
+
+	printf("created qgroup for %llu\n", qgroupid);
+	return ret;
+}
+
+static int create_qgroups(struct btrfs_fs_info *fs_info,
+			  struct btrfs_trans_handle *trans)
+{
+	struct btrfs_key key = {
+		.objectid = 0,
+		.type = BTRFS_ROOT_REF_KEY,
+		.offset = 0,
+	};
+	struct btrfs_path path;
+	struct extent_buffer *leaf;
+	int slot;
+	struct btrfs_root *tree_root = fs_info->tree_root;
+	int ret;
+
+
+	ret = create_qgroup(fs_info, trans, BTRFS_FS_TREE_OBJECTID);
+	if (ret)
+		goto out;
+
+	btrfs_init_path(&path);
+	ret = btrfs_search_slot_for_read(tree_root, &key, &path, 1, 0);
+	if (ret)
+		goto out;
+
+	while (1) {
+		slot = path.slots[0];
+		leaf = path.nodes[0];
+		btrfs_item_key_to_cpu(leaf, &key, slot);
+		if (key.type == BTRFS_ROOT_REF_KEY) {
+			ret = create_qgroup(fs_info, trans, key.offset);
+			if (ret)
+				goto out;
+		}
+		ret = btrfs_next_item(tree_root, &path);
+		if (ret < 0) {
+			error("failed to advance to next item");
+			goto out;
+		}
+		if (ret)
+			break;
+	}
+
+out:
+	btrfs_release_path(&path);
+	return ret;
+}
+
+int enable_quota(struct btrfs_fs_info *fs_info, bool simple)
+{
+	struct btrfs_super_block *sb = fs_info->super_copy;
+	struct btrfs_trans_handle *trans;
+	int super_flags = btrfs_super_incompat_flags(sb);
+	struct btrfs_qgroup_status_item *qsi;
+	struct btrfs_root *quota_root;
+	struct btrfs_path path;
+	struct btrfs_key key;
+	int flags;
+	int ret;
+
+	trans = btrfs_start_transaction(fs_info->tree_root, 2);
+	if (IS_ERR(trans)) {
+		ret = PTR_ERR(trans);
+		errno = -ret;
+		error_msg(ERROR_MSG_START_TRANS, "%m");
+		return ret;
+	}
+
+	ret = btrfs_create_root(trans, fs_info, BTRFS_QUOTA_TREE_OBJECTID);
+	if (ret < 0) {
+		error("failed to create quota root: %d (%m)", ret);
+		goto fail;
+	}
+	quota_root = fs_info->quota_root;
+
+	/* Create the qgroup status item */
+	key.objectid = 0;
+	key.type = BTRFS_QGROUP_STATUS_KEY;
+	key.offset = 0;
+
+	btrfs_init_path(&path);
+	ret = btrfs_insert_empty_item(trans, quota_root, &path, &key,
+				      sizeof(*qsi));
+	if (ret < 0) {
+		error("failed to insert qgroup status item: %d (%m)", ret);
+		goto fail;
+	}
+
+	qsi = btrfs_item_ptr(path.nodes[0], path.slots[0],
+			     struct btrfs_qgroup_status_item);
+	btrfs_set_qgroup_status_generation(path.nodes[0], qsi, trans->transid);
+	btrfs_set_qgroup_status_rescan(path.nodes[0], qsi, 0);
+	flags = BTRFS_QGROUP_STATUS_FLAG_ON;
+	if (simple) {
+		btrfs_set_qgroup_status_enable_gen(path.nodes[0], qsi, trans->transid);
+		flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE;
+	} else {
+		flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
+	}
+
+	btrfs_set_qgroup_status_version(path.nodes[0], qsi, 1);
+	btrfs_set_qgroup_status_flags(path.nodes[0], qsi, flags);
+	btrfs_release_path(&path);
+
+	/* Create the qgroup items */
+	ret = create_qgroups(fs_info, trans);
+	if (ret < 0) {
+		error("failed to create qgroup items for subvols %d (%m)", ret);
+		goto fail;
+	}
+
+	/* Set squota incompat flag */
+	if (simple) {
+		super_flags |= BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA;
+		btrfs_set_super_incompat_flags(sb, super_flags);
+	}
+
+	ret = btrfs_commit_transaction(trans, fs_info->tree_root);
+	if (ret < 0) {
+		errno = -ret;
+		error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
+		return ret;
+	}
+	return ret;
+fail:
+	btrfs_abort_transaction(trans, ret);
+	return ret;
+}
diff --git a/tune/tune.h b/tune/tune.h
index 0ef249d89..cbf33b2e7 100644
--- a/tune/tune.h
+++ b/tune/tune.h
@@ -33,4 +33,7 @@ int convert_to_bg_tree(struct btrfs_fs_info *fs_info);
 int convert_to_extent_tree(struct btrfs_fs_info *fs_info);
 
 int btrfs_change_csum_type(struct btrfs_fs_info *fs_info, u16 new_csum_type);
+
+int enable_quota(struct btrfs_fs_info *fs_info, bool simple);
+
 #endif
