diff mbox

[RFC,v2,1/1] quota: report root dir quota usage in statfs

Message ID 20180131105313.24802-2-lhenriques@suse.com (mailing list archive)
State New, archived
Headers show

Commit Message

Luis Henriques Jan. 31, 2018, 10:53 a.m. UTC
This commit changes statfs default behaviour when reporting usage
statistics.  Instead of using the overall filesystem usage, statfs now
reports the quota for the filesystem root, if ceph.quota.max_bytes has
been set for this inode.  If quota hasn't been set, it falls back to the
old statfs behaviour.

A new mount option is also added ('noquotadf') to disable this behaviour.

Signed-off-by: Luis Henriques <lhenriques@suse.com>
---
 Documentation/filesystems/ceph.txt |  4 +++
 fs/ceph/quota.c                    | 56 ++++++++++++++++++++++++++++++++++++++
 fs/ceph/super.c                    | 29 ++++++++++++++++++--
 fs/ceph/super.h                    |  3 ++
 4 files changed, 89 insertions(+), 3 deletions(-)

--
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

Comments

Yan, Zheng Feb. 1, 2018, 3:55 a.m. UTC | #1
> On 31 Jan 2018, at 18:53, Luis Henriques <lhenriques@suse.com> wrote:
> 
> This commit changes statfs default behaviour when reporting usage
> statistics.  Instead of using the overall filesystem usage, statfs now
> reports the quota for the filesystem root, if ceph.quota.max_bytes has
> been set for this inode.  If quota hasn't been set, it falls back to the
> old statfs behaviour.
> 
> A new mount option is also added ('noquotadf') to disable this behaviour.
> 
> Signed-off-by: Luis Henriques <lhenriques@suse.com>
> ---
> Documentation/filesystems/ceph.txt |  4 +++
> fs/ceph/quota.c                    | 56 ++++++++++++++++++++++++++++++++++++++
> fs/ceph/super.c                    | 29 ++++++++++++++++++--
> fs/ceph/super.h                    |  3 ++
> 4 files changed, 89 insertions(+), 3 deletions(-)
> 
> diff --git a/Documentation/filesystems/ceph.txt b/Documentation/filesystems/ceph.txt
> index 094772481263..d7f011ddc150 100644
> --- a/Documentation/filesystems/ceph.txt
> +++ b/Documentation/filesystems/ceph.txt
> @@ -149,6 +149,10 @@ Mount Options
>   noasyncreaddir
> 	Do not use the dcache as above for readdir.
> 
> +  noquotadf
> +        Report overall filesystem usage in statfs instead of using the root
> +        directory quota.
> +
> More Information
> ================
> 
> diff --git a/fs/ceph/quota.c b/fs/ceph/quota.c
> index 588744b4665f..242bfa5c0539 100644
> --- a/fs/ceph/quota.c
> +++ b/fs/ceph/quota.c
> @@ -18,6 +18,8 @@
>  * along with this program; if not, see <http://www.gnu.org/licenses/>.
>  */
> 
> +#include <linux/statfs.h>
> +
> #include "super.h"
> #include "mds_client.h"
> 
> @@ -303,3 +305,57 @@ bool ceph_quota_is_max_bytes_approaching(struct inode *inode, loff_t newsize)
> 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_APPROACHING_OP,
> 				    (newsize - size));
> }
> +
> +/*
> + * ceph_quota_update_statfs - if root has quota update statfs with quota status
> + * @fsc:	filesystem client instance
> + * @buf:	statfs to update
> + *
> + * If the mounted filesystem root has max_bytes quota set, update the filesystem
> + * statistics with the quota status.
> + *
> + * This function returns true if the stats have been updated, false otherwise.
> + */
> +bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf)
> +{
> +	struct ceph_mds_client *mdsc = fsc->mdsc;
> +	struct ceph_inode_info *ci;
> +	struct ceph_snap_realm *realm;
> +	struct inode *in;
> +	u64 total = 0, used, free;
> +	bool is_updated = false;
> +
> +	down_read(&mdsc->snap_rwsem);
> +	realm = get_quota_realm(mdsc, d_inode(fsc->sb->s_root));
> +	up_read(&mdsc->snap_rwsem);
> +	if (!realm)
> +		return false;
> +
> +	spin_lock(&realm->inodes_with_caps_lock);
> +	in = realm->inode ? igrab(realm->inode) : NULL;
> +	spin_unlock(&realm->inodes_with_caps_lock);
> +	if (in) {
> +		ci = ceph_inode(in);
> +		spin_lock(&ci->i_ceph_lock);
> +		if (ci->i_max_bytes) {
> +			total = ci->i_max_bytes >> CEPH_BLOCK_SHIFT;
> +			used = ci->i_rbytes >> CEPH_BLOCK_SHIFT;
> +			/* It is possible for a quota to be exceeded.
> +			 * Report 'zero' in that case
> +			 */
> +			free = total > used ? total - used : 0;
> +		}
> +		spin_unlock(&ci->i_ceph_lock);
> +		if (total) {
> +			buf->f_blocks = total;
> +			buf->f_bfree = free;
> +			buf->f_bavail = free;
> +			is_updated = true;
> +		}
> +		iput(in);
> +	}
> +	ceph_put_snap_realm(mdsc, realm);
> +
> +	return is_updated;
> +}
> +
> diff --git a/fs/ceph/super.c b/fs/ceph/super.c
> index 9b4929420c13..5062d52f161e 100644
> --- a/fs/ceph/super.c
> +++ b/fs/ceph/super.c
> @@ -76,9 +76,18 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf)
> 	 */
> 	buf->f_bsize = 1 << CEPH_BLOCK_SHIFT;
> 	buf->f_frsize = 1 << CEPH_BLOCK_SHIFT;
> -	buf->f_blocks = le64_to_cpu(st.kb) >> (CEPH_BLOCK_SHIFT-10);
> -	buf->f_bfree = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);
> -	buf->f_bavail = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);
> +
> +	/*
> +	 * By default use root quota for stats; fallback to overall filesystem
> +	 * usage if using 'noquotadf' mount option or if the root dir doesn't
> +	 * have max_bytes quota set.
> +	 */
> +	if ((fsc->mount_options->flags & CEPH_MOUNT_OPT_NOQUOTADF) ||
> +	    !ceph_quota_update_statfs(fsc, buf)) {
> +		buf->f_blocks = le64_to_cpu(st.kb) >> (CEPH_BLOCK_SHIFT-10);
> +		buf->f_bfree = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);
> +		buf->f_bavail = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);
> +	}
> 
> 	buf->f_files = le64_to_cpu(st.num_objects);
> 	buf->f_ffree = -1;
> @@ -151,6 +160,8 @@ enum {
> 	Opt_acl,
> #endif
> 	Opt_noacl,
> +	Opt_quotadf,
> +	Opt_noquotadf,
> };
> 
> static match_table_t fsopt_tokens = {
> @@ -187,6 +198,8 @@ static match_table_t fsopt_tokens = {
> 	{Opt_acl, "acl"},
> #endif
> 	{Opt_noacl, "noacl"},
> +	{Opt_quotadf, "quotadf"},
> +	{Opt_noquotadf, "noquotadf"},
> 	{-1, NULL}
> };
> 
> @@ -328,6 +341,12 @@ static int parse_fsopt_token(char *c, void *private)
> 	case Opt_norequire_active_mds:
> 		fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT;
> 		break;
> +	case Opt_quotadf:
> +		fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF;
> +		break;
> +	case Opt_noquotadf:
> +		fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF;
> +		break;
> #ifdef CONFIG_CEPH_FS_POSIX_ACL
> 	case Opt_acl:
> 		fsopt->sb_flags |= SB_POSIXACL;
> @@ -517,6 +536,10 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root)
> 	}
> 	if (fsopt->flags & CEPH_MOUNT_OPT_NOPOOLPERM)
> 		seq_puts(m, ",nopoolperm");
> +	if (fsopt->flags & CEPH_MOUNT_OPT_NOQUOTADF)
> +		seq_puts(m, ",noquotadf");
> +	else
> +		seq_puts(m, ",quotadf");
> 
> #ifdef CONFIG_CEPH_FS_POSIX_ACL
> 	if (fsopt->sb_flags & SB_POSIXACL)
> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> index 92d189074008..60ed9399fe73 100644
> --- a/fs/ceph/super.h
> +++ b/fs/ceph/super.h
> @@ -39,6 +39,7 @@
> #define CEPH_MOUNT_OPT_FSCACHE         (1<<10) /* use fscache */
> #define CEPH_MOUNT_OPT_NOPOOLPERM      (1<<11) /* no pool permission check */
> #define CEPH_MOUNT_OPT_MOUNTWAIT       (1<<12) /* mount waits if no mds is up */
> +#define CEPH_MOUNT_OPT_NOQUOTADF       (1<<13) /* no root dir quota in statfs */
> 
> #define CEPH_MOUNT_OPT_DEFAULT    CEPH_MOUNT_OPT_DCACHE
> 
> @@ -1111,5 +1112,7 @@ extern bool ceph_quota_is_max_bytes_exceeded(struct inode *inode,
> 					     loff_t newlen);
> extern bool ceph_quota_is_max_bytes_approaching(struct inode *inode,
> 						loff_t newlen);
> +extern bool ceph_quota_update_statfs(struct ceph_fs_client *fsc,
> +				     struct kstatfs *buf);
> 
> #endif /* _FS_CEPH_SUPER_H */

