summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/dquot.c81
-rw-r--r--fs/ext3/super.c10
-rw-r--r--fs/quota.c3
-rw-r--r--include/linux/quota.h6
4 files changed, 58 insertions, 42 deletions
diff --git a/fs/dquot.c b/fs/dquot.c
index e3bd99508c39..557ad24485ab 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -274,16 +274,25 @@ static void wait_on_dquot(struct dquot *dquot)
#define mark_dquot_dirty(dquot) ((dquot)->dq_sb->dq_op->mark_dirty(dquot))
-/* No locks needed here as ANY_DQUOT_DIRTY is used just by sync and so the
- * worst what can happen is that dquot is not written by concurrent sync... */
int dquot_mark_dquot_dirty(struct dquot *dquot)
{
- set_bit(DQ_MOD_B, &(dquot)->dq_flags);
- set_bit(DQF_ANY_DQUOT_DIRTY_B, &(sb_dqopt((dquot)->dq_sb)->
- info[(dquot)->dq_type].dqi_flags));
+ spin_lock(&dq_list_lock);
+ if (!test_and_set_bit(DQ_MOD_B, &dquot->dq_flags))
+ list_add(&dquot->dq_dirty, &sb_dqopt(dquot->dq_sb)->
+ info[dquot->dq_type].dqi_dirty_list);
+ spin_unlock(&dq_list_lock);
return 0;
}
+/* This function needs dq_list_lock */
+static inline int clear_dquot_dirty(struct dquot *dquot)
+{
+ if (!test_and_clear_bit(DQ_MOD_B, &dquot->dq_flags))
+ return 0;
+ list_del_init(&dquot->dq_dirty);
+ return 1;
+}
+
void mark_info_dirty(struct super_block *sb, int type)
{
set_bit(DQF_INFO_DIRTY_B, &sb_dqopt(sb)->info[type].dqi_flags);
@@ -328,11 +337,17 @@ int dquot_commit(struct dquot *dquot)
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
down(&dqopt->dqio_sem);
- clear_bit(DQ_MOD_B, &dquot->dq_flags);
+ spin_lock(&dq_list_lock);
+ if (!clear_dquot_dirty(dquot)) {
+ spin_unlock(&dq_list_lock);
+ goto out_sem;
+ }
+ spin_unlock(&dq_list_lock);
/* Inactive dquot can be only if there was error during read/init
* => we have better not writing it */
if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
+out_sem:
up(&dqopt->dqio_sem);
if (info_dirty(&dqopt->info[dquot->dq_type]))
dquot->dq_sb->dq_op->write_info(dquot->dq_sb, dquot->dq_type);
@@ -392,42 +407,38 @@ static void invalidate_dquots(struct super_block *sb, int type)
int vfs_quota_sync(struct super_block *sb, int type)
{
- struct list_head *head;
+ struct list_head *dirty;
struct dquot *dquot;
struct quota_info *dqopt = sb_dqopt(sb);
int cnt;
down(&dqopt->dqonoff_sem);
-restart:
- /* At this point any dirty dquot will definitely be written so we can clear
- dirty flag from info */
- spin_lock(&dq_data_lock);
- for (cnt = 0; cnt < MAXQUOTAS; cnt++)
- if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt))
- clear_bit(DQF_ANY_DQUOT_DIRTY_B, &dqopt->info[cnt].dqi_flags);
- spin_unlock(&dq_data_lock);
- spin_lock(&dq_list_lock);
- list_for_each(head, &inuse_list) {
- dquot = list_entry(head, struct dquot, dq_inuse);
- if (sb && dquot->dq_sb != sb)
- continue;
- if (type != -1 && dquot->dq_type != type)
- continue;
- if (!dquot_dirty(dquot))
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (type != -1 && cnt != type)
continue;
- /* Dirty and inactive can be only bad dquot... */
- if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
+ if (!sb_has_quota_enabled(sb, cnt))
continue;
- /* Now we have active dquot from which someone is holding reference so we
- * can safely just increase use count */
- atomic_inc(&dquot->dq_count);
- dqstats.lookups++;
+ spin_lock(&dq_list_lock);
+ dirty = &dqopt->info[cnt].dqi_dirty_list;
+ while (!list_empty(dirty)) {
+ dquot = list_entry(dirty->next, struct dquot, dq_dirty);
+ /* Dirty and inactive can be only bad dquot... */
+ if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
+ clear_dquot_dirty(dquot);
+ continue;
+ }
+ /* Now we have active dquot from which someone is
+ * holding reference so we can safely just increase
+ * use count */
+ atomic_inc(&dquot->dq_count);
+ dqstats.lookups++;
+ spin_unlock(&dq_list_lock);
+ sb->dq_op->write_dquot(dquot);
+ dqput(dquot);
+ spin_lock(&dq_list_lock);
+ }
spin_unlock(&dq_list_lock);
- sb->dq_op->write_dquot(dquot);
- dqput(dquot);
- goto restart;
}
- spin_unlock(&dq_list_lock);
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt)
@@ -515,7 +526,7 @@ we_slept:
goto we_slept;
}
/* Clear flag in case dquot was inactive (something bad happened) */
- clear_bit(DQ_MOD_B, &dquot->dq_flags);
+ clear_dquot_dirty(dquot);
if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
spin_unlock(&dq_list_lock);
dquot_release(dquot);
@@ -544,6 +555,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
INIT_LIST_HEAD(&dquot->dq_free);
INIT_LIST_HEAD(&dquot->dq_inuse);
INIT_HLIST_NODE(&dquot->dq_hash);
+ INIT_LIST_HEAD(&dquot->dq_dirty);
dquot->dq_sb = sb;
dquot->dq_type = type;
atomic_set(&dquot->dq_count, 1);
@@ -1373,6 +1385,7 @@ static int vfs_quota_on_file(struct file *f, int type, int format_id)
dqopt->ops[type] = fmt->qf_ops;
dqopt->info[type].dqi_format = fmt;
+ INIT_LIST_HEAD(&dqopt->info[type].dqi_dirty_list);
down(&dqopt->dqio_sem);
if ((error = dqopt->ops[type]->read_file_info(sb, type)) < 0) {
up(&dqopt->dqio_sem);
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index 7f8cd966de41..3ce34d52bdf4 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -2174,14 +2174,16 @@ static int ext3_write_dquot(struct dquot *dquot)
return ret;
}
-static int ext3_mark_dquot_dirty(struct dquot * dquot)
+static int ext3_mark_dquot_dirty(struct dquot *dquot)
{
/* Are we journalling quotas? */
- if (EXT3_SB(dquot->dq_sb)->s_qf_names[0] ||
- EXT3_SB(dquot->dq_sb)->s_qf_names[1])
+ if (EXT3_SB(dquot->dq_sb)->s_qf_names[USRQUOTA] ||
+ EXT3_SB(dquot->dq_sb)->s_qf_names[GRPQUOTA]) {
+ dquot_mark_dquot_dirty(dquot);
return ext3_write_dquot(dquot);
- else
+ } else {
return dquot_mark_dquot_dirty(dquot);
+ }
}
static int ext3_write_info(struct super_block *sb, int type)
diff --git a/fs/quota.c b/fs/quota.c
index ce929f581b53..867fe35e5433 100644
--- a/fs/quota.c
+++ b/fs/quota.c
@@ -114,9 +114,10 @@ restart:
list_for_each(head, &super_blocks) {
struct super_block *sb = list_entry(head, struct super_block, s_list);
+ /* This test just improves performance so it needn't be reliable... */
for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++)
if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt)
- && info_any_dquot_dirty(&sb_dqopt(sb)->info[cnt]))
+ && info_any_dirty(&sb_dqopt(sb)->info[cnt]))
dirty = 1;
if (!dirty)
continue;
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 7f43ae55e891..bbd9134abaf3 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -163,6 +163,7 @@ struct quota_format_type;
struct mem_dqinfo {
struct quota_format_type *dqi_format;
+ struct list_head dqi_dirty_list; /* List of dirty dquots */
unsigned long dqi_flags;
unsigned int dqi_bgrace;
unsigned int dqi_igrace;
@@ -176,13 +177,11 @@ struct super_block;
#define DQF_MASK 0xffff /* Mask for format specific flags */
#define DQF_INFO_DIRTY_B 16
-#define DQF_ANY_DQUOT_DIRTY_B 17
#define DQF_INFO_DIRTY (1 << DQF_INFO_DIRTY_B) /* Is info dirty? */
-#define DQF_ANY_DQUOT_DIRTY (1 << DQF_ANY_DQUOT_DIRTY_B) /* Is any dquot dirty? */
extern void mark_info_dirty(struct super_block *sb, int type);
#define info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags)
-#define info_any_dquot_dirty(info) test_bit(DQF_ANY_DQUOT_DIRTY_B, &(info)->dqi_flags)
+#define info_any_dquot_dirty(info) (!list_empty(&(info)->dqi_dirty_list))
#define info_any_dirty(info) (info_dirty(info) || info_any_dquot_dirty(info))
#define sb_dqopt(sb) (&(sb)->s_dquot)
@@ -213,6 +212,7 @@ struct dquot {
struct hlist_node dq_hash; /* Hash list in memory */
struct list_head dq_inuse; /* List of all quotas */
struct list_head dq_free; /* Free list element */
+ struct list_head dq_dirty; /* List of dirty dquots */
struct semaphore dq_lock; /* dquot IO lock */
atomic_t dq_count; /* Use count */
wait_queue_head_t dq_wait_unused; /* Wait queue for dquot to become unused */