2008-10-31 21:27:28

by Mingming Cao

[permalink] [raw]
Subject: [PATCH V2 1/3] quota: Add reservation support for delayed block allocation

Quota: Add quota reservation support

Delayed allocation defers the block allocation at the dirty pages
flush-out time, doing quota charge/check at that time is too late.
But we can't charge the quota blocks until blocks are really allocated,
otherwise users could get overcharged after reboot from system crash.

This patch adds quota reservation for delayed llocation. Quota blocks
are reserved in memory, inode and quota won't gets dirtied until later
block allocation time.

Based on Jan Kara's 64 bit quota changes, tested on 2.6.28-rc2.

Signed-off-by: Mingming Cao <[email protected]>


---
fs/dquot.c | 113 ++++++++++++++++++++++++++++++++++-------------
include/linux/quota.h | 2
include/linux/quotaops.h | 30 ++++++++++++
3 files changed, 115 insertions(+), 30 deletions(-)

Index: linux-2.6.28-rc2/fs/dquot.c
===================================================================
--- linux-2.6.28-rc2.orig/fs/dquot.c 2008-10-29 13:26:57.000000000 -0700
+++ linux-2.6.28-rc2/fs/dquot.c 2008-10-30 14:41:50.000000000 -0700
@@ -841,6 +841,11 @@ static inline void dquot_incr_space(stru
dquot->dq_dqb.dqb_curspace += number;
}

+static inline void dquot_resv_space(struct dquot *dquot, qsize_t number)
+{
+ dquot->dq_dqb.dqb_rsvspace += number;
+}
+
static inline void dquot_decr_inodes(struct dquot *dquot, qsize_t number)
{
if (dquot->dq_dqb.dqb_curinodes > number)
@@ -1069,13 +1074,18 @@ static int check_idq(struct dquot *dquot
/* needs dq_data_lock */
static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype)
{
+ qsize_t tspace;
+
*warntype = QUOTA_NL_NOWARN;
if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type) ||
test_bit(DQ_FAKE_B, &dquot->dq_flags))
return QUOTA_OK;

+ tspace = dquot->dq_dqb.dqb_curspace + dquot->dq_dqb.dqb_rsvspace
+ + space;
+
if (dquot->dq_dqb.dqb_bhardlimit &&
- dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bhardlimit &&
+ tspace > dquot->dq_dqb.dqb_bhardlimit &&
!ignore_hardlimit(dquot)) {
if (!prealloc)
*warntype = QUOTA_NL_BHARDWARN;
@@ -1083,7 +1093,7 @@ static int check_bdq(struct dquot *dquot
}

if (dquot->dq_dqb.dqb_bsoftlimit &&
- dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bsoftlimit &&
+ tspace > dquot->dq_dqb.dqb_bsoftlimit &&
dquot->dq_dqb.dqb_btime && get_seconds() >= dquot->dq_dqb.dqb_btime &&
!ignore_hardlimit(dquot)) {
if (!prealloc)
@@ -1092,7 +1102,7 @@ static int check_bdq(struct dquot *dquot
}

if (dquot->dq_dqb.dqb_bsoftlimit &&
- dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bsoftlimit &&
+ tspace > dquot->dq_dqb.dqb_bsoftlimit &&
dquot->dq_dqb.dqb_btime == 0) {
if (!prealloc) {
*warntype = QUOTA_NL_BSOFTWARN;
@@ -1227,48 +1237,90 @@ void vfs_dq_drop(struct inode *inode)
/*
* This operation can block, but only after everything is updated
*/
-int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
+int __dquot_alloc_space(struct inode *inode, qsize_t number,
+ int warn, int reserve)
{
- int cnt, ret = NO_QUOTA;
+ int cnt, ret = QUOTA_OK;
char warntype[MAXQUOTAS];

- /* First test before acquiring mutex - solves deadlocks when we
- * re-enter the quota code and are already holding the mutex */
- if (IS_NOQUOTA(inode)) {
-out_add:
- inode_add_bytes(inode, number);
- return QUOTA_OK;
- }
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
warntype[cnt] = QUOTA_NL_NOWARN;

- down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
- if (IS_NOQUOTA(inode)) { /* Now we can do reliable test... */
- up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
- goto out_add;
- }
spin_lock(&dq_data_lock);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (inode->i_dquot[cnt] == NODQUOT)
continue;
- if (check_bdq(inode->i_dquot[cnt], number, warn, warntype+cnt) == NO_QUOTA)
- goto warn_put_all;
+ if (check_bdq(inode->i_dquot[cnt], number, warn, warntype+cnt)
+ == NO_QUOTA) {
+ ret = NO_QUOTA;
+ goto out_unlock;
+ }
}
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (inode->i_dquot[cnt] == NODQUOT)
continue;
- dquot_incr_space(inode->i_dquot[cnt], number);
+ if (reserve)
+ dquot_resv_space(inode->i_dquot[cnt], number);
+ else
+ dquot_incr_space(inode->i_dquot[cnt], number);
}
- inode_add_bytes(inode, number);
- ret = QUOTA_OK;
-warn_put_all:
- spin_unlock(&dq_data_lock);
- if (ret == QUOTA_OK)
- /* Dirtify all the dquots - this can block when journalling */
- for (cnt = 0; cnt < MAXQUOTAS; cnt++)
- if (inode->i_dquot[cnt])
- mark_dquot_dirty(inode->i_dquot[cnt]);
+out_unlock:
flush_warnings(inode->i_dquot, warntype);
+ spin_unlock(&dq_data_lock);
+ return ret;
+}
+
+int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
+{
+ int cnt, ret = QUOTA_OK;
+
+ /*
+ * First test before acquiring mutex - solves deadlocks when we
+ * re-enter the quota code and are already holding the mutex
+ */
+ if (IS_NOQUOTA(inode)) {
+ inode_add_bytes(inode, number);
+ return ret;
+ }
+
+ down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+ if (IS_NOQUOTA(inode)) {
+ /* Now we can do reliable test... */
+ up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+ inode_add_bytes(inode, number);
+ return ret;
+ }
+
+ ret = __dquot_alloc_space(inode, number, warn, 0);
+ if (ret == NO_QUOTA) {
+ up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+ return ret;
+ }
+
+ /* Dirtify all the dquots - this can block when journalling */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+ if (inode->i_dquot[cnt])
+ mark_dquot_dirty(inode->i_dquot[cnt]);
+ up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+ inode_add_bytes(inode, number);
+ return ret;
+}
+
+int dquot_reserve_space(struct inode *inode, qsize_t number, int warn)
+{
+ int ret = QUOTA_OK;
+
+ if (IS_NOQUOTA(inode))
+ return ret;
+
+ down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+ if (IS_NOQUOTA(inode)) {
+ /* Now we can do reliable test... */
+ up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+ return ret;
+ }
+
+ ret = __dquot_alloc_space(inode, number, warn, 1);
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
return ret;
}
@@ -1958,7 +2010,7 @@ static void do_get_dqblk(struct dquot *d
spin_lock(&dq_data_lock);
di->dqb_bhardlimit = stoqb(dm->dqb_bhardlimit);
di->dqb_bsoftlimit = stoqb(dm->dqb_bsoftlimit);
- di->dqb_curspace = dm->dqb_curspace;
+ di->dqb_curspace = dm->dqb_curspace + dm->dqb_rsvspace;
di->dqb_ihardlimit = dm->dqb_ihardlimit;
di->dqb_isoftlimit = dm->dqb_isoftlimit;
di->dqb_curinodes = dm->dqb_curinodes;
@@ -2297,6 +2349,7 @@ EXPORT_SYMBOL(dquot_alloc_space);
EXPORT_SYMBOL(dquot_alloc_inode);
EXPORT_SYMBOL(dquot_free_space);
EXPORT_SYMBOL(dquot_free_inode);
+EXPORT_SYMBOL(dquot_reserve_space);
EXPORT_SYMBOL(dquot_transfer);
EXPORT_SYMBOL(vfs_dq_transfer);
EXPORT_SYMBOL(vfs_dq_quota_on_remount);
Index: linux-2.6.28-rc2/include/linux/quota.h
===================================================================
--- linux-2.6.28-rc2.orig/include/linux/quota.h 2008-10-29 13:56:50.000000000 -0700
+++ linux-2.6.28-rc2/include/linux/quota.h 2008-10-30 14:41:35.000000000 -0700
@@ -186,6 +186,7 @@ struct mem_dqblk {
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 */
+ qsize_t dqb_rsvspace; /* current reserved space for delalloc*/
qsize_t dqb_ihardlimit; /* absolute limit on allocated inodes */
qsize_t dqb_isoftlimit; /* preferred inode limit */
qsize_t dqb_curinodes; /* current # allocated inodes */
@@ -291,6 +292,7 @@ struct dquot_operations {
int (*release_dquot) (struct dquot *); /* Quota is going to be deleted from disk */
int (*mark_dirty) (struct dquot *); /* Dquot is marked dirty */
int (*write_info) (struct super_block *, int); /* Write of quota "superblock" */
+ int (*reserve_space) (struct inode *, qsize_t, int); /* reserve quota for delayed block allocation */
};

/* Operations handling requests from userspace */
Index: linux-2.6.28-rc2/include/linux/quotaops.h
===================================================================
--- linux-2.6.28-rc2.orig/include/linux/quotaops.h 2008-10-29 13:26:57.000000000 -0700
+++ linux-2.6.28-rc2/include/linux/quotaops.h 2008-10-30 14:41:35.000000000 -0700
@@ -176,6 +176,16 @@ static inline int vfs_dq_alloc_space(str
return ret;
}

+static inline int vfs_dq_reserve_space(struct inode *inode, qsize_t nr)
+{
+ if (sb_any_quota_active(inode->i_sb)) {
+ /* Used space is updated in alloc_space() */
+ if (inode->i_sb->dq_op->reserve_space(inode, nr, 0) == NO_QUOTA)
+ return 1;
+ }
+ return 0;
+}
+
static inline int vfs_dq_alloc_inode(struct inode *inode)
{
if (sb_any_quota_active(inode->i_sb)) {
@@ -327,6 +337,11 @@ static inline int vfs_dq_alloc_space(str
return 0;
}

+static inline int vfs_dq_reserve_space(struct inode *inode, qsize_t nr)
+{
+ return 0;
+}
+
static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr)
{
inode_sub_bytes(inode, nr);
@@ -358,12 +373,19 @@ static inline int vfs_dq_alloc_block_nod
nr << inode->i_sb->s_blocksize_bits);
}

+
static inline int vfs_dq_alloc_block(struct inode *inode, qsize_t nr)
{
return vfs_dq_alloc_space(inode,
nr << inode->i_sb->s_blocksize_bits);
}

+static inline int vfs_dq_reserve_block(struct inode *inode, qsize_t nr)
+{
+ return vfs_dq_reserve_space(inode,
+ nr << inode->i_sb->s_blocksize_bits);
+}
+
static inline void vfs_dq_free_block_nodirty(struct inode *inode, qsize_t nr)
{
vfs_dq_free_space_nodirty(inode, nr << inode->i_sb->s_blocksize_bits);
@@ -392,6 +414,7 @@ static inline void vfs_dq_free_block(str
#define DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr) \
vfs_dq_alloc_block_nodirty(inode, nr)
#define DQUOT_ALLOC_BLOCK(inode, nr) vfs_dq_alloc_block(inode, nr)
+#define DQUOT_RESERVE_BLOCK(inode, nr) vfs_dq_reserve_block(inode, nr)
#define DQUOT_ALLOC_INODE(inode) vfs_dq_alloc_inode(inode)
#define DQUOT_FREE_SPACE_NODIRTY(inode, nr) \
vfs_dq_free_space_nodirty(inode, nr)




2008-11-05 01:15:22

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH V2 1/3] quota: Add reservation support for delayed block allocation

On Fri, 31 Oct 2008 14:27:22 -0700
Mingming Cao <[email protected]> wrote:

> +int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
> +{
> + int cnt, ret = QUOTA_OK;
> +
> + /*
> + * First test before acquiring mutex - solves deadlocks when we
> + * re-enter the quota code and are already holding the mutex
> + */
> + if (IS_NOQUOTA(inode)) {
> + inode_add_bytes(inode, number);
> + return ret;
> + }
> +
> + down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
> + if (IS_NOQUOTA(inode)) {
> + /* Now we can do reliable test... */
> + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
> + inode_add_bytes(inode, number);
> + return ret;
> + }
> +
> + ret = __dquot_alloc_space(inode, number, warn, 0);
> + if (ret == NO_QUOTA) {
> + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
> + return ret;
> + }
> +
> + /* Dirtify all the dquots - this can block when journalling */
> + for (cnt = 0; cnt < MAXQUOTAS; cnt++)
> + if (inode->i_dquot[cnt])
> + mark_dquot_dirty(inode->i_dquot[cnt]);
> + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
> + inode_add_bytes(inode, number);
> + return ret;
> +}

I'm going to have to call "ug" on that code.

Multiple return points per function really is a maintenance problem.
It's a great source of code duplication, locking errors and resource
leaks as the code evolves.

Can we please rework this code (and any other similar code here) to use
the usual `goto out' pattern?


int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
{
int cnt, ret = QUOTA_OK;

/*
* First test before acquiring mutex - solves deadlocks when we
* re-enter the quota code and are already holding the mutex
*/
if (IS_NOQUOTA(inode))
goto out;

down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
if (IS_NOQUOTA(inode)) /* Now we can do reliable test... */
goto out_unlock;

ret = __dquot_alloc_space(inode, number, warn, 0);

if (ret == NO_QUOTA) {
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
return ret;
}

<<

I don't know what to do here - did we really want to skip the
inode_add_bytes? If so, we could do

number = 0;
goto out_unlock;

>>

/* Dirtify all the dquots - this can block when journalling */
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if (inode->i_dquot[cnt])
mark_dquot_dirty(inode->i_dquot[cnt]);
out_unlock:
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
out:
inode_add_bytes(inode, number);
return ret;
}

or something like that.



2008-11-06 23:28:15

by Mingming Cao

[permalink] [raw]
Subject: Re: [PATCH V2 1/3] quota: Add reservation support for delayed block allocation


On Tue, 2008-11-04 at 17:14 -0800, Andrew Morton wrote:
> On Fri, 31 Oct 2008 14:27:22 -0700
> Mingming Cao <[email protected]> wrote:
>
> > +int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
> > +{
> > + int cnt, ret = QUOTA_OK;
> > +
> > + /*
> > + * First test before acquiring mutex - solves deadlocks when we
> > + * re-enter the quota code and are already holding the mutex
> > + */
> > + if (IS_NOQUOTA(inode)) {
> > + inode_add_bytes(inode, number);
> > + return ret;
> > + }
> > +
> > + down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
> > + if (IS_NOQUOTA(inode)) {
> > + /* Now we can do reliable test... */
> > + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
> > + inode_add_bytes(inode, number);
> > + return ret;
> > + }
> > +
> > + ret = __dquot_alloc_space(inode, number, warn, 0);
> > + if (ret == NO_QUOTA) {
> > + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
> > + return ret;
> > + }
> > +
> > + /* Dirtify all the dquots - this can block when journalling */
> > + for (cnt = 0; cnt < MAXQUOTAS; cnt++)
> > + if (inode->i_dquot[cnt])
> > + mark_dquot_dirty(inode->i_dquot[cnt]);
> > + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
> > + inode_add_bytes(inode, number);
> > + return ret;
> > +}
>
> I'm going to have to call "ug" on that code.
>
> Multiple return points per function really is a maintenance problem.
> It's a great source of code duplication, locking errors and resource
> leaks as the code evolves.
>
> Can we please rework this code (and any other similar code here) to use
> the usual `goto out' pattern?
>
>

Ok, I was following the same style in the quota code, but I will do
that.
> int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
> {
> int cnt, ret = QUOTA_OK;
>
> /*
> * First test before acquiring mutex - solves deadlocks when we
> * re-enter the quota code and are already holding the mutex
> */
> if (IS_NOQUOTA(inode))
> goto out;
>
> down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
> if (IS_NOQUOTA(inode)) /* Now we can do reliable test... */
> goto out_unlock;
>
> ret = __dquot_alloc_space(inode, number, warn, 0);
>
> if (ret == NO_QUOTA) {
> up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
> return ret;
> }
>
> <<
>
> I don't know what to do here - did we really want to skip the
> inode_add_bytes?

Yes, this is the failure case. In case the request exceeds the quota
limit, we will quit with error without block allocation, so the inode
bytes update should be skipped.

> If so, we could do
>
> number = 0;
> goto out_unlock;
>
> >>
>
> /* Dirtify all the dquots - this can block when journalling */
> for (cnt = 0; cnt < MAXQUOTAS; cnt++)
> if (inode->i_dquot[cnt])
> mark_dquot_dirty(inode->i_dquot[cnt]);
> out_unlock:
> up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
> out:
> inode_add_bytes(inode, number);
> return ret;
> }
>
> or something like that.
>
>

Attached is the incremental fixes. I will post the updated series
later.

Thanks,
---
fs/dquot.c | 40 +++++++++++++++++-----------------------
include/linux/quotaops.h | 2 +-
2 files changed, 18 insertions(+), 24 deletions(-)

Index: linux-2.6.28-rc2/fs/dquot.c
===================================================================
--- linux-2.6.28-rc2.orig/fs/dquot.c 2008-11-06 12:28:22.000000000 -0800
+++ linux-2.6.28-rc2/fs/dquot.c 2008-11-06 12:49:07.000000000 -0800
@@ -1276,33 +1276,28 @@ int dquot_alloc_space(struct inode *inod

/*
* First test before acquiring mutex - solves deadlocks when we
- * re-enter the quota code and are already holding the mutex
- */
- if (IS_NOQUOTA(inode)) {
- inode_add_bytes(inode, number);
- return ret;
- }
+ * re-enter the quota code and are already holding the mutex
+ */
+ if (IS_NOQUOTA(inode))
+ goto out;

down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
- if (IS_NOQUOTA(inode)) {
- /* Now we can do reliable test... */
- up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
- inode_add_bytes(inode, number);
- return ret;
- }
+ if (IS_NOQUOTA(inode))
+ goto out_unlock;

ret = __dquot_alloc_space(inode, number, warn, 0);
- if (ret == NO_QUOTA) {
- up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
- return ret;
- }
+ if (ret == NO_QUOTA)
+ goto out_unlock;

/* Dirtify all the dquots - this can block when journalling */
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if (inode->i_dquot[cnt])
mark_dquot_dirty(inode->i_dquot[cnt]);
+out_unlock:
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
- inode_add_bytes(inode, number);
+out:
+ if (ret == QUOTA_OK)
+ inode_add_bytes(inode, number);
return ret;
}

@@ -1311,17 +1306,16 @@ int dquot_reserve_space(struct inode *in
int ret = QUOTA_OK;

if (IS_NOQUOTA(inode))
- return ret;
+ goto out;

down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
- if (IS_NOQUOTA(inode)) {
- /* Now we can do reliable test... */
- up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
- return ret;
- }
+ if (IS_NOQUOTA(inode))
+ goto out_unlock;

ret = __dquot_alloc_space(inode, number, warn, 1);
+out_unlock:
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+out:
return ret;
}

Index: linux-2.6.28-rc2/include/linux/quotaops.h
===================================================================
--- linux-2.6.28-rc2.orig/include/linux/quotaops.h 2008-11-06 12:28:22.000000000 -0800
+++ linux-2.6.28-rc2/include/linux/quotaops.h 2008-11-06 12:48:24.000000000 -0800
@@ -383,7 +383,7 @@ static inline int vfs_dq_alloc_block(str
static inline int vfs_dq_reserve_block(struct inode *inode, qsize_t nr)
{
return vfs_dq_reserve_space(inode,
- nr << inode->i_sb->s_blocksize_bits);
+ nr << inode->i_blkbits);
}

static inline void vfs_dq_free_block_nodirty(struct inode *inode, qsize_t nr)






2008-11-06 23:36:39

by Mingming Cao

[permalink] [raw]
Subject: [PATCH V3 2/3] quota: Add quota claim and release reserved quota blocks operations

quota: Add quota reservation claim and released operations

Reserved quota will be claimed at the block allocation time. Over-booked
quota could be returned back with the release callback function.

Signed-off-by: Mingming Cao <[email protected]>
---
fs/dquot.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/quota.h | 4 +-
include/linux/quotaops.h | 55 +++++++++++++++++++++++++++++
3 files changed, 145 insertions(+), 1 deletion(-)

Index: linux-2.6.28-rc2/include/linux/quota.h
===================================================================
--- linux-2.6.28-rc2.orig/include/linux/quota.h 2008-11-06 13:36:42.000000000 -0800
+++ linux-2.6.28-rc2/include/linux/quota.h 2008-11-06 14:03:52.000000000 -0800
@@ -292,7 +292,9 @@ struct dquot_operations {
int (*release_dquot) (struct dquot *); /* Quota is going to be deleted from disk */
int (*mark_dirty) (struct dquot *); /* Dquot is marked dirty */
int (*write_info) (struct super_block *, int); /* Write of quota "superblock" */
- int (*reserve_space) (struct inode *, qsize_t, int); /* reserve quota for delayed block allocation */
+ int (*reserve_space) (struct inode *, qsize_t, int); /* reserve quota for delayed alloc */
+ int (*claim_space) (struct inode *, qsize_t); /* claim reserved quota for delayed alloc */
+ void (*release_rsv) (struct inode *, qsize_t); /* release rsved quota for delayed alloc */
};

/* Operations handling requests from userspace */
Index: linux-2.6.28-rc2/include/linux/quotaops.h
===================================================================
--- linux-2.6.28-rc2.orig/include/linux/quotaops.h 2008-11-06 13:37:04.000000000 -0800
+++ linux-2.6.28-rc2/include/linux/quotaops.h 2008-11-06 14:03:52.000000000 -0800
@@ -28,6 +28,11 @@ 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, qsize_t number);

+int dquot_reserve_space(struct inode *inode, qsize_t number, int prealloc);
+int dquot_claim_space(struct inode *inode, qsize_t number);
+void dquot_release_reserved_space(struct inode *inode, qsize_t number);
+
+
int dquot_free_space(struct inode *inode, qsize_t number);
int dquot_free_inode(const struct inode *inode, qsize_t number);

@@ -196,6 +201,31 @@ static inline int vfs_dq_alloc_inode(str
return 0;
}

+/*
+ * Convert in-memory reserved quotas to real consumed quotas
+ */
+static inline int vfs_dq_claim_space(struct inode *inode, qsize_t nr)
+{
+ if (sb_any_quota_active(inode->i_sb)) {
+ if (inode->i_sb->dq_op->claim_space(inode, nr) == NO_QUOTA)
+ return 1;
+ } else
+ inode_add_bytes(inode, nr);
+
+ mark_inode_dirty(inode);
+ return 0;
+}
+
+/*
+ * Release reserved (in-memory) quotas
+ */
+static inline
+void vfs_dq_release_reservation_space(struct inode *inode, qsize_t nr)
+{
+ if (sb_any_quota_active(inode->i_sb))
+ inode->i_sb->dq_op->release_rsv(inode, nr);
+}
+
static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr)
{
if (sb_any_quota_active(inode->i_sb))
@@ -342,6 +372,17 @@ static inline int vfs_dq_reserve_space(s
return 0;
}

+static inline int vfs_dq_claim_space(struct inode *inode, qsize_t nr)
+{
+ return vfs_dq_alloc_space(inode, nr);
+}
+
+static inline
+int vfs_dq_release_reservation_space(struct inode *inode, qsize_t nr)
+{
+ return 0;
+}
+
static inline void vfs_dq_free_space_nodirty(struct inode *inode, qsize_t nr)
{
inode_sub_bytes(inode, nr);
@@ -386,6 +427,18 @@ static inline int vfs_dq_reserve_block(s
nr << inode->i_blkbits);
}

+static inline int vfs_dq_claim_block(struct inode *inode, qsize_t nr)
+{
+ return vfs_dq_claim_space(inode,
+ nr << inode->i_blkbits);
+}
+
+static inline
+void vfs_dq_release_reservation(struct inode *inode, qsize_t nr)
+{
+ vfs_dq_release_reservation_space(inode, nr << inode->i_blkbits);
+}
+
static inline void vfs_dq_free_block_nodirty(struct inode *inode, qsize_t nr)
{
vfs_dq_free_space_nodirty(inode, nr << inode->i_sb->s_blocksize_bits);
@@ -415,6 +468,8 @@ static inline void vfs_dq_free_block(str
vfs_dq_alloc_block_nodirty(inode, nr)
#define DQUOT_ALLOC_BLOCK(inode, nr) vfs_dq_alloc_block(inode, nr)
#define DQUOT_RESERVE_BLOCK(inode, nr) vfs_dq_reserve_block(inode, nr)
+#define DQUOT_CLAIM_BLOCK(inode, nr) vfs_dq_claim_block(inode, nr)
+#define DQUOT_RELEASE_RSV_BLOCK(inode, nr) vfs_dq_release_reservation(inode, nr)
#define DQUOT_ALLOC_INODE(inode) vfs_dq_alloc_inode(inode)
#define DQUOT_FREE_SPACE_NODIRTY(inode, nr) \
vfs_dq_free_space_nodirty(inode, nr)
Index: linux-2.6.28-rc2/fs/dquot.c
===================================================================
--- linux-2.6.28-rc2.orig/fs/dquot.c 2008-11-06 13:37:04.000000000 -0800
+++ linux-2.6.28-rc2/fs/dquot.c 2008-11-06 14:03:52.000000000 -0800
@@ -846,6 +846,24 @@ static inline void dquot_resv_space(stru
dquot->dq_dqb.dqb_rsvspace += number;
}

+/*
+ * Claim reserved quota space
+ */
+static int dquot_claim_reserved_space(struct dquot *dquot,
+ qsize_t number)
+{
+ if (dquot->dq_dqb.dqb_rsvspace < number) {
+ printk(KERN_WARNING "Reserved quota %llu is not enough for"
+ "request %llu bytes\n",
+ (unsigned long long)dquot->dq_dqb.dqb_rsvspace, number);
+ return 1;
+ }
+
+ dquot->dq_dqb.dqb_curspace += number;
+ dquot->dq_dqb.dqb_rsvspace -= number;
+ return 0;
+}
+
static inline void dquot_decr_inodes(struct dquot *dquot, qsize_t number)
{
if (dquot->dq_dqb.dqb_curinodes > number)
@@ -1319,6 +1337,73 @@ out:
return ret;
}

+int dquot_claim_space(struct inode *inode, qsize_t number)
+{
+ int cnt;
+ int ret = QUOTA_OK;
+
+ if (IS_NOQUOTA(inode)) {
+ inode_add_bytes(inode, number);
+ return ret;
+ }
+
+ down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+ if (IS_NOQUOTA(inode)) {
+ up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+ inode_add_bytes(inode, number);
+ return ret;
+ }
+
+ /* Claim reserved quotas to allocated quotas */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] != NODQUOT)
+ ret = dquot_claim_reserved_space(inode->i_dquot[cnt],
+ number);
+ }
+ if (ret == NO_QUOTA) {
+ up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+ return ret;
+ }
+ /* Dirtify all the dquots - this can block when journalling */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+ if (inode->i_dquot[cnt])
+ mark_dquot_dirty(inode->i_dquot[cnt]);
+ up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+
+ /* Update inode bytes */
+ inode_add_bytes(inode, number);
+ return ret;
+}
+
+/*
+ * Release reserved quota space
+ */
+void dquot_release_reserved_space(struct inode *inode, qsize_t number)
+{
+ int cnt;
+ struct dquot *dquot;
+
+ if (IS_NOQUOTA(inode))
+ goto out;
+
+ down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+ if (IS_NOQUOTA(inode))
+ goto out_unlock;
+
+ /* Release reserved dquots */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] != NODQUOT) {
+ dquot = inode->i_dquot[cnt];
+ dquot->dq_dqb.dqb_rsvspace -= number;
+ }
+ }
+
+out_unlock:
+ up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
+out:
+ return;
+}
+
/*
* This operation can block, but only after everything is updated
*/
@@ -2344,6 +2429,8 @@ EXPORT_SYMBOL(dquot_alloc_inode);
EXPORT_SYMBOL(dquot_free_space);
EXPORT_SYMBOL(dquot_free_inode);
EXPORT_SYMBOL(dquot_reserve_space);
+EXPORT_SYMBOL(dquot_claim_space);
+EXPORT_SYMBOL(dquot_release_reserved_space);
EXPORT_SYMBOL(dquot_transfer);
EXPORT_SYMBOL(vfs_dq_transfer);
EXPORT_SYMBOL(vfs_dq_quota_on_remount);