Message ID | 20171220151841.22355-3-lhenriques@suse.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Wed, Dec 20, 2017 at 11:18 PM, Luis Henriques <lhenriques@suse.com> wrote: > This patch adds support for the max_files quota. It hooks into all the > ceph functions that add new filesystem objects that need to be checked > against the quota limits. When these limits are hit, -EDQUOT is returned. > > Note that we're not checking quotas on ceph_link(). ceph_link doesn't > really create a new inode, and since the MDS doesn't update the directory > statistics when a new (hard) link is created (only with symlinks), they > are not accounted as a new file. > > Link: http://tracker.ceph.com/issues/22372 > Signed-off-by: Luis Henriques <lhenriques@suse.com> > --- > fs/ceph/dir.c | 11 +++++++++ > fs/ceph/file.c | 4 +++- > fs/ceph/quota.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > fs/ceph/super.h | 1 + > 4 files changed, 84 insertions(+), 1 deletion(-) > > diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c > index 8a5266699b67..66550d92b1ac 100644 > --- a/fs/ceph/dir.c > +++ b/fs/ceph/dir.c > @@ -818,6 +818,9 @@ static int ceph_mknod(struct inode *dir, struct dentry *dentry, > if (ceph_snap(dir) != CEPH_NOSNAP) > return -EROFS; > > + if (ceph_quota_is_max_files_exceeded(dir)) > + return -EDQUOT; > + > err = ceph_pre_init_acls(dir, &mode, &acls); > if (err < 0) > return err; > @@ -871,6 +874,9 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry, > if (ceph_snap(dir) != CEPH_NOSNAP) > return -EROFS; > > + if (ceph_quota_is_max_files_exceeded(dir)) > + return -EDQUOT; > + > dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest); > req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS); > if (IS_ERR(req)) { > @@ -920,6 +926,11 @@ static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) > goto out; > } > > + if (ceph_quota_is_max_files_exceeded(dir)) { > + err = -EDQUOT; > + goto out; > + } > + > mode |= S_IFDIR; > err = ceph_pre_init_acls(dir, &mode, &acls); > if (err < 0) > diff --git a/fs/ceph/file.c b/fs/ceph/file.c > index 5c17125f45c7..5a77a66e3d6b 100644 > --- a/fs/ceph/file.c > +++ b/fs/ceph/file.c > @@ -371,7 +371,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, > struct ceph_mds_request *req; > struct dentry *dn; > struct ceph_acls_info acls = {}; > - int mask; > + int mask; > int err; > > dout("atomic_open %p dentry %p '%pd' %s flags %d mode 0%o\n", > @@ -382,6 +382,8 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, > return -ENAMETOOLONG; > > if (flags & O_CREAT) { > + if (ceph_quota_is_max_files_exceeded(dir)) > + return -EDQUOT; > err = ceph_pre_init_acls(dir, &mode, &acls); > if (err < 0) > return err; > diff --git a/fs/ceph/quota.c b/fs/ceph/quota.c > index 7bde6e85b609..06b3268f8f7f 100644 > --- a/fs/ceph/quota.c > +++ b/fs/ceph/quota.c > @@ -61,3 +61,72 @@ void ceph_handle_quota(struct ceph_mds_client *mdsc, > > iput(inode); > } > + > +enum quota_check_op { > + QUOTA_CHECK_MAX_FILES_OP /* check quota max_files limit */ > +}; > + > +/* > + * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each > + * realm, it will execute quota check operation defined by the 'op' parameter. > + * The snaprealm walk is interrupted if the quota check detects that the quota > + * is exceeded or if the root inode is reached. > + */ > +static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op, > + loff_t size) > +{ > + struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; > + struct ceph_inode_info *ci; > + struct ceph_snap_realm *realm, *next; > + struct ceph_vino vino; > + struct inode *ino; It's better to use name 'in' > + u64 max = 0, rvalue = 0; > + bool quota_exceeded = false, is_root = false; > + > + WARN_ON(!S_ISDIR(inode->i_mode)); > + > + down_read(&mdsc->snap_rwsem); > + realm = ceph_inode(inode)->i_snap_realm; > + ceph_get_snap_realm(mdsc, realm); > + while (realm) { > + vino.ino = realm->ino; > + vino.snap = CEPH_NOSNAP; > + ino = ceph_find_inode(inode->i_sb, vino); > + if (!ino) { > + pr_warn("Failed to find inode for %llu\n", vino.ino); > + break; > + } > + ci = ceph_inode(ino); > + switch(op) { > + case QUOTA_CHECK_MAX_FILES_OP: > + spin_lock(&ci->i_ceph_lock); > + max = ci->i_max_files; > + rvalue = ci->i_rfiles + ci->i_rsubdirs; > + is_root = (ci->i_vino.ino == CEPH_INO_ROOT); > + spin_unlock(&ci->i_ceph_lock); > + quota_exceeded = (max && (rvalue >= max)); > + break; > + default: > + /* Shouldn't happen */ > + pr_warn("Invalid quota check op (%d)\n", op); > + is_root = true; /* Just break the loop */ > + } > + iput(ino); > + > + if (quota_exceeded || is_root) > + break; > + next = realm->parent; > + ceph_get_snap_realm(mdsc, next); > + ceph_put_snap_realm(mdsc, realm); > + realm = next; > + } > + ceph_put_snap_realm(mdsc, realm); > + up_read(&mdsc->snap_rwsem); > + > + return quota_exceeded; > +} > + > +bool ceph_quota_is_max_files_exceeded(struct inode *inode) > +{ > + return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 0); > +} > diff --git a/fs/ceph/super.h b/fs/ceph/super.h > index f998b7f076cf..20197e29a7f0 100644 > --- a/fs/ceph/super.h > +++ b/fs/ceph/super.h > @@ -1026,5 +1026,6 @@ extern void ceph_fs_debugfs_cleanup(struct ceph_fs_client *client); > extern void ceph_handle_quota(struct ceph_mds_client *mdsc, > struct ceph_mds_session *session, > struct ceph_msg *msg); > +extern bool ceph_quota_is_max_files_exceeded(struct inode *inode); > > #endif /* _FS_CEPH_SUPER_H */ > -- > To unsubscribe from this list: send the line "unsubscribe ceph-devel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe ceph-devel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 8a5266699b67..66550d92b1ac 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -818,6 +818,9 @@ static int ceph_mknod(struct inode *dir, struct dentry *dentry, if (ceph_snap(dir) != CEPH_NOSNAP) return -EROFS; + if (ceph_quota_is_max_files_exceeded(dir)) + return -EDQUOT; + err = ceph_pre_init_acls(dir, &mode, &acls); if (err < 0) return err; @@ -871,6 +874,9 @@ static int ceph_symlink(struct inode *dir, struct dentry *dentry, if (ceph_snap(dir) != CEPH_NOSNAP) return -EROFS; + if (ceph_quota_is_max_files_exceeded(dir)) + return -EDQUOT; + dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest); req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS); if (IS_ERR(req)) { @@ -920,6 +926,11 @@ static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) goto out; } + if (ceph_quota_is_max_files_exceeded(dir)) { + err = -EDQUOT; + goto out; + } + mode |= S_IFDIR; err = ceph_pre_init_acls(dir, &mode, &acls); if (err < 0) diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 5c17125f45c7..5a77a66e3d6b 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -371,7 +371,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, struct ceph_mds_request *req; struct dentry *dn; struct ceph_acls_info acls = {}; - int mask; + int mask; int err; dout("atomic_open %p dentry %p '%pd' %s flags %d mode 0%o\n", @@ -382,6 +382,8 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, return -ENAMETOOLONG; if (flags & O_CREAT) { + if (ceph_quota_is_max_files_exceeded(dir)) + return -EDQUOT; err = ceph_pre_init_acls(dir, &mode, &acls); if (err < 0) return err; diff --git a/fs/ceph/quota.c b/fs/ceph/quota.c index 7bde6e85b609..06b3268f8f7f 100644 --- a/fs/ceph/quota.c +++ b/fs/ceph/quota.c @@ -61,3 +61,72 @@ void ceph_handle_quota(struct ceph_mds_client *mdsc, iput(inode); } + +enum quota_check_op { + QUOTA_CHECK_MAX_FILES_OP /* check quota max_files limit */ +}; + +/* + * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each + * realm, it will execute quota check operation defined by the 'op' parameter. + * The snaprealm walk is interrupted if the quota check detects that the quota + * is exceeded or if the root inode is reached. + */ +static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op, + loff_t size) +{ + struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; + struct ceph_inode_info *ci; + struct ceph_snap_realm *realm, *next; + struct ceph_vino vino; + struct inode *ino; + u64 max = 0, rvalue = 0; + bool quota_exceeded = false, is_root = false; + + WARN_ON(!S_ISDIR(inode->i_mode)); + + down_read(&mdsc->snap_rwsem); + realm = ceph_inode(inode)->i_snap_realm; + ceph_get_snap_realm(mdsc, realm); + while (realm) { + vino.ino = realm->ino; + vino.snap = CEPH_NOSNAP; + ino = ceph_find_inode(inode->i_sb, vino); + if (!ino) { + pr_warn("Failed to find inode for %llu\n", vino.ino); + break; + } + ci = ceph_inode(ino); + switch(op) { + case QUOTA_CHECK_MAX_FILES_OP: + spin_lock(&ci->i_ceph_lock); + max = ci->i_max_files; + rvalue = ci->i_rfiles + ci->i_rsubdirs; + is_root = (ci->i_vino.ino == CEPH_INO_ROOT); + spin_unlock(&ci->i_ceph_lock); + quota_exceeded = (max && (rvalue >= max)); + break; + default: + /* Shouldn't happen */ + pr_warn("Invalid quota check op (%d)\n", op); + is_root = true; /* Just break the loop */ + } + iput(ino); + + if (quota_exceeded || is_root) + break; + next = realm->parent; + ceph_get_snap_realm(mdsc, next); + ceph_put_snap_realm(mdsc, realm); + realm = next; + } + ceph_put_snap_realm(mdsc, realm); + up_read(&mdsc->snap_rwsem); + + return quota_exceeded; +} + +bool ceph_quota_is_max_files_exceeded(struct inode *inode) +{ + return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 0); +} diff --git a/fs/ceph/super.h b/fs/ceph/super.h index f998b7f076cf..20197e29a7f0 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -1026,5 +1026,6 @@ extern void ceph_fs_debugfs_cleanup(struct ceph_fs_client *client); extern void ceph_handle_quota(struct ceph_mds_client *mdsc, struct ceph_mds_session *session, struct ceph_msg *msg); +extern bool ceph_quota_is_max_files_exceeded(struct inode *inode); #endif /* _FS_CEPH_SUPER_H */
This patch adds support for the max_files quota. It hooks into all the ceph functions that add new filesystem objects that need to be checked against the quota limits. When these limits are hit, -EDQUOT is returned. Note that we're not checking quotas on ceph_link(). ceph_link doesn't really create a new inode, and since the MDS doesn't update the directory statistics when a new (hard) link is created (only with symlinks), they are not accounted as a new file. Link: http://tracker.ceph.com/issues/22372 Signed-off-by: Luis Henriques <lhenriques@suse.com> --- fs/ceph/dir.c | 11 +++++++++ fs/ceph/file.c | 4 +++- fs/ceph/quota.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ceph/super.h | 1 + 4 files changed, 84 insertions(+), 1 deletion(-) -- To unsubscribe from this list: send the line "unsubscribe ceph-devel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html