2010-02-08 14:28:26

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 00/11] [RFC] Introduce subtree quota support This patch set introduce subtree and subtree-quota support for ext4

A subtree of a directory tree T is a tree consisting of a directory
(the subtree root) in T and all of its descendants in T.

Subtree assumptions:
* Each inode has subtree id.
* Subtree id is inherent from parent directory
* Inode can not belongs to different subtree

Subtree with id == 0 has special meaning. It may contains roots of
other subtrees (this feature is used for cross subtree renames)
This feature has much in common with XFS project_id.

In fact subtrees support may be implemented in generic vfs layer (may be
sometimes it will be)

There are many usecases for such subtrees

1) Choroot environment or Containers on common file-system
administrator creates a subtree,and setup quota
#mkdir chroot-env
#chattr -Q ${subtree_id} chroot-env
#tar jxf /tmp/fedora-x86_64.tar.bz2 -C chroot-env
#### as soon as subtree id inherented from parent untared content
#### automatically belongs to ${subtree_id}
#quotactl --type=subtree --id=${subtree_id} --bsoft=1000000 --bhard=1000000
###
#chroot chroot-env /bin/bash
2) NFS quota on server side
Administrator perform exactly the same stages, but instead of chroot
hi just export result subtree to nfs server


One may imagine it's own usecase.

Usually we already have some content which we want to see as subtree
In this case we have to in-depth traverse it from the subtree root.
add_to_subtree(dir, subtree_id) /* in-depth tree traversal */
{
while(de = getdents(dir)) {
if (IS_DIR(de))
add_to_subtree(de, subtree_id)
else
set_subtree_id(de, subtree);
}
set_subtree_id(de, subtree);
}
It is possible to manipulate subtree content

For example we have following hierarchy
/root/subtree-1/a/b/c/d/e/f/g
/root/subtree-2/dir/
want rename /root/subtree-1/a to /root/subtree-2/dir/AA
first we have to move "a" to default tree "root"
#rename /root/subtree-1/a /root/a-1
Then we have assign to default subtree. Do it in width traverse order
Because otherwise this result in subtree assumptions violation.
#walk_in_width(/root/AA, 0 /* default subtree */)
Then assign target subtree_id
#walk_in_depth(/root/AA, 2)
Ok now all content from /root/a-1 belongs to subtree_id == 2 so it is possible
rename it in to target place
#rename /root/a-1 /root/subtree-2/dir/AA

If you still reading this then i'll give comment about patch set organization

## Patch set prepared against ext4.git:next
## Following two patches already acceped by Jan Kara,
## but not yet in ext4 tree
##
ext4-mount-flags-manipulation-cleanup.patch
ext4-trivial-quota-cleanup.patch
## Following for patches perform necessety generic quota changes
## in order to new quota type
quota-sb_quota-state-flags-cleanup.patch
quota-generalize-quota-transfer-interface.patch
quota-introduce-get_id-callback.patch
quota-add-generic-subtree-quota-support.patch
## ext4 is so feature rich so there is no enouth space to store
## all it's mount flags
ext4-enlarge-mount-option-field.patch
## subtree support for ext4
ext4-introduce-subtree-logic.patch
ext4-subtree-add-kconfig-options.patch
ext4-add-subtree-support.patch
## subtree quota support for ext3
ext4-add-subtree-quota-support.patch


2010-02-08 14:28:28

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 05/11] quota: introduce get_id callback

During some quota oparations we have to determine quota_id for given inode
according to quota_type. But only USRQUOTA/GRPQUOTA id are intermediately
accessible from generic vfs-inode. This patch introduce new per_sb quota
operation for this purpose.

