Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6760bdcd authored by Bob Peterson's avatar Bob Peterson Committed by Steven Whitehouse
Browse files

[GFS2] Prevent infinite loop in try_rgrp_unlink()



This is patch three of five for bug #248176.

The try_rgrp_unlink code in rgrp.c had an infinite loop.  This was
caused because the bitmap function rgblk_search can return a block
less than the "goal" block, in which case it was looping.  The fix is
to make it always march forward as needed.

Signed-off-by: default avatarBob Peterson <rpeterso@redhat.com>
Signed-off-by: default avatarSteven Whitehouse <swhiteho@redhat.com>
parent 693ddeab
Loading
Loading
Loading
Loading
+17 −13
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#include "inode.h"

#define BFITNOENT ((u32)~0)
#define NO_BLOCK ((u64)~0)

/*
 * These routines are used by the resource group routines (rgrp.c)
@@ -116,8 +117,7 @@ static unsigned char gfs2_testbit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
 * @buffer: the buffer that holds the bitmaps
 * @buflen: the length (in bytes) of the buffer
 * @goal: start search at this block's bit-pair (within @buffer)
 * @old_state: GFS2_BLKST_XXX the state of the block we're looking for;
 *       bit 0 = alloc(1)/free(0), bit 1 = meta(1)/data(0)
 * @old_state: GFS2_BLKST_XXX the state of the block we're looking for.
 *
 * Scope of @goal and returned block number is only within this bitmap buffer,
 * not entire rgrp or filesystem.  @buffer will be offset from the actual
@@ -137,9 +137,13 @@ static u32 gfs2_bitfit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
	byte = buffer + (goal / GFS2_NBBY);
	bit = (goal % GFS2_NBBY) * GFS2_BIT_SIZE;
	end = buffer + buflen;
	alloc = (old_state & 1) ? 0 : 0x55;
	alloc = (old_state == GFS2_BLKST_FREE) ? 0x55 : 0;

	while (byte < end) {
		/* If we're looking for a free block we can eliminate all
		   bitmap settings with 0x55, which represents four data
		   blocks in a row.  If we're looking for a data block, we can
		   eliminate 0x00 which corresponds to four free blocks. */
		if ((*byte & 0x55) == alloc) {
			blk += (8 - bit) >> 1;

@@ -859,19 +863,21 @@ static int try_rgrp_fit(struct gfs2_rgrpd *rgd, struct gfs2_alloc *al)
static struct inode *try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked)
{
	struct inode *inode;
	u32 goal = 0;
	u32 goal = 0, block;
	u64 no_addr;

	for(;;) {
		if (goal >= rgd->rd_data)
			break;
		goal = rgblk_search(rgd, goal, GFS2_BLKST_UNLINKED,
		block = rgblk_search(rgd, goal, GFS2_BLKST_UNLINKED,
				     GFS2_BLKST_UNLINKED);
		if (goal == BFITNOENT)
		if (block == BFITNOENT)
			break;
		no_addr = goal + rgd->rd_data0;
		/* rgblk_search can return a block < goal, so we need to
		   keep it marching forward. */
		no_addr = block + rgd->rd_data0;
		goal++;
		if (no_addr < *last_unlinked)
		if (*last_unlinked != NO_BLOCK && no_addr <= *last_unlinked)
			continue;
		*last_unlinked = no_addr;
		inode = gfs2_inode_lookup(rgd->rd_sbd->sd_vfs, DT_UNKNOWN,
@@ -1152,7 +1158,7 @@ int gfs2_inplace_reserve_i(struct gfs2_inode *ip, char *file, unsigned int line)
	struct gfs2_alloc *al = &ip->i_alloc;
	struct inode *inode;
	int error = 0;
	u64 last_unlinked = 0;
	u64 last_unlinked = NO_BLOCK;

	if (gfs2_assert_warn(sdp, al->al_requested))
		return -EINVAL;
@@ -1305,9 +1311,7 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
		goal = 0;
	}

	if (old_state != new_state) {
		gfs2_assert_withdraw(rgd->rd_sbd, blk != BFITNOENT);

	if (blk != BFITNOENT && old_state != new_state) {
		gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
		gfs2_setbit(rgd, bi->bi_bh->b_data + bi->bi_offset,
			    bi->bi_len, blk, new_state);