Patchwork [3/5] ext4: Implement project ID support for ext4 filesystem

login
register
mail settings
Submitter Dmitri Monakho
Date June 21, 2012, 9:08 a.m.
Message ID <1340269733-25922-4-git-send-email-dmonakhov@openvz.org>
Download mbox | patch
Permalink /patch/166268/
State Superseded
Headers show

Comments

Dmitri Monakho - June 21, 2012, 9:08 a.m.
* 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.

  *NOTE*: User is allowed to break pure subtree hierarchy via manual
          id manipulation.

  Project subtrees assumptions:
  (1) Each inode has an id. This id is persistently stored inside
      inode (xattr, usually inside ibody)
  (2) Project id is inherent from parent directory

  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
  project quota. Will appear in later patches.

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

* User interface
  Project id is accessible via generic xattr interface "system.project_id"

* Notes
  ext4_setattr interface to prjid: Semantically prjid must being changed
  similar to uid/gid, but project_id is stored inside xattr so on-disk
  structures updates is not that trivial, so I've move prjid change
  logic to separate function.

Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
---
 fs/ext4/Kconfig   |    8 ++
 fs/ext4/Makefile  |    1 +
 fs/ext4/ext4.h    |    4 +
 fs/ext4/ialloc.c  |    6 ++
 fs/ext4/inode.c   |    4 +
 fs/ext4/project.c |  213 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/project.h |   45 +++++++++++
 fs/ext4/super.c   |   16 ++++
 fs/ext4/xattr.c   |    7 ++
 fs/ext4/xattr.h   |    2 +
 10 files changed, 306 insertions(+), 0 deletions(-)
 create mode 100644 fs/ext4/project.c
 create mode 100644 fs/ext4/project.h
Jan Kara - June 21, 2012, 11:51 p.m.
On Thu 21-06-12 13:08:51, Dmitry Monakhov 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.
> 
>   *NOTE*: User is allowed to break pure subtree hierarchy via manual
>           id manipulation.
> 
>   Project subtrees assumptions:
>   (1) Each inode has an id. This id is persistently stored inside
>       inode (xattr, usually inside ibody)
>   (2) Project id is inherent from parent directory
> 
>   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
>   project quota. Will appear in later patches.
> 
> * Disk layout
>   Project id is stored on disk inside xattr usually inside ibody.
>   Xattr is used only as a data storage, It has not user visible xattr
>   interface.
> 
> * User interface
>   Project id is accessible via generic xattr interface "system.project_id"
> 
> * Notes
>   ext4_setattr interface to prjid: Semantically prjid must being changed
>   similar to uid/gid, but project_id is stored inside xattr so on-disk
>   structures updates is not that trivial, so I've move prjid change
>   logic to separate function.
  Generally, I like the patch. Some comments are below.

> Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
> ---
>  fs/ext4/Kconfig   |    8 ++
>  fs/ext4/Makefile  |    1 +
>  fs/ext4/ext4.h    |    4 +
>  fs/ext4/ialloc.c  |    6 ++
>  fs/ext4/inode.c   |    4 +
>  fs/ext4/project.c |  213 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/ext4/project.h |   45 +++++++++++
>  fs/ext4/super.c   |   16 ++++
>  fs/ext4/xattr.c   |    7 ++
>  fs/ext4/xattr.h   |    2 +
>  10 files changed, 306 insertions(+), 0 deletions(-)
>  create mode 100644 fs/ext4/project.c
>  create mode 100644 fs/ext4/project.h
> 
> diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
> index c22f170..377af40 100644
> --- a/fs/ext4/Kconfig
> +++ b/fs/ext4/Kconfig
> @@ -76,6 +76,14 @@ 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_PROJECT_ID
> +	bool "Ext4 project_id support"
> +	depends on PROJECT_ID
> +	depends on EXT4_FS_XATTR
> +	help
> +	  Enables project inode identifier support for ext4 filesystem.
> +	  This feature allow to assign some id to inodes similar to
> +	  uid/gid. 
  Is separate config option useful? The amount of code added for this is
not really big and there is not other speed / space benefit in disabling
this, is there?

>  config EXT4_DEBUG
>  	bool "EXT4 debugging support"
> diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
> index 56fd8f8..692fe56 100644
> --- a/fs/ext4/Makefile
> +++ b/fs/ext4/Makefile
> @@ -12,3 +12,4 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.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_PROJECT_ID)		+= project.o
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index cfc4e01..c0e33d7 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -925,6 +925,9 @@ struct ext4_inode_info {
>  
>  	/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
>  	__u32 i_csum_seed;
> +#ifdef CONFIG_EXT4_PROJECT_ID
> +	__u32 i_prjid;
> +#endif
>  };
>  
>  /*
> @@ -962,6 +965,7 @@ struct ext4_inode_info {
>  #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_PROJECT_ID		0x40000 /* project owner id support */
  And I would even question the necessity of the mount option. Can we make
the rule that no system.prjid xattr simply means prjid == 0. When someone
sets prjid, it gets further automatically inherited and everything works
fine. I don't see the need for special mount option for this - again no
significant overhead is introduced when we always check whether we should
inherit non-zero prjid... Thoughts?

