+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Oracle. All Rights Reserved.
- *
* Author: Darrick J. Wong <darrick.wong@oracle.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <libxfs.h>
#include "btree.h"
#include "dinode.h"
#include "slab.h"
#include "rmap.h"
+#include "bitmap.h"
#undef RMAP_DEBUG
static bool rmapbt_suspect;
static bool refcbt_suspect;
-/*
- * Compare rmap observations for array sorting.
- */
-static int
-rmap_compare(
- const void *a,
- const void *b)
+static inline int rmap_compare(const void *a, const void *b)
{
- const struct xfs_rmap_irec *pa;
- const struct xfs_rmap_irec *pb;
- __u64 oa;
- __u64 ob;
-
- pa = a; pb = b;
- oa = libxfs_rmap_irec_offset_pack(pa);
- ob = libxfs_rmap_irec_offset_pack(pb);
-
- if (pa->rm_startblock < pb->rm_startblock)
- return -1;
- else if (pa->rm_startblock > pb->rm_startblock)
- return 1;
- else if (pa->rm_owner < pb->rm_owner)
- return -1;
- else if (pa->rm_owner > pb->rm_owner)
- return 1;
- else if (oa < ob)
- return -1;
- else if (oa > ob)
- return 1;
- else
- return 0;
+ return libxfs_rmap_compare(a, b);
}
/*
static int
find_first_zero_bit(
- __uint64_t mask)
+ uint64_t mask)
{
int n;
int b = 0;
static int
popcnt(
- __uint64_t mask)
+ uint64_t mask)
{
int n;
int b = 0;
struct xfs_buf *agbp = NULL;
struct xfs_buf *agflbp = NULL;
struct xfs_trans *tp;
- struct xfs_trans_res tres = {0};
__be32 *agfl_bno, *b;
+ struct xfs_ag_rmap *ag_rmap = &ag_rmaps[agno];
+ struct bitmap *own_ag_bitmap = NULL;
int error = 0;
- struct xfs_owner_info oinfo;
if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
return 0;
/* Release the ar_rmaps; they were put into the rmapbt during p5. */
- free_slab(&ag_rmaps[agno].ar_rmaps);
- error = init_slab(&ag_rmaps[agno].ar_rmaps,
- sizeof(struct xfs_rmap_irec));
+ free_slab(&ag_rmap->ar_rmaps);
+ error = init_slab(&ag_rmap->ar_rmaps, sizeof(struct xfs_rmap_irec));
if (error)
goto err;
* rmap, we only need to add rmap records for AGFL blocks past
* that point in the AGFL because those blocks are a result of a
* no-rmap no-shrink freelist fixup that we did earlier.
+ *
+ * However, some blocks end up on the AGFL because the free space
+ * btrees shed blocks as a result of allocating space to fix the
+ * freelist. We already created in-core rmap records for the free
+ * space btree blocks, so we must be careful not to create those
+ * records again. Create a bitmap of already-recorded OWN_AG rmaps.
*/
+ error = init_slab_cursor(ag_rmap->ar_raw_rmaps, rmap_compare, &rm_cur);
+ if (error)
+ goto err;
+ if (!bitmap_init(&own_ag_bitmap)) {
+ error = -ENOMEM;
+ goto err_slab;
+ }
+ while ((rm_rec = pop_slab_cursor(rm_cur)) != NULL) {
+ if (rm_rec->rm_owner != XFS_RMAP_OWN_AG)
+ continue;
+ if (!bitmap_set(own_ag_bitmap, rm_rec->rm_startblock,
+ rm_rec->rm_blockcount)) {
+ error = EFSCORRUPTED;
+ goto err_slab;
+ }
+ }
+ free_slab_cursor(&rm_cur);
+
+ /* Create rmaps for any AGFL blocks that aren't already rmapped. */
agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agflbp);
- b = agfl_bno + ag_rmaps[agno].ar_flcount;
- while (*b != NULLAGBLOCK && b - agfl_bno < XFS_AGFL_SIZE(mp)) {
- error = rmap_add_ag_rec(mp, agno, be32_to_cpu(*b), 1,
- XFS_RMAP_OWN_AG);
- if (error)
- goto err;
+ b = agfl_bno + ag_rmap->ar_flcount;
+ while (*b != cpu_to_be32(NULLAGBLOCK) &&
+ b - agfl_bno < libxfs_agfl_size(mp)) {
+ xfs_agblock_t agbno;
+
+ agbno = be32_to_cpu(*b);
+ if (!bitmap_test(own_ag_bitmap, agbno, 1)) {
+ error = rmap_add_ag_rec(mp, agno, agbno, 1,
+ XFS_RMAP_OWN_AG);
+ if (error)
+ goto err;
+ }
b++;
}
libxfs_putbuf(agflbp);
agflbp = NULL;
+ bitmap_free(&own_ag_bitmap);
/* Merge all the raw rmaps into the main list */
error = rmap_fold_raw_recs(mp, agno);
goto err;
/* Create cursors to refcount structures */
- error = init_slab_cursor(ag_rmaps[agno].ar_rmaps, rmap_compare,
- &rm_cur);
+ error = init_slab_cursor(ag_rmap->ar_rmaps, rmap_compare, &rm_cur);
if (error)
goto err;
/* Insert rmaps into the btree one at a time */
rm_rec = pop_slab_cursor(rm_cur);
while (rm_rec) {
- error = -libxfs_trans_alloc(mp, &tres, 16, 0, 0, &tp);
+ struct xfs_owner_info oinfo = {};
+
+ error = -libxfs_trans_alloc_rollable(mp, 16, &tp);
if (error)
goto err_slab;
goto err_trans;
ASSERT(XFS_RMAP_NON_INODE_OWNER(rm_rec->rm_owner));
- libxfs_rmap_ag_owner(&oinfo, rm_rec->rm_owner);
+ oinfo.oi_owner = rm_rec->rm_owner;
error = -libxfs_rmap_alloc(tp, agbp, agno, rm_rec->rm_startblock,
rm_rec->rm_blockcount, &oinfo);
if (error)
err:
if (agflbp)
libxfs_putbuf(agflbp);
+ if (own_ag_bitmap)
+ bitmap_free(&own_ag_bitmap);
return error;
}
mark_inode_rl(mp, stack_top);
/* Set nbno to the bno of the next refcount change */
- if (n < slab_count(rmaps))
+ if (n < slab_count(rmaps) && array_cur)
nbno = array_cur->rm_startblock;
else
nbno = NULLAGBLOCK;
return -libxfs_rmap_get_rec(bt_cur, tmp, have);
}
+/* Look for an rmap in the rmapbt that matches a given rmap. */
+static int
+rmap_lookup_overlapped(
+ struct xfs_btree_cur *bt_cur,
+ struct xfs_rmap_irec *rm_rec,
+ struct xfs_rmap_irec *tmp,
+ int *have)
+{
+ /* Have to use our fancy version for overlapped */
+ return -libxfs_rmap_lookup_le_range(bt_cur, rm_rec->rm_startblock,
+ rm_rec->rm_owner, rm_rec->rm_offset,
+ rm_rec->rm_flags, tmp, have);
+}
+
/* Does the btree rmap cover the observed rmap? */
#define NEXTP(x) ((x)->rm_startblock + (x)->rm_blockcount)
#define NEXTL(x) ((x)->rm_offset + (x)->rm_blockcount)
error = rmap_lookup(bt_cur, rm_rec, &tmp, &have);
if (error)
goto err;
+ /*
+ * Using the range query is expensive, so only do it if
+ * the regular lookup doesn't find anything or if it doesn't
+ * match the observed rmap.
+ */
+ if (xfs_sb_version_hasreflink(&bt_cur->bc_mp->m_sb) &&
+ (!have || !rmap_is_good(rm_rec, &tmp))) {
+ error = rmap_lookup_overlapped(bt_cur, rm_rec,
+ &tmp, &have);
+ if (error)
+ goto err;
+ }
if (!have) {
do_warn(
_("Missing reverse-mapping record for (%u/%u) %slen %u owner %"PRId64" \
* Compare the key fields of two rmap records -- positive if key1 > key2,
* negative if key1 < key2, and zero if equal.
*/
-__int64_t
+int64_t
rmap_diffkeys(
struct xfs_rmap_irec *kp1,
struct xfs_rmap_irec *kp2)
{
__u64 oa;
__u64 ob;
- __int64_t d;
+ int64_t d;
struct xfs_rmap_irec tmp;
tmp = *kp1;
tmp.rm_flags &= ~XFS_RMAP_REC_FLAGS;
ob = libxfs_rmap_irec_offset_pack(&tmp);
- d = (__int64_t)kp1->rm_startblock - kp2->rm_startblock;
+ d = (int64_t)kp1->rm_startblock - kp2->rm_startblock;
if (d)
return d;
{
struct ino_tree_node *irec;
int bit;
- __uint64_t was;
- __uint64_t is;
- __uint64_t diff;
- __uint64_t mask;
+ uint64_t was;
+ uint64_t is;
+ uint64_t diff;
+ uint64_t mask;
int error = 0;
xfs_agino_t agino;
pag->pagf_init = 0;
libxfs_perag_put(pag);
- bt_cur = libxfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL);
+ bt_cur = libxfs_refcountbt_init_cursor(mp, NULL, agbp, agno);
if (!bt_cur) {
error = -ENOMEM;
goto err;
err:
if (bt_cur)
- libxfs_btree_del_cursor(bt_cur, XFS_BTREE_NOERROR);
+ libxfs_btree_del_cursor(bt_cur, error ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
if (agbp)
libxfs_putbuf(agbp);
free_slab_cursor(&rl_cur);
{
xfs_alloc_arg_t args;
xfs_trans_t *tp;
- struct xfs_trans_res tres = {0};
int flags;
int error;
args.agno = agno;
args.alignment = 1;
args.pag = libxfs_perag_get(mp, agno);
- error = -libxfs_trans_alloc(mp, &tres,
- libxfs_alloc_min_freelist(mp, args.pag), 0, 0, &tp);
+ error = -libxfs_trans_alloc_rollable(mp, 0, &tp);
if (error)
do_error(_("failed to fix AGFL on AG %d, error %d\n"),
agno, error);
do_error(_("failed to fix AGFL on AG %d, error %d\n"),
agno, error);
}
- libxfs_trans_commit(tp);
+ error = -libxfs_trans_commit(tp);
+ if (error)
+ do_error(_("%s: commit failed, error %d\n"), __func__, error);
}
/*