fallocate and quota fixes
cover fallocate and quota fixes
This is a revised version of the patches required to properly fix the fallocate quota issue described in bz1174295
patch1: This patch supplies gfs2_quota_check() with the number of blocks
the caller intends to allocate in the current operation, resulting in a more accurate quota check.
patch2: gfs2_quota_check() and gfs2_inplace_reserve() return the number
of blocks available subject to quota limits and rgrp size respectively. These functions don't return error if atleast ap.min_target (if set) blocks are allocatable.
patch3: The fallocate() function uses the features of patch2 to determine
how many total blocks are available for allocation and uses them right away, instead of guessing/retrying inefficiently.
The behavior of quota enforcement is altered by this patchset.
i. Quotas are exceeded (a warning message is also issued to syslog) before
the actual usage blocks exceed the imposed limit. In fact, the actual usage can never exceed the limit. Whenever it is determined that the completion of an operation will cause a quota to exceed its limit, such an operation is aborted with -EDQUOT and a 'quota exceeded' message is dispatched. Note: When min_target is set and allowed blocks are >= min_target, we don't issue an error. It is assumed that the caller will only allocate the allowed blocks.
ii. The gfs2_write_calc_reserv()/calc_max_reserv() functions are used to
map between available blocks and the data bytes that can be written using them. Typically, for large files, some blocks are used up for metadata and only the remaining blocks can be used for data. Example:
To write only a handful of bytes that would easily fit in one block, we might have to allocate an extra bunch of intermediate metadata blocks. If we had only 1 block left in our allotted quota, this operation would likely fail.
The functions mentioned in ii. are not very efficient. They always compute the worst case number of extra blocks required and it is often the case that not all those extra blocks are used. We need to find a better algorithm to get a tighter estimate on the blocks needed for a given number of bytes.
I've run some basic tests and things seem to be holding up. The failing case in bz1174295 is fixed using this patchset. I'll do test build and pass it on to Nate to test with.
Abhi Das (3):
gfs2: perform quota checks against allocation parameters
gfs2: allow quota_check and inplace_reserve to return available blocks
gfs2: allow fallocate to max out quotas/fs efficiently
fs/gfs2/aops.c | 6 ++--- fs/gfs2/bmap.c | 2 +- fs/gfs2/file.c | 81 ++++++++++++++++++++++++++++++++++++-------------------- fs/gfs2/incore.h | 4 ++- fs/gfs2/inode.c | 18 +++++++------ fs/gfs2/quota.c | 54 +++++++++++++++++++++++++++---------- fs/gfs2/quota.h | 8 +++--- fs/gfs2/rgrp.c | 20 ++++++++++---- fs/gfs2/rgrp.h | 3 ++- fs/gfs2/xattr.c | 2 +- 10 files changed, 133 insertions(+), 65 deletions(-)
Signature · 1 line
1.8.1.4
PATCH 1/3 gfs2: perform quota checks against allocation parameters
Use struct gfs2_alloc_parms as an argument to gfs2_quota_check() and gfs2_quota_lock_check() to check for quota violations while accounting for the new blocks requested by the current operation in ap->target.
Previously, the number of new blocks requested during an operation were not accounted for during quota_check and would allow these operations to exceed quota. This was not very apparent since most operations allocated only 1 block at a time and quotas would get violated in the next operation. i.e. quota excess would only be by 1 block or so. With fallocate, (where we allocate a bunch of blocks at once) the quota excess is non-trivial and is addressed by this patch.
Resolves: rhbz#1174295
--- fs/gfs2/aops.c | 6 +++--- fs/gfs2/bmap.c | 2 +- fs/gfs2/file.c | 15 ++++++++------- fs/gfs2/incore.h | 2 +- fs/gfs2/inode.c | 18 ++++++++++-------- fs/gfs2/quota.c | 6 +++--- fs/gfs2/quota.h | 8 +++++--- fs/gfs2/xattr.c | 2 +- 8 files changed, 32 insertions(+), 27 deletions(-)
fs/gfs2/aops.c +3 -3
@@ -671,12 +671,12 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping,if (alloc_required) {struct gfs2_alloc_parms ap = { .aflags =0 , }; - error =gfs2_quota_lock_check (ip); + requested = data_blocks + ind_blocks; + ap.target = requested; + error =gfs2_quota_lock_check (ip, &ap);if (error) goto out_unlock; - requested = data_blocks + ind_blocks; - ap.target = requested; error =gfs2_inplace_reserve (ip, &ap);if (error) goto out_qunlock;
fs/gfs2/bmap.c +1 -1
@@ -1224,7 +1224,7 @@ static int do_grow(struct inode *inode, u64 size)if (gfs2_is_stuffed (ip) && (size > (sdp->sd_sb.sb_bsize -sizeof (struct gfs2_dinode )))) { - error =gfs2_quota_lock_check (ip); + error =gfs2_quota_lock_check (ip, &ap);if (error)return error;
fs/gfs2/file.c +8 -7
@@ -429,11 +429,11 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)if (ret) goto out_unlock; - ret =gfs2_quota_lock_check (ip); -if (ret) - goto out_unlock;gfs2_write_calc_reserv (ip,PAGE_CACHE_SIZE , &data_blocks, &ind_blocks); ap.target = data_blocks + ind_blocks; + ret =gfs2_quota_lock_check (ip, &ap); +if (ret) + goto out_unlock; ret =gfs2_inplace_reserve (ip, &ap);if (ret) goto out_quota_unlock;
@@ -827,13 +827,13 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t offset += bytes;continue ; } - error =gfs2_quota_lock_check (ip); -if (error) -return error; retry:gfs2_write_calc_reserv (ip, bytes, &data_blocks, &ind_blocks); - ap.target = data_blocks + ind_blocks; + + error =gfs2_quota_lock_check (ip, &ap); +if (error) +return error; error =gfs2_inplace_reserve (ip, &ap);if (error) {if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) {
@@ -841,6 +841,7 @@ retry: bytes &= bsize_mask;if (bytes ==0 ) bytes = sdp->sd_sb.sb_bsize; +gfs2_quota_unlock (ip); goto retry; } goto out_qunlock;
fs/gfs2/incore.h +1 -1
@@ -301,7 +301,7 @@ struct gfs2_blkreserv {* to the allocation code. */ struct gfs2_alloc_parms { -u32 target; +u64 target;u32 aflags; };
fs/gfs2/inode.c +10 -8
@@ -382,7 +382,7 @@ static int alloc_dinode(struct gfs2_inode *ip, u32 flags, unsigned *dblocks)struct gfs2_alloc_parms ap = { .target = *dblocks, .aflags = flags, };int error; - error =gfs2_quota_lock_check (ip); + error =gfs2_quota_lock_check (ip, &ap);if (error) goto out;
@@ -525,7 +525,7 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name,int error;if (da->nr_blocks) { - error =gfs2_quota_lock_check (dip); + error =gfs2_quota_lock_check (dip, &ap);if (error) goto fail_quota_locks;
@@ -953,7 +953,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,if (da.nr_blocks) {struct gfs2_alloc_parms ap = { .target = da.nr_blocks, }; - error =gfs2_quota_lock_check (dip); + error =gfs2_quota_lock_check (dip, &ap);if (error) goto out_gunlock;
@@ -1470,7 +1470,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,if (da.nr_blocks) {struct gfs2_alloc_parms ap = { .target = da.nr_blocks, }; - error =gfs2_quota_lock_check (ndip); + error =gfs2_quota_lock_check (ndip, &ap);if (error) goto out_gunlock;
@@ -1669,6 +1669,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr)kuid_t ouid, nuid;kgid_t ogid, ngid;int error; +struct gfs2_alloc_parms ap; ouid = inode->i_uid; ogid = inode->i_gid;
@@ -1696,9 +1697,11 @@ static int setattr_chown(struct inode *inode, struct iattr *attr)if (error) goto out; + ap.target =gfs2_get_inode_blocks (&ip->i_inode); +if (!uid_eq (ouid,NO_UID_QUOTA_CHANGE ) || !gid_eq (ogid,NO_GID_QUOTA_CHANGE )) { - error =gfs2_quota_check (ip, nuid, ngid); + error =gfs2_quota_check (ip, nuid, ngid, &ap);if (error) goto out_gunlock_q; }
@@ -1713,9 +1716,8 @@ static int setattr_chown(struct inode *inode, struct iattr *attr)if (!uid_eq (ouid,NO_UID_QUOTA_CHANGE ) || !gid_eq (ogid,NO_GID_QUOTA_CHANGE )) { -u64 blocks =gfs2_get_inode_blocks (&ip->i_inode); -gfs2_quota_change (ip, -blocks, ouid, ogid); -gfs2_quota_change (ip, blocks, nuid, ngid); +gfs2_quota_change (ip, -ap.target, ouid, ogid); +gfs2_quota_change (ip, ap.target, nuid, ngid); } out_end_trans:
fs/gfs2/quota.c +3 -3
@@ -1094,7 +1094,8 @@ static int print_message(struct gfs2_quota_data *qd, char *type)return 0 ; } -int gfs2_quota_check (struct gfs2_inode *ip,kuid_t uid,kgid_t gid) +int gfs2_quota_check (struct gfs2_inode *ip,kuid_t uid,kgid_t gid, +struct gfs2_alloc_parms *ap) {struct gfs2_sbd *sdp =GFS2_SB (&ip->i_inode);struct gfs2_quota_data *qd;
@@ -1117,14 +1118,13 @@ int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) value = (s64 )be64_to_cpu (qd->qd_qb.qb_value);spin_lock (&qd_lock); - value += qd->qd_change; + value += qd->qd_change + ap->target;spin_unlock (&qd_lock);if (be64_to_cpu (qd->qd_qb.qb_limit) && (s64 )be64_to_cpu (qd->qd_qb.qb_limit) < value) {print_message (qd,"exceeded" );quota_send_warning (qd->qd_id, sdp->sd_vfs->s_dev,QUOTA_NL_BHARDWARN ); - error = -EDQUOT ;break ; } else if (be64_to_cpu(qd->qd_qb.qb_warn) &&
fs/gfs2/quota.h +5 -3
@@ -24,7 +24,8 @@ extern void gfs2_quota_unhold(struct gfs2_inode *ip);extern int gfs2_quota_lock (struct gfs2_inode *ip,kuid_t uid,kgid_t gid);extern void gfs2_quota_unlock (struct gfs2_inode *ip); -extern int gfs2_quota_check (struct gfs2_inode *ip,kuid_t uid,kgid_t gid); +extern int gfs2_quota_check (struct gfs2_inode *ip,kuid_t uid,kgid_t gid, +struct gfs2_alloc_parms *ap);extern void gfs2_quota_change (struct gfs2_inode *ip,s64 change,kuid_t uid,kgid_t gid);
@@ -37,7 +38,8 @@ extern int gfs2_quotad(void *data);extern void gfs2_wake_up_statfs (struct gfs2_sbd *sdp); -static inline int gfs2_quota_lock_check (struct gfs2_inode *ip) +static inline int gfs2_quota_lock_check (struct gfs2_inode *ip, +struct gfs2_alloc_parms *ap) {struct gfs2_sbd *sdp =GFS2_SB (&ip->i_inode);int ret;
@@ -48,7 +50,7 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip)return ret;if (sdp->sd_args.ar_quota !=GFS2_QUOTA_ON )return 0 ; - ret =gfs2_quota_check (ip, ip->i_inode.i_uid, ip->i_inode.i_gid); + ret =gfs2_quota_check (ip, ip->i_inode.i_uid, ip->i_inode.i_gid, ap);if (ret)gfs2_quota_unlock (ip);return ret;
fs/gfs2/xattr.c +1 -1
@@ -732,7 +732,7 @@ static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er,if (error)return error; - error =gfs2_quota_lock_check (ip); + error =gfs2_quota_lock_check (ip, &ap);if (error)return error;
Signature · 1 line
1.8.1.4
PATCH 2/3 gfs2: allow quota_check and inplace_reserve to return available blocks
In reply to Abhi Dasstruct gfs2_alloc_parms is passed to gfs2_quota_check() and gfs2_inplace_reserve() with ap->target containing the number of blocks being requested for allocation in the current operation.
We add a new field to struct gfs2_alloc_parms called 'allowed'. gfs2_quota_check() and gfs2_inplace_reserve() return the max blocks allowed by quota and the max blocks allowed by the chosen rgrp respectively in 'allowed'.
A new field 'min_target', when non-zero, tells gfs2_quota_check() and gfs2_inplace_reserve() to not return -EDQUOT/-ENOSPC when there are atleast 'min_target' blocks allowable/available. The assumption is that the caller is ok with just 'min_target' blocks and will likely proceed with allocating them.
--- fs/gfs2/incore.h | 2 ++ fs/gfs2/quota.c | 52 +++++++++++++++++++++++++++++++++++++++------------- fs/gfs2/rgrp.c | 20 +++++++++++++++----- fs/gfs2/rgrp.h | 3 ++- 4 files changed, 58 insertions(+), 19 deletions(-)
fs/gfs2/incore.h +2 -0
@@ -302,7 +302,9 @@ struct gfs2_blkreserv {*/ struct gfs2_alloc_parms {u64 target; +u32 min_target;u32 aflags; +u64 allowed; };enum {
fs/gfs2/quota.c +39 -13
83 lines not on this page · raw message
fs/gfs2/rgrp.c +15 -5
@@ -1946,10 +1946,18 @@ static inline int fast_to_acquire(struct gfs2_rgrpd *rgd)* @ip: the inode to reserve space for * @ap: the allocation parameters * -* Returns: errno +* We try our best to find an rgrp that has at least ap->target blocks +* available. After a couple of passes (loops == 2), the prospects of finding +* such an rgrp diminish. At this stage, we return the first rgrp that has +* atleast ap->min_target blocks available. Either way, we set ap->allowed to +* the number of blocks available in the chosen rgrp. +* +* Returns: 0 on success, +* -ENOMEM if a suitable rgrp can't be found +* errno otherwise */ -int gfs2_inplace_reserve (struct gfs2_inode *ip,const struct gfs2_alloc_parms *ap) +int gfs2_inplace_reserve (struct gfs2_inode *ip,struct gfs2_alloc_parms *ap) {struct gfs2_sbd *sdp =GFS2_SB (&ip->i_inode);struct gfs2_rgrpd *begin =NULL ;
@@ -2012,7 +2020,7 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, const struct gfs2_alloc_parms *a/* Skip unuseable resource groups */ if ((rs->rs_rbm.rgd->rd_flags & (GFS2_RGF_NOALLOC |GFS2_RDF_ERROR )) || - (ap->target > rs->rs_rbm.rgd->rd_extfail_pt)) + (loops ==0 && ap->target > rs->rs_rbm.rgd->rd_extfail_pt)) goto skip_rgrp;if (sdp->sd_args.ar_rgrplvb)
@@ -2027,11 +2035,13 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, const struct gfs2_alloc_parms *a goto check_rgrp;/* If rgrp has enough free space, use it */ -if (rs->rs_rbm.rgd->rd_free_clone >= ap->target) { +if (rs->rs_rbm.rgd->rd_free_clone >= ap->target || + (loops ==2 && ap->min_target && + rs->rs_rbm.rgd->rd_free_clone >= ap->min_target)) { ip->i_rgd = rs->rs_rbm.rgd; + ap->allowed = ip->i_rgd->rd_free_clone;return 0 ; } - check_rgrp:/* Check for unlinked inodes which can be reclaimed */ if (rs->rs_rbm.rgd->rd_flags &GFS2_RDF_CHECK )
fs/gfs2/rgrp.h +2 -1
@@ -41,7 +41,8 @@ extern void gfs2_rgrp_go_unlock(struct gfs2_holder *gh);extern struct gfs2_alloc *gfs2_alloc_get (struct gfs2_inode *ip);#define GFS2_AF_ORLOV 1 -extern int gfs2_inplace_reserve (struct gfs2_inode *ip,const struct gfs2_alloc_parms *ap); +extern int gfs2_inplace_reserve (struct gfs2_inode *ip, +struct gfs2_alloc_parms *ap);extern void gfs2_inplace_release (struct gfs2_inode *ip); extern int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *n,
Signature · 1 line
1.8.1.4
PATCH 3/3 gfs2: allow fallocate to max out quotas/fs efficiently
In reply to Abhi DasWe can quickly get an estimate of how many blocks are available for allocation restricted by quota and fs size respectively, using the ap->allowed field in the gfs2_alloc_parms structure. gfs2_quota_check() and gfs2_inplace_reserve() provide these values.
Once we have the total number of blocks available to us, we can compute how many bytes of data can be written using those blocks instead of guessing inefficiently.
--- fs/gfs2/file.c | 70 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 23 deletions(-)
fs/gfs2/file.c +47 -23
@@ -765,22 +765,30 @@ out:brelse (dibh);return error; } - -static void calc_max_reserv (struct gfs2_inode *ip,loff_t max,loff_t *len, -unsigned int *data_blocks,unsigned int *ind_blocks) +/** +* calc_max_reserv() - Reverse of write_calc_reserv. Given a number of +* blocks, determine how many bytes can be written. +* @ip: The inode in question. +* @len: Max cap of bytes. What we return in *len must be <= this. +* @data_blocks: Compute and return the number of data blocks needed +* @ind_blocks: Compute and return the number of indirect blocks needed +* @max_blocks: The total blocks available to work with. +* +* Returns: void, but @len, @data_blocks and @ind_blocks are filled in. +*/ +static void calc_max_reserv (struct gfs2_inode *ip,loff_t *len, +unsigned int *data_blocks,unsigned int *ind_blocks, +unsigned int max_blocks) { +loff_t max = *len;const struct gfs2_sbd *sdp =GFS2_SB (&ip->i_inode); -unsigned int max_blocks = ip->i_rgd->rd_free_clone;unsigned int tmp, max_data = max_blocks -3 * (sdp->sd_max_height -1 );for (tmp = max_data; tmp > sdp->sd_diptrs;) { tmp =DIV_ROUND_UP (tmp, sdp->sd_inptrs); max_data -= tmp; } -/* This calculation isn't the exact reverse of gfs2_write_calc_reserve, -so it might end up with fewer data blocks */ -if (max_data <= *data_blocks) -return ; + *data_blocks = max_data; *ind_blocks = max_blocks - max_data; *len = ((loff_t )max_data -3 ) << sdp->sd_sb.sb_bsize_shift;
@@ -797,7 +805,7 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_tstruct gfs2_inode *ip =GFS2_I (inode);struct gfs2_alloc_parms ap = { .aflags =0 , };unsigned int data_blocks =0 , ind_blocks =0 , rblocks; -loff_t bytes, max_bytes; +loff_t bytes, max_bytes, max_blks =UINT_MAX ;int error;const loff_t pos = offset;const loff_t count = len;
@@ -819,6 +827,9 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_tgfs2_size_hint (file, offset, len); +gfs2_write_calc_reserv (ip,PAGE_SIZE , &data_blocks, &ind_blocks); + ap.min_target = data_blocks + ind_blocks; +while (len >0 ) {if (len < bytes) bytes = len;
@@ -827,28 +838,41 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t offset += bytes;continue ; } -retry: + +/* We need to determine how many bytes we can actually +* fallocate without exceeding quota or going over the +* end of the fs. We start off optimistically by assuming +* we can write max_bytes */ + max_bytes = (len > max_chunk_size) ? max_chunk_size : len; + +/* Since max_bytes is most likely a theoretical max, we +* calculate a more realistic 'bytes' to serve as a good +* starting point for the number of bytes we may be able +* to write */ gfs2_write_calc_reserv (ip, bytes, &data_blocks, &ind_blocks); ap.target = data_blocks + ind_blocks; error =gfs2_quota_lock_check (ip, &ap);if (error)return error; +/* ap.allowed tells us how many blocks quota will allow +* us to write. Check if this reduces max_blks */ +if (ap.allowed && ap.allowed < max_blks) + max_blks = ap.allowed; + error =gfs2_inplace_reserve (ip, &ap); -if (error) { -if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) { - bytes >>=1 ; - bytes &= bsize_mask; -if (bytes ==0 ) - bytes = sdp->sd_sb.sb_bsize; -gfs2_quota_unlock (ip); - goto retry; - } +if (error) goto out_qunlock; - } - max_bytes = bytes; -calc_max_reserv (ip, (len > max_chunk_size)? max_chunk_size: len, - &max_bytes, &data_blocks, &ind_blocks); + +/* check if the selected rgrp limits our max_blks further */ +if (ap.allowed && ap.allowed < max_blks) + max_blks = ap.allowed; + +/* Almost done. Calculate bytes that can be written using +* max_blks. We also recompute max_bytes, data_blocks and +* ind_blocks */ +calc_max_reserv (ip, &max_bytes, &data_blocks, + &ind_blocks, max_blks); rblocks =RES_DINODE + ind_blocks +RES_STATFS +RES_QUOTA +RES_RG_HDR +gfs2_rg_blocks (ip, data_blocks + ind_blocks);
Signature · 1 line
1.8.1.4
cover fallocate and quota fixes
In reply to Abhi DasHi,
Steve.
Quoted 60 lines · Abhi Das
This is a revised version of the patches required to properly fix the fallocate quota issue described in bz1174295
patch1: This patch supplies
gfs2_quota_check()with the number of blocks
the caller intends to allocate in the current operation, resulting in a more accurate quota check.patch2:
gfs2_quota_check()andgfs2_inplace_reserve()return the number
of blocks available subject to quota limits and rgrp size respectively. These functions don't return error if atleast ap.min_target (if set) blocks are allocatable.patch3: The
fallocate()function uses the features of patch2 to determine
how many total blocks are available for allocation and uses them right away, instead of guessing/retrying inefficiently.The behavior of quota enforcement is altered by this patchset.
i. Quotas are exceeded (a warning message is also issued to syslog) before
the actual usage blocks exceed the imposed limit. In fact, the actual usage can never exceed the limit. Whenever it is determined that the completion of an operation will cause a quota to exceed its limit, such an operation is aborted with-EDQUOTand a 'quota exceeded' message is dispatched. Note: Whenmin_targetis set and allowed blocks are >=min_target, we don't issue an error. It is assumed that the caller will only allocate the allowed blocks.
ii. The gfs2_write_calc_reserv()/calc_max_reserv() functions are used to
map between available blocks and the data bytes that can be written using them. Typically, for large files, some blocks are used up for metadata and only the remaining blocks can be used for data. Example:
To write only a handful of bytes that would easily fit in one block, we might have to allocate an extra bunch of intermediate metadata blocks. If we had only 1 block left in our allotted quota, this operation would likely fail.The functions mentioned in ii. are not very efficient. They always compute the worst case number of extra blocks required and it is often the case that not all those extra blocks are used. We need to find a better algorithm to get a tighter estimate on the blocks needed for a given number of bytes.
I've run some basic tests and things seem to be holding up. The failing case in bz1174295 is fixed using this patchset. I'll do test build and pass it on to Nate to test with.
Abhi Das (3):
gfs2: perform quota checks against allocation parameters
gfs2: allowquota_checkandinplace_reserveto return available blocks
gfs2: allow fallocate to max out quotas/fs efficientlyfs/gfs2/aops.c | 6 ++--- fs/gfs2/bmap.c | 2 +- fs/gfs2/file.c | 81 ++++++++++++++++++++++++++++++++++++-------------------- fs/gfs2/incore.h | 4 ++- fs/gfs2/inode.c | 18 +++++++------ fs/gfs2/quota.c | 54 +++++++++++++++++++++++++++---------- fs/gfs2/quota.h | 8 +++--- fs/gfs2/rgrp.c | 20 ++++++++++---- fs/gfs2/rgrp.h | 3 ++- fs/gfs2/xattr.c | 2 +- 10 files changed, 133 insertions(+), 65 deletions(-)
cover fallocate and quota fixes
In reply to Abhi Das----- Original Message -----
Quoted 61 lines
This is a revised version of the patches required to properly fix the fallocate quota issue described in bz1174295
patch1: This patch supplies
gfs2_quota_check()with the number of blocks
the caller intends to allocate in the current operation, resulting in a more accurate quota check.patch2:
gfs2_quota_check()andgfs2_inplace_reserve()return the number
of blocks available subject to quota limits and rgrp size respectively. These functions don't return error if atleast ap.min_target (if set) blocks are allocatable.patch3: The
fallocate()function uses the features of patch2 to determine
how many total blocks are available for allocation and uses them right away, instead of guessing/retrying inefficiently.The behavior of quota enforcement is altered by this patchset.
i. Quotas are exceeded (a warning message is also issued to syslog) before
the actual usage blocks exceed the imposed limit. In fact, the actual usage can never exceed the limit. Whenever it is determined that the completion of an operation will cause a quota to exceed its limit, such an operation is aborted with-EDQUOTand a 'quota exceeded' message is dispatched. Note: Whenmin_targetis set and allowed blocks are >=min_target, we don't issue an error. It is assumed that the caller will only allocate the allowed blocks.
ii. The gfs2_write_calc_reserv()/calc_max_reserv() functions are used to
map between available blocks and the data bytes that can be written using them. Typically, for large files, some blocks are used up for metadata and only the remaining blocks can be used for data. Example:
To write only a handful of bytes that would easily fit in one block, we might have to allocate an extra bunch of intermediate metadata blocks. If we had only 1 block left in our allotted quota, this operation would likely fail.The functions mentioned in ii. are not very efficient. They always compute the worst case number of extra blocks required and it is often the case that not all those extra blocks are used. We need to find a better algorithm to get a tighter estimate on the blocks needed for a given number of bytes.
I've run some basic tests and things seem to be holding up. The failing case in bz1174295 is fixed using this patchset. I'll do test build and pass it on to Nate to test with.
Abhi Das (3):
gfs2: perform quota checks against allocation parameters
gfs2: allowquota_checkandinplace_reserveto return available blocks
gfs2: allow fallocate to max out quotas/fs efficientlyfs/gfs2/aops.c | 6 ++--- fs/gfs2/bmap.c | 2 +- fs/gfs2/file.c | 81 ++++++++++++++++++++++++++++++++++++-------------------- fs/gfs2/incore.h | 4 ++- fs/gfs2/inode.c | 18 +++++++------ fs/gfs2/quota.c | 54 +++++++++++++++++++++++++++---------- fs/gfs2/quota.h | 8 +++--- fs/gfs2/rgrp.c | 20 ++++++++++---- fs/gfs2/rgrp.h | 3 ++- fs/gfs2/xattr.c | 2 +- 10 files changed, 133 insertions(+), 65 deletions(-)
Quoted 2 lines
--
1.8.1.4
Hi,
ACK to all three.
Now pushed to the for-next branch of the linux-gfs2.git git tree.
Regards,
Bob Peterson