Applied, thanks

Yan, Zheng

--
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 mbox

Patch

diff --git a/Documentation/filesystems/ceph.txt b/Documentation/filesystems/ceph.txt
index 094772481263..d7f011ddc150 100644
--- a/Documentation/filesystems/ceph.txt
+++ b/Documentation/filesystems/ceph.txt
@@ -149,6 +149,10 @@  Mount Options
   noasyncreaddir
 	Do not use the dcache as above for readdir.
 
+  noquotadf
+        Report overall filesystem usage in statfs instead of using the root
+        directory quota.
+
 More Information
 ================
 
diff --git a/fs/ceph/quota.c b/fs/ceph/quota.c
index 588744b4665f..242bfa5c0539 100644
--- a/fs/ceph/quota.c
+++ b/fs/ceph/quota.c
@@ -18,6 +18,8 @@ 
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/statfs.h>
+
 #include "super.h"
 #include "mds_client.h"
 
@@ -303,3 +305,57 @@  bool ceph_quota_is_max_bytes_approaching(struct inode *inode, loff_t newsize)
 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_APPROACHING_OP,
 				    (newsize - size));
 }
+
+/*
+ * ceph_quota_update_statfs - if root has quota update statfs with quota status
+ * @fsc:	filesystem client instance
+ * @buf:	statfs to update
+ *
+ * If the mounted filesystem root has max_bytes quota set, update the filesystem
+ * statistics with the quota status.
+ *
+ * This function returns true if the stats have been updated, false otherwise.
+ */
+bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf)
+{
+	struct ceph_mds_client *mdsc = fsc->mdsc;
+	struct ceph_inode_info *ci;
+	struct ceph_snap_realm *realm;
+	struct inode *in;
+	u64 total = 0, used, free;
+	bool is_updated = false;
+
+	down_read(&mdsc->snap_rwsem);
+	realm = get_quota_realm(mdsc, d_inode(fsc->sb->s_root));
+	up_read(&mdsc->snap_rwsem);
+	if (!realm)
+		return false;
+
+	spin_lock(&realm->inodes_with_caps_lock);
+	in = realm->inode ? igrab(realm->inode) : NULL;
+	spin_unlock(&realm->inodes_with_caps_lock);
+	if (in) {
+		ci = ceph_inode(in);
+		spin_lock(&ci->i_ceph_lock);
+		if (ci->i_max_bytes) {
+			total = ci->i_max_bytes >> CEPH_BLOCK_SHIFT;
+			used = ci->i_rbytes >> CEPH_BLOCK_SHIFT;
+			/* It is possible for a quota to be exceeded.
+			 * Report 'zero' in that case
+			 */
+			free = total > used ? total - used : 0;
+		}
+		spin_unlock(&ci->i_ceph_lock);
+		if (total) {
+			buf->f_blocks = total;
+			buf->f_bfree = free;
+			buf->f_bavail = free;
+			is_updated = true;
+		}
+		iput(in);
+	}
+	ceph_put_snap_realm(mdsc, realm);
+
+	return is_updated;
+}
+
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index 9b4929420c13..5062d52f161e 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -76,9 +76,18 @@  static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf)
 	 */
 	buf->f_bsize = 1 << CEPH_BLOCK_SHIFT;
 	buf->f_frsize = 1 << CEPH_BLOCK_SHIFT;
