]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ryusuke...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 3 Mar 2010 16:53:49 +0000 (08:53 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 3 Mar 2010 16:53:49 +0000 (08:53 -0800)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ryusuke/nilfs2:
  nilfs2: add reader's lock for cno in nilfs_ioctl_sync
  nilfs2: delete unnecessary condition in load_segment_summary
  nilfs2: move iterator to write log into segment buffer
  nilfs2: get rid of s_dirt flag use
  nilfs2: get rid of nilfs_segctor_req struct
  nilfs2: delete unnecessary condition in nilfs_dat_translate
  nilfs2: fix potential hang in nilfs_error on errors=remount-ro
  nilfs2: use mnt_want_write in ioctls where write access is needed
  nilfs2: issue discard request after cleaning segments

12 files changed:
Documentation/filesystems/nilfs2.txt
fs/nilfs2/dat.c
fs/nilfs2/ioctl.c
fs/nilfs2/recovery.c
fs/nilfs2/segbuf.c
fs/nilfs2/segbuf.h
fs/nilfs2/segment.c
fs/nilfs2/segment.h
fs/nilfs2/super.c
fs/nilfs2/the_nilfs.c
fs/nilfs2/the_nilfs.h
include/linux/nilfs2_fs.h

index 839efd8a8a8c19ddce029661e2c166a434f5f080..cf6d0d85ca82308a171a4a70f66638caca7105d9 100644 (file)
@@ -74,6 +74,9 @@ norecovery            Disable recovery of the filesystem on mount.
                        This disables every write access on the device for
                        read-only mounts or snapshots.  This option will fail
                        for r/w mounts on an unclean volume.
+discard                        Issue discard/TRIM commands to the underlying block
+                       device when blocks are freed.  This is useful for SSD
+                       devices and sparse/thinly-provisioned LUNs.
 
 NILFS2 usage
 ============
index 187dd07ba86c659b465daa6b988cf2b80cc9ce02..9d1e5de91afb3965436b289d2a405b48c745eea1 100644 (file)
@@ -388,8 +388,7 @@ int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp)
                ret = -ENOENT;
                goto out;
        }
-       if (blocknrp != NULL)
-               *blocknrp = blocknr;
+       *blocknrp = blocknr;
 
  out:
        kunmap_atomic(kaddr, KM_USER0);
index d6b2b83de3633a7401a8f8720de0e6ec5b4568c7..313d0a21da480afc05afacfb46bec6f74e34b76a 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/capability.h>  /* capable() */
 #include <linux/uaccess.h>     /* copy_from_user(), copy_to_user() */
 #include <linux/vmalloc.h>
+#include <linux/mount.h>       /* mnt_want_write(), mnt_drop_write() */
 #include <linux/nilfs2_fs.h>
 #include "nilfs.h"
 #include "segment.h"
@@ -107,20 +108,28 @@ static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
+
+       ret = mnt_want_write(filp->f_path.mnt);
+       if (ret)
+               return ret;
+
+       ret = -EFAULT;
        if (copy_from_user(&cpmode, argp, sizeof(cpmode)))
-               return -EFAULT;
+               goto out;
 
        mutex_lock(&nilfs->ns_mount_mutex);
+
        nilfs_transaction_begin(inode->i_sb, &ti, 0);
        ret = nilfs_cpfile_change_cpmode(
                cpfile, cpmode.cm_cno, cpmode.cm_mode);
-       if (unlikely(ret < 0)) {
+       if (unlikely(ret < 0))
                nilfs_transaction_abort(inode->i_sb);
-               mutex_unlock(&nilfs->ns_mount_mutex);
-               return ret;
-       }
-       nilfs_transaction_commit(inode->i_sb); /* never fails */
+       else
+               nilfs_transaction_commit(inode->i_sb); /* never fails */
+
        mutex_unlock(&nilfs->ns_mount_mutex);
+out:
+       mnt_drop_write(filp->f_path.mnt);
        return ret;
 }
 
@@ -135,16 +144,23 @@ nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp,
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
+
+       ret = mnt_want_write(filp->f_path.mnt);
+       if (ret)
+               return ret;
+
+       ret = -EFAULT;
        if (copy_from_user(&cno, argp, sizeof(cno)))
-               return -EFAULT;
+               goto out;
 
        nilfs_transaction_begin(inode->i_sb, &ti, 0);
        ret = nilfs_cpfile_delete_checkpoint(cpfile, cno);
