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.
Unified · Split
| @@ -149,6 +149,10 @@ Mount Options · 148 unmodified lines | ||
| 149 | 149 | noasyncreaddir |
| 150 | 150 | Do not use the dcache as above for readdir. |
| 151 | 151 | |
| 152 | + noquotadf | |
| 153 | + Report overall filesystem usage in statfs instead of using the root | |
| 154 | + directory quota. | |
| 155 | + | |
| 152 | 156 | More Information |
| 153 | 157 | ================ |
| 154 | 158 | |
| @@ -18,6 +18,8 @@ · 17 unmodified lines | ||
| 18 | 18 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
| 19 | 19 | */ |
| 20 | 20 | |
| 21 | +#include <linux/statfs.h> | |
| 22 | + | |
| 21 | 23 | #include "super.h" |
| 22 | 24 | #include "mds_client.h" |
| 23 | 25 | |
| @@ -303,3 +305,57 @@ bool ceph_quota_is_max_bytes_approaching(struct inode *inode, loff_t newsize) · 279 unmodified lines | ||
| 303 | 305 | return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_APPROACHING_OP, |
| 304 | 306 | (newsize - size)); |
| 305 | 307 | } |
| 308 | + | |
| 309 | +/* | |
| 310 | + * ceph_quota_update_statfs - if root has quota update statfs with quota status | |
| 311 | + * @fsc: filesystem client instance | |
| 312 | + * @buf: statfs to update | |
| 313 | + * | |
| 314 | + * If the mounted filesystem root has max_bytes quota set, update the filesystem | |
| 315 | + * statistics with the quota status. | |
| 316 | + * | |
| 317 | + * This function returns true if the stats have been updated, false otherwise. | |
| 318 | + */ | |
| 319 | +bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf) | |
| 320 | +{ | |
| 321 | + struct ceph_mds_client *mdsc = fsc->mdsc; | |
| 322 | + struct ceph_inode_info *ci; | |
| 323 | + struct ceph_snap_realm *realm; | |
| 324 | + struct inode *in; | |
| 325 | + u64 total = 0, used, free; | |
| 326 | + bool is_updated = false; | |
| 327 | + | |
| 328 | + down_read(&mdsc->snap_rwsem); | |
| 329 | + realm = get_quota_realm(mdsc, d_inode(fsc->sb->s_root)); | |
| 330 | + up_read(&mdsc->snap_rwsem); | |
| 331 | + if (!realm) | |
| 332 | + return false; | |
| 333 | + | |
| 334 | + spin_lock(&realm->inodes_with_caps_lock); | |
| 335 | + in = realm->inode ? igrab(realm->inode) : NULL; | |
| 336 | + spin_unlock(&realm->inodes_with_caps_lock); | |
| 337 | + if (in) { | |
| 338 | + ci = ceph_inode(in); | |
| 339 | + spin_lock(&ci->i_ceph_lock); | |
| 340 | + if (ci->i_max_bytes) { | |
| 341 | + total = ci->i_max_bytes >> CEPH_BLOCK_SHIFT; | |
| 342 | + used = ci->i_rbytes >> CEPH_BLOCK_SHIFT; | |
| 343 | + /* It is possible for a quota to be exceeded. | |
| 344 | + * Report 'zero' in that case | |
| 345 | + */ | |
| 346 | + free = total > used ? total - used : 0; | |
| 347 | + } | |
| 348 | + spin_unlock(&ci->i_ceph_lock); | |
| 349 | + if (total) { | |
| 350 | + buf->f_blocks = total; | |
| 351 | + buf->f_bfree = free; | |
| 352 | + buf->f_bavail = free; | |
| 353 | + is_updated = true; | |
| 354 | + } | |
| 355 | + iput(in); | |
| 356 | + } | |
| 357 | + ceph_put_snap_realm(mdsc, realm); | |
| 358 | + | |
| 359 | + return is_updated; | |
| 360 | +} | |
| 361 | + | |
| @@ -76,9 +76,18 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) · 75 unmodified lines | ||
| 76 | 76 | */ |
| 77 | 77 | buf->f_bsize = 1 << CEPH_BLOCK_SHIFT; |
| 78 | 78 | buf->f_frsize = 1 << CEPH_BLOCK_SHIFT; |
| 79 | − buf->f_blocks = le64_to_cpu(st.kb) >> (CEPH_BLOCK_SHIFT-10); | |
| 80 | − buf->f_bfree = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10); | |
| 81 | − buf->f_bavail = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10); | |
| 79 | + | |
| 80 | + /* | |
| 81 | + * By default use root quota for stats; fallback to overall filesystem | |
| 82 | + * usage if using 'noquotadf' mount option or if the root dir doesn't | |
| 83 | + * have max_bytes quota set. | |
| 84 | + */ | |
| 85 | + if (ceph_test_mount_opt(fsc, NOQUOTADF) || | |
| 86 | + !ceph_quota_update_statfs(fsc, buf)) { | |
| 87 | + buf->f_blocks = le64_to_cpu(st.kb) >> (CEPH_BLOCK_SHIFT-10); | |
| 88 | + buf->f_bfree = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10); | |
| 89 | + buf->f_bavail = le64_to_cpu(st.kb_avail) >> (CEPH_BLOCK_SHIFT-10); | |
| 90 | + } | |
| 82 | 91 | |
| 83 | 92 | buf->f_files = le64_to_cpu(st.num_objects); |
| 84 | 93 | buf->f_ffree = -1; |
| @@ -151,6 +160,8 @@ enum { · 66 unmodified lines | ||
| 151 | 160 | Opt_acl, |
| 152 | 161 | #endif |
| 153 | 162 | Opt_noacl, |
| 163 | + Opt_quotadf, | |
| 164 | + Opt_noquotadf, | |
| 154 | 165 | }; |
| 155 | 166 | |
| 156 | 167 | static match_table_t fsopt_tokens = { |
| @@ -187,6 +198,8 @@ static match_table_t fsopt_tokens = { · 30 unmodified lines | ||
| 187 | 198 | {Opt_acl, "acl"}, |
| 188 | 199 | #endif |
| 189 | 200 | {Opt_noacl, "noacl"}, |
| 201 | + {Opt_quotadf, "quotadf"}, | |
| 202 | + {Opt_noquotadf, "noquotadf"}, | |
| 190 | 203 | {-1, NULL} |
| 191 | 204 | }; |
| 192 | 205 | |
| @@ -334,6 +347,12 @@ static int parse_fsopt_token(char *c, void *private) · 141 unmodified lines | ||
| 334 | 347 | case Opt_norequire_active_mds: |
| 335 | 348 | fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT; |
| 336 | 349 | break; |
| 350 | + case Opt_quotadf: | |
| 351 | + fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF; | |
| 352 | + break; | |
| 353 | + case Opt_noquotadf: | |
| 354 | + fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF; | |
| 355 | + break; | |
| 337 | 356 | #ifdef CONFIG_CEPH_FS_POSIX_ACL |
| 338 | 357 | case Opt_acl: |
| 339 | 358 | fsopt->sb_flags |= SB_POSIXACL; |
| @@ -520,6 +539,8 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root) · 180 unmodified lines | ||
| 520 | 539 | } |
| 521 | 540 | if (fsopt->flags & CEPH_MOUNT_OPT_NOPOOLPERM) |
| 522 | 541 | seq_puts(m, ",nopoolperm"); |
| 542 | + if (fsopt->flags & CEPH_MOUNT_OPT_NOQUOTADF) | |
| 543 | + seq_puts(m, ",noquotadf"); | |
| 523 | 544 | |
| 524 | 545 | #ifdef CONFIG_CEPH_FS_POSIX_ACL |
| 525 | 546 | if (fsopt->sb_flags & SB_POSIXACL) |
| @@ -39,6 +39,7 @@ · 38 unmodified lines | ||
| 39 | 39 | #define CEPH_MOUNT_OPT_FSCACHE (1<<10) /* use fscache */ |
| 40 | 40 | #define CEPH_MOUNT_OPT_NOPOOLPERM (1<<11) /* no pool permission check */ |
| 41 | 41 | #define CEPH_MOUNT_OPT_MOUNTWAIT (1<<12) /* mount waits if no mds is up */ |
| 42 | +#define CEPH_MOUNT_OPT_NOQUOTADF (1<<13) /* no root dir quota in statfs */ | |
| 42 | 43 | |
| 43 | 44 | #define CEPH_MOUNT_OPT_DEFAULT CEPH_MOUNT_OPT_DCACHE |
| 44 | 45 | |
| @@ -1104,5 +1105,7 @@ extern bool ceph_quota_is_max_bytes_exceeded(struct inode *inode, · 1059 unmodified lines | ||
| 1104 | 1105 | loff_t newlen); |
| 1105 | 1106 | extern bool ceph_quota_is_max_bytes_approaching(struct inode *inode, |
| 1106 | 1107 | loff_t newlen); |
| 1108 | +extern bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, | |
| 1109 | + struct kstatfs *buf); | |
| 1107 | 1110 | |
| 1108 | 1111 | #endif /* _FS_CEPH_SUPER_H */ |