summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTahsin Erdogan <tahsin@google.com>2017-06-22 11:46:48 -0400
committerTheodore Ts'o <tytso@mit.edu>2017-06-22 11:46:48 -0400
commit7a9ca53aea10ad4677a0f347ad7639c304b80194 (patch)
tree8ad3016dbd9f8820b17a7df3d74233cad1bd52e0
parentdec214d00e0d78a08b947d7dccdfdb84407a9f4d (diff)
quota: add get_inode_usage callback to transfer multi-inode charges
Ext4 ea_inode feature allows storing xattr values in external inodes to be able to store values that are bigger than a block in size. Ext4 also has deduplication support for these type of inodes. With deduplication, the actual storage waste is eliminated but the users of such inodes are still charged full quota for the inodes as if there was no sharing happening in the background. This design requires ext4 to manually charge the users because the inodes are shared. An implication of this is that, if someone calls chown on a file that has such references we need to transfer the quota for the file and xattr inodes. Current dquot_transfer() function implicitly transfers one inode charge. With ea_inode feature, we would like to transfer multiple inode charges. Add get_inode_usage callback which can interrogate the total number of inodes that were charged for a given inode. [ Applied fix from Colin King to make sure the 'ret' variable is initialized on the successful return path. Detected by CoverityScan, CID#1446616 ("Uninitialized scalar variable") --tytso] Signed-off-by: Tahsin Erdogan <tahsin@google.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Colin Ian King <colin.king@canonical.com> Acked-by: Jan Kara <jack@suse.cz>
-rw-r--r--fs/ext4/inode.c7
-rw-r--r--fs/ext4/ioctl.c6
-rw-r--r--fs/ext4/super.c21
-rw-r--r--fs/ext4/xattr.c55
-rw-r--r--fs/ext4/xattr.h2
-rw-r--r--fs/quota/dquot.c16
-rw-r--r--include/linux/quota.h2
7 files changed, 95 insertions, 14 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 962f28a0e176..d9733aa955e9 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -5295,7 +5295,14 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
5295 error = PTR_ERR(handle); 5295 error = PTR_ERR(handle);
5296 goto err_out; 5296 goto err_out;
5297 } 5297 }
5298
5299 /* dquot_transfer() calls back ext4_get_inode_usage() which
5300 * counts xattr inode references.
5301 */
5302 down_read(&EXT4_I(inode)->xattr_sem);
5298 error = dquot_transfer(inode, attr); 5303 error = dquot_transfer(inode, attr);
5304 up_read(&EXT4_I(inode)->xattr_sem);
5305
5299 if (error) { 5306 if (error) {
5300 ext4_journal_stop(handle); 5307 ext4_journal_stop(handle);
5301 return error; 5308 return error;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index dde8deb11e59..42b3a73143cf 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -373,7 +373,13 @@ static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
373 373
374 transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid)); 374 transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
375 if (!IS_ERR(transfer_to[PRJQUOTA])) { 375 if (!IS_ERR(transfer_to[PRJQUOTA])) {
376
377 /* __dquot_transfer() calls back ext4_get_inode_usage() which
378 * counts xattr inode references.
379 */
380 down_read(&EXT4_I(inode)->xattr_sem);
376 err = __dquot_transfer(inode, transfer_to); 381 err = __dquot_transfer(inode, transfer_to);
382 up_read(&EXT4_I(inode)->xattr_sem);
377 dqput(transfer_to[PRJQUOTA]); 383 dqput(transfer_to[PRJQUOTA]);
378 if (err) 384 if (err)
379 goto out_dirty; 385 goto out_dirty;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index d501f8256dc4..5ac76e8d4013 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1263,16 +1263,17 @@ static struct dquot **ext4_get_dquots(struct inode *inode)
1263} 1263}
1264 1264
1265static const struct dquot_operations ext4_quota_operations = { 1265static const struct dquot_operations ext4_quota_operations = {
1266 .get_reserved_space = ext4_get_reserved_space, 1266 .get_reserved_space = ext4_get_reserved_space,
1267 .write_dquot = ext4_write_dquot, 1267 .write_dquot = ext4_write_dquot,
1268 .acquire_dquot = ext4_acquire_dquot, 1268 .acquire_dquot = ext4_acquire_dquot,
1269 .release_dquot = ext4_release_dquot, 1269 .release_dquot = ext4_release_dquot,
1270 .mark_dirty = ext4_mark_dquot_dirty, 1270 .mark_dirty = ext4_mark_dquot_dirty,
1271 .write_info = ext4_write_info, 1271 .write_info = ext4_write_info,
1272 .alloc_dquot = dquot_alloc, 1272 .alloc_dquot = dquot_alloc,
1273 .destroy_dquot = dquot_destroy, 1273 .destroy_dquot = dquot_destroy,
1274 .get_projid = ext4_get_projid, 1274 .get_projid = ext4_get_projid,
1275 .get_next_id = ext4_get_next_id, 1275 .get_inode_usage = ext4_get_inode_usage,
1276 .get_next_id = ext4_get_next_id,
1276}; 1277};
1277 1278
1278static const struct quotactl_ops ext4_qctl_operations = { 1279static const struct quotactl_ops ext4_qctl_operations = {
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index a4c8fe3692a2..22bfb6221a2d 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -733,6 +733,61 @@ static void ext4_xattr_update_super_block(handle_t *handle,
733 } 733 }
734} 734}
735 735
736int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)
737{
738 struct ext4_iloc iloc = { .bh = NULL };
739 struct buffer_head *bh = NULL;
740 struct ext4_inode *raw_inode;
741 struct ext4_xattr_ibody_header *header;
742 struct ext4_xattr_entry *entry;
743 qsize_t ea_inode_refs = 0;
744 void *end;
745 int ret;
746
747 lockdep_assert_held_read(&EXT4_I(inode)->xattr_sem);
748
749 if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
750 ret = ext4_get_inode_loc(inode, &iloc);
751 if (ret)
752 goto out;
753 raw_inode = ext4_raw_inode(&iloc);
754 header = IHDR(inode, raw_inode);
755 end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
756 ret = xattr_check_inode(inode, header, end);
757 if (ret)
758 goto out;
759
760 for (entry = IFIRST(header); !IS_LAST_ENTRY(entry);
761 entry = EXT4_XATTR_NEXT(entry))
762 if (entry->e_value_inum)
763 ea_inode_refs++;
764 }
765
766 if (EXT4_I(inode)->i_file_acl) {
767 bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl);
768 if (!bh) {
769 ret = -EIO;
770 goto out;
771 }
772
773 if (ext4_xattr_check_block(inode, bh)) {
774 ret = -EFSCORRUPTED;
775 goto out;
776 }
777
778 for (entry = BFIRST(bh); !IS_LAST_ENTRY(entry);
779 entry = EXT4_XATTR_NEXT(entry))
780 if (entry->e_value_inum)
781 ea_inode_refs++;
782 }
783 *usage = ea_inode_refs + 1;
784 ret = 0;
785out:
786 brelse(iloc.bh);
787 brelse(bh);
788 return ret;
789}
790
736static inline size_t round_up_cluster(struct inode *inode, size_t length) 791static inline size_t round_up_cluster(struct inode *inode, size_t length)
737{ 792{
738 struct super_block *sb = inode->i_sb; 793 struct super_block *sb = inode->i_sb;
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 67616cb9a059..26119a67c8c3 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -193,3 +193,5 @@ extern void ext4_xattr_inode_set_class(struct inode *ea_inode);
193#else 193#else
194static inline void ext4_xattr_inode_set_class(struct inode *ea_inode) { } 194static inline void ext4_xattr_inode_set_class(struct inode *ea_inode) { }
195#endif 195#endif
196
197extern int ext4_get_inode_usage(struct inode *inode, qsize_t *usage);
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 48813aeaab80..53a17496c5c5 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1910,6 +1910,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
1910{ 1910{
1911 qsize_t space, cur_space; 1911 qsize_t space, cur_space;
1912 qsize_t rsv_space = 0; 1912 qsize_t rsv_space = 0;
1913 qsize_t inode_usage = 1;
1913 struct dquot *transfer_from[MAXQUOTAS] = {}; 1914 struct dquot *transfer_from[MAXQUOTAS] = {};
1914 int cnt, ret = 0; 1915 int cnt, ret = 0;
1915 char is_valid[MAXQUOTAS] = {}; 1916 char is_valid[MAXQUOTAS] = {};
@@ -1919,6 +1920,13 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
1919 1920
1920 if (IS_NOQUOTA(inode)) 1921 if (IS_NOQUOTA(inode))
1921 return 0; 1922 return 0;
1923
1924 if (inode->i_sb->dq_op->get_inode_usage) {
1925 ret = inode->i_sb->dq_op->get_inode_usage(inode, &inode_usage);
1926 if (ret)
1927 return ret;
1928 }
1929
1922 /* Initialize the arrays */ 1930 /* Initialize the arrays */
1923 for (cnt = 0; cnt < MAXQUOTAS; cnt++) { 1931 for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
1924 warn_to[cnt].w_type = QUOTA_NL_NOWARN; 1932 warn_to[cnt].w_type = QUOTA_NL_NOWARN;
@@ -1946,7 +1954,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
1946 continue; 1954 continue;
1947 is_valid[cnt] = 1; 1955 is_valid[cnt] = 1;
1948 transfer_from[cnt] = i_dquot(inode)[cnt]; 1956 transfer_from[cnt] = i_dquot(inode)[cnt];
1949 ret = check_idq(transfer_to[cnt], 1, &warn_to[cnt]); 1957 ret = check_idq(transfer_to[cnt], inode_usage, &warn_to[cnt]);
1950 if (ret) 1958 if (ret)
1951 goto over_quota; 1959 goto over_quota;
1952 ret = check_bdq(transfer_to[cnt], space, 0, &warn_to[cnt]); 1960 ret = check_bdq(transfer_to[cnt], space, 0, &warn_to[cnt]);
@@ -1963,7 +1971,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
1963 /* Due to IO error we might not have transfer_from[] structure */ 1971 /* Due to IO error we might not have transfer_from[] structure */
1964 if (transfer_from[cnt]) { 1972 if (transfer_from[cnt]) {
1965 int wtype; 1973 int wtype;
1966 wtype = info_idq_free(transfer_from[cnt], 1); 1974 wtype = info_idq_free(transfer_from[cnt], inode_usage);
1967 if (wtype != QUOTA_NL_NOWARN) 1975 if (wtype != QUOTA_NL_NOWARN)
1968 prepare_warning(&warn_from_inodes[cnt], 1976 prepare_warning(&warn_from_inodes[cnt],
1969 transfer_from[cnt], wtype); 1977 transfer_from[cnt], wtype);
@@ -1971,13 +1979,13 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
1971 if (wtype != QUOTA_NL_NOWARN) 1979 if (wtype != QUOTA_NL_NOWARN)
1972 prepare_warning(&warn_from_space[cnt], 1980 prepare_warning(&warn_from_space[cnt],
1973 transfer_from[cnt], wtype); 1981 transfer_from[cnt], wtype);
1974 dquot_decr_inodes(transfer_from[cnt], 1); 1982 dquot_decr_inodes(transfer_from[cnt], inode_usage);
1975 dquot_decr_space(transfer_from[cnt], cur_space); 1983 dquot_decr_space(transfer_from[cnt], cur_space);
1976 dquot_free_reserved_space(transfer_from[cnt], 1984 dquot_free_reserved_space(transfer_from[cnt],
1977 rsv_space); 1985 rsv_space);
1978 } 1986 }
1979 1987
1980 dquot_incr_inodes(transfer_to[cnt], 1); 1988 dquot_incr_inodes(transfer_to[cnt], inode_usage);
1981 dquot_incr_space(transfer_to[cnt], cur_space); 1989 dquot_incr_space(transfer_to[cnt], cur_space);
1982 dquot_resv_space(transfer_to[cnt], rsv_space); 1990 dquot_resv_space(transfer_to[cnt], rsv_space);
1983 1991
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 3434eef2a5aa..bfd077ca6ac3 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -332,6 +332,8 @@ struct dquot_operations {
332 * quota code only */ 332 * quota code only */
333 qsize_t *(*get_reserved_space) (struct inode *); 333 qsize_t *(*get_reserved_space) (struct inode *);
334 int (*get_projid) (struct inode *, kprojid_t *);/* Get project ID */ 334 int (*get_projid) (struct inode *, kprojid_t *);/* Get project ID */
335 /* Get number of inodes that were charged for a given inode */
336 int (*get_inode_usage) (struct inode *, qsize_t *);
335 /* Get next ID with active quota structure */ 337 /* Get next ID with active quota structure */
336 int (*get_next_id) (struct super_block *sb, struct kqid *qid); 338 int (*get_next_id) (struct super_block *sb, struct kqid *qid);
337}; 339};