11da177e4SLinus Torvalds /* 27b718769SNathan Scott * Copyright (c) 2000-2005 Silicon Graphics, Inc. 37b718769SNathan Scott * All Rights Reserved. 41da177e4SLinus Torvalds * 57b718769SNathan Scott * This program is free software; you can redistribute it and/or 67b718769SNathan Scott * modify it under the terms of the GNU General Public License as 71da177e4SLinus Torvalds * published by the Free Software Foundation. 81da177e4SLinus Torvalds * 97b718769SNathan Scott * This program is distributed in the hope that it would be useful, 107b718769SNathan Scott * but WITHOUT ANY WARRANTY; without even the implied warranty of 117b718769SNathan Scott * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 127b718769SNathan Scott * GNU General Public License for more details. 131da177e4SLinus Torvalds * 147b718769SNathan Scott * You should have received a copy of the GNU General Public License 157b718769SNathan Scott * along with this program; if not, write the Free Software Foundation, 167b718769SNathan Scott * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 171da177e4SLinus Torvalds */ 181da177e4SLinus Torvalds #include "xfs.h" 19a844f451SNathan Scott #include "xfs_fs.h" 201da177e4SLinus Torvalds #include "xfs_types.h" 21a844f451SNathan Scott #include "xfs_bit.h" 221da177e4SLinus Torvalds #include "xfs_inum.h" 231da177e4SLinus Torvalds #include "xfs_log.h" 241da177e4SLinus Torvalds #include "xfs_trans.h" 251da177e4SLinus Torvalds #include "xfs_sb.h" 26a844f451SNathan Scott #include "xfs_ag.h" 27a844f451SNathan Scott #include "xfs_dir2.h" 281da177e4SLinus Torvalds #include "xfs_dmapi.h" 291da177e4SLinus Torvalds #include "xfs_mount.h" 301da177e4SLinus Torvalds #include "xfs_bmap_btree.h" 31a844f451SNathan Scott #include "xfs_alloc_btree.h" 321da177e4SLinus Torvalds #include "xfs_ialloc_btree.h" 33a844f451SNathan Scott #include "xfs_dir2_sf.h" 34a844f451SNathan Scott #include "xfs_attr_sf.h" 35a844f451SNathan Scott #include "xfs_dinode.h" 36a844f451SNathan Scott #include "xfs_inode.h" 37a844f451SNathan Scott #include "xfs_inode_item.h" 381da177e4SLinus Torvalds #include "xfs_btree.h" 391da177e4SLinus Torvalds #include "xfs_error.h" 401da177e4SLinus Torvalds #include "xfs_alloc.h" 411da177e4SLinus Torvalds #include "xfs_ialloc.h" 421da177e4SLinus Torvalds #include "xfs_fsops.h" 431da177e4SLinus Torvalds #include "xfs_itable.h" 441da177e4SLinus Torvalds #include "xfs_trans_space.h" 451da177e4SLinus Torvalds #include "xfs_rtalloc.h" 46a844f451SNathan Scott #include "xfs_rw.h" 472a82b8beSDavid Chinner #include "xfs_filestream.h" 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds /* 501da177e4SLinus Torvalds * File system operations 511da177e4SLinus Torvalds */ 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds int 541da177e4SLinus Torvalds xfs_fs_geometry( 551da177e4SLinus Torvalds xfs_mount_t *mp, 561da177e4SLinus Torvalds xfs_fsop_geom_t *geo, 571da177e4SLinus Torvalds int new_version) 581da177e4SLinus Torvalds { 591da177e4SLinus Torvalds geo->blocksize = mp->m_sb.sb_blocksize; 601da177e4SLinus Torvalds geo->rtextsize = mp->m_sb.sb_rextsize; 611da177e4SLinus Torvalds geo->agblocks = mp->m_sb.sb_agblocks; 621da177e4SLinus Torvalds geo->agcount = mp->m_sb.sb_agcount; 631da177e4SLinus Torvalds geo->logblocks = mp->m_sb.sb_logblocks; 641da177e4SLinus Torvalds geo->sectsize = mp->m_sb.sb_sectsize; 651da177e4SLinus Torvalds geo->inodesize = mp->m_sb.sb_inodesize; 661da177e4SLinus Torvalds geo->imaxpct = mp->m_sb.sb_imax_pct; 671da177e4SLinus Torvalds geo->datablocks = mp->m_sb.sb_dblocks; 681da177e4SLinus Torvalds geo->rtblocks = mp->m_sb.sb_rblocks; 691da177e4SLinus Torvalds geo->rtextents = mp->m_sb.sb_rextents; 701da177e4SLinus Torvalds geo->logstart = mp->m_sb.sb_logstart; 711da177e4SLinus Torvalds ASSERT(sizeof(geo->uuid)==sizeof(mp->m_sb.sb_uuid)); 721da177e4SLinus Torvalds memcpy(geo->uuid, &mp->m_sb.sb_uuid, sizeof(mp->m_sb.sb_uuid)); 731da177e4SLinus Torvalds if (new_version >= 2) { 741da177e4SLinus Torvalds geo->sunit = mp->m_sb.sb_unit; 751da177e4SLinus Torvalds geo->swidth = mp->m_sb.sb_width; 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds if (new_version >= 3) { 781da177e4SLinus Torvalds geo->version = XFS_FSOP_GEOM_VERSION; 791da177e4SLinus Torvalds geo->flags = 8062118709SEric Sandeen (xfs_sb_version_hasattr(&mp->m_sb) ? 811da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_ATTR : 0) | 8262118709SEric Sandeen (xfs_sb_version_hasnlink(&mp->m_sb) ? 831da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_NLINK : 0) | 8462118709SEric Sandeen (xfs_sb_version_hasquota(&mp->m_sb) ? 851da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_QUOTA : 0) | 8662118709SEric Sandeen (xfs_sb_version_hasalign(&mp->m_sb) ? 871da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_IALIGN : 0) | 8862118709SEric Sandeen (xfs_sb_version_hasdalign(&mp->m_sb) ? 891da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_DALIGN : 0) | 9062118709SEric Sandeen (xfs_sb_version_hasshared(&mp->m_sb) ? 911da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_SHARED : 0) | 9262118709SEric Sandeen (xfs_sb_version_hasextflgbit(&mp->m_sb) ? 931da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_EXTFLG : 0) | 9462118709SEric Sandeen (xfs_sb_version_hasdirv2(&mp->m_sb) ? 951da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) | 9662118709SEric Sandeen (xfs_sb_version_hassector(&mp->m_sb) ? 97d8cc890dSNathan Scott XFS_FSOP_GEOM_FLAGS_SECTOR : 0) | 98189f4bf2SBarry Naujok (xfs_sb_version_hasasciici(&mp->m_sb) ? 99189f4bf2SBarry Naujok XFS_FSOP_GEOM_FLAGS_DIRV2CI : 0) | 10092821e2bSDavid Chinner (xfs_sb_version_haslazysbcount(&mp->m_sb) ? 10192821e2bSDavid Chinner XFS_FSOP_GEOM_FLAGS_LAZYSB : 0) | 10262118709SEric Sandeen (xfs_sb_version_hasattr2(&mp->m_sb) ? 103d8cc890dSNathan Scott XFS_FSOP_GEOM_FLAGS_ATTR2 : 0); 10462118709SEric Sandeen geo->logsectsize = xfs_sb_version_hassector(&mp->m_sb) ? 1051da177e4SLinus Torvalds mp->m_sb.sb_logsectsize : BBSIZE; 1061da177e4SLinus Torvalds geo->rtsectsize = mp->m_sb.sb_blocksize; 1071da177e4SLinus Torvalds geo->dirblocksize = mp->m_dirblksize; 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds if (new_version >= 4) { 1101da177e4SLinus Torvalds geo->flags |= 11162118709SEric Sandeen (xfs_sb_version_haslogv2(&mp->m_sb) ? 1121da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_LOGV2 : 0); 1131da177e4SLinus Torvalds geo->logsunit = mp->m_sb.sb_logsunit; 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds return 0; 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds static int 1191da177e4SLinus Torvalds xfs_growfs_data_private( 1201da177e4SLinus Torvalds xfs_mount_t *mp, /* mount point for filesystem */ 1211da177e4SLinus Torvalds xfs_growfs_data_t *in) /* growfs data input struct */ 1221da177e4SLinus Torvalds { 1231da177e4SLinus Torvalds xfs_agf_t *agf; 1241da177e4SLinus Torvalds xfs_agi_t *agi; 1251da177e4SLinus Torvalds xfs_agnumber_t agno; 1261da177e4SLinus Torvalds xfs_extlen_t agsize; 1271da177e4SLinus Torvalds xfs_extlen_t tmpsize; 1281da177e4SLinus Torvalds xfs_alloc_rec_t *arec; 1297cc95a82SChristoph Hellwig struct xfs_btree_block *block; 1301da177e4SLinus Torvalds xfs_buf_t *bp; 1311da177e4SLinus Torvalds int bucket; 1321da177e4SLinus Torvalds int dpct; 1331da177e4SLinus Torvalds int error; 1341da177e4SLinus Torvalds xfs_agnumber_t nagcount; 1351da177e4SLinus Torvalds xfs_agnumber_t nagimax = 0; 1361da177e4SLinus Torvalds xfs_rfsblock_t nb, nb_mod; 1371da177e4SLinus Torvalds xfs_rfsblock_t new; 1381da177e4SLinus Torvalds xfs_rfsblock_t nfree; 1391da177e4SLinus Torvalds xfs_agnumber_t oagcount; 1401da177e4SLinus Torvalds int pct; 1411da177e4SLinus Torvalds xfs_trans_t *tp; 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds nb = in->newblocks; 1441da177e4SLinus Torvalds pct = in->imaxpct; 1451da177e4SLinus Torvalds if (nb < mp->m_sb.sb_dblocks || pct < 0 || pct > 100) 1461da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 1474cc929eeSNathan Scott if ((error = xfs_sb_validate_fsb_count(&mp->m_sb, nb))) 1484cc929eeSNathan Scott return error; 1491da177e4SLinus Torvalds dpct = pct - mp->m_sb.sb_imax_pct; 1501da177e4SLinus Torvalds error = xfs_read_buf(mp, mp->m_ddev_targp, 1511da177e4SLinus Torvalds XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1), 1521da177e4SLinus Torvalds XFS_FSS_TO_BB(mp, 1), 0, &bp); 1531da177e4SLinus Torvalds if (error) 1541da177e4SLinus Torvalds return error; 1551da177e4SLinus Torvalds ASSERT(bp); 1561da177e4SLinus Torvalds xfs_buf_relse(bp); 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds new = nb; /* use new as a temporary here */ 1591da177e4SLinus Torvalds nb_mod = do_div(new, mp->m_sb.sb_agblocks); 1601da177e4SLinus Torvalds nagcount = new + (nb_mod != 0); 1611da177e4SLinus Torvalds if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) { 1621da177e4SLinus Torvalds nagcount--; 1631da177e4SLinus Torvalds nb = nagcount * mp->m_sb.sb_agblocks; 1641da177e4SLinus Torvalds if (nb < mp->m_sb.sb_dblocks) 1651da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds new = nb - mp->m_sb.sb_dblocks; 1681da177e4SLinus Torvalds oagcount = mp->m_sb.sb_agcount; 1691da177e4SLinus Torvalds if (nagcount > oagcount) { 1702a82b8beSDavid Chinner xfs_filestream_flush(mp); 1711da177e4SLinus Torvalds down_write(&mp->m_peraglock); 1721da177e4SLinus Torvalds mp->m_perag = kmem_realloc(mp->m_perag, 1731da177e4SLinus Torvalds sizeof(xfs_perag_t) * nagcount, 1741da177e4SLinus Torvalds sizeof(xfs_perag_t) * oagcount, 1751da177e4SLinus Torvalds KM_SLEEP); 1761da177e4SLinus Torvalds memset(&mp->m_perag[oagcount], 0, 1771da177e4SLinus Torvalds (nagcount - oagcount) * sizeof(xfs_perag_t)); 1781da177e4SLinus Torvalds mp->m_flags |= XFS_MOUNT_32BITINODES; 179b267ce99SChristoph Hellwig nagimax = xfs_initialize_perag(mp, nagcount); 1801da177e4SLinus Torvalds up_write(&mp->m_peraglock); 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS); 18384e1e99fSDavid Chinner tp->t_flags |= XFS_TRANS_RESERVE; 1841da177e4SLinus Torvalds if ((error = xfs_trans_reserve(tp, XFS_GROWFS_SPACE_RES(mp), 1851da177e4SLinus Torvalds XFS_GROWDATA_LOG_RES(mp), 0, 0, 0))) { 1861da177e4SLinus Torvalds xfs_trans_cancel(tp, 0); 1871da177e4SLinus Torvalds return error; 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds nfree = 0; 1911da177e4SLinus Torvalds for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) { 1921da177e4SLinus Torvalds /* 1931da177e4SLinus Torvalds * AG freelist header block 1941da177e4SLinus Torvalds */ 1951da177e4SLinus Torvalds bp = xfs_buf_get(mp->m_ddev_targp, 1961da177e4SLinus Torvalds XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)), 1971da177e4SLinus Torvalds XFS_FSS_TO_BB(mp, 1), 0); 1981da177e4SLinus Torvalds agf = XFS_BUF_TO_AGF(bp); 1991da177e4SLinus Torvalds memset(agf, 0, mp->m_sb.sb_sectsize); 20016259e7dSChristoph Hellwig agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC); 20116259e7dSChristoph Hellwig agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION); 20216259e7dSChristoph Hellwig agf->agf_seqno = cpu_to_be32(agno); 2031da177e4SLinus Torvalds if (agno == nagcount - 1) 2041da177e4SLinus Torvalds agsize = 2051da177e4SLinus Torvalds nb - 2061da177e4SLinus Torvalds (agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks); 2071da177e4SLinus Torvalds else 2081da177e4SLinus Torvalds agsize = mp->m_sb.sb_agblocks; 20916259e7dSChristoph Hellwig agf->agf_length = cpu_to_be32(agsize); 21016259e7dSChristoph Hellwig agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp)); 21116259e7dSChristoph Hellwig agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp)); 21216259e7dSChristoph Hellwig agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1); 21316259e7dSChristoph Hellwig agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1); 2141da177e4SLinus Torvalds agf->agf_flfirst = 0; 21516259e7dSChristoph Hellwig agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1); 2161da177e4SLinus Torvalds agf->agf_flcount = 0; 2171da177e4SLinus Torvalds tmpsize = agsize - XFS_PREALLOC_BLOCKS(mp); 21816259e7dSChristoph Hellwig agf->agf_freeblks = cpu_to_be32(tmpsize); 21916259e7dSChristoph Hellwig agf->agf_longest = cpu_to_be32(tmpsize); 2201da177e4SLinus Torvalds error = xfs_bwrite(mp, bp); 2211da177e4SLinus Torvalds if (error) { 2221da177e4SLinus Torvalds goto error0; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds /* 2251da177e4SLinus Torvalds * AG inode header block 2261da177e4SLinus Torvalds */ 2271da177e4SLinus Torvalds bp = xfs_buf_get(mp->m_ddev_targp, 2281da177e4SLinus Torvalds XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)), 2291da177e4SLinus Torvalds XFS_FSS_TO_BB(mp, 1), 0); 2301da177e4SLinus Torvalds agi = XFS_BUF_TO_AGI(bp); 2311da177e4SLinus Torvalds memset(agi, 0, mp->m_sb.sb_sectsize); 23216259e7dSChristoph Hellwig agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC); 23316259e7dSChristoph Hellwig agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION); 23416259e7dSChristoph Hellwig agi->agi_seqno = cpu_to_be32(agno); 23516259e7dSChristoph Hellwig agi->agi_length = cpu_to_be32(agsize); 2361da177e4SLinus Torvalds agi->agi_count = 0; 23716259e7dSChristoph Hellwig agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp)); 23816259e7dSChristoph Hellwig agi->agi_level = cpu_to_be32(1); 2391da177e4SLinus Torvalds agi->agi_freecount = 0; 24016259e7dSChristoph Hellwig agi->agi_newino = cpu_to_be32(NULLAGINO); 24116259e7dSChristoph Hellwig agi->agi_dirino = cpu_to_be32(NULLAGINO); 2421da177e4SLinus Torvalds for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) 24316259e7dSChristoph Hellwig agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO); 2441da177e4SLinus Torvalds error = xfs_bwrite(mp, bp); 2451da177e4SLinus Torvalds if (error) { 2461da177e4SLinus Torvalds goto error0; 2471da177e4SLinus Torvalds } 2481da177e4SLinus Torvalds /* 2491da177e4SLinus Torvalds * BNO btree root block 2501da177e4SLinus Torvalds */ 2511da177e4SLinus Torvalds bp = xfs_buf_get(mp->m_ddev_targp, 2521da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)), 2531da177e4SLinus Torvalds BTOBB(mp->m_sb.sb_blocksize), 0); 2547cc95a82SChristoph Hellwig block = XFS_BUF_TO_BLOCK(bp); 2551da177e4SLinus Torvalds memset(block, 0, mp->m_sb.sb_blocksize); 25616259e7dSChristoph Hellwig block->bb_magic = cpu_to_be32(XFS_ABTB_MAGIC); 2571da177e4SLinus Torvalds block->bb_level = 0; 25816259e7dSChristoph Hellwig block->bb_numrecs = cpu_to_be16(1); 2597cc95a82SChristoph Hellwig block->bb_u.s.bb_leftsib = cpu_to_be32(NULLAGBLOCK); 2607cc95a82SChristoph Hellwig block->bb_u.s.bb_rightsib = cpu_to_be32(NULLAGBLOCK); 261136341b4SChristoph Hellwig arec = XFS_ALLOC_REC_ADDR(mp, block, 1); 26216259e7dSChristoph Hellwig arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp)); 26316259e7dSChristoph Hellwig arec->ar_blockcount = cpu_to_be32( 26416259e7dSChristoph Hellwig agsize - be32_to_cpu(arec->ar_startblock)); 2651da177e4SLinus Torvalds error = xfs_bwrite(mp, bp); 2661da177e4SLinus Torvalds if (error) { 2671da177e4SLinus Torvalds goto error0; 2681da177e4SLinus Torvalds } 2691da177e4SLinus Torvalds /* 2701da177e4SLinus Torvalds * CNT btree root block 2711da177e4SLinus Torvalds */ 2721da177e4SLinus Torvalds bp = xfs_buf_get(mp->m_ddev_targp, 2731da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)), 2741da177e4SLinus Torvalds BTOBB(mp->m_sb.sb_blocksize), 0); 2757cc95a82SChristoph Hellwig block = XFS_BUF_TO_BLOCK(bp); 2761da177e4SLinus Torvalds memset(block, 0, mp->m_sb.sb_blocksize); 27716259e7dSChristoph Hellwig block->bb_magic = cpu_to_be32(XFS_ABTC_MAGIC); 2781da177e4SLinus Torvalds block->bb_level = 0; 27916259e7dSChristoph Hellwig block->bb_numrecs = cpu_to_be16(1); 2807cc95a82SChristoph Hellwig block->bb_u.s.bb_leftsib = cpu_to_be32(NULLAGBLOCK); 2817cc95a82SChristoph Hellwig block->bb_u.s.bb_rightsib = cpu_to_be32(NULLAGBLOCK); 282136341b4SChristoph Hellwig arec = XFS_ALLOC_REC_ADDR(mp, block, 1); 28316259e7dSChristoph Hellwig arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp)); 28416259e7dSChristoph Hellwig arec->ar_blockcount = cpu_to_be32( 28516259e7dSChristoph Hellwig agsize - be32_to_cpu(arec->ar_startblock)); 28616259e7dSChristoph Hellwig nfree += be32_to_cpu(arec->ar_blockcount); 2871da177e4SLinus Torvalds error = xfs_bwrite(mp, bp); 2881da177e4SLinus Torvalds if (error) { 2891da177e4SLinus Torvalds goto error0; 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds /* 2921da177e4SLinus Torvalds * INO btree root block 2931da177e4SLinus Torvalds */ 2941da177e4SLinus Torvalds bp = xfs_buf_get(mp->m_ddev_targp, 2951da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)), 2961da177e4SLinus Torvalds BTOBB(mp->m_sb.sb_blocksize), 0); 2977cc95a82SChristoph Hellwig block = XFS_BUF_TO_BLOCK(bp); 2981da177e4SLinus Torvalds memset(block, 0, mp->m_sb.sb_blocksize); 29916259e7dSChristoph Hellwig block->bb_magic = cpu_to_be32(XFS_IBT_MAGIC); 3001da177e4SLinus Torvalds block->bb_level = 0; 3011da177e4SLinus Torvalds block->bb_numrecs = 0; 3027cc95a82SChristoph Hellwig block->bb_u.s.bb_leftsib = cpu_to_be32(NULLAGBLOCK); 3037cc95a82SChristoph Hellwig block->bb_u.s.bb_rightsib = cpu_to_be32(NULLAGBLOCK); 3041da177e4SLinus Torvalds error = xfs_bwrite(mp, bp); 3051da177e4SLinus Torvalds if (error) { 3061da177e4SLinus Torvalds goto error0; 3071da177e4SLinus Torvalds } 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds xfs_trans_agblocks_delta(tp, nfree); 3101da177e4SLinus Torvalds /* 3111da177e4SLinus Torvalds * There are new blocks in the old last a.g. 3121da177e4SLinus Torvalds */ 3131da177e4SLinus Torvalds if (new) { 3141da177e4SLinus Torvalds /* 3151da177e4SLinus Torvalds * Change the agi length. 3161da177e4SLinus Torvalds */ 3171da177e4SLinus Torvalds error = xfs_ialloc_read_agi(mp, tp, agno, &bp); 3181da177e4SLinus Torvalds if (error) { 3191da177e4SLinus Torvalds goto error0; 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds ASSERT(bp); 3221da177e4SLinus Torvalds agi = XFS_BUF_TO_AGI(bp); 323413d57c9SMarcin Slusarz be32_add_cpu(&agi->agi_length, new); 3241da177e4SLinus Torvalds ASSERT(nagcount == oagcount || 32516259e7dSChristoph Hellwig be32_to_cpu(agi->agi_length) == mp->m_sb.sb_agblocks); 3261da177e4SLinus Torvalds xfs_ialloc_log_agi(tp, bp, XFS_AGI_LENGTH); 3271da177e4SLinus Torvalds /* 3281da177e4SLinus Torvalds * Change agf length. 3291da177e4SLinus Torvalds */ 3301da177e4SLinus Torvalds error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp); 3311da177e4SLinus Torvalds if (error) { 3321da177e4SLinus Torvalds goto error0; 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds ASSERT(bp); 3351da177e4SLinus Torvalds agf = XFS_BUF_TO_AGF(bp); 336413d57c9SMarcin Slusarz be32_add_cpu(&agf->agf_length, new); 33716259e7dSChristoph Hellwig ASSERT(be32_to_cpu(agf->agf_length) == 33816259e7dSChristoph Hellwig be32_to_cpu(agi->agi_length)); 3390164af51STim Shimmin xfs_alloc_log_agf(tp, bp, XFS_AGF_LENGTH); 3401da177e4SLinus Torvalds /* 3411da177e4SLinus Torvalds * Free the new space. 3421da177e4SLinus Torvalds */ 3431da177e4SLinus Torvalds error = xfs_free_extent(tp, XFS_AGB_TO_FSB(mp, agno, 34416259e7dSChristoph Hellwig be32_to_cpu(agf->agf_length) - new), new); 3451da177e4SLinus Torvalds if (error) { 3461da177e4SLinus Torvalds goto error0; 3471da177e4SLinus Torvalds } 3481da177e4SLinus Torvalds } 3491da177e4SLinus Torvalds if (nagcount > oagcount) 3501da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount); 3511da177e4SLinus Torvalds if (nb > mp->m_sb.sb_dblocks) 3521da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, 3531da177e4SLinus Torvalds nb - mp->m_sb.sb_dblocks); 3541da177e4SLinus Torvalds if (nfree) 3551da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree); 3561da177e4SLinus Torvalds if (dpct) 3571da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct); 3581c72bf90SEric Sandeen error = xfs_trans_commit(tp, 0); 3591da177e4SLinus Torvalds if (error) { 3601da177e4SLinus Torvalds return error; 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds /* New allocation groups fully initialized, so update mount struct */ 3631da177e4SLinus Torvalds if (nagimax) 3641da177e4SLinus Torvalds mp->m_maxagi = nagimax; 3651da177e4SLinus Torvalds if (mp->m_sb.sb_imax_pct) { 3661da177e4SLinus Torvalds __uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct; 3671da177e4SLinus Torvalds do_div(icount, 100); 3681da177e4SLinus Torvalds mp->m_maxicount = icount << mp->m_sb.sb_inopblog; 3691da177e4SLinus Torvalds } else 3701da177e4SLinus Torvalds mp->m_maxicount = 0; 3711da177e4SLinus Torvalds for (agno = 1; agno < nagcount; agno++) { 3721da177e4SLinus Torvalds error = xfs_read_buf(mp, mp->m_ddev_targp, 3731da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)), 3741da177e4SLinus Torvalds XFS_FSS_TO_BB(mp, 1), 0, &bp); 3751da177e4SLinus Torvalds if (error) { 3761da177e4SLinus Torvalds xfs_fs_cmn_err(CE_WARN, mp, 3771da177e4SLinus Torvalds "error %d reading secondary superblock for ag %d", 3781da177e4SLinus Torvalds error, agno); 3791da177e4SLinus Torvalds break; 3801da177e4SLinus Torvalds } 3812bdf7cd0SChristoph Hellwig xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb, XFS_SB_ALL_BITS); 3821da177e4SLinus Torvalds /* 3831da177e4SLinus Torvalds * If we get an error writing out the alternate superblocks, 3841da177e4SLinus Torvalds * just issue a warning and continue. The real work is 3851da177e4SLinus Torvalds * already done and committed. 3861da177e4SLinus Torvalds */ 3871da177e4SLinus Torvalds if (!(error = xfs_bwrite(mp, bp))) { 3881da177e4SLinus Torvalds continue; 3891da177e4SLinus Torvalds } else { 3901da177e4SLinus Torvalds xfs_fs_cmn_err(CE_WARN, mp, 3911da177e4SLinus Torvalds "write error %d updating secondary superblock for ag %d", 3921da177e4SLinus Torvalds error, agno); 3931da177e4SLinus Torvalds break; /* no point in continuing */ 3941da177e4SLinus Torvalds } 3951da177e4SLinus Torvalds } 3961da177e4SLinus Torvalds return 0; 3971da177e4SLinus Torvalds 3981da177e4SLinus Torvalds error0: 3991da177e4SLinus Torvalds xfs_trans_cancel(tp, XFS_TRANS_ABORT); 4001da177e4SLinus Torvalds return error; 4011da177e4SLinus Torvalds } 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds static int 4041da177e4SLinus Torvalds xfs_growfs_log_private( 4051da177e4SLinus Torvalds xfs_mount_t *mp, /* mount point for filesystem */ 4061da177e4SLinus Torvalds xfs_growfs_log_t *in) /* growfs log input struct */ 4071da177e4SLinus Torvalds { 4081da177e4SLinus Torvalds xfs_extlen_t nb; 4091da177e4SLinus Torvalds 4101da177e4SLinus Torvalds nb = in->newblocks; 4111da177e4SLinus Torvalds if (nb < XFS_MIN_LOG_BLOCKS || nb < XFS_B_TO_FSB(mp, XFS_MIN_LOG_BYTES)) 4121da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 4131da177e4SLinus Torvalds if (nb == mp->m_sb.sb_logblocks && 4141da177e4SLinus Torvalds in->isint == (mp->m_sb.sb_logstart != 0)) 4151da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 4161da177e4SLinus Torvalds /* 4171da177e4SLinus Torvalds * Moving the log is hard, need new interfaces to sync 4181da177e4SLinus Torvalds * the log first, hold off all activity while moving it. 4191da177e4SLinus Torvalds * Can have shorter or longer log in the same space, 4201da177e4SLinus Torvalds * or transform internal to external log or vice versa. 4211da177e4SLinus Torvalds */ 4221da177e4SLinus Torvalds return XFS_ERROR(ENOSYS); 4231da177e4SLinus Torvalds } 4241da177e4SLinus Torvalds 4251da177e4SLinus Torvalds /* 4261da177e4SLinus Torvalds * protected versions of growfs function acquire and release locks on the mount 4271da177e4SLinus Torvalds * point - exported through ioctls: XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG, 4281da177e4SLinus Torvalds * XFS_IOC_FSGROWFSRT 4291da177e4SLinus Torvalds */ 4301da177e4SLinus Torvalds 4311da177e4SLinus Torvalds 4321da177e4SLinus Torvalds int 4331da177e4SLinus Torvalds xfs_growfs_data( 4341da177e4SLinus Torvalds xfs_mount_t *mp, 4351da177e4SLinus Torvalds xfs_growfs_data_t *in) 4361da177e4SLinus Torvalds { 4371da177e4SLinus Torvalds int error; 438*743bb465Ssandeen@sandeen.net 439*743bb465Ssandeen@sandeen.net if (!capable(CAP_SYS_ADMIN)) 440*743bb465Ssandeen@sandeen.net return XFS_ERROR(EPERM); 441cc92e7acSChristoph Hellwig if (!mutex_trylock(&mp->m_growlock)) 4421da177e4SLinus Torvalds return XFS_ERROR(EWOULDBLOCK); 4431da177e4SLinus Torvalds error = xfs_growfs_data_private(mp, in); 444cc92e7acSChristoph Hellwig mutex_unlock(&mp->m_growlock); 4451da177e4SLinus Torvalds return error; 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds 4481da177e4SLinus Torvalds int 4491da177e4SLinus Torvalds xfs_growfs_log( 4501da177e4SLinus Torvalds xfs_mount_t *mp, 4511da177e4SLinus Torvalds xfs_growfs_log_t *in) 4521da177e4SLinus Torvalds { 4531da177e4SLinus Torvalds int error; 454*743bb465Ssandeen@sandeen.net 455*743bb465Ssandeen@sandeen.net if (!capable(CAP_SYS_ADMIN)) 456*743bb465Ssandeen@sandeen.net return XFS_ERROR(EPERM); 457cc92e7acSChristoph Hellwig if (!mutex_trylock(&mp->m_growlock)) 4581da177e4SLinus Torvalds return XFS_ERROR(EWOULDBLOCK); 4591da177e4SLinus Torvalds error = xfs_growfs_log_private(mp, in); 460cc92e7acSChristoph Hellwig mutex_unlock(&mp->m_growlock); 4611da177e4SLinus Torvalds return error; 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds 4641da177e4SLinus Torvalds /* 4651da177e4SLinus Torvalds * exported through ioctl XFS_IOC_FSCOUNTS 4661da177e4SLinus Torvalds */ 4671da177e4SLinus Torvalds 4681da177e4SLinus Torvalds int 4691da177e4SLinus Torvalds xfs_fs_counts( 4701da177e4SLinus Torvalds xfs_mount_t *mp, 4711da177e4SLinus Torvalds xfs_fsop_counts_t *cnt) 4721da177e4SLinus Torvalds { 473d4d90b57SChristoph Hellwig xfs_icsb_sync_counters(mp, XFS_ICSB_LAZY_COUNT); 4743685c2a1SEric Sandeen spin_lock(&mp->m_sb_lock); 4754be536deSDavid Chinner cnt->freedata = mp->m_sb.sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp); 4761da177e4SLinus Torvalds cnt->freertx = mp->m_sb.sb_frextents; 4771da177e4SLinus Torvalds cnt->freeino = mp->m_sb.sb_ifree; 4781da177e4SLinus Torvalds cnt->allocino = mp->m_sb.sb_icount; 4793685c2a1SEric Sandeen spin_unlock(&mp->m_sb_lock); 4801da177e4SLinus Torvalds return 0; 4811da177e4SLinus Torvalds } 4821da177e4SLinus Torvalds 4831da177e4SLinus Torvalds /* 4841da177e4SLinus Torvalds * exported through ioctl XFS_IOC_SET_RESBLKS & XFS_IOC_GET_RESBLKS 4851da177e4SLinus Torvalds * 4861da177e4SLinus Torvalds * xfs_reserve_blocks is called to set m_resblks 4871da177e4SLinus Torvalds * in the in-core mount table. The number of unused reserved blocks 488c41564b5SNathan Scott * is kept in m_resblks_avail. 4891da177e4SLinus Torvalds * 4901da177e4SLinus Torvalds * Reserve the requested number of blocks if available. Otherwise return 4911da177e4SLinus Torvalds * as many as possible to satisfy the request. The actual number 4921da177e4SLinus Torvalds * reserved are returned in outval 4931da177e4SLinus Torvalds * 4941da177e4SLinus Torvalds * A null inval pointer indicates that only the current reserved blocks 4951da177e4SLinus Torvalds * available should be returned no settings are changed. 4961da177e4SLinus Torvalds */ 4971da177e4SLinus Torvalds 4981da177e4SLinus Torvalds int 4991da177e4SLinus Torvalds xfs_reserve_blocks( 5001da177e4SLinus Torvalds xfs_mount_t *mp, 5011da177e4SLinus Torvalds __uint64_t *inval, 5021da177e4SLinus Torvalds xfs_fsop_resblks_t *outval) 5031da177e4SLinus Torvalds { 504dbcabad1SDavid Chinner __int64_t lcounter, delta, fdblks_delta; 5051da177e4SLinus Torvalds __uint64_t request; 5061da177e4SLinus Torvalds 5071da177e4SLinus Torvalds /* If inval is null, report current values and return */ 5081da177e4SLinus Torvalds if (inval == (__uint64_t *)NULL) { 50984e1e99fSDavid Chinner if (!outval) 51084e1e99fSDavid Chinner return EINVAL; 5111da177e4SLinus Torvalds outval->resblks = mp->m_resblks; 5121da177e4SLinus Torvalds outval->resblks_avail = mp->m_resblks_avail; 513014c2544SJesper Juhl return 0; 5141da177e4SLinus Torvalds } 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds request = *inval; 517dbcabad1SDavid Chinner 518dbcabad1SDavid Chinner /* 519dbcabad1SDavid Chinner * With per-cpu counters, this becomes an interesting 520dbcabad1SDavid Chinner * problem. we needto work out if we are freeing or allocation 521dbcabad1SDavid Chinner * blocks first, then we can do the modification as necessary. 522dbcabad1SDavid Chinner * 5233685c2a1SEric Sandeen * We do this under the m_sb_lock so that if we are near 524dbcabad1SDavid Chinner * ENOSPC, we will hold out any changes while we work out 525dbcabad1SDavid Chinner * what to do. This means that the amount of free space can 526dbcabad1SDavid Chinner * change while we do this, so we need to retry if we end up 527dbcabad1SDavid Chinner * trying to reserve more space than is available. 528dbcabad1SDavid Chinner * 529dbcabad1SDavid Chinner * We also use the xfs_mod_incore_sb() interface so that we 530dbcabad1SDavid Chinner * don't have to care about whether per cpu counter are 531dbcabad1SDavid Chinner * enabled, disabled or even compiled in.... 532dbcabad1SDavid Chinner */ 533dbcabad1SDavid Chinner retry: 5343685c2a1SEric Sandeen spin_lock(&mp->m_sb_lock); 535d4d90b57SChristoph Hellwig xfs_icsb_sync_counters_locked(mp, 0); 5361da177e4SLinus Torvalds 5371da177e4SLinus Torvalds /* 5381da177e4SLinus Torvalds * If our previous reservation was larger than the current value, 5391da177e4SLinus Torvalds * then move any unused blocks back to the free pool. 5401da177e4SLinus Torvalds */ 541dbcabad1SDavid Chinner fdblks_delta = 0; 5421da177e4SLinus Torvalds if (mp->m_resblks > request) { 5431da177e4SLinus Torvalds lcounter = mp->m_resblks_avail - request; 5441da177e4SLinus Torvalds if (lcounter > 0) { /* release unused blocks */ 545dbcabad1SDavid Chinner fdblks_delta = lcounter; 5461da177e4SLinus Torvalds mp->m_resblks_avail -= lcounter; 5471da177e4SLinus Torvalds } 5481da177e4SLinus Torvalds mp->m_resblks = request; 5491da177e4SLinus Torvalds } else { 5504be536deSDavid Chinner __int64_t free; 5514be536deSDavid Chinner 5524be536deSDavid Chinner free = mp->m_sb.sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp); 553dbcabad1SDavid Chinner if (!free) 554dbcabad1SDavid Chinner goto out; /* ENOSPC and fdblks_delta = 0 */ 555dbcabad1SDavid Chinner 5561da177e4SLinus Torvalds delta = request - mp->m_resblks; 5574be536deSDavid Chinner lcounter = free - delta; 5581da177e4SLinus Torvalds if (lcounter < 0) { 5591da177e4SLinus Torvalds /* We can't satisfy the request, just get what we can */ 5604be536deSDavid Chinner mp->m_resblks += free; 5614be536deSDavid Chinner mp->m_resblks_avail += free; 562dbcabad1SDavid Chinner fdblks_delta = -free; 5631da177e4SLinus Torvalds } else { 564dbcabad1SDavid Chinner fdblks_delta = -delta; 5651da177e4SLinus Torvalds mp->m_resblks = request; 5661da177e4SLinus Torvalds mp->m_resblks_avail += delta; 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds } 569dbcabad1SDavid Chinner out: 57084e1e99fSDavid Chinner if (outval) { 5711da177e4SLinus Torvalds outval->resblks = mp->m_resblks; 5721da177e4SLinus Torvalds outval->resblks_avail = mp->m_resblks_avail; 57384e1e99fSDavid Chinner } 5743685c2a1SEric Sandeen spin_unlock(&mp->m_sb_lock); 575dbcabad1SDavid Chinner 576dbcabad1SDavid Chinner if (fdblks_delta) { 577dbcabad1SDavid Chinner /* 578dbcabad1SDavid Chinner * If we are putting blocks back here, m_resblks_avail is 579dbcabad1SDavid Chinner * already at it's max so this will put it in the free pool. 580dbcabad1SDavid Chinner * 581dbcabad1SDavid Chinner * If we need space, we'll either succeed in getting it 582dbcabad1SDavid Chinner * from the free block count or we'll get an enospc. If 583dbcabad1SDavid Chinner * we get a ENOSPC, it means things changed while we were 584dbcabad1SDavid Chinner * calculating fdblks_delta and so we should try again to 585dbcabad1SDavid Chinner * see if there is anything left to reserve. 586dbcabad1SDavid Chinner * 587dbcabad1SDavid Chinner * Don't set the reserved flag here - we don't want to reserve 588dbcabad1SDavid Chinner * the extra reserve blocks from the reserve..... 589dbcabad1SDavid Chinner */ 590dbcabad1SDavid Chinner int error; 591dbcabad1SDavid Chinner error = xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS, fdblks_delta, 0); 592dbcabad1SDavid Chinner if (error == ENOSPC) 593dbcabad1SDavid Chinner goto retry; 594dbcabad1SDavid Chinner } 595014c2544SJesper Juhl return 0; 5961da177e4SLinus Torvalds } 5971da177e4SLinus Torvalds 598e13a73f0SChristoph Hellwig void 599421ad134SNathan Scott xfs_fs_log_dummy( 600421ad134SNathan Scott xfs_mount_t *mp) 601e13a73f0SChristoph Hellwig { 602e13a73f0SChristoph Hellwig xfs_trans_t *tp; 603e13a73f0SChristoph Hellwig xfs_inode_t *ip; 604e13a73f0SChristoph Hellwig 605e13a73f0SChristoph Hellwig tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1); 606e13a73f0SChristoph Hellwig if (xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0)) { 607e13a73f0SChristoph Hellwig xfs_trans_cancel(tp, 0); 608e13a73f0SChristoph Hellwig return; 609e13a73f0SChristoph Hellwig } 610e13a73f0SChristoph Hellwig 611e13a73f0SChristoph Hellwig ip = mp->m_rootip; 612e13a73f0SChristoph Hellwig xfs_ilock(ip, XFS_ILOCK_EXCL); 613e13a73f0SChristoph Hellwig 614e13a73f0SChristoph Hellwig xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); 615e13a73f0SChristoph Hellwig xfs_trans_ihold(tp, ip); 616e13a73f0SChristoph Hellwig xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); 617e13a73f0SChristoph Hellwig xfs_trans_set_sync(tp); 6181c72bf90SEric Sandeen xfs_trans_commit(tp, 0); 619e13a73f0SChristoph Hellwig 620e13a73f0SChristoph Hellwig xfs_iunlock(ip, XFS_ILOCK_EXCL); 621e13a73f0SChristoph Hellwig } 622e13a73f0SChristoph Hellwig 6231da177e4SLinus Torvalds int 6241da177e4SLinus Torvalds xfs_fs_goingdown( 6251da177e4SLinus Torvalds xfs_mount_t *mp, 6261da177e4SLinus Torvalds __uint32_t inflags) 6271da177e4SLinus Torvalds { 6281da177e4SLinus Torvalds switch (inflags) { 6291da177e4SLinus Torvalds case XFS_FSOP_GOING_FLAGS_DEFAULT: { 630b267ce99SChristoph Hellwig struct super_block *sb = freeze_bdev(mp->m_super->s_bdev); 6311da177e4SLinus Torvalds 632f33c6797SChristoph Hellwig if (sb && !IS_ERR(sb)) { 6337d04a335SNathan Scott xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT); 6341da177e4SLinus Torvalds thaw_bdev(sb->s_bdev, sb); 6351da177e4SLinus Torvalds } 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds break; 6381da177e4SLinus Torvalds } 6391da177e4SLinus Torvalds case XFS_FSOP_GOING_FLAGS_LOGFLUSH: 6407d04a335SNathan Scott xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT); 6411da177e4SLinus Torvalds break; 6421da177e4SLinus Torvalds case XFS_FSOP_GOING_FLAGS_NOLOGFLUSH: 6437d04a335SNathan Scott xfs_force_shutdown(mp, 6447d04a335SNathan Scott SHUTDOWN_FORCE_UMOUNT | SHUTDOWN_LOG_IO_ERROR); 6451da177e4SLinus Torvalds break; 6461da177e4SLinus Torvalds default: 6471da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 6481da177e4SLinus Torvalds } 6491da177e4SLinus Torvalds 6501da177e4SLinus Torvalds return 0; 6511da177e4SLinus Torvalds } 652