include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / fs / nilfs2 / segment.c
index 689deb9..6a7dbd8 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/kthread.h>
 #include <linux/crc32.h>
 #include <linux/pagevec.h>
+#include <linux/slab.h>
 #include "nilfs.h"
 #include "btnode.h"
 #include "page.h"
@@ -141,7 +142,7 @@ int nilfs_init_transaction_cache(void)
 }
 
 /**
- * nilfs_detroy_transaction_cache - destroy the cache for transaction info
+ * nilfs_destroy_transaction_cache - destroy the cache for transaction info
  *
  * nilfs_destroy_transaction_cache() frees the slab cache for the struct
  * nilfs_transaction_info.
@@ -201,7 +202,7 @@ static int nilfs_prepare_segment_lock(struct nilfs_transaction_info *ti)
  * This function allocates a nilfs_transaction_info struct to keep context
  * information on it.  It is initialized and hooked onto the current task in
  * the outermost call.  If a pre-allocated struct is given to @ti, it is used
- * instead; othewise a new struct is assigned from a slab.
+ * instead; otherwise a new struct is assigned from a slab.
  *
  * When @vacancy_check flag is set, this function will check the amount of
  * free space, and will wait for the GC to reclaim disk space if low capacity.
@@ -1273,48 +1274,69 @@ static int nilfs_segctor_collect_blocks(struct nilfs_sc_info *sci, int mode)
        return err;
 }
 
+/**
+ * nilfs_segctor_begin_construction - setup segment buffer to make a new log
+ * @sci: nilfs_sc_info
+ * @nilfs: nilfs object
+ */
 static int nilfs_segctor_begin_construction(struct nilfs_sc_info *sci,
                                            struct the_nilfs *nilfs)
 {
-       struct nilfs_segment_buffer *segbuf;
+       struct nilfs_segment_buffer *segbuf, *prev;
        __u64 nextnum;
-       int err;
+       int err, alloc = 0;
 
-       if (list_empty(&sci->sc_segbufs)) {
-               segbuf = nilfs_segbuf_new(sci->sc_super);
-               if (unlikely(!segbuf))
-                       return -ENOMEM;
-               list_add(&segbuf->sb_list, &sci->sc_segbufs);
-       } else
-               segbuf = NILFS_FIRST_SEGBUF(&sci->sc_segbufs);
+       segbuf = nilfs_segbuf_new(sci->sc_super);
+       if (unlikely(!segbuf))
+               return -ENOMEM;
 
-       nilfs_segbuf_map(segbuf, nilfs->ns_segnum, nilfs->ns_pseg_offset,
-                        nilfs);
+       if (list_empty(&sci->sc_write_logs)) {
+               nilfs_segbuf_map(segbuf, nilfs->ns_segnum,
+                                nilfs->ns_pseg_offset, nilfs);
+               if (segbuf->sb_rest_blocks < NILFS_PSEG_MIN_BLOCKS) {
+                       nilfs_shift_to_next_segment(nilfs);
+                       nilfs_segbuf_map(segbuf, nilfs->ns_segnum, 0, nilfs);
+               }
+
+               segbuf->sb_sum.seg_seq = nilfs->ns_seg_seq;
+               nextnum = nilfs->ns_nextnum;
 
-       if (segbuf->sb_rest_blocks < NILFS_PSEG_MIN_BLOCKS) {
-               nilfs_shift_to_next_segment(nilfs);
-               nilfs_segbuf_map(segbuf, nilfs->ns_segnum, 0, nilfs);
+               if (nilfs->ns_segnum == nilfs->ns_nextnum)
+                       /* Start from the head of a new full segment */
+                       alloc++;
+       } else {
+               /* Continue logs */
+               prev = NILFS_LAST_SEGBUF(&sci->sc_write_logs);
+               nilfs_segbuf_map_cont(segbuf, prev);
+               segbuf->sb_sum.seg_seq = prev->sb_sum.seg_seq;
+               nextnum = prev->sb_nextnum;
+
+               if (segbuf->sb_rest_blocks < NILFS_PSEG_MIN_BLOCKS) {
+                       nilfs_segbuf_map(segbuf, prev->sb_nextnum, 0, nilfs);
+                       segbuf->sb_sum.seg_seq++;
+                       alloc++;
+               }
        }
-       sci->sc_segbuf_nblocks = segbuf->sb_rest_blocks;
 
        err = nilfs_sufile_mark_dirty(nilfs->ns_sufile, segbuf->sb_segnum);
-       if (unlikely(err))
-               return err;
+       if (err)
+               goto failed;
 
-       if (nilfs->ns_segnum == nilfs->ns_nextnum) {
-               /* Start from the head of a new full segment */
+       if (alloc) {
                err = nilfs_sufile_alloc(nilfs->ns_sufile, &nextnum);
-               if (unlikely(err))
-                       return err;
-       } else
-               nextnum = nilfs->ns_nextnum;
-
-       segbuf->sb_sum.seg_seq = nilfs->ns_seg_seq;
+               if (err)
+                       goto failed;
+       }
        nilfs_segbuf_set_next_segnum(segbuf, nextnum, nilfs);
 
-       /* truncating segment buffers */
-       nilfs_truncate_logs(&sci->sc_segbufs, segbuf);
+       BUG_ON(!list_empty(&sci->sc_segbufs));
+       list_add_tail(&segbuf->sb_list, &sci->sc_segbufs);
+       sci->sc_segbuf_nblocks = segbuf->sb_rest_blocks;
        return 0;
+
+ failed:
+       nilfs_segbuf_free(segbuf);
+       return err;
 }
 
 static int nilfs_segctor_extend_segments(struct nilfs_sc_info *sci,
@@ -1373,15 +1395,16 @@ static int nilfs_segctor_extend_segments(struct nilfs_sc_info *sci,
        return err;
 }
 
-static void nilfs_segctor_free_incomplete_segments(struct nilfs_sc_info *sci,
-                                                  struct the_nilfs *nilfs)
+static void nilfs_free_incomplete_logs(struct list_head *logs,
+                                      struct the_nilfs *nilfs)
 {
-       struct nilfs_segment_buffer *segbuf;
+       struct nilfs_segment_buffer *segbuf, *prev;
+       struct inode *sufile = nilfs->ns_sufile;
        int ret;
 
-       segbuf = NILFS_FIRST_SEGBUF(&sci->sc_segbufs);
+       segbuf = NILFS_FIRST_SEGBUF(logs);
        if (nilfs->ns_nextnum != segbuf->sb_nextnum) {
-               ret = nilfs_sufile_free(nilfs->ns_sufile, segbuf->sb_nextnum);
+               ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
                WARN_ON(ret); /* never fails */
        }
        if (atomic_read(&segbuf->sb_err)) {
@@ -1395,34 +1418,18 @@ static void nilfs_segctor_free_incomplete_segments(struct nilfs_sc_info *sci,
                        set_nilfs_discontinued(nilfs);
        }
 
-       list_for_each_entry_continue(segbuf, &sci->sc_segbufs, sb_list) {
-               ret = nilfs_sufile_free(nilfs->ns_sufile, segbuf->sb_nextnum);
-               WARN_ON(ret); /* never fails */
+       prev = segbuf;
+       list_for_each_entry_continue(segbuf, logs, sb_list) {
+               if (prev->sb_nextnum != segbuf->sb_nextnum) {
+                       ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
+                       WARN_ON(ret); /* never fails */
+               }
                if (atomic_read(&segbuf->sb_err) &&
                    segbuf->sb_segnum != nilfs->ns_nextnum)
                        /* Case 2: extended segment (!= next) failed */
-                       nilfs_sufile_set_error(nilfs->ns_sufile,
-                                              segbuf->sb_segnum);
-       }
-}
-
-static void nilfs_segctor_end_construction(struct nilfs_sc_info *sci,
-                                          struct the_nilfs *nilfs, int err)
-{
-       if (unlikely(err)) {
-               nilfs_segctor_free_incomplete_segments(sci, nilfs);
-               if (sci->sc_stage.flags & NILFS_CF_SUFREED) {
-                       int ret;
-
-                       ret = nilfs_sufile_cancel_freev(nilfs->ns_sufile,
-                                                       sci->sc_freesegs,
-                                                       sci->sc_nfreesegs,
-                                                       NULL);
-                       WARN_ON(ret); /* do not happen */
-               }
+                       nilfs_sufile_set_error(sufile, segbuf->sb_segnum);
+               prev = segbuf;
        }
-       nilfs_clear_logs(&sci->sc_segbufs);
-       sci->sc_super_root = NULL;
 }
 
 static void nilfs_segctor_update_segusage(struct nilfs_sc_info *sci,
@@ -1442,19 +1449,18 @@ static void nilfs_segctor_update_segusage(struct nilfs_sc_info *sci,
        }
 }
 
-static void nilfs_segctor_cancel_segusage(struct nilfs_sc_info *sci,
-                                         struct inode *sufile)
+static void nilfs_cancel_segusage(struct list_head *logs, struct inode *sufile)
 {
        struct nilfs_segment_buffer *segbuf;
        int ret;
 
-       segbuf = NILFS_FIRST_SEGBUF(&sci->sc_segbufs);
+       segbuf = NILFS_FIRST_SEGBUF(logs);
        ret = nilfs_sufile_set_segment_usage(sufile, segbuf->sb_segnum,
                                             segbuf->sb_pseg_start -
                                             segbuf->sb_fseg_start, 0);
        WARN_ON(ret); /* always succeed because the segusage is dirty */
 
-       list_for_each_entry_continue(segbuf, &sci->sc_segbufs, sb_list) {
+       list_for_each_entry_continue(segbuf, logs, sb_list) {
                ret = nilfs_sufile_set_segment_usage(sufile, segbuf->sb_segnum,
                                                     0, 0);
                WARN_ON(ret); /* always succeed */
@@ -1505,6 +1511,12 @@ static int nilfs_segctor_collect(struct nilfs_sc_info *sci,
                if (mode != SC_LSEG_SR || sci->sc_stage.scnt < NILFS_ST_CPFILE)
                        break;
 
+               nilfs_clear_logs(&sci->sc_segbufs);
+
+               err = nilfs_segctor_extend_segments(sci, nilfs, nadd);
+               if (unlikely(err))
+                       return err;
+
                if (sci->sc_stage.flags & NILFS_CF_SUFREED) {
                        err = nilfs_sufile_cancel_freev(nilfs->ns_sufile,
                                                        sci->sc_freesegs,
@@ -1512,12 +1524,6 @@ static int nilfs_segctor_collect(struct nilfs_sc_info *sci,
                                                        NULL);
                        WARN_ON(err); /* do not happen */
                }
-               nilfs_clear_logs(&sci->sc_segbufs);
-
-               err = nilfs_segctor_extend_segments(sci, nilfs, nadd);
-               if (unlikely(err))
-                       return err;
-
                nadd = min_t(int, nadd << 1, SC_MAX_SEGDELTA);
                sci->sc_stage = prev_stage;
        }
@@ -1759,18 +1765,11 @@ static int nilfs_segctor_prepare_write(struct nilfs_sc_info *sci,
 static int nilfs_segctor_write(struct nilfs_sc_info *sci,
                               struct the_nilfs *nilfs)
 {
-       struct nilfs_segment_buffer *segbuf;
-       int err, res;
-
-       list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) {
-               err = nilfs_segbuf_write(segbuf, nilfs);
+       int ret;
 
-               res = nilfs_segbuf_wait(segbuf);
-               err = err ? : res;
-               if (err)
-                       return err;
-       }
-       return 0;
+       ret = nilfs_write_logs(&sci->sc_segbufs, nilfs);
+       list_splice_tail_init(&sci->sc_segbufs, &sci->sc_write_logs);
+       return ret;
 }
 
 static void __nilfs_end_page_io(struct page *page, int err)
@@ -1848,15 +1847,17 @@ static void nilfs_clear_copied_buffers(struct list_head *list, int err)
        }
 }
 
-static void nilfs_segctor_abort_write(struct nilfs_sc_info *sci,
-                                     struct page *failed_page, int err)
+static void nilfs_abort_logs(struct list_head *logs, struct page *failed_page,
+                            struct buffer_head *bh_sr, int err)
 {
        struct nilfs_segment_buffer *segbuf;
        struct page *bd_page = NULL, *fs_page = NULL;
+       struct buffer_head *bh;
 
-       list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) {
-               struct buffer_head *bh;
+       if (list_empty(logs))
+               return;
 
+       list_for_each_entry(segbuf, logs, sb_list) {
                list_for_each_entry(bh, &segbuf->sb_segsum_buffers,
                                    b_assoc_buffers) {
                        if (bh->b_page != bd_page) {
@@ -1868,7 +1869,7 @@ static void nilfs_segctor_abort_write(struct nilfs_sc_info *sci,
 
                list_for_each_entry(bh, &segbuf->sb_payload_buffers,
                                    b_assoc_buffers) {
-                       if (bh == sci->sc_super_root) {
+                       if (bh == bh_sr) {
                                if (bh->b_page != bd_page) {
                                        end_page_writeback(bd_page);
                                        bd_page = bh->b_page;
@@ -1878,7 +1879,7 @@ static void nilfs_segctor_abort_write(struct nilfs_sc_info *sci,
                        if (bh->b_page != fs_page) {
                                nilfs_end_page_io(fs_page, err);
                                if (fs_page && fs_page == failed_page)
-                                       goto done;
+                                       return;
                                fs_page = bh->b_page;
                        }
                }
@@ -1887,8 +1888,33 @@ static void nilfs_segctor_abort_write(struct nilfs_sc_info *sci,
                end_page_writeback(bd_page);
 
        nilfs_end_page_io(fs_page, err);
- done:
+}
+
+static void nilfs_segctor_abort_construction(struct nilfs_sc_info *sci,
+                                            struct the_nilfs *nilfs, int err)
+{
+       LIST_HEAD(logs);
+       int ret;
+
+       list_splice_tail_init(&sci->sc_write_logs, &logs);
+       ret = nilfs_wait_on_logs(&logs);
+       nilfs_abort_logs(&logs, NULL, sci->sc_super_root, ret ? : err);
+
+       list_splice_tail_init(&sci->sc_segbufs, &logs);
+       nilfs_cancel_segusage(&logs, nilfs->ns_sufile);
+       nilfs_free_incomplete_logs(&logs, nilfs);
        nilfs_clear_copied_buffers(&sci->sc_copied_buffers, err);
+
+       if (sci->sc_stage.flags & NILFS_CF_SUFREED) {
+               ret = nilfs_sufile_cancel_freev(nilfs->ns_sufile,
+                                               sci->sc_freesegs,
+                                               sci->sc_nfreesegs,
+                                               NULL);
+               WARN_ON(ret); /* do not happen */
+       }
+
+       nilfs_destroy_logs(&logs);
+       sci->sc_super_root = NULL;
 }
 
 static void nilfs_set_next_segment(struct the_nilfs *nilfs,
@@ -1906,11 +1932,10 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci)
 {
        struct nilfs_segment_buffer *segbuf;
        struct page *bd_page = NULL, *fs_page = NULL;
-       struct nilfs_sb_info *sbi = sci->sc_sbi;
-       struct the_nilfs *nilfs = sbi->s_nilfs;
+       struct the_nilfs *nilfs = sci->sc_sbi->s_nilfs;
        int update_sr = (sci->sc_super_root != NULL);
 
-       list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) {
+       list_for_each_entry(segbuf, &sci->sc_write_logs, sb_list) {
                struct buffer_head *bh;
 
                list_for_each_entry(bh, &segbuf->sb_segsum_buffers,
@@ -1983,21 +2008,34 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci)
 
        sci->sc_nblk_inc += sci->sc_nblk_this_inc;
 
-       segbuf = NILFS_LAST_SEGBUF(&sci->sc_segbufs);
+       segbuf = NILFS_LAST_SEGBUF(&sci->sc_write_logs);
        nilfs_set_next_segment(nilfs, segbuf);
 
        if (update_sr) {
                nilfs_set_last_segment(nilfs, segbuf->sb_pseg_start,
                                       segbuf->sb_sum.seg_seq, nilfs->ns_cno++);
-               sbi->s_super->s_dirt = 1;
+               set_nilfs_sb_dirty(nilfs);
 
                clear_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags);
                clear_bit(NILFS_SC_DIRTY, &sci->sc_flags);
                set_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags);
+               nilfs_segctor_clear_metadata_dirty(sci);
        } else
                clear_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags);
 }
 
+static int nilfs_segctor_wait(struct nilfs_sc_info *sci)
+{
+       int ret;
+
+       ret = nilfs_wait_on_logs(&sci->sc_write_logs);
+       if (!ret) {
+               nilfs_segctor_complete_write(sci);
+               nilfs_destroy_logs(&sci->sc_write_logs);
+       }
+       return ret;
+}
+
 static int nilfs_segctor_check_in_files(struct nilfs_sc_info *sci,
                                        struct nilfs_sb_info *sbi)
 {
@@ -2110,7 +2148,7 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode)
                /* Avoid empty segment */
                if (sci->sc_stage.scnt == NILFS_ST_DONE &&
                    NILFS_SEG_EMPTY(&sci->sc_curseg->sb_sum)) {
-                       nilfs_segctor_end_construction(sci, nilfs, 1);
+                       nilfs_segctor_abort_construction(sci, nilfs, 1);
                        goto out;
                }
 
@@ -2124,7 +2162,7 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode)
                if (has_sr) {
                        err = nilfs_segctor_fill_in_checkpoint(sci);
                        if (unlikely(err))
-                               goto failed_to_make_up;
+                               goto failed_to_write;
 
                        nilfs_segctor_fill_in_super_root(sci, nilfs);
                }
@@ -2132,47 +2170,51 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode)
 
                /* Write partial segments */
                err = nilfs_segctor_prepare_write(sci, &failed_page);
-               if (unlikely(err))
+               if (err) {
+                       nilfs_abort_logs(&sci->sc_segbufs, failed_page,
+                                        sci->sc_super_root, err);
                        goto failed_to_write;
-
+               }
                nilfs_segctor_fill_in_checksums(sci, nilfs->ns_crc_seed);
 
                err = nilfs_segctor_write(sci, nilfs);
                if (unlikely(err))
                        goto failed_to_write;
 
-               nilfs_segctor_complete_write(sci);
-
-               /* Commit segments */
-               if (has_sr)
-                       nilfs_segctor_clear_metadata_dirty(sci);
-
-               nilfs_segctor_end_construction(sci, nilfs, 0);
-
+               if (sci->sc_stage.scnt == NILFS_ST_DONE ||
+                   nilfs->ns_blocksize_bits != PAGE_CACHE_SHIFT) {
+                       /*
+                        * At this point, we avoid double buffering
+                        * for blocksize < pagesize because page dirty
+                        * flag is turned off during write and dirty
+                        * buffers are not properly collected for
+                        * pages crossing over segments.
+                        */
+                       err = nilfs_segctor_wait(sci);
+                       if (err)
+                               goto failed_to_write;
+               }
        } while (sci->sc_stage.scnt != NILFS_ST_DONE);
 
+       sci->sc_super_root = NULL;
+
  out:
-       nilfs_destroy_logs(&sci->sc_segbufs);
        nilfs_segctor_check_out_files(sci, sbi);
        return err;
 
  failed_to_write:
-       nilfs_segctor_abort_write(sci, failed_page, err);
-       nilfs_segctor_cancel_segusage(sci, nilfs->ns_sufile);
-
- failed_to_make_up:
        if (sci->sc_stage.flags & NILFS_CF_IFILE_STARTED)
                nilfs_redirty_inodes(&sci->sc_dirty_files);
 
  failed:
        if (nilfs_doing_gc())
                nilfs_redirty_inodes(&sci->sc_gc_inodes);
-       nilfs_segctor_end_construction(sci, nilfs, err);
+       nilfs_segctor_abort_construction(sci, nilfs, err);
        goto out;
 }
 
 /**
- * nilfs_secgtor_start_timer - set timer of background write
+ * nilfs_segctor_start_timer - set timer of background write
  * @sci: nilfs_sc_info
  *
  * If the timer has already been set, it ignores the new request.
@@ -2377,43 +2419,43 @@ int nilfs_construct_dsync_segment(struct super_block *sb, struct inode *inode,
        return err;
 }
 
-struct nilfs_segctor_req {
-       int mode;
-       __u32 seq_accepted;
-       int sc_err;  /* construction failure */
-       int sb_err;  /* super block writeback failure */
-};
-
 #define FLUSH_FILE_BIT (0x1) /* data file only */
 #define FLUSH_DAT_BIT  (1 << NILFS_DAT_INO) /* DAT only */
 
-static void nilfs_segctor_accept(struct nilfs_sc_info *sci,
-                                struct nilfs_segctor_req *req)
+/**
+ * nilfs_segctor_accept - record accepted sequence count of log-write requests
+ * @sci: segment constructor object
+ */
+static void nilfs_segctor_accept(struct nilfs_sc_info *sci)
 {
-       req->sc_err = req->sb_err = 0;
        spin_lock(&sci->sc_state_lock);
-       req->seq_accepted = sci->sc_seq_request;
+       sci->sc_seq_accepted = sci->sc_seq_request;
        spin_unlock(&sci->sc_state_lock);
 
        if (sci->sc_timer)
                del_timer_sync(sci->sc_timer);
 }
 
-static void nilfs_segctor_notify(struct nilfs_sc_info *sci,
-                                struct nilfs_segctor_req *req)
+/**
+ * nilfs_segctor_notify - notify the result of request to caller threads
+ * @sci: segment constructor object
+ * @mode: mode of log forming
+ * @err: error code to be notified
+ */
+static void nilfs_segctor_notify(struct nilfs_sc_info *sci, int mode, int err)
 {
        /* Clear requests (even when the construction failed) */
        spin_lock(&sci->sc_state_lock);
 
-       if (req->mode == SC_LSEG_SR) {
+       if (mode == SC_LSEG_SR) {
                sci->sc_state &= ~NILFS_SEGCTOR_COMMIT;
-               sci->sc_seq_done = req->seq_accepted;
-               nilfs_segctor_wakeup(sci, req->sc_err ? : req->sb_err);
+               sci->sc_seq_done = sci->sc_seq_accepted;
+               nilfs_segctor_wakeup(sci, err);
                sci->sc_flush_request = 0;
        } else {
-               if (req->mode == SC_FLUSH_FILE)
+               if (mode == SC_FLUSH_FILE)
                        sci->sc_flush_request &= ~FLUSH_FILE_BIT;
-               else if (req->mode == SC_FLUSH_DAT)
+               else if (mode == SC_FLUSH_DAT)
                        sci->sc_flush_request &= ~FLUSH_DAT_BIT;
 
                /* re-enable timer if checkpoint creation was not done */
@@ -2424,30 +2466,37 @@ static void nilfs_segctor_notify(struct nilfs_sc_info *sci,
        spin_unlock(&sci->sc_state_lock);
 }
 
-static int nilfs_segctor_construct(struct nilfs_sc_info *sci,
-                                  struct nilfs_segctor_req *req)
+/**
+ * nilfs_segctor_construct - form logs and write them to disk
+ * @sci: segment constructor object
+ * @mode: mode of log forming
+ */
+static int nilfs_segctor_construct(struct nilfs_sc_info *sci, int mode)
 {
        struct nilfs_sb_info *sbi = sci->sc_sbi;
        struct the_nilfs *nilfs = sbi->s_nilfs;
        int err = 0;
 
+       nilfs_segctor_accept(sci);
+
        if (nilfs_discontinued(nilfs))
-               req->mode = SC_LSEG_SR;
-       if (!nilfs_segctor_confirm(sci)) {
-               err = nilfs_segctor_do_construct(sci, req->mode);
-               req->sc_err = err;
-       }
+               mode = SC_LSEG_SR;
+       if (!nilfs_segctor_confirm(sci))
+               err = nilfs_segctor_do_construct(sci, mode);
+
        if (likely(!err)) {
-               if (req->mode != SC_FLUSH_DAT)
+               if (mode != SC_FLUSH_DAT)
                        atomic_set(&nilfs->ns_ndirtyblks, 0);
                if (test_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags) &&
                    nilfs_discontinued(nilfs)) {
                        down_write(&nilfs->ns_sem);
-                       req->sb_err = nilfs_commit_super(sbi,
-                                       nilfs_altsb_need_update(nilfs));
+                       err = nilfs_commit_super(
+                               sbi, nilfs_altsb_need_update(nilfs));
                        up_write(&nilfs->ns_sem);
                }
        }
+
+       nilfs_segctor_notify(sci, mode, err);
        return err;
 }
 
@@ -2478,7 +2527,6 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv,
        struct nilfs_sc_info *sci = NILFS_SC(sbi);
        struct the_nilfs *nilfs = sbi->s_nilfs;
        struct nilfs_transaction_info ti;
-       struct nilfs_segctor_req req = { .mode = SC_LSEG_SR };
        int err;
 
        if (unlikely(!sci))
@@ -2499,10 +2547,8 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv,
        list_splice_tail_init(&nilfs->ns_gc_inodes, &sci->sc_gc_inodes);
 
        for (;;) {
-               nilfs_segctor_accept(sci, &req);
-               err = nilfs_segctor_construct(sci, &req);
+               err = nilfs_segctor_construct(sci, SC_LSEG_SR);
                nilfs_remove_written_gcinodes(nilfs, &sci->sc_gc_inodes);
-               nilfs_segctor_notify(sci, &req);
 
                if (likely(!err))
                        break;
@@ -2512,6 +2558,16 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv,
                set_current_state(TASK_INTERRUPTIBLE);
                schedule_timeout(sci->sc_interval);
        }
+       if (nilfs_test_opt(sbi, DISCARD)) {
+               int ret = nilfs_discard_segments(nilfs, sci->sc_freesegs,
+                                                sci->sc_nfreesegs);
+               if (ret) {
+                       printk(KERN_WARNING
+                              "NILFS warning: error %d on discard request, "
+                              "turning discards off for the device\n", ret);
+                       nilfs_clear_opt(sbi, DISCARD);
+               }
+       }
 
  out_unlock:
        sci->sc_freesegs = NULL;
@@ -2525,13 +2581,9 @@ static void nilfs_segctor_thread_construct(struct nilfs_sc_info *sci, int mode)
 {
        struct nilfs_sb_info *sbi = sci->sc_sbi;
        struct nilfs_transaction_info ti;
-       struct nilfs_segctor_req req = { .mode = mode };
 
        nilfs_transaction_lock(sbi, &ti, 0);
-
-       nilfs_segctor_accept(sci, &req);
-       nilfs_segctor_construct(sci, &req);
-       nilfs_segctor_notify(sci, &req);
+       nilfs_segctor_construct(sci, mode);
 
        /*
         * Unclosed segment should be retried.  We do this using sc_timer.
@@ -2587,6 +2639,7 @@ static int nilfs_segctor_flush_mode(struct nilfs_sc_info *sci)
 static int nilfs_segctor_thread(void *arg)
 {
        struct nilfs_sc_info *sci = (struct nilfs_sc_info *)arg;
+       struct the_nilfs *nilfs = sci->sc_sbi->s_nilfs;
        struct timer_list timer;
        int timeout = 0;
 
@@ -2632,7 +2685,6 @@ static int nilfs_segctor_thread(void *arg)
        } else {
                DEFINE_WAIT(wait);
                int should_sleep = 1;
-               struct the_nilfs *nilfs;
 
                prepare_to_wait(&sci->sc_wait_daemon, &wait,
                                TASK_INTERRUPTIBLE);
@@ -2653,8 +2705,8 @@ static int nilfs_segctor_thread(void *arg)
                finish_wait(&sci->sc_wait_daemon, &wait);
                timeout = ((sci->sc_state & NILFS_SEGCTOR_COMMIT) &&
                           time_after_eq(jiffies, sci->sc_timer->expires));
-               nilfs = sci->sc_sbi->s_nilfs;
-               if (sci->sc_super->s_dirt && nilfs_sb_need_update(nilfs))
+
+               if (nilfs_sb_dirty(nilfs) && nilfs_sb_need_update(nilfs))
                        set_nilfs_discontinued(nilfs);
        }
        goto loop;
@@ -2725,6 +2777,7 @@ static struct nilfs_sc_info *nilfs_segctor_new(struct nilfs_sb_info *sbi)
        spin_lock_init(&sci->sc_state_lock);
        INIT_LIST_HEAD(&sci->sc_dirty_files);
        INIT_LIST_HEAD(&sci->sc_segbufs);
+       INIT_LIST_HEAD(&sci->sc_write_logs);
        INIT_LIST_HEAD(&sci->sc_gc_inodes);
        INIT_LIST_HEAD(&sci->sc_copied_buffers);
 
@@ -2748,12 +2801,9 @@ static void nilfs_segctor_write_out(struct nilfs_sc_info *sci)
        do {
                struct nilfs_sb_info *sbi = sci->sc_sbi;
                struct nilfs_transaction_info ti;
-               struct nilfs_segctor_req req = { .mode = SC_LSEG_SR };
 
                nilfs_transaction_lock(sbi, &ti, 0);
-               nilfs_segctor_accept(sci, &req);
-               ret = nilfs_segctor_construct(sci, &req);
-               nilfs_segctor_notify(sci, &req);
+               ret = nilfs_segctor_construct(sci, SC_LSEG_SR);
                nilfs_transaction_unlock(sbi);
 
        } while (ret && retrycount-- > 0);
@@ -2780,7 +2830,7 @@ static void nilfs_segctor_destroy(struct nilfs_sc_info *sci)
                || sci->sc_seq_request != sci->sc_seq_done);
        spin_unlock(&sci->sc_state_lock);
 
-       if (flag || nilfs_segctor_confirm(sci))
+       if (flag || !nilfs_segctor_confirm(sci))
                nilfs_segctor_write_out(sci);
 
        WARN_ON(!list_empty(&sci->sc_copied_buffers));
@@ -2792,6 +2842,7 @@ static void nilfs_segctor_destroy(struct nilfs_sc_info *sci)
        }
 
        WARN_ON(!list_empty(&sci->sc_segbufs));
+       WARN_ON(!list_empty(&sci->sc_write_logs));
 
        down_write(&sbi->s_nilfs->ns_segctor_sem);
 
@@ -2803,7 +2854,7 @@ static void nilfs_segctor_destroy(struct nilfs_sc_info *sci)
  * @sbi: nilfs_sb_info
  *
  * nilfs_attach_segment_constructor() allocates a struct nilfs_sc_info,
- * initilizes it, and starts the segment constructor.
+ * initializes it, and starts the segment constructor.
  *
  * Return Value: On success, 0 is returned. On error, one of the following
  * negative error code is returned.
@@ -2815,8 +2866,15 @@ int nilfs_attach_segment_constructor(struct nilfs_sb_info *sbi)
        struct the_nilfs *nilfs = sbi->s_nilfs;
        int err;
 
-       /* Each field of nilfs_segctor is cleared through the initialization
-          of super-block info */
+       if (NILFS_SC(sbi)) {
+               /*
+                * This happens if the filesystem was remounted
+                * read/write after nilfs_error degenerated it into a
+                * read-only mount.
+                */
+               nilfs_detach_segment_constructor(sbi);
+       }
+
        sbi->s_sc_info = nilfs_segctor_new(sbi);
        if (!sbi->s_sc_info)
                return -ENOMEM;