xfs_fsops.c 22.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2 3
 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
Linus Torvalds's avatar
Linus Torvalds committed
4
 *
5 6
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
Linus Torvalds's avatar
Linus Torvalds committed
7 8
 * published by the Free Software Foundation.
 *
9 10 11 12
 * This program is distributed in the hope that it would be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
Linus Torvalds's avatar
Linus Torvalds committed
13
 *
14 15 16
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write the Free Software Foundation,
 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Linus Torvalds's avatar
Linus Torvalds committed
17 18
 */
#include "xfs.h"
19
#include "xfs_fs.h"
Linus Torvalds's avatar
Linus Torvalds committed
20
#include "xfs_types.h"
21 22
#include "xfs_format.h"
#include "xfs_shared.h"
Linus Torvalds's avatar
Linus Torvalds committed
23 24 25
#include "xfs_log.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
26
#include "xfs_ag.h"
Linus Torvalds's avatar
Linus Torvalds committed
27 28
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
29
#include "xfs_alloc_btree.h"
Linus Torvalds's avatar
Linus Torvalds committed
30
#include "xfs_ialloc_btree.h"
31 32 33
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_inode_item.h"
Linus Torvalds's avatar
Linus Torvalds committed
34 35 36 37 38 39 40 41
#include "xfs_btree.h"
#include "xfs_error.h"
#include "xfs_alloc.h"
#include "xfs_ialloc.h"
#include "xfs_fsops.h"
#include "xfs_itable.h"
#include "xfs_trans_space.h"
#include "xfs_rtalloc.h"
42
#include "xfs_filestream.h"
43
#include "xfs_trace.h"
Linus Torvalds's avatar
Linus Torvalds committed
44 45 46 47 48 49 50 51 52 53 54

/*
 * File system operations
 */

int
xfs_fs_geometry(
	xfs_mount_t		*mp,
	xfs_fsop_geom_t		*geo,
	int			new_version)
{
55 56 57

	memset(geo, 0, sizeof(*geo));

Linus Torvalds's avatar
Linus Torvalds committed
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
	geo->blocksize = mp->m_sb.sb_blocksize;
	geo->rtextsize = mp->m_sb.sb_rextsize;
	geo->agblocks = mp->m_sb.sb_agblocks;
	geo->agcount = mp->m_sb.sb_agcount;
	geo->logblocks = mp->m_sb.sb_logblocks;
	geo->sectsize = mp->m_sb.sb_sectsize;
	geo->inodesize = mp->m_sb.sb_inodesize;
	geo->imaxpct = mp->m_sb.sb_imax_pct;
	geo->datablocks = mp->m_sb.sb_dblocks;
	geo->rtblocks = mp->m_sb.sb_rblocks;
	geo->rtextents = mp->m_sb.sb_rextents;
	geo->logstart = mp->m_sb.sb_logstart;
	ASSERT(sizeof(geo->uuid)==sizeof(mp->m_sb.sb_uuid));
	memcpy(geo->uuid, &mp->m_sb.sb_uuid, sizeof(mp->m_sb.sb_uuid));
	if (new_version >= 2) {
		geo->sunit = mp->m_sb.sb_unit;
		geo->swidth = mp->m_sb.sb_width;
	}
	if (new_version >= 3) {
		geo->version = XFS_FSOP_GEOM_VERSION;
		geo->flags =
79
			(xfs_sb_version_hasattr(&mp->m_sb) ?
Linus Torvalds's avatar
Linus Torvalds committed
80
				XFS_FSOP_GEOM_FLAGS_ATTR : 0) |
81
			(xfs_sb_version_hasnlink(&mp->m_sb) ?
Linus Torvalds's avatar
Linus Torvalds committed
82
				XFS_FSOP_GEOM_FLAGS_NLINK : 0) |
83
			(xfs_sb_version_hasquota(&mp->m_sb) ?
Linus Torvalds's avatar
Linus Torvalds committed
84
				XFS_FSOP_GEOM_FLAGS_QUOTA : 0) |
85
			(xfs_sb_version_hasalign(&mp->m_sb) ?
Linus Torvalds's avatar
Linus Torvalds committed
86
				XFS_FSOP_GEOM_FLAGS_IALIGN : 0) |
87
			(xfs_sb_version_hasdalign(&mp->m_sb) ?
Linus Torvalds's avatar
Linus Torvalds committed
88
				XFS_FSOP_GEOM_FLAGS_DALIGN : 0) |
89
			(xfs_sb_version_hasshared(&mp->m_sb) ?
Linus Torvalds's avatar
Linus Torvalds committed
90
				XFS_FSOP_GEOM_FLAGS_SHARED : 0) |
91
			(xfs_sb_version_hasextflgbit(&mp->m_sb) ?
Linus Torvalds's avatar
Linus Torvalds committed
92
				XFS_FSOP_GEOM_FLAGS_EXTFLG : 0) |
93
			(xfs_sb_version_hasdirv2(&mp->m_sb) ?
Linus Torvalds's avatar
Linus Torvalds committed
94
				XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) |
95
			(xfs_sb_version_hassector(&mp->m_sb) ?
96
				XFS_FSOP_GEOM_FLAGS_SECTOR : 0) |
97 98
			(xfs_sb_version_hasasciici(&mp->m_sb) ?
				XFS_FSOP_GEOM_FLAGS_DIRV2CI : 0) |
99 100
			(xfs_sb_version_haslazysbcount(&mp->m_sb) ?
				XFS_FSOP_GEOM_FLAGS_LAZYSB : 0) |
101
			(xfs_sb_version_hasattr2(&mp->m_sb) ?
102 103
				XFS_FSOP_GEOM_FLAGS_ATTR2 : 0) |
			(xfs_sb_version_hasprojid32bit(&mp->m_sb) ?
104 105
				XFS_FSOP_GEOM_FLAGS_PROJID32 : 0) |
			(xfs_sb_version_hascrc(&mp->m_sb) ?
106 107 108
				XFS_FSOP_GEOM_FLAGS_V5SB : 0) |
			(xfs_sb_version_hasftype(&mp->m_sb) ?
				XFS_FSOP_GEOM_FLAGS_FTYPE : 0);
109
		geo->logsectsize = xfs_sb_version_hassector(&mp->m_sb) ?
Linus Torvalds's avatar
Linus Torvalds committed
110 111 112 113 114 115
				mp->m_sb.sb_logsectsize : BBSIZE;
		geo->rtsectsize = mp->m_sb.sb_blocksize;
		geo->dirblocksize = mp->m_dirblksize;
	}
	if (new_version >= 4) {
		geo->flags |=
116
			(xfs_sb_version_haslogv2(&mp->m_sb) ?
Linus Torvalds's avatar
Linus Torvalds committed
117 118 119 120 121 122
				XFS_FSOP_GEOM_FLAGS_LOGV2 : 0);
		geo->logsunit = mp->m_sb.sb_logsunit;
	}
	return 0;
}

123 124 125 126 127
static struct xfs_buf *
xfs_growfs_get_hdr_buf(
	struct xfs_mount	*mp,
	xfs_daddr_t		blkno,
	size_t			numblks,
128 129
	int			flags,
	const struct xfs_buf_ops *ops)
130 131 132 133 134 135 136 137 138 139
{
	struct xfs_buf		*bp;

	bp = xfs_buf_get_uncached(mp->m_ddev_targp, numblks, flags);
	if (!bp)
		return NULL;

	xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
	bp->b_bn = blkno;
	bp->b_maps[0].bm_bn = blkno;
140
	bp->b_ops = ops;
141 142 143 144

	return bp;
}

Linus Torvalds's avatar
Linus Torvalds committed
145 146 147 148 149 150
static int
xfs_growfs_data_private(
	xfs_mount_t		*mp,		/* mount point for filesystem */
	xfs_growfs_data_t	*in)		/* growfs data input struct */
{
	xfs_agf_t		*agf;
151
	struct xfs_agfl		*agfl;
Linus Torvalds's avatar
Linus Torvalds committed
152 153 154 155 156 157 158 159
	xfs_agi_t		*agi;
	xfs_agnumber_t		agno;
	xfs_extlen_t		agsize;
	xfs_extlen_t		tmpsize;
	xfs_alloc_rec_t		*arec;
	xfs_buf_t		*bp;
	int			bucket;
	int			dpct;
160
	int			error, saved_error = 0;
Linus Torvalds's avatar
Linus Torvalds committed
161 162 163 164 165 166 167 168 169 170 171 172 173
	xfs_agnumber_t		nagcount;
	xfs_agnumber_t		nagimax = 0;
	xfs_rfsblock_t		nb, nb_mod;
	xfs_rfsblock_t		new;
	xfs_rfsblock_t		nfree;
	xfs_agnumber_t		oagcount;
	int			pct;
	xfs_trans_t		*tp;

	nb = in->newblocks;
	pct = in->imaxpct;
	if (nb < mp->m_sb.sb_dblocks || pct < 0 || pct > 100)
		return XFS_ERROR(EINVAL);
174 175
	if ((error = xfs_sb_validate_fsb_count(&mp->m_sb, nb)))
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
176
	dpct = pct - mp->m_sb.sb_imax_pct;
177
	bp = xfs_buf_read_uncached(mp->m_ddev_targp,
178
				XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1),
179
				XFS_FSS_TO_BB(mp, 1), 0, NULL);
180 181
	if (!bp)
		return EIO;
182
	if (bp->b_error) {
183
		error = bp->b_error;
184 185 186
		xfs_buf_relse(bp);
		return error;
	}
Linus Torvalds's avatar
Linus Torvalds committed
187 188 189 190 191 192 193
	xfs_buf_relse(bp);

	new = nb;	/* use new as a temporary here */
	nb_mod = do_div(new, mp->m_sb.sb_agblocks);
	nagcount = new + (nb_mod != 0);
	if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
		nagcount--;
194
		nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;
Linus Torvalds's avatar
Linus Torvalds committed
195 196 197 198 199
		if (nb < mp->m_sb.sb_dblocks)
			return XFS_ERROR(EINVAL);
	}
	new = nb - mp->m_sb.sb_dblocks;
	oagcount = mp->m_sb.sb_agcount;
200

201 202 203 204 205
	/* allocate the new per-ag structures */
	if (nagcount > oagcount) {
		error = xfs_initialize_perag(mp, nagcount, &nagimax);
		if (error)
			return error;
Linus Torvalds's avatar
Linus Torvalds committed
206
	}
207

Linus Torvalds's avatar
Linus Torvalds committed
208
	tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS);
209
	tp->t_flags |= XFS_TRANS_RESERVE;
210 211 212
	error = xfs_trans_reserve(tp, &M_RES(mp)->tr_growdata,
				  XFS_GROWFS_SPACE_RES(mp), 0);
	if (error) {
Linus Torvalds's avatar
Linus Torvalds committed
213 214 215 216
		xfs_trans_cancel(tp, 0);
		return error;
	}

217 218 219 220 221
	/*
	 * Write new AG headers to disk. Non-transactional, but written
	 * synchronously so they are completed prior to the growfs transaction
	 * being logged.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
222 223 224
	nfree = 0;
	for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
		/*
225
		 * AG freespace header block
Linus Torvalds's avatar
Linus Torvalds committed
226
		 */
227 228
		bp = xfs_growfs_get_hdr_buf(mp,
				XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
229 230
				XFS_FSS_TO_BB(mp, 1), 0,
				&xfs_agf_buf_ops);
231 232 233 234
		if (!bp) {
			error = ENOMEM;
			goto error0;
		}
235

Linus Torvalds's avatar
Linus Torvalds committed
236
		agf = XFS_BUF_TO_AGF(bp);
237 238 239
		agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
		agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
		agf->agf_seqno = cpu_to_be32(agno);
Linus Torvalds's avatar
Linus Torvalds committed
240 241 242 243 244 245
		if (agno == nagcount - 1)
			agsize =
				nb -
				(agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
		else
			agsize = mp->m_sb.sb_agblocks;
246 247 248 249 250
		agf->agf_length = cpu_to_be32(agsize);
		agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
		agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
		agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
		agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
Linus Torvalds's avatar
Linus Torvalds committed
251
		agf->agf_flfirst = 0;
252
		agf->agf_fllast = cpu_to_be32(XFS_AGFL_SIZE(mp) - 1);
Linus Torvalds's avatar
Linus Torvalds committed
253 254
		agf->agf_flcount = 0;
		tmpsize = agsize - XFS_PREALLOC_BLOCKS(mp);
255 256
		agf->agf_freeblks = cpu_to_be32(tmpsize);
		agf->agf_longest = cpu_to_be32(tmpsize);
257 258 259
		if (xfs_sb_version_hascrc(&mp->m_sb))
			uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_uuid);

260 261 262
		error = xfs_bwrite(bp);
		xfs_buf_relse(bp);
		if (error)
Linus Torvalds's avatar
Linus Torvalds committed
263
			goto error0;
264

265 266 267 268 269
		/*
		 * AG freelist header block
		 */
		bp = xfs_growfs_get_hdr_buf(mp,
				XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
270 271
				XFS_FSS_TO_BB(mp, 1), 0,
				&xfs_agfl_buf_ops);
272 273 274 275 276 277
		if (!bp) {
			error = ENOMEM;
			goto error0;
		}

		agfl = XFS_BUF_TO_AGFL(bp);
278 279 280 281 282
		if (xfs_sb_version_hascrc(&mp->m_sb)) {
			agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
			agfl->agfl_seqno = cpu_to_be32(agno);
			uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_uuid);
		}
283 284 285 286 287 288 289 290
		for (bucket = 0; bucket < XFS_AGFL_SIZE(mp); bucket++)
			agfl->agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);

		error = xfs_bwrite(bp);
		xfs_buf_relse(bp);
		if (error)
			goto error0;

