From patchwork Wed Jun 21 21:21:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tahsin Erdogan X-Patchwork-Id: 779147 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3wtHnG3Rlvz9s2s for ; Thu, 22 Jun 2017 07:25:38 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="DB5fpQUz"; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752042AbdFUVZJ (ORCPT ); Wed, 21 Jun 2017 17:25:09 -0400 Received: from mail-pg0-f45.google.com ([74.125.83.45]:35800 "EHLO mail-pg0-f45.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752751AbdFUVWa (ORCPT ); Wed, 21 Jun 2017 17:22:30 -0400 Received: by mail-pg0-f45.google.com with SMTP id 132so48543420pgb.2 for ; Wed, 21 Jun 2017 14:22:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=4/2pOIS1J1ZEzaPHkhNKunlJRk/ld9jXcaGokcB5fjk=; b=DB5fpQUzjSNwfbIhIeIaOV4OSkpJ8zdNQpMxTVvHev6z+HDGKBjt4X5WAtPm1at2rG Iv8ASpVQVNSSy1ItMiEQVqKSpQaQ6alrWhvmMdi04WGiT0CD5pTNCxUUotZg3mAHL2v8 gBnaDTsv9xp3zkZ5vld1UAQSfn7sehlF77VqgpQQ+mFgYal3oG/F9U1UA7jMV3xpg0xP +foYjKhIRdEGaKLIQ+h1rw5UptK/XC+OQdw0TvqXLXOUFnJEG/BJPJVh2YPgf6vKl9pM iMLU86Ds1VIE9MEEm//6X/gyifwpABxktaeuiziEhYT7WfVewK24UTgjBLEnUBCTs8Zs QdNQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=4/2pOIS1J1ZEzaPHkhNKunlJRk/ld9jXcaGokcB5fjk=; b=tHxSch3uJM32kOVp0PThcRITlVEyudw57MvihnyNzEXMkCw6lp+YHCw0o/mMZ2eh50 y1OIS3VhtQ6M+9tpIwiYBjULHv757i7y0N2yoVTCEp7tgo25CRDKWXoHgqLBnuYs1CFU mlCURY/+FJjZwK2zsWyFPFFuQ8Ssu2Fb5qeXLQKgyiE2L+M5+qw7C4mdmv0KeBuycjfj +uEo0o18odx/DhmZ0tqyf4zG5VolsEwkWjMJGlVyMQvsyShrSpWb/OATJf6cGkl+CvFh jW6A3ohrdZml8vDaDzdxBeMzJiMcSfFBEYM/p8ocrwiRG+gx6DTFMM4BGQ25tNMcS2GN 2RVQ== X-Gm-Message-State: AKS2vOwI+WMI0XbyIekd1dEWzllpySkz9bxR304zPqsibsd46Xkt6G13 ztgRV7sC3Cd86yvj X-Received: by 10.99.126.8 with SMTP id z8mr39990865pgc.149.1498080149814; Wed, 21 Jun 2017 14:22:29 -0700 (PDT) Received: from tahsin1.svl.corp.google.com ([100.123.230.167]) by smtp.gmail.com with ESMTPSA id s64sm1829980pfd.77.2017.06.21.14.22.28 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 21 Jun 2017 14:22:29 -0700 (PDT) From: Tahsin Erdogan To: Andreas Dilger , "Darrick J . Wong" , Jan Kara , Theodore Ts'o , linux-ext4@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Tahsin Erdogan Subject: [PATCH 28/32] quota: add get_inode_usage callback to transfer multi-inode charges Date: Wed, 21 Jun 2017 14:21:38 -0700 Message-Id: <20170621212142.16581-28-tahsin@google.com> X-Mailer: git-send-email 2.13.1.611.g7e3b11ae1-goog In-Reply-To: <20170621212142.16581-1-tahsin@google.com> References: <20170621212142.16581-1-tahsin@google.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org 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. Signed-off-by: Tahsin Erdogan Acked-by: Jan Kara --- v3: added Acked-by v2: - added get_inode_usage() callback to query total inodes charge to be transferred fs/ext4/inode.c | 7 +++++++ fs/ext4/ioctl.c | 6 ++++++ fs/ext4/super.c | 21 ++++++++++---------- fs/ext4/xattr.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ext4/xattr.h | 2 ++ fs/quota/dquot.c | 16 +++++++++++---- include/linux/quota.h | 2 ++ 7 files changed, 94 insertions(+), 14 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index ea95bd9eab81..cd22de0b5d2c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5295,7 +5295,14 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) error = PTR_ERR(handle); goto err_out; } + + /* dquot_transfer() calls back ext4_get_inode_usage() which + * counts xattr inode references. + */ + down_read(&EXT4_I(inode)->xattr_sem); error = dquot_transfer(inode, attr); + up_read(&EXT4_I(inode)->xattr_sem); + if (error) { ext4_journal_stop(handle); 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) transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid)); if (!IS_ERR(transfer_to[PRJQUOTA])) { + + /* __dquot_transfer() calls back ext4_get_inode_usage() which + * counts xattr inode references. + */ + down_read(&EXT4_I(inode)->xattr_sem); err = __dquot_transfer(inode, transfer_to); + up_read(&EXT4_I(inode)->xattr_sem); dqput(transfer_to[PRJQUOTA]); if (err) goto out_dirty; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 70ff5e298ddf..f347b01ca5ca 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1263,16 +1263,17 @@ static struct dquot **ext4_get_dquots(struct inode *inode) } static const struct dquot_operations ext4_quota_operations = { - .get_reserved_space = ext4_get_reserved_space, - .write_dquot = ext4_write_dquot, - .acquire_dquot = ext4_acquire_dquot, - .release_dquot = ext4_release_dquot, - .mark_dirty = ext4_mark_dquot_dirty, - .write_info = ext4_write_info, - .alloc_dquot = dquot_alloc, - .destroy_dquot = dquot_destroy, - .get_projid = ext4_get_projid, - .get_next_id = ext4_get_next_id, + .get_reserved_space = ext4_get_reserved_space, + .write_dquot = ext4_write_dquot, + .acquire_dquot = ext4_acquire_dquot, + .release_dquot = ext4_release_dquot, + .mark_dirty = ext4_mark_dquot_dirty, + .write_info = ext4_write_info, + .alloc_dquot = dquot_alloc, + .destroy_dquot = dquot_destroy, + .get_projid = ext4_get_projid, + .get_inode_usage = ext4_get_inode_usage, + .get_next_id = ext4_get_next_id, }; static const struct quotactl_ops ext4_qctl_operations = { diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 8271c81ee8a1..8284cdfe7d80 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -733,6 +733,60 @@ static void ext4_xattr_update_super_block(handle_t *handle, } } +int ext4_get_inode_usage(struct inode *inode, qsize_t *usage) +{ + struct ext4_iloc iloc = { .bh = NULL }; + struct buffer_head *bh = NULL; + struct ext4_inode *raw_inode; + struct ext4_xattr_ibody_header *header; + struct ext4_xattr_entry *entry; + qsize_t ea_inode_refs = 0; + void *end; + int ret; + + lockdep_assert_held_read(&EXT4_I(inode)->xattr_sem); + + if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) { + ret = ext4_get_inode_loc(inode, &iloc); + if (ret) + goto out; + raw_inode = ext4_raw_inode(&iloc); + header = IHDR(inode, raw_inode); + end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; + ret = xattr_check_inode(inode, header, end); + if (ret) + goto out; + + for (entry = IFIRST(header); !IS_LAST_ENTRY(entry); + entry = EXT4_XATTR_NEXT(entry)) + if (entry->e_value_inum) + ea_inode_refs++; + } + + if (EXT4_I(inode)->i_file_acl) { + bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl); + if (!bh) { + ret = -EIO; + goto out; + } + + if (ext4_xattr_check_block(inode, bh)) { + ret = -EFSCORRUPTED; + goto out; + } + + for (entry = BFIRST(bh); !IS_LAST_ENTRY(entry); + entry = EXT4_XATTR_NEXT(entry)) + if (entry->e_value_inum) + ea_inode_refs++; + } + *usage = ea_inode_refs + 1; +out: + brelse(iloc.bh); + brelse(bh); + return ret; +} + static inline size_t round_up_cluster(struct inode *inode, size_t length) { 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); #else static inline void ext4_xattr_inode_set_class(struct inode *ea_inode) { } #endif + +extern 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) { qsize_t space, cur_space; qsize_t rsv_space = 0; + qsize_t inode_usage = 1; struct dquot *transfer_from[MAXQUOTAS] = {}; int cnt, ret = 0; char is_valid[MAXQUOTAS] = {}; @@ -1919,6 +1920,13 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to) if (IS_NOQUOTA(inode)) return 0; + + if (inode->i_sb->dq_op->get_inode_usage) { + ret = inode->i_sb->dq_op->get_inode_usage(inode, &inode_usage); + if (ret) + return ret; + } + /* Initialize the arrays */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { warn_to[cnt].w_type = QUOTA_NL_NOWARN; @@ -1946,7 +1954,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to) continue; is_valid[cnt] = 1; transfer_from[cnt] = i_dquot(inode)[cnt]; - ret = check_idq(transfer_to[cnt], 1, &warn_to[cnt]); + ret = check_idq(transfer_to[cnt], inode_usage, &warn_to[cnt]); if (ret) goto over_quota; 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) /* Due to IO error we might not have transfer_from[] structure */ if (transfer_from[cnt]) { int wtype; - wtype = info_idq_free(transfer_from[cnt], 1); + wtype = info_idq_free(transfer_from[cnt], inode_usage); if (wtype != QUOTA_NL_NOWARN) prepare_warning(&warn_from_inodes[cnt], transfer_from[cnt], wtype); @@ -1971,13 +1979,13 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to) if (wtype != QUOTA_NL_NOWARN) prepare_warning(&warn_from_space[cnt], transfer_from[cnt], wtype); - dquot_decr_inodes(transfer_from[cnt], 1); + dquot_decr_inodes(transfer_from[cnt], inode_usage); dquot_decr_space(transfer_from[cnt], cur_space); dquot_free_reserved_space(transfer_from[cnt], rsv_space); } - dquot_incr_inodes(transfer_to[cnt], 1); + dquot_incr_inodes(transfer_to[cnt], inode_usage); dquot_incr_space(transfer_to[cnt], cur_space); dquot_resv_space(transfer_to[cnt], rsv_space); 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 { * quota code only */ qsize_t *(*get_reserved_space) (struct inode *); int (*get_projid) (struct inode *, kprojid_t *);/* Get project ID */ + /* Get number of inodes that were charged for a given inode */ + int (*get_inode_usage) (struct inode *, qsize_t *); /* Get next ID with active quota structure */ int (*get_next_id) (struct super_block *sb, struct kqid *qid); };