Skip to content

Commit 498c601

Browse files
committed
quota: Implement quota format with 64-bit space and inode limits
So far the maximum quota space limit was 4TB. Apparently this isn't enough for Lustre guys anymore. So implement new quota format which raises block limits to 2^64 bytes. Also store number of inodes and inode limits in 64-bit variables as 2^32 files isn't that insanely high anymore. The first version of the patch has been developed by Andrew Perepechko <[email protected]>. CC: [email protected] Signed-off-by: Jan Kara <[email protected]>
1 parent 3067393 commit 498c601

File tree

4 files changed

+154
-39
lines changed

4 files changed

+154
-39
lines changed

fs/quota/Kconfig

+5-3
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,14 @@ config QFMT_V1
4646
format say Y here.
4747

4848
config QFMT_V2
49-
tristate "Quota format v2 support"
49+
tristate "Quota format vfsv0 and vfsv1 support"
5050
depends on QUOTA
5151
select QUOTA_TREE
5252
help
53-
This quota format allows using quotas with 32-bit UIDs/GIDs. If you
54-
need this functionality say Y here.
53+
This config option enables kernel support for vfsv0 and vfsv1 quota
54+
formats. Both these formats support 32-bit UIDs/GIDs and vfsv1 format
55+
also supports 64-bit inode and block quota limits. If you need this
56+
functionality say Y here.
5557

5658
config QUOTACTL
5759
bool

fs/quota/quota_v2.c

+132-33
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,23 @@ MODULE_LICENSE("GPL");
2323

2424
#define __QUOTA_V2_PARANOIA
2525

26-
static void v2_mem2diskdqb(void *dp, struct dquot *dquot);
27-
static void v2_disk2memdqb(struct dquot *dquot, void *dp);
28-
static int v2_is_id(void *dp, struct dquot *dquot);
29-
30-
static struct qtree_fmt_operations v2_qtree_ops = {
31-
.mem2disk_dqblk = v2_mem2diskdqb,
32-
.disk2mem_dqblk = v2_disk2memdqb,
33-
.is_id = v2_is_id,
26+
static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot);
27+
static void v2r0_disk2memdqb(struct dquot *dquot, void *dp);
28+
static int v2r0_is_id(void *dp, struct dquot *dquot);
29+
static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot);
30+
static void v2r1_disk2memdqb(struct dquot *dquot, void *dp);
31+
static int v2r1_is_id(void *dp, struct dquot *dquot);
32+
33+
static struct qtree_fmt_operations v2r0_qtree_ops = {
34+
.mem2disk_dqblk = v2r0_mem2diskdqb,
35+
.disk2mem_dqblk = v2r0_disk2memdqb,
36+
.is_id = v2r0_is_id,
37+
};
38+
39+
static struct qtree_fmt_operations v2r1_qtree_ops = {
40+
.mem2disk_dqblk = v2r1_mem2diskdqb,
41+
.disk2mem_dqblk = v2r1_disk2memdqb,
42+
.is_id = v2r1_is_id,
3443
};
3544

3645
#define QUOTABLOCK_BITS 10
@@ -46,23 +55,33 @@ static inline qsize_t v2_qbtos(qsize_t blocks)
4655
return blocks << QUOTABLOCK_BITS;
4756
}
4857

