add patch fix-off-by-one-fsmap-error-on-1k-block-filesystems
[ext4-patch-queue.git] / add-get_inode_usage-callback-to-transfer-multi-inode-charges
blob381c70258f2b5f97d3c8d305973312091700810c
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
13 inodes are shared.
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
19 charges.
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>
27 ---
28 v3: added Acked-by
30 v2:
31   - added get_inode_usage() callback to query total inodes charge to
32     be transferred
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
45 --- a/fs/ext4/inode.c
46 +++ b/fs/ext4/inode.c
47 @@ -5295,7 +5295,14 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
48                         error = PTR_ERR(handle);
49                         goto err_out;
50                 }
52 +               /* dquot_transfer() calls back ext4_get_inode_usage() which
53 +                * counts xattr inode references.
54 +                */
55 +               down_read(&EXT4_I(inode)->xattr_sem);
56                 error = dquot_transfer(inode, attr);
57 +               up_read(&EXT4_I(inode)->xattr_sem);
59                 if (error) {
60                         ext4_journal_stop(handle);
61                         return error;
62 diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
63 index dde8deb11e59..42b3a73143cf 100644
64 --- a/fs/ext4/ioctl.c
65 +++ b/fs/ext4/ioctl.c
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.
73 +                */
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]);
78                 if (err)
79                         goto out_dirty;
80 diff --git a/fs/ext4/super.c b/fs/ext4/super.c
81 index d501f8256dc4..5ac76e8d4013 100644
82 --- a/fs/ext4/super.c
83 +++ b/fs/ext4/super.c
84 @@ -1263,16 +1263,17 @@ static struct dquot **ext4_get_dquots(struct inode *inode)
85  }
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,
109  };
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,
117         }
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;
128 +       void *end;
129 +       int ret;
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);
135 +               if (ret)
136 +                       goto out;
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);
141 +               if (ret)
142 +                       goto out;
144 +               for (entry = IFIRST(header); !IS_LAST_ENTRY(entry);
145 +                    entry = EXT4_XATTR_NEXT(entry))
146 +                       if (entry->e_value_inum)
147 +                               ea_inode_refs++;
148 +       }
150 +       if (EXT4_I(inode)->i_file_acl) {
151 +               bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl);
152 +               if (!bh) {
153 +                       ret = -EIO;
154 +                       goto out;
155 +               }
157 +               if (ext4_xattr_check_block(inode, bh)) {
158 +                       ret = -EFSCORRUPTED;
159 +                       goto out;
160 +               }
162 +               for (entry = BFIRST(bh); !IS_LAST_ENTRY(entry);
163 +                    entry = EXT4_XATTR_NEXT(entry))
164 +                       if (entry->e_value_inum)
165 +                               ea_inode_refs++;
166 +       }
167 +       *usage = ea_inode_refs + 1;
168 +out:
169 +       brelse(iloc.bh);
170 +       brelse(bh);
171 +       return ret;
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);
182  #else
183  static inline void ext4_xattr_inode_set_class(struct inode *ea_inode) { }
184  #endif
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] = {};
197         int cnt, ret = 0;
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))
202                 return 0;
204 +       if (inode->i_sb->dq_op->get_inode_usage) {
205 +               ret = inode->i_sb->dq_op->get_inode_usage(inode, &inode_usage);
206 +               if (ret)
207 +                       return ret;
208 +       }
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)
214                         continue;
215                 is_valid[cnt] = 1;
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]);
219                 if (ret)
220                         goto over_quota;
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]) {
225                         int wtype;
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],
239                                                   rsv_space);
240                 }
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 {
252          * quota code only */
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);
259  };
260 -- 
261 2.13.1.611.g7e3b11ae1-goog