2929 lines · 78.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * NILFS segment constructor. * * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. * * Written by Ryusuke Konishi. * */ #include <linux/pagemap.h>#include <linux/buffer_head.h>#include <linux/writeback.h>#include <linux/bitops.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/folio_batch.h>#include <linux/slab.h>#include <linux/sched/signal.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,}; #define CREATE_TRACE_POINTS#include <trace/events/nilfs2.h> /* * nilfs_sc_cstage_inc(), nilfs_sc_cstage_set(), nilfs_sc_cstage_get() are * wrapper functions of stage count (nilfs_sc_info->sc_stage.scnt). Users of * the variable must use them because transition of stage count must involve * trace events (trace_nilfs2_collection_stage_transition). * * nilfs_sc_cstage_get() isn't required for the above purpose because it doesn't * produce tracepoint events. It is provided just for making the intention * clear. */static inline void nilfs_sc_cstage_inc(struct nilfs_sc_info *sci){	sci->sc_stage.scnt++;	trace_nilfs2_collection_stage_transition(sci);} static inline void nilfs_sc_cstage_set(struct nilfs_sc_info *sci, int next_scnt){	sci->sc_stage.scnt = next_scnt;	trace_nilfs2_collection_stage_transition(sci);} static inline int nilfs_sc_cstage_get(struct nilfs_sc_info *sci){	return sci->sc_stage.scnt;} /* 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_ge(a, b)   \	(typecheck(__u32, a) && typecheck(__u32, b) && \	 ((__s32)((a) - (b)) >= 0)) static int nilfs_prepare_segment_lock(struct super_block *sb,				      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; 		/*		 * If journal_info field is occupied by other FS,		 * it is saved and will be restored on		 * nilfs_transaction_commit().		 */		nilfs_warn(sb, "journal info from a different FS");		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: 0 on success, or one of the following negative error codes on * failure: * * %-ENOMEM	- Insufficient memory available. * * %-ENOSPC	- No space left on device (if checking free space). */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(sb, ti);	struct nilfs_transaction_info *trace_ti; 	if (unlikely(ret < 0))		return ret;	if (ret > 0) {		trace_ti = current->journal_info; 		trace_nilfs2_transaction_transition(sb, trace_ti,				    trace_ti->ti_count, trace_ti->ti_flags,				    TRACE_NILFS2_TRANSACTION_BEGIN);		return 0;	} 	sb_start_intwrite(sb); 	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;	} 	trace_ti = current->journal_info;	trace_nilfs2_transaction_transition(sb, trace_ti, trace_ti->ti_count,					    trace_ti->ti_flags,					    TRACE_NILFS2_TRANSACTION_BEGIN);	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);	sb_end_intwrite(sb);	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. * * Return: 0 on success, or a negative error code on failure. */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--;		trace_nilfs2_transaction_transition(sb, ti, ti->ti_count,			    ti->ti_flags, TRACE_NILFS2_TRANSACTION_COMMIT);		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);	trace_nilfs2_transaction_transition(sb, ti, ti->ti_count,			    ti->ti_flags, TRACE_NILFS2_TRANSACTION_COMMIT); 	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);	sb_end_intwrite(sb);	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--;		trace_nilfs2_transaction_transition(sb, ti, ti->ti_count,			    ti->ti_flags, TRACE_NILFS2_TRANSACTION_ABORT);		return;	}	up_read(&nilfs->ns_segctor_sem); 	trace_nilfs2_transaction_transition(sb, ti, ti->ti_count,		    ti->ti_flags, TRACE_NILFS2_TRANSACTION_ABORT); 	current->journal_info = ti->ti_save;	if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC)		kmem_cache_free(nilfs_transaction_cachep, ti);	sb_end_intwrite(sb);} 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 (sb_rdonly(sb) || unlikely(!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;	current->journal_info = ti; 	for (;;) {		trace_nilfs2_transaction_transition(sb, ti, ti->ti_count,			    ti->ti_flags, TRACE_NILFS2_TRANSACTION_TRYLOCK); 		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);		cond_resched();	}	if (gcflag)		ti->ti_flags |= NILFS_TI_GC; 	trace_nilfs2_transaction_transition(sb, ti, ti->ti_count,			    ti->ti_flags, TRACE_NILFS2_TRANSACTION_LOCK);} 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; 	trace_nilfs2_transaction_transition(sb, ti, ti->ti_count,			    ti->ti_flags, TRACE_NILFS2_TRANSACTION_UNLOCK);} static void *nilfs_segctor_map_segsum_entry(struct nilfs_sc_info *sci,					    struct nilfs_segsum_pointer *ssp,					    unsigned int bytes){	struct nilfs_segment_buffer *segbuf = sci->sc_curseg;	unsigned int 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 * * Return: 0 on success, or a negative error code on failure. */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 int sumbytes;	unsigned int 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;} /** * nilfs_segctor_zeropad_segsum - zero pad the rest of the segment summary area * @sci: segment constructor object * * nilfs_segctor_zeropad_segsum() zero-fills unallocated space at the end of * the current segment summary block. */static void nilfs_segctor_zeropad_segsum(struct nilfs_sc_info *sci){	struct nilfs_segsum_pointer *ssp; 	ssp = sci->sc_blk_cnt > 0 ? &sci->sc_binfo_ptr : &sci->sc_finfo_ptr;	if (ssp->offset < ssp->bh->b_size)		memset(ssp->bh->b_data + ssp->offset, 0,		       ssp->bh->b_size - ssp->offset);} 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)				*/	nilfs_segctor_zeropad_segsum(sci);	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 int binfo_size){	unsigned int 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++;

Lines 1–500 of 2929.