#include "quota.h"
 #include "super.h"
 #include "util.h"
+#include "bmap.h"
 
 struct gfs2_gl_hash_bucket {
         struct hlist_head hb_list;
  * do_promote - promote as many requests as possible on the current queue
  * @gl: The glock
  * 
- * Returns: true if there is a blocked holder at the head of the list
+ * Returns: 1 if there is a blocked holder at the head of the list, or 2
+ *          if a type specific operation is underway.
  */
 
 static int do_promote(struct gfs2_glock *gl)
                                ret = glops->go_lock(gh);
                                spin_lock(&gl->gl_spin);
                                if (ret) {
+                                       if (ret == 1)
+                                               return 2;
                                        gh->gh_error = ret;
                                        list_del_init(&gh->gh_list);
                                        gfs2_holder_wake(gh);
        const struct gfs2_glock_operations *glops = gl->gl_ops;
        struct gfs2_holder *gh;
        unsigned state = ret & LM_OUT_ST_MASK;
+       int rv;
 
        spin_lock(&gl->gl_spin);
        state_change(gl, state);
                gfs2_demote_wake(gl);
        if (state != LM_ST_UNLOCKED) {
                if (glops->go_xmote_bh) {
-                       int rv;
                        spin_unlock(&gl->gl_spin);
                        rv = glops->go_xmote_bh(gl, gh);
                        if (rv == -EAGAIN)
                                goto out;
                        }
                }
-               do_promote(gl);
+               rv = do_promote(gl);
+               if (rv == 2)
+                       goto out_locked;
        }
 out:
        clear_bit(GLF_LOCK, &gl->gl_flags);
+out_locked:
        spin_unlock(&gl->gl_spin);
        gfs2_glock_put(gl);
 }
 __acquires(&gl->gl_spin)
 {
        struct gfs2_holder *gh = NULL;
+       int ret;
 
        if (test_and_set_bit(GLF_LOCK, &gl->gl_flags))
                return;
        } else {
                if (test_bit(GLF_DEMOTE, &gl->gl_flags))
                        gfs2_demote_wake(gl);
-               if (do_promote(gl) == 0)
+               ret = do_promote(gl);
+               if (ret == 0)
                        goto out;
+               if (ret == 2)
+                       return;
                gh = find_first_waiter(gl);
                gl->gl_target = gh->gh_state;
                if (!(gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)))
        }
 }
 
+void gfs2_glock_finish_truncate(struct gfs2_inode *ip)
+{
+       struct gfs2_glock *gl = ip->i_gl;
+       int ret;
+
+       ret = gfs2_truncatei_resume(ip);
+       gfs2_assert_withdraw(gl->gl_sbd, ret == 0);
+
+       spin_lock(&gl->gl_spin);
+       clear_bit(GLF_LOCK, &gl->gl_flags);
+       run_queue(gl, 1);
+       spin_unlock(&gl->gl_spin);
+}
+
 static const char *state2str(unsigned state)
 {
        switch(state) {
 
 void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl);
 void gfs2_reclaim_glock(struct gfs2_sbd *sdp);
 void gfs2_gl_hash_clear(struct gfs2_sbd *sdp);
+void gfs2_glock_finish_truncate(struct gfs2_inode *ip);
 
 int __init gfs2_glock_init(void);
 void gfs2_glock_exit(void);
 
 static int inode_go_lock(struct gfs2_holder *gh)
 {
        struct gfs2_glock *gl = gh->gh_gl;
+       struct gfs2_sbd *sdp = gl->gl_sbd;
        struct gfs2_inode *ip = gl->gl_object;
        int error = 0;
 
 
        if ((ip->i_diskflags & GFS2_DIF_TRUNC_IN_PROG) &&
            (gl->gl_state == LM_ST_EXCLUSIVE) &&
-           (gh->gh_state == LM_ST_EXCLUSIVE))
-               error = gfs2_truncatei_resume(ip);
+           (gh->gh_state == LM_ST_EXCLUSIVE)) {
+               spin_lock(&sdp->sd_trunc_lock);
+               if (list_empty(&ip->i_trunc_list))
+                       list_add(&sdp->sd_trunc_list, &ip->i_trunc_list);
+               spin_unlock(&sdp->sd_trunc_lock);
+               wake_up(&sdp->sd_quota_wait);
+               return 1;
+       }
 
        return error;
 }
 
        struct gfs2_alloc *i_alloc;
        u64 i_goal;     /* goal block for allocations */
        struct rw_semaphore i_rw_mutex;
+       struct list_head i_trunc_list;
        u32 i_entries;
        u32 i_diskflags;
        u8 i_height;
        spinlock_t sd_quota_spin;
        struct mutex sd_quota_mutex;
        wait_queue_head_t sd_quota_wait;
+       struct list_head sd_trunc_list;
+       spinlock_t sd_trunc_lock;
 
        unsigned int sd_quota_slots;
        unsigned int sd_quota_chunks;
 
 
        inode_init_once(&ip->i_inode);
        init_rwsem(&ip->i_rw_mutex);
+       INIT_LIST_HEAD(&ip->i_trunc_list);
        ip->i_alloc = NULL;
 }
 
 
        spin_lock_init(&sdp->sd_quota_spin);
        mutex_init(&sdp->sd_quota_mutex);
        init_waitqueue_head(&sdp->sd_quota_wait);
+       INIT_LIST_HEAD(&sdp->sd_trunc_list);
+       spin_lock_init(&sdp->sd_trunc_lock);
 
        spin_lock_init(&sdp->sd_log_lock);
 
 
        }
 }
 
+static void quotad_check_trunc_list(struct gfs2_sbd *sdp)
+{
+       struct gfs2_inode *ip;
+
+       while(1) {
+               ip = NULL;
+               spin_lock(&sdp->sd_trunc_lock);
+               if (!list_empty(&sdp->sd_trunc_list)) {
+                       ip = list_entry(sdp->sd_trunc_list.next,
+                                       struct gfs2_inode, i_trunc_list);
+                       list_del_init(&ip->i_trunc_list);
+               }
+               spin_unlock(&sdp->sd_trunc_lock);
+               if (ip == NULL)
+                       return;
+               gfs2_glock_finish_truncate(ip);
+       }
+}
+
 /**
  * gfs2_quotad - Write cached quota changes into the quota file
  * @sdp: Pointer to GFS2 superblock
        unsigned long quotad_timeo = 0;
        unsigned long t = 0;
        DEFINE_WAIT(wait);
+       int empty;
 
        while (!kthread_should_stop()) {
 
                /* FIXME: This should be turned into a shrinker */
                gfs2_quota_scan(sdp);
 
+               /* Check for & recover partially truncated inodes */
+               quotad_check_trunc_list(sdp);
+
                if (freezing(current))
                        refrigerator();
                t = min(quotad_timeo, statfs_timeo);
 
                prepare_to_wait(&sdp->sd_quota_wait, &wait, TASK_UNINTERRUPTIBLE);
-               t -= schedule_timeout(t);
+               spin_lock(&sdp->sd_trunc_lock);
+               empty = list_empty(&sdp->sd_trunc_list);
+               spin_unlock(&sdp->sd_trunc_lock);
+               if (empty)
+                       t -= schedule_timeout(t);
+               else
+                       t = 0;
                finish_wait(&sdp->sd_quota_wait, &wait);
        }