2707 lines · 71.0 KB · C
RawDownload
/* * segment.c - NILFS segment constructor. * * 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> * */ #include <linux/pagemap.h>#include <linux/buffer_head.h>#include <linux/writeback.h>#include <linux/bio.h>#include <linux/completion.h>#include <linux/blkdev.h>#include <linux/backing-dev.h>#include <linux/freezer.h>#include <linux/kthread.h>#include <linux/crc32.h>#include <linux/pagevec.h>#include <linux/slab.h>#include "nilfs.h"#include "btnode.h"#include "page.h"#include "segment.h"#include "sufile.h"#include "cpfile.h"#include "ifile.h"#include "segbuf.h"  /* * Segment constructor */#define SC_N_INODEVEC	16   /* Size of locally allocated inode vector */ #define SC_MAX_SEGDELTA 64   /* Upper limit of the number of segments				appended in collection retry loop */ /* Construction mode */enum {	SC_LSEG_SR = 1,	/* Make a logical segment having a super root */	SC_LSEG_DSYNC,	/* Flush data blocks of a given file and make			   a logical segment without a super root */	SC_FLUSH_FILE,	/* Flush data files, leads to segment writes without			   creating a checkpoint */	SC_FLUSH_DAT,	/* Flush DAT file. This also creates segments without			   a checkpoint */}; /* Stage numbers of dirty block collection */enum {	NILFS_ST_INIT = 0,	NILFS_ST_GC,		/* Collecting dirty blocks for GC */	NILFS_ST_FILE,	NILFS_ST_IFILE,	NILFS_ST_CPFILE,	NILFS_ST_SUFILE,	NILFS_ST_DAT,	NILFS_ST_SR,		/* Super root */	NILFS_ST_DSYNC,		/* Data sync blocks */	NILFS_ST_DONE,}; /* State flags of collection */#define NILFS_CF_NODE		0x0001	/* Collecting node blocks */#define NILFS_CF_IFILE_STARTED	0x0002	/* IFILE stage has started */#define NILFS_CF_SUFREED	0x0004	/* segment usages has been freed */#define NILFS_CF_HISTORY_MASK	(NILFS_CF_IFILE_STARTED | NILFS_CF_SUFREED) /* Operations depending on the construction mode and file type */struct nilfs_sc_operations {	int (*collect_data)(struct nilfs_sc_info *, struct buffer_head *,			    struct inode *);	int (*collect_node)(struct nilfs_sc_info *, struct buffer_head *,			    struct inode *);	int (*collect_bmap)(struct nilfs_sc_info *, struct buffer_head *,			    struct inode *);	void (*write_data_binfo)(struct nilfs_sc_info *,				 struct nilfs_segsum_pointer *,				 union nilfs_binfo *);	void (*write_node_binfo)(struct nilfs_sc_info *,				 struct nilfs_segsum_pointer *,				 union nilfs_binfo *);}; /* * Other definitions */static void nilfs_segctor_start_timer(struct nilfs_sc_info *);static void nilfs_segctor_do_flush(struct nilfs_sc_info *, int);static void nilfs_segctor_do_immediate_flush(struct nilfs_sc_info *);static void nilfs_dispose_list(struct the_nilfs *, struct list_head *, int); #define nilfs_cnt32_gt(a, b)   \	(typecheck(__u32, a) && typecheck(__u32, b) && \	 ((__s32)(b) - (__s32)(a) < 0))#define nilfs_cnt32_ge(a, b)   \	(typecheck(__u32, a) && typecheck(__u32, b) && \	 ((__s32)(a) - (__s32)(b) >= 0))#define nilfs_cnt32_lt(a, b)  nilfs_cnt32_gt(b, a)#define nilfs_cnt32_le(a, b)  nilfs_cnt32_ge(b, a) static int nilfs_prepare_segment_lock(struct nilfs_transaction_info *ti){	struct nilfs_transaction_info *cur_ti = current->journal_info;	void *save = NULL; 	if (cur_ti) {		if (cur_ti->ti_magic == NILFS_TI_MAGIC)			return ++cur_ti->ti_count;		else {			/*			 * If journal_info field is occupied by other FS,			 * it is saved and will be restored on			 * nilfs_transaction_commit().			 */			printk(KERN_WARNING			       "NILFS warning: journal info from a different "			       "FS\n");			save = current->journal_info;		}	}	if (!ti) {		ti = kmem_cache_alloc(nilfs_transaction_cachep, GFP_NOFS);		if (!ti)			return -ENOMEM;		ti->ti_flags = NILFS_TI_DYNAMIC_ALLOC;	} else {		ti->ti_flags = 0;	}	ti->ti_count = 0;	ti->ti_save = save;	ti->ti_magic = NILFS_TI_MAGIC;	current->journal_info = ti;	return 0;} /** * nilfs_transaction_begin - start indivisible file operations. * @sb: super block * @ti: nilfs_transaction_info * @vacancy_check: flags for vacancy rate checks * * nilfs_transaction_begin() acquires a reader/writer semaphore, called * the segment semaphore, to make a segment construction and write tasks * exclusive.  The function is used with nilfs_transaction_commit() in pairs. * The region enclosed by these two functions can be nested.  To avoid a * deadlock, the semaphore is only acquired or released in the outermost call. * * This function allocates a nilfs_transaction_info struct to keep context * information on it.  It is initialized and hooked onto the current task in * the outermost call.  If a pre-allocated struct is given to @ti, it is used * instead; otherwise a new struct is assigned from a slab. * * When @vacancy_check flag is set, this function will check the amount of * free space, and will wait for the GC to reclaim disk space if low capacity. * * Return Value: On success, 0 is returned. On error, one of the following * negative error code is returned. * * %-ENOMEM - Insufficient memory available. * * %-ENOSPC - No space left on device */int nilfs_transaction_begin(struct super_block *sb,			    struct nilfs_transaction_info *ti,			    int vacancy_check){	struct the_nilfs *nilfs;	int ret = nilfs_prepare_segment_lock(ti); 	if (unlikely(ret < 0))		return ret;	if (ret > 0)		return 0; 	vfs_check_frozen(sb, SB_FREEZE_WRITE); 	nilfs = sb->s_fs_info;	down_read(&nilfs->ns_segctor_sem);	if (vacancy_check && nilfs_near_disk_full(nilfs)) {		up_read(&nilfs->ns_segctor_sem);		ret = -ENOSPC;		goto failed;	}	return 0;  failed:	ti = current->journal_info;	current->journal_info = ti->ti_save;	if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC)		kmem_cache_free(nilfs_transaction_cachep, ti);	return ret;} /** * nilfs_transaction_commit - commit indivisible file operations. * @sb: super block * * nilfs_transaction_commit() releases the read semaphore which is * acquired by nilfs_transaction_begin(). This is only performed * in outermost call of this function.  If a commit flag is set, * nilfs_transaction_commit() sets a timer to start the segment * constructor.  If a sync flag is set, it starts construction * directly. */int nilfs_transaction_commit(struct super_block *sb){	struct nilfs_transaction_info *ti = current->journal_info;	struct the_nilfs *nilfs = sb->s_fs_info;	int err = 0; 	BUG_ON(ti == NULL || ti->ti_magic != NILFS_TI_MAGIC);	ti->ti_flags |= NILFS_TI_COMMIT;	if (ti->ti_count > 0) {		ti->ti_count--;		return 0;	}	if (nilfs->ns_writer) {		struct nilfs_sc_info *sci = nilfs->ns_writer; 		if (ti->ti_flags & NILFS_TI_COMMIT)			nilfs_segctor_start_timer(sci);		if (atomic_read(&nilfs->ns_ndirtyblks) > sci->sc_watermark)			nilfs_segctor_do_flush(sci, 0);	}	up_read(&nilfs->ns_segctor_sem);	current->journal_info = ti->ti_save; 	if (ti->ti_flags & NILFS_TI_SYNC)		err = nilfs_construct_segment(sb);	if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC)		kmem_cache_free(nilfs_transaction_cachep, ti);	return err;} void nilfs_transaction_abort(struct super_block *sb){	struct nilfs_transaction_info *ti = current->journal_info;	struct the_nilfs *nilfs = sb->s_fs_info; 	BUG_ON(ti == NULL || ti->ti_magic != NILFS_TI_MAGIC);	if (ti->ti_count > 0) {		ti->ti_count--;		return;	}	up_read(&nilfs->ns_segctor_sem); 	current->journal_info = ti->ti_save;	if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC)		kmem_cache_free(nilfs_transaction_cachep, ti);} void nilfs_relax_pressure_in_lock(struct super_block *sb){	struct the_nilfs *nilfs = sb->s_fs_info;	struct nilfs_sc_info *sci = nilfs->ns_writer; 	if (!sci || !sci->sc_flush_request)		return; 	set_bit(NILFS_SC_PRIOR_FLUSH, &sci->sc_flags);	up_read(&nilfs->ns_segctor_sem); 	down_write(&nilfs->ns_segctor_sem);	if (sci->sc_flush_request &&	    test_bit(NILFS_SC_PRIOR_FLUSH, &sci->sc_flags)) {		struct nilfs_transaction_info *ti = current->journal_info; 		ti->ti_flags |= NILFS_TI_WRITER;		nilfs_segctor_do_immediate_flush(sci);		ti->ti_flags &= ~NILFS_TI_WRITER;	}	downgrade_write(&nilfs->ns_segctor_sem);} static void nilfs_transaction_lock(struct super_block *sb,				   struct nilfs_transaction_info *ti,				   int gcflag){	struct nilfs_transaction_info *cur_ti = current->journal_info;	struct the_nilfs *nilfs = sb->s_fs_info;	struct nilfs_sc_info *sci = nilfs->ns_writer; 	WARN_ON(cur_ti);	ti->ti_flags = NILFS_TI_WRITER;	ti->ti_count = 0;	ti->ti_save = cur_ti;	ti->ti_magic = NILFS_TI_MAGIC;	INIT_LIST_HEAD(&ti->ti_garbage);	current->journal_info = ti; 	for (;;) {		down_write(&nilfs->ns_segctor_sem);		if (!test_bit(NILFS_SC_PRIOR_FLUSH, &sci->sc_flags))			break; 		nilfs_segctor_do_immediate_flush(sci); 		up_write(&nilfs->ns_segctor_sem);		yield();	}	if (gcflag)		ti->ti_flags |= NILFS_TI_GC;} static void nilfs_transaction_unlock(struct super_block *sb){	struct nilfs_transaction_info *ti = current->journal_info;	struct the_nilfs *nilfs = sb->s_fs_info; 	BUG_ON(ti == NULL || ti->ti_magic != NILFS_TI_MAGIC);	BUG_ON(ti->ti_count > 0); 	up_write(&nilfs->ns_segctor_sem);	current->journal_info = ti->ti_save;	if (!list_empty(&ti->ti_garbage))		nilfs_dispose_list(nilfs, &ti->ti_garbage, 0);} static void *nilfs_segctor_map_segsum_entry(struct nilfs_sc_info *sci,					    struct nilfs_segsum_pointer *ssp,					    unsigned bytes){	struct nilfs_segment_buffer *segbuf = sci->sc_curseg;	unsigned blocksize = sci->sc_super->s_blocksize;	void *p; 	if (unlikely(ssp->offset + bytes > blocksize)) {		ssp->offset = 0;		BUG_ON(NILFS_SEGBUF_BH_IS_LAST(ssp->bh,					       &segbuf->sb_segsum_buffers));		ssp->bh = NILFS_SEGBUF_NEXT_BH(ssp->bh);	}	p = ssp->bh->b_data + ssp->offset;	ssp->offset += bytes;	return p;} /** * nilfs_segctor_reset_segment_buffer - reset the current segment buffer * @sci: nilfs_sc_info */static int nilfs_segctor_reset_segment_buffer(struct nilfs_sc_info *sci){	struct nilfs_segment_buffer *segbuf = sci->sc_curseg;	struct buffer_head *sumbh;	unsigned sumbytes;	unsigned flags = 0;	int err; 	if (nilfs_doing_gc())		flags = NILFS_SS_GC;	err = nilfs_segbuf_reset(segbuf, flags, sci->sc_seg_ctime, sci->sc_cno);	if (unlikely(err))		return err; 	sumbh = NILFS_SEGBUF_FIRST_BH(&segbuf->sb_segsum_buffers);	sumbytes = segbuf->sb_sum.sumbytes;	sci->sc_finfo_ptr.bh = sumbh;  sci->sc_finfo_ptr.offset = sumbytes;	sci->sc_binfo_ptr.bh = sumbh;  sci->sc_binfo_ptr.offset = sumbytes;	sci->sc_blk_cnt = sci->sc_datablk_cnt = 0;	return 0;} static int nilfs_segctor_feed_segment(struct nilfs_sc_info *sci){	sci->sc_nblk_this_inc += sci->sc_curseg->sb_sum.nblocks;	if (NILFS_SEGBUF_IS_LAST(sci->sc_curseg, &sci->sc_segbufs))		return -E2BIG; /* The current segment is filled up				  (internal code) */	sci->sc_curseg = NILFS_NEXT_SEGBUF(sci->sc_curseg);	return nilfs_segctor_reset_segment_buffer(sci);} static int nilfs_segctor_add_super_root(struct nilfs_sc_info *sci){	struct nilfs_segment_buffer *segbuf = sci->sc_curseg;	int err; 	if (segbuf->sb_sum.nblocks >= segbuf->sb_rest_blocks) {		err = nilfs_segctor_feed_segment(sci);		if (err)			return err;		segbuf = sci->sc_curseg;	}	err = nilfs_segbuf_extend_payload(segbuf, &segbuf->sb_super_root);	if (likely(!err))		segbuf->sb_sum.flags |= NILFS_SS_SR;	return err;} /* * Functions for making segment summary and payloads */static int nilfs_segctor_segsum_block_required(	struct nilfs_sc_info *sci, const struct nilfs_segsum_pointer *ssp,	unsigned binfo_size){	unsigned blocksize = sci->sc_super->s_blocksize;	/* Size of finfo and binfo is enough small against blocksize */ 	return ssp->offset + binfo_size +		(!sci->sc_blk_cnt ? sizeof(struct nilfs_finfo) : 0) >		blocksize;} static void nilfs_segctor_begin_finfo(struct nilfs_sc_info *sci,				      struct inode *inode){	sci->sc_curseg->sb_sum.nfinfo++;	sci->sc_binfo_ptr = sci->sc_finfo_ptr;	nilfs_segctor_map_segsum_entry(		sci, &sci->sc_binfo_ptr, sizeof(struct nilfs_finfo)); 	if (NILFS_I(inode)->i_root &&	    !test_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags))		set_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags);	/* skip finfo */} static void nilfs_segctor_end_finfo(struct nilfs_sc_info *sci,				    struct inode *inode){	struct nilfs_finfo *finfo;	struct nilfs_inode_info *ii;	struct nilfs_segment_buffer *segbuf;	__u64 cno; 	if (sci->sc_blk_cnt == 0)		return; 	ii = NILFS_I(inode); 	if (test_bit(NILFS_I_GCINODE, &ii->i_state))		cno = ii->i_cno;	else if (NILFS_ROOT_METADATA_FILE(inode->i_ino))		cno = 0;	else		cno = sci->sc_cno; 	finfo = nilfs_segctor_map_segsum_entry(sci, &sci->sc_finfo_ptr,						 sizeof(*finfo));	finfo->fi_ino = cpu_to_le64(inode->i_ino);	finfo->fi_nblocks = cpu_to_le32(sci->sc_blk_cnt);	finfo->fi_ndatablk = cpu_to_le32(sci->sc_datablk_cnt);	finfo->fi_cno = cpu_to_le64(cno); 	segbuf = sci->sc_curseg;	segbuf->sb_sum.sumbytes = sci->sc_binfo_ptr.offset +		sci->sc_super->s_blocksize * (segbuf->sb_sum.nsumblk - 1);	sci->sc_finfo_ptr = sci->sc_binfo_ptr;	sci->sc_blk_cnt = sci->sc_datablk_cnt = 0;} static int nilfs_segctor_add_file_block(struct nilfs_sc_info *sci,					struct buffer_head *bh,					struct inode *inode,					unsigned binfo_size){	struct nilfs_segment_buffer *segbuf;	int required, err = 0;  retry:	segbuf = sci->sc_curseg;	required = nilfs_segctor_segsum_block_required(		sci, &sci->sc_binfo_ptr, binfo_size);	if (segbuf->sb_sum.nblocks + required + 1 > segbuf->sb_rest_blocks) {		nilfs_segctor_end_finfo(sci, inode);		err = nilfs_segctor_feed_segment(sci);		if (err)			return err;		goto retry;	}	if (unlikely(required)) {		err = nilfs_segbuf_extend_segsum(segbuf);		if (unlikely(err))			goto failed;	}	if (sci->sc_blk_cnt == 0)		nilfs_segctor_begin_finfo(sci, inode); 	nilfs_segctor_map_segsum_entry(sci, &sci->sc_binfo_ptr, binfo_size);	/* Substitution to vblocknr is delayed until update_blocknr() */	nilfs_segbuf_add_file_buffer(segbuf, bh);

Lines 1–500 of 2707.