Commit cdbe0206 authored by Linus Torvalds's avatar Linus Torvalds

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
...@@ -1280,7 +1280,6 @@ xfs_bmap_read_extents( ...@@ -1280,7 +1280,6 @@ xfs_bmap_read_extents(
xfs_bmbt_rec_t *frp; xfs_bmbt_rec_t *frp;
xfs_fsblock_t nextbno; xfs_fsblock_t nextbno;
xfs_extnum_t num_recs; xfs_extnum_t num_recs;
xfs_extnum_t start;
num_recs = xfs_btree_get_numrecs(block); num_recs = xfs_btree_get_numrecs(block);
if (unlikely(i + num_recs > room)) { if (unlikely(i + num_recs > room)) {
...@@ -1303,7 +1302,6 @@ xfs_bmap_read_extents( ...@@ -1303,7 +1302,6 @@ xfs_bmap_read_extents(
* Copy records into the extent records. * Copy records into the extent records.
*/ */
frp = XFS_BMBT_REC_ADDR(mp, block, 1); frp = XFS_BMBT_REC_ADDR(mp, block, 1);
start = i;
for (j = 0; j < num_recs; j++, i++, frp++) { for (j = 0; j < num_recs; j++, i++, frp++) {
xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i); xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
trp->l0 = be64_to_cpu(frp->l0); trp->l0 = be64_to_cpu(frp->l0);
...@@ -2065,8 +2063,10 @@ xfs_bmap_add_extent_delay_real( ...@@ -2065,8 +2063,10 @@ xfs_bmap_add_extent_delay_real(
} }
temp = xfs_bmap_worst_indlen(bma->ip, temp); temp = xfs_bmap_worst_indlen(bma->ip, temp);
temp2 = xfs_bmap_worst_indlen(bma->ip, temp2); temp2 = xfs_bmap_worst_indlen(bma->ip, temp2);
diff = (int)(temp + temp2 - startblockval(PREV.br_startblock) - diff = (int)(temp + temp2 -
(bma->cur ? bma->cur->bc_private.b.allocated : 0)); (startblockval(PREV.br_startblock) -
(bma->cur ?
bma->cur->bc_private.b.allocated : 0)));
if (diff > 0) { if (diff > 0) {
error = xfs_mod_fdblocks(bma->ip->i_mount, error = xfs_mod_fdblocks(bma->ip->i_mount,
-((int64_t)diff), false); -((int64_t)diff), false);
...@@ -2123,7 +2123,6 @@ xfs_bmap_add_extent_delay_real( ...@@ -2123,7 +2123,6 @@ xfs_bmap_add_extent_delay_real(
temp = da_new; temp = da_new;
if (bma->cur) if (bma->cur)
temp += bma->cur->bc_private.b.allocated; temp += bma->cur->bc_private.b.allocated;
ASSERT(temp <= da_old);
if (temp < da_old) if (temp < da_old)
xfs_mod_fdblocks(bma->ip->i_mount, xfs_mod_fdblocks(bma->ip->i_mount,
(int64_t)(da_old - temp), false); (int64_t)(da_old - temp), false);
......
...@@ -4395,7 +4395,7 @@ xfs_btree_visit_blocks( ...@@ -4395,7 +4395,7 @@ xfs_btree_visit_blocks(
xfs_btree_readahead_ptr(cur, ptr, 1); xfs_btree_readahead_ptr(cur, ptr, 1);
/* save for the next iteration of the loop */ /* save for the next iteration of the loop */
lptr = *ptr; xfs_btree_copy_ptrs(cur, &lptr, ptr, 1);
} }
/* for each buffer in the level */ /* for each buffer in the level */
......
...@@ -1629,13 +1629,28 @@ xfs_refcount_recover_cow_leftovers( ...@@ -1629,13 +1629,28 @@ xfs_refcount_recover_cow_leftovers(
if (mp->m_sb.sb_agblocks >= XFS_REFC_COW_START) if (mp->m_sb.sb_agblocks >= XFS_REFC_COW_START)
return -EOPNOTSUPP; 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) if (error)
return 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. */ /* Find all the leftover CoW staging extents. */
INIT_LIST_HEAD(&debris);
memset(&low, 0, sizeof(low)); memset(&low, 0, sizeof(low));
memset(&high, 0, sizeof(high)); memset(&high, 0, sizeof(high));
low.rc.rc_startblock = XFS_REFC_COW_START; low.rc.rc_startblock = XFS_REFC_COW_START;
...@@ -1645,10 +1660,11 @@ xfs_refcount_recover_cow_leftovers( ...@@ -1645,10 +1660,11 @@ xfs_refcount_recover_cow_leftovers(
if (error) if (error)
goto out_cursor; goto out_cursor;
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); 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 */ /* 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. */ /* Set up transaction. */
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0, 0, &tp); error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0, 0, &tp);
if (error) if (error)
...@@ -1676,8 +1692,16 @@ xfs_refcount_recover_cow_leftovers( ...@@ -1676,8 +1692,16 @@ xfs_refcount_recover_cow_leftovers(
error = xfs_trans_commit(tp); error = xfs_trans_commit(tp);
if (error) if (error)
goto out_free; 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: out_free:
/* Free the leftover list */ /* Free the leftover list */
list_for_each_entry_safe(rr, n, &debris, rr_list) { list_for_each_entry_safe(rr, n, &debris, rr_list) {
...@@ -1688,11 +1712,6 @@ xfs_refcount_recover_cow_leftovers( ...@@ -1688,11 +1712,6 @@ xfs_refcount_recover_cow_leftovers(
out_cursor: out_cursor:
xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
xfs_buf_relse(agbp); xfs_trans_brelse(tp, agbp);
goto out_free; goto out_trans;
out_defer:
xfs_defer_cancel(&dfops);
xfs_trans_cancel(tp);
goto out_free;
} }
...@@ -582,9 +582,13 @@ xfs_getbmap( ...@@ -582,9 +582,13 @@ xfs_getbmap(
} }
break; break;
default: 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 && 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_BTREE)
ip->i_d.di_format != XFS_DINODE_FMT_LOCAL)
return -EINVAL; return -EINVAL;
if (xfs_get_extsz_hint(ip) || if (xfs_get_extsz_hint(ip) ||
...@@ -712,7 +716,7 @@ xfs_getbmap( ...@@ -712,7 +716,7 @@ xfs_getbmap(
* extents. * extents.
*/ */
if (map[i].br_startblock == DELAYSTARTBLOCK && 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); ASSERT((iflags & BMV_IF_DELALLOC) != 0);
if (map[i].br_startblock == HOLESTARTBLOCK && if (map[i].br_startblock == HOLESTARTBLOCK &&
......
...@@ -1043,49 +1043,17 @@ xfs_find_get_desired_pgoff( ...@@ -1043,49 +1043,17 @@ xfs_find_get_desired_pgoff(
index = startoff >> PAGE_SHIFT; index = startoff >> PAGE_SHIFT;
endoff = XFS_FSB_TO_B(mp, map->br_startoff + map->br_blockcount); endoff = XFS_FSB_TO_B(mp, map->br_startoff + map->br_blockcount);
end = endoff >> PAGE_SHIFT; end = (endoff - 1) >> PAGE_SHIFT;
do { do {
int want; int want;
unsigned nr_pages; unsigned nr_pages;
unsigned int i; 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, nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index,
want); want);
/* if (nr_pages == 0)
* 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)
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; break;
}
for (i = 0; i < nr_pages; i++) { for (i = 0; i < nr_pages; i++) {
struct page *page = pvec.pages[i]; struct page *page = pvec.pages[i];
...@@ -1098,18 +1066,18 @@ xfs_find_get_desired_pgoff( ...@@ -1098,18 +1066,18 @@ xfs_find_get_desired_pgoff(
* file mapping. However, page->index will not change * file mapping. However, page->index will not change
* because we have a reference on the page. * because we have a reference on the page.
* *
* Searching done if the page index is out of range. * If current page offset is beyond where we've ended,
* If the current offset is not reaches the end of * we've found a hole.
* the specified search range, there should be a hole
* between them.
*/ */
if (page->index > end) { if (type == HOLE_OFF && lastoff < endoff &&
if (type == HOLE_OFF && lastoff < endoff) { lastoff < page_offset(pvec.pages[i])) {
*offset = lastoff; found = true;
found = true; *offset = lastoff;
}
goto out; goto out;
} }
/* Searching done if the page index is out of range. */
if (page->index > end)
goto out;
lock_page(page); lock_page(page);
/* /*
...@@ -1151,21 +1119,20 @@ xfs_find_get_desired_pgoff( ...@@ -1151,21 +1119,20 @@ xfs_find_get_desired_pgoff(
/* /*
* The number of returned pages less than our desired, search * The number of returned pages less than our desired, search
* done. In this case, nothing was found for searching data, * done.
* but we found a hole behind the last offset.
*/ */
if (nr_pages < want) { if (nr_pages < want)
if (type == HOLE_OFF) {
*offset = lastoff;
found = true;
}
break; break;
}
index = pvec.pages[i - 1]->index + 1; index = pvec.pages[i - 1]->index + 1;
pagevec_release(&pvec); pagevec_release(&pvec);
} while (index <= end); } 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: out:
pagevec_release(&pvec); pagevec_release(&pvec);
return found; return found;
......
...@@ -828,6 +828,7 @@ xfs_getfsmap( ...@@ -828,6 +828,7 @@ xfs_getfsmap(
struct xfs_fsmap dkeys[2]; /* per-dev keys */ struct xfs_fsmap dkeys[2]; /* per-dev keys */
struct xfs_getfsmap_dev handlers[XFS_GETFSMAP_DEVS]; struct xfs_getfsmap_dev handlers[XFS_GETFSMAP_DEVS];
struct xfs_getfsmap_info info = { NULL }; struct xfs_getfsmap_info info = { NULL };
bool use_rmap;
int i; int i;
int error = 0; int error = 0;
...@@ -837,12 +838,14 @@ xfs_getfsmap( ...@@ -837,12 +838,14 @@ xfs_getfsmap(
!xfs_getfsmap_is_valid_device(mp, &head->fmh_keys[1])) !xfs_getfsmap_is_valid_device(mp, &head->fmh_keys[1]))
return -EINVAL; return -EINVAL;
use_rmap = capable(CAP_SYS_ADMIN) &&
xfs_sb_version_hasrmapbt(&mp->m_sb);
head->fmh_entries = 0; head->fmh_entries = 0;
/* Set up our device handlers. */ /* Set up our device handlers. */
memset(handlers, 0, sizeof(handlers)); memset(handlers, 0, sizeof(handlers));
handlers[0].dev = new_encode_dev(mp->m_ddev_targp->bt_dev); handlers[0].dev = new_encode_dev(mp->m_ddev_targp->bt_dev);
if (xfs_sb_version_hasrmapbt(&mp->m_sb)) if (use_rmap)
handlers[0].fn = xfs_getfsmap_datadev_rmapbt; handlers[0].fn = xfs_getfsmap_datadev_rmapbt;
else else
handlers[0].fn = xfs_getfsmap_datadev_bnobt; handlers[0].fn = xfs_getfsmap_datadev_bnobt;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment