Skip to content

Commit de4c0e7

Browse files
Lukas Czernerbrauner
authored andcommitted
shmem: Add default quota limit mount options
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]> Reviewed-by: Jan Kara <[email protected]> Message-Id: <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent e09764c commit de4c0e7

File tree

4 files changed

+127
-10
lines changed

4 files changed

+127
-10
lines changed

Documentation/filesystems/tmpfs.rst

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,31 @@ force huge pages on all tmpfs mounts for testing.
125125

126126
tmpfs also supports quota with the following mount options
127127

128-
======== =============================================================
129-
quota User and group quota accounting and enforcement is enabled on
130-
the mount. Tmpfs is using hidden system quota files that are
131-
initialized on mount.
132-
usrquota User quota accounting and enforcement is enabled on the
133-
mount.
134-
grpquota Group quota accounting and enforcement is enabled on the
135-
mount.
136-
======== =============================================================
128+
======================== =================================================
129+
quota User and group quota accounting and enforcement
130+
is enabled on the mount. Tmpfs is using hidden
131+
system quota files that are initialized on mount.
132+
usrquota User quota accounting and enforcement is enabled
133+
on the mount.
134+
grpquota Group quota accounting and enforcement is enabled
135+
on the mount.
136+
usrquota_block_hardlimit Set global user quota block hard limit.
137+
usrquota_inode_hardlimit Set global user quota inode hard limit.
138+
grpquota_block_hardlimit Set global group quota block hard limit.
139+
grpquota_inode_hardlimit Set global group quota inode hard limit.
140+
======================== =================================================
141+
142+
None of the quota related mount options can be set or changed on remount.
143+
144+
Quota limit parameters accept a suffix k, m or g for kilo, mega and giga
145+
and can't be changed on remount. Default global quota limits are taking
146+
effect for any and all user/group/project except root the first time the
147+
quota entry for user/group/project id is being accessed - typically the
148+
first time an inode with a particular id ownership is being created after
149+
the mount. In other words, instead of the limits being initialized to zero,
150+
they are initialized with the particular value provided with these mount
151+
options. The limits can be changed for any user/group id at any time as they
152+
normally can be.
137153

138154
Note that tmpfs quotas do not support user namespaces so no uid/gid
139155
translation is done if quotas are enabled inside user namespaces.

include/linux/shmem_fs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ struct shmem_inode_info {
4242
(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL | FS_NOATIME_FL)
4343
#define SHMEM_FL_INHERITED (FS_NODUMP_FL | FS_NOATIME_FL)
4444

45+
struct shmem_quota_limits {
46+
qsize_t usrquota_bhardlimit; /* Default user quota block hard limit */
47+
qsize_t usrquota_ihardlimit; /* Default user quota inode hard limit */
48+
qsize_t grpquota_bhardlimit; /* Default group quota block hard limit */
49+
qsize_t grpquota_ihardlimit; /* Default group quota inode hard limit */
50+
};
51+
4552
struct shmem_sb_info {
4653
unsigned long max_blocks; /* How many blocks are allowed */
4754
struct percpu_counter used_blocks; /* How many are allocated */
@@ -60,6 +67,7 @@ struct shmem_sb_info {
6067
spinlock_t shrinklist_lock; /* Protects shrinklist */
6168
struct list_head shrinklist; /* List of shinkable inodes */
6269
unsigned long shrinklist_len; /* Length of shrinklist */
70+
struct shmem_quota_limits qlimits; /* Default quota limits */
6371
};
6472

6573
static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)

mm/shmem.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ struct shmem_options {
118118
int seen;
119119
bool noswap;
120120
unsigned short quota_types;
121+
struct shmem_quota_limits qlimits;
121122
#define SHMEM_SEEN_BLOCKS 1
122123
#define SHMEM_SEEN_INODES 2
123124
#define SHMEM_SEEN_HUGE 4
@@ -3738,6 +3739,10 @@ enum shmem_param {
37383739
Opt_quota,
37393740
Opt_usrquota,
37403741
Opt_grpquota,
3742+
Opt_usrquota_block_hardlimit,
3743+
Opt_usrquota_inode_hardlimit,
3744+
Opt_grpquota_block_hardlimit,
3745+
Opt_grpquota_inode_hardlimit,
37413746
};
37423747

37433748
static const struct constant_table shmem_param_enums_huge[] = {
@@ -3764,6 +3769,10 @@ const struct fs_parameter_spec shmem_fs_parameters[] = {
37643769
fsparam_flag ("quota", Opt_quota),
37653770
fsparam_flag ("usrquota", Opt_usrquota),
37663771
fsparam_flag ("grpquota", Opt_grpquota),
3772+
fsparam_string("usrquota_block_hardlimit", Opt_usrquota_block_hardlimit),
3773+
fsparam_string("usrquota_inode_hardlimit", Opt_usrquota_inode_hardlimit),
3774+
fsparam_string("grpquota_block_hardlimit", Opt_grpquota_block_hardlimit),
3775+
fsparam_string("grpquota_inode_hardlimit", Opt_grpquota_inode_hardlimit),
37673776
#endif
37683777
{}
37693778
};
@@ -3874,6 +3883,42 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
38743883
ctx->seen |= SHMEM_SEEN_QUOTA;
38753884
ctx->quota_types |= QTYPE_MASK_GRP;
38763885
break;
3886+
case Opt_usrquota_block_hardlimit:
3887+
size = memparse(param->string, &rest);
3888+
if (*rest || !size)
3889+
goto bad_value;
3890+
if (size > SHMEM_QUOTA_MAX_SPC_LIMIT)
3891+
return invalfc(fc,
3892+
"User quota block hardlimit too large.");
3893+
ctx->qlimits.usrquota_bhardlimit = size;
3894+
break;
3895+
case Opt_grpquota_block_hardlimit:
3896+
size = memparse(param->string, &rest);
3897+
if (*rest || !size)
3898+
goto bad_value;
3899+
if (size > SHMEM_QUOTA_MAX_SPC_LIMIT)
3900+
return invalfc(fc,
3901+
"Group quota block hardlimit too large.");
3902+
ctx->qlimits.grpquota_bhardlimit = size;
3903+
break;
3904+
case Opt_usrquota_inode_hardlimit:
3905+
size = memparse(param->string, &rest);
3906+
if (*rest || !size)
3907+
goto bad_value;
3908+
if (size > SHMEM_QUOTA_MAX_INO_LIMIT)
3909+
return invalfc(fc,
3910+
"User quota inode hardlimit too large.");
3911+
ctx->qlimits.usrquota_ihardlimit = size;
3912+
break;
3913+
case Opt_grpquota_inode_hardlimit:
3914+
size = memparse(param->string, &rest);
3915+
if (*rest || !size)
3916+
goto bad_value;
3917+
if (size > SHMEM_QUOTA_MAX_INO_LIMIT)
3918+
return invalfc(fc,
3919+
"Group quota inode hardlimit too large.");
3920+
ctx->qlimits.grpquota_ihardlimit = size;
3921+
break;
38773922
}
38783923
return 0;
38793924

@@ -3987,6 +4032,18 @@ static int shmem_reconfigure(struct fs_context *fc)
39874032
goto out;
39884033
}
39894034

4035+
#ifdef CONFIG_TMPFS_QUOTA
4036+
#define CHANGED_LIMIT(name) \
4037+
(ctx->qlimits.name## hardlimit && \
4038+
(ctx->qlimits.name## hardlimit != sbinfo->qlimits.name## hardlimit))
4039+
4040+
if (CHANGED_LIMIT(usrquota_b) || CHANGED_LIMIT(usrquota_i) ||
4041+
CHANGED_LIMIT(grpquota_b) || CHANGED_LIMIT(grpquota_i)) {
4042+
err = "Cannot change global quota limit on remount";
4043+
goto out;
4044+
}
4045+
#endif /* CONFIG_TMPFS_QUOTA */
4046+
39904047
if (ctx->seen & SHMEM_SEEN_HUGE)
39914048
sbinfo->huge = ctx->huge;
39924049
if (ctx->seen & SHMEM_SEEN_INUMS)
@@ -4166,6 +4223,10 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
41664223
sb->s_qcop = &dquot_quotactl_sysfile_ops;
41674224
sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP;
41684225

4226+
/* Copy the default limits from ctx into sbinfo */
4227+
memcpy(&sbinfo->qlimits, &ctx->qlimits,
4228+
sizeof(struct shmem_quota_limits));
4229+
41694230
if (shmem_enable_quotas(sb, ctx->quota_types))
41704231
goto failed;
41714232
}

mm/shmem_quota.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ static int shmem_acquire_dquot(struct dquot *dquot)
166166
{
167167
struct mem_dqinfo *info = sb_dqinfo(dquot->dq_sb, dquot->dq_id.type);
168168
struct rb_node **n = &((struct rb_root *)info->dqi_priv)->rb_node;
169+
struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info;
169170
struct rb_node *parent = NULL, *new_node = NULL;
170171
struct quota_id *new_entry, *entry;
171172
qid_t id = from_kqid(&init_user_ns, dquot->dq_id);
@@ -195,6 +196,14 @@ static int shmem_acquire_dquot(struct dquot *dquot)
195196
}
196197

197198
new_entry->id = id;
199+
if (dquot->dq_id.type == USRQUOTA) {
200+
new_entry->bhardlimit = sbinfo->qlimits.usrquota_bhardlimit;
201+
new_entry->ihardlimit = sbinfo->qlimits.usrquota_ihardlimit;
202+
} else if (dquot->dq_id.type == GRPQUOTA) {
203+
new_entry->bhardlimit = sbinfo->qlimits.grpquota_bhardlimit;
204+
new_entry->ihardlimit = sbinfo->qlimits.grpquota_ihardlimit;
205+
}
206+
198207
new_node = &new_entry->node;
199208
rb_link_node(new_node, parent, n);
200209
rb_insert_color(new_node, (struct rb_root *)info->dqi_priv);
@@ -224,6 +233,29 @@ static int shmem_acquire_dquot(struct dquot *dquot)
224233
return ret;
225234
}
226235

236+
static bool shmem_is_empty_dquot(struct dquot *dquot)
237+
{
238+
struct shmem_sb_info *sbinfo = dquot->dq_sb->s_fs_info;
239+
qsize_t bhardlimit;
240+
qsize_t ihardlimit;
241+
242+
if (dquot->dq_id.type == USRQUOTA) {
243+
bhardlimit = sbinfo->qlimits.usrquota_bhardlimit;
244+
ihardlimit = sbinfo->qlimits.usrquota_ihardlimit;
245+
} else if (dquot->dq_id.type == GRPQUOTA) {
246+
bhardlimit = sbinfo->qlimits.grpquota_bhardlimit;
247+
ihardlimit = sbinfo->qlimits.grpquota_ihardlimit;
248+
}
249+
250+
if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
251+
(dquot->dq_dqb.dqb_curspace == 0 &&
252+
dquot->dq_dqb.dqb_curinodes == 0 &&
253+
dquot->dq_dqb.dqb_bhardlimit == bhardlimit &&
254+
dquot->dq_dqb.dqb_ihardlimit == ihardlimit))
255+
return true;
256+
257+
return false;
258+
}
227259
/*
228260
* Store limits from dquot in the tree unless it's fake. If it is fake
229261
* 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)
261293
return -ENOENT;
262294

263295
found:
264-
if (test_bit(DQ_FAKE_B, &dquot->dq_flags)) {
296+
if (shmem_is_empty_dquot(dquot)) {
265297
/* Remove entry from the tree */
266298
rb_erase(&entry->node, info->dqi_priv);
267299
kfree(entry);

0 commit comments

Comments
 (0)