Commit 0adc313c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull gfs2 updates from Andreas Gruenbacher:

 - Use iomap for non-journaled buffered I/O. This largely eliminates
   buffer heads on filesystems where the block size matches the page
   size. Many thanks to Christoph Hellwig for this patch!

 - Fixes for some more journaled data filesystem bugs, found by running
   xfstests with data journaling on for all files (chattr +j $MNT) (Bob
   Peterson)

 - gfs2_evict_inode refactoring (Bob Peterson)

 - Use the statfs data in the journal during recovery instead of reading
   it in from the local statfs inodes (Abhi Das)

 - Several other minor fixes by various people

* tag 'gfs2-for-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2: (30 commits)
  gfs2: Recover statfs info in journal head
  gfs2: lookup local statfs inodes prior to journal recovery
  gfs2: Add fields for statfs info in struct gfs2_log_header_host
  gfs2: Ignore subsequent errors after withdraw in rgrp_go_sync
  gfs2: Eliminate gl_vm
  gfs2: Only access gl_delete for iopen glocks
  gfs2: Fix comments to glock_hash_walk
  gfs2: eliminate GLF_QUEUED flag in favor of list_empty(gl_holders)
  gfs2: Ignore journal log writes for jdata holes
  gfs2: simplify gfs2_block_map
  gfs2: Only set PageChecked if we have a transaction
  gfs2: don't lock sd_ail_lock in gfs2_releasepage
  gfs2: make gfs2_ail1_empty_one return the count of active items
  gfs2: Wipe jdata and ail1 in gfs2_journal_wipe, formerly gfs2_meta_wipe
  gfs2: enhance log_blocks trace point to show log blocks free
  gfs2: add missing log_blocks trace points in gfs2_write_revokes
  gfs2: rename gfs2_write_full_page to gfs2_write_jdata_page, remove parm
  gfs2: add validation checks for size of superblock
  gfs2: use-after-free in sysfs deregistration
  gfs2: Fix NULL pointer dereference in gfs2_rgrp_dump
  ...
parents 0613ed91 bedb0f05
Loading
Loading
Loading
Loading
+36 −32
Original line number Diff line number Diff line
@@ -91,22 +91,13 @@ static int gfs2_writepage(struct page *page, struct writeback_control *wbc)
	struct inode *inode = page->mapping->host;
	struct gfs2_inode *ip = GFS2_I(inode);
	struct gfs2_sbd *sdp = GFS2_SB(inode);
	loff_t i_size = i_size_read(inode);
	pgoff_t end_index = i_size >> PAGE_SHIFT;
	unsigned offset;
	struct iomap_writepage_ctx wpc = { };

	if (gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(ip->i_gl)))
		goto out;
	if (current->journal_info)
		goto redirty;
	/* Is the page fully outside i_size? (truncate in progress) */
	offset = i_size & (PAGE_SIZE-1);
	if (page->index > end_index || (page->index == end_index && !offset)) {
		page->mapping->a_ops->invalidatepage(page, 0, PAGE_SIZE);
		goto out;
	}

	return nobh_writepage(page, gfs2_get_block_noalloc, wbc);
	return iomap_writepage(page, wbc, &wpc, &gfs2_writeback_ops);

redirty:
	redirty_page_for_writepage(wbc, page);
@@ -115,10 +106,15 @@ out:
	return 0;
}

/* This is the same as calling block_write_full_page, but it also
/**
 * gfs2_write_jdata_page - gfs2 jdata-specific version of block_write_full_page
 * @page: The page to write
 * @wbc: The writeback control
 *
 * This is the same as calling block_write_full_page, but it also
 * writes pages outside of i_size
 */
