1153323Srodrigc/*
2159451Srodrigc * Copyright (c) 2000-2005 Silicon Graphics, Inc.
3159451Srodrigc * All Rights Reserved.
4153323Srodrigc *
5159451Srodrigc * This program is free software; you can redistribute it and/or
6159451Srodrigc * modify it under the terms of the GNU General Public License as
7153323Srodrigc * published by the Free Software Foundation.
8153323Srodrigc *
9159451Srodrigc * This program is distributed in the hope that it would be useful,
10159451Srodrigc * but WITHOUT ANY WARRANTY; without even the implied warranty of
11159451Srodrigc * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12159451Srodrigc * GNU General Public License for more details.
13153323Srodrigc *
14159451Srodrigc * You should have received a copy of the GNU General Public License
15159451Srodrigc * along with this program; if not, write the Free Software Foundation,
16159451Srodrigc * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17153323Srodrigc */
18153323Srodrigc#include "xfs.h"
19159451Srodrigc#include "xfs_fs.h"
20153323Srodrigc#include "xfs_types.h"
21159451Srodrigc#include "xfs_bit.h"
22153323Srodrigc#include "xfs_inum.h"
23153323Srodrigc#include "xfs_log.h"
24153323Srodrigc#include "xfs_trans.h"
25153323Srodrigc#include "xfs_sb.h"
26159451Srodrigc#include "xfs_ag.h"
27153323Srodrigc#include "xfs_dir.h"
28159451Srodrigc#include "xfs_dir2.h"
29153323Srodrigc#include "xfs_dmapi.h"
30153323Srodrigc#include "xfs_mount.h"
31159451Srodrigc#include "xfs_bmap_btree.h"
32153323Srodrigc#include "xfs_alloc_btree.h"
33153323Srodrigc#include "xfs_ialloc_btree.h"
34159451Srodrigc#include "xfs_dir_sf.h"
35159451Srodrigc#include "xfs_dir2_sf.h"
36159451Srodrigc#include "xfs_attr_sf.h"
37159451Srodrigc#include "xfs_dinode.h"
38159451Srodrigc#include "xfs_inode.h"
39159451Srodrigc#include "xfs_inode_item.h"
40153323Srodrigc#include "xfs_btree.h"
41153323Srodrigc#include "xfs_error.h"
42153323Srodrigc#include "xfs_alloc.h"
43153323Srodrigc#include "xfs_ialloc.h"
44153323Srodrigc#include "xfs_fsops.h"
45153323Srodrigc#include "xfs_itable.h"
46153323Srodrigc#include "xfs_trans_space.h"
47153323Srodrigc#include "xfs_rtalloc.h"
48159451Srodrigc#include "xfs_rw.h"
49153323Srodrigc
50153323Srodrigc/*
51153323Srodrigc * File system operations
52153323Srodrigc */
53153323Srodrigc
54153323Srodrigcint
55153323Srodrigcxfs_fs_geometry(
56153323Srodrigc	xfs_mount_t		*mp,
57153323Srodrigc	xfs_fsop_geom_t		*geo,
58153323Srodrigc	int			new_version)
59153323Srodrigc{
60153323Srodrigc	geo->blocksize = mp->m_sb.sb_blocksize;
61153323Srodrigc	geo->rtextsize = mp->m_sb.sb_rextsize;
62153323Srodrigc	geo->agblocks = mp->m_sb.sb_agblocks;
63153323Srodrigc	geo->agcount = mp->m_sb.sb_agcount;
64153323Srodrigc	geo->logblocks = mp->m_sb.sb_logblocks;
65153323Srodrigc	geo->sectsize = mp->m_sb.sb_sectsize;
66153323Srodrigc	geo->inodesize = mp->m_sb.sb_inodesize;
67153323Srodrigc	geo->imaxpct = mp->m_sb.sb_imax_pct;
68153323Srodrigc	geo->datablocks = mp->m_sb.sb_dblocks;
69153323Srodrigc	geo->rtblocks = mp->m_sb.sb_rblocks;
70153323Srodrigc	geo->rtextents = mp->m_sb.sb_rextents;
71153323Srodrigc	geo->logstart = mp->m_sb.sb_logstart;
72153323Srodrigc	ASSERT(sizeof(geo->uuid)==sizeof(mp->m_sb.sb_uuid));
73153323Srodrigc	memcpy(geo->uuid, &mp->m_sb.sb_uuid, sizeof(mp->m_sb.sb_uuid));
74153323Srodrigc	if (new_version >= 2) {
75153323Srodrigc		geo->sunit = mp->m_sb.sb_unit;
76153323Srodrigc		geo->swidth = mp->m_sb.sb_width;
77153323Srodrigc	}
78153323Srodrigc	if (new_version >= 3) {
79153323Srodrigc		geo->version = XFS_FSOP_GEOM_VERSION;
80153323Srodrigc		geo->flags =
81153323Srodrigc			(XFS_SB_VERSION_HASATTR(&mp->m_sb) ?
82153323Srodrigc				XFS_FSOP_GEOM_FLAGS_ATTR : 0) |
83153323Srodrigc			(XFS_SB_VERSION_HASNLINK(&mp->m_sb) ?
84153323Srodrigc				XFS_FSOP_GEOM_FLAGS_NLINK : 0) |
85153323Srodrigc			(XFS_SB_VERSION_HASQUOTA(&mp->m_sb) ?
86153323Srodrigc				XFS_FSOP_GEOM_FLAGS_QUOTA : 0) |
87153323Srodrigc			(XFS_SB_VERSION_HASALIGN(&mp->m_sb) ?
88153323Srodrigc				XFS_FSOP_GEOM_FLAGS_IALIGN : 0) |
89153323Srodrigc			(XFS_SB_VERSION_HASDALIGN(&mp->m_sb) ?
90153323Srodrigc				XFS_FSOP_GEOM_FLAGS_DALIGN : 0) |
91153323Srodrigc			(XFS_SB_VERSION_HASSHARED(&mp->m_sb) ?
92153323Srodrigc				XFS_FSOP_GEOM_FLAGS_SHARED : 0) |
93153323Srodrigc			(XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) ?
94153323Srodrigc				XFS_FSOP_GEOM_FLAGS_EXTFLG : 0) |
95153323Srodrigc			(XFS_SB_VERSION_HASDIRV2(&mp->m_sb) ?
96153323Srodrigc				XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) |
97153323Srodrigc			(XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ?
98159451Srodrigc				XFS_FSOP_GEOM_FLAGS_SECTOR : 0) |
99159451Srodrigc			(XFS_SB_VERSION_HASATTR2(&mp->m_sb) ?
100159451Srodrigc				XFS_FSOP_GEOM_FLAGS_ATTR2 : 0);
101153323Srodrigc		geo->logsectsize = XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ?
102153323Srodrigc				mp->m_sb.sb_logsectsize : BBSIZE;
103153323Srodrigc		geo->rtsectsize = mp->m_sb.sb_blocksize;
104153323Srodrigc		geo->dirblocksize = mp->m_dirblksize;
105153323Srodrigc	}
106153323Srodrigc	if (new_version >= 4) {
107153323Srodrigc		geo->flags |=
108153323Srodrigc			(XFS_SB_VERSION_HASLOGV2(&mp->m_sb) ?
109153323Srodrigc				XFS_FSOP_GEOM_FLAGS_LOGV2 : 0);
110153323Srodrigc		geo->logsunit = mp->m_sb.sb_logsunit;
111153323Srodrigc	}
112153323Srodrigc	return 0;
113153323Srodrigc}
114153323Srodrigc
115153323Srodrigcstatic int
116153323Srodrigcxfs_growfs_data_private(
117153323Srodrigc	xfs_mount_t		*mp,		/* mount point for filesystem */
118153323Srodrigc	xfs_growfs_data_t	*in)		/* growfs data input struct */
119153323Srodrigc{
120153323Srodrigc	xfs_agf_t		*agf;
121153323Srodrigc	xfs_agi_t		*agi;
122153323Srodrigc	xfs_agnumber_t		agno;
123153323Srodrigc	xfs_extlen_t		agsize;
124153323Srodrigc	xfs_extlen_t		tmpsize;
125153323Srodrigc	xfs_alloc_rec_t		*arec;
126153323Srodrigc	xfs_btree_sblock_t	*block;
127153323Srodrigc	xfs_buf_t		*bp;
128153323Srodrigc	int			bucket;
129153323Srodrigc	int			dpct;
130153323Srodrigc	int			error;
131153323Srodrigc	xfs_agnumber_t		nagcount;
132159451Srodrigc	xfs_agnumber_t		nagimax = 0;
133153323Srodrigc	xfs_rfsblock_t		nb, nb_mod;
134153323Srodrigc	xfs_rfsblock_t		new;
135153323Srodrigc	xfs_rfsblock_t		nfree;
136153323Srodrigc	xfs_agnumber_t		oagcount;
137153323Srodrigc	int			pct;
138153323Srodrigc	xfs_sb_t		*sbp;
139153323Srodrigc	xfs_trans_t		*tp;
140153323Srodrigc
141153323Srodrigc	nb = in->newblocks;
142153323Srodrigc	pct = in->imaxpct;
143153323Srodrigc	if (nb < mp->m_sb.sb_dblocks || pct < 0 || pct > 100)
144153323Srodrigc		return XFS_ERROR(EINVAL);
145153323Srodrigc	dpct = pct - mp->m_sb.sb_imax_pct;
146153323Srodrigc	error = xfs_read_buf(mp, mp->m_ddev_targp,
147153323Srodrigc			XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1),
148153323Srodrigc			XFS_FSS_TO_BB(mp, 1), 0, &bp);
149153323Srodrigc	if (error)
150153323Srodrigc		return error;
151153323Srodrigc	ASSERT(bp);
152153323Srodrigc	xfs_buf_relse(bp);
153153323Srodrigc
154153323Srodrigc	new = nb;	/* use new as a temporary here */
155153323Srodrigc	nb_mod = do_div(new, mp->m_sb.sb_agblocks);
156153323Srodrigc	nagcount = new + (nb_mod != 0);
157153323Srodrigc	if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
158153323Srodrigc		nagcount--;
159153323Srodrigc		nb = nagcount * mp->m_sb.sb_agblocks;
160153323Srodrigc		if (nb < mp->m_sb.sb_dblocks)
161153323Srodrigc			return XFS_ERROR(EINVAL);
162153323Srodrigc	}
163153323Srodrigc	new = nb - mp->m_sb.sb_dblocks;
164153323Srodrigc	oagcount = mp->m_sb.sb_agcount;
165153323Srodrigc	if (nagcount > oagcount) {
166153323Srodrigc		down_write(&mp->m_peraglock);
167153323Srodrigc		mp->m_perag = kmem_realloc(mp->m_perag,
168153323Srodrigc			sizeof(xfs_perag_t) * nagcount,
169153323Srodrigc			sizeof(xfs_perag_t) * oagcount,
170153323Srodrigc			KM_SLEEP);
171153323Srodrigc		memset(&mp->m_perag[oagcount], 0,
172153323Srodrigc			(nagcount - oagcount) * sizeof(xfs_perag_t));
173153323Srodrigc		mp->m_flags |= XFS_MOUNT_32BITINODES;
174159451Srodrigc		nagimax = xfs_initialize_perag(XFS_MTOVFS(mp), mp, nagcount);
175153323Srodrigc		up_write(&mp->m_peraglock);
176153323Srodrigc	}
177153323Srodrigc	tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS);
178153323Srodrigc	if ((error = xfs_trans_reserve(tp, XFS_GROWFS_SPACE_RES(mp),
179153323Srodrigc			XFS_GROWDATA_LOG_RES(mp), 0, 0, 0))) {
180153323Srodrigc		xfs_trans_cancel(tp, 0);
181153323Srodrigc		return error;
182153323Srodrigc	}
183153323Srodrigc
184153323Srodrigc	nfree = 0;
185153323Srodrigc	for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
186153323Srodrigc		/*
187153323Srodrigc		 * AG freelist header block
188153323Srodrigc		 */
189153323Srodrigc		bp = xfs_buf_get(mp->m_ddev_targp,
190153323Srodrigc				  XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
191153323Srodrigc				  XFS_FSS_TO_BB(mp, 1), 0);
192153323Srodrigc		agf = XFS_BUF_TO_AGF(bp);
193153323Srodrigc		memset(agf, 0, mp->m_sb.sb_sectsize);
194159451Srodrigc		agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
195159451Srodrigc		agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
196159451Srodrigc		agf->agf_seqno = cpu_to_be32(agno);
197153323Srodrigc		if (agno == nagcount - 1)
198153323Srodrigc			agsize =
199153323Srodrigc				nb -
200153323Srodrigc				(agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
201153323Srodrigc		else
202153323Srodrigc			agsize = mp->m_sb.sb_agblocks;
203159451Srodrigc		agf->agf_length = cpu_to_be32(agsize);
204159451Srodrigc		agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
205159451Srodrigc		agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
206159451Srodrigc		agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
207159451Srodrigc		agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
208159451Srodrigc		agf->agf_flfirst = 0;
209159451Srodrigc		agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1);
210159451Srodrigc		agf->agf_flcount = 0;
211153323Srodrigc		tmpsize = agsize - XFS_PREALLOC_BLOCKS(mp);
212159451Srodrigc		agf->agf_freeblks = cpu_to_be32(tmpsize);
213159451Srodrigc		agf->agf_longest = cpu_to_be32(tmpsize);
214153323Srodrigc		error = xfs_bwrite(mp, bp);
215153323Srodrigc		if (error) {
216153323Srodrigc			goto error0;
217153323Srodrigc		}
218153323Srodrigc		/*
219153323Srodrigc		 * AG inode header block
220153323Srodrigc		 */
221153323Srodrigc		bp = xfs_buf_get(mp->m_ddev_targp,
222153323Srodrigc				  XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
223153323Srodrigc				  XFS_FSS_TO_BB(mp, 1), 0);
224153323Srodrigc		agi = XFS_BUF_TO_AGI(bp);
225153323Srodrigc		memset(agi, 0, mp->m_sb.sb_sectsize);
226159451Srodrigc		agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
227159451Srodrigc		agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
228159451Srodrigc		agi->agi_seqno = cpu_to_be32(agno);
229159451Srodrigc		agi->agi_length = cpu_to_be32(agsize);
230159451Srodrigc		agi->agi_count = 0;
231159451Srodrigc		agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
232159451Srodrigc		agi->agi_level = cpu_to_be32(1);
233159451Srodrigc		agi->agi_freecount = 0;
234159451Srodrigc		agi->agi_newino = cpu_to_be32(NULLAGINO);
235159451Srodrigc		agi->agi_dirino = cpu_to_be32(NULLAGINO);
236153323Srodrigc		for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
237159451Srodrigc			agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
238153323Srodrigc		error = xfs_bwrite(mp, bp);
239153323Srodrigc		if (error) {
240153323Srodrigc			goto error0;
241153323Srodrigc		}
242153323Srodrigc		/*
243153323Srodrigc		 * BNO btree root block
244153323Srodrigc		 */
245153323Srodrigc		bp = xfs_buf_get(mp->m_ddev_targp,
246153323Srodrigc			XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
247153323Srodrigc			BTOBB(mp->m_sb.sb_blocksize), 0);
248153323Srodrigc		block = XFS_BUF_TO_SBLOCK(bp);
249153323Srodrigc		memset(block, 0, mp->m_sb.sb_blocksize);
250159451Srodrigc		block->bb_magic = cpu_to_be32(XFS_ABTB_MAGIC);
251159451Srodrigc		block->bb_level = 0;
252159451Srodrigc		block->bb_numrecs = cpu_to_be16(1);
253159451Srodrigc		block->bb_leftsib = cpu_to_be32(NULLAGBLOCK);
254159451Srodrigc		block->bb_rightsib = cpu_to_be32(NULLAGBLOCK);
255153323Srodrigc		arec = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_alloc,
256153323Srodrigc			block, 1, mp->m_alloc_mxr[0]);
257159451Srodrigc		arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp));
258159451Srodrigc		arec->ar_blockcount = cpu_to_be32(
259159451Srodrigc			agsize - be32_to_cpu(arec->ar_startblock));
260153323Srodrigc		error = xfs_bwrite(mp, bp);
261153323Srodrigc		if (error) {
262153323Srodrigc			goto error0;
263153323Srodrigc		}
264153323Srodrigc		/*
265153323Srodrigc		 * CNT btree root block
266153323Srodrigc		 */
267153323Srodrigc		bp = xfs_buf_get(mp->m_ddev_targp,
268153323Srodrigc			XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
269153323Srodrigc			BTOBB(mp->m_sb.sb_blocksize), 0);
270153323Srodrigc		block = XFS_BUF_TO_SBLOCK(bp);
271153323Srodrigc		memset(block, 0, mp->m_sb.sb_blocksize);
272159451Srodrigc		block->bb_magic = cpu_to_be32(XFS_ABTC_MAGIC);
273159451Srodrigc		block->bb_level = 0;
274159451Srodrigc		block->bb_numrecs = cpu_to_be16(1);
275159451Srodrigc		block->bb_leftsib = cpu_to_be32(NULLAGBLOCK);
276159451Srodrigc		block->bb_rightsib = cpu_to_be32(NULLAGBLOCK);
277153323Srodrigc		arec = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_alloc,
278153323Srodrigc			block, 1, mp->m_alloc_mxr[0]);
279159451Srodrigc		arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp));
280159451Srodrigc		arec->ar_blockcount = cpu_to_be32(
281159451Srodrigc			agsize - be32_to_cpu(arec->ar_startblock));
282159451Srodrigc		nfree += be32_to_cpu(arec->ar_blockcount);
283153323Srodrigc		error = xfs_bwrite(mp, bp);
284153323Srodrigc		if (error) {
285153323Srodrigc			goto error0;
286153323Srodrigc		}
287153323Srodrigc		/*
288153323Srodrigc		 * INO btree root block
289153323Srodrigc		 */
290153323Srodrigc		bp = xfs_buf_get(mp->m_ddev_targp,
291153323Srodrigc			XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
292153323Srodrigc			BTOBB(mp->m_sb.sb_blocksize), 0);
293153323Srodrigc		block = XFS_BUF_TO_SBLOCK(bp);
294153323Srodrigc		memset(block, 0, mp->m_sb.sb_blocksize);
295159451Srodrigc		block->bb_magic = cpu_to_be32(XFS_IBT_MAGIC);
296159451Srodrigc		block->bb_level = 0;
297159451Srodrigc		block->bb_numrecs = 0;
298159451Srodrigc		block->bb_leftsib = cpu_to_be32(NULLAGBLOCK);
299159451Srodrigc		block->bb_rightsib = cpu_to_be32(NULLAGBLOCK);
300153323Srodrigc		error = xfs_bwrite(mp, bp);
301153323Srodrigc		if (error) {
302153323Srodrigc			goto error0;
303153323Srodrigc		}
304153323Srodrigc	}
305153323Srodrigc	xfs_trans_agblocks_delta(tp, nfree);
306153323Srodrigc	/*
307153323Srodrigc	 * There are new blocks in the old last a.g.
308153323Srodrigc	 */
309153323Srodrigc	if (new) {
310153323Srodrigc		/*
311153323Srodrigc		 * Change the agi length.
312153323Srodrigc		 */
313153323Srodrigc		error = xfs_ialloc_read_agi(mp, tp, agno, &bp);
314153323Srodrigc		if (error) {
315153323Srodrigc			goto error0;
316153323Srodrigc		}
317153323Srodrigc		ASSERT(bp);
318153323Srodrigc		agi = XFS_BUF_TO_AGI(bp);
319159451Srodrigc		be32_add(&agi->agi_length, new);
320153323Srodrigc		ASSERT(nagcount == oagcount ||
321159451Srodrigc		       be32_to_cpu(agi->agi_length) == mp->m_sb.sb_agblocks);
322153323Srodrigc		xfs_ialloc_log_agi(tp, bp, XFS_AGI_LENGTH);
323153323Srodrigc		/*
324153323Srodrigc		 * Change agf length.
325153323Srodrigc		 */
326153323Srodrigc		error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp);
327153323Srodrigc		if (error) {
328153323Srodrigc			goto error0;
329153323Srodrigc		}
330153323Srodrigc		ASSERT(bp);
331153323Srodrigc		agf = XFS_BUF_TO_AGF(bp);
332159451Srodrigc		be32_add(&agf->agf_length, new);
333159451Srodrigc		ASSERT(be32_to_cpu(agf->agf_length) ==
334159451Srodrigc		       be32_to_cpu(agi->agi_length));
335153323Srodrigc		/*
336153323Srodrigc		 * Free the new space.
337153323Srodrigc		 */
338153323Srodrigc		error = xfs_free_extent(tp, XFS_AGB_TO_FSB(mp, agno,
339159451Srodrigc			be32_to_cpu(agf->agf_length) - new), new);
340153323Srodrigc		if (error) {
341153323Srodrigc			goto error0;
342153323Srodrigc		}
343153323Srodrigc	}
344153323Srodrigc	if (nagcount > oagcount)
345153323Srodrigc		xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
346153323Srodrigc	if (nb > mp->m_sb.sb_dblocks)
347153323Srodrigc		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS,
348153323Srodrigc				 nb - mp->m_sb.sb_dblocks);
349153323Srodrigc	if (nfree)
350153323Srodrigc		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree);
351153323Srodrigc	if (dpct)
352153323Srodrigc		xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
353153323Srodrigc	error = xfs_trans_commit(tp, 0, NULL);
354153323Srodrigc	if (error) {
355153323Srodrigc		return error;
356153323Srodrigc	}
357159451Srodrigc	/* New allocation groups fully initialized, so update mount struct */
358159451Srodrigc	if (nagimax)
359159451Srodrigc		mp->m_maxagi = nagimax;
360153323Srodrigc	if (mp->m_sb.sb_imax_pct) {
361153323Srodrigc		__uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct;
362153323Srodrigc		do_div(icount, 100);
363153323Srodrigc		mp->m_maxicount = icount << mp->m_sb.sb_inopblog;
364153323Srodrigc	} else
365153323Srodrigc		mp->m_maxicount = 0;
366153323Srodrigc	for (agno = 1; agno < nagcount; agno++) {
367153323Srodrigc		error = xfs_read_buf(mp, mp->m_ddev_targp,
368153323Srodrigc				  XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
369153323Srodrigc				  XFS_FSS_TO_BB(mp, 1), 0, &bp);
370153323Srodrigc		if (error) {
371153323Srodrigc			xfs_fs_cmn_err(CE_WARN, mp,
372153323Srodrigc			"error %d reading secondary superblock for ag %d",
373153323Srodrigc				error, agno);
374153323Srodrigc			break;
375153323Srodrigc		}
376153323Srodrigc		sbp = XFS_BUF_TO_SBP(bp);
377159451Srodrigc		xfs_xlatesb(sbp, &mp->m_sb, -1, XFS_SB_ALL_BITS);
378153323Srodrigc		/*
379153323Srodrigc		 * If we get an error writing out the alternate superblocks,
380153323Srodrigc		 * just issue a warning and continue.  The real work is
381153323Srodrigc		 * already done and committed.
382153323Srodrigc		 */
383153323Srodrigc		if (!(error = xfs_bwrite(mp, bp))) {
384153323Srodrigc			continue;
385153323Srodrigc		} else {
386153323Srodrigc			xfs_fs_cmn_err(CE_WARN, mp,
387153323Srodrigc		"write error %d updating secondary superblock for ag %d",
388153323Srodrigc				error, agno);
389153323Srodrigc			break; /* no point in continuing */
390153323Srodrigc		}
391153323Srodrigc	}
392153323Srodrigc	return 0;
393153323Srodrigc
394153323Srodrigc error0:
395153323Srodrigc	xfs_trans_cancel(tp, XFS_TRANS_ABORT);
396153323Srodrigc	return error;
397153323Srodrigc}
398153323Srodrigc
399153323Srodrigcstatic int
400153323Srodrigcxfs_growfs_log_private(
401153323Srodrigc	xfs_mount_t		*mp,	/* mount point for filesystem */
402153323Srodrigc	xfs_growfs_log_t	*in)	/* growfs log input struct */
403153323Srodrigc{
404153323Srodrigc	xfs_extlen_t		nb;
405153323Srodrigc
406153323Srodrigc	nb = in->newblocks;
407153323Srodrigc	if (nb < XFS_MIN_LOG_BLOCKS || nb < XFS_B_TO_FSB(mp, XFS_MIN_LOG_BYTES))
408153323Srodrigc		return XFS_ERROR(EINVAL);
409153323Srodrigc	if (nb == mp->m_sb.sb_logblocks &&
410153323Srodrigc	    in->isint == (mp->m_sb.sb_logstart != 0))
411153323Srodrigc		return XFS_ERROR(EINVAL);
412153323Srodrigc	/*
413153323Srodrigc	 * Moving the log is hard, need new interfaces to sync
414153323Srodrigc	 * the log first, hold off all activity while moving it.
415153323Srodrigc	 * Can have shorter or longer log in the same space,
416153323Srodrigc	 * or transform internal to external log or vice versa.
417153323Srodrigc	 */
418153323Srodrigc	return XFS_ERROR(ENOSYS);
419153323Srodrigc}
420153323Srodrigc
421153323Srodrigc/*
422153323Srodrigc * protected versions of growfs function acquire and release locks on the mount
423153323Srodrigc * point - exported through ioctls: XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG,
424153323Srodrigc * XFS_IOC_FSGROWFSRT
425153323Srodrigc */
426153323Srodrigc
427153323Srodrigc
428153323Srodrigcint
429153323Srodrigcxfs_growfs_data(
430153323Srodrigc	xfs_mount_t		*mp,
431153323Srodrigc	xfs_growfs_data_t	*in)
432153323Srodrigc{
433153323Srodrigc	int error;
434153323Srodrigc	if (!cpsema(&mp->m_growlock))
435153323Srodrigc		return XFS_ERROR(EWOULDBLOCK);
436153323Srodrigc	error = xfs_growfs_data_private(mp, in);
437153323Srodrigc	vsema(&mp->m_growlock);
438153323Srodrigc	return error;
439153323Srodrigc}
440153323Srodrigc
441153323Srodrigcint
442153323Srodrigcxfs_growfs_log(
443153323Srodrigc	xfs_mount_t		*mp,
444153323Srodrigc	xfs_growfs_log_t	*in)
445153323Srodrigc{
446153323Srodrigc	int error;
447153323Srodrigc	if (!cpsema(&mp->m_growlock))
448153323Srodrigc		return XFS_ERROR(EWOULDBLOCK);
449153323Srodrigc	error = xfs_growfs_log_private(mp, in);
450153323Srodrigc	vsema(&mp->m_growlock);
451153323Srodrigc	return error;
452153323Srodrigc}
453153323Srodrigc
454153323Srodrigc/*
455153323Srodrigc * exported through ioctl XFS_IOC_FSCOUNTS
456153323Srodrigc */
457153323Srodrigc
458153323Srodrigcint
459153323Srodrigcxfs_fs_counts(
460153323Srodrigc	xfs_mount_t		*mp,
461153323Srodrigc	xfs_fsop_counts_t	*cnt)
462153323Srodrigc{
463153323Srodrigc	unsigned long	s;
464153323Srodrigc
465159451Srodrigc	xfs_icsb_sync_counters_lazy(mp);
466153323Srodrigc	s = XFS_SB_LOCK(mp);
467153323Srodrigc	cnt->freedata = mp->m_sb.sb_fdblocks;
468153323Srodrigc	cnt->freertx = mp->m_sb.sb_frextents;
469153323Srodrigc	cnt->freeino = mp->m_sb.sb_ifree;
470153323Srodrigc	cnt->allocino = mp->m_sb.sb_icount;
471153323Srodrigc	XFS_SB_UNLOCK(mp, s);
472153323Srodrigc	return 0;
473153323Srodrigc}
474153323Srodrigc
475153323Srodrigc/*
476153323Srodrigc * exported through ioctl XFS_IOC_SET_RESBLKS & XFS_IOC_GET_RESBLKS
477153323Srodrigc *
478153323Srodrigc * xfs_reserve_blocks is called to set m_resblks
479153323Srodrigc * in the in-core mount table. The number of unused reserved blocks
480159451Srodrigc * is kept in m_resblks_avail.
481153323Srodrigc *
482153323Srodrigc * Reserve the requested number of blocks if available. Otherwise return
483153323Srodrigc * as many as possible to satisfy the request. The actual number
484153323Srodrigc * reserved are returned in outval
485153323Srodrigc *
486153323Srodrigc * A null inval pointer indicates that only the current reserved blocks
487153323Srodrigc * available  should  be returned no settings are changed.
488153323Srodrigc */
489153323Srodrigc
490153323Srodrigcint
491153323Srodrigcxfs_reserve_blocks(
492153323Srodrigc	xfs_mount_t             *mp,
493153323Srodrigc	__uint64_t              *inval,
494153323Srodrigc	xfs_fsop_resblks_t      *outval)
495153323Srodrigc{
496159451Srodrigc	__int64_t		lcounter, delta;
497159451Srodrigc	__uint64_t		request;
498159451Srodrigc	unsigned long		s;
499153323Srodrigc
500153323Srodrigc	/* If inval is null, report current values and return */
501153323Srodrigc
502153323Srodrigc	if (inval == (__uint64_t *)NULL) {
503153323Srodrigc		outval->resblks = mp->m_resblks;
504153323Srodrigc		outval->resblks_avail = mp->m_resblks_avail;
505159451Srodrigc		return 0;
506153323Srodrigc	}
507153323Srodrigc
508153323Srodrigc	request = *inval;
509153323Srodrigc	s = XFS_SB_LOCK(mp);
510153323Srodrigc
511153323Srodrigc	/*
512153323Srodrigc	 * If our previous reservation was larger than the current value,
513153323Srodrigc	 * then move any unused blocks back to the free pool.
514153323Srodrigc	 */
515153323Srodrigc
516153323Srodrigc	if (mp->m_resblks > request) {
517153323Srodrigc		lcounter = mp->m_resblks_avail - request;
518153323Srodrigc		if (lcounter  > 0) {		/* release unused blocks */
519153323Srodrigc			mp->m_sb.sb_fdblocks += lcounter;
520153323Srodrigc			mp->m_resblks_avail -= lcounter;
521153323Srodrigc		}
522153323Srodrigc		mp->m_resblks = request;
523153323Srodrigc	} else {
524153323Srodrigc		delta = request - mp->m_resblks;
525159451Srodrigc		lcounter = mp->m_sb.sb_fdblocks - delta;
526153323Srodrigc		if (lcounter < 0) {
527153323Srodrigc			/* We can't satisfy the request, just get what we can */
528153323Srodrigc			mp->m_resblks += mp->m_sb.sb_fdblocks;
529153323Srodrigc			mp->m_resblks_avail += mp->m_sb.sb_fdblocks;
530153323Srodrigc			mp->m_sb.sb_fdblocks = 0;
531153323Srodrigc		} else {
532153323Srodrigc			mp->m_sb.sb_fdblocks = lcounter;
533153323Srodrigc			mp->m_resblks = request;
534153323Srodrigc			mp->m_resblks_avail += delta;
535153323Srodrigc		}
536153323Srodrigc	}
537153323Srodrigc
538153323Srodrigc	outval->resblks = mp->m_resblks;
539153323Srodrigc	outval->resblks_avail = mp->m_resblks_avail;
540153323Srodrigc	XFS_SB_UNLOCK(mp, s);
541159451Srodrigc	return 0;
542153323Srodrigc}
543153323Srodrigc
544159451Srodrigcvoid
545159451Srodrigcxfs_fs_log_dummy(xfs_mount_t *mp)
546153323Srodrigc{
547159451Srodrigc	xfs_trans_t *tp;
548159451Srodrigc	xfs_inode_t *ip;
549153323Srodrigc
550153323Srodrigc
551159451Srodrigc	tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1);
552159451Srodrigc	atomic_inc(&mp->m_active_trans);
553159451Srodrigc	if (xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0)) {
554159451Srodrigc		xfs_trans_cancel(tp, 0);
555159451Srodrigc		return;
556153323Srodrigc	}
557153323Srodrigc
558159451Srodrigc	ip = mp->m_rootip;
559159451Srodrigc	xfs_ilock(ip, XFS_ILOCK_EXCL);
560153323Srodrigc
561159451Srodrigc	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
562159451Srodrigc	xfs_trans_ihold(tp, ip);
563159451Srodrigc	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
564159451Srodrigc	xfs_trans_set_sync(tp);
565159451Srodrigc	xfs_trans_commit(tp, 0, NULL);
566153323Srodrigc
567159451Srodrigc	xfs_iunlock(ip, XFS_ILOCK_EXCL);
568153323Srodrigc}
569153323Srodrigc
570153323Srodrigcint
571153323Srodrigcxfs_fs_goingdown(
572153323Srodrigc	xfs_mount_t	*mp,
573153323Srodrigc	__uint32_t	inflags)
574153323Srodrigc{
575159451Srodrigc	switch (inflags) {
576159451Srodrigc	case XFS_FSOP_GOING_FLAGS_DEFAULT: {
577159451Srodrigc#ifdef RMC
578159451Srodrigc		struct xfs_vfs *vfsp = XFS_MTOVFS(mp);
579159451Srodrigc		struct super_block *sb = freeze_bdev(vfsp->vfs_super->s_bdev);
580159451Srodrigc
581159451Srodrigc		if (sb && !IS_ERR(sb)) {
582159451Srodrigc			xfs_force_shutdown(mp, XFS_FORCE_UMOUNT);
583159451Srodrigc			thaw_bdev(sb->s_bdev, sb);
584159451Srodrigc		}
585159451Srodrigc#endif
586153323Srodrigc		break;
587159451Srodrigc	}
588153323Srodrigc	case XFS_FSOP_GOING_FLAGS_LOGFLUSH:
589153323Srodrigc		xfs_force_shutdown(mp, XFS_FORCE_UMOUNT);
590153323Srodrigc		break;
591153323Srodrigc	case XFS_FSOP_GOING_FLAGS_NOLOGFLUSH:
592153323Srodrigc		xfs_force_shutdown(mp, XFS_FORCE_UMOUNT|XFS_LOG_IO_ERROR);
593153323Srodrigc		break;
594153323Srodrigc	default:
595153323Srodrigc		return XFS_ERROR(EINVAL);
596153323Srodrigc	}
597153323Srodrigc
598153323Srodrigc	return 0;
599153323Srodrigc}
600