static char * version_string( xfs_sb_t *sbp) { static char s[1024]; if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_1) strcpy(s, "V1"); else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_2) strcpy(s, "V2"); else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_3) strcpy(s, "V3"); else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) strcpy(s, "V4"); else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) strcpy(s, "V5"); /* * We assume the state of these features now, so macros don't exist for * them any more. */ if (sbp->sb_versionnum & XFS_SB_VERSION_NLINKBIT) strcat(s, ",NLINK"); if (sbp->sb_versionnum & XFS_SB_VERSION_SHAREDBIT) strcat(s, ",SHARED"); if (sbp->sb_versionnum & XFS_SB_VERSION_DIRV2BIT) strcat(s, ",DIRV2"); if (xfs_sb_version_hasattr(sbp)) strcat(s, ",ATTR"); if (xfs_sb_version_hasquota(sbp)) strcat(s, ",QUOTA"); if (xfs_sb_version_hasalign(sbp)) strcat(s, ",ALIGN"); if (xfs_sb_version_hasdalign(sbp)) strcat(s, ",DALIGN"); if (xfs_sb_version_haslogv2(sbp)) strcat(s, ",LOGV2"); if (xfs_sb_version_hasextflgbit(sbp)) strcat(s, ",EXTFLG"); if (xfs_sb_version_hassector(sbp)) strcat(s, ",SECTOR"); if (xfs_sb_version_hasasciici(sbp)) strcat(s, ",ASCII_CI"); if (xfs_sb_version_hasmorebits(sbp)) strcat(s, ",MOREBITS"); if (xfs_sb_version_hasattr2(sbp)) strcat(s, ",ATTR2"); if (xfs_sb_version_haslazysbcount(sbp)) strcat(s, ",LAZYSBCOUNT"); if (xfs_sb_version_hasprojid32bit(sbp)) strcat(s, ",PROJID32BIT"); if (xfs_sb_version_hascrc(sbp)) strcat(s, ",CRC"); if (xfs_sb_version_hasftype(sbp)) strcat(s, ",FTYPE"); if (xfs_sb_version_hasfinobt(sbp)) strcat(s, ",FINOBT"); if (xfs_sb_version_hassparseinodes(sbp)) strcat(s, ",SPARSE_INODES"); if (xfs_sb_version_hasmetauuid(sbp)) strcat(s, ",META_UUID"); return s; }
/* * xfs_trans_unreserve_and_mod_sb() is called to release unused reservations * and apply superblock counter changes to the in-core superblock. The * t_res_fdblocks_delta and t_res_frextents_delta fields are explicitly NOT * applied to the in-core superblock. The idea is that that has already been * done. * * This is done efficiently with a single call to xfs_mod_incore_sb_batch(). * However, we have to ensure that we only modify each superblock field only * once because the application of the delta values may not be atomic. That can * lead to ENOSPC races occurring if we have two separate modifcations of the * free space counter to put back the entire reservation and then take away * what we used. * * If we are not logging superblock counters, then the inode allocated/free and * used block counts are not updated in the on disk superblock. In this case, * XFS_TRANS_SB_DIRTY will not be set when the transaction is updated but we * still need to update the incore superblock with the changes. */ STATIC void xfs_trans_unreserve_and_mod_sb( xfs_trans_t *tp) { xfs_mod_sb_t msb[14]; /* If you add cases, add entries */ xfs_mod_sb_t *msbp; xfs_mount_t *mp = tp->t_mountp; /* REFERENCED */ int error; int rsvd; int64_t blkdelta = 0; int64_t rtxdelta = 0; msbp = msb; rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0; /* calculate free blocks delta */ if (tp->t_blk_res > 0) blkdelta = tp->t_blk_res; if ((tp->t_fdblocks_delta != 0) && (xfs_sb_version_haslazysbcount(&mp->m_sb) || (tp->t_flags & XFS_TRANS_SB_DIRTY))) blkdelta += tp->t_fdblocks_delta; if (blkdelta != 0) { msbp->msb_field = XFS_SBS_FDBLOCKS; msbp->msb_delta = blkdelta; msbp++; } /* calculate free realtime extents delta */ if (tp->t_rtx_res > 0) rtxdelta = tp->t_rtx_res; if ((tp->t_frextents_delta != 0) && (tp->t_flags & XFS_TRANS_SB_DIRTY)) rtxdelta += tp->t_frextents_delta; if (rtxdelta != 0) { msbp->msb_field = XFS_SBS_FREXTENTS; msbp->msb_delta = rtxdelta; msbp++; } /* apply remaining deltas */ if (xfs_sb_version_haslazysbcount(&mp->m_sb) || (tp->t_flags & XFS_TRANS_SB_DIRTY)) { if (tp->t_icount_delta != 0) { msbp->msb_field = XFS_SBS_ICOUNT; msbp->msb_delta = tp->t_icount_delta; msbp++; } if (tp->t_ifree_delta != 0) { msbp->msb_field = XFS_SBS_IFREE; msbp->msb_delta = tp->t_ifree_delta; msbp++; } } if (tp->t_flags & XFS_TRANS_SB_DIRTY) { if (tp->t_dblocks_delta != 0) { msbp->msb_field = XFS_SBS_DBLOCKS; msbp->msb_delta = tp->t_dblocks_delta; msbp++; } if (tp->t_agcount_delta != 0) { msbp->msb_field = XFS_SBS_AGCOUNT; msbp->msb_delta = tp->t_agcount_delta; msbp++; } if (tp->t_imaxpct_delta != 0) { msbp->msb_field = XFS_SBS_IMAX_PCT; msbp->msb_delta = tp->t_imaxpct_delta; msbp++; } if (tp->t_rextsize_delta != 0) { msbp->msb_field = XFS_SBS_REXTSIZE; msbp->msb_delta = tp->t_rextsize_delta; msbp++; } if (tp->t_rbmblocks_delta != 0) { msbp->msb_field = XFS_SBS_RBMBLOCKS; msbp->msb_delta = tp->t_rbmblocks_delta; msbp++; } if (tp->t_rblocks_delta != 0) { msbp->msb_field = XFS_SBS_RBLOCKS; msbp->msb_delta = tp->t_rblocks_delta; msbp++; } if (tp->t_rextents_delta != 0) { msbp->msb_field = XFS_SBS_REXTENTS; msbp->msb_delta = tp->t_rextents_delta; msbp++; } if (tp->t_rextslog_delta != 0) { msbp->msb_field = XFS_SBS_REXTSLOG; msbp->msb_delta = tp->t_rextslog_delta; msbp++; } } /* * If we need to change anything, do it. */ if (msbp > msb) { error = xfs_mod_incore_sb_batch(tp->t_mountp, msb, (uint)(msbp - msb), rsvd); ASSERT(error == 0); } }
/* * xfs_trans_apply_sb_deltas() is called from the commit code * to bring the superblock buffer into the current transaction * and modify it as requested by earlier calls to xfs_trans_mod_sb(). * * For now we just look at each field allowed to change and change * it if necessary. */ STATIC void xfs_trans_apply_sb_deltas( xfs_trans_t *tp) { xfs_dsb_t *sbp; xfs_buf_t *bp; int whole = 0; bp = xfs_trans_getsb(tp, tp->t_mountp, 0); sbp = XFS_BUF_TO_SBP(bp); /* * Check that superblock mods match the mods made to AGF counters. */ ASSERT((tp->t_fdblocks_delta + tp->t_res_fdblocks_delta) == (tp->t_ag_freeblks_delta + tp->t_ag_flist_delta + tp->t_ag_btree_delta)); /* * Only update the superblock counters if we are logging them */ if (!xfs_sb_version_haslazysbcount(&(tp->t_mountp->m_sb))) { if (tp->t_icount_delta) be64_add_cpu(&sbp->sb_icount, tp->t_icount_delta); if (tp->t_ifree_delta) be64_add_cpu(&sbp->sb_ifree, tp->t_ifree_delta); if (tp->t_fdblocks_delta) be64_add_cpu(&sbp->sb_fdblocks, tp->t_fdblocks_delta); if (tp->t_res_fdblocks_delta) be64_add_cpu(&sbp->sb_fdblocks, tp->t_res_fdblocks_delta); } if (tp->t_frextents_delta) be64_add_cpu(&sbp->sb_frextents, tp->t_frextents_delta); if (tp->t_res_frextents_delta) be64_add_cpu(&sbp->sb_frextents, tp->t_res_frextents_delta); if (tp->t_dblocks_delta) { be64_add_cpu(&sbp->sb_dblocks, tp->t_dblocks_delta); whole = 1; } if (tp->t_agcount_delta) { be32_add_cpu(&sbp->sb_agcount, tp->t_agcount_delta); whole = 1; } if (tp->t_imaxpct_delta) { sbp->sb_imax_pct += tp->t_imaxpct_delta; whole = 1; } if (tp->t_rextsize_delta) { be32_add_cpu(&sbp->sb_rextsize, tp->t_rextsize_delta); whole = 1; } if (tp->t_rbmblocks_delta) { be32_add_cpu(&sbp->sb_rbmblocks, tp->t_rbmblocks_delta); whole = 1; } if (tp->t_rblocks_delta) { be64_add_cpu(&sbp->sb_rblocks, tp->t_rblocks_delta); whole = 1; } if (tp->t_rextents_delta) { be64_add_cpu(&sbp->sb_rextents, tp->t_rextents_delta); whole = 1; } if (tp->t_rextslog_delta) { sbp->sb_rextslog += tp->t_rextslog_delta; whole = 1; } if (whole) /* * Log the whole thing, the fields are noncontiguous. */ xfs_trans_log_buf(tp, bp, 0, sizeof(xfs_dsb_t) - 1); else /* * Since all the modifiable fields are contiguous, we * can get away with this. */ xfs_trans_log_buf(tp, bp, offsetof(xfs_dsb_t, sb_icount), offsetof(xfs_dsb_t, sb_frextents) + sizeof(sbp->sb_frextents) - 1); tp->t_mountp->m_super->s_dirt = 1; }
/* * Record the indicated change to the given field for application * to the file system's superblock when the transaction commits. * For now, just store the change in the transaction structure. * * Mark the transaction structure to indicate that the superblock * needs to be updated before committing. * * Because we may not be keeping track of allocated/free inodes and * used filesystem blocks in the superblock, we do not mark the * superblock dirty in this transaction if we modify these fields. * We still need to update the transaction deltas so that they get * applied to the incore superblock, but we don't want them to * cause the superblock to get locked and logged if these are the * only fields in the superblock that the transaction modifies. */ void xfs_trans_mod_sb( xfs_trans_t *tp, uint field, int64_t delta) { uint32_t flags = (XFS_TRANS_DIRTY|XFS_TRANS_SB_DIRTY); xfs_mount_t *mp = tp->t_mountp; switch (field) { case XFS_TRANS_SB_ICOUNT: tp->t_icount_delta += delta; if (xfs_sb_version_haslazysbcount(&mp->m_sb)) flags &= ~XFS_TRANS_SB_DIRTY; break; case XFS_TRANS_SB_IFREE: tp->t_ifree_delta += delta; if (xfs_sb_version_haslazysbcount(&mp->m_sb)) flags &= ~XFS_TRANS_SB_DIRTY; break; case XFS_TRANS_SB_FDBLOCKS: /* * Track the number of blocks allocated in the * transaction. Make sure it does not exceed the * number reserved. */ if (delta < 0) { tp->t_blk_res_used += (uint)-delta; ASSERT(tp->t_blk_res_used <= tp->t_blk_res); } tp->t_fdblocks_delta += delta; if (xfs_sb_version_haslazysbcount(&mp->m_sb)) flags &= ~XFS_TRANS_SB_DIRTY; break; case XFS_TRANS_SB_RES_FDBLOCKS: /* * The allocation has already been applied to the * in-core superblock's counter. This should only * be applied to the on-disk superblock. */ ASSERT(delta < 0); tp->t_res_fdblocks_delta += delta; if (xfs_sb_version_haslazysbcount(&mp->m_sb)) flags &= ~XFS_TRANS_SB_DIRTY; break; case XFS_TRANS_SB_FREXTENTS: /* * Track the number of blocks allocated in the * transaction. Make sure it does not exceed the * number reserved. */ if (delta < 0) { tp->t_rtx_res_used += (uint)-delta; ASSERT(tp->t_rtx_res_used <= tp->t_rtx_res); } tp->t_frextents_delta += delta; break; case XFS_TRANS_SB_RES_FREXTENTS: /* * The allocation has already been applied to the * in-core superblock's counter. This should only * be applied to the on-disk superblock. */ ASSERT(delta < 0); tp->t_res_frextents_delta += delta; break; case XFS_TRANS_SB_DBLOCKS: ASSERT(delta > 0); tp->t_dblocks_delta += delta; break; case XFS_TRANS_SB_AGCOUNT: ASSERT(delta > 0); tp->t_agcount_delta += delta; break; case XFS_TRANS_SB_IMAXPCT: tp->t_imaxpct_delta += delta; break; case XFS_TRANS_SB_REXTSIZE: tp->t_rextsize_delta += delta; break; case XFS_TRANS_SB_RBMBLOCKS: tp->t_rbmblocks_delta += delta; break; case XFS_TRANS_SB_RBLOCKS: tp->t_rblocks_delta += delta; break; case XFS_TRANS_SB_REXTENTS: tp->t_rextents_delta += delta; break; case XFS_TRANS_SB_REXTSLOG: tp->t_rextslog_delta += delta; break; default: ASSERT(0); return; } tp->t_flags |= flags; }
/* ARGSUSED */ void phase1(xfs_mount_t *mp) { xfs_sb_t *sb; char *ag_bp; int rval; do_log(_("Phase 1 - find and verify superblock...\n")); primary_sb_modified = 0; need_root_inode = 0; need_root_dotdot = 0; need_rbmino = 0; need_rsumino = 0; lost_quotas = 0; /* * get AG 0 into ag header buf */ ag_bp = alloc_ag_buf(MAX_SECTSIZE); sb = (xfs_sb_t *) ag_bp; if (get_sb(sb, 0LL, MAX_SECTSIZE, 0) == XR_EOF) do_error(_("error reading primary superblock\n")); /* * is this really an sb, verify internal consistency */ if ((rval = verify_sb(sb, 1)) != XR_OK) { do_warn(_("bad primary superblock - %s !!!\n"), err_string(rval)); if (!find_secondary_sb(sb)) no_sb(); primary_sb_modified = 1; } else if ((rval = verify_set_primary_sb(sb, 0, &primary_sb_modified)) != XR_OK) { do_warn(_("couldn't verify primary superblock - %s !!!\n"), err_string(rval)); if (!find_secondary_sb(sb)) no_sb(); primary_sb_modified = 1; } /* * Check bad_features2 and make sure features2 the same as * bad_features (ORing the two together). Leave bad_features2 * set so older kernels can still use it and not mount unsupported * filesystems when it reads bad_features2. */ if (sb->sb_bad_features2 != 0 && sb->sb_bad_features2 != sb->sb_features2) { sb->sb_features2 |= sb->sb_bad_features2; sb->sb_bad_features2 = sb->sb_features2; primary_sb_modified = 1; do_warn(_("superblock has a features2 mismatch, correcting\n")); } /* * apply any version changes or conversions after the primary * superblock has been verified or repaired * * Send output to stdout as do_log and everything else in repair * is sent to stderr and there is no "quiet" option. xfs_admin * will filter stderr but not stdout. This situation must be improved. */ if (convert_lazy_count) { if (lazy_count && !xfs_sb_version_haslazysbcount(sb)) { sb->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT; sb->sb_features2 |= XFS_SB_VERSION2_LAZYSBCOUNTBIT; sb->sb_bad_features2 |= XFS_SB_VERSION2_LAZYSBCOUNTBIT; primary_sb_modified = 1; printf(_("Enabling lazy-counters\n")); } else if (!lazy_count && xfs_sb_version_haslazysbcount(sb)) { sb->sb_features2 &= ~XFS_SB_VERSION2_LAZYSBCOUNTBIT; sb->sb_bad_features2 &= ~XFS_SB_VERSION2_LAZYSBCOUNTBIT; printf(_("Disabling lazy-counters\n")); primary_sb_modified = 1; } else { printf(_("Lazy-counters are already %s\n"), lazy_count ? _("enabled") : _("disabled")); exit(0); /* no conversion required, exit */ } } if (primary_sb_modified) { if (!no_modify) { do_warn(_("writing modified primary superblock\n")); write_primary_sb(sb, sb->sb_sectsize); } else { do_warn(_("would write modified primary superblock\n")); } } /* * misc. global var initialization */ sb_ifree = sb_icount = sb_fdblocks = sb_frextents = 0; free(sb); }
/* * build both the agf and the agfl for an agno given both * btree cursors. * * XXX: yet more common code that can be shared with mkfs/growfs. */ static void build_agf_agfl(xfs_mount_t *mp, xfs_agnumber_t agno, bt_status_t *bno_bt, bt_status_t *bcnt_bt, xfs_extlen_t freeblks, /* # free blocks in tree */ int lostblocks) /* # blocks that will be lost */ { extent_tree_node_t *ext_ptr; xfs_buf_t *agf_buf, *agfl_buf; int i; int j; xfs_agfl_t *agfl; xfs_agf_t *agf; __be32 *freelist; agf_buf = libxfs_getbuf(mp->m_dev, XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)), mp->m_sb.sb_sectsize/BBSIZE); agf_buf->b_ops = &xfs_agf_buf_ops; agf = XFS_BUF_TO_AGF(agf_buf); memset(agf, 0, mp->m_sb.sb_sectsize); #ifdef XR_BLD_FREE_TRACE fprintf(stderr, "agf = 0x%p, agf_buf->b_addr = 0x%p\n", agf, agf_buf->b_addr); #endif /* * set up fixed part of agf */ agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC); agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION); agf->agf_seqno = cpu_to_be32(agno); if (agno < mp->m_sb.sb_agcount - 1) agf->agf_length = cpu_to_be32(mp->m_sb.sb_agblocks); else agf->agf_length = cpu_to_be32(mp->m_sb.sb_dblocks - (xfs_rfsblock_t) mp->m_sb.sb_agblocks * agno); agf->agf_roots[XFS_BTNUM_BNO] = cpu_to_be32(bno_bt->root); agf->agf_levels[XFS_BTNUM_BNO] = cpu_to_be32(bno_bt->num_levels); agf->agf_roots[XFS_BTNUM_CNT] = cpu_to_be32(bcnt_bt->root); agf->agf_levels[XFS_BTNUM_CNT] = cpu_to_be32(bcnt_bt->num_levels); agf->agf_freeblks = cpu_to_be32(freeblks); /* * Count and record the number of btree blocks consumed if required. */ if (xfs_sb_version_haslazysbcount(&mp->m_sb)) { /* * Don't count the root blocks as they are already * accounted for. */ agf->agf_btreeblks = cpu_to_be32( (bno_bt->num_tot_blocks - bno_bt->num_free_blocks) + (bcnt_bt->num_tot_blocks - bcnt_bt->num_free_blocks) - 2); #ifdef XR_BLD_FREE_TRACE fprintf(stderr, "agf->agf_btreeblks = %u\n", be32_to_cpu(agf->agf_btreeblks)); #endif } #ifdef XR_BLD_FREE_TRACE fprintf(stderr, "bno root = %u, bcnt root = %u, indices = %u %u\n", be32_to_cpu(agf->agf_roots[XFS_BTNUM_BNO]), be32_to_cpu(agf->agf_roots[XFS_BTNUM_CNT]), XFS_BTNUM_BNO, XFS_BTNUM_CNT); #endif if (xfs_sb_version_hascrc(&mp->m_sb)) platform_uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid); /* initialise the AGFL, then fill it if there are blocks left over. */ agfl_buf = libxfs_getbuf(mp->m_dev, XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)), mp->m_sb.sb_sectsize/BBSIZE); agfl_buf->b_ops = &xfs_agfl_buf_ops; agfl = XFS_BUF_TO_AGFL(agfl_buf); /* setting to 0xff results in initialisation to NULLAGBLOCK */ memset(agfl, 0xff, mp->m_sb.sb_sectsize); if (xfs_sb_version_hascrc(&mp->m_sb)) { agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC); agfl->agfl_seqno = cpu_to_be32(agno); platform_uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid); for (i = 0; i < XFS_AGFL_SIZE(mp); i++) agfl->agfl_bno[i] = cpu_to_be32(NULLAGBLOCK); } freelist = XFS_BUF_TO_AGFL_BNO(mp, agfl_buf); /* * do we have left-over blocks in the btree cursors that should * be used to fill the AGFL? */ if (bno_bt->num_free_blocks > 0 || bcnt_bt->num_free_blocks > 0) { /* * yes, now grab as many blocks as we can */ i = j = 0; while (bno_bt->num_free_blocks > 0 && i < XFS_AGFL_SIZE(mp)) { freelist[i] = cpu_to_be32( get_next_blockaddr(agno, 0, bno_bt)); i++; } while (bcnt_bt->num_free_blocks > 0 && i < XFS_AGFL_SIZE(mp)) { freelist[i] = cpu_to_be32( get_next_blockaddr(agno, 0, bcnt_bt)); i++; } /* * now throw the rest of the blocks away and complain */ while (bno_bt->num_free_blocks > 0) { (void) get_next_blockaddr(agno, 0, bno_bt); j++; } while (bcnt_bt->num_free_blocks > 0) { (void) get_next_blockaddr(agno, 0, bcnt_bt); j++; } if (j > 0) { if (j == lostblocks) do_warn(_("lost %d blocks in ag %u\n"), j, agno); else do_warn(_("thought we were going to lose %d " "blocks in ag %u, actually lost " "%d\n"), lostblocks, j, agno); } agf->agf_flfirst = 0; agf->agf_fllast = cpu_to_be32(i - 1); agf->agf_flcount = cpu_to_be32(i); #ifdef XR_BLD_FREE_TRACE fprintf(stderr, "writing agfl for ag %u\n", agno); #endif } else { agf->agf_flfirst = 0; agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1); agf->agf_flcount = 0; } libxfs_writebuf(agfl_buf, 0); ext_ptr = findbiggest_bcnt_extent(agno); agf->agf_longest = cpu_to_be32((ext_ptr != NULL) ? ext_ptr->ex_blockcount : 0); ASSERT(be32_to_cpu(agf->agf_roots[XFS_BTNUM_BNOi]) != be32_to_cpu(agf->agf_roots[XFS_BTNUM_CNTi])); libxfs_writebuf(agf_buf, 0); /* * now fix up the free list appropriately * XXX: code lifted from mkfs, should be shared. */ { xfs_alloc_arg_t args; xfs_trans_t *tp; struct xfs_trans_res tres = {0}; int error; memset(&args, 0, sizeof(args)); args.tp = tp = libxfs_trans_alloc(mp, 0); args.mp = mp; args.agno = agno; args.alignment = 1; args.pag = xfs_perag_get(mp,agno); libxfs_trans_reserve(tp, &tres, xfs_alloc_min_freelist(mp, args.pag), 0); error = libxfs_alloc_fix_freelist(&args, 0); xfs_perag_put(args.pag); if (error) { do_error(_("failed to fix AGFL on AG %d, error %d\n"), agno, error); } libxfs_trans_commit(tp); } #ifdef XR_BLD_FREE_TRACE fprintf(stderr, "wrote agf for ag %u\n", agno); #endif }