Message ID | 20230403084759.884681-6-cem@kernel.org (mailing list archive) |
---|---|
State | Mainlined, archived |
Headers | show |
Series | shmem: Add user and group quota support for tmpfs | expand |
Hi, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on linus/master] [also build test WARNING on v6.3-rc5] [cannot apply to akpm-mm/mm-everything next-20230403] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/cem-kernel-org/shmem-make-shmem_inode_acct_block-return-error/20230403-165022 patch link: https://lore.kernel.org/r/20230403084759.884681-6-cem%40kernel.org patch subject: [PATCH 5/6] shmem: quota support config: i386-allnoconfig (https://download.01.org/0day-ci/archive/20230403/202304032216.0SXl7l2X-lkp@intel.com/config) compiler: gcc-11 (Debian 11.3.0-8) 11.3.0 reproduce (this is a W=1 build): # https://github.com/intel-lab-lkp/linux/commit/e060b9e86fd92d5e87f5b0c447e4bc610a3d3bbe git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review cem-kernel-org/shmem-make-shmem_inode_acct_block-return-error/20230403-165022 git checkout e060b9e86fd92d5e87f5b0c447e4bc610a3d3bbe # save the config file mkdir build_dir && cp config build_dir/.config make W=1 O=build_dir ARCH=i386 olddefconfig make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> | Link: https://lore.kernel.org/oe-kbuild-all/202304032216.0SXl7l2X-lkp@intel.com/ All warnings (new ones prefixed by >>): mm/shmem.c: In function 'shmem_init': >> mm/shmem.c:4252:1: warning: label 'out3' defined but not used [-Wunused-label] 4252 | out3: | ^~~~ vim +/out3 +4252 mm/shmem.c 4224 4225 error = register_filesystem(&shmem_fs_type); 4226 if (error) { 4227 pr_err("Could not register tmpfs\n"); 4228 goto out2; 4229 } 4230 4231 shm_mnt = kern_mount(&shmem_fs_type); 4232 if (IS_ERR(shm_mnt)) { 4233 error = PTR_ERR(shm_mnt); 4234 pr_err("Could not kern_mount tmpfs\n"); 4235 goto out1; 4236 } 4237 4238 #ifdef CONFIG_TRANSPARENT_HUGEPAGE 4239 if (has_transparent_hugepage() && shmem_huge > SHMEM_HUGE_DENY) 4240 SHMEM_SB(shm_mnt->mnt_sb)->huge = shmem_huge; 4241 else 4242 shmem_huge = SHMEM_HUGE_NEVER; /* just in case it was patched */ 4243 #endif 4244 return; 4245 4246 out1: 4247 unregister_filesystem(&shmem_fs_type); 4248 out2: 4249 #ifdef CONFIG_TMPFS_QUOTA 4250 unregister_quota_format(&shmem_quota_format); 4251 #endif > 4252 out3: 4253 shmem_destroy_inodecache(); 4254 shm_mnt = ERR_PTR(error); 4255 } 4256
On Mon, Apr 03, 2023 at 10:47:58AM +0200, cem@kernel.org wrote: > From: Lukas Czerner <lczerner@redhat.com> > > Now the basic infra-structure is in place, enable quota support for tmpfs. > > Signed-off-by: Lukas Czerner <lczerner@redhat.com> > Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com> > --- > Documentation/filesystems/tmpfs.rst | 12 +++ > include/linux/shmem_fs.h | 9 ++ > mm/shmem.c | 162 ++++++++++++++++++++++++++-- > 3 files changed, 174 insertions(+), 9 deletions(-) > > diff --git a/Documentation/filesystems/tmpfs.rst b/Documentation/filesystems/tmpfs.rst > index 0408c245785e3..3f8d89bb7e1a5 100644 > --- a/Documentation/filesystems/tmpfs.rst > +++ b/Documentation/filesystems/tmpfs.rst > @@ -86,6 +86,18 @@ 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. > +======== ============================================================= > + > 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 ...' > diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h > index cf38381bdb4c1..3e7e18726feb5 100644 > --- a/include/linux/shmem_fs.h > +++ b/include/linux/shmem_fs.h > @@ -26,6 +26,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]; Why allocate three dquot pointers here... > +#endif > struct inode vfs_inode; > }; > > @@ -171,4 +174,10 @@ 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 > +#define SHMEM_MAXQUOTAS 2 ...when you're only allowing user and group quotas? (Or: Why not allow project quotas? But that's outside the scope you defined.) --D > +extern const struct dquot_operations shmem_quota_operations; > +extern struct quota_format_type shmem_quota_format; > +#endif /* CONFIG_TMPFS_QUOTA */ > + > #endif > diff --git a/mm/shmem.c b/mm/shmem.c > index 88e13930fc013..d7529c883eaf5 100644 > --- a/mm/shmem.c > +++ b/mm/shmem.c > @@ -79,6 +79,7 @@ static struct vfsmount *shm_mnt; > #include <linux/userfaultfd_k.h> > #include <linux/rmap.h> > #include <linux/uuid.h> > +#include <linux/quotaops.h> > > #include <linux/uaccess.h> > > @@ -116,10 +117,12 @@ struct shmem_options { > bool full_inums; > int huge; > int seen; > + unsigned short quota_types; > #define SHMEM_SEEN_BLOCKS 1 > #define SHMEM_SEEN_INODES 2 > #define SHMEM_SEEN_HUGE 4 > #define SHMEM_SEEN_INUMS 8 > +#define SHMEM_SEEN_QUOTA 16 > }; > > #ifdef CONFIG_TMPFS > @@ -211,8 +214,11 @@ static inline int shmem_inode_acct_block(struct inode *inode, long pages) > if (percpu_counter_compare(&sbinfo->used_blocks, > sbinfo->max_blocks - pages) > 0) > goto unacct; > + if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) > + goto unacct; > percpu_counter_add(&sbinfo->used_blocks, pages); > - } > + } else if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) > + goto unacct; > > return 0; > > @@ -226,6 +232,8 @@ static inline void shmem_inode_unacct_blocks(struct inode *inode, long pages) > struct shmem_inode_info *info = SHMEM_I(inode); > struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); > > + dquot_free_block_nodirty(inode, pages); > + > if (sbinfo->max_blocks) > percpu_counter_sub(&sbinfo->used_blocks, pages); > shmem_unacct_blocks(info->flags, pages); > @@ -254,6 +262,47 @@ bool vma_is_shmem(struct vm_area_struct *vma) > static LIST_HEAD(shmem_swaplist); > static DEFINE_MUTEX(shmem_swaplist_mutex); > > +#ifdef CONFIG_TMPFS_QUOTA > + > +static int shmem_enable_quotas(struct super_block *sb, > + unsigned short quota_types) > +{ > + int type, err = 0; > + > + sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NOLIST_DIRTY; > + for (type = 0; type < SHMEM_MAXQUOTAS; type++) { > + if (!(quota_types & (1 << type))) > + continue; > + err = dquot_load_quota_sb(sb, type, QFMT_SHMEM, > + DQUOT_USAGE_ENABLED | > + DQUOT_LIMITS_ENABLED); > + if (err) > + goto out_err; > + } > + return 0; > + > +out_err: > + pr_warn("tmpfs: failed to enable quota tracking (type=%d, err=%d)\n", > + type, err); > + for (type--; type >= 0; type--) > + dquot_quota_off(sb, type); > + return err; > +} > + > +static void shmem_disable_quotas(struct super_block *sb) > +{ > + int type; > + > + for (type = 0; type < SHMEM_MAXQUOTAS; type++) > + dquot_quota_off(sb, type); > +} > + > +static struct dquot **shmem_get_dquots(struct inode *inode) > +{ > + return SHMEM_I(inode)->i_dquot; > +} > +#endif /* CONFIG_TMPFS_QUOTA */ > + > /* > * shmem_reserve_inode() performs bookkeeping to reserve a shmem inode, and > * produces a novel ino for the newly allocated inode. > @@ -360,7 +409,6 @@ static void shmem_recalc_inode(struct inode *inode) > freed = info->alloced - info->swapped - inode->i_mapping->nrpages; > if (freed > 0) { > info->alloced -= freed; > - inode->i_blocks -= freed * BLOCKS_PER_PAGE; > shmem_inode_unacct_blocks(inode, freed); > } > } > @@ -378,7 +426,6 @@ bool shmem_charge(struct inode *inode, long pages) > > spin_lock_irqsave(&info->lock, flags); > info->alloced += pages; > - inode->i_blocks += pages * BLOCKS_PER_PAGE; > shmem_recalc_inode(inode); > spin_unlock_irqrestore(&info->lock, flags); > > @@ -394,7 +441,6 @@ void shmem_uncharge(struct inode *inode, long pages) > > spin_lock_irqsave(&info->lock, flags); > info->alloced -= pages; > - inode->i_blocks -= pages * BLOCKS_PER_PAGE; > shmem_recalc_inode(inode); > spin_unlock_irqrestore(&info->lock, flags); > > @@ -1133,6 +1179,15 @@ static int shmem_setattr(struct mnt_idmap *idmap, > } > } > > + /* Transfer quota accounting */ > + if (i_uid_needs_update(idmap, attr, inode) || > + i_gid_needs_update(idmap, attr,inode)) { > + error = dquot_transfer(idmap, inode, attr); > + > + if (error) > + return error; > + } > + > setattr_copy(idmap, inode, attr); > if (attr->ia_valid & ATTR_MODE) > error = posix_acl_chmod(idmap, dentry, inode->i_mode); > @@ -1178,7 +1233,9 @@ static void shmem_evict_inode(struct inode *inode) > simple_xattrs_free(&info->xattrs); > WARN_ON(inode->i_blocks); > shmem_free_inode(inode->i_sb); > + dquot_free_inode(inode); > clear_inode(inode); > + dquot_drop(inode); > } > > static int shmem_find_swap_entries(struct address_space *mapping, > @@ -1975,7 +2032,6 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index, > > spin_lock_irq(&info->lock); > info->alloced += folio_nr_pages(folio); > - inode->i_blocks += (blkcnt_t)BLOCKS_PER_PAGE << folio_order(folio); > shmem_recalc_inode(inode); > spin_unlock_irq(&info->lock); > alloced = true; > @@ -2346,9 +2402,10 @@ static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags) > #define shmem_initxattrs NULL > #endif > > -static 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) > +static struct inode *shmem_get_inode_noquota(struct mnt_idmap *idmap, > + struct super_block *sb, > + struct inode *dir, umode_t mode, > + dev_t dev, unsigned long flags) > { > struct inode *inode; > struct shmem_inode_info *info; > @@ -2422,6 +2479,37 @@ static struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_block > return inode; > } > > +static 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) > +{ > + int err; > + struct inode *inode; > + > + inode = shmem_get_inode_noquota(idmap, sb, dir, mode, dev, flags); > + if (IS_ERR(inode)) > + return inode; > + > + err = dquot_initialize(inode); > + if (err) > + goto errout; > + > + err = dquot_alloc_inode(inode); > + if (err) { > + dquot_drop(inode); > + goto errout; > + } > + return inode; > + > +errout: > + inode->i_flags |= S_NOQUOTA; > + iput(inode); > + shmem_free_inode(sb); > + if (err) > + return ERR_PTR(err); > + return NULL; > +} > + > #ifdef CONFIG_USERFAULTFD > int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, > pmd_t *dst_pmd, > @@ -2525,7 +2613,6 @@ int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, > > spin_lock_irq(&info->lock); > info->alloced++; > - inode->i_blocks += BLOCKS_PER_PAGE; > shmem_recalc_inode(inode); > spin_unlock_irq(&info->lock); > > @@ -3372,6 +3459,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) > > static const struct inode_operations shmem_short_symlink_operations = { > .getattr = shmem_getattr, > + .setattr = shmem_setattr, > .get_link = simple_get_link, > #ifdef CONFIG_TMPFS_XATTR > .listxattr = shmem_listxattr, > @@ -3380,6 +3468,7 @@ static const struct inode_operations shmem_short_symlink_operations = { > > static const struct inode_operations shmem_symlink_inode_operations = { > .getattr = shmem_getattr, > + .setattr = shmem_setattr, > .get_link = shmem_get_link, > #ifdef CONFIG_TMPFS_XATTR > .listxattr = shmem_listxattr, > @@ -3478,6 +3567,9 @@ enum shmem_param { > Opt_uid, > Opt_inode32, > Opt_inode64, > + Opt_quota, > + Opt_usrquota, > + Opt_grpquota, > }; > > static const struct constant_table shmem_param_enums_huge[] = { > @@ -3499,6 +3591,11 @@ const struct fs_parameter_spec shmem_fs_parameters[] = { > fsparam_u32 ("uid", Opt_uid), > fsparam_flag ("inode32", Opt_inode32), > fsparam_flag ("inode64", Opt_inode64), > +#ifdef CONFIG_TMPFS_QUOTA > + fsparam_flag ("quota", Opt_quota), > + fsparam_flag ("usrquota", Opt_usrquota), > + fsparam_flag ("grpquota", Opt_grpquota), > +#endif > {} > }; > > @@ -3582,6 +3679,18 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) > ctx->full_inums = true; > ctx->seen |= SHMEM_SEEN_INUMS; > break; > + case Opt_quota: > + ctx->seen |= SHMEM_SEEN_QUOTA; > + ctx->quota_types |= (QTYPE_MASK_USR | QTYPE_MASK_GRP); > + break; > + case Opt_usrquota: > + ctx->seen |= SHMEM_SEEN_QUOTA; > + ctx->quota_types |= QTYPE_MASK_USR; > + break; > + case Opt_grpquota: > + ctx->seen |= SHMEM_SEEN_QUOTA; > + ctx->quota_types |= QTYPE_MASK_GRP; > + break; > } > return 0; > > @@ -3681,6 +3790,12 @@ static int shmem_reconfigure(struct fs_context *fc) > goto out; > } > > + if (ctx->seen & SHMEM_SEEN_QUOTA && > + !sb_any_quota_loaded(fc->root->d_sb)) { > + err = "Cannot enable quota on remount"; > + goto out; > + } > + > if (ctx->seen & SHMEM_SEEN_HUGE) > sbinfo->huge = ctx->huge; > if (ctx->seen & SHMEM_SEEN_INUMS) > @@ -3763,6 +3878,9 @@ static void shmem_put_super(struct super_block *sb) > { > struct shmem_sb_info *sbinfo = SHMEM_SB(sb); > > +#ifdef CONFIG_TMPFS_QUOTA > + shmem_disable_quotas(sb); > +#endif > free_percpu(sbinfo->ino_batch); > percpu_counter_destroy(&sbinfo->used_blocks); > mpol_put(sbinfo->mpol); > @@ -3841,6 +3959,17 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) > #endif > uuid_gen(&sb->s_uuid); > > +#ifdef CONFIG_TMPFS_QUOTA > + if (ctx->seen & SHMEM_SEEN_QUOTA) { > + sb->dq_op = &shmem_quota_operations; > + sb->s_qcop = &dquot_quotactl_sysfile_ops; > + sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP; > + > + if (shmem_enable_quotas(sb, ctx->quota_types)) > + goto failed; > + } > +#endif /* CONFIG_TMPFS_QUOTA */ > + > inode = shmem_get_inode(&nop_mnt_idmap, sb, NULL, S_IFDIR | sbinfo->mode, 0, > VM_NORESERVE); > if (IS_ERR(inode)) { > @@ -4016,6 +4145,9 @@ static const struct super_operations shmem_ops = { > #ifdef CONFIG_TMPFS > .statfs = shmem_statfs, > .show_options = shmem_show_options, > +#endif > +#ifdef CONFIG_TMPFS_QUOTA > + .get_dquots = shmem_get_dquots, > #endif > .evict_inode = shmem_evict_inode, > .drop_inode = generic_delete_inode, > @@ -4082,6 +4214,14 @@ void __init shmem_init(void) > > shmem_init_inodecache(); > > +#ifdef CONFIG_TMPFS_QUOTA > + error = register_quota_format(&shmem_quota_format); > + if (error < 0) { > + pr_err("Could not register quota format\n"); > + goto out3; > + } > +#endif > + > error = register_filesystem(&shmem_fs_type); > if (error) { > pr_err("Could not register tmpfs\n"); > @@ -4106,6 +4246,10 @@ void __init shmem_init(void) > out1: > unregister_filesystem(&shmem_fs_type); > out2: > +#ifdef CONFIG_TMPFS_QUOTA > + unregister_quota_format(&shmem_quota_format); > +#endif > +out3: > shmem_destroy_inodecache(); > shm_mnt = ERR_PTR(error); > } > -- > 2.30.2 >
Hi, kernel test robot noticed the following build warnings: [auto build test WARNING on linus/master] [also build test WARNING on v6.3-rc5] [cannot apply to akpm-mm/mm-everything next-20230403] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/cem-kernel-org/shmem-make-shmem_inode_acct_block-return-error/20230403-165022 patch link: https://lore.kernel.org/r/20230403084759.884681-6-cem%40kernel.org patch subject: [PATCH 5/6] shmem: quota support config: hexagon-randconfig-r045-20230403 (https://download.01.org/0day-ci/archive/20230404/202304040523.Z5vLqfkr-lkp@intel.com/config) compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project 67409911353323ca5edf2049ef0df54132fa1ca7) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/e060b9e86fd92d5e87f5b0c447e4bc610a3d3bbe git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review cem-kernel-org/shmem-make-shmem_inode_acct_block-return-error/20230403-165022 git checkout e060b9e86fd92d5e87f5b0c447e4bc610a3d3bbe # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> | Link: https://lore.kernel.org/oe-kbuild-all/202304040523.Z5vLqfkr-lkp@intel.com/ All warnings (new ones prefixed by >>): In file included from mm/shmem.c:29: In file included from include/linux/pagemap.h:11: In file included from include/linux/highmem.h:12: In file included from include/linux/hardirq.h:11: In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1: In file included from include/asm-generic/hardirq.h:17: In file included from include/linux/irq.h:20: In file included from include/linux/io.h:13: In file included from arch/hexagon/include/asm/io.h:334: include/asm-generic/io.h:547:31: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] val = __raw_readb(PCI_IOBASE + addr); ~~~~~~~~~~ ^ include/asm-generic/io.h:560:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] val = __le16_to_cpu((__le16 __force)__raw_readw(PCI_IOBASE + addr)); ~~~~~~~~~~ ^ include/uapi/linux/byteorder/little_endian.h:37:51: note: expanded from macro '__le16_to_cpu' #define __le16_to_cpu(x) ((__force __u16)(__le16)(x)) ^ In file included from mm/shmem.c:29: In file included from include/linux/pagemap.h:11: In file included from include/linux/highmem.h:12: In file included from include/linux/hardirq.h:11: In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1: In file included from include/asm-generic/hardirq.h:17: In file included from include/linux/irq.h:20: In file included from include/linux/io.h:13: In file included from arch/hexagon/include/asm/io.h:334: include/asm-generic/io.h:573:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr)); ~~~~~~~~~~ ^ include/uapi/linux/byteorder/little_endian.h:35:51: note: expanded from macro '__le32_to_cpu' #define __le32_to_cpu(x) ((__force __u32)(__le32)(x)) ^ In file included from mm/shmem.c:29: In file included from include/linux/pagemap.h:11: In file included from include/linux/highmem.h:12: In file included from include/linux/hardirq.h:11: In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1: In file included from include/asm-generic/hardirq.h:17: In file included from include/linux/irq.h:20: In file included from include/linux/io.h:13: In file included from arch/hexagon/include/asm/io.h:334: include/asm-generic/io.h:584:33: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] __raw_writeb(value, PCI_IOBASE + addr); ~~~~~~~~~~ ^ include/asm-generic/io.h:594:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] __raw_writew((u16 __force)cpu_to_le16(value), PCI_IOBASE + addr); ~~~~~~~~~~ ^ include/asm-generic/io.h:604:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] __raw_writel((u32 __force)cpu_to_le32(value), PCI_IOBASE + addr); ~~~~~~~~~~ ^ >> mm/shmem.c:4252:1: warning: unused label 'out3' [-Wunused-label] out3: ^~~~~ mm/shmem.c:1534:20: warning: unused function 'shmem_show_mpol' [-Wunused-function] static inline void shmem_show_mpol(struct seq_file *seq, struct mempolicy *mpol) ^ 8 warnings generated. vim +/out3 +4252 mm/shmem.c 4224 4225 error = register_filesystem(&shmem_fs_type); 4226 if (error) { 4227 pr_err("Could not register tmpfs\n"); 4228 goto out2; 4229 } 4230 4231 shm_mnt = kern_mount(&shmem_fs_type); 4232 if (IS_ERR(shm_mnt)) { 4233 error = PTR_ERR(shm_mnt); 4234 pr_err("Could not kern_mount tmpfs\n"); 4235 goto out1; 4236 } 4237 4238 #ifdef CONFIG_TRANSPARENT_HUGEPAGE 4239 if (has_transparent_hugepage() && shmem_huge > SHMEM_HUGE_DENY) 4240 SHMEM_SB(shm_mnt->mnt_sb)->huge = shmem_huge; 4241 else 4242 shmem_huge = SHMEM_HUGE_NEVER; /* just in case it was patched */ 4243 #endif 4244 return; 4245 4246 out1: 4247 unregister_filesystem(&shmem_fs_type); 4248 out2: 4249 #ifdef CONFIG_TMPFS_QUOTA 4250 unregister_quota_format(&shmem_quota_format); 4251 #endif > 4252 out3: 4253 shmem_destroy_inodecache(); 4254 shm_mnt = ERR_PTR(error); 4255 } 4256
Hello, kernel test robot noticed "BUG:kernel_NULL_pointer_dereference,address" on: commit: e060b9e86fd92d5e87f5b0c447e4bc610a3d3bbe ("[PATCH 5/6] shmem: quota support") url: https://github.com/intel-lab-lkp/linux/commits/cem-kernel-org/shmem-make-shmem_inode_acct_block-return-error/20230403-165022 base: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git 7e364e56293bb98cae1b55fd835f5991c4e96e7d patch link: https://lore.kernel.org/all/20230403084759.884681-6-cem@kernel.org/ patch subject: [PATCH 5/6] shmem: quota support in testcase: boot compiler: gcc-11 test machine: qemu-system-x86_64 -enable-kvm -cpu SandyBridge -smp 2 -m 16G (please refer to attached dmesg/kmsg for entire log/backtrace) +---------------------------------------------+------------+------------+ | | 2dc93cb54d | e060b9e86f | +---------------------------------------------+------------+------------+ | boot_successes | 14 | 0 | | boot_failures | 0 | 12 | | BUG:kernel_NULL_pointer_dereference,address | 0 | 12 | | Oops:#[##] | 0 | 12 | | Kernel_panic-not_syncing:Fatal_exception | 0 | 12 | +---------------------------------------------+------------+------------+ If you fix the issue, kindly add following tag | Reported-by: kernel test robot <yujie.liu@intel.com> | Link: https://lore.kernel.org/oe-lkp/202304041417.1199f918-yujie.liu@intel.com [ 8.196316][ T58] BUG: kernel NULL pointer dereference, address: 00000000 [ 8.196987][ T58] #PF: supervisor read access in kernel mode [ 8.197478][ T58] #PF: error_code(0x0000) - not-present page [ 8.197962][ T58] *pde = 00000000 [ 8.198265][ T58] Oops: 0000 [#1] [ 8.198562][ T58] CPU: 0 PID: 58 Comm: rm Not tainted 6.3.0-rc5-00005-ge060b9e86fd9 #1 b33e4695914080c2d2a6f223361e2009396ebd64 [ 8.199506][ T58] EIP: 0x0 [ 8.199759][ T58] Code: Unable to access opcode bytes at 0xffffffd6. Code starting with the faulting instruction =========================================== [ 8.200289][ T58] EAX: ee6a6388 EBX: ee6a6388 ECX: 00000007 EDX: c162e7c0 [ 8.200854][ T58] ESI: ee5b89c0 EDI: ee6a6348 EBP: ee6adecc ESP: ee6adec8 [ 8.201416][ T58] DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068 EFLAGS: 00010246 [ 8.202029][ T58] CR0: 80050033 CR2: ffffffd6 CR3: 2e5d5000 CR4: 00040690 [ 8.207818][ T58] DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000 [ 8.208397][ T58] DR6: fffe0ff0 DR7: 00000400 [ 8.208781][ T58] Call Trace: [ 8.209049][ T58] i_dquot (fs/quota/dquot.c:940) [ 8.209367][ T58] dquot_drop (fs/quota/dquot.c:1610 fs/quota/dquot.c:1593) [ 8.209709][ T58] shmem_evict_inode (mm/shmem.c:1239 (discriminator 3)) [ 8.210105][ T58] ? _raw_spin_unlock (kernel/locking/spinlock.c:187) [ 8.210498][ T58] evict (fs/inode.c:665) [ 8.210806][ T58] iput (fs/inode.c:1776) [ 8.211107][ T58] do_unlinkat (fs/namei.c:4325) [ 8.211481][ T58] __ia32_sys_unlink (fs/namei.c:4362) [ 8.211879][ T58] __do_fast_syscall_32 (arch/x86/entry/common.c:112 arch/x86/entry/common.c:178) [ 8.212299][ T58] do_fast_syscall_32 (arch/x86/entry/common.c:203) [ 8.212698][ T58] do_SYSENTER_32 (arch/x86/entry/common.c:247) [ 8.213076][ T58] entry_SYSENTER_32 (arch/x86/entry/entry_32.S:867) [ 8.213473][ T58] EIP: 0xb7ed356d [ 8.213773][ T58] Code: c4 01 10 03 03 74 c0 01 10 05 03 74 b8 01 10 06 03 74 b4 01 10 07 03 74 b0 01 10 08 03 74 d8 01 00 51 52 55 89 e5 0f 34 cd 80 <5d> 5a 59 c3 90 90 90 90 8d 76 00 58 b8 77 00 00 00 cd 80 90 8d 76 All code ======== 0: c4 01 10 03 (bad) 4: 03 74 c0 01 add 0x1(%rax,%rax,8),%esi 8: 10 05 03 74 b8 01 adc %al,0x1b87403(%rip) # 0x1b87411 e: 10 06 adc %al,(%rsi) 10: 03 74 b4 01 add 0x1(%rsp,%rsi,4),%esi 14: 10 07 adc %al,(%rdi) 16: 03 74 b0 01 add 0x1(%rax,%rsi,4),%esi 1a: 10 08 adc %cl,(%rax) 1c: 03 74 d8 01 add 0x1(%rax,%rbx,8),%esi 20: 00 51 52 add %dl,0x52(%rcx) 23: 55 push %rbp 24: 89 e5 mov %esp,%ebp 26: 0f 34 sysenter 28: cd 80 int $0x80 2a:* 5d pop %rbp <-- trapping instruction 2b: 5a pop %rdx 2c: 59 pop %rcx 2d: c3 ret 2e: 90 nop 2f: 90 nop 30: 90 nop 31: 90 nop 32: 8d 76 00 lea 0x0(%rsi),%esi 35: 58 pop %rax 36: b8 77 00 00 00 mov $0x77,%eax 3b: cd 80 int $0x80 3d: 90 nop 3e: 8d .byte 0x8d 3f: 76 .byte 0x76 Code starting with the faulting instruction =========================================== 0: 5d pop %rbp 1: 5a pop %rdx 2: 59 pop %rcx 3: c3 ret 4: 90 nop 5: 90 nop 6: 90 nop 7: 90 nop 8: 8d 76 00 lea 0x0(%rsi),%esi b: 58 pop %rax c: b8 77 00 00 00 mov $0x77,%eax 11: cd 80 int $0x80 13: 90 nop 14: 8d .byte 0x8d 15: 76 .byte 0x76 [ 8.215315][ T58] EAX: ffffffda EBX: bfb99c97 ECX: bfb97fe0 EDX: 00000000 [ 8.215899][ T58] ESI: bfb9814c EDI: bfb99c91 EBP: bfb97fb8 ESP: bfb97f98 [ 8.216451][ T58] DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b EFLAGS: 00000282 [ 8.217056][ T58] Modules linked in: [ 8.217386][ T58] CR2: 0000000000000000 [ 8.217833][ T58] ---[ end trace 0000000000000000 ]--- [ 8.218341][ T58] EIP: 0x0 [ 8.218608][ T58] Code: Unable to access opcode bytes at 0xffffffd6. Code starting with the faulting instruction =========================================== To reproduce: # build kernel cd linux cp config-6.3.0-rc5-00005-ge060b9e86fd9 .config make HOSTCC=gcc-11 CC=gcc-11 ARCH=i386 olddefconfig prepare modules_prepare bzImage modules make HOSTCC=gcc-11 CC=gcc-11 ARCH=i386 INSTALL_MOD_PATH=<mod-install-dir> modules_install cd <mod-install-dir> find lib/ | cpio -o -H newc --quiet | gzip > modules.cgz git clone https://github.com/intel/lkp-tests.git cd lkp-tests bin/lkp qemu -k <bzImage> -m modules.cgz job-script # job-script is attached in this email # if come across any failure that blocks the test, # please remove ~/.lkp and /lkp dir to run from a clean state.
Hi. > > 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]; > > Why allocate three dquot pointers here... > > > +#endif > > struct inode vfs_inode; > > }; > > > > @@ -171,4 +174,10 @@ 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 > > +#define SHMEM_MAXQUOTAS 2 > > ...when you're only allowing user and group quotas? My bad, I should have used SHMEM_MAXQUOTAS to define the i_dquot > > (Or: Why not allow project quotas? But that's outside the scope you > defined.) This is indeed on my plan, which I want to do later, I want to deal with the 'avoid users to consume all memory' issue, then I want to add prjquotas here. I want to limit the scope of this series by now to avoid it snowballing with more and more features. > > --D > > > +extern const struct dquot_operations shmem_quota_operations; > > +extern struct quota_format_type shmem_quota_format; > > +#endif /* CONFIG_TMPFS_QUOTA */ > > + > > #endif > > diff --git a/mm/shmem.c b/mm/shmem.c > > index 88e13930fc013..d7529c883eaf5 100644 > > --- a/mm/shmem.c > > +++ b/mm/shmem.c > > @@ -79,6 +79,7 @@ static struct vfsmount *shm_mnt; > > #include <linux/userfaultfd_k.h> > > #include <linux/rmap.h> > > #include <linux/uuid.h> > > +#include <linux/quotaops.h> > > > > #include <linux/uaccess.h> > > > > @@ -116,10 +117,12 @@ struct shmem_options { > > bool full_inums; > > int huge; > > int seen; > > + unsigned short quota_types; > > #define SHMEM_SEEN_BLOCKS 1 > > #define SHMEM_SEEN_INODES 2 > > #define SHMEM_SEEN_HUGE 4 > > #define SHMEM_SEEN_INUMS 8 > > +#define SHMEM_SEEN_QUOTA 16 > > }; > > > > #ifdef CONFIG_TMPFS > > @@ -211,8 +214,11 @@ static inline int shmem_inode_acct_block(struct inode *inode, long pages) > > if (percpu_counter_compare(&sbinfo->used_blocks, > > sbinfo->max_blocks - pages) > 0) > > goto unacct; > > + if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) > > + goto unacct; > > percpu_counter_add(&sbinfo->used_blocks, pages); > > - } > > + } else if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) > > + goto unacct; > > > > return 0; > > > > @@ -226,6 +232,8 @@ static inline void shmem_inode_unacct_blocks(struct inode *inode, long pages) > > struct shmem_inode_info *info = SHMEM_I(inode); > > struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); > > > > + dquot_free_block_nodirty(inode, pages); > > + > > if (sbinfo->max_blocks) > > percpu_counter_sub(&sbinfo->used_blocks, pages); > > shmem_unacct_blocks(info->flags, pages); > > @@ -254,6 +262,47 @@ bool vma_is_shmem(struct vm_area_struct *vma) > > static LIST_HEAD(shmem_swaplist); > > static DEFINE_MUTEX(shmem_swaplist_mutex); > > > > +#ifdef CONFIG_TMPFS_QUOTA > > + > > +static int shmem_enable_quotas(struct super_block *sb, > > + unsigned short quota_types) > > +{ > > + int type, err = 0; > > + > > + sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NOLIST_DIRTY; > > + for (type = 0; type < SHMEM_MAXQUOTAS; type++) { > > + if (!(quota_types & (1 << type))) > > + continue; > > + err = dquot_load_quota_sb(sb, type, QFMT_SHMEM, > > + DQUOT_USAGE_ENABLED | > > + DQUOT_LIMITS_ENABLED); > > + if (err) > > + goto out_err; > > + } > > + return 0; > > + > > +out_err: > > + pr_warn("tmpfs: failed to enable quota tracking (type=%d, err=%d)\n", > > + type, err); > > + for (type--; type >= 0; type--) > > + dquot_quota_off(sb, type); > > + return err; > > +} > > + > > +static void shmem_disable_quotas(struct super_block *sb) > > +{ > > + int type; > > + > > + for (type = 0; type < SHMEM_MAXQUOTAS; type++) > > + dquot_quota_off(sb, type); > > +} > > + > > +static struct dquot **shmem_get_dquots(struct inode *inode) > > +{ > > + return SHMEM_I(inode)->i_dquot; > > +} > > +#endif /* CONFIG_TMPFS_QUOTA */ > > + > > /* > > * shmem_reserve_inode() performs bookkeeping to reserve a shmem inode, and > > * produces a novel ino for the newly allocated inode. > > @@ -360,7 +409,6 @@ static void shmem_recalc_inode(struct inode *inode) > > freed = info->alloced - info->swapped - inode->i_mapping->nrpages; > > if (freed > 0) { > > info->alloced -= freed; > > - inode->i_blocks -= freed * BLOCKS_PER_PAGE; > > shmem_inode_unacct_blocks(inode, freed); > > } > > } > > @@ -378,7 +426,6 @@ bool shmem_charge(struct inode *inode, long pages) > > > > spin_lock_irqsave(&info->lock, flags); > > info->alloced += pages; > > - inode->i_blocks += pages * BLOCKS_PER_PAGE; > > shmem_recalc_inode(inode); > > spin_unlock_irqrestore(&info->lock, flags); > > > > @@ -394,7 +441,6 @@ void shmem_uncharge(struct inode *inode, long pages) > > > > spin_lock_irqsave(&info->lock, flags); > > info->alloced -= pages; > > - inode->i_blocks -= pages * BLOCKS_PER_PAGE; > > shmem_recalc_inode(inode); > > spin_unlock_irqrestore(&info->lock, flags); > > > > @@ -1133,6 +1179,15 @@ static int shmem_setattr(struct mnt_idmap *idmap, > > } > > } > > > > + /* Transfer quota accounting */ > > + if (i_uid_needs_update(idmap, attr, inode) || > > + i_gid_needs_update(idmap, attr,inode)) { > > + error = dquot_transfer(idmap, inode, attr); > > + > > + if (error) > > + return error; > > + } > > + > > setattr_copy(idmap, inode, attr); > > if (attr->ia_valid & ATTR_MODE) > > error = posix_acl_chmod(idmap, dentry, inode->i_mode); > > @@ -1178,7 +1233,9 @@ static void shmem_evict_inode(struct inode *inode) > > simple_xattrs_free(&info->xattrs); > > WARN_ON(inode->i_blocks); > > shmem_free_inode(inode->i_sb); > > + dquot_free_inode(inode); > > clear_inode(inode); > > + dquot_drop(inode); > > } > > > > static int shmem_find_swap_entries(struct address_space *mapping, > > @@ -1975,7 +2032,6 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index, > > > > spin_lock_irq(&info->lock); > > info->alloced += folio_nr_pages(folio); > > - inode->i_blocks += (blkcnt_t)BLOCKS_PER_PAGE << folio_order(folio); > > shmem_recalc_inode(inode); > > spin_unlock_irq(&info->lock); > > alloced = true; > > @@ -2346,9 +2402,10 @@ static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags) > > #define shmem_initxattrs NULL > > #endif > > > > -static 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) > > +static struct inode *shmem_get_inode_noquota(struct mnt_idmap *idmap, > > + struct super_block *sb, > > + struct inode *dir, umode_t mode, > > + dev_t dev, unsigned long flags) > > { > > struct inode *inode; > > struct shmem_inode_info *info; > > @@ -2422,6 +2479,37 @@ static struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_block > > return inode; > > } > > > > +static 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) > > +{ > > + int err; > > + struct inode *inode; > > + > > + inode = shmem_get_inode_noquota(idmap, sb, dir, mode, dev, flags); > > + if (IS_ERR(inode)) > > + return inode; > > + > > + err = dquot_initialize(inode); > > + if (err) > > + goto errout; > > + > > + err = dquot_alloc_inode(inode); > > + if (err) { > > + dquot_drop(inode); > > + goto errout; > > + } > > + return inode; > > + > > +errout: > > + inode->i_flags |= S_NOQUOTA; > > + iput(inode); > > + shmem_free_inode(sb); > > + if (err) > > + return ERR_PTR(err); > > + return NULL; > > +} > > + > > #ifdef CONFIG_USERFAULTFD > > int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, > > pmd_t *dst_pmd, > > @@ -2525,7 +2613,6 @@ int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, > > > > spin_lock_irq(&info->lock); > > info->alloced++; > > - inode->i_blocks += BLOCKS_PER_PAGE; > > shmem_recalc_inode(inode); > > spin_unlock_irq(&info->lock); > > > > @@ -3372,6 +3459,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) > > > > static const struct inode_operations shmem_short_symlink_operations = { > > .getattr = shmem_getattr, > > + .setattr = shmem_setattr, > > .get_link = simple_get_link, > > #ifdef CONFIG_TMPFS_XATTR > > .listxattr = shmem_listxattr, > > @@ -3380,6 +3468,7 @@ static const struct inode_operations shmem_short_symlink_operations = { > > > > static const struct inode_operations shmem_symlink_inode_operations = { > > .getattr = shmem_getattr, > > + .setattr = shmem_setattr, > > .get_link = shmem_get_link, > > #ifdef CONFIG_TMPFS_XATTR > > .listxattr = shmem_listxattr, > > @@ -3478,6 +3567,9 @@ enum shmem_param { > > Opt_uid, > > Opt_inode32, > > Opt_inode64, > > + Opt_quota, > > + Opt_usrquota, > > + Opt_grpquota, > > }; > > > > static const struct constant_table shmem_param_enums_huge[] = { > > @@ -3499,6 +3591,11 @@ const struct fs_parameter_spec shmem_fs_parameters[] = { > > fsparam_u32 ("uid", Opt_uid), > > fsparam_flag ("inode32", Opt_inode32), > > fsparam_flag ("inode64", Opt_inode64), > > +#ifdef CONFIG_TMPFS_QUOTA > > + fsparam_flag ("quota", Opt_quota), > > + fsparam_flag ("usrquota", Opt_usrquota), > > + fsparam_flag ("grpquota", Opt_grpquota), > > +#endif > > {} > > }; > > > > @@ -3582,6 +3679,18 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) > > ctx->full_inums = true; > > ctx->seen |= SHMEM_SEEN_INUMS; > > break; > > + case Opt_quota: > > + ctx->seen |= SHMEM_SEEN_QUOTA; > > + ctx->quota_types |= (QTYPE_MASK_USR | QTYPE_MASK_GRP); > > + break; > > + case Opt_usrquota: > > + ctx->seen |= SHMEM_SEEN_QUOTA; > > + ctx->quota_types |= QTYPE_MASK_USR; > > + break; > > + case Opt_grpquota: > > + ctx->seen |= SHMEM_SEEN_QUOTA; > > + ctx->quota_types |= QTYPE_MASK_GRP; > > + break; > > } > > return 0; > > > > @@ -3681,6 +3790,12 @@ static int shmem_reconfigure(struct fs_context *fc) > > goto out; > > } > > > > + if (ctx->seen & SHMEM_SEEN_QUOTA && > > + !sb_any_quota_loaded(fc->root->d_sb)) { > > + err = "Cannot enable quota on remount"; > > + goto out; > > + } > > + > > if (ctx->seen & SHMEM_SEEN_HUGE) > > sbinfo->huge = ctx->huge; > > if (ctx->seen & SHMEM_SEEN_INUMS) > > @@ -3763,6 +3878,9 @@ static void shmem_put_super(struct super_block *sb) > > { > > struct shmem_sb_info *sbinfo = SHMEM_SB(sb); > > > > +#ifdef CONFIG_TMPFS_QUOTA > > + shmem_disable_quotas(sb); > > +#endif > > free_percpu(sbinfo->ino_batch); > > percpu_counter_destroy(&sbinfo->used_blocks); > > mpol_put(sbinfo->mpol); > > @@ -3841,6 +3959,17 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) > > #endif > > uuid_gen(&sb->s_uuid); > > > > +#ifdef CONFIG_TMPFS_QUOTA > > + if (ctx->seen & SHMEM_SEEN_QUOTA) { > > + sb->dq_op = &shmem_quota_operations; > > + sb->s_qcop = &dquot_quotactl_sysfile_ops; > > + sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP; > > + > > + if (shmem_enable_quotas(sb, ctx->quota_types)) > > + goto failed; > > + } > > +#endif /* CONFIG_TMPFS_QUOTA */ > > + > > inode = shmem_get_inode(&nop_mnt_idmap, sb, NULL, S_IFDIR | sbinfo->mode, 0, > > VM_NORESERVE); > > if (IS_ERR(inode)) { > > @@ -4016,6 +4145,9 @@ static const struct super_operations shmem_ops = { > > #ifdef CONFIG_TMPFS > > .statfs = shmem_statfs, > > .show_options = shmem_show_options, > > +#endif > > +#ifdef CONFIG_TMPFS_QUOTA > > + .get_dquots = shmem_get_dquots, > > #endif > > .evict_inode = shmem_evict_inode, > > .drop_inode = generic_delete_inode, > > @@ -4082,6 +4214,14 @@ void __init shmem_init(void) > > > > shmem_init_inodecache(); > > > > +#ifdef CONFIG_TMPFS_QUOTA > > + error = register_quota_format(&shmem_quota_format); > > + if (error < 0) { > > + pr_err("Could not register quota format\n"); > > + goto out3; > > + } > > +#endif > > + > > error = register_filesystem(&shmem_fs_type); > > if (error) { > > pr_err("Could not register tmpfs\n"); > > @@ -4106,6 +4246,10 @@ void __init shmem_init(void) > > out1: > > unregister_filesystem(&shmem_fs_type); > > out2: > > +#ifdef CONFIG_TMPFS_QUOTA > > + unregister_quota_format(&shmem_quota_format); > > +#endif > > +out3: > > shmem_destroy_inodecache(); > > shm_mnt = ERR_PTR(error); > > } > > -- > > 2.30.2 > >
On Tue, Apr 04, 2023 at 03:41:19PM +0200, Carlos Maiolino wrote: > Hi. > > > > 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]; > > > > Why allocate three dquot pointers here... > > > > > +#endif > > > struct inode vfs_inode; > > > }; > > > > > > @@ -171,4 +174,10 @@ 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 > > > +#define SHMEM_MAXQUOTAS 2 > > > > ...when you're only allowing user and group quotas? > > My bad, I should have used SHMEM_MAXQUOTAS to define the i_dquot > > > > > (Or: Why not allow project quotas? But that's outside the scope you > > defined.) > > This is indeed on my plan, which I want to do later, I want to deal with the > 'avoid users to consume all memory' issue, then I want to add prjquotas here. I > want to limit the scope of this series by now to avoid it snowballing with more > and more features. Ok. This all mostly looks fine to me ... to the extent that I know anything about tmpfs. ;) --D > > > > --D > > > > > +extern const struct dquot_operations shmem_quota_operations; > > > +extern struct quota_format_type shmem_quota_format; > > > +#endif /* CONFIG_TMPFS_QUOTA */ > > > + > > > #endif > > > diff --git a/mm/shmem.c b/mm/shmem.c > > > index 88e13930fc013..d7529c883eaf5 100644 > > > --- a/mm/shmem.c > > > +++ b/mm/shmem.c > > > @@ -79,6 +79,7 @@ static struct vfsmount *shm_mnt; > > > #include <linux/userfaultfd_k.h> > > > #include <linux/rmap.h> > > > #include <linux/uuid.h> > > > +#include <linux/quotaops.h> > > > > > > #include <linux/uaccess.h> > > > > > > @@ -116,10 +117,12 @@ struct shmem_options { > > > bool full_inums; > > > int huge; > > > int seen; > > > + unsigned short quota_types; > > > #define SHMEM_SEEN_BLOCKS 1 > > > #define SHMEM_SEEN_INODES 2 > > > #define SHMEM_SEEN_HUGE 4 > > > #define SHMEM_SEEN_INUMS 8 > > > +#define SHMEM_SEEN_QUOTA 16 > > > }; > > > > > > #ifdef CONFIG_TMPFS > > > @@ -211,8 +214,11 @@ static inline int shmem_inode_acct_block(struct inode *inode, long pages) > > > if (percpu_counter_compare(&sbinfo->used_blocks, > > > sbinfo->max_blocks - pages) > 0) > > > goto unacct; > > > + if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) > > > + goto unacct; > > > percpu_counter_add(&sbinfo->used_blocks, pages); > > > - } > > > + } else if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) > > > + goto unacct; > > > > > > return 0; > > > > > > @@ -226,6 +232,8 @@ static inline void shmem_inode_unacct_blocks(struct inode *inode, long pages) > > > struct shmem_inode_info *info = SHMEM_I(inode); > > > struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); > > > > > > + dquot_free_block_nodirty(inode, pages); > > > + > > > if (sbinfo->max_blocks) > > > percpu_counter_sub(&sbinfo->used_blocks, pages); > > > shmem_unacct_blocks(info->flags, pages); > > > @@ -254,6 +262,47 @@ bool vma_is_shmem(struct vm_area_struct *vma) > > > static LIST_HEAD(shmem_swaplist); > > > static DEFINE_MUTEX(shmem_swaplist_mutex); > > > > > > +#ifdef CONFIG_TMPFS_QUOTA > > > + > > > +static int shmem_enable_quotas(struct super_block *sb, > > > + unsigned short quota_types) > > > +{ > > > + int type, err = 0; > > > + > > > + sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NOLIST_DIRTY; > > > + for (type = 0; type < SHMEM_MAXQUOTAS; type++) { > > > + if (!(quota_types & (1 << type))) > > > + continue; > > > + err = dquot_load_quota_sb(sb, type, QFMT_SHMEM, > > > + DQUOT_USAGE_ENABLED | > > > + DQUOT_LIMITS_ENABLED); > > > + if (err) > > > + goto out_err; > > > + } > > > + return 0; > > > + > > > +out_err: > > > + pr_warn("tmpfs: failed to enable quota tracking (type=%d, err=%d)\n", > > > + type, err); > > > + for (type--; type >= 0; type--) > > > + dquot_quota_off(sb, type); > > > + return err; > > > +} > > > + > > > +static void shmem_disable_quotas(struct super_block *sb) > > > +{ > > > + int type; > > > + > > > + for (type = 0; type < SHMEM_MAXQUOTAS; type++) > > > + dquot_quota_off(sb, type); > > > +} > > > + > > > +static struct dquot **shmem_get_dquots(struct inode *inode) > > > +{ > > > + return SHMEM_I(inode)->i_dquot; > > > +} > > > +#endif /* CONFIG_TMPFS_QUOTA */ > > > + > > > /* > > > * shmem_reserve_inode() performs bookkeeping to reserve a shmem inode, and > > > * produces a novel ino for the newly allocated inode. > > > @@ -360,7 +409,6 @@ static void shmem_recalc_inode(struct inode *inode) > > > freed = info->alloced - info->swapped - inode->i_mapping->nrpages; > > > if (freed > 0) { > > > info->alloced -= freed; > > > - inode->i_blocks -= freed * BLOCKS_PER_PAGE; > > > shmem_inode_unacct_blocks(inode, freed); > > > } > > > } > > > @@ -378,7 +426,6 @@ bool shmem_charge(struct inode *inode, long pages) > > > > > > spin_lock_irqsave(&info->lock, flags); > > > info->alloced += pages; > > > - inode->i_blocks += pages * BLOCKS_PER_PAGE; > > > shmem_recalc_inode(inode); > > > spin_unlock_irqrestore(&info->lock, flags); > > > > > > @@ -394,7 +441,6 @@ void shmem_uncharge(struct inode *inode, long pages) > > > > > > spin_lock_irqsave(&info->lock, flags); > > > info->alloced -= pages; > > > - inode->i_blocks -= pages * BLOCKS_PER_PAGE; > > > shmem_recalc_inode(inode); > > > spin_unlock_irqrestore(&info->lock, flags); > > > > > > @@ -1133,6 +1179,15 @@ static int shmem_setattr(struct mnt_idmap *idmap, > > > } > > > } > > > > > > + /* Transfer quota accounting */ > > > + if (i_uid_needs_update(idmap, attr, inode) || > > > + i_gid_needs_update(idmap, attr,inode)) { > > > + error = dquot_transfer(idmap, inode, attr); > > > + > > > + if (error) > > > + return error; > > > + } > > > + > > > setattr_copy(idmap, inode, attr); > > > if (attr->ia_valid & ATTR_MODE) > > > error = posix_acl_chmod(idmap, dentry, inode->i_mode); > > > @@ -1178,7 +1233,9 @@ static void shmem_evict_inode(struct inode *inode) > > > simple_xattrs_free(&info->xattrs); > > > WARN_ON(inode->i_blocks); > > > shmem_free_inode(inode->i_sb); > > > + dquot_free_inode(inode); > > > clear_inode(inode); > > > + dquot_drop(inode); > > > } > > > > > > static int shmem_find_swap_entries(struct address_space *mapping, > > > @@ -1975,7 +2032,6 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index, > > > > > > spin_lock_irq(&info->lock); > > > info->alloced += folio_nr_pages(folio); > > > - inode->i_blocks += (blkcnt_t)BLOCKS_PER_PAGE << folio_order(folio); > > > shmem_recalc_inode(inode); > > > spin_unlock_irq(&info->lock); > > > alloced = true; > > > @@ -2346,9 +2402,10 @@ static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags) > > > #define shmem_initxattrs NULL > > > #endif > > > > > > -static 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) > > > +static struct inode *shmem_get_inode_noquota(struct mnt_idmap *idmap, > > > + struct super_block *sb, > > > + struct inode *dir, umode_t mode, > > > + dev_t dev, unsigned long flags) > > > { > > > struct inode *inode; > > > struct shmem_inode_info *info; > > > @@ -2422,6 +2479,37 @@ static struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_block > > > return inode; > > > } > > > > > > +static 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) > > > +{ > > > + int err; > > > + struct inode *inode; > > > + > > > + inode = shmem_get_inode_noquota(idmap, sb, dir, mode, dev, flags); > > > + if (IS_ERR(inode)) > > > + return inode; > > > + > > > + err = dquot_initialize(inode); > > > + if (err) > > > + goto errout; > > > + > > > + err = dquot_alloc_inode(inode); > > > + if (err) { > > > + dquot_drop(inode); > > > + goto errout; > > > + } > > > + return inode; > > > + > > > +errout: > > > + inode->i_flags |= S_NOQUOTA; > > > + iput(inode); > > > + shmem_free_inode(sb); > > > + if (err) > > > + return ERR_PTR(err); > > > + return NULL; > > > +} > > > + > > > #ifdef CONFIG_USERFAULTFD > > > int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, > > > pmd_t *dst_pmd, > > > @@ -2525,7 +2613,6 @@ int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, > > > > > > spin_lock_irq(&info->lock); > > > info->alloced++; > > > - inode->i_blocks += BLOCKS_PER_PAGE; > > > shmem_recalc_inode(inode); > > > spin_unlock_irq(&info->lock); > > > > > > @@ -3372,6 +3459,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) > > > > > > static const struct inode_operations shmem_short_symlink_operations = { > > > .getattr = shmem_getattr, > > > + .setattr = shmem_setattr, > > > .get_link = simple_get_link, > > > #ifdef CONFIG_TMPFS_XATTR > > > .listxattr = shmem_listxattr, > > > @@ -3380,6 +3468,7 @@ static const struct inode_operations shmem_short_symlink_operations = { > > > > > > static const struct inode_operations shmem_symlink_inode_operations = { > > > .getattr = shmem_getattr, > > > + .setattr = shmem_setattr, > > > .get_link = shmem_get_link, > > > #ifdef CONFIG_TMPFS_XATTR > > > .listxattr = shmem_listxattr, > > > @@ -3478,6 +3567,9 @@ enum shmem_param { > > > Opt_uid, > > > Opt_inode32, > > > Opt_inode64, > > > + Opt_quota, > > > + Opt_usrquota, > > > + Opt_grpquota, > > > }; > > > > > > static const struct constant_table shmem_param_enums_huge[] = { > > > @@ -3499,6 +3591,11 @@ const struct fs_parameter_spec shmem_fs_parameters[] = { > > > fsparam_u32 ("uid", Opt_uid), > > > fsparam_flag ("inode32", Opt_inode32), > > > fsparam_flag ("inode64", Opt_inode64), > > > +#ifdef CONFIG_TMPFS_QUOTA > > > + fsparam_flag ("quota", Opt_quota), > > > + fsparam_flag ("usrquota", Opt_usrquota), > > > + fsparam_flag ("grpquota", Opt_grpquota), > > > +#endif > > > {} > > > }; > > > > > > @@ -3582,6 +3679,18 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) > > > ctx->full_inums = true; > > > ctx->seen |= SHMEM_SEEN_INUMS; > > > break; > > > + case Opt_quota: > > > + ctx->seen |= SHMEM_SEEN_QUOTA; > > > + ctx->quota_types |= (QTYPE_MASK_USR | QTYPE_MASK_GRP); > > > + break; > > > + case Opt_usrquota: > > > + ctx->seen |= SHMEM_SEEN_QUOTA; > > > + ctx->quota_types |= QTYPE_MASK_USR; > > > + break; > > > + case Opt_grpquota: > > > + ctx->seen |= SHMEM_SEEN_QUOTA; > > > + ctx->quota_types |= QTYPE_MASK_GRP; > > > + break; > > > } > > > return 0; > > > > > > @@ -3681,6 +3790,12 @@ static int shmem_reconfigure(struct fs_context *fc) > > > goto out; > > > } > > > > > > + if (ctx->seen & SHMEM_SEEN_QUOTA && > > > + !sb_any_quota_loaded(fc->root->d_sb)) { > > > + err = "Cannot enable quota on remount"; > > > + goto out; > > > + } > > > + > > > if (ctx->seen & SHMEM_SEEN_HUGE) > > > sbinfo->huge = ctx->huge; > > > if (ctx->seen & SHMEM_SEEN_INUMS) > > > @@ -3763,6 +3878,9 @@ static void shmem_put_super(struct super_block *sb) > > > { > > > struct shmem_sb_info *sbinfo = SHMEM_SB(sb); > > > > > > +#ifdef CONFIG_TMPFS_QUOTA > > > + shmem_disable_quotas(sb); > > > +#endif > > > free_percpu(sbinfo->ino_batch); > > > percpu_counter_destroy(&sbinfo->used_blocks); > > > mpol_put(sbinfo->mpol); > > > @@ -3841,6 +3959,17 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) > > > #endif > > > uuid_gen(&sb->s_uuid); > > > > > > +#ifdef CONFIG_TMPFS_QUOTA > > > + if (ctx->seen & SHMEM_SEEN_QUOTA) { > > > + sb->dq_op = &shmem_quota_operations; > > > + sb->s_qcop = &dquot_quotactl_sysfile_ops; > > > + sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP; > > > + > > > + if (shmem_enable_quotas(sb, ctx->quota_types)) > > > + goto failed; > > > + } > > > +#endif /* CONFIG_TMPFS_QUOTA */ > > > + > > > inode = shmem_get_inode(&nop_mnt_idmap, sb, NULL, S_IFDIR | sbinfo->mode, 0, > > > VM_NORESERVE); > > > if (IS_ERR(inode)) { > > > @@ -4016,6 +4145,9 @@ static const struct super_operations shmem_ops = { > > > #ifdef CONFIG_TMPFS > > > .statfs = shmem_statfs, > > > .show_options = shmem_show_options, > > > +#endif > > > +#ifdef CONFIG_TMPFS_QUOTA > > > + .get_dquots = shmem_get_dquots, > > > #endif > > > .evict_inode = shmem_evict_inode, > > > .drop_inode = generic_delete_inode, > > > @@ -4082,6 +4214,14 @@ void __init shmem_init(void) > > > > > > shmem_init_inodecache(); > > > > > > +#ifdef CONFIG_TMPFS_QUOTA > > > + error = register_quota_format(&shmem_quota_format); > > > + if (error < 0) { > > > + pr_err("Could not register quota format\n"); > > > + goto out3; > > > + } > > > +#endif > > > + > > > error = register_filesystem(&shmem_fs_type); > > > if (error) { > > > pr_err("Could not register tmpfs\n"); > > > @@ -4106,6 +4246,10 @@ void __init shmem_init(void) > > > out1: > > > unregister_filesystem(&shmem_fs_type); > > > out2: > > > +#ifdef CONFIG_TMPFS_QUOTA > > > + unregister_quota_format(&shmem_quota_format); > > > +#endif > > > +out3: > > > shmem_destroy_inodecache(); > > > shm_mnt = ERR_PTR(error); > > > } > > > -- > > > 2.30.2 > > > > > -- > Carlos Maiolino
On Mon 03-04-23 10:47:58, cem@kernel.org wrote: > From: Lukas Czerner <lczerner@redhat.com> > > Now the basic infra-structure is in place, enable quota support for tmpfs. > > Signed-off-by: Lukas Czerner <lczerner@redhat.com> > Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com> Some comments below... > diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h > index cf38381bdb4c1..3e7e18726feb5 100644 > --- a/include/linux/shmem_fs.h > +++ b/include/linux/shmem_fs.h > @@ -26,6 +26,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; > }; > > @@ -171,4 +174,10 @@ 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 > +#define SHMEM_MAXQUOTAS 2 You have this definition already in mm/shmem_quota.c. > +extern const struct dquot_operations shmem_quota_operations; > +extern struct quota_format_type shmem_quota_format; > +#endif /* CONFIG_TMPFS_QUOTA */ > + > #endif > diff --git a/mm/shmem.c b/mm/shmem.c > index 88e13930fc013..d7529c883eaf5 100644 > --- a/mm/shmem.c > +++ b/mm/shmem.c > @@ -79,6 +79,7 @@ static struct vfsmount *shm_mnt; > #include <linux/userfaultfd_k.h> > #include <linux/rmap.h> > #include <linux/uuid.h> > +#include <linux/quotaops.h> > > #include <linux/uaccess.h> > > @@ -116,10 +117,12 @@ struct shmem_options { > bool full_inums; > int huge; > int seen; > + unsigned short quota_types; > #define SHMEM_SEEN_BLOCKS 1 > #define SHMEM_SEEN_INODES 2 > #define SHMEM_SEEN_HUGE 4 > #define SHMEM_SEEN_INUMS 8 > +#define SHMEM_SEEN_QUOTA 16 > }; > > #ifdef CONFIG_TMPFS > @@ -211,8 +214,11 @@ static inline int shmem_inode_acct_block(struct inode *inode, long pages) > if (percpu_counter_compare(&sbinfo->used_blocks, > sbinfo->max_blocks - pages) > 0) > goto unacct; > + if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) > + goto unacct; We generally try to avoid assignments in conditions so I'd do: err = dquot_alloc_block_nodirty(inode, pages); if (err) goto unacct; > percpu_counter_add(&sbinfo->used_blocks, pages); > - } > + } else if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) > + goto unacct; > The same here... > @@ -1133,6 +1179,15 @@ static int shmem_setattr(struct mnt_idmap *idmap, > } > } > > + /* Transfer quota accounting */ > + if (i_uid_needs_update(idmap, attr, inode) || > + i_gid_needs_update(idmap, attr,inode)) { > + error = dquot_transfer(idmap, inode, attr); > + > + if (error) > + return error; > + } > + I think you also need to add: if (is_quota_modification(idmap, inode, attr)) { error = dquot_initialize(inode); if (error) return error; } to shmem_setattr(). > setattr_copy(idmap, inode, attr); > if (attr->ia_valid & ATTR_MODE) > error = posix_acl_chmod(idmap, dentry, inode->i_mode); > @@ -1178,7 +1233,9 @@ static void shmem_evict_inode(struct inode *inode) > simple_xattrs_free(&info->xattrs); > WARN_ON(inode->i_blocks); > shmem_free_inode(inode->i_sb); > + dquot_free_inode(inode); > clear_inode(inode); > + dquot_drop(inode); > } > > static int shmem_find_swap_entries(struct address_space *mapping, > @@ -1975,7 +2032,6 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index, > > spin_lock_irq(&info->lock); > info->alloced += folio_nr_pages(folio); > - inode->i_blocks += (blkcnt_t)BLOCKS_PER_PAGE << folio_order(folio); > shmem_recalc_inode(inode); > spin_unlock_irq(&info->lock); > alloced = true; > @@ -2346,9 +2402,10 @@ static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags) > #define shmem_initxattrs NULL > #endif > > -static 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) > +static struct inode *shmem_get_inode_noquota(struct mnt_idmap *idmap, > + struct super_block *sb, > + struct inode *dir, umode_t mode, > + dev_t dev, unsigned long flags) > { > struct inode *inode; > struct shmem_inode_info *info; > @@ -2422,6 +2479,37 @@ static struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_block > return inode; > } > > +static 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) > +{ > + int err; > + struct inode *inode; > + > + inode = shmem_get_inode_noquota(idmap, sb, dir, mode, dev, flags); > + if (IS_ERR(inode)) > + return inode; > + > + err = dquot_initialize(inode); > + if (err) > + goto errout; > + > + err = dquot_alloc_inode(inode); > + if (err) { > + dquot_drop(inode); > + goto errout; > + } > + return inode; > + > +errout: > + inode->i_flags |= S_NOQUOTA; > + iput(inode); > + shmem_free_inode(sb); I think shmem_free_inode() is superfluous here. iput() above should already unaccount the inode... > + if (err) How could err be possibly unset here? > + return ERR_PTR(err); > + return NULL; > +} > + > @@ -3582,6 +3679,18 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) > ctx->full_inums = true; > ctx->seen |= SHMEM_SEEN_INUMS; > break; > + case Opt_quota: > + ctx->seen |= SHMEM_SEEN_QUOTA; > + ctx->quota_types |= (QTYPE_MASK_USR | QTYPE_MASK_GRP); > + break; > + case Opt_usrquota: > + ctx->seen |= SHMEM_SEEN_QUOTA; > + ctx->quota_types |= QTYPE_MASK_USR; > + break; > + case Opt_grpquota: > + ctx->seen |= SHMEM_SEEN_QUOTA; > + ctx->quota_types |= QTYPE_MASK_GRP; > + break; > } > return 0; > > @@ -3681,6 +3790,12 @@ static int shmem_reconfigure(struct fs_context *fc) > goto out; > } > > + if (ctx->seen & SHMEM_SEEN_QUOTA && > + !sb_any_quota_loaded(fc->root->d_sb)) { > + err = "Cannot enable quota on remount"; > + goto out; > + } > + > if (ctx->seen & SHMEM_SEEN_HUGE) > sbinfo->huge = ctx->huge; > if (ctx->seen & SHMEM_SEEN_INUMS) > @@ -3763,6 +3878,9 @@ static void shmem_put_super(struct super_block *sb) > { > struct shmem_sb_info *sbinfo = SHMEM_SB(sb); > > +#ifdef CONFIG_TMPFS_QUOTA > + shmem_disable_quotas(sb); > +#endif > free_percpu(sbinfo->ino_batch); > percpu_counter_destroy(&sbinfo->used_blocks); > mpol_put(sbinfo->mpol); > @@ -3841,6 +3959,17 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) > #endif > uuid_gen(&sb->s_uuid); > > +#ifdef CONFIG_TMPFS_QUOTA > + if (ctx->seen & SHMEM_SEEN_QUOTA) { > + sb->dq_op = &shmem_quota_operations; > + sb->s_qcop = &dquot_quotactl_sysfile_ops; > + sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP; s_quota_types should rather be copied from ctx, shouldn't it? Or why is s_quota_types inconsistent with ctx->quota_types? Honza
On Wed, Apr 05, 2023 at 01:42:45PM +0200, Jan Kara wrote: > On Mon 03-04-23 10:47:58, cem@kernel.org wrote: > > From: Lukas Czerner <lczerner@redhat.com> > > > > Now the basic infra-structure is in place, enable quota support for tmpfs. > > > > Signed-off-by: Lukas Czerner <lczerner@redhat.com> > > Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com> > > Some comments below... > > > diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h > > index cf38381bdb4c1..3e7e18726feb5 100644 > > --- a/include/linux/shmem_fs.h > > +++ b/include/linux/shmem_fs.h > > @@ -26,6 +26,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; > > }; > > > > @@ -171,4 +174,10 @@ 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 > > +#define SHMEM_MAXQUOTAS 2 > > You have this definition already in mm/shmem_quota.c. > True, that define is not visible from here though, I'll simply remove the one from mm/shmem_quota.c and keep it here in shmem_fs.h. > > +extern const struct dquot_operations shmem_quota_operations; > > +extern struct quota_format_type shmem_quota_format; > > +#endif /* CONFIG_TMPFS_QUOTA */ > > + > > #endif > > diff --git a/mm/shmem.c b/mm/shmem.c > > index 88e13930fc013..d7529c883eaf5 100644 > > --- a/mm/shmem.c > > +++ b/mm/shmem.c > > @@ -79,6 +79,7 @@ static struct vfsmount *shm_mnt; > > #include <linux/userfaultfd_k.h> > > #include <linux/rmap.h> > > #include <linux/uuid.h> > > +#include <linux/quotaops.h> > > > > #include <linux/uaccess.h> > > > > @@ -116,10 +117,12 @@ struct shmem_options { > > bool full_inums; > > int huge; > > int seen; > > + unsigned short quota_types; > > #define SHMEM_SEEN_BLOCKS 1 > > #define SHMEM_SEEN_INODES 2 > > #define SHMEM_SEEN_HUGE 4 > > #define SHMEM_SEEN_INUMS 8 > > +#define SHMEM_SEEN_QUOTA 16 > > }; > > > > #ifdef CONFIG_TMPFS > > @@ -211,8 +214,11 @@ static inline int shmem_inode_acct_block(struct inode *inode, long pages) > > if (percpu_counter_compare(&sbinfo->used_blocks, > > sbinfo->max_blocks - pages) > 0) > > goto unacct; > > + if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) > > + goto unacct; > > We generally try to avoid assignments in conditions so I'd do: > > err = dquot_alloc_block_nodirty(inode, pages); > if (err) > goto unacct; Fair enough. Will update it for the new version > > > percpu_counter_add(&sbinfo->used_blocks, pages); > > - } > > + } else if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) > > + goto unacct; > > > > The same here... > > > @@ -1133,6 +1179,15 @@ static int shmem_setattr(struct mnt_idmap *idmap, > > } > > } > > > > + /* Transfer quota accounting */ > > + if (i_uid_needs_update(idmap, attr, inode) || > > + i_gid_needs_update(idmap, attr,inode)) { > > + error = dquot_transfer(idmap, inode, attr); > > + > > + if (error) > > + return error; > > + } > > + > > I think you also need to add: > > if (is_quota_modification(idmap, inode, attr)) { > error = dquot_initialize(inode); > if (error) > return error; > } > > to shmem_setattr(). Ok. > > > setattr_copy(idmap, inode, attr); > > if (attr->ia_valid & ATTR_MODE) > > error = posix_acl_chmod(idmap, dentry, inode->i_mode); > > @@ -1178,7 +1233,9 @@ static void shmem_evict_inode(struct inode *inode) > > simple_xattrs_free(&info->xattrs); > > WARN_ON(inode->i_blocks); > > shmem_free_inode(inode->i_sb); > > + dquot_free_inode(inode); > > clear_inode(inode); > > + dquot_drop(inode); > > } > > > > static int shmem_find_swap_entries(struct address_space *mapping, > > @@ -1975,7 +2032,6 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index, > > > > spin_lock_irq(&info->lock); > > info->alloced += folio_nr_pages(folio); > > - inode->i_blocks += (blkcnt_t)BLOCKS_PER_PAGE << folio_order(folio); > > shmem_recalc_inode(inode); > > spin_unlock_irq(&info->lock); > > alloced = true; > > @@ -2346,9 +2402,10 @@ static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags) > > #define shmem_initxattrs NULL > > #endif > > > > -static 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) > > +static struct inode *shmem_get_inode_noquota(struct mnt_idmap *idmap, > > + struct super_block *sb, > > + struct inode *dir, umode_t mode, > > + dev_t dev, unsigned long flags) > > { > > struct inode *inode; > > struct shmem_inode_info *info; > > @@ -2422,6 +2479,37 @@ static struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_block > > return inode; > > } > > > > +static 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) > > +{ > > + int err; > > + struct inode *inode; > > + > > + inode = shmem_get_inode_noquota(idmap, sb, dir, mode, dev, flags); > > + if (IS_ERR(inode)) > > + return inode; > > + > > + err = dquot_initialize(inode); > > + if (err) > > + goto errout; > > + > > + err = dquot_alloc_inode(inode); > > + if (err) { > > + dquot_drop(inode); > > + goto errout; > > + } > > + return inode; > > + > > +errout: > > + inode->i_flags |= S_NOQUOTA; > > + iput(inode); > > + shmem_free_inode(sb); > > I think shmem_free_inode() is superfluous here. iput() above should already > unaccount the inode... Right, I see it can be called from .evict_inode during iput_final(). Thanks for spotting it. > > > + if (err) > > How could err be possibly unset here? I don't think it can, I'll update it. > > > + return ERR_PTR(err); > > + return NULL; > > +} > > + > > > @@ -3582,6 +3679,18 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) > > ctx->full_inums = true; > > ctx->seen |= SHMEM_SEEN_INUMS; > > break; > > + case Opt_quota: > > + ctx->seen |= SHMEM_SEEN_QUOTA; > > + ctx->quota_types |= (QTYPE_MASK_USR | QTYPE_MASK_GRP); > > + break; > > + case Opt_usrquota: > > + ctx->seen |= SHMEM_SEEN_QUOTA; > > + ctx->quota_types |= QTYPE_MASK_USR; > > + break; > > + case Opt_grpquota: > > + ctx->seen |= SHMEM_SEEN_QUOTA; > > + ctx->quota_types |= QTYPE_MASK_GRP; > > + break; > > } > > return 0; > > > > @@ -3681,6 +3790,12 @@ static int shmem_reconfigure(struct fs_context *fc) > > goto out; > > } > > > > + if (ctx->seen & SHMEM_SEEN_QUOTA && > > + !sb_any_quota_loaded(fc->root->d_sb)) { > > + err = "Cannot enable quota on remount"; > > + goto out; > > + } > > + > > if (ctx->seen & SHMEM_SEEN_HUGE) > > sbinfo->huge = ctx->huge; > > if (ctx->seen & SHMEM_SEEN_INUMS) > > @@ -3763,6 +3878,9 @@ static void shmem_put_super(struct super_block *sb) > > { > > struct shmem_sb_info *sbinfo = SHMEM_SB(sb); > > > > +#ifdef CONFIG_TMPFS_QUOTA > > + shmem_disable_quotas(sb); > > +#endif > > free_percpu(sbinfo->ino_batch); > > percpu_counter_destroy(&sbinfo->used_blocks); > > mpol_put(sbinfo->mpol); > > @@ -3841,6 +3959,17 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) > > #endif > > uuid_gen(&sb->s_uuid); > > > > +#ifdef CONFIG_TMPFS_QUOTA > > + if (ctx->seen & SHMEM_SEEN_QUOTA) { > > + sb->dq_op = &shmem_quota_operations; > > + sb->s_qcop = &dquot_quotactl_sysfile_ops; > > + sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP; > > s_quota_types should rather be copied from ctx, shouldn't it? Or why is > s_quota_types inconsistent with ctx->quota_types? I believe s_qupta_types here is a bitmask of supported quota types, while ctx->quota_types refers to the mount options being passed from the user. So we should enable in sb->s_quota_types which quota types the filesystem supports, not which were enabled by the user. Cheers.
On Tue 11-04-23 11:37:26, Carlos Maiolino wrote: > On Wed, Apr 05, 2023 at 01:42:45PM +0200, Jan Kara wrote: > > > @@ -3763,6 +3878,9 @@ static void shmem_put_super(struct super_block *sb) > > > { > > > struct shmem_sb_info *sbinfo = SHMEM_SB(sb); > > > > > > +#ifdef CONFIG_TMPFS_QUOTA > > > + shmem_disable_quotas(sb); > > > +#endif > > > free_percpu(sbinfo->ino_batch); > > > percpu_counter_destroy(&sbinfo->used_blocks); > > > mpol_put(sbinfo->mpol); > > > @@ -3841,6 +3959,17 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) > > > #endif > > > uuid_gen(&sb->s_uuid); > > > > > > +#ifdef CONFIG_TMPFS_QUOTA > > > + if (ctx->seen & SHMEM_SEEN_QUOTA) { > > > + sb->dq_op = &shmem_quota_operations; > > > + sb->s_qcop = &dquot_quotactl_sysfile_ops; > > > + sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP; > > > > s_quota_types should rather be copied from ctx, shouldn't it? Or why is > > s_quota_types inconsistent with ctx->quota_types? > > I believe s_qupta_types here is a bitmask of supported quota types, while > ctx->quota_types refers to the mount options being passed from the user. > > So we should enable in sb->s_quota_types which quota types the filesystem > supports, not which were enabled by the user. Oh, right. Honza
diff --git a/Documentation/filesystems/tmpfs.rst b/Documentation/filesystems/tmpfs.rst index 0408c245785e3..3f8d89bb7e1a5 100644 --- a/Documentation/filesystems/tmpfs.rst +++ b/Documentation/filesystems/tmpfs.rst @@ -86,6 +86,18 @@ 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. +======== ============================================================= + 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 ...' diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index cf38381bdb4c1..3e7e18726feb5 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -26,6 +26,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; }; @@ -171,4 +174,10 @@ 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 +#define SHMEM_MAXQUOTAS 2 +extern const struct dquot_operations shmem_quota_operations; +extern struct quota_format_type shmem_quota_format; +#endif /* CONFIG_TMPFS_QUOTA */ + #endif diff --git a/mm/shmem.c b/mm/shmem.c index 88e13930fc013..d7529c883eaf5 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -79,6 +79,7 @@ static struct vfsmount *shm_mnt; #include <linux/userfaultfd_k.h> #include <linux/rmap.h> #include <linux/uuid.h> +#include <linux/quotaops.h> #include <linux/uaccess.h> @@ -116,10 +117,12 @@ struct shmem_options { bool full_inums; int huge; int seen; + unsigned short quota_types; #define SHMEM_SEEN_BLOCKS 1 #define SHMEM_SEEN_INODES 2 #define SHMEM_SEEN_HUGE 4 #define SHMEM_SEEN_INUMS 8 +#define SHMEM_SEEN_QUOTA 16 }; #ifdef CONFIG_TMPFS @@ -211,8 +214,11 @@ static inline int shmem_inode_acct_block(struct inode *inode, long pages) if (percpu_counter_compare(&sbinfo->used_blocks, sbinfo->max_blocks - pages) > 0) goto unacct; + if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) + goto unacct; percpu_counter_add(&sbinfo->used_blocks, pages); - } + } else if ((err = dquot_alloc_block_nodirty(inode, pages)) != 0) + goto unacct; return 0; @@ -226,6 +232,8 @@ static inline void shmem_inode_unacct_blocks(struct inode *inode, long pages) struct shmem_inode_info *info = SHMEM_I(inode); struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); + dquot_free_block_nodirty(inode, pages); + if (sbinfo->max_blocks) percpu_counter_sub(&sbinfo->used_blocks, pages); shmem_unacct_blocks(info->flags, pages); @@ -254,6 +262,47 @@ bool vma_is_shmem(struct vm_area_struct *vma) static LIST_HEAD(shmem_swaplist); static DEFINE_MUTEX(shmem_swaplist_mutex); +#ifdef CONFIG_TMPFS_QUOTA + +static int shmem_enable_quotas(struct super_block *sb, + unsigned short quota_types) +{ + int type, err = 0; + + sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NOLIST_DIRTY; + for (type = 0; type < SHMEM_MAXQUOTAS; type++) { + if (!(quota_types & (1 << type))) + continue; + err = dquot_load_quota_sb(sb, type, QFMT_SHMEM, + DQUOT_USAGE_ENABLED | + DQUOT_LIMITS_ENABLED); + if (err) + goto out_err; + } + return 0; + +out_err: + pr_warn("tmpfs: failed to enable quota tracking (type=%d, err=%d)\n", + type, err); + for (type--; type >= 0; type--) + dquot_quota_off(sb, type); + return err; +} + +static void shmem_disable_quotas(struct super_block *sb) +{ + int type; + + for (type = 0; type < SHMEM_MAXQUOTAS; type++) + dquot_quota_off(sb, type); +} + +static struct dquot **shmem_get_dquots(struct inode *inode) +{ + return SHMEM_I(inode)->i_dquot; +} +#endif /* CONFIG_TMPFS_QUOTA */ + /* * shmem_reserve_inode() performs bookkeeping to reserve a shmem inode, and * produces a novel ino for the newly allocated inode. @@ -360,7 +409,6 @@ static void shmem_recalc_inode(struct inode *inode) freed = info->alloced - info->swapped - inode->i_mapping->nrpages; if (freed > 0) { info->alloced -= freed; - inode->i_blocks -= freed * BLOCKS_PER_PAGE; shmem_inode_unacct_blocks(inode, freed); } } @@ -378,7 +426,6 @@ bool shmem_charge(struct inode *inode, long pages) spin_lock_irqsave(&info->lock, flags); info->alloced += pages; - inode->i_blocks += pages * BLOCKS_PER_PAGE; shmem_recalc_inode(inode); spin_unlock_irqrestore(&info->lock, flags); @@ -394,7 +441,6 @@ void shmem_uncharge(struct inode *inode, long pages) spin_lock_irqsave(&info->lock, flags); info->alloced -= pages; - inode->i_blocks -= pages * BLOCKS_PER_PAGE; shmem_recalc_inode(inode); spin_unlock_irqrestore(&info->lock, flags); @@ -1133,6 +1179,15 @@ static int shmem_setattr(struct mnt_idmap *idmap, } } + /* Transfer quota accounting */ + if (i_uid_needs_update(idmap, attr, inode) || + i_gid_needs_update(idmap, attr,inode)) { + error = dquot_transfer(idmap, inode, attr); + + if (error) + return error; + } + setattr_copy(idmap, inode, attr); if (attr->ia_valid & ATTR_MODE) error = posix_acl_chmod(idmap, dentry, inode->i_mode); @@ -1178,7 +1233,9 @@ static void shmem_evict_inode(struct inode *inode) simple_xattrs_free(&info->xattrs); WARN_ON(inode->i_blocks); shmem_free_inode(inode->i_sb); + dquot_free_inode(inode); clear_inode(inode); + dquot_drop(inode); } static int shmem_find_swap_entries(struct address_space *mapping, @@ -1975,7 +2032,6 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index, spin_lock_irq(&info->lock); info->alloced += folio_nr_pages(folio); - inode->i_blocks += (blkcnt_t)BLOCKS_PER_PAGE << folio_order(folio); shmem_recalc_inode(inode); spin_unlock_irq(&info->lock); alloced = true; @@ -2346,9 +2402,10 @@ static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags) #define shmem_initxattrs NULL #endif -static 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) +static struct inode *shmem_get_inode_noquota(struct mnt_idmap *idmap, + struct super_block *sb, + struct inode *dir, umode_t mode, + dev_t dev, unsigned long flags) { struct inode *inode; struct shmem_inode_info *info; @@ -2422,6 +2479,37 @@ static struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_block return inode; } +static 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) +{ + int err; + struct inode *inode; + + inode = shmem_get_inode_noquota(idmap, sb, dir, mode, dev, flags); + if (IS_ERR(inode)) + return inode; + + err = dquot_initialize(inode); + if (err) + goto errout; + + err = dquot_alloc_inode(inode); + if (err) { + dquot_drop(inode); + goto errout; + } + return inode; + +errout: + inode->i_flags |= S_NOQUOTA; + iput(inode); + shmem_free_inode(sb); + if (err) + return ERR_PTR(err); + return NULL; +} + #ifdef CONFIG_USERFAULTFD int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, pmd_t *dst_pmd, @@ -2525,7 +2613,6 @@ int shmem_mfill_atomic_pte(struct mm_struct *dst_mm, spin_lock_irq(&info->lock); info->alloced++; - inode->i_blocks += BLOCKS_PER_PAGE; shmem_recalc_inode(inode); spin_unlock_irq(&info->lock); @@ -3372,6 +3459,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) static const struct inode_operations shmem_short_symlink_operations = { .getattr = shmem_getattr, + .setattr = shmem_setattr, .get_link = simple_get_link, #ifdef CONFIG_TMPFS_XATTR .listxattr = shmem_listxattr, @@ -3380,6 +3468,7 @@ static const struct inode_operations shmem_short_symlink_operations = { static const struct inode_operations shmem_symlink_inode_operations = { .getattr = shmem_getattr, + .setattr = shmem_setattr, .get_link = shmem_get_link, #ifdef CONFIG_TMPFS_XATTR .listxattr = shmem_listxattr, @@ -3478,6 +3567,9 @@ enum shmem_param { Opt_uid, Opt_inode32, Opt_inode64, + Opt_quota, + Opt_usrquota, + Opt_grpquota, }; static const struct constant_table shmem_param_enums_huge[] = { @@ -3499,6 +3591,11 @@ const struct fs_parameter_spec shmem_fs_parameters[] = { fsparam_u32 ("uid", Opt_uid), fsparam_flag ("inode32", Opt_inode32), fsparam_flag ("inode64", Opt_inode64), +#ifdef CONFIG_TMPFS_QUOTA + fsparam_flag ("quota", Opt_quota), + fsparam_flag ("usrquota", Opt_usrquota), + fsparam_flag ("grpquota", Opt_grpquota), +#endif {} }; @@ -3582,6 +3679,18 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) ctx->full_inums = true; ctx->seen |= SHMEM_SEEN_INUMS; break; + case Opt_quota: + ctx->seen |= SHMEM_SEEN_QUOTA; + ctx->quota_types |= (QTYPE_MASK_USR | QTYPE_MASK_GRP); + break; + case Opt_usrquota: + ctx->seen |= SHMEM_SEEN_QUOTA; + ctx->quota_types |= QTYPE_MASK_USR; + break; + case Opt_grpquota: + ctx->seen |= SHMEM_SEEN_QUOTA; + ctx->quota_types |= QTYPE_MASK_GRP; + break; } return 0; @@ -3681,6 +3790,12 @@ static int shmem_reconfigure(struct fs_context *fc) goto out; } + if (ctx->seen & SHMEM_SEEN_QUOTA && + !sb_any_quota_loaded(fc->root->d_sb)) { + err = "Cannot enable quota on remount"; + goto out; + } + if (ctx->seen & SHMEM_SEEN_HUGE) sbinfo->huge = ctx->huge; if (ctx->seen & SHMEM_SEEN_INUMS) @@ -3763,6 +3878,9 @@ static void shmem_put_super(struct super_block *sb) { struct shmem_sb_info *sbinfo = SHMEM_SB(sb); +#ifdef CONFIG_TMPFS_QUOTA + shmem_disable_quotas(sb); +#endif free_percpu(sbinfo->ino_batch); percpu_counter_destroy(&sbinfo->used_blocks); mpol_put(sbinfo->mpol); @@ -3841,6 +3959,17 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) #endif uuid_gen(&sb->s_uuid); +#ifdef CONFIG_TMPFS_QUOTA + if (ctx->seen & SHMEM_SEEN_QUOTA) { + sb->dq_op = &shmem_quota_operations; + sb->s_qcop = &dquot_quotactl_sysfile_ops; + sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP; + + if (shmem_enable_quotas(sb, ctx->quota_types)) + goto failed; + } +#endif /* CONFIG_TMPFS_QUOTA */ + inode = shmem_get_inode(&nop_mnt_idmap, sb, NULL, S_IFDIR | sbinfo->mode, 0, VM_NORESERVE); if (IS_ERR(inode)) { @@ -4016,6 +4145,9 @@ static const struct super_operations shmem_ops = { #ifdef CONFIG_TMPFS .statfs = shmem_statfs, .show_options = shmem_show_options, +#endif +#ifdef CONFIG_TMPFS_QUOTA + .get_dquots = shmem_get_dquots, #endif .evict_inode = shmem_evict_inode, .drop_inode = generic_delete_inode, @@ -4082,6 +4214,14 @@ void __init shmem_init(void) shmem_init_inodecache(); +#ifdef CONFIG_TMPFS_QUOTA + error = register_quota_format(&shmem_quota_format); + if (error < 0) { + pr_err("Could not register quota format\n"); + goto out3; + } +#endif + error = register_filesystem(&shmem_fs_type); if (error) { pr_err("Could not register tmpfs\n"); @@ -4106,6 +4246,10 @@ void __init shmem_init(void) out1: unregister_filesystem(&shmem_fs_type); out2: +#ifdef CONFIG_TMPFS_QUOTA + unregister_quota_format(&shmem_quota_format); +#endif +out3: shmem_destroy_inodecache(); shm_mnt = ERR_PTR(error); }