static int gfs2_write_full_page(struct page *page, get_block_t *get_block,
static int gfs2_write_jdata_page(struct page *page,
				 struct writeback_control *wbc)
{
	struct inode * const inode = page->mapping->host;
@@ -137,7 +133,7 @@ static int gfs2_write_full_page(struct page *page, get_block_t *get_block,
	if (page->index == end_index && offset)
		zero_user_segment(page, offset, PAGE_SIZE);

	return __block_write_full_page(inode, page, get_block, wbc,
	return __block_write_full_page(inode, page, gfs2_get_block_noalloc, wbc,
				       end_buffer_async_write);
}

@@ -166,7 +162,7 @@ static int __gfs2_jdata_writepage(struct page *page, struct writeback_control *w
		}
		gfs2_page_add_databufs(ip, page, 0, sdp->sd_vfs->s_blocksize);
	}
	return gfs2_write_full_page(page, gfs2_get_block_noalloc, wbc);
	return gfs2_write_jdata_page(page, wbc);
}

/**
@@ -208,7 +204,8 @@ static int gfs2_writepages(struct address_space *mapping,
			   struct writeback_control *wbc)
{
	struct gfs2_sbd *sdp = gfs2_mapping2sbd(mapping);
	int ret = mpage_writepages(mapping, wbc, gfs2_get_block_noalloc);
	struct iomap_writepage_ctx wpc = { };
	int ret;

	/*
	 * Even if we didn't write any pages here, we might still be holding
@@ -216,9 +213,9 @@ static int gfs2_writepages(struct address_space *mapping,
	 * want balance_dirty_pages() to loop indefinitely trying to write out
	 * pages held in the ail that it can't find.
	 */
	ret = iomap_writepages(mapping, wbc, &wpc, &gfs2_writeback_ops);
	if (ret == 0)
		set_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags);

	return ret;
}

@@ -470,12 +467,13 @@ static int stuffed_readpage(struct gfs2_inode *ip, struct page *page)

static int __gfs2_readpage(void *file, struct page *page)
{
	struct gfs2_inode *ip = GFS2_I(page->mapping->host);
	struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host);
	struct inode *inode = page->mapping->host;
	struct gfs2_inode *ip = GFS2_I(inode);
	struct gfs2_sbd *sdp = GFS2_SB(inode);
	int error;

	if (i_blocksize(page->mapping->host) == PAGE_SIZE &&
	    !page_has_buffers(page)) {
	if (!gfs2_is_jdata(ip) ||
	    (i_blocksize(inode) == PAGE_SIZE && !page_has_buffers(page))) {
		error = iomap_readpage(page, &gfs2_iomap_ops);
	} else if (gfs2_is_stuffed(ip)) {
		error = stuffed_readpage(ip, page);
@@ -563,8 +561,12 @@ static void gfs2_readahead(struct readahead_control *rac)
	struct inode *inode = rac->mapping->host;
	struct gfs2_inode *ip = GFS2_I(inode);

	if (!gfs2_is_stuffed(ip))
	if (gfs2_is_stuffed(ip))
		;
	else if (gfs2_is_jdata(ip))
		mpage_readahead(rac, gfs2_block_map);
	else
		iomap_readahead(rac, &gfs2_iomap_ops);
}

/**
@@ -621,6 +623,7 @@ out:
 
static int jdata_set_page_dirty(struct page *page)
{
	if (current->journal_info)
		SetPageChecked(page);
	return __set_page_dirty_buffers(page);
}
@@ -663,8 +666,11 @@ static void gfs2_discard(struct gfs2_sbd *sdp, struct buffer_head *bh)
	if (bd) {
		if (!list_empty(&bd->bd_list) && !buffer_pinned(bh))
			list_del_init(&bd->bd_list);
		else
		else {
			spin_lock(&sdp->sd_ail_lock);
			gfs2_remove_from_journal(bh, REMOVE_JDATA);
			spin_unlock(&sdp->sd_ail_lock);
		}
	}
	bh->b_bdev = NULL;
	clear_buffer_mapped(bh);
@@ -736,7 +742,6 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask)
	 */

	gfs2_log_lock(sdp);
	spin_lock(&sdp->sd_ail_lock);
	head = bh = page_buffers(page);
	do {
		if (atomic_read(&bh->b_count))
@@ -748,7 +753,6 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask)
			goto cannot_release;
		bh = bh->b_this_page;
	} while(bh != head);
	spin_unlock(&sdp->sd_ail_lock);

	head = bh = page_buffers(page);
	do {
@@ -774,7 +778,6 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask)
	return try_to_free_buffers(page);

cannot_release:
	spin_unlock(&sdp->sd_ail_lock);
	gfs2_log_unlock(sdp);
	return 0;
}
@@ -784,12 +787,13 @@ static const struct address_space_operations gfs2_aops = {
	.writepages = gfs2_writepages,
	.readpage = gfs2_readpage,
	.readahead = gfs2_readahead,
	.set_page_dirty = iomap_set_page_dirty,
	.releasepage = iomap_releasepage,
	.invalidatepage = iomap_invalidatepage,
	.bmap = gfs2_bmap,
	.invalidatepage = gfs2_invalidatepage,
	.releasepage = gfs2_releasepage,
	.direct_IO = noop_direct_IO,
	.migratepage = buffer_migrate_page,
	.is_partially_uptodate = block_is_partially_uptodate,
	.migratepage = iomap_migrate_page,
	.is_partially_uptodate = iomap_is_partially_uptodate,
	.error_remove_page = generic_error_remove_page,
};

