mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 05:41:55 +00:00
nilfs2: segment constructor
This adds the segment constructor (also called log writer). The segment constructor collects dirty buffers for every dirty inode, makes summaries of the buffers, assigns disk block addresses to the buffers, and then submits BIOs for the buffers. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
64b5a32e0b
commit
9ff05123e3
@ -73,10 +73,66 @@ nilfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct page *page)
|
||||
static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
{
|
||||
if (!(vma->vm_flags & (VM_WRITE | VM_MAYWRITE)))
|
||||
return -EPERM;
|
||||
struct page *page = vmf->page;
|
||||
struct inode *inode = vma->vm_file->f_dentry->d_inode;
|
||||
struct nilfs_transaction_info ti;
|
||||
int ret;
|
||||
|
||||
if (unlikely(nilfs_near_disk_full(NILFS_SB(inode->i_sb)->s_nilfs)))
|
||||
return VM_FAULT_SIGBUS; /* -ENOSPC */
|
||||
|
||||
lock_page(page);
|
||||
if (page->mapping != inode->i_mapping ||
|
||||
page_offset(page) >= i_size_read(inode) || !PageUptodate(page)) {
|
||||
unlock_page(page);
|
||||
return VM_FAULT_NOPAGE; /* make the VM retry the fault */
|
||||
}
|
||||
|
||||
/*
|
||||
* check to see if the page is mapped already (no holes)
|
||||
*/
|
||||
if (PageMappedToDisk(page)) {
|
||||
unlock_page(page);
|
||||
goto mapped;
|
||||
}
|
||||
if (page_has_buffers(page)) {
|
||||
struct buffer_head *bh, *head;
|
||||
int fully_mapped = 1;
|
||||
|
||||
bh = head = page_buffers(page);
|
||||
do {
|
||||
if (!buffer_mapped(bh)) {
|
||||
fully_mapped = 0;
|
||||
break;
|
||||
}
|
||||
} while (bh = bh->b_this_page, bh != head);
|
||||
|
||||
if (fully_mapped) {
|
||||
SetPageMappedToDisk(page);
|
||||
unlock_page(page);
|
||||
goto mapped;
|
||||
}
|
||||
}
|
||||
unlock_page(page);
|
||||
|
||||
/*
|
||||
* fill hole blocks
|
||||
*/
|
||||
ret = nilfs_transaction_begin(inode->i_sb, &ti, 1);
|
||||
/* never returns -ENOMEM, but may return -ENOSPC */
|
||||
if (unlikely(ret))
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
ret = block_page_mkwrite(vma, vmf, nilfs_get_block);
|
||||
if (unlikely(ret)) {
|
||||
nilfs_transaction_abort(inode->i_sb);
|
||||
return ret;
|
||||
}
|
||||
nilfs_transaction_commit(inode->i_sb);
|
||||
|
||||
mapped:
|
||||
SetPageChecked(page);
|
||||
wait_on_page_writeback(page);
|
||||
return 0;
|
||||
|
85
fs/nilfs2/seglist.h
Normal file
85
fs/nilfs2/seglist.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* seglist.h - expediential structure and routines to handle list of segments
|
||||
* (would be removed in a future release)
|
||||
*
|
||||
* Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Written by Ryusuke Konishi <ryusuke@osrg.net>
|
||||
*
|
||||
*/
|
||||
#ifndef _NILFS_SEGLIST_H
|
||||
#define _NILFS_SEGLIST_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/nilfs2_fs.h>
|
||||
#include "sufile.h"
|
||||
|
||||
struct nilfs_segment_entry {
|
||||
__u64 segnum;
|
||||
|
||||
#define NILFS_SLH_FREED 0x0001 /* The segment was freed provisonally.
|
||||
It must be cancelled if
|
||||
construction aborted */
|
||||
|
||||
unsigned flags;
|
||||
struct list_head list;
|
||||
struct buffer_head *bh_su;
|
||||
struct nilfs_segment_usage *raw_su;
|
||||
};
|
||||
|
||||
|
||||
void nilfs_dispose_segment_list(struct list_head *);
|
||||
|
||||
static inline struct nilfs_segment_entry *
|
||||
nilfs_alloc_segment_entry(__u64 segnum)
|
||||
{
|
||||
struct nilfs_segment_entry *ent = kmalloc(sizeof(*ent), GFP_NOFS);
|
||||
|
||||
if (likely(ent)) {
|
||||
ent->segnum = segnum;
|
||||
ent->flags = 0;
|
||||
ent->bh_su = NULL;
|
||||
ent->raw_su = NULL;
|
||||
INIT_LIST_HEAD(&ent->list);
|
||||
}
|
||||
return ent;
|
||||
}
|
||||
|
||||
static inline int nilfs_open_segment_entry(struct nilfs_segment_entry *ent,
|
||||
struct inode *sufile)
|
||||
{
|
||||
return nilfs_sufile_get_segment_usage(sufile, ent->segnum,
|
||||
&ent->raw_su, &ent->bh_su);
|
||||
}
|
||||
|
||||
static inline void nilfs_close_segment_entry(struct nilfs_segment_entry *ent,
|
||||
struct inode *sufile)
|
||||
{
|
||||
if (!ent->bh_su)
|
||||
return;
|
||||
nilfs_sufile_put_segment_usage(sufile, ent->segnum, ent->bh_su);
|
||||
ent->bh_su = NULL;
|
||||
ent->raw_su = NULL;
|
||||
}
|
||||
|
||||
static inline void nilfs_free_segment_entry(struct nilfs_segment_entry *ent)
|
||||
{
|
||||
kfree(ent);
|
||||
}
|
||||
|
||||
#endif /* _NILFS_SEGLIST_H */
|
3187
fs/nilfs2/segment.c
Normal file
3187
fs/nilfs2/segment.c
Normal file
File diff suppressed because it is too large
Load Diff
246
fs/nilfs2/segment.h
Normal file
246
fs/nilfs2/segment.h
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* segment.h - NILFS Segment constructor prototypes and definitions
|
||||
*
|
||||
* Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Written by Ryusuke Konishi <ryusuke@osrg.net>
|
||||
*
|
||||
*/
|
||||
#ifndef _NILFS_SEGMENT_H
|
||||
#define _NILFS_SEGMENT_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/nilfs2_fs.h>
|
||||
#include "sb.h"
|
||||
|
||||
/**
|
||||
* struct nilfs_recovery_info - Recovery infomation
|
||||
* @ri_need_recovery: Recovery status
|
||||
* @ri_super_root: Block number of the last super root
|
||||
* @ri_ri_cno: Number of the last checkpoint
|
||||
* @ri_lsegs_start: Region for roll-forwarding (start block number)
|
||||
* @ri_lsegs_end: Region for roll-forwarding (end block number)
|
||||
* @ri_lseg_start_seq: Sequence value of the segment at ri_lsegs_start
|
||||
* @ri_used_segments: List of segments to be mark active
|
||||
* @ri_pseg_start: Block number of the last partial segment
|
||||
* @ri_seq: Sequence number on the last partial segment
|
||||
* @ri_segnum: Segment number on the last partial segment
|
||||
* @ri_nextnum: Next segment number on the last partial segment
|
||||
*/
|
||||
struct nilfs_recovery_info {
|
||||
int ri_need_recovery;
|
||||
sector_t ri_super_root;
|
||||
__u64 ri_cno;
|
||||
|
||||
sector_t ri_lsegs_start;
|
||||
sector_t ri_lsegs_end;
|
||||
u64 ri_lsegs_start_seq;
|
||||
struct list_head ri_used_segments;
|
||||
sector_t ri_pseg_start;
|
||||
u64 ri_seq;
|
||||
__u64 ri_segnum;
|
||||
__u64 ri_nextnum;
|
||||
};
|
||||
|
||||
/* ri_need_recovery */
|
||||
#define NILFS_RECOVERY_SR_UPDATED 1 /* The super root was updated */
|
||||
#define NILFS_RECOVERY_ROLLFORWARD_DONE 2 /* Rollforward was carried out */
|
||||
|
||||
/**
|
||||
* struct nilfs_cstage - Context of collection stage
|
||||
* @scnt: Stage count
|
||||
* @flags: State flags
|
||||
* @dirty_file_ptr: Pointer on dirty_files list, or inode of a target file
|
||||
* @gc_inode_ptr: Pointer on the list of gc-inodes
|
||||
*/
|
||||
struct nilfs_cstage {
|
||||
int scnt;
|
||||
unsigned flags;
|
||||
struct nilfs_inode_info *dirty_file_ptr;
|
||||
struct nilfs_inode_info *gc_inode_ptr;
|
||||
};
|
||||
|
||||
struct nilfs_segment_buffer;
|
||||
|
||||
struct nilfs_segsum_pointer {
|
||||
struct buffer_head *bh;
|
||||
unsigned offset; /* offset in bytes */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nilfs_sc_info - Segment constructor information
|
||||
* @sc_super: Back pointer to super_block struct
|
||||
* @sc_sbi: Back pointer to nilfs_sb_info struct
|
||||
* @sc_nblk_inc: Block count of current generation
|
||||
* @sc_dirty_files: List of files to be written
|
||||
* @sc_gc_inodes: List of GC inodes having blocks to be written
|
||||
* @sc_active_segments: List of active segments that were already written out
|
||||
* @sc_cleaning_segments: List of segments to be freed through construction
|
||||
* @sc_copied_buffers: List of copied buffers (buffer heads) to freeze data
|
||||
* @sc_segbufs: List of segment buffers
|
||||
* @sc_segbuf_nblocks: Number of available blocks in segment buffers.
|
||||
* @sc_curseg: Current segment buffer
|
||||
* @sc_super_root: Pointer to the super root buffer
|
||||
* @sc_stage: Collection stage
|
||||
* @sc_finfo_ptr: pointer to the current finfo struct in the segment summary
|
||||
* @sc_binfo_ptr: pointer to the current binfo struct in the segment summary
|
||||
* @sc_blk_cnt: Block count of a file
|
||||
* @sc_datablk_cnt: Data block count of a file
|
||||
* @sc_nblk_this_inc: Number of blocks included in the current logical segment
|
||||
* @sc_seg_ctime: Creation time
|
||||
* @sc_flags: Internal flags
|
||||
* @sc_sketch_inode: Inode of the sketch file
|
||||
* @sc_state_lock: spinlock for sc_state and so on
|
||||
* @sc_state: Segctord state flags
|
||||
* @sc_flush_request: inode bitmap of metadata files to be flushed
|
||||
* @sc_wait_request: Client request queue
|
||||
* @sc_wait_daemon: Daemon wait queue
|
||||
* @sc_wait_task: Start/end wait queue to control segctord task
|
||||
* @sc_seq_request: Request counter
|
||||
* @sc_seq_done: Completion counter
|
||||
* @sc_sync: Request of explicit sync operation
|
||||
* @sc_interval: Timeout value of background construction
|
||||
* @sc_mjcp_freq: Frequency of creating checkpoints
|
||||
* @sc_lseg_stime: Start time of the latest logical segment
|
||||
* @sc_watermark: Watermark for the number of dirty buffers
|
||||
* @sc_timer: Timer for segctord
|
||||
* @sc_task: current thread of segctord
|
||||
*/
|
||||
struct nilfs_sc_info {
|
||||
struct super_block *sc_super;
|
||||
struct nilfs_sb_info *sc_sbi;
|
||||
|
||||
unsigned long sc_nblk_inc;
|
||||
|
||||
struct list_head sc_dirty_files;
|
||||
struct list_head sc_gc_inodes;
|
||||
struct list_head sc_active_segments;
|
||||
struct list_head sc_cleaning_segments;
|
||||
struct list_head sc_copied_buffers;
|
||||
|
||||
/* Segment buffers */
|
||||
struct list_head sc_segbufs;
|
||||
unsigned long sc_segbuf_nblocks;
|
||||
struct nilfs_segment_buffer *sc_curseg;
|
||||
struct buffer_head *sc_super_root;
|
||||
|
||||
struct nilfs_cstage sc_stage;
|
||||
|
||||
struct nilfs_segsum_pointer sc_finfo_ptr;
|
||||
struct nilfs_segsum_pointer sc_binfo_ptr;
|
||||
unsigned long sc_blk_cnt;
|
||||
unsigned long sc_datablk_cnt;
|
||||
unsigned long sc_nblk_this_inc;
|
||||
time_t sc_seg_ctime;
|
||||
|
||||
unsigned long sc_flags;
|
||||
|
||||
/*
|
||||
* Pointer to an inode of the sketch.
|
||||
* This pointer is kept only while it contains data.
|
||||
* We protect it with a semaphore of the segment constructor.
|
||||
*/
|
||||
struct inode *sc_sketch_inode;
|
||||
|
||||
spinlock_t sc_state_lock;
|
||||
unsigned long sc_state;
|
||||
unsigned long sc_flush_request;
|
||||
|
||||
wait_queue_head_t sc_wait_request;
|
||||
wait_queue_head_t sc_wait_daemon;
|
||||
wait_queue_head_t sc_wait_task;
|
||||
|
||||
__u32 sc_seq_request;
|
||||
__u32 sc_seq_done;
|
||||
|
||||
int sc_sync;
|
||||
unsigned long sc_interval;
|
||||
unsigned long sc_mjcp_freq;
|
||||
unsigned long sc_lseg_stime; /* in 1/HZ seconds */
|
||||
unsigned long sc_watermark;
|
||||
|
||||
struct timer_list *sc_timer;
|
||||
struct task_struct *sc_task;
|
||||
};
|
||||
|
||||
/* sc_flags */
|
||||
enum {
|
||||
NILFS_SC_DIRTY, /* One or more dirty meta-data blocks exist */
|
||||
NILFS_SC_UNCLOSED, /* Logical segment is not closed */
|
||||
NILFS_SC_SUPER_ROOT, /* The latest segment has a super root */
|
||||
NILFS_SC_PRIOR_FLUSH, /* Requesting immediate flush without making a
|
||||
checkpoint */
|
||||
};
|
||||
|
||||
/* sc_state */
|
||||
#define NILFS_SEGCTOR_QUIT 0x0001 /* segctord is being destroyed */
|
||||
#define NILFS_SEGCTOR_COMMIT 0x0004 /* committed transaction exists */
|
||||
|
||||
/*
|
||||
* Constant parameters
|
||||
*/
|
||||
#define NILFS_SC_CLEANUP_RETRY 3 /* Retry count of construction when
|
||||
destroying segctord */
|
||||
|
||||
/*
|
||||
* Default values of timeout, in seconds.
|
||||
*/
|
||||
#define NILFS_SC_DEFAULT_TIMEOUT 5 /* Timeout value of dirty blocks.
|
||||
It triggers construction of a
|
||||
logical segment with a super root */
|
||||
#define NILFS_SC_DEFAULT_SR_FREQ 30 /* Maximum frequency of super root
|
||||
creation */
|
||||
#define NILFS_SC_DEFAULT_SB_FREQ 30 /* Minimum interval of periodical
|
||||
update of superblock (reserved) */
|
||||
|
||||
/*
|
||||
* The default threshold amount of data, in block counts.
|
||||
*/
|
||||
#define NILFS_SC_DEFAULT_WATERMARK 3600
|
||||
|
||||
|
||||
/* segment.c */
|
||||
extern int nilfs_init_transaction_cache(void);
|
||||
extern void nilfs_destroy_transaction_cache(void);
|
||||
extern void nilfs_relax_pressure_in_lock(struct super_block *);
|
||||
|
||||
extern int nilfs_construct_segment(struct super_block *);
|
||||
extern int nilfs_construct_dsync_segment(struct super_block *,
|
||||
struct inode *);
|
||||
extern void nilfs_flush_segment(struct super_block *, ino_t);
|
||||
extern int nilfs_clean_segments(struct super_block *, void __user *);
|
||||
|
||||
extern int nilfs_segctor_add_segments_to_be_freed(struct nilfs_sc_info *,
|
||||
__u64 *, size_t);
|
||||
extern void nilfs_segctor_clear_segments_to_be_freed(struct nilfs_sc_info *);
|
||||
|
||||
extern int nilfs_attach_segment_constructor(struct nilfs_sb_info *,
|
||||
struct nilfs_recovery_info *);
|
||||
extern void nilfs_detach_segment_constructor(struct nilfs_sb_info *);
|
||||
|
||||
/* recovery.c */
|
||||
extern int nilfs_read_super_root_block(struct super_block *, sector_t,
|
||||
struct buffer_head **, int);
|
||||
extern int nilfs_search_super_root(struct the_nilfs *, struct nilfs_sb_info *,
|
||||
struct nilfs_recovery_info *);
|
||||
extern int nilfs_recover_logical_segments(struct the_nilfs *,
|
||||
struct nilfs_sb_info *,
|
||||
struct nilfs_recovery_info *);
|
||||
|
||||
#endif /* _NILFS_SEGMENT_H */
|
Loading…
Reference in New Issue
Block a user