>  #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 */
> diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
> index d48e8b1..d4b72e5 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 "project.h"
>  
>  #include <trace/events/ext4.h>
>  
> @@ -898,6 +899,8 @@ got:
>  
>  	ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
>  
> +	ext4_setprjid(inode, ext4_getprjid(dir));
> +
>  	ret = inode;
>  	dquot_initialize(inode);
>  	err = dquot_alloc_inode(inode);
> @@ -911,6 +914,9 @@ got:
>  	err = ext4_init_security(handle, inode, dir, qstr);
>  	if (err)
>  		goto fail_free_drop;
> +	err = ext4_prj_init(handle, inode);
> +	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*/
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index 02bc8cb..c98d8d6 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -42,6 +42,7 @@
>  #include "xattr.h"
>  #include "acl.h"
>  #include "truncate.h"
> +#include "project.h"
>  
>  #include <trace/events/ext4.h>
>  
> @@ -3870,6 +3871,9 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
>  	}
>  	if (ret)
>  		goto bad_inode;
> +	ret = ext4_prj_read(inode);
> +	if (ret)
> +		goto bad_inode;
>  
>  	if (S_ISREG(inode->i_mode)) {
>  		inode->i_op = &ext4_file_inode_operations;
> diff --git a/fs/ext4/project.c b/fs/ext4/project.c
> new file mode 100644
> index 0000000..a262a49
> --- /dev/null
> +++ b/fs/ext4/project.c
> @@ -0,0 +1,213 @@
> +/*
> + * linux/fs/ext4/projectid.c
> + *
> + * Copyright (C) 2010 Parallels Inc
> + * Dmitry Monakhov <dmonakhov@openvz.org>
> + */
> +
> +#include <linux/init.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/capability.h>
> +#include <linux/fs.h>
> +#include <linux/quotaops.h>
> +#include "ext4_jbd2.h"
> +#include "ext4.h"
> +#include "xattr.h"
> +#include "project.h"
> +
> +
> +/*
> + * PROJECT SUBTREE
> + * 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.
> + *
> + * Project Subtree's 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
> + */
> +
> +/*
> + * Read project_id id from inode's xattr
> + * Locking: none
> + */
> +int ext4_prj_xattr_read(struct inode *inode, unsigned int *prjid)
> +{
> +	__le32 dsk_prjid;
> +	int retval;
  Please add empty line between declarations and function body... Also
holds for some functions below.

> +	retval = ext4_xattr_get(inode, EXT4_XATTR_INDEX_PROJECT_ID, "",
> +				&dsk_prjid, sizeof (dsk_prjid));
> +	if (retval > 0) {
> +		if (retval != sizeof(dsk_prjid))
> +			return -EIO;
> +		else
> +			retval = 0;
> +	}
> +	*prjid = le32_to_cpu(dsk_prjid);
> +	return retval;
> +
> +}

								Honza
Dave Chinner - June 22, 2012, 3:07 a.m.
On Thu, Jun 21, 2012 at 01:08:51PM +0400, Dmitry Monakhov 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.
> 
>   *NOTE*: User is allowed to break pure subtree hierarchy via manual
>           id manipulation.
> 
>   Project subtrees assumptions:
>   (1) Each inode has an id. This id is persistently stored inside
>       inode (xattr, usually inside ibody)
>   (2) Project id is inherent from parent directory

You are mashing two different concepts into one, and naming it in a
manner guaranteed to create confusion with all the people already
using project quotas on XFS. You're implementing subtree quotas, not
project quotas.

>   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 project quota. Will appear in later
>   patches.

As implied above, project quotas ID in XFS are not subtree quotas.
They are used to implement project quotas, not subtree quotas.
subtree quotas are a userspace management construction using a
subset of project quota infrastructure. i.e. the inheritance of
project IDs in XFS and the restriction of operations to within
subtrees is not a function of project quotas - it's a separately
managed feature that was added to support the functioanlity needed
for subtree quotas.

ISTR raising this objection last time you posted this patch set -
either you implement project quotas as they exist in XFS and handle
subtree quotas as a constrainted set of project quota functionality
or you need to drop all references to "project quotas" and call this
something like sub-tree quotas that uses subtree IDs.

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

If you've got enough xattrs on the inode, then it will end up out of
line and performance is going to suck - every time you write to a
cold file you now need two IOs - one to read the extent list, and
one to read the xattrs to get the project ID....

> * User interface
>   Project id is accessible via generic xattr interface "system.project_id"

Which means it is an incompatible with XFS. What purpose does this
serve except to confuse people? Why not a generic set/get project ID
quotactl/ioctl? That's much easier for applications to use compared
to xattrs, and far easier to support different implementations
across filesystems. The way the project ID is stored in ext4 should
not define the API.

Indeed, an ioctl/quotactl style interface matches the existing quota
interface, so it's much more natural to use a similar API to one
already used for quota manipulation...

> * Notes
>   ext4_setattr interface to prjid: Semantically prjid must being changed
>   similar to uid/gid, but project_id is stored inside xattr so on-disk
>   structures updates is not that trivial, so I've move prjid change
>   logic to separate function.

I suspect that there are corner cases you haven't thought about
yet. What happens when you hard link a file into two separate
subtrees? how do you account for that? What happens when you remove
the hard link from the subtree the inode is accounted to?  What
happens when a rename occurs, moving a file from one subtree to
another? 

.....

> +static int
> +ext4_xattr_prj_set(struct dentry *dentry, const char *name,
> +		const void *value, size_t size, int flags, int type)
> +{
> +	unsigned int new_prjid;
> +	if (strcmp(name, "") != 0)
> +		return -EINVAL;
> +	new_prjid = simple_strtoul(value, (char **)&value, 0);
> +	return ext4_prj_change(dentry->d_inode, new_prjid);

Because you are using strings rather than integers for the ID, you
need to do a lot better verification here - what will happen when
someone tries to set an ID of "frobnozzle"? Using integers via
ioctls for the user API make this problem....

Cheers,

Dave.
Jan Kara - June 28, 2012, 10:16 a.m.
On Fri 22-06-12 13:07:53, Dave Chinner wrote:
> On Thu, Jun 21, 2012 at 01:08:51PM +0400, Dmitry Monakhov 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.
> > 
> >   *NOTE*: User is allowed to break pure subtree hierarchy via manual
> >           id manipulation.
> > 
> >   Project subtrees assumptions:
> >   (1) Each inode has an id. This id is persistently stored inside
> >       inode (xattr, usually inside ibody)
> >   (2) Project id is inherent from parent directory
> 
> You are mashing two different concepts into one, and naming it in a
> manner guaranteed to create confusion with all the people already
> using project quotas on XFS. You're implementing subtree quotas, not
> project quotas.
> 
> >   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 project quota. Will appear in later
> >   patches.
> 
> As implied above, project quotas ID in XFS are not subtree quotas.
> They are used to implement project quotas, not subtree quotas.
> subtree quotas are a userspace management construction using a
> subset of project quota infrastructure. i.e. the inheritance of
> project IDs in XFS and the restriction of operations to within
> subtrees is not a function of project quotas - it's a separately
> managed feature that was added to support the functioanlity needed
> for subtree quotas.
> 
> ISTR raising this objection last time you posted this patch set -
> either you implement project quotas as they exist in XFS and handle
> subtree quotas as a constrainted set of project quota functionality
> or you need to drop all references to "project quotas" and call this
> something like sub-tree quotas that uses subtree IDs.
  Frankly, I fail to see the difference between XFS project quotas and how
Dmitry implemented his variant of quotas. He associated additional ID with
each inode and that ID can have quota limits. This seems pretty much like
what XFS project quotas do but maybe I'm missing something.

I agree that his changelogs somewhat suggest that he had only subtrees in
mind but the code does not really depend on that AFAICS. The only
difference I can see is that in XFS project ID is inherited only if
XFS_DIFLAG_PROJINHERIT is set and that is inherited by default but can be
manipulated from userspace. But that looks like a minor thing.

> > * Disk layout
> >   Project id is stored on disk inside xattr usually inside ibody.
> >   Xattr is used only as a data storage, It has not user visible xattr
> >   interface.
> 
> If you've got enough xattrs on the inode, then it will end up out of
> line and performance is going to suck - every time you write to a
> cold file you now need two IOs - one to read the extent list, and
> one to read the xattrs to get the project ID....
> 
> > * User interface
> >   Project id is accessible via generic xattr interface "system.project_id"
> 
> Which means it is an incompatible with XFS. What purpose does this
> serve except to confuse people? Why not a generic set/get project ID
> quotactl/ioctl? That's much easier for applications to use compared
> to xattrs, and far easier to support different implementations
> across filesystems. The way the project ID is stored in ext4 should
> not define the API.
> 
> Indeed, an ioctl/quotactl style interface matches the existing quota
> interface, so it's much more natural to use a similar API to one
> already used for quota manipulation...
  I'm kind of undecided here - xattrs have the disadvantages you wrote
above but also some advantages like that backup tools know about them, you
can manipulate them with setxattr, getxattr, etc.

From kernel POV I agree that for XFS it's a bit ugly to fake the project
quota xattr though...

> > * Notes
> >   ext4_setattr interface to prjid: Semantically prjid must being changed
> >   similar to uid/gid, but project_id is stored inside xattr so on-disk
> >   structures updates is not that trivial, so I've move prjid change
> >   logic to separate function.
> 
> I suspect that there are corner cases you haven't thought about
> yet. What happens when you hard link a file into two separate
> subtrees? how do you account for that? What happens when you remove
> the hard link from the subtree the inode is accounted to?  What
> happens when a rename occurs, moving a file from one subtree to
> another?
  Yeah, handling of these things is missing from an ext4 patch. Good that
you've spotted that. I forgot about it.

> .....
> 
> > +static int
> > +ext4_xattr_prj_set(struct dentry *dentry, const char *name,
> > +		const void *value, size_t size, int flags, int type)
> > +{
> > +	unsigned int new_prjid;
> > +	if (strcmp(name, "") != 0)
> > +		return -EINVAL;
> > +	new_prjid = simple_strtoul(value, (char **)&value, 0);
> > +	return ext4_prj_change(dentry->d_inode, new_prjid);
> 
> Because you are using strings rather than integers for the ID, you
> need to do a lot better verification here - what will happen when
> someone tries to set an ID of "frobnozzle"? Using integers via
> ioctls for the user API make this problem....
  Well, simple_strtoul() returns something for arbitary string - that's the
project ID ;). But I agree forcing project ID 0 if there's parse error is
more user friendly given the simple_strtoul() return value for unparseable
strings can change.

									Honza
Dmitri Monakho - July 3, 2012, 6:46 p.m.
On Fri, 22 Jun 2012 01:51:46 +0200, Jan Kara <jack@suse.cz> wrote:
> On Thu 21-06-12 13:08:51, Dmitry Monakhov 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.
> > 
> >   *NOTE*: User is allowed to break pure subtree hierarchy via manual
> >           id manipulation.
> > 
> >   Project subtrees assumptions:
> >   (1) Each inode has an id. This id is persistently stored inside
> >       inode (xattr, usually inside ibody)
> >   (2) Project id is inherent from parent directory
> > 
> >   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
> >   project quota. Will appear in later patches.
> > 
> > * Disk layout
> >   Project id is stored on disk inside xattr usually inside ibody.
> >   Xattr is used only as a data storage, It has not user visible xattr
> >   interface.
> > 
> > * User interface
> >   Project id is accessible via generic xattr interface "system.project_id"
> > 
> > * Notes
> >   ext4_setattr interface to prjid: Semantically prjid must being changed
> >   similar to uid/gid, but project_id is stored inside xattr so on-disk
> >   structures updates is not that trivial, so I've move prjid change
> >   logic to separate function.
>   Generally, I like the patch. Some comments are below.
Also reasonable, will redo.
> 
> > Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
> > ---
> >  fs/ext4/Kconfig   |    8 ++
> >  fs/ext4/Makefile  |    1 +
> >  fs/ext4/ext4.h    |    4 +
> >  fs/ext4/ialloc.c  |    6 ++
> >  fs/ext4/inode.c   |    4 +
> >  fs/ext4/project.c |  213 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  fs/ext4/project.h |   45 +++++++++++
> >  fs/ext4/super.c   |   16 ++++
> >  fs/ext4/xattr.c   |    7 ++
> >  fs/ext4/xattr.h   |    2 +
> >  10 files changed, 306 insertions(+), 0 deletions(-)
> >  create mode 100644 fs/ext4/project.c
> >  create mode 100644 fs/ext4/project.h
> > 
> > diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
> > index c22f170..377af40 100644
> > --- a/fs/ext4/Kconfig
> > +++ b/fs/ext4/Kconfig
> > @@ -76,6 +76,14 @@ 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_PROJECT_ID
> > +	bool "Ext4 project_id support"
> > +	depends on PROJECT_ID
> > +	depends on EXT4_FS_XATTR
> > +	help
> > +	  Enables project inode identifier support for ext4 filesystem.
> > +	  This feature allow to assign some id to inodes similar to
> > +	  uid/gid. 
>   Is separate config option useful? The amount of code added for this is
> not really big and there is not other speed / space benefit in disabling
> this, is there?
> 
> >  config EXT4_DEBUG
> >  	bool "EXT4 debugging support"
> > diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
> > index 56fd8f8..692fe56 100644
> > --- a/fs/ext4/Makefile
> > +++ b/fs/ext4/Makefile
> > @@ -12,3 +12,4 @@ ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.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_PROJECT_ID)		+= project.o
> > diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> > index cfc4e01..c0e33d7 100644
> > --- a/fs/ext4/ext4.h
> > +++ b/fs/ext4/ext4.h
> > @@ -925,6 +925,9 @@ struct ext4_inode_info {
> >  
> >  	/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
> >  	__u32 i_csum_seed;
> > +#ifdef CONFIG_EXT4_PROJECT_ID
> > +	__u32 i_prjid;
> > +#endif
> >  };
> >  
> >  /*
> > @@ -962,6 +965,7 @@ struct ext4_inode_info {
> >  #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_PROJECT_ID		0x40000 /* project owner id support */
>   And I would even question the necessity of the mount option. Can we make
> the rule that no system.prjid xattr simply means prjid == 0. When someone
> sets prjid, it gets further automatically inherited and everything works
> fine. I don't see the need for special mount option for this - again no
> significant overhead is introduced when we always check whether we should
> inherit non-zero prjid... Thoughts?
Yep. this reasonable, Also it allows us to save mountopt bit space for
new crazy stuff. 
> 
> >  #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 */
> > diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
> > index d48e8b1..d4b72e5 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 "project.h"
> >  
> >  #include <trace/events/ext4.h>
> >  
> > @@ -898,6 +899,8 @@ got:
> >  
> >  	ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
> >  
> > +	ext4_setprjid(inode, ext4_getprjid(dir));
> > +
> >  	ret = inode;
> >  	dquot_initialize(inode);
> >  	err = dquot_alloc_inode(inode);
> > @@ -911,6 +914,9 @@ got:
> >  	err = ext4_init_security(handle, inode, dir, qstr);
> >  	if (err)
> >  		goto fail_free_drop;
> > +	err = ext4_prj_init(handle, inode);
> > +	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*/
> > diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> > index 02bc8cb..c98d8d6 100644
> > --- a/fs/ext4/inode.c
> > +++ b/fs/ext4/inode.c
> > @@ -42,6 +42,7 @@
> >  #include "xattr.h"
> >  #include "acl.h"
> >  #include "truncate.h"
> > +#include "project.h"
> >  
> >  #include <trace/events/ext4.h>
> >  
> > @@ -3870,6 +3871,9 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
> >  	}
> >  	if (ret)
> >  		goto bad_inode;
> > +	ret = ext4_prj_read(inode);
> > +	if (ret)
> > +		goto bad_inode;
> >  
> >  	if (S_ISREG(inode->i_mode)) {
> >  		inode->i_op = &ext4_file_inode_operations;
> > diff --git a/fs/ext4/project.c b/fs/ext4/project.c
> > new file mode 100644
> > index 0000000..a262a49
> > --- /dev/null
> > +++ b/fs/ext4/project.c
> > @@ -0,0 +1,213 @@
> > +/*
> > + * linux/fs/ext4/projectid.c
> > + *
> > + * Copyright (C) 2010 Parallels Inc
> > + * Dmitry Monakhov <dmonakhov@openvz.org>
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/sched.h>
> > +#include <linux/slab.h>
> > +#include <linux/capability.h>
> > +#include <linux/fs.h>
> > +#include <linux/quotaops.h>
> > +#include "ext4_jbd2.h"
> > +#include "ext4.h"
> > +#include "xattr.h"
> > +#include "project.h"
> > +
> > +
> > +/*
> > + * PROJECT SUBTREE
> > + * 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.
> > + *
> > + * Project Subtree's 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
> > + */
> > +
> > +/*
> > + * Read project_id id from inode's xattr
> > + * Locking: none
> > + */
> > +int ext4_prj_xattr_read(struct inode *inode, unsigned int *prjid)
> > +{
> > +	__le32 dsk_prjid;
> > +	int retval;
>   Please add empty line between declarations and function body... Also
> holds for some functions below.
> 
> > +	retval = ext4_xattr_get(inode, EXT4_XATTR_INDEX_PROJECT_ID, "",
> > +				&dsk_prjid, sizeof (dsk_prjid));
> > +	if (retval > 0) {
> > +		if (retval != sizeof(dsk_prjid))
> > +			return -EIO;
> > +		else
> > +			retval = 0;
> > +	}
> > +	*prjid = le32_to_cpu(dsk_prjid);
> > +	return retval;
> > +
> > +}
> 
> 								Honza
> -- 
> Jan Kara <jack@suse.cz>
> SUSE Labs, CR
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Dmitri Monakho - July 3, 2012, 7:11 p.m.
On Fri, 22 Jun 2012 13:07:53 +1000, Dave Chinner <david@fromorbit.com> wrote:
> On Thu, Jun 21, 2012 at 01:08:51PM +0400, Dmitry Monakhov 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.
> > 
> >   *NOTE*: User is allowed to break pure subtree hierarchy via manual
> >           id manipulation.
> > 
> >   Project subtrees assumptions:
> >   (1) Each inode has an id. This id is persistently stored inside
> >       inode (xattr, usually inside ibody)
> >   (2) Project id is inherent from parent directory
> 
Very very sorry for a long replay,
> You are mashing two different concepts into one, and naming it in a
> manner guaranteed to create confusion with all the people already
> using project quotas on XFS. You're implementing subtree quotas, not
> project quotas.
Actually this was already discussed, Initially i've called it as
subtree-id will strict tree behavior similar to XFS
1) Subtrees are true ADG
2) Id inherited from a parent
1) No hadrlink allowed across two subtries with different id
2) No cross-tree renames are allowed
But it some people, especially AlViro was not happy about this.
So i've simplify id ideology to simple rule
1) Inherent id from parent
This this is just an third ID similar to UID and GID without any
restrictions, but in case of project-id (or subtree-id) it can not be
inherent from current task because currently there is no way to
understand which container or sub-process group this task belongs to
That's why i've chose simplest and most obvious rule just inherent id
from parent. 
As far as i know nor project-id nor subtree-id are not ideal, but you
probably right most people already know that project-id affect
rename/link so i have to change the name to 'subtree-id', or may be 'bundle-id'
> 
> >   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 project quota. Will appear in later
> >   patches.
> 
> As implied above, project quotas ID in XFS are not subtree quotas.
> They are used to implement project quotas, not subtree quotas.
> subtree quotas are a userspace management construction using a
> subset of project quota infrastructure. i.e. the inheritance of
> project IDs in XFS and the restriction of operations to within
> subtrees is not a function of project quotas - it's a separately
> managed feature that was added to support the functioanlity needed
> for subtree quotas.
> 
> ISTR raising this objection last time you posted this patch set -
> either you implement project quotas as they exist in XFS and handle
> subtree quotas as a constrainted set of project quota functionality
> or you need to drop all references to "project quotas" and call this
> something like sub-tree quotas that uses subtree IDs.
> 
> > * Disk layout
> >   Project id is stored on disk inside xattr usually inside ibody.
> >   Xattr is used only as a data storage, It has not user visible xattr
> >   interface.
> 
> If you've got enough xattrs on the inode, then it will end up out of
> line and performance is going to suck - every time you write to a
> cold file you now need two IOs - one to read the extent list, and
> one to read the xattrs to get the project ID....
> 
> > * User interface
> >   Project id is accessible via generic xattr interface "system.project_id"
> 
> Which means it is an incompatible with XFS. What purpose does this
> serve except to confuse people? Why not a generic set/get project ID
> quotactl/ioctl? That's much easier for applications to use compared
> to xattrs, and far easier to support different implementations
> across filesystems. The way the project ID is stored in ext4 should
> not define the API.
> 
> Indeed, an ioctl/quotactl style interface matches the existing quota
> interface, so it's much more natural to use a similar API to one
> already used for quota manipulation...
> 
> > * Notes
> >   ext4_setattr interface to prjid: Semantically prjid must being changed
> >   similar to uid/gid, but project_id is stored inside xattr so on-disk
> >   structures updates is not that trivial, so I've move prjid change
> >   logic to separate function.
> 
> I suspect that there are corner cases you haven't thought about
> yet. What happens when you hard link a file into two separate
> subtrees? how do you account for that? What happens when you remove
> the hard link from the subtree the inode is accounted to?  What
> happens when a rename occurs, moving a file from one subtree to
> another? 
As i already noted my implementation does not affect rename/link at all.
But as far as i know most obvious use-case is when will restrict access
to some subtree via bindmout and pass it as a root to some set of tasks (container,cgroup)
Such set of task should not have privileges to change project-id
explicitly (only inheritance are allowed), in that case we will have
pure sub-trees hierarchy.
> 
> .....
> 
> > +static int
> > +ext4_xattr_prj_set(struct dentry *dentry, const char *name,
> > +		const void *value, size_t size, int flags, int type)
> > +{
> > +	unsigned int new_prjid;
> > +	if (strcmp(name, "") != 0)
> > +		return -EINVAL;
> > +	new_prjid = simple_strtoul(value, (char **)&value, 0);
> > +	return ext4_prj_change(dentry->d_inode, new_prjid);
> 
> Because you are using strings rather than integers for the ID, you
> need to do a lot better verification here - what will happen when
> someone tries to set an ID of "frobnozzle"? Using integers via
> ioctls for the user API make this problem....
> 
> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index c22f170..377af40 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -76,6 +76,14 @@  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_PROJECT_ID
+	bool "Ext4 project_id support"
+	depends on PROJECT_ID
+	depends on EXT4_FS_XATTR
+	help
+	  Enables project inode identifier support for ext4 filesystem.
+	  This feature allow to assign some id to inodes similar to
+	  uid/gid. 
 
 config EXT4_DEBUG
 	bool "EXT4 debugging support"
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 56fd8f8..692fe56 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -12,3 +12,4 @@  ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.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_PROJECT_ID)		+= project.o
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index cfc4e01..c0e33d7 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -925,6 +925,9 @@  struct ext4_inode_info {
 
 	/* Precomputed uuid+inum+igen checksum for seeding inode checksums */
 	__u32 i_csum_seed;
+#ifdef CONFIG_EXT4_PROJECT_ID
+	__u32 i_prjid;
+#endif
 };
 
 /*
@@ -962,6 +965,7 @@  struct ext4_inode_info {
 #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_PROJECT_ID		0x40000 /* project owner id support */
 #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 */
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index d48e8b1..d4b72e5 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 "project.h"
 
 #include <trace/events/ext4.h>
 
@@ -898,6 +899,8 @@  got:
 
 	ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
 
+	ext4_setprjid(inode, ext4_getprjid(dir));
+
 	ret = inode;
 	dquot_initialize(inode);
 	err = dquot_alloc_inode(inode);
@@ -911,6 +914,9 @@  got:
 	err = ext4_init_security(handle, inode, dir, qstr);
 	if (err)
 		goto fail_free_drop;
+	err = ext4_prj_init(handle, inode);
+	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*/
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 02bc8cb..c98d8d6 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -42,6 +42,7 @@ 
 #include "xattr.h"
 #include "acl.h"
 #include "truncate.h"
+#include "project.h"
 
 #include <trace/events/ext4.h>
 
@@ -3870,6 +3871,9 @@  struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 	}
 	if (ret)
 		goto bad_inode;
+	ret = ext4_prj_read(inode);
+	if (ret)
+		goto bad_inode;
 
 	if (S_ISREG(inode->i_mode)) {
 		inode->i_op = &ext4_file_inode_operations;
diff --git a/fs/ext4/project.c b/fs/ext4/project.c
new file mode 100644
index 0000000..a262a49
--- /dev/null
+++ b/fs/ext4/project.c
@@ -0,0 +1,213 @@ 
+/*
+ * linux/fs/ext4/projectid.c
+ *
+ * Copyright (C) 2010 Parallels Inc
+ * Dmitry Monakhov <dmonakhov@openvz.org>
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/capability.h>
+#include <linux/fs.h>
+#include <linux/quotaops.h>
+#include "ext4_jbd2.h"
+#include "ext4.h"
+#include "xattr.h"
+#include "project.h"
+
+
+/*
+ * PROJECT SUBTREE
+ * 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.
+ *
+ * Project Subtree's 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
+ */
+
+/*
+ * Read project_id id from inode's xattr
+ * Locking: none
+ */
+int ext4_prj_xattr_read(struct inode *inode, unsigned int *prjid)
+{
+	__le32 dsk_prjid;
+	int retval;
+	retval = ext4_xattr_get(inode, EXT4_XATTR_INDEX_PROJECT_ID, "",
+				&dsk_prjid, sizeof (dsk_prjid));
+	if (retval > 0) {
+		if (retval != sizeof(dsk_prjid))
+			return -EIO;
+		else
+			retval = 0;
+	}
+	*prjid = le32_to_cpu(dsk_prjid);
+	return retval;
+
+}
+
+/*
+ * Save project_id id to inode's xattr
+ * Locking: none
+ */
+int ext4_prj_xattr_write(handle_t *handle, struct inode *inode,
+				unsigned int prjid, int xflags)
+{
+	__le32 dsk_prjid = cpu_to_le32(prjid);
+	int retval;
+	retval = ext4_xattr_set_handle(handle,
+				inode, EXT4_XATTR_INDEX_PROJECT_ID, "",
+				&dsk_prjid, sizeof (dsk_prjid), xflags);
+	if (retval > 0) {
+		if (retval != sizeof(dsk_prjid))
+			retval =  -EIO;
+		else
+			retval = 0;
+	}
+	return retval;
+}
+
+/*
+ * Change project_id id.
+ * Called under inode->i_mutex
+ */
+int ext4_prj_change(struct inode *inode, unsigned int new_prjid)
+{
+	/*
+	 * One data_trans_blocks chunk for xattr update.
+	 * One quota_trans_blocks chunk for quota transfer, and one
+	 * quota_trans_block chunk for emergency quota rollback transfer,
+	 * because quota rollback may result new quota blocks allocation.
+	 */
+	unsigned credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb) +
+		EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb) * 2;
+	int ret, ret2 = 0;
+	unsigned retries = 0;
+	handle_t *handle;
+	struct dquot *dquot[MAXQUOTAS] = {};
+	int old_id = ext4_getprjid(inode);
+	dquot_initialize(inode);
+
+	dquot[PRJQUOTA] = dqget(inode->i_sb, new_prjid, PRJQUOTA);
+retry:
+	handle = ext4_journal_start(inode, credits);
+	if (IS_ERR(handle)) {
+		ret = PTR_ERR(handle);
+		ext4_std_error(inode->i_sb, ret);
+		goto out;
+	}
+	/* Inode may not have project_id xattr yet. Create it explicitly */
+	ret = ext4_prj_xattr_write(handle, inode, old_id, XATTR_CREATE);
+	if (ret == -EEXIST)
+		ret = 0;
+	if (ret) {
+		ret2 = ext4_journal_stop(handle);
+		if (ret2)
+			ret = ret2;
+		if (ret == -ENOSPC &&
+			ext4_should_retry_alloc(inode->i_sb, &retries))
+			goto retry;
+	}
+#ifdef CONFIG_QUOTA
+	ret = __dquot_transfer(inode, dquot);
+	if (ret)
+		return ret;
+#endif
+	ret = ext4_prj_xattr_write(handle, inode, new_prjid, XATTR_REPLACE);
+	if (ret) {
+		/*
+		 * Function may fail only due to fatal error, Nor than less
+		 * we have try to rollback quota changes.
+		 */
+#ifdef CONFIG_QUOTA
+		__dquot_transfer(inode, dquot);
+#endif
+		ext4_std_error(inode->i_sb, ret);
+
+	} else
+		ext4_setprjid(inode, new_prjid);
+
+	ret2 = ext4_journal_stop(handle);
+out:
+	dqput(dquot[PRJQUOTA]);
+	if (ret2)
+		ret = ret2;
+	return ret;
+}
+
+int ext4_prj_read(struct inode *inode)
+{
+	int ret = 0;
+	int prjid = 0;
+	if(test_opt(inode->i_sb, PROJECT_ID)) {
+		ret = ext4_prj_xattr_read(inode, &prjid);
+		if (ret == -ENODATA) {
+			prjid = 0;
+			ret = 0;
+		}
+	}
+	if (!ret)
+		ext4_setprjid(inode, prjid);
+	return ret;
+}
+/*
+ * Initialize the projectid xattr of a new inode. Called from ext4_new_inode.
+ *
+ * dir->i_mutex: down
+ * inode->i_mutex: up (access to inode is still exclusive)
+ * Note: caller must assign correct project id to inode before.
+ */
+int ext4_prj_init(handle_t *handle, struct inode *inode)
+{
+	return ext4_prj_xattr_write(handle, inode, EXT4_I(inode)->i_prjid,
+				XATTR_CREATE);
+}
+
+static size_t
+ext4_xattr_prj_list(struct dentry *dentry, char *list, size_t list_size,
+		const char *name, size_t name_len, int type)
+{
+	if (list && XATTR_PRJID_LEN <= list_size)
+		memcpy(list, XATTR_PRJID, XATTR_PRJID_LEN);
+	return XATTR_PRJID_LEN;
+
+}
+
+static int
+ext4_xattr_prj_get(struct dentry *dentry, const char *name,
+		       void *buffer, size_t size, int type)
+{
+	int ret;
+	unsigned prjid;
+	char buf[32];
+	if (strcmp(name, "") != 0)
+		return -EINVAL;
+	ret = ext4_prj_xattr_read(dentry->d_inode, &prjid);
+	if (ret)
+		return ret;
+	snprintf(buf, sizeof(buf)-1, "%u", prjid);
+	buf[31] = '\0';
+	strncpy(buffer, buf, size);
+	return strlen(buf);
+}
+
+static int
+ext4_xattr_prj_set(struct dentry *dentry, const char *name,
+		const void *value, size_t size, int flags, int type)
+{
+	unsigned int new_prjid;
+	if (strcmp(name, "") != 0)
+		return -EINVAL;
+	new_prjid = simple_strtoul(value, (char **)&value, 0);
+	return ext4_prj_change(dentry->d_inode, new_prjid);
+}
+
+const struct xattr_handler ext4_xattr_prj_handler = {
+	.prefix	= XATTR_PRJID,
+	.list	= ext4_xattr_prj_list,
+	.get	= ext4_xattr_prj_get,
+	.set	= ext4_xattr_prj_set,
+};
diff --git a/fs/ext4/project.h b/fs/ext4/project.h
new file mode 100644
index 0000000..054ec4c
--- /dev/null
+++ b/fs/ext4/project.h
@@ -0,0 +1,45 @@ 
+#include <linux/xattr.h>
+#include <linux/fs.h>
+
+#ifdef CONFIG_EXT4_PROJECT_ID
+extern int ext4_prj_xattr_read(struct inode *inode, unsigned int *prjid);
+extern int ext4_prj_xattr_write(handle_t *handle, struct inode *inode,
+				unsigned int prjid, int xflags);
+extern int ext4_prj_init(handle_t *handle, struct inode *inode);
+extern int ext4_prj_read(struct inode *inode);
+extern int ext4_prj_change(struct inode *inode, unsigned int new_prjid);
+static inline u32 ext4_getprjid(const struct inode *inode)
+{
+	const struct ext4_inode_info *ei =
+		container_of(inode, const struct ext4_inode_info, vfs_inode);
+	return ei->i_prjid;
+}
+static inline void ext4_setprjid(struct inode *inode, u32 id)
+{
+	EXT4_I(inode)->i_prjid = id;
+}
+#else
+#define ext4_getprjid(inode) do {} while (0)
+#define ext4_setprjid(inode, id) do {} while (0)
+static inline int ext4_prj_xattr_read(struct inode *inode, unsigned int *prjid)
+{
+	return -ENOTSUPP;
+}
+static inline int ext4_prj_xattr_write(handle_t *handle, struct inode *inode,
+				unsigned int prjid, int xflags)
+{
+	return -ENOTSUPP;
+}
+static inline int ext4_prj_read(struct inode *inode)
+{
+	return 0;
+}
+static inline int ext4_prj_change(struct inode *inode, unsigned int new_prjid)
+{
+	return -ENOTSUPP;
+}
+static inline int ext4_prj_init(handle_t *handle, struct inode *inode)
+{
+	return 0;
+}
+#endif
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index eb7aa3e..bf6f4ba 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -50,6 +50,7 @@ 
 #include "xattr.h"
 #include "acl.h"
 #include "mballoc.h"
