On Mon 03-04-23 10:47:58, cem@xxxxxxxxxx wrote: > From: Lukas Czerner <lczerner@xxxxxxxxxx> > > Now the basic infra-structure is in place, enable quota support for tmpfs. > > Signed-off-by: Lukas Czerner <lczerner@xxxxxxxxxx> > Signed-off-by: Carlos Maiolino <cmaiolino@xxxxxxxxxx> 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 -- Jan Kara <jack@xxxxxxxx> SUSE Labs, CR