+43 −19
Original line number Diff line number Diff line
@@ -56,7 +56,6 @@ static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh,
			       u64 block, struct page *page)
{
	struct inode *inode = &ip->i_inode;
	struct buffer_head *bh;
	int release = 0;

	if (!page || page->index) {
@@ -80,20 +79,21 @@ static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh,
		SetPageUptodate(page);
	}

	if (gfs2_is_jdata(ip)) {
		struct buffer_head *bh;

		if (!page_has_buffers(page))
			create_empty_buffers(page, BIT(inode->i_blkbits),
					     BIT(BH_Uptodate));

		bh = page_buffers(page);

		if (!buffer_mapped(bh))
			map_bh(bh, inode->i_sb, block);

		set_buffer_uptodate(bh);
	if (gfs2_is_jdata(ip))
		gfs2_trans_add_data(ip->i_gl, bh);
	else {
		mark_buffer_dirty(bh);
	} else {
		set_page_dirty(page);
		gfs2_ordered_add_inode(ip);
	}

@@ -1158,6 +1158,7 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length,
	struct metapath mp = { .mp_aheight = 1, };
	int ret;

	if (gfs2_is_jdata(ip))
		iomap->flags |= IOMAP_F_BUFFER_HEAD;

	trace_gfs2_iomap_start(ip, pos, length, flags);
@@ -1291,6 +1292,7 @@ int gfs2_block_map(struct inode *inode, sector_t lblock,
	loff_t length = bh_map->b_size;
	struct metapath mp = { .mp_aheight = 1, };
	struct iomap iomap = { };
	int flags = create ? IOMAP_WRITE : 0;
	int ret;

	clear_buffer_mapped(bh_map);
@@ -1298,15 +1300,14 @@ int gfs2_block_map(struct inode *inode, sector_t lblock,
	clear_buffer_boundary(bh_map);
	trace_gfs2_bmap(ip, bh_map, lblock, create, 1);

	if (create) {
		ret = gfs2_iomap_get(inode, pos, length, IOMAP_WRITE, &iomap, &mp);
		if (!ret && iomap.type == IOMAP_HOLE)
	ret = gfs2_iomap_get(inode, pos, length, flags, &iomap, &mp);
	if (!ret && iomap.type == IOMAP_HOLE) {
		if (create)
			ret = gfs2_iomap_alloc(inode, &iomap, &mp);
		release_metapath(&mp);
	} else {
		ret = gfs2_iomap_get(inode, pos, length, 0, &iomap, &mp);
		release_metapath(&mp);
		else
			ret = -ENODATA;
	}
	release_metapath(&mp);
	if (ret)
		goto out;

@@ -2518,3 +2519,26 @@ out:
		gfs2_trans_end(sdp);
	return error;
}

static int gfs2_map_blocks(struct iomap_writepage_ctx *wpc, struct inode *inode,
		loff_t offset)
{
	struct metapath mp = { .mp_aheight = 1, };
	int ret;

	if (WARN_ON_ONCE(gfs2_is_stuffed(GFS2_I(inode))))
		return -EIO;

	if (offset >= wpc->iomap.offset &&
	    offset < wpc->iomap.offset + wpc->iomap.length)
		return 0;

	memset(&wpc->iomap, 0, sizeof(wpc->iomap));
	ret = gfs2_iomap_get(inode, offset, INT_MAX, 0, &wpc->iomap, &mp);
	release_metapath(&mp);
	return ret;
}

const struct iomap_writeback_ops gfs2_writeback_ops = {
	.map_blocks		= gfs2_map_blocks,
};
+1 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ static inline void gfs2_write_calc_reserv(const struct gfs2_inode *ip,
}

extern const struct iomap_ops gfs2_iomap_ops;
extern const struct iomap_writeback_ops gfs2_writeback_ops;

extern int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page);
extern int gfs2_block_map(struct inode *inode, sector_t lblock,
+19 −33
Original line number Diff line number Diff line
@@ -270,7 +270,12 @@ static void __gfs2_glock_put(struct gfs2_glock *gl)
	gfs2_glock_remove_from_lru(gl);
	spin_unlock(&gl->gl_lockref.lock);
	GLOCK_BUG_ON(gl, !list_empty(&gl->gl_holders));
	GLOCK_BUG_ON(gl, mapping && mapping->nrpages && !gfs2_withdrawn(sdp));
	if (mapping) {
		truncate_inode_pages_final(mapping);
		if (!gfs2_withdrawn(sdp))
			GLOCK_BUG_ON(gl, mapping->nrpages ||
				     mapping->nrexceptional);
	}
	trace_gfs2_glock_put(gl);
	sdp->sd_lockstruct.ls_ops->lm_put_lock(gl);
}
@@ -453,9 +458,6 @@ static void state_change(struct gfs2_glock *gl, unsigned int new_state)
		else
			gl->gl_lockref.count--;
	}
	if (held1 && held2 && list_empty(&gl->gl_holders))
		clear_bit(GLF_QUEUED, &gl->gl_flags);

	if (new_state != gl->gl_target)
		/* shorten our minimum hold time */
		gl->gl_hold_time = max(gl->gl_hold_time - GL_GLOCK_HOLD_DECR,
@@ -1049,6 +1051,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number,
	gl->gl_object = NULL;
	gl->gl_hold_time = GL_GLOCK_DFT_HOLD;
	INIT_DELAYED_WORK(&gl->gl_work, glock_work_func);
	if (gl->gl_name.ln_type == LM_TYPE_IOPEN)
		INIT_DELAYED_WORK(&gl->gl_delete, delete_work_func);

	mapping = gfs2_glock2aspace(gl);
@@ -1345,7 +1348,6 @@ fail:
		if (unlikely((gh->gh_flags & LM_FLAG_PRIORITY) && !insert_pt))
			insert_pt = &gh2->gh_list;
	}
	set_bit(GLF_QUEUED, &gl->gl_flags);
	trace_gfs2_glock_queue(gh, 1);
	gfs2_glstats_inc(gl, GFS2_LKS_QCOUNT);
	gfs2_sbstats_inc(gl, GFS2_LKS_QCOUNT);
@@ -1646,16 +1648,15 @@ void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state)
	unsigned long now = jiffies;

	gfs2_glock_hold(gl);
	spin_lock(&gl->gl_lockref.lock);
	holdtime = gl->gl_tchange + gl->gl_hold_time;
	if (test_bit(GLF_QUEUED, &gl->gl_flags) &&
	if (!list_empty(&gl->gl_holders) &&
	    gl->gl_name.ln_type == LM_TYPE_INODE) {
		if (time_before(now, holdtime))
			delay = holdtime - now;
		if (test_bit(GLF_REPLY_PENDING, &gl->gl_flags))
			delay = gl->gl_hold_time;
	}

	spin_lock(&gl->gl_lockref.lock);
	handle_callback(gl, state, delay, true);
	__gfs2_glock_queue_work(gl, delay);
	spin_unlock(&gl->gl_lockref.lock);
@@ -1842,10 +1843,9 @@ static struct shrinker glock_shrinker = {
};

/**
 * examine_bucket - Call a function for glock in a hash bucket
 * glock_hash_walk - Call a function for glock in a hash bucket
 * @examiner: the function
 * @sdp: the filesystem
 * @bucket: the bucket
 *
 * Note that the function can be called multiple times on the same
 * object.  So the user must ensure that the function can cope with
@@ -1901,10 +1901,12 @@ bool gfs2_delete_work_queued(const struct gfs2_glock *gl)

static void flush_delete_work(struct gfs2_glock *gl)
{
	if (gl->gl_name.ln_type == LM_TYPE_IOPEN) {
		if (cancel_delayed_work(&gl->gl_delete)) {
			queue_delayed_work(gfs2_delete_workqueue,
					   &gl->gl_delete, 0);
		}
	}
	gfs2_glock_queue_work(gl, 0);
}

@@ -2100,7 +2102,7 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl)
		*p++ = 'I';
	if (test_bit(GLF_FROZEN, gflags))
		*p++ = 'F';
	if (test_bit(GLF_QUEUED, gflags))
	if (!list_empty(&gl->gl_holders))
		*p++ = 'q';
	if (test_bit(GLF_LRU, gflags))
		*p++ = 'L';
@@ -2415,7 +2417,7 @@ static const struct seq_operations gfs2_glstats_seq_ops = {
	.show  = gfs2_glstats_seq_show,
};

static const struct seq_operations gfs2_sbstats_seq_ops = {
static const struct seq_operations gfs2_sbstats_sops = {
	.start = gfs2_sbstats_seq_start,
	.next  = gfs2_sbstats_seq_next,
	.stop  = gfs2_sbstats_seq_stop,
@@ -2468,16 +2470,6 @@ static int gfs2_glstats_open(struct inode *inode, struct file *file)
	return __gfs2_glocks_open(inode, file, &gfs2_glstats_seq_ops);
}

static int gfs2_sbstats_open(struct inode *inode, struct file *file)
{
	int ret = seq_open(file, &gfs2_sbstats_seq_ops);
	if (ret == 0) {
		struct seq_file *seq = file->private_data;
		seq->private = inode->i_private;  /* sdp */
	}
	return ret;
}