+#include "project.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/ext4.h>
@@ -1185,6 +1186,9 @@  static const struct super_operations ext4_sops = {
 	.quota_write	= ext4_quota_write,
 #endif
 	.bdev_try_to_free_page = bdev_try_to_free_page,
+#ifdef CONFIG_EXT4_PROJECT_ID
+	.get_prjid      = ext4_getprjid,
+#endif
 };
 
 static const struct super_operations ext4_nojournal_sops = {
@@ -1204,6 +1208,9 @@  static const struct super_operations ext4_nojournal_sops = {
 	.quota_write	= ext4_quota_write,
 #endif
 	.bdev_try_to_free_page = bdev_try_to_free_page,
+#ifdef CONFIG_EXT4_PROJECT_ID
+	.get_prjid      = ext4_getprjid,
+#endif
 };
 
 static const struct export_operations ext4_export_ops = {
@@ -1231,6 +1238,7 @@  enum {
 	Opt_inode_readahead_blks, Opt_journal_ioprio,
 	Opt_dioread_nolock, Opt_dioread_lock,
 	Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
+	Opt_project_id,
 };
 
 static const match_table_t tokens = {
@@ -1304,6 +1312,7 @@  static const match_table_t tokens = {
 	{Opt_init_itable, "init_itable=%u"},
 	{Opt_init_itable, "init_itable"},
 	{Opt_noinit_itable, "noinit_itable"},
+	{Opt_project_id, "prjid"},
 	{Opt_removed, "check=none"},	/* mount option from ext2/3 */
 	{Opt_removed, "nocheck"},	/* mount option from ext2/3 */
 	{Opt_removed, "reservation"},	/* mount option from ext2/3 */
@@ -1484,6 +1493,11 @@  static const struct mount_opts {
 	{Opt_jqfmt_vfsold, QFMT_VFS_OLD, MOPT_QFMT},
 	{Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
 	{Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
+#ifdef CONFIG_EXT4_PROJECT_ID
+	{Opt_project_id, EXT4_MOUNT_PROJECT_ID, MOPT_SET},
+#else
+	{Opt_project_id, 0, MOPT_NOSUPPORT},
+#endif
 	{Opt_err, 0, 0}
 };
 
@@ -1822,6 +1836,8 @@  static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
 		else if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)
 			SEQ_OPTS_PUTS("data=writeback");
 	}
+	if (test_opt(sb, PROJECT_ID))
+		SEQ_OPTS_PUTS("prjid");
 	if (nodefs ||
 	    sbi->s_inode_readahead_blks != EXT4_DEF_INODE_READAHEAD_BLKS)
 		SEQ_OPTS_PRINT("inode_readahead_blks=%u",
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index e56c9ed..ff24df7 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -107,6 +107,10 @@  static const struct xattr_handler *ext4_xattr_handler_map[] = {
 #ifdef CONFIG_EXT4_FS_SECURITY
 	[EXT4_XATTR_INDEX_SECURITY]	     = &ext4_xattr_security_handler,
 #endif
+#ifdef CONFIG_EXT4_PROJECT_ID
+	[EXT4_XATTR_INDEX_PROJECT_ID]	     = &ext4_xattr_prj_handler,
+#endif
+
 };
 
 const struct xattr_handler *ext4_xattr_handlers[] = {
@@ -119,6 +123,9 @@  const struct xattr_handler *ext4_xattr_handlers[] = {
 #ifdef CONFIG_EXT4_FS_SECURITY
 	&ext4_xattr_security_handler,
 #endif
+#ifdef CONFIG_EXT4_PROJECT_ID
+	&ext4_xattr_prj_handler,
+#endif
 	NULL
 };
 
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 91f31ca..2b3e1ff 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_PROJECT_ID	        7
 
 struct ext4_xattr_header {
 	__le32	h_magic;	/* magic number for identification */
@@ -72,6 +73,7 @@  extern const struct xattr_handler ext4_xattr_trusted_handler;
 extern const struct xattr_handler ext4_xattr_acl_access_handler;
 extern const struct xattr_handler ext4_xattr_acl_default_handler;
 extern const struct xattr_handler ext4_xattr_security_handler;
+extern const struct xattr_handler ext4_xattr_prj_handler;
 
 extern ssize_t ext4_listxattr(struct dentry *, char *, size_t);