58+
static int v2_read_header(struct super_block *sb, int type,
59+
struct v2_disk_dqheader *dqhead)
60+
{
61+
ssize_t size;
62+
63+
size = sb->s_op->quota_read(sb, type, (char *)dqhead,
64+
sizeof(struct v2_disk_dqheader), 0);
65+
if (size != sizeof(struct v2_disk_dqheader)) {
66+
printk(KERN_WARNING "quota_v2: Failed header read:"
67+
" expected=%zd got=%zd\n",
68+
sizeof(struct v2_disk_dqheader), size);
69+
return 0;
70+
}
71+
return 1;
72+
}
73+
4974
/* Check whether given file is really vfsv0 quotafile */
5075
static int v2_check_quota_file(struct super_block *sb, int type)
5176
{
5277
struct v2_disk_dqheader dqhead;
53-
ssize_t size;
5478
static const uint quota_magics[] = V2_INITQMAGICS;
5579
static const uint quota_versions[] = V2_INITQVERSIONS;
5680

57-
size = sb->s_op->quota_read(sb, type, (char *)&dqhead,
58-
sizeof(struct v2_disk_dqheader), 0);
59-
if (size != sizeof(struct v2_disk_dqheader)) {
60-
printk("quota_v2: failed read expected=%zd got=%zd\n",
61-
sizeof(struct v2_disk_dqheader), size);
81+
if (!v2_read_header(sb, type, &dqhead))
6282
return 0;
63-
}
6483
if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
65-
le32_to_cpu(dqhead.dqh_version) != quota_versions[type])
84+
le32_to_cpu(dqhead.dqh_version) > quota_versions[type])
6685
return 0;
6786
return 1;
6887
}
@@ -71,14 +90,20 @@ static int v2_check_quota_file(struct super_block *sb, int type)
7190
static int v2_read_file_info(struct super_block *sb, int type)
7291
{
7392
struct v2_disk_dqinfo dinfo;
93+
struct v2_disk_dqheader dqhead;
7494
struct mem_dqinfo *info = sb_dqinfo(sb, type);
7595
struct qtree_mem_dqinfo *qinfo;
7696
ssize_t size;
97+
unsigned int version;
98+
99+
if (!v2_read_header(sb, type, &dqhead))
100+
return 0;
101+
version = le32_to_cpu(dqhead.dqh_version);
77102

78103
size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
79104
sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
80105
if (size != sizeof(struct v2_disk_dqinfo)) {
81-
printk(KERN_WARNING "Can't read info structure on device %s.\n",
106+
printk(KERN_WARNING "quota_v2: Can't read info structure on device %s.\n",
82107
sb->s_id);
83108
return -1;
84109
}
@@ -89,9 +114,15 @@ static int v2_read_file_info(struct super_block *sb, int type)
89114
return -1;
90115
}
91116
qinfo = info->dqi_priv;
92-
/* limits are stored as unsigned 32-bit data */
93-
info->dqi_maxblimit = 0xffffffff;
94-
info->dqi_maxilimit = 0xffffffff;
117+
if (version == 0) {
118+
/* limits are stored as unsigned 32-bit data */
119+
info->dqi_maxblimit = 0xffffffff;
120+
info->dqi_maxilimit = 0xffffffff;
121+
} else {
122+
/* used space is stored as unsigned 64-bit value */
123+
info->dqi_maxblimit = 0xffffffffffffffff; /* 2^64-1 */
124+
info->dqi_maxilimit = 0xffffffffffffffff;
125+
}
95126
info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
96127
info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
97128
info->dqi_flags = le32_to_cpu(dinfo.dqi_flags);
@@ -103,8 +134,13 @@ static int v2_read_file_info(struct super_block *sb, int type)
103134
qinfo->dqi_blocksize_bits = V2_DQBLKSIZE_BITS;
104135
qinfo->dqi_usable_bs = 1 << V2_DQBLKSIZE_BITS;
105136
qinfo->dqi_qtree_depth = qtree_depth(qinfo);
106-
qinfo->dqi_entry_size = sizeof(struct v2_disk_dqblk);
107-
qinfo->dqi_ops = &v2_qtree_ops;
137+
if (version == 0) {
138+
qinfo->dqi_entry_size = sizeof(struct v2r0_disk_dqblk);
139+
qinfo->dqi_ops = &v2r0_qtree_ops;
140+
} else {
141+
qinfo->dqi_entry_size = sizeof(struct v2r1_disk_dqblk);
142+
qinfo->dqi_ops = &v2r1_qtree_ops;
143+
}
108144
return 0;
109145
}
110146

@@ -135,9 +171,9 @@ static int v2_write_file_info(struct super_block *sb, int type)
135171
return 0;
136172
}
137173