static const struct file_operations gfs2_glocks_fops = {
	.owner   = THIS_MODULE,
	.open    = gfs2_glocks_open,
@@ -2494,13 +2486,7 @@ static const struct file_operations gfs2_glstats_fops = {
	.release = gfs2_glocks_release,
};

static const struct file_operations gfs2_sbstats_fops = {
	.owner   = THIS_MODULE,
	.open	 = gfs2_sbstats_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = seq_release,
};
DEFINE_SEQ_ATTRIBUTE(gfs2_sbstats);

void gfs2_create_debugfs_file(struct gfs2_sbd *sdp)
{
+21 −15
Original line number Diff line number Diff line
@@ -178,6 +178,9 @@ static int rgrp_go_sync(struct gfs2_glock *gl)
	struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
	struct address_space *mapping = &sdp->sd_aspace;
	struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl);
	const unsigned bsize = sdp->sd_sb.sb_bsize;
	loff_t start = (rgd->rd_addr * bsize) & PAGE_MASK;
	loff_t end = PAGE_ALIGN((rgd->rd_addr + rgd->rd_length) * bsize) - 1;
	int error;

	if (!test_and_clear_bit(GLF_DIRTY, &gl->gl_flags))
@@ -186,18 +189,13 @@ static int rgrp_go_sync(struct gfs2_glock *gl)

	gfs2_log_flush(sdp, gl, GFS2_LOG_HEAD_FLUSH_NORMAL |
		       GFS2_LFC_RGRP_GO_SYNC);
	filemap_fdatawrite_range(mapping, gl->gl_vm.start, gl->gl_vm.end);
	error = filemap_fdatawait_range(mapping, gl->gl_vm.start, gl->gl_vm.end);
	WARN_ON_ONCE(error);
	filemap_fdatawrite_range(mapping, start, end);
	error = filemap_fdatawait_range(mapping, start, end);
	WARN_ON_ONCE(error && !gfs2_withdrawn(sdp));
	mapping_set_error(mapping, error);
	if (!error)
		error = gfs2_ail_empty_gl(gl);

	spin_lock(&gl->gl_lockref.lock);
	rgd = gl->gl_object;
	if (rgd)
	gfs2_free_clones(rgd);
	spin_unlock(&gl->gl_lockref.lock);
	return error;
}