-       if (unlikely(ret < 0)) {
+       if (unlikely(ret < 0))
                nilfs_transaction_abort(inode->i_sb);
-               return ret;
-       }
-       nilfs_transaction_commit(inode->i_sb); /* never fails */
+       else
+               nilfs_transaction_commit(inode->i_sb); /* never fails */
+out:
+       mnt_drop_write(filp->f_path.mnt);
        return ret;
 }
 
@@ -496,12 +512,19 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
 
+       ret = mnt_want_write(filp->f_path.mnt);
+       if (ret)
+               return ret;
+
+       ret = -EFAULT;
        if (copy_from_user(argv, argp, sizeof(argv)))
-               return -EFAULT;
+               goto out;
 
+       ret = -EINVAL;
        nsegs = argv[4].v_nmembs;
        if (argv[4].v_size != argsz[4])
-               return -EINVAL;
+               goto out;
+
        /*
         * argv[4] points to segment numbers this ioctl cleans.  We
         * use kmalloc() for its buffer because memory used for the
@@ -509,9 +532,10 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
         */
        kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base,
                               nsegs * sizeof(__u64));
-       if (IS_ERR(kbufs[4]))
-               return PTR_ERR(kbufs[4]);
-
+       if (IS_ERR(kbufs[4])) {
+               ret = PTR_ERR(kbufs[4]);
+               goto out;
+       }
        nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
 
        for (n = 0; n < 4; n++) {
@@ -563,10 +587,12 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
                nilfs_remove_all_gcinode(nilfs);
        clear_nilfs_gc_running(nilfs);
 
- out_free:
+out_free:
        while (--n >= 0)
                vfree(kbufs[n]);
        kfree(kbufs[4]);
+out:
+       mnt_drop_write(filp->f_path.mnt);
        return ret;
 }
 