Signed-off-by: Dmitry Monakhov <[email protected]>
---
fs/ext3/super.c | 1 +
fs/ext4/super.c | 1 +
fs/ocfs2/quota_global.c | 1 +
fs/quota/dquot.c | 39 +++++++++++++++++++++++++++++----------
fs/reiserfs/super.c | 1 +
include/linux/quota.h | 1 +
include/linux/quotaops.h | 1 +
7 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index afa2b56..4ce86a6 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -758,6 +758,7 @@ static const struct dquot_operations ext3_quota_operations = {
.free_space = dquot_free_space,
.free_inode = dquot_free_inode,
.transfer = dquot_transfer,
+ .get_id = dquot_get_id,
.write_dquot = ext3_write_dquot,
.acquire_dquot = ext3_acquire_dquot,
.release_dquot = ext3_release_dquot,
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 4fd8cfb..68a5915 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1029,6 +1029,7 @@ static const struct dquot_operations ext4_quota_operations = {
.free_space = dquot_free_space,
.free_inode = dquot_free_inode,
.transfer = dquot_transfer,
+ .get_id = dquot_get_id,
.write_dquot = ext4_write_dquot,
.acquire_dquot = ext4_acquire_dquot,
.release_dquot = ext4_release_dquot,
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index b437dc0..1ede78f 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -858,6 +858,7 @@ const struct dquot_operations ocfs2_quota_operations = {
.free_space = dquot_free_space,
.free_inode = dquot_free_inode,
.transfer = dquot_transfer,
+ .get_id = dquot_get_id,
.write_dquot = ocfs2_write_dquot,
.acquire_dquot = ocfs2_acquire_dquot,
.release_dquot = ocfs2_release_dquot,
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index a34e57c..033c271 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1242,6 +1242,26 @@ static int info_bdq_free(struct dquot *dquot, qsize_t space)
return QUOTA_NL_BHARDBELOW;
return QUOTA_NL_NOWARN;
}
+
+qid_t dquot_get_id(struct inode *inode, int type, qid_t *new)
+{
+ switch (type) {
+ case USRQUOTA:
+ if (new)
+ return new[USRQUOTA];
+ else
+ return inode->i_uid;
+ case GRPQUOTA:
+ if (new)
+ return new[GRPQUOTA];
+ else
+ return inode->i_gid;
+ default:
+ BUG();
+ }
+}
+EXPORT_SYMBOL(dquot_get_id);
+
/*
* Initialize quota pointers in inode
* We do things in a bit complicated way but by that we avoid calling
@@ -1263,14 +1283,9 @@ int dquot_initialize(struct inode *inode, int type)
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (type != -1 && cnt != type)
continue;
- switch (cnt) {
- case USRQUOTA:
- id = inode->i_uid;
- break;
- case GRPQUOTA:
- id = inode->i_gid;
- break;
- }
+ if (!sb_has_quota_active(sb, cnt))
+ continue;
+ id = inode->i_sb->dq_op->get_id(inode, cnt, NULL);
got[cnt] = dqget(sb, id, cnt);
}

@@ -1668,6 +1683,7 @@ int dquot_transfer(struct inode *inode, qid_t *chid, unsigned long mask)
int cnt, ret = QUOTA_OK;
char warntype_to[MAXQUOTAS];
char warntype_from_inodes[MAXQUOTAS], warntype_from_space[MAXQUOTAS];
+ qid_t id;

/* First test before acquiring mutex - solves deadlocks when we
* re-enter the quota code and are already holding the mutex */
@@ -1680,8 +1696,10 @@ int dquot_transfer(struct inode *inode, qid_t *chid, unsigned long mask)
warntype_to[cnt] = QUOTA_NL_NOWARN;
}
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
- if (mask & (1 << cnt))
- transfer_to[cnt] = dqget(inode->i_sb, chid[cnt], cnt);
+ if (mask & (1 << cnt)) {
+ id = inode->i_sb->dq_op->get_id(inode, cnt, chid);
+ transfer_to[cnt] = dqget(inode->i_sb, id, cnt);
+ }
}
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
/* Now recheck reliably when holding dqptr_sem */
@@ -1810,6 +1828,7 @@ const struct dquot_operations dquot_operations = {
.free_space = dquot_free_space,
.free_inode = dquot_free_inode,
.transfer = dquot_transfer,
+ .get_id = dquot_get_id,
.write_dquot = dquot_commit,
.acquire_dquot = dquot_acquire,
.release_dquot = dquot_release,
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index b4a7dd0..897f2bc 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -623,6 +623,7 @@ static const struct dquot_operations reiserfs_quota_operations = {
.free_space = dquot_free_space,
.free_inode = dquot_free_inode,
.transfer = dquot_transfer,
+ .get_id = dquot_get_id,
.write_dquot = reiserfs_write_dquot,
.acquire_dquot = reiserfs_acquire_dquot,
.release_dquot = reiserfs_release_dquot,
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 74738e9..abf6a5a 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -303,6 +303,7 @@ struct dquot_operations {
int (*free_inode) (const struct inode *, qsize_t);
int (*transfer) (struct inode *, qid_t *, unsigned long);
int (*write_dquot) (struct dquot *); /* Ordinary dquot write */
+ qid_t (*get_id)(struct inode *, int, qid_t *new); /* Quota id for given type */
struct dquot *(*alloc_dquot)(struct super_block *, int); /* Allocate memory for new dquot */
void (*destroy_dquot)(struct dquot *); /* Free memory for dquot */
int (*acquire_dquot) (struct dquot *); /* Quota is going to be created on disk */
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index b803fd7..878f2a0 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -26,6 +26,7 @@ static inline void writeout_quota_sb(struct super_block *sb, int type)
sb->s_qcop->quota_sync(sb, type);
}

+qid_t dquot_get_id(struct inode *inode, int type, qid_t *new);
int dquot_initialize(struct inode *inode, int type);
int dquot_drop(struct inode *inode);
struct dquot *dqget(struct super_block *sb, unsigned int id, int type);
--
1.6.3.3


2010-02-08 14:28:25

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 01/11] ext4: mount flags manipulation cleanup

Replace intermediate EXT4_MOUNT_XXX flags manipulation to
corresponding macro.

Signed-off-by: Dmitry Monakhov <[email protected]>
---
fs/ext4/super.c | 31 +++++++++++++------------------
1 files changed, 13 insertions(+), 18 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 9e45e62..a247673 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -797,10 +797,10 @@ static inline void ext4_show_quota_options(struct seq_file *seq,
if (sbi->s_qf_names[GRPQUOTA])
seq_printf(seq, ",grpjquota=%s", sbi->s_qf_names[GRPQUOTA]);

- if (sbi->s_mount_opt & EXT4_MOUNT_USRQUOTA)
+ if (test_opt(sb, USRQUOTA))
seq_puts(seq, ",usrquota");

- if (sbi->s_mount_opt & EXT4_MOUNT_GRPQUOTA)
+ if (test_opt(sb, GRPQUOTA))
seq_puts(seq, ",grpquota");
#endif
}
@@ -1385,14 +1385,13 @@ static int parse_options(char *options, struct super_block *sb,
data_opt = EXT4_MOUNT_WRITEBACK_DATA;
datacheck:
if (is_remount) {
- if ((sbi->s_mount_opt & EXT4_MOUNT_DATA_FLAGS)
- != data_opt) {
+ if (test_opt(sb, DATA_FLAGS) != data_opt) {
ext4_msg(sb, KERN_ERR,
"Cannot change data mode on remount");
return 0;
}
} else {
- sbi->s_mount_opt &= ~EXT4_MOUNT_DATA_FLAGS;
+ clear_opt(sbi->s_mount_opt, DATA_FLAGS);
sbi->s_mount_opt |= data_opt;
}
break;
@@ -1631,18 +1630,14 @@ set_qf_format:
}
#ifdef CONFIG_QUOTA
if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
- if ((sbi->s_mount_opt & EXT4_MOUNT_USRQUOTA) &&
- sbi->s_qf_names[USRQUOTA])
+ if (test_opt(sb, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
clear_opt(sbi->s_mount_opt, USRQUOTA);

- if ((sbi->s_mount_opt & EXT4_MOUNT_GRPQUOTA) &&
- sbi->s_qf_names[GRPQUOTA])
+ if (test_opt(sb, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
clear_opt(sbi->s_mount_opt, GRPQUOTA);

- if ((sbi->s_qf_names[USRQUOTA] &&
- (sbi->s_mount_opt & EXT4_MOUNT_GRPQUOTA)) ||
- (sbi->s_qf_names[GRPQUOTA] &&
- (sbi->s_mount_opt & EXT4_MOUNT_USRQUOTA))) {
+ if ((sbi->s_qf_names[USRQUOTA] && test_opt(sb, GRPQUOTA)) ||
+ (sbi->s_qf_names[GRPQUOTA] && test_opt(sb, USRQUOTA))) {
ext4_msg(sb, KERN_ERR, "old and new quota "
"format mixing");
return 0;
@@ -2458,11 +2453,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
set_opt(sbi->s_mount_opt, POSIX_ACL);
#endif
if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_DATA)
- sbi->s_mount_opt |= EXT4_MOUNT_JOURNAL_DATA;
+ set_opt(sbi->s_mount_opt, JOURNAL_DATA);
else if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_ORDERED)
- sbi->s_mount_opt |= EXT4_MOUNT_ORDERED_DATA;
+ set_opt(sbi->s_mount_opt, ORDERED_DATA);
else if ((def_mount_opts & EXT4_DEFM_JMODE) == EXT4_DEFM_JMODE_WBACK)
- sbi->s_mount_opt |= EXT4_MOUNT_WRITEBACK_DATA;
+ set_opt(sbi->s_mount_opt, WRITEBACK_DATA);

if (le16_to_cpu(sbi->s_es->s_errors) == EXT4_ERRORS_PANIC)
set_opt(sbi->s_mount_opt, ERRORS_PANIC);
@@ -2490,7 +2485,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
goto failed_mount;

sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
- ((sbi->s_mount_opt & EXT4_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
+ (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);

if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV &&
(EXT4_HAS_COMPAT_FEATURE(sb, ~0U) ||
@@ -3537,7 +3532,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
ext4_abort(sb, __func__, "Abort forced by user");

sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
- ((sbi->s_mount_opt & EXT4_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
+ (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);

es = sbi->s_es;

--
1.6.3.3


2010-02-08 14:28:26

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 02/11] ext4: trivial quota cleanup

The patch is aimed to reorganize and simplify quota code a bit.
Quota code is itself complex enouth, but we can make it more readable
in some places:
- Move quota option parsing to separate functions.
- Simplify old-quota and journaled-quota mix check.

Signed-off-by: Dmitry Monakhov <[email protected]>
---
fs/ext4/super.c | 122 ++++++++++++++++++++++++++++++------------------------
1 files changed, 68 insertions(+), 54 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index a247673..4fd8cfb 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1213,6 +1213,63 @@ static ext4_fsblk_t get_sb_block(void **data)

#define DEFAULT_JOURNAL_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 3))

+#ifdef CONFIG_QUOTA
+static int set_qf_name(struct super_block *sb, int qtype, substring_t *args)
+{
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+ char *qname;
+
+ if (sb_any_quota_loaded(sb) &&
+ !sbi->s_qf_names[qtype]) {
+ ext4_msg(sb, KERN_ERR,
+ "Cannot change journaled "
+ "quota options when quota turned on");
+ return 0;
+ }
+ qname = match_strdup(args);
+ if (!qname) {
+ ext4_msg(sb, KERN_ERR,
+ "Not enough memory for storing quotafile name");
+ return 0;
+ }
+ if (sbi->s_qf_names[qtype] &&
+ strcmp(sbi->s_qf_names[qtype], qname)) {
+ ext4_msg(sb, KERN_ERR,
+ "%s quota file already specified", QTYPE2NAME(qtype));
+ kfree(qname);
+ return 0;
+ }
+ sbi->s_qf_names[qtype] = qname;
+ if (strchr(sbi->s_qf_names[qtype], '/')) {
+ ext4_msg(sb, KERN_ERR,
+ "quotafile must be on filesystem root");
+ kfree(sbi->s_qf_names[qtype]);
+ sbi->s_qf_names[qtype] = NULL;
+ return 0;
+ }
+ set_opt(sbi->s_mount_opt, QUOTA);
+ return 1;
+}
+
+static int clear_qf_name(struct super_block *sb, int qtype) {
+
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+
+ if (sb_any_quota_loaded(sb) &&
+ sbi->s_qf_names[qtype]) {
+ ext4_msg(sb, KERN_ERR, "Cannot change journaled quota options"
+ " when quota turned on");
+ return 0;
+ }
+ /*
+ * The space will be released later when all options are confirmed
+ * to be correct
+ */
+ sbi->s_qf_names[qtype] = NULL;
+ return 1;
+}
+#endif
+
static int parse_options(char *options, struct super_block *sb,
unsigned long *journal_devnum,
unsigned int *journal_ioprio,
@@ -1224,8 +1281,7 @@ static int parse_options(char *options, struct super_block *sb,
int data_opt = 0;
int option;
#ifdef CONFIG_QUOTA
- int qtype, qfmt;
- char *qname;
+ int qfmt;
#endif

if (!options)
@@ -1403,63 +1459,22 @@ static int parse_options(char *options, struct super_block *sb,
break;
#ifdef CONFIG_QUOTA
case Opt_usrjquota:
- qtype = USRQUOTA;
- goto set_qf_name;
- case Opt_grpjquota:
- qtype = GRPQUOTA;
-set_qf_name:
- if (sb_any_quota_loaded(sb) &&
- !sbi->s_qf_names[qtype]) {
- ext4_msg(sb, KERN_ERR,
- "Cannot change journaled "
- "quota options when quota turned on");
- return 0;
- }
- qname = match_strdup(&args[0]);
- if (!qname) {
- ext4_msg(sb, KERN_ERR,
- "Not enough memory for "
- "storing quotafile name");
+ if (!set_qf_name(sb, USRQUOTA, &args[0]))
return 0;
- }
- if (sbi->s_qf_names[qtype] &&
- strcmp(sbi->s_qf_names[qtype], qname)) {
- ext4_msg(sb, KERN_ERR,
- "%s quota file already "
- "specified", QTYPE2NAME(qtype));
- kfree(qname);
- return 0;
- }
- sbi->s_qf_names[qtype] = qname;
- if (strchr(sbi->s_qf_names[qtype], '/')) {
- ext4_msg(sb, KERN_ERR,
- "quotafile must be on "
- "filesystem root");
- kfree(sbi->s_qf_names[qtype]);
- sbi->s_qf_names[qtype] = NULL;
+ break;
+ case Opt_grpjquota:
+ if (!set_qf_name(sb, GRPQUOTA, &args[0]))
return 0;
- }
- set_opt(sbi->s_mount_opt, QUOTA);
break;
case Opt_offusrjquota:
- qtype = USRQUOTA;
- goto clear_qf_name;
+ if (!clear_qf_name(sb, USRQUOTA))
+ return 0;
+ break;
case Opt_offgrpjquota:
- qtype = GRPQUOTA;
-clear_qf_name:
- if (sb_any_quota_loaded(sb) &&
- sbi->s_qf_names[qtype]) {
- ext4_msg(sb, KERN_ERR, "Cannot change "
- "journaled quota options when "
- "quota turned on");
+ if (!clear_qf_name(sb, GRPQUOTA))
return 0;
- }
- /*
- * The space will be released later when all options
- * are confirmed to be correct
- */
- sbi->s_qf_names[qtype] = NULL;
break;
+
case Opt_jqfmt_vfsold:
qfmt = QFMT_VFS_OLD;
goto set_qf_format;
@@ -1636,8 +1651,7 @@ set_qf_format:
if (test_opt(sb, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
clear_opt(sbi->s_mount_opt, GRPQUOTA);

- if ((sbi->s_qf_names[USRQUOTA] && test_opt(sb, GRPQUOTA)) ||
- (sbi->s_qf_names[GRPQUOTA] && test_opt(sb, USRQUOTA))) {
+ if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
ext4_msg(sb, KERN_ERR, "old and new quota "
"format mixing");
return 0;
--
1.6.3.3


2010-02-08 14:28:27

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 03/11] quota: sb_quota state flags cleanup

- remove hardcoded USRQUOTA/GRPQUOTA flags
- convert int to bool for appropriate functions

Signed-off-by: Dmitry Monakhov <[email protected]>
---
include/linux/quota.h | 15 +++++++--------
include/linux/quotaops.h | 31 +++++++++++++++++--------------
2 files changed, 24 insertions(+), 22 deletions(-)

diff --git a/include/linux/quota.h b/include/linux/quota.h
index a6861f1..1f1a3a1 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -357,26 +357,25 @@ enum {
#define DQUOT_STATE_FLAGS (DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED | \
DQUOT_SUSPENDED)
/* Other quota flags */
-#define DQUOT_QUOTA_SYS_FILE (1 << 6) /* Quota file is a special
+#define DQUOT_STATE_LAST (_DQUOT_STATE_FLAGS * MAXQUOTAS)
+#define DQUOT_QUOTA_SYS_FILE (1 << DQUOT_STATE_LAST)
+ /* Quota file is a special
* system file and user cannot
* touch it. Filesystem is
* responsible for setting
* S_NOQUOTA, S_NOATIME flags
*/
-#define DQUOT_NEGATIVE_USAGE (1 << 7) /* Allow negative quota usage */
+#define DQUOT_NEGATIVE_USAGE (1 << (DQUOT_STATE_LAST + 1))
+ /* Allow negative quota usage */

static inline unsigned int dquot_state_flag(unsigned int flags, int type)
{
- if (type == USRQUOTA)
- return flags;
- return flags << _DQUOT_STATE_FLAGS;
+ return flags << _DQUOT_STATE_FLAGS * type;
}

static inline unsigned int dquot_generic_flag(unsigned int flags, int type)
{
- if (type == USRQUOTA)
- return flags;
- return flags >> _DQUOT_STATE_FLAGS;
+ return (flags >> _DQUOT_STATE_FLAGS * type) & DQUOT_STATE_FLAGS;
}

#ifdef CONFIG_QUOTA_NETLINK_INTERFACE
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index 3ebb231..70de499 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -83,53 +83,56 @@ static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type)
* Functions for checking status of quota
*/

-static inline int sb_has_quota_usage_enabled(struct super_block *sb, int type)
+static inline bool sb_has_quota_usage_enabled(struct super_block *sb, int type)
{
return sb_dqopt(sb)->flags &
dquot_state_flag(DQUOT_USAGE_ENABLED, type);
}

-static inline int sb_has_quota_limits_enabled(struct super_block *sb, int type)
+static inline bool sb_has_quota_limits_enabled(struct super_block *sb, int type)
{
return sb_dqopt(sb)->flags &
dquot_state_flag(DQUOT_LIMITS_ENABLED, type);
}

-static inline int sb_has_quota_suspended(struct super_block *sb, int type)
+static inline bool sb_has_quota_suspended(struct super_block *sb, int type)
{
return sb_dqopt(sb)->flags &
dquot_state_flag(DQUOT_SUSPENDED, type);
}

-static inline int sb_any_quota_suspended(struct super_block *sb)
+static inline unsigned sb_any_quota_suspended(struct super_block *sb)
{
- return sb_has_quota_suspended(sb, USRQUOTA) ||
- sb_has_quota_suspended(sb, GRPQUOTA);
+ unsigned type, tmsk = 0;
+ for (type = 0; type < MAXQUOTAS; type++)
+ tmsk |= sb_has_quota_suspended(sb, type) << type;
+ return tmsk;
}

/* Does kernel know about any quota information for given sb + type? */
-static inline int sb_has_quota_loaded(struct super_block *sb, int type)
+static inline bool sb_has_quota_loaded(struct super_block *sb, int type)
{
/* Currently if anything is on, then quota usage is on as well */
return sb_has_quota_usage_enabled(sb, type);
}

-static inline int sb_any_quota_loaded(struct super_block *sb)
+static inline unsigned sb_any_quota_loaded(struct super_block *sb)
{
- return sb_has_quota_loaded(sb, USRQUOTA) ||
- sb_has_quota_loaded(sb, GRPQUOTA);
+ unsigned type, tmsk = 0;
+ for (type = 0; type < MAXQUOTAS; type++)
+ tmsk |= sb_has_quota_loaded(sb, type) << type;
+ return tmsk;
}

-static inline int sb_has_quota_active(struct super_block *sb, int type)
+static inline bool sb_has_quota_active(struct super_block *sb, int type)
{
return sb_has_quota_loaded(sb, type) &&
!sb_has_quota_suspended(sb, type);
}

-static inline int sb_any_quota_active(struct super_block *sb)
+static inline unsigned sb_any_quota_active(struct super_block *sb)
{
- return sb_has_quota_active(sb, USRQUOTA) ||
- sb_has_quota_active(sb, GRPQUOTA);
+ return sb_any_quota_loaded(sb) & ~sb_any_quota_suspended(sb);
}

/*
--
1.6.3.3


2010-02-08 14:28:33

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 09/11] ext4: subtree add kconfig options


Signed-off-by: Dmitry Monakhov <[email protected]>
---
fs/ext4/Kconfig | 9 +++++++++
fs/ext4/Makefile | 1 +
2 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index 9ed1bb1..6c05d1a 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -75,6 +75,15 @@ config EXT4_FS_SECURITY
If you are not using a security module that requires using
extended attributes for file security labels, say N.

+config EXT4_SUBTREE
+ bool "Ext4 subtree support"
+ depends on EXT4_FS_XATTR
+ default y
+ help
+ Enables subtree support for ext4 filesystem
+
+ If you don't know what subtree are, say N
+
config EXT4_DEBUG
bool "EXT4 debugging support"
depends on EXT4_FS
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 8867b2a..2c314ce 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -11,3 +11,4 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
+ext4-$(CONFIG_EXT4_SUBTREE) += subtree.o
--
1.6.3.3


2010-02-08 14:28:34

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 11/11] ext4: add subtree quota support

This patch add full subtree quota support to ext4
mount option: sbtrquota, sbtrjquota, nosbtrjquota

Signed-off-by: Dmitry Monakhov <[email protected]>
---
fs/ext4/ext4.h | 1 +
fs/ext4/subtree.c | 15 ++++++++-
fs/ext4/super.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 92 insertions(+), 6 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index c707c7c..2799c6c 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -766,6 +766,7 @@ struct ext4_inode_info {
#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000LL /* Block validity checking */
#define EXT4_MOUNT_DISCARD 0x40000000LL /* Issue DISCARD requests */
#define EXT4_MOUNT_SUBTREE 0x80000000LL /* Dedicated subtree */
+#define EXT4_MOUNT_SBTRQUOTA 0x100000000LL /* Subtree quota support */

#define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt
#define set_opt(o, opt) o |= EXT4_MOUNT_##opt
diff --git a/fs/ext4/subtree.c b/fs/ext4/subtree.c
index 40d3d85..a27c245 100644
--- a/fs/ext4/subtree.c
+++ b/fs/ext4/subtree.c
@@ -198,14 +198,25 @@ retry:
/*
* Quota transfer only after xattr update. Because it may be
* impossible to roll back quota changed due to -EDQUOT
- * TODO: add quota transfer here
*/
+ if (sb_any_quota_active(inode->i_sb) && !IS_NOQUOTA(inode)) {
+ unsigned msk = 1 << SBTRQUOTA;
+ qid_t new[MAXQUOTAS];
+ new[SBTRQUOTA] = subtree;
+ vfs_dq_init(inode);
+ ret = inode->i_sb->dq_op->transfer(inode, new, msk);
+ if (ret) {
+ ret = -EDQUOT;
+ goto err;
+ }
+ }
+
EXT4_I_SUBTREE(inode) = subtree;
ret2 = ext4_journal_stop(handle);
if (!ret)
ret = ret2;
return ret;
-
+err:
/*
* Restore xattr to previous value. Xattr is already allocated, so
* operation may fail only due to some serious error.
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 2684453..7efa7c6 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -798,11 +798,17 @@ static inline void ext4_show_quota_options(struct seq_file *seq,
if (sbi->s_qf_names[GRPQUOTA])
seq_printf(seq, ",grpjquota=%s", sbi->s_qf_names[GRPQUOTA]);

+ if (sbi->s_qf_names[SBTRQUOTA])
+ seq_printf(seq, ",trjquota=%s", sbi->s_qf_names[SBTRQUOTA]);
+
if (test_opt(sb, USRQUOTA))
seq_puts(seq, ",usrquota");

if (test_opt(sb, GRPQUOTA))
seq_puts(seq, ",grpquota");
+
+ if (test_opt(sb, SBTRQUOTA))
+ seq_puts(seq, ",trquota");
#endif
}

@@ -1017,7 +1023,7 @@ static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data,
size_t len, loff_t off);
static ssize_t ext4_quota_write(struct super_block *sb, int type,
const char *data, size_t len, loff_t off);
-
+static qid_t ext4_get_quotid(struct inode *inode, int type, qid_t *new);
static const struct dquot_operations ext4_quota_operations = {
.initialize = dquot_initialize,
.drop = dquot_drop,
@@ -1032,7 +1038,7 @@ static const struct dquot_operations ext4_quota_operations = {
.free_space = dquot_free_space,
.free_inode = dquot_free_inode,
.transfer = dquot_transfer,
- .get_id = dquot_get_id,
+ .get_id = ext4_get_quotid,
.write_dquot = ext4_write_dquot,
.acquire_dquot = ext4_acquire_dquot,
.release_dquot = ext4_release_dquot,
@@ -1111,6 +1117,7 @@ enum {
Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
Opt_data_err_abort, Opt_data_err_ignore,
Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
+ Opt_sbtrquota, Opt_sbtrjquota, Opt_offtsbrquota, Opt_offsbtrjquota,
Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
Opt_noquota, Opt_ignore, Opt_barrier, Opt_nobarrier, Opt_err,
Opt_resize, Opt_usrquota, Opt_grpquota, Opt_i_version,
@@ -1163,6 +1170,8 @@ static const match_table_t tokens = {
{Opt_usrjquota, "usrjquota=%s"},
{Opt_offgrpjquota, "grpjquota="},
{Opt_grpjquota, "grpjquota=%s"},
+ {Opt_offsbtrjquota, "sbtrjquota="},
+ {Opt_sbtrjquota, "sbtrjquota=%s"},
{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
{Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
@@ -1170,6 +1179,7 @@ static const match_table_t tokens = {
{Opt_noquota, "noquota"},
{Opt_quota, "quota"},
{Opt_usrquota, "usrquota"},
+ {Opt_sbtrquota, "sbtrquota"},
{Opt_barrier, "barrier=%u"},
{Opt_barrier, "barrier"},
{Opt_nobarrier, "nobarrier"},
@@ -1472,6 +1482,37 @@ static int parse_options(char *options, struct super_block *sb,
if (!set_qf_name(sb, GRPQUOTA, &args[0]))
return 0;
break;
+#ifdef CONFIG_EXT4_SUBTREE
+ case Opt_sbtrjquota:
+ if (!test_opt(sb, SUBTREE)) {
+ ext4_msg(sb, KERN_ERR, "Cannot use subtree_"
+ "quota without subtree support");
+ return 0;
+ }
+ if (!set_qf_name(sb, SBTRQUOTA, &args[0]))
+ return 0;
+ break;
+ case Opt_offsbtrjquota:
+ if (!clear_qf_name(sb, SBTRQUOTA))
+ return 0;
+ break;
+ case Opt_sbtrquota:
+ if (!test_opt(sb, SUBTREE)) {
+ ext4_msg(sb, KERN_ERR, "Cannot use subtree_"
+ "quota without subtree support");
+ return 0;
+ }
+ set_opt(sbi->s_mount_opt, QUOTA);
+ set_opt(sbi->s_mount_opt, SBTRQUOTA);
+ break;
+#else
+ case Opt_sbtrjquota:
+ case Opt_offsbtrjquota:
+ case Opt_sbtrquota:
+ ext4_msg(sb, KERN_ERR,
+ "subtree quota options not supported");
+ break;
+#endif
case Opt_offusrjquota:
if (!clear_qf_name(sb, USRQUOTA))
return 0;
@@ -1517,18 +1558,22 @@ set_qf_format:
clear_opt(sbi->s_mount_opt, QUOTA);
clear_opt(sbi->s_mount_opt, USRQUOTA);
clear_opt(sbi->s_mount_opt, GRPQUOTA);
+ clear_opt(sbi->s_mount_opt, SBTRQUOTA);
break;
#else
case Opt_quota:
case Opt_usrquota:
case Opt_grpquota:
+ case Opt_sbtrquota:
ext4_msg(sb, KERN_ERR,
"quota options not supported");
break;
case Opt_usrjquota:
case Opt_grpjquota:
+ case Opt_sbtrjquota:
case Opt_offusrjquota:
case Opt_offgrpjquota:
+ case Opt_offsbtrjquota:
case Opt_jqfmt_vfsold:
case Opt_jqfmt_vfsv0:
case Opt_jqfmt_vfsv1:
@@ -1661,14 +1706,19 @@ set_qf_format:
}
}
#ifdef CONFIG_QUOTA
- if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
+ if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA] ||
+ sbi->s_qf_names[SBTRQUOTA]) {
if (test_opt(sb, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
clear_opt(sbi->s_mount_opt, USRQUOTA);

if (test_opt(sb, GRPQUOTA) && sbi->s_qf_names[GRPQUOTA])
clear_opt(sbi->s_mount_opt, GRPQUOTA);

- if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
+ if (test_opt(sb, SBTRQUOTA) && sbi->s_qf_names[SBTRQUOTA])
+ clear_opt(sbi->s_mount_opt, SBTRQUOTA);
+
+ if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA) ||
+ test_opt(sb, SBTRQUOTA)) {
ext4_msg(sb, KERN_ERR, "old and new quota "
"format mixing");
return 0;
@@ -3769,6 +3819,30 @@ static inline struct inode *dquot_to_inode(struct dquot *dquot)
return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
}

+static qid_t ext4_get_quotid(struct inode *inode, int type, qid_t *new)
+{
+ switch (type) {
+ case USRQUOTA:
+ if (new)
+ return new[USRQUOTA];
+ else
+ return inode->i_uid;
+ case GRPQUOTA:
+ if (new)
+ return new[GRPQUOTA];
+ else
+ return inode->i_gid;
+ case SBTRQUOTA:
+ BUG_ON(!test_opt(inode->i_sb, SBTRQUOTA) &&
+ !(EXT4_SB(inode->i_sb)->s_qf_names[SBTRQUOTA]));
+ if (new)
+ return new[SBTRQUOTA];
+ else
+ return EXT4_I(inode)->i_subtree;
+ default:
+ BUG();
+ }
+}
static int ext4_write_dquot(struct dquot *dquot)
{
int ret, err;
--
1.6.3.3


2010-02-08 14:28:29

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 07/11] ext4: enlarge mount option field

Currently only one bit left in s_mount_opt. Let's
double size it for future purposes.

Signed-off-by: Dmitry Monakhov <[email protected]>
---
fs/ext4/ext4.h | 64 +++++++++++++++++++++++++-------------------------
fs/ext4/ext4_jbd2.c | 2 +-
fs/ext4/super.c | 2 +-
3 files changed, 34 insertions(+), 34 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 67859fa..55f1034 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -412,7 +412,7 @@ struct ext4_new_group_data {
* Mount options
*/
struct ext4_mount_options {
- unsigned long s_mount_opt;
+ unsigned long long s_mount_opt;
uid_t s_resuid;
gid_t s_resgid;
unsigned long s_commit_interval;
@@ -730,36 +730,36 @@ struct ext4_inode_info {
/*
* Mount flags
*/
-#define EXT4_MOUNT_OLDALLOC 0x00002 /* Don't use the new Orlov allocator */
-#define EXT4_MOUNT_GRPID 0x00004 /* Create files with directory's group */
-#define EXT4_MOUNT_DEBUG 0x00008 /* Some debugging messages */
-#define EXT4_MOUNT_ERRORS_CONT 0x00010 /* Continue on errors */
-#define EXT4_MOUNT_ERRORS_RO 0x00020 /* Remount fs ro on errors */
-#define EXT4_MOUNT_ERRORS_PANIC 0x00040 /* Panic on errors */
-#define EXT4_MOUNT_MINIX_DF 0x00080 /* Mimics the Minix statfs */
-#define EXT4_MOUNT_NOLOAD 0x00100 /* Don't use existing journal*/
-#define EXT4_MOUNT_DATA_FLAGS 0x00C00 /* Mode for data writes: */
-#define EXT4_MOUNT_JOURNAL_DATA 0x00400 /* Write data to journal */
-#define EXT4_MOUNT_ORDERED_DATA 0x00800 /* Flush data before commit */
-#define EXT4_MOUNT_WRITEBACK_DATA 0x00C00 /* No data ordering */
-#define EXT4_MOUNT_UPDATE_JOURNAL 0x01000 /* Update the journal format */
-#define EXT4_MOUNT_NO_UID32 0x02000 /* Disable 32-bit UIDs */
-#define EXT4_MOUNT_XATTR_USER 0x04000 /* Extended user attributes */
-#define EXT4_MOUNT_POSIX_ACL 0x08000 /* POSIX Access Control Lists */
-#define EXT4_MOUNT_NO_AUTO_DA_ALLOC 0x10000 /* No auto delalloc mapping */
-#define EXT4_MOUNT_BARRIER 0x20000 /* Use block barriers */
-#define EXT4_MOUNT_NOBH 0x40000 /* No bufferheads */
-#define EXT4_MOUNT_QUOTA 0x80000 /* Some quota option set */
-#define EXT4_MOUNT_USRQUOTA 0x100000 /* "old" user quota */
-#define EXT4_MOUNT_GRPQUOTA 0x200000 /* "old" group quota */
-#define EXT4_MOUNT_DIOREAD_NOLOCK 0x400000 /* Enable support for dio read nolocking */
-#define EXT4_MOUNT_JOURNAL_CHECKSUM 0x800000 /* Journal checksums */
-#define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000 /* Journal Async Commit */
-#define EXT4_MOUNT_I_VERSION 0x2000000 /* i_version support */
-#define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */
-#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */
-#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */
-#define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */
+#define EXT4_MOUNT_OLDALLOC 0x00002LL /* Don't use the new Orlov allocator */
+#define EXT4_MOUNT_GRPID 0x00004LL /* Create files with directory's group */
+#define EXT4_MOUNT_DEBUG 0x00008LL /* Some debugging messages */
+#define EXT4_MOUNT_ERRORS_CONT 0x00010LL /* Continue on errors */
+#define EXT4_MOUNT_ERRORS_RO 0x00020LL /* Remount fs ro on errors */
+#define EXT4_MOUNT_ERRORS_PANIC 0x00040LL /* Panic on errors */
+#define EXT4_MOUNT_MINIX_DF 0x00080LL /* Mimics the Minix statfs */
+#define EXT4_MOUNT_NOLOAD 0x00100LL /* Don't use existing journal*/
+#define EXT4_MOUNT_DATA_FLAGS 0x00C00LL /* Mode for data writes: */
+#define EXT4_MOUNT_JOURNAL_DATA 0x00400LL /* Write data to journal */
+#define EXT4_MOUNT_ORDERED_DATA 0x00800LL /* Flush data before commit */
+#define EXT4_MOUNT_WRITEBACK_DATA 0x00C00LL /* No data ordering */
+#define EXT4_MOUNT_UPDATE_JOURNAL 0x01000LL /* Update the journal format */
+#define EXT4_MOUNT_NO_UID32 0x02000LL /* Disable 32-bit UIDs */
+#define EXT4_MOUNT_XATTR_USER 0x04000LL /* Extended user attributes */
+#define EXT4_MOUNT_POSIX_ACL 0x08000LL /* POSIX Access Control Lists */
+#define EXT4_MOUNT_NO_AUTO_DA_ALLOC 0x10000LL /* No auto delalloc mapping */
+#define EXT4_MOUNT_BARRIER 0x20000LL /* Use block barriers */
+#define EXT4_MOUNT_NOBH 0x40000LL /* No bufferheads */
+#define EXT4_MOUNT_QUOTA 0x80000LL /* Some quota option set */
+#define EXT4_MOUNT_USRQUOTA 0x100000LL /* "old" user quota */
+#define EXT4_MOUNT_GRPQUOTA 0x200000LL /* "old" group quota */
+#define EXT4_MOUNT_DIOREAD_NOLOCK 0x400000LL /* Enable support for dio read nolocking */
+#define EXT4_MOUNT_JOURNAL_CHECKSUM 0x800000LL /* Journal checksums */
+#define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000LL /* Journal Async Commit */
+#define EXT4_MOUNT_I_VERSION 0x2000000LL /* i_version support */
+#define EXT4_MOUNT_DELALLOC 0x8000000LL /* Delalloc support */
+#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000LL /* Abort on file data write */
+#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000LL /* Block validity checking */
+#define EXT4_MOUNT_DISCARD 0x40000000LL /* Issue DISCARD requests */

#define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt
#define set_opt(o, opt) o |= EXT4_MOUNT_##opt
@@ -908,7 +908,7 @@ struct ext4_sb_info {
struct buffer_head * s_sbh; /* Buffer containing the super block */
struct ext4_super_block *s_es; /* Pointer to the super block in the buffer */
struct buffer_head **s_group_desc;
- unsigned int s_mount_opt;
+ unsigned long long s_mount_opt;
unsigned int s_mount_flags;
ext4_fsblk_t s_sb_block;
uid_t s_resuid;
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index b57e5c7..36e1f98 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -58,7 +58,7 @@ int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
BUFFER_TRACE(bh, "enter");

jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, "
- "data mode %x\n",
+ "data mode %llx\n",
bh, is_metadata, inode->i_mode,
test_opt(inode->i_sb, DATA_FLAGS));

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 68a5915..93ba5e2 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1720,7 +1720,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
ext4_commit_super(sb, 1);
if (test_opt(sb, DEBUG))
printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
- "bpg=%lu, ipg=%lu, mo=%04x]\n",
+ "bpg=%lu, ipg=%lu, mo=%08llx]\n",
sb->s_blocksize,
sbi->s_groups_count,
EXT4_BLOCKS_PER_GROUP(sb),
--
1.6.3.3


2010-02-08 14:28:30

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 08/11] ext4: introduce subtree logic

* Abstract
A subtree of a directory tree T is a tree consisting of a directory
(the subtree root) in T and all of its descendants in T.

Subtree feature allows to create an isolated (from user point of view)
trees.

Subtree assumptions:
(1) Each inode has subtree id. This id is persistently stored inside
inode (xattr, usually inside ibody)
(2) Subtree id is inherent from parent directory
(3) Inode can not belongs to different subtree
Otherwise changes in one subtree result in changes in other subtree
which contradict to isolation criteria.

This feature is similar to project-id in XFS. One may assign some id to
a subtree. Each entry from the subtree may be accounted in directory
subtree quota. Will appear in later patches.

* Disk layout
Subtree id is stored on disk inside xattr usually inside ibody.
Xattr is used only as a data storage, It has not user visiable xattr
interface.

Signed-off-by: Dmitry Monakhov <[email protected]>
---
fs/ext4/subtree.c | 292 +++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/ext4/subtree.h | 60 +++++++++++
fs/ext4/xattr.h | 1 +
3 files changed, 353 insertions(+), 0 deletions(-)
create mode 100644 fs/ext4/subtree.c
create mode 100644 fs/ext4/subtree.h

diff --git a/fs/ext4/subtree.c b/fs/ext4/subtree.c
new file mode 100644
index 0000000..40d3d85
--- /dev/null
+++ b/fs/ext4/subtree.c
@@ -0,0 +1,292 @@
+/*
+ * linux/fs/ext4/subtree.c
+ *
+ * Support for subtree quota for ext4 filesystem
+ *
+ * Copyright (C) Parallels Inc, 2010
+ * Dmitry Monakhov <[email protected]>
+ *
+ */
+
+/*
+ * *Abstract*
+ * A subtree of a directory tree T is a tree consisting of a directory
+ * (the subtree root) in T and all of its descendants in T.
+ *
+ * Subtree feature allows to create an isolated trees.
+ * Isolation means what:
+ * 1) Subtrees has no common inodes (no hadlinks across subtrees)
+ * 2) All descendants belongs to the same subtree.
+ * Such isolated subtrees may be restricted by subtree quota.
+ *
+ * Subtree assumptions:
+ * (1) Each inode has subtree id. This id is persistently stored inside
+ * inode's xattr, usually inside ibody
+ * (2) Subtree id is inherent from parent directory
+ * (3) Inode can not belongs to different subtree trees
+ * Otherwise changes in one subtree result in changes in other subtree
+ * which contradict to isolation criteria.
+ *
+ * Default directory tree (with subtree_id == 0) has special meaning.
+ * directory which belongs to default subtree may contains entries with
+ * other trees. It may be used for renames between different subtrees.
+ */
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/mbcache.h>
+#include <linux/quotaops.h>
+#include <linux/rwsem.h>
+#include "ext4_jbd2.h"
+#include "ext4.h"
+#include "xattr.h"
+#include "acl.h"
+#include "subtree.h"
+enum {
+ EXT4_SUBTREE_SAME = 1, /* Both nodes belongs to same subtree */
+ EXT4_SUBTREE_COMMON, /* Ancestor tree includes descent subtree*/
+ EXT4_SUBTREE_CROSS, /* Nodes belongs to different subtrees */
+};
+
+#define EXT4_I_SUBTREE(inode) (EXT4_I(inode)->i_subtree)
+/**
+ Check ancestor descendant subtree relationship.
+ @ancino: ancestor inode
+ @inode: descendant inode
+ */
+static inline int ext4_which_subtree(struct inode *ancino, struct inode *inode)
+{
+#ifdef EXT4_SUBTREE_DEBUG
+ BUG_ON(EXT4_I_SUBTREE(ancino) > EXT4_SUBTREE_ID_MAX);
+ BUG_ON(EXT4_I_SUBTREE(inode) > EXT4_SUBTREE_ID_MAX);
+#endif
+ if (EXT4_I_SUBTREE(inode) == EXT4_I_SUBTREE(ancino))
+ return EXT4_SUBTREE_SAME;
+ else if (EXT4_I_SUBTREE(ancino) == 0)
+ /*
+ * Ancestor inode belongs to default tree and it includes
+ * other subtrees by default
+ */
+ return EXT4_SUBTREE_COMMON;
+ return EXT4_SUBTREE_CROSS;
+}
+/*
+ * Function is called for new inode before quota init.
+ */
+void ext4_st_inherent_subtree(struct inode *inode, struct inode *dir)
+{
+#ifdef EXT4_SUBTREE_DEBUG
+ BUG_ON(EXT4_I_SUBTREE(dir) > EXT4_SUBTREE_ID_MAX);
+#endif
+ EXT4_I_SUBTREE(inode) = EXT4_I_SUBTREE(dir);
+}
+/*
+ * Initialize directory subtree id of a new inode. Called from ext4_new_inode.
+ *
+ * dir->i_mutex: down
+ * inode->i_mutex: up (access to inode is still exclusive)
+ */
+int ext4_st_init(handle_t *handle, struct inode *inode, struct inode *dir)
+{
+ struct ext4_subtree_entry est;
+ int ret;
+ if (!test_opt(inode->i_sb, SUBTREE))
+ return 0;
+
+ if (EXT4_I_SUBTREE(inode) > EXT4_SUBTREE_ID_MAX)
+ return -EINVAL;
+
+ est.est_id = cpu_to_le32(EXT4_I_SUBTREE(dir));
+ est.est_fl = cpu_to_le16(EXT4_SUBTREE_FL_VALID);
+ ret = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_SUBTREE,
+ "", &est, sizeof(est), XATTR_CREATE);
+ return ret;
+}
+
+/*
+ * Read subtree from inode.
+ */
+int ext4_st_read(struct inode *inode)
+{
+ struct ext4_subtree_entry est;
+ int ret;
+ if (!test_opt(inode->i_sb, SUBTREE))
+ return 0;
+
+ ret = ext4_xattr_get(inode, EXT4_XATTR_INDEX_SUBTREE, "",
+ &est, sizeof(est));
+
+ if (ret != -ENODATA && ret != sizeof(est)) {
+ EXT4_I_SUBTREE(inode) = 0;
+ if (ret > 0)
+ /* Index currupted */
+ ret = -EIO;
+ return ret;
+ }
+ if (ret != -ENODATA) {
+ /*
+ * Inode has not subtree xattr yet. That's ok
+ * Belongs to default tree.
+ */
+ EXT4_I_SUBTREE(inode) = 0;
+ return 0;
+ }
+ if (!(le16_to_cpu(est.est_fl) | EXT4_SUBTREE_FL_VALID) ||
+ le32_to_cpu(est.est_id) > EXT4_SUBTREE_ID_MAX) {
+ EXT4_I_SUBTREE(inode) = 0;
+ return -EINVAL;
+ }
+
+ EXT4_I_SUBTREE(inode) = le32_to_cpu(est.est_id);
+ return 0;
+}
+
+/*
+ * Change subtree id for a given inode
+ * ->i_mutex is locked.
+ */
+int ext4_st_change(struct inode *inode, struct inode *dir, int subtree)
+{
+ struct ext4_subtree_entry est;
+ int credits = EXT4_XATTR_TRANS_BLOCKS +
+ EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
+ handle_t *handle;
+ int retries = 0;
+ int ret, ret2;
+
+ if (subtree > EXT4_SUBTREE_ID_MAX)
+ return -EINVAL;
+
+ /*
+ * In order to preserve subtree structure caller must traverse
+ * hierarchy in following order.
+ * change from some subtree to default subtree: from parent to child
+ * change from default subtree to some subtree: from child to parent
+ */
+ if (dir && (EXT4_I_SUBTREE(dir) != 0 &&
+ EXT4_I_SUBTREE(dir) != subtree))
+ return -EXDEV;
+
+ if (EXT4_I_SUBTREE(inode) == subtree)
+ return 0;
+retry:
+ handle = ext4_journal_start(inode, credits);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ est.est_id = cpu_to_le32(subtree);
+ est.est_fl = cpu_to_le16(EXT4_SUBTREE_FL_VALID);
+ ret = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_SUBTREE,
+ "", &est, sizeof(est), XATTR_REPLACE);
+ if (unlikely(ret == -ENODATA)) {
+ /* Inode has not subtree xattr on disk */
+ BUG_ON(EXT4_I_SUBTREE(inode) != 0);
+ ret = ext4_xattr_set_handle(handle, inode,
+ EXT4_XATTR_INDEX_SUBTREE,
+ "", &est, sizeof(est), XATTR_CREATE);
+ }
+ if (ret) {
+ ret2 = ext4_journal_stop(handle);
+ if (!ret2 && ret == -ENOSPC &&
+ ext4_should_retry_alloc(inode->i_sb, &retries))
+ goto retry;
+ if (ret == 0)
+ ret = ret2;
+ }
+ if (ret)
+ return ret;
+ /*
+ * Quota transfer only after xattr update. Because it may be
+ * impossible to roll back quota changed due to -EDQUOT
+ * TODO: add quota transfer here
+ */
+ EXT4_I_SUBTREE(inode) = subtree;
+ ret2 = ext4_journal_stop(handle);
+ if (!ret)
+ ret = ret2;
+ return ret;
+
+ /*
+ * Restore xattr to previous value. Xattr is already allocated, so
+ * operation may fail only due to some serious error.
+ */
+ est.est_id = cpu_to_le32(EXT4_I_SUBTREE(inode));
+ est.est_fl = cpu_to_le16(EXT4_SUBTREE_FL_VALID);
+
+ ret2 = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_SUBTREE,
+ "", &est, sizeof(est), XATTR_REPLACE);
+ if (ret2)
+ ext4_warning(inode->i_sb, __func__,
+ "Cant restore subtree id err:%d", ret2);
+ ext4_journal_stop(handle);
+ return ret;
+
+}
+/**
+ * Check subtree assumptions on ext4_link()
+ * @tdir: target directory inode
+ * @inode: inode in question
+ * @return: true if link is possible, zero otherwise
+ */
+inline int ext4_st_may_link(struct inode *tdir, struct inode *inode)
+{
+ if (!test_opt(inode->i_sb, SUBTREE))
+ return 1;
+ /*
+ * According to subtree quota assumptions inode can not belongs to
+ * different quota trees.
+ */
+ if(ext4_which_subtree(tdir, inode) != EXT4_SUBTREE_SAME)
+ return 0;
+ return 1;
+}
+
+/**
+ * Check for directory subtree assumptions on ext4_rename()
+ * @new_dir: new directory inode
+ * @inode: inode in question
+ * @return: true if rename is possible, zero otherwise.
+ */
+inline int ext4_st_may_rename(struct inode *new_dir, struct inode *inode)
+{
+ int same;
+ // XXX: Seems what i_nlink check is racy
+ // Is it possible to get inode->i_mutex here?
+ if (!test_opt(inode->i_sb, SUBTREE))
+ return 1;
+ same = ext4_which_subtree(new_dir, inode);
+ if (S_ISDIR(inode->i_mode)) {
+ if (same == EXT4_SUBTREE_CROSS)
+ return 0;
+ } else {
+ if (inode->i_nlink > 1) {
+ /*
+ * If we allow to move any dentry of inode which has
+ * more than one link between subtrees then we end up
+ * with inode which belongs to different subtrees.
+ */
+ if (same != EXT4_SUBTREE_SAME)
+ return 0;
+ } else {
+ if (same == EXT4_SUBTREE_CROSS)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Check subtree parent/child relationship assumptions.
+ */
+inline void ext4_st_check_parent(struct inode *dir, struct inode *inode)
+{
+ if (!test_opt(dir->i_sb, SUBTREE))
+ return;
+ if (ext4_which_subtree(dir, inode) == EXT4_SUBTREE_CROSS) {
+ ext4_warning(inode->i_sb, __func__,
+ "Bad subtree hierarchy: directory{ino:%lu, sbtr:%u}"
+ "inoode{ino:%lu, sbtr:%u}\n",
+ dir->i_ino, EXT4_I_SUBTREE(dir),
+ inode->i_ino, EXT4_I_SUBTREE(inode));
+ }
+}
diff --git a/fs/ext4/subtree.h b/fs/ext4/subtree.h
new file mode 100644
index 0000000..83583c7
--- /dev/null
+++ b/fs/ext4/subtree.h
@@ -0,0 +1,60 @@
+/*
+ * linux/fs/ext4/subtree.h
+ *
+ * Support for subtree quota for ext4 filesystem
+ *
+ * Copyright (C) Parallels Inc, 2010
+ * Dmitry Monakhov <[email protected]>
+ *
+ */
+#ifndef _EXT4_SUBTREE_H
+#define _EXT4_SUBTREE_H
+
+/* On disk data size of subtree entry */
+struct ext4_subtree_entry
+{
+ __le16 est_fl;
+ __le32 est_id;
+ __le16 est_reserved;
+};
+enum {
+ EXT4_SUBTREE_FL_VALID = 0x1,
+};
+#define EXT4_SUBTREE_ID_MAX ((1UL << 31) -16)
+
+#ifdef CONFIG_EXT4_SUBTREE
+void ext4_st_inherent_subtree(struct inode *inode, struct inode *dir);
+int ext4_st_init(handle_t *h, struct inode *inode, struct inode *dir);
+int ext4_st_read(struct inode *inode);
+int ext4_st_change(struct inode *inode, struct inode *dir, int subtree);
+int ext4_st_may_link(struct inode *dir, struct inode *inode);
+int ext4_st_may_rename(struct inode *dir, struct inode *inode);
+void ext4_st_check_parent(struct inode *dir, struct inode *inode);
+#else
+inline void ext4_st_inherent_subtree(struct inode *inode, struct inode *dir)
+{
+ return 0;
+}
+inline int ext4_st_init(handle_t *, struct inode *, struct inode *)
+{
+ return 0
+}
+inline int ext4_st_read(struct inode *inode)
+{
+ return 0;
+}
+inline int ext4_st_change(struct inode *inode, struct inode *dir, int subtree)
+{
+ return 0;
+}
+inline int ext4_st_may_link(struct inode *dir, struct inode *inode)
+{
+ return 1;
+}
+inline int ext4_st_may_rename(struct inode *dir, struct inode *inode)
+{
+ return 1;
+}
+inline void ext4_st_check_parent(struct inode *dir, struct inode *inode) {}
+#endif /* CONFIG_EXT4_SUBTREE */
+#endif
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 8ede88b..688dde8 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -21,6 +21,7 @@
#define EXT4_XATTR_INDEX_TRUSTED 4
#define EXT4_XATTR_INDEX_LUSTRE 5
#define EXT4_XATTR_INDEX_SECURITY 6
+#define EXT4_XATTR_INDEX_SUBTREE 7

struct ext4_xattr_header {
__le32 h_magic; /* magic number for identification */
--
1.6.3.3


2010-02-08 14:28:34

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 10/11] ext4: add subtree support

After this patch ext4 has basic subtree support.
* User visiable changes
mount option: "subtree"
mount flag: EXT4_MOUNT_SUBTREE
ioctl EXT4_IOC_GET_TREEID / EXT4_IOC_SET_TREEID

Signed-off-by: Dmitry Monakhov <[email protected]>
---
fs/ext4/ext4.h | 6 ++++++
fs/ext4/ialloc.c | 6 ++++++
fs/ext4/inode.c | 5 +++++
fs/ext4/ioctl.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/ext4/namei.c | 9 ++++++++-
fs/ext4/super.c | 18 +++++++++++++++++-
6 files changed, 94 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 55f1034..c707c7c 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -390,6 +390,8 @@ struct ext4_new_group_data {
/* note ioctl 11 reserved for filesystem-independent FIEMAP ioctl */
#define EXT4_IOC_ALLOC_DA_BLKS _IO('f', 12)
#define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent)
+#define EXT4_IOC_GET_SUBTREE _IOR('f', 16, unsigned int)
+#define EXT4_IOC_SET_SUBTREE _IOW('f', 17, unsigned int)

/*
* ioctl commands in 32 bit emulation
@@ -711,6 +713,9 @@ struct ext4_inode_info {
*/
tid_t i_sync_tid;
tid_t i_datasync_tid;
+#ifdef CONFIG_EXT4_SUBTREE
+ unsigned i_subtree;
+#endif
};

/*
@@ -760,6 +765,7 @@ struct ext4_inode_info {
#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000LL /* Abort on file data write */
#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000LL /* Block validity checking */
#define EXT4_MOUNT_DISCARD 0x40000000LL /* Issue DISCARD requests */
+#define EXT4_MOUNT_SUBTREE 0x80000000LL /* Dedicated subtree */

#define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt
#define set_opt(o, opt) o |= EXT4_MOUNT_##opt
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 2fab5ad..d527a0c 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -28,6 +28,7 @@
#include "ext4_jbd2.h"
#include "xattr.h"
#include "acl.h"
+#include "subtree.h"

#include <trace/events/ext4.h>

@@ -1033,6 +1034,7 @@ got:
ext4_set_inode_state(inode, EXT4_STATE_NEW);

ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
+ ext4_st_inherent_subtree(inode, dir);

ret = inode;
if (vfs_dq_alloc_inode(inode)) {
@@ -1048,6 +1050,10 @@ got:
if (err)
goto fail_free_drop;

+ err = ext4_st_init(handle, inode, dir);
+ if (err)
+ goto fail_free_drop;
+
if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS)) {
/* set extent flag only for directory, file and normal symlink*/
if (S_ISDIR(mode) || S_ISREG(mode) || S_ISLNK(mode)) {
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 218ea0b..2a31b0f 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -44,6 +44,7 @@
#include "xattr.h"
#include "acl.h"
#include "ext4_extents.h"
+#include "subtree.h"

#include <trace/events/ext4.h>

@@ -5053,6 +5054,10 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
if (ret)
goto bad_inode;

+ ret = ext4_st_read(inode);
+ if (ret)
+ goto bad_inode;
+
if (S_ISREG(inode->i_mode)) {
inode->i_op = &ext4_file_inode_operations;
inode->i_fop = &ext4_file_operations;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index b63d193..135d059 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -17,6 +17,7 @@
#include <asm/uaccess.h>
#include "ext4_jbd2.h"
#include "ext4.h"
+#include "subtree.h"

long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
@@ -164,6 +165,57 @@ setversion_out:
mnt_drop_write(filp->f_path.mnt);
return err;
}
+#ifdef CONFIG_EXT4_SUBTREE
+ case EXT4_IOC_GET_SUBTREE:
+ if (test_opt(inode->i_sb, SUBTREE))
+ return put_user(EXT4_I(inode)->i_subtree,
+ (unsigned int __user*) arg);
+ else
+ return -EINVAL;
+ case EXT4_IOC_SET_SUBTREE: {
+ int err;
+ unsigned int subtree;
+ struct inode *dir = IS_ROOT(filp->f_dentry) ?
+ NULL : filp->f_dentry->d_parent->d_inode;
+
+ if (!test_opt(inode->i_sb, SUBTREE))
+ return -EINVAL;
+
+ if (!is_owner_or_cap(inode))
+ return -EACCES;
+
+ if (get_user(subtree, (unsigned int __user *) arg))
+ return -EFAULT;
+
+ err = mnt_want_write(filp->f_path.mnt);
+ if (err)
+ return err;
+ /*
+ * Nested subtrees are not allowed! We have to hold
+ * dir->i_mutex for entire operation in order to prevent
+ * from races with parent's ioctl.
+ */
+ if (dir)
+ mutex_lock(&dir->i_mutex);
+ mutex_lock(&inode->i_mutex);
+
+ err = -EPERM;
+ /* Is it quota file? Do not allow user to mess with it */
+ if (IS_NOQUOTA(inode))
+ goto settree_out;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ goto settree_out;
+
+ err = ext4_st_change(inode, dir, subtree);
+settree_out:
+ if (dir)
+ mutex_unlock(&dir->i_mutex);
+ mutex_unlock(&inode->i_mutex);
+ mnt_drop_write(filp->f_path.mnt);
+ return err;
+ }
+#endif
#ifdef CONFIG_JBD2_DEBUG
case EXT4_IOC_WAIT_FOR_READONLY:
/*
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 17a17e1..4b1ac37 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -39,6 +39,7 @@

#include "xattr.h"
#include "acl.h"
+#include "subtree.h"

/*
* define how far ahead to read directories while searching them.
@@ -1086,6 +1087,7 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, stru
return ERR_CAST(inode);
}
}
+ ext4_st_check_parent(dir, inode);
}
return d_splice_alias(inode, dentry);
}
@@ -2314,7 +2316,8 @@ static int ext4_link(struct dentry *old_dentry,
*/
if (inode->i_nlink == 0)
return -ENOENT;
-
+ if (!ext4_st_may_link(dir, inode))
+ return -EXDEV;
retry:
handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS);
@@ -2364,6 +2367,10 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
* in separate transaction */
if (new_dentry->d_inode)
vfs_dq_init(new_dentry->d_inode);
+
+ if (!ext4_st_may_rename(new_dir, old_dentry->d_inode))
+ return -EXDEV;
+
handle = ext4_journal_start(old_dir, 2 *
EXT4_DATA_TRANS_BLOCKS(old_dir->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2);
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 93ba5e2..2684453 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -46,6 +46,7 @@
#include "xattr.h"
#include "acl.h"
#include "mballoc.h"
+#include "subtree.h"

#define CREATE_TRACE_POINTS
#include <trace/events/ext4.h>
@@ -853,6 +854,8 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)
seq_puts(seq, ",debug");
if (test_opt(sb, OLDALLOC))
seq_puts(seq, ",oldalloc");
+ if (test_opt(sb, SUBTREE))
+ seq_puts(seq, ",subtree");
#ifdef CONFIG_EXT4_FS_XATTR
if (test_opt(sb, XATTR_USER) &&
!(def_mount_opts & EXT4_DEFM_XATTR_USER))
@@ -1115,7 +1118,7 @@ enum {
Opt_block_validity, Opt_noblock_validity,
Opt_inode_readahead_blks, Opt_journal_ioprio,
Opt_dioread_nolock, Opt_dioread_lock,
- Opt_discard, Opt_nodiscard,
+ Opt_discard, Opt_nodiscard, Opt_subtree, Opt_nosubtree,
};

static const match_table_t tokens = {
@@ -1186,6 +1189,8 @@ static const match_table_t tokens = {
{Opt_dioread_lock, "dioread_lock"},
{Opt_discard, "discard"},
{Opt_nodiscard, "nodiscard"},
+ {Opt_subtree, "subtree"},
+ {Opt_nosubtree, "nosubtree"},
{Opt_err, NULL},
};

@@ -1612,6 +1617,17 @@ set_qf_format:
*journal_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE,
option);
break;
+ case Opt_subtree:
+ if (is_remount && !test_opt(sb, SUBTREE)) {
+ ext4_msg(sb, KERN_ERR,
+ "Cannot toggle subtree support on remount");
+ return 0;
+ }
+ set_opt(sbi->s_mount_opt, SUBTREE);
+ break;
+ case Opt_nosubtree:
+ clear_opt(sbi->s_mount_opt, SUBTREE);
+ break;
case Opt_noauto_da_alloc:
set_opt(sbi->s_mount_opt,NO_AUTO_DA_ALLOC);
break;
--
1.6.3.3


2010-02-08 14:28:28

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 04/11] quota: generalize quota transfer interface

Current quota transfer interface support only uid/gid.
This patch extend interface in order to support various quotas types
The goal is accomplished without changes in most frequently used
vfs_dq_transfer() func.

Signed-off-by: Dmitry Monakhov <[email protected]>
---
fs/quota/dquot.c | 32 ++++++++++++++++++++------------
include/linux/quota.h | 2 +-
include/linux/quotaops.h | 2 +-
3 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index dea86ab..a34e57c 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1659,15 +1659,13 @@ EXPORT_SYMBOL(dquot_free_inode);
* This operation can block, but only after everything is updated
* A transaction must be started when entering this function.
*/
-int dquot_transfer(struct inode *inode, struct iattr *iattr)
+int dquot_transfer(struct inode *inode, qid_t *chid, unsigned long mask)
{
qsize_t space, cur_space;
qsize_t rsv_space = 0;
struct dquot *transfer_from[MAXQUOTAS];
struct dquot *transfer_to[MAXQUOTAS];
int cnt, ret = QUOTA_OK;
- int chuid = iattr->ia_valid & ATTR_UID && inode->i_uid != iattr->ia_uid,
- chgid = iattr->ia_valid & ATTR_GID && inode->i_gid != iattr->ia_gid;
char warntype_to[MAXQUOTAS];
char warntype_from_inodes[MAXQUOTAS], warntype_from_space[MAXQUOTAS];

@@ -1681,13 +1679,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
transfer_to[cnt] = NULL;
warntype_to[cnt] = QUOTA_NL_NOWARN;
}
- if (chuid)
- transfer_to[USRQUOTA] = dqget(inode->i_sb, iattr->ia_uid,
- USRQUOTA);
- if (chgid)
- transfer_to[GRPQUOTA] = dqget(inode->i_sb, iattr->ia_gid,
- GRPQUOTA);
-
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (mask & (1 << cnt))
+ transfer_to[cnt] = dqget(inode->i_sb, chid[cnt], cnt);
+ }
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
/* Now recheck reliably when holding dqptr_sem */
if (IS_NOQUOTA(inode)) { /* File without quota accounting? */
@@ -1764,12 +1759,25 @@ over_quota:
}
EXPORT_SYMBOL(dquot_transfer);

-/* Wrapper for transferring ownership of an inode */
+/* Wrapper for transferring ownership of an inode for uid/gid only
+ * Called from FSXXX_setattr()
+ */
int vfs_dq_transfer(struct inode *inode, struct iattr *iattr)
{
+ qid_t chid[MAXQUOTAS];
+ unsigned long mask = 0;
+
+ if (iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) {
+ mask |= 1 << USRQUOTA;
+ chid[USRQUOTA] = iattr->ia_uid;
+ }
+ if (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid) {
+ mask |= 1 << GRPQUOTA;
+ chid[GRPQUOTA] = iattr->ia_gid;
+ }
if (sb_any_quota_active(inode->i_sb) && !IS_NOQUOTA(inode)) {
vfs_dq_init(inode);
- if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA)
+ if (inode->i_sb->dq_op->transfer(inode, chid, mask) == NO_QUOTA)
return 1;
}
return 0;
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 1f1a3a1..74738e9 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -301,7 +301,7 @@ struct dquot_operations {
int (*alloc_inode) (const struct inode *, qsize_t);
int (*free_space) (struct inode *, qsize_t);
int (*free_inode) (const struct inode *, qsize_t);
- int (*transfer) (struct inode *, struct iattr *);
+ int (*transfer) (struct inode *, qid_t *, unsigned long);
int (*write_dquot) (struct dquot *); /* Ordinary dquot write */
struct dquot *(*alloc_dquot)(struct super_block *, int); /* Allocate memory for new dquot */
void (*destroy_dquot)(struct dquot *); /* Free memory for dquot */
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index 70de499..b803fd7 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -47,7 +47,7 @@ qsize_t dquot_get_reserved_space(struct inode *inode);
int dquot_free_space(struct inode *inode, qsize_t number);
int dquot_free_inode(const struct inode *inode, qsize_t number);

-int dquot_transfer(struct inode *inode, struct iattr *iattr);
+int dquot_transfer(struct inode *inode, qid_t *chid, unsigned long mask);
int dquot_commit(struct dquot *dquot);
int dquot_acquire(struct dquot *dquot);
int dquot_release(struct dquot *dquot);
--
1.6.3.3


2010-02-08 14:28:29

by Dmitry Monakhov

[permalink] [raw]
Subject: [PATCH 06/11] quota: add generic subtree quota support

Just introduce new and update macros.
It will be used later.

Signed-off-by: Dmitry Monakhov <[email protected]>
---
fs/quota/dquot.c | 5 +++++
fs/quota/quotaio_v2.h | 6 ++++--
include/linux/quota.h | 6 ++++--
3 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 033c271..5483149 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1041,6 +1041,11 @@ static int need_print_warning(struct dquot *dquot)
return current_fsuid() == dquot->dq_id;
case GRPQUOTA:
return in_group_p(dquot->dq_id);
+ case SBTRQUOTA:
+ /* XXX: Currently there is no way to understand
+ which subtree this task belonges to, So print
+ a warn message unconditionally. -dmon */
+ return 1;
}
return 0;
}
diff --git a/fs/quota/quotaio_v2.h b/fs/quota/quotaio_v2.h
index f1966b4..01e8203 100644
--- a/fs/quota/quotaio_v2.h
+++ b/fs/quota/quotaio_v2.h
@@ -13,12 +13,14 @@
*/
#define V2_INITQMAGICS {\
0xd9c01f11, /* USRQUOTA */\
- 0xd9c01927 /* GRPQUOTA */\
+ 0xd9c01927, /* GRPQUOTA */\
+ 0xd9c03f14 /* SBTRQUOTA */\
}

#define V2_INITQVERSIONS {\
1, /* USRQUOTA */\
- 1 /* GRPQUOTA */\
+ 1, /* GRPQUOTA */ \
+ 1 /* SBTRPQUOTA */\
}

/* First generic header */
diff --git a/include/linux/quota.h b/include/linux/quota.h
index abf6a5a..0bcd295 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -36,11 +36,12 @@
#include <linux/errno.h>
#include <linux/types.h>

-#define __DQUOT_VERSION__ "dquot_6.5.2"
+#define __DQUOT_VERSION__ "dquot_6.6.0"

-#define MAXQUOTAS 2
+#define MAXQUOTAS 3
#define USRQUOTA 0 /* element used for user quotas */
#define GRPQUOTA 1 /* element used for group quotas */
+#define SBTRQUOTA 2 /* element used for directory tree quotas */

/*
* Definitions for the default names of the quotas files.
@@ -48,6 +49,7 @@
#define INITQFNAMES { \
"user", /* USRQUOTA */ \
"group", /* GRPQUOTA */ \
+ "subtree", /* SBTRQUOTA */ \
"undefined", \
};

--
1.6.3.3


2010-02-08 23:54:38

by Greg Freemyer

[permalink] [raw]
Subject: Re: [PATCH 08/11] ext4: introduce subtree logic

On Mon, Feb 8, 2010 at 8:28 AM, Dmitry Monakhov <[email protected]> wrote:
> * Abstract
> ?A subtree of a directory tree T is a tree consisting of a directory
> ?(the subtree root) in T and all of its descendants in T.
>
> ?Subtree feature allows to create an isolated (from user point of view)
> ?trees.
>
> ?Subtree assumptions:
> ?(1) Each inode has subtree id. This id is persistently stored inside
> ? ? ?inode (xattr, usually inside ibody)
> ?(2) Subtree id is inherent from parent directory
> ?(3) Inode can not belongs to different subtree
> ? ? ?Otherwise changes in one subtree result in changes in other subtree
> ? ? ?which contradict to isolation criteria.
>
> ?This feature is similar to project-id in XFS. One may assign some id to
> ?a subtree. Each entry from the subtree may be accounted in directory
> ?subtree quota. Will appear in later patches.
>
> * Disk layout
> ?Subtree id is stored on disk inside xattr usually inside ibody.
> ?Xattr is used only as a data storage, It has not user visiable xattr
> ?interface.
>
> Signed-off-by: Dmitry Monakhov <[email protected]>

Dmitry,

I think the idea of subtrees is useful, but I'm curious about other
use cases than just quota.

At first glance you are attempting to create a generic subtree
functionality for ext4, but criteria 3) above says a inode can only be
in one subtree at a time.

Thus if quota utilizes subtrees and another future feature were to use
subtrees and the layout of the subtree details were not identical,
they would collide. Thus with the current patch you can only have one
subtree dependent feature at a time for a given filesystem.

It seems you need something along the lines of a subtree name space
etc. in order to allow orthogonal service users to create orthogonal
subtrees.

Also, I can envision use cases where you have subtrees within subtrees.

Envision a projects folder that forms one subtree, but one specific
project within that folder needs to be in its own subtree. If I read
your patch description right, that is not allowed because only
directories of subtree 0 are allowed to contain diverging subtrees.

ie. A directory of subtree 0 can contain a directory of subtrees 1
and 2, but a directory of subtree 10 is not allowed to contain
subtrees 11 and 12.

Just food for thought.

Greg

2010-02-09 00:10:46

by Greg Freemyer

[permalink] [raw]
Subject: Re: [PATCH 09/11] ext4: subtree add kconfig options

On Mon, Feb 8, 2010 at 8:28 AM, Dmitry Monakhov <[email protected]> wrote:
>
> Signed-off-by: Dmitry Monakhov <[email protected]>
> ---
> ?fs/ext4/Kconfig ?| ? ?9 +++++++++
> ?fs/ext4/Makefile | ? ?1 +
> ?2 files changed, 10 insertions(+), 0 deletions(-)
>
> diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
> index 9ed1bb1..6c05d1a 100644
> --- a/fs/ext4/Kconfig
> +++ b/fs/ext4/Kconfig
> @@ -75,6 +75,15 @@ config EXT4_FS_SECURITY
> ? ? ? ? ?If you are not using a security module that requires using
> ? ? ? ? ?extended attributes for file security labels, say N.
>
> +config EXT4_SUBTREE
> + ? ? ? bool "Ext4 subtree support"
> + ? ? ? depends on EXT4_FS_XATTR
> + ? ? ? default y
> + ? ? ? help
> + ? ? ? ? Enables subtree support for ext4 filesystem
> +
> + ? ? ? ? If you don't know what subtree are, say N
> +

Unless the subtree patch is enhanced to support orthogonal use cases,
the help text should explicitly comment on this limitation.

Greg

2010-02-09 04:02:42

by Andreas Dilger

[permalink] [raw]
Subject: Re: [PATCH 08/11] ext4: introduce subtree logic

On 2010-02-08, at 06:28, Dmitry Monakhov wrote:
> A subtree of a directory tree T is a tree consisting of a directory
> (the subtree root) in T and all of its descendants in T.
>
> Subtree feature allows to create an isolated (from user point of
> view)
> trees.
>
> Subtree assumptions:
> (1) Each inode has subtree id. This id is persistently stored inside
> inode (xattr, usually inside ibody)
> (2) Subtree id is inherent from parent directory
> (3) Inode can not belongs to different subtree
> Otherwise changes in one subtree result in changes in other
> subtree
> which contradict to isolation criteria.
>
> This feature is similar to project-id in XFS. One may assign some
> id to
> a subtree. Each entry from the subtree may be accounted in directory
> subtree quota. Will appear in later patches.
>
> * Disk layout
> Subtree id is stored on disk inside xattr usually inside ibody.
> Xattr is used only as a data storage, It has not user visiable xattr
> interface.


Presumably this would need e2fsck support, in order to ensure that
inodes in a given directory have the same subtree ID as the parent
directory.

Also, how are the subtree IDs generated, to ensure that they are
unique between subtrees?

Cheers, Andreas
--
Andreas Dilger
Sr. Staff Engineer, Lustre Group
Sun Microsystems of Canada, Inc.


2010-02-09 09:29:34

by Dmitry Monakhov

[permalink] [raw]
Subject: Re: [PATCH 08/11] ext4: introduce subtree logic

Andreas Dilger <[email protected]> writes:

> On 2010-02-08, at 06:28, Dmitry Monakhov wrote:
>> A subtree of a directory tree T is a tree consisting of a directory
>> (the subtree root) in T and all of its descendants in T.
>>
>> Subtree feature allows to create an isolated (from user point of
>> view)
>> trees.
>>
>> Subtree assumptions:
>> (1) Each inode has subtree id. This id is persistently stored inside
>> inode (xattr, usually inside ibody)
>> (2) Subtree id is inherent from parent directory
>> (3) Inode can not belongs to different subtree
>> Otherwise changes in one subtree result in changes in other
>> subtree
>> which contradict to isolation criteria.
>>
>> This feature is similar to project-id in XFS. One may assign some
>> id to
>> a subtree. Each entry from the subtree may be accounted in directory
>> subtree quota. Will appear in later patches.
>>
>> * Disk layout
>> Subtree id is stored on disk inside xattr usually inside ibody.
>> Xattr is used only as a data storage, It has not user visiable xattr
>> interface.
>
>
> Presumably this would need e2fsck support, in order to ensure that
> inodes in a given directory have the same subtree ID as the parent
> directory.
Yes i'm working on this, I've already prepared ioctl support
for chattr/lsattr.
> Also, how are the subtree IDs generated, to ensure that they are
> unique between subtrees?
This is user responsibility. There is possible situation where
set of subtrees may has same ID. The main idea it that each of
such subtrees is a *full* subtree (it includes all nodes starting
from the subtree root)
About isolation: currently there is a discussion about this feature
some people state that subtrees may exist without a isolation
requirement. Let's wait where it goes.
>
> Cheers, Andreas
> --
> Andreas Dilger
> Sr. Staff Engineer, Lustre Group
> Sun Microsystems of Canada, Inc.

2010-02-09 10:06:26

by Dmitry Monakhov

[permalink] [raw]
Subject: Re: [PATCH 08/11] ext4: introduce subtree logic

Greg Freemyer <[email protected]> writes:

> On Mon, Feb 8, 2010 at 8:28 AM, Dmitry Monakhov <[email protected]> wrote:
>> * Abstract
>>  A subtree of a directory tree T is a tree consisting of a directory
>>  (the subtree root) in T and all of its descendants in T.
>>
>>  Subtree feature allows to create an isolated (from user point of view)
>>  trees.
>>
>>  Subtree assumptions:
>>  (1) Each inode has subtree id. This id is persistently stored inside
>>      inode (xattr, usually inside ibody)
>>  (2) Subtree id is inherent from parent directory
>>  (3) Inode can not belongs to different subtree
>>      Otherwise changes in one subtree result in changes in other subtree
>>      which contradict to isolation criteria.
>>
>>  This feature is similar to project-id in XFS. One may assign some id to
>>  a subtree. Each entry from the subtree may be accounted in directory
>>  subtree quota. Will appear in later patches.
>>
>> * Disk layout
>>  Subtree id is stored on disk inside xattr usually inside ibody.
>>  Xattr is used only as a data storage, It has not user visiable xattr
>>  interface.
>>
>> Signed-off-by: Dmitry Monakhov <[email protected]>
>
> Dmitry,
>
> I think the idea of subtrees is useful, but I'm curious about other
> use cases than just quota.
>
> At first glance you are attempting to create a generic subtree
> functionality for ext4, but criteria 3) above says a inode can only be
> in one subtree at a time.
Theoretically this is possible, but this dramatically complicate things
Just think about this. If inode belongs to different subtrees then
it must have several tree-dquota objects attached to it. This means
that quota require great quota redesign.
Obviously i don't know any use case for this feature. do you know any?
IMHO isolated subtrees has well defined use-cases. Wat's why two
independent teams (xfs-team and openvz) implemented this feature
in semantically identical way.
>
> Thus if quota utilizes subtrees and another future feature were to use
> subtrees and the layout of the subtree details were not identical,
> they would collide. Thus with the current patch you can only have one
> subtree dependent feature at a time for a given filesystem.
>
> It seems you need something along the lines of a subtree name space
> etc. in order to allow orthogonal service users to create orthogonal
> subtrees.
>
> Also, I can envision use cases where you have subtrees within subtrees.
>
> Envision a projects folder that forms one subtree, but one specific
> project within that folder needs to be in its own subtree. If I read
> your patch description right, that is not allowed because only
> directories of subtree 0 are allowed to contain diverging subtrees.
Default tree (ID == 0) is just analog of common space.
where other subrees exist. It is used for subtree manipulation.
>
> ie. A directory of subtree 0 can contain a directory of subtrees 1
> and 2, but a directory of subtree 10 is not allowed to contain
> subtrees 11 and 12.
>
> Just food for thought.
>
> Greg

2010-02-09 13:42:35

by Greg Freemyer

[permalink] [raw]
Subject: Re: [PATCH 08/11] ext4: introduce subtree logic

On Tue, Feb 9, 2010 at 5:06 AM, Dmitry Monakhov <[email protected]> wrote:
> Greg Freemyer <[email protected]> writes:
>
>> On Mon, Feb 8, 2010 at 8:28 AM, Dmitry Monakhov <[email protected]> wrote:
>>> * Abstract
>>> ?A subtree of a directory tree T is a tree consisting of a directory
>>> ?(the subtree root) in T and all of its descendants in T.
>>>
>>> ?Subtree feature allows to create an isolated (from user point of view)
>>> ?trees.
>>>
>>> ?Subtree assumptions:
>>> ?(1) Each inode has subtree id. This id is persistently stored inside
>>> ? ? ?inode (xattr, usually inside ibody)
>>> ?(2) Subtree id is inherent from parent directory
>>> ?(3) Inode can not belongs to different subtree
>>> ? ? ?Otherwise changes in one subtree result in changes in other subtree
>>> ? ? ?which contradict to isolation criteria.
>>>
>>> ?This feature is similar to project-id in XFS. One may assign some id to
>>> ?a subtree. Each entry from the subtree may be accounted in directory
>>> ?subtree quota. Will appear in later patches.
>>>
>>> * Disk layout
>>> ?Subtree id is stored on disk inside xattr usually inside ibody.
>>> ?Xattr is used only as a data storage, It has not user visiable xattr
>>> ?interface.
>>>
>>> Signed-off-by: Dmitry Monakhov <[email protected]>
>>
>> Dmitry,
>>
>> I think the idea of subtrees is useful, but I'm curious about other
>> use cases than just quota.
>>
>> At first glance you are attempting to create a generic subtree
>> functionality for ext4, but criteria 3) above says a inode can only be
>> in one subtree at a time.
> Theoretically this is possible, but this dramatically complicate things
> Just think about this. If inode belongs to different subtrees then
> it must have several tree-dquota objects attached to it. This means
> that quota require great quota redesign.
> Obviously i don't know any use case for this feature. do you know any?

Maybe we're talking about different things. I working with the OHSM
project <http://ohsm.sourceforge.net/>

We haven't submitted any patches yet, but for one of our features we
have something fairly close to your subtree patch. If we were to
leverage your patch and drop that part of ours we would be in the
unhappy situation that quota and ohsm could not both be enabled on the
same filesystem because the ohsm subtree geography is not likely to be
consistent with the quota subtree geography.

If we call quota and ohsm services then my desire would be to see your
subtree patches support orthoganal subtree groups. One group per
service.

I haven't looked into the actual implementation, but from an API
perspective it is just a matter of adding a service parameter to the
various calls. For a given subtree service group, a given inode could
only be part of one subtree, but a single inode could participate in
multiple subtree service groups.

Greg

2010-02-09 13:53:10

by Dmitry Monakhov

[permalink] [raw]
Subject: Re: [PATCH 08/11] ext4: introduce subtree logic

Greg Freemyer <[email protected]> writes:

> On Tue, Feb 9, 2010 at 5:06 AM, Dmitry Monakhov <[email protected]> wrote:
>> Greg Freemyer <[email protected]> writes:
>>
>>> On Mon, Feb 8, 2010 at 8:28 AM, Dmitry Monakhov <[email protected]> wrote:
>>>> * Abstract
>>>>  A subtree of a directory tree T is a tree consisting of a directory
>>>>  (the subtree root) in T and all of its descendants in T.
>>>>
>>>>  Subtree feature allows to create an isolated (from user point of view)
>>>>  trees.
>>>>
>>>>  Subtree assumptions:
>>>>  (1) Each inode has subtree id. This id is persistently stored inside
>>>>      inode (xattr, usually inside ibody)
>>>>  (2) Subtree id is inherent from parent directory
>>>>  (3) Inode can not belongs to different subtree
>>>>      Otherwise changes in one subtree result in changes in other subtree
>>>>      which contradict to isolation criteria.
>>>>
>>>>  This feature is similar to project-id in XFS. One may assign some id to
>>>>  a subtree. Each entry from the subtree may be accounted in directory
>>>>  subtree quota. Will appear in later patches.
>>>>
>>>> * Disk layout
>>>>  Subtree id is stored on disk inside xattr usually inside ibody.
>>>>  Xattr is used only as a data storage, It has not user visiable xattr
>>>>  interface.
>>>>
>>>> Signed-off-by: Dmitry Monakhov <[email protected]>
>>>
>>> Dmitry,
>>>
>>> I think the idea of subtrees is useful, but I'm curious about other
>>> use cases than just quota.
>>>
>>> At first glance you are attempting to create a generic subtree
>>> functionality for ext4, but criteria 3) above says a inode can only be
>>> in one subtree at a time.
>> Theoretically this is possible, but this dramatically complicate things
>> Just think about this. If inode belongs to different subtrees then
>> it must have several tree-dquota objects attached to it. This means
>> that quota require great quota redesign.
>> Obviously i don't know any use case for this feature. do you know any?
>
> Maybe we're talking about different things. I working with the OHSM
> project <http://ohsm.sourceforge.net/>
>
> We haven't submitted any patches yet, but for one of our features we
> have something fairly close to your subtree patch. If we were to
> leverage your patch and drop that part of ours we would be in the
> unhappy situation that quota and ohsm could not both be enabled on the
> same filesystem because the ohsm subtree geography is not likely to be
> consistent with the quota subtree geography.
>
> If we call quota and ohsm services then my desire would be to see your
> subtree patches support orthoganal subtree groups. One group per
> service.
>
> I haven't looked into the actual implementation, but from an API
> perspective it is just a matter of adding a service parameter to the
> various calls. For a given subtree service group, a given inode could
> only be part of one subtree, but a single inode could participate in
> multiple subtree service groups.
Ok now i think i understand what you are talking about.
one subtree <=> one service is the main rule i'm standing.
If you wan to support several services, no problem
I can easily extend xattr to support different services
something like this
subtree_entry
{
__le16 sbe_flags /* entry flags */
__le16 sbe_type /* service type */
__le32 sbe_id /* subtree id */
}
/* subtree entry flags
enum {
SUBTREE_FL_VALID = 1, /* entry contains valid data */
SUBTREE_FL_INHERENT = 2, /* inherent id from parent on
create *
SUBTREE_FL_ISOLATE = 4 /* Isolated subtree */
/* and soon */
};

>
> Greg

2010-02-09 14:29:45

by Greg Freemyer

[permalink] [raw]
Subject: Re: [PATCH 08/11] ext4: introduce subtree logic

On Tue, Feb 9, 2010 at 8:53 AM, Dmitry Monakhov <[email protected]> wrote:
> Greg Freemyer <[email protected]> writes:
>
>> On Tue, Feb 9, 2010 at 5:06 AM, Dmitry Monakhov <[email protected]> wrote:
>>> Greg Freemyer <[email protected]> writes:
>>>
>>>> On Mon, Feb 8, 2010 at 8:28 AM, Dmitry Monakhov <[email protected]> wrote:
>>>>> * Abstract
>>>>> ?A subtree of a directory tree T is a tree consisting of a directory
>>>>> ?(the subtree root) in T and all of its descendants in T.
>>>>>
>>>>> ?Subtree feature allows to create an isolated (from user point of view)
>>>>> ?trees.
>>>>>
>>>>> ?Subtree assumptions:
>>>>> ?(1) Each inode has subtree id. This id is persistently stored inside
>>>>> ? ? ?inode (xattr, usually inside ibody)
>>>>> ?(2) Subtree id is inherent from parent directory
>>>>> ?(3) Inode can not belongs to different subtree
>>>>> ? ? ?Otherwise changes in one subtree result in changes in other subtree
>>>>> ? ? ?which contradict to isolation criteria.
>>>>>
>>>>> ?This feature is similar to project-id in XFS. One may assign some id to
>>>>> ?a subtree. Each entry from the subtree may be accounted in directory
>>>>> ?subtree quota. Will appear in later patches.
>>>>>
>>>>> * Disk layout
>>>>> ?Subtree id is stored on disk inside xattr usually inside ibody.
>>>>> ?Xattr is used only as a data storage, It has not user visiable xattr
>>>>> ?interface.
>>>>>
>>>>> Signed-off-by: Dmitry Monakhov <[email protected]>
>>>>
>>>> Dmitry,
>>>>
>>>> I think the idea of subtrees is useful, but I'm curious about other
>>>> use cases than just quota.
>>>>
>>>> At first glance you are attempting to create a generic subtree
>>>> functionality for ext4, but criteria 3) above says a inode can only be
>>>> in one subtree at a time.
>>> Theoretically this is possible, but this dramatically complicate things
>>> Just think about this. If inode belongs to different subtrees then
>>> it must have several tree-dquota objects attached to it. This means
>>> that quota require great quota redesign.
>>> Obviously i don't know any use case for this feature. do you know any?
>>
>> Maybe we're talking about different things. ?I working with the OHSM
>> project <http://ohsm.sourceforge.net/>
>>
>> We haven't submitted any patches yet, but for one of our features we
>> have something fairly close to your subtree patch. ?If we were to
>> leverage your patch and drop that part of ours we would be in the
>> unhappy situation that quota and ohsm could not both be enabled on the
>> same filesystem because the ohsm subtree geography is not likely to be
>> consistent with the quota subtree geography.
>>
>> If we call quota and ohsm services then my desire would be to see your
>> subtree patches support orthoganal subtree groups. ?One group per
>> service.
>>
>> I haven't looked into the actual implementation, but from an API
>> perspective it is just a matter of adding a service parameter to the
>> various calls. ?For a given subtree service group, a given inode could
>> only be part of one subtree, but a single inode could participate in
>> multiple subtree service groups.
> Ok now i think i understand what you are talking about.
> one subtree <=> one service is the main rule i'm standing.
> If you wan to support several services, no problem
> I can easily extend xattr to support different services
> something like this
> subtree_entry
> {
> ? ? ? ?__le16 sbe_flags /* entry flags */
> ? ? ? ?__le16 sbe_type ?/* service type */
> ? ? ? ?__le32 sbe_id ? ?/* subtree id */
> }

Conceptually agreed.

But you'd still be limited to one subtree_entry per inode with that right?

Another option would be more like

#define EXT4_SUBTREE_MAX_SERVICE_GROUPS 1
#define EXT4_SUBTREE_MAX_SERVICE_GROUP_QUOTA 0

// OHSM would patch the above when its ready to submit

subtree_entry[EXT4_SUBTREE_MAX_SERVICE_GROUPS]
{
__le16 sbe_flags /* entry flags */
__le32 sbe_id /* subtree id */
}

(Clearly that won't actually work as is, but you get the idea. Each
service group gets its own flags and id.)

Another option would be separate xattr entries for each subtree group.

Regardless, it looks very doable and its just a matter of figuring out
the best way to support multiple subtree service groups.

Greg

2010-02-09 14:48:15

by Dmitry Monakhov

[permalink] [raw]
Subject: Re: [PATCH 08/11] ext4: introduce subtree logic

Greg Freemyer <[email protected]> writes:

> On Tue, Feb 9, 2010 at 8:53 AM, Dmitry Monakhov <[email protected]> wrote:
>> Greg Freemyer <[email protected]> writes:
>>
>>> On Tue, Feb 9, 2010 at 5:06 AM, Dmitry Monakhov <[email protected]> wrote:
>>>> Greg Freemyer <[email protected]> writes:
>>>>
>>>>> On Mon, Feb 8, 2010 at 8:28 AM, Dmitry Monakhov <[email protected]> wrote:
>>>>>> * Abstract
>>>>>>  A subtree of a directory tree T is a tree consisting of a directory
>>>>>>  (the subtree root) in T and all of its descendants in T.
>>>>>>
>>>>>>  Subtree feature allows to create an isolated (from user point of view)
>>>>>>  trees.
>>>>>>
>>>>>>  Subtree assumptions:
>>>>>>  (1) Each inode has subtree id. This id is persistently stored inside
>>>>>>      inode (xattr, usually inside ibody)
>>>>>>  (2) Subtree id is inherent from parent directory
>>>>>>  (3) Inode can not belongs to different subtree
>>>>>>      Otherwise changes in one subtree result in changes in other subtree
>>>>>>      which contradict to isolation criteria.
>>>>>>
>>>>>>  This feature is similar to project-id in XFS. One may assign some id to
>>>>>>  a subtree. Each entry from the subtree may be accounted in directory
>>>>>>  subtree quota. Will appear in later patches.
>>>>>>
>>>>>> * Disk layout
>>>>>>  Subtree id is stored on disk inside xattr usually inside ibody.
>>>>>>  Xattr is used only as a data storage, It has not user visiable xattr
>>>>>>  interface.
>>>>>>
>>>>>> Signed-off-by: Dmitry Monakhov <[email protected]>
>>>>>
>>>>> Dmitry,
>>>>>
>>>>> I think the idea of subtrees is useful, but I'm curious about other
>>>>> use cases than just quota.
>>>>>
>>>>> At first glance you are attempting to create a generic subtree
>>>>> functionality for ext4, but criteria 3) above says a inode can only be
>>>>> in one subtree at a time.
>>>> Theoretically this is possible, but this dramatically complicate things
>>>> Just think about this. If inode belongs to different subtrees then
>>>> it must have several tree-dquota objects attached to it. This means
>>>> that quota require great quota redesign.
>>>> Obviously i don't know any use case for this feature. do you know any?
>>>
>>> Maybe we're talking about different things.  I working with the OHSM
>>> project <http://ohsm.sourceforge.net/>
>>>
>>> We haven't submitted any patches yet, but for one of our features we
>>> have something fairly close to your subtree patch.  If we were to
>>> leverage your patch and drop that part of ours we would be in the
>>> unhappy situation that quota and ohsm could not both be enabled on the
>>> same filesystem because the ohsm subtree geography is not likely to be
>>> consistent with the quota subtree geography.
>>>
>>> If we call quota and ohsm services then my desire would be to see your
>>> subtree patches support orthoganal subtree groups.  One group per
>>> service.
>>>
>>> I haven't looked into the actual implementation, but from an API
>>> perspective it is just a matter of adding a service parameter to the
>>> various calls.  For a given subtree service group, a given inode could
>>> only be part of one subtree, but a single inode could participate in
>>> multiple subtree service groups.
>> Ok now i think i understand what you are talking about.
>> one subtree <=> one service is the main rule i'm standing.
>> If you wan to support several services, no problem
>> I can easily extend xattr to support different services
>> something like this
>> subtree_entry
>> {
>>        __le16 sbe_flags /* entry flags */
>>        __le16 sbe_type  /* service type */
>>        __le32 sbe_id    /* subtree id */
>> }
>
> Conceptually agreed.
>
> But you'd still be limited to one subtree_entry per inode with that right?
>
> Another option would be more like
>
> #define EXT4_SUBTREE_MAX_SERVICE_GROUPS 1
> #define EXT4_SUBTREE_MAX_SERVICE_GROUP_QUOTA 0
>
> // OHSM would patch the above when its ready to submit
>
> subtree_entry[EXT4_SUBTREE_MAX_SERVICE_GROUPS]
> {
> __le16 sbe_flags /* entry flags */
> __le32 sbe_id /* subtree id */
> }
>
> (Clearly that won't actually work as is, but you get the idea. Each
> service group gets its own flags and id.)
>
> Another option would be separate xattr entries for each subtree group.
>
> Regardless, it looks very doable and its just a matter of figuring out
> the best way to support multiple subtree service groups.
Off course i mean that. In fact i'm plan to pack entries in to one
xattr data block because each xattr has significant space overhead.

2010-02-09 14:52:37

by Greg Freemyer

[permalink] [raw]
Subject: Re: [PATCH 08/11] ext4: introduce subtree logic

On Tue, Feb 9, 2010 at 9:48 AM, Dmitry Monakhov <[email protected]> wrote:
> Greg Freemyer <[email protected]> writes:
>
>> On Tue, Feb 9, 2010 at 8:53 AM, Dmitry Monakhov <[email protected]> wrote:
>>> Greg Freemyer <[email protected]> writes:
>>>
>>>> On Tue, Feb 9, 2010 at 5:06 AM, Dmitry Monakhov <[email protected]> wrote:
>>>>> Greg Freemyer <[email protected]> writes:
>>>>>
>>>>>> On Mon, Feb 8, 2010 at 8:28 AM, Dmitry Monakhov <[email protected]> wrote:
>>>>>>> * Abstract
>>>>>>> ?A subtree of a directory tree T is a tree consisting of a directory
>>>>>>> ?(the subtree root) in T and all of its descendants in T.
>>>>>>>
>>>>>>> ?Subtree feature allows to create an isolated (from user point of view)
>>>>>>> ?trees.
>>>>>>>
>>>>>>> ?Subtree assumptions:
>>>>>>> ?(1) Each inode has subtree id. This id is persistently stored inside
>>>>>>> ? ? ?inode (xattr, usually inside ibody)
>>>>>>> ?(2) Subtree id is inherent from parent directory
>>>>>>> ?(3) Inode can not belongs to different subtree
>>>>>>> ? ? ?Otherwise changes in one subtree result in changes in other subtree
>>>>>>> ? ? ?which contradict to isolation criteria.
>>>>>>>
>>>>>>> ?This feature is similar to project-id in XFS. One may assign some id to
>>>>>>> ?a subtree. Each entry from the subtree may be accounted in directory
>>>>>>> ?subtree quota. Will appear in later patches.
>>>>>>>
>>>>>>> * Disk layout
>>>>>>> ?Subtree id is stored on disk inside xattr usually inside ibody.
>>>>>>> ?Xattr is used only as a data storage, It has not user visiable xattr
>>>>>>> ?interface.
>>>>>>>
>>>>>>> Signed-off-by: Dmitry Monakhov <[email protected]>
>>>>>>
>>>>>> Dmitry,
>>>>>>
>>>>>> I think the idea of subtrees is useful, but I'm curious about other
>>>>>> use cases than just quota.
>>>>>>
>>>>>> At first glance you are attempting to create a generic subtree
>>>>>> functionality for ext4, but criteria 3) above says a inode can only be
>>>>>> in one subtree at a time.
>>>>> Theoretically this is possible, but this dramatically complicate things
>>>>> Just think about this. If inode belongs to different subtrees then
>>>>> it must have several tree-dquota objects attached to it. This means
>>>>> that quota require great quota redesign.
>>>>> Obviously i don't know any use case for this feature. do you know any?
>>>>
>>>> Maybe we're talking about different things. ?I working with the OHSM
>>>> project <http://ohsm.sourceforge.net/>
>>>>
>>>> We haven't submitted any patches yet, but for one of our features we
>>>> have something fairly close to your subtree patch. ?If we were to
>>>> leverage your patch and drop that part of ours we would be in the
>>>> unhappy situation that quota and ohsm could not both be enabled on the
>>>> same filesystem because the ohsm subtree geography is not likely to be
>>>> consistent with the quota subtree geography.
>>>>
>>>> If we call quota and ohsm services then my desire would be to see your
>>>> subtree patches support orthoganal subtree groups. ?One group per
>>>> service.
>>>>
>>>> I haven't looked into the actual implementation, but from an API
>>>> perspective it is just a matter of adding a service parameter to the
>>>> various calls. ?For a given subtree service group, a given inode could
>>>> only be part of one subtree, but a single inode could participate in
>>>> multiple subtree service groups.
>>> Ok now i think i understand what you are talking about.
>>> one subtree <=> one service is the main rule i'm standing.
>>> If you wan to support several services, no problem
>>> I can easily extend xattr to support different services
>>> something like this
>>> subtree_entry
>>> {
>>> ? ? ? ?__le16 sbe_flags /* entry flags */
>>> ? ? ? ?__le16 sbe_type ?/* service type */
>>> ? ? ? ?__le32 sbe_id ? ?/* subtree id */
>>> }
>>
>> Conceptually agreed.
>>
>> But you'd still be limited to one subtree_entry per inode with that right?
>>
>> Another option would be more like
>>
>> #define EXT4_SUBTREE_MAX_SERVICE_GROUPS 1
>> #define EXT4_SUBTREE_MAX_SERVICE_GROUP_QUOTA 0
>>
>> // OHSM would patch the above when its ready to submit
>>
>> subtree_entry[EXT4_SUBTREE_MAX_SERVICE_GROUPS]
>> {
>> ? ? ? ?__le16 sbe_flags /* entry flags */
>> ? ? ? ?__le32 sbe_id ? ?/* subtree id */
>> }
>>
>> (Clearly that won't actually work as is, but you get the idea. ?Each
>> service group gets its own flags and id.)
>>
>> Another option would be separate xattr entries for each subtree group.
>>
>> Regardless, it looks very doable and its just a matter of figuring out
>> the best way to support multiple subtree service groups.
> Off course i mean that. In fact i'm plan to pack entries in to one
> xattr data block because each xattr has significant space overhead.
>

Sounds great. Does xfs support multiple service groups with its
current offering?

Greg

2010-02-09 15:21:41

by Dmitry Monakhov

[permalink] [raw]
Subject: Re: [PATCH 08/11] ext4: introduce subtree logic

Greg Freemyer <[email protected]> writes:

> On Tue, Feb 9, 2010 at 9:48 AM, Dmitry Monakhov <[email protected]> wrote:
>> Greg Freemyer <[email protected]> writes:
>>
>>> On Tue, Feb 9, 2010 at 8:53 AM, Dmitry Monakhov <[email protected]> wrote:
>>>> Greg Freemyer <[email protected]> writes:
>>>>
>>>>> On Tue, Feb 9, 2010 at 5:06 AM, Dmitry Monakhov <[email protected]> wrote:
>>>>>> Greg Freemyer <[email protected]> writes:
>>>>>>
>>>>>>> On Mon, Feb 8, 2010 at 8:28 AM, Dmitry Monakhov <[email protected]> wrote:
>>>>>>>> * Abstract
>>>>>>>>  A subtree of a directory tree T is a tree consisting of a directory
>>>>>>>>  (the subtree root) in T and all of its descendants in T.
>>>>>>>>
>>>>>>>>  Subtree feature allows to create an isolated (from user point of view)
>>>>>>>>  trees.
>>>>>>>>
>>>>>>>>  Subtree assumptions:
>>>>>>>>  (1) Each inode has subtree id. This id is persistently stored inside
>>>>>>>>      inode (xattr, usually inside ibody)
>>>>>>>>  (2) Subtree id is inherent from parent directory
>>>>>>>>  (3) Inode can not belongs to different subtree
>>>>>>>>      Otherwise changes in one subtree result in changes in other subtree
>>>>>>>>      which contradict to isolation criteria.
>>>>>>>>
>>>>>>>>  This feature is similar to project-id in XFS. One may assign some id to
>>>>>>>>  a subtree. Each entry from the subtree may be accounted in directory
>>>>>>>>  subtree quota. Will appear in later patches.
>>>>>>>>
>>>>>>>> * Disk layout
>>>>>>>>  Subtree id is stored on disk inside xattr usually inside ibody.
>>>>>>>>  Xattr is used only as a data storage, It has not user visiable xattr
>>>>>>>>  interface.
>>>>>>>>
>>>>>>>> Signed-off-by: Dmitry Monakhov <[email protected]>
>>>>>>>
>>>>>>> Dmitry,
>>>>>>>
>>>>>>> I think the idea of subtrees is useful, but I'm curious about other
>>>>>>> use cases than just quota.
>>>>>>>
>>>>>>> At first glance you are attempting to create a generic subtree
>>>>>>> functionality for ext4, but criteria 3) above says a inode can only be
>>>>>>> in one subtree at a time.
>>>>>> Theoretically this is possible, but this dramatically complicate things
>>>>>> Just think about this. If inode belongs to different subtrees then
>>>>>> it must have several tree-dquota objects attached to it. This means
>>>>>> that quota require great quota redesign.
>>>>>> Obviously i don't know any use case for this feature. do you know any?
>>>>>
>>>>> Maybe we're talking about different things.  I working with the OHSM
>>>>> project <http://ohsm.sourceforge.net/>
>>>>>
>>>>> We haven't submitted any patches yet, but for one of our features we
>>>>> have something fairly close to your subtree patch.  If we were to
>>>>> leverage your patch and drop that part of ours we would be in the
>>>>> unhappy situation that quota and ohsm could not both be enabled on the
>>>>> same filesystem because the ohsm subtree geography is not likely to be
>>>>> consistent with the quota subtree geography.
>>>>>
>>>>> If we call quota and ohsm services then my desire would be to see your
>>>>> subtree patches support orthoganal subtree groups.  One group per
>>>>> service.
>>>>>
>>>>> I haven't looked into the actual implementation, but from an API
>>>>> perspective it is just a matter of adding a service parameter to the
>>>>> various calls.  For a given subtree service group, a given inode could
>>>>> only be part of one subtree, but a single inode could participate in
>>>>> multiple subtree service groups.
>>>> Ok now i think i understand what you are talking about.
>>>> one subtree <=> one service is the main rule i'm standing.
>>>> If you wan to support several services, no problem
>>>> I can easily extend xattr to support different services
>>>> something like this
>>>> subtree_entry
>>>> {
>>>>        __le16 sbe_flags /* entry flags */
>>>>        __le16 sbe_type  /* service type */
>>>>        __le32 sbe_id    /* subtree id */
>>>> }
>>>
>>> Conceptually agreed.
>>>
>>> But you'd still be limited to one subtree_entry per inode with that right?
>>>
>>> Another option would be more like
>>>
>>> #define EXT4_SUBTREE_MAX_SERVICE_GROUPS 1
>>> #define EXT4_SUBTREE_MAX_SERVICE_GROUP_QUOTA 0
>>>
>>> // OHSM would patch the above when its ready to submit
>>>
>>> subtree_entry[EXT4_SUBTREE_MAX_SERVICE_GROUPS]
>>> {
>>>        __le16 sbe_flags /* entry flags */
>>>        __le32 sbe_id    /* subtree id */
>>> }
>>>
>>> (Clearly that won't actually work as is, but you get the idea.  Each
>>> service group gets its own flags and id.)
>>>
>>> Another option would be separate xattr entries for each subtree group.
>>>
>>> Regardless, it looks very doable and its just a matter of figuring out
>>> the best way to support multiple subtree service groups.
>> Off course i mean that. In fact i'm plan to pack entries in to one
>> xattr data block because each xattr has significant space overhead.
>>
>
> Sounds great. Does xfs support multiple service groups with its
> current offering?
IFAIK there is only one group, this is because they use it for quota
and for isolation needs.
In fact due to current linux implementation
the cant use group quota,while project quota is enabled because there
is only two types of types available. This will be fixed by generic
quota improvements see 3-6 patches in the series.