1 quota: add get_inode_usage callback to transfer multi-inode charges
3 From: Tahsin Erdogan <tahsin@google.com>
5 Ext4 ea_inode feature allows storing xattr values in external inodes to
6 be able to store values that are bigger than a block in size. Ext4 also
7 has deduplication support for these type of inodes. With deduplication,
8 the actual storage waste is eliminated but the users of such inodes are
9 still charged full quota for the inodes as if there was no sharing
10 happening in the background.
12 This design requires ext4 to manually charge the users because the
15 An implication of this is that, if someone calls chown on a file that
16 has such references we need to transfer the quota for the file and xattr
17 inodes. Current dquot_transfer() function implicitly transfers one inode
18 charge. With ea_inode feature, we would like to transfer multiple inode
21 Add get_inode_usage callback which can interrogate the total number of
22 inodes that were charged for a given inode.
24 Signed-off-by: Tahsin Erdogan <tahsin@google.com>
25 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
26 Acked-by: Jan Kara <jack@suse.cz>
31 - added get_inode_usage() callback to query total inodes charge to
34 fs/ext4/inode.c | 7 +++++++
35 fs/ext4/ioctl.c | 6 ++++++
36 fs/ext4/super.c | 21 ++++++++++----------
37 fs/ext4/xattr.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++
38 fs/ext4/xattr.h | 2 ++
39 fs/quota/dquot.c | 16 +++++++++++----
40 include/linux/quota.h | 2 ++
41 7 files changed, 94 insertions(+), 14 deletions(-)
43 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
44 index ea95bd9eab81..cd22de0b5d2c 100644
47 @@ -5295,7 +5295,14 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
48 error = PTR_ERR(handle);
52 + /* dquot_transfer() calls back ext4_get_inode_usage() which
53 + * counts xattr inode references.
55 + down_read(&EXT4_I(inode)->xattr_sem);
56 error = dquot_transfer(inode, attr);
57 + up_read(&EXT4_I(inode)->xattr_sem);
60 ext4_journal_stop(handle);
62 diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
63 index dde8deb11e59..42b3a73143cf 100644
66 @@ -373,7 +373,13 @@ static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
68 transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
69 if (!IS_ERR(transfer_to[PRJQUOTA])) {
71 + /* __dquot_transfer() calls back ext4_get_inode_usage() which
72 + * counts xattr inode references.
74 + down_read(&EXT4_I(inode)->xattr_sem);
75 err = __dquot_transfer(inode, transfer_to);
76 + up_read(&EXT4_I(inode)->xattr_sem);
77 dqput(transfer_to[PRJQUOTA]);
80 diff --git a/fs/ext4/super.c b/fs/ext4/super.c
81 index d501f8256dc4..5ac76e8d4013 100644
84 @@ -1263,16 +1263,17 @@ static struct dquot **ext4_get_dquots(struct inode *inode)
87 static const struct dquot_operations ext4_quota_operations = {
88 - .get_reserved_space = ext4_get_reserved_space,
89 - .write_dquot = ext4_write_dquot,
90 - .acquire_dquot = ext4_acquire_dquot,
91 - .release_dquot = ext4_release_dquot,
92 - .mark_dirty = ext4_mark_dquot_dirty,
93 - .write_info = ext4_write_info,
94 - .alloc_dquot = dquot_alloc,
95 - .destroy_dquot = dquot_destroy,
96 - .get_projid = ext4_get_projid,
97 - .get_next_id = ext4_get_next_id,
98 + .get_reserved_space = ext4_get_reserved_space,
99 + .write_dquot = ext4_write_dquot,
100 + .acquire_dquot = ext4_acquire_dquot,
101 + .release_dquot = ext4_release_dquot,
102 + .mark_dirty = ext4_mark_dquot_dirty,
103 + .write_info = ext4_write_info,
104 + .alloc_dquot = dquot_alloc,
105 + .destroy_dquot = dquot_destroy,
106 + .get_projid = ext4_get_projid,
107 + .get_inode_usage = ext4_get_inode_usage,
108 + .get_next_id = ext4_get_next_id,
111 static const struct quotactl_ops ext4_qctl_operations = {
112 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
113 index 15c9f736dcc4..a3f8bf4558f3 100644
114 --- a/fs/ext4/xattr.c
115 +++ b/fs/ext4/xattr.c
116 @@ -733,6 +733,60 @@ static void ext4_xattr_update_super_block(handle_t *handle,
120 +int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)
122 + struct ext4_iloc iloc = { .bh = NULL };
123 + struct buffer_head *bh = NULL;
124 + struct ext4_inode *raw_inode;
125 + struct ext4_xattr_ibody_header *header;
126 + struct ext4_xattr_entry *entry;
127 + qsize_t ea_inode_refs = 0;
131 + lockdep_assert_held_read(&EXT4_I(inode)->xattr_sem);
133 + if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
134 + ret = ext4_get_inode_loc(inode, &iloc);
137 + raw_inode = ext4_raw_inode(&iloc);
138 + header = IHDR(inode, raw_inode);
139 + end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
140 + ret = xattr_check_inode(inode, header, end);
144 + for (entry = IFIRST(header); !IS_LAST_ENTRY(entry);
145 + entry = EXT4_XATTR_NEXT(entry))
146 + if (entry->e_value_inum)
150 + if (EXT4_I(inode)->i_file_acl) {
151 + bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl);
157 + if (ext4_xattr_check_block(inode, bh)) {
158 + ret = -EFSCORRUPTED;
162 + for (entry = BFIRST(bh); !IS_LAST_ENTRY(entry);
163 + entry = EXT4_XATTR_NEXT(entry))
164 + if (entry->e_value_inum)
167 + *usage = ea_inode_refs + 1;
174 static inline size_t round_up_cluster(struct inode *inode, size_t length)
176 struct super_block *sb = inode->i_sb;
177 diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
178 index 67616cb9a059..26119a67c8c3 100644
179 --- a/fs/ext4/xattr.h
180 +++ b/fs/ext4/xattr.h
181 @@ -193,3 +193,5 @@ extern void ext4_xattr_inode_set_class(struct inode *ea_inode);
183 static inline void ext4_xattr_inode_set_class(struct inode *ea_inode) { }
186 +extern int ext4_get_inode_usage(struct inode *inode, qsize_t *usage);
187 diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
188 index 48813aeaab80..53a17496c5c5 100644
189 --- a/fs/quota/dquot.c
190 +++ b/fs/quota/dquot.c
191 @@ -1910,6 +1910,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
193 qsize_t space, cur_space;
194 qsize_t rsv_space = 0;
195 + qsize_t inode_usage = 1;
196 struct dquot *transfer_from[MAXQUOTAS] = {};
198 char is_valid[MAXQUOTAS] = {};
199 @@ -1919,6 +1920,13 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
201 if (IS_NOQUOTA(inode))
204 + if (inode->i_sb->dq_op->get_inode_usage) {
205 + ret = inode->i_sb->dq_op->get_inode_usage(inode, &inode_usage);
210 /* Initialize the arrays */
211 for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
212 warn_to[cnt].w_type = QUOTA_NL_NOWARN;
213 @@ -1946,7 +1954,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
216 transfer_from[cnt] = i_dquot(inode)[cnt];
217 - ret = check_idq(transfer_to[cnt], 1, &warn_to[cnt]);
218 + ret = check_idq(transfer_to[cnt], inode_usage, &warn_to[cnt]);
221 ret = check_bdq(transfer_to[cnt], space, 0, &warn_to[cnt]);
222 @@ -1963,7 +1971,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
223 /* Due to IO error we might not have transfer_from[] structure */
224 if (transfer_from[cnt]) {
226 - wtype = info_idq_free(transfer_from[cnt], 1);
227 + wtype = info_idq_free(transfer_from[cnt], inode_usage);
228 if (wtype != QUOTA_NL_NOWARN)
229 prepare_warning(&warn_from_inodes[cnt],
230 transfer_from[cnt], wtype);
231 @@ -1971,13 +1979,13 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
232 if (wtype != QUOTA_NL_NOWARN)
233 prepare_warning(&warn_from_space[cnt],
234 transfer_from[cnt], wtype);
235 - dquot_decr_inodes(transfer_from[cnt], 1);
236 + dquot_decr_inodes(transfer_from[cnt], inode_usage);
237 dquot_decr_space(transfer_from[cnt], cur_space);
238 dquot_free_reserved_space(transfer_from[cnt],
242 - dquot_incr_inodes(transfer_to[cnt], 1);
243 + dquot_incr_inodes(transfer_to[cnt], inode_usage);
244 dquot_incr_space(transfer_to[cnt], cur_space);
245 dquot_resv_space(transfer_to[cnt], rsv_space);
247 diff --git a/include/linux/quota.h b/include/linux/quota.h
248 index 3434eef2a5aa..bfd077ca6ac3 100644
249 --- a/include/linux/quota.h
250 +++ b/include/linux/quota.h
251 @@ -332,6 +332,8 @@ struct dquot_operations {
253 qsize_t *(*get_reserved_space) (struct inode *);
254 int (*get_projid) (struct inode *, kprojid_t *);/* Get project ID */
255 + /* Get number of inodes that were charged for a given inode */
256 + int (*get_inode_usage) (struct inode *, qsize_t *);
257 /* Get next ID with active quota structure */
258 int (*get_next_id) (struct super_block *sb, struct kqid *qid);
261 2.13.1.611.g7e3b11ae1-goog