@@ -216,15 +214,23 @@ static void rgrp_go_inval(struct gfs2_glock *gl, int flags)
	struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
	struct address_space *mapping = &sdp->sd_aspace;
	struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl);
	const unsigned bsize = sdp->sd_sb.sb_bsize;
	loff_t start = (rgd->rd_addr * bsize) & PAGE_MASK;
	loff_t end = PAGE_ALIGN((rgd->rd_addr + rgd->rd_length) * bsize) - 1;

	if (rgd)
	gfs2_rgrp_brelse(rgd);

	WARN_ON_ONCE(!(flags & DIO_METADATA));
	truncate_inode_pages_range(mapping, gl->gl_vm.start, gl->gl_vm.end);
	truncate_inode_pages_range(mapping, start, end);
	rgd->rd_flags &= ~GFS2_RDF_UPTODATE;
}

static void gfs2_rgrp_go_dump(struct seq_file *seq, struct gfs2_glock *gl,
			      const char *fs_id_buf)
{
	struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl);

	if (rgd)
		rgd->rd_flags &= ~GFS2_RDF_UPTODATE;
		gfs2_rgrp_dump(seq, rgd, fs_id_buf);
}

static struct gfs2_inode *gfs2_glock2inode(struct gfs2_glock *gl)
@@ -712,7 +718,7 @@ const struct gfs2_glock_operations gfs2_rgrp_glops = {
	.go_sync = rgrp_go_sync,
	.go_inval = rgrp_go_inval,
	.go_lock = gfs2_rgrp_go_lock,
	.go_dump = gfs2_rgrp_dump,
	.go_dump = gfs2_rgrp_go_dump,
	.go_type = LM_TYPE_RGRP,
	.go_flags = GLOF_LVB,
};
Loading