diff options
author | Jan Kara <jack@suse.cz> | 2008-08-21 14:13:17 -0400 |
---|---|---|
committer | Mark Fasheh <mfasheh@suse.com> | 2009-01-05 11:40:24 -0500 |
commit | 19ece546a418997226bd91552fbc41abcb05cea6 (patch) | |
tree | f10ba25a07d020551833f53720bbba475788f957 /fs/ocfs2/super.c | |
parent | 2205363dce7447b8e85f1ead14387664c1a98753 (diff) |
ocfs2: Enable quota accounting on mount, disable on umount
Enable quota usage tracking on mount and disable it on umount. Also
add support for quota on and quota off quotactls and usrquota and
grpquota mount options. Add quota features among supported ones.
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Diffstat (limited to 'fs/ocfs2/super.c')
-rw-r--r-- | fs/ocfs2/super.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 60f1d29421ad..2eb657c3e7a8 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <linux/debugfs.h> | 41 | #include <linux/debugfs.h> |
42 | #include <linux/mount.h> | 42 | #include <linux/mount.h> |
43 | #include <linux/seq_file.h> | 43 | #include <linux/seq_file.h> |
44 | #include <linux/quotaops.h> | ||
44 | 45 | ||
45 | #define MLOG_MASK_PREFIX ML_SUPER | 46 | #define MLOG_MASK_PREFIX ML_SUPER |
46 | #include <cluster/masklog.h> | 47 | #include <cluster/masklog.h> |
@@ -127,6 +128,9 @@ static int ocfs2_get_sector(struct super_block *sb, | |||
127 | static void ocfs2_write_super(struct super_block *sb); | 128 | static void ocfs2_write_super(struct super_block *sb); |
128 | static struct inode *ocfs2_alloc_inode(struct super_block *sb); | 129 | static struct inode *ocfs2_alloc_inode(struct super_block *sb); |
129 | static void ocfs2_destroy_inode(struct inode *inode); | 130 | static void ocfs2_destroy_inode(struct inode *inode); |
131 | static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend); | ||
132 | static int ocfs2_enable_quotas(struct ocfs2_super *osb); | ||
133 | static void ocfs2_disable_quotas(struct ocfs2_super *osb); | ||
130 | 134 | ||
131 | static const struct super_operations ocfs2_sops = { | 135 | static const struct super_operations ocfs2_sops = { |
132 | .statfs = ocfs2_statfs, | 136 | .statfs = ocfs2_statfs, |
@@ -165,6 +169,8 @@ enum { | |||
165 | Opt_inode64, | 169 | Opt_inode64, |
166 | Opt_acl, | 170 | Opt_acl, |
167 | Opt_noacl, | 171 | Opt_noacl, |
172 | Opt_usrquota, | ||
173 | Opt_grpquota, | ||
168 | Opt_err, | 174 | Opt_err, |
169 | }; | 175 | }; |
170 | 176 | ||
@@ -189,6 +195,8 @@ static const match_table_t tokens = { | |||
189 | {Opt_inode64, "inode64"}, | 195 | {Opt_inode64, "inode64"}, |
190 | {Opt_acl, "acl"}, | 196 | {Opt_acl, "acl"}, |
191 | {Opt_noacl, "noacl"}, | 197 | {Opt_noacl, "noacl"}, |
198 | {Opt_usrquota, "usrquota"}, | ||
199 | {Opt_grpquota, "grpquota"}, | ||
192 | {Opt_err, NULL} | 200 | {Opt_err, NULL} |
193 | }; | 201 | }; |
194 | 202 | ||
@@ -452,6 +460,12 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data) | |||
452 | 460 | ||
453 | /* We're going to/from readonly mode. */ | 461 | /* We're going to/from readonly mode. */ |
454 | if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) { | 462 | if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) { |
463 | /* Disable quota accounting before remounting RO */ | ||
464 | if (*flags & MS_RDONLY) { | ||
465 | ret = ocfs2_susp_quotas(osb, 0); | ||
466 | if (ret < 0) | ||
467 | goto out; | ||
468 | } | ||
455 | /* Lock here so the check of HARD_RO and the potential | 469 | /* Lock here so the check of HARD_RO and the potential |
456 | * setting of SOFT_RO is atomic. */ | 470 | * setting of SOFT_RO is atomic. */ |
457 | spin_lock(&osb->osb_lock); | 471 | spin_lock(&osb->osb_lock); |
@@ -487,6 +501,21 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data) | |||
487 | } | 501 | } |
488 | unlock_osb: | 502 | unlock_osb: |
489 | spin_unlock(&osb->osb_lock); | 503 | spin_unlock(&osb->osb_lock); |
504 | /* Enable quota accounting after remounting RW */ | ||
505 | if (!ret && !(*flags & MS_RDONLY)) { | ||
506 | if (sb_any_quota_suspended(sb)) | ||
507 | ret = ocfs2_susp_quotas(osb, 1); | ||
508 | else | ||
509 | ret = ocfs2_enable_quotas(osb); | ||
510 | if (ret < 0) { | ||
511 | /* Return back changes... */ | ||
512 | spin_lock(&osb->osb_lock); | ||
513 | sb->s_flags |= MS_RDONLY; | ||
514 | osb->osb_flags |= OCFS2_OSB_SOFT_RO; | ||
515 | spin_unlock(&osb->osb_lock); | ||
516 | goto out; | ||
517 | } | ||
518 | } | ||
490 | } | 519 | } |
491 | 520 | ||
492 | if (!ret) { | 521 | if (!ret) { |
@@ -647,6 +676,131 @@ static int ocfs2_verify_userspace_stack(struct ocfs2_super *osb, | |||
647 | return 0; | 676 | return 0; |
648 | } | 677 | } |
649 | 678 | ||
679 | static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend) | ||
680 | { | ||
681 | int type; | ||
682 | struct super_block *sb = osb->sb; | ||
683 | unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, | ||
684 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; | ||
685 | int status = 0; | ||
686 | |||
687 | for (type = 0; type < MAXQUOTAS; type++) { | ||
688 | if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type])) | ||
689 | continue; | ||
690 | if (unsuspend) | ||
691 | status = vfs_quota_enable( | ||
692 | sb_dqopt(sb)->files[type], | ||
693 | type, QFMT_OCFS2, | ||
694 | DQUOT_SUSPENDED); | ||
695 | else | ||
696 | status = vfs_quota_disable(sb, type, | ||
697 | DQUOT_SUSPENDED); | ||
698 | if (status < 0) | ||
699 | break; | ||
700 | } | ||
701 | if (status < 0) | ||
702 | mlog(ML_ERROR, "Failed to suspend/unsuspend quotas on " | ||
703 | "remount (error = %d).\n", status); | ||
704 | return status; | ||
705 | } | ||
706 | |||
707 | static int ocfs2_enable_quotas(struct ocfs2_super *osb) | ||
708 | { | ||
709 | struct inode *inode[MAXQUOTAS] = { NULL, NULL }; | ||
710 | struct super_block *sb = osb->sb; | ||
711 | unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, | ||
712 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; | ||
713 | unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE, | ||
714 | LOCAL_GROUP_QUOTA_SYSTEM_INODE }; | ||
715 | int status; | ||
716 | int type; | ||
717 | |||
718 | sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NEGATIVE_USAGE; | ||
719 | for (type = 0; type < MAXQUOTAS; type++) { | ||
720 | if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type])) | ||
721 | continue; | ||
722 | inode[type] = ocfs2_get_system_file_inode(osb, ino[type], | ||
723 | osb->slot_num); | ||
724 | if (!inode[type]) { | ||
725 | status = -ENOENT; | ||
726 | goto out_quota_off; | ||
727 | } | ||
728 | status = vfs_quota_enable(inode[type], type, QFMT_OCFS2, | ||
729 | DQUOT_USAGE_ENABLED); | ||
730 | if (status < 0) | ||
731 | goto out_quota_off; | ||
732 | } | ||
733 | |||
734 | for (type = 0; type < MAXQUOTAS; type++) | ||
735 | iput(inode[type]); | ||
736 | return 0; | ||
737 | out_quota_off: | ||
738 | ocfs2_disable_quotas(osb); | ||
739 | for (type = 0; type < MAXQUOTAS; type++) | ||
740 | iput(inode[type]); | ||
741 | mlog_errno(status); | ||
742 | return status; | ||
743 | } | ||
744 | |||
745 | static void ocfs2_disable_quotas(struct ocfs2_super *osb) | ||
746 | { | ||
747 | int type; | ||
748 | struct inode *inode; | ||
749 | struct super_block *sb = osb->sb; | ||
750 | |||
751 | /* We mostly ignore errors in this function because there's not much | ||
752 | * we can do when we see them */ | ||
753 | for (type = 0; type < MAXQUOTAS; type++) { | ||
754 | if (!sb_has_quota_loaded(sb, type)) | ||
755 | continue; | ||
756 | inode = igrab(sb->s_dquot.files[type]); | ||
757 | /* Turn off quotas. This will remove all dquot structures from | ||
758 | * memory and so they will be automatically synced to global | ||
759 | * quota files */ | ||
760 | vfs_quota_disable(sb, type, DQUOT_USAGE_ENABLED | | ||
761 | DQUOT_LIMITS_ENABLED); | ||
762 | if (!inode) | ||
763 | continue; | ||
764 | iput(inode); | ||
765 | } | ||
766 | } | ||
767 | |||
768 | /* Handle quota on quotactl */ | ||
769 | static int ocfs2_quota_on(struct super_block *sb, int type, int format_id, | ||
770 | char *path, int remount) | ||
771 | { | ||
772 | unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, | ||
773 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; | ||
774 | |||
775 | if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type])) | ||
776 | return -EINVAL; | ||
777 | |||
778 | if (remount) | ||
779 | return 0; /* Just ignore it has been handled in | ||
780 | * ocfs2_remount() */ | ||
781 | return vfs_quota_enable(sb_dqopt(sb)->files[type], type, | ||
782 | format_id, DQUOT_LIMITS_ENABLED); | ||
783 | } | ||
784 | |||
785 | /* Handle quota off quotactl */ | ||
786 | static int ocfs2_quota_off(struct super_block *sb, int type, int remount) | ||
787 | { | ||
788 | if (remount) | ||
789 | return 0; /* Ignore now and handle later in | ||
790 | * ocfs2_remount() */ | ||
791 | return vfs_quota_disable(sb, type, DQUOT_LIMITS_ENABLED); | ||
792 | } | ||
793 | |||
794 | static struct quotactl_ops ocfs2_quotactl_ops = { | ||
795 | .quota_on = ocfs2_quota_on, | ||
796 | .quota_off = ocfs2_quota_off, | ||
797 | .quota_sync = vfs_quota_sync, | ||
798 | .get_info = vfs_get_dqinfo, | ||
799 | .set_info = vfs_set_dqinfo, | ||
800 | .get_dqblk = vfs_get_dqblk, | ||
801 | .set_dqblk = vfs_set_dqblk, | ||
802 | }; | ||
803 | |||
650 | static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) | 804 | static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) |
651 | { | 805 | { |
652 | struct dentry *root; | 806 | struct dentry *root; |
@@ -689,6 +843,22 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) | |||
689 | osb->osb_commit_interval = parsed_options.commit_interval; | 843 | osb->osb_commit_interval = parsed_options.commit_interval; |
690 | osb->local_alloc_default_bits = ocfs2_megabytes_to_clusters(sb, parsed_options.localalloc_opt); | 844 | osb->local_alloc_default_bits = ocfs2_megabytes_to_clusters(sb, parsed_options.localalloc_opt); |
691 | osb->local_alloc_bits = osb->local_alloc_default_bits; | 845 | osb->local_alloc_bits = osb->local_alloc_default_bits; |
846 | if (osb->s_mount_opt & OCFS2_MOUNT_USRQUOTA && | ||
847 | !OCFS2_HAS_RO_COMPAT_FEATURE(sb, | ||
848 | OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { | ||
849 | status = -EINVAL; | ||
850 | mlog(ML_ERROR, "User quotas were requested, but this " | ||
851 | "filesystem does not have the feature enabled.\n"); | ||
852 | goto read_super_error; | ||
853 | } | ||
854 | if (osb->s_mount_opt & OCFS2_MOUNT_GRPQUOTA && | ||
855 | !OCFS2_HAS_RO_COMPAT_FEATURE(sb, | ||
856 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { | ||
857 | status = -EINVAL; | ||
858 | mlog(ML_ERROR, "Group quotas were requested, but this " | ||
859 | "filesystem does not have the feature enabled.\n"); | ||
860 | goto read_super_error; | ||
861 | } | ||
692 | 862 | ||
693 | status = ocfs2_verify_userspace_stack(osb, &parsed_options); | 863 | status = ocfs2_verify_userspace_stack(osb, &parsed_options); |
694 | if (status) | 864 | if (status) |
@@ -793,6 +963,28 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) | |||
793 | atomic_set(&osb->vol_state, VOLUME_MOUNTED); | 963 | atomic_set(&osb->vol_state, VOLUME_MOUNTED); |
794 | wake_up(&osb->osb_mount_event); | 964 | wake_up(&osb->osb_mount_event); |
795 | 965 | ||
966 | /* Now we can initialize quotas because we can afford to wait | ||
967 | * for cluster locks recovery now. That also means that truncation | ||
968 | * log recovery can happen but that waits for proper quota setup */ | ||
969 | if (!(sb->s_flags & MS_RDONLY)) { | ||
970 | status = ocfs2_enable_quotas(osb); | ||
971 | if (status < 0) { | ||
972 | /* We have to err-out specially here because | ||
973 | * s_root is already set */ | ||
974 | mlog_errno(status); | ||
975 | atomic_set(&osb->vol_state, VOLUME_DISABLED); | ||
976 | wake_up(&osb->osb_mount_event); | ||
977 | mlog_exit(status); | ||
978 | return status; | ||
979 | } | ||
980 | } | ||
981 | |||
982 | ocfs2_complete_quota_recovery(osb); | ||
983 | |||
984 | /* Now we wake up again for processes waiting for quotas */ | ||
985 | atomic_set(&osb->vol_state, VOLUME_MOUNTED_QUOTAS); | ||
986 | wake_up(&osb->osb_mount_event); | ||
987 | |||
796 | mlog_exit(status); | 988 | mlog_exit(status); |
797 | return status; | 989 | return status; |
798 | 990 | ||
@@ -980,6 +1172,28 @@ static int ocfs2_parse_options(struct super_block *sb, | |||
980 | case Opt_inode64: | 1172 | case Opt_inode64: |
981 | mopt->mount_opt |= OCFS2_MOUNT_INODE64; | 1173 | mopt->mount_opt |= OCFS2_MOUNT_INODE64; |
982 | break; | 1174 | break; |
1175 | case Opt_usrquota: | ||
1176 | /* We check only on remount, otherwise features | ||
1177 | * aren't yet initialized. */ | ||
1178 | if (is_remount && !OCFS2_HAS_RO_COMPAT_FEATURE(sb, | ||
1179 | OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { | ||
1180 | mlog(ML_ERROR, "User quota requested but " | ||
1181 | "filesystem feature is not set\n"); | ||
1182 | status = 0; | ||
1183 | goto bail; | ||
1184 | } | ||
1185 | mopt->mount_opt |= OCFS2_MOUNT_USRQUOTA; | ||
1186 | break; | ||
1187 | case Opt_grpquota: | ||
1188 | if (is_remount && !OCFS2_HAS_RO_COMPAT_FEATURE(sb, | ||
1189 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { | ||
1190 | mlog(ML_ERROR, "Group quota requested but " | ||
1191 | "filesystem feature is not set\n"); | ||
1192 | status = 0; | ||
1193 | goto bail; | ||
1194 | } | ||
1195 | mopt->mount_opt |= OCFS2_MOUNT_GRPQUOTA; | ||
1196 | break; | ||
983 | #ifdef CONFIG_OCFS2_FS_POSIX_ACL | 1197 | #ifdef CONFIG_OCFS2_FS_POSIX_ACL |
984 | case Opt_acl: | 1198 | case Opt_acl: |
985 | mopt->mount_opt |= OCFS2_MOUNT_POSIX_ACL; | 1199 | mopt->mount_opt |= OCFS2_MOUNT_POSIX_ACL; |
@@ -1056,6 +1270,10 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) | |||
1056 | if (osb->osb_cluster_stack[0]) | 1270 | if (osb->osb_cluster_stack[0]) |
1057 | seq_printf(s, ",cluster_stack=%.*s", OCFS2_STACK_LABEL_LEN, | 1271 | seq_printf(s, ",cluster_stack=%.*s", OCFS2_STACK_LABEL_LEN, |
1058 | osb->osb_cluster_stack); | 1272 | osb->osb_cluster_stack); |
1273 | if (opts & OCFS2_MOUNT_USRQUOTA) | ||
1274 | seq_printf(s, ",usrquota"); | ||
1275 | if (opts & OCFS2_MOUNT_GRPQUOTA) | ||
1276 | seq_printf(s, ",grpquota"); | ||
1059 | 1277 | ||
1060 | if (opts & OCFS2_MOUNT_NOUSERXATTR) | 1278 | if (opts & OCFS2_MOUNT_NOUSERXATTR) |
1061 | seq_printf(s, ",nouser_xattr"); | 1279 | seq_printf(s, ",nouser_xattr"); |
@@ -1394,6 +1612,8 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err) | |||
1394 | osb = OCFS2_SB(sb); | 1612 | osb = OCFS2_SB(sb); |
1395 | BUG_ON(!osb); | 1613 | BUG_ON(!osb); |
1396 | 1614 | ||
1615 | ocfs2_disable_quotas(osb); | ||
1616 | |||
1397 | ocfs2_shutdown_local_alloc(osb); | 1617 | ocfs2_shutdown_local_alloc(osb); |
1398 | 1618 | ||
1399 | ocfs2_truncate_log_shutdown(osb); | 1619 | ocfs2_truncate_log_shutdown(osb); |
@@ -1504,6 +1724,8 @@ static int ocfs2_initialize_super(struct super_block *sb, | |||
1504 | sb->s_fs_info = osb; | 1724 | sb->s_fs_info = osb; |
1505 | sb->s_op = &ocfs2_sops; | 1725 | sb->s_op = &ocfs2_sops; |
1506 | sb->s_export_op = &ocfs2_export_ops; | 1726 | sb->s_export_op = &ocfs2_export_ops; |
1727 | sb->s_qcop = &ocfs2_quotactl_ops; | ||
1728 | sb->dq_op = &ocfs2_quota_operations; | ||
1507 | sb->s_xattr = ocfs2_xattr_handlers; | 1729 | sb->s_xattr = ocfs2_xattr_handlers; |
1508 | sb->s_time_gran = 1; | 1730 | sb->s_time_gran = 1; |
1509 | sb->s_flags |= MS_NOATIME; | 1731 | sb->s_flags |= MS_NOATIME; |