shmem: Add user and group quota support for tmpfs
Subsystems (5)
v2 cover shmem: Add user and group quota support for tmpfs
From: Carlos Maiolino <[email protected]>
Hello folks.
This is the version 2 of the quota support from tmpfs addressing some issues discussed on V1 and a few extra things, details are within each patch. Original cover-letter below.
Hi folks. this work has been done originally by Lukas, but he left the company, so I'm taking over his work from where he left it of. This series is virtually done, and he had updated it with comments from the last version, but, I'm initially posting it as a RFC because it's been a while since he posted the last version.
Most of what I did here was rebase his last work on top of current Linus's tree.
Honza, there is one patch from you in this series, which I believe you had it suggested to Lukas on a previous version.
The original cover-letter follows...
people have been asking for quota support in tmpfs many times in the past mostly to avoid one malicious user, or misbehaving user/program to consume all of the system memory. This has been partially solved with the size mount option, but some problems still prevail.
One of the problems is the fact that /dev/shm is still generally unprotected with this and another is administration overhead of managing multiple tmpfs mounts and lack of more fine grained control.
Quota support can solve all these problems in a somewhat standard way people are already familiar with from regular file systems. It can give us more fine grained control over how much memory user/groups can consume. Additionally it can also control number of inodes and with special quota mount options introduced with a second patch we can set global limits allowing us to replace the size mount option with quota entirely.
Currently the standard userspace quota tools (quota, xfs_quota) are only using quotactl ioctl which is expecting a block device. I patched quota [1] and xfs_quota [2] to use quotactl_fd in case we want to run the tools on mount point directory to work nicely with tmpfs.
The implementation was tested on patched version of xfstests [3].
[1] https://github.com/lczerner/quota/tree/quotactl_fd_support
[2] https://github.com/lczerner/xfsprogs/tree/quotactl_fd_support
[3] https://github.com/lczerner/xfstests/tree/tmpfs_quota_support
Jan Kara (1):
quota: Check presence of quota operation structures instead of
->quota_read and ->quota_write callbacks
Lukas Czerner (5):
shmem: make shmem_inode_acct_block() return error
shmem: make shmem_get_inode() return ERR_PTR instead of NULL
shmem: prepare shmem quota infrastructure
shmem: quota support
Add default quota limit mount options
Documentation/filesystems/tmpfs.rst | 31 ++ fs/Kconfig | 12 + fs/quota/dquot.c | 2 +- include/linux/shmem_fs.h | 28 ++ include/uapi/linux/quota.h | 1 + mm/Makefile | 2 +- mm/shmem.c | 473 +++++++++++++++++++++------- mm/shmem_quota.c | 327 +++++++++++++++++++ 8 files changed, 768 insertions(+), 108 deletions(-) create mode 100644 mm/shmem_quota.c
Signature · 1 line
2.30.2
PATCH v2 1/6 shmem: make shmem_inode_acct_block() return error
From: Lukas Czerner <[email protected]>
Make shmem_inode_acct_block() return proper error code instead of bool. This will be useful later when we introduce quota support.
There should be no functional change.
---
V2:
- remove unused variable initialization on shmem_alloc_and_acct_folio
mm/shmem.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-)
mm/shmem.c +10 -8
@@ -198,13 +198,14 @@ static inline void shmem_unacct_blocks(unsigned long flags, long pages)vm_unacct_memory (pages *VM_ACCT (PAGE_SIZE )); } -static inline bool shmem_inode_acct_block (struct inode *inode,long pages) +static inline int shmem_inode_acct_block (struct inode *inode,long pages) {struct shmem_inode_info *info =SHMEM_I (inode);struct shmem_sb_info *sbinfo =SHMEM_SB (inode->i_sb); +int err = -ENOSPC ;if (shmem_acct_block (info->flags, pages)) -return false; +return err;if (sbinfo->max_blocks) { if (percpu_counter_compare(&sbinfo->used_blocks,
@@ -213,11 +214,11 @@ static inline bool shmem_inode_acct_block(struct inode *inode, long pages)percpu_counter_add (&sbinfo->used_blocks, pages); } -return true; +return 0 ; unacct:shmem_unacct_blocks (info->flags, pages); -return false; +return err; }static inline void shmem_inode_unacct_blocks (struct inode *inode,long pages)
@@ -369,7 +370,7 @@ bool shmem_charge(struct inode *inode, long pages)struct shmem_inode_info *info =SHMEM_I (inode);unsigned long flags; -if (!shmem_inode_acct_block (inode, pages)) +if (shmem_inode_acct_block (inode, pages))return false;/* nrpages adjustment first, then shmem_recalc_inode() when balanced */
@@ -1583,13 +1584,14 @@ static struct folio *shmem_alloc_and_acct_folio(gfp_t gfp, struct inode *inode,struct shmem_inode_info *info =SHMEM_I (inode);struct folio *folio;int nr; -int err = -ENOSPC ; +int err;if (!IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE )) huge = false; nr = huge ?HPAGE_PMD_NR :1 ; -if (!shmem_inode_acct_block (inode, nr)) + err =shmem_inode_acct_block (inode, nr); +if (err) goto failed;if (huge)
@@ -2433,7 +2435,7 @@ int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,int ret;pgoff_t max_off; -if (!shmem_inode_acct_block (inode,1 )) { +if (shmem_inode_acct_block (inode,1 )) {/* * We may have got a page, returned -ENOENT triggering a retry, * and now we find ourselves with -ENOMEM. Release the page, to
Signature · 1 line
2.30.2
Re: [PATCH V2 1/6] shmem: make shmem_inode_acct_block() return error
Quoted 9 lines · On Thu 20-04-23 10:03:54, [email protected] wrote:
From: Lukas Czerner <[email protected]>
Make
shmem_inode_acct_block()return proper error code instead of bool. This will be useful later when we introduce quota support.There should be no functional change.
Signed-off-by Lukas Czerner <[email protected]>Signed-off-by Carlos Maiolino <[email protected]>
Looks good. Feel free to add:
Honza
Quoted 82 lines
---
V2:
- remove unused variable initialization onshmem_alloc_and_acct_foliomm/shmem.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-)mm/shmem.c +10
-8@@ -198,13 +198,14 @@ static inline void shmem_unacct_blocks(unsigned long flags, long pages)vm_unacct_memory (pages *VM_ACCT (PAGE_SIZE )); } -static inline bool shmem_inode_acct_block (struct inode *inode,long pages) +static inline int shmem_inode_acct_block (struct inode *inode,long pages) {struct shmem_inode_info *info =SHMEM_I (inode);struct shmem_sb_info *sbinfo =SHMEM_SB (inode->i_sb); +int err = -ENOSPC ;if (shmem_acct_block (info->flags, pages)) -return false; +return err;if (sbinfo->max_blocks) { if (percpu_counter_compare(&sbinfo->used_blocks,@@ -213,11 +214,11 @@ static inline bool shmem_inode_acct_block(struct inode *inode, long pages)percpu_counter_add (&sbinfo->used_blocks, pages); } -return true; +return 0 ; unacct:shmem_unacct_blocks (info->flags, pages); -return false; +return err; }static inline void shmem_inode_unacct_blocks (struct inode *inode,long pages)@@ -369,7 +370,7 @@ bool shmem_charge(struct inode *inode, long pages)struct shmem_inode_info *info =SHMEM_I (inode);unsigned long flags; -if (!shmem_inode_acct_block (inode, pages)) +if (shmem_inode_acct_block (inode, pages))return false;/* nrpages adjustment first, then shmem_recalc_inode() when balanced */ @@ -1583,13 +1584,14 @@ static struct folio *shmem_alloc_and_acct_folio(gfp_t gfp, struct inode *inode,struct shmem_inode_info *info =SHMEM_I (inode);struct folio *folio;int nr; -int err = -ENOSPC ; +int err;if (!IS_ENABLED (CONFIG_TRANSPARENT_HUGEPAGE )) huge = false; nr = huge ?HPAGE_PMD_NR :1 ; -if (!shmem_inode_acct_block (inode, nr)) + err =shmem_inode_acct_block (inode, nr); +if (err) goto failed;if (huge)@@ -2433,7 +2435,7 @@ int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,int ret;pgoff_t max_off; -if (!shmem_inode_acct_block (inode,1 )) { +if (shmem_inode_acct_block (inode,1 )) {/* * We may have got a page, returned -ENOENT triggering a retry, * and now we find ourselves with -ENOMEM. Release the page, to Signature · 1 line
2.30.2
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
PATCH v2 2/6 shmem: make shmem_get_inode() return ERR_PTR instead of NULL
In reply to [email protected]From: Lukas Czerner <[email protected]>
Make shmem_get_inode() return ERR_PTR instead of NULL on error. This will be useful later when we introduce quota support.
There should be no functional change.
---
V2:
- Fix error handling logic in shmem_tmpfile()
- Add missing idmap argument to the ramfs version of shmem_get_inode(), reported by Intel's kernel test robot <[email protected]>
mm/shmem.c | 206 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 116 insertions(+), 90 deletions(-)
mm/shmem.c +116 -90
@@ -2354,65 +2354,71 @@ static struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_blockstruct shmem_inode_info *info;struct shmem_sb_info *sbinfo =SHMEM_SB (sb);ino_t ino; +int err; + + err =shmem_reserve_inode (sb, &ino); +if (err) +return ERR_PTR (err); -if (shmem_reserve_inode (sb, &ino)) -return NULL ; inode =new_inode (sb); -if (inode) { - inode->i_ino = ino; -inode_init_owner (idmap, inode, dir, mode); - inode->i_blocks =0 ; - inode->i_atime = inode->i_mtime = inode->i_ctime =current_time (inode); - inode->i_generation =get_random_u32 (); - info =SHMEM_I (inode); -memset (info,0 , (char *)inode - (char *)info); -spin_lock_init (&info->lock); -atomic_set (&info->stop_eviction,0 ); - info->seals =F_SEAL_SEAL ; - info->flags = flags &VM_NORESERVE ; - info->i_crtime = inode->i_mtime; - info->fsflags = (dir ==NULL ) ?0 : -SHMEM_I (dir)->fsflags &SHMEM_FL_INHERITED ; -if (info->fsflags) -shmem_set_inode_flags (inode, info->fsflags); -INIT_LIST_HEAD (&info->shrinklist); -INIT_LIST_HEAD (&info->swaplist); -simple_xattrs_init (&info->xattrs); -cache_no_acl (inode); -mapping_set_large_folios (inode->i_mapping); - -switch (mode &S_IFMT ) { -default : - inode->i_op = &shmem_special_inode_operations; -init_special_inode (inode, mode, dev); -break ; -case S_IFREG : - inode->i_mapping->a_ops = &shmem_aops; - inode->i_op = &shmem_inode_operations; - inode->i_fop = &shmem_file_operations; -mpol_shared_policy_init (&info->policy, -shmem_get_sbmpol (sbinfo)); -break ; -case S_IFDIR : -inc_nlink (inode); -/* Some things misbehave if size == 0 on a directory */ - inode->i_size =2 *BOGO_DIRENT_SIZE ; - inode->i_op = &shmem_dir_inode_operations; - inode->i_fop = &simple_dir_operations; -break ; -case S_IFLNK : -/* -* Must not load anything in the rbtree, -* mpol_free_shared_policy will not be called. -*/ -mpol_shared_policy_init (&info->policy,NULL ); -break ; - } -lockdep_annotate_inode_mutex_key (inode); - }else +if (!inode) {shmem_free_inode (sb); +return ERR_PTR (-ENOSPC ); + } + + inode->i_ino = ino; +inode_init_owner (idmap, inode, dir, mode); + inode->i_blocks =0 ; + inode->i_atime = inode->i_mtime = inode->i_ctime =current_time (inode); + inode->i_generation =get_random_u32 (); + info =SHMEM_I (inode); +memset (info,0 , (char *)inode - (char *)info); +spin_lock_init (&info->lock); +atomic_set (&info->stop_eviction,0 ); + info->seals =F_SEAL_SEAL ; + info->flags = flags &VM_NORESERVE ; + info->i_crtime = inode->i_mtime; + info->fsflags = (dir ==NULL ) ?0 : +SHMEM_I (dir)->fsflags &SHMEM_FL_INHERITED ; +if (info->fsflags) +shmem_set_inode_flags (inode, info->fsflags); +INIT_LIST_HEAD (&info->shrinklist); +INIT_LIST_HEAD (&info->swaplist); +simple_xattrs_init (&info->xattrs); +cache_no_acl (inode); +mapping_set_large_folios (inode->i_mapping); + +switch (mode &S_IFMT ) { +default : + inode->i_op = &shmem_special_inode_operations; +init_special_inode (inode, mode, dev); +break ; +case S_IFREG : + inode->i_mapping->a_ops = &shmem_aops; + inode->i_op = &shmem_inode_operations; + inode->i_fop = &shmem_file_operations; +mpol_shared_policy_init (&info->policy, +shmem_get_sbmpol (sbinfo)); +break ; +case S_IFDIR : +inc_nlink (inode); +/* Some things misbehave if size == 0 on a directory */ + inode->i_size =2 *BOGO_DIRENT_SIZE ; + inode->i_op = &shmem_dir_inode_operations; + inode->i_fop = &simple_dir_operations; +break ; +case S_IFLNK : +/* +* Must not load anything in the rbtree, +* mpol_free_shared_policy will not be called. +*/ +mpol_shared_policy_init (&info->policy,NULL ); +break ; + } + +lockdep_annotate_inode_mutex_key (inode);return inode; }
@@ -2927,27 +2933,30 @@ shmem_mknod(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { struct inode *inode; -int error = -ENOSPC ; +int error; inode =shmem_get_inode (idmap, dir->i_sb, dir, mode, dev,VM_NORESERVE ); -if (inode) { - error =simple_acl_create (dir, inode); -if (error) - goto out_iput; - error =security_inode_init_security (inode, dir, - &dentry->d_name, - shmem_initxattrs,NULL ); -if (error && error != -EOPNOTSUPP ) - goto out_iput; - error =0 ; - dir->i_size +=BOGO_DIRENT_SIZE ; - dir->i_ctime = dir->i_mtime =current_time (dir); -inode_inc_iversion (dir); -d_instantiate (dentry, inode); -dget (dentry);/* Extra count - pin the dentry in core */ - } +if (IS_ERR (inode)) +return PTR_ERR (inode); + + error =simple_acl_create (dir, inode); +if (error) + goto out_iput; + error =security_inode_init_security (inode, dir, + &dentry->d_name, + shmem_initxattrs,NULL ); +if (error && error != -EOPNOTSUPP ) + goto out_iput; + + error =0 ; + dir->i_size +=BOGO_DIRENT_SIZE ; + dir->i_ctime = dir->i_mtime =current_time (dir); +inode_inc_iversion (dir); +d_instantiate (dentry, inode); +dget (dentry);/* Extra count - pin the dentry in core */ return error; + out_iput:iput (inode);return error;
@@ -2958,20 +2967,26 @@ shmem_tmpfile(struct mnt_idmap *idmap, struct inode *dir, struct file *file, umode_t mode) {struct inode *inode; -int error = -ENOSPC ; +int error; inode =shmem_get_inode (idmap, dir->i_sb, dir, mode,0 ,VM_NORESERVE ); -if (inode) { - error =security_inode_init_security (inode, dir, -NULL , - shmem_initxattrs,NULL ); -if (error && error != -EOPNOTSUPP ) - goto out_iput; - error =simple_acl_create (dir, inode); -if (error) - goto out_iput; -d_tmpfile (file, inode); + +if (IS_ERR (inode)) { + error =PTR_ERR (inode); + goto err_out; } + + error =security_inode_init_security (inode, dir, +NULL , + shmem_initxattrs,NULL ); +if (error && error != -EOPNOTSUPP ) + goto out_iput; + error =simple_acl_create (dir, inode); +if (error) + goto out_iput; +d_tmpfile (file, inode); + +err_out:return finish_open_simple (file, error); out_iput:iput (inode);
@@ -3146,8 +3161,9 @@ static int shmem_symlink(struct mnt_idmap *idmap, struct inode *dir, inode =shmem_get_inode (idmap, dir->i_sb, dir,S_IFLNK |0777 ,0 ,VM_NORESERVE ); -if (!inode) -return -ENOSPC ; + +if (IS_ERR (inode)) +return PTR_ERR (inode); error =security_inode_init_security (inode, dir, &dentry->d_name, shmem_initxattrs,NULL );
@@ -3762,12 +3778,13 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)struct shmem_options *ctx = fc->fs_private;struct inode *inode;struct shmem_sb_info *sbinfo; +int error = -ENOMEM ;/* Round up to L1_CACHE_BYTES to resist false sharing */ sbinfo =kzalloc (max ((int )sizeof (struct shmem_sb_info ),L1_CACHE_BYTES ),GFP_KERNEL );if (!sbinfo) -return -ENOMEM ; +return error; sb->s_fs_info = sbinfo;
@@ -3829,8 +3846,10 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) inode =shmem_get_inode (&nop_mnt_idmap, sb,NULL ,S_IFDIR | sbinfo->mode,0 ,VM_NORESERVE ); -if (!inode) +if (IS_ERR (inode)) { + error =PTR_ERR (inode); goto failed; + } inode->i_uid = sbinfo->uid; inode->i_gid = sbinfo->gid; sb->s_root =d_make_root (inode);
@@ -3840,7 +3859,7 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) failed:shmem_put_super (sb); -return -ENOMEM ; +return error; }static int shmem_get_tree (struct fs_context *fc)
@@ -4209,10 +4228,16 @@ EXPORT_SYMBOL_GPL(shmem_truncate_range);#define shmem_vm_ops generic_file_vm_ops#define shmem_anon_vm_ops generic_file_vm_ops#define shmem_file_operations ramfs_file_operations -#define shmem_get_inode (idmap, sb, dir, mode, dev, flags) ramfs_get_inode(sb, dir, mode, dev)#define shmem_acct_size (flags, size) 0#define shmem_unacct_size (flags, size) do {} while (0) +static inline struct inode *shmem_get_inode (struct mnt_idmap ,struct super_block *sb,struct inode *dir, +umode_t mode,dev_t dev,unsigned long flags) +{ +struct inode *inode =ramfs_get_inode (sb, dir, mode, dev); +return inode ? inode :ERR_PTR (-ENOSPC ); +} +#endif /* CONFIG_SHMEM */ /* common code */
@@ -4237,9 +4262,10 @@ static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name, l inode =shmem_get_inode (&nop_mnt_idmap, mnt->mnt_sb,NULL ,S_IFREG |S_IRWXUGO ,0 , flags); -if (unlikely (!inode)) { + +if (IS_ERR (inode)) {shmem_unacct_size (flags, size); -return ERR_PTR (-ENOSPC ); +return ERR_CAST (inode); } inode->i_flags |= i_flags; inode->i_size = size;
Signature · 1 line
2.30.2
Jan Kara
reviewerRe: [PATCH V2 2/6] shmem: make shmem_get_inode() return ERR_PTR instead of NULL
Quoted 9 lines · On Thu 20-04-23 10:03:55, [email protected] wrote:
From: Lukas Czerner <[email protected]>
Make
shmem_get_inode()returnERR_PTRinstead of NULL on error. This will be useful later when we introduce quota support.There should be no functional change.
Signed-off-by Lukas Czerner <[email protected]>Signed-off-by Carlos Maiolino <[email protected]>
Looks good to me except for one problem with ramfs fallback:
mm/shmem.c EXPORT_SYMBOL_GPL(shmem_truncate_range); · line 4235
Quoted 10 lines
mm/shmem.c +2
-1@@ -4209,10 +4228,16 @@ EXPORT_SYMBOL_GPL(shmem_truncate_range);#define shmem_vm_ops generic_file_vm_ops#define shmem_anon_vm_ops generic_file_vm_ops#define shmem_file_operations ramfs_file_operations -#define shmem_get_inode (idmap, sb, dir, mode, dev, flags) ramfs_get_inode(sb, dir, mode, dev)#define shmem_acct_size (flags, size) 0#define shmem_unacct_size (flags, size) do {} while (0) +static inline struct inode *shmem_get_inode (struct mnt_idmap ,struct super_block *sb,struct inode *dir, +umode_t mode,dev_t dev,unsigned long flags)
IMO this won't even compile - "struct mnt_idmap," does not look like valid C.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH V2 2/6] shmem: make shmem_get_inode() return ERR_PTR instead of NULL
Quoted 21 lines · Jan Kara
Quoted 9 lines · On Thu 20-04-23 10:03:55, [email protected] wrote:
From: Lukas Czerner <[email protected]>
Make
shmem_get_inode()returnERR_PTRinstead of NULL on error. This will be useful later when we introduce quota support.There should be no functional change.
Signed-off-by Lukas Czerner <[email protected]>Signed-off-by Carlos Maiolino <[email protected]>Looks good to me except for one problem with ramfs fallback:
Quoted 8 lines
mm/shmem.c +0
-1@@ -4209,10 +4228,16 @@ EXPORT_SYMBOL_GPL(shmem_truncate_range);#define shmem_vm_ops generic_file_vm_ops#define shmem_anon_vm_ops generic_file_vm_ops#define shmem_file_operations ramfs_file_operations -#define shmem_get_inode (idmap, sb, dir, mode, dev, flags) ramfs_get_inode(sb, dir, mode, dev)#define shmem_acct_size (flags, size) 0#define shmem_unacct_size (flags, size) do {} while (0)
Quoted 5 lines
mm/shmem.c EXPORT_SYMBOL_GPL(shmem_truncate_range); · line 4235
Quoted 2 lines
mm/shmem.c +2
-0+static inline struct inode *shmem_get_inode (struct mnt_idmap ,struct super_block *sb,struct inode *dir, +umode_t mode,dev_t dev,unsigned long flags)IMO this won't even compile - "struct
mnt_idmap," does not look like valid C.
True, I totally overlooked at it as I've been testing with/without TMPFS_QUOTA and QUOTA, I totally forgot to disable CONFIG_SHMEM for testing. I'll update it on a next version once we get a few eyes on it.
Thanks for spotting it Honza!
Quoted 5 lines
Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR
Signature · 1 line
Carlos Maiolino
PATCH v3 2/6 shmem: make shmem_get_inode() return ERR_PTR instead of NULL
In reply to [email protected]From: Lukas Czerner <[email protected]>
Make shmem_get_inode() return ERR_PTR instead of NULL on error. This will be useful later when we introduce quota support.
There should be no functional change.
---
V3:
- Fix shmem_get_inode() definition when CONFIG_SHMEM is not enabled
V2:
- Fix error handling logic in shmem_tmpfile()
- Add missing idmap argument to the ramfs version of shmem_get_inode(), reported by Intel's kernel test robot <[email protected]>
mm/shmem.c | 206 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 116 insertions(+), 90 deletions(-)
mm/shmem.c +116 -90
@@ -2354,65 +2354,71 @@ static struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_blockstruct shmem_inode_info *info;struct shmem_sb_info *sbinfo =SHMEM_SB (sb);ino_t ino; +int err; + + err =shmem_reserve_inode (sb, &ino); +if (err) +return ERR_PTR (err); -if (shmem_reserve_inode (sb, &ino)) -return NULL ; inode =new_inode (sb); -if (inode) { - inode->i_ino = ino; -inode_init_owner (idmap, inode, dir, mode); - inode->i_blocks =0 ; - inode->i_atime = inode->i_mtime = inode->i_ctime =current_time (inode); - inode->i_generation =get_random_u32 (); - info =SHMEM_I (inode); -memset (info,0 , (char *)inode - (char *)info); -spin_lock_init (&info->lock); -atomic_set (&info->stop_eviction,0 ); - info->seals =F_SEAL_SEAL ; - info->flags = flags &VM_NORESERVE ; - info->i_crtime = inode->i_mtime; - info->fsflags = (dir ==NULL ) ?0 : -SHMEM_I (dir)->fsflags &SHMEM_FL_INHERITED ; -if (info->fsflags) -shmem_set_inode_flags (inode, info->fsflags); -INIT_LIST_HEAD (&info->shrinklist); -INIT_LIST_HEAD (&info->swaplist); -simple_xattrs_init (&info->xattrs); -cache_no_acl (inode); -mapping_set_large_folios (inode->i_mapping); - -switch (mode &S_IFMT ) { -default : - inode->i_op = &shmem_special_inode_operations; -init_special_inode (inode, mode, dev); -break ; -case S_IFREG : - inode->i_mapping->a_ops = &shmem_aops; - inode->i_op = &shmem_inode_operations; - inode->i_fop = &shmem_file_operations; -mpol_shared_policy_init (&info->policy, -shmem_get_sbmpol (sbinfo)); -break ; -case S_IFDIR : -inc_nlink (inode); -/* Some things misbehave if size == 0 on a directory */ - inode->i_size =2 *BOGO_DIRENT_SIZE ; - inode->i_op = &shmem_dir_inode_operations; - inode->i_fop = &simple_dir_operations; -break ; -case S_IFLNK : -/* -* Must not load anything in the rbtree, -* mpol_free_shared_policy will not be called. -*/ -mpol_shared_policy_init (&info->policy,NULL ); -break ; - } -lockdep_annotate_inode_mutex_key (inode); - }else +if (!inode) {shmem_free_inode (sb); +return ERR_PTR (-ENOSPC ); + } + + inode->i_ino = ino; +inode_init_owner (idmap, inode, dir, mode); + inode->i_blocks =0 ; + inode->i_atime = inode->i_mtime = inode->i_ctime =current_time (inode); + inode->i_generation =get_random_u32 (); + info =SHMEM_I (inode); +memset (info,0 , (char *)inode - (char *)info); +spin_lock_init (&info->lock); +atomic_set (&info->stop_eviction,0 ); + info->seals =F_SEAL_SEAL ; + info->flags = flags &VM_NORESERVE ; + info->i_crtime = inode->i_mtime; + info->fsflags = (dir ==NULL ) ?0 : +SHMEM_I (dir)->fsflags &SHMEM_FL_INHERITED ; +if (info->fsflags) +shmem_set_inode_flags (inode, info->fsflags); +INIT_LIST_HEAD (&info->shrinklist); +INIT_LIST_HEAD (&info->swaplist); +simple_xattrs_init (&info->xattrs); +cache_no_acl (inode); +mapping_set_large_folios (inode->i_mapping); + +switch (mode &S_IFMT ) { +default : + inode->i_op = &shmem_special_inode_operations; +init_special_inode (inode, mode, dev); +break ; +case S_IFREG : + inode->i_mapping->a_ops = &shmem_aops; + inode->i_op = &shmem_inode_operations; + inode->i_fop = &shmem_file_operations; +mpol_shared_policy_init (&info->policy, +shmem_get_sbmpol (sbinfo)); +break ; +case S_IFDIR : +inc_nlink (inode); +/* Some things misbehave if size == 0 on a directory */ + inode->i_size =2 *BOGO_DIRENT_SIZE ; + inode->i_op = &shmem_dir_inode_operations; + inode->i_fop = &simple_dir_operations; +break ; +case S_IFLNK : +/* +* Must not load anything in the rbtree, +* mpol_free_shared_policy will not be called. +*/ +mpol_shared_policy_init (&info->policy,NULL ); +break ; + } + +lockdep_annotate_inode_mutex_key (inode);return inode; }
@@ -2927,27 +2933,30 @@ shmem_mknod(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { struct inode *inode; -int error = -ENOSPC ; +int error; inode =shmem_get_inode (idmap, dir->i_sb, dir, mode, dev,VM_NORESERVE ); -if (inode) { - error =simple_acl_create (dir, inode); -if (error) - goto out_iput; - error =security_inode_init_security (inode, dir, - &dentry->d_name, - shmem_initxattrs,NULL ); -if (error && error != -EOPNOTSUPP ) - goto out_iput; - error =0 ; - dir->i_size +=BOGO_DIRENT_SIZE ; - dir->i_ctime = dir->i_mtime =current_time (dir); -inode_inc_iversion (dir); -d_instantiate (dentry, inode); -dget (dentry);/* Extra count - pin the dentry in core */ - } +if (IS_ERR (inode)) +return PTR_ERR (inode); + + error =simple_acl_create (dir, inode); +if (error) + goto out_iput; + error =security_inode_init_security (inode, dir, + &dentry->d_name, + shmem_initxattrs,NULL ); +if (error && error != -EOPNOTSUPP ) + goto out_iput; + + error =0 ; + dir->i_size +=BOGO_DIRENT_SIZE ; + dir->i_ctime = dir->i_mtime =current_time (dir); +inode_inc_iversion (dir); +d_instantiate (dentry, inode); +dget (dentry);/* Extra count - pin the dentry in core */ return error; + out_iput:iput (inode);return error;
@@ -2958,20 +2967,26 @@ shmem_tmpfile(struct mnt_idmap *idmap, struct inode *dir, struct file *file, umode_t mode) {struct inode *inode; -int error = -ENOSPC ; +int error; inode =shmem_get_inode (idmap, dir->i_sb, dir, mode,0 ,VM_NORESERVE ); -if (inode) { - error =security_inode_init_security (inode, dir, -NULL , - shmem_initxattrs,NULL ); -if (error && error != -EOPNOTSUPP ) - goto out_iput; - error =simple_acl_create (dir, inode); -if (error) - goto out_iput; -d_tmpfile (file, inode); + +if (IS_ERR (inode)) { + error =PTR_ERR (inode); + goto err_out; } + + error =security_inode_init_security (inode, dir, +NULL , + shmem_initxattrs,NULL ); +if (error && error != -EOPNOTSUPP ) + goto out_iput; + error =simple_acl_create (dir, inode); +if (error) + goto out_iput; +d_tmpfile (file, inode); + +err_out:return finish_open_simple (file, error); out_iput:iput (inode);
@@ -3146,8 +3161,9 @@ static int shmem_symlink(struct mnt_idmap *idmap, struct inode *dir, inode =shmem_get_inode (idmap, dir->i_sb, dir,S_IFLNK |0777 ,0 ,VM_NORESERVE ); -if (!inode) -return -ENOSPC ; + +if (IS_ERR (inode)) +return PTR_ERR (inode); error =security_inode_init_security (inode, dir, &dentry->d_name, shmem_initxattrs,NULL );
@@ -3762,12 +3778,13 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)struct shmem_options *ctx = fc->fs_private;struct inode *inode;struct shmem_sb_info *sbinfo; +int error = -ENOMEM ;/* Round up to L1_CACHE_BYTES to resist false sharing */ sbinfo =kzalloc (max ((int )sizeof (struct shmem_sb_info ),L1_CACHE_BYTES ),GFP_KERNEL );if (!sbinfo) -return -ENOMEM ; +return error; sb->s_fs_info = sbinfo;
@@ -3829,8 +3846,10 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) inode =shmem_get_inode (&nop_mnt_idmap, sb,NULL ,S_IFDIR | sbinfo->mode,0 ,VM_NORESERVE ); -if (!inode) +if (IS_ERR (inode)) { + error =PTR_ERR (inode); goto failed; + } inode->i_uid = sbinfo->uid; inode->i_gid = sbinfo->gid; sb->s_root =d_make_root (inode);
@@ -3840,7 +3859,7 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) failed:shmem_put_super (sb); -return -ENOMEM ; +return error; }static int shmem_get_tree (struct fs_context *fc)
@@ -4209,10 +4228,16 @@ EXPORT_SYMBOL_GPL(shmem_truncate_range);#define shmem_vm_ops generic_file_vm_ops#define shmem_anon_vm_ops generic_file_vm_ops#define shmem_file_operations ramfs_file_operations -#define shmem_get_inode (idmap, sb, dir, mode, dev, flags) ramfs_get_inode(sb, dir, mode, dev)#define shmem_acct_size (flags, size) 0#define shmem_unacct_size (flags, size) do {} while (0) +static inline struct inode *shmem_get_inode (struct mnt_idmap *idmap,struct super_block *sb,struct inode *dir, +umode_t mode,dev_t dev,unsigned long flags) +{ +struct inode *inode =ramfs_get_inode (sb, dir, mode, dev); +return inode ? inode :ERR_PTR (-ENOSPC ); +} +#endif /* CONFIG_SHMEM */ /* common code */
@@ -4237,9 +4262,10 @@ static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name, l inode =shmem_get_inode (&nop_mnt_idmap, mnt->mnt_sb,NULL ,S_IFREG |S_IRWXUGO ,0 , flags); -if (unlikely (!inode)) { + +if (IS_ERR (inode)) {shmem_unacct_size (flags, size); -return ERR_PTR (-ENOSPC ); +return ERR_CAST (inode); } inode->i_flags |= i_flags; inode->i_size = size;
Signature · 1 line
2.30.2
Re: [PATCH V3 2/6] shmem: make shmem_get_inode() return ERR_PTR instead of NULL
Quoted 9 lines · On Tue 25-04-23 13:54:00, [email protected] wrote:
From: Lukas Czerner <[email protected]>
Make
shmem_get_inode()returnERR_PTRinstead of NULL on error. This will be useful later when we introduce quota support.There should be no functional change.
Signed-off-by Lukas Czerner <[email protected]>Signed-off-by Carlos Maiolino <[email protected]>
Looks good to me. Feel free to add:
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
PATCH v2 3/6 quota: Check presence of quota operation structures instead of ->quota_read and ->quota_write callbacks
In reply to [email protected]From: Jan Kara <[email protected]>
Currently we check whether superblock has ->quota_read and ->quota_write operations to check whether filesystem supports quotas. However for example for shmfs we will not read or write dquots so check whether quota operations are set in the superblock instead.
--- fs/quota/dquot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/quota/dquot.c +1 -1
@@ -2367,7 +2367,7 @@ int dquot_load_quota_sb(struct super_block *sb, int type, int format_id,if (!fmt)return -ESRCH ; -if (!sb->s_op->quota_write || !sb->s_op->quota_read || +if (!sb->dq_op || !sb->s_qcop || (type ==PRJQUOTA && sb->dq_op->get_projid ==NULL )) { error = -EINVAL ; goto out_fmt;
Signature · 1 line
2.30.2
PATCH v2 4/6 shmem: prepare shmem quota infrastructure
In reply to [email protected]From: Lukas Czerner <[email protected]>
Add new shmem quota format, its quota_format_ops together with dquot_operations
---
V2:
- define SHMEM_MAXQUOTAS in include/linux/shmem_fs.h instead of mm/shmem_quota.c
- Add a warning and error return if shmem_release_dquot() does not find an entry in the rb tree.
fs/Kconfig | 12 ++ include/linux/shmem_fs.h | 12 ++ include/uapi/linux/quota.h | 1 + mm/Makefile | 2 +- mm/shmem_quota.c | 318 +++++++++++++++++++++++++++++++++++++ 5 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 mm/shmem_quota.c
fs/Kconfig +12 -0
@@ -233,6 +233,18 @@ config TMPFS_INODE64 If unsure, say N. +config TMPFS_QUOTA + bool "Tmpfs quota support" + depends on TMPFS + select QUOTA + help + Quota support allows to set per user and group limits for tmpfs + usage. Say Y to enable quota support. Once enabled you can control + user and group quota enforcement with quota, usrquota and grpquota + mount options. + + If unsure, say N. + config ARCH_SUPPORTS_HUGETLBFS def_bool n
include/linux/shmem_fs.h +12 -0
@@ -12,6 +12,10 @@/* inode in-kernel data */ +#ifdef CONFIG_TMPFS_QUOTA +#define SHMEM_MAXQUOTAS 2 +#endif +struct shmem_inode_info {spinlock_t lock;unsigned int seals;/* shmem seals */
@@ -163,4 +167,12 @@ extern int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, pmd_t *dst_pmd,#endif /* CONFIG_SHMEM */ #endif /* CONFIG_USERFAULTFD */ +/* +* Used space is stored as unsigned 64-bit value in bytes but +* quota core supports only signed 64-bit values so use that +* as a limit +*/ +#define SHMEM_QUOTA_MAX_SPC_LIMIT 0x7fffffffffffffffLL/* 2^63-1 */ +#define SHMEM_QUOTA_MAX_INO_LIMIT 0x7fffffffffffffffLL +#endif
include/uapi/linux/quota.h +1 -0
@@ -77,6 +77,7 @@#define QFMT_VFS_V0 2#define QFMT_OCFS2 3#define QFMT_VFS_V1 4 +#define QFMT_SHMEM 5/* Size of block in which space limits are passed through the quota * interface */
mm/Makefile +1 -1
@@ -52,7 +52,7 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \ readahead.o swap.o truncate.o vmscan.o shmem.o \ util.o mmzone.o vmstat.o backing-dev.o \ mm_init.o percpu.o slab_common.o \ - compaction.o \ + compaction.o shmem_quota.o\ interval_tree.o list_lru.o workingset.o \ debug.o gup.o mmap_lock.o $(mmu-y)
mm/shmem_quota.c new file +318 -0
318 lines not on this page · raw message
Signature · 1 line
2.30.2
Re: [PATCH V2 4/6] shmem: prepare shmem quota infrastructure
Quoted 7 lines · On Thu 20-04-23 10:03:57, [email protected] wrote:
From: Lukas Czerner <[email protected]>
Add new shmem quota format, its
quota_format_opstogether withdquot_operationsSigned-off-by Lukas Czerner <[email protected]>Signed-off-by Carlos Maiolino <[email protected]>
Looks good to me. Feel free to add:
Honza
Quoted 418 lines
---
V2:
- defineSHMEM_MAXQUOTASininclude/linux/shmem_fs.hinstead ofmm/shmem_quota.c
- Add a warning and error return ifshmem_release_dquot()does not find an entry in the rb tree.fs/Kconfig | 12 ++ include/linux/shmem_fs.h | 12 ++ include/uapi/linux/quota.h | 1 + mm/Makefile | 2 +- mm/shmem_quota.c | 318 +++++++++++++++++++++++++++++++++++++ 5 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 mm/shmem_quota.cfs/Kconfig +12
-0@@ -233,6 +233,18 @@ config TMPFS_INODE64 If unsure, say N. +config TMPFS_QUOTA + bool "Tmpfs quota support" + depends on TMPFS + select QUOTA + help + Quota support allows to set per user and group limits for tmpfs + usage. Say Y to enable quota support. Once enabled you can control + user and group quota enforcement with quota, usrquota and grpquota + mount options. + + If unsure, say N. + config ARCH_SUPPORTS_HUGETLBFS def_bool ninclude/linux/shmem_fs.h +12
-0@@ -12,6 +12,10 @@/* inode in-kernel data */ +#ifdef CONFIG_TMPFS_QUOTA +#define SHMEM_MAXQUOTAS 2 +#endif +struct shmem_inode_info {spinlock_t lock;unsigned int seals;/* shmem seals */ @@ -163,4 +167,12 @@ extern int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, pmd_t *dst_pmd,#endif /* CONFIG_SHMEM */ #endif /* CONFIG_USERFAULTFD */ +/* +* Used space is stored as unsigned 64-bit value in bytes but +* quota core supports only signed 64-bit values so use that +* as a limit +*/ +#define SHMEM_QUOTA_MAX_SPC_LIMIT 0x7fffffffffffffffLL/* 2^63-1 */ +#define SHMEM_QUOTA_MAX_INO_LIMIT 0x7fffffffffffffffLL +#endif include/uapi/linux/quota.h +1
-0@@ -77,6 +77,7 @@#define QFMT_VFS_V0 2#define QFMT_OCFS2 3#define QFMT_VFS_V1 4 +#define QFMT_SHMEM 5/* Size of block in which space limits are passed through the quota * interface */ mm/Makefile +1
-1@@ -52,7 +52,7 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \ readahead.o swap.o truncate.o vmscan.o shmem.o \ util.o mmzone.o vmstat.o backing-dev.o \ mm_init.o percpu.o slab_common.o \ - compaction.o \ + compaction.o shmem_quota.o\ interval_tree.o list_lru.o workingset.o \ debug.o gup.o mmap_lock.o $(mmu-y)mm/shmem_quota.c new file +318
-0@@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +* In memory quota format relies on quota infrastructure to store dquot +* information for us. While conventional quota formats for file systems +* with persistent storage can load quota information into dquot from the +* storage on-demand and hence quota dquot shrinker can free any dquot +* that is not currently being used, it must be avoided here. Otherwise we +* can lose valuable information, user provided limits, because there is +* no persistent storage to load the information from afterwards. +* +* One information that in-memory quota format needs to keep track of is +* a sorted list of ids for each quota type. This is done by utilizing +* an rb tree which root is stored in mem_dqinfo->dqi_priv for each quota +* type. +* +* This format can be used to support quota on file system without persistent +* storage such as tmpfs. +* +* Author: Lukas Czerner <[email protected]> +* Carlos Maiolino <[email protected]> +* +* Copyright (C) 2023 Red Hat, Inc. +*/ +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/mount.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/rbtree.h> +#include <linux/shmem_fs.h> + +#include <linux/quotaops.h> +#include <linux/quota.h> + +#ifdef CONFIG_TMPFS_QUOTA + +/* +* The following constants define the amount of time given a user +* before the soft limits are treated as hard limits (usually resulting +* in an allocation failure). The timer is started when the user crosses +* their soft limit, it is reset when they go below their soft limit. +*/ +#define SHMEM_MAX_IQ_TIME 604800/* (7*24*60*60) 1 week */ +#define SHMEM_MAX_DQ_TIME 604800/* (7*24*60*60) 1 week */ + +struct quota_id { +struct rb_node node; +qid_t id; +qsize_t bhardlimit; +qsize_t bsoftlimit; +qsize_t ihardlimit; +qsize_t isoftlimit; +}; + +static int shmem_check_quota_file (struct super_block *sb,int type) +{ +/* There is no real quota file, nothing to do */ +return 1 ; +} + +/* +* There is no real quota file. Just allocate rb_root for quota ids and +* set limits +*/ +static int shmem_read_file_info (struct super_block *sb,int type) +{ +struct quota_info *dqopt =sb_dqopt (sb); +struct mem_dqinfo *info = &dqopt->info[type]; + + info->dqi_priv =kzalloc (sizeof (struct rb_root ),GFP_NOFS ); +if (!info->dqi_priv) +return -ENOMEM ; + + info->dqi_max_spc_limit =SHMEM_QUOTA_MAX_SPC_LIMIT ; + info->dqi_max_ino_limit =SHMEM_QUOTA_MAX_INO_LIMIT ; + + info->dqi_bgrace =SHMEM_MAX_DQ_TIME ; + info->dqi_igrace =SHMEM_MAX_IQ_TIME ; + info->dqi_flags =0 ; + +return 0 ; +} + +static int shmem_write_file_info (struct super_block *sb,int type) +{ +/* There is no real quota file, nothing to do */ +return 0 ; +} + +/* +* Free all the quota_id entries in the rb tree and rb_root. +*/ +static int shmem_free_file_info (struct super_block *sb,int type) +{ +struct mem_dqinfo *info = &sb_dqopt (sb)->info[type]; +struct rb_root *root = info->dqi_priv; +struct quota_id *entry; +struct rb_node *node; + + info->dqi_priv =NULL ; + node =rb_first (root); +while (node) { + entry = rb_entry(node, struct quota_id, node); + node =rb_next (&entry->node); + +rb_erase (&entry->node, root); +kfree (entry); + } + +kfree (root); +return 0 ; +} + +static int shmem_get_next_id (struct super_block *sb,struct kqid *qid) +{ +struct mem_dqinfo *info =sb_dqinfo (sb, qid->type); +struct rb_node *node = ((struct rb_root *)info->dqi_priv)->rb_node; +qid_t id =from_kqid (&init_user_ns, *qid); +struct quota_info *dqopt =sb_dqopt (sb); +struct quota_id *entry =NULL ; +int ret =0 ; + +if (!sb_has_quota_active (sb, qid->type)) +return -ESRCH ; + +down_read (&dqopt->dqio_sem); +while (node) { + entry = rb_entry(node, struct quota_id, node); + +if (id < entry->id) + node = node->rb_left; +else if (id > entry->id) + node = node->rb_right; +else + goto got_next_id; + } + +if (!entry) { + ret = -ENOENT ; + goto out_unlock; + } + +if (id > entry->id) { + node =rb_next (&entry->node); +if (!node) { + ret = -ENOENT ; + goto out_unlock; + } + entry = rb_entry(node, struct quota_id, node); + } + +got_next_id: + *qid =make_kqid (&init_user_ns, qid->type, entry->id); +out_unlock: +up_read (&dqopt->dqio_sem); +return ret; +} + +/* +* Load dquot with limits from existing entry, or create the new entry if +* it does not exist. +*/ +static int shmem_acquire_dquot (struct dquot *dquot) +{ +struct mem_dqinfo *info =sb_dqinfo (dquot->dq_sb, dquot->dq_id.type); +struct rb_node **n = &((struct rb_root *)info->dqi_priv)->rb_node; +struct rb_node *parent =NULL , *new_node =NULL ; +struct quota_id *new_entry, *entry; +qid_t id =from_kqid (&init_user_ns, dquot->dq_id); +struct quota_info *dqopt =sb_dqopt (dquot->dq_sb); +int ret =0 ; + +mutex_lock (&dquot->dq_lock); + +down_write (&dqopt->dqio_sem); +while (*n) { + parent = *n; + entry = rb_entry(parent, struct quota_id, node); + +if (id < entry->id) + n = &(*n)->rb_left; +else if (id > entry->id) + n = &(*n)->rb_right; +else + goto found; + } + +/* We don't have entry for this id yet, create it */ + new_entry =kzalloc (sizeof (struct quota_id ),GFP_NOFS ); +if (!new_entry) { + ret = -ENOMEM ; + goto out_unlock; + } + + new_entry->id = id; + new_node = &new_entry->node; +rb_link_node (new_node, parent, n); +rb_insert_color (new_node, (struct rb_root *)info->dqi_priv); + entry = new_entry; + +found: +/* Load the stored limits from the tree */ +spin_lock (&dquot->dq_dqb_lock); + dquot->dq_dqb.dqb_bhardlimit = entry->bhardlimit; + dquot->dq_dqb.dqb_bsoftlimit = entry->bsoftlimit; + dquot->dq_dqb.dqb_ihardlimit = entry->ihardlimit; + dquot->dq_dqb.dqb_isoftlimit = entry->isoftlimit; + +if (!dquot->dq_dqb.dqb_bhardlimit && + !dquot->dq_dqb.dqb_bsoftlimit && + !dquot->dq_dqb.dqb_ihardlimit && + !dquot->dq_dqb.dqb_isoftlimit) +set_bit (DQ_FAKE_B , &dquot->dq_flags); +spin_unlock (&dquot->dq_dqb_lock); + +/* Make sure flags update is visible after dquot has been filled */ +smp_mb__before_atomic (); +set_bit (DQ_ACTIVE_B , &dquot->dq_flags); +out_unlock: +up_write (&dqopt->dqio_sem); +mutex_unlock (&dquot->dq_lock); +return ret; +} + +/* +* Store limits from dquot in the tree unless it's fake. If it is fake +* remove the id from the tree since there is no useful information in +* there. +*/ +static int shmem_release_dquot (struct dquot *dquot) +{ +struct mem_dqinfo *info =sb_dqinfo (dquot->dq_sb, dquot->dq_id.type); +struct rb_node *node = ((struct rb_root *)info->dqi_priv)->rb_node; +qid_t id =from_kqid (&init_user_ns, dquot->dq_id); +struct quota_info *dqopt =sb_dqopt (dquot->dq_sb); +struct quota_id *entry =NULL ; + +mutex_lock (&dquot->dq_lock); +/* Check whether we are not racing with some other dqget() */ +if (dquot_is_busy (dquot)) + goto out_dqlock; + +down_write (&dqopt->dqio_sem); +while (node) { + entry = rb_entry(node, struct quota_id, node); + +if (id < entry->id) + node = node->rb_left; +else if (id > entry->id) + node = node->rb_right; +else + goto found; + } + +/* We should always find the entry in the rb tree */ +WARN_ONCE (1 ,"quota id %u from dquot %p, not in rb tree!\n" , id, dquot); +up_write (&dqopt->dqio_sem); +mutex_unlock (&dquot->dq_lock); +return -ENOENT ; + +found: +if (test_bit (DQ_FAKE_B , &dquot->dq_flags)) { +/* Remove entry from the tree */ +rb_erase (&entry->node, info->dqi_priv); +kfree (entry); + }else { +/* Store the limits in the tree */ +spin_lock (&dquot->dq_dqb_lock); + entry->bhardlimit = dquot->dq_dqb.dqb_bhardlimit; + entry->bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit; + entry->ihardlimit = dquot->dq_dqb.dqb_ihardlimit; + entry->isoftlimit = dquot->dq_dqb.dqb_isoftlimit; +spin_unlock (&dquot->dq_dqb_lock); + } + +clear_bit (DQ_ACTIVE_B , &dquot->dq_flags); +up_write (&dqopt->dqio_sem); + +out_dqlock: +mutex_unlock (&dquot->dq_lock); +return 0 ; +} + +int shmem_mark_dquot_dirty (struct dquot *dquot) +{ +return 0 ; +} + +int shmem_dquot_write_info (struct super_block *sb,int type) +{ +return 0 ; +} + +static const struct quota_format_ops shmem_format_ops = { + .check_quota_file = shmem_check_quota_file, + .read_file_info = shmem_read_file_info, + .write_file_info = shmem_write_file_info, + .free_file_info = shmem_free_file_info, +}; + +struct quota_format_type shmem_quota_format = { + .qf_fmt_id =QFMT_SHMEM , + .qf_ops = &shmem_format_ops, + .qf_owner =THIS_MODULE +}; + +const struct dquot_operations shmem_quota_operations = { + .acquire_dquot = shmem_acquire_dquot, + .release_dquot = shmem_release_dquot, + .alloc_dquot = dquot_alloc, + .destroy_dquot = dquot_destroy, + .write_info = shmem_dquot_write_info, + .mark_dirty = shmem_mark_dquot_dirty, + .get_next_id = shmem_get_next_id, +}; +#endif /* CONFIG_TMPFS_QUOTA */ Signature · 1 line
2.30.2
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
PATCH v2 5/6 shmem: quota support
In reply to [email protected]From: Lukas Czerner <[email protected]>
Now the basic infra-structure is in place, enable quota support for tmpfs.
This offers user and group quotas to tmpfs (project quotas will be added later). Also, as other filesystems, the tmpfs quota is not supported within user namespaces yet, so idmapping is not translated.
---
V2: - remove SHMEM_MAXQUOTAS definition from this patch, as it has
been added to a previous patch
- Don't use assignments within conditions
- add a call to is_quota_modification() within shmem_setattr()
- Add a note to the documentation describing user namespaces are not supported.
- Refactor shmem_get_inode, and add a new inlined version if TMPFS_QUOTA is not enabled. This prevents quota-specific helpers from being called when CONFIG_TMPFS_QUOTA is not set.
Documentation/filesystems/tmpfs.rst | 15 +++ include/linux/shmem_fs.h | 8 ++ mm/shmem.c | 180 ++++++++++++++++++++++++++-- 3 files changed, 195 insertions(+), 8 deletions(-)
Documentation/filesystems/tmpfs.rst +15 -0
@@ -86,6 +86,21 @@ use up all the memory on the machine; but enhances the scalability of that instance in a system with many CPUs making intensive use of it. +tmpfs also supports quota with the following mount options + +======== ============================================================= +quota User and group quota accounting and enforcement is enabled on + the mount. Tmpfs is using hidden system quota files that are + initialized on mount. +usrquota User quota accounting and enforcement is enabled on the + mount. +grpquota Group quota accounting and enforcement is enabled on the + mount. +======== ============================================================= + +Note that tmpfs quotas do not support user namespaces so no uid/gid +translation is done if quotas are enable inside user namespaces. + tmpfs has a mount option to set the NUMA memory allocation policy for all files in that instance (if CONFIG_NUMA is enabled) - which can be adjusted on the fly via 'mount -o remount ...'
include/linux/shmem_fs.h +8 -0
@@ -30,6 +30,9 @@ struct shmem_inode_info {atomic_t stop_eviction;/* hold when working on inode */ struct timespec64 i_crtime;/* file creation time */ unsigned int fsflags;/* flags for FS_IOC_[SG]ETFLAGS */ +#ifdef CONFIG_TMPFS_QUOTA +struct dquot *i_dquot[MAXQUOTAS ]; +#endif struct inode vfs_inode; };
@@ -175,4 +178,9 @@ extern int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, pmd_t *dst_pmd,#define SHMEM_QUOTA_MAX_SPC_LIMIT 0x7fffffffffffffffLL/* 2^63-1 */ #define SHMEM_QUOTA_MAX_INO_LIMIT 0x7fffffffffffffffLL +#ifdef CONFIG_TMPFS_QUOTA +extern const struct dquot_operations shmem_quota_operations; +extern struct quota_format_type shmem_quota_format; +#endif /* CONFIG_TMPFS_QUOTA */ +#endif
mm/shmem.c +172 -8
335 lines not on this page · raw message
Signature · 1 line
2.30.2
Re: [PATCH V2 5/6] shmem: quota support
Quoted 10 lines · On Thu 20-04-23 10:03:58, [email protected] wrote:
From: Lukas Czerner <[email protected]>
Now the basic infra-structure is in place, enable quota support for tmpfs.
This offers user and group quotas to tmpfs (project quotas will be added later). Also, as other filesystems, the tmpfs quota is not supported within user namespaces yet, so idmapping is not translated.
Signed-off-by Lukas Czerner <[email protected]>Signed-off-by Carlos Maiolino <[email protected]>
I've found only one typo fix below. Otherwise feel free to add:
Quoted 12 lines
Documentation/filesystems/tmpfs.rst +4
-0@@ -86,6 +86,21 @@ use up all the memory on the machine; but enhances the scalability of that instance in a system with many CPUs making intensive use of it. +tmpfs also supports quota with the following mount options + +======== ============================================================= +quota User and group quota accounting and enforcement is enabled on
Documentation/filesystems/tmpfs.rst use up all the memory on the machine; but enhances the scalability of · line 102
Quoted 10 lines
Documentation/filesystems/tmpfs.rst +10
-0+ the mount. Tmpfs is using hidden system quota files that are + initialized on mount. +usrquota User quota accounting and enforcement is enabled on the + mount. +grpquota Group quota accounting and enforcement is enabled on the + mount. +======== ============================================================= + +Note that tmpfs quotas do not support user namespaces so no uid/gid +translation is done if quotas are enable inside user namespaces.
^^ enabled
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
PATCH v2 6/6 Add default quota limit mount options
In reply to [email protected]From: Lukas Czerner <[email protected]>
Allow system administrator to set default global quota limits at tmpfs mount time.
--- Documentation/filesystems/tmpfs.rst | 34 ++++++++++---- include/linux/shmem_fs.h | 8 ++++ mm/shmem.c | 69 +++++++++++++++++++++++++++++ mm/shmem_quota.c | 9 ++++ 4 files changed, 111 insertions(+), 9 deletions(-)
Documentation/filesystems/tmpfs.rst +25 -9
@@ -88,15 +88,31 @@ that instance in a system with many CPUs making intensive use of it. tmpfs also supports quota with the following mount options -======== ============================================================= -quota User and group quota accounting and enforcement is enabled on - the mount. Tmpfs is using hidden system quota files that are - initialized on mount. -usrquota User quota accounting and enforcement is enabled on the - mount. -grpquota Group quota accounting and enforcement is enabled on the - mount. -======== ============================================================= +======================== ================================================= +quota User and group quota accounting and enforcement + is enabled on the mount. Tmpfs is using hidden + system quota files that are initialized on mount. +usrquota User quota accounting and enforcement is enabled + on the mount. +grpquota Group quota accounting and enforcement is enabled + on the mount. +usrquota_block_hardlimit Set global user quota block hard limit. +usrquota_inode_hardlimit Set global user quota inode hard limit. +grpquota_block_hardlimit Set global group quota block hard limit. +grpquota_inode_hardlimit Set global group quota inode hard limit. +======================== ================================================= + +None of the quota related mount options can be set or changed on remount. + +Quota limit parameters accept a suffix k, m or g for kilo, mega and giga +and can't be changed on remount. Default global quota limits are taking +effect for any and all user/group/project except root the first time the +quota entry for user/group/project id is being accessed - typically the +first time an inode with a particular id ownership is being created after +the mount. In other words, instead of the limits being initialized to zero, +they are initialized with the particular value provided with these mount +options. The limits can be changed for any user/group id at any time as it +normally can. Note that tmpfs quotas do not support user namespaces so no uid/gid translation is done if quotas are enable inside user namespaces.
include/linux/shmem_fs.h +8 -0
@@ -41,6 +41,13 @@ struct shmem_inode_info { (FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL | FS_NOATIME_FL)#define SHMEM_FL_INHERITED (FS_NODUMP_FL | FS_NOATIME_FL) +struct shmem_quota_limits { +qsize_t usrquota_bhardlimit;/* Default user quota block hard limit */ +qsize_t usrquota_ihardlimit;/* Default user quota inode hard limit */ +qsize_t grpquota_bhardlimit;/* Default group quota block hard limit */ +qsize_t grpquota_ihardlimit;/* Default group quota inode hard limit */ +}; +struct shmem_sb_info {unsigned long max_blocks;/* How many blocks are allowed */ struct percpu_counter used_blocks;/* How many are allocated */
@@ -58,6 +65,7 @@ struct shmem_sb_info {spinlock_t shrinklist_lock;/* Protects shrinklist */ struct list_head shrinklist;/* List of shinkable inodes */ unsigned long shrinklist_len;/* Length of shrinklist */ +struct shmem_quota_limits qlimits;/* Default quota limits */ };static inline struct shmem_inode_info *SHMEM_I (struct inode *inode)
mm/shmem.c +69 -0
105 lines not on this page · raw message
mm/shmem_quota.c +9 -0
@@ -166,6 +166,7 @@ static int shmem_acquire_dquot(struct dquot *dquot) {struct mem_dqinfo *info =sb_dqinfo (dquot->dq_sb, dquot->dq_id.type);struct rb_node **n = &((struct rb_root *)info->dqi_priv)->rb_node; +struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info;struct rb_node *parent =NULL , *new_node =NULL ;struct quota_id *new_entry, *entry;qid_t id =from_kqid (&init_user_ns, dquot->dq_id);
@@ -195,6 +196,14 @@ static int shmem_acquire_dquot(struct dquot *dquot) } new_entry->id = id; +if (dquot->dq_id.type ==USRQUOTA ) { + new_entry->bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; + }else if (dquot->dq_id.type ==GRPQUOTA ) { + new_entry->bhardlimit = sbinfo->qlimits.grpquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.grpquota_ihardlimit; + } + new_node = &new_entry->node;rb_link_node (new_node, parent, n);rb_insert_color (new_node, (struct rb_root *)info->dqi_priv);
Signature · 1 line
2.30.2
Jan Kara
reviewerRe: [PATCH 6/6] Add default quota limit mount options
Quoted 45 lines · On Thu 20-04-23 10:03:59, [email protected] wrote:
From: Lukas Czerner <[email protected]>
Allow system administrator to set default global quota limits at tmpfs mount time.
Signed-off-by Lukas Czerner <[email protected]>Signed-off-by Carlos Maiolino <[email protected]>--- Documentation/filesystems/tmpfs.rst | 34 ++++++++++---- include/linux/shmem_fs.h | 8 ++++ mm/shmem.c | 69 +++++++++++++++++++++++++++++ mm/shmem_quota.c | 9 ++++ 4 files changed, 111 insertions(+), 9 deletions(-)Documentation/filesystems/tmpfs.rst +14
-9@@ -88,15 +88,31 @@ that instance in a system with many CPUs making intensive use of it. tmpfs also supports quota with the following mount options -======== ============================================================= -quota User and group quota accounting and enforcement is enabled on - the mount. Tmpfs is using hidden system quota files that are - initialized on mount. -usrquota User quota accounting and enforcement is enabled on the - mount. -grpquota Group quota accounting and enforcement is enabled on the - mount. -======== ============================================================= +======================== ================================================= +quota User and group quota accounting and enforcement + is enabled on the mount. Tmpfs is using hidden + system quota files that are initialized on mount. +usrquota User quota accounting and enforcement is enabled + on the mount. +grpquota Group quota accounting and enforcement is enabled + on the mount. +usrquota_block_hardlimit Set global user quota block hard limit. +usrquota_inode_hardlimit Set global user quota inode hard limit. +grpquota_block_hardlimit Set global group quota block hard limit. +grpquota_inode_hardlimit Set global group quota inode hard limit. +======================== ================================================= +
Documentation/filesystems/tmpfs.rst that instance in a system with many CPUs making intensive use of it. · line 114
Quoted 10 lines
Documentation/filesystems/tmpfs.rst +10
-0+None of the quota related mount options can be set or changed on remount. + +Quota limit parameters accept a suffix k, m or g for kilo, mega and giga +and can't be changed on remount. Default global quota limits are taking +effect for any and all user/group/project except root the first time the +quota entry for user/group/project id is being accessed - typically the +first time an inode with a particular id ownership is being created after +the mount. In other words, instead of the limits being initialized to zero, +they are initialized with the particular value provided with these mount +options. The limits can be changed for any user/group id at any time as it
^^ they
Documentation/filesystems/tmpfs.rst that instance in a system with many CPUs making intensive use of it. · line 115
Quoted 1 line
Documentation/filesystems/tmpfs.rst +1
-0+normally can.
^^^ can be
Quoted 4 lines
Documentation/filesystems/tmpfs.rst +0
-0@@ -3714,6 +3723,50 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) ctx->seen |= SHMEM_SEEN_QUOTA; ctx->quota_types |= QTYPE_MASK_GRP; break;
Documentation/filesystems/tmpfs.rst static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) · line 3735
Quoted 10 lines
Documentation/filesystems/tmpfs.rst +10
-0+ case Opt_usrquota_block_hardlimit: + size = memparse(param->string, &rest); + if (*rest || !size) + goto bad_value; + if (size > SHMEM_QUOTA_MAX_SPC_LIMIT) + return invalfc(fc, + "User quota block hardlimit too large."); + ctx->qlimits.usrquota_bhardlimit = size; + ctx->seen |= SHMEM_SEEN_QUOTA; + ctx->quota_types |= QTYPE_MASK_USR;
So if I get it right, the intention here is that if usrquota_block_hardlimit=value option is used, it automatically enables user quota accounting and enforcement. I guess it is logical but it is not documented and I'd prefer to require explicit usrquota mount option to enable accounting & enforcement - it is then e.g. easier to parse mount options (in userspace) for finding out whether enforcement is enabled or not. Also I can imagine we would allow changing the default limits on remount but it isn't easy to enable quota accounting on remount etc.
Quoted 17 lines
mm/shmem_quota.c +2
-0@@ -166,6 +166,7 @@ static int shmem_acquire_dquot(struct dquot *dquot) {struct mem_dqinfo *info =sb_dqinfo (dquot->dq_sb, dquot->dq_id.type);struct rb_node **n = &((struct rb_root *)info->dqi_priv)->rb_node; +struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info;struct rb_node *parent =NULL , *new_node =NULL ;struct quota_id *new_entry, *entry;qid_t id =from_kqid (&init_user_ns, dquot->dq_id);@@ -195,6 +196,14 @@ static int shmem_acquire_dquot(struct dquot *dquot) } new_entry->id = id; +if (dquot->dq_id.type ==USRQUOTA ) {
mm/shmem_quota.c static int shmem_acquire_dquot(struct dquot *dquot) · line 209
Quoted 10 lines
mm/shmem_quota.c +7
-0+ new_entry->bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; + }else if (dquot->dq_id.type ==GRPQUOTA ) { + new_entry->bhardlimit = sbinfo->qlimits.grpquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.grpquota_ihardlimit; + } + new_node = &new_entry->node;rb_link_node (new_node, parent, n);rb_insert_color (new_node, (struct rb_root *)info->dqi_priv);
Maybe in shmem_dquot_release() when usage is 0 and limits are at default limits, we can free the structure?
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 6/6] Add default quota limit mount options
Quoted 57 lines · Jan Kara
Quoted 45 lines · On Thu 20-04-23 10:03:59, [email protected] wrote:
From: Lukas Czerner <[email protected]>
Allow system administrator to set default global quota limits at tmpfs mount time.
Signed-off-by Lukas Czerner <[email protected]>Signed-off-by Carlos Maiolino <[email protected]>--- Documentation/filesystems/tmpfs.rst | 34 ++++++++++---- include/linux/shmem_fs.h | 8 ++++ mm/shmem.c | 69 +++++++++++++++++++++++++++++ mm/shmem_quota.c | 9 ++++ 4 files changed, 111 insertions(+), 9 deletions(-)Documentation/filesystems/tmpfs.rst +14
-9@@ -88,15 +88,31 @@ that instance in a system with many CPUs making intensive use of it. tmpfs also supports quota with the following mount options -======== ============================================================= -quota User and group quota accounting and enforcement is enabled on - the mount. Tmpfs is using hidden system quota files that are - initialized on mount. -usrquota User quota accounting and enforcement is enabled on the - mount. -grpquota Group quota accounting and enforcement is enabled on the - mount. -======== ============================================================= +======================== ================================================= +quota User and group quota accounting and enforcement + is enabled on the mount. Tmpfs is using hidden + system quota files that are initialized on mount. +usrquota User quota accounting and enforcement is enabled + on the mount. +grpquota Group quota accounting and enforcement is enabled + on the mount. +usrquota_block_hardlimit Set global user quota block hard limit. +usrquota_inode_hardlimit Set global user quota inode hard limit. +grpquota_block_hardlimit Set global group quota block hard limit. +grpquota_inode_hardlimit Set global group quota inode hard limit. +======================== ================================================= +Documentation/filesystems/tmpfs.rst that instance in a system with many CPUs making intensive use of it. · line 114
Quoted 10 lines
Documentation/filesystems/tmpfs.rst +10
-0+None of the quota related mount options can be set or changed on remount. + +Quota limit parameters accept a suffix k, m or g for kilo, mega and giga +and can't be changed on remount. Default global quota limits are taking +effect for any and all user/group/project except root the first time the +quota entry for user/group/project id is being accessed - typically the +first time an inode with a particular id ownership is being created after +the mount. In other words, instead of the limits being initialized to zero, +they are initialized with the particular value provided with these mount +options. The limits can be changed for any user/group id at any time as it^^ they
Quoted 3 lines
Documentation/filesystems/tmpfs.rst that instance in a system with many CPUs making intensive use of it. · line 115
Quoted 1 line
Documentation/filesystems/tmpfs.rst +1
-0+normally can.^^^ can be
Thanks!
Quoted 15 lines
Quoted 4 lines
Documentation/filesystems/tmpfs.rst +0
-0@@ -3714,6 +3723,50 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) ctx->seen |= SHMEM_SEEN_QUOTA; ctx->quota_types |= QTYPE_MASK_GRP; break;Documentation/filesystems/tmpfs.rst static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) · line 3735
Quoted 10 lines
Documentation/filesystems/tmpfs.rst +10
-0+ case Opt_usrquota_block_hardlimit: + size = memparse(param->string, &rest); + if (*rest || !size) + goto bad_value; + if (size > SHMEM_QUOTA_MAX_SPC_LIMIT) + return invalfc(fc, + "User quota block hardlimit too large."); + ctx->qlimits.usrquota_bhardlimit = size; + ctx->seen |= SHMEM_SEEN_QUOTA; + ctx->quota_types |= QTYPE_MASK_USR;
Quoted 7 lines
So if I get it right, the intention here is that if usrquota_block_hardlimit=value option is used, it automatically enables user quota accounting and enforcement. I guess it is logical but it is not documented and I'd prefer to require explicit usrquota mount option to enable accounting & enforcement - it is then e.g. easier to parse mount options (in userspace) for finding out whether enforcement is enabled or not.
Hmmm, I think I see what you mean. I can make usrquota_block_hardlimit options to not actually set the quota flag on quota_types, so this should be explicitly set by usrquota/grpquota options. Does that work for you?
Quoted 3 lines
Also I can imagine we would allow changing the default limits on remount but it isn't easy to enable quota accounting on remount etc.
hmm, yes, maybe enabling default limits to be changed on remount isn't a big deal, once the quota is already enabled, so everything is already in place.
Quoted 15 lines
Quoted 15 lines
mm/shmem_quota.c +1
-0@@ -166,6 +166,7 @@ static int shmem_acquire_dquot(struct dquot *dquot) {struct mem_dqinfo *info =sb_dqinfo (dquot->dq_sb, dquot->dq_id.type);struct rb_node **n = &((struct rb_root *)info->dqi_priv)->rb_node; +struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info;struct rb_node *parent =NULL , *new_node =NULL ;struct quota_id *new_entry, *entry;qid_t id =from_kqid (&init_user_ns, dquot->dq_id);@@ -195,6 +196,14 @@ static int shmem_acquire_dquot(struct dquot *dquot) }
Quoted 15 lines
mm/shmem_quota.c static int shmem_acquire_dquot(struct dquot *dquot) · line 209
Quoted 12 lines
mm/shmem_quota.c +8
-0new_entry->id = id; +if (dquot->dq_id.type ==USRQUOTA ) { + new_entry->bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; + }else if (dquot->dq_id.type ==GRPQUOTA ) { + new_entry->bhardlimit = sbinfo->qlimits.grpquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.grpquota_ihardlimit; + } + new_node = &new_entry->node;rb_link_node (new_node, parent, n);rb_insert_color (new_node, (struct rb_root *)info->dqi_priv);Maybe in
shmem_dquot_release()when usage is 0 and limits are at default limits, we can free the structure?
hmmm, which struct are you talking about? quota_id? As we do for DQ_FAKE?
Quoted 5 lines
Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR
Signature · 1 line
Carlos Maiolino
Jan Kara
reviewerRe: [PATCH 6/6] Add default quota limit mount options
Quoted 25 lines · Carlos Maiolino
Quoted 16 lines · Jan Kara
Quoted 4 lines · On Thu 20-04-23 10:03:59, [email protected] wrote:
mm/shmem.c +0
-0@@ -3714,6 +3723,50 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) ctx->seen |=SHMEM_SEEN_QUOTA ; ctx->quota_types |=QTYPE_MASK_GRP ;break ;mm/shmem.c static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) · line 3735
Quoted 10 lines
mm/shmem.c +10
-0+case Opt_usrquota_block_hardlimit: + size =memparse (param->string, &rest); +if (*rest || !size) + goto bad_value; +if (size >SHMEM_QUOTA_MAX_SPC_LIMIT ) +return invalfc (fc, +"User quota block hardlimit too large." ); + ctx->qlimits.usrquota_bhardlimit = size; + ctx->seen |=SHMEM_SEEN_QUOTA ; + ctx->quota_types |=QTYPE_MASK_USR ;Quoted 7 lines
So if I get it right, the intention here is that if usrquota_block_hardlimit=value option is used, it automatically enables user quota accounting and enforcement. I guess it is logical but it is not documented and I'd prefer to require explicit usrquota mount option to enable accounting & enforcement - it is then e.g. easier to parse mount options (in userspace) for finding out whether enforcement is enabled or not.
Quoted 3 lines
Hmmm, I think I see what you mean. I can make
usrquota_block_hardlimitoptions to not actually set the quota flag onquota_types, so this should be explicitly set by usrquota/grpquota options. Does that work for you?
Yes, works for me!
Quoted 6 lines
Quoted 3 lines
Also I can imagine we would allow changing the default limits on remount but it isn't easy to enable quota accounting on remount etc.
hmm, yes, maybe enabling default limits to be changed on remount isn't a big deal, once the quota is already enabled, so everything is already in place.
Exactly. I don't say you have to do it now as I don't think that is super useful. But if there's a demand we can easily do it.
Quoted 31 lines
Quoted 15 lines
Quoted 15 lines
mm/shmem_quota.c +1
-0@@ -166,6 +166,7 @@ static int shmem_acquire_dquot(struct dquot *dquot) {struct mem_dqinfo *info =sb_dqinfo (dquot->dq_sb, dquot->dq_id.type);struct rb_node **n = &((struct rb_root *)info->dqi_priv)->rb_node; +struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info;struct rb_node *parent =NULL , *new_node =NULL ;struct quota_id *new_entry, *entry;qid_t id =from_kqid (&init_user_ns, dquot->dq_id);@@ -195,6 +196,14 @@ static int shmem_acquire_dquot(struct dquot *dquot) }Quoted 15 lines
mm/shmem_quota.c static int shmem_acquire_dquot(struct dquot *dquot) · line 209
Quoted 12 lines
mm/shmem_quota.c +8
-0new_entry->id = id; +if (dquot->dq_id.type ==USRQUOTA ) { + new_entry->bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; + }else if (dquot->dq_id.type ==GRPQUOTA ) { + new_entry->bhardlimit = sbinfo->qlimits.grpquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.grpquota_ihardlimit; + } + new_node = &new_entry->node;rb_link_node (new_node, parent, n);rb_insert_color (new_node, (struct rb_root *)info->dqi_priv);Maybe in
shmem_dquot_release()when usage is 0 and limits are at default limits, we can free the structure?
Quoted 1 line
hmmm, which struct are you talking about?
quota_id? As we do forDQ_FAKE?
Yes.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 6/6] Add default quota limit mount options
Quoted 26 lines · Jan Kara
Quoted 25 lines · Carlos Maiolino
Quoted 23 lines · Jan Kara
Quoted 4 lines · On Thu 20-04-23 10:03:59, [email protected] wrote:
mm/shmem.c +0
-0@@ -3714,6 +3723,50 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) ctx->seen |=SHMEM_SEEN_QUOTA ; ctx->quota_types |=QTYPE_MASK_GRP ;break ;mm/shmem.c static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) · line 3735
Quoted 10 lines
mm/shmem.c +10
-0+case Opt_usrquota_block_hardlimit: + size =memparse (param->string, &rest); +if (*rest || !size) + goto bad_value; +if (size >SHMEM_QUOTA_MAX_SPC_LIMIT ) +return invalfc (fc, +"User quota block hardlimit too large." ); + ctx->qlimits.usrquota_bhardlimit = size; + ctx->seen |=SHMEM_SEEN_QUOTA ; + ctx->quota_types |=QTYPE_MASK_USR ;So if I get it right, the intention here is that if usrquota_block_hardlimit=value option is used, it automatically enables user quota accounting and enforcement. I guess it is logical but it is not documented and I'd prefer to require explicit usrquota mount option to enable accounting & enforcement - it is then e.g. easier to parse mount options (in userspace) for finding out whether enforcement is enabled or not.
Quoted 5 lines
Quoted 3 lines
Hmmm, I think I see what you mean. I can make
usrquota_block_hardlimitoptions to not actually set the quota flag onquota_types, so this should be explicitly set by usrquota/grpquota options. Does that work for you?Yes, works for me!
Great!
Quoted 10 lines
Quoted 7 lines
Quoted 4 lines
Also I can imagine we would allow changing the default limits on remount but it isn't easy to enable quota accounting on remount etc.
hmm, yes, maybe enabling default limits to be changed on remount isn't a big deal, once the quota is already enabled, so everything is already in place.
Exactly. I don't say you have to do it now as I don't think that is super useful. But if there's a demand we can easily do it.
Sounds good :) If you don't mind, I'd postpone it, as I am planning to add prjquotas to it later, I can add it to the same series, just to avoid adding more review overhead to this one.
Quoted 40 lines
Quoted 32 lines
Quoted 16 lines
Quoted 16 lines
mm/shmem_quota.c +1
-0@@ -166,6 +166,7 @@ static int shmem_acquire_dquot(struct dquot *dquot) {struct mem_dqinfo *info =sb_dqinfo (dquot->dq_sb, dquot->dq_id.type);struct rb_node **n = &((struct rb_root *)info->dqi_priv)->rb_node; +struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info;struct rb_node *parent =NULL , *new_node =NULL ;struct quota_id *new_entry, *entry;qid_t id =from_kqid (&init_user_ns, dquot->dq_id);@@ -195,6 +196,14 @@ static int shmem_acquire_dquot(struct dquot *dquot) }Quoted 15 lines
mm/shmem_quota.c static int shmem_acquire_dquot(struct dquot *dquot) · line 209
Quoted 12 lines
mm/shmem_quota.c +8
-0new_entry->id = id; +if (dquot->dq_id.type ==USRQUOTA ) { + new_entry->bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; + }else if (dquot->dq_id.type ==GRPQUOTA ) { + new_entry->bhardlimit = sbinfo->qlimits.grpquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.grpquota_ihardlimit; + } + new_node = &new_entry->node;rb_link_node (new_node, parent, n);rb_insert_color (new_node, (struct rb_root *)info->dqi_priv);Maybe in
shmem_dquot_release()when usage is 0 and limits are at default limits, we can free the structure?Quoted 1 line
hmmm, which struct are you talking about?
quota_id? As we do forDQ_FAKE?Yes.
Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR
Signature · 1 line
Carlos Maiolino
PATCH v3 6/6 Add default quota limit mount options
In reply to [email protected]From: Lukas Czerner <[email protected]>
Allow system administrator to set default global quota limits at tmpfs mount time.
---
V3:
- Do not enable quotas if usrquota or grpquota options are not explicitly set.
- shmem_release_dquot() now also free up quota_info if there is no usage and there are no custom limits set.
V2:
- Fix documentation phrasing
Documentation/filesystems/tmpfs.rst | 34 +++++++++++----- include/linux/shmem_fs.h | 8 ++++ mm/shmem.c | 61 +++++++++++++++++++++++++++++ mm/shmem_quota.c | 34 +++++++++++++++- 4 files changed, 127 insertions(+), 10 deletions(-)
Documentation/filesystems/tmpfs.rst +25 -9
@@ -88,15 +88,31 @@ that instance in a system with many CPUs making intensive use of it. tmpfs also supports quota with the following mount options -======== ============================================================= -quota User and group quota accounting and enforcement is enabled on - the mount. Tmpfs is using hidden system quota files that are - initialized on mount. -usrquota User quota accounting and enforcement is enabled on the - mount. -grpquota Group quota accounting and enforcement is enabled on the - mount. -======== ============================================================= +======================== ================================================= +quota User and group quota accounting and enforcement + is enabled on the mount. Tmpfs is using hidden + system quota files that are initialized on mount. +usrquota User quota accounting and enforcement is enabled + on the mount. +grpquota Group quota accounting and enforcement is enabled + on the mount. +usrquota_block_hardlimit Set global user quota block hard limit. +usrquota_inode_hardlimit Set global user quota inode hard limit. +grpquota_block_hardlimit Set global group quota block hard limit. +grpquota_inode_hardlimit Set global group quota inode hard limit. +======================== ================================================= + +None of the quota related mount options can be set or changed on remount. + +Quota limit parameters accept a suffix k, m or g for kilo, mega and giga +and can't be changed on remount. Default global quota limits are taking +effect for any and all user/group/project except root the first time the +quota entry for user/group/project id is being accessed - typically the +first time an inode with a particular id ownership is being created after +the mount. In other words, instead of the limits being initialized to zero, +they are initialized with the particular value provided with these mount +options. The limits can be changed for any user/group id at any time as they +normally can be. Note that tmpfs quotas do not support user namespaces so no uid/gid translation is done if quotas are enabled inside user namespaces.
include/linux/shmem_fs.h +8 -0
@@ -41,6 +41,13 @@ struct shmem_inode_info { (FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL | FS_NOATIME_FL)#define SHMEM_FL_INHERITED (FS_NODUMP_FL | FS_NOATIME_FL) +struct shmem_quota_limits { +qsize_t usrquota_bhardlimit;/* Default user quota block hard limit */ +qsize_t usrquota_ihardlimit;/* Default user quota inode hard limit */ +qsize_t grpquota_bhardlimit;/* Default group quota block hard limit */ +qsize_t grpquota_ihardlimit;/* Default group quota inode hard limit */ +}; +struct shmem_sb_info {unsigned long max_blocks;/* How many blocks are allowed */ struct percpu_counter used_blocks;/* How many are allocated */
@@ -58,6 +65,7 @@ struct shmem_sb_info {spinlock_t shrinklist_lock;/* Protects shrinklist */ struct list_head shrinklist;/* List of shinkable inodes */ unsigned long shrinklist_len;/* Length of shrinklist */ +struct shmem_quota_limits qlimits;/* Default quota limits */ };static inline struct shmem_inode_info *SHMEM_I (struct inode *inode)
mm/shmem.c +61 -0
97 lines not on this page · raw message
mm/shmem_quota.c +33 -1
@@ -166,6 +166,7 @@ static int shmem_acquire_dquot(struct dquot *dquot) {struct mem_dqinfo *info =sb_dqinfo (dquot->dq_sb, dquot->dq_id.type);struct rb_node **n = &((struct rb_root *)info->dqi_priv)->rb_node; +struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info;struct rb_node *parent =NULL , *new_node =NULL ;struct quota_id *new_entry, *entry;qid_t id =from_kqid (&init_user_ns, dquot->dq_id);
@@ -195,6 +196,14 @@ static int shmem_acquire_dquot(struct dquot *dquot) } new_entry->id = id; +if (dquot->dq_id.type ==USRQUOTA ) { + new_entry->bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; + }else if (dquot->dq_id.type ==GRPQUOTA ) { + new_entry->bhardlimit = sbinfo->qlimits.grpquota_bhardlimit; + new_entry->ihardlimit = sbinfo->qlimits.grpquota_ihardlimit; + } + new_node = &new_entry->node;rb_link_node (new_node, parent, n);rb_insert_color (new_node, (struct rb_root *)info->dqi_priv);
@@ -224,6 +233,29 @@ static int shmem_acquire_dquot(struct dquot *dquot)return ret; } +static bool shmem_is_empty_dquot (struct dquot *dquot) +{ +struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info; +qsize_t bhardlimit; +qsize_t ihardlimit; + +if (dquot->dq_id.type ==USRQUOTA ) { + bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; + }else if (dquot->dq_id.type ==GRPQUOTA ) { + bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; + } + +if (test_bit (DQ_FAKE_B , &dquot->dq_flags) || + (dquot->dq_dqb.dqb_curspace ==0 && + dquot->dq_dqb.dqb_curinodes ==0 && + dquot->dq_dqb.dqb_bhardlimit == bhardlimit && + dquot->dq_dqb.dqb_ihardlimit == ihardlimit)) +return true; + +return false; +}/* * Store limits from dquot in the tree unless it's fake. If it is fake * remove the id from the tree since there is no useful information in
@@ -261,7 +293,7 @@ static int shmem_release_dquot(struct dquot *dquot)return -ENOENT ; found: -if (test_bit (DQ_FAKE_B , &dquot->dq_flags)) { +if (shmem_is_empty_dquot (dquot)) {/* Remove entry from the tree */ rb_erase (&entry->node, info->dqi_priv);kfree (entry);
Signature · 1 line
2.30.2
Jan Kara
reviewerRe: [PATCH V3 6/6] Add default quota limit mount options
Quoted 7 lines · On Tue 25-04-23 13:57:25, [email protected] wrote:
From: Lukas Czerner <[email protected]>
Allow system administrator to set default global quota limits at tmpfs mount time.
Signed-off-by Lukas Czerner <[email protected]>Signed-off-by Carlos Maiolino <[email protected]>
...
Quoted 6 lines
mm/shmem_quota.c +2
-0@@ -224,6 +233,29 @@ static int shmem_acquire_dquot(struct dquot *dquot)return ret; } +static bool shmem_is_empty_dquot (struct dquot *dquot) +{
mm/shmem_quota.c static int shmem_acquire_dquot(struct dquot *dquot) · line 247
Quoted 10 lines
mm/shmem_quota.c +10
-0+struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info; +qsize_t bhardlimit; +qsize_t ihardlimit; + +if (dquot->dq_id.type ==USRQUOTA ) { + bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; + }else if (dquot->dq_id.type ==GRPQUOTA ) { + bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + ihardlimit = sbinfo->qlimits.usrquota_ihardlimit;
There should be grpquota in the above two lines. Otherwise the patch looks good to me.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH V3 6/6] Add default quota limit mount options
Quoted 13 lines · Jan Kara
Quoted 7 lines · On Tue 25-04-23 13:57:25, [email protected] wrote:
From: Lukas Czerner <[email protected]>
Allow system administrator to set default global quota limits at tmpfs mount time.
Signed-off-by Lukas Czerner <[email protected]>Signed-off-by Carlos Maiolino <[email protected]>...
Quoted 4 lines
mm/shmem_quota.c +0
-0@@ -224,6 +233,29 @@ static int shmem_acquire_dquot(struct dquot *dquot)return ret; }
Quoted 15 lines
mm/shmem_quota.c static int shmem_acquire_dquot(struct dquot *dquot) · line 247
Quoted 12 lines
mm/shmem_quota.c +12
-0+static bool shmem_is_empty_dquot (struct dquot *dquot) +{ +struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info; +qsize_t bhardlimit; +qsize_t ihardlimit; + +if (dquot->dq_id.type ==USRQUOTA ) { + bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; + }else if (dquot->dq_id.type ==GRPQUOTA ) { + bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + ihardlimit = sbinfo->qlimits.usrquota_ihardlimit;There should be grpquota in the above two lines. Otherwise the patch looks good to me.
Uff, sorry, copy/paste mistake. Can I add your RwB once I fix it? Or do you want me to send a V4?
Quoted 6 lines
Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR
Signature · 1 line
Carlos Maiolino
Re: [PATCH V3 6/6] Add default quota limit mount options
Quoted 30 lines · Carlos Maiolino
Quoted 13 lines · Jan Kara
Quoted 7 lines · On Tue 25-04-23 13:57:25, [email protected] wrote:
From: Lukas Czerner <[email protected]>
Allow system administrator to set default global quota limits at tmpfs mount time.
Signed-off-by Lukas Czerner <[email protected]>Signed-off-by Carlos Maiolino <[email protected]>...
Quoted 4 lines
mm/shmem_quota.c +0
-0@@ -224,6 +233,29 @@ static int shmem_acquire_dquot(struct dquot *dquot)return ret; }Quoted 15 lines
mm/shmem_quota.c static int shmem_acquire_dquot(struct dquot *dquot) · line 247
Quoted 12 lines
mm/shmem_quota.c +12
-0+static bool shmem_is_empty_dquot (struct dquot *dquot) +{ +struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info; +qsize_t bhardlimit; +qsize_t ihardlimit; + +if (dquot->dq_id.type ==USRQUOTA ) { + bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + ihardlimit = sbinfo->qlimits.usrquota_ihardlimit; + }else if (dquot->dq_id.type ==GRPQUOTA ) { + bhardlimit = sbinfo->qlimits.usrquota_bhardlimit; + ihardlimit = sbinfo->qlimits.usrquota_ihardlimit;There should be grpquota in the above two lines. Otherwise the patch looks good to me.
Quoted 2 lines
Uff, sorry, copy/paste mistake. Can I add your RwB once I fix it? Or do you want me to send a V4?
Yes, once fixed feel free to add:
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR