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" 2070a9883cSDave Chinner #include "xfs_shared.h" 21*239880efSDave Chinner #include "xfs_log_format.h" 22*239880efSDave Chinner #include "xfs_format.h" 23*239880efSDave Chinner #include "xfs_trans_resv.h" 241da177e4SLinus Torvalds #include "xfs_sb.h" 25a844f451SNathan Scott #include "xfs_ag.h" 261da177e4SLinus Torvalds #include "xfs_mount.h" 271da177e4SLinus Torvalds #include "xfs_bmap_btree.h" 28a844f451SNathan Scott #include "xfs_alloc_btree.h" 291da177e4SLinus Torvalds #include "xfs_ialloc_btree.h" 30a844f451SNathan Scott #include "xfs_dinode.h" 31a844f451SNathan Scott #include "xfs_inode.h" 32*239880efSDave Chinner #include "xfs_trans.h" 33a844f451SNathan Scott #include "xfs_inode_item.h" 341da177e4SLinus Torvalds #include "xfs_btree.h" 351da177e4SLinus Torvalds #include "xfs_error.h" 361da177e4SLinus Torvalds #include "xfs_alloc.h" 371da177e4SLinus Torvalds #include "xfs_ialloc.h" 381da177e4SLinus Torvalds #include "xfs_fsops.h" 391da177e4SLinus Torvalds #include "xfs_itable.h" 401da177e4SLinus Torvalds #include "xfs_trans_space.h" 411da177e4SLinus Torvalds #include "xfs_rtalloc.h" 422a82b8beSDavid Chinner #include "xfs_filestream.h" 430b1b213fSChristoph Hellwig #include "xfs_trace.h" 44*239880efSDave Chinner #include "xfs_log.h" 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds /* 471da177e4SLinus Torvalds * File system operations 481da177e4SLinus Torvalds */ 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds int 511da177e4SLinus Torvalds xfs_fs_geometry( 521da177e4SLinus Torvalds xfs_mount_t *mp, 531da177e4SLinus Torvalds xfs_fsop_geom_t *geo, 541da177e4SLinus Torvalds int new_version) 551da177e4SLinus Torvalds { 56c4d0c3b0SDan Rosenberg 57c4d0c3b0SDan Rosenberg memset(geo, 0, sizeof(*geo)); 58c4d0c3b0SDan Rosenberg 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) ? 10369a58a43SEric Sandeen XFS_FSOP_GEOM_FLAGS_ATTR2 : 0) | 10469a58a43SEric Sandeen (xfs_sb_version_hasprojid32bit(&mp->m_sb) ? 10574137fffSDave Chinner XFS_FSOP_GEOM_FLAGS_PROJID32 : 0) | 10674137fffSDave Chinner (xfs_sb_version_hascrc(&mp->m_sb) ? 1072900a579SMark Tinguely XFS_FSOP_GEOM_FLAGS_V5SB : 0) | 1082900a579SMark Tinguely (xfs_sb_version_hasftype(&mp->m_sb) ? 1092900a579SMark Tinguely XFS_FSOP_GEOM_FLAGS_FTYPE : 0); 11062118709SEric Sandeen geo->logsectsize = xfs_sb_version_hassector(&mp->m_sb) ? 1111da177e4SLinus Torvalds mp->m_sb.sb_logsectsize : BBSIZE; 1121da177e4SLinus Torvalds geo->rtsectsize = mp->m_sb.sb_blocksize; 1131da177e4SLinus Torvalds geo->dirblocksize = mp->m_dirblksize; 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds if (new_version >= 4) { 1161da177e4SLinus Torvalds geo->flags |= 11762118709SEric Sandeen (xfs_sb_version_haslogv2(&mp->m_sb) ? 1181da177e4SLinus Torvalds XFS_FSOP_GEOM_FLAGS_LOGV2 : 0); 1191da177e4SLinus Torvalds geo->logsunit = mp->m_sb.sb_logsunit; 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds return 0; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 124fd23683cSDave Chinner static struct xfs_buf * 125fd23683cSDave Chinner xfs_growfs_get_hdr_buf( 126fd23683cSDave Chinner struct xfs_mount *mp, 127fd23683cSDave Chinner xfs_daddr_t blkno, 128fd23683cSDave Chinner size_t numblks, 1291813dd64SDave Chinner int flags, 1301813dd64SDave Chinner const struct xfs_buf_ops *ops) 131fd23683cSDave Chinner { 132fd23683cSDave Chinner struct xfs_buf *bp; 133fd23683cSDave Chinner 134fd23683cSDave Chinner bp = xfs_buf_get_uncached(mp->m_ddev_targp, numblks, flags); 135fd23683cSDave Chinner if (!bp) 136fd23683cSDave Chinner return NULL; 137fd23683cSDave Chinner 138fd23683cSDave Chinner xfs_buf_zero(bp, 0, BBTOB(bp->b_length)); 139fd23683cSDave Chinner bp->b_bn = blkno; 140fd23683cSDave Chinner bp->b_maps[0].bm_bn = blkno; 1411813dd64SDave Chinner bp->b_ops = ops; 142fd23683cSDave Chinner 143fd23683cSDave Chinner return bp; 144fd23683cSDave Chinner } 145fd23683cSDave Chinner 1461da177e4SLinus Torvalds static int 1471da177e4SLinus Torvalds xfs_growfs_data_private( 1481da177e4SLinus Torvalds xfs_mount_t *mp, /* mount point for filesystem */ 1491da177e4SLinus Torvalds xfs_growfs_data_t *in) /* growfs data input struct */ 1501da177e4SLinus Torvalds { 1511da177e4SLinus Torvalds xfs_agf_t *agf; 152de497688SDave Chinner struct xfs_agfl *agfl; 1531da177e4SLinus Torvalds xfs_agi_t *agi; 1541da177e4SLinus Torvalds xfs_agnumber_t agno; 1551da177e4SLinus Torvalds xfs_extlen_t agsize; 1561da177e4SLinus Torvalds xfs_extlen_t tmpsize; 1571da177e4SLinus Torvalds xfs_alloc_rec_t *arec; 1581da177e4SLinus Torvalds xfs_buf_t *bp; 1591da177e4SLinus Torvalds int bucket; 1601da177e4SLinus Torvalds int dpct; 16159e5a0e8SEric Sandeen int error, saved_error = 0; 1621da177e4SLinus Torvalds xfs_agnumber_t nagcount; 1631da177e4SLinus Torvalds xfs_agnumber_t nagimax = 0; 1641da177e4SLinus Torvalds xfs_rfsblock_t nb, nb_mod; 1651da177e4SLinus Torvalds xfs_rfsblock_t new; 1661da177e4SLinus Torvalds xfs_rfsblock_t nfree; 1671da177e4SLinus Torvalds xfs_agnumber_t oagcount; 1681da177e4SLinus Torvalds int pct; 1691da177e4SLinus Torvalds xfs_trans_t *tp; 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds nb = in->newblocks; 1721da177e4SLinus Torvalds pct = in->imaxpct; 1731da177e4SLinus Torvalds if (nb < mp->m_sb.sb_dblocks || pct < 0 || pct > 100) 1741da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 1754cc929eeSNathan Scott if ((error = xfs_sb_validate_fsb_count(&mp->m_sb, nb))) 1764cc929eeSNathan Scott return error; 1771da177e4SLinus Torvalds dpct = pct - mp->m_sb.sb_imax_pct; 178e70b73f8SDave Chinner bp = xfs_buf_read_uncached(mp->m_ddev_targp, 1791da177e4SLinus Torvalds XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1), 180c3f8fc73SDave Chinner XFS_FSS_TO_BB(mp, 1), 0, NULL); 1811922c949SDave Chinner if (!bp) 1821922c949SDave Chinner return EIO; 183eab4e633SDave Chinner if (bp->b_error) { 184897366f0SJie Liu error = bp->b_error; 185eab4e633SDave Chinner xfs_buf_relse(bp); 186eab4e633SDave Chinner return error; 187eab4e633SDave Chinner } 1881da177e4SLinus Torvalds xfs_buf_relse(bp); 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds new = nb; /* use new as a temporary here */ 1911da177e4SLinus Torvalds nb_mod = do_div(new, mp->m_sb.sb_agblocks); 1921da177e4SLinus Torvalds nagcount = new + (nb_mod != 0); 1931da177e4SLinus Torvalds if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) { 1941da177e4SLinus Torvalds nagcount--; 195e6da7c9fSEric Sandeen nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks; 1961da177e4SLinus Torvalds if (nb < mp->m_sb.sb_dblocks) 1971da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds new = nb - mp->m_sb.sb_dblocks; 2001da177e4SLinus Torvalds oagcount = mp->m_sb.sb_agcount; 2011c1c6ebcSDave Chinner 2021c1c6ebcSDave Chinner /* allocate the new per-ag structures */ 2031da177e4SLinus Torvalds if (nagcount > oagcount) { 2041c1c6ebcSDave Chinner error = xfs_initialize_perag(mp, nagcount, &nagimax); 2051c1c6ebcSDave Chinner if (error) 2061c1c6ebcSDave Chinner return error; 2071da177e4SLinus Torvalds } 2081c1c6ebcSDave Chinner 2091da177e4SLinus Torvalds tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS); 21084e1e99fSDavid Chinner tp->t_flags |= XFS_TRANS_RESERVE; 2113d3c8b52SJie Liu error = xfs_trans_reserve(tp, &M_RES(mp)->tr_growdata, 2123d3c8b52SJie Liu XFS_GROWFS_SPACE_RES(mp), 0); 2133d3c8b52SJie Liu if (error) { 2141da177e4SLinus Torvalds xfs_trans_cancel(tp, 0); 2151da177e4SLinus Torvalds return error; 2161da177e4SLinus Torvalds } 2171da177e4SLinus Torvalds 2181c1c6ebcSDave Chinner /* 2191c1c6ebcSDave Chinner * Write new AG headers to disk. Non-transactional, but written 2201c1c6ebcSDave Chinner * synchronously so they are completed prior to the growfs transaction 2211c1c6ebcSDave Chinner * being logged. 2221c1c6ebcSDave Chinner */ 2231da177e4SLinus Torvalds nfree = 0; 2241da177e4SLinus Torvalds for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) { 2251da177e4SLinus Torvalds /* 226de497688SDave Chinner * AG freespace header block 2271da177e4SLinus Torvalds */ 228fd23683cSDave Chinner bp = xfs_growfs_get_hdr_buf(mp, 2291da177e4SLinus Torvalds XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)), 2301813dd64SDave Chinner XFS_FSS_TO_BB(mp, 1), 0, 2311813dd64SDave Chinner &xfs_agf_buf_ops); 232b522950fSChandra Seetharaman if (!bp) { 233b522950fSChandra Seetharaman error = ENOMEM; 234b522950fSChandra Seetharaman goto error0; 235b522950fSChandra Seetharaman } 236fd23683cSDave Chinner 2371da177e4SLinus Torvalds agf = XFS_BUF_TO_AGF(bp); 23816259e7dSChristoph Hellwig agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC); 23916259e7dSChristoph Hellwig agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION); 24016259e7dSChristoph Hellwig agf->agf_seqno = cpu_to_be32(agno); 2411da177e4SLinus Torvalds if (agno == nagcount - 1) 2421da177e4SLinus Torvalds agsize = 2431da177e4SLinus Torvalds nb - 2441da177e4SLinus Torvalds (agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks); 2451da177e4SLinus Torvalds else 2461da177e4SLinus Torvalds agsize = mp->m_sb.sb_agblocks; 24716259e7dSChristoph Hellwig agf->agf_length = cpu_to_be32(agsize); 24816259e7dSChristoph Hellwig agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp)); 24916259e7dSChristoph Hellwig agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp)); 25016259e7dSChristoph Hellwig agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1); 25116259e7dSChristoph Hellwig agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1); 2521da177e4SLinus Torvalds agf->agf_flfirst = 0; 25316259e7dSChristoph Hellwig agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1); 2541da177e4SLinus Torvalds agf->agf_flcount = 0; 2551da177e4SLinus Torvalds tmpsize = agsize - XFS_PREALLOC_BLOCKS(mp); 25616259e7dSChristoph Hellwig agf->agf_freeblks = cpu_to_be32(tmpsize); 25716259e7dSChristoph Hellwig agf->agf_longest = cpu_to_be32(tmpsize); 2584e0e6040SDave Chinner if (xfs_sb_version_hascrc(&mp->m_sb)) 2594e0e6040SDave Chinner uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_uuid); 2604e0e6040SDave Chinner 261c2b006c1SChristoph Hellwig error = xfs_bwrite(bp); 262c2b006c1SChristoph Hellwig xfs_buf_relse(bp); 263c2b006c1SChristoph Hellwig if (error) 2641da177e4SLinus Torvalds goto error0; 265c2b006c1SChristoph Hellwig 2661da177e4SLinus Torvalds /* 267de497688SDave Chinner * AG freelist header block 268de497688SDave Chinner */ 269de497688SDave Chinner bp = xfs_growfs_get_hdr_buf(mp, 270de497688SDave Chinner XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)), 2711813dd64SDave Chinner XFS_FSS_TO_BB(mp, 1), 0, 2721813dd64SDave Chinner &xfs_agfl_buf_ops); 273de497688SDave Chinner if (!bp) { 274de497688SDave Chinner error = ENOMEM; 275de497688SDave Chinner goto error0; 276de497688SDave Chinner } 277de497688SDave Chinner 278de497688SDave Chinner agfl = XFS_BUF_TO_AGFL(bp); 27977c95bbaSChristoph Hellwig if (xfs_sb_version_hascrc(&mp->m_sb)) { 28077c95bbaSChristoph Hellwig agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC); 28177c95bbaSChristoph Hellwig agfl->agfl_seqno = cpu_to_be32(agno); 28277c95bbaSChristoph Hellwig uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_uuid); 28377c95bbaSChristoph Hellwig } 284de497688SDave Chinner for (bucket = 0; bucket < XFS_AGFL_SIZE(mp); bucket++) 285de497688SDave Chinner agfl->agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK); 286de497688SDave Chinner 287de497688SDave Chinner error = xfs_bwrite(bp); 288de497688SDave Chinner xfs_buf_relse(bp); 289de497688SDave Chinner if (error) 290de497688SDave Chinner goto error0; 291de497688SDave Chinner 292de497688SDave Chinner /* 2931da177e4SLinus Torvalds * AG inode header block 2941da177e4SLinus Torvalds */ 295fd23683cSDave Chinner bp = xfs_growfs_get_hdr_buf(mp, 2961da177e4SLinus Torvalds XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)), 2971813dd64SDave Chinner XFS_FSS_TO_BB(mp, 1), 0, 2981813dd64SDave Chinner &xfs_agi_buf_ops); 299b522950fSChandra Seetharaman if (!bp) { 300b522950fSChandra Seetharaman error = ENOMEM; 301b522950fSChandra Seetharaman goto error0; 302b522950fSChandra Seetharaman } 303fd23683cSDave Chinner 3041da177e4SLinus Torvalds agi = XFS_BUF_TO_AGI(bp); 30516259e7dSChristoph Hellwig agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC); 30616259e7dSChristoph Hellwig agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION); 30716259e7dSChristoph Hellwig agi->agi_seqno = cpu_to_be32(agno); 30816259e7dSChristoph Hellwig agi->agi_length = cpu_to_be32(agsize); 3091da177e4SLinus Torvalds agi->agi_count = 0; 31016259e7dSChristoph Hellwig agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp)); 31116259e7dSChristoph Hellwig agi->agi_level = cpu_to_be32(1); 3121da177e4SLinus Torvalds agi->agi_freecount = 0; 31316259e7dSChristoph Hellwig agi->agi_newino = cpu_to_be32(NULLAGINO); 31416259e7dSChristoph Hellwig agi->agi_dirino = cpu_to_be32(NULLAGINO); 315983d09ffSDave Chinner if (xfs_sb_version_hascrc(&mp->m_sb)) 316983d09ffSDave Chinner uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_uuid); 3171da177e4SLinus Torvalds for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) 31816259e7dSChristoph Hellwig agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO); 319983d09ffSDave Chinner 320c2b006c1SChristoph Hellwig error = xfs_bwrite(bp); 321c2b006c1SChristoph Hellwig xfs_buf_relse(bp); 322c2b006c1SChristoph Hellwig if (error) 3231da177e4SLinus Torvalds goto error0; 324c2b006c1SChristoph Hellwig 3251da177e4SLinus Torvalds /* 3261da177e4SLinus Torvalds * BNO btree root block 3271da177e4SLinus Torvalds */ 328fd23683cSDave Chinner bp = xfs_growfs_get_hdr_buf(mp, 3291da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)), 3301813dd64SDave Chinner BTOBB(mp->m_sb.sb_blocksize), 0, 3311813dd64SDave Chinner &xfs_allocbt_buf_ops); 332fd23683cSDave Chinner 333b522950fSChandra Seetharaman if (!bp) { 334b522950fSChandra Seetharaman error = ENOMEM; 335b522950fSChandra Seetharaman goto error0; 336b522950fSChandra Seetharaman } 337b64f3a39SDave Chinner 338ee1a47abSChristoph Hellwig if (xfs_sb_version_hascrc(&mp->m_sb)) 339ee1a47abSChristoph Hellwig xfs_btree_init_block(mp, bp, XFS_ABTB_CRC_MAGIC, 0, 1, 340ee1a47abSChristoph Hellwig agno, XFS_BTREE_CRC_BLOCKS); 341ee1a47abSChristoph Hellwig else 342ee1a47abSChristoph Hellwig xfs_btree_init_block(mp, bp, XFS_ABTB_MAGIC, 0, 1, 343ee1a47abSChristoph Hellwig agno, 0); 344ee1a47abSChristoph Hellwig 345b64f3a39SDave Chinner arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1); 34616259e7dSChristoph Hellwig arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp)); 34716259e7dSChristoph Hellwig arec->ar_blockcount = cpu_to_be32( 34816259e7dSChristoph Hellwig agsize - be32_to_cpu(arec->ar_startblock)); 349b64f3a39SDave Chinner 350c2b006c1SChristoph Hellwig error = xfs_bwrite(bp); 351c2b006c1SChristoph Hellwig xfs_buf_relse(bp); 352c2b006c1SChristoph Hellwig if (error) 3531da177e4SLinus Torvalds goto error0; 354c2b006c1SChristoph Hellwig 3551da177e4SLinus Torvalds /* 3561da177e4SLinus Torvalds * CNT btree root block 3571da177e4SLinus Torvalds */ 358fd23683cSDave Chinner bp = xfs_growfs_get_hdr_buf(mp, 3591da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)), 3601813dd64SDave Chinner BTOBB(mp->m_sb.sb_blocksize), 0, 3611813dd64SDave Chinner &xfs_allocbt_buf_ops); 362b522950fSChandra Seetharaman if (!bp) { 363b522950fSChandra Seetharaman error = ENOMEM; 364b522950fSChandra Seetharaman goto error0; 365b522950fSChandra Seetharaman } 366b64f3a39SDave Chinner 367ee1a47abSChristoph Hellwig if (xfs_sb_version_hascrc(&mp->m_sb)) 368ee1a47abSChristoph Hellwig xfs_btree_init_block(mp, bp, XFS_ABTC_CRC_MAGIC, 0, 1, 369ee1a47abSChristoph Hellwig agno, XFS_BTREE_CRC_BLOCKS); 370ee1a47abSChristoph Hellwig else 371ee1a47abSChristoph Hellwig xfs_btree_init_block(mp, bp, XFS_ABTC_MAGIC, 0, 1, 372ee1a47abSChristoph Hellwig agno, 0); 373ee1a47abSChristoph Hellwig 374b64f3a39SDave Chinner arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1); 37516259e7dSChristoph Hellwig arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp)); 37616259e7dSChristoph Hellwig arec->ar_blockcount = cpu_to_be32( 37716259e7dSChristoph Hellwig agsize - be32_to_cpu(arec->ar_startblock)); 37816259e7dSChristoph Hellwig nfree += be32_to_cpu(arec->ar_blockcount); 379b64f3a39SDave Chinner 380c2b006c1SChristoph Hellwig error = xfs_bwrite(bp); 381c2b006c1SChristoph Hellwig xfs_buf_relse(bp); 382c2b006c1SChristoph Hellwig if (error) 3831da177e4SLinus Torvalds goto error0; 384c2b006c1SChristoph Hellwig 3851da177e4SLinus Torvalds /* 3861da177e4SLinus Torvalds * INO btree root block 3871da177e4SLinus Torvalds */ 388fd23683cSDave Chinner bp = xfs_growfs_get_hdr_buf(mp, 3891da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)), 3901813dd64SDave Chinner BTOBB(mp->m_sb.sb_blocksize), 0, 3911813dd64SDave Chinner &xfs_inobt_buf_ops); 392b522950fSChandra Seetharaman if (!bp) { 393b522950fSChandra Seetharaman error = ENOMEM; 394b522950fSChandra Seetharaman goto error0; 395b522950fSChandra Seetharaman } 396fd23683cSDave Chinner 397ee1a47abSChristoph Hellwig if (xfs_sb_version_hascrc(&mp->m_sb)) 398ee1a47abSChristoph Hellwig xfs_btree_init_block(mp, bp, XFS_IBT_CRC_MAGIC, 0, 0, 399ee1a47abSChristoph Hellwig agno, XFS_BTREE_CRC_BLOCKS); 400ee1a47abSChristoph Hellwig else 401ee1a47abSChristoph Hellwig xfs_btree_init_block(mp, bp, XFS_IBT_MAGIC, 0, 0, 402ee1a47abSChristoph Hellwig agno, 0); 403b64f3a39SDave Chinner 404c2b006c1SChristoph Hellwig error = xfs_bwrite(bp); 405c2b006c1SChristoph Hellwig xfs_buf_relse(bp); 406c2b006c1SChristoph Hellwig if (error) 4071da177e4SLinus Torvalds goto error0; 4081da177e4SLinus Torvalds } 4091da177e4SLinus Torvalds xfs_trans_agblocks_delta(tp, nfree); 4101da177e4SLinus Torvalds /* 4111da177e4SLinus Torvalds * There are new blocks in the old last a.g. 4121da177e4SLinus Torvalds */ 4131da177e4SLinus Torvalds if (new) { 4141da177e4SLinus Torvalds /* 4151da177e4SLinus Torvalds * Change the agi length. 4161da177e4SLinus Torvalds */ 4171da177e4SLinus Torvalds error = xfs_ialloc_read_agi(mp, tp, agno, &bp); 4181da177e4SLinus Torvalds if (error) { 4191da177e4SLinus Torvalds goto error0; 4201da177e4SLinus Torvalds } 4211da177e4SLinus Torvalds ASSERT(bp); 4221da177e4SLinus Torvalds agi = XFS_BUF_TO_AGI(bp); 423413d57c9SMarcin Slusarz be32_add_cpu(&agi->agi_length, new); 4241da177e4SLinus Torvalds ASSERT(nagcount == oagcount || 42516259e7dSChristoph Hellwig be32_to_cpu(agi->agi_length) == mp->m_sb.sb_agblocks); 4261da177e4SLinus Torvalds xfs_ialloc_log_agi(tp, bp, XFS_AGI_LENGTH); 4271da177e4SLinus Torvalds /* 4281da177e4SLinus Torvalds * Change agf length. 4291da177e4SLinus Torvalds */ 4301da177e4SLinus Torvalds error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp); 4311da177e4SLinus Torvalds if (error) { 4321da177e4SLinus Torvalds goto error0; 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds ASSERT(bp); 4351da177e4SLinus Torvalds agf = XFS_BUF_TO_AGF(bp); 436413d57c9SMarcin Slusarz be32_add_cpu(&agf->agf_length, new); 43716259e7dSChristoph Hellwig ASSERT(be32_to_cpu(agf->agf_length) == 43816259e7dSChristoph Hellwig be32_to_cpu(agi->agi_length)); 4390b1b213fSChristoph Hellwig 4400164af51STim Shimmin xfs_alloc_log_agf(tp, bp, XFS_AGF_LENGTH); 4411da177e4SLinus Torvalds /* 4421da177e4SLinus Torvalds * Free the new space. 4431da177e4SLinus Torvalds */ 4441da177e4SLinus Torvalds error = xfs_free_extent(tp, XFS_AGB_TO_FSB(mp, agno, 44516259e7dSChristoph Hellwig be32_to_cpu(agf->agf_length) - new), new); 4461da177e4SLinus Torvalds if (error) { 4471da177e4SLinus Torvalds goto error0; 4481da177e4SLinus Torvalds } 4491da177e4SLinus Torvalds } 4501c1c6ebcSDave Chinner 4511c1c6ebcSDave Chinner /* 4521c1c6ebcSDave Chinner * Update changed superblock fields transactionally. These are not 4531c1c6ebcSDave Chinner * seen by the rest of the world until the transaction commit applies 4541c1c6ebcSDave Chinner * them atomically to the superblock. 4551c1c6ebcSDave Chinner */ 4561da177e4SLinus Torvalds if (nagcount > oagcount) 4571da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount); 4581da177e4SLinus Torvalds if (nb > mp->m_sb.sb_dblocks) 4591da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, 4601da177e4SLinus Torvalds nb - mp->m_sb.sb_dblocks); 4611da177e4SLinus Torvalds if (nfree) 4621da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree); 4631da177e4SLinus Torvalds if (dpct) 4641da177e4SLinus Torvalds xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct); 4651c72bf90SEric Sandeen error = xfs_trans_commit(tp, 0); 4661c1c6ebcSDave Chinner if (error) 4671da177e4SLinus Torvalds return error; 4681c1c6ebcSDave Chinner 4691da177e4SLinus Torvalds /* New allocation groups fully initialized, so update mount struct */ 4701da177e4SLinus Torvalds if (nagimax) 4711da177e4SLinus Torvalds mp->m_maxagi = nagimax; 4721da177e4SLinus Torvalds if (mp->m_sb.sb_imax_pct) { 4731da177e4SLinus Torvalds __uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct; 4741da177e4SLinus Torvalds do_div(icount, 100); 4751da177e4SLinus Torvalds mp->m_maxicount = icount << mp->m_sb.sb_inopblog; 4761da177e4SLinus Torvalds } else 4771da177e4SLinus Torvalds mp->m_maxicount = 0; 478055388a3SDave Chinner xfs_set_low_space_thresholds(mp); 4791c1c6ebcSDave Chinner 4801c1c6ebcSDave Chinner /* update secondary superblocks. */ 4811da177e4SLinus Torvalds for (agno = 1; agno < nagcount; agno++) { 4821375cb65SDave Chinner error = 0; 4831375cb65SDave Chinner /* 4841375cb65SDave Chinner * new secondary superblocks need to be zeroed, not read from 4851375cb65SDave Chinner * disk as the contents of the new area we are growing into is 4861375cb65SDave Chinner * completely unknown. 4871375cb65SDave Chinner */ 4881375cb65SDave Chinner if (agno < oagcount) { 4897ca790a5SDave Chinner error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, 4901da177e4SLinus Torvalds XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)), 49198021821SDave Chinner XFS_FSS_TO_BB(mp, 1), 0, &bp, 4921813dd64SDave Chinner &xfs_sb_buf_ops); 4931375cb65SDave Chinner } else { 4941375cb65SDave Chinner bp = xfs_trans_get_buf(NULL, mp->m_ddev_targp, 4951375cb65SDave Chinner XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)), 4961375cb65SDave Chinner XFS_FSS_TO_BB(mp, 1), 0); 497b0f539deSDave Chinner if (bp) { 4981813dd64SDave Chinner bp->b_ops = &xfs_sb_buf_ops; 4991375cb65SDave Chinner xfs_buf_zero(bp, 0, BBTOB(bp->b_length)); 500b0f539deSDave Chinner } else 5011375cb65SDave Chinner error = ENOMEM; 5021375cb65SDave Chinner } 5031375cb65SDave Chinner 50459e5a0e8SEric Sandeen /* 50559e5a0e8SEric Sandeen * If we get an error reading or writing alternate superblocks, 50659e5a0e8SEric Sandeen * continue. xfs_repair chooses the "best" superblock based 50759e5a0e8SEric Sandeen * on most matches; if we break early, we'll leave more 50859e5a0e8SEric Sandeen * superblocks un-updated than updated, and xfs_repair may 50959e5a0e8SEric Sandeen * pick them over the properly-updated primary. 51059e5a0e8SEric Sandeen */ 5111da177e4SLinus Torvalds if (error) { 51253487786SDave Chinner xfs_warn(mp, 5131da177e4SLinus Torvalds "error %d reading secondary superblock for ag %d", 5141da177e4SLinus Torvalds error, agno); 51559e5a0e8SEric Sandeen saved_error = error; 51659e5a0e8SEric Sandeen continue; 5171da177e4SLinus Torvalds } 5182bdf7cd0SChristoph Hellwig xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb, XFS_SB_ALL_BITS); 51998021821SDave Chinner 520c2b006c1SChristoph Hellwig error = xfs_bwrite(bp); 521c2b006c1SChristoph Hellwig xfs_buf_relse(bp); 522c2b006c1SChristoph Hellwig if (error) { 52353487786SDave Chinner xfs_warn(mp, 5241da177e4SLinus Torvalds "write error %d updating secondary superblock for ag %d", 5251da177e4SLinus Torvalds error, agno); 52659e5a0e8SEric Sandeen saved_error = error; 52759e5a0e8SEric Sandeen continue; 5281da177e4SLinus Torvalds } 5291da177e4SLinus Torvalds } 53059e5a0e8SEric Sandeen return saved_error ? saved_error : error; 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds error0: 5331da177e4SLinus Torvalds xfs_trans_cancel(tp, XFS_TRANS_ABORT); 5341da177e4SLinus Torvalds return error; 5351da177e4SLinus Torvalds } 5361da177e4SLinus Torvalds 5371da177e4SLinus Torvalds static int 5381da177e4SLinus Torvalds xfs_growfs_log_private( 5391da177e4SLinus Torvalds xfs_mount_t *mp, /* mount point for filesystem */ 5401da177e4SLinus Torvalds xfs_growfs_log_t *in) /* growfs log input struct */ 5411da177e4SLinus Torvalds { 5421da177e4SLinus Torvalds xfs_extlen_t nb; 5431da177e4SLinus Torvalds 5441da177e4SLinus Torvalds nb = in->newblocks; 5451da177e4SLinus Torvalds if (nb < XFS_MIN_LOG_BLOCKS || nb < XFS_B_TO_FSB(mp, XFS_MIN_LOG_BYTES)) 5461da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 5471da177e4SLinus Torvalds if (nb == mp->m_sb.sb_logblocks && 5481da177e4SLinus Torvalds in->isint == (mp->m_sb.sb_logstart != 0)) 5491da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 5501da177e4SLinus Torvalds /* 5511da177e4SLinus Torvalds * Moving the log is hard, need new interfaces to sync 5521da177e4SLinus Torvalds * the log first, hold off all activity while moving it. 5531da177e4SLinus Torvalds * Can have shorter or longer log in the same space, 5541da177e4SLinus Torvalds * or transform internal to external log or vice versa. 5551da177e4SLinus Torvalds */ 5561da177e4SLinus Torvalds return XFS_ERROR(ENOSYS); 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds 5591da177e4SLinus Torvalds /* 5601da177e4SLinus Torvalds * protected versions of growfs function acquire and release locks on the mount 5611da177e4SLinus Torvalds * point - exported through ioctls: XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG, 5621da177e4SLinus Torvalds * XFS_IOC_FSGROWFSRT 5631da177e4SLinus Torvalds */ 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds 5661da177e4SLinus Torvalds int 5671da177e4SLinus Torvalds xfs_growfs_data( 5681da177e4SLinus Torvalds xfs_mount_t *mp, 5691da177e4SLinus Torvalds xfs_growfs_data_t *in) 5701da177e4SLinus Torvalds { 5711da177e4SLinus Torvalds int error; 572743bb465Ssandeen@sandeen.net 573743bb465Ssandeen@sandeen.net if (!capable(CAP_SYS_ADMIN)) 574743bb465Ssandeen@sandeen.net return XFS_ERROR(EPERM); 575cc92e7acSChristoph Hellwig if (!mutex_trylock(&mp->m_growlock)) 5761da177e4SLinus Torvalds return XFS_ERROR(EWOULDBLOCK); 5771da177e4SLinus Torvalds error = xfs_growfs_data_private(mp, in); 578cc92e7acSChristoph Hellwig mutex_unlock(&mp->m_growlock); 5791da177e4SLinus Torvalds return error; 5801da177e4SLinus Torvalds } 5811da177e4SLinus Torvalds 5821da177e4SLinus Torvalds int 5831da177e4SLinus Torvalds xfs_growfs_log( 5841da177e4SLinus Torvalds xfs_mount_t *mp, 5851da177e4SLinus Torvalds xfs_growfs_log_t *in) 5861da177e4SLinus Torvalds { 5871da177e4SLinus Torvalds int error; 588743bb465Ssandeen@sandeen.net 589743bb465Ssandeen@sandeen.net if (!capable(CAP_SYS_ADMIN)) 590743bb465Ssandeen@sandeen.net return XFS_ERROR(EPERM); 591cc92e7acSChristoph Hellwig if (!mutex_trylock(&mp->m_growlock)) 5921da177e4SLinus Torvalds return XFS_ERROR(EWOULDBLOCK); 5931da177e4SLinus Torvalds error = xfs_growfs_log_private(mp, in); 594cc92e7acSChristoph Hellwig mutex_unlock(&mp->m_growlock); 5951da177e4SLinus Torvalds return error; 5961da177e4SLinus Torvalds } 5971da177e4SLinus Torvalds 5981da177e4SLinus Torvalds /* 5991da177e4SLinus Torvalds * exported through ioctl XFS_IOC_FSCOUNTS 6001da177e4SLinus Torvalds */ 6011da177e4SLinus Torvalds 6021da177e4SLinus Torvalds int 6031da177e4SLinus Torvalds xfs_fs_counts( 6041da177e4SLinus Torvalds xfs_mount_t *mp, 6051da177e4SLinus Torvalds xfs_fsop_counts_t *cnt) 6061da177e4SLinus Torvalds { 607d4d90b57SChristoph Hellwig xfs_icsb_sync_counters(mp, XFS_ICSB_LAZY_COUNT); 6083685c2a1SEric Sandeen spin_lock(&mp->m_sb_lock); 6094be536deSDavid Chinner cnt->freedata = mp->m_sb.sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp); 6101da177e4SLinus Torvalds cnt->freertx = mp->m_sb.sb_frextents; 6111da177e4SLinus Torvalds cnt->freeino = mp->m_sb.sb_ifree; 6121da177e4SLinus Torvalds cnt->allocino = mp->m_sb.sb_icount; 6133685c2a1SEric Sandeen spin_unlock(&mp->m_sb_lock); 6141da177e4SLinus Torvalds return 0; 6151da177e4SLinus Torvalds } 6161da177e4SLinus Torvalds 6171da177e4SLinus Torvalds /* 6181da177e4SLinus Torvalds * exported through ioctl XFS_IOC_SET_RESBLKS & XFS_IOC_GET_RESBLKS 6191da177e4SLinus Torvalds * 6201da177e4SLinus Torvalds * xfs_reserve_blocks is called to set m_resblks 6211da177e4SLinus Torvalds * in the in-core mount table. The number of unused reserved blocks 622c41564b5SNathan Scott * is kept in m_resblks_avail. 6231da177e4SLinus Torvalds * 6241da177e4SLinus Torvalds * Reserve the requested number of blocks if available. Otherwise return 6251da177e4SLinus Torvalds * as many as possible to satisfy the request. The actual number 6261da177e4SLinus Torvalds * reserved are returned in outval 6271da177e4SLinus Torvalds * 6281da177e4SLinus Torvalds * A null inval pointer indicates that only the current reserved blocks 6291da177e4SLinus Torvalds * available should be returned no settings are changed. 6301da177e4SLinus Torvalds */ 6311da177e4SLinus Torvalds 6321da177e4SLinus Torvalds int 6331da177e4SLinus Torvalds xfs_reserve_blocks( 6341da177e4SLinus Torvalds xfs_mount_t *mp, 6351da177e4SLinus Torvalds __uint64_t *inval, 6361da177e4SLinus Torvalds xfs_fsop_resblks_t *outval) 6371da177e4SLinus Torvalds { 638dbcabad1SDavid Chinner __int64_t lcounter, delta, fdblks_delta; 6391da177e4SLinus Torvalds __uint64_t request; 6401da177e4SLinus Torvalds 6411da177e4SLinus Torvalds /* If inval is null, report current values and return */ 6421da177e4SLinus Torvalds if (inval == (__uint64_t *)NULL) { 64384e1e99fSDavid Chinner if (!outval) 64484e1e99fSDavid Chinner return EINVAL; 6451da177e4SLinus Torvalds outval->resblks = mp->m_resblks; 6461da177e4SLinus Torvalds outval->resblks_avail = mp->m_resblks_avail; 647014c2544SJesper Juhl return 0; 6481da177e4SLinus Torvalds } 6491da177e4SLinus Torvalds 6501da177e4SLinus Torvalds request = *inval; 651dbcabad1SDavid Chinner 652dbcabad1SDavid Chinner /* 653dbcabad1SDavid Chinner * With per-cpu counters, this becomes an interesting 654dbcabad1SDavid Chinner * problem. we needto work out if we are freeing or allocation 655dbcabad1SDavid Chinner * blocks first, then we can do the modification as necessary. 656dbcabad1SDavid Chinner * 6573685c2a1SEric Sandeen * We do this under the m_sb_lock so that if we are near 658dbcabad1SDavid Chinner * ENOSPC, we will hold out any changes while we work out 659dbcabad1SDavid Chinner * what to do. This means that the amount of free space can 660dbcabad1SDavid Chinner * change while we do this, so we need to retry if we end up 661dbcabad1SDavid Chinner * trying to reserve more space than is available. 662dbcabad1SDavid Chinner * 663dbcabad1SDavid Chinner * We also use the xfs_mod_incore_sb() interface so that we 664dbcabad1SDavid Chinner * don't have to care about whether per cpu counter are 665dbcabad1SDavid Chinner * enabled, disabled or even compiled in.... 666dbcabad1SDavid Chinner */ 667dbcabad1SDavid Chinner retry: 6683685c2a1SEric Sandeen spin_lock(&mp->m_sb_lock); 669d4d90b57SChristoph Hellwig xfs_icsb_sync_counters_locked(mp, 0); 6701da177e4SLinus Torvalds 6711da177e4SLinus Torvalds /* 6721da177e4SLinus Torvalds * If our previous reservation was larger than the current value, 6731da177e4SLinus Torvalds * then move any unused blocks back to the free pool. 6741da177e4SLinus Torvalds */ 675dbcabad1SDavid Chinner fdblks_delta = 0; 6761da177e4SLinus Torvalds if (mp->m_resblks > request) { 6771da177e4SLinus Torvalds lcounter = mp->m_resblks_avail - request; 6781da177e4SLinus Torvalds if (lcounter > 0) { /* release unused blocks */ 679dbcabad1SDavid Chinner fdblks_delta = lcounter; 6801da177e4SLinus Torvalds mp->m_resblks_avail -= lcounter; 6811da177e4SLinus Torvalds } 6821da177e4SLinus Torvalds mp->m_resblks = request; 6831da177e4SLinus Torvalds } else { 6844be536deSDavid Chinner __int64_t free; 6854be536deSDavid Chinner 6864be536deSDavid Chinner free = mp->m_sb.sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp); 687dbcabad1SDavid Chinner if (!free) 688dbcabad1SDavid Chinner goto out; /* ENOSPC and fdblks_delta = 0 */ 689dbcabad1SDavid Chinner 6901da177e4SLinus Torvalds delta = request - mp->m_resblks; 6914be536deSDavid Chinner lcounter = free - delta; 6921da177e4SLinus Torvalds if (lcounter < 0) { 6931da177e4SLinus Torvalds /* We can't satisfy the request, just get what we can */ 6944be536deSDavid Chinner mp->m_resblks += free; 6954be536deSDavid Chinner mp->m_resblks_avail += free; 696dbcabad1SDavid Chinner fdblks_delta = -free; 6971da177e4SLinus Torvalds } else { 698dbcabad1SDavid Chinner fdblks_delta = -delta; 6991da177e4SLinus Torvalds mp->m_resblks = request; 7001da177e4SLinus Torvalds mp->m_resblks_avail += delta; 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds } 703dbcabad1SDavid Chinner out: 70484e1e99fSDavid Chinner if (outval) { 7051da177e4SLinus Torvalds outval->resblks = mp->m_resblks; 7061da177e4SLinus Torvalds outval->resblks_avail = mp->m_resblks_avail; 70784e1e99fSDavid Chinner } 7083685c2a1SEric Sandeen spin_unlock(&mp->m_sb_lock); 709dbcabad1SDavid Chinner 710dbcabad1SDavid Chinner if (fdblks_delta) { 711dbcabad1SDavid Chinner /* 712dbcabad1SDavid Chinner * If we are putting blocks back here, m_resblks_avail is 7139da096fdSMalcolm Parsons * already at its max so this will put it in the free pool. 714dbcabad1SDavid Chinner * 715dbcabad1SDavid Chinner * If we need space, we'll either succeed in getting it 716dbcabad1SDavid Chinner * from the free block count or we'll get an enospc. If 717dbcabad1SDavid Chinner * we get a ENOSPC, it means things changed while we were 718dbcabad1SDavid Chinner * calculating fdblks_delta and so we should try again to 719dbcabad1SDavid Chinner * see if there is anything left to reserve. 720dbcabad1SDavid Chinner * 721dbcabad1SDavid Chinner * Don't set the reserved flag here - we don't want to reserve 722dbcabad1SDavid Chinner * the extra reserve blocks from the reserve..... 723dbcabad1SDavid Chinner */ 724dbcabad1SDavid Chinner int error; 72596540c78SChristoph Hellwig error = xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS, 72696540c78SChristoph Hellwig fdblks_delta, 0); 727dbcabad1SDavid Chinner if (error == ENOSPC) 728dbcabad1SDavid Chinner goto retry; 729dbcabad1SDavid Chinner } 730014c2544SJesper Juhl return 0; 7311da177e4SLinus Torvalds } 7321da177e4SLinus Torvalds 7331a387d3bSDave Chinner /* 7341a387d3bSDave Chinner * Dump a transaction into the log that contains no real change. This is needed 7351a387d3bSDave Chinner * to be able to make the log dirty or stamp the current tail LSN into the log 7361a387d3bSDave Chinner * during the covering operation. 7371a387d3bSDave Chinner * 7381a387d3bSDave Chinner * We cannot use an inode here for this - that will push dirty state back up 7391a387d3bSDave Chinner * into the VFS and then periodic inode flushing will prevent log covering from 740c58efdb4SDave Chinner * making progress. Hence we log a field in the superblock instead and use a 741c58efdb4SDave Chinner * synchronous transaction to ensure the superblock is immediately unpinned 742c58efdb4SDave Chinner * and can be written back. 7431a387d3bSDave Chinner */ 744c4be0c1dSTakashi Sato int 745421ad134SNathan Scott xfs_fs_log_dummy( 746c58efdb4SDave Chinner xfs_mount_t *mp) 747e13a73f0SChristoph Hellwig { 748e13a73f0SChristoph Hellwig xfs_trans_t *tp; 749c4be0c1dSTakashi Sato int error; 750e13a73f0SChristoph Hellwig 75180641dc6SChristoph Hellwig tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1, KM_SLEEP); 7523d3c8b52SJie Liu error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0); 753c4be0c1dSTakashi Sato if (error) { 754e13a73f0SChristoph Hellwig xfs_trans_cancel(tp, 0); 755c4be0c1dSTakashi Sato return error; 756e13a73f0SChristoph Hellwig } 757e13a73f0SChristoph Hellwig 7581a387d3bSDave Chinner /* log the UUID because it is an unchanging field */ 7591a387d3bSDave Chinner xfs_mod_sb(tp, XFS_SB_UUID); 760e13a73f0SChristoph Hellwig xfs_trans_set_sync(tp); 7611a387d3bSDave Chinner return xfs_trans_commit(tp, 0); 762e13a73f0SChristoph Hellwig } 763e13a73f0SChristoph Hellwig 7641da177e4SLinus Torvalds int 7651da177e4SLinus Torvalds xfs_fs_goingdown( 7661da177e4SLinus Torvalds xfs_mount_t *mp, 7671da177e4SLinus Torvalds __uint32_t inflags) 7681da177e4SLinus Torvalds { 7691da177e4SLinus Torvalds switch (inflags) { 7701da177e4SLinus Torvalds case XFS_FSOP_GOING_FLAGS_DEFAULT: { 771b267ce99SChristoph Hellwig struct super_block *sb = freeze_bdev(mp->m_super->s_bdev); 7721da177e4SLinus Torvalds 773f33c6797SChristoph Hellwig if (sb && !IS_ERR(sb)) { 7747d04a335SNathan Scott xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT); 7751da177e4SLinus Torvalds thaw_bdev(sb->s_bdev, sb); 7761da177e4SLinus Torvalds } 7771da177e4SLinus Torvalds 7781da177e4SLinus Torvalds break; 7791da177e4SLinus Torvalds } 7801da177e4SLinus Torvalds case XFS_FSOP_GOING_FLAGS_LOGFLUSH: 7817d04a335SNathan Scott xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT); 7821da177e4SLinus Torvalds break; 7831da177e4SLinus Torvalds case XFS_FSOP_GOING_FLAGS_NOLOGFLUSH: 7847d04a335SNathan Scott xfs_force_shutdown(mp, 7857d04a335SNathan Scott SHUTDOWN_FORCE_UMOUNT | SHUTDOWN_LOG_IO_ERROR); 7861da177e4SLinus Torvalds break; 7871da177e4SLinus Torvalds default: 7881da177e4SLinus Torvalds return XFS_ERROR(EINVAL); 7891da177e4SLinus Torvalds } 7901da177e4SLinus Torvalds 7911da177e4SLinus Torvalds return 0; 7921da177e4SLinus Torvalds } 7932af51f3aSDave Chinner 7942af51f3aSDave Chinner /* 7952af51f3aSDave Chinner * Force a shutdown of the filesystem instantly while keeping the filesystem 7962af51f3aSDave Chinner * consistent. We don't do an unmount here; just shutdown the shop, make sure 7972af51f3aSDave Chinner * that absolutely nothing persistent happens to this filesystem after this 7982af51f3aSDave Chinner * point. 7992af51f3aSDave Chinner */ 8002af51f3aSDave Chinner void 8012af51f3aSDave Chinner xfs_do_force_shutdown( 8022af51f3aSDave Chinner xfs_mount_t *mp, 8032af51f3aSDave Chinner int flags, 8042af51f3aSDave Chinner char *fname, 8052af51f3aSDave Chinner int lnnum) 8062af51f3aSDave Chinner { 8072af51f3aSDave Chinner int logerror; 8082af51f3aSDave Chinner 8092af51f3aSDave Chinner logerror = flags & SHUTDOWN_LOG_IO_ERROR; 8102af51f3aSDave Chinner 8112af51f3aSDave Chinner if (!(flags & SHUTDOWN_FORCE_UMOUNT)) { 8122af51f3aSDave Chinner xfs_notice(mp, 8132af51f3aSDave Chinner "%s(0x%x) called from line %d of file %s. Return address = 0x%p", 8142af51f3aSDave Chinner __func__, flags, lnnum, fname, __return_address); 8152af51f3aSDave Chinner } 8162af51f3aSDave Chinner /* 8172af51f3aSDave Chinner * No need to duplicate efforts. 8182af51f3aSDave Chinner */ 8192af51f3aSDave Chinner if (XFS_FORCED_SHUTDOWN(mp) && !logerror) 8202af51f3aSDave Chinner return; 8212af51f3aSDave Chinner 8222af51f3aSDave Chinner /* 8232af51f3aSDave Chinner * This flags XFS_MOUNT_FS_SHUTDOWN, makes sure that we don't 8242af51f3aSDave Chinner * queue up anybody new on the log reservations, and wakes up 8252af51f3aSDave Chinner * everybody who's sleeping on log reservations to tell them 8262af51f3aSDave Chinner * the bad news. 8272af51f3aSDave Chinner */ 8282af51f3aSDave Chinner if (xfs_log_force_umount(mp, logerror)) 8292af51f3aSDave Chinner return; 8302af51f3aSDave Chinner 8312af51f3aSDave Chinner if (flags & SHUTDOWN_CORRUPT_INCORE) { 8322af51f3aSDave Chinner xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_CORRUPT, 8332af51f3aSDave Chinner "Corruption of in-memory data detected. Shutting down filesystem"); 8342af51f3aSDave Chinner if (XFS_ERRLEVEL_HIGH <= xfs_error_level) 8352af51f3aSDave Chinner xfs_stack_trace(); 8362af51f3aSDave Chinner } else if (!(flags & SHUTDOWN_FORCE_UMOUNT)) { 8372af51f3aSDave Chinner if (logerror) { 8382af51f3aSDave Chinner xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_LOGERROR, 8392af51f3aSDave Chinner "Log I/O Error Detected. Shutting down filesystem"); 8402af51f3aSDave Chinner } else if (flags & SHUTDOWN_DEVICE_REQ) { 8412af51f3aSDave Chinner xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_IOERROR, 8422af51f3aSDave Chinner "All device paths lost. Shutting down filesystem"); 8432af51f3aSDave Chinner } else if (!(flags & SHUTDOWN_REMOTE_REQ)) { 8442af51f3aSDave Chinner xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_IOERROR, 8452af51f3aSDave Chinner "I/O Error Detected. Shutting down filesystem"); 8462af51f3aSDave Chinner } 8472af51f3aSDave Chinner } 8482af51f3aSDave Chinner if (!(flags & SHUTDOWN_FORCE_UMOUNT)) { 8492af51f3aSDave Chinner xfs_alert(mp, 8502af51f3aSDave Chinner "Please umount the filesystem and rectify the problem(s)"); 8512af51f3aSDave Chinner } 8522af51f3aSDave Chinner } 853