-	buf->f_blocks = le64_to_cpu(st.kb) >> (CEPH_BLOCK_SHIFT-10);
-	buf->f_bfree = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);
-	buf->f_bavail = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);
+
+	/*
+	 * By default use root quota for stats; fallback to overall filesystem
+	 * usage if using 'noquotadf' mount option or if the root dir doesn't
+	 * have max_bytes quota set.
+	 */
+	if ((fsc->mount_options->flags & CEPH_MOUNT_OPT_NOQUOTADF) ||
+	    !ceph_quota_update_statfs(fsc, buf)) {
+		buf->f_blocks = le64_to_cpu(st.kb) >> (CEPH_BLOCK_SHIFT-10);
+		buf->f_bfree = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);
+		buf->f_bavail = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10);
+	}
 
 	buf->f_files = le64_to_cpu(st.num_objects);
 	buf->f_ffree = -1;
@@ -151,6 +160,8 @@  enum {
 	Opt_acl,
 #endif
 	Opt_noacl,
+	Opt_quotadf,
+	Opt_noquotadf,
 };
 
 static match_table_t fsopt_tokens = {
@@ -187,6 +198,8 @@  static match_table_t fsopt_tokens = {
 	{Opt_acl, "acl"},
 #endif
 	{Opt_noacl, "noacl"},
+	{Opt_quotadf, "quotadf"},
+	{Opt_noquotadf, "noquotadf"},
 	{-1, NULL}
 };
 
@@ -328,6 +341,12 @@  static int parse_fsopt_token(char *c, void *private)
 	case Opt_norequire_active_mds:
 		fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT;
 		break;
+	case Opt_quotadf:
+		fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF;
+		break;
+	case Opt_noquotadf:
+		fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF;
+		break;
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
 	case Opt_acl:
 		fsopt->sb_flags |= SB_POSIXACL;
@@ -517,6 +536,10 @@  static int ceph_show_options(struct seq_file *m, struct dentry *root)
 	}
 	if (fsopt->flags & CEPH_MOUNT_OPT_NOPOOLPERM)
 		seq_puts(m, ",nopoolperm");
+	if (fsopt->flags & CEPH_MOUNT_OPT_NOQUOTADF)
+		seq_puts(m, ",noquotadf");
+	else
+		seq_puts(m, ",quotadf");
 
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
 	if (fsopt->sb_flags & SB_POSIXACL)
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 92d189074008..60ed9399fe73 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -39,6 +39,7 @@ 
 #define CEPH_MOUNT_OPT_FSCACHE         (1<<10) /* use fscache */
 #define CEPH_MOUNT_OPT_NOPOOLPERM      (1<<11) /* no pool permission check */
 #define CEPH_MOUNT_OPT_MOUNTWAIT       (1<<12) /* mount waits if no mds is up */
+#define CEPH_MOUNT_OPT_NOQUOTADF       (1<<13) /* no root dir quota in statfs */
 
 #define CEPH_MOUNT_OPT_DEFAULT    CEPH_MOUNT_OPT_DCACHE
 
@@ -1111,5 +1112,7 @@  extern bool ceph_quota_is_max_bytes_exceeded(struct inode *inode,
 					     loff_t newlen);
 extern bool ceph_quota_is_max_bytes_approaching(struct inode *inode,
 						loff_t newlen);
+extern bool ceph_quota_update_statfs(struct ceph_fs_client *fsc,
+				     struct kstatfs *buf);
 
 #endif /* _FS_CEPH_SUPER_H */