138-
static void v2_disk2memdqb(struct dquot *dquot, void *dp)
174+
static void v2r0_disk2memdqb(struct dquot *dquot, void *dp)
139175
{
140-
struct v2_disk_dqblk *d = dp, empty;
176+
struct v2r0_disk_dqblk *d = dp, empty;
141177
struct mem_dqblk *m = &dquot->dq_dqb;
142178

143179
m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit);
@@ -149,15 +185,15 @@ static void v2_disk2memdqb(struct dquot *dquot, void *dp)
149185
m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
150186
m->dqb_btime = le64_to_cpu(d->dqb_btime);
151187
/* We need to escape back all-zero structure */
152-
memset(&empty, 0, sizeof(struct v2_disk_dqblk));
188+
memset(&empty, 0, sizeof(struct v2r0_disk_dqblk));
153189
empty.dqb_itime = cpu_to_le64(1);
154-
if (!memcmp(&empty, dp, sizeof(struct v2_disk_dqblk)))
190+
if (!memcmp(&empty, dp, sizeof(struct v2r0_disk_dqblk)))
155191
m->dqb_itime = 0;
156192
}
157193

158-
static void v2_mem2diskdqb(void *dp, struct dquot *dquot)
194+
static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)
159195
{
160-
struct v2_disk_dqblk *d = dp;
196+
struct v2r0_disk_dqblk *d = dp;
161197
struct mem_dqblk *m = &dquot->dq_dqb;
162198
struct qtree_mem_dqinfo *info =
163199
sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
@@ -175,9 +211,60 @@ static void v2_mem2diskdqb(void *dp, struct dquot *dquot)
175211
d->dqb_itime = cpu_to_le64(1);
176212
}
177213

178-
static int v2_is_id(void *dp, struct dquot *dquot)
214+
static int v2r0_is_id(void *dp, struct dquot *dquot)
215+
{
216+
struct v2r0_disk_dqblk *d = dp;
217+
struct qtree_mem_dqinfo *info =
218+
sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
219+
220+
if (qtree_entry_unused(info, dp))
221+
return 0;
222+
return le32_to_cpu(d->dqb_id) == dquot->dq_id;
223+
}
224+
225+
static void v2r1_disk2memdqb(struct dquot *dquot, void *dp)
226+
{
227+
struct v2r1_disk_dqblk *d = dp, empty;
228+
struct mem_dqblk *m = &dquot->dq_dqb;
229+
230+
m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);
231+
m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);
232+
m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);
233+
m->dqb_itime = le64_to_cpu(d->dqb_itime);
234+
m->dqb_bhardlimit = v2_qbtos(le64_to_cpu(d->dqb_bhardlimit));
235+
m->dqb_bsoftlimit = v2_qbtos(le64_to_cpu(d->dqb_bsoftlimit));
236+
m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
237+
m->dqb_btime = le64_to_cpu(d->dqb_btime);
238+
/* We need to escape back all-zero structure */
239+
memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));
240+
empty.dqb_itime = cpu_to_le64(1);
241+
if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk)))
242+
m->dqb_itime = 0;
243+
}
244+
245+
static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)
246+
{
247+
struct v2r1_disk_dqblk *d = dp;
248+
struct mem_dqblk *m = &dquot->dq_dqb;
249+
struct qtree_mem_dqinfo *info =
250+
sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
251+
252+
d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
253+
d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
254+
d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
255+
d->dqb_itime = cpu_to_le64(m->dqb_itime);
256+
d->dqb_bhardlimit = cpu_to_le64(v2_stoqb(m->dqb_bhardlimit));
257+
d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(m->dqb_bsoftlimit));
258+
d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
259+
d->dqb_btime = cpu_to_le64(m->dqb_btime);
260+
d->dqb_id = cpu_to_le32(dquot->dq_id);
261+
if (qtree_entry_unused(info, dp))
262+
d->dqb_itime = cpu_to_le64(1);
263+
}
264+
265+
static int v2r1_is_id(void *dp, struct dquot *dquot)
179266
{
180-
struct v2_disk_dqblk *d = dp;
267+
struct v2r1_disk_dqblk *d = dp;
181268
struct qtree_mem_dqinfo *info =
182269
sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
183270

@@ -217,20 +304,32 @@ static const struct quota_format_ops v2_format_ops = {
217304
.release_dqblk = v2_release_dquot,
218305
};
219306

220-
static struct quota_format_type v2_quota_format = {
307+
static struct quota_format_type v2r0_quota_format = {
221308
.qf_fmt_id = QFMT_VFS_V0,
222309
.qf_ops = &v2_format_ops,
223310
.qf_owner = THIS_MODULE
224311
};
225312

313+
static struct quota_format_type v2r1_quota_format = {
314+
.qf_fmt_id = QFMT_VFS_V1,
315+
.qf_ops = &v2_format_ops,
316+
.qf_owner = THIS_MODULE
317+
};
318+
226319
static int __init init_v2_quota_format(void)
227320
{
228-
return register_quota_format(&v2_quota_format);
321+
int ret;
322+
323+
ret = register_quota_format(&v2r0_quota_format);
324+
if (ret)
325+
return ret;
326+
return register_quota_format(&v2r1_quota_format);
229327
}
230328

231329
static void __exit exit_v2_quota_format(void)
232330
{
233-
unregister_quota_format(&v2_quota_format);
331+
unregister_quota_format(&v2r0_quota_format);
332+
unregister_quota_format(&v2r1_quota_format);
234333
}
235334

236335
module_init(init_v2_quota_format);

fs/quota/quotaio_v2.h

+16-3
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
}
1818

1919
#define V2_INITQVERSIONS {\
20-
0, /* USRQUOTA */\
21-
0 /* GRPQUOTA */\
20+
1, /* USRQUOTA */\
21+
1 /* GRPQUOTA */\
2222
}
2323

