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 = 801da177e4SLinus Torvalds (XFS_SB_VERSION_HASATTR(&mp->m_sb) ? 811da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_ATTR : 0) | 821da177e4SLinus Torvalds (XFS_SB_VERSION_HASNLINK(&mp->m_sb) ? 831da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_NLINK : 0) | 841da177e4SLinus Torvalds (XFS_SB_VERSION_HASQUOTA(&mp->m_sb) ? 851da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_QUOTA : 0) | 861da177e4SLinus Torvalds (XFS_SB_VERSION_HASALIGN(&mp->m_sb) ? 871da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_IALIGN : 0) | 881da177e4SLinus Torvalds (XFS_SB_VERSION_HASDALIGN(&mp->m_sb) ? 891da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_DALIGN : 0) | 901da177e4SLinus Torvalds (XFS_SB_VERSION_HASSHARED(&mp->m_sb) ? 911da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_SHARED : 0) | 921da177e4SLinus Torvalds (XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) ? 931da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_EXTFLG : 0) | 941da177e4SLinus Torvalds (XFS_SB_VERSION_HASDIRV2(&mp->m_sb) ? 951da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) | 961da177e4SLinus Torvalds (XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ? 97d8cc890dSNathan Scott XFS_FSOP_GEOM_FLAGS_SECTOR : 0) | 9892821e2bSDavid Chinner (xfs_sb_version_haslazysbcount(&mp->m_sb) ? 9992821e2bSDavid Chinner XFS_FSOP_GEOM_FLAGS_LAZYSB : 0) | 100d8cc890dSNathan Scott (XFS_SB_VERSION_HASATTR2(&mp->m_sb) ? 101d8cc890dSNathan Scott XFS_FSOP_GEOM_FLAGS_ATTR2 : 0); 1021da177e4SLinus Torvalds geo->logsectsize = XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ? 1031da177e4SLinus Torvalds mp->m_sb.sb_logsectsize : BBSIZE; 1041da177e4SLinus Torvalds geo->rtsectsize = mp->m_sb.sb_blocksize; 1051da177e4SLinus Torvalds geo->dirblocksize = mp->m_dirblksize; 1061da177e4SLinus Torvalds } 1071da177e4SLinus Torvalds if (new_version >= 4) { 1081da177e4SLinus Torvalds geo->flags |= 1091da177e4SLinus Torvalds (XFS_SB_VERSION_HASLOGV2(&mp->m_sb) ? 1101da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_LOGV2 : 0); 1111da177e4SLinus Torvalds geo->logsunit = mp->m_sb.sb_logsunit; 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds return 0; 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds static int 1171da177e4SLinus Torvalds xfs_growfs_data_private( 1181da177e4SLinus Torvalds xfs_mount_t *mp, /* mount point for filesystem */ 1191da177e4SLinus Torvalds xfs_growfs_data_t *in) /* growfs data input struct */ 1201da177e4SLinus Torvalds { 1211da177e4SLinus Torvalds xfs_agf_t *agf; 1221da177e4SLinus Torvalds xfs_agi_t *agi; 1231da177e4SLinus Torvalds xfs_agnumber_t agno; 1241da177e4SLinus Torvalds xfs_extlen_t agsize; 1251da177e4SLinus Torvalds xfs_extlen_t tmpsize; 1261da177e4SLinus Torvalds xfs_alloc_rec_t *arec; 1271da177e4SLinus Torvalds xfs_btree_sblock_t *block; 1281da177e4SLinus Torvalds xfs_buf_t *bp; 1291da177e4SLinus Torvalds int bucket; 1301da177e4SLinus Torvalds int dpct; 1311da177e4SLinus Torvalds int error; 1321da177e4SLinus Torvalds xfs_agnumber_t nagcount; 1331da177e4SLinus Torvalds xfs_agnumber_t nagimax = 0; 1341da177e4SLinus Torvalds xfs_rfsblock_t nb, nb_mod; 1351da177e4SLinus Torvalds xfs_rfsblock_t new; 1361da177e4SLinus Torvalds xfs_rfsblock_t nfree; 1371da177e4SLinus Torvalds xfs_agnumber_t oagcount; 1381da177e4SLinus Torvalds int pct; 1391da177e4SLinus Torvalds xfs_trans_t *tp; 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds nb = in->newblocks; 1421da177e4SLinus Torvalds pct = in->imaxpct; 1431da177e4SLinus Torvalds if (nb < mp->m_sb.sb_dblocks || pct < 0 || pct > 100) 1441da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 1454cc929eeSNathan Scott if ((error = xfs_sb_validate_fsb_count(&mp->m_sb, nb))) 1464cc929eeSNathan Scott return error; 1471da177e4SLinus Torvalds dpct = pct - mp->m_sb.sb_imax_pct; 1481da177e4SLinus Torvalds error = xfs_read_buf(mp, mp->m_ddev_targp, 1491da177e4SLinus Torvalds XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1), 1501da177e4SLinus Torvalds XFS_FSS_TO_BB(mp, 1), 0, &bp); 1511da177e4SLinus Torvalds if (error) 1521da177e4SLinus Torvalds return error; 1531da177e4SLinus Torvalds ASSERT(bp); 1541da177e4SLinus Torvalds xfs_buf_relse(bp); 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds new = nb; /* use new as a temporary here */ 1571da177e4SLinus Torvalds nb_mod = do_div(new, mp->m_sb.sb_agblocks); 1581da177e4SLinus Torvalds nagcount = new + (nb_mod != 0); 1591da177e4SLinus Torvalds if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) { 1601da177e4SLinus Torvalds nagcount--; 1611da177e4SLinus Torvalds nb = nagcount * mp->m_sb.sb_agblocks; 1621da177e4SLinus Torvalds if (nb < mp->m_sb.sb_dblocks) 1631da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 1641da177e4SLinus Torvalds } 1651da177e4SLinus Torvalds new = nb - mp->m_sb.sb_dblocks; 1661da177e4SLinus Torvalds oagcount = mp->m_sb.sb_agcount; 1671da177e4SLinus Torvalds if (nagcount > oagcount) { 1682a82b8beSDavid Chinner xfs_filestream_flush(mp); 1691da177e4SLinus Torvalds down_write(&mp->m_peraglock); 1701da177e4SLinus Torvalds mp->m_perag = kmem_realloc(mp->m_perag, 1711da177e4SLinus Torvalds sizeof(xfs_perag_t) * nagcount, 1721da177e4SLinus Torvalds sizeof(xfs_perag_t) * oagcount, 1731da177e4SLinus Torvalds KM_SLEEP); 1741da177e4SLinus Torvalds memset(&mp->m_perag[oagcount], 0, 1751da177e4SLinus Torvalds (nagcount - oagcount) * sizeof(xfs_perag_t)); 1761da177e4SLinus Torvalds mp->m_flags |= XFS_MOUNT_32BITINODES; 177b267ce99SChristoph Hellwig nagimax = xfs_initialize_perag(mp, nagcount); 1781da177e4SLinus Torvalds up_write(&mp->m_peraglock); 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS); 18184e1e99fSDavid Chinner tp->t_flags |= XFS_TRANS_RESERVE; 1821da177e4SLinus Torvalds if ((error = xfs_trans_reserve(tp, XFS_GROWFS_SPACE_RES(mp), 1831da177e4SLinus Torvalds XFS_GROWDATA_LOG_RES(mp), 0, 0, 0))) { 1841da177e4SLinus Torvalds xfs_trans_cancel(tp, 0); 1851da177e4SLinus Torvalds return error; 1861da177e4SLinus Torvalds } 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds nfree = 0; 1891da177e4SLinus Torvalds for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) { 1901da177e4SLinus Torvalds /* 1911da177e4SLinus Torvalds * AG freelist header block 1921da177e4SLinus Torvalds */ 1931da177e4SLinus Torvalds bp = xfs_buf_get(mp->m_ddev_targp, 1941da177e4SLinus Torvalds XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)), 1951da177e4SLinus Torvalds XFS_FSS_TO_BB(mp, 1), 0); 1961da177e4SLinus Torvalds agf = XFS_BUF_TO_AGF(bp); 1971da177e4SLinus Torvalds memset(agf, 0, mp->m_sb.sb_sectsize); 19816259e7dSChristoph Hellwig agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC); 19916259e7dSChristoph Hellwig agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION); 20016259e7dSChristoph Hellwig agf->agf_seqno = cpu_to_be32(agno); 2011da177e4SLinus Torvalds if (agno == nagcount - 1) 2021da177e4SLinus Torvalds agsize = 2031da177e4SLinus Torvalds nb - 2041da177e4SLinus Torvalds (agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks); 2051da177e4SLinus Torvalds else 2061da177e4SLinus Torvalds agsize = mp->m_sb.sb_agblocks; 20716259e7dSChristoph Hellwig agf->agf_length = cpu_to_be32(agsize); 20816259e7dSChristoph Hellwig agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp)); 20916259e7dSChristoph Hellwig agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp)); 21016259e7dSChristoph Hellwig agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1); 21116259e7dSChristoph Hellwig agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1); 2121da177e4SLinus Torvalds agf->agf_flfirst = 0; 21316259e7dSChristoph Hellwig agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1); 2141da177e4SLinus Torvalds agf->agf_flcount = 0; 2151da177e4SLinus Torvalds tmpsize = agsize - XFS_PREALLOC_BLOCKS(mp); 21616259e7dSChristoph Hellwig agf->agf_freeblks = cpu_to_be32(tmpsize); 21716259e7dSChristoph Hellwig agf->agf_longest = cpu_to_be32(tmpsize); 2181da177e4SLinus Torvalds error = xfs_bwrite(mp, bp); 2191da177e4SLinus Torvalds if (error) { 2201da177e4SLinus Torvalds goto error0; 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds /* 2231da177e4SLinus Torvalds * AG inode header block 2241da177e4SLinus Torvalds */ 2251da177e4SLinus Torvalds bp = xfs_buf_get(mp->m_ddev_targp, 2261da177e4SLinus Torvalds XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)), 2271da177e4SLinus Torvalds XFS_FSS_TO_BB(mp, 1), 0); 2281da177e4SLinus Torvalds agi = XFS_BUF_TO_AGI(bp); 2291da177e4SLinus Torvalds memset(agi, 0, mp->m_sb.sb_sectsize); 23016259e7dSChristoph Hellwig agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC); 23116259e7dSChristoph Hellwig agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION); 23216259e7dSChristoph Hellwig agi->agi_seqno = cpu_to_be32(agno); 23316259e7dSChristoph Hellwig agi->agi_length = cpu_to_be32(agsize); 2341da177e4SLinus Torvalds agi->agi_count = 0; 23516259e7dSChristoph Hellwig agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp)); 23616259e7dSChristoph Hellwig agi->agi_level = cpu_to_be32(1); 2371da177e4SLinus Torvalds agi->agi_freecount = 0; 23816259e7dSChristoph Hellwig agi->agi_newino = cpu_to_be32(NULLAGINO); 23916259e7dSChristoph Hellwig agi->agi_dirino = cpu_to_be32(NULLAGINO); 2401da177e4SLinus Torvalds for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) 24116259e7dSChristoph Hellwig agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO); 2421da177e4SLinus Torvalds error = xfs_bwrite(mp, bp); 2431da177e4SLinus Torvalds if (error) { 2441da177e4SLinus Torvalds goto error0; 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds /* 2471da177e4SLinus Torvalds * BNO btree root block 2481da177e4SLinus Torvalds */ 2491da177e4SLinus Torvalds bp = xfs_buf_get(mp->m_ddev_targp, 2501da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)), 2511da177e4SLinus Torvalds BTOBB(mp->m_sb.sb_blocksize), 0); 2521da177e4SLinus Torvalds block = XFS_BUF_TO_SBLOCK(bp); 2531da177e4SLinus Torvalds memset(block, 0, mp->m_sb.sb_blocksize); 25416259e7dSChristoph Hellwig block->bb_magic = cpu_to_be32(XFS_ABTB_MAGIC); 2551da177e4SLinus Torvalds block->bb_level = 0; 25616259e7dSChristoph Hellwig block->bb_numrecs = cpu_to_be16(1); 25716259e7dSChristoph Hellwig block->bb_leftsib = cpu_to_be32(NULLAGBLOCK); 25816259e7dSChristoph Hellwig block->bb_rightsib = cpu_to_be32(NULLAGBLOCK); 2592c36ddedSEric Sandeen arec = XFS_BTREE_REC_ADDR(xfs_alloc, block, 1); 26016259e7dSChristoph Hellwig arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp)); 26116259e7dSChristoph Hellwig arec->ar_blockcount = cpu_to_be32( 26216259e7dSChristoph Hellwig agsize - be32_to_cpu(arec->ar_startblock)); 2631da177e4SLinus Torvalds error = xfs_bwrite(mp, bp); 2641da177e4SLinus Torvalds if (error) { 2651da177e4SLinus Torvalds goto error0; 2661da177e4SLinus Torvalds } 2671da177e4SLinus Torvalds /* 2681da177e4SLinus Torvalds * CNT btree root block 2691da177e4SLinus Torvalds */ 2701da177e4SLinus Torvalds bp = xfs_buf_get(mp->m_ddev_targp, 2711da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)), 2721da177e4SLinus Torvalds BTOBB(mp->m_sb.sb_blocksize), 0); 2731da177e4SLinus Torvalds block = XFS_BUF_TO_SBLOCK(bp); 2741da177e4SLinus Torvalds memset(block, 0, mp->m_sb.sb_blocksize); 27516259e7dSChristoph Hellwig block->bb_magic = cpu_to_be32(XFS_ABTC_MAGIC); 2761da177e4SLinus Torvalds block->bb_level = 0; 27716259e7dSChristoph Hellwig block->bb_numrecs = cpu_to_be16(1); 27816259e7dSChristoph Hellwig block->bb_leftsib = cpu_to_be32(NULLAGBLOCK); 27916259e7dSChristoph Hellwig block->bb_rightsib = cpu_to_be32(NULLAGBLOCK); 2802c36ddedSEric Sandeen arec = XFS_BTREE_REC_ADDR(xfs_alloc, block, 1); 28116259e7dSChristoph Hellwig arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp)); 28216259e7dSChristoph Hellwig arec->ar_blockcount = cpu_to_be32( 28316259e7dSChristoph Hellwig agsize - be32_to_cpu(arec->ar_startblock)); 28416259e7dSChristoph Hellwig nfree += be32_to_cpu(arec->ar_blockcount); 2851da177e4SLinus Torvalds error = xfs_bwrite(mp, bp); 2861da177e4SLinus Torvalds if (error) { 2871da177e4SLinus Torvalds goto error0; 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds /* 2901da177e4SLinus Torvalds * INO btree root block 2911da177e4SLinus Torvalds */ 2921da177e4SLinus Torvalds bp = xfs_buf_get(mp->m_ddev_targp, 2931da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)), 2941da177e4SLinus Torvalds BTOBB(mp->m_sb.sb_blocksize), 0); 2951da177e4SLinus Torvalds block = XFS_BUF_TO_SBLOCK(bp); 2961da177e4SLinus Torvalds memset(block, 0, mp->m_sb.sb_blocksize); 29716259e7dSChristoph Hellwig block->bb_magic = cpu_to_be32(XFS_IBT_MAGIC); 2981da177e4SLinus Torvalds block->bb_level = 0; 2991da177e4SLinus Torvalds block->bb_numrecs = 0; 30016259e7dSChristoph Hellwig block->bb_leftsib = cpu_to_be32(NULLAGBLOCK); 30116259e7dSChristoph Hellwig block->bb_rightsib = cpu_to_be32(NULLAGBLOCK); 3021da177e4SLinus Torvalds error = xfs_bwrite(mp, bp); 3031da177e4SLinus Torvalds if (error) { 3041da177e4SLinus Torvalds goto error0; 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds xfs_trans_agblocks_delta(tp, nfree); 3081da177e4SLinus Torvalds /* 3091da177e4SLinus Torvalds * There are new blocks in the old last a.g. 3101da177e4SLinus Torvalds */ 3111da177e4SLinus Torvalds if (new) { 3121da177e4SLinus Torvalds /* 3131da177e4SLinus Torvalds * Change the agi length. 3141da177e4SLinus Torvalds */ 3151da177e4SLinus Torvalds error = xfs_ialloc_read_agi(mp, tp, agno, &bp); 3161da177e4SLinus Torvalds if (error) { 3171da177e4SLinus Torvalds goto error0; 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds ASSERT(bp); 3201da177e4SLinus Torvalds agi = XFS_BUF_TO_AGI(bp); 32116259e7dSChristoph Hellwig be32_add(&agi->agi_length, new); 3221da177e4SLinus Torvalds ASSERT(nagcount == oagcount || 32316259e7dSChristoph Hellwig be32_to_cpu(agi->agi_length) == mp->m_sb.sb_agblocks); 3241da177e4SLinus Torvalds xfs_ialloc_log_agi(tp, bp, XFS_AGI_LENGTH); 3251da177e4SLinus Torvalds /* 3261da177e4SLinus Torvalds * Change agf length. 3271da177e4SLinus Torvalds */ 3281da177e4SLinus Torvalds error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp); 3291da177e4SLinus Torvalds if (error) { 3301da177e4SLinus Torvalds goto error0; 3311da177e4SLinus Torvalds } 3321da177e4SLinus Torvalds ASSERT(bp); 3331da177e4SLinus Torvalds agf = XFS_BUF_TO_AGF(bp); 33416259e7dSChristoph Hellwig be32_add(&agf->agf_length, new); 33516259e7dSChristoph Hellwig ASSERT(be32_to_cpu(agf->agf_length) == 33616259e7dSChristoph Hellwig be32_to_cpu(agi->agi_length)); 3370164af51STim Shimmin xfs_alloc_log_agf(tp, bp, XFS_AGF_LENGTH); 3381da177e4SLinus Torvalds /* 3391da177e4SLinus Torvalds * Free the new space. 3401da177e4SLinus Torvalds */ 3411da177e4SLinus Torvalds error = xfs_free_extent(tp, XFS_AGB_TO_FSB(mp, agno, 34216259e7dSChristoph Hellwig be32_to_cpu(agf->agf_length) - new), new); 3431da177e4SLinus Torvalds if (error) { 3441da177e4SLinus Torvalds goto error0; 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds if (nagcount > oagcount) 3481da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount); 3491da177e4SLinus Torvalds if (nb > mp->m_sb.sb_dblocks) 3501da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, 3511da177e4SLinus Torvalds nb - mp->m_sb.sb_dblocks); 3521da177e4SLinus Torvalds if (nfree) 3531da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree); 3541da177e4SLinus Torvalds if (dpct) 3551da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct); 3561c72bf90SEric Sandeen error = xfs_trans_commit(tp, 0); 3571da177e4SLinus Torvalds if (error) { 3581da177e4SLinus Torvalds return error; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds /* New allocation groups fully initialized, so update mount struct */ 3611da177e4SLinus Torvalds if (nagimax) 3621da177e4SLinus Torvalds mp->m_maxagi = nagimax; 3631da177e4SLinus Torvalds if (mp->m_sb.sb_imax_pct) { 3641da177e4SLinus Torvalds __uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct; 3651da177e4SLinus Torvalds do_div(icount, 100); 3661da177e4SLinus Torvalds mp->m_maxicount = icount << mp->m_sb.sb_inopblog; 3671da177e4SLinus Torvalds } else 3681da177e4SLinus Torvalds mp->m_maxicount = 0; 3691da177e4SLinus Torvalds for (agno = 1; agno < nagcount; agno++) { 3701da177e4SLinus Torvalds error = xfs_read_buf(mp, mp->m_ddev_targp, 3711da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)), 3721da177e4SLinus Torvalds XFS_FSS_TO_BB(mp, 1), 0, &bp); 3731da177e4SLinus Torvalds if (error) { 3741da177e4SLinus Torvalds xfs_fs_cmn_err(CE_WARN, mp, 3751da177e4SLinus Torvalds "error %d reading secondary superblock for ag %d", 3761da177e4SLinus Torvalds error, agno); 3771da177e4SLinus Torvalds break; 3781da177e4SLinus Torvalds } 3792bdf7cd0SChristoph Hellwig xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb, XFS_SB_ALL_BITS); 3801da177e4SLinus Torvalds /* 3811da177e4SLinus Torvalds * If we get an error writing out the alternate superblocks, 3821da177e4SLinus Torvalds * just issue a warning and continue. The real work is 3831da177e4SLinus Torvalds * already done and committed. 3841da177e4SLinus Torvalds */ 3851da177e4SLinus Torvalds if (!(error = xfs_bwrite(mp, bp))) { 3861da177e4SLinus Torvalds continue; 3871da177e4SLinus Torvalds } else { 3881da177e4SLinus Torvalds xfs_fs_cmn_err(CE_WARN, mp, 3891da177e4SLinus Torvalds "write error %d updating secondary superblock for ag %d", 3901da177e4SLinus Torvalds error, agno); 3911da177e4SLinus Torvalds break; /* no point in continuing */ 3921da177e4SLinus Torvalds } 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds return 0; 3951da177e4SLinus Torvalds 3961da177e4SLinus Torvalds error0: 3971da177e4SLinus Torvalds xfs_trans_cancel(tp, XFS_TRANS_ABORT); 3981da177e4SLinus Torvalds return error; 3991da177e4SLinus Torvalds } 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds static int 4021da177e4SLinus Torvalds xfs_growfs_log_private( 4031da177e4SLinus Torvalds xfs_mount_t *mp, /* mount point for filesystem */ 4041da177e4SLinus Torvalds xfs_growfs_log_t *in) /* growfs log input struct */ 4051da177e4SLinus Torvalds { 4061da177e4SLinus Torvalds xfs_extlen_t nb; 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds nb = in->newblocks; 4091da177e4SLinus Torvalds if (nb < XFS_MIN_LOG_BLOCKS || nb < XFS_B_TO_FSB(mp, XFS_MIN_LOG_BYTES)) 4101da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 4111da177e4SLinus Torvalds if (nb == mp->m_sb.sb_logblocks && 4121da177e4SLinus Torvalds in->isint == (mp->m_sb.sb_logstart != 0)) 4131da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 4141da177e4SLinus Torvalds /* 4151da177e4SLinus Torvalds * Moving the log is hard, need new interfaces to sync 4161da177e4SLinus Torvalds * the log first, hold off all activity while moving it. 4171da177e4SLinus Torvalds * Can have shorter or longer log in the same space, 4181da177e4SLinus Torvalds * or transform internal to external log or vice versa. 4191da177e4SLinus Torvalds */ 4201da177e4SLinus Torvalds return XFS_ERROR(ENOSYS); 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds /* 4241da177e4SLinus Torvalds * protected versions of growfs function acquire and release locks on the mount 4251da177e4SLinus Torvalds * point - exported through ioctls: XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG, 4261da177e4SLinus Torvalds * XFS_IOC_FSGROWFSRT 4271da177e4SLinus Torvalds */ 4281da177e4SLinus Torvalds 4291da177e4SLinus Torvalds 4301da177e4SLinus Torvalds int 4311da177e4SLinus Torvalds xfs_growfs_data( 4321da177e4SLinus Torvalds xfs_mount_t *mp, 4331da177e4SLinus Torvalds xfs_growfs_data_t *in) 4341da177e4SLinus Torvalds { 4351da177e4SLinus Torvalds int error; 436*cc92e7acSChristoph Hellwig if (!mutex_trylock(&mp->m_growlock)) 4371da177e4SLinus Torvalds return XFS_ERROR(EWOULDBLOCK); 4381da177e4SLinus Torvalds error = xfs_growfs_data_private(mp, in); 439*cc92e7acSChristoph Hellwig mutex_unlock(&mp->m_growlock); 4401da177e4SLinus Torvalds return error; 4411da177e4SLinus Torvalds } 4421da177e4SLinus Torvalds 4431da177e4SLinus Torvalds int 4441da177e4SLinus Torvalds xfs_growfs_log( 4451da177e4SLinus Torvalds xfs_mount_t *mp, 4461da177e4SLinus Torvalds xfs_growfs_log_t *in) 4471da177e4SLinus Torvalds { 4481da177e4SLinus Torvalds int error; 449*cc92e7acSChristoph Hellwig if (!mutex_trylock(&mp->m_growlock)) 4501da177e4SLinus Torvalds return XFS_ERROR(EWOULDBLOCK); 4511da177e4SLinus Torvalds error = xfs_growfs_log_private(mp, in); 452*cc92e7acSChristoph Hellwig mutex_unlock(&mp->m_growlock); 4531da177e4SLinus Torvalds return error; 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds /* 4571da177e4SLinus Torvalds * exported through ioctl XFS_IOC_FSCOUNTS 4581da177e4SLinus Torvalds */ 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds int 4611da177e4SLinus Torvalds xfs_fs_counts( 4621da177e4SLinus Torvalds xfs_mount_t *mp, 4631da177e4SLinus Torvalds xfs_fsop_counts_t *cnt) 4641da177e4SLinus Torvalds { 4651da177e4SLinus Torvalds unsigned long s; 4661da177e4SLinus Torvalds 467dbcabad1SDavid Chinner xfs_icsb_sync_counters_flags(mp, XFS_ICSB_LAZY_COUNT); 4681da177e4SLinus Torvalds s = XFS_SB_LOCK(mp); 4694be536deSDavid Chinner cnt->freedata = mp->m_sb.sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp); 4701da177e4SLinus Torvalds cnt->freertx = mp->m_sb.sb_frextents; 4711da177e4SLinus Torvalds cnt->freeino = mp->m_sb.sb_ifree; 4721da177e4SLinus Torvalds cnt->allocino = mp->m_sb.sb_icount; 4731da177e4SLinus Torvalds XFS_SB_UNLOCK(mp, s); 4741da177e4SLinus Torvalds return 0; 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds 4771da177e4SLinus Torvalds /* 4781da177e4SLinus Torvalds * exported through ioctl XFS_IOC_SET_RESBLKS & XFS_IOC_GET_RESBLKS 4791da177e4SLinus Torvalds * 4801da177e4SLinus Torvalds * xfs_reserve_blocks is called to set m_resblks 4811da177e4SLinus Torvalds * in the in-core mount table. The number of unused reserved blocks 482c41564b5SNathan Scott * is kept in m_resblks_avail. 4831da177e4SLinus Torvalds * 4841da177e4SLinus Torvalds * Reserve the requested number of blocks if available. Otherwise return 4851da177e4SLinus Torvalds * as many as possible to satisfy the request. The actual number 4861da177e4SLinus Torvalds * reserved are returned in outval 4871da177e4SLinus Torvalds * 4881da177e4SLinus Torvalds * A null inval pointer indicates that only the current reserved blocks 4891da177e4SLinus Torvalds * available should be returned no settings are changed. 4901da177e4SLinus Torvalds */ 4911da177e4SLinus Torvalds 4921da177e4SLinus Torvalds int 4931da177e4SLinus Torvalds xfs_reserve_blocks( 4941da177e4SLinus Torvalds xfs_mount_t *mp, 4951da177e4SLinus Torvalds __uint64_t *inval, 4961da177e4SLinus Torvalds xfs_fsop_resblks_t *outval) 4971da177e4SLinus Torvalds { 498dbcabad1SDavid Chinner __int64_t lcounter, delta, fdblks_delta; 4991da177e4SLinus Torvalds __uint64_t request; 5001da177e4SLinus Torvalds unsigned long s; 5011da177e4SLinus Torvalds 5021da177e4SLinus Torvalds /* If inval is null, report current values and return */ 5031da177e4SLinus Torvalds if (inval == (__uint64_t *)NULL) { 50484e1e99fSDavid Chinner if (!outval) 50584e1e99fSDavid Chinner return EINVAL; 5061da177e4SLinus Torvalds outval->resblks = mp->m_resblks; 5071da177e4SLinus Torvalds outval->resblks_avail = mp->m_resblks_avail; 508014c2544SJesper Juhl return 0; 5091da177e4SLinus Torvalds } 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds request = *inval; 512dbcabad1SDavid Chinner 513dbcabad1SDavid Chinner /* 514dbcabad1SDavid Chinner * With per-cpu counters, this becomes an interesting 515dbcabad1SDavid Chinner * problem. we needto work out if we are freeing or allocation 516dbcabad1SDavid Chinner * blocks first, then we can do the modification as necessary. 517dbcabad1SDavid Chinner * 518dbcabad1SDavid Chinner * We do this under the XFS_SB_LOCK so that if we are near 519dbcabad1SDavid Chinner * ENOSPC, we will hold out any changes while we work out 520dbcabad1SDavid Chinner * what to do. This means that the amount of free space can 521dbcabad1SDavid Chinner * change while we do this, so we need to retry if we end up 522dbcabad1SDavid Chinner * trying to reserve more space than is available. 523dbcabad1SDavid Chinner * 524dbcabad1SDavid Chinner * We also use the xfs_mod_incore_sb() interface so that we 525dbcabad1SDavid Chinner * don't have to care about whether per cpu counter are 526dbcabad1SDavid Chinner * enabled, disabled or even compiled in.... 527dbcabad1SDavid Chinner */ 528dbcabad1SDavid Chinner retry: 5291da177e4SLinus Torvalds s = XFS_SB_LOCK(mp); 530dbcabad1SDavid Chinner xfs_icsb_sync_counters_flags(mp, XFS_ICSB_SB_LOCKED); 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds /* 5331da177e4SLinus Torvalds * If our previous reservation was larger than the current value, 5341da177e4SLinus Torvalds * then move any unused blocks back to the free pool. 5351da177e4SLinus Torvalds */ 536dbcabad1SDavid Chinner fdblks_delta = 0; 5371da177e4SLinus Torvalds if (mp->m_resblks > request) { 5381da177e4SLinus Torvalds lcounter = mp->m_resblks_avail - request; 5391da177e4SLinus Torvalds if (lcounter > 0) { /* release unused blocks */ 540dbcabad1SDavid Chinner fdblks_delta = lcounter; 5411da177e4SLinus Torvalds mp->m_resblks_avail -= lcounter; 5421da177e4SLinus Torvalds } 5431da177e4SLinus Torvalds mp->m_resblks = request; 5441da177e4SLinus Torvalds } else { 5454be536deSDavid Chinner __int64_t free; 5464be536deSDavid Chinner 5474be536deSDavid Chinner free = mp->m_sb.sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp); 548dbcabad1SDavid Chinner if (!free) 549dbcabad1SDavid Chinner goto out; /* ENOSPC and fdblks_delta = 0 */ 550dbcabad1SDavid Chinner 5511da177e4SLinus Torvalds delta = request - mp->m_resblks; 5524be536deSDavid Chinner lcounter = free - delta; 5531da177e4SLinus Torvalds if (lcounter < 0) { 5541da177e4SLinus Torvalds /* We can't satisfy the request, just get what we can */ 5554be536deSDavid Chinner mp->m_resblks += free; 5564be536deSDavid Chinner mp->m_resblks_avail += free; 557dbcabad1SDavid Chinner fdblks_delta = -free; 5584be536deSDavid Chinner mp->m_sb.sb_fdblocks = XFS_ALLOC_SET_ASIDE(mp); 5591da177e4SLinus Torvalds } else { 560dbcabad1SDavid Chinner fdblks_delta = -delta; 5614be536deSDavid Chinner mp->m_sb.sb_fdblocks = 5624be536deSDavid Chinner lcounter + XFS_ALLOC_SET_ASIDE(mp); 5631da177e4SLinus Torvalds mp->m_resblks = request; 5641da177e4SLinus Torvalds mp->m_resblks_avail += delta; 5651da177e4SLinus Torvalds } 5661da177e4SLinus Torvalds } 567dbcabad1SDavid Chinner out: 56884e1e99fSDavid Chinner if (outval) { 5691da177e4SLinus Torvalds outval->resblks = mp->m_resblks; 5701da177e4SLinus Torvalds outval->resblks_avail = mp->m_resblks_avail; 57184e1e99fSDavid Chinner } 5721da177e4SLinus Torvalds XFS_SB_UNLOCK(mp, s); 573dbcabad1SDavid Chinner 574dbcabad1SDavid Chinner if (fdblks_delta) { 575dbcabad1SDavid Chinner /* 576dbcabad1SDavid Chinner * If we are putting blocks back here, m_resblks_avail is 577dbcabad1SDavid Chinner * already at it's max so this will put it in the free pool. 578dbcabad1SDavid Chinner * 579dbcabad1SDavid Chinner * If we need space, we'll either succeed in getting it 580dbcabad1SDavid Chinner * from the free block count or we'll get an enospc. If 581dbcabad1SDavid Chinner * we get a ENOSPC, it means things changed while we were 582dbcabad1SDavid Chinner * calculating fdblks_delta and so we should try again to 583dbcabad1SDavid Chinner * see if there is anything left to reserve. 584dbcabad1SDavid Chinner * 585dbcabad1SDavid Chinner * Don't set the reserved flag here - we don't want to reserve 586dbcabad1SDavid Chinner * the extra reserve blocks from the reserve..... 587dbcabad1SDavid Chinner */ 588dbcabad1SDavid Chinner int error; 589dbcabad1SDavid Chinner error = xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS, fdblks_delta, 0); 590dbcabad1SDavid Chinner if (error == ENOSPC) 591dbcabad1SDavid Chinner goto retry; 592dbcabad1SDavid Chinner } 593dbcabad1SDavid Chinner 594014c2544SJesper Juhl return 0; 5951da177e4SLinus Torvalds } 5961da177e4SLinus Torvalds 597e13a73f0SChristoph Hellwig void 598421ad134SNathan Scott xfs_fs_log_dummy( 599421ad134SNathan Scott xfs_mount_t *mp) 600e13a73f0SChristoph Hellwig { 601e13a73f0SChristoph Hellwig xfs_trans_t *tp; 602e13a73f0SChristoph Hellwig xfs_inode_t *ip; 603e13a73f0SChristoph Hellwig 604e13a73f0SChristoph Hellwig tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1); 605e13a73f0SChristoph Hellwig if (xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0)) { 606e13a73f0SChristoph Hellwig xfs_trans_cancel(tp, 0); 607e13a73f0SChristoph Hellwig return; 608e13a73f0SChristoph Hellwig } 609e13a73f0SChristoph Hellwig 610e13a73f0SChristoph Hellwig ip = mp->m_rootip; 611e13a73f0SChristoph Hellwig xfs_ilock(ip, XFS_ILOCK_EXCL); 612e13a73f0SChristoph Hellwig 613e13a73f0SChristoph Hellwig xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); 614e13a73f0SChristoph Hellwig xfs_trans_ihold(tp, ip); 615e13a73f0SChristoph Hellwig xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); 616e13a73f0SChristoph Hellwig xfs_trans_set_sync(tp); 6171c72bf90SEric Sandeen xfs_trans_commit(tp, 0); 618e13a73f0SChristoph Hellwig 619e13a73f0SChristoph Hellwig xfs_iunlock(ip, XFS_ILOCK_EXCL); 620e13a73f0SChristoph Hellwig } 621e13a73f0SChristoph Hellwig 6221da177e4SLinus Torvalds int 6231da177e4SLinus Torvalds xfs_fs_goingdown( 6241da177e4SLinus Torvalds xfs_mount_t *mp, 6251da177e4SLinus Torvalds __uint32_t inflags) 6261da177e4SLinus Torvalds { 6271da177e4SLinus Torvalds switch (inflags) { 6281da177e4SLinus Torvalds case XFS_FSOP_GOING_FLAGS_DEFAULT: { 629b267ce99SChristoph Hellwig struct super_block *sb = freeze_bdev(mp->m_super->s_bdev); 6301da177e4SLinus Torvalds 631f33c6797SChristoph Hellwig if (sb && !IS_ERR(sb)) { 6327d04a335SNathan Scott xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT); 6331da177e4SLinus Torvalds thaw_bdev(sb->s_bdev, sb); 6341da177e4SLinus Torvalds } 6351da177e4SLinus Torvalds 6361da177e4SLinus Torvalds break; 6371da177e4SLinus Torvalds } 6381da177e4SLinus Torvalds case XFS_FSOP_GOING_FLAGS_LOGFLUSH: 6397d04a335SNathan Scott xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT); 6401da177e4SLinus Torvalds break; 6411da177e4SLinus Torvalds case XFS_FSOP_GOING_FLAGS_NOLOGFLUSH: 6427d04a335SNathan Scott xfs_force_shutdown(mp, 6437d04a335SNathan Scott SHUTDOWN_FORCE_UMOUNT | SHUTDOWN_LOG_IO_ERROR); 6441da177e4SLinus Torvalds break; 6451da177e4SLinus Torvalds default: 6461da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 6471da177e4SLinus Torvalds } 6481da177e4SLinus Torvalds 6491da177e4SLinus Torvalds return 0; 6501da177e4SLinus Torvalds } 651