Linus Torvalds's avatar
Linus Torvalds committed
291 292 293
		/*
		 * AG inode header block
		 */
294 295
		bp = xfs_growfs_get_hdr_buf(mp,
				XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
296 297
				XFS_FSS_TO_BB(mp, 1), 0,
				&xfs_agi_buf_ops);
298 299 300 301
		if (!bp) {
			error = ENOMEM;
			goto error0;
		}
302

Linus Torvalds's avatar
Linus Torvalds committed
303
		agi = XFS_BUF_TO_AGI(bp);
304 305 306 307
		agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
		agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
		agi->agi_seqno = cpu_to_be32(agno);
		agi->agi_length = cpu_to_be32(agsize);
Linus Torvalds's avatar
Linus Torvalds committed
308
		agi->agi_count = 0;
309 310
		agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
		agi->agi_level = cpu_to_be32(1);
Linus Torvalds's avatar
Linus Torvalds committed
311
		agi->agi_freecount = 0;
312 313
		agi->agi_newino = cpu_to_be32(NULLAGINO);
		agi->agi_dirino = cpu_to_be32(NULLAGINO);
314 315
		if (xfs_sb_version_hascrc(&mp->m_sb))
			uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_uuid);
Linus Torvalds's avatar
Linus Torvalds committed
316
		for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
317
			agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
318

319 320 321
		error = xfs_bwrite(bp);
		xfs_buf_relse(bp);
		if (error)
Linus Torvalds's avatar
Linus Torvalds committed
322
			goto error0;
323

Linus Torvalds's avatar
Linus Torvalds committed
324 325 326
		/*
		 * BNO btree root block
		 */
327 328
		bp = xfs_growfs_get_hdr_buf(mp,
				XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
329 330
				BTOBB(mp->m_sb.sb_blocksize), 0,
				&xfs_allocbt_buf_ops);
331

332 333 334 335
		if (!bp) {
			error = ENOMEM;
			goto error0;
		}
336

337 338 339 340 341 342 343
		if (xfs_sb_version_hascrc(&mp->m_sb))
			xfs_btree_init_block(mp, bp, XFS_ABTB_CRC_MAGIC, 0, 1,
						agno, XFS_BTREE_CRC_BLOCKS);
		else
			xfs_btree_init_block(mp, bp, XFS_ABTB_MAGIC, 0, 1,
						agno, 0);

344
		arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
345 346 347
		arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp));
		arec->ar_blockcount = cpu_to_be32(
			agsize - be32_to_cpu(arec->ar_startblock));
348

349 350 351
		error = xfs_bwrite(bp);
		xfs_buf_relse(bp);
		if (error)
Linus Torvalds's avatar
Linus Torvalds committed
352
			goto error0;
353

Linus Torvalds's avatar
Linus Torvalds committed
354 355 356
		/*
		 * CNT btree root block
		 */
357 358
		bp = xfs_growfs_get_hdr_buf(mp,
				XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
359 360
				BTOBB(mp->m_sb.sb_blocksize), 0,
				&xfs_allocbt_buf_ops);
361 362 363 364
		if (!bp) {
			error = ENOMEM;
			goto error0;
		}
365

366 367 368 369 370 371 372
		if (xfs_sb_version_hascrc(&mp->m_sb))
			xfs_btree_init_block(mp, bp, XFS_ABTC_CRC_MAGIC, 0, 1,
						agno, XFS_BTREE_CRC_BLOCKS);
		else
			xfs_btree_init_block(mp, bp, XFS_ABTC_MAGIC, 0, 1,
						agno, 0);

373
		arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
374 375 376 377
		arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp));
		arec->ar_blockcount = cpu_to_be32(
			agsize - be32_to_cpu(arec->ar_startblock));
		nfree += be32_to_cpu(arec->ar_blockcount);
378

379 380 381
		error = xfs_bwrite(bp);
		xfs_buf_relse(bp);
		if (error)
Linus Torvalds's avatar
Linus Torvalds committed
382
			goto error0;
383

Linus Torvalds's avatar
Linus Torvalds committed
384 385 386
		/*
		 * INO btree root block
		 */
387 388
		bp = xfs_growfs_get_hdr_buf(mp,
				XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
389 390
				BTOBB(mp->m_sb.sb_blocksize), 0,
				&xfs_inobt_buf_ops);
391 392 393 394
		if (!bp) {
			error = ENOMEM;
			goto error0;
		}
395

396 397 398 399 400 401
		if (xfs_sb_version_hascrc(&mp->m_sb))
			xfs_btree_init_block(mp, bp, XFS_IBT_CRC_MAGIC, 0, 0,
						agno, XFS_BTREE_CRC_BLOCKS);
		else
			xfs_btree_init_block(mp, bp, XFS_IBT_MAGIC, 0, 0,
						agno, 0);
402

403 404 405
		error = xfs_bwrite(bp);
		xfs_buf_relse(bp);
		if (error)
Linus Torvalds's avatar
Linus Torvalds committed
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
			goto error0;
	}
	xfs_trans_agblocks_delta(tp, nfree);
	/*
	 * There are new blocks in the old last a.g.
	 */
	if (new) {
		/*
		 * Change the agi length.
		 */
		error = xfs_ialloc_read_agi(mp, tp, agno, &bp);
		if (error) {
			goto error0;
		}
		ASSERT(bp);
		agi = XFS_BUF_TO_AGI(bp);
422
		be32_add_cpu(&agi->agi_length, new);
Linus Torvalds's avatar
Linus Torvalds committed
423
		ASSERT(nagcount == oagcount ||
424
		       be32_to_cpu(agi->agi_length) == mp->m_sb.sb_agblocks);
Linus Torvalds's avatar
Linus Torvalds committed
425 426 427 428 429 430 431 432 433 434
		xfs_ialloc_log_agi(tp, bp, XFS_AGI_LENGTH);
		/*
		 * Change agf length.
		 */
		error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp);
		if (error) {
			goto error0;
		}
		ASSERT(bp);
		agf = XFS_BUF_TO_AGF(bp);
435
		be32_add_cpu(&agf->agf_length, new);
436 437
		ASSERT(be32_to_cpu(agf->agf_length) ==
		       be32_to_cpu(agi->agi_length));
438

439
		xfs_alloc_log_agf(tp, bp, XFS_AGF_LENGTH);
Linus Torvalds's avatar
Linus Torvalds committed
440 441 442 443
		/*
		 * Free the new space.
		 */
		error = xfs_free_extent(tp, XFS_AGB_TO_FSB(mp, agno,
444
			be32_to_cpu(agf->agf_length) - new), new);
Linus Torvalds's avatar
Linus Torvalds committed
445 446 447 448
		if (error) {
			goto error0;
		}
	}
449 450 451 452 453 454

	/*
	 * Update changed superblock fields transactionally. These are not
	 * seen by the rest of the world until the transaction commit applies
	 * them atomically to the superblock.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
455 456 457 458 459 460 461 462 463
	if (nagcount > oagcount)
		xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
	if (nb > mp->m_sb.sb_dblocks)
		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS,
				 nb - mp->m_sb.sb_dblocks);
	if (nfree)
		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree);
	if (dpct)
		xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
464
	error = xfs_trans_commit(tp, 0);
465
	if (error)
Linus Torvalds's avatar
Linus Torvalds committed
466
		return error;
467

Linus Torvalds's avatar
Linus Torvalds committed
468 469 470 471 472 473 474 475 476
	/* New allocation groups fully initialized, so update mount struct */
	if (nagimax)
		mp->m_maxagi = nagimax;
	if (mp->m_sb.sb_imax_pct) {
		__uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct;
		do_div(icount, 100);
		mp->m_maxicount = icount << mp->m_sb.sb_inopblog;
	} else
		mp->m_maxicount = 0;
477
	xfs_set_low_space_thresholds(mp);
478 479

	/* update secondary superblocks. */
Linus Torvalds's avatar
Linus Torvalds committed
480
	for (agno = 1; agno < nagcount; agno++) {
481 482 483 484 485 486 487 488
		error = 0;
		/*
		 * new secondary superblocks need to be zeroed, not read from
		 * disk as the contents of the new area we are growing into is
		 * completely unknown.
		 */
		if (agno < oagcount) {
			error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp,
Linus Torvalds's avatar
Linus Torvalds committed
489
				  XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
490
				  XFS_FSS_TO_BB(mp, 1), 0, &bp,
491
				  &xfs_sb_buf_ops);
492 493 494 495
		} else {
			bp = xfs_trans_get_buf(NULL, mp->m_ddev_targp,
				  XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
				  XFS_FSS_TO_BB(mp, 1), 0);
496
			if (bp) {
497
				bp->b_ops = &xfs_sb_buf_ops;
498
				xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
499
			} else
500 501 502
				error = ENOMEM;
		}

503 504 505 506 507 508 509
		/*
		 * If we get an error reading or writing alternate superblocks,
		 * continue.  xfs_repair chooses the "best" superblock based
		 * on most matches; if we break early, we'll leave more
		 * superblocks un-updated than updated, and xfs_repair may
		 * pick them over the properly-updated primary.
		 */
