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.
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
============
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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;
......
......@@ -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)
......
......@@ -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;
......
......@@ -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;
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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 */
/**
......
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