Implement quotas for OCFS2 (version 2)
Subsystems (6)
Jan Kara
reviewercover Implement quotas for OCFS2 (version 2)
Oops, and now really the patch series... Sorry for the bad post.
Honza
Jan Kara
reviewerPATCH 1/29 quota: Add callbacks for allocating and destroying dquot structures
Some filesystems would like to keep private information together with each dquot. Add callbacks alloc_dquot and destroy_dquot allowing filesystem to allocate larger dquots from their private slab in a similar fashion we currently allocate inodes.
--- fs/dquot.c | 19 +++++++++++++++---- include/linux/quota.h | 2 ++ 2 files changed, 17 insertions(+), 4 deletions(-)
fs/dquot.c +15 -4
@@ -417,6 +417,14 @@ out_dqlock:return ret; } +static void destroy_dquot (struct dquot *dquot) +{ +if (dquot->dq_sb->dq_op->destroy_dquot) + dquot->dq_sb->dq_op->destroy_dquot (dquot); +else +kmem_cache_free (dquot_cachep, dquot); +} +/* Invalidate all dquots on the list. Note that this function is called after * quota is disabled and pointers from inodes removed so there cannot be new * quota users. There can still be some users of quotas due to inodes being
@@ -465,7 +473,7 @@ restart:remove_dquot_hash (dquot);remove_free_dquot (dquot);remove_inuse (dquot); -kmem_cache_free (dquot_cachep, dquot); +destroy_dquot (dquot); }spin_unlock (&dq_list_lock); }
@@ -529,7 +537,7 @@ static void prune_dqcache(int count)remove_dquot_hash (dquot);remove_free_dquot (dquot);remove_inuse (dquot); -kmem_cache_free (dquot_cachep, dquot); +destroy_dquot (dquot); count--; head = free_dquots.prev; }
@@ -631,7 +639,10 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type) {struct dquot *dquot; - dquot =kmem_cache_zalloc (dquot_cachep,GFP_NOFS ); +if (sb->dq_op->alloc_dquot) + dquot = sb->dq_op->alloc_dquot (sb, type); +else + dquot =kmem_cache_zalloc (dquot_cachep,GFP_NOFS );if (!dquot)return NODQUOT ;
@@ -684,7 +695,7 @@ we_slept: dqstats.lookups++;spin_unlock (&dq_list_lock);if (empty) -kmem_cache_free (dquot_cachep, empty); +destroy_dquot (empty); }/* Wait for dq_lock - after this we know that either dquot_release() is already * finished or it will be canceled due to dq_count > 1 test */
include/linux/quota.h +2 -0
@@ -294,6 +294,8 @@ struct dquot_operations {int (*free_inode) (const struct inode *,unsigned long );int (*transfer) (struct inode *,struct iattr *);int (*write_dquot) (struct dquot *);/* Ordinary dquot write */ +struct dquot *(*alloc_dquot)(struct super_block *,int );/* Allocate memory for new dquot (can be NULL if no special entries dquot are needed) */ +void (*destroy_dquot)(struct dquot *);/* Free memory for dquot (can be NULL if alloc_dquot is NULL) */ int (*acquire_dquot) (struct dquot *);/* Quota is going to be created on disk */ int (*release_dquot) (struct dquot *);/* Quota is going to be deleted from disk */ int (*mark_dirty) (struct dquot *);/* Dquot is marked dirty */
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 2/29 quota: Increase size of variables for limits and inode usage
In reply to Jan KaraSo far quota was fine with quota block limits and inode limits/numbers in a 32-bit type. Now with rapid increase in storage sizes there are coming requests to be able to handle quota limits above 4TB / more that 2^32 inodes. So bump up sizes of types in mem_dqblk structure to 64-bits to be able to handle this. Also update inode allocation / checking functions to use qsize_t and make global structure keep quota limits in bytes so that things are consistent.
--- fs/dquot.c | 50 ++++++++++++++++++++++++++------------------- fs/quota_v1.c | 25 +++++++++++++++++----- fs/quota_v2.c | 21 +++++++++++++++--- include/linux/quota.h | 28 +++++++++++-------------- include/linux/quotaops.h | 4 +- 5 files changed, 79 insertions(+), 49 deletions(-)
fs/dquot.c +29 -21
@@ -833,7 +833,7 @@ static void drop_dquot_ref(struct super_block *sb, int type) } } -static inline void dquot_incr_inodes (struct dquot *dquot,unsigned long number) +static inline void dquot_incr_inodes (struct dquot *dquot,qsize_t number) { dquot->dq_dqb.dqb_curinodes += number; }
@@ -843,7 +843,7 @@ static inline void dquot_incr_space(struct dquot *dquot, qsize_t number) dquot->dq_dqb.dqb_curspace += number; } -static inline void dquot_decr_inodes (struct dquot *dquot,unsigned long number) +static inline void dquot_decr_inodes (struct dquot *dquot,qsize_t number) {if (dquot->dq_dqb.dqb_curinodes > number) dquot->dq_dqb.dqb_curinodes -= number;
@@ -860,7 +860,7 @@ static inline void dquot_decr_space(struct dquot *dquot, qsize_t number) dquot->dq_dqb.dqb_curspace -= number; else dquot->dq_dqb.dqb_curspace = 0; -if (toqb (dquot->dq_dqb.dqb_curspace) <= dquot->dq_dqb.dqb_bsoftlimit) +if (dquot->dq_dqb.dqb_curspace <= dquot->dq_dqb.dqb_bsoftlimit) dquot->dq_dqb.dqb_btime = (time_t )0 ;clear_bit (DQ_BLKS_B , &dquot->dq_flags); }
@@ -1036,7 +1036,7 @@ static inline char ignore_hardlimit(struct dquot *dquot) }/* needs dq_data_lock */ -static int check_idq (struct dquot *dquot,ulong inodes,char *warntype) +static int check_idq (struct dquot *dquot,qsize_t inodes,char *warntype) { *warntype =QUOTA_NL_NOWARN ;if (inodes <=0 ||test_bit (DQ_FAKE_B , &dquot->dq_flags))
@@ -1075,7 +1075,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warreturn QUOTA_OK ;if (dquot->dq_dqb.dqb_bhardlimit && -toqb (dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bhardlimit && + dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bhardlimit && !ignore_hardlimit (dquot)) {if (!prealloc) *warntype =QUOTA_NL_BHARDWARN ;
@@ -1083,7 +1083,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war }if (dquot->dq_dqb.dqb_bsoftlimit && -toqb (dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit && + dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bsoftlimit && dquot->dq_dqb.dqb_btime &&get_seconds () >= dquot->dq_dqb.dqb_btime && !ignore_hardlimit (dquot)) {if (!prealloc)
@@ -1092,7 +1092,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war }if (dquot->dq_dqb.dqb_bsoftlimit && -toqb (dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit && + dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bsoftlimit && dquot->dq_dqb.dqb_btime ==0 ) {if (!prealloc) { *warntype =QUOTA_NL_BSOFTWARN ;
@@ -1109,7 +1109,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warreturn QUOTA_OK ; } -static int info_idq_free (struct dquot *dquot,ulong inodes) +static int info_idq_free (struct dquot *dquot,qsize_t inodes) {if (test_bit (DQ_FAKE_B , &dquot->dq_flags) || dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit)
@@ -1126,15 +1126,13 @@ static int info_idq_free(struct dquot *dquot, ulong inodes)static int info_bdq_free (struct dquot *dquot,qsize_t space) {if (test_bit (DQ_FAKE_B , &dquot->dq_flags) || -toqb (dquot->dq_dqb.dqb_curspace) <= dquot->dq_dqb.dqb_bsoftlimit) + dquot->dq_dqb.dqb_curspace <= dquot->dq_dqb.dqb_bsoftlimit)return QUOTA_NL_NOWARN ; -if (toqb (dquot->dq_dqb.dqb_curspace - space) <= - dquot->dq_dqb.dqb_bsoftlimit) +if (dquot->dq_dqb.dqb_curspace - space <= dquot->dq_dqb.dqb_bsoftlimit)return QUOTA_NL_BSOFTBELOW ; -if (toqb (dquot->dq_dqb.dqb_curspace) >= dquot->dq_dqb.dqb_bhardlimit && -toqb (dquot->dq_dqb.dqb_curspace - space) < - dquot->dq_dqb.dqb_bhardlimit) +if (dquot->dq_dqb.dqb_curspace >= dquot->dq_dqb.dqb_bhardlimit && + dquot->dq_dqb.dqb_curspace - space < dquot->dq_dqb.dqb_bhardlimit)return QUOTA_NL_BHARDBELOW ;return QUOTA_NL_NOWARN ; }
@@ -1277,7 +1275,7 @@ warn_put_all:/* * This operation can block, but only after everything is updated */ -int dquot_alloc_inode (const struct inode *inode,unsigned long number) +int dquot_alloc_inode (const struct inode *inode,qsize_t number) {int cnt, ret =NO_QUOTA ;char warntype[MAXQUOTAS ];
@@ -1362,7 +1360,7 @@ out_sub:/* * This operation can block, but only after everything is updated */ -int dquot_free_inode (const struct inode *inode,unsigned long number) +int dquot_free_inode (const struct inode *inode,qsize_t number) {unsigned int cnt;char warntype[MAXQUOTAS ];
@@ -1879,14 +1877,24 @@ int vfs_dq_quota_on_remount(struct super_block *sb)return ret; } +static inline qsize_t qbtos (qsize_t blocks) +{ +return blocks <<QIF_DQBLKSIZE_BITS ; +} + +static inline qsize_t stoqb (qsize_t space) +{ +return (space +QIF_DQBLKSIZE -1 ) >>QIF_DQBLKSIZE_BITS ; +} +/* Generic routine for getting common part of quota structure */ static void do_get_dqblk (struct dquot *dquot,struct if_dqblk *di) {struct mem_dqblk *dm = &dquot->dq_dqb;spin_lock (&dq_data_lock); - di->dqb_bhardlimit = dm->dqb_bhardlimit; - di->dqb_bsoftlimit = dm->dqb_bsoftlimit; + di->dqb_bhardlimit =stoqb (dm->dqb_bhardlimit); + di->dqb_bsoftlimit =stoqb (dm->dqb_bsoftlimit); di->dqb_curspace = dm->dqb_curspace; di->dqb_ihardlimit = dm->dqb_ihardlimit; di->dqb_isoftlimit = dm->dqb_isoftlimit;
@@ -1933,8 +1941,8 @@ static int do_set_dqblk(struct dquot *dquot, struct if_dqblk *di) check_blim =1 ; }if (di->dqb_valid &QIF_BLIMITS ) { - dm->dqb_bsoftlimit = di->dqb_bsoftlimit; - dm->dqb_bhardlimit = di->dqb_bhardlimit; + dm->dqb_bsoftlimit =qbtos (di->dqb_bsoftlimit); + dm->dqb_bhardlimit =qbtos (di->dqb_bhardlimit); check_blim =1 ; }if (di->dqb_valid &QIF_INODES ) {
@@ -1952,7 +1960,7 @@ static int do_set_dqblk(struct dquot *dquot, struct if_dqblk *di) dm->dqb_itime = di->dqb_itime;if (check_blim) { -if (!dm->dqb_bsoftlimit ||toqb (dm->dqb_curspace) < dm->dqb_bsoftlimit) { +if (!dm->dqb_bsoftlimit || dm->dqb_curspace < dm->dqb_bsoftlimit) { dm->dqb_btime =0 ;clear_bit (DQ_BLKS_B , &dquot->dq_flags); }
fs/quota_v1.c +19 -6
@@ -14,14 +14,27 @@ MODULE_AUTHOR("Jan Kara");MODULE_DESCRIPTION ("Old quota format support" );MODULE_LICENSE ("GPL" ); +#define QUOTABLOCK_BITS 10 +#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS) + +static inline qsize_t v1_stoqb (qsize_t space) +{ +return (space +QUOTABLOCK_SIZE -1 ) >>QUOTABLOCK_BITS ; +} + +static inline qsize_t v1_qbtos (qsize_t blocks) +{ +return blocks <<QUOTABLOCK_BITS ; +} +static void v1_disk2mem_dqblk (struct mem_dqblk *m,struct v1_disk_dqblk *d) { m->dqb_ihardlimit = d->dqb_ihardlimit; m->dqb_isoftlimit = d->dqb_isoftlimit; m->dqb_curinodes = d->dqb_curinodes; - m->dqb_bhardlimit = d->dqb_bhardlimit; - m->dqb_bsoftlimit = d->dqb_bsoftlimit; - m->dqb_curspace = ((qsize_t )d->dqb_curblocks) <<QUOTABLOCK_BITS ; + m->dqb_bhardlimit =v1_qbtos (d->dqb_bhardlimit); + m->dqb_bsoftlimit =v1_qbtos (d->dqb_bsoftlimit); + m->dqb_curspace =v1_qbtos (d->dqb_curblocks); m->dqb_itime = d->dqb_itime; m->dqb_btime = d->dqb_btime; }
@@ -31,9 +44,9 @@ static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m) d->dqb_ihardlimit = m->dqb_ihardlimit; d->dqb_isoftlimit = m->dqb_isoftlimit; d->dqb_curinodes = m->dqb_curinodes; - d->dqb_bhardlimit = m->dqb_bhardlimit; - d->dqb_bsoftlimit = m->dqb_bsoftlimit; - d->dqb_curblocks =toqb (m->dqb_curspace); + d->dqb_bhardlimit =v1_stoqb (m->dqb_bhardlimit); + d->dqb_bsoftlimit =v1_stoqb (m->dqb_bsoftlimit); + d->dqb_curblocks =v1_stoqb (m->dqb_curspace); d->dqb_itime = m->dqb_itime; d->dqb_btime = m->dqb_btime; }
fs/quota_v2.c +17 -4
@@ -26,6 +26,19 @@ typedef char *dqbuf_t;#define GETIDINDEX (id, depth) (((id) >> ((V2_DQTREEDEPTH-(depth)-1)*8)) & 0xff)#define GETENTRIES (buf) ((struct v2_disk_dqblk *)(((char *)buf)+sizeof(struct v2_disk_dqdbheader))) +#define QUOTABLOCK_BITS 10 +#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS) + +static inline qsize_t v2_stoqb (qsize_t space) +{ +return (space +QUOTABLOCK_SIZE -1 ) >>QUOTABLOCK_BITS ; +} + +static inline qsize_t v2_qbtos (qsize_t blocks) +{ +return blocks <<QUOTABLOCK_BITS ; +} +/* Check whether given file is really vfsv0 quotafile */ static int v2_check_quota_file (struct super_block *sb,int type) {
@@ -104,8 +117,8 @@ static void disk2memdqb(struct mem_dqblk *m, struct v2_disk_dqblk *d) m->dqb_isoftlimit =le32_to_cpu (d->dqb_isoftlimit); m->dqb_curinodes =le32_to_cpu (d->dqb_curinodes); m->dqb_itime =le64_to_cpu (d->dqb_itime); - m->dqb_bhardlimit =le32_to_cpu (d->dqb_bhardlimit); - m->dqb_bsoftlimit =le32_to_cpu (d->dqb_bsoftlimit); + m->dqb_bhardlimit =v2_qbtos (le32_to_cpu (d->dqb_bhardlimit)); + m->dqb_bsoftlimit =v2_qbtos (le32_to_cpu (d->dqb_bsoftlimit)); m->dqb_curspace =le64_to_cpu (d->dqb_curspace); m->dqb_btime =le64_to_cpu (d->dqb_btime); }
@@ -116,8 +129,8 @@ static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id) d->dqb_isoftlimit =cpu_to_le32 (m->dqb_isoftlimit); d->dqb_curinodes =cpu_to_le32 (m->dqb_curinodes); d->dqb_itime =cpu_to_le64 (m->dqb_itime); - d->dqb_bhardlimit =cpu_to_le32 (m->dqb_bhardlimit); - d->dqb_bsoftlimit =cpu_to_le32 (m->dqb_bsoftlimit); + d->dqb_bhardlimit =cpu_to_le32 (v2_qbtos (m->dqb_bhardlimit)); + d->dqb_bsoftlimit =cpu_to_le32 (v2_qbtos (m->dqb_bsoftlimit)); d->dqb_curspace =cpu_to_le64 (m->dqb_curspace); d->dqb_btime =cpu_to_le64 (m->dqb_btime); d->dqb_id =cpu_to_le32 (id);
include/linux/quota.h +12 -16
@@ -41,15 +41,6 @@#define __DQUOT_VERSION__ "dquot_6.5.1"#define __DQUOT_NUM_VERSION__ 6*10000+5*100+1 -/* Size of blocks in which are counted size limits */ -#define QUOTABLOCK_BITS 10 -#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS) - -/* Conversion routines from and to quota blocks */ -#define qb2kb (x) ((x) << (QUOTABLOCK_BITS-10)) -#define kb2qb (x) ((x) >> (QUOTABLOCK_BITS-10)) -#define toqb (x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS) -#define MAXQUOTAS 2#define USRQUOTA 0/* element used for user quotas */ #define GRPQUOTA 1/* element used for group quotas */
@@ -82,6 +73,11 @@#define Q_GETQUOTA 0x800007/* get user quota structure */ #define Q_SETQUOTA 0x800008/* set user quota structure */ +/* Size of block in which space limits are passed through the quota +* interface */ +#define QIF_DQBLKSIZE_BITS 10 +#define QIF_DQBLKSIZE (1 << QIF_DQBLKSIZE_BITS) +/* * Quota structure used for communication with userspace via quotactl * Following flags are used to specify which fields are valid
@@ -189,12 +185,12 @@ extern spinlock_t dq_data_lock;* Data for one user/group kept in memory */ struct mem_dqblk { -__u32 dqb_bhardlimit;/* absolute limit on disk blks alloc */ -__u32 dqb_bsoftlimit;/* preferred limit on disk blks */ +qsize_t dqb_bhardlimit;/* absolute limit on disk blks alloc */ +qsize_t dqb_bsoftlimit;/* preferred limit on disk blks */ qsize_t dqb_curspace;/* current used space */ -__u32 dqb_ihardlimit;/* absolute limit on allocated inodes */ -__u32 dqb_isoftlimit;/* preferred inode limit */ -__u32 dqb_curinodes;/* current # allocated inodes */ +qsize_t dqb_ihardlimit;/* absolute limit on allocated inodes */ +qsize_t dqb_isoftlimit;/* preferred inode limit */ +qsize_t dqb_curinodes;/* current # allocated inodes */ time_t dqb_btime;/* time limit for excessive disk use */ time_t dqb_itime;/* time limit for excessive inode use */ };
@@ -289,9 +285,9 @@ struct dquot_operations {int (*initialize) (struct inode *,int );int (*drop) (struct inode *);int (*alloc_space) (struct inode *,qsize_t ,int ); -int (*alloc_inode) (const struct inode *,unsigned long ); +int (*alloc_inode) (const struct inode *,qsize_t );int (*free_space) (struct inode *,qsize_t ); -int (*free_inode) (const struct inode *,unsigned long ); +int (*free_inode) (const struct inode *,qsize_t );int (*transfer) (struct inode *,struct iattr *);int (*write_dquot) (struct dquot *);/* Ordinary dquot write */ struct dquot *(*alloc_dquot)(struct super_block *,int );/* Allocate memory for new dquot (can be NULL if no special entries dquot are needed) */
include/linux/quotaops.h +2 -2
@@ -29,10 +29,10 @@ int dquot_initialize(struct inode *inode, int type);int dquot_drop (struct inode *inode);int dquot_alloc_space (struct inode *inode,qsize_t number,int prealloc); -int dquot_alloc_inode (const struct inode *inode,unsigned long number); +int dquot_alloc_inode (const struct inode *inode,qsize_t number);int dquot_free_space (struct inode *inode,qsize_t number); -int dquot_free_inode (const struct inode *inode,unsigned long number); +int dquot_free_inode (const struct inode *inode,qsize_t number);int dquot_transfer (struct inode *inode,struct iattr *iattr);int dquot_commit (struct dquot *dquot);
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 3/29 quota: Remove bogus 'optimization' in check_idq() and check_bdq()
In reply to Jan KaraChecks like <= 0 for an unsigned type do not make much sence. The value could be only 0 and that does not happen often enough for the check to be worth it.
--- fs/dquot.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
fs/dquot.c +2 -2
@@ -1039,7 +1039,7 @@ static inline char ignore_hardlimit(struct dquot *dquot)static int check_idq (struct dquot *dquot,qsize_t inodes,char *warntype) { *warntype =QUOTA_NL_NOWARN ; -if (inodes <=0 ||test_bit (DQ_FAKE_B , &dquot->dq_flags)) +if (test_bit (DQ_FAKE_B , &dquot->dq_flags))return QUOTA_OK ; if (dquot->dq_dqb.dqb_ihardlimit &&
@@ -1071,7 +1071,7 @@ static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)static int check_bdq (struct dquot *dquot,qsize_t space,int prealloc,char *warntype) { *warntype =QUOTA_NL_NOWARN ; -if (space <=0 ||test_bit (DQ_FAKE_B , &dquot->dq_flags)) +if (test_bit (DQ_FAKE_B , &dquot->dq_flags))return QUOTA_OK ; if (dquot->dq_dqb.dqb_bhardlimit &&
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 4/29 quota: Make _SUSPENDED just a flag
In reply to Jan KaraUpto now, DQUOT_USR_SUSPENDED behaved like a state - i.e., either quota was enabled or suspended or none. Now allowed states are 0, ENABLED, ENABLED | SUSPENDED. This will be useful later when we implement separate enabling of quota usage tracking and limits enforcement because we need to keep track of a state which has been suspended.
--- fs/dquot.c | 10 ++++++---- include/linux/quotaops.h | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-)
fs/dquot.c +6 -4
@@ -1566,18 +1566,20 @@ static inline void reset_enable_flags(struct quota_info *dqopt, int type, {switch (type) {case USRQUOTA : - dqopt->flags &= ~DQUOT_USR_ENABLED ;if (remount) dqopt->flags |=DQUOT_USR_SUSPENDED ; -else +else { + dqopt->flags &= ~DQUOT_USR_ENABLED ; dqopt->flags &= ~DQUOT_USR_SUSPENDED ; + }break ;case GRPQUOTA : - dqopt->flags &= ~DQUOT_GRP_ENABLED ;if (remount) dqopt->flags |=DQUOT_GRP_SUSPENDED ; -else +else { + dqopt->flags &= ~DQUOT_GRP_ENABLED ; dqopt->flags &= ~DQUOT_GRP_SUSPENDED ; + }break ; } }
include/linux/quotaops.h +4 -2
@@ -70,8 +70,10 @@ static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type)static inline int sb_has_quota_enabled (struct super_block *sb,int type) {if (type ==USRQUOTA ) -return sb_dqopt (sb)->flags &DQUOT_USR_ENABLED ; -return sb_dqopt (sb)->flags &DQUOT_GRP_ENABLED ; +return (sb_dqopt (sb)->flags &DQUOT_USR_ENABLED ) + && !(sb_dqopt (sb)->flags &DQUOT_USR_SUSPENDED ); +return (sb_dqopt (sb)->flags &DQUOT_GRP_ENABLED ) + && !(sb_dqopt (sb)->flags &DQUOT_GROUP_SUSPENDED ); }static inline int sb_any_quota_enabled (struct super_block *sb)
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 5/29 quota: Allow to separately enable quota accounting and enforcing limits
In reply to Jan KaraSplit DQUOT_USR_ENABLED (and DQUOT_GRP_ENABLED) into DQUOT_USR_USAGE_ENABLED and DQUOT_USR_LIMITS_ENABLED. This way we are able to separately enable / disable whether we should:
1) ignore quotas completely
2) just keep uptodate information about usage
3) actually enforce quota limits
This is going to be useful when quota is treated as filesystem metadata - we then want to keep quota information uptodate all the time and just enable / disable limits enforcement.
--- fs/dquot.c | 222 ++++++++++++++++++++++++++++----------------- fs/quota.c | 8 +- include/linux/quota.h | 30 ++++++- include/linux/quotaops.h | 86 ++++++++++++++---- 4 files changed, 234 insertions(+), 112 deletions(-)
fs/dquot.c +138 -84
@@ -489,7 +489,7 @@ int vfs_quota_sync(struct super_block *sb, int type)for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) {if (type !=-1 && cnt != type)continue ; -if (!sb_has_quota_enabled (sb, cnt)) +if (!sb_has_quota_active (sb, cnt))continue ;spin_lock (&dq_list_lock); dirty = &dqopt->info[cnt].dqi_dirty_list;
@@ -514,8 +514,8 @@ int vfs_quota_sync(struct super_block *sb, int type) }for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) -if ((cnt == type || type ==-1 ) &&sb_has_quota_enabled (sb, cnt) - &&info_dirty (&dqopt->info[cnt])) +if ((cnt == type || type ==-1 ) &&sb_has_quota_active (sb, cnt) + &&info_dirty (&dqopt->info[cnt])) sb->dq_op->write_info (sb, cnt);spin_lock (&dq_list_lock); dqstats.syncs++;
@@ -594,7 +594,7 @@ we_slept:/* We have more than one user... nothing to do */ atomic_dec (&dquot->dq_count);/* Releasing dquot during quotaoff phase? */ -if (!sb_has_quota_enabled (dquot->dq_sb, dquot->dq_type) && +if (!sb_has_quota_active (dquot->dq_sb, dquot->dq_type) &&atomic_read (&dquot->dq_count) ==1 )wake_up (&dquot->dq_wait_unused);spin_unlock (&dq_list_lock);
@@ -668,7 +668,7 @@ static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)unsigned int hashent =hashfn (sb, id, type);struct dquot *dquot, *empty =NODQUOT ; -if (!sb_has_quota_enabled (sb, type)) +if (!sb_has_quota_active (sb, type))return NODQUOT ; we_slept:spin_lock (&dq_list_lock);
@@ -1039,7 +1039,8 @@ static inline char ignore_hardlimit(struct dquot *dquot)static int check_idq (struct dquot *dquot,qsize_t inodes,char *warntype) { *warntype =QUOTA_NL_NOWARN ; -if (test_bit (DQ_FAKE_B , &dquot->dq_flags)) +if (!sb_has_quota_limits_enabled (dquot->dq_sb, dquot->dq_type) || +test_bit (DQ_FAKE_B , &dquot->dq_flags))return QUOTA_OK ; if (dquot->dq_dqb.dqb_ihardlimit &&
@@ -1071,7 +1072,8 @@ static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)static int check_bdq (struct dquot *dquot,qsize_t space,int prealloc,char *warntype) { *warntype =QUOTA_NL_NOWARN ; -if (test_bit (DQ_FAKE_B , &dquot->dq_flags)) +if (!sb_has_quota_limits_enabled (dquot->dq_sb, dquot->dq_type) || +test_bit (DQ_FAKE_B , &dquot->dq_flags))return QUOTA_OK ; if (dquot->dq_dqb.dqb_bhardlimit &&
@@ -1112,7 +1114,8 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warstatic int info_idq_free (struct dquot *dquot,qsize_t inodes) {if (test_bit (DQ_FAKE_B , &dquot->dq_flags) || - dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit) + dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit || + !sb_has_quota_limits_enabled (dquot->dq_sb, dquot->dq_type))return QUOTA_NL_NOWARN ;if (dquot->dq_dqb.dqb_curinodes - inodes <= dquot->dq_dqb.dqb_isoftlimit)
@@ -1506,7 +1509,7 @@ warn_put_all:/* Wrapper for transferring ownership of an inode */ int vfs_dq_transfer (struct inode *inode,struct iattr *iattr) { -if (sb_any_quota_enabled (inode->i_sb) && !IS_NOQUOTA (inode)) { +if (sb_any_quota_active (inode->i_sb) && !IS_NOQUOTA (inode)) {vfs_dq_init (inode);if (inode->i_sb->dq_op->transfer (inode, iattr) ==NO_QUOTA )return 1 ;
@@ -1547,53 +1550,22 @@ struct dquot_operations dquot_operations = { .write_info = dquot_commit_info }; -static inline void set_enable_flags (struct quota_info *dqopt,int type) -{ -switch (type) { -case USRQUOTA : - dqopt->flags |=DQUOT_USR_ENABLED ; - dqopt->flags &= ~DQUOT_USR_SUSPENDED ; -break ; -case GRPQUOTA : - dqopt->flags |=DQUOT_GRP_ENABLED ; - dqopt->flags &= ~DQUOT_GRP_SUSPENDED ; -break ; - } -} - -static inline void reset_enable_flags (struct quota_info *dqopt,int type, -int remount) -{ -switch (type) { -case USRQUOTA : -if (remount) - dqopt->flags |=DQUOT_USR_SUSPENDED ; -else { - dqopt->flags &= ~DQUOT_USR_ENABLED ; - dqopt->flags &= ~DQUOT_USR_SUSPENDED ; - } -break ; -case GRPQUOTA : -if (remount) - dqopt->flags |=DQUOT_GRP_SUSPENDED ; -else { - dqopt->flags &= ~DQUOT_GRP_ENABLED ; - dqopt->flags &= ~DQUOT_GRP_SUSPENDED ; - } -break ; - } -} - -/* * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount) */ -int vfs_quota_off (struct super_block *sb,int type,int remount) +int vfs_quota_disable (struct super_block *sb,int type,unsigned int flags) {int cnt, ret =0 ;struct quota_info *dqopt =sb_dqopt (sb);struct inode *toputinode[MAXQUOTAS ]; +/* Cannot turn off usage accounting without turning off limits, or +* suspend quotas and simultaneously turn quotas off. */ +if ((flags &DQUOT_USAGE_ENABLED && !(flags &DQUOT_LIMITS_ENABLED )) + || (flags &DQUOT_SUSPENDED && flags & (DQUOT_LIMITS_ENABLED | +DQUOT_USAGE_ENABLED ))) +return -EINVAL ; +/* We need to serialize quota_off() for device */ mutex_lock (&dqopt->dqonoff_mutex);
@@ -1602,7 +1574,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)* sometimes we are called when fill_super() failed and calling * sync_fs() in such cases does no good. */ -if (!sb_any_quota_enabled (sb) && !sb_any_quota_suspended (sb)) { +if (!sb_any_quota_loaded (sb)) {mutex_unlock (&dqopt->dqonoff_mutex);return 0 ; }
@@ -1610,17 +1582,28 @@ int vfs_quota_off(struct super_block *sb, int type, int remount) toputinode[cnt] =NULL ;if (type !=-1 && cnt != type)continue ; -/* If we keep inodes of quota files after remount and quotaoff -* is called, drop kept inodes. */ -if (!remount &&sb_has_quota_suspended (sb, cnt)) { -iput (dqopt->files[cnt]); - dqopt->files[cnt] =NULL ; -reset_enable_flags (dqopt, cnt,0 ); +if (!sb_has_quota_loaded (sb, cnt))continue ; + +if (flags &DQUOT_SUSPENDED ) { + dqopt->flags |= +dquot_state_flag (DQUOT_SUSPENDED , cnt); + }else { + dqopt->flags &= ~dquot_state_flag (flags, cnt); +/* Turning off suspended quotas? */ +if (!sb_has_quota_loaded (sb, cnt) && +sb_has_quota_suspended (sb, cnt)) { + dqopt->flags &= ~dquot_state_flag ( +DQUOT_SUSPENDED , cnt); +iput (dqopt->files[cnt]); + dqopt->files[cnt] =NULL ; +continue ; + } } -if (!sb_has_quota_enabled (sb, cnt)) + +/* We still have to keep quota loaded? */ +if (sb_has_quota_loaded (sb, cnt) && !(flags &DQUOT_SUSPENDED ))continue ; -reset_enable_flags (dqopt, cnt, remount);/* Note: these are blocking operations */ drop_dquot_ref (sb, cnt);
@@ -1636,7 +1619,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)put_quota_format (dqopt->info[cnt].dqi_format); toputinode[cnt] = dqopt->files[cnt]; -if (!remount) +if (!sb_has_quota_loaded (sb, cnt)) dqopt->files[cnt] =NULL ; dqopt->info[cnt].dqi_flags =0 ; dqopt->info[cnt].dqi_igrace =0 ;
@@ -1659,7 +1642,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)mutex_lock (&dqopt->dqonoff_mutex);/* If quota was reenabled in the meantime, we have * nothing to do */ -if (!sb_has_quota_enabled (sb, cnt)) { +if (!sb_has_quota_loaded (sb, cnt)) {mutex_lock_nested (&toputinode[cnt]->i_mutex,I_MUTEX_QUOTA ); toputinode[cnt]->i_flags &= ~(S_IMMUTABLE |S_NOATIME |S_NOQUOTA );
@@ -1669,10 +1652,13 @@ int vfs_quota_off(struct super_block *sb, int type, int remount) }mutex_unlock (&dqopt->dqonoff_mutex);/* On remount RO, we keep the inode pointer so that we -* can reenable quota on the subsequent remount RW. -* But we have better not keep inode pointer when there -* is pending delete on the quota file... */ -if (!remount) +* can reenable quota on the subsequent remount RW. We +* have to check 'flags' variable and not use sb_has_ +* function because another quotaon / quotaoff could +* change global state before we got here. We refuse +* to suspend quotas when there is pending delete on +* the quota file... */ +if (!(flags &DQUOT_SUSPENDED ))iput (toputinode[cnt]);else if (!toputinode[cnt]->i_nlink) ret = -EBUSY ;
@@ -1682,12 +1668,22 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)return ret; } +int vfs_quota_off (struct super_block *sb,int type,int remount) +{ +return vfs_quota_disable (sb, type, remount ?DQUOT_SUSPENDED : + (DQUOT_USAGE_ENABLED |DQUOT_LIMITS_ENABLED )); +} +/* * Turn quotas on on a device */ -/* Helper function when we already have the inode */ -static int vfs_quota_on_inode (struct inode *inode,int type,int format_id) +/* +* Helper function to turn quotas on when we already have the inode of +* quota file and no quota information is loaded. +*/ +static int vfs_load_quota_inode (struct inode *inode,int type,int format_id, +unsigned int flags) {struct quota_format_type *fmt =find_quota_format (format_id);struct super_block *sb = inode->i_sb;
@@ -1709,6 +1705,11 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id) error = -EINVAL ; goto out_fmt; } +/* Usage always has to be set... */ +if (!(flags &DQUOT_USAGE_ENABLED )) { + error = -EINVAL ; + goto out_fmt; + }/* As we bypass the pagecache we must now flush the inode so that * we see all the changes from userspace... */
@@ -1717,8 +1718,7 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)invalidate_bdev (sb->s_bdev);mutex_lock (&inode->i_mutex);mutex_lock (&dqopt->dqonoff_mutex); -if (sb_has_quota_enabled (sb, type) || -sb_has_quota_suspended (sb, type)) { +if (sb_has_quota_loaded (sb, type)) { error = -EBUSY ; goto out_lock; }
@@ -1750,7 +1750,7 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id) }mutex_unlock (&dqopt->dqio_mutex);mutex_unlock (&inode->i_mutex); -set_enable_flags (dqopt, type); + dqopt->flags |=dquot_state_flag (flags, type);add_dquot_ref (sb, type);mutex_unlock (&dqopt->dqonoff_mutex);
@@ -1783,20 +1783,23 @@ static int vfs_quota_on_remount(struct super_block *sb, int type)struct quota_info *dqopt =sb_dqopt (sb);struct inode *inode;int ret; +unsigned int flags;mutex_lock (&dqopt->dqonoff_mutex);if (!sb_has_quota_suspended (sb, type)) {mutex_unlock (&dqopt->dqonoff_mutex);return 0 ; } -BUG_ON (sb_has_quota_enabled (sb, type)); - inode = dqopt->files[type]; dqopt->files[type] =NULL ; -reset_enable_flags (dqopt, type,0 ); + flags = dqopt->flags &dquot_state_flag (DQUOT_USAGE_ENABLED | +DQUOT_LIMITS_ENABLED , type); + dqopt->flags &= ~dquot_state_flag (DQUOT_STATE_FLAGS , type);mutex_unlock (&dqopt->dqonoff_mutex); - ret =vfs_quota_on_inode (inode, type, dqopt->info[type].dqi_fmt_id); + flags =dquot_generic_flag (flags, type); + ret =vfs_load_quota_inode (inode, type, dqopt->info[type].dqi_fmt_id, + flags);iput (inode);return ret;
@@ -1812,12 +1815,12 @@ int vfs_quota_on_path(struct super_block *sb, int type, int format_id,if (path->mnt->mnt_sb != sb) error = -EXDEV ;else - error =vfs_quota_on_inode (path->dentry->d_inode, type, - format_id); + error =vfs_load_quota_inode (path->dentry->d_inode, type, + format_id,DQUOT_USAGE_ENABLED | +DQUOT_LIMITS_ENABLED );return error; } -/* Actual function called from quotactl() */ int vfs_quota_on (struct super_block *sb,int type,int format_id,char *path,int remount) {
@@ -1836,6 +1839,50 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path, }/* +* More powerful function for turning on quotas allowing setting +* of individual quota flags +*/ +int vfs_quota_enable (struct inode *inode,int type,int format_id, +unsigned int flags) +{ +int ret =0 ; +struct super_block *sb = inode->i_sb; +struct quota_info *dqopt =sb_dqopt (sb); + +/* Just unsuspend quotas? */ +if (flags &DQUOT_SUSPENDED ) +return vfs_quota_on_remount (sb, type); +if (!flags) +return 0 ; +/* Just updating flags needed? */ +if (sb_has_quota_loaded (sb, type)) { +mutex_lock (&dqopt->dqonoff_mutex); +/* Now do a reliable test... */ +if (!sb_has_quota_loaded (sb, type)) { +mutex_unlock (&dqopt->dqonoff_mutex); + goto load_quota; + } +if (flags &DQUOT_USAGE_ENABLED && +sb_has_quota_usage_enabled (sb, type)) { + ret = -EBUSY ; + goto out_lock; + } +if (flags &DQUOT_LIMITS_ENABLED && +sb_has_quota_limits_enabled (sb, type)) { + ret = -EBUSY ; + goto out_lock; + } +sb_dqopt (sb)->flags |=dquot_state_flag (flags, type); +out_lock: +mutex_unlock (&dqopt->dqonoff_mutex); +return ret; + } + +load_quota: +return vfs_load_quota_inode (inode, type, format_id, flags); +} + +/* * This function is used when filesystem needs to initialize quotas * during mount time. */
@@ -1856,7 +1903,8 @@ int vfs_quota_on_mount(struct super_block *sb, char *qf_name, error =security_quota_on (dentry);if (!error) - error =vfs_quota_on_inode (dentry->d_inode, type, format_id); + error =vfs_load_quota_inode (dentry->d_inode, type, format_id, +DQUOT_USAGE_ENABLED |DQUOT_LIMITS_ENABLED ); out:dput (dentry);
@@ -1993,12 +2041,14 @@ int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *dint rc;mutex_lock (&sb_dqopt (sb)->dqonoff_mutex); -if (!(dquot =dqget (sb, id, type))) { -mutex_unlock (&sb_dqopt (sb)->dqonoff_mutex); -return -ESRCH ; + dquot =dqget (sb, id, type); +if (!dquot) { + rc = -ESRCH ; + goto out; } rc =do_set_dqblk (dquot, di);dqput (dquot); +out:mutex_unlock (&sb_dqopt (sb)->dqonoff_mutex);return rc; }
@@ -2009,7 +2059,7 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)struct mem_dqinfo *mi;mutex_lock (&sb_dqopt (sb)->dqonoff_mutex); -if (!sb_has_quota_enabled (sb, type)) { +if (!sb_has_quota_active (sb, type)) {mutex_unlock (&sb_dqopt (sb)->dqonoff_mutex);return -ESRCH ; }
@@ -2028,11 +2078,12 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)int vfs_set_dqinfo (struct super_block *sb,int type,struct if_dqinfo *ii) {struct mem_dqinfo *mi; +int err =0 ;mutex_lock (&sb_dqopt (sb)->dqonoff_mutex); -if (!sb_has_quota_enabled (sb, type)) { -mutex_unlock (&sb_dqopt (sb)->dqonoff_mutex); -return -ESRCH ; +if (!sb_has_quota_active (sb, type)) { + err = -ESRCH ; + goto out; } mi =sb_dqopt (sb)->info + type;spin_lock (&dq_data_lock);
@@ -2046,8 +2097,9 @@ int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)mark_info_dirty (sb, type);/* Force write to disk */ sb->dq_op->write_info (sb, type); +out:mutex_unlock (&sb_dqopt (sb)->dqonoff_mutex); -return 0 ; +return err; }struct quotactl_ops vfs_quotactl_ops = {
@@ -2209,9 +2261,11 @@ EXPORT_SYMBOL(register_quota_format);EXPORT_SYMBOL (unregister_quota_format);EXPORT_SYMBOL (dqstats);EXPORT_SYMBOL (dq_data_lock); +EXPORT_SYMBOL (vfs_quota_enable);EXPORT_SYMBOL (vfs_quota_on);EXPORT_SYMBOL (vfs_quota_on_path);EXPORT_SYMBOL (vfs_quota_on_mount); +EXPORT_SYMBOL (vfs_quota_disable);EXPORT_SYMBOL (vfs_quota_off);EXPORT_SYMBOL (vfs_quota_sync);EXPORT_SYMBOL (vfs_get_dqinfo);
fs/quota.c +4 -4
@@ -73,7 +73,7 @@ static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qidcase Q_SETQUOTA :case Q_GETQUOTA :/* This is just informative test so we are satisfied without a lock */ -if (!sb_has_quota_enabled (sb, type)) +if (!sb_has_quota_active (sb, type))return -ESRCH ; }
@@ -175,7 +175,7 @@ static void quota_sync_sb(struct super_block *sb, int type)for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) {if (type !=-1 && cnt != type)continue ; -if (!sb_has_quota_enabled (sb, cnt)) +if (!sb_has_quota_active (sb, cnt))continue ;mutex_lock_nested (&sb_dqopt (sb)->files[cnt]->i_mutex,I_MUTEX_QUOTA );truncate_inode_pages (&sb_dqopt (sb)->files[cnt]->i_data,0 );
@@ -201,7 +201,7 @@ restart:for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) {if (type !=-1 && type != cnt)continue ; -if (!sb_has_quota_enabled (sb, cnt)) +if (!sb_has_quota_active (sb, cnt))continue ;if (!info_dirty (&sb_dqopt (sb)->info[cnt]) &&list_empty (&sb_dqopt (sb)->info[cnt].dqi_dirty_list))
@@ -245,7 +245,7 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void__u32 fmt;down_read (&sb_dqopt (sb)->dqptr_sem); -if (!sb_has_quota_enabled (sb, type)) { +if (!sb_has_quota_active (sb, type)) {up_read (&sb_dqopt (sb)->dqptr_sem);return -ESRCH ; }
include/linux/quota.h +26 -4
@@ -320,12 +320,34 @@ struct quota_format_type {struct quota_format_type *qf_next; }; -#define DQUOT_USR_ENABLED 0x01/* User diskquotas enabled */ -#define DQUOT_GRP_ENABLED 0x02/* Group diskquotas enabled */ -#define DQUOT_USR_SUSPENDED 0x04/* User diskquotas are off, but +/* Quota state flags - they actually come in two flavors - for users and groups */ +enum { + _DQUOT_USAGE_ENABLED =0 ,/* Track disk usage for users */ + _DQUOT_LIMITS_ENABLED,/* Enforce quota limits for users */ + _DQUOT_SUSPENDED,/* User diskquotas are off, but * we have necessary info in * memory to turn them on */ -#define DQUOT_GRP_SUSPENDED 0x08/* The same for group quotas */ + _DQUOT_STATE_FLAGS +}; +#define DQUOT_USAGE_ENABLED (1 << _DQUOT_USAGE_ENABLED) +#define DQUOT_LIMITS_ENABLED (1 << _DQUOT_LIMITS_ENABLED) +#define DQUOT_SUSPENDED (1 << _DQUOT_SUSPENDED) +#define DQUOT_STATE_FLAGS (DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED | \ + DQUOT_SUSPENDED) + +static inline unsigned int dquot_state_flag (unsigned int flags,int type) +{ +if (type ==USRQUOTA ) +return flags; +return flags << _DQUOT_STATE_FLAGS; +} + +static inline unsigned int dquot_generic_flag (unsigned int flags,int type) +{ +if (type ==USRQUOTA ) +return flags; +return flags >> _DQUOT_STATE_FLAGS; +}struct quota_info {unsigned int flags;/* Flags for diskquotas on this device */
include/linux/quotaops.h +66 -20
169 lines not on this page · raw message
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 6/29 ext3: Use sb_any_quota_loaded() instead of sb_any_quota_enabled()
In reply to Jan Kara--- fs/ext3/super.c | 12 ++++-------- 1 files changed, 4 insertions(+), 8 deletions(-)
fs/ext3/super.c +4 -8
@@ -1018,8 +1018,7 @@ static int parse_options (char *options, struct super_block *sb,case Opt_grpjquota: qtype =GRPQUOTA ; set_qf_name: -if ((sb_any_quota_enabled (sb) || -sb_any_quota_suspended (sb)) && +if (sb_any_quota_loaded (sb) && !sbi->s_qf_names[qtype]) {printk (KERN_ERR "EXT3-fs: Cannot change journaled "
@@ -1058,8 +1057,7 @@ set_qf_name:case Opt_offgrpjquota: qtype =GRPQUOTA ; clear_qf_name: -if ((sb_any_quota_enabled (sb) || -sb_any_quota_suspended (sb)) && +if (sb_any_quota_loaded (sb) && sbi->s_qf_names[qtype]) {printk (KERN_ERR "EXT3-fs: Cannot change " "journaled quota options when "
@@ -1078,8 +1076,7 @@ clear_qf_name:case Opt_jqfmt_vfsv0: qfmt =QFMT_VFS_V0 ; set_qf_format: -if ((sb_any_quota_enabled (sb) || -sb_any_quota_suspended (sb)) && +if (sb_any_quota_loaded (sb) && sbi->s_jquota_fmt != qfmt) {printk (KERN_ERR "EXT3-fs: Cannot change " "journaled quota options when "
@@ -1098,8 +1095,7 @@ set_qf_format:set_opt (sbi->s_mount_opt,GRPQUOTA );break ;case Opt_noquota: -if (sb_any_quota_enabled (sb) || -sb_any_quota_suspended (sb)) { +if (sb_any_quota_loaded (sb)) {printk (KERN_ERR "EXT3-fs: Cannot change quota " "options when quota turned on.\n" );return 0 ;
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 7/29 ext4: Use sb_any_quota_loaded() instead of sb_any_quota_enabled()
In reply to Jan Kara--- fs/ext4/super.c | 11 ++++------- 1 files changed, 4 insertions(+), 7 deletions(-)
fs/ext4/super.c +4 -7
@@ -1206,8 +1206,7 @@ static int parse_options(char *options, struct super_block *sb,case Opt_grpjquota: qtype =GRPQUOTA ; set_qf_name: -if ((sb_any_quota_enabled (sb) || -sb_any_quota_suspended (sb)) && +if (sb_any_quota_loaded (sb) && !sbi->s_qf_names[qtype]) {printk (KERN_ERR "EXT4-fs: Cannot change journaled "
@@ -1246,8 +1245,7 @@ set_qf_name:case Opt_offgrpjquota: qtype =GRPQUOTA ; clear_qf_name: -if ((sb_any_quota_enabled (sb) || -sb_any_quota_suspended (sb)) && +if (sb_any_quota_loaded (sb) && sbi->s_qf_names[qtype]) {printk (KERN_ERR "EXT4-fs: Cannot change " "journaled quota options when "
@@ -1266,8 +1264,7 @@ clear_qf_name:case Opt_jqfmt_vfsv0: qfmt =QFMT_VFS_V0 ; set_qf_format: -if ((sb_any_quota_enabled (sb) || -sb_any_quota_suspended (sb)) && +if (sb_any_quota_loaded (sb) && sbi->s_jquota_fmt != qfmt) {printk (KERN_ERR "EXT4-fs: Cannot change " "journaled quota options when "
@@ -1286,7 +1283,7 @@ set_qf_format:set_opt (sbi->s_mount_opt,GRPQUOTA );break ;case Opt_noquota: -if (sb_any_quota_enabled (sb)) { +if (sb_any_quota_loaded (sb)) {printk (KERN_ERR "EXT4-fs: Cannot change quota " "options when quota turned on.\n" );return 0 ;
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 8/29 reiserfs: Use sb_any_quota_loaded() instead of sb_any_quota_enabled().
In reply to Jan Kara--- fs/reiserfs/super.c | 8 +++----- 1 files changed, 3 insertions(+), 5 deletions(-)
fs/reiserfs/super.c +3 -5
@@ -994,8 +994,7 @@ static int reiserfs_parse_options(struct super_block *s, char *options, /* strinif (c =='u' || c =='g' ) {int qtype = c =='u' ?USRQUOTA :GRPQUOTA ; -if ((sb_any_quota_enabled (s) || -sb_any_quota_suspended (s)) && +if (sb_any_quota_loaded (s) && (!*arg != !REISERFS_SB (s)->s_qf_names[qtype])) {reiserfs_warning (s,"reiserfs_parse_options: cannot change journaled quota options when quota turned on." );
@@ -1041,8 +1040,7 @@ static int reiserfs_parse_options(struct super_block *s, char *options, /* strin "reiserfs_parse_options: unknown quota format specified.");return 0 ; } -if ((sb_any_quota_enabled (s) || -sb_any_quota_suspended (s)) && +if (sb_any_quota_loaded (s) && *qfmt !=REISERFS_SB (s)->s_jquota_fmt) {reiserfs_warning (s,"reiserfs_parse_options: cannot change journaled quota options when quota turned on." );
@@ -1067,7 +1065,7 @@ static int reiserfs_parse_options(struct super_block *s, char *options, /* strin }/* This checking is not precise wrt the quota type but for our purposes it is sufficient */ if (!(*mount_options & (1 <<REISERFS_QUOTA )) - &&sb_any_quota_enabled (s)) { + &&sb_any_quota_loaded (s)) {reiserfs_warning (s,"reiserfs_parse_options: quota options must be present when quota is turned on." );return 0 ;
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 9/29 quota: Remove compatibility function sb_any_quota_enabled()
In reply to Jan Kara--- include/linux/quotaops.h | 6 ------ 1 files changed, 0 insertions(+), 6 deletions(-)
include/linux/quotaops.h +0 -6
@@ -119,9 +119,6 @@ static inline int sb_any_quota_active(struct super_block *sb)sb_has_quota_active (sb,GRPQUOTA ); } -/* For backward compatibility until we remove all users */ -#define sb_any_quota_enabled (sb) sb_any_quota_active(sb) -/* * Operations supported for diskquotas. */
@@ -265,9 +262,6 @@ static inline int sb_any_quota_active(struct super_block *sb)return 0 ; } -/* For backward compatibility until we remove all users */ -#define sb_any_quota_enabled (sb) sb_any_quota_active(sb) -/* * NO-OP when quota not configured. */
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 10/29 quota: Introduce DQUOT_QUOTA_SYS_FILE flag
In reply to Jan KaraIf filesystem can handle quota files as system files hidden from users, we can skip a lot of cache invalidation, syncing, inode flags setting etc. when turning quotas on, off and quota_sync. Allow filesystem to indicate that it is hiding quota files from users by DQUOT_QUOTA_SYS_FILE flag.
--- fs/dquot.c | 45 ++++++++++++++++++++++++++++++--------------- fs/quota.c | 3 +++ include/linux/quota.h | 7 +++++++ 3 files changed, 40 insertions(+), 15 deletions(-)
fs/dquot.c +30 -15
@@ -1627,6 +1627,11 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags) dqopt->ops[cnt] =NULL ; }mutex_unlock (&dqopt->dqonoff_mutex); + +/* Skip syncing and setting flags if quota files are hidden */ +if (dqopt->flags &DQUOT_QUOTA_SYS_FILE ) + goto put_inodes; +/* Sync the superblock so that buffers with quota data are written to * disk (and so userspace sees correct data afterwards). */ if (sb->s_op->sync_fs)
@@ -1651,6 +1656,12 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)mark_inode_dirty (toputinode[cnt]); }mutex_unlock (&dqopt->dqonoff_mutex); + } +if (sb->s_bdev) +invalidate_bdev (sb->s_bdev); +put_inodes: +for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) +if (toputinode[cnt]) {/* On remount RO, we keep the inode pointer so that we * can reenable quota on the subsequent remount RW. We * have to check 'flags' variable and not use sb_has_
@@ -1663,8 +1674,6 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)else if (!toputinode[cnt]->i_nlink) ret = -EBUSY ; } -if (sb->s_bdev) -invalidate_bdev (sb->s_bdev);return ret; }
@@ -1711,25 +1720,31 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id, goto out_fmt; } -/* As we bypass the pagecache we must now flush the inode so that -* we see all the changes from userspace... */ -write_inode_now (inode,1 ); -/* And now flush the block cache so that kernel sees the changes */ -invalidate_bdev (sb->s_bdev); +if (!(dqopt->flags &DQUOT_QUOTA_SYS_FILE )) { +/* As we bypass the pagecache we must now flush the inode so +* that we see all the changes from userspace... */ +write_inode_now (inode,1 ); +/* And now flush the block cache so that kernel sees the +* changes */ +invalidate_bdev (sb->s_bdev); + }mutex_lock (&inode->i_mutex);mutex_lock (&dqopt->dqonoff_mutex);if (sb_has_quota_loaded (sb, type)) { error = -EBUSY ; goto out_lock; } -/* We don't want quota and atime on quota files (deadlocks possible) -* Also nobody should write to the file - we use special IO operations -* which ignore the immutable bit. */ -down_write (&dqopt->dqptr_sem); - oldflags = inode->i_flags & (S_NOATIME |S_IMMUTABLE |S_NOQUOTA ); - inode->i_flags |=S_NOQUOTA |S_NOATIME |S_IMMUTABLE ; -up_write (&dqopt->dqptr_sem); - sb->dq_op->drop (inode); + +if (!(dqopt->flags &DQUOT_QUOTA_SYS_FILE )) { +/* We don't want quota and atime on quota files (deadlocks +* possible) Also nobody should write to the file - we use +* special IO operations which ignore the immutable bit. */ +down_write (&dqopt->dqptr_sem); + oldflags = inode->i_flags & (S_NOATIME |S_IMMUTABLE |S_NOQUOTA ); + inode->i_flags |=S_NOQUOTA |S_NOATIME |S_IMMUTABLE ; +up_write (&dqopt->dqptr_sem); + sb->dq_op->drop (inode); + } error = -EIO ; dqopt->files[type] =igrab (inode);
fs/quota.c +3 -0
@@ -160,6 +160,9 @@ static void quota_sync_sb(struct super_block *sb, int type)int cnt; sb->s_qcop->quota_sync (sb, type); + +if (sb_dqopt (sb)->flags &DQUOT_QUOTA_SYS_FILE ) +return ;/* This is not very clever (and fast) but currently I don't know about * any other simple way of getting quota data to disk and we must get * them there for userspace to be visible... */
include/linux/quota.h +7 -0
@@ -334,6 +334,13 @@ enum {#define DQUOT_SUSPENDED (1 << _DQUOT_SUSPENDED)#define DQUOT_STATE_FLAGS (DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED | \ DQUOT_SUSPENDED) +/* Other quota flags */ +#define DQUOT_QUOTA_SYS_FILE (1 << 6)/* Quota file is a special +* system file and user cannot +* touch it. Filesystem is +* responsible for setting +* S_NOQUOTA, S_NOATIME flags +*/ static inline unsigned int dquot_state_flag (unsigned int flags,int type) {
Signature · 1 line
1.5.2.4
PATCH 10/29 quota: Introduce DQUOT_QUOTA_SYS_FILE flag
Quoted 29 lines · Jan Kara
If filesystem can handle quota files as system files hidden from users, we can skip a lot of cache invalidation, syncing, inode flags setting etc. when turning quotas on, off and
quota_sync. Allow filesystem to indicate that it is hiding quota files from users byDQUOT_QUOTA_SYS_FILEflag.Signed-off-by Jan Kara <[email protected]>--- fs/dquot.c | 45 ++++++++++++++++++++++++++++++--------------- fs/quota.c | 3 +++ include/linux/quota.h | 7 +++++++ 3 files changed, 40 insertions(+), 15 deletions(-)fs/dquot.c +5
-0@@ -1627,6 +1627,11 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags) dqopt->ops[cnt] =NULL ; }mutex_unlock (&dqopt->dqonoff_mutex); + +/* Skip syncing and setting flags if quota files are hidden */ +if (dqopt->flags &DQUOT_QUOTA_SYS_FILE ) + goto put_inodes; +/* Sync the superblock so that buffers with quota data are written to * disk (and so userspace sees correct data afterwards). */ if (sb->s_op->sync_fs)
fs/dquot.c int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags) · line 1661
Quoted 6 lines
fs/dquot.c +3
-0@@ -1651,6 +1656,12 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)mark_inode_dirty (toputinode[cnt]); }mutex_unlock (&dqopt->dqonoff_mutex); + } +if (sb->s_bdev) +invalidate_bdev (sb->s_bdev);
Is the indentation here weird, or am I just reading this wrong?
Otherwise btw, this patch is a fantastic idea. I hadn't thought of it before, but this should save Ocfs2 a good amount of pain.
--Mark
--
Mark Fasheh
Jan Kara
reviewerPATCH 10/29 quota: Introduce DQUOT_QUOTA_SYS_FILE flag
Quoted 30 lines · Mark Fasheh
Quoted 29 lines · Jan Kara
If filesystem can handle quota files as system files hidden from users, we can skip a lot of cache invalidation, syncing, inode flags setting etc. when turning quotas on, off and
quota_sync. Allow filesystem to indicate that it is hiding quota files from users byDQUOT_QUOTA_SYS_FILEflag.Signed-off-by Jan Kara <[email protected]>--- fs/dquot.c | 45 ++++++++++++++++++++++++++++++--------------- fs/quota.c | 3 +++ include/linux/quota.h | 7 +++++++ 3 files changed, 40 insertions(+), 15 deletions(-)fs/dquot.c +5
-0@@ -1627,6 +1627,11 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags) dqopt->ops[cnt] =NULL ; }mutex_unlock (&dqopt->dqonoff_mutex); + +/* Skip syncing and setting flags if quota files are hidden */ +if (dqopt->flags &DQUOT_QUOTA_SYS_FILE ) + goto put_inodes; +/* Sync the superblock so that buffers with quota data are written to * disk (and so userspace sees correct data afterwards). */ if (sb->s_op->sync_fs)@@ -1651,6 +1656,12 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
Quoted 8 lines
fs/dquot.c int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags) · line 1661
Quoted 6 lines
fs/dquot.c +3
-0mark_inode_dirty (toputinode[cnt]); }mutex_unlock (&dqopt->dqonoff_mutex); + } +if (sb->s_bdev) +invalidate_bdev (sb->s_bdev);Is the indentation here weird, or am I just reading this wrong?
It is:
for (...)
if (...) {
...
}Which looks strange in the patch but otherwise is fine.
Quoted 2 lines
Otherwise btw, this patch is a fantastic idea. I hadn't thought of it before, but this should save Ocfs2 a good amount of pain.
Thanks :).
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Jan Kara
reviewerPATCH 11/29 quota: Move quotaio_v[12].h from include/linux/ to fs/
In reply to Jan KaraSince these include files are used only by implementation of quota formats, there's no need to have them in include/linux/.
--- fs/quota_v1.c | 3 +- fs/quota_v2.c | 7 ++-- fs/quotaio_v1.h | 33 ++++++++++++++++++ fs/quotaio_v2.h | 79 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/quotaio_v1.h | 33 ------------------ include/linux/quotaio_v2.h | 79 -------------------------------------------- 6 files changed, 118 insertions(+), 116 deletions(-) create mode 100644 fs/quotaio_v1.h create mode 100644 fs/quotaio_v2.h delete mode 100644 include/linux/quotaio_v1.h delete mode 100644 include/linux/quotaio_v2.h
fs/quota_v1.c +2 -1
@@ -3,13 +3,14 @@#include <linux/quota.h> #include <linux/quotaops.h> #include <linux/dqblk_v1.h> -#include <linux/quotaio_v1.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <asm/byteorder.h> +#include "quotaio_v1.h" +MODULE_AUTHOR ("Jan Kara" );MODULE_DESCRIPTION ("Old quota format support" );MODULE_LICENSE ("GPL" );
fs/quota_v2.c +4 -3
@@ -6,7 +6,6 @@#include <linux/fs.h> #include <linux/mount.h> #include <linux/dqblk_v2.h> -#include <linux/quotaio_v2.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h>
@@ -15,6 +14,8 @@#include <asm/byteorder.h> +#include "quotaio_v2.h" +MODULE_AUTHOR ("Jan Kara" );MODULE_DESCRIPTION ("Quota format v2 support" );MODULE_LICENSE ("GPL" );
@@ -129,8 +130,8 @@ static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id) d->dqb_isoftlimit =cpu_to_le32 (m->dqb_isoftlimit); d->dqb_curinodes =cpu_to_le32 (m->dqb_curinodes); d->dqb_itime =cpu_to_le64 (m->dqb_itime); - d->dqb_bhardlimit =cpu_to_le32 (v2_qbtos (m->dqb_bhardlimit)); - d->dqb_bsoftlimit =cpu_to_le32 (v2_qbtos (m->dqb_bsoftlimit)); + d->dqb_bhardlimit =cpu_to_le32 (v2_stoqb (m->dqb_bhardlimit)); + d->dqb_bsoftlimit =cpu_to_le32 (v2_stoqb (m->dqb_bsoftlimit)); d->dqb_curspace =cpu_to_le64 (m->dqb_curspace); d->dqb_btime =cpu_to_le64 (m->dqb_btime); d->dqb_id =cpu_to_le32 (id);
fs/quotaio_v1.h new file +33 -0
@@ -0,0 +1,33 @@ +#ifndef _LINUX_QUOTAIO_V1_H +#define _LINUX_QUOTAIO_V1_H + +#include <linux/types.h> + +/* +* The following constants define the amount of time given a user +* before the soft limits are treated as hard limits (usually resulting +* in an allocation failure). The timer is started when the user crosses +* their soft limit, it is reset when they go below their soft limit. +*/ +#define MAX_IQ_TIME 604800/* (7*24*60*60) 1 week */ +#define MAX_DQ_TIME 604800/* (7*24*60*60) 1 week */ + +/* +* The following structure defines the format of the disk quota file +* (as it appears on disk) - the file is an array of these structures +* indexed by user or group number. +*/ +struct v1_disk_dqblk { +__u32 dqb_bhardlimit;/* absolute limit on disk blks alloc */ +__u32 dqb_bsoftlimit;/* preferred limit on disk blks */ +__u32 dqb_curblocks;/* current block count */ +__u32 dqb_ihardlimit;/* absolute limit on allocated inodes */ +__u32 dqb_isoftlimit;/* preferred inode limit */ +__u32 dqb_curinodes;/* current # allocated inodes */ +time_t dqb_btime;/* time limit for excessive disk use */ +time_t dqb_itime;/* time limit for excessive inode use */ +}; + +#define v1_dqoff (UID ) ((loff_t)((UID) * sizeof (struct v1_disk_dqblk))) + +#endif /* _LINUX_QUOTAIO_V1_H */
fs/quotaio_v2.h new file +79 -0
79 lines not on this page · raw message
include/linux/quotaio_v1.h deleted +0 -33
@@ -1,33 +0,0 @@ -#ifndef _LINUX_QUOTAIO_V1_H -#define _LINUX_QUOTAIO_V1_H - -#include <linux/types.h> - -/* -* The following constants define the amount of time given a user -* before the soft limits are treated as hard limits (usually resulting -* in an allocation failure). The timer is started when the user crosses -* their soft limit, it is reset when they go below their soft limit. -*/ -#define MAX_IQ_TIME 604800/* (7*24*60*60) 1 week */ -#define MAX_DQ_TIME 604800/* (7*24*60*60) 1 week */ - -/* -* The following structure defines the format of the disk quota file -* (as it appears on disk) - the file is an array of these structures -* indexed by user or group number. -*/ -struct v1_disk_dqblk { -__u32 dqb_bhardlimit;/* absolute limit on disk blks alloc */ -__u32 dqb_bsoftlimit;/* preferred limit on disk blks */ -__u32 dqb_curblocks;/* current block count */ -__u32 dqb_ihardlimit;/* absolute limit on allocated inodes */ -__u32 dqb_isoftlimit;/* preferred inode limit */ -__u32 dqb_curinodes;/* current # allocated inodes */ -time_t dqb_btime;/* time limit for excessive disk use */ -time_t dqb_itime;/* time limit for excessive inode use */ -}; - -#define v1_dqoff (UID ) ((loff_t)((UID) * sizeof (struct v1_disk_dqblk))) - -#endif /* _LINUX_QUOTAIO_V1_H */
include/linux/quotaio_v2.h deleted +0 -79
79 lines not on this page · raw message
Signature · 1 line
1.5.2.4
PATCH 11/29 quota: Move quotaio_v[12].h from include/linux/ to fs/
Quoted 2 lines · Jan Kara
Since these include files are used only by implementation of quota formats, there's no need to have them in
include/linux/.
Also a good idea imho.
--Mark
--
Mark Fasheh
Jan Kara
reviewerPATCH 12/29 quota: Split off quota tree handling into a separate file
In reply to Jan KaraThere is going to be a new version of quota format having 64-bit quota limits and a new quota format for OCFS2. They are both going to use the same tree structure as VFSv0 quota format. So split out tree handling into a separate file and make size of leaf blocks, amount of space usable in each block (needed for checksumming) and structures contained in them configurable so that the code can be shared.
--- fs/Kconfig | 5 + fs/Makefile | 1 + fs/quota_tree.c | 645 +++++++++++++++++++++++++++++++++++++++++++ fs/quota_tree.h | 25 ++ fs/quota_v2.c | 596 ++++------------------------------------ fs/quotaio_v2.h | 33 +-- include/linux/dqblk_qtree.h | 56 ++++ include/linux/dqblk_v2.h | 19 +- 8 files changed, 799 insertions(+), 581 deletions(-) create mode 100644 fs/quota_tree.c create mode 100644 fs/quota_tree.h create mode 100644 include/linux/dqblk_qtree.h
fs/Kconfig +5 -0
@@ -601,6 +601,10 @@ config PRINT_QUOTA_WARNING Note that this behavior is currently deprecated and may go away in future. Please use notification via netlink socket instead. +# Generic support for tree structured quota files. Seleted when needed. +config QUOTA_TREE + tristate + config QFMT_V1 tristate "Old quota format support" depends on QUOTA
@@ -612,6 +616,7 @@ config QFMT_V1 config QFMT_V2 tristate "Quota format v2 support" depends on QUOTA + select QUOTA_TREE help This quota format allows using quotas with 32-bit UIDs/GIDs. If you need this functionality say Y here.
fs/Makefile +1 -0
@@ -54,6 +54,7 @@ obj-$(CONFIG_GENERIC_ACL) += generic_acl.o obj-$(CONFIG_QUOTA) += dquot.o obj-$(CONFIG_QFMT_V1) += quota_v1.o obj-$(CONFIG_QFMT_V2) += quota_v2.o +obj-$(CONFIG_QUOTA_TREE) += quota_tree.o obj-$(CONFIG_QUOTACTL) += quota.o obj-$(CONFIG_DNOTIFY) += dnotify.o
fs/quota_tree.c new file +645 -0
645 lines not on this page · raw message
fs/quota_tree.h new file +25 -0
@@ -0,0 +1,25 @@ +/* +* Definitions of structures for vfsv0 quota format +*/ + +#ifndef _LINUX_QUOTA_TREE_H +#define _LINUX_QUOTA_TREE_H + +#include <linux/types.h> +#include <linux/quota.h> + +/* +* Structure of header of block with quota structures. It is padded to 16 bytes so +* there will be space for exactly 21 quota-entries in a block +*/ +struct qt_disk_dqdbheader { +__le32 dqdh_next_free;/* Number of next block with free entry */ +__le32 dqdh_prev_free;/* Number of previous block with free entry */ +__le16 dqdh_entries;/* Number of valid entries in block */ +__le16 dqdh_pad1; +__le32 dqdh_pad2; +}; + +#define QT_TREEOFF 1/* Offset of tree in file in blocks */ + +#endif /* _LINUX_QUOTAIO_TREE_H */
fs/quota_v2.c +52 -544
671 lines not on this page · raw message
fs/quotaio_v2.h +7 -26
@@ -21,6 +21,12 @@ 0 /* GRPQUOTA */\ } +/* First generic header */ +struct v2_disk_dqheader { +__le32 dqh_magic;/* Magic number identifying file */ +__le32 dqh_version;/* File version */ +}; +/* * The following structure defines the format of the disk quota file * (as it appears on disk) - the file is a radix tree whose leaves point
@@ -38,15 +44,6 @@ struct v2_disk_dqblk {__le64 dqb_itime;/* time limit for excessive inode use */ }; -/* -* Here are header structures as written on disk and their in-memory copies -*/ -/* First generic header */ -struct v2_disk_dqheader { -__le32 dqh_magic;/* Magic number identifying file */ -__le32 dqh_version;/* File version */ -}; -/* Header with type and version specific information */ struct v2_disk_dqinfo {__le32 dqi_bgrace;/* Time before block soft limit becomes hard limit */
@@ -57,23 +54,7 @@ struct v2_disk_dqinfo { __le32 dqi_free_entry; /* Number of block with at least one free entry */ }; -/* -* Structure of header of block with quota structures. It is padded to 16 bytes so -* there will be space for exactly 21 quota-entries in a block -*/ -struct v2_disk_dqdbheader { -__le32 dqdh_next_free;/* Number of next block with free entry */ -__le32 dqdh_prev_free;/* Number of previous block with free entry */ -__le16 dqdh_entries;/* Number of valid entries in block */ -__le16 dqdh_pad1; -__le32 dqdh_pad2; -}; -#define V2_DQINFOOFF sizeof(struct v2_disk_dqheader)/* Offset of info header in file */ -#define V2_DQBLKSIZE_BITS 10 -#define V2_DQBLKSIZE (1 << V2_DQBLKSIZE_BITS)/* Size of block with quota structures */ -#define V2_DQTREEOFF 1/* Offset of tree in file in blocks */ -#define V2_DQTREEDEPTH 4/* Depth of quota tree */ -#define V2_DQSTRINBLK ((V2_DQBLKSIZE - sizeof(struct v2_disk_dqdbheader)) / sizeof(struct v2_disk_dqblk))/* Number of entries in one blocks */ +#define V2_DQBLKSIZE_BITS 10/* Size of leaf block in tree */ #endif /* _LINUX_QUOTAIO_V2_H */
include/linux/dqblk_qtree.h new file +56 -0
56 lines not on this page · raw message
include/linux/dqblk_v2.h +8 -11
@@ -1,26 +1,23 @@/* -* Definitions of structures for vfsv0 quota format +* Definitions for vfsv0 quota format */ #ifndef _LINUX_DQBLK_V2_H#define _LINUX_DQBLK_V2_H -#include <linux/types.h> +#include <linux/dqblk_qtree.h> -/* id numbers of quota format */ +/* Id number of quota format */ #define QFMT_VFS_V0 2/* Numbers of blocks needed for updates */ -#define V2_INIT_ALLOC 4 -#define V2_INIT_REWRITE 2 -#define V2_DEL_ALLOC 0 -#define V2_DEL_REWRITE 6 +#define V2_INIT_ALLOC QTREE_INIT_ALLOC +#define V2_INIT_REWRITE QTREE_INIT_REWRITE +#define V2_DEL_ALLOC QTREE_DEL_ALLOC +#define V2_DEL_REWRITE QTREE_DEL_REWRITE -/* Inmemory copy of version specific information */ struct v2_mem_dqinfo { -unsigned int dqi_blocks; -unsigned int dqi_free_blk; -unsigned int dqi_free_entry; +struct qtree_mem_dqinfo i; };#endif /* _LINUX_DQBLK_V2_H */
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 13/29 quota: Convert union in mem_dqinfo to a pointer
In reply to Jan KaraComing quota support for OCFS2 is going to need quite a bit of additional per-sb quota information. Moreover having fs.h include all the types needed for this structure would be a pain in the a**. So remove the union from mem_dqinfo and add a private pointer for filesystem's use.
--- fs/quota_v2.c | 53 +++++++++++++++++++++++++++++---------------- include/linux/dqblk_v1.h | 4 --- include/linux/dqblk_v2.h | 4 --- include/linux/quota.h | 5 +--- 4 files changed, 35 insertions(+), 31 deletions(-)
fs/quota_v2.c +34 -19
@@ -71,6 +71,7 @@ static int v2_read_file_info(struct super_block *sb, int type) {struct v2_disk_dqinfo dinfo;struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct qtree_mem_dqinfo *qinfo;ssize_t size; size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
@@ -80,22 +81,29 @@ static int v2_read_file_info(struct super_block *sb, int type) sb->s_id);return -1 ; } + info->dqi_priv =kmalloc (sizeof (struct qtree_mem_dqinfo ),GFP_NOFS ); +if (!info->dqi_priv) { +printk (KERN_WARNING "Not enough memory for quota information" +"structure.\n" ); +return -1 ; + } + qinfo = info->dqi_priv;/* limits are stored as unsigned 32-bit data */ info->dqi_maxblimit =0xffffffff ; info->dqi_maxilimit =0xffffffff ; info->dqi_bgrace =le32_to_cpu (dinfo.dqi_bgrace); info->dqi_igrace =le32_to_cpu (dinfo.dqi_igrace); info->dqi_flags =le32_to_cpu (dinfo.dqi_flags); - info->u.v2_i.i.dqi_sb = sb; - info->u.v2_i.i.dqi_type = type; - info->u.v2_i.i.dqi_blocks =le32_to_cpu (dinfo.dqi_blocks); - info->u.v2_i.i.dqi_free_blk =le32_to_cpu (dinfo.dqi_free_blk); - info->u.v2_i.i.dqi_free_entry =le32_to_cpu (dinfo.dqi_free_entry); - info->u.v2_i.i.dqi_blocksize_bits =V2_DQBLKSIZE_BITS ; - info->u.v2_i.i.dqi_usable_bs =1 <<V2_DQBLKSIZE_BITS ; - info->u.v2_i.i.dqi_qtree_depth =qtree_depth (&info->u.v2_i.i); - info->u.v2_i.i.dqi_entry_size =sizeof (struct v2_disk_dqblk ); - info->u.v2_i.i.dqi_ops = &v2_qtree_ops; + qinfo->dqi_sb = sb; + qinfo->dqi_type = type; + qinfo->dqi_blocks =le32_to_cpu (dinfo.dqi_blocks); + qinfo->dqi_free_blk =le32_to_cpu (dinfo.dqi_free_blk); + qinfo->dqi_free_entry =le32_to_cpu (dinfo.dqi_free_entry); + qinfo->dqi_blocksize_bits =V2_DQBLKSIZE_BITS ; + qinfo->dqi_usable_bs =1 <<V2_DQBLKSIZE_BITS ; + qinfo->dqi_qtree_depth =qtree_depth (qinfo); + qinfo->dqi_entry_size =sizeof (struct v2_disk_dqblk ); + qinfo->dqi_ops = &v2_qtree_ops;return 0 ; }
@@ -104,6 +112,7 @@ static int v2_write_file_info(struct super_block *sb, int type) {struct v2_disk_dqinfo dinfo;struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct qtree_mem_dqinfo *qinfo = info->dqi_priv;ssize_t size;spin_lock (&dq_data_lock);
@@ -112,9 +121,9 @@ static int v2_write_file_info(struct super_block *sb, int type) dinfo.dqi_igrace =cpu_to_le32 (info->dqi_igrace); dinfo.dqi_flags =cpu_to_le32 (info->dqi_flags &DQF_MASK );spin_unlock (&dq_data_lock); - dinfo.dqi_blocks =cpu_to_le32 (info->u.v2_i.i.dqi_blocks); - dinfo.dqi_free_blk =cpu_to_le32 (info->u.v2_i.i.dqi_free_blk); - dinfo.dqi_free_entry =cpu_to_le32 (info->u.v2_i.i.dqi_free_entry); + dinfo.dqi_blocks =cpu_to_le32 (qinfo->dqi_blocks); + dinfo.dqi_free_blk =cpu_to_le32 (qinfo->dqi_free_blk); + dinfo.dqi_free_entry =cpu_to_le32 (qinfo->dqi_free_entry); size = sb->s_op->quota_write (sb, type, (char *)&dinfo,sizeof (struct v2_disk_dqinfo ),V2_DQINFOOFF );if (size !=sizeof (struct v2_disk_dqinfo )) {
@@ -150,7 +159,7 @@ static void v2_mem2diskdqb(void *dp, struct dquot *dquot)struct v2_disk_dqblk *d = dp;struct mem_dqblk *m = &dquot->dq_dqb;struct qtree_mem_dqinfo *info = - &sb_dqinfo (dquot->dq_sb, dquot->dq_type)->u.v2_i.i; +sb_dqinfo (dquot->dq_sb, dquot->dq_type)->dqi_priv; d->dqb_ihardlimit =cpu_to_le32 (m->dqb_ihardlimit); d->dqb_isoftlimit =cpu_to_le32 (m->dqb_isoftlimit);
@@ -169,7 +178,7 @@ static int v2_is_id(void *dp, struct dquot *dquot) {struct v2_disk_dqblk *d = dp;struct qtree_mem_dqinfo *info = - &sb_dqinfo (dquot->dq_sb, dquot->dq_type)->u.v2_i.i; +sb_dqinfo (dquot->dq_sb, dquot->dq_type)->dqi_priv;if (qtree_entry_unused (info, dp))return 0 ;
@@ -178,24 +187,30 @@ static int v2_is_id(void *dp, struct dquot *dquot)static int v2_read_dquot (struct dquot *dquot) { -return qtree_read_dquot (&sb_dqinfo (dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot); +return qtree_read_dquot (sb_dqinfo (dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot); }static int v2_write_dquot (struct dquot *dquot) { -return qtree_write_dquot (&sb_dqinfo (dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot); +return qtree_write_dquot (sb_dqinfo (dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot); }static int v2_release_dquot (struct dquot *dquot) { -return qtree_release_dquot (&sb_dqinfo (dquot->dq_sb, dquot->dq_type)->u.v2_i.i, dquot); +return qtree_release_dquot (sb_dqinfo (dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot); +} + +static int v2_free_file_info (struct super_block *sb,int type) +{ +kfree (sb_dqinfo (sb, type)->dqi_priv); +return 0 ; }static struct quota_format_ops v2_format_ops = { .check_quota_file = v2_check_quota_file, .read_file_info = v2_read_file_info, .write_file_info = v2_write_file_info, - .free_file_info =NULL , + .free_file_info = v2_free_file_info, .read_dqblk = v2_read_dquot, .commit_dqblk = v2_write_dquot, .release_dqblk = v2_release_dquot,
include/linux/dqblk_v1.h +0 -4
@@ -17,8 +17,4 @@#define V1_DEL_ALLOC 0#define V1_DEL_REWRITE 2 -/* Special information about quotafile */ -struct v1_mem_dqinfo { -}; -#endif /* _LINUX_DQBLK_V1_H */
include/linux/dqblk_v2.h +0 -4
@@ -16,8 +16,4 @@#define V2_DEL_ALLOC QTREE_DEL_ALLOC#define V2_DEL_REWRITE QTREE_DEL_REWRITE -struct v2_mem_dqinfo { -struct qtree_mem_dqinfo i; -}; -#endif /* _LINUX_DQBLK_V2_H */
include/linux/quota.h +1 -4
@@ -210,10 +210,7 @@ struct mem_dqinfo {unsigned int dqi_igrace;qsize_t dqi_maxblimit;qsize_t dqi_maxilimit; -union { -struct v1_mem_dqinfo v1_i; -struct v2_mem_dqinfo v2_i; - } u; +void *dqi_priv; };struct super_block ;
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 14/29 quota: Allow negative usage of space and inodes
In reply to Jan KaraFor clustered filesystems, it can happen that space / inode usage goes negative temporarily (because some node is allocating another node is freeing and they are not completely in sync). So let quota code allow this and change qsize_t so a signed type so that we don't underflow the variables.
--- fs/dquot.c | 6 ++++-- include/linux/quota.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-)
fs/dquot.c +4 -2
@@ -845,7 +845,8 @@ static inline void dquot_incr_space(struct dquot *dquot, qsize_t number)static inline void dquot_decr_inodes (struct dquot *dquot,qsize_t number) { -if (dquot->dq_dqb.dqb_curinodes > number) +if (sb_dqopt (dquot->dq_sb)->flags &DQUOT_NEGATIVE_USAGE || + dquot->dq_dqb.dqb_curinodes >= number) dquot->dq_dqb.dqb_curinodes -= number;else dquot->dq_dqb.dqb_curinodes =0 ;
@@ -856,7 +857,8 @@ static inline void dquot_decr_inodes(struct dquot *dquot, qsize_t number)static inline void dquot_decr_space (struct dquot *dquot,qsize_t number) { -if (dquot->dq_dqb.dqb_curspace > number) +if (sb_dqopt (dquot->dq_sb)->flags &DQUOT_NEGATIVE_USAGE || + dquot->dq_dqb.dqb_curspace >= number) dquot->dq_dqb.dqb_curspace -= number;else dquot->dq_dqb.dqb_curspace =0 ;
include/linux/quota.h +2 -1
@@ -170,7 +170,7 @@ enum {#include <asm/atomic.h> typedef __kernel_uid32_t qid_t ;/* Type in which we store ids in memory */ -typedef __u64 qsize_t ;/* Type in which we store sizes */ +typedef __s64 qsize_t ;/* Type in which we store sizes */ extern spinlock_t dq_data_lock;
@@ -338,6 +338,7 @@ enum {* responsible for setting * S_NOQUOTA, S_NOATIME flags */ +#define DQUOT_NEGATIVE_USAGE (1 << 7)/* Allow negative quota usage */ static inline unsigned int dquot_state_flag (unsigned int flags,int type) {
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 15/29 quota: Keep which entries were set by SETQUOTA quotactl
In reply to Jan KaraQuota in a clustered environment needs to synchronize quota information among cluster nodes. This means we have to occasionally update some information in dquot from disk / network. On the other hand we have to be careful not to overwrite changes administrator did via SETQUOTA. So indicate in dquot->dq_flags which entries have been set by SETQUOTA and quota format can clear these flags when it properly propagated the changes.
--- fs/dquot.c | 12 ++++++++++-- include/linux/quota.h | 26 ++++++++++++++++++++------ 2 files changed, 30 insertions(+), 8 deletions(-)
fs/dquot.c +10 -2
@@ -2006,25 +2006,33 @@ static int do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)if (di->dqb_valid &QIF_SPACE ) { dm->dqb_curspace = di->dqb_curspace; check_blim =1 ; +__set_bit (DQ_LASTSET_B +QIF_SPACE_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_BLIMITS ) { dm->dqb_bsoftlimit =qbtos (di->dqb_bsoftlimit); dm->dqb_bhardlimit =qbtos (di->dqb_bhardlimit); check_blim =1 ; +__set_bit (DQ_LASTSET_B +QIF_BLIMITS_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_INODES ) { dm->dqb_curinodes = di->dqb_curinodes; check_ilim =1 ; +__set_bit (DQ_LASTSET_B +QIF_INODES_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_ILIMITS ) { dm->dqb_isoftlimit = di->dqb_isoftlimit; dm->dqb_ihardlimit = di->dqb_ihardlimit; check_ilim =1 ; +__set_bit (DQ_LASTSET_B +QIF_ILIMITS_B , &dquot->dq_flags); } -if (di->dqb_valid &QIF_BTIME ) +if (di->dqb_valid &QIF_BTIME ) { dm->dqb_btime = di->dqb_btime; -if (di->dqb_valid &QIF_ITIME ) +__set_bit (DQ_LASTSET_B +QIF_BTIME_B , &dquot->dq_flags); + } +if (di->dqb_valid &QIF_ITIME ) { dm->dqb_itime = di->dqb_itime; +__set_bit (DQ_LASTSET_B +QIF_ITIME_B , &dquot->dq_flags); + }if (check_blim) {if (!dm->dqb_bsoftlimit || dm->dqb_curspace < dm->dqb_bsoftlimit) {
include/linux/quota.h +20 -6
@@ -82,12 +82,21 @@* Quota structure used for communication with userspace via quotactl * Following flags are used to specify which fields are valid */ -#define QIF_BLIMITS 1 -#define QIF_SPACE 2 -#define QIF_ILIMITS 4 -#define QIF_INODES 8 -#define QIF_BTIME 16 -#define QIF_ITIME 32 +enum { +QIF_BLIMITS_B =0 , +QIF_SPACE_B , +QIF_ILIMITS_B , +QIF_INODES_B , +QIF_BTIME_B , +QIF_ITIME_B , +}; + +#define QIF_BLIMITS (1 << QIF_BLIMITS_B) +#define QIF_SPACE (1 << QIF_SPACE_B) +#define QIF_ILIMITS (1 << QIF_ILIMITS_B) +#define QIF_INODES (1 << QIF_INODES_B) +#define QIF_BTIME (1 << QIF_BTIME_B) +#define QIF_ITIME (1 << QIF_ITIME_B)#define QIF_LIMITS (QIF_BLIMITS | QIF_ILIMITS)#define QIF_USAGE (QIF_SPACE | QIF_INODES)#define QIF_TIMES (QIF_BTIME | QIF_ITIME)
@@ -244,6 +253,11 @@ extern struct dqstats dqstats;#define DQ_FAKE_B 3/* no limits only usage */ #define DQ_READ_B 4/* dquot was read into memory */ #define DQ_ACTIVE_B 5/* dquot is active (dquot_release not called) */ +#define DQ_LASTSET_B 6/* Following 6 bits (see QIF_) are reserved\ +* for the mask of entries set via SETQUOTA\ +* quotactl. They are set under dq_data_lock\ +* and the quota format handling dquot can\ +* clear them when it sees fit. */ struct dquot {struct hlist_node dq_hash;/* Hash list in memory */
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 16/29 quota: Add helpers to allow ocfs2 specific quota initialization, freeing and recovery
In reply to Jan KaraOCFS2 needs to peek whether quota structure is already in memory so that it can avoid expensive cluster locking in that case. Similarly when freeing dquots, it checks whether it is the last quota structure user or not. Finally, it needs to get reference to dquot structure for specified id and quota type when recovering quota file after crash.
--- fs/dquot.c | 38 ++++++++++++++++++++++++++++++++------ include/linux/quotaops.h | 4 ++++ 2 files changed, 36 insertions(+), 6 deletions(-)
fs/dquot.c +32 -6
@@ -213,8 +213,6 @@ static struct hlist_head *dquot_hash;struct dqstats dqstats; -static void dqput (struct dquot *dquot); -static inline unsigned int hashfn (const struct super_block *sb,unsigned int id,int type) {
@@ -568,7 +566,7 @@ static struct shrinker dqcache_shrinker = {* NOTE: If you change this function please check whether dqput_blocks() works right... * MUST be called with either dqptr_sem or dqonoff_mutex held */ -static void dqput (struct dquot *dquot) +void dqput (struct dquot *dquot) {int ret;
@@ -660,10 +658,28 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type) }/* +* Check whether dquot is in memory. +* MUST be called with either dqptr_sem or dqonoff_mutex held +*/ +int dquot_is_cached (struct super_block *sb,unsigned int id,int type) +{ +unsigned int hashent =hashfn (sb, id, type); +int ret =0 ; + +if (!sb_has_quota_active (sb, type)) +return 0 ; +spin_lock (&dq_list_lock); +if (find_dquot (hashent, sb, id, type) !=NODQUOT ) + ret =1 ; +spin_unlock (&dq_list_lock); +return ret; +} + +/* * Get reference to dquot * MUST be called with either dqptr_sem or dqonoff_mutex held */ -static struct dquot *dqget (struct super_block *sb,unsigned int id,int type) +struct dquot *dqget (struct super_block *sb,unsigned int id,int type) {unsigned int hashent =hashfn (sb, id, type);struct dquot *dquot, *empty =NODQUOT ;
@@ -1182,17 +1198,23 @@ out_err:* Release all quotas referenced by inode * Transaction must be started at an entry */ -int dquot_drop (struct inode *inode) +int dquot_drop_locked (struct inode *inode) {int cnt; -down_write (&sb_dqopt (inode->i_sb)->dqptr_sem);for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) {if (inode->i_dquot[cnt] !=NODQUOT ) {dqput (inode->i_dquot[cnt]); inode->i_dquot[cnt] =NODQUOT ; } } +return 0 ; +} + +int dquot_drop (struct inode *inode) +{ +down_write (&sb_dqopt (inode->i_sb)->dqptr_sem); +dquot_drop_locked (inode);up_write (&sb_dqopt (inode->i_sb)->dqptr_sem);return 0 ; }
@@ -2304,7 +2326,11 @@ EXPORT_SYMBOL(dquot_release);EXPORT_SYMBOL (dquot_mark_dquot_dirty);EXPORT_SYMBOL (dquot_initialize);EXPORT_SYMBOL (dquot_drop); +EXPORT_SYMBOL (dquot_drop_locked);EXPORT_SYMBOL (vfs_dq_drop); +EXPORT_SYMBOL (dqget); +EXPORT_SYMBOL (dqput); +EXPORT_SYMBOL (dquot_is_cached);EXPORT_SYMBOL (dquot_alloc_space);EXPORT_SYMBOL (dquot_alloc_inode);EXPORT_SYMBOL (dquot_free_space);
include/linux/quotaops.h +4 -0
@@ -27,6 +27,10 @@ void sync_dquots(struct super_block *sb, int type);int dquot_initialize (struct inode *inode,int type);int dquot_drop (struct inode *inode); +int dquot_drop_locked (struct inode *inode); +struct dquot *dqget (struct super_block *sb,unsigned int id,int type); +void dqput (struct dquot *dquot); +int dquot_is_cached (struct super_block *sb,unsigned int id,int type);int dquot_alloc_space (struct inode *inode,qsize_t number,int prealloc);int dquot_alloc_inode (const struct inode *inode,qsize_t number);
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 17/29 quota: Implement function for scanning active dquots
In reply to Jan KaraOCFS2 needs to scan all active dquots once in a while and sync quota information among cluster nodes. Provide a helper function for it so that it does not have to reimplement internally a list which VFS already has. Moreover this function is probably going to be useful for other clustered filesystems if they decide to use VFS quotas.
--- fs/dquot.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/quotaops.h | 3 +++ 2 files changed, 39 insertions(+), 0 deletions(-)
fs/dquot.c +36 -0
@@ -476,6 +476,41 @@ restart:spin_unlock (&dq_list_lock); } +/* Call callback for every active dquot on given filesystem */ +int dquot_scan_active (struct super_block *sb, +int (*fn)(struct dquot *dquot,unsigned long priv), +unsigned long priv) +{ +struct dquot *dquot, *old_dquot =NULL ; +int ret =0 ; + +mutex_lock (&sb_dqopt (sb)->dqonoff_mutex); +spin_lock (&dq_list_lock); +list_for_each_entry (dquot, &inuse_list, dq_inuse) { +if (!test_bit (DQ_ACTIVE_B , &dquot->dq_flags)) +continue ; +if (dquot->dq_sb != sb) +continue ; +/* Now we have active dquot so we can just increase use count */ +atomic_inc (&dquot->dq_count); + dqstats.lookups++; +spin_unlock (&dq_list_lock); +dqput (old_dquot); + old_dquot = dquot; + ret =fn (dquot, priv); +if (ret <0 ) + goto out; +spin_lock (&dq_list_lock); +/* We are safe to continue now because our dquot could not +* be moved out of the inuse list while we hold the reference */ + } +spin_unlock (&dq_list_lock); +out: +dqput (old_dquot); +mutex_unlock (&sb_dqopt (sb)->dqonoff_mutex); +return ret; +} +int vfs_quota_sync (struct super_block *sb,int type) {struct list_head *dirty;
@@ -2314,6 +2349,7 @@ EXPORT_SYMBOL(vfs_quota_on_path);EXPORT_SYMBOL (vfs_quota_on_mount);EXPORT_SYMBOL (vfs_quota_disable);EXPORT_SYMBOL (vfs_quota_off); +EXPORT_SYMBOL (dquot_scan_active);EXPORT_SYMBOL (vfs_quota_sync);EXPORT_SYMBOL (vfs_get_dqinfo);EXPORT_SYMBOL (vfs_set_dqinfo);
include/linux/quotaops.h +3 -0
@@ -31,6 +31,9 @@ int dquot_drop_locked(struct inode *inode);struct dquot *dqget (struct super_block *sb,unsigned int id,int type);void dqput (struct dquot *dquot);int dquot_is_cached (struct super_block *sb,unsigned int id,int type); +int dquot_scan_active (struct super_block *sb, +int (*fn)(struct dquot *dquot,unsigned long priv), +unsigned long priv);int dquot_alloc_space (struct inode *inode,qsize_t number,int prealloc);int dquot_alloc_inode (const struct inode *inode,qsize_t number);
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 18/29 mm: Export pdflush_operation()
In reply to Jan KaraOCSF2 will need to queue up work for periodic syncing of quotas among nodes in the cluster. pdflush() is good thread for this so export it's controlling function so that OCFS2 can use it.
--- mm/pdflush.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
mm/pdflush.c +1 -0
@@ -223,6 +223,7 @@ int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0)return ret; } +EXPORT_SYMBOL (pdflush_operation);static void start_one_pdflush_thread (void ) {
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 19/29 ocfs2: Fix check of return value of ocfs2_start_trans()
In reply to Jan KaraOn failure, ocfs2_start_trans() returns values like ERR_PTR(-ENOMEM). Thus checks for !handle are wrong. Fix them to use IS_ERR().
--- fs/ocfs2/file.c | 20 ++++++++++---------- 1 files changed, 10 insertions(+), 10 deletions(-)
fs/ocfs2/file.c +10 -10
@@ -247,8 +247,8 @@ int ocfs2_update_inode_atime(struct inode *inode,mlog_entry_void (); handle =ocfs2_start_trans (osb,OCFS2_INODE_UPDATE_CREDITS ); -if (handle ==NULL ) { - ret = -ENOMEM ; +if (IS_ERR (handle)) { + ret =PTR_ERR (handle);mlog_errno (ret); goto out; }
@@ -312,8 +312,8 @@ static int ocfs2_simple_size_update(struct inode *inode,handle_t *handle =NULL ; handle =ocfs2_start_trans (osb,OCFS2_INODE_UPDATE_CREDITS ); -if (handle ==NULL ) { - ret = -ENOMEM ; +if (IS_ERR (handle)) { + ret =PTR_ERR (handle);mlog_errno (ret); goto out; }
@@ -1056,8 +1056,8 @@ static int __ocfs2_write_remove_suid(struct inode *inode, (unsigned long long)OCFS2_I(inode)->ip_blkno, inode->i_mode); handle =ocfs2_start_trans (osb,OCFS2_INODE_UPDATE_CREDITS ); -if (handle ==NULL ) { - ret = -ENOMEM ; +if (IS_ERR (handle)) { + ret =PTR_ERR (handle);mlog_errno (ret); goto out; }
@@ -1260,8 +1260,8 @@ static int __ocfs2_remove_inode_range(struct inode *inode, } handle =ocfs2_start_trans (osb,OCFS2_REMOVE_EXTENT_CREDITS ); -if (handle ==NULL ) { - ret = -ENOMEM ; +if (IS_ERR (handle)) { + ret =PTR_ERR (handle);mlog_errno (ret); goto out; }
@@ -1353,8 +1353,8 @@ static int ocfs2_zero_partial_clusters(struct inode *inode, goto out; handle =ocfs2_start_trans (osb,OCFS2_INODE_UPDATE_CREDITS ); -if (handle ==NULL ) { - ret = -ENOMEM ; +if (IS_ERR (handle)) { + ret =PTR_ERR (handle);mlog_errno (ret); goto out; }
Signature · 1 line
1.5.2.4
PATCH 19/29 ocfs2: Fix check of return value of ocfs2_start_trans()
Quoted 2 lines · Jan Kara
On failure,
ocfs2_start_trans()returns values likeERR_PTR(-ENOMEM). Thus checks for !handle are wrong. Fix them to useIS_ERR().
Btw, this is in the 'fixes' branch of ocfs2.git. now.
--Mark
--
Mark Fasheh
Jan Kara
reviewerPATCH 20/29 ocfs2: Support nested transactions
In reply to Jan KaraOCFS2 can easily support nested transactions. We just have to take care and not spoil statistics acquire semaphore unnecessarily.
--- fs/ocfs2/journal.c | 14 +++++++------- 1 files changed, 7 insertions(+), 7 deletions(-)
fs/ocfs2/journal.c +7 -7
@@ -256,11 +256,9 @@ handle_t *ocfs2_start_trans(struct ocfs2_super *osb, int max_buffs)BUG_ON (osb->journal->j_state ==OCFS2_JOURNAL_FREE );BUG_ON (max_buffs <=0 ); -/* JBD might support this, but our journalling code doesn't yet. */ -if (journal_current_handle ()) { -mlog (ML_ERROR ,"Recursive transaction attempted!\n" ); -BUG (); - } +/* Nested transaction? Just return the handle... */ +if (journal_current_handle ()) +return jbd2_journal_start (journal, max_buffs);down_read (&osb->journal->j_trans_barrier);
@@ -285,16 +283,18 @@ handle_t *ocfs2_start_trans(struct ocfs2_super *osb, int max_buffs)int ocfs2_commit_trans (struct ocfs2_super *osb,handle_t *handle) { -int ret; +int ret, nested;struct ocfs2_journal *journal = osb->journal;BUG_ON (!handle); + nested = handle->h_ref >1 ; ret =jbd2_journal_stop (handle);if (ret <0 )mlog_errno (ret); -up_read (&journal->j_trans_barrier); +if (!nested) +up_read (&journal->j_trans_barrier);return ret; }
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 21/29 ocfs2: Fix checking of return value of new_inode()
In reply to Jan Karanew_inode() does not return ERR_PTR() but NULL in case of failure. Correct checking of the return value.
--- fs/ocfs2/namei.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
fs/ocfs2/namei.c +2 -2
@@ -378,8 +378,8 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb, } inode =new_inode (dir->i_sb); -if (IS_ERR (inode)) { - status =PTR_ERR (inode); +if (!inode) { + status = -ENOMEM ;mlog (ML_ERROR ,"new_inode failed!\n" ); goto leave; }
Signature · 1 line
1.5.2.4
PATCH 21/29 ocfs2: Fix checking of return value of new_inode()
Quoted 2 lines · Jan Kara
new_inode()does not returnERR_PTR()but NULL in case of failure. Correct checking of the return value.
This one also I'm carrying now.
--Mark
--
Mark Fasheh
Jan Kara
reviewerPATCH 22/29 ocfs2: Let inode be really deleted when ocfs2_mknod_locked() fails
In reply to Jan KaraWe forgot to set i_nlink to 0 when returning due to error from ocfs2_mknod_locked() and thus inode was not properly released via ocfs2_delete_inode() (e.g. claimed space was not released). Fix it.
--- fs/ocfs2/namei.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-)
fs/ocfs2/namei.c +3 -1
@@ -491,8 +491,10 @@ leave:brelse (*new_fe_bh); *new_fe_bh =NULL ; } -if (inode) +if (inode) { +clear_nlink (inode);iput (inode); + } }mlog_exit (status);
Signature · 1 line
1.5.2.4
PATCH 22/29 ocfs2: Let inode be really deleted when ocfs2_mknod_locked() fails
Quoted 3 lines · Jan Kara
We forgot to set
i_nlinkto 0 when returning due to error fromocfs2_mknod_locked()and thus inode was not properly released viaocfs2_delete_inode()(e.g. claimed space was not released). Fix it.
By the way, does ocfs2_delete_inode() complain when this can't be found in the orphan dir?
--Mark
--
Mark Fasheh
Jan Kara
reviewerPATCH 22/29 ocfs2: Let inode be really deleted when ocfs2_mknod_locked() fails
Quoted 7 lines · Mark Fasheh
Quoted 3 lines · Jan Kara
We forgot to set
i_nlinkto 0 when returning due to error fromocfs2_mknod_locked()and thus inode was not properly released viaocfs2_delete_inode()(e.g. claimed space was not released). Fix it.By the way, does
ocfs2_delete_inode()complain when this can't be found in the orphan dir?
If the allocation fails due to quota, inode is not really initialized so
it does not complain. Previously, when I did quota checks after allocation of space, I saw some complaints about inode not being in orphan dir - yes. But they are in fact false since we remove the inode in the same transaction is which we allocated it so it cannot happen that it would be left allocated in case of crash. So yeah, it might be nice to actually tweak the code so that we can avoid the warning in case of error cleanup.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Jan Kara
reviewerPATCH 23/29 ocfs2: Assign feature bits and system inodes to quota feature and quota files
In reply to Jan Kara--- fs/ocfs2/inode.c | 2 ++ fs/ocfs2/ocfs2_fs.h | 23 ++++++++++++++++++++--- fs/ocfs2/super.c | 17 +++++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-)
fs/ocfs2/inode.c +2 -0
@@ -292,6 +292,8 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,mlog (0 ,"local alloc inode: i_ino=%lu\n" , inode->i_ino); }else if (fe->i_flags &cpu_to_le32 (OCFS2_BITMAP_FL )) {OCFS2_I (inode)->ip_flags |=OCFS2_INODE_BITMAP ; + }else if (fe->i_flags &cpu_to_le32 (OCFS2_QUOTA_FL )) { + inode->i_flags |=S_NOQUOTA ; }else if (fe->i_flags &cpu_to_le32 (OCFS2_SUPER_BLOCK_FL )) {mlog (0 ,"superblock inode: i_ino=%lu\n" , inode->i_ino);/* we can't actually hit this as read_inode can't
fs/ocfs2/ocfs2_fs.h +20 -3
@@ -93,7 +93,9 @@ | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \ | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \ | OCFS2_FEATURE_INCOMPAT_XATTR) -#define OCFS2_FEATURE_RO_COMPAT_SUPP OCFS2_FEATURE_RO_COMPAT_UNWRITTEN +#define OCFS2_FEATURE_RO_COMPAT_SUPP (OCFS2_FEATURE_RO_COMPAT_UNWRITTEN \ + | OCFS2_FEATURE_RO_COMPAT_USRQUOTA \ + | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)/* * Heartbeat-only devices are missing journals and other files. The
@@ -157,6 +159,12 @@*/ #define OCFS2_FEATURE_RO_COMPAT_UNWRITTEN 0x0001 +/* +* Maintain quota information for this filesystem +*/ +#define OCFS2_FEATURE_RO_COMPAT_USRQUOTA 0x0002 +#define OCFS2_FEATURE_RO_COMPAT_GRPQUOTA 0x0004 +/* The byte offset of the first backup block will be 1G. * The following will be 4G, 16G, 64G, 256G and 1T. */
@@ -186,6 +194,7 @@#define OCFS2_HEARTBEAT_FL (0x00000200)/* Heartbeat area */ #define OCFS2_CHAIN_FL (0x00000400)/* Chain allocator */ #define OCFS2_DEALLOC_FL (0x00000800)/* Truncate log */ +#define OCFS2_QUOTA_FL (0x00001000)/* Quota file */ /* * Flags on ocfs2_dinode.i_dyn_features
@@ -323,13 +332,17 @@ enum {#define OCFS2_FIRST_ONLINE_SYSTEM_INODE SLOT_MAP_SYSTEM_INODEHEARTBEAT_SYSTEM_INODE ,GLOBAL_BITMAP_SYSTEM_INODE , -#define OCFS2_LAST_GLOBAL_SYSTEM_INODE GLOBAL_BITMAP_SYSTEM_INODE +USER_QUOTA_SYSTEM_INODE , +GROUP_QUOTA_SYSTEM_INODE , +#define OCFS2_LAST_GLOBAL_SYSTEM_INODE GROUP_QUOTA_SYSTEM_INODEORPHAN_DIR_SYSTEM_INODE ,EXTENT_ALLOC_SYSTEM_INODE ,INODE_ALLOC_SYSTEM_INODE ,JOURNAL_SYSTEM_INODE ,LOCAL_ALLOC_SYSTEM_INODE ,TRUNCATE_LOG_SYSTEM_INODE , +LOCAL_USER_QUOTA_SYSTEM_INODE , +LOCAL_GROUP_QUOTA_SYSTEM_INODE ,NUM_SYSTEM_INODES };
@@ -343,6 +356,8 @@ static struct ocfs2_system_inode_info ocfs2_system_inodes[NUM_SYSTEM_INODES] = { [SLOT_MAP_SYSTEM_INODE] = { "slot_map", 0, S_IFREG | 0644 }, [HEARTBEAT_SYSTEM_INODE] = { "heartbeat", OCFS2_HEARTBEAT_FL, S_IFREG | 0644 }, [GLOBAL_BITMAP_SYSTEM_INODE] = { "global_bitmap", 0, S_IFREG | 0644 }, + [USER_QUOTA_SYSTEM_INODE] = { "aquota.user", OCFS2_QUOTA_FL, S_IFREG | 0644 }, + [GROUP_QUOTA_SYSTEM_INODE] = { "aquota.group", OCFS2_QUOTA_FL, S_IFREG | 0644 }, /* Slot-specific system inodes (one copy per slot) */ [ORPHAN_DIR_SYSTEM_INODE] = { "orphan_dir:%04d", 0, S_IFDIR | 0755 },
@@ -350,7 +365,9 @@ static struct ocfs2_system_inode_info ocfs2_system_inodes[NUM_SYSTEM_INODES] = { [INODE_ALLOC_SYSTEM_INODE] = { "inode_alloc:%04d", OCFS2_BITMAP_FL | OCFS2_CHAIN_FL, S_IFREG | 0644 }, [JOURNAL_SYSTEM_INODE] = { "journal:%04d", OCFS2_JOURNAL_FL, S_IFREG | 0644 }, [LOCAL_ALLOC_SYSTEM_INODE] = { "local_alloc:%04d", OCFS2_BITMAP_FL | OCFS2_LOCAL_ALLOC_FL, S_IFREG | 0644 }, - [TRUNCATE_LOG_SYSTEM_INODE ] = {"truncate_log:%04d" ,OCFS2_DEALLOC_FL ,S_IFREG |0644 } + [TRUNCATE_LOG_SYSTEM_INODE] = { "truncate_log:%04d", OCFS2_DEALLOC_FL, S_IFREG | 0644 }, + [LOCAL_USER_QUOTA_SYSTEM_INODE] = { "aquota%04d.user", OCFS2_QUOTA_FL, S_IFREG | 0644 }, + [LOCAL_GROUP_QUOTA_SYSTEM_INODE] = { "aquota%04d.group", OCFS2_QUOTA_FL, S_IFREG | 0644 }, };/* Parameter passed from mount.ocfs2 to module */
fs/ocfs2/super.c +17 -0
@@ -221,6 +221,19 @@ static int ocfs2_sync_fs(struct super_block *sb, int wait)return 0 ; } +static int ocfs2_need_system_inode (struct ocfs2_super *osb,int ino) +{ +if (!OCFS2_HAS_RO_COMPAT_FEATURE (osb->sb,OCFS2_FEATURE_RO_COMPAT_USRQUOTA ) + && (ino ==USER_QUOTA_SYSTEM_INODE + || ino ==LOCAL_USER_QUOTA_SYSTEM_INODE )) +return 0 ; +if (!OCFS2_HAS_RO_COMPAT_FEATURE (osb->sb,OCFS2_FEATURE_RO_COMPAT_GRPQUOTA ) + && (ino ==GROUP_QUOTA_SYSTEM_INODE + || ino ==LOCAL_GROUP_QUOTA_SYSTEM_INODE )) +return 0 ; +return 1 ; +} +static int ocfs2_init_global_system_inodes (struct ocfs2_super *osb) {struct inode *new =NULL ;
@@ -247,6 +260,8 @@ static int ocfs2_init_global_system_inodes(struct ocfs2_super *osb)for (i =OCFS2_FIRST_ONLINE_SYSTEM_INODE ; i <=OCFS2_LAST_GLOBAL_SYSTEM_INODE ; i++) { +if (!ocfs2_need_system_inode (osb, i)) +continue ; new =ocfs2_get_system_file_inode (osb, i, osb->slot_num);if (!new) {ocfs2_release_system_inodes (osb);
@@ -277,6 +292,8 @@ static int ocfs2_init_local_system_inodes(struct ocfs2_super *osb)for (i =OCFS2_LAST_GLOBAL_SYSTEM_INODE +1 ; i <NUM_SYSTEM_INODES ; i++) { +if (!ocfs2_need_system_inode (osb, i)) +continue ; new =ocfs2_get_system_file_inode (osb, i, osb->slot_num);if (!new) {ocfs2_release_system_inodes (osb);
Signature · 1 line
1.5.2.4
PATCH 23/29 ocfs2: Assign feature bits and system inodes to quota feature and quota files
fs/ocfs2/ocfs2_fs.h line 98
Quoted 12 lines · Jan Kara
fs/ocfs2/ocfs2_fs.h +3
-1@@ -93,7 +93,9 @@ | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \ | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \ | OCFS2_FEATURE_INCOMPAT_XATTR) -#define OCFS2_FEATURE_RO_COMPAT_SUPP OCFS2_FEATURE_RO_COMPAT_UNWRITTEN +#define OCFS2_FEATURE_RO_COMPAT_SUPP (OCFS2_FEATURE_RO_COMPAT_UNWRITTEN \ + | OCFS2_FEATURE_RO_COMPAT_USRQUOTA \ + | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)
I know you're just in the progress of working, so this isn't an
immediate thing, but I wanted to put this down in the discussion.
When we land the quota work, the feature bits have to be
absolutely last. It's not about whether quota works (eg, the mounting code later in the series). It's about the cluster's handling of quotas. After this patch, a node will happily mount the filesystem while ignoring quotas. Another node, one with the full series, will actually use and write out quotas. This leads to inconsistency. By having the very last patch add quotas to the supported features, we ensure this doesn't happen.
Joel
Signature · 8 lines
"Up and down that road in our worn out shoes, Talking bout good things and singing the blues." Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127
Jan Kara
reviewerPATCH 23/29 ocfs2: Assign feature bits and system inodes to quota feature and quota files
Quoted 16 lines · Joel Becker
fs/ocfs2/ocfs2_fs.h line 98
Quoted 12 lines · Jan Kara
fs/ocfs2/ocfs2_fs.h +3
-1@@ -93,7 +93,9 @@ | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \ | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \ | OCFS2_FEATURE_INCOMPAT_XATTR) -#define OCFS2_FEATURE_RO_COMPAT_SUPP OCFS2_FEATURE_RO_COMPAT_UNWRITTEN +#define OCFS2_FEATURE_RO_COMPAT_SUPP (OCFS2_FEATURE_RO_COMPAT_UNWRITTEN \ + | OCFS2_FEATURE_RO_COMPAT_USRQUOTA \ + | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)I know you're just in the progress of working, so this isn't an
immediate thing, but I wanted to put this down in the discussion.
Quoted 8 lines
When we land the quota work, the feature bits have to be
absolutely last. It's not about whether quota works (eg, the mounting code later in the series). It's about the cluster's handling of quotas. After this patch, a node will happily mount the filesystem while ignoring quotas. Another node, one with the full series, will actually use and write out quotas. This leads to inconsistency. By having the very last patch add quotas to the supported features, we ensure this doesn't happen.
Ah, OK. I didn't realize this. Thanks for pointing this out. I'll move
this patch to the end and merge it with the "Enable quotas on mount" patch.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Jan Kara
reviewerPATCH 24/29 ocfs2: Mark system files as not subject to quota accounting
In reply to Jan KaraMark system files as not subject to quota accounting. This prevents possible recursions into quota code and thus deadlocks.
--- fs/ocfs2/inode.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-)
fs/ocfs2/inode.c +3 -1
@@ -284,8 +284,10 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, inode->i_nlink =le16_to_cpu (fe->i_links_count); -if (fe->i_flags &cpu_to_le32 (OCFS2_SYSTEM_FL )) +if (fe->i_flags &cpu_to_le32 (OCFS2_SYSTEM_FL )) {OCFS2_I (inode)->ip_flags |=OCFS2_INODE_SYSTEM_FILE ; + inode->i_flags |=S_NOQUOTA ; + }if (fe->i_flags &cpu_to_le32 (OCFS2_LOCAL_ALLOC_FL )) {OCFS2_I (inode)->ip_flags |=OCFS2_INODE_BITMAP ;
Signature · 1 line
1.5.2.4
Jan Kara
reviewerPATCH 25/29 ocfs2: Implementation of local and global quota file handling
In reply to Jan KaraFor each quota type each node has local quota file. In this file it stores changes users have made to disk usage via this node. Once in a while this information is synced to global file (and thus with other nodes) so that limits enforcement at least aproximately works.
Global quota files contain all the information about usage and limits. It's mostly handled by the generic VFS code (which implements a trie of structures inside a quota file). We only have to provide functions to convert structures from on-disk format to in-memory one. We also have to provide wrappers for various quota functions starting transactions and acquiring necessary cluster locks before the actual IO is really started.
--- fs/ocfs2/Makefile | 2 + fs/ocfs2/cluster/masklog.h | 1 + fs/ocfs2/dir.c | 4 +- fs/ocfs2/dlmglue.c | 137 +++++++ fs/ocfs2/dlmglue.h | 17 + fs/ocfs2/file.c | 6 +- fs/ocfs2/file.h | 3 + fs/ocfs2/inode.h | 2 + fs/ocfs2/ocfs2_fs.h | 95 +++++ fs/ocfs2/ocfs2_lockid.h | 5 + fs/ocfs2/quota.h | 97 +++++ fs/ocfs2/quota_global.c | 863 ++++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/quota_local.c | 833 ++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/super.c | 38 ++- 14 files changed, 2096 insertions(+), 7 deletions(-) create mode 100644 fs/ocfs2/quota.h create mode 100644 fs/ocfs2/quota_global.c create mode 100644 fs/ocfs2/quota_local.c
fs/ocfs2/Makefile +2 -0
@@ -35,6 +35,8 @@ ocfs2-objs := \ sysfile.o \ uptodate.o \ ver.o \ + quota_local.o \ + quota_global.o \ xattr.o ocfs2_stackglue-objs := stackglue.o
fs/ocfs2/cluster/masklog.h +1 -0
@@ -113,6 +113,7 @@#define ML_QUORUM 0x0000000008000000ULL/* net connection quorum */ #define ML_EXPORT 0x0000000010000000ULL/* ocfs2 export operations */ #define ML_XATTR 0x0000000020000000ULL/* ocfs2 extended attributes */ +#define ML_QUOTA 0x0000000040000000ULL/* ocfs2 quota operations */ /* bits that are infrequently given and frequently matched in the high word */ #define ML_ERROR 0x0000000100000000ULL/* sent to KERN_ERR */ #define ML_NOTICE 0x0000000200000000ULL/* setn to KERN_NOTICE */
fs/ocfs2/dir.c +2 -2
@@ -82,8 +82,8 @@ static int ocfs2_do_extend_dir(struct super_block *sb,struct ocfs2_alloc_context *meta_ac, struct buffer_head **new_bh); -static struct buffer_head *ocfs2_bread (struct inode *inode, -int block,int *err,int reada) +struct buffer_head *ocfs2_bread (struct inode *inode, +int block,int *err,int reada) {struct buffer_head *bh =NULL ;int tmperr;
fs/ocfs2/dlmglue.c +137 -0
191 lines not on this page · raw message
fs/ocfs2/dlmglue.h +17 -0
@@ -49,6 +49,17 @@ struct ocfs2_meta_lvb {__be32 lvb_reserved2; }; +struct ocfs2_qinfo_lvb { +__u8 lvb_version; +__u8 lvb_reserved[3 ]; +__be32 lvb_bgrace; +__be32 lvb_igrace; +__be32 lvb_syncms; +__be32 lvb_blocks; +__be32 lvb_free_blk; +__be32 lvb_free_entry; +}; +/* ocfs2_inode_lock_full() 'arg_flags' flags */ /* don't wait on recovery. */ #define OCFS2_META_LOCK_RECOVERY (0x01)
@@ -69,6 +80,9 @@ void ocfs2_dentry_lock_res_init(struct ocfs2_dentry_lock *dl,struct ocfs2_file_private ;void ocfs2_file_lock_res_init (struct ocfs2_lock_res *lockres,struct ocfs2_file_private *fp); +struct ocfs2_mem_dqinfo ; +void ocfs2_qinfo_lock_res_init (struct ocfs2_lock_res *lockres, +struct ocfs2_mem_dqinfo *info);void ocfs2_lock_res_free (struct ocfs2_lock_res *res);int ocfs2_create_new_inode_locks (struct inode *inode);int ocfs2_drop_inode_locks (struct inode *inode);
@@ -103,6 +117,9 @@ int ocfs2_dentry_lock(struct dentry *dentry, int ex);void ocfs2_dentry_unlock (struct dentry *dentry,int ex);int ocfs2_file_lock (struct file *file,int ex,int trylock);void ocfs2_file_unlock (struct file *file); +int ocfs2_qinfo_lock (struct ocfs2_mem_dqinfo *oinfo,int ex); +void ocfs2_qinfo_unlock (struct ocfs2_mem_dqinfo *oinfo,int ex); +void ocfs2_mark_lockres_freeing (struct ocfs2_lock_res *lockres); void ocfs2_simple_drop_lockres(struct ocfs2_super *osb,
fs/ocfs2/file.c +3 -3
@@ -303,9 +303,9 @@ bail:return status; } -static int ocfs2_simple_size_update (struct inode *inode, -struct buffer_head *di_bh, -u64 new_i_size) +int ocfs2_simple_size_update (struct inode *inode, +struct buffer_head *di_bh, +u64 new_i_size) {int ret;struct ocfs2_super *osb =OCFS2_SB (inode->i_sb);
fs/ocfs2/file.h +3 -0
@@ -51,6 +51,9 @@ int ocfs2_add_inode_data(struct ocfs2_super *osb,struct ocfs2_alloc_context *data_ac, struct ocfs2_alloc_context *meta_ac, enum ocfs2_alloc_restarted *reason_ret); +int ocfs2_simple_size_update (struct inode *inode, +struct buffer_head *di_bh, +u64 new_i_size);int ocfs2_extend_no_holes (struct inode *inode,u64 new_i_size,u64 zero_to);int ocfs2_setattr (struct dentry *dentry,struct iattr *attr);
fs/ocfs2/inode.h +2 -0
@@ -142,6 +142,8 @@ int ocfs2_mark_inode_dirty(handle_t *handle, struct buffer_head *bh);int ocfs2_aio_read (struct file *file,struct kiocb *req,struct iocb *iocb);int ocfs2_aio_write (struct file *file,struct kiocb *req,struct iocb *iocb); +struct buffer_head *ocfs2_bread (struct inode *inode, +int block,int *err,int reada);void ocfs2_set_inode_flags (struct inode *inode);void ocfs2_get_inode_flags (struct ocfs2_inode_info *oi);
fs/ocfs2/ocfs2_fs.h +95 -0
101 lines not on this page · raw message
fs/ocfs2/ocfs2_lockid.h +5 -0
@@ -46,6 +46,7 @@ enum ocfs2_lock_type {OCFS2_LOCK_TYPE_DENTRY ,OCFS2_LOCK_TYPE_OPEN ,OCFS2_LOCK_TYPE_FLOCK , +OCFS2_LOCK_TYPE_QINFO , OCFS2_NUM_LOCK_TYPES };
@@ -77,6 +78,9 @@ static inline char ocfs2_lock_type_char(enum ocfs2_lock_type type)case OCFS2_LOCK_TYPE_FLOCK : c ='F' ;break ; +case OCFS2_LOCK_TYPE_QINFO : + c ='Q' ; +break ;default : c ='\0' ; }
@@ -95,6 +99,7 @@ static char *ocfs2_lock_type_strings[] = { [OCFS2_LOCK_TYPE_DENTRY] = "Dentry", [OCFS2_LOCK_TYPE_OPEN] = "Open", [OCFS2_LOCK_TYPE_FLOCK] = "Flock", + [OCFS2_LOCK_TYPE_QINFO] = "Quota", };static inline const char *ocfs2_lock_type_string (enum ocfs2_lock_type type)
fs/ocfs2/quota.h new file +97 -0
97 lines not on this page · raw message
fs/ocfs2/quota_global.c new file +863 -0
863 lines not on this page · raw message
fs/ocfs2/quota_local.c new file +833 -0
833 lines not on this page · raw message
fs/ocfs2/super.c +36 -2
@@ -65,10 +65,13 @@#include "uptodate.h" #include "ver.h" #include "xattr.h" +#include "quota.h" #include "buffer_head_io.h" static struct kmem_cache *ocfs2_inode_cachep =NULL ; +struct kmem_cache *ocfs2_dquot_cachep; +struct kmem_cache *ocfs2_qf_chunk_cachep;/* OCFS2 needs to schedule several differnt types of work which * require cluster locking, disk I/O, recovery waits, etc. Since these
@@ -137,6 +140,8 @@ static const struct super_operations ocfs2_sops = { .put_super = ocfs2_put_super, .remount_fs = ocfs2_remount, .show_options = ocfs2_show_options, + .quota_read = ocfs2_quota_read, + .quota_write = ocfs2_quota_write, };enum {
@@ -1071,6 +1076,7 @@ static int __init ocfs2_init(void)ocfs2_set_locking_protocol (); + status =register_quota_format (&ocfs2_quota_format); leave:if (status <0 ) {ocfs2_free_mem_caches ();
@@ -1094,6 +1100,8 @@ static void __exit ocfs2_exit(void)destroy_workqueue (ocfs2_wq); } +unregister_quota_format (&ocfs2_quota_format); +debugfs_remove (ocfs2_debugfs_root);ocfs2_free_mem_caches ();
@@ -1209,8 +1217,27 @@ static int ocfs2_initialize_mem_caches(void) (SLAB_HWCACHE_ALIGN |SLAB_RECLAIM_ACCOUNT |SLAB_MEM_SPREAD ), ocfs2_inode_init_once); -if (!ocfs2_inode_cachep) + ocfs2_dquot_cachep =kmem_cache_create ("ocfs2_dquot_cache" , +sizeof (struct ocfs2_dquot ), +0 , + (SLAB_HWCACHE_ALIGN |SLAB_RECLAIM_ACCOUNT | +SLAB_MEM_SPREAD ), +NULL ); + ocfs2_qf_chunk_cachep =kmem_cache_create ("ocfs2_qf_chunk_cache" , +sizeof (struct ocfs2_quota_chunk ), +0 , + (SLAB_RECLAIM_ACCOUNT |SLAB_MEM_SPREAD ), +NULL ); +if (!ocfs2_inode_cachep || !ocfs2_dquot_cachep || + !ocfs2_qf_chunk_cachep) { +if (ocfs2_inode_cachep) +kmem_cache_destroy (ocfs2_inode_cachep); +if (ocfs2_dquot_cachep) +kmem_cache_destroy (ocfs2_dquot_cachep); +if (ocfs2_qf_chunk_cachep) +kmem_cache_destroy (ocfs2_qf_chunk_cachep);return -ENOMEM ; + }return 0 ; }
@@ -1219,8 +1246,15 @@ static void ocfs2_free_mem_caches(void) {if (ocfs2_inode_cachep)kmem_cache_destroy (ocfs2_inode_cachep); - ocfs2_inode_cachep =NULL ; + +if (ocfs2_dquot_cachep) +kmem_cache_destroy (ocfs2_dquot_cachep); + ocfs2_dquot_cachep =NULL ; + +if (ocfs2_qf_chunk_cachep) +kmem_cache_destroy (ocfs2_qf_chunk_cachep); + ocfs2_qf_chunk_cachep =NULL ; } static int ocfs2_get_sector(struct super_block *sb,
Signature · 1 line
1.5.2.4
PATCH 25/29 ocfs2: Implementation of local and global quota file handling
Quoted 11 lines · Jan Kara
For each quota type each node has local quota file. In this file it stores changes users have made to disk usage via this node. Once in a while this information is synced to global file (and thus with other nodes) so that limits enforcement at least aproximately works.
Global quota files contain all the information about usage and limits. It's mostly handled by the generic VFS code (which implements a trie of structures inside a quota file). We only have to provide functions to convert structures from on-disk format to in-memory one. We also have to provide wrappers for various quota functions starting transactions and acquiring necessary cluster locks before the actual IO is really started.
Thanks for the description, it makes it a lot more
understandable.
Joel
Quoted 2,396 lines
Signed-off-by Jan Kara <[email protected]>--- fs/ocfs2/Makefile | 2 + fs/ocfs2/cluster/masklog.h | 1 + fs/ocfs2/dir.c | 4 +- fs/ocfs2/dlmglue.c | 137 +++++++ fs/ocfs2/dlmglue.h | 17 + fs/ocfs2/file.c | 6 +- fs/ocfs2/file.h | 3 + fs/ocfs2/inode.h | 2 + fs/ocfs2/ocfs2_fs.h | 95 +++++ fs/ocfs2/ocfs2_lockid.h | 5 + fs/ocfs2/quota.h | 97 +++++ fs/ocfs2/quota_global.c | 863 ++++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/quota_local.c | 833 ++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/super.c | 38 ++- 14 files changed, 2096 insertions(+), 7 deletions(-) create mode 100644 fs/ocfs2/quota.h create mode 100644 fs/ocfs2/quota_global.c create mode 100644 fs/ocfs2/quota_local.cfs/ocfs2/Makefile +2
-0@@ -35,6 +35,8 @@ ocfs2-objs := \ sysfile.o \ uptodate.o \ ver.o \ + quota_local.o \ + quota_global.o \ xattr.o ocfs2_stackglue-objs := stackglue.ofs/ocfs2/cluster/masklog.h +1
-0@@ -113,6 +113,7 @@#define ML_QUORUM 0x0000000008000000ULL/* net connection quorum */ #define ML_EXPORT 0x0000000010000000ULL/* ocfs2 export operations */ #define ML_XATTR 0x0000000020000000ULL/* ocfs2 extended attributes */ +#define ML_QUOTA 0x0000000040000000ULL/* ocfs2 quota operations */ /* bits that are infrequently given and frequently matched in the high word */ #define ML_ERROR 0x0000000100000000ULL/* sent to KERN_ERR */ #define ML_NOTICE 0x0000000200000000ULL/* setn to KERN_NOTICE */ fs/ocfs2/dir.c +2
-2@@ -82,8 +82,8 @@ static int ocfs2_do_extend_dir(struct super_block *sb,struct ocfs2_alloc_context *meta_ac, struct buffer_head **new_bh); -static struct buffer_head *ocfs2_bread (struct inode *inode, -int block,int *err,int reada) +struct buffer_head *ocfs2_bread (struct inode *inode, +int block,int *err,int reada) {struct buffer_head *bh =NULL ;int tmperr;fs/ocfs2/dlmglue.c +137
-0@@ -32,6 +32,7 @@#include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/time.h> +#include <linux/quotaops.h> #define MLOG_MASK_PREFIX ML_DLM_GLUE#include <cluster/masklog.h> @@ -51,6 +52,7 @@#include "slot_map.h" #include "super.h" #include "uptodate.h" +#include "quota.h" #include "buffer_head_io.h" @@ -68,6 +70,7 @@ struct ocfs2_mask_waiter {static struct ocfs2_super *ocfs2_get_dentry_osb (struct ocfs2_lock_res *lockres);static struct ocfs2_super *ocfs2_get_inode_osb (struct ocfs2_lock_res *lockres);static struct ocfs2_super *ocfs2_get_file_osb (struct ocfs2_lock_res *lockres); +static struct ocfs2_super *ocfs2_get_qinfo_osb (struct ocfs2_lock_res *lockres);/* * Return value from ->downconvert_worker functions. @@ -102,6 +105,7 @@ static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres,static void ocfs2_dentry_post_unlock (struct ocfs2_super *osb,struct ocfs2_lock_res *lockres); +static void ocfs2_set_qinfo_lvb (struct ocfs2_lock_res *lockres);#define mlog_meta_lvb (__level, __lockres) ocfs2_dump_meta_lvb_info(__level, __PRETTY_FUNCTION__, __LINE__, __lockres)@@ -258,6 +262,12 @@ static struct ocfs2_lock_res_ops ocfs2_flock_lops = { .flags = 0, }; +static struct ocfs2_lock_res_ops ocfs2_qinfo_lops = { + .set_lvb = ocfs2_set_qinfo_lvb, + .get_osb = ocfs2_get_qinfo_osb, + .flags =LOCK_TYPE_REQUIRES_REFRESH |LOCK_TYPE_USES_LVB , +}; +static inline int ocfs2_is_inode_lock (struct ocfs2_lock_res *lockres) { return lockres->l_type == OCFS2_LOCK_TYPE_META ||@@ -279,6 +289,13 @@ static inline struct ocfs2_dentry_lock *ocfs2_lock_res_dl(struct ocfs2_lock_resreturn (struct ocfs2_dentry_lock *)lockres->l_priv; } +static inline struct ocfs2_mem_dqinfo *ocfs2_lock_res_qinfo (struct ocfs2_lock_res *lockres) +{ +BUG_ON (lockres->l_type !=OCFS2_LOCK_TYPE_QINFO ); + +return (struct ocfs2_mem_dqinfo *)lockres->l_priv; +} +static inline struct ocfs2_super *ocfs2_get_lockres_osb (struct ocfs2_lock_res *lockres) {if (lockres->l_ops->get_osb)@@ -507,6 +524,13 @@ static struct ocfs2_super *ocfs2_get_inode_osb(struct ocfs2_lock_res *lockres)return OCFS2_SB (inode->i_sb); } +static struct ocfs2_super *ocfs2_get_qinfo_osb (struct ocfs2_lock_res *lockres) +{ +struct ocfs2_mem_dqinfo *info = lockres->l_priv; + +return OCFS2_SB (info->dqi_gi.dqi_sb); +} +static struct ocfs2_super *ocfs2_get_file_osb (struct ocfs2_lock_res *lockres) {struct ocfs2_file_private *fp = lockres->l_priv;@@ -609,6 +633,17 @@ void ocfs2_file_lock_res_init(struct ocfs2_lock_res *lockres, lockres->l_flags |=OCFS2_LOCK_NOCACHE ; } +void ocfs2_qinfo_lock_res_init (struct ocfs2_lock_res *lockres, +struct ocfs2_mem_dqinfo *info) +{ +ocfs2_lock_res_init_once (lockres); +ocfs2_build_lock_name (OCFS2_LOCK_TYPE_QINFO , info->dqi_gi.dqi_type, +0 , lockres->l_name); +ocfs2_lock_res_init_common (OCFS2_SB (info->dqi_gi.dqi_sb), lockres, +OCFS2_LOCK_TYPE_QINFO , &ocfs2_qinfo_lops, + info); +} +void ocfs2_lock_res_free (struct ocfs2_lock_res *res) {mlog_entry_void ();@@ -3450,6 +3485,108 @@ static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres,return UNBLOCK_CONTINUE_POST ; } +static void ocfs2_set_qinfo_lvb (struct ocfs2_lock_res *lockres) +{ +struct ocfs2_qinfo_lvb *lvb; +struct ocfs2_mem_dqinfo *oinfo =ocfs2_lock_res_qinfo (lockres); +struct mem_dqinfo *info =sb_dqinfo (oinfo->dqi_gi.dqi_sb, + oinfo->dqi_gi.dqi_type); + +mlog_entry_void (); + + lvb = (struct ocfs2_qinfo_lvb *)ocfs2_dlm_lvb (&lockres->l_lksb); + lvb->lvb_version =OCFS2_LVB_VERSION ; + lvb->lvb_bgrace =cpu_to_be32 (info->dqi_bgrace); + lvb->lvb_igrace =cpu_to_be32 (info->dqi_igrace); + lvb->lvb_syncms =cpu_to_be32 (oinfo->dqi_syncms); + lvb->lvb_blocks =cpu_to_be32 (oinfo->dqi_gi.dqi_blocks); + lvb->lvb_free_blk =cpu_to_be32 (oinfo->dqi_gi.dqi_free_blk); + lvb->lvb_free_entry =cpu_to_be32 (oinfo->dqi_gi.dqi_free_entry); + +mlog_exit_void (); +} + +void ocfs2_qinfo_unlock (struct ocfs2_mem_dqinfo *oinfo,int ex) +{ +struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock; +struct ocfs2_super *osb =OCFS2_SB (oinfo->dqi_gi.dqi_sb); +int level = ex ?DLM_LOCK_EX :DLM_LOCK_PR ; + +mlog_entry_void (); +if (!ocfs2_is_hard_readonly (osb) && !ocfs2_mount_local (osb)) +ocfs2_cluster_unlock (osb, lockres, level); +mlog_exit_void (); +} + +/* Lock quota info, this function expects at least shared lock on the quota file +* so that we can safely refresh quota info from disk. */ +int ocfs2_qinfo_lock (struct ocfs2_mem_dqinfo *oinfo,int ex) +{ +struct mem_dqinfo *info =sb_dqinfo (oinfo->dqi_gi.dqi_sb, + oinfo->dqi_gi.dqi_type); +struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock; +struct ocfs2_super *osb =OCFS2_SB (oinfo->dqi_gi.dqi_sb); +struct ocfs2_qinfo_lvb *lvb; +int level = ex ?DLM_LOCK_EX :DLM_LOCK_PR ; +int status =0 ; +struct buffer_head *bh; +struct ocfs2_global_disk_dqinfo *gdinfo; + +mlog_entry_void (); + +/* We'll allow faking a readonly metadata lock for +* rodevices. */ +if (ocfs2_is_hard_readonly (osb)) { +if (ex) + status = -EROFS ; + goto bail; + } +if (ocfs2_mount_local (osb)) + goto bail; + + status =ocfs2_cluster_lock (osb, lockres, level,0 ,0 ); +if (status <0 ) { +mlog_errno (status); + goto bail; + } +if (!ocfs2_should_refresh_lock_res (lockres)) + goto bail; +/* OK, we have the lock but we need to refresh the quota info */ + lvb =ocfs2_dlm_lvb (&lockres->l_lksb); +if (lvb->lvb_version ==OCFS2_LVB_VERSION ) { + info->dqi_bgrace =be32_to_cpu (lvb->lvb_bgrace); + info->dqi_igrace =be32_to_cpu (lvb->lvb_igrace); + oinfo->dqi_syncms =be32_to_cpu (lvb->lvb_syncms); + oinfo->dqi_gi.dqi_blocks =be32_to_cpu (lvb->lvb_blocks); + oinfo->dqi_gi.dqi_free_blk =be32_to_cpu (lvb->lvb_free_blk); + oinfo->dqi_gi.dqi_free_entry = +be32_to_cpu (lvb->lvb_free_entry); + }else { + bh =ocfs2_read_quota_block (oinfo->dqi_gqinode,0 , &status); +if (!bh) { +ocfs2_qinfo_unlock (oinfo, ex); +mlog_errno (status); + goto bail_refresh; + } + gdinfo = (struct ocfs2_global_disk_dqinfo *) + (bh->b_data +OCFS2_GLOBAL_INFO_OFF ); + info->dqi_bgrace =le32_to_cpu (gdinfo->dqi_bgrace); + info->dqi_igrace =le32_to_cpu (gdinfo->dqi_igrace); + oinfo->dqi_syncms =le32_to_cpu (gdinfo->dqi_syncms); + oinfo->dqi_gi.dqi_blocks =le32_to_cpu (gdinfo->dqi_blocks); + oinfo->dqi_gi.dqi_free_blk =le32_to_cpu (gdinfo->dqi_free_blk); + oinfo->dqi_gi.dqi_free_entry = +le32_to_cpu (gdinfo->dqi_free_entry); +brelse (bh); +ocfs2_track_lock_refresh (lockres); + } +bail_refresh: +ocfs2_complete_lock_res_refresh (lockres, status); +bail: +mlog_exit (status); +return status; +} +/* * This is the filesystem locking protocol. It provides the lock handling * hooks for the underlying DLM. It has a maximum version number. fs/ocfs2/dlmglue.h +17
-0@@ -49,6 +49,17 @@ struct ocfs2_meta_lvb {__be32 lvb_reserved2; }; +struct ocfs2_qinfo_lvb { +__u8 lvb_version; +__u8 lvb_reserved[3 ]; +__be32 lvb_bgrace; +__be32 lvb_igrace; +__be32 lvb_syncms; +__be32 lvb_blocks; +__be32 lvb_free_blk; +__be32 lvb_free_entry; +}; +/* ocfs2_inode_lock_full() 'arg_flags' flags */ /* don't wait on recovery. */ #define OCFS2_META_LOCK_RECOVERY (0x01)@@ -69,6 +80,9 @@ void ocfs2_dentry_lock_res_init(struct ocfs2_dentry_lock *dl,struct ocfs2_file_private ;void ocfs2_file_lock_res_init (struct ocfs2_lock_res *lockres,struct ocfs2_file_private *fp); +struct ocfs2_mem_dqinfo ; +void ocfs2_qinfo_lock_res_init (struct ocfs2_lock_res *lockres, +struct ocfs2_mem_dqinfo *info);void ocfs2_lock_res_free (struct ocfs2_lock_res *res);int ocfs2_create_new_inode_locks (struct inode *inode);int ocfs2_drop_inode_locks (struct inode *inode);@@ -103,6 +117,9 @@ int ocfs2_dentry_lock(struct dentry *dentry, int ex);void ocfs2_dentry_unlock (struct dentry *dentry,int ex);int ocfs2_file_lock (struct file *file,int ex,int trylock);void ocfs2_file_unlock (struct file *file); +int ocfs2_qinfo_lock (struct ocfs2_mem_dqinfo *oinfo,int ex); +void ocfs2_qinfo_unlock (struct ocfs2_mem_dqinfo *oinfo,int ex); +void ocfs2_mark_lockres_freeing (struct ocfs2_lock_res *lockres); void ocfs2_simple_drop_lockres(struct ocfs2_super *osb,fs/ocfs2/file.c +3
-3@@ -303,9 +303,9 @@ bail:return status; } -static int ocfs2_simple_size_update (struct inode *inode, -struct buffer_head *di_bh, -u64 new_i_size) +int ocfs2_simple_size_update (struct inode *inode, +struct buffer_head *di_bh, +u64 new_i_size) {int ret;struct ocfs2_super *osb =OCFS2_SB (inode->i_sb);fs/ocfs2/file.h +3
-0@@ -51,6 +51,9 @@ int ocfs2_add_inode_data(struct ocfs2_super *osb,struct ocfs2_alloc_context *data_ac, struct ocfs2_alloc_context *meta_ac, enum ocfs2_alloc_restarted *reason_ret); +int ocfs2_simple_size_update (struct inode *inode, +struct buffer_head *di_bh, +u64 new_i_size);int ocfs2_extend_no_holes (struct inode *inode,u64 new_i_size,u64 zero_to);int ocfs2_setattr (struct dentry *dentry,struct iattr *attr);fs/ocfs2/inode.h +2
-0@@ -142,6 +142,8 @@ int ocfs2_mark_inode_dirty(handle_t *handle, struct buffer_head *bh);int ocfs2_aio_read (struct file *file,struct kiocb *req,struct iocb *iocb);int ocfs2_aio_write (struct file *file,struct kiocb *req,struct iocb *iocb); +struct buffer_head *ocfs2_bread (struct inode *inode, +int block,int *err,int reada);void ocfs2_set_inode_flags (struct inode *inode);void ocfs2_get_inode_flags (struct ocfs2_inode_info *oi);fs/ocfs2/ocfs2_fs.h +95
-0@@ -878,6 +878,101 @@ static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)return xe->xe_type &OCFS2_XATTR_TYPE_MASK ; } +/* +* On disk structures for global quota file +*/ + +/* Magic numbers and known versions for global quota files */ +#define OCFS2_GLOBAL_QMAGICS {\ + 0x0cf52470, /* USRQUOTA */ \ + 0x0cf52471 /* GRPQUOTA */ \ +} + +#define OCFS2_GLOBAL_QVERSIONS {\ + 0, \ + 0, \ +} + +/* Generic header of all quota files */ +struct ocfs2_disk_dqheader { +__le32 dqh_magic;/* Magic number identifying file */ +__le32 dqh_version;/* Quota format version */ +}; + +#define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader)) + +/* Information header of global quota file (immediately follows the generic +* header) */ +struct ocfs2_global_disk_dqinfo { +/*00*/ __le32 dqi_bgrace; +__le32 dqi_igrace; +__le32 dqi_syncms; +__le32 dqi_blocks; +/*10*/ __le32 dqi_free_blk; +__le32 dqi_free_entry; +}; + +/* Structure with global user / group information. We reserve some space +* for future use. */ +struct ocfs2_global_disk_dqblk { +/*00*/ __le32 dqb_id;/* ID the structure belongs to */ +__le32 dqb_use_count;/* Number of nodes having reference to this structure */ +__le64 dqb_ihardlimit;/* absolute limit on allocated inodes */ +/*10*/ __le64 dqb_isoftlimit;/* preferred inode limit */ +__le64 dqb_curinodes;/* current # allocated inodes */ +/*20*/ __le64 dqb_bhardlimit;/* absolute limit on disk space */ +__le64 dqb_bsoftlimit;/* preferred limit on disk space */ +/*30*/ __le64 dqb_curspace;/* current space occupied */ +__le64 dqb_btime;/* time limit for excessive disk use */ +/*40*/ __le64 dqb_itime;/* time limit for excessive inode use */ +__le64 dqb_pad1; +/*50*/ __le64 dqb_pad2; +}; + +/* +* On-disk structures for local quota file +*/ + +/* Magic numbers and known versions for local quota files */ +#define OCFS2_LOCAL_QMAGICS {\ + 0x0cf524c0, /* USRQUOTA */ \ + 0x0cf524c1 /* GRPQUOTA */ \ +} + +#define OCFS2_LOCAL_QVERSIONS {\ + 0, \ + 0, \ +} + +/* Quota flags in dqinfo header */ +#define OLQF_CLEAN 0x0001/* Quota file is empty (this should be after\ +* quota has been cleanly turned off) */ + +#define OCFS2_LOCAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader)) + +/* Information header of local quota file (immediately follows the generic +* header) */ +struct ocfs2_local_disk_dqinfo { +__le32 dqi_flags;/* Flags for quota file */ +__le32 dqi_chunks;/* Number of chunks of quota structures +* with a bitmap */ +__le32 dqi_blocks;/* Number of blocks allocated for quota file */ +}; + +/* Header of one chunk of a quota file */ +struct ocfs2_local_disk_chunk { +__le32 dqc_free;/* Number of free entries in the bitmap */ +u8 dqc_bitmap[0 ];/* Bitmap of entries in the corresponding +* chunk of quota file */ +}; + +/* One entry in local quota file */ +struct ocfs2_local_disk_dqblk { +/*00*/ __le64 dqb_id;/* id this quota applies to */ +__le64 dqb_spacemod;/* Change in the amount of used space */ +/*10*/ __le64 dqb_inodemod;/* Change in the amount of used inodes */ +}; +#ifdef __KERNEL__static inline int ocfs2_fast_symlink_chars (struct super_block *sb) {fs/ocfs2/ocfs2_lockid.h +5
-0@@ -46,6 +46,7 @@ enum ocfs2_lock_type {OCFS2_LOCK_TYPE_DENTRY ,OCFS2_LOCK_TYPE_OPEN ,OCFS2_LOCK_TYPE_FLOCK , +OCFS2_LOCK_TYPE_QINFO , OCFS2_NUM_LOCK_TYPES };@@ -77,6 +78,9 @@ static inline char ocfs2_lock_type_char(enum ocfs2_lock_type type)case OCFS2_LOCK_TYPE_FLOCK : c ='F' ;break ; +case OCFS2_LOCK_TYPE_QINFO : + c ='Q' ; +break ;default : c ='\0' ; }@@ -95,6 +99,7 @@ static char *ocfs2_lock_type_strings[] = { [OCFS2_LOCK_TYPE_DENTRY] = "Dentry", [OCFS2_LOCK_TYPE_OPEN] = "Open", [OCFS2_LOCK_TYPE_FLOCK] = "Flock", + [OCFS2_LOCK_TYPE_QINFO] = "Quota", };static inline const char *ocfs2_lock_type_string (enum ocfs2_lock_type type)fs/ocfs2/quota.h new file +97
-0@@ -0,0 +1,97 @@ +/* +* quota.h for OCFS2 +* +* On disk quota structures for local and global quota file, in-memory +* structures. +* +*/ + +#ifndef _OCFS2_QUOTA_H +#define _OCFS2_QUOTA_H + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/quota.h> +#include <linux/list.h> +#include <linux/dqblk_qtree.h> + +#include "ocfs2.h" + +/* Common stuff */ +/* id number of quota format */ +#define QFMT_OCFS2 3 + +/* How many bytes to we reserve in each quota file block for our internal +* purposes? E.g. checksums... */ +#define OCFS2_QBLK_RESERVED_SPACE 8 + +/* +* In-memory structures +*/ +struct ocfs2_dquot { +struct dquot dq_dquot;/* Generic VFS dquot */ +loff_t dq_local_off;/* Offset in the local quota file */ +struct ocfs2_quota_chunk *dq_chunk;/* Chunk dquot is in */ +unsigned int dq_use_count;/* Number of nodes having reference to this entry in global quota file */ +s64 dq_origspace;/* Last globally synced space usage */ +s64 dq_originodes;/* Last globally synced inode usage */ +}; + +/* In-memory structure with quota header information */ +struct ocfs2_mem_dqinfo { +unsigned int dqi_type;/* Quota type this structure describes */ +unsigned int dqi_chunks;/* Number of chunks in local quota file */ +unsigned int dqi_blocks;/* Number of blocks allocated for local quota file */ +unsigned int dqi_syncms;/* How often should we sync with other nodes */ +struct list_head dqi_chunk;/* List of chunks */ +struct inode *dqi_gqinode;/* Global quota file inode */ +struct ocfs2_lock_res dqi_gqlock;/* Lock protecting quota information structure */ +struct buffer_head *dqi_gqi_bh;/* Buffer head with global quota file inode - set only if inode lock is obtained */ +int dqi_gqi_count;/* Number of holders of dqi_gqi_bh */ +struct buffer_head *dqi_lqi_bh;/* Buffer head with local quota file inode */ +struct buffer_head *dqi_ibh;/* Buffer with information header */ +struct qtree_mem_dqinfo dqi_gi;/* Info about global file */ +}; + +static inline struct ocfs2_dquot *OCFS2_DQUOT (struct dquot *dquot) +{ + return container_of(dquot, struct ocfs2_dquot, dq_dquot); +} + +struct ocfs2_quota_chunk { +struct list_head qc_chunk;/* List of quotafile chunks */ +int qc_num;/* Number of quota chunk */ +struct buffer_head *qc_headerbh;/* Buffer head with chunk header */ +}; + +extern struct kmem_cache *ocfs2_dquot_cachep; +extern struct kmem_cache *ocfs2_qf_chunk_cachep; + +extern struct qtree_fmt_operations ocfs2_global_ops; + +ssize_t ocfs2_quota_read (struct super_block *sb,int type,char *data, +size_t len,loff_t off); +ssize_t ocfs2_quota_write (struct super_block *sb,int type, +const char *data,size_t len,loff_t off); +int ocfs2_global_read_info (struct super_block *sb,int type); +int ocfs2_global_write_info (struct super_block *sb,int type); +int ocfs2_global_read_dquot (struct dquot *dquot); +int __ocfs2_sync_dquot (struct dquot *dquot,int freeing); +static inline int ocfs2_sync_dquot (struct dquot *dquot) +{ +return __ocfs2_sync_dquot (dquot,0 ); +} +static inline int ocfs2_global_release_dquot (struct dquot *dquot) +{ +return __ocfs2_sync_dquot (dquot,1 ); +} + +int ocfs2_lock_global_qf (struct ocfs2_mem_dqinfo *oinfo,int ex); +void ocfs2_unlock_global_qf (struct ocfs2_mem_dqinfo *oinfo,int ex); +struct buffer_head *ocfs2_read_quota_block (struct inode *inode, +int block,int *err); + +extern struct dquot_operations ocfs2_quota_operations; +extern struct quota_format_type ocfs2_quota_format; + +#endif /* _OCFS2_QUOTA_H */ fs/ocfs2/quota_global.c new file +863
-0@@ -0,0 +1,863 @@ +/* +* Implementation of operations over global quota file +*/ +#include <linux/fs.h> +#include <linux/quota.h> +#include <linux/quotaops.h> +#include <linux/dqblk_qtree.h> + +#define MLOG_MASK_PREFIX ML_QUOTA +#include <cluster/masklog.h> + +#include "ocfs2_fs.h" +#include "ocfs2.h" +#include "alloc.h" +#include "inode.h" +#include "journal.h" +#include "file.h" +#include "sysfile.h" +#include "dlmglue.h" +#include "quota.h" + +static void ocfs2_global_disk2memdqb (struct dquot *dquot,void *dp) +{ +struct ocfs2_global_disk_dqblk *d = dp; +struct mem_dqblk *m = &dquot->dq_dqb; + +/* Update from disk only entries not set by the admin */ +if (!test_bit (DQ_LASTSET_B +QIF_ILIMITS_B , &dquot->dq_flags)) { + m->dqb_ihardlimit =le64_to_cpu (d->dqb_ihardlimit); + m->dqb_isoftlimit =le64_to_cpu (d->dqb_isoftlimit); + } +if (!test_bit (DQ_LASTSET_B +QIF_INODES_B , &dquot->dq_flags)) + m->dqb_curinodes =le64_to_cpu (d->dqb_curinodes); +if (!test_bit (DQ_LASTSET_B +QIF_BLIMITS_B , &dquot->dq_flags)) { + m->dqb_bhardlimit =le64_to_cpu (d->dqb_bhardlimit); + m->dqb_bsoftlimit =le64_to_cpu (d->dqb_bsoftlimit); + } +if (!test_bit (DQ_LASTSET_B +QIF_SPACE_B , &dquot->dq_flags)) + m->dqb_curspace =le64_to_cpu (d->dqb_curspace); +if (!test_bit (DQ_LASTSET_B +QIF_BTIME_B , &dquot->dq_flags)) + m->dqb_btime =le64_to_cpu (d->dqb_btime); +if (!test_bit (DQ_LASTSET_B +QIF_ITIME_B , &dquot->dq_flags)) + m->dqb_itime =le64_to_cpu (d->dqb_itime); +OCFS2_DQUOT (dquot)->dq_use_count =le32_to_cpu (d->dqb_use_count); +} + +static void ocfs2_global_mem2diskdqb (void *dp,struct dquot *dquot) +{ +struct ocfs2_global_disk_dqblk *d = dp; +struct mem_dqblk *m = &dquot->dq_dqb; + + d->dqb_id =cpu_to_le32 (dquot->dq_id); + d->dqb_use_count =cpu_to_le32 (OCFS2_DQUOT (dquot)->dq_use_count); + d->dqb_ihardlimit =cpu_to_le64 (m->dqb_ihardlimit); + d->dqb_isoftlimit =cpu_to_le64 (m->dqb_isoftlimit); + d->dqb_curinodes =cpu_to_le64 (m->dqb_curinodes); + d->dqb_bhardlimit =cpu_to_le64 (m->dqb_bhardlimit); + d->dqb_bsoftlimit =cpu_to_le64 (m->dqb_bsoftlimit); + d->dqb_curspace =cpu_to_le64 (m->dqb_curspace); + d->dqb_btime =cpu_to_le64 (m->dqb_btime); + d->dqb_itime =cpu_to_le64 (m->dqb_itime); + d->dqb_pad1 = d->dqb_pad2 =0 ; +} + +static int ocfs2_global_is_id (void *dp,struct dquot *dquot) +{ +struct ocfs2_global_disk_dqblk *d = dp; +struct ocfs2_mem_dqinfo *oinfo = +sb_dqinfo (dquot->dq_sb, dquot->dq_type)->dqi_priv; + +if (qtree_entry_unused (&oinfo->dqi_gi, dp)) +return 0 ; +return le32_to_cpu (d->dqb_id) == dquot->dq_id; +} + +struct qtree_fmt_operations ocfs2_global_ops = { + .mem2disk_dqblk = ocfs2_global_mem2diskdqb, + .disk2mem_dqblk = ocfs2_global_disk2memdqb, + .is_id = ocfs2_global_is_id, +}; + + +struct buffer_head *ocfs2_read_quota_block (struct inode *inode, +int block,int *err) +{ +return ocfs2_bread (inode, block, err,0 ); +} + +/* Read data from global quotafile - avoid pagecache and such because we cannot +* afford acquiring the locks... We use quota cluster lock to serialize +* operations. Caller is responsible for acquiring it. */ +ssize_t ocfs2_quota_read (struct super_block *sb,int type,char *data, +size_t len,loff_t off) +{ +struct ocfs2_mem_dqinfo *oinfo =sb_dqinfo (sb, type)->dqi_priv; +struct inode *gqinode = oinfo->dqi_gqinode; +loff_t i_size =i_size_read (gqinode); +int offset = off & (sb->s_blocksize -1 ); +sector_t blk = off >> sb->s_blocksize_bits; +int err =0 ; +struct buffer_head *bh; +size_t toread, tocopy; + +if (off > i_size) +return 0 ; +if (off + len > i_size) + len = i_size - off; + toread = len; +while (toread >0 ) { + tocopy =min ((size_t )(sb->s_blocksize - offset), toread); + bh =ocfs2_read_quota_block (gqinode, blk, &err); +if (!bh) { +mlog_errno (err); +return err; + } +memcpy (data, bh->b_data + offset, tocopy); +brelse (bh); + offset =0 ; + toread -= tocopy; + data += tocopy; + blk++; + } +return len; +} + +/* Write to quotafile (we know the transaction is already started and has +* enough credits) */ +ssize_t ocfs2_quota_write (struct super_block *sb,int type, +const char *data,size_t len,loff_t off) +{ +struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; +struct inode *gqinode = oinfo->dqi_gqinode; +int offset = off & (sb->s_blocksize -1 ); +sector_t blk = off >> sb->s_blocksize_bits; +int err =0 ; +struct buffer_head *bh; +handle_t *handle =journal_current_handle (); +size_t tocopy, towrite = len; + +if (!handle) { +mlog (ML_ERROR ,"Quota write (off=%llu, len=%llu) cancelled " +"because transaction was not started.\n" , + (unsigned long long )off, (unsigned long long )len); +return -EIO ; + } +mutex_lock_nested (&gqinode->i_mutex,I_MUTEX_QUOTA ); +if (gqinode->i_size < off + len) { +down_write (&OCFS2_I (gqinode)->ip_alloc_sem); + err =ocfs2_extend_no_holes (gqinode, off + len, off); +up_write (&OCFS2_I (gqinode)->ip_alloc_sem); +if (err <0 ) + goto out; + err =ocfs2_simple_size_update (gqinode, + oinfo->dqi_gqi_bh, + off + len); +if (err <0 ) + goto out; + } +WARN_ON (off >> sb->s_blocksize_bits != \ + (off + len) >> sb->s_blocksize_bits); +WARN_ON (((off + len) & ((1 << sb->s_blocksize_bits) -1 )) > + sb->s_blocksize -OCFS2_QBLK_RESERVED_SPACE ); +for (towrite = len; towrite >0 ; towrite -= tocopy) { + tocopy =min (towrite, (size_t )(sb->s_blocksize - offset)); + bh =ocfs2_read_quota_block (gqinode, blk, &err); +if (!bh) { +mlog_errno (err); +return err; + } + err =ocfs2_journal_access (handle, gqinode, bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (err <0 ) { +brelse (bh); + goto out; + } +lock_buffer (bh); +memcpy (bh->b_data + offset, data, tocopy); +flush_dcache_page (bh->b_page); +unlock_buffer (bh); + err =ocfs2_journal_dirty (handle, bh); +brelse (bh); +if (err <0 ) + goto out; + offset =0 ; + data += tocopy; + blk++; + } +out: +/* Nothing written? */ +if (len == towrite) { +mutex_unlock (&gqinode->i_mutex); +mlog_errno (err); +return err; + } + gqinode->i_version++; +ocfs2_mark_inode_dirty (handle, gqinode, oinfo->dqi_gqi_bh); +mutex_unlock (&gqinode->i_mutex); +return len - towrite; +} + +int ocfs2_lock_global_qf (struct ocfs2_mem_dqinfo *oinfo,int ex) +{ +int status; +struct buffer_head *bh =NULL ; + + status =ocfs2_inode_lock (oinfo->dqi_gqinode, &bh, ex); +if (status <0 ) +return status; +spin_lock (&dq_data_lock); +if (!oinfo->dqi_gqi_count++) + oinfo->dqi_gqi_bh = bh; +else +WARN_ON (bh != oinfo->dqi_gqi_bh); +spin_unlock (&dq_data_lock); +return 0 ; +} + +void ocfs2_unlock_global_qf (struct ocfs2_mem_dqinfo *oinfo,int ex) +{ +ocfs2_inode_unlock (oinfo->dqi_gqinode, ex); +brelse (oinfo->dqi_gqi_bh); +spin_lock (&dq_data_lock); +if (!--oinfo->dqi_gqi_count) + oinfo->dqi_gqi_bh =NULL ; +spin_unlock (&dq_data_lock); +} + +/* Read information header from global quota file */ +int ocfs2_global_read_info (struct super_block *sb,int type) +{ +struct inode *gqinode =NULL ; +unsigned int ino[MAXQUOTAS ] = {USER_QUOTA_SYSTEM_INODE , +GROUP_QUOTA_SYSTEM_INODE }; +struct ocfs2_global_disk_dqinfo dinfo; +struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; +int status; + +mlog_entry_void (); + +/* Read global header */ + gqinode =ocfs2_get_system_file_inode (OCFS2_SB (sb), ino[type], +OCFS2_INVALID_SLOT ); +if (!gqinode) { +mlog (ML_ERROR ,"failed to get global quota inode (type=%d)\n" , + type); + status = -EINVAL ; + goto out_err; + } + oinfo->dqi_gi.dqi_sb = sb; + oinfo->dqi_gi.dqi_type = type; +ocfs2_qinfo_lock_res_init (&oinfo->dqi_gqlock, oinfo); + oinfo->dqi_gi.dqi_entry_size =sizeof (struct ocfs2_global_disk_dqblk ); + oinfo->dqi_gi.dqi_ops = &ocfs2_global_ops; + oinfo->dqi_gqi_bh =NULL ; + oinfo->dqi_gqi_count =0 ; + oinfo->dqi_gqinode = gqinode; + status =ocfs2_lock_global_qf (oinfo,0 ); +if (status <0 ) { +mlog_errno (status); + goto out_err; + } + status = sb->s_op->quota_read (sb, type, (char *)&dinfo, +sizeof (struct ocfs2_global_disk_dqinfo ), +OCFS2_GLOBAL_INFO_OFF ); +ocfs2_unlock_global_qf (oinfo,0 ); +if (status !=sizeof (struct ocfs2_global_disk_dqinfo )) { +mlog (ML_ERROR ,"Cannot read global quota info (%d).\n" , + status); +if (status >=0 ) + status = -EIO ; +mlog_errno (status); + goto out_err; + } + info->dqi_bgrace =le32_to_cpu (dinfo.dqi_bgrace); + info->dqi_igrace =le32_to_cpu (dinfo.dqi_igrace); + oinfo->dqi_syncms =le32_to_cpu (dinfo.dqi_syncms); + oinfo->dqi_gi.dqi_blocks =le32_to_cpu (dinfo.dqi_blocks); + oinfo->dqi_gi.dqi_free_blk =le32_to_cpu (dinfo.dqi_free_blk); + oinfo->dqi_gi.dqi_free_entry =le32_to_cpu (dinfo.dqi_free_entry); + oinfo->dqi_gi.dqi_blocksize_bits = sb->s_blocksize_bits; + oinfo->dqi_gi.dqi_usable_bs = sb->s_blocksize - +OCFS2_QBLK_RESERVED_SPACE ; + oinfo->dqi_gi.dqi_qtree_depth =qtree_depth (&oinfo->dqi_gi); +out_err: +mlog_exit (status); +return status; +} + +/* Write information to global quota file. Expects exlusive lock on quota +* file inode and quota info */ +static int __ocfs2_global_write_info (struct super_block *sb,int type) +{ +struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; +struct ocfs2_global_disk_dqinfo dinfo; +ssize_t size; + +spin_lock (&dq_data_lock); + info->dqi_flags &= ~DQF_INFO_DIRTY ; + dinfo.dqi_bgrace =cpu_to_le32 (info->dqi_bgrace); + dinfo.dqi_igrace =cpu_to_le32 (info->dqi_igrace); +spin_unlock (&dq_data_lock); + dinfo.dqi_syncms =cpu_to_le32 (oinfo->dqi_syncms); + dinfo.dqi_blocks =cpu_to_le32 (oinfo->dqi_gi.dqi_blocks); + dinfo.dqi_free_blk =cpu_to_le32 (oinfo->dqi_gi.dqi_free_blk); + dinfo.dqi_free_entry =cpu_to_le32 (oinfo->dqi_gi.dqi_free_entry); + size = sb->s_op->quota_write (sb, type, (char *)&dinfo, +sizeof (struct ocfs2_global_disk_dqinfo ), +OCFS2_GLOBAL_INFO_OFF ); +if (size !=sizeof (struct ocfs2_global_disk_dqinfo )) { +mlog (ML_ERROR ,"Cannot write global quota info structure\n" ); +if (size >=0 ) + size = -EIO ; +return size; + } +return 0 ; +} + +int ocfs2_global_write_info (struct super_block *sb,int type) +{ +int err; +struct ocfs2_mem_dqinfo *info =sb_dqinfo (sb, type)->dqi_priv; + + err =ocfs2_qinfo_lock (info,1 ); +if (err <0 ) +return err; + err =__ocfs2_global_write_info (sb, type); +ocfs2_qinfo_unlock (info,1 ); +return err; +} + +/* Read in information from global quota file and acquire a reference to it. +* dquot_acquire() has already started the transaction and locked quota file */ +int ocfs2_global_read_dquot (struct dquot *dquot) +{ +int err, err2, ex =0 ; +struct ocfs2_mem_dqinfo *info = +sb_dqinfo (dquot->dq_sb, dquot->dq_type)->dqi_priv; + + err =ocfs2_qinfo_lock (info,0 ); +if (err <0 ) + goto out; + err =qtree_read_dquot (&info->dqi_gi, dquot); +if (err <0 ) + goto out_qlock; +OCFS2_DQUOT (dquot)->dq_use_count++; +OCFS2_DQUOT (dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace; +OCFS2_DQUOT (dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes; +if (!dquot->dq_off) {/* No real quota entry? */ +/* Upgrade to exclusive lock for allocation */ + err =ocfs2_qinfo_lock (info,1 ); +if (err <0 ) + goto out_qlock; + ex =1 ; + } + err =qtree_write_dquot (&info->dqi_gi, dquot); +if (ex &&info_dirty (sb_dqinfo (dquot->dq_sb, dquot->dq_type))) { + err2 =__ocfs2_global_write_info (dquot->dq_sb, dquot->dq_type); +if (!err) + err = err2; + } +out_qlock: +if (ex) +ocfs2_qinfo_unlock (info,1 ); +ocfs2_qinfo_unlock (info,0 ); +out: +if (err <0 ) +mlog_errno (err); +return err; +} + +/* Sync local information about quota modifications with global quota file. +* Caller must have started the transaction and obtained exclusive lock for +* global quota file inode */ +int __ocfs2_sync_dquot (struct dquot *dquot,int freeing) +{ +int err, err2; +struct super_block *sb = dquot->dq_sb; +int type = dquot->dq_type; +struct ocfs2_mem_dqinfo *info =sb_dqinfo (sb, type)->dqi_priv; +struct ocfs2_global_disk_dqblk dqblk; +s64 spacechange, inodechange; +time_t olditime, oldbtime; + + err = sb->s_op->quota_read (sb, type, (char *)&dqblk, +sizeof (struct ocfs2_global_disk_dqblk ), + dquot->dq_off); +if (err !=sizeof (struct ocfs2_global_disk_dqblk )) { +if (err >=0 ) { +mlog (ML_ERROR ,"Short read from global quota file " +"(%u read)\n" , err); + err = -EIO ; + } + goto out; + } + +/* Update space and inode usage. Get also other information from +* global quota file so that we don't overwrite any changes there. +* We are */ +spin_lock (&dq_data_lock); + spacechange = dquot->dq_dqb.dqb_curspace - +OCFS2_DQUOT (dquot)->dq_origspace; + inodechange = dquot->dq_dqb.dqb_curinodes - +OCFS2_DQUOT (dquot)->dq_originodes; + olditime = dquot->dq_dqb.dqb_itime; + oldbtime = dquot->dq_dqb.dqb_btime; +ocfs2_global_disk2memdqb (dquot, &dqblk); +mlog (0 ,"Syncing global dquot %d space %lld+%lld, inodes %lld+%lld\n" , + dquot->dq_id, dquot->dq_dqb.dqb_curspace, spacechange, + dquot->dq_dqb.dqb_curinodes, inodechange); +if (!test_bit (DQ_LASTSET_B +QIF_SPACE_B , &dquot->dq_flags)) + dquot->dq_dqb.dqb_curspace += spacechange; +if (!test_bit (DQ_LASTSET_B +QIF_INODES_B , &dquot->dq_flags)) + dquot->dq_dqb.dqb_curinodes += inodechange; +/* Now merge grace time changes... */ +if (!test_bit (DQ_LASTSET_B +QIF_BTIME_B , &dquot->dq_flags) && + oldbtime >0 ) { +if (dquot->dq_dqb.dqb_btime >0 ) + dquot->dq_dqb.dqb_btime = +min (dquot->dq_dqb.dqb_btime, oldbtime); +else + dquot->dq_dqb.dqb_btime = oldbtime; + } +if (!test_bit (DQ_LASTSET_B +QIF_ITIME_B , &dquot->dq_flags) && + olditime >0 ) { +if (dquot->dq_dqb.dqb_itime >0 ) + dquot->dq_dqb.dqb_itime = +min (dquot->dq_dqb.dqb_itime, olditime); +else + dquot->dq_dqb.dqb_itime = olditime; + } +/* All information is properly updated, clear the flags */ +__clear_bit (DQ_LASTSET_B +QIF_SPACE_B , &dquot->dq_flags); +__clear_bit (DQ_LASTSET_B +QIF_INODES_B , &dquot->dq_flags); +__clear_bit (DQ_LASTSET_B +QIF_BLIMITS_B , &dquot->dq_flags); +__clear_bit (DQ_LASTSET_B +QIF_ILIMITS_B , &dquot->dq_flags); +__clear_bit (DQ_LASTSET_B +QIF_BTIME_B , &dquot->dq_flags); +__clear_bit (DQ_LASTSET_B +QIF_ITIME_B , &dquot->dq_flags); +OCFS2_DQUOT (dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace; +OCFS2_DQUOT (dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes; +spin_unlock (&dq_data_lock); + err =ocfs2_qinfo_lock (info, freeing); +if (err <0 ) { +mlog (ML_ERROR ,"Failed to lock quota info, loosing quota write" +" (type=%d, id=%u)\n" , dquot->dq_type, + (unsigned )dquot->dq_id); + goto out; + } +if (freeing) +OCFS2_DQUOT (dquot)->dq_use_count--; + err =qtree_write_dquot (&info->dqi_gi, dquot); +if (err <0 ) + goto out_qlock; +if (freeing && !OCFS2_DQUOT (dquot)->dq_use_count) { + err =qtree_release_dquot (&info->dqi_gi, dquot); +if (info_dirty (sb_dqinfo (sb, type))) { + err2 =__ocfs2_global_write_info (sb, type); +if (!err) + err = err2; + } + } +out_qlock: +ocfs2_qinfo_unlock (info, freeing); +out: +if (err <0 ) +mlog_errno (err); +return err; +} + +/* +* Wrappers for generic quota functions +*/ + +static int ocfs2_write_dquot (struct dquot *dquot) +{ +handle_t *handle; +struct ocfs2_super *osb =OCFS2_SB (dquot->dq_sb); +int status =0 ; + +mlog_entry ("id=%u, type=%d" , dquot->dq_id, dquot->dq_type); + + handle =ocfs2_start_trans (osb,OCFS2_QWRITE_CREDITS ); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto out; + } + status =dquot_commit (dquot); +ocfs2_commit_trans (osb, handle); +out: +mlog_exit (status); +return status; +} + +int ocfs2_calc_qdel_credits (struct super_block *sb,int type) +{ +struct ocfs2_mem_dqinfo *oinfo; +int features[MAXQUOTAS ] = {OCFS2_FEATURE_RO_COMPAT_USRQUOTA , +OCFS2_FEATURE_RO_COMPAT_GRPQUOTA }; + +if (!OCFS2_HAS_RO_COMPAT_FEATURE (sb, features[type])) +return 0 ; + + oinfo =sb_dqinfo (sb, type)->dqi_priv; +/* We modify tree, leaf block, global info, local chunk header, +* global and local inode */ +return oinfo->dqi_gi.dqi_qtree_depth +2 +1 + +2 *OCFS2_INODE_UPDATE_CREDITS ; +} + +static int ocfs2_release_dquot (struct dquot *dquot) +{ +handle_t *handle; +struct ocfs2_mem_dqinfo *oinfo = +sb_dqinfo (dquot->dq_sb, dquot->dq_type)->dqi_priv; +struct ocfs2_super *osb =OCFS2_SB (dquot->dq_sb); +int status =0 ; + +mlog_entry ("id=%u, type=%d" , dquot->dq_id, dquot->dq_type); + + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) + goto out; + handle =ocfs2_start_trans (osb, +ocfs2_calc_qdel_credits (dquot->dq_sb, dquot->dq_type)); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto out_ilock; + } + status =dquot_release (dquot); +ocfs2_commit_trans (osb, handle); +out_ilock: +ocfs2_unlock_global_qf (oinfo,1 ); +out: +mlog_exit (status); +return status; +} + +int ocfs2_calc_qinit_credits (struct super_block *sb,int type) +{ +struct ocfs2_mem_dqinfo *oinfo; +int features[MAXQUOTAS ] = {OCFS2_FEATURE_RO_COMPAT_USRQUOTA , +OCFS2_FEATURE_RO_COMPAT_GRPQUOTA }; +struct ocfs2_dinode *lfe, *gfe; + +if (!OCFS2_HAS_RO_COMPAT_FEATURE (sb, features[type])) +return 0 ; + + oinfo =sb_dqinfo (sb, type)->dqi_priv; + gfe = (struct ocfs2_dinode *)oinfo->dqi_gqi_bh->b_data; + lfe = (struct ocfs2_dinode *)oinfo->dqi_lqi_bh->b_data; +/* We can extend local file + global file. In local file we +* can modify info, chunk header block and dquot block. In +* global file we can modify info, tree and leaf block */ +return ocfs2_calc_extend_credits (sb, &lfe->id2.i_list,0 ) + +ocfs2_calc_extend_credits (sb, &gfe->id2.i_list,0 ) + +3 + oinfo->dqi_gi.dqi_qtree_depth +2 ; +} + +static int ocfs2_acquire_dquot (struct dquot *dquot) +{ +handle_t *handle; +struct ocfs2_mem_dqinfo *oinfo = +sb_dqinfo (dquot->dq_sb, dquot->dq_type)->dqi_priv; +struct ocfs2_super *osb =OCFS2_SB (dquot->dq_sb); +int status =0 ; + +mlog_entry ("id=%u, type=%d" , dquot->dq_id, dquot->dq_type); +/* We need an exclusive lock, because we're going to update use count +* and instantiate possibly new dquot structure */ + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) + goto out; + handle =ocfs2_start_trans (osb, +ocfs2_calc_qinit_credits (dquot->dq_sb, dquot->dq_type)); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto out_ilock; + } + status =dquot_acquire (dquot); +ocfs2_commit_trans (osb, handle); +out_ilock: +ocfs2_unlock_global_qf (oinfo,1 ); +out: +mlog_exit (status); +return status; +} + +static int ocfs2_mark_dquot_dirty (struct dquot *dquot) +{ +unsigned long mask = (1 << (DQ_LASTSET_B +QIF_ILIMITS_B )) | + (1 << (DQ_LASTSET_B +QIF_BLIMITS_B )) | + (1 << (DQ_LASTSET_B +QIF_INODES_B )) | + (1 << (DQ_LASTSET_B +QIF_SPACE_B )) | + (1 << (DQ_LASTSET_B +QIF_BTIME_B )) | + (1 << (DQ_LASTSET_B +QIF_ITIME_B )); +int sync =0 ; +int status; +struct super_block *sb = dquot->dq_sb; +int type = dquot->dq_type; +struct ocfs2_mem_dqinfo *oinfo =sb_dqinfo (sb, type)->dqi_priv; +handle_t *handle; +struct ocfs2_super *osb =OCFS2_SB (sb); + +mlog_entry ("id=%u, type=%d" , dquot->dq_id, type); +dquot_mark_dquot_dirty (dquot); + +/* In case user set some limits, sync dquot immediately to global +* quota file so that information propagates quicker */ +spin_lock (&dq_data_lock); +if (dquot->dq_flags & mask) + sync =1 ; +spin_unlock (&dq_data_lock); +if (!sync) { + status =ocfs2_write_dquot (dquot); + goto out; + } + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) + goto out; + handle =ocfs2_start_trans (osb,OCFS2_QSYNC_CREDITS ); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto out_ilock; + } + status =ocfs2_sync_dquot (dquot); +if (status <0 ) { +mlog_errno (status); + goto out_trans; + } +/* Now write updated local dquot structure */ + status =dquot_commit (dquot); +out_trans: +ocfs2_commit_trans (osb, handle); +out_ilock: +ocfs2_unlock_global_qf (oinfo,1 ); +out: +mlog_exit (status); +return status; +} + +/* This should happen only after set_dqinfo(). */ +static int ocfs2_write_info (struct super_block *sb,int type) +{ +handle_t *handle; +int status =0 ; +struct ocfs2_mem_dqinfo *oinfo =sb_dqinfo (sb, type)->dqi_priv; + +mlog_entry_void (); + + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) + goto out; + handle =ocfs2_start_trans (OCFS2_SB (sb),OCFS2_QINFO_WRITE_CREDITS ); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto out_ilock; + } + status =dquot_commit_info (sb, type); +ocfs2_commit_trans (OCFS2_SB (sb), handle); +out_ilock: +ocfs2_unlock_global_qf (oinfo,1 ); +out: +mlog_exit (status); +return status; +} + +/* This is difficult. We have to lock quota inode and start transaction +* in this function but we don't want to take the penalty of exlusive +* quota file lock when we are just going to use cached structures. So +* we just take read lock check whether we have dquot cached and if so, +* we don't have to take the write lock... */ +static int ocfs2_dquot_initialize (struct inode *inode,int type) +{ +handle_t *handle =NULL ; +int status =0 ; +struct super_block *sb = inode->i_sb; +struct ocfs2_mem_dqinfo *oinfo; +int exclusive =0 ; +int cnt; +qid_t id; + +mlog_entry_void (); + +for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) { +if (type !=-1 && cnt != type) +continue ; + oinfo =sb_dqinfo (sb, cnt)->dqi_priv; + status =ocfs2_lock_global_qf (oinfo,0 ); +if (status <0 ) + goto out; +/* This is just a performance optimization not a reliable test. +* Since we hold an inode lock, noone can actually release +* the structure until we are finished with initialization. */ +if (inode->i_dquot[cnt] !=NODQUOT ) { +ocfs2_unlock_global_qf (oinfo,0 ); +continue ; + } +/* When we have inode lock, we know that no dquot_release() can +* run and thus we can safely check whether we need to +* read+modify global file to get quota information or whether +* our node already has it. */ +if (cnt ==USRQUOTA ) + id = inode->i_uid; +else if (cnt ==GRPQUOTA ) + id = inode->i_gid; +else +BUG (); +/* Obtain exclusion from quota off... */ +down_write (&sb_dqopt (sb)->dqptr_sem); + exclusive = !dquot_is_cached (sb, id, cnt); +up_write (&sb_dqopt (sb)->dqptr_sem); +if (exclusive) { + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) { + exclusive =0 ; +mlog_errno (status); + goto out_ilock; + } + handle =ocfs2_start_trans (OCFS2_SB (sb), +ocfs2_calc_qinit_credits (sb, cnt)); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto out_ilock; + } + } +dquot_initialize (inode, cnt); +if (exclusive) { +ocfs2_commit_trans (OCFS2_SB (sb), handle); +ocfs2_unlock_global_qf (oinfo,1 ); + } +ocfs2_unlock_global_qf (oinfo,0 ); + } +mlog_exit (0 ); +return 0 ; +out_ilock: +if (exclusive) +ocfs2_unlock_global_qf (oinfo,1 ); +ocfs2_unlock_global_qf (oinfo,0 ); +out: +mlog_exit (status); +return status; +} + +static int ocfs2_dquot_drop_slow (struct inode *inode) +{ +int status; +int cnt; +int got_lock[MAXQUOTAS ] = {0 ,0 }; +handle_t *handle; +struct super_block *sb = inode->i_sb; +struct ocfs2_mem_dqinfo *oinfo; + +for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) { + oinfo =sb_dqinfo (sb, cnt)->dqi_priv; + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) + goto out; + got_lock[cnt] =1 ; + } + handle =ocfs2_start_trans (OCFS2_SB (sb), +ocfs2_calc_qinit_credits (sb,USRQUOTA ) + +ocfs2_calc_qinit_credits (sb,GRPQUOTA )); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto out; + } +dquot_drop (inode); +ocfs2_commit_trans (OCFS2_SB (sb), handle); +out: +for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) +if (got_lock[cnt]) { + oinfo =sb_dqinfo (sb, cnt)->dqi_priv; +ocfs2_unlock_global_qf (oinfo,1 ); + } +return status; +} + +/* See the comment before ocfs2_dquot_initialize. */ +static int ocfs2_dquot_drop (struct inode *inode) +{ +int status =0 ; +struct super_block *sb = inode->i_sb; +struct ocfs2_mem_dqinfo *oinfo; +int exclusive =0 ; +int cnt; +int got_lock[MAXQUOTAS ] = {0 ,0 }; + +mlog_entry_void (); +for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) { + oinfo =sb_dqinfo (sb, cnt)->dqi_priv; + status =ocfs2_lock_global_qf (oinfo,0 ); +if (status <0 ) + goto out; + got_lock[cnt] =1 ; + } +/* Lock against anyone releasing references so that when when we check +* we know we are not going to be last ones to release dquot */ +down_write (&sb_dqopt (sb)->dqptr_sem); +/* Urgh, this is a terrible hack :( */ +for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) { +if (inode->i_dquot[cnt] !=NODQUOT && +atomic_read (&inode->i_dquot[cnt]->dq_count) >1 ) { + exclusive =1 ; +break ; + } + } +if (!exclusive) +dquot_drop_locked (inode); +up_write (&sb_dqopt (sb)->dqptr_sem); +out: +for (cnt =0 ; cnt <MAXQUOTAS ; cnt++) +if (got_lock[cnt]) { + oinfo =sb_dqinfo (sb, cnt)->dqi_priv; +ocfs2_unlock_global_qf (oinfo,0 ); + } +/* In case we bailed out because we had to do expensive locking +* do it now... */ +if (exclusive) + status =ocfs2_dquot_drop_slow (inode); +mlog_exit (status); +return status; +} + +static struct dquot *ocfs2_alloc_dquot (struct super_block *sb,int type) +{ +struct ocfs2_dquot *dquot = +kmem_cache_zalloc (ocfs2_dquot_cachep,GFP_NOFS ); + +if (!dquot) +return NULL ; +return &dquot->dq_dquot; +} + +static void ocfs2_destroy_dquot (struct dquot *dquot) +{ +kmem_cache_free (ocfs2_dquot_cachep, dquot); +} + +struct dquot_operations ocfs2_quota_operations = { + .initialize = ocfs2_dquot_initialize, + .drop = ocfs2_dquot_drop, + .alloc_space = dquot_alloc_space, + .alloc_inode = dquot_alloc_inode, + .free_space = dquot_free_space, + .free_inode = dquot_free_inode, + .transfer = dquot_transfer, + .write_dquot = ocfs2_write_dquot, + .acquire_dquot = ocfs2_acquire_dquot, + .release_dquot = ocfs2_release_dquot, + .mark_dirty = ocfs2_mark_dquot_dirty, + .write_info = ocfs2_write_info, + .alloc_dquot = ocfs2_alloc_dquot, + .destroy_dquot = ocfs2_destroy_dquot, +};fs/ocfs2/quota_local.c new file +833
-0@@ -0,0 +1,833 @@ +/* +* Implementation of operations over local quota file +*/ + +#include <linux/fs.h> +#include <linux/quota.h> +#include <linux/quotaops.h> +#include <linux/module.h> + +#define MLOG_MASK_PREFIX ML_QUOTA +#include <cluster/masklog.h> + +#include "ocfs2_fs.h" +#include "ocfs2.h" +#include "inode.h" +#include "alloc.h" +#include "file.h" +#include "buffer_head_io.h" +#include "journal.h" +#include "sysfile.h" +#include "dlmglue.h" +#include "quota.h" + +/* Number of local quota structures per block */ +static inline unsigned int ol_quota_entries_per_block (struct super_block *sb) +{ +return ((sb->s_blocksize -OCFS2_QBLK_RESERVED_SPACE ) / +sizeof (struct ocfs2_local_disk_dqblk )); +} + +/* Number of blocks with entries in one chunk */ +static inline unsigned int ol_chunk_blocks (struct super_block *sb) +{ +return ((sb->s_blocksize -sizeof (struct ocfs2_local_disk_chunk ) - +OCFS2_QBLK_RESERVED_SPACE ) <<3 ) / +ol_quota_entries_per_block (sb); +} + +/* Number of entries in a chunk bitmap */ +static unsigned int ol_chunk_entries (struct super_block *sb) +{ +return ol_chunk_blocks (sb) *ol_quota_entries_per_block (sb); +} + +/* Offset of the chunk in quota file */ +static unsigned int ol_quota_chunk_block (struct super_block *sb,int c) +{ +/* 1 block for local quota file info, 1 block per chunk for chunk info */ +return 1 + (ol_chunk_blocks (sb) +1 ) * c; +} + +/* Offset of the dquot structure in the quota file */ +static loff_t ol_dqblk_off (struct super_block *sb,int c,int off) +{ +int epb =ol_quota_entries_per_block (sb); + +return ((ol_quota_chunk_block (sb, c) +1 + off / epb) + << sb->s_blocksize_bits) + + (off % epb) *sizeof (struct ocfs2_local_disk_dqblk ); +} + +/* Compute block number from given offset */ +static inline unsigned int ol_dqblk_file_block (struct super_block *sb,loff_t off) +{ +return off >> sb->s_blocksize_bits; +} + +static inline unsigned int ol_dqblk_block_offset (struct super_block *sb,loff_t off) +{ +return off & ((1 << sb->s_blocksize_bits) -1 ); +} + +/* Compute offset in the chunk of a structure with the given offset */ +static int ol_dqblk_chunk_off (struct super_block *sb,int c,loff_t off) +{ +int epb =ol_quota_entries_per_block (sb); + +return ((off >> sb->s_blocksize_bits) - +ol_quota_chunk_block (sb, c) -1 ) * epb + + ((unsigned int )(off & ((1 << sb->s_blocksize_bits) -1 ))) / +sizeof (struct ocfs2_local_disk_dqblk ); +} + +/* Write bufferhead into the fs */ +static int ocfs2_modify_bh (struct inode *inode,struct buffer_head *bh, +void (*modify)(struct buffer_head *,void *),void *private) +{ +struct super_block *sb = inode->i_sb; +handle_t *handle; +int status; + + handle =ocfs2_start_trans (OCFS2_SB (sb),1 ); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); +return status; + } + status =ocfs2_journal_access (handle, inode, bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (status <0 ) { +mlog_errno (status); +ocfs2_commit_trans (OCFS2_SB (sb), handle); +return status; + } +lock_buffer (bh); +modify (bh, private); +unlock_buffer (bh); + status =ocfs2_journal_dirty (handle, bh); +if (status <0 ) { +mlog_errno (status); +ocfs2_commit_trans (OCFS2_SB (sb), handle); +return status; + } + status =ocfs2_commit_trans (OCFS2_SB (sb), handle); +if (status <0 ) { +mlog_errno (status); +return status; + } +return 0 ; +} + +/* Check whether we understand format of quota files */ +static int ocfs2_local_check_quota_file (struct super_block *sb,int type) +{ +unsigned int lmagics[MAXQUOTAS ] =OCFS2_LOCAL_QMAGICS ; +unsigned int lversions[MAXQUOTAS ] =OCFS2_LOCAL_QVERSIONS ; +unsigned int gmagics[MAXQUOTAS ] =OCFS2_GLOBAL_QMAGICS ; +unsigned int gversions[MAXQUOTAS ] =OCFS2_GLOBAL_QVERSIONS ; +unsigned int ino[MAXQUOTAS ] = {USER_QUOTA_SYSTEM_INODE , +GROUP_QUOTA_SYSTEM_INODE }; +struct buffer_head *bh; +struct inode *linode =sb_dqopt (sb)->files[type]; +struct inode *ginode =NULL ; +struct ocfs2_disk_dqheader *dqhead; +int status, ret =0 ; + +/* First check whether we understand local quota file */ + bh =ocfs2_read_quota_block (linode,0 , &status); +if (!bh) { +mlog_errno (status); +mlog (ML_ERROR ,"failed to read quota file header (type=%d)\n" , + type); + goto out_err; + } + dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data); +if (le32_to_cpu (dqhead->dqh_magic) != lmagics[type]) { +mlog (ML_ERROR ,"quota file magic does not match (%u != %u)," +" type=%d\n" ,le32_to_cpu (dqhead->dqh_magic), + lmagics[type], type); + goto out_err; + } +if (le32_to_cpu (dqhead->dqh_version) != lversions[type]) { +mlog (ML_ERROR ,"quota file version does not match (%u != %u)," +" type=%d\n" ,le32_to_cpu (dqhead->dqh_version), + lversions[type], type); + goto out_err; + } +brelse (bh); + bh =NULL ; + +/* Next check whether we understand global quota file */ + ginode =ocfs2_get_system_file_inode (OCFS2_SB (sb), ino[type], +OCFS2_INVALID_SLOT ); +if (!ginode) { +mlog (ML_ERROR ,"cannot get global quota file inode " +"(type=%d)\n" , type); + goto out_err; + } +/* Since the header is read only, we don't care about locking */ + bh =ocfs2_read_quota_block (ginode,0 , &status); +if (!bh) { +mlog_errno (status); +mlog (ML_ERROR ,"failed to read global quota file header " +"(type=%d)\n" , type); + goto out_err; + } + dqhead = (struct ocfs2_disk_dqheader *)(bh->b_data); +if (le32_to_cpu (dqhead->dqh_magic) != gmagics[type]) { +mlog (ML_ERROR ,"global quota file magic does not match " +"(%u != %u), type=%d\n" , +le32_to_cpu (dqhead->dqh_magic), gmagics[type], type); + goto out_err; + } +if (le32_to_cpu (dqhead->dqh_version) != gversions[type]) { +mlog (ML_ERROR ,"global quota file version does not match " +"(%u != %u), type=%d\n" , +le32_to_cpu (dqhead->dqh_version), gversions[type], + type); + goto out_err; + } + + ret =1 ; +out_err: +brelse (bh); +iput (ginode); +return ret; +} + +/* Release given list of quota file chunks */ +static void ocfs2_release_local_quota_bitmaps (struct list_head *head) +{ +struct ocfs2_quota_chunk *pos, *next; + +list_for_each_entry_safe (pos, next, head, qc_chunk) { +list_del (&pos->qc_chunk); +brelse (pos->qc_headerbh); +kmem_cache_free (ocfs2_qf_chunk_cachep, pos); + } +} + +/* Load quota bitmaps into memory */ +static int ocfs2_load_local_quota_bitmaps (struct inode *inode, +struct ocfs2_local_disk_dqinfo *ldinfo, +struct list_head *head) +{ +struct ocfs2_quota_chunk *newchunk; +int i, status; + +INIT_LIST_HEAD (head); +for (i =0 ; i <le32_to_cpu (ldinfo->dqi_chunks); i++) { + newchunk =kmem_cache_alloc (ocfs2_qf_chunk_cachep,GFP_NOFS ); +if (!newchunk) { +ocfs2_release_local_quota_bitmaps (head); +return -ENOMEM ; + } + newchunk->qc_num = i; + newchunk->qc_headerbh =ocfs2_read_quota_block (inode, +ol_quota_chunk_block (inode->i_sb, i), + &status); +if (!newchunk->qc_headerbh) { +mlog_errno (status); +kmem_cache_free (ocfs2_qf_chunk_cachep, newchunk); +ocfs2_release_local_quota_bitmaps (head); +return status; + } +list_add_tail (&newchunk->qc_chunk, head); + } +return 0 ; +} + +static void olq_update_info (struct buffer_head *bh,void *private) +{ +struct mem_dqinfo *info = private; +struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; +struct ocfs2_local_disk_dqinfo *ldinfo; + + ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + +OCFS2_LOCAL_INFO_OFF ); +spin_lock (&dq_data_lock); + ldinfo->dqi_flags =cpu_to_le32 (info->dqi_flags &DQF_MASK ); + ldinfo->dqi_chunks =cpu_to_le32 (oinfo->dqi_chunks); + ldinfo->dqi_blocks =cpu_to_le32 (oinfo->dqi_blocks); +spin_unlock (&dq_data_lock); +} + +/* Read information header from quota file */ +static int ocfs2_local_read_info (struct super_block *sb,int type) +{ +struct ocfs2_local_disk_dqinfo *ldinfo; +struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct ocfs2_mem_dqinfo *oinfo; +struct inode *lqinode =sb_dqopt (sb)->files[type]; +int status; +struct buffer_head *bh =NULL ; +int locked =0 ; + + info->dqi_maxblimit =0x7fffffffffffffffLL ; + info->dqi_maxilimit =0x7fffffffffffffffLL ; + oinfo =kmalloc (sizeof (struct ocfs2_mem_dqinfo ),GFP_NOFS ); +if (!oinfo) { +mlog (ML_ERROR ,"failed to allocate memory for ocfs2 quota" +" info." ); + goto out_err; + } + info->dqi_priv = oinfo; + oinfo->dqi_type = type; +INIT_LIST_HEAD (&oinfo->dqi_chunk); + oinfo->dqi_lqi_bh =NULL ; + oinfo->dqi_ibh =NULL ; + + status =ocfs2_global_read_info (sb, type); +if (status <0 ) + goto out_err; + + status =ocfs2_inode_lock (lqinode, &oinfo->dqi_lqi_bh,1 ); +if (status <0 ) { +mlog_errno (status); + goto out_err; + } + locked =1 ; + +/* Now read local header */ + bh =ocfs2_read_quota_block (lqinode,0 , &status); +if (!bh) { +mlog_errno (status); +mlog (ML_ERROR ,"failed to read quota file info header " +"(type=%d)\n" , type); + goto out_err; + } + ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + +OCFS2_LOCAL_INFO_OFF ); + info->dqi_flags =le32_to_cpu (ldinfo->dqi_flags); + oinfo->dqi_chunks =le32_to_cpu (ldinfo->dqi_chunks); + oinfo->dqi_blocks =le32_to_cpu (ldinfo->dqi_blocks); + oinfo->dqi_ibh = bh; + +/* We crashed when using local quota file? */ +if (!(info->dqi_flags &OLQF_CLEAN )) + goto out_err;/* So far we just bail out. Later we should resync here */ + + status =ocfs2_load_local_quota_bitmaps (sb_dqopt (sb)->files[type], + ldinfo, + &oinfo->dqi_chunk); +if (status <0 ) { +mlog_errno (status); + goto out_err; + } + +/* Now mark quota file as used */ + info->dqi_flags &= ~OLQF_CLEAN ; + status =ocfs2_modify_bh (lqinode, bh, olq_update_info, info); +if (status <0 ) { +mlog_errno (status); + goto out_err; + } + +return 0 ; +out_err: +if (oinfo) { +iput (oinfo->dqi_gqinode); +ocfs2_simple_drop_lockres (OCFS2_SB (sb), &oinfo->dqi_gqlock); +ocfs2_lock_res_free (&oinfo->dqi_gqlock); +brelse (oinfo->dqi_lqi_bh); +if (locked) +ocfs2_inode_unlock (lqinode,1 ); +ocfs2_release_local_quota_bitmaps (&oinfo->dqi_chunk); +kfree (oinfo); + } +brelse (bh); +return -1 ; +} + +/* Write local info to quota file */ +static int ocfs2_local_write_info (struct super_block *sb,int type) +{ +struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct buffer_head *bh = ((struct ocfs2_mem_dqinfo *)info->dqi_priv) + ->dqi_ibh; +int status; + + status =ocfs2_modify_bh (sb_dqopt (sb)->files[type], bh, olq_update_info, + info); +if (status <0 ) { +mlog_errno (status); +return -1 ; + } + +return 0 ; +} + +/* Release info from memory */ +static int ocfs2_local_free_info (struct super_block *sb,int type) +{ +struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; +struct ocfs2_quota_chunk *chunk; +struct ocfs2_local_disk_chunk *dchunk; +int mark_clean =1 , len; +int status; + +iput (oinfo->dqi_gqinode); +ocfs2_simple_drop_lockres (OCFS2_SB (sb), &oinfo->dqi_gqlock); +ocfs2_lock_res_free (&oinfo->dqi_gqlock); +list_for_each_entry (chunk, &oinfo->dqi_chunk, qc_chunk) { + dchunk = (struct ocfs2_local_disk_chunk *) + (chunk->qc_headerbh->b_data); +if (chunk->qc_num < oinfo->dqi_chunks -1 ) { + len =ol_chunk_entries (sb); + }else { + len = (oinfo->dqi_blocks - +ol_quota_chunk_block (sb, chunk->qc_num) -1 ) + *ol_quota_entries_per_block (sb); + } +/* Not all entries free? Bug! */ +if (le32_to_cpu (dchunk->dqc_free) != len) { +mlog (ML_ERROR ,"releasing quota file with used " +"entries (type=%d)\n" , type); + mark_clean =0 ; + } + } +ocfs2_release_local_quota_bitmaps (&oinfo->dqi_chunk); + +if (!mark_clean) + goto out; + +/* Mark local file as clean */ + info->dqi_flags |=OLQF_CLEAN ; + status =ocfs2_modify_bh (sb_dqopt (sb)->files[type], + oinfo->dqi_ibh, + olq_update_info, + info); +if (status <0 ) { +mlog_errno (status); + goto out; + } + +out: +ocfs2_inode_unlock (sb_dqopt (sb)->files[type],1 ); +brelse (oinfo->dqi_ibh); +brelse (oinfo->dqi_lqi_bh); +kfree (oinfo); +return 0 ; +} + +static void olq_set_dquot (struct buffer_head *bh,void *private) +{ +struct ocfs2_dquot *od = private; +struct ocfs2_local_disk_dqblk *dqblk; +struct super_block *sb = od->dq_dquot.dq_sb; + + dqblk = (struct ocfs2_local_disk_dqblk *)(bh->b_data + +ol_dqblk_block_offset (sb, od->dq_local_off)); + + dqblk->dqb_id =cpu_to_le64 (od->dq_dquot.dq_id); +spin_lock (&dq_data_lock); + dqblk->dqb_spacemod =cpu_to_le64 (od->dq_dquot.dq_dqb.dqb_curspace - + od->dq_origspace); + dqblk->dqb_inodemod =cpu_to_le64 (od->dq_dquot.dq_dqb.dqb_curinodes - + od->dq_originodes); +spin_unlock (&dq_data_lock); +mlog (0 ,"Writing local dquot %u space %lld inodes %lld\n" , + od->dq_dquot.dq_id, dqblk->dqb_spacemod, dqblk->dqb_inodemod); +} + +/* Write dquot to local quota file */ +static int ocfs2_local_write_dquot (struct dquot *dquot) +{ +struct super_block *sb = dquot->dq_sb; +struct ocfs2_dquot *od =OCFS2_DQUOT (dquot); +struct buffer_head *bh; +int status; + + bh =ocfs2_read_quota_block (sb_dqopt (sb)->files[dquot->dq_type], +ol_dqblk_file_block (sb, od->dq_local_off), + &status); +if (!bh) { +mlog_errno (status); + goto out; + } + status =ocfs2_modify_bh (sb_dqopt (sb)->files[dquot->dq_type], bh, + olq_set_dquot, od); +if (status <0 ) { +mlog_errno (status); + goto out; + } +out: +brelse (bh); +return status; +} + +/* Find free entry in local quota file */ +static struct ocfs2_quota_chunk *ocfs2_find_free_entry (struct super_block *sb, +int type, +int *offset) +{ +struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; +struct ocfs2_quota_chunk *chunk; +struct ocfs2_local_disk_chunk *dchunk; +int found =0 , len; + +list_for_each_entry (chunk, &oinfo->dqi_chunk, qc_chunk) { + dchunk = (struct ocfs2_local_disk_chunk *) + chunk->qc_headerbh->b_data; +if (le32_to_cpu (dchunk->dqc_free) >0 ) { + found =1 ; +break ; + } + } +if (!found) +return NULL ; + +if (chunk->qc_num < oinfo->dqi_chunks -1 ) { + len =ol_chunk_entries (sb); + }else { + len = (oinfo->dqi_blocks - +ol_quota_chunk_block (sb, chunk->qc_num) -1 ) + *ol_quota_entries_per_block (sb); + } + + found =ocfs2_find_next_zero_bit (dchunk->dqc_bitmap, len,0 ); +/* We failed? */ +if (found == len) { +mlog (ML_ERROR ,"Did not find empty entry in chunk %d with %u" +" entries free (type=%d)\n" , chunk->qc_num, +le32_to_cpu (dchunk->dqc_free), type); +return ERR_PTR (-EIO ); + } + *offset = found; +return chunk; +} + +/* Add new chunk to the local quota file */ +static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk ( +struct super_block *sb, +int type, +int *offset) +{ +struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; +struct inode *lqinode =sb_dqopt (sb)->files[type]; +struct ocfs2_quota_chunk *chunk =NULL ; +struct ocfs2_local_disk_chunk *dchunk; +int status; +handle_t *handle; +struct buffer_head *bh =NULL ; +u64 p_blkno; + +/* We are protected by dqio_sem so no locking needed */ + status =ocfs2_extend_no_holes (lqinode, + lqinode->i_size +2 * sb->s_blocksize, + lqinode->i_size); +if (status <0 ) { +mlog_errno (status); + goto out; + } + status =ocfs2_simple_size_update (lqinode, oinfo->dqi_lqi_bh, + lqinode->i_size +2 * sb->s_blocksize); +if (status <0 ) { +mlog_errno (status); + goto out; + } + + chunk =kmem_cache_alloc (ocfs2_qf_chunk_cachep,GFP_NOFS ); +if (!chunk) { + status = -ENOMEM ; +mlog_errno (status); + goto out; + } + +down_read (&OCFS2_I (lqinode)->ip_alloc_sem); + status =ocfs2_extent_map_get_blocks (lqinode, oinfo->dqi_blocks, + &p_blkno,NULL ,NULL ); +up_read (&OCFS2_I (lqinode)->ip_alloc_sem); +if (status <0 ) { +mlog_errno (status); + goto out; + } + bh =sb_getblk (sb, p_blkno); +if (!bh) { + status = -ENOMEM ; +mlog_errno (status); + goto out; + } + dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data; + + handle =ocfs2_start_trans (OCFS2_SB (sb),2 ); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto out; + } + + status =ocfs2_journal_access (handle, lqinode, bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (status <0 ) { +mlog_errno (status); + goto out_trans; + } +lock_buffer (bh); + dchunk->dqc_free =ol_quota_entries_per_block (sb); +memset (dchunk->dqc_bitmap,0 , + sb->s_blocksize -sizeof (struct ocfs2_local_disk_chunk ) - +OCFS2_QBLK_RESERVED_SPACE ); +set_buffer_uptodate (bh); +unlock_buffer (bh); + status =ocfs2_journal_dirty (handle, bh); +if (status <0 ) { +mlog_errno (status); + goto out_trans; + } + + oinfo->dqi_blocks +=2 ; + oinfo->dqi_chunks++; + status =ocfs2_local_write_info (sb, type); +if (status <0 ) { +mlog_errno (status); + goto out_trans; + } + status =ocfs2_commit_trans (OCFS2_SB (sb), handle); +if (status <0 ) { +mlog_errno (status); + goto out; + } + +list_add_tail (&chunk->qc_chunk, &oinfo->dqi_chunk); + chunk->qc_num =list_entry (chunk->qc_chunk.prev, + struct ocfs2_quota_chunk, + qc_chunk)->qc_num +1 ; + chunk->qc_headerbh = bh; + *offset =0 ; +return chunk; +out_trans: +ocfs2_commit_trans (OCFS2_SB (sb), handle); +out: +brelse (bh); +kmem_cache_free (ocfs2_qf_chunk_cachep, chunk); +return ERR_PTR (status); +} + +/* Find free entry in local quota file */ +static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file ( +struct super_block *sb, +int type, +int *offset) +{ +struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; +struct ocfs2_quota_chunk *chunk; +struct inode *lqinode =sb_dqopt (sb)->files[type]; +struct ocfs2_local_disk_chunk *dchunk; +int epb =ol_quota_entries_per_block (sb); +unsigned int chunk_blocks; +int status; +handle_t *handle; + +if (list_empty (&oinfo->dqi_chunk)) +return ocfs2_local_quota_add_chunk (sb, type, offset); +/* Is the last chunk full? */ + chunk =list_entry (oinfo->dqi_chunk.prev, + struct ocfs2_quota_chunk, qc_chunk); + chunk_blocks = oinfo->dqi_blocks - +ol_quota_chunk_block (sb, chunk->qc_num) -1 ; +if (ol_chunk_blocks (sb) == chunk_blocks) +return ocfs2_local_quota_add_chunk (sb, type, offset); + +/* We are protected by dqio_sem so no locking needed */ + status =ocfs2_extend_no_holes (lqinode, + lqinode->i_size + sb->s_blocksize, + lqinode->i_size); +if (status <0 ) { +mlog_errno (status); + goto out; + } + status =ocfs2_simple_size_update (lqinode, oinfo->dqi_lqi_bh, + lqinode->i_size + sb->s_blocksize); +if (status <0 ) { +mlog_errno (status); + goto out; + } + handle =ocfs2_start_trans (OCFS2_SB (sb),2 ); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto out; + } + status =ocfs2_journal_access (handle, lqinode, chunk->qc_headerbh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (status <0 ) { +mlog_errno (status); + goto out_trans; + } + + dchunk = (struct ocfs2_local_disk_chunk *)chunk->qc_headerbh->b_data; +lock_buffer (chunk->qc_headerbh); +le32_add_cpu (&dchunk->dqc_free,ol_quota_entries_per_block (sb)); +unlock_buffer (chunk->qc_headerbh); + status =ocfs2_journal_dirty (handle, chunk->qc_headerbh); +if (status <0 ) { +mlog_errno (status); + goto out_trans; + } + oinfo->dqi_blocks++; + status =ocfs2_local_write_info (sb, type); +if (status <0 ) { +mlog_errno (status); + goto out_trans; + } + + status =ocfs2_commit_trans (OCFS2_SB (sb), handle); +if (status <0 ) { +mlog_errno (status); + goto out; + } + *offset = chunk_blocks * epb; +return chunk; +out_trans: +ocfs2_commit_trans (OCFS2_SB (sb), handle); +out: +return ERR_PTR (status); +} + +void olq_alloc_dquot (struct buffer_head *bh,void *private) +{ +int *offset = private; +struct ocfs2_local_disk_chunk *dchunk; + + dchunk = (struct ocfs2_local_disk_chunk *)bh->b_data; +ocfs2_set_bit (*offset, dchunk->dqc_bitmap); +le32_add_cpu (&dchunk->dqc_free,-1 ); +} + +/* Create dquot in the local file for given id */ +static int ocfs2_create_local_dquot (struct dquot *dquot) +{ +struct super_block *sb = dquot->dq_sb; +int type = dquot->dq_type; +struct inode *lqinode =sb_dqopt (sb)->files[type]; +struct ocfs2_quota_chunk *chunk; +struct ocfs2_dquot *od =OCFS2_DQUOT (dquot); +int offset; +int status; + + chunk =ocfs2_find_free_entry (sb, type, &offset); +if (!chunk) { + chunk =ocfs2_extend_local_quota_file (sb, type, &offset); +if (IS_ERR (chunk)) +return PTR_ERR (chunk); + }else if (IS_ERR (chunk)) { +return PTR_ERR (chunk); + } + od->dq_local_off =ol_dqblk_off (sb, chunk->qc_num, offset); + od->dq_chunk = chunk; + +/* Initialize dquot structure on disk */ + status =ocfs2_local_write_dquot (dquot); +if (status <0 ) { +mlog_errno (status); + goto out; + } + +/* Mark structure as allocated */ + status =ocfs2_modify_bh (lqinode, chunk->qc_headerbh, olq_alloc_dquot, + &offset); +if (status <0 ) { +mlog_errno (status); + goto out; + } +out: +return status; +} + +/* Create entry in local file for dquot, load data from the global file */ +static int ocfs2_local_read_dquot (struct dquot *dquot) +{ +int status; + +mlog_entry ("id=%u, type=%d\n" , dquot->dq_id, dquot->dq_type); + + status =ocfs2_global_read_dquot (dquot); +if (status <0 ) { +mlog_errno (status); + goto out_err; + } + +/* Now create entry in the local quota file */ + status =ocfs2_create_local_dquot (dquot); +if (status <0 ) { +mlog_errno (status); + goto out_err; + } +mlog_exit (0 ); +return 0 ; +out_err: +mlog_exit (status); +return status; +} + +/* Release dquot structure from local quota file. ocfs2_release_dquot() has +* already started a transaction and obtained exclusive lock for global +* quota file. */ +static int ocfs2_local_release_dquot (struct dquot *dquot) +{ +int status; +int type = dquot->dq_type; +struct ocfs2_dquot *od =OCFS2_DQUOT (dquot); +struct super_block *sb = dquot->dq_sb; +struct ocfs2_local_disk_chunk *dchunk; +int offset; +handle_t *handle =journal_current_handle (); + +BUG_ON (!handle); +/* First write all local changes to global file */ + status =ocfs2_global_release_dquot (dquot); +if (status <0 ) { +mlog_errno (status); + goto out; + } + + status =ocfs2_journal_access (handle,sb_dqopt (sb)->files[type], + od->dq_chunk->qc_headerbh,OCFS2_JOURNAL_ACCESS_WRITE ); +if (status <0 ) { +mlog_errno (status); + goto out; + } + offset =ol_dqblk_chunk_off (sb, od->dq_chunk->qc_num, + od->dq_local_off); + dchunk = (struct ocfs2_local_disk_chunk *) + (od->dq_chunk->qc_headerbh->b_data); +/* Mark structure as freed */ +lock_buffer (od->dq_chunk->qc_headerbh); +ocfs2_clear_bit (offset, dchunk->dqc_bitmap); +le32_add_cpu (&dchunk->dqc_free,1 ); +unlock_buffer (od->dq_chunk->qc_headerbh); + status =ocfs2_journal_dirty (handle, od->dq_chunk->qc_headerbh); +if (status <0 ) { +mlog_errno (status); + goto out; + } + status =0 ; +out: +/* Clear the read bit so that next time someone uses this +* dquot he reads fresh info from disk and allocates local +* dquot structure */ +clear_bit (DQ_READ_B , &dquot->dq_flags); +return status; +} + +static struct quota_format_ops ocfs2_format_ops = { + .check_quota_file = ocfs2_local_check_quota_file, + .read_file_info = ocfs2_local_read_info, + .write_file_info = ocfs2_global_write_info, + .free_file_info = ocfs2_local_free_info, + .read_dqblk = ocfs2_local_read_dquot, + .commit_dqblk = ocfs2_local_write_dquot, + .release_dqblk = ocfs2_local_release_dquot, +}; + +struct quota_format_type ocfs2_quota_format = { + .qf_fmt_id =QFMT_OCFS2 , + .qf_ops = &ocfs2_format_ops, + .qf_owner =THIS_MODULE +};fs/ocfs2/super.c +36
-2@@ -65,10 +65,13 @@#include "uptodate.h" #include "ver.h" #include "xattr.h" +#include "quota.h" #include "buffer_head_io.h" static struct kmem_cache *ocfs2_inode_cachep =NULL ; +struct kmem_cache *ocfs2_dquot_cachep; +struct kmem_cache *ocfs2_qf_chunk_cachep;/* OCFS2 needs to schedule several differnt types of work which * require cluster locking, disk I/O, recovery waits, etc. Since these @@ -137,6 +140,8 @@ static const struct super_operations ocfs2_sops = { .put_super = ocfs2_put_super, .remount_fs = ocfs2_remount, .show_options = ocfs2_show_options, + .quota_read = ocfs2_quota_read, + .quota_write = ocfs2_quota_write, };enum {@@ -1071,6 +1076,7 @@ static int __init ocfs2_init(void)ocfs2_set_locking_protocol (); + status =register_quota_format (&ocfs2_quota_format); leave:if (status <0 ) {ocfs2_free_mem_caches ();@@ -1094,6 +1100,8 @@ static void __exit ocfs2_exit(void)destroy_workqueue (ocfs2_wq); } +unregister_quota_format (&ocfs2_quota_format); +debugfs_remove (ocfs2_debugfs_root);ocfs2_free_mem_caches ();@@ -1209,8 +1217,27 @@ static int ocfs2_initialize_mem_caches(void) (SLAB_HWCACHE_ALIGN |SLAB_RECLAIM_ACCOUNT |SLAB_MEM_SPREAD ), ocfs2_inode_init_once); -if (!ocfs2_inode_cachep) + ocfs2_dquot_cachep =kmem_cache_create ("ocfs2_dquot_cache" , +sizeof (struct ocfs2_dquot ), +0 , + (SLAB_HWCACHE_ALIGN |SLAB_RECLAIM_ACCOUNT | +SLAB_MEM_SPREAD ), +NULL ); + ocfs2_qf_chunk_cachep =kmem_cache_create ("ocfs2_qf_chunk_cache" , +sizeof (struct ocfs2_quota_chunk ), +0 , + (SLAB_RECLAIM_ACCOUNT |SLAB_MEM_SPREAD ), +NULL ); +if (!ocfs2_inode_cachep || !ocfs2_dquot_cachep || + !ocfs2_qf_chunk_cachep) { +if (ocfs2_inode_cachep) +kmem_cache_destroy (ocfs2_inode_cachep); +if (ocfs2_dquot_cachep) +kmem_cache_destroy (ocfs2_dquot_cachep); +if (ocfs2_qf_chunk_cachep) +kmem_cache_destroy (ocfs2_qf_chunk_cachep);return -ENOMEM ; + }return 0 ; }@@ -1219,8 +1246,15 @@ static void ocfs2_free_mem_caches(void) {if (ocfs2_inode_cachep)kmem_cache_destroy (ocfs2_inode_cachep); - ocfs2_inode_cachep =NULL ; + +if (ocfs2_dquot_cachep) +kmem_cache_destroy (ocfs2_dquot_cachep); + ocfs2_dquot_cachep =NULL ; + +if (ocfs2_qf_chunk_cachep) +kmem_cache_destroy (ocfs2_qf_chunk_cachep); + ocfs2_qf_chunk_cachep =NULL ; } static int ocfs2_get_sector(struct super_block *sb,Signature · 7 lines
1.5.2.4 _______________________________________________ Ocfs2-devel mailing list Ocfs2-devel at oss.oracle.com http://oss.oracle.com/mailman/listinfo/ocfs2-devel
Signature · 10 lines
"Born under a bad sign. I been down since I began to crawl. If it wasn't for bad luck, I wouldn't have no luck at all." Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127
PATCH 25/29 ocfs2: Implementation of local and global quota file handling
In reply to Jan KaraQuoted 35 lines · Jan Kara
For each quota type each node has local quota file. In this file it stores changes users have made to disk usage via this node. Once in a while this information is synced to global file (and thus with other nodes) so that limits enforcement at least aproximately works.
fs/ocfs2/ocfs2_fs.h +23
-0@@ -878,6 +878,101 @@ static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)return xe->xe_type &OCFS2_XATTR_TYPE_MASK ; } +/* +* On disk structures for global quota file +*/ + +/* Magic numbers and known versions for global quota files */ +#define OCFS2_GLOBAL_QMAGICS {\ + 0x0cf52470, /* USRQUOTA */ \ + 0x0cf52471 /* GRPQUOTA */ \ +} + +#define OCFS2_GLOBAL_QVERSIONS {\ + 0, \ + 0, \ +} + +/* Generic header of all quota files */ +struct ocfs2_disk_dqheader { +__le32 dqh_magic;/* Magic number identifying file */ +__le32 dqh_version;/* Quota format version */ +}; + +#define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader)) +
fs/ocfs2/ocfs2_fs.h static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe) · line 913
Quoted 10 lines
fs/ocfs2/ocfs2_fs.h +10
-0+/* Information header of global quota file (immediately follows the generic +* header) */ +struct ocfs2_global_disk_dqinfo { +/*00*/ __le32 dqi_bgrace; +__le32 dqi_igrace; +__le32 dqi_syncms; +__le32 dqi_blocks; +/*10*/ __le32 dqi_free_blk; +__le32 dqi_free_entry; +};
The way I read this, the first block of a quota file contains
first the disk_dqheader, and then the local info (global_disk_dqinfo,local_disk_dqinfo). Is that right? I'd love to see the ecc field (__le64 for now) in the disk_dqheader.
Quoted 6 lines
fs/ocfs2/ocfs2_fs.h +6
-0+/* Structure with global user / group information. We reserve some space +* for future use. */ +struct ocfs2_global_disk_dqblk { +/*00*/ __le32 dqb_id;/* ID the structure belongs to */ +__le32 dqb_use_count;/* Number of nodes having reference to this structure */ +__le64 dqb_ihardlimit;/* absolute limit on allocated inodes */
fs/ocfs2/ocfs2_fs.h static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe) · line 930
Quoted 10 lines
fs/ocfs2/ocfs2_fs.h +10
-0+/*10*/ __le64 dqb_isoftlimit;/* preferred inode limit */ +__le64 dqb_curinodes;/* current # allocated inodes */ +/*20*/ __le64 dqb_bhardlimit;/* absolute limit on disk space */ +__le64 dqb_bsoftlimit;/* preferred limit on disk space */ +/*30*/ __le64 dqb_curspace;/* current space occupied */ +__le64 dqb_btime;/* time limit for excessive disk use */ +/*40*/ __le64 dqb_itime;/* time limit for excessive inode use */ +__le64 dqb_pad1; +/*50*/ __le64 dqb_pad2; +};
We also need space for the ecc data here. It looks like ecc
space wasn't added to this series. Rather than comment on each structure, just let me know when that's ready.
Joel
Signature · 9 lines
"I don't want to achieve immortality through my work; I want to
achieve immortality through not dying."
- Woody Allen
Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127Jan Kara
reviewerPATCH 25/29 ocfs2: Implementation of local and global quota file handling
Quoted 43 lines · Joel Becker
Quoted 42 lines · Jan Kara
For each quota type each node has local quota file. In this file it stores changes users have made to disk usage via this node. Once in a while this information is synced to global file (and thus with other nodes) so that limits enforcement at least aproximately works.
fs/ocfs2/ocfs2_fs.h +30
-0@@ -878,6 +878,101 @@ static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)return xe->xe_type &OCFS2_XATTR_TYPE_MASK ; } +/* +* On disk structures for global quota file +*/ + +/* Magic numbers and known versions for global quota files */ +#define OCFS2_GLOBAL_QMAGICS {\ + 0x0cf52470, /* USRQUOTA */ \ + 0x0cf52471 /* GRPQUOTA */ \ +} + +#define OCFS2_GLOBAL_QVERSIONS {\ + 0, \ + 0, \ +} + +/* Generic header of all quota files */ +struct ocfs2_disk_dqheader { +__le32 dqh_magic;/* Magic number identifying file */ +__le32 dqh_version;/* Quota format version */ +}; + +#define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader)) + +/* Information header of global quota file (immediately follows the generic +* header) */ +struct ocfs2_global_disk_dqinfo { +/*00*/ __le32 dqi_bgrace; +__le32 dqi_igrace; +__le32 dqi_syncms; +__le32 dqi_blocks;
Quoted 8 lines
fs/ocfs2/ocfs2_fs.h static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe) · line 913
Quoted 3 lines
fs/ocfs2/ocfs2_fs.h +3
-0+/*10*/ __le32 dqi_free_blk; +__le32 dqi_free_entry; +};The way I read this, the first block of a quota file contains
first thedisk_dqheader, and then the local info (global_disk_dqinfo,local_disk_dqinfo). Is that right? I'd love to see the ecc field (__le64for now) in thedisk_dqheader.
Yes. Actually, this set of patches already counts with ECC, but it's
hidden ;). See below.
Quoted 12 lines
Quoted 12 lines
fs/ocfs2/ocfs2_fs.h +12
-0+/* Structure with global user / group information. We reserve some space +* for future use. */ +struct ocfs2_global_disk_dqblk { +/*00*/ __le32 dqb_id;/* ID the structure belongs to */ +__le32 dqb_use_count;/* Number of nodes having reference to this structure */ +__le64 dqb_ihardlimit;/* absolute limit on allocated inodes */ +/*10*/ __le64 dqb_isoftlimit;/* preferred inode limit */ +__le64 dqb_curinodes;/* current # allocated inodes */ +/*20*/ __le64 dqb_bhardlimit;/* absolute limit on disk space */ +__le64 dqb_bsoftlimit;/* preferred limit on disk space */ +/*30*/ __le64 dqb_curspace;/* current space occupied */ +__le64 dqb_btime;/* time limit for excessive disk use */
Quoted 8 lines
fs/ocfs2/ocfs2_fs.h static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe) · line 930
Quoted 4 lines
fs/ocfs2/ocfs2_fs.h +4
-0+/*40*/ __le64 dqb_itime;/* time limit for excessive inode use */ +__le64 dqb_pad1; +/*50*/ __le64 dqb_pad2; +};We also need space for the ecc data here. It looks like ecc
space wasn't added to this series. Rather than comment on each structure, just let me know when that's ready.
What I did is, that in the end of each quotafile block there are 8 bytes
reserved for OCFS2 use (both in local and global quota files). So these bytes are intended to be used for ECC. I hope that's fine.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
PATCH 25/29 ocfs2: Implementation of local and global quota file handling
Quoted 20 lines · Jan Kara
Quoted 12 lines
Quoted 12 lines
fs/ocfs2/ocfs2_fs.h +12
-0+/* Structure with global user / group information. We reserve some space +* for future use. */ +struct ocfs2_global_disk_dqblk { +/*00*/ __le32 dqb_id;/* ID the structure belongs to */ +__le32 dqb_use_count;/* Number of nodes having reference to this structure */ +__le64 dqb_ihardlimit;/* absolute limit on allocated inodes */ +/*10*/ __le64 dqb_isoftlimit;/* preferred inode limit */ +__le64 dqb_curinodes;/* current # allocated inodes */ +/*20*/ __le64 dqb_bhardlimit;/* absolute limit on disk space */ +__le64 dqb_bsoftlimit;/* preferred limit on disk space */ +/*30*/ __le64 dqb_curspace;/* current space occupied */ +__le64 dqb_btime;/* time limit for excessive disk use */ Quoted 8 lines
fs/ocfs2/ocfs2_fs.h static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe) · line 930
Quoted 4 lines
fs/ocfs2/ocfs2_fs.h +4
-0+/*40*/ __le64 dqb_itime;/* time limit for excessive inode use */ +__le64 dqb_pad1; +/*50*/ __le64 dqb_pad2; +};We also need space for the ecc data here. It looks like ecc
space wasn't added to this series. Rather than comment on each structure, just let me know when that's ready.
Quoted 3 lines
What I did is, that in the end of each quotafile block there are 8 bytes
reserved for OCFS2 use (both in local and global quota files). So these bytes are intended to be used for ECC. I hope that's fine.
So every block type (disk header, dq_block, etc) has the last 8
bytes free? This seems like something we should find a way to mention in the disk header.
Joel
Signature · 9 lines
Bram's Law: The easier a piece of software is to write, the worse it's implemented in practice. Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127
Jan Kara
reviewerPATCH 25/29 ocfs2: Implementation of local and global quota file handling
Quoted 21 lines · Joel Becker
Quoted 20 lines · Jan Kara
Quoted 20 lines
Quoted 6 lines
fs/ocfs2/ocfs2_fs.h +6
-0+/* Structure with global user / group information. We reserve some space +* for future use. */ +struct ocfs2_global_disk_dqblk { +/*00*/ __le32 dqb_id;/* ID the structure belongs to */ +__le32 dqb_use_count;/* Number of nodes having reference to this structure */ +__le64 dqb_ihardlimit;/* absolute limit on allocated inodes */ fs/ocfs2/ocfs2_fs.h static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe) · line 930
Quoted 10 lines
fs/ocfs2/ocfs2_fs.h +10
-0+/*10*/ __le64 dqb_isoftlimit;/* preferred inode limit */ +__le64 dqb_curinodes;/* current # allocated inodes */ +/*20*/ __le64 dqb_bhardlimit;/* absolute limit on disk space */ +__le64 dqb_bsoftlimit;/* preferred limit on disk space */ +/*30*/ __le64 dqb_curspace;/* current space occupied */ +__le64 dqb_btime;/* time limit for excessive disk use */ +/*40*/ __le64 dqb_itime;/* time limit for excessive inode use */ +__le64 dqb_pad1; +/*50*/ __le64 dqb_pad2; +};We also need space for the ecc data here. It looks like ecc
space wasn't added to this series. Rather than comment on each structure, just let me know when that's ready.
Quoted 6 lines
Quoted 3 lines
What I did is, that in the end of each quotafile block there are 8 bytes
reserved for OCFS2 use (both in local and global quota files). So these bytes are intended to be used for ECC. I hope that's fine.So every block type (disk header,
dq_block, etc) has the last 8
bytes free?
Yes.
Quoted 2 lines
This seems like something we should find a way to mention in the disk header.
You mean like in the ocfs2_fs.h file? There is already a constant and
a comment in quota.h, but probably it makes sence to move it to ocfs2_fs.h.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
PATCH 25/29 ocfs2: Implementation of local and global quota file handling
Quoted 6 lines · Jan Kara
Quoted 5 lines · Joel Becker
Quoted 3 lines · Jan Kara
What I did is, that in the end of each quotafile block there are 8 bytes
reserved for OCFS2 use (both in local and global quota files). So these bytes are intended to be used for ECC. I hope that's fine.
Quoted 8 lines
Quoted 2 lines
So every block type (disk header,
dq_block, etc) has the last 8
bytes free?Yes.
Quoted 2 lines
This seems like something we should find a way to mention in the disk header.
You mean like in the
ocfs2_fs.hfile? There is already a constant and
a comment inquota.h, but probably it makes sence to move it toocfs2_fs.h.
Well, if other filesystems get it via quota.h too, it should
stay there. Perhaps just a comment in ocfs2_fs.h about it so readers are aware.
Joel
Signature · 8 lines
The zen have a saying: "When you learn how to listen, ANYONE can be your teacher." Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127
Jan Kara
reviewerPATCH 25/29 ocfs2: Implementation of local and global quota file handling
Quoted 11 lines · Joel Becker
Quoted 10 lines · Jan Kara
Quoted 7 lines · Joel Becker
Quoted 3 lines · Jan Kara
What I did is, that in the end of each quotafile block there are 8 bytes
reserved for OCFS2 use (both in local and global quota files). So these bytes are intended to be used for ECC. I hope that's fine.So every block type (disk header,
dq_block, etc) has the last 8
bytes free?Yes.
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
This seems like something we should find a way to mention in the disk header.
You mean like in the
ocfs2_fs.hfile? There is already a constant and
a comment inquota.h, but probably it makes sence to move it toocfs2_fs.h.Well, if other filesystems get it via
quota.htoo, it should
stay there. Perhaps just a comment inocfs2_fs.habout it so readers are aware.
No - it is OCFS2-specific constant in OCFS2-specific quota header. I've
moved the constant to ocfs2_fs.h since it defines disk format in a way...
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
PATCH 25/29 ocfs2: Implementation of local and global quota file handling
In reply to Jan KaraQuoted 5 lines · Jan Kara
fs/ocfs2/dlmglue.c +1
-0@@ -3450,6 +3485,108 @@ static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres,return UNBLOCK_CONTINUE_POST ; } +static void ocfs2_set_qinfo_lvb (struct ocfs2_lock_res *lockres)
fs/ocfs2/dlmglue.c static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres, · line 3498
Quoted 10 lines
fs/ocfs2/dlmglue.c +10
-0+{ +struct ocfs2_qinfo_lvb *lvb; +struct ocfs2_mem_dqinfo *oinfo =ocfs2_lock_res_qinfo (lockres); +struct mem_dqinfo *info =sb_dqinfo (oinfo->dqi_gi.dqi_sb, + oinfo->dqi_gi.dqi_type); + +mlog_entry_void (); + + lvb = (struct ocfs2_qinfo_lvb *)ocfs2_dlm_lvb (&lockres->l_lksb); + lvb->lvb_version =OCFS2_LVB_VERSION ;
You might want your own 'OCFS2_QINFO_LVB_VERSION' value. That one is misnamed in that it actually only governs meta data lvbs. If we changed the meta data lvb format, we'd want to bump it seperately from the quote lvb format version and vice-versa. We should probably rename OCFS2_LVB_VERSION to OCFS2_INODE_LVB_VERSION at some point too, but don't feel like you have to do that in this series.
Quoted 30 lines
fs/ocfs2/dlmglue.c +30
-0+ lvb->lvb_bgrace =cpu_to_be32 (info->dqi_bgrace); + lvb->lvb_igrace =cpu_to_be32 (info->dqi_igrace); + lvb->lvb_syncms =cpu_to_be32 (oinfo->dqi_syncms); + lvb->lvb_blocks =cpu_to_be32 (oinfo->dqi_gi.dqi_blocks); + lvb->lvb_free_blk =cpu_to_be32 (oinfo->dqi_gi.dqi_free_blk); + lvb->lvb_free_entry =cpu_to_be32 (oinfo->dqi_gi.dqi_free_entry); + +mlog_exit_void (); +} + +void ocfs2_qinfo_unlock (struct ocfs2_mem_dqinfo *oinfo,int ex) +{ +struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock; +struct ocfs2_super *osb =OCFS2_SB (oinfo->dqi_gi.dqi_sb); +int level = ex ?DLM_LOCK_EX :DLM_LOCK_PR ; + +mlog_entry_void (); +if (!ocfs2_is_hard_readonly (osb) && !ocfs2_mount_local (osb)) +ocfs2_cluster_unlock (osb, lockres, level); +mlog_exit_void (); +} + +/* Lock quota info, this function expects at least shared lock on the quota file +* so that we can safely refresh quota info from disk. */ +int ocfs2_qinfo_lock (struct ocfs2_mem_dqinfo *oinfo,int ex) +{ +struct mem_dqinfo *info =sb_dqinfo (oinfo->dqi_gi.dqi_sb, + oinfo->dqi_gi.dqi_type); +struct ocfs2_lock_res *lockres = &oinfo->dqi_gqlock; +struct ocfs2_super *osb =OCFS2_SB (oinfo->dqi_gi.dqi_sb);
fs/ocfs2/dlmglue.c static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres, · line 3538
Quoted 10 lines
fs/ocfs2/dlmglue.c +10
-0+struct ocfs2_qinfo_lvb *lvb; +int level = ex ?DLM_LOCK_EX :DLM_LOCK_PR ; +int status =0 ; +struct buffer_head *bh; +struct ocfs2_global_disk_dqinfo *gdinfo; + +mlog_entry_void (); + +/* We'll allow faking a readonly metadata lock for +* rodevices. */
You might want to update this comment ;)
Quoted 36 lines
fs/ocfs2/dlmglue.c +36
-0+if (ocfs2_is_hard_readonly (osb)) { +if (ex) + status = -EROFS ; + goto bail; + } +if (ocfs2_mount_local (osb)) + goto bail; + + status =ocfs2_cluster_lock (osb, lockres, level,0 ,0 ); +if (status <0 ) { +mlog_errno (status); + goto bail; + } +if (!ocfs2_should_refresh_lock_res (lockres)) + goto bail; +/* OK, we have the lock but we need to refresh the quota info */ + lvb =ocfs2_dlm_lvb (&lockres->l_lksb); +if (lvb->lvb_version ==OCFS2_LVB_VERSION ) { + info->dqi_bgrace =be32_to_cpu (lvb->lvb_bgrace); + info->dqi_igrace =be32_to_cpu (lvb->lvb_igrace); + oinfo->dqi_syncms =be32_to_cpu (lvb->lvb_syncms); + oinfo->dqi_gi.dqi_blocks =be32_to_cpu (lvb->lvb_blocks); + oinfo->dqi_gi.dqi_free_blk =be32_to_cpu (lvb->lvb_free_blk); + oinfo->dqi_gi.dqi_free_entry = +be32_to_cpu (lvb->lvb_free_entry); + }else { + bh =ocfs2_read_quota_block (oinfo->dqi_gqinode,0 , &status); +if (!bh) { +ocfs2_qinfo_unlock (oinfo, ex); +mlog_errno (status); + goto bail_refresh; + } + gdinfo = (struct ocfs2_global_disk_dqinfo *) + (bh->b_data +OCFS2_GLOBAL_INFO_OFF ); + info->dqi_bgrace =le32_to_cpu (gdinfo->dqi_bgrace); + info->dqi_igrace =le32_to_cpu (gdinfo->dqi_igrace);
fs/ocfs2/dlmglue.c static int ocfs2_dentry_convert_worker(struct ocfs2_lock_res *lockres, · line 3584
Quoted 10 lines
fs/ocfs2/dlmglue.c +10
-0+ oinfo->dqi_syncms =le32_to_cpu (gdinfo->dqi_syncms); + oinfo->dqi_gi.dqi_blocks =le32_to_cpu (gdinfo->dqi_blocks); + oinfo->dqi_gi.dqi_free_blk =le32_to_cpu (gdinfo->dqi_free_blk); + oinfo->dqi_gi.dqi_free_entry = +le32_to_cpu (gdinfo->dqi_free_entry); +brelse (bh); +ocfs2_track_lock_refresh (lockres); + } +bail_refresh: +ocfs2_complete_lock_res_refresh (lockres, status);
Can we put the refresh logic in another function, please?
Quoted 31 lines
fs/ocfs2/ocfs2_fs.h +23
-0@@ -878,6 +878,101 @@ static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)return xe->xe_type &OCFS2_XATTR_TYPE_MASK ; } +/* +* On disk structures for global quota file +*/ + +/* Magic numbers and known versions for global quota files */ +#define OCFS2_GLOBAL_QMAGICS {\ + 0x0cf52470, /* USRQUOTA */ \ + 0x0cf52471 /* GRPQUOTA */ \ +} + +#define OCFS2_GLOBAL_QVERSIONS {\ + 0, \ + 0, \ +} + +/* Generic header of all quota files */ +struct ocfs2_disk_dqheader { +__le32 dqh_magic;/* Magic number identifying file */ +__le32 dqh_version;/* Quota format version */ +}; + +#define OCFS2_GLOBAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader)) +
fs/ocfs2/ocfs2_fs.h static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe) · line 913
Quoted 10 lines
fs/ocfs2/ocfs2_fs.h +10
-0+/* Information header of global quota file (immediately follows the generic +* header) */ +struct ocfs2_global_disk_dqinfo { +/*00*/ __le32 dqi_bgrace; +__le32 dqi_igrace; +__le32 dqi_syncms; +__le32 dqi_blocks; +/*10*/ __le32 dqi_free_blk; +__le32 dqi_free_entry; +};
Can we get some comments explaining the fields in this structure? I had to search around to get the idea behind some of them...
Quoted 255 lines
fs/ocfs2/ocfs2_fs.h +61
-0+/* Structure with global user / group information. We reserve some space +* for future use. */ +struct ocfs2_global_disk_dqblk { +/*00*/ __le32 dqb_id;/* ID the structure belongs to */ +__le32 dqb_use_count;/* Number of nodes having reference to this structure */ +__le64 dqb_ihardlimit;/* absolute limit on allocated inodes */ +/*10*/ __le64 dqb_isoftlimit;/* preferred inode limit */ +__le64 dqb_curinodes;/* current # allocated inodes */ +/*20*/ __le64 dqb_bhardlimit;/* absolute limit on disk space */ +__le64 dqb_bsoftlimit;/* preferred limit on disk space */ +/*30*/ __le64 dqb_curspace;/* current space occupied */ +__le64 dqb_btime;/* time limit for excessive disk use */ +/*40*/ __le64 dqb_itime;/* time limit for excessive inode use */ +__le64 dqb_pad1; +/*50*/ __le64 dqb_pad2; +}; + +/* +* On-disk structures for local quota file +*/ + +/* Magic numbers and known versions for local quota files */ +#define OCFS2_LOCAL_QMAGICS {\ + 0x0cf524c0, /* USRQUOTA */ \ + 0x0cf524c1 /* GRPQUOTA */ \ +} + +#define OCFS2_LOCAL_QVERSIONS {\ + 0, \ + 0, \ +} + +/* Quota flags in dqinfo header */ +#define OLQF_CLEAN 0x0001/* Quota file is empty (this should be after\ +* quota has been cleanly turned off) */ + +#define OCFS2_LOCAL_INFO_OFF (sizeof(struct ocfs2_disk_dqheader)) + +/* Information header of local quota file (immediately follows the generic +* header) */ +struct ocfs2_local_disk_dqinfo { +__le32 dqi_flags;/* Flags for quota file */ +__le32 dqi_chunks;/* Number of chunks of quota structures +* with a bitmap */ +__le32 dqi_blocks;/* Number of blocks allocated for quota file */ +}; + +/* Header of one chunk of a quota file */ +struct ocfs2_local_disk_chunk { +__le32 dqc_free;/* Number of free entries in the bitmap */ +u8 dqc_bitmap[0 ];/* Bitmap of entries in the corresponding +* chunk of quota file */ +}; + +/* One entry in local quota file */ +struct ocfs2_local_disk_dqblk { +/*00*/ __le64 dqb_id;/* id this quota applies to */ +__le64 dqb_spacemod;/* Change in the amount of used space */ +/*10*/ __le64 dqb_inodemod;/* Change in the amount of used inodes */ +}; +#ifdef __KERNEL__static inline int ocfs2_fast_symlink_chars (struct super_block *sb) {fs/ocfs2/ocfs2_lockid.h +5
-0@@ -46,6 +46,7 @@ enum ocfs2_lock_type {OCFS2_LOCK_TYPE_DENTRY ,OCFS2_LOCK_TYPE_OPEN ,OCFS2_LOCK_TYPE_FLOCK , +OCFS2_LOCK_TYPE_QINFO , OCFS2_NUM_LOCK_TYPES };@@ -77,6 +78,9 @@ static inline char ocfs2_lock_type_char(enum ocfs2_lock_type type)case OCFS2_LOCK_TYPE_FLOCK : c ='F' ;break ; +case OCFS2_LOCK_TYPE_QINFO : + c ='Q' ; +break ;default : c ='\0' ; }@@ -95,6 +99,7 @@ static char *ocfs2_lock_type_strings[] = { [OCFS2_LOCK_TYPE_DENTRY] = "Dentry", [OCFS2_LOCK_TYPE_OPEN] = "Open", [OCFS2_LOCK_TYPE_FLOCK] = "Flock", + [OCFS2_LOCK_TYPE_QINFO] = "Quota", };static inline const char *ocfs2_lock_type_string (enum ocfs2_lock_type type)fs/ocfs2/quota.h new file +97
-0@@ -0,0 +1,97 @@ +/* +* quota.h for OCFS2 +* +* On disk quota structures for local and global quota file, in-memory +* structures. +* +*/ + +#ifndef _OCFS2_QUOTA_H +#define _OCFS2_QUOTA_H + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/quota.h> +#include <linux/list.h> +#include <linux/dqblk_qtree.h> + +#include "ocfs2.h" + +/* Common stuff */ +/* id number of quota format */ +#define QFMT_OCFS2 3 + +/* How many bytes to we reserve in each quota file block for our internal +* purposes? E.g. checksums... */ +#define OCFS2_QBLK_RESERVED_SPACE 8 + +/* +* In-memory structures +*/ +struct ocfs2_dquot { +struct dquot dq_dquot;/* Generic VFS dquot */ +loff_t dq_local_off;/* Offset in the local quota file */ +struct ocfs2_quota_chunk *dq_chunk;/* Chunk dquot is in */ +unsigned int dq_use_count;/* Number of nodes having reference to this entry in global quota file */ +s64 dq_origspace;/* Last globally synced space usage */ +s64 dq_originodes;/* Last globally synced inode usage */ +}; + +/* In-memory structure with quota header information */ +struct ocfs2_mem_dqinfo { +unsigned int dqi_type;/* Quota type this structure describes */ +unsigned int dqi_chunks;/* Number of chunks in local quota file */ +unsigned int dqi_blocks;/* Number of blocks allocated for local quota file */ +unsigned int dqi_syncms;/* How often should we sync with other nodes */ +struct list_head dqi_chunk;/* List of chunks */ +struct inode *dqi_gqinode;/* Global quota file inode */ +struct ocfs2_lock_res dqi_gqlock;/* Lock protecting quota information structure */ +struct buffer_head *dqi_gqi_bh;/* Buffer head with global quota file inode - set only if inode lock is obtained */ +int dqi_gqi_count;/* Number of holders of dqi_gqi_bh */ +struct buffer_head *dqi_lqi_bh;/* Buffer head with local quota file inode */ +struct buffer_head *dqi_ibh;/* Buffer with information header */ +struct qtree_mem_dqinfo dqi_gi;/* Info about global file */ +}; + +static inline struct ocfs2_dquot *OCFS2_DQUOT (struct dquot *dquot) +{ + return container_of(dquot, struct ocfs2_dquot, dq_dquot); +} + +struct ocfs2_quota_chunk { +struct list_head qc_chunk;/* List of quotafile chunks */ +int qc_num;/* Number of quota chunk */ +struct buffer_head *qc_headerbh;/* Buffer head with chunk header */ +}; + +extern struct kmem_cache *ocfs2_dquot_cachep; +extern struct kmem_cache *ocfs2_qf_chunk_cachep; + +extern struct qtree_fmt_operations ocfs2_global_ops; + +ssize_t ocfs2_quota_read (struct super_block *sb,int type,char *data, +size_t len,loff_t off); +ssize_t ocfs2_quota_write (struct super_block *sb,int type, +const char *data,size_t len,loff_t off); +int ocfs2_global_read_info (struct super_block *sb,int type); +int ocfs2_global_write_info (struct super_block *sb,int type); +int ocfs2_global_read_dquot (struct dquot *dquot); +int __ocfs2_sync_dquot (struct dquot *dquot,int freeing); +static inline int ocfs2_sync_dquot (struct dquot *dquot) +{ +return __ocfs2_sync_dquot (dquot,0 ); +} +static inline int ocfs2_global_release_dquot (struct dquot *dquot) +{ +return __ocfs2_sync_dquot (dquot,1 ); +} + +int ocfs2_lock_global_qf (struct ocfs2_mem_dqinfo *oinfo,int ex); +void ocfs2_unlock_global_qf (struct ocfs2_mem_dqinfo *oinfo,int ex); +struct buffer_head *ocfs2_read_quota_block (struct inode *inode, +int block,int *err); + +extern struct dquot_operations ocfs2_quota_operations; +extern struct quota_format_type ocfs2_quota_format; + +#endif /* _OCFS2_QUOTA_H */ fs/ocfs2/quota_global.c new file +52
-0@@ -0,0 +1,863 @@ +/* +* Implementation of operations over global quota file +*/ +#include <linux/fs.h> +#include <linux/quota.h> +#include <linux/quotaops.h> +#include <linux/dqblk_qtree.h> + +#define MLOG_MASK_PREFIX ML_QUOTA +#include <cluster/masklog.h> + +#include "ocfs2_fs.h" +#include "ocfs2.h" +#include "alloc.h" +#include "inode.h" +#include "journal.h" +#include "file.h" +#include "sysfile.h" +#include "dlmglue.h" +#include "quota.h" + +static void ocfs2_global_disk2memdqb (struct dquot *dquot,void *dp) +{ +struct ocfs2_global_disk_dqblk *d = dp; +struct mem_dqblk *m = &dquot->dq_dqb; + +/* Update from disk only entries not set by the admin */ +if (!test_bit (DQ_LASTSET_B +QIF_ILIMITS_B , &dquot->dq_flags)) { + m->dqb_ihardlimit =le64_to_cpu (d->dqb_ihardlimit); + m->dqb_isoftlimit =le64_to_cpu (d->dqb_isoftlimit); + } +if (!test_bit (DQ_LASTSET_B +QIF_INODES_B , &dquot->dq_flags)) + m->dqb_curinodes =le64_to_cpu (d->dqb_curinodes); +if (!test_bit (DQ_LASTSET_B +QIF_BLIMITS_B , &dquot->dq_flags)) { + m->dqb_bhardlimit =le64_to_cpu (d->dqb_bhardlimit); + m->dqb_bsoftlimit =le64_to_cpu (d->dqb_bsoftlimit); + } +if (!test_bit (DQ_LASTSET_B +QIF_SPACE_B , &dquot->dq_flags)) + m->dqb_curspace =le64_to_cpu (d->dqb_curspace); +if (!test_bit (DQ_LASTSET_B +QIF_BTIME_B , &dquot->dq_flags)) + m->dqb_btime =le64_to_cpu (d->dqb_btime); +if (!test_bit (DQ_LASTSET_B +QIF_ITIME_B , &dquot->dq_flags)) + m->dqb_itime =le64_to_cpu (d->dqb_itime); +OCFS2_DQUOT (dquot)->dq_use_count =le32_to_cpu (d->dqb_use_count); +} + +static void ocfs2_global_mem2diskdqb (void *dp,struct dquot *dquot) +{ +struct ocfs2_global_disk_dqblk *d = dp; +struct mem_dqblk *m = &dquot->dq_dqb; + + d->dqb_id =cpu_to_le32 (dquot->dq_id);
fs/ocfs2/quota_global.c line 62
Quoted 10 lines
fs/ocfs2/quota_global.c new file +10
-0+ d->dqb_use_count =cpu_to_le32 (OCFS2_DQUOT (dquot)->dq_use_count); + d->dqb_ihardlimit =cpu_to_le64 (m->dqb_ihardlimit); + d->dqb_isoftlimit =cpu_to_le64 (m->dqb_isoftlimit); + d->dqb_curinodes =cpu_to_le64 (m->dqb_curinodes); + d->dqb_bhardlimit =cpu_to_le64 (m->dqb_bhardlimit); + d->dqb_bsoftlimit =cpu_to_le64 (m->dqb_bsoftlimit); + d->dqb_curspace =cpu_to_le64 (m->dqb_curspace); + d->dqb_btime =cpu_to_le64 (m->dqb_btime); + d->dqb_itime =cpu_to_le64 (m->dqb_itime); + d->dqb_pad1 = d->dqb_pad2 =0 ;
Just to be clear - do we definitely want to reset dqb_pad1 and dqb_pad2 every time the header is written? This means that a future version of the quota code can't put values there without having them overwritten by nodes which don't understand the new fields...
Quoted 23 lines
fs/ocfs2/quota_global.c new file +23
-0+/* Write to quotafile (we know the transaction is already started and has +* enough credits) */ +ssize_t ocfs2_quota_write (struct super_block *sb,int type, +const char *data,size_t len,loff_t off) +{ +struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; +struct inode *gqinode = oinfo->dqi_gqinode; +int offset = off & (sb->s_blocksize -1 ); +sector_t blk = off >> sb->s_blocksize_bits; +int err =0 ; +struct buffer_head *bh; +handle_t *handle =journal_current_handle (); +size_t tocopy, towrite = len; + +if (!handle) { +mlog (ML_ERROR ,"Quota write (off=%llu, len=%llu) cancelled " +"because transaction was not started.\n" , + (unsigned long long )off, (unsigned long long )len); +return -EIO ; + } +mutex_lock_nested (&gqinode->i_mutex,I_MUTEX_QUOTA ); +if (gqinode->i_size < off + len) {
fs/ocfs2/quota_global.c line 158
Quoted 10 lines
fs/ocfs2/quota_global.c new file +10
-0+down_write (&OCFS2_I (gqinode)->ip_alloc_sem); + err =ocfs2_extend_no_holes (gqinode, off + len, off); +up_write (&OCFS2_I (gqinode)->ip_alloc_sem); +if (err <0 ) + goto out; + err =ocfs2_simple_size_update (gqinode, + oinfo->dqi_gqi_bh, + off + len); +if (err <0 ) + goto out;
Is it safe if we crash / error here, after the size has been extended but before we've written into the newly allocated blocks? In particular, could we wind up reading those blocks later and wrongly interpreting whatever data happens to be there? Hmm ok, I guess ocfs2_zero_extend() from ocfs2_extend_no_holes() fixes that for you, but it's using the page cache. Does that interact reasonably with what we're doing below?
Quoted 8 lines
fs/ocfs2/quota_global.c new file +8
-0+ } +WARN_ON (off >> sb->s_blocksize_bits != \ + (off + len) >> sb->s_blocksize_bits); +WARN_ON (((off + len) & ((1 << sb->s_blocksize_bits) -1 )) > + sb->s_blocksize -OCFS2_QBLK_RESERVED_SPACE ); +for (towrite = len; towrite >0 ; towrite -= tocopy) { + tocopy =min (towrite, (size_t )(sb->s_blocksize - offset)); + bh =ocfs2_read_quota_block (gqinode, blk, &err);
fs/ocfs2/quota_global.c line 176
Quoted 10 lines
fs/ocfs2/quota_global.c new file +10
-0+if (!bh) { +mlog_errno (err); +return err; + } + err =ocfs2_journal_access (handle, gqinode, bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (err <0 ) { +brelse (bh); + goto out; + }
We can optimize away the disk read if the block is newly allocated. There's no need to read it off disk, so we can just sb_getblk() it. Likewise, you'd want to use OCFS2_JOURNAL_ACCESS_CREATE for those. How often does the quota file get extended though? If it's sufficiently rare, we could save this for later. Otherwise, it's probably worth the effort imho.
--Mark
--
Mark Fasheh
Jan Kara
reviewerPATCH 25/29 ocfs2: Implementation of local and global quota file handling
Hi Mark!
Thanks for the review. Your comments which aren't deleted has been just
silently implemented :).
Quoted 13 lines
Quoted 13 lines
fs/ocfs2/quota_global.c new file +13
-0+static void ocfs2_global_mem2diskdqb (void *dp,struct dquot *dquot) +{ +struct ocfs2_global_disk_dqblk *d = dp; +struct mem_dqblk *m = &dquot->dq_dqb; + + d->dqb_id =cpu_to_le32 (dquot->dq_id); + d->dqb_use_count =cpu_to_le32 (OCFS2_DQUOT (dquot)->dq_use_count); + d->dqb_ihardlimit =cpu_to_le64 (m->dqb_ihardlimit); + d->dqb_isoftlimit =cpu_to_le64 (m->dqb_isoftlimit); + d->dqb_curinodes =cpu_to_le64 (m->dqb_curinodes); + d->dqb_bhardlimit =cpu_to_le64 (m->dqb_bhardlimit); + d->dqb_bsoftlimit =cpu_to_le64 (m->dqb_bsoftlimit); + d->dqb_curspace =cpu_to_le64 (m->dqb_curspace);
Quoted 8 lines
fs/ocfs2/quota_global.c line 62
Quoted 3 lines
fs/ocfs2/quota_global.c new file +3
-0+ d->dqb_btime =cpu_to_le64 (m->dqb_btime); + d->dqb_itime =cpu_to_le64 (m->dqb_itime); + d->dqb_pad1 = d->dqb_pad2 =0 ;Just to be clear - do we definitely want to reset
dqb_pad1anddqb_pad2every time the header is written? This means that a future version of the quota code can't put values there without having them overwritten by nodes which don't understand the new fields...
OK, done. Since the block is zeroed-out before it's reused, it's fine to
not zero padding fields. OTOH I suspect that we'll have to require all nodes to understand new meaning of these fields when we start using them anyway. The only advantage is we will not have to change disk layout.
Quoted 32 lines
Quoted 32 lines
fs/ocfs2/quota_global.c new file +32
-0+/* Write to quotafile (we know the transaction is already started and has +* enough credits) */ +ssize_t ocfs2_quota_write (struct super_block *sb,int type, +const char *data,size_t len,loff_t off) +{ +struct mem_dqinfo *info =sb_dqinfo (sb, type); +struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv; +struct inode *gqinode = oinfo->dqi_gqinode; +int offset = off & (sb->s_blocksize -1 ); +sector_t blk = off >> sb->s_blocksize_bits; +int err =0 ; +struct buffer_head *bh; +handle_t *handle =journal_current_handle (); +size_t tocopy, towrite = len; + +if (!handle) { +mlog (ML_ERROR ,"Quota write (off=%llu, len=%llu) cancelled " +"because transaction was not started.\n" , + (unsigned long long )off, (unsigned long long )len); +return -EIO ; + } +mutex_lock_nested (&gqinode->i_mutex,I_MUTEX_QUOTA ); +if (gqinode->i_size < off + len) { +down_write (&OCFS2_I (gqinode)->ip_alloc_sem); + err =ocfs2_extend_no_holes (gqinode, off + len, off); +up_write (&OCFS2_I (gqinode)->ip_alloc_sem); +if (err <0 ) + goto out; + err =ocfs2_simple_size_update (gqinode, + oinfo->dqi_gqi_bh, + off + len); +if (err <0 )
Quoted 8 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ goto out;Is it safe if we crash / error here, after the size has been extended but before we've written into the newly allocated blocks? In particular, could we wind up reading those blocks later and wrongly interpreting whatever data happens to be there? Hmm ok, I guess
ocfs2_zero_extend()fromocfs2_extend_no_holes()fixes that for you, but it's using the page cache. Does that interact reasonably with what we're doing below?
It should work fine. I actually don't use ocfs2_zero_extend() (note that
I pass 'off' to ocfs2_extend_no_holes() as the third parameter). But when generic quota code decides to extend the file, it checks whether the write succeeded and if not, it just internally does not increase it's notion of quotafile size.
Quoted 16 lines
Quoted 16 lines
fs/ocfs2/quota_global.c new file +16
-0+ } +WARN_ON (off >> sb->s_blocksize_bits != \ + (off + len) >> sb->s_blocksize_bits); +WARN_ON (((off + len) & ((1 << sb->s_blocksize_bits) -1 )) > + sb->s_blocksize -OCFS2_QBLK_RESERVED_SPACE ); +for (towrite = len; towrite >0 ; towrite -= tocopy) { + tocopy =min (towrite, (size_t )(sb->s_blocksize - offset)); + bh =ocfs2_read_quota_block (gqinode, blk, &err); +if (!bh) { +mlog_errno (err); +return err; + } + err =ocfs2_journal_access (handle, gqinode, bh, +OCFS2_JOURNAL_ACCESS_WRITE ); +if (err <0 ) { +brelse (bh);
Quoted 8 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ goto out; + }We can optimize away the disk read if the block is newly allocated. There's no need to read it off disk, so we can just
sb_getblk()it. Likewise, you'd want to useOCFS2_JOURNAL_ACCESS_CREATEfor those. How often does the quota file get extended though? If it's sufficiently rare, we could save this for later. Otherwise, it's probably worth the effort imho.
Actually, extending quota file is really rare operation - happens only
once per 20 new users of the cluster :). But your comment got me an idea, that we can save read in most rewrite cases because upper level quota code provides us with a complete content of the block. So I've rewritten the function to optimize this case. But please check whether the function is right when I submit next version of patches.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Jan Kara
reviewerPATCH 26/29 ocfs2: Add quota calls for allocation and freeing of inodes and space
In reply to Jan KaraAdd quota calls for allocation and freeing of inodes and space, also update estimates on number of needed credits for a transaction. Move out inode allocation from ocfs2_mknod_locked() because vfs_dq_init() must be called outside of a transaction.
--- fs/ocfs2/alloc.c | 18 ++++++- fs/ocfs2/aops.c | 16 +++++- fs/ocfs2/dir.c | 24 ++++++++- fs/ocfs2/file.c | 70 ++++++++++++++++++++++-- fs/ocfs2/inode.c | 10 +++- fs/ocfs2/journal.h | 84 ++++++++++++++++++++++++----- fs/ocfs2/namei.c | 151 +++++++++++++++++++++++++++++++++++---------------- fs/ocfs2/xattr.c | 4 +- 8 files changed, 298 insertions(+), 79 deletions(-)
fs/ocfs2/alloc.c +17 -1
@@ -28,6 +28,7 @@#include <linux/slab.h> #include <linux/highmem.h> #include <linux/swap.h> +#include <linux/quotaops.h> #define MLOG_MASK_PREFIX ML_DISK_ALLOC#include <cluster/masklog.h>
@@ -6350,6 +6351,8 @@ static int ocfs2_do_truncate(struct ocfs2_super *osb, goto bail; } +vfs_dq_free_space_nodirty (inode, +ocfs2_clusters_to_bytes (osb->sb, clusters_to_del));spin_lock (&OCFS2_I (inode)->ip_lock);OCFS2_I (inode)->ip_clusters =le32_to_cpu (fe->i_clusters) - clusters_to_del;
@@ -6663,6 +6666,7 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,struct page **pages =NULL ;loff_t end = osb->s_clustersize;struct ocfs2_extent_tree et; +int did_quota =0 ; has_data =i_size_read (inode) ?1 :0 ;
@@ -6682,7 +6686,8 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode, } } - handle =ocfs2_start_trans (osb,OCFS2_INLINE_TO_EXTENTS_CREDITS ); + handle =ocfs2_start_trans (osb, +ocfs2_inline_to_extents_credits (osb->sb));if (IS_ERR (handle)) { ret =PTR_ERR (handle);mlog_errno (ret);
@@ -6701,6 +6706,13 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,unsigned int page_end;u64 phys; +if (vfs_dq_alloc_space_nodirty (inode, +ocfs2_clusters_to_bytes (osb->sb,1 ))) { + ret = -EDQUOT ; + goto out_commit; + } + did_quota =1 ; + ret =ocfs2_claim_clusters (osb, handle, data_ac,1 , &bit_off, &num);if (ret) {
@@ -6774,6 +6786,10 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode, } out_commit: +if (ret <0 && did_quota) +vfs_dq_free_space_nodirty (inode, +ocfs2_clusters_to_bytes (osb->sb,1 )); +ocfs2_commit_trans (osb, handle); out_unlock:
fs/ocfs2/aops.c +13 -3
@@ -27,6 +27,7 @@#include <linux/swap.h> #include <linux/pipe_fs_i.h> #include <linux/mpage.h> +#include <linux/quotaops.h> #define MLOG_MASK_PREFIX ML_FILE_IO#include <cluster/masklog.h>
@@ -1750,6 +1751,11 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, wc->w_handle = handle; +if (clusters_to_alloc &&vfs_dq_alloc_space_nodirty (inode, +ocfs2_clusters_to_bytes (osb->sb, clusters_to_alloc))) { + ret = -EDQUOT ; + goto out_commit; + }/* * We don't want this to fail in ocfs2_write_end(), so do it * here.
@@ -1758,7 +1764,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, OCFS2_JOURNAL_ACCESS_WRITE);if (ret) {mlog_errno (ret); - goto out_commit; + goto out_quota; }/*
@@ -1771,14 +1777,14 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, mmap_page);if (ret) {mlog_errno (ret); - goto out_commit; + goto out_quota; } ret =ocfs2_write_cluster_by_desc (mapping, data_ac, meta_ac, wc, pos, len);if (ret) {mlog_errno (ret); - goto out_commit; + goto out_quota; }if (data_ac)
@@ -1790,6 +1796,10 @@ success: *pagep = wc->w_target_page; *fsdata = wc;return 0 ; +out_quota: +if (clusters_to_alloc) +vfs_dq_free_space (inode, +ocfs2_clusters_to_bytes (osb->sb, clusters_to_alloc)); out_commit:ocfs2_commit_trans (osb, handle);
fs/ocfs2/dir.c +22 -2
@@ -40,6 +40,7 @@#include <linux/types.h> #include <linux/slab.h> #include <linux/highmem.h> +#include <linux/quotaops.h> #define MLOG_MASK_PREFIX ML_NAMEI#include <cluster/masklog.h>
@@ -1216,9 +1217,9 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,unsigned int blocks_wanted, struct buffer_head **first_block_bh) { - int ret, credits = OCFS2_INLINE_TO_EXTENTS_CREDITS; u32 alloc, bit_off, len;struct super_block *sb = dir->i_sb; +int ret, credits =ocfs2_inline_to_extents_credits (sb);u64 blkno, bytes = blocks_wanted << sb->s_blocksize_bits;struct ocfs2_super *osb =OCFS2_SB (dir->i_sb);struct ocfs2_inode_info *oi =OCFS2_I (dir);
@@ -1227,6 +1228,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;handle_t *handle;struct ocfs2_extent_tree et; +int did_quota =0 ;ocfs2_init_dinode_extent_tree (&et, dir, di_bh);
@@ -1264,6 +1266,12 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, goto out_sem; } +if (vfs_dq_alloc_space_nodirty (dir, +ocfs2_clusters_to_bytes (osb->sb, alloc))) { + ret = -EDQUOT ; + goto out_commit; + } + did_quota =1 ;/* * Try to claim as many clusters as the bitmap can give though * if we only get one now, that's enough to continue. The rest
@@ -1386,6 +1394,9 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, dirdata_bh =NULL ; out_commit: +if (ret <0 && did_quota) +vfs_dq_free_space_nodirty (dir, +ocfs2_clusters_to_bytes (osb->sb,2 ));ocfs2_commit_trans (osb, handle); out_sem:
@@ -1410,7 +1421,7 @@ static int ocfs2_do_extend_dir(struct super_block *sb, struct buffer_head **new_bh) {int status; -int extend; +int extend, did_quota =0 ;u64 p_blkno, v_blkno;spin_lock (&OCFS2_I (dir)->ip_lock);
@@ -1420,6 +1431,13 @@ static int ocfs2_do_extend_dir(struct super_block *sb,if (extend) {u32 offset =OCFS2_I (dir)->ip_clusters; +if (vfs_dq_alloc_space_nodirty (dir, +ocfs2_clusters_to_bytes (sb,1 ))) { + status = -EDQUOT ; + goto bail; + } + did_quota =1 ; + status =ocfs2_add_inode_data (OCFS2_SB (sb), dir, &offset,1 ,0 , parent_fe_bh, handle, data_ac, meta_ac,NULL );
@@ -1445,6 +1463,8 @@ static int ocfs2_do_extend_dir(struct super_block *sb, } status =0 ; bail: +if (did_quota && status <0 ) +vfs_dq_free_space_nodirty (dir,ocfs2_clusters_to_bytes (sb,1 ));mlog_exit (status);return status; }
fs/ocfs2/file.c +64 -6
124 lines not on this page · raw message
fs/ocfs2/inode.c +8 -2
@@ -28,6 +28,7 @@#include <linux/slab.h> #include <linux/highmem.h> #include <linux/pagemap.h> +#include <linux/quotaops.h> #include <asm/byteorder.h>
@@ -619,7 +620,8 @@ static int ocfs2_remove_inode(struct inode *inode, goto bail; } - handle =ocfs2_start_trans (osb,OCFS2_DELETE_INODE_CREDITS ); + handle =ocfs2_start_trans (osb,OCFS2_DELETE_INODE_CREDITS + +ocfs2_quota_trans_credits (inode->i_sb));if (IS_ERR (handle)) { status =PTR_ERR (handle);mlog_errno (status);
@@ -651,6 +653,7 @@ static int ocfs2_remove_inode(struct inode *inode, }ocfs2_remove_from_cache (inode, di_bh); +vfs_dq_free_inode (inode); status =ocfs2_free_dinode (handle, inode_alloc_inode, inode_alloc_bh, di);
@@ -933,7 +936,10 @@ void ocfs2_delete_inode(struct inode *inode)mlog_entry ("(inode->i_ino = %lu)\n" , inode->i_ino); -if (is_bad_inode (inode)) { +/* When we fail in read_inode() we mark inode as bad. The second test +* catches the case when inode allocation fails before allocating +* a block for inode. */ +if (is_bad_inode (inode) || !OCFS2_I (inode)->ip_blkno) {mlog (0 ,"Skipping delete of bad inode\n" ); goto bail; }
fs/ocfs2/journal.h +69 -15
144 lines not on this page · raw message
fs/ocfs2/namei.c +103 -48
317 lines not on this page · raw message
fs/ocfs2/xattr.c +2 -2
@@ -332,7 +332,7 @@ static int __ocfs2_remove_xattr_range(struct inode *inode, } } - handle =ocfs2_start_trans (osb,OCFS2_REMOVE_EXTENT_CREDITS ); + handle =ocfs2_start_trans (osb,ocfs2_remove_extent_credits (osb->sb));if (IS_ERR (handle)) { ret =PTR_ERR (handle);mlog_errno (ret);
@@ -4312,7 +4312,7 @@ static int ocfs2_rm_xattr_cluster(struct inode *inode, } } - handle =ocfs2_start_trans (osb,OCFS2_REMOVE_EXTENT_CREDITS ); + handle =ocfs2_start_trans (osb,ocfs2_remove_extent_credits (osb->sb));if (handle ==NULL ) { ret = -ENOMEM ;mlog_errno (ret);
Signature · 1 line
1.5.2.4
PATCH 26/29 ocfs2: Add quota calls for allocation and freeing of inodes and space
Quoted 43 lines · Jan Kara
fs/ocfs2/file.c +10
-0@@ -35,6 +35,7 @@#include <linux/mount.h> #include <linux/writeback.h> #include <linux/falloc.h> +#include <linux/quotaops.h> #define MLOG_MASK_PREFIX ML_INODE#include <cluster/masklog.h> @@ -56,6 +57,7 @@#include "suballoc.h" #include "super.h" #include "xattr.h" +#include "quota.h" #include "buffer_head_io.h" @@ -536,6 +538,8 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start,enum ocfs2_alloc_restarted why;struct ocfs2_super *osb =OCFS2_SB (inode->i_sb);struct ocfs2_extent_tree et; +u32 total_clusters = clusters_to_add; +int did_quota =0 ;mlog_entry ("(clusters_to_add = %u)\n" , clusters_to_add);@@ -584,6 +588,12 @@ restart_all: goto leave; } +if (!did_quota &&vfs_dq_alloc_space_nodirty (inode, +ocfs2_clusters_to_bytes (osb->sb, total_clusters))) { + status = -EDQUOT ; + goto leave; + } + did_quota =1 ; restarted_transaction:/* reserve a write to the file entry early on - that we if we * run out of credits in the allocation path, we can still
fs/ocfs2/file.c restarted_transaction: · line 669
Quoted 6 lines
fs/ocfs2/file.c +3
-0@@ -654,6 +664,9 @@ restarted_transaction: OCFS2_I(inode)->ip_clusters, (long long)i_size_read(inode)); leave: +if (status <0 && did_quota) +vfs_dq_free_space (inode, +ocfs2_clusters_to_bytes (osb->sb, total_clusters));
Shouldn't you subtract clusters_to_add clusters instead of total_clusters here? In theory, we could have allocated *some* of the inode but failed during a later pass.
Quoted 33 lines
fs/ocfs2/file.c +24
-5@@ -956,11 +972,47 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) } } - handle =ocfs2_start_trans (osb,OCFS2_INODE_UPDATE_CREDITS ); -if (IS_ERR (handle)) { - status =PTR_ERR (handle); -mlog_errno (status); - goto bail_unlock; +if ((attr->ia_valid &ATTR_UID && attr->ia_uid != inode->i_uid) || + (attr->ia_valid &ATTR_GID && attr->ia_gid != inode->i_gid)) { + credits =OCFS2_INODE_UPDATE_CREDITS ; +if (attr->ia_valid &ATTR_UID && attr->ia_uid != inode->i_uid + &&OCFS2_HAS_RO_COMPAT_FEATURE (sb, +OCFS2_FEATURE_RO_COMPAT_USRQUOTA )) { + oinfo =sb_dqinfo (sb,USRQUOTA )->dqi_priv; + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) + goto bail_unlock; + credits +=ocfs2_calc_qinit_credits (sb,USRQUOTA ) + +ocfs2_calc_qdel_credits (sb,USRQUOTA ); + locked[USRQUOTA ] =1 ; + } +if (attr->ia_valid &ATTR_GID && attr->ia_gid != inode->i_gid + &&OCFS2_HAS_RO_COMPAT_FEATURE (sb, +OCFS2_FEATURE_RO_COMPAT_GRPQUOTA )) { + oinfo =sb_dqinfo (sb,GRPQUOTA )->dqi_priv; + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) + goto bail_unlock; + credits +=ocfs2_calc_qinit_credits (sb,GRPQUOTA ) + +ocfs2_calc_qdel_credits (sb,GRPQUOTA ); + locked[GRPQUOTA ] =1 ;
fs/ocfs2/file.c int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) · line 1008
Quoted 10 lines
fs/ocfs2/file.c +10
-0+ } + handle =ocfs2_start_trans (osb, credits); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto bail_unlock; + } + status =vfs_dq_transfer (inode, attr) ? -EDQUOT :0 ; +if (status <0 ) + goto bail_commit;
Ok, so this will all cause syncing on the global quota file because we need to transfer one record to/from another? Is there any better way?
Rest of the patch looks great btw.
--Mark
--
Mark Fasheh
Jan Kara
reviewerPATCH 26/29 ocfs2: Add quota calls for allocation and freeing of inodes and space
Quoted 46 lines · Mark Fasheh
Quoted 45 lines · Jan Kara
fs/ocfs2/file.c +10
-0@@ -35,6 +35,7 @@#include <linux/mount.h> #include <linux/writeback.h> #include <linux/falloc.h> +#include <linux/quotaops.h> #define MLOG_MASK_PREFIX ML_INODE#include <cluster/masklog.h> @@ -56,6 +57,7 @@#include "suballoc.h" #include "super.h" #include "xattr.h" +#include "quota.h" #include "buffer_head_io.h" @@ -536,6 +538,8 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start,enum ocfs2_alloc_restarted why;struct ocfs2_super *osb =OCFS2_SB (inode->i_sb);struct ocfs2_extent_tree et; +u32 total_clusters = clusters_to_add; +int did_quota =0 ;mlog_entry ("(clusters_to_add = %u)\n" , clusters_to_add);@@ -584,6 +588,12 @@ restart_all: goto leave; } +if (!did_quota &&vfs_dq_alloc_space_nodirty (inode, +ocfs2_clusters_to_bytes (osb->sb, total_clusters))) { + status = -EDQUOT ; + goto leave; + } + did_quota =1 ; restarted_transaction:/* reserve a write to the file entry early on - that we if we * run out of credits in the allocation path, we can still @@ -654,6 +664,9 @@ restarted_transaction: OCFS2_I(inode)->ip_clusters, (long long)i_size_read(inode));
Quoted 8 lines
fs/ocfs2/file.c restarted_transaction: · line 669
Quoted 4 lines
fs/ocfs2/file.c +3
-0leave: +if (status <0 && did_quota) +vfs_dq_free_space (inode, +ocfs2_clusters_to_bytes (osb->sb, total_clusters));Shouldn't you subtract
clusters_to_addclusters instead oftotal_clustershere? In theory, we could have allocated *some* of the inode but failed during a later pass.
True, actually to be correct WRT crash in the middle of the allocation,
we have to release unused quota before we restart a transaction and then ask for remaining blocks again - quota operation must always belong to the same transaction as the allocation itself. It should be fixed now.
Quoted 38 lines
Quoted 38 lines
fs/ocfs2/file.c +29
-5@@ -956,11 +972,47 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) } } - handle =ocfs2_start_trans (osb,OCFS2_INODE_UPDATE_CREDITS ); -if (IS_ERR (handle)) { - status =PTR_ERR (handle); -mlog_errno (status); - goto bail_unlock; +if ((attr->ia_valid &ATTR_UID && attr->ia_uid != inode->i_uid) || + (attr->ia_valid &ATTR_GID && attr->ia_gid != inode->i_gid)) { + credits =OCFS2_INODE_UPDATE_CREDITS ; +if (attr->ia_valid &ATTR_UID && attr->ia_uid != inode->i_uid + &&OCFS2_HAS_RO_COMPAT_FEATURE (sb, +OCFS2_FEATURE_RO_COMPAT_USRQUOTA )) { + oinfo =sb_dqinfo (sb,USRQUOTA )->dqi_priv; + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) + goto bail_unlock; + credits +=ocfs2_calc_qinit_credits (sb,USRQUOTA ) + +ocfs2_calc_qdel_credits (sb,USRQUOTA ); + locked[USRQUOTA ] =1 ; + } +if (attr->ia_valid &ATTR_GID && attr->ia_gid != inode->i_gid + &&OCFS2_HAS_RO_COMPAT_FEATURE (sb, +OCFS2_FEATURE_RO_COMPAT_GRPQUOTA )) { + oinfo =sb_dqinfo (sb,GRPQUOTA )->dqi_priv; + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) + goto bail_unlock; + credits +=ocfs2_calc_qinit_credits (sb,GRPQUOTA ) + +ocfs2_calc_qdel_credits (sb,GRPQUOTA ); + locked[GRPQUOTA ] =1 ; + } + handle =ocfs2_start_trans (osb, credits); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status);
Quoted 8 lines
fs/ocfs2/file.c int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) · line 1008
Quoted 5 lines
fs/ocfs2/file.c +5
-0+ goto bail_unlock; + } + status =vfs_dq_transfer (inode, attr) ? -EDQUOT :0 ; +if (status <0 ) + goto bail_commit;Ok, so this will all cause syncing on the global quota file because we need to transfer one record to/from another? Is there any better way?
Yes, we take exclusive lock on global quota file because this file might
be the first file user ever has (or the last file source user has) and so we have to create new quota structure for him (remove quota structure respectively). This is a rare case but has to be taken care of. Of course, we could play similar tricks as we do in ocfs2_dquot_initialize() to optimize for the common case where shared lock is enough because all structures already exist. But I felt chown/chgrp is rare enough so for now I took the simple way. If this is a problem, it can be changed.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Jan Kara
reviewerPATCH 27/29 ocfs2: Implement quota syncing thread
In reply to Jan KaraThis patch implements functions and timer setup which handles periodic syncing of locally cached quota information to global quota file.
--- fs/ocfs2/quota.h | 3 ++ fs/ocfs2/quota_global.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/quota_local.c | 4 ++ 3 files changed, 78 insertions(+), 0 deletions(-)
fs/ocfs2/quota.h +3 -0
@@ -14,6 +14,7 @@#include <linux/quota.h> #include <linux/list.h> #include <linux/dqblk_qtree.h> +#include <linux/timer.h> #include "ocfs2.h"
@@ -43,6 +44,7 @@ struct ocfs2_mem_dqinfo {unsigned int dqi_chunks;/* Number of chunks in local quota file */ unsigned int dqi_blocks;/* Number of blocks allocated for local quota file */ unsigned int dqi_syncms;/* How often should we sync with other nodes */ +unsigned int dqi_syncjiff;/* Precomputed dqi_syncms in jiffies */ struct list_head dqi_chunk;/* List of chunks */ struct inode *dqi_gqinode;/* Global quota file inode */ struct ocfs2_lock_res dqi_gqlock;/* Lock protecting quota information structure */
@@ -51,6 +53,7 @@ struct ocfs2_mem_dqinfo {struct buffer_head *dqi_lqi_bh;/* Buffer head with local quota file inode */ struct buffer_head *dqi_ibh;/* Buffer with information header */ struct qtree_mem_dqinfo dqi_gi;/* Info about global file */ +struct timer_list dqi_sync_timer;/* Timer for syncing dquots */ };static inline struct ocfs2_dquot *OCFS2_DQUOT (struct dquot *dquot)
fs/ocfs2/quota_global.c +71 -0
105 lines not on this page · raw message
fs/ocfs2/quota_local.c +4 -0
@@ -368,6 +368,10 @@ static int ocfs2_local_free_info(struct super_block *sb, int type)int mark_clean =1 , len;int status; +/* At this point we know there are no more dquots and thus +* even if there's some sync in the pdflush queue, it won't +* find any dquots and return without doing anything */ +del_timer_sync (&oinfo->dqi_sync_timer);iput (oinfo->dqi_gqinode);ocfs2_simple_drop_lockres (OCFS2_SB (sb), &oinfo->dqi_gqlock);ocfs2_lock_res_free (&oinfo->dqi_gqlock);
Signature · 1 line
1.5.2.4
PATCH 27/29 ocfs2: Implement quota syncing thread
Quoted 2 lines · Jan Kara
This patch implements functions and timer setup which handles periodic syncing of locally cached quota information to global quota file.
This too looks good.
--Mark
--
Mark Fasheh
Jan Kara
reviewerPATCH 28/29 ocfs2: Implement quota recovery
In reply to Jan KaraImplement functions for recovery after a crash. Functions just read local quota file and sync info to global quota file.
--- fs/ocfs2/journal.c | 105 +++++++++--- fs/ocfs2/journal.h | 1 + fs/ocfs2/ocfs2.h | 4 +- fs/ocfs2/quota.h | 21 +++ fs/ocfs2/quota_global.c | 1 - fs/ocfs2/quota_local.c | 430 ++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 530 insertions(+), 32 deletions(-)
fs/ocfs2/journal.c +83 -22
@@ -45,6 +45,7 @@#include "slot_map.h" #include "super.h" #include "sysfile.h" +#include "quota.h" #include "buffer_head_io.h"
@@ -52,7 +53,7 @@ DEFINE_SPINLOCK(trans_inc_lock);static int ocfs2_force_read_journal (struct inode *inode);static int ocfs2_recover_node (struct ocfs2_super *osb, -int node_num); +int node_num,int slot_num);static int __ocfs2_recovery_thread (void *arg);static int ocfs2_commit_cache (struct ocfs2_super *osb);static int ocfs2_wait_on_mount (struct ocfs2_super *osb);
@@ -877,6 +878,7 @@ struct ocfs2_la_recovery_item {int lri_slot;struct ocfs2_dinode *lri_la_dinode;struct ocfs2_dinode *lri_tl_dinode; +struct ocfs2_quota_recovery *lri_qrec; };/* Does the second half of the recovery process. By this point, the
@@ -897,6 +899,7 @@ void ocfs2_complete_recovery(struct work_struct *work)struct ocfs2_super *osb = journal->j_osb;struct ocfs2_dinode *la_dinode, *tl_dinode;struct ocfs2_la_recovery_item *item, *n; +struct ocfs2_quota_recovery *qrec;LIST_HEAD (tmp_la_list);mlog_entry_void ();
@@ -942,6 +945,16 @@ void ocfs2_complete_recovery(struct work_struct *work)if (ret <0 )mlog_errno (ret); + qrec = item->lri_qrec; +if (qrec) { +mlog (0 ,"Recovering quota files" ); + ret =ocfs2_finish_quota_recovery (osb, qrec, + item->lri_slot); +if (ret <0 ) +mlog_errno (ret); +/* Recovery info is already freed now */ + } +kfree (item); }
@@ -955,7 +968,8 @@ void ocfs2_complete_recovery(struct work_struct *work)static void ocfs2_queue_recovery_completion (struct ocfs2_journal *journal,int slot_num,struct ocfs2_dinode *la_dinode, -struct ocfs2_dinode *tl_dinode) +struct ocfs2_dinode *tl_dinode, +struct ocfs2_quota_recovery *qrec) {struct ocfs2_la_recovery_item *item;
@@ -970,6 +984,9 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,if (tl_dinode)kfree (tl_dinode); +if (qrec) +ocfs2_free_quota_recovery (qrec); +mlog_errno (-ENOMEM );return ; }
@@ -978,6 +995,7 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal, item->lri_la_dinode = la_dinode; item->lri_slot = slot_num; item->lri_tl_dinode = tl_dinode; + item->lri_qrec = qrec;spin_lock (&journal->j_lock);list_add_tail (&item->lri_list, &journal->j_la_cleanups);
@@ -997,6 +1015,7 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb)ocfs2_queue_recovery_completion (journal, osb->slot_num, osb->local_alloc_copy, +NULL ,NULL );ocfs2_schedule_truncate_log_flush (osb,0 );
@@ -1005,11 +1024,26 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb) } } +void ocfs2_complete_quota_recovery (struct ocfs2_super *osb) +{ +if (osb->quota_rec) { +ocfs2_queue_recovery_completion (osb->journal, + osb->slot_num, +NULL , +NULL , + osb->quota_rec); + osb->quota_rec =NULL ; + } +} +static int __ocfs2_recovery_thread (void *arg) { -int status, node_num; +int status, node_num, slot_num;struct ocfs2_super *osb = arg;struct ocfs2_recovery_map *rm = osb->recovery_map; +int *rm_quota =NULL ; +int rm_quota_used =0 , i; +struct ocfs2_quota_recovery *qrec;mlog_entry_void ();
@@ -1018,6 +1052,11 @@ static int __ocfs2_recovery_thread(void *arg) goto bail; } + rm_quota =kzalloc (osb->max_slots *sizeof (int ),GFP_NOFS ); +if (!rm_quota) { + status = -ENOMEM ; + goto bail; + } restart: status =ocfs2_super_lock (osb,1 );if (status <0 ) {
@@ -1031,8 +1070,28 @@ restart:* clear it until ocfs2_recover_node() has succeeded. */ node_num = rm->rm_entries[0 ];spin_unlock (&osb->osb_lock); - - status =ocfs2_recover_node (osb, node_num); +mlog (0 ,"checking node %d\n" , node_num); + slot_num =ocfs2_node_num_to_slot (osb, node_num); +if (slot_num == -ENOENT ) { + status =0 ; +mlog (0 ,"no slot for this node, so no recovery" +"required.\n" ); + goto skip_recovery; + } +mlog (0 ,"node %d was using slot %d\n" , node_num, slot_num); + +/* It is a bit subtle with quota recovery. We cannot do it +* immediately because we have to obtain cluster locks from +* quota files and we also don't want to just skip it because +* then quota usage would be out of sync until some node takes +* the slot. So we remember which nodes need quota recovery +* and when everything else is done, we recover quotas. */ +for (i =0 ; i < rm_quota_used && rm_quota[i] != slot_num; i++); +if (i == rm_quota_used) + rm_quota[rm_quota_used++] = slot_num; + + status =ocfs2_recover_node (osb, node_num, slot_num); +skip_recovery:if (!status) {ocfs2_recovery_map_clear (osb, node_num); }else {
@@ -1056,11 +1115,22 @@ restart:ocfs2_super_unlock (osb,1 ); +/* Now it is right time to recover quotas... */ +for (i =0 ; i < rm_quota_used; i++) { + qrec =ocfs2_begin_quota_recovery (osb, rm_quota[i]); +if (IS_ERR (qrec)) { + status =PTR_ERR (qrec); +mlog_errno (status); + } +ocfs2_queue_recovery_completion (osb->journal, rm_quota[i], +NULL ,NULL , qrec); + } +/* We always run recovery on our own orphan dir - the dead * node(s) may have disallowd a previos inode delete. Re-processing * is therefore required. */ ocfs2_queue_recovery_completion (osb->journal, osb->slot_num,NULL , -NULL ); +NULL ,NULL ); bail:mutex_lock (&osb->recovery_lock);
@@ -1075,6 +1145,9 @@ bail:mutex_unlock (&osb->recovery_lock); +if (rm_quota) +kfree (rm_quota); +mlog_exit (status);/* no one is callint kthread_stop() for us so the kthread() api * requires that we call do_exit(). And it isn't exported, but
@@ -1303,31 +1376,19 @@ done:* far less concerning. */ static int ocfs2_recover_node (struct ocfs2_super *osb, -int node_num) +int node_num,int slot_num) {int status =0 ; -int slot_num;struct ocfs2_dinode *la_copy =NULL ;struct ocfs2_dinode *tl_copy =NULL ; -mlog_entry ("(node_num=%d, osb->node_num = %d)\n" , - node_num, osb->node_num); - -mlog (0 ,"checking node %d\n" , node_num); +mlog_entry ("(node_num=%d, slot_num=%d, osb->node_num = %d)\n" , + node_num, slot_num, osb->node_num);/* Should not ever be called to recover ourselves -- in that * case we should've called ocfs2_journal_load instead. */ BUG_ON (osb->node_num == node_num); - slot_num =ocfs2_node_num_to_slot (osb, node_num); -if (slot_num == -ENOENT ) { - status =0 ; -mlog (0 ,"no slot for this node, so no recovery required.\n" ); - goto done; - } - -mlog (0 ,"node %d was using slot %d\n" , node_num, slot_num); - status =ocfs2_replay_journal (osb, node_num, slot_num);if (status <0 ) {if (status == -EBUSY ) {
@@ -1363,7 +1424,7 @@ static int ocfs2_recover_node(struct ocfs2_super *osb,/* This will kfree the memory pointed to by la_copy and tl_copy */ ocfs2_queue_recovery_completion (osb->journal, slot_num, la_copy, - tl_copy); + tl_copy,NULL ); status =0 ; done:
fs/ocfs2/journal.h +1 -0
@@ -173,6 +173,7 @@ void ocfs2_recovery_thread(struct ocfs2_super *osb, int node_num);int ocfs2_mark_dead_nodes (struct ocfs2_super *osb);void ocfs2_complete_mount_recovery (struct ocfs2_super *osb); +void ocfs2_complete_quota_recovery (struct ocfs2_super *osb);static inline void ocfs2_start_checkpoint (struct ocfs2_super *osb) {
fs/ocfs2/ocfs2.h +3 -1
@@ -205,6 +205,7 @@ enum ocfs2_mount_optionsstruct ocfs2_journal ;struct ocfs2_slot_info ;struct ocfs2_recovery_map ; +struct ocfs2_quota_recovery ;struct ocfs2_super {struct task_struct *commit_task;
@@ -286,10 +287,11 @@ struct ocfs2_superchar *local_alloc_debug_buf;#endif -/* Next two fields are for local node slot recovery during +/* Next three fields are for local node slot recovery during * mount. */ int dirty;struct ocfs2_dinode *local_alloc_copy; +struct ocfs2_quota_recovery *quota_rec;struct ocfs2_alloc_stats alloc_stats;char dev_str[20 ];/* "major,minor" of the device */
fs/ocfs2/quota.h +21 -0
@@ -38,6 +38,17 @@ struct ocfs2_dquot {s64 dq_originodes;/* Last globally synced inode usage */ }; +/* Description of one chunk to recover in memory */ +struct ocfs2_recovery_chunk { +struct list_head rc_list;/* List of chunks */ +int rc_chunk;/* Chunk number */ +unsigned long *rc_bitmap;/* Bitmap of entries to recover */ +}; + +struct ocfs2_quota_recovery { +struct list_head r_list[MAXQUOTAS ];/* List of chunks to recover */ +}; +/* In-memory structure with quota header information */ struct ocfs2_mem_dqinfo {unsigned int dqi_type;/* Quota type this structure describes */
@@ -54,6 +65,10 @@ struct ocfs2_mem_dqinfo {struct buffer_head *dqi_ibh;/* Buffer with information header */ struct qtree_mem_dqinfo dqi_gi;/* Info about global file */ struct timer_list dqi_sync_timer;/* Timer for syncing dquots */ +struct ocfs2_quota_recovery *dqi_rec;/* Pointer to recovery +* information, in case we +* enable quotas on file +* needing it */ };static inline struct ocfs2_dquot *OCFS2_DQUOT (struct dquot *dquot)
@@ -72,6 +87,12 @@ extern struct kmem_cache *ocfs2_qf_chunk_cachep;extern struct qtree_fmt_operations ocfs2_global_ops; +struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery ( +struct ocfs2_super *osb,int slot_num); +int ocfs2_finish_quota_recovery (struct ocfs2_super *osb, +struct ocfs2_quota_recovery *rec, +int slot_num); +void ocfs2_free_quota_recovery (struct ocfs2_quota_recovery *rec);ssize_t ocfs2_quota_read (struct super_block *sb,int type,char *data,size_t len,loff_t off); ssize_t ocfs2_quota_write(struct super_block *sb, int type,
fs/ocfs2/quota_global.c +0 -1
@@ -85,7 +85,6 @@ struct qtree_fmt_operations ocfs2_global_ops = { .is_id = ocfs2_global_is_id, }; -struct buffer_head *ocfs2_read_quota_block (struct inode *inode,int block,int *err) {
fs/ocfs2/quota_local.c +422 -8
470 lines not on this page · raw message
Signature · 1 line
1.5.2.4
PATCH 28/29 ocfs2: Implement quota recovery
Quoted 178 lines · Jan Kara
Implement functions for recovery after a crash. Functions just read local quota file and sync info to global quota file.
Signed-off-by Jan Kara <[email protected]>--- fs/ocfs2/journal.c | 105 +++++++++--- fs/ocfs2/journal.h | 1 + fs/ocfs2/ocfs2.h | 4 +- fs/ocfs2/quota.h | 21 +++ fs/ocfs2/quota_global.c | 1 - fs/ocfs2/quota_local.c | 430 ++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 530 insertions(+), 32 deletions(-)fs/ocfs2/journal.c +64
-5@@ -45,6 +45,7 @@#include "slot_map.h" #include "super.h" #include "sysfile.h" +#include "quota.h" #include "buffer_head_io.h" @@ -52,7 +53,7 @@ DEFINE_SPINLOCK(trans_inc_lock);static int ocfs2_force_read_journal (struct inode *inode);static int ocfs2_recover_node (struct ocfs2_super *osb, -int node_num); +int node_num,int slot_num);static int __ocfs2_recovery_thread (void *arg);static int ocfs2_commit_cache (struct ocfs2_super *osb);static int ocfs2_wait_on_mount (struct ocfs2_super *osb);@@ -877,6 +878,7 @@ struct ocfs2_la_recovery_item {int lri_slot;struct ocfs2_dinode *lri_la_dinode;struct ocfs2_dinode *lri_tl_dinode; +struct ocfs2_quota_recovery *lri_qrec; };/* Does the second half of the recovery process. By this point, the @@ -897,6 +899,7 @@ void ocfs2_complete_recovery(struct work_struct *work)struct ocfs2_super *osb = journal->j_osb;struct ocfs2_dinode *la_dinode, *tl_dinode;struct ocfs2_la_recovery_item *item, *n; +struct ocfs2_quota_recovery *qrec;LIST_HEAD (tmp_la_list);mlog_entry_void ();@@ -942,6 +945,16 @@ void ocfs2_complete_recovery(struct work_struct *work)if (ret <0 )mlog_errno (ret); + qrec = item->lri_qrec; +if (qrec) { +mlog (0 ,"Recovering quota files" ); + ret =ocfs2_finish_quota_recovery (osb, qrec, + item->lri_slot); +if (ret <0 ) +mlog_errno (ret); +/* Recovery info is already freed now */ + } +kfree (item); }@@ -955,7 +968,8 @@ void ocfs2_complete_recovery(struct work_struct *work)static void ocfs2_queue_recovery_completion (struct ocfs2_journal *journal,int slot_num,struct ocfs2_dinode *la_dinode, -struct ocfs2_dinode *tl_dinode) +struct ocfs2_dinode *tl_dinode, +struct ocfs2_quota_recovery *qrec) {struct ocfs2_la_recovery_item *item;@@ -970,6 +984,9 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,if (tl_dinode)kfree (tl_dinode); +if (qrec) +ocfs2_free_quota_recovery (qrec); +mlog_errno (-ENOMEM );return ; }@@ -978,6 +995,7 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal, item->lri_la_dinode = la_dinode; item->lri_slot = slot_num; item->lri_tl_dinode = tl_dinode; + item->lri_qrec = qrec;spin_lock (&journal->j_lock);list_add_tail (&item->lri_list, &journal->j_la_cleanups);@@ -997,6 +1015,7 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb)ocfs2_queue_recovery_completion (journal, osb->slot_num, osb->local_alloc_copy, +NULL ,NULL );ocfs2_schedule_truncate_log_flush (osb,0 );@@ -1005,11 +1024,26 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb) } } +void ocfs2_complete_quota_recovery (struct ocfs2_super *osb) +{ +if (osb->quota_rec) { +ocfs2_queue_recovery_completion (osb->journal, + osb->slot_num, +NULL , +NULL , + osb->quota_rec); + osb->quota_rec =NULL ; + } +} +static int __ocfs2_recovery_thread (void *arg) { -int status, node_num; +int status, node_num, slot_num;struct ocfs2_super *osb = arg;struct ocfs2_recovery_map *rm = osb->recovery_map; +int *rm_quota =NULL ; +int rm_quota_used =0 , i; +struct ocfs2_quota_recovery *qrec;mlog_entry_void ();@@ -1018,6 +1052,11 @@ static int __ocfs2_recovery_thread(void *arg) goto bail; } + rm_quota =kzalloc (osb->max_slots *sizeof (int ),GFP_NOFS ); +if (!rm_quota) { + status = -ENOMEM ; + goto bail; + } restart: status =ocfs2_super_lock (osb,1 );if (status <0 ) {@@ -1031,8 +1070,28 @@ restart:* clear it until ocfs2_recover_node() has succeeded. */ node_num = rm->rm_entries[0 ];spin_unlock (&osb->osb_lock); - - status =ocfs2_recover_node (osb, node_num); +mlog (0 ,"checking node %d\n" , node_num); + slot_num =ocfs2_node_num_to_slot (osb, node_num); +if (slot_num == -ENOENT ) { + status =0 ; +mlog (0 ,"no slot for this node, so no recovery" +"required.\n" ); + goto skip_recovery; + } +mlog (0 ,"node %d was using slot %d\n" , node_num, slot_num); + +/* It is a bit subtle with quota recovery. We cannot do it +* immediately because we have to obtain cluster locks from +* quota files and we also don't want to just skip it because +* then quota usage would be out of sync until some node takes +* the slot. So we remember which nodes need quota recovery +* and when everything else is done, we recover quotas. */ +for (i =0 ; i < rm_quota_used && rm_quota[i] != slot_num; i++); +if (i == rm_quota_used) + rm_quota[rm_quota_used++] = slot_num; + + status =ocfs2_recover_node (osb, node_num, slot_num); +skip_recovery:if (!status) {ocfs2_recovery_map_clear (osb, node_num); }else {@@ -1056,11 +1115,22 @@ restart:ocfs2_super_unlock (osb,1 );
fs/ocfs2/journal.c restart: · line 1127
Quoted 10 lines
fs/ocfs2/journal.c +10
-0+/* Now it is right time to recover quotas... */ +for (i =0 ; i < rm_quota_used; i++) { + qrec =ocfs2_begin_quota_recovery (osb, rm_quota[i]); +if (IS_ERR (qrec)) { + status =PTR_ERR (qrec); +mlog_errno (status); + } +ocfs2_queue_recovery_completion (osb->journal, rm_quota[i], +NULL ,NULL , qrec); + }
I think we want to do this *within* the super block cluster lock. What if a node mounts and then crashes (with quota file changes in it's journal) in between ocfs2_super_unlock(osb, 1) and the call in ocfs2_begin_quota_recovery()? We'd get the cluster lock, but the quota file info would be stale because we'd have an unrecovered slot.
Also, we call ocfs2_queue_recovery_completion() here even when quota isn't enabled on the file system. Perhaps ocfs2_begin_quota_recovery() should return NULL in that case and we can skip the recovery_completion. Otherwise I think we're just running it against an empty qrec, which seems like a bug too.
Quoted 21 lines
fs/ocfs2/journal.c +21
-0+/* Load information we need for quota recovery into memory */ +struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery ( +struct ocfs2_super *osb, +int slot_num) +{ +unsigned int feature[MAXQUOTAS ] = {OCFS2_FEATURE_RO_COMPAT_USRQUOTA , +OCFS2_FEATURE_RO_COMPAT_GRPQUOTA }; +unsigned int ino[MAXQUOTAS ] = {LOCAL_USER_QUOTA_SYSTEM_INODE , +LOCAL_GROUP_QUOTA_SYSTEM_INODE }; +struct super_block *sb = osb->sb; +struct ocfs2_local_disk_dqinfo *ldinfo; +struct inode *lqinode; +struct buffer_head *bh; +int type; +int status; +struct ocfs2_quota_recovery *rec; + +mlog (ML_NOTICE ,"Beginning quota recovery in slot %u\n" , slot_num); + rec =ocfs2_alloc_quota_recovery (); +if (!rec) +return ERR_PTR (-ENOMEM );
fs/ocfs2/journal.c restart:
Quoted 10 lines
fs/ocfs2/journal.c +10
-0+/* First init... */ + +for (type =0 ; type <MAXQUOTAS ; type++) { +if (!OCFS2_HAS_RO_COMPAT_FEATURE (sb, feature[type])) +continue ; + lqinode =ocfs2_get_system_file_inode (osb, ino[type], slot_num); +if (!lqinode) { + status = -ENOENT ; + goto out; + }
Can we add a comment here saying that we've already recovered the journal, so local quota file meta data is up to date and can be trusted?
fs/ocfs2/journal.c
Quoted 2 lines
fs/ocfs2/journal.c +2
-0+ status =ocfs2_inode_lock_full (lqinode,NULL ,1 , +OCFS2_META_LOCK_NOQUEUE );
I think you also want to add OCFS2_META_LOCK_RECOVERY here, otherwise we'll hang if another node dies before we get the lock.
Quoted 181 lines
fs/ocfs2/journal.c +181
-0+/* Someone else is holding the lock? Then he must be +* doing the recovery. Just skip the file... */ +if (status == -EAGAIN ) { +mlog (ML_NOTICE ,"skipping quota recovery for slot %d " +"because quota file is locked.\n" , slot_num); + status =0 ; + goto out_put; + }else if (status <0 ) { +mlog_errno (status); + goto out_put; + } +/* Now read local header */ + bh =ocfs2_read_quota_block (lqinode,0 , &status); +if (!bh) { +mlog_errno (status); +mlog (ML_ERROR ,"failed to read quota file info header " +"(slot=%d type=%d)\n" , slot_num, type); + goto out_lock; + } + ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + +OCFS2_LOCAL_INFO_OFF ); + status =ocfs2_recovery_load_quota (lqinode, ldinfo, type, + &rec->r_list[type]); +brelse (bh); +out_lock: +ocfs2_inode_unlock (lqinode,1 ); +out_put: +iput (lqinode); +if (status <0 ) +break ; + } +out: +if (status <0 ) { +ocfs2_free_quota_recovery (rec); + rec =ERR_PTR (status); + } +return rec; +} + +/* Sync changes in local quota file into global quota file and +* reinitialize local quota file. +* The function expects local quota file to be already locked and +* dqonoff_mutex locked. */ +static int ocfs2_recover_local_quota_file (struct inode *lqinode, +int type, +struct ocfs2_quota_recovery *rec) +{ +struct super_block *sb = lqinode->i_sb; +struct ocfs2_mem_dqinfo *oinfo =sb_dqinfo (sb, type)->dqi_priv; +struct ocfs2_local_disk_chunk *dchunk; +struct ocfs2_local_disk_dqblk *dqblk; +struct dquot *dquot; +handle_t *handle; +struct buffer_head *hbh =NULL , *qbh =NULL ; +int status =0 ; +int bit, chunk; +struct ocfs2_recovery_chunk *rchunk, *next; +qsize_t spacechange, inodechange; + +mlog_entry ("ino=%lu type=%u" , (unsigned long )lqinode->i_ino, type); + + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) + goto out; + +list_for_each_entry_safe (rchunk, next, &(rec->r_list[type]), rc_list) { + chunk = rchunk->rc_chunk; + hbh =ocfs2_read_quota_block (lqinode, +ol_quota_chunk_block (sb, chunk), + &status); +if (!hbh) { +mlog_errno (status); +break ; + } + dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data; +for_each_bit (bit, rchunk->rc_bitmap,ol_chunk_entries (sb)) { + qbh =ocfs2_read_quota_block (lqinode, +ol_dqblk_block (sb, chunk, bit), + &status); +if (!qbh) { +mlog_errno (status); +break ; + } + dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data + +ol_dqblk_block_off (sb, chunk, bit)); + dquot =dqget (sb,le64_to_cpu (dqblk->dqb_id), type); +if (!dquot) { + status = -EIO ; +mlog (ML_ERROR ,"Failed to get quota structure " +"for id %u, type %d. Cannot finish quota " +"file recovery.\n" , + (unsigned )le64_to_cpu (dqblk->dqb_id), + type); + goto out_put_bh; + } + handle =ocfs2_start_trans (OCFS2_SB (sb), +OCFS2_QSYNC_CREDITS ); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto out_put_dquot; + } +mutex_lock (&sb_dqopt (sb)->dqio_mutex); +spin_lock (&dq_data_lock); +/* Add usage from quota entry into quota changes +* of our node. Auxiliary variables are important +* due to signedness */ + spacechange =le64_to_cpu (dqblk->dqb_spacemod); + inodechange =le64_to_cpu (dqblk->dqb_inodemod); + dquot->dq_dqb.dqb_curspace += spacechange; + dquot->dq_dqb.dqb_curinodes += inodechange; +spin_unlock (&dq_data_lock); +/* We want to drop reference held by the crashed +* node. Since we have our own reference we know +* global structure actually won't be freed. */ + status =ocfs2_global_release_dquot (dquot); +if (status <0 ) { +mlog_errno (status); + goto out_commit; + } +/* Release local quota file entry */ + status =ocfs2_journal_access (handle, lqinode, + qbh,OCFS2_JOURNAL_ACCESS_WRITE ); +if (status <0 ) { +mlog_errno (status); + goto out_commit; + } +lock_buffer (qbh); +WARN_ON (!ocfs2_test_bit (bit, dchunk->dqc_bitmap)); +ocfs2_clear_bit (bit, dchunk->dqc_bitmap); +le32_add_cpu (&dchunk->dqc_free,1 ); +unlock_buffer (qbh); + status =ocfs2_journal_dirty (handle, qbh); +if (status <0 ) +mlog_errno (status); +out_commit: +mutex_unlock (&sb_dqopt (sb)->dqio_mutex); +ocfs2_commit_trans (OCFS2_SB (sb), handle); +out_put_dquot: +dqput (dquot); +out_put_bh: +brelse (qbh); +if (status <0 ) +break ; + } +brelse (hbh); +list_del (&rchunk->rc_list); +kfree (rchunk->rc_bitmap); +kfree (rchunk); +if (status <0 ) +break ; + } +ocfs2_unlock_global_qf (oinfo,1 ); +out: +if (status <0 ) +free_recovery_list (&(rec->r_list[type])); +mlog_exit (status); +return status; +} + +/* Recover local quota files for given node different from us */ +int ocfs2_finish_quota_recovery (struct ocfs2_super *osb, +struct ocfs2_quota_recovery *rec, +int slot_num) +{ +unsigned int ino[MAXQUOTAS ] = {LOCAL_USER_QUOTA_SYSTEM_INODE , +LOCAL_GROUP_QUOTA_SYSTEM_INODE }; +struct super_block *sb = osb->sb; +struct ocfs2_local_disk_dqinfo *ldinfo; +struct buffer_head *bh; +handle_t *handle; +int type; +int status =0 ; +struct inode *lqinode; +unsigned int flags; + +mlog (ML_NOTICE ,"Finishing quota recovery in slot %u\n" , slot_num); +mutex_lock (&sb_dqopt (sb)->dqonoff_mutex); +for (type =0 ; type <MAXQUOTAS ; type++) { +if (list_empty (&(rec->r_list[type]))) +continue ;
fs/ocfs2/journal.c
Quoted 10 lines
fs/ocfs2/journal.c +10
-0+mlog (0 ,"Recovering quota in slot %d\n" , slot_num); + lqinode =ocfs2_get_system_file_inode (osb, ino[type], slot_num); +if (!lqinode) { + status = -ENOENT ; + goto out; + } + status =ocfs2_inode_lock_full (lqinode,NULL ,1 , +OCFS2_META_LOCK_NOQUEUE ); +/* Someone else is holding the lock? Then he must be +* doing the recovery. Just skip the file... */
Hmm, given that we have to do this for quota recovery completion, what's the point of all the work we do in ocfs2_begin_quota_recovery() ?
--Mark
--
Mark Fasheh
Jan Kara
reviewerPATCH 28/29 ocfs2: Implement quota recovery
Quoted 179 lines · Mark Fasheh
Quoted 178 lines · Jan Kara
Implement functions for recovery after a crash. Functions just read local quota file and sync info to global quota file.
Signed-off-by Jan Kara <[email protected]>--- fs/ocfs2/journal.c | 105 +++++++++--- fs/ocfs2/journal.h | 1 + fs/ocfs2/ocfs2.h | 4 +- fs/ocfs2/quota.h | 21 +++ fs/ocfs2/quota_global.c | 1 - fs/ocfs2/quota_local.c | 430 ++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 530 insertions(+), 32 deletions(-)fs/ocfs2/journal.c +64
-5@@ -45,6 +45,7 @@#include "slot_map.h" #include "super.h" #include "sysfile.h" +#include "quota.h" #include "buffer_head_io.h" @@ -52,7 +53,7 @@ DEFINE_SPINLOCK(trans_inc_lock);static int ocfs2_force_read_journal (struct inode *inode);static int ocfs2_recover_node (struct ocfs2_super *osb, -int node_num); +int node_num,int slot_num);static int __ocfs2_recovery_thread (void *arg);static int ocfs2_commit_cache (struct ocfs2_super *osb);static int ocfs2_wait_on_mount (struct ocfs2_super *osb);@@ -877,6 +878,7 @@ struct ocfs2_la_recovery_item {int lri_slot;struct ocfs2_dinode *lri_la_dinode;struct ocfs2_dinode *lri_tl_dinode; +struct ocfs2_quota_recovery *lri_qrec; };/* Does the second half of the recovery process. By this point, the @@ -897,6 +899,7 @@ void ocfs2_complete_recovery(struct work_struct *work)struct ocfs2_super *osb = journal->j_osb;struct ocfs2_dinode *la_dinode, *tl_dinode;struct ocfs2_la_recovery_item *item, *n; +struct ocfs2_quota_recovery *qrec;LIST_HEAD (tmp_la_list);mlog_entry_void ();@@ -942,6 +945,16 @@ void ocfs2_complete_recovery(struct work_struct *work)if (ret <0 )mlog_errno (ret); + qrec = item->lri_qrec; +if (qrec) { +mlog (0 ,"Recovering quota files" ); + ret =ocfs2_finish_quota_recovery (osb, qrec, + item->lri_slot); +if (ret <0 ) +mlog_errno (ret); +/* Recovery info is already freed now */ + } +kfree (item); }@@ -955,7 +968,8 @@ void ocfs2_complete_recovery(struct work_struct *work)static void ocfs2_queue_recovery_completion (struct ocfs2_journal *journal,int slot_num,struct ocfs2_dinode *la_dinode, -struct ocfs2_dinode *tl_dinode) +struct ocfs2_dinode *tl_dinode, +struct ocfs2_quota_recovery *qrec) {struct ocfs2_la_recovery_item *item;@@ -970,6 +984,9 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,if (tl_dinode)kfree (tl_dinode); +if (qrec) +ocfs2_free_quota_recovery (qrec); +mlog_errno (-ENOMEM );return ; }@@ -978,6 +995,7 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal, item->lri_la_dinode = la_dinode; item->lri_slot = slot_num; item->lri_tl_dinode = tl_dinode; + item->lri_qrec = qrec;spin_lock (&journal->j_lock);list_add_tail (&item->lri_list, &journal->j_la_cleanups);@@ -997,6 +1015,7 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb)ocfs2_queue_recovery_completion (journal, osb->slot_num, osb->local_alloc_copy, +NULL ,NULL );ocfs2_schedule_truncate_log_flush (osb,0 );@@ -1005,11 +1024,26 @@ void ocfs2_complete_mount_recovery(struct ocfs2_super *osb) } } +void ocfs2_complete_quota_recovery (struct ocfs2_super *osb) +{ +if (osb->quota_rec) { +ocfs2_queue_recovery_completion (osb->journal, + osb->slot_num, +NULL , +NULL , + osb->quota_rec); + osb->quota_rec =NULL ; + } +} +static int __ocfs2_recovery_thread (void *arg) { -int status, node_num; +int status, node_num, slot_num;struct ocfs2_super *osb = arg;struct ocfs2_recovery_map *rm = osb->recovery_map; +int *rm_quota =NULL ; +int rm_quota_used =0 , i; +struct ocfs2_quota_recovery *qrec;mlog_entry_void ();@@ -1018,6 +1052,11 @@ static int __ocfs2_recovery_thread(void *arg) goto bail; } + rm_quota =kzalloc (osb->max_slots *sizeof (int ),GFP_NOFS ); +if (!rm_quota) { + status = -ENOMEM ; + goto bail; + } restart: status =ocfs2_super_lock (osb,1 );if (status <0 ) {@@ -1031,8 +1070,28 @@ restart:* clear it until ocfs2_recover_node() has succeeded. */ node_num = rm->rm_entries[0 ];spin_unlock (&osb->osb_lock); - - status =ocfs2_recover_node (osb, node_num); +mlog (0 ,"checking node %d\n" , node_num); + slot_num =ocfs2_node_num_to_slot (osb, node_num); +if (slot_num == -ENOENT ) { + status =0 ; +mlog (0 ,"no slot for this node, so no recovery" +"required.\n" ); + goto skip_recovery; + } +mlog (0 ,"node %d was using slot %d\n" , node_num, slot_num); + +/* It is a bit subtle with quota recovery. We cannot do it +* immediately because we have to obtain cluster locks from +* quota files and we also don't want to just skip it because +* then quota usage would be out of sync until some node takes +* the slot. So we remember which nodes need quota recovery +* and when everything else is done, we recover quotas. */ +for (i =0 ; i < rm_quota_used && rm_quota[i] != slot_num; i++); +if (i == rm_quota_used) + rm_quota[rm_quota_used++] = slot_num; + + status =ocfs2_recover_node (osb, node_num, slot_num); +skip_recovery:if (!status) {ocfs2_recovery_map_clear (osb, node_num); }else {@@ -1056,11 +1115,22 @@ restart:ocfs2_super_unlock (osb,1 );
Quoted 16 lines
fs/ocfs2/journal.c restart: · line 1127
Quoted 10 lines
fs/ocfs2/journal.c +10
-0+/* Now it is right time to recover quotas... */ +for (i =0 ; i < rm_quota_used; i++) { + qrec =ocfs2_begin_quota_recovery (osb, rm_quota[i]); +if (IS_ERR (qrec)) { + status =PTR_ERR (qrec); +mlog_errno (status); + } +ocfs2_queue_recovery_completion (osb->journal, rm_quota[i], +NULL ,NULL , qrec); + }I think we want to do this *within* the super block cluster lock. What if a node mounts and then crashes (with quota file changes in it's journal) in between
ocfs2_super_unlock(osb, 1)and the call inocfs2_begin_quota_recovery()? We'd get the cluster lock, but the quota file info would be stale because we'd have an unrecovered slot.
I'm not sure I see the problem (which is probably because I miss
something in the recovery code). When node is using its local quota file, it has a cluster lock from it. So as soon as it has some modifications of it in its journal, it also holds its lock. So I thought that if this node crashes, noone can obtain lock from the quota file before the journal of the node is replayed. Or is it otherwise?
The only problem I can see is that if node doing recovery crashes in the
place you describe, data in the local quota file will remain unsynced until somebody starts using the slot. Maybe you meant this bug. But then I don't see how moving inside super block cluster lock will help it. Argh, this is nasty. Probably we'll have to do what you once proposed - i.e., scanning all slots in the cluster and check whether quota files don't need recovery in them...
Quoted 5 lines
Also, we call
ocfs2_queue_recovery_completion()here even when quota isn't enabled on the file system. Perhapsocfs2_begin_quota_recovery()should return NULL in that case and we can skip therecovery_completion. Otherwise I think we're just running it against an empty qrec, which seems like a bug too.
This might be slightly suboptimal but is handled just fine AFAICS.
Quoted 26 lines
Quoted 26 lines
fs/ocfs2/journal.c +26
-0+/* Load information we need for quota recovery into memory */ +struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery ( +struct ocfs2_super *osb, +int slot_num) +{ +unsigned int feature[MAXQUOTAS ] = {OCFS2_FEATURE_RO_COMPAT_USRQUOTA , +OCFS2_FEATURE_RO_COMPAT_GRPQUOTA }; +unsigned int ino[MAXQUOTAS ] = {LOCAL_USER_QUOTA_SYSTEM_INODE , +LOCAL_GROUP_QUOTA_SYSTEM_INODE }; +struct super_block *sb = osb->sb; +struct ocfs2_local_disk_dqinfo *ldinfo; +struct inode *lqinode; +struct buffer_head *bh; +int type; +int status; +struct ocfs2_quota_recovery *rec; + +mlog (ML_NOTICE ,"Beginning quota recovery in slot %u\n" , slot_num); + rec =ocfs2_alloc_quota_recovery (); +if (!rec) +return ERR_PTR (-ENOMEM ); +/* First init... */ + +for (type =0 ; type <MAXQUOTAS ; type++) { +if (!OCFS2_HAS_RO_COMPAT_FEATURE (sb, feature[type])) +continue ;
Quoted 8 lines
fs/ocfs2/journal.c
Quoted 5 lines
fs/ocfs2/journal.c +5
-0+ lqinode =ocfs2_get_system_file_inode (osb, ino[type], slot_num); +if (!lqinode) { + status = -ENOENT ; + goto out; + }Can we add a comment here saying that we've already recovered the journal, so local quota file meta data is up to date and can be trusted?
OK.
Quoted 5 lines
fs/ocfs2/journal.c
Quoted 2 lines
fs/ocfs2/journal.c +2
-0+ status =ocfs2_inode_lock_full (lqinode,NULL ,1 , +OCFS2_META_LOCK_NOQUEUE );I think you also want to add
OCFS2_META_LOCK_RECOVERYhere, otherwise we'll hang if another node dies before we get the lock.
Because our recovery thread would get stuck waiting for recovery, I see.
But I really have to be sure that the journal for the slot has been replayed... OK, but when this is done under super block cluster lock, noone can really start using the slot before we get to ocfs2_begin_quota_recovery() so that should be fine. We even should not need NOQUEUE because if someone starts using the slot, we would not start recovering it and thus would not get to ocfs2_begin_quota_recovery().
Quoted 186 lines
Quoted 186 lines
fs/ocfs2/journal.c +186
-0+/* Someone else is holding the lock? Then he must be +* doing the recovery. Just skip the file... */ +if (status == -EAGAIN ) { +mlog (ML_NOTICE ,"skipping quota recovery for slot %d " +"because quota file is locked.\n" , slot_num); + status =0 ; + goto out_put; + }else if (status <0 ) { +mlog_errno (status); + goto out_put; + } +/* Now read local header */ + bh =ocfs2_read_quota_block (lqinode,0 , &status); +if (!bh) { +mlog_errno (status); +mlog (ML_ERROR ,"failed to read quota file info header " +"(slot=%d type=%d)\n" , slot_num, type); + goto out_lock; + } + ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data + +OCFS2_LOCAL_INFO_OFF ); + status =ocfs2_recovery_load_quota (lqinode, ldinfo, type, + &rec->r_list[type]); +brelse (bh); +out_lock: +ocfs2_inode_unlock (lqinode,1 ); +out_put: +iput (lqinode); +if (status <0 ) +break ; + } +out: +if (status <0 ) { +ocfs2_free_quota_recovery (rec); + rec =ERR_PTR (status); + } +return rec; +} + +/* Sync changes in local quota file into global quota file and +* reinitialize local quota file. +* The function expects local quota file to be already locked and +* dqonoff_mutex locked. */ +static int ocfs2_recover_local_quota_file (struct inode *lqinode, +int type, +struct ocfs2_quota_recovery *rec) +{ +struct super_block *sb = lqinode->i_sb; +struct ocfs2_mem_dqinfo *oinfo =sb_dqinfo (sb, type)->dqi_priv; +struct ocfs2_local_disk_chunk *dchunk; +struct ocfs2_local_disk_dqblk *dqblk; +struct dquot *dquot; +handle_t *handle; +struct buffer_head *hbh =NULL , *qbh =NULL ; +int status =0 ; +int bit, chunk; +struct ocfs2_recovery_chunk *rchunk, *next; +qsize_t spacechange, inodechange; + +mlog_entry ("ino=%lu type=%u" , (unsigned long )lqinode->i_ino, type); + + status =ocfs2_lock_global_qf (oinfo,1 ); +if (status <0 ) + goto out; + +list_for_each_entry_safe (rchunk, next, &(rec->r_list[type]), rc_list) { + chunk = rchunk->rc_chunk; + hbh =ocfs2_read_quota_block (lqinode, +ol_quota_chunk_block (sb, chunk), + &status); +if (!hbh) { +mlog_errno (status); +break ; + } + dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data; +for_each_bit (bit, rchunk->rc_bitmap,ol_chunk_entries (sb)) { + qbh =ocfs2_read_quota_block (lqinode, +ol_dqblk_block (sb, chunk, bit), + &status); +if (!qbh) { +mlog_errno (status); +break ; + } + dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data + +ol_dqblk_block_off (sb, chunk, bit)); + dquot =dqget (sb,le64_to_cpu (dqblk->dqb_id), type); +if (!dquot) { + status = -EIO ; +mlog (ML_ERROR ,"Failed to get quota structure " +"for id %u, type %d. Cannot finish quota " +"file recovery.\n" , + (unsigned )le64_to_cpu (dqblk->dqb_id), + type); + goto out_put_bh; + } + handle =ocfs2_start_trans (OCFS2_SB (sb), +OCFS2_QSYNC_CREDITS ); +if (IS_ERR (handle)) { + status =PTR_ERR (handle); +mlog_errno (status); + goto out_put_dquot; + } +mutex_lock (&sb_dqopt (sb)->dqio_mutex); +spin_lock (&dq_data_lock); +/* Add usage from quota entry into quota changes +* of our node. Auxiliary variables are important +* due to signedness */ + spacechange =le64_to_cpu (dqblk->dqb_spacemod); + inodechange =le64_to_cpu (dqblk->dqb_inodemod); + dquot->dq_dqb.dqb_curspace += spacechange; + dquot->dq_dqb.dqb_curinodes += inodechange; +spin_unlock (&dq_data_lock); +/* We want to drop reference held by the crashed +* node. Since we have our own reference we know +* global structure actually won't be freed. */ + status =ocfs2_global_release_dquot (dquot); +if (status <0 ) { +mlog_errno (status); + goto out_commit; + } +/* Release local quota file entry */ + status =ocfs2_journal_access (handle, lqinode, + qbh,OCFS2_JOURNAL_ACCESS_WRITE ); +if (status <0 ) { +mlog_errno (status); + goto out_commit; + } +lock_buffer (qbh); +WARN_ON (!ocfs2_test_bit (bit, dchunk->dqc_bitmap)); +ocfs2_clear_bit (bit, dchunk->dqc_bitmap); +le32_add_cpu (&dchunk->dqc_free,1 ); +unlock_buffer (qbh); + status =ocfs2_journal_dirty (handle, qbh); +if (status <0 ) +mlog_errno (status); +out_commit: +mutex_unlock (&sb_dqopt (sb)->dqio_mutex); +ocfs2_commit_trans (OCFS2_SB (sb), handle); +out_put_dquot: +dqput (dquot); +out_put_bh: +brelse (qbh); +if (status <0 ) +break ; + } +brelse (hbh); +list_del (&rchunk->rc_list); +kfree (rchunk->rc_bitmap); +kfree (rchunk); +if (status <0 ) +break ; + } +ocfs2_unlock_global_qf (oinfo,1 ); +out: +if (status <0 ) +free_recovery_list (&(rec->r_list[type])); +mlog_exit (status); +return status; +} + +/* Recover local quota files for given node different from us */ +int ocfs2_finish_quota_recovery (struct ocfs2_super *osb, +struct ocfs2_quota_recovery *rec, +int slot_num) +{ +unsigned int ino[MAXQUOTAS ] = {LOCAL_USER_QUOTA_SYSTEM_INODE , +LOCAL_GROUP_QUOTA_SYSTEM_INODE }; +struct super_block *sb = osb->sb; +struct ocfs2_local_disk_dqinfo *ldinfo; +struct buffer_head *bh; +handle_t *handle; +int type; +int status =0 ; +struct inode *lqinode; +unsigned int flags; + +mlog (ML_NOTICE ,"Finishing quota recovery in slot %u\n" , slot_num); +mutex_lock (&sb_dqopt (sb)->dqonoff_mutex); +for (type =0 ; type <MAXQUOTAS ; type++) { +if (list_empty (&(rec->r_list[type]))) +continue ; +mlog (0 ,"Recovering quota in slot %d\n" , slot_num); + lqinode =ocfs2_get_system_file_inode (osb, ino[type], slot_num); +if (!lqinode) { + status = -ENOENT ; + goto out;
Quoted 8 lines
fs/ocfs2/journal.c
Quoted 5 lines
fs/ocfs2/journal.c +5
-0+ } + status =ocfs2_inode_lock_full (lqinode,NULL ,1 , +OCFS2_META_LOCK_NOQUEUE ); +/* Someone else is holding the lock? Then he must be +* doing the recovery. Just skip the file... */ Hmm, given that we have to do this for quota recovery completion, what's the point of all the work we do in
ocfs2_begin_quota_recovery()?
I case we are recovering our own slot, we have to know, which entries in
local quota file are just stale entries (used by the node before the crash) and which are really currently used because we sync and release stale entries while fs is mounted and node is normally using quota files...
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Jan Kara
reviewerPATCH 29/29 ocfs2: Enable quota accounting on mount, disable on umount
In reply to Jan KaraEnable 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.
--- fs/ocfs2/journal.c | 20 ++++- fs/ocfs2/ocfs2.h | 3 + fs/ocfs2/super.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+), 3 deletions(-)
fs/ocfs2/journal.c +17 -3
@@ -56,7 +56,7 @@ static int ocfs2_recover_node(struct ocfs2_super *osb, int node_num, int slot_num);static int __ocfs2_recovery_thread (void *arg);static int ocfs2_commit_cache (struct ocfs2_super *osb); -static int ocfs2_wait_on_mount (struct ocfs2_super *osb); +static int __ocfs2_wait_on_mount (struct ocfs2_super *osb,int quota);static int ocfs2_journal_toggle_dirty (struct ocfs2_super *osb,int dirty,int replayed); static int ocfs2_trylock_journal(struct ocfs2_super *osb,
@@ -65,6 +65,17 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb, int slot);static int ocfs2_commit_thread (void *arg); +static inline int ocfs2_wait_on_mount (struct ocfs2_super *osb) +{ +return __ocfs2_wait_on_mount (osb,0 ); +} + +static inline int ocfs2_wait_on_quotas (struct ocfs2_super *osb) +{ +return __ocfs2_wait_on_mount (osb,1 ); +} + +/* * The recovery_list is a simple linked list of node numbers to recover.
@@ -915,6 +926,8 @@ void ocfs2_complete_recovery(struct work_struct *work)mlog (0 ,"Complete recovery for slot %d\n" , item->lri_slot); +ocfs2_wait_on_quotas (osb); + la_dinode = item->lri_la_dinode;if (la_dinode) { mlog(0, "Clean up local alloc %llu\n",
@@ -1719,13 +1732,14 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb,return ret; } -static int ocfs2_wait_on_mount (struct ocfs2_super *osb) +static int __ocfs2_wait_on_mount (struct ocfs2_super *osb,int quota) {/* This check is good because ocfs2 will wait on our recovery * thread before changing it to something other than MOUNTED * or DISABLED. */ wait_event (osb->osb_mount_event, -atomic_read (&osb->vol_state) ==VOLUME_MOUNTED || + (!quota &&atomic_read (&osb->vol_state) ==VOLUME_MOUNTED ) || +atomic_read (&osb->vol_state) ==VOLUME_MOUNTED_QUOTAS ||atomic_read (&osb->vol_state) ==VOLUME_DISABLED );/* If there's an error on mount, then we may never get to the
fs/ocfs2/ocfs2.h +3 -0
@@ -161,6 +161,7 @@ enum ocfs2_vol_state {VOLUME_INIT =0 ,VOLUME_MOUNTED , +VOLUME_MOUNTED_QUOTAS ,VOLUME_DISMOUNTED , VOLUME_DISABLED };
@@ -195,6 +196,8 @@ enum ocfs2_mount_optionsOCFS2_MOUNT_LOCALFLOCKS =1 <<5 ,/* No cluster aware user file locks */ OCFS2_MOUNT_NOUSERXATTR =1 <<6 ,/* No user xattr */ OCFS2_MOUNT_INODE64 =1 <<7 ,/* Allow inode numbers > 2^32 */ +OCFS2_MOUNT_USRQUOTA =1 <<8 ,/* We support user quotas */ + OCFS2_MOUNT_GRPQUOTA = 1 << 9, /* We support group quotas */ };#define OCFS2_OSB_SOFT_RO 0x0001
fs/ocfs2/super.c +217 -0
295 lines not on this page · raw message
Signature · 1 line
1.5.2.4
PATCH 29/29 ocfs2: Enable quota accounting on mount, disable on umount
Quoted 4 lines · Jan Kara
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.
<snip>
Quoted 18 lines
fs/ocfs2/super.c +10
-0@@ -780,6 +950,23 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)atomic_set (&osb->vol_state,VOLUME_MOUNTED );wake_up (&osb->osb_mount_event); +/* Now we can initialize quotas because we can afford to wait +* for cluster locks recovery now. That also means that truncation +* log recovery can happen but that waits for proper quota setup */ +if (!(sb->s_flags &MS_RDONLY )) { + status =ocfs2_enable_quotas (osb); +if (status <0 ) { +mlog_errno (status); + goto read_super_error; + } + }
fs/ocfs2/super.c static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) · line 972
Quoted 10 lines
fs/ocfs2/super.c +7
-0+ +ocfs2_complete_quota_recovery (osb); + +/* Now we wake up again for processes waiting for quotas */ +atomic_set (&osb->vol_state,VOLUME_MOUNTED_QUOTAS ); +wake_up (&osb->osb_mount_event); +mlog_exit (status);return status;
This hunk still has the bug where you are jumping toread_super_error after setting sb->s_root. I'm guessing you have that fixed on your machine?
Joel
Signature · 9 lines
Life's Little Instruction Book #80 "Slow dance" Joel Becker Principal Software Developer Oracle E-mail: joel.becker at oracle.com Phone: (650) 506-8127
Jan Kara
reviewerPATCH 29/29 ocfs2: Enable quota accounting on mount, disable on umount
Quoted 32 lines · Joel Becker
Quoted 4 lines · Jan Kara
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.
<snip>
Quoted 24 lines
fs/ocfs2/super.c +16
-0@@ -780,6 +950,23 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)atomic_set (&osb->vol_state,VOLUME_MOUNTED );wake_up (&osb->osb_mount_event); +/* Now we can initialize quotas because we can afford to wait +* for cluster locks recovery now. That also means that truncation +* log recovery can happen but that waits for proper quota setup */ +if (!(sb->s_flags &MS_RDONLY )) { + status =ocfs2_enable_quotas (osb); +if (status <0 ) { +mlog_errno (status); + goto read_super_error; + } + } + +ocfs2_complete_quota_recovery (osb); + +/* Now we wake up again for processes waiting for quotas */ +atomic_set (&osb->vol_state,VOLUME_MOUNTED_QUOTAS ); +wake_up (&osb->osb_mount_event);
Quoted 8 lines
fs/ocfs2/super.c static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) · line 972
Quoted 4 lines
fs/ocfs2/super.c +1
-0+mlog_exit (status);return status;This hunk still has the bug where you are jumping to
read_super_errorafter settingsb->s_root. I'm guessing you have that fixed on your machine?
Not yet fixed but yeah, I have it on my todo list :). These patches went
out before you pointed out the bug to me. Thanks for the review.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR