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

Commit cdbe0206 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'xfs-4.12-fixes-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull XFS fixes from Darrick Wong:
 "A few miscellaneous bug fixes & cleanups:

   - Fix indlen block reservation accounting bug when splitting delalloc
     extent

   - Fix warnings about unused variables that appeared in -rc1.

   - Don't spew errors when bmapping a local format directory

   - Fix an off-by-one error in a delalloc eof assertion

   - Make fsmap only return inode information for CAP_SYS_ADMIN

   - Fix a potential mount time deadlock recovering cow extents

   - Fix unaligned memory access in _btree_visit_blocks

   - Fix various SEEK_HOLE/SEEK_DATA bugs"

* tag 'xfs-4.12-fixes-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: Move handling of missing page into one place in xfs_find_get_desired_pgoff()
  xfs: Fix off-by-in in loop termination in xfs_find_get_desired_pgoff()
  xfs: Fix missed holes in SEEK_HOLE implementation
  xfs: fix off-by-one on max nr_pages in xfs_find_get_desired_pgoff()
  xfs: fix unaligned access in xfs_btree_visit_blocks
  xfs: avoid mount-time deadlock in CoW extent recovery
  xfs: only return detailed fsmap info if the caller has CAP_SYS_ADMIN
  xfs: bad assertion for delalloc an extent that start at i_size
  xfs: fix warnings about unused stack variables
  xfs: BMAPX shouldn't barf on inline-format directories
  xfs: fix indlen accounting error on partial delalloc conversion
parents 1b8f2ffc a54fba8f
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -1280,7 +1280,6 @@ xfs_bmap_read_extents(
		xfs_bmbt_rec_t	*frp;
		xfs_fsblock_t	nextbno;
		xfs_extnum_t	num_recs;
		xfs_extnum_t	start;

		num_recs = xfs_btree_get_numrecs(block);
		if (unlikely(i + num_recs > room)) {
@@ -1303,7 +1302,6 @@ xfs_bmap_read_extents(
		 * Copy records into the extent records.
		 */
		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
		start = i;
		for (j = 0; j < num_recs; j++, i++, frp++) {
			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
			trp->l0 = be64_to_cpu(frp->l0);
@@ -2065,8 +2063,10 @@ xfs_bmap_add_extent_delay_real(
		}
		temp = xfs_bmap_worst_indlen(bma->ip, temp);
		temp2 = xfs_bmap_worst_indlen(bma->ip, temp2);
		diff = (int)(temp + temp2 - startblockval(PREV.br_startblock) -
			(bma->cur ? bma->cur->bc_private.b.allocated : 0));
		diff = (int)(temp + temp2 -
			     (startblockval(PREV.br_startblock) -
			      (bma->cur ?
			       bma->cur->bc_private.b.allocated : 0)));
		if (diff > 0) {
			error = xfs_mod_fdblocks(bma->ip->i_mount,
						 -((int64_t)diff), false);
@@ -2123,7 +2123,6 @@ xfs_bmap_add_extent_delay_real(
		temp = da_new;
		if (bma->cur)
			temp += bma->cur->bc_private.b.allocated;
		ASSERT(temp <= da_old);
		if (temp < da_old)
			xfs_mod_fdblocks(bma->ip->i_mount,
					(int64_t)(da_old - temp), false);
+1 −1
Original line number Diff line number Diff line
@@ -4395,7 +4395,7 @@ xfs_btree_visit_blocks(
			xfs_btree_readahead_ptr(cur, ptr, 1);

			/* save for the next iteration of the loop */
			lptr = *ptr;
			xfs_btree_copy_ptrs(cur, &lptr, ptr, 1);
		}

		/* for each buffer in the level */
+31 −12
Original line number Diff line number Diff line
@@ -1629,13 +1629,28 @@ xfs_refcount_recover_cow_leftovers(
	if (mp->m_sb.sb_agblocks >= XFS_REFC_COW_START)
		return -EOPNOTSUPP;

	error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
	INIT_LIST_HEAD(&debris);

	/*
	 * In this first part, we use an empty transaction to gather up
	 * all the leftover CoW extents so that we can subsequently
	 * delete them.  The empty transaction is used to avoid
	 * a buffer lock deadlock if there happens to be a loop in the
	 * refcountbt because we're allowed to re-grab a buffer that is
	 * already attached to our transaction.  When we're done
	 * recording the CoW debris we cancel the (empty) transaction
	 * and everything goes away cleanly.
	 */
	error = xfs_trans_alloc_empty(mp, &tp);
	if (error)
		return error;
	cur = xfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL);

	error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp);
	if (error)
		goto out_trans;
	cur = xfs_refcountbt_init_cursor(mp, tp, agbp, agno, NULL);

	/* Find all the leftover CoW staging extents. */
	INIT_LIST_HEAD(&debris);
	memset(&low, 0, sizeof(low));
	memset(&high, 0, sizeof(high));
	low.rc.rc_startblock = XFS_REFC_COW_START;
@@ -1645,10 +1660,11 @@ xfs_refcount_recover_cow_leftovers(
	if (error)
		goto out_cursor;
	xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
	xfs_buf_relse(agbp);
	xfs_trans_brelse(tp, agbp);
	xfs_trans_cancel(tp);

	/* Now iterate the list to free the leftovers */
	list_for_each_entry(rr, &debris, rr_list) {
	list_for_each_entry_safe(rr, n, &debris, rr_list) {
		/* Set up transaction. */
		error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0, 0, &tp);
		if (error)
@@ -1676,8 +1692,16 @@ xfs_refcount_recover_cow_leftovers(
		error = xfs_trans_commit(tp);
		if (error)
			goto out_free;

		list_del(&rr->rr_list);
		kmem_free(rr);
	}

	return error;
out_defer:
	xfs_defer_cancel(&dfops);
out_trans:
	xfs_trans_cancel(tp);
out_free:
	/* Free the leftover list */
	list_for_each_entry_safe(rr, n, &debris, rr_list) {
@@ -1688,11 +1712,6 @@ xfs_refcount_recover_cow_leftovers(

out_cursor:
	xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
	xfs_buf_relse(agbp);
	goto out_free;

out_defer:
	xfs_defer_cancel(&dfops);
	xfs_trans_cancel(tp);
	goto out_free;
	xfs_trans_brelse(tp, agbp);
	goto out_trans;
}
+7 −3
Original line number Diff line number Diff line
@@ -582,9 +582,13 @@ xfs_getbmap(
		}
		break;
	default:
		/* Local format data forks report no extents. */
		if (ip->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
			bmv->bmv_entries = 0;
			return 0;
		}
		if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
		    ip->i_d.di_format != XFS_DINODE_FMT_BTREE &&
		    ip->i_d.di_format != XFS_DINODE_FMT_LOCAL)
		    ip->i_d.di_format != XFS_DINODE_FMT_BTREE)
			return -EINVAL;

		if (xfs_get_extsz_hint(ip) ||
@@ -712,7 +716,7 @@ xfs_getbmap(
			 * extents.
			 */
			if (map[i].br_startblock == DELAYSTARTBLOCK &&
			    map[i].br_startoff <= XFS_B_TO_FSB(mp, XFS_ISIZE(ip)))
			    map[i].br_startoff < XFS_B_TO_FSB(mp, XFS_ISIZE(ip)))
				ASSERT((iflags & BMV_IF_DELALLOC) != 0);

                        if (map[i].br_startblock == HOLESTARTBLOCK &&
+19 −52
Original line number Diff line number Diff line
@@ -1043,50 +1043,18 @@ xfs_find_get_desired_pgoff(

	index = startoff >> PAGE_SHIFT;
	endoff = XFS_FSB_TO_B(mp, map->br_startoff + map->br_blockcount);
	end = endoff >> PAGE_SHIFT;
	end = (endoff - 1) >> PAGE_SHIFT;
	do {
		int		want;
		unsigned	nr_pages;
		unsigned int	i;

		want = min_t(pgoff_t, end - index, PAGEVEC_SIZE);
		want = min_t(pgoff_t, end - index, PAGEVEC_SIZE - 1) + 1;
		nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index,
					  want);
		/*
		 * No page mapped into given range.  If we are searching holes
		 * and if this is the first time we got into the loop, it means
		 * that the given offset is landed in a hole, return it.
		 *
		 * If we have already stepped through some block buffers to find
		 * holes but they all contains data.  In this case, the last
		 * offset is already updated and pointed to the end of the last
		 * mapped page, if it does not reach the endpoint to search,
		 * that means there should be a hole between them.
		 */
		if (nr_pages == 0) {
			/* Data search found nothing */
			if (type == DATA_OFF)
		if (nr_pages == 0)
			break;

			ASSERT(type == HOLE_OFF);
			if (lastoff == startoff || lastoff < endoff) {
				found = true;
				*offset = lastoff;
			}
			break;
		}

		/*
		 * At lease we found one page.  If this is the first time we
		 * step into the loop, and if the first page index offset is
		 * greater than the given search offset, a hole was found.
		 */
		if (type == HOLE_OFF && lastoff == startoff &&
		    lastoff < page_offset(pvec.pages[0])) {
			found = true;
			break;
		}

		for (i = 0; i < nr_pages; i++) {
			struct page	*page = pvec.pages[i];
			loff_t		b_offset;
@@ -1098,18 +1066,18 @@ xfs_find_get_desired_pgoff(
			 * file mapping. However, page->index will not change
			 * because we have a reference on the page.
			 *
			 * Searching done if the page index is out of range.
			 * If the current offset is not reaches the end of
			 * the specified search range, there should be a hole
			 * between them.
			 * If current page offset is beyond where we've ended,
			 * we've found a hole.
			 */
			if (page->index > end) {
				if (type == HOLE_OFF && lastoff < endoff) {
					*offset = lastoff;
			if (type == HOLE_OFF && lastoff < endoff &&
			    lastoff < page_offset(pvec.pages[i])) {
				found = true;
				}
				*offset = lastoff;
				goto out;
			}
			/* Searching done if the page index is out of range. */
			if (page->index > end)
				goto out;

			lock_page(page);
			/*
@@ -1151,21 +1119,20 @@ xfs_find_get_desired_pgoff(

		/*
		 * The number of returned pages less than our desired, search
		 * done.  In this case, nothing was found for searching data,
		 * but we found a hole behind the last offset.
		 * done.
		 */
		if (nr_pages < want) {
			if (type == HOLE_OFF) {
				*offset = lastoff;
				found = true;
			}
		if (nr_pages < want)
			break;
		}

		index = pvec.pages[i - 1]->index + 1;
		pagevec_release(&pvec);
	} while (index <= end);

	/* No page at lastoff and we are not done - we found a hole. */
	if (type == HOLE_OFF && lastoff < endoff) {
		*offset = lastoff;
		found = true;
	}
out:
	pagevec_release(&pvec);
	return found;
Loading