@@ -575,13 +601,17 @@ static int nilfs_ioctl_sync(struct inode *inode, struct file *filp,
 {
        __u64 cno;
        int ret;
+       struct the_nilfs *nilfs;
 
        ret = nilfs_construct_segment(inode->i_sb);
        if (ret < 0)
                return ret;
 
        if (argp != NULL) {
-               cno = NILFS_SB(inode->i_sb)->s_nilfs->ns_cno - 1;
+               nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
+               down_read(&nilfs->ns_segctor_sem);
+               cno = nilfs->ns_cno - 1;
+               up_read(&nilfs->ns_segctor_sem);
                if (copy_to_user(argp, &cno, sizeof(cno)))
                        return -EFAULT;
        }
index c9c96c7825dc0e007d80bd1bc34004a3d86a051a..017bedc761a048c66b538008ef4b561163f474e0 100644 (file)
@@ -39,7 +39,6 @@ enum {
        NILFS_SEG_FAIL_IO,
        NILFS_SEG_FAIL_MAGIC,
        NILFS_SEG_FAIL_SEQ,
-       NILFS_SEG_FAIL_CHECKSUM_SEGSUM,
        NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT,
        NILFS_SEG_FAIL_CHECKSUM_FULL,
        NILFS_SEG_FAIL_CONSISTENCY,
@@ -71,10 +70,6 @@ static int nilfs_warn_segment_error(int err)
                printk(KERN_WARNING
                       "NILFS warning: Sequence number mismatch\n");
                break;
-       case NILFS_SEG_FAIL_CHECKSUM_SEGSUM:
-               printk(KERN_WARNING
-                      "NILFS warning: Checksum error in segment summary\n");
-               break;
        case NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT:
                printk(KERN_WARNING
                       "NILFS warning: Checksum error in super root\n");
@@ -206,19 +201,15 @@ int nilfs_read_super_root_block(struct super_block *sb, sector_t sr_block,
  * @pseg_start: start disk block number of partial segment
  * @seg_seq: sequence number requested
  * @ssi: pointer to nilfs_segsum_info struct to store information
- * @full_check: full check flag
- *              (0: only checks segment summary CRC, 1: data CRC)
  */
 static int
 load_segment_summary(struct nilfs_sb_info *sbi, sector_t pseg_start,
-                    u64 seg_seq, struct nilfs_segsum_info *ssi,
-                    int full_check)
+                    u64 seg_seq, struct nilfs_segsum_info *ssi)
 {
        struct buffer_head *bh_sum;
        struct nilfs_segment_summary *sum;
-       unsigned long offset, nblock;
-       u64 check_bytes;
-       u32 crc, crc_sum;
+       unsigned long nblock;
+       u32 crc;
        int ret = NILFS_SEG_FAIL_IO;
 
        bh_sum = sb_bread(sbi->s_super, pseg_start);
@@ -237,34 +228,24 @@ load_segment_summary(struct nilfs_sb_info *sbi, sector_t pseg_start,
                ret = NILFS_SEG_FAIL_SEQ;
                goto failed;
        }
-       if (full_check) {
-               offset = sizeof(sum->ss_datasum);
-               check_bytes =
-                       ((u64)ssi->nblocks << sbi->s_super->s_blocksize_bits);
-               nblock = ssi->nblocks;
-               crc_sum = le32_to_cpu(sum->ss_datasum);
-               ret = NILFS_SEG_FAIL_CHECKSUM_FULL;
-       } else { /* only checks segment summary */
-               offset = sizeof(sum->ss_datasum) + sizeof(sum->ss_sumsum);
-               check_bytes = ssi->sumbytes;
-               nblock = ssi->nsumblk;
-               crc_sum = le32_to_cpu(sum->ss_sumsum);
-               ret = NILFS_SEG_FAIL_CHECKSUM_SEGSUM;
-       }
 
+       nblock = ssi->nblocks;
        if (unlikely(nblock == 0 ||
                     nblock > sbi->s_nilfs->ns_blocks_per_segment)) {
                /* This limits the number of blocks read in the CRC check */
                ret = NILFS_SEG_FAIL_CONSISTENCY;
                goto failed;
        }
-       if (calc_crc_cont(sbi, bh_sum, &crc, offset, check_bytes,
+       if (calc_crc_cont(sbi, bh_sum, &crc, sizeof(sum->ss_datasum),
+                         ((u64)nblock << sbi->s_super->s_blocksize_bits),
                          pseg_start, nblock)) {
                ret = NILFS_SEG_FAIL_IO;
                goto failed;
        }
-       if (crc == crc_sum)
+       if (crc == le32_to_cpu(sum->ss_datasum))
                ret = 0;
+       else
+               ret = NILFS_SEG_FAIL_CHECKSUM_FULL;
  failed:
        brelse(bh_sum);
  out:
@@ -598,7 +579,7 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs,
 
        while (segnum != ri->ri_segnum || pseg_start <= ri->ri_pseg_start) {
 
-               ret = load_segment_summary(sbi, pseg_start, seg_seq, &ssi, 1);
+               ret = load_segment_summary(sbi, pseg_start, seg_seq, &ssi);
                if (ret) {
                        if (ret == NILFS_SEG_FAIL_IO) {
                                err = -EIO;
@@ -821,7 +802,7 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi,
 
        for (;;) {
                /* Load segment summary */
-               ret = load_segment_summary(sbi, pseg_start, seg_seq, &ssi, 1);
+               ret = load_segment_summary(sbi, pseg_start, seg_seq, &ssi);
                if (ret) {
                        if (ret == NILFS_SEG_FAIL_IO)
                                goto failed;
index 645c78656aa0e6f95c8e2c2b8dc5d2e7b24b48ea..ab56fe44e377a6025f10a55019ebd4d0b2e29fa9 100644 (file)
@@ -40,6 +40,11 @@ struct nilfs_write_info {
 };
 
 
+static int nilfs_segbuf_write(struct nilfs_segment_buffer *segbuf,
+                             struct the_nilfs *nilfs);
+static int nilfs_segbuf_wait(struct nilfs_segment_buffer *segbuf);
+
+
 static struct kmem_cache *nilfs_segbuf_cachep;
 
 static void nilfs_segbuf_init_once(void *obj)
@@ -302,6 +307,19 @@ void nilfs_truncate_logs(struct list_head *logs,
        }
 }
 
+int nilfs_write_logs(struct list_head *logs, struct the_nilfs *nilfs)
+{
+       struct nilfs_segment_buffer *segbuf;
+       int ret = 0;
+
+       list_for_each_entry(segbuf, logs, sb_list) {
+               ret = nilfs_segbuf_write(segbuf, nilfs);
+               if (ret)
+                       break;
+       }
+       return ret;
+}
+
 int nilfs_wait_on_logs(struct list_head *logs)
 {
        struct nilfs_segment_buffer *segbuf;
index 6af1630fb401fd088058c4abbde50d4bb592f036..94dfd3517bc0f5f8f96f0d4efc4588a0ff7dc775 100644 (file)
@@ -166,13 +166,10 @@ nilfs_segbuf_add_file_buffer(struct nilfs_segment_buffer *segbuf,
        segbuf->sb_sum.nfileblk++;
 }
 
-int nilfs_segbuf_write(struct nilfs_segment_buffer *segbuf,
-                      struct the_nilfs *nilfs);
-int nilfs_segbuf_wait(struct nilfs_segment_buffer *segbuf);
-
 void nilfs_clear_logs(struct list_head *logs);
 void nilfs_truncate_logs(struct list_head *logs,
                         struct nilfs_segment_buffer *last);
+int nilfs_write_logs(struct list_head *logs, struct the_nilfs *nilfs);
 int nilfs_wait_on_logs(struct list_head *logs);
 
 static inline void nilfs_destroy_logs(struct list_head *logs)
index 105b508b47a8530166199edc48f7267b826e52df..ada2f1b947a3833fc1eb3c3783de56d7310fb122 100644 (file)
@@ -1764,14 +1764,9 @@ 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 ret = 0;
+       int ret;
 
-       list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) {
-               ret = nilfs_segbuf_write(segbuf, nilfs);
-               if (ret)
-                       break;
-       }
+       ret = nilfs_write_logs(&sci->sc_segbufs, nilfs);
        list_splice_tail_init(&sci->sc_segbufs, &sci->sc_write_logs);
        return ret;
 }
@@ -1937,8 +1932,7 @@ 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_write_logs, sb_list) {
@@ -2020,7 +2014,7 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci)
        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);
@@ -2425,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 */
@@ -2472,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;
 }
 
@@ -2526,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))
@@ -2547,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;
@@ -2560,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;
@@ -2573,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.
@@ -2635,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;
 
@@ -2680,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);
@@ -2701,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;
@@ -2797,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);
@@ -2865,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;
index 3d3ab2f9864ce52605ec6eb1d40a4ce4aa025889..3155e0c7f4151fa865bca5d7d124ed6dc6972e70 100644 (file)
@@ -116,6 +116,7 @@ struct nilfs_segsum_pointer {
  * @sc_wait_daemon: Daemon wait queue
  * @sc_wait_task: Start/end wait queue to control segctord task
  * @sc_seq_request: Request counter
+ * @sc_seq_accept: Accepted request count
  * @sc_seq_done: Completion counter
  * @sc_sync: Request of explicit sync operation
  * @sc_interval: Timeout value of background construction
@@ -169,6 +170,7 @@ struct nilfs_sc_info {
        wait_queue_head_t       sc_wait_task;
 
        __u32                   sc_seq_request;
+       __u32                   sc_seq_accepted;
        __u32                   sc_seq_done;
 
        int                     sc_sync;
index 8173faee31e64b9ceacc91d3169d806a4ce22eab..92579cc4c93532de14509e049071874e6f72e7eb 100644 (file)
@@ -96,9 +96,6 @@ void nilfs_error(struct super_block *sb, const char *function,
        if (!(sb->s_flags & MS_RDONLY)) {
                struct the_nilfs *nilfs = sbi->s_nilfs;
 
-               if (!nilfs_test_opt(sbi, ERRORS_CONT))
-                       nilfs_detach_segment_constructor(sbi);
-
                down_write(&nilfs->ns_sem);
                if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) {
                        nilfs->ns_mount_state |= NILFS_ERROR_FS;
@@ -301,7 +298,7 @@ int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb)
                memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
                nilfs->ns_sbwtime[1] = t;
        }
-       sbi->s_super->s_dirt = 0;
+       clear_nilfs_sb_dirty(nilfs);
        return nilfs_sync_super(sbi, dupsb);
 }
 
@@ -345,7 +342,7 @@ static int nilfs_sync_fs(struct super_block *sb, int wait)
                err = nilfs_construct_segment(sb);
 
        down_write(&nilfs->ns_sem);
-       if (sb->s_dirt)
+       if (nilfs_sb_dirty(nilfs))
                nilfs_commit_super(sbi, 1);
        up_write(&nilfs->ns_sem);
 
@@ -481,6 +478,8 @@ static int nilfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
                seq_printf(seq, ",order=strict");
        if (nilfs_test_opt(sbi, NORECOVERY))
                seq_printf(seq, ",norecovery");
+       if (nilfs_test_opt(sbi, DISCARD))
+               seq_printf(seq, ",discard");
 
        return 0;
 }
@@ -550,7 +549,7 @@ static const struct export_operations nilfs_export_ops = {
 enum {
        Opt_err_cont, Opt_err_panic, Opt_err_ro,
        Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery,
-       Opt_err,
+       Opt_discard, Opt_err,
 };
 
 static match_table_t tokens = {
@@ -561,6 +560,7 @@ static match_table_t tokens = {
        {Opt_snapshot, "cp=%u"},
        {Opt_order, "order=%s"},
        {Opt_norecovery, "norecovery"},
+       {Opt_discard, "discard"},
        {Opt_err, NULL}
 };
 
@@ -614,6 +614,9 @@ static int parse_options(char *options, struct super_block *sb)
                case Opt_norecovery:
                        nilfs_set_opt(sbi, NORECOVERY);
                        break;
+               case Opt_discard:
+                       nilfs_set_opt(sbi, DISCARD);
+                       break;
                default:
                        printk(KERN_ERR
                               "NILFS: Unrecognized mount option \"%s\"\n", p);
index 6241e1722efc570f3bb62c81f30a4b4e8f7092e3..92733d5651d24ed68c8c5081346155554d62c520 100644 (file)
@@ -646,6 +646,44 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data)
        goto out;
 }
 
+int nilfs_discard_segments(struct the_nilfs *nilfs, __u64 *segnump,
+                           size_t nsegs)
+{
+       sector_t seg_start, seg_end;
+       sector_t start = 0, nblocks = 0;
+       unsigned int sects_per_block;
+       __u64 *sn;
+       int ret = 0;
+
+       sects_per_block = (1 << nilfs->ns_blocksize_bits) /
+               bdev_logical_block_size(nilfs->ns_bdev);
+       for (sn = segnump; sn < segnump + nsegs; sn++) {
+               nilfs_get_segment_range(nilfs, *sn, &seg_start, &seg_end);
+
+               if (!nblocks) {
+                       start = seg_start;
+                       nblocks = seg_end - seg_start + 1;
+               } else if (start + nblocks == seg_start) {
+                       nblocks += seg_end - seg_start + 1;
+               } else {
+                       ret = blkdev_issue_discard(nilfs->ns_bdev,
+                                                  start * sects_per_block,
+                                                  nblocks * sects_per_block,
+                                                  GFP_NOFS,
+                                                  DISCARD_FL_BARRIER);
+                       if (ret < 0)
+                               return ret;
+                       nblocks = 0;
+               }
+       }
+       if (nblocks)
+               ret = blkdev_issue_discard(nilfs->ns_bdev,
+                                          start * sects_per_block,
+                                          nblocks * sects_per_block,
+                                          GFP_NOFS, DISCARD_FL_BARRIER);
+       return ret;
+}
+
 int nilfs_count_free_blocks(struct the_nilfs *nilfs, sector_t *nblocks)
 {
        struct inode *dat = nilfs_dat_inode(nilfs);
index 589786e33464661a2e2df9321d75725011ba522f..e9795f1724d7f0cc487729fd1e7cc2bb982a3b7a 100644 (file)
@@ -38,6 +38,7 @@ enum {
                                   the latest checkpoint was loaded */
        THE_NILFS_DISCONTINUED, /* 'next' pointer chain has broken */
        THE_NILFS_GC_RUNNING,   /* gc process is running */
+       THE_NILFS_SB_DIRTY,     /* super block is dirty */
 };
 
 /**
@@ -197,6 +198,7 @@ THE_NILFS_FNS(INIT, init)
 THE_NILFS_FNS(LOADED, loaded)
 THE_NILFS_FNS(DISCONTINUED, discontinued)
 THE_NILFS_FNS(GC_RUNNING, gc_running)
+THE_NILFS_FNS(SB_DIRTY, sb_dirty)
 
 /* Minimum interval of periodical update of superblocks (in seconds) */
 #define NILFS_SB_FREQ          10
@@ -221,6 +223,7 @@ struct the_nilfs *find_or_create_nilfs(struct block_device *);
 void put_nilfs(struct the_nilfs *);
 int init_nilfs(struct the_nilfs *, struct nilfs_sb_info *, char *);
 int load_nilfs(struct the_nilfs *, struct nilfs_sb_info *);
+int nilfs_discard_segments(struct the_nilfs *, __u64 *, size_t);
 int nilfs_count_free_blocks(struct the_nilfs *, sector_t *);
 struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *, int, __u64);
 int nilfs_checkpoint_is_mounted(struct the_nilfs *, __u64, int);
index 3fe02cf8b65a3097785fd1543097b9cdadf3116e..640702e97457f58eaab573283906996071ea680e 100644 (file)
@@ -153,6 +153,7 @@ struct nilfs_super_root {
                                                   semantics also for data */
 #define NILFS_MOUNT_NORECOVERY         0x4000  /* Disable write access during
                                                   mount-time recovery */
+#define NILFS_MOUNT_DISCARD            0x8000  /* Issue DISCARD requests */
 
 
 /**