Linus Torvalds's avatar
Linus Torvalds committed
510
		if (error) {
511 512
			xfs_warn(mp,
		"error %d reading secondary superblock for ag %d",
Linus Torvalds's avatar
Linus Torvalds committed
513
				error, agno);
514 515
			saved_error = error;
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
516
		}
517
		xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb, XFS_SB_ALL_BITS);
518

519 520 521
		error = xfs_bwrite(bp);
		xfs_buf_relse(bp);
		if (error) {
522
			xfs_warn(mp,
Linus Torvalds's avatar
Linus Torvalds committed
523 524
		"write error %d updating secondary superblock for ag %d",
				error, agno);
525 526
			saved_error = error;
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
527 528
		}
	}
529
	return saved_error ? saved_error : error;
Linus Torvalds's avatar
Linus Torvalds committed
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570

 error0:
	xfs_trans_cancel(tp, XFS_TRANS_ABORT);
	return error;
}

static int
xfs_growfs_log_private(
	xfs_mount_t		*mp,	/* mount point for filesystem */
	xfs_growfs_log_t	*in)	/* growfs log input struct */
{
	xfs_extlen_t		nb;

	nb = in->newblocks;
	if (nb < XFS_MIN_LOG_BLOCKS || nb < XFS_B_TO_FSB(mp, XFS_MIN_LOG_BYTES))
		return XFS_ERROR(EINVAL);
	if (nb == mp->m_sb.sb_logblocks &&
	    in->isint == (mp->m_sb.sb_logstart != 0))
		return XFS_ERROR(EINVAL);
	/*
	 * Moving the log is hard, need new interfaces to sync
	 * the log first, hold off all activity while moving it.
	 * Can have shorter or longer log in the same space,
	 * or transform internal to external log or vice versa.
	 */
	return XFS_ERROR(ENOSYS);
}

/*
 * protected versions of growfs function acquire and release locks on the mount
 * point - exported through ioctls: XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG,
 * XFS_IOC_FSGROWFSRT
 */


int
xfs_growfs_data(
	xfs_mount_t		*mp,
	xfs_growfs_data_t	*in)
{
	int error;
571 572 573

	if (!capable(CAP_SYS_ADMIN))
		return XFS_ERROR(EPERM);
574
	if (!mutex_trylock(&mp->m_growlock))
Linus Torvalds's avatar
Linus Torvalds committed
575 576
		return XFS_ERROR(EWOULDBLOCK);
	error = xfs_growfs_data_private(mp, in);
577
	mutex_unlock(&mp->m_growlock);
Linus Torvalds's avatar
Linus Torvalds committed
578 579 580 581 582 583 584 585 586
	return error;
}

int
xfs_growfs_log(
	xfs_mount_t		*mp,
	xfs_growfs_log_t	*in)
{
	int error;
587 588 589

	if (!capable(CAP_SYS_ADMIN))
		return XFS_ERROR(EPERM);
590
	if (!mutex_trylock(&mp->m_growlock))
Linus Torvalds's avatar
Linus Torvalds committed
591 592
		return XFS_ERROR(EWOULDBLOCK);
	error = xfs_growfs_log_private(mp, in);
593
	mutex_unlock(&mp->m_growlock);
Linus Torvalds's avatar
Linus Torvalds committed
594 595 596 597 598 599 600 601 602 603 604 605
	return error;
}

/*
 * exported through ioctl XFS_IOC_FSCOUNTS
 */

int
xfs_fs_counts(
	xfs_mount_t		*mp,
	xfs_fsop_counts_t	*cnt)
{
606
	xfs_icsb_sync_counters(mp, XFS_ICSB_LAZY_COUNT);
Eric Sandeen's avatar
Eric Sandeen committed
607
	spin_lock(&mp->m_sb_lock);
608
	cnt->freedata = mp->m_sb.sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp);
Linus Torvalds's avatar
Linus Torvalds committed
609 610 611
	cnt->freertx = mp->m_sb.sb_frextents;
	cnt->freeino = mp->m_sb.sb_ifree;
	cnt->allocino = mp->m_sb.sb_icount;
Eric Sandeen's avatar
Eric Sandeen committed
612
	spin_unlock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
613 614 615 616 617 618 619 620
	return 0;
}

/*
 * exported through ioctl XFS_IOC_SET_RESBLKS & XFS_IOC_GET_RESBLKS
 *
 * xfs_reserve_blocks is called to set m_resblks
 * in the in-core mount table. The number of unused reserved blocks
621
 * is kept in m_resblks_avail.
Linus Torvalds's avatar
Linus Torvalds committed
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
 *
 * Reserve the requested number of blocks if available. Otherwise return
 * as many as possible to satisfy the request. The actual number
 * reserved are returned in outval
 *
 * A null inval pointer indicates that only the current reserved blocks
 * available  should  be returned no settings are changed.
 */