2424
/* First generic header */
@@ -32,7 +32,7 @@ struct v2_disk_dqheader {
3232
* (as it appears on disk) - the file is a radix tree whose leaves point
3333
* to blocks of these structures.
3434
*/
35-
struct v2_disk_dqblk {
35+
struct v2r0_disk_dqblk {
3636
__le32 dqb_id; /* id this quota applies to */
3737
__le32 dqb_ihardlimit; /* absolute limit on allocated inodes */
3838
__le32 dqb_isoftlimit; /* preferred inode limit */
@@ -44,6 +44,19 @@ struct v2_disk_dqblk {
4444
__le64 dqb_itime; /* time limit for excessive inode use */
4545
};
4646

47+
struct v2r1_disk_dqblk {
48+
__le32 dqb_id; /* id this quota applies to */
49+
__le32 dqb_pad;
50+
__le64 dqb_ihardlimit; /* absolute limit on allocated inodes */
51+
__le64 dqb_isoftlimit; /* preferred inode limit */
52+
__le64 dqb_curinodes; /* current # allocated inodes */
53+
__le64 dqb_bhardlimit; /* absolute limit on disk space (in QUOTABLOCK_SIZE) */
54+
__le64 dqb_bsoftlimit; /* preferred limit on disk space (in QUOTABLOCK_SIZE) */
55+
__le64 dqb_curspace; /* current space occupied (in bytes) */
56+
__le64 dqb_btime; /* time limit for excessive disk use */
57+
__le64 dqb_itime; /* time limit for excessive inode use */
58+
};
59+
4760
/* Header with type and version specific information */
4861
struct v2_disk_dqinfo {
4962
__le32 dqi_bgrace; /* Time before block soft limit becomes hard limit */

include/linux/quota.h

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
#define QFMT_VFS_OLD 1
7575
#define QFMT_VFS_V0 2
7676
#define QFMT_OCFS2 3
77+
#define QFMT_VFS_V1 4
7778

7879
/* Size of block in which space limits are passed through the quota
7980
* interface */

0 commit comments

Comments
 (0)