Commit feaf77d5 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ryusuke/nilfs2

* '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
parents eca281aa 0d561f12
...@@ -74,6 +74,9 @@ norecovery Disable recovery of the filesystem on mount. ...@@ -74,6 +74,9 @@ norecovery Disable recovery of the filesystem on mount.
This disables every write access on the device for This disables every write access on the device for
read-only mounts or snapshots. This option will fail read-only mounts or snapshots. This option will fail
for r/w mounts on an unclean volume. 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 NILFS2 usage
============ ============
......
...@@ -388,7 +388,6 @@ int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp) ...@@ -388,7 +388,6 @@ int nilfs_dat_translate(struct inode *dat, __u64 vblocknr, sector_t *blocknrp)
ret = -ENOENT; ret = -ENOENT;
goto out; goto out;
} }
if (blocknrp != NULL)
*blocknrp = blocknr; *blocknrp = blocknr;
out: out:
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/capability.h> /* capable() */ #include <linux/capability.h> /* capable() */
#include <linux/uaccess.h> /* copy_from_user(), copy_to_user() */ #include <linux/uaccess.h> /* copy_from_user(), copy_to_user() */
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/mount.h> /* mnt_want_write(), mnt_drop_write() */
#include <linux/nilfs2_fs.h> #include <linux/nilfs2_fs.h>
#include "nilfs.h" #include "nilfs.h"
#include "segment.h" #include "segment.h"
...@@ -107,20 +108,28 @@ static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp, ...@@ -107,20 +108,28 @@ static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
ret = mnt_want_write(filp->f_path.mnt);
if (ret)
return ret;
ret = -EFAULT;
if (copy_from_user(&cpmode, argp, sizeof(cpmode))) if (copy_from_user(&cpmode, argp, sizeof(cpmode)))
return -EFAULT; goto out;
mutex_lock(&nilfs->ns_mount_mutex); mutex_lock(&nilfs->ns_mount_mutex);
nilfs_transaction_begin(inode->i_sb, &ti, 0); nilfs_transaction_begin(inode->i_sb, &ti, 0);
ret = nilfs_cpfile_change_cpmode( ret = nilfs_cpfile_change_cpmode(
cpfile, cpmode.cm_cno, cpmode.cm_mode); cpfile, cpmode.cm_cno, cpmode.cm_mode);
if (unlikely(ret < 0)) { if (unlikely(ret < 0))
nilfs_transaction_abort(inode->i_sb); nilfs_transaction_abort(inode->i_sb);
mutex_unlock(&nilfs->ns_mount_mutex); else
return ret;
}
nilfs_transaction_commit(inode->i_sb); /* never fails */ nilfs_transaction_commit(inode->i_sb); /* never fails */
mutex_unlock(&nilfs->ns_mount_mutex); mutex_unlock(&nilfs->ns_mount_mutex);
out:
mnt_drop_write(filp->f_path.mnt);
return ret; return ret;
} }
...@@ -135,16 +144,23 @@ nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp, ...@@ -135,16 +144,23 @@ nilfs_ioctl_delete_checkpoint(struct inode *inode, struct file *filp,
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
ret = mnt_want_write(filp->f_path.mnt);
if (ret)
return ret;
ret = -EFAULT;
if (copy_from_user(&cno, argp, sizeof(cno))) if (copy_from_user(&cno, argp, sizeof(cno)))
return -EFAULT; goto out;
nilfs_transaction_begin(inode->i_sb, &ti, 0); nilfs_transaction_begin(inode->i_sb, &ti, 0);
ret = nilfs_cpfile_delete_checkpoint(cpfile, cno); ret = nilfs_cpfile_delete_checkpoint(cpfile, cno);
if (unlikely(ret < 0)) { if (unlikely(ret < 0))
nilfs_transaction_abort(inode->i_sb); nilfs_transaction_abort(inode->i_sb);
return ret; else
}
nilfs_transaction_commit(inode->i_sb); /* never fails */ nilfs_transaction_commit(inode->i_sb); /* never fails */
out:
mnt_drop_write(filp->f_path.mnt);
return ret; return ret;
} }
...@@ -496,12 +512,19 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, ...@@ -496,12 +512,19 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
ret = mnt_want_write(filp->f_path.mnt);
if (ret)
return ret;
ret = -EFAULT;
if (copy_from_user(argv, argp, sizeof(argv))) if (copy_from_user(argv, argp, sizeof(argv)))
return -EFAULT; goto out;
ret = -EINVAL;
nsegs = argv[4].v_nmembs; nsegs = argv[4].v_nmembs;
if (argv[4].v_size != argsz[4]) if (argv[4].v_size != argsz[4])
return -EINVAL; goto out;
/* /*
* argv[4] points to segment numbers this ioctl cleans. We * argv[4] points to segment numbers this ioctl cleans. We
* use kmalloc() for its buffer because memory used for the * 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, ...@@ -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, kbufs[4] = memdup_user((void __user *)(unsigned long)argv[4].v_base,
nsegs * sizeof(__u64)); nsegs * sizeof(__u64));
if (IS_ERR(kbufs[4])) if (IS_ERR(kbufs[4])) {
return PTR_ERR(kbufs[4]); ret = PTR_ERR(kbufs[4]);
goto out;
}
nilfs = NILFS_SB(inode->i_sb)->s_nilfs; nilfs = NILFS_SB(inode->i_sb)->s_nilfs;
for (n = 0; n < 4; n++) { for (n = 0; n < 4; n++) {
...@@ -563,10 +587,12 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, ...@@ -563,10 +587,12 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
nilfs_remove_all_gcinode(nilfs); nilfs_remove_all_gcinode(nilfs);
clear_nilfs_gc_running(nilfs); clear_nilfs_gc_running(nilfs);
out_free: out_free:
while (--n >= 0) while (--n >= 0)
vfree(kbufs[n]); vfree(kbufs[n]);
kfree(kbufs[4]); kfree(kbufs[4]);
out:
mnt_drop_write(filp->f_path.mnt);
return ret; return ret;
} }
...@@ -575,13 +601,17 @@ static int nilfs_ioctl_sync(struct inode *inode, struct file *filp, ...@@ -575,13 +601,17 @@ static int nilfs_ioctl_sync(struct inode *inode, struct file *filp,
{ {
__u64 cno; __u64 cno;
int ret; int ret;
struct the_nilfs *nilfs;
ret = nilfs_construct_segment(inode->i_sb); ret = nilfs_construct_segment(inode->i_sb);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (argp != NULL) { 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))) if (copy_to_user(argp, &cno, sizeof(cno)))
return -EFAULT; return -EFAULT;
} }
......
...@@ -39,7 +39,6 @@ enum { ...@@ -39,7 +39,6 @@ enum {
NILFS_SEG_FAIL_IO, NILFS_SEG_FAIL_IO,
NILFS_SEG_FAIL_MAGIC, NILFS_SEG_FAIL_MAGIC,
NILFS_SEG_FAIL_SEQ, NILFS_SEG_FAIL_SEQ,
NILFS_SEG_FAIL_CHECKSUM_SEGSUM,
NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT, NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT,
NILFS_SEG_FAIL_CHECKSUM_FULL, NILFS_SEG_FAIL_CHECKSUM_FULL,
NILFS_SEG_FAIL_CONSISTENCY, NILFS_SEG_FAIL_CONSISTENCY,
...@@ -71,10 +70,6 @@ static int nilfs_warn_segment_error(int err) ...@@ -71,10 +70,6 @@ static int nilfs_warn_segment_error(int err)
printk(KERN_WARNING printk(KERN_WARNING
"NILFS warning: Sequence number mismatch\n"); "NILFS warning: Sequence number mismatch\n");
break; 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: case NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT:
printk(KERN_WARNING printk(KERN_WARNING
"NILFS warning: Checksum error in super root\n"); "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, ...@@ -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 * @pseg_start: start disk block number of partial segment
* @seg_seq: sequence number requested * @seg_seq: sequence number requested
* @ssi: pointer to nilfs_segsum_info struct to store information * @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 static int
load_segment_summary(struct nilfs_sb_info *sbi, sector_t pseg_start, load_segment_summary(struct nilfs_sb_info *sbi, sector_t pseg_start,
u64 seg_seq, struct nilfs_segsum_info *ssi, u64 seg_seq, struct nilfs_segsum_info *ssi)
int full_check)
{ {
struct buffer_head *bh_sum; struct buffer_head *bh_sum;
struct nilfs_segment_summary *sum; struct nilfs_segment_summary *sum;
unsigned long offset, nblock; unsigned long nblock;
u64 check_bytes; u32 crc;
u32 crc, crc_sum;
int ret = NILFS_SEG_FAIL_IO; int ret = NILFS_SEG_FAIL_IO;
bh_sum = sb_bread(sbi->s_super, pseg_start); 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, ...@@ -237,34 +228,24 @@ load_segment_summary(struct nilfs_sb_info *sbi, sector_t pseg_start,
ret = NILFS_SEG_FAIL_SEQ; ret = NILFS_SEG_FAIL_SEQ;
goto failed; 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 || if (unlikely(nblock == 0 ||
nblock > sbi->s_nilfs->ns_blocks_per_segment)) { nblock > sbi->s_nilfs->ns_blocks_per_segment)) {
/* This limits the number of blocks read in the CRC check */ /* This limits the number of blocks read in the CRC check */
ret = NILFS_SEG_FAIL_CONSISTENCY; ret = NILFS_SEG_FAIL_CONSISTENCY;
goto failed; 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)) { pseg_start, nblock)) {
ret = NILFS_SEG_FAIL_IO; ret = NILFS_SEG_FAIL_IO;
goto failed; goto failed;
} }
if (crc == crc_sum) if (crc == le32_to_cpu(sum->ss_datasum))
ret = 0; ret = 0;
else
ret = NILFS_SEG_FAIL_CHECKSUM_FULL;
failed: failed:
brelse(bh_sum); brelse(bh_sum);
out: out:
...@@ -598,7 +579,7 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, ...@@ -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) { 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) {
if (ret == NILFS_SEG_FAIL_IO) { if (ret == NILFS_SEG_FAIL_IO) {
err = -EIO; err = -EIO;
...@@ -821,7 +802,7 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, ...@@ -821,7 +802,7 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi,
for (;;) { for (;;) {
/* Load segment summary */ /* 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) {
if (ret == NILFS_SEG_FAIL_IO) if (ret == NILFS_SEG_FAIL_IO)
goto failed; goto failed;
......
...@@ -40,6 +40,11 @@ struct nilfs_write_info { ...@@ -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 struct kmem_cache *nilfs_segbuf_cachep;
static void nilfs_segbuf_init_once(void *obj) static void nilfs_segbuf_init_once(void *obj)
...@@ -302,6 +307,19 @@ void nilfs_truncate_logs(struct list_head *logs, ...@@ -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) int nilfs_wait_on_logs(struct list_head *logs)
{ {
struct nilfs_segment_buffer *segbuf; struct nilfs_segment_buffer *segbuf;
......
...@@ -166,13 +166,10 @@ nilfs_segbuf_add_file_buffer(struct nilfs_segment_buffer *segbuf, ...@@ -166,13 +166,10 @@ nilfs_segbuf_add_file_buffer(struct nilfs_segment_buffer *segbuf,
segbuf->sb_sum.nfileblk++; 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_clear_logs(struct list_head *logs);
void nilfs_truncate_logs(struct list_head *logs, void nilfs_truncate_logs(struct list_head *logs,
struct nilfs_segment_buffer *last); 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); int nilfs_wait_on_logs(struct list_head *logs);
static inline void nilfs_destroy_logs(struct list_head *logs) static inline void nilfs_destroy_logs(struct list_head *logs)
......
...@@ -1764,14 +1764,9 @@ static int nilfs_segctor_prepare_write(struct nilfs_sc_info *sci, ...@@ -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, static int nilfs_segctor_write(struct nilfs_sc_info *sci,
struct the_nilfs *nilfs) struct the_nilfs *nilfs)
{ {
struct nilfs_segment_buffer *segbuf; int ret;
int ret = 0;
list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) { ret = nilfs_write_logs(&sci->sc_segbufs, nilfs);
ret = nilfs_segbuf_write(segbuf, nilfs);
if (ret)
break;
}
list_splice_tail_init(&sci->sc_segbufs, &sci->sc_write_logs); list_splice_tail_init(&sci->sc_segbufs, &sci->sc_write_logs);
return ret; return ret;
} }
...@@ -1937,8 +1932,7 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci) ...@@ -1937,8 +1932,7 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci)
{ {
struct nilfs_segment_buffer *segbuf; struct nilfs_segment_buffer *segbuf;
struct page *bd_page = NULL, *fs_page = NULL; struct page *bd_page = NULL, *fs_page = NULL;
struct nilfs_sb_info *sbi = sci->sc_sbi; struct the_nilfs *nilfs = sci->sc_sbi->s_nilfs;
struct the_nilfs *nilfs = sbi->s_nilfs;
int update_sr = (sci->sc_super_root != NULL); int update_sr = (sci->sc_super_root != NULL);
list_for_each_entry(segbuf, &sci->sc_write_logs, sb_list) { 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) ...@@ -2020,7 +2014,7 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci)
if (update_sr) { if (update_sr) {
nilfs_set_last_segment(nilfs, segbuf->sb_pseg_start, nilfs_set_last_segment(nilfs, segbuf->sb_pseg_start,
segbuf->sb_sum.seg_seq, nilfs->ns_cno++); 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_HAVE_DELTA, &sci->sc_flags);
clear_bit(NILFS_SC_DIRTY, &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, ...@@ -2425,43 +2419,43 @@ int nilfs_construct_dsync_segment(struct super_block *sb, struct inode *inode,
return err; 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_FILE_BIT (0x1) /* data file only */
#define FLUSH_DAT_BIT (1 << NILFS_DAT_INO) /* DAT 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); 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); spin_unlock(&sci->sc_state_lock);
if (sci->sc_timer) if (sci->sc_timer)
del_timer_sync(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) */ /* Clear requests (even when the construction failed) */
spin_lock(&sci->sc_state_lock); 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_state &= ~NILFS_SEGCTOR_COMMIT;
sci->sc_seq_done = req->seq_accepted; sci->sc_seq_done = sci->sc_seq_accepted;
nilfs_segctor_wakeup(sci, req->sc_err ? : req->sb_err); nilfs_segctor_wakeup(sci, err);
sci->sc_flush_request = 0; sci->sc_flush_request = 0;
} else { } else {
if (req->mode == SC_FLUSH_FILE) if (mode == SC_FLUSH_FILE)
sci->sc_flush_request &= ~FLUSH_FILE_BIT; 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; sci->sc_flush_request &= ~FLUSH_DAT_BIT;
/* re-enable timer if checkpoint creation was not done */ /* re-enable timer if checkpoint creation was not done */
...@@ -2472,30 +2466,37 @@ static void nilfs_segctor_notify(struct nilfs_sc_info *sci, ...@@ -2472,30 +2466,37 @@ static void nilfs_segctor_notify(struct nilfs_sc_info *sci,
spin_unlock(&sci->sc_state_lock); 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 nilfs_sb_info *sbi = sci->sc_sbi;
struct the_nilfs *nilfs = sbi->s_nilfs; struct the_nilfs *nilfs = sbi->s_nilfs;
int err = 0; int err = 0;
nilfs_segctor_accept(sci);
if (nilfs_discontinued(nilfs)) if (nilfs_discontinued(nilfs))
req->mode = SC_LSEG_SR; mode = SC_LSEG_SR;
if (!nilfs_segctor_confirm(sci)) { if (!nilfs_segctor_confirm(sci))
err = nilfs_segctor_do_construct(sci, req->mode); err = nilfs_segctor_do_construct(sci, mode);
req->sc_err = err;
}
if (likely(!err)) { if (likely(!err)) {
if (req->mode != SC_FLUSH_DAT) if (mode != SC_FLUSH_DAT)
atomic_set(&nilfs->ns_ndirtyblks, 0); atomic_set(&nilfs->ns_ndirtyblks, 0);
if (test_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags) && if (test_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags) &&
nilfs_discontinued(nilfs)) { nilfs_discontinued(nilfs)) {
down_write(&nilfs->ns_sem); down_write(&nilfs->ns_sem);
req->sb_err = nilfs_commit_super(sbi, err = nilfs_commit_super(
nilfs_altsb_need_update(nilfs)); sbi, nilfs_altsb_need_update(nilfs));
up_write(&nilfs->ns_sem); up_write(&nilfs->ns_sem);
} }
} }
nilfs_segctor_notify(sci, mode, err);
return err; return err;
} }
...@@ -2526,7 +2527,6 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv, ...@@ -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 nilfs_sc_info *sci = NILFS_SC(sbi);
struct the_nilfs *nilfs = sbi->s_nilfs; struct the_nilfs *nilfs = sbi->s_nilfs;
struct nilfs_transaction_info ti; struct nilfs_transaction_info ti;
struct nilfs_segctor_req req = { .mode = SC_LSEG_SR };
int err; int err;
if (unlikely(!sci)) if (unlikely(!sci))
...@@ -2547,10 +2547,8 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv, ...@@ -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); list_splice_tail_init(&nilfs->ns_gc_inodes, &sci->sc_gc_inodes);
for (;;) { for (;;) {
nilfs_segctor_accept(sci, &req); err = nilfs_segctor_construct(sci, SC_LSEG_SR);
err = nilfs_segctor_construct(sci, &req);
nilfs_remove_written_gcinodes(nilfs, &sci->sc_gc_inodes); nilfs_remove_written_gcinodes(nilfs, &sci->sc_gc_inodes);
nilfs_segctor_notify(sci, &req);
if (likely(!err)) if (likely(!err))
break; break;
...@@ -2560,6 +2558,16 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv, ...@@ -2560,6 +2558,16 @@ int nilfs_clean_segments(struct super_block *sb, struct nilfs_argv *argv,
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(sci->sc_interval); 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: out_unlock:
sci->sc_freesegs = NULL; sci->sc_freesegs = NULL;
...@@ -2573,13 +2581,9 @@ static void nilfs_segctor_thread_construct(struct nilfs_sc_info *sci, int mode) ...@@ -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_sb_info *sbi = sci->sc_sbi;
struct nilfs_transaction_info ti; struct nilfs_transaction_info ti;
struct nilfs_segctor_req req = { .mode = mode };
nilfs_transaction_lock(sbi, &ti, 0); nilfs_transaction_lock(sbi, &ti, 0);
nilfs_segctor_construct(sci, mode);
nilfs_segctor_accept(sci, &req);
nilfs_segctor_construct(sci, &req);
nilfs_segctor_notify(sci, &req);
/* /*
* Unclosed segment should be retried. We do this using sc_timer. * 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) ...@@ -2635,6 +2639,7 @@ static int nilfs_segctor_flush_mode(struct nilfs_sc_info *sci)
static int nilfs_segctor_thread(void *arg) static int nilfs_segctor_thread(void *arg)
{ {
struct nilfs_sc_info *sci = (struct nilfs_sc_info *)arg; struct nilfs_sc_info *sci = (struct nilfs_sc_info *)arg;
struct the_nilfs *nilfs = sci->sc_sbi->s_nilfs;
struct timer_list timer; struct timer_list timer;
int timeout = 0; int timeout = 0;
...@@ -2680,7 +2685,6 @@ static int nilfs_segctor_thread(void *arg) ...@@ -2680,7 +2685,6 @@ static int nilfs_segctor_thread(void *arg)
} else { } else {
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
int should_sleep = 1; int should_sleep = 1;
struct the_nilfs *nilfs;
prepare_to_wait(&sci->sc_wait_daemon, &wait, prepare_to_wait(&sci->sc_wait_daemon, &wait,
TASK_INTERRUPTIBLE); TASK_INTERRUPTIBLE);
...@@ -2701,8 +2705,8 @@ static int nilfs_segctor_thread(void *arg) ...@@ -2701,8 +2705,8 @@ static int nilfs_segctor_thread(void *arg)
finish_wait(&sci->sc_wait_daemon, &wait); finish_wait(&sci->sc_wait_daemon, &wait);
timeout = ((sci->sc_state & NILFS_SEGCTOR_COMMIT) && timeout = ((sci->sc_state & NILFS_SEGCTOR_COMMIT) &&
time_after_eq(jiffies, sci->sc_timer->expires)); 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); set_nilfs_discontinued(nilfs);
} }
goto loop; goto loop;
...@@ -2797,12 +2801,9 @@ static void nilfs_segctor_write_out(struct nilfs_sc_info *sci) ...@@ -2797,12 +2801,9 @@ static void nilfs_segctor_write_out(struct nilfs_sc_info *sci)
do { do {
struct nilfs_sb_info *sbi = sci->sc_sbi; struct nilfs_sb_info *sbi = sci->sc_sbi;
struct nilfs_transaction_info ti; struct nilfs_transaction_info ti;
struct nilfs_segctor_req req = { .mode = SC_LSEG_SR };
nilfs_transaction_lock(sbi, &ti, 0); nilfs_transaction_lock(sbi, &ti, 0);
nilfs_segctor_accept(sci, &req); ret = nilfs_segctor_construct(sci, SC_LSEG_SR);
ret = nilfs_segctor_construct(sci, &req);
nilfs_segctor_notify(sci, &req);
nilfs_transaction_unlock(sbi); nilfs_transaction_unlock(sbi);
} while (ret && retrycount-- > 0); } while (ret && retrycount-- > 0);
...@@ -2865,8 +2866,15 @@ int nilfs_attach_segment_constructor(struct nilfs_sb_info *sbi) ...@@ -2865,8 +2866,15 @@ int nilfs_attach_segment_constructor(struct nilfs_sb_info *sbi)
struct the_nilfs *nilfs = sbi->s_nilfs; struct the_nilfs *nilfs = sbi->s_nilfs;
int err; int err;
/* Each field of nilfs_segctor is cleared through the initialization if (NILFS_SC(sbi)) {
of super-block info */ /*
* 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); sbi->s_sc_info = nilfs_segctor_new(sbi);
if (!sbi->s_sc_info) if (!sbi->s_sc_info)
return -ENOMEM; return -ENOMEM;
......
...@@ -116,6 +116,7 @@ struct nilfs_segsum_pointer { ...@@ -116,6 +116,7 @@ struct nilfs_segsum_pointer {
* @sc_wait_daemon: Daemon wait queue * @sc_wait_daemon: Daemon wait queue
* @sc_wait_task: Start/end wait queue to control segctord task * @sc_wait_task: Start/end wait queue to control segctord task
* @sc_seq_request: Request counter * @sc_seq_request: Request counter
* @sc_seq_accept: Accepted request count
* @sc_seq_done: Completion counter * @sc_seq_done: Completion counter
* @sc_sync: Request of explicit sync operation * @sc_sync: Request of explicit sync operation
* @sc_interval: Timeout value of background construction * @sc_interval: Timeout value of background construction
...@@ -169,6 +170,7 @@ struct nilfs_sc_info { ...@@ -169,6 +170,7 @@ struct nilfs_sc_info {
wait_queue_head_t sc_wait_task; wait_queue_head_t sc_wait_task;
__u32 sc_seq_request; __u32 sc_seq_request;
__u32 sc_seq_accepted;
__u32 sc_seq_done; __u32 sc_seq_done;
int sc_sync; int sc_sync;
......
...@@ -96,9 +96,6 @@ void nilfs_error(struct super_block *sb, const char *function, ...@@ -96,9 +96,6 @@ void nilfs_error(struct super_block *sb, const char *function,
if (!(sb->s_flags & MS_RDONLY)) { if (!(sb->s_flags & MS_RDONLY)) {
struct the_nilfs *nilfs = sbi->s_nilfs; struct the_nilfs *nilfs = sbi->s_nilfs;
if (!nilfs_test_opt(sbi, ERRORS_CONT))
nilfs_detach_segment_constructor(sbi);
down_write(&nilfs->ns_sem); down_write(&nilfs->ns_sem);
if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) { if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) {
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) ...@@ -301,7 +298,7 @@ int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb)
memcpy(sbp[1], sbp[0], nilfs->ns_sbsize); memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
nilfs->ns_sbwtime[1] = t; nilfs->ns_sbwtime[1] = t;
} }
sbi->s_super->s_dirt = 0; clear_nilfs_sb_dirty(nilfs);
return nilfs_sync_super(sbi, dupsb); return nilfs_sync_super(sbi, dupsb);
} }
...@@ -345,7 +342,7 @@ static int nilfs_sync_fs(struct super_block *sb, int wait) ...@@ -345,7 +342,7 @@ static int nilfs_sync_fs(struct super_block *sb, int wait)
err = nilfs_construct_segment(sb); err = nilfs_construct_segment(sb);
down_write(&nilfs->ns_sem); down_write(&nilfs->ns_sem);
if (sb->s_dirt) if (nilfs_sb_dirty(nilfs))
nilfs_commit_super(sbi, 1); nilfs_commit_super(sbi, 1);
up_write(&nilfs->ns_sem); up_write(&nilfs->ns_sem);
...@@ -481,6 +478,8 @@ static int nilfs_show_options(struct seq_file *seq, struct vfsmount *vfs) ...@@ -481,6 +478,8 @@ static int nilfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
seq_printf(seq, ",order=strict"); seq_printf(seq, ",order=strict");
if (nilfs_test_opt(sbi, NORECOVERY)) if (nilfs_test_opt(sbi, NORECOVERY))
seq_printf(seq, ",norecovery"); seq_printf(seq, ",norecovery");
if (nilfs_test_opt(sbi, DISCARD))
seq_printf(seq, ",discard");
return 0; return 0;
} }
...@@ -550,7 +549,7 @@ static const struct export_operations nilfs_export_ops = { ...@@ -550,7 +549,7 @@ static const struct export_operations nilfs_export_ops = {
enum { enum {
Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_err_cont, Opt_err_panic, Opt_err_ro,
Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery, Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery,
Opt_err, Opt_discard, Opt_err,
}; };
static match_table_t tokens = { static match_table_t tokens = {
...@@ -561,6 +560,7 @@ static match_table_t tokens = { ...@@ -561,6 +560,7 @@ static match_table_t tokens = {
{Opt_snapshot, "cp=%u"}, {Opt_snapshot, "cp=%u"},
{Opt_order, "order=%s"}, {Opt_order, "order=%s"},
{Opt_norecovery, "norecovery"}, {Opt_norecovery, "norecovery"},
{Opt_discard, "discard"},
{Opt_err, NULL} {Opt_err, NULL}
}; };
...@@ -614,6 +614,9 @@ static int parse_options(char *options, struct super_block *sb) ...@@ -614,6 +614,9 @@ static int parse_options(char *options, struct super_block *sb)
case Opt_norecovery: case Opt_norecovery:
nilfs_set_opt(sbi, NORECOVERY); nilfs_set_opt(sbi, NORECOVERY);
break; break;
case Opt_discard:
nilfs_set_opt(sbi, DISCARD);
break;
default: default:
printk(KERN_ERR printk(KERN_ERR
"NILFS: Unrecognized mount option \"%s\"\n", p); "NILFS: Unrecognized mount option \"%s\"\n", p);
......
...@@ -646,6 +646,44 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) ...@@ -646,6 +646,44 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data)
goto out; 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) int nilfs_count_free_blocks(struct the_nilfs *nilfs, sector_t *nblocks)
{ {
struct inode *dat = nilfs_dat_inode(nilfs); struct inode *dat = nilfs_dat_inode(nilfs);
......
...@@ -38,6 +38,7 @@ enum { ...@@ -38,6 +38,7 @@ enum {
the latest checkpoint was loaded */ the latest checkpoint was loaded */
THE_NILFS_DISCONTINUED, /* 'next' pointer chain has broken */ THE_NILFS_DISCONTINUED, /* 'next' pointer chain has broken */
THE_NILFS_GC_RUNNING, /* gc process is running */ 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) ...@@ -197,6 +198,7 @@ THE_NILFS_FNS(INIT, init)
THE_NILFS_FNS(LOADED, loaded) THE_NILFS_FNS(LOADED, loaded)
THE_NILFS_FNS(DISCONTINUED, discontinued) THE_NILFS_FNS(DISCONTINUED, discontinued)
THE_NILFS_FNS(GC_RUNNING, gc_running) THE_NILFS_FNS(GC_RUNNING, gc_running)
THE_NILFS_FNS(SB_DIRTY, sb_dirty)
/* Minimum interval of periodical update of superblocks (in seconds) */ /* Minimum interval of periodical update of superblocks (in seconds) */
#define NILFS_SB_FREQ 10 #define NILFS_SB_FREQ 10
...@@ -221,6 +223,7 @@ struct the_nilfs *find_or_create_nilfs(struct block_device *); ...@@ -221,6 +223,7 @@ struct the_nilfs *find_or_create_nilfs(struct block_device *);
void put_nilfs(struct the_nilfs *); void put_nilfs(struct the_nilfs *);
int init_nilfs(struct the_nilfs *, struct nilfs_sb_info *, char *); int init_nilfs(struct the_nilfs *, struct nilfs_sb_info *, char *);
int load_nilfs(struct the_nilfs *, struct nilfs_sb_info *); 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 *); int nilfs_count_free_blocks(struct the_nilfs *, sector_t *);
struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *, int, __u64); struct nilfs_sb_info *nilfs_find_sbinfo(struct the_nilfs *, int, __u64);
int nilfs_checkpoint_is_mounted(struct the_nilfs *, __u64, int); int nilfs_checkpoint_is_mounted(struct the_nilfs *, __u64, int);
......
...@@ -153,6 +153,7 @@ struct nilfs_super_root { ...@@ -153,6 +153,7 @@ struct nilfs_super_root {
semantics also for data */ semantics also for data */
#define NILFS_MOUNT_NORECOVERY 0x4000 /* Disable write access during #define NILFS_MOUNT_NORECOVERY 0x4000 /* Disable write access during
mount-time recovery */ mount-time recovery */
#define NILFS_MOUNT_DISCARD 0x8000 /* Issue DISCARD requests */
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment