mm: write_cache_pages integrity fix
[safe/jmp/linux-2.6] / fs / dquot.c
index 7569633..61bfff6 100644 (file)
@@ -211,8 +211,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)
 {
@@ -415,10 +413,11 @@ out_dqlock:
        return ret;
 }
 
-static void dquot_destroy(struct dquot *dquot)
+void dquot_destroy(struct dquot *dquot)
 {
        kmem_cache_free(dquot_cachep, dquot);
 }
+EXPORT_SYMBOL(dquot_destroy);
 
 static inline void do_destroy_dquot(struct dquot *dquot)
 {
@@ -478,6 +477,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;
@@ -568,7 +602,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;
 
@@ -635,10 +669,11 @@ we_slept:
        spin_unlock(&dq_list_lock);
 }
 
-static struct dquot *dquot_alloc(struct super_block *sb, int type)
+struct dquot *dquot_alloc(struct super_block *sb, int type)
 {
        return kmem_cache_zalloc(dquot_cachep, GFP_NOFS);
 }
+EXPORT_SYMBOL(dquot_alloc);
 
 static struct dquot *get_empty_dquot(struct super_block *sb, int type)
 {
@@ -662,10 +697,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;
@@ -847,7 +900,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;
@@ -858,7 +912,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;
@@ -1182,17 +1237,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;
 }
@@ -1631,6 +1692,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)
@@ -1655,6 +1721,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_
@@ -1667,8 +1739,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;
 }
 
@@ -1715,25 +1785,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);
@@ -1993,25 +2069,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) {
@@ -2271,6 +2355,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);
@@ -2283,7 +2368,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);