int
xfs_reserve_blocks(
	xfs_mount_t             *mp,
	__uint64_t              *inval,
	xfs_fsop_resblks_t      *outval)
{
637
	__int64_t		lcounter, delta, fdblks_delta;
Linus Torvalds's avatar
Linus Torvalds committed
638 639 640 641
	__uint64_t		request;

	/* If inval is null, report current values and return */
	if (inval == (__uint64_t *)NULL) {
642 643
		if (!outval)
			return EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
644 645
		outval->resblks = mp->m_resblks;
		outval->resblks_avail = mp->m_resblks_avail;
646
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
647 648 649
	}

	request = *inval;
650 651 652 653 654 655

	/*
	 * With per-cpu counters, this becomes an interesting
	 * problem. we needto work out if we are freeing or allocation
	 * blocks first, then we can do the modification as necessary.
	 *
Eric Sandeen's avatar
Eric Sandeen committed
656
	 * We do this under the m_sb_lock so that if we are near
657 658 659 660 661 662 663 664 665 666
	 * ENOSPC, we will hold out any changes while we work out
	 * what to do. This means that the amount of free space can
	 * change while we do this, so we need to retry if we end up
	 * trying to reserve more space than is available.
	 *
	 * We also use the xfs_mod_incore_sb() interface so that we
	 * don't have to care about whether per cpu counter are
	 * enabled, disabled or even compiled in....
	 */
retry:
Eric Sandeen's avatar
Eric Sandeen committed
667
	spin_lock(&mp->m_sb_lock);
668
	xfs_icsb_sync_counters_locked(mp, 0);
Linus Torvalds's avatar
Linus Torvalds committed
669 670 671 672 673

	/*
	 * If our previous reservation was larger than the current value,
	 * then move any unused blocks back to the free pool.
	 */
674
	fdblks_delta = 0;
Linus Torvalds's avatar
Linus Torvalds committed
675 676 677
	if (mp->m_resblks > request) {
		lcounter = mp->m_resblks_avail - request;
		if (lcounter  > 0) {		/* release unused blocks */
678
			fdblks_delta = lcounter;
Linus Torvalds's avatar
Linus Torvalds committed
679 680 681 682
			mp->m_resblks_avail -= lcounter;
		}
		mp->m_resblks = request;
	} else {
683 684 685
		__int64_t	free;

		free =  mp->m_sb.sb_fdblocks - XFS_ALLOC_SET_ASIDE(mp);
686 687 688
		if (!free)
			goto out; /* ENOSPC and fdblks_delta = 0 */

Linus Torvalds's avatar
Linus Torvalds committed
689
		delta = request - mp->m_resblks;
690
		lcounter = free - delta;
Linus Torvalds's avatar
Linus Torvalds committed
691 692
		if (lcounter < 0) {
			/* We can't satisfy the request, just get what we can */
693 694
			mp->m_resblks += free;
			mp->m_resblks_avail += free;
695
			fdblks_delta = -free;
Linus Torvalds's avatar
Linus Torvalds committed
696
		} else {
697
			fdblks_delta = -delta;
Linus Torvalds's avatar
Linus Torvalds committed
698 699 700 701
			mp->m_resblks = request;
			mp->m_resblks_avail += delta;
		}
	}
702
out:
703 704 705 706
	if (outval) {
		outval->resblks = mp->m_resblks;
		outval->resblks_avail = mp->m_resblks_avail;
	}
Eric Sandeen's avatar
Eric Sandeen committed
707
	spin_unlock(&mp->m_sb_lock);
708 709 710 711

	if (fdblks_delta) {
		/*
		 * If we are putting blocks back here, m_resblks_avail is
Malcolm Parsons's avatar
Malcolm Parsons committed
712
		 * already at its max so this will put it in the free pool.
713 714 715 716 717 718 719 720 721 722 723
		 *
		 * If we need space, we'll either succeed in getting it
		 * from the free block count or we'll get an enospc. If
		 * we get a ENOSPC, it means things changed while we were
		 * calculating fdblks_delta and so we should try again to
		 * see if there is anything left to reserve.
		 *
		 * Don't set the reserved flag here - we don't want to reserve
		 * the extra reserve blocks from the reserve.....
		 */
		int error;
724 725
		error = xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS,
						 fdblks_delta, 0);
726 727 728
		if (error == ENOSPC)
			goto retry;
	}
729
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
730 731
}

732 733 734 735 736 737 738
/*
 * Dump a transaction into the log that contains no real change. This is needed
 * to be able to make the log dirty or stamp the current tail LSN into the log
 * during the covering operation.
 *
 * We cannot use an inode here for this - that will push dirty state back up
 * into the VFS and then periodic inode flushing will prevent log covering from
739 740 741
 * making progress. Hence we log a field in the superblock instead and use a
 * synchronous transaction to ensure the superblock is immediately unpinned
 * and can be written back.
742
 */
743
int
744
xfs_fs_log_dummy(
745
	xfs_mount_t	*mp)
746
{
747
	xfs_trans_t	*tp;
748
	int		error;
749

750
	tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1, KM_SLEEP);
751
	error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0);
752
	if (error) {
753
		xfs_trans_cancel(tp, 0);
754
		return error;
755 756
	}

757 758
	/* log the UUID because it is an unchanging field */
	xfs_mod_sb(tp, XFS_SB_UUID);
759
	xfs_trans_set_sync(tp);
760
	return xfs_trans_commit(tp, 0);
761 762
}

Linus Torvalds's avatar
Linus Torvalds committed
763 764 765 766 767 768 769
int
xfs_fs_goingdown(
	xfs_mount_t	*mp,
	__uint32_t	inflags)
{
	switch (inflags) {
	case XFS_FSOP_GOING_FLAGS_DEFAULT: {
770
		struct super_block *sb = freeze_bdev(mp->m_super->s_bdev);
Linus Torvalds's avatar
Linus Torvalds committed
771

772
		if (sb && !IS_ERR(sb)) {
773
			xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT);
Linus Torvalds's avatar
Linus Torvalds committed
774 775
			thaw_bdev(sb->s_bdev, sb);
		}
776

Linus Torvalds's avatar
Linus Torvalds committed
777 778 779
		break;
	}
	case XFS_FSOP_GOING_FLAGS_LOGFLUSH:
780
		xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT);
Linus Torvalds's avatar
Linus Torvalds committed
781 782
		break;
	case XFS_FSOP_GOING_FLAGS_NOLOGFLUSH:
783 784
		xfs_force_shutdown(mp,
				SHUTDOWN_FORCE_UMOUNT | SHUTDOWN_LOG_IO_ERROR);
Linus Torvalds's avatar
Linus Torvalds committed
785 786 787 788 789 790 791
		break;
	default:
		return XFS_ERROR(EINVAL);
	}

	return 0;
}
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851

/*
 * Force a shutdown of the filesystem instantly while keeping the filesystem
 * consistent. We don't do an unmount here; just shutdown the shop, make sure
 * that absolutely nothing persistent happens to this filesystem after this
 * point.
 */
void
xfs_do_force_shutdown(
	xfs_mount_t	*mp,
	int		flags,
	char		*fname,
	int		lnnum)
{
	int		logerror;

	logerror = flags & SHUTDOWN_LOG_IO_ERROR;

	if (!(flags & SHUTDOWN_FORCE_UMOUNT)) {
		xfs_notice(mp,
	"%s(0x%x) called from line %d of file %s.  Return address = 0x%p",
			__func__, flags, lnnum, fname, __return_address);
	}
	/*
	 * No need to duplicate efforts.
	 */
	if (XFS_FORCED_SHUTDOWN(mp) && !logerror)
		return;

	/*
	 * This flags XFS_MOUNT_FS_SHUTDOWN, makes sure that we don't
	 * queue up anybody new on the log reservations, and wakes up
	 * everybody who's sleeping on log reservations to tell them
	 * the bad news.
	 */
	if (xfs_log_force_umount(mp, logerror))
		return;

	if (flags & SHUTDOWN_CORRUPT_INCORE) {
		xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_CORRUPT,
    "Corruption of in-memory data detected.  Shutting down filesystem");
		if (XFS_ERRLEVEL_HIGH <= xfs_error_level)
			xfs_stack_trace();
	} else if (!(flags & SHUTDOWN_FORCE_UMOUNT)) {
		if (logerror) {
			xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_LOGERROR,
		"Log I/O Error Detected.  Shutting down filesystem");
		} else if (flags & SHUTDOWN_DEVICE_REQ) {
			xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_IOERROR,
		"All device paths lost.  Shutting down filesystem");
		} else if (!(flags & SHUTDOWN_REMOTE_REQ)) {
			xfs_alert_tag(mp, XFS_PTAG_SHUTDOWN_IOERROR,
		"I/O Error Detected. Shutting down filesystem");
		}
	}
	if (!(flags & SHUTDOWN_FORCE_UMOUNT)) {
		xfs_alert(mp,
	"Please umount the filesystem and rectify the problem(s)");
	}
}