diff options
Diffstat (limited to 'fs/xfs/xfs_fsops.c')
-rw-r--r-- | fs/xfs/xfs_fsops.c | 616 |
1 files changed, 616 insertions, 0 deletions
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c new file mode 100644 index 000000000000..21213057c27f --- /dev/null +++ b/fs/xfs/xfs_fsops.c | |||
@@ -0,0 +1,616 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | |||
33 | #include "xfs.h" | ||
34 | #include "xfs_macros.h" | ||
35 | #include "xfs_types.h" | ||
36 | #include "xfs_inum.h" | ||
37 | #include "xfs_log.h" | ||
38 | #include "xfs_trans.h" | ||
39 | #include "xfs_sb.h" | ||
40 | #include "xfs_dir.h" | ||
41 | #include "xfs_dmapi.h" | ||
42 | #include "xfs_mount.h" | ||
43 | #include "xfs_ag.h" | ||
44 | #include "xfs_alloc_btree.h" | ||
45 | #include "xfs_bmap_btree.h" | ||
46 | #include "xfs_ialloc_btree.h" | ||
47 | #include "xfs_btree.h" | ||
48 | #include "xfs_error.h" | ||
49 | #include "xfs_alloc.h" | ||
50 | #include "xfs_ialloc.h" | ||
51 | #include "xfs_fsops.h" | ||
52 | #include "xfs_itable.h" | ||
53 | #include "xfs_rw.h" | ||
54 | #include "xfs_refcache.h" | ||
55 | #include "xfs_trans_space.h" | ||
56 | #include "xfs_rtalloc.h" | ||
57 | #include "xfs_dir2.h" | ||
58 | #include "xfs_attr_sf.h" | ||
59 | #include "xfs_dir_sf.h" | ||
60 | #include "xfs_dir2_sf.h" | ||
61 | #include "xfs_dinode.h" | ||
62 | #include "xfs_inode.h" | ||
63 | #include "xfs_inode_item.h" | ||
64 | |||
65 | /* | ||
66 | * File system operations | ||
67 | */ | ||
68 | |||
69 | int | ||
70 | xfs_fs_geometry( | ||
71 | xfs_mount_t *mp, | ||
72 | xfs_fsop_geom_t *geo, | ||
73 | int new_version) | ||
74 | { | ||
75 | geo->blocksize = mp->m_sb.sb_blocksize; | ||
76 | geo->rtextsize = mp->m_sb.sb_rextsize; | ||
77 | geo->agblocks = mp->m_sb.sb_agblocks; | ||
78 | geo->agcount = mp->m_sb.sb_agcount; | ||
79 | geo->logblocks = mp->m_sb.sb_logblocks; | ||
80 | geo->sectsize = mp->m_sb.sb_sectsize; | ||
81 | geo->inodesize = mp->m_sb.sb_inodesize; | ||
82 | geo->imaxpct = mp->m_sb.sb_imax_pct; | ||
83 | geo->datablocks = mp->m_sb.sb_dblocks; | ||
84 | geo->rtblocks = mp->m_sb.sb_rblocks; | ||
85 | geo->rtextents = mp->m_sb.sb_rextents; | ||
86 | geo->logstart = mp->m_sb.sb_logstart; | ||
87 | ASSERT(sizeof(geo->uuid)==sizeof(mp->m_sb.sb_uuid)); | ||
88 | memcpy(geo->uuid, &mp->m_sb.sb_uuid, sizeof(mp->m_sb.sb_uuid)); | ||
89 | if (new_version >= 2) { | ||
90 | geo->sunit = mp->m_sb.sb_unit; | ||
91 | geo->swidth = mp->m_sb.sb_width; | ||
92 | } | ||
93 | if (new_version >= 3) { | ||
94 | geo->version = XFS_FSOP_GEOM_VERSION; | ||
95 | geo->flags = | ||
96 | (XFS_SB_VERSION_HASATTR(&mp->m_sb) ? | ||
97 | XFS_FSOP_GEOM_FLAGS_ATTR : 0) | | ||
98 | (XFS_SB_VERSION_HASNLINK(&mp->m_sb) ? | ||
99 | XFS_FSOP_GEOM_FLAGS_NLINK : 0) | | ||
100 | (XFS_SB_VERSION_HASQUOTA(&mp->m_sb) ? | ||
101 | XFS_FSOP_GEOM_FLAGS_QUOTA : 0) | | ||
102 | (XFS_SB_VERSION_HASALIGN(&mp->m_sb) ? | ||
103 | XFS_FSOP_GEOM_FLAGS_IALIGN : 0) | | ||
104 | (XFS_SB_VERSION_HASDALIGN(&mp->m_sb) ? | ||
105 | XFS_FSOP_GEOM_FLAGS_DALIGN : 0) | | ||
106 | (XFS_SB_VERSION_HASSHARED(&mp->m_sb) ? | ||
107 | XFS_FSOP_GEOM_FLAGS_SHARED : 0) | | ||
108 | (XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) ? | ||
109 | XFS_FSOP_GEOM_FLAGS_EXTFLG : 0) | | ||
110 | (XFS_SB_VERSION_HASDIRV2(&mp->m_sb) ? | ||
111 | XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) | | ||
112 | (XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ? | ||
113 | XFS_FSOP_GEOM_FLAGS_SECTOR : 0); | ||
114 | geo->logsectsize = XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ? | ||
115 | mp->m_sb.sb_logsectsize : BBSIZE; | ||
116 | geo->rtsectsize = mp->m_sb.sb_blocksize; | ||
117 | geo->dirblocksize = mp->m_dirblksize; | ||
118 | } | ||
119 | if (new_version >= 4) { | ||
120 | geo->flags |= | ||
121 | (XFS_SB_VERSION_HASLOGV2(&mp->m_sb) ? | ||
122 | XFS_FSOP_GEOM_FLAGS_LOGV2 : 0); | ||
123 | geo->logsunit = mp->m_sb.sb_logsunit; | ||
124 | } | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int | ||
129 | xfs_growfs_data_private( | ||
130 | xfs_mount_t *mp, /* mount point for filesystem */ | ||
131 | xfs_growfs_data_t *in) /* growfs data input struct */ | ||
132 | { | ||
133 | xfs_agf_t *agf; | ||
134 | xfs_agi_t *agi; | ||
135 | xfs_agnumber_t agno; | ||
136 | xfs_extlen_t agsize; | ||
137 | xfs_extlen_t tmpsize; | ||
138 | xfs_alloc_rec_t *arec; | ||
139 | xfs_btree_sblock_t *block; | ||
140 | xfs_buf_t *bp; | ||
141 | int bucket; | ||
142 | int dpct; | ||
143 | int error; | ||
144 | xfs_agnumber_t nagcount; | ||
145 | xfs_agnumber_t nagimax = 0; | ||
146 | xfs_rfsblock_t nb, nb_mod; | ||
147 | xfs_rfsblock_t new; | ||
148 | xfs_rfsblock_t nfree; | ||
149 | xfs_agnumber_t oagcount; | ||
150 | int pct; | ||
151 | xfs_sb_t *sbp; | ||
152 | xfs_trans_t *tp; | ||
153 | |||
154 | nb = in->newblocks; | ||
155 | pct = in->imaxpct; | ||
156 | if (nb < mp->m_sb.sb_dblocks || pct < 0 || pct > 100) | ||
157 | return XFS_ERROR(EINVAL); | ||
158 | dpct = pct - mp->m_sb.sb_imax_pct; | ||
159 | error = xfs_read_buf(mp, mp->m_ddev_targp, | ||
160 | XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1), | ||
161 | XFS_FSS_TO_BB(mp, 1), 0, &bp); | ||
162 | if (error) | ||
163 | return error; | ||
164 | ASSERT(bp); | ||
165 | xfs_buf_relse(bp); | ||
166 | |||
167 | new = nb; /* use new as a temporary here */ | ||
168 | nb_mod = do_div(new, mp->m_sb.sb_agblocks); | ||
169 | nagcount = new + (nb_mod != 0); | ||
170 | if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) { | ||
171 | nagcount--; | ||
172 | nb = nagcount * mp->m_sb.sb_agblocks; | ||
173 | if (nb < mp->m_sb.sb_dblocks) | ||
174 | return XFS_ERROR(EINVAL); | ||
175 | } | ||
176 | new = nb - mp->m_sb.sb_dblocks; | ||
177 | oagcount = mp->m_sb.sb_agcount; | ||
178 | if (nagcount > oagcount) { | ||
179 | down_write(&mp->m_peraglock); | ||
180 | mp->m_perag = kmem_realloc(mp->m_perag, | ||
181 | sizeof(xfs_perag_t) * nagcount, | ||
182 | sizeof(xfs_perag_t) * oagcount, | ||
183 | KM_SLEEP); | ||
184 | memset(&mp->m_perag[oagcount], 0, | ||
185 | (nagcount - oagcount) * sizeof(xfs_perag_t)); | ||
186 | mp->m_flags |= XFS_MOUNT_32BITINODES; | ||
187 | nagimax = xfs_initialize_perag(mp, nagcount); | ||
188 | up_write(&mp->m_peraglock); | ||
189 | } | ||
190 | tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS); | ||
191 | if ((error = xfs_trans_reserve(tp, XFS_GROWFS_SPACE_RES(mp), | ||
192 | XFS_GROWDATA_LOG_RES(mp), 0, 0, 0))) { | ||
193 | xfs_trans_cancel(tp, 0); | ||
194 | return error; | ||
195 | } | ||
196 | |||
197 | nfree = 0; | ||
198 | for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) { | ||
199 | /* | ||
200 | * AG freelist header block | ||
201 | */ | ||
202 | bp = xfs_buf_get(mp->m_ddev_targp, | ||
203 | XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)), | ||
204 | XFS_FSS_TO_BB(mp, 1), 0); | ||
205 | agf = XFS_BUF_TO_AGF(bp); | ||
206 | memset(agf, 0, mp->m_sb.sb_sectsize); | ||
207 | INT_SET(agf->agf_magicnum, ARCH_CONVERT, XFS_AGF_MAGIC); | ||
208 | INT_SET(agf->agf_versionnum, ARCH_CONVERT, XFS_AGF_VERSION); | ||
209 | INT_SET(agf->agf_seqno, ARCH_CONVERT, agno); | ||
210 | if (agno == nagcount - 1) | ||
211 | agsize = | ||
212 | nb - | ||
213 | (agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks); | ||
214 | else | ||
215 | agsize = mp->m_sb.sb_agblocks; | ||
216 | INT_SET(agf->agf_length, ARCH_CONVERT, agsize); | ||
217 | INT_SET(agf->agf_roots[XFS_BTNUM_BNOi], ARCH_CONVERT, | ||
218 | XFS_BNO_BLOCK(mp)); | ||
219 | INT_SET(agf->agf_roots[XFS_BTNUM_CNTi], ARCH_CONVERT, | ||
220 | XFS_CNT_BLOCK(mp)); | ||
221 | INT_SET(agf->agf_levels[XFS_BTNUM_BNOi], ARCH_CONVERT, 1); | ||
222 | INT_SET(agf->agf_levels[XFS_BTNUM_CNTi], ARCH_CONVERT, 1); | ||
223 | agf->agf_flfirst = 0; | ||
224 | INT_SET(agf->agf_fllast, ARCH_CONVERT, XFS_AGFL_SIZE(mp) - 1); | ||
225 | agf->agf_flcount = 0; | ||
226 | tmpsize = agsize - XFS_PREALLOC_BLOCKS(mp); | ||
227 | INT_SET(agf->agf_freeblks, ARCH_CONVERT, tmpsize); | ||
228 | INT_SET(agf->agf_longest, ARCH_CONVERT, tmpsize); | ||
229 | error = xfs_bwrite(mp, bp); | ||
230 | if (error) { | ||
231 | goto error0; | ||
232 | } | ||
233 | /* | ||
234 | * AG inode header block | ||
235 | */ | ||
236 | bp = xfs_buf_get(mp->m_ddev_targp, | ||
237 | XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)), | ||
238 | XFS_FSS_TO_BB(mp, 1), 0); | ||
239 | agi = XFS_BUF_TO_AGI(bp); | ||
240 | memset(agi, 0, mp->m_sb.sb_sectsize); | ||
241 | INT_SET(agi->agi_magicnum, ARCH_CONVERT, XFS_AGI_MAGIC); | ||
242 | INT_SET(agi->agi_versionnum, ARCH_CONVERT, XFS_AGI_VERSION); | ||
243 | INT_SET(agi->agi_seqno, ARCH_CONVERT, agno); | ||
244 | INT_SET(agi->agi_length, ARCH_CONVERT, agsize); | ||
245 | agi->agi_count = 0; | ||
246 | INT_SET(agi->agi_root, ARCH_CONVERT, XFS_IBT_BLOCK(mp)); | ||
247 | INT_SET(agi->agi_level, ARCH_CONVERT, 1); | ||
248 | agi->agi_freecount = 0; | ||
249 | INT_SET(agi->agi_newino, ARCH_CONVERT, NULLAGINO); | ||
250 | INT_SET(agi->agi_dirino, ARCH_CONVERT, NULLAGINO); | ||
251 | for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) | ||
252 | INT_SET(agi->agi_unlinked[bucket], ARCH_CONVERT, | ||
253 | NULLAGINO); | ||
254 | error = xfs_bwrite(mp, bp); | ||
255 | if (error) { | ||
256 | goto error0; | ||
257 | } | ||
258 | /* | ||
259 | * BNO btree root block | ||
260 | */ | ||
261 | bp = xfs_buf_get(mp->m_ddev_targp, | ||
262 | XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)), | ||
263 | BTOBB(mp->m_sb.sb_blocksize), 0); | ||
264 | block = XFS_BUF_TO_SBLOCK(bp); | ||
265 | memset(block, 0, mp->m_sb.sb_blocksize); | ||
266 | INT_SET(block->bb_magic, ARCH_CONVERT, XFS_ABTB_MAGIC); | ||
267 | block->bb_level = 0; | ||
268 | INT_SET(block->bb_numrecs, ARCH_CONVERT, 1); | ||
269 | INT_SET(block->bb_leftsib, ARCH_CONVERT, NULLAGBLOCK); | ||
270 | INT_SET(block->bb_rightsib, ARCH_CONVERT, NULLAGBLOCK); | ||
271 | arec = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_alloc, | ||
272 | block, 1, mp->m_alloc_mxr[0]); | ||
273 | INT_SET(arec->ar_startblock, ARCH_CONVERT, | ||
274 | XFS_PREALLOC_BLOCKS(mp)); | ||
275 | INT_SET(arec->ar_blockcount, ARCH_CONVERT, | ||
276 | agsize - INT_GET(arec->ar_startblock, ARCH_CONVERT)); | ||
277 | error = xfs_bwrite(mp, bp); | ||
278 | if (error) { | ||
279 | goto error0; | ||
280 | } | ||
281 | /* | ||
282 | * CNT btree root block | ||
283 | */ | ||
284 | bp = xfs_buf_get(mp->m_ddev_targp, | ||
285 | XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)), | ||
286 | BTOBB(mp->m_sb.sb_blocksize), 0); | ||
287 | block = XFS_BUF_TO_SBLOCK(bp); | ||
288 | memset(block, 0, mp->m_sb.sb_blocksize); | ||
289 | INT_SET(block->bb_magic, ARCH_CONVERT, XFS_ABTC_MAGIC); | ||
290 | block->bb_level = 0; | ||
291 | INT_SET(block->bb_numrecs, ARCH_CONVERT, 1); | ||
292 | INT_SET(block->bb_leftsib, ARCH_CONVERT, NULLAGBLOCK); | ||
293 | INT_SET(block->bb_rightsib, ARCH_CONVERT, NULLAGBLOCK); | ||
294 | arec = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_alloc, | ||
295 | block, 1, mp->m_alloc_mxr[0]); | ||
296 | INT_SET(arec->ar_startblock, ARCH_CONVERT, | ||
297 | XFS_PREALLOC_BLOCKS(mp)); | ||
298 | INT_SET(arec->ar_blockcount, ARCH_CONVERT, | ||
299 | agsize - INT_GET(arec->ar_startblock, ARCH_CONVERT)); | ||
300 | nfree += INT_GET(arec->ar_blockcount, ARCH_CONVERT); | ||
301 | error = xfs_bwrite(mp, bp); | ||
302 | if (error) { | ||
303 | goto error0; | ||
304 | } | ||
305 | /* | ||
306 | * INO btree root block | ||
307 | */ | ||
308 | bp = xfs_buf_get(mp->m_ddev_targp, | ||
309 | XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)), | ||
310 | BTOBB(mp->m_sb.sb_blocksize), 0); | ||
311 | block = XFS_BUF_TO_SBLOCK(bp); | ||
312 | memset(block, 0, mp->m_sb.sb_blocksize); | ||
313 | INT_SET(block->bb_magic, ARCH_CONVERT, XFS_IBT_MAGIC); | ||
314 | block->bb_level = 0; | ||
315 | block->bb_numrecs = 0; | ||
316 | INT_SET(block->bb_leftsib, ARCH_CONVERT, NULLAGBLOCK); | ||
317 | INT_SET(block->bb_rightsib, ARCH_CONVERT, NULLAGBLOCK); | ||
318 | error = xfs_bwrite(mp, bp); | ||
319 | if (error) { | ||
320 | goto error0; | ||
321 | } | ||
322 | } | ||
323 | xfs_trans_agblocks_delta(tp, nfree); | ||
324 | /* | ||
325 | * There are new blocks in the old last a.g. | ||
326 | */ | ||
327 | if (new) { | ||
328 | /* | ||
329 | * Change the agi length. | ||
330 | */ | ||
331 | error = xfs_ialloc_read_agi(mp, tp, agno, &bp); | ||
332 | if (error) { | ||
333 | goto error0; | ||
334 | } | ||
335 | ASSERT(bp); | ||
336 | agi = XFS_BUF_TO_AGI(bp); | ||
337 | INT_MOD(agi->agi_length, ARCH_CONVERT, new); | ||
338 | ASSERT(nagcount == oagcount || | ||
339 | INT_GET(agi->agi_length, ARCH_CONVERT) == | ||
340 | mp->m_sb.sb_agblocks); | ||
341 | xfs_ialloc_log_agi(tp, bp, XFS_AGI_LENGTH); | ||
342 | /* | ||
343 | * Change agf length. | ||
344 | */ | ||
345 | error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp); | ||
346 | if (error) { | ||
347 | goto error0; | ||
348 | } | ||
349 | ASSERT(bp); | ||
350 | agf = XFS_BUF_TO_AGF(bp); | ||
351 | INT_MOD(agf->agf_length, ARCH_CONVERT, new); | ||
352 | ASSERT(INT_GET(agf->agf_length, ARCH_CONVERT) == | ||
353 | INT_GET(agi->agi_length, ARCH_CONVERT)); | ||
354 | /* | ||
355 | * Free the new space. | ||
356 | */ | ||
357 | error = xfs_free_extent(tp, XFS_AGB_TO_FSB(mp, agno, | ||
358 | INT_GET(agf->agf_length, ARCH_CONVERT) - new), new); | ||
359 | if (error) { | ||
360 | goto error0; | ||
361 | } | ||
362 | } | ||
363 | if (nagcount > oagcount) | ||
364 | xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount); | ||
365 | if (nb > mp->m_sb.sb_dblocks) | ||
366 | xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, | ||
367 | nb - mp->m_sb.sb_dblocks); | ||
368 | if (nfree) | ||
369 | xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree); | ||
370 | if (dpct) | ||
371 | xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct); | ||
372 | error = xfs_trans_commit(tp, 0, NULL); | ||
373 | if (error) { | ||
374 | return error; | ||
375 | } | ||
376 | /* New allocation groups fully initialized, so update mount struct */ | ||
377 | if (nagimax) | ||
378 | mp->m_maxagi = nagimax; | ||
379 | if (mp->m_sb.sb_imax_pct) { | ||
380 | __uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct; | ||
381 | do_div(icount, 100); | ||
382 | mp->m_maxicount = icount << mp->m_sb.sb_inopblog; | ||
383 | } else | ||
384 | mp->m_maxicount = 0; | ||
385 | for (agno = 1; agno < nagcount; agno++) { | ||
386 | error = xfs_read_buf(mp, mp->m_ddev_targp, | ||
387 | XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)), | ||
388 | XFS_FSS_TO_BB(mp, 1), 0, &bp); | ||
389 | if (error) { | ||
390 | xfs_fs_cmn_err(CE_WARN, mp, | ||
391 | "error %d reading secondary superblock for ag %d", | ||
392 | error, agno); | ||
393 | break; | ||
394 | } | ||
395 | sbp = XFS_BUF_TO_SBP(bp); | ||
396 | xfs_xlatesb(sbp, &mp->m_sb, -1, XFS_SB_ALL_BITS); | ||
397 | /* | ||
398 | * If we get an error writing out the alternate superblocks, | ||
399 | * just issue a warning and continue. The real work is | ||
400 | * already done and committed. | ||
401 | */ | ||
402 | if (!(error = xfs_bwrite(mp, bp))) { | ||
403 | continue; | ||
404 | } else { | ||
405 | xfs_fs_cmn_err(CE_WARN, mp, | ||
406 | "write error %d updating secondary superblock for ag %d", | ||
407 | error, agno); | ||
408 | break; /* no point in continuing */ | ||
409 | } | ||
410 | } | ||
411 | return 0; | ||
412 | |||
413 | error0: | ||
414 | xfs_trans_cancel(tp, XFS_TRANS_ABORT); | ||
415 | return error; | ||
416 | } | ||
417 | |||
418 | static int | ||
419 | xfs_growfs_log_private( | ||
420 | xfs_mount_t *mp, /* mount point for filesystem */ | ||
421 | xfs_growfs_log_t *in) /* growfs log input struct */ | ||
422 | { | ||
423 | xfs_extlen_t nb; | ||
424 | |||
425 | nb = in->newblocks; | ||
426 | if (nb < XFS_MIN_LOG_BLOCKS || nb < XFS_B_TO_FSB(mp, XFS_MIN_LOG_BYTES)) | ||
427 | return XFS_ERROR(EINVAL); | ||
428 | if (nb == mp->m_sb.sb_logblocks && | ||
429 | in->isint == (mp->m_sb.sb_logstart != 0)) | ||
430 | return XFS_ERROR(EINVAL); | ||
431 | /* | ||
432 | * Moving the log is hard, need new interfaces to sync | ||
433 | * the log first, hold off all activity while moving it. | ||
434 | * Can have shorter or longer log in the same space, | ||
435 | * or transform internal to external log or vice versa. | ||
436 | */ | ||
437 | return XFS_ERROR(ENOSYS); | ||
438 | } | ||
439 | |||
440 | /* | ||
441 | * protected versions of growfs function acquire and release locks on the mount | ||
442 | * point - exported through ioctls: XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG, | ||
443 | * XFS_IOC_FSGROWFSRT | ||
444 | */ | ||
445 | |||
446 | |||
447 | int | ||
448 | xfs_growfs_data( | ||
449 | xfs_mount_t *mp, | ||
450 | xfs_growfs_data_t *in) | ||
451 | { | ||
452 | int error; | ||
453 | if (!cpsema(&mp->m_growlock)) | ||
454 | return XFS_ERROR(EWOULDBLOCK); | ||
455 | error = xfs_growfs_data_private(mp, in); | ||
456 | vsema(&mp->m_growlock); | ||
457 | return error; | ||
458 | } | ||
459 | |||
460 | int | ||
461 | xfs_growfs_log( | ||
462 | xfs_mount_t *mp, | ||
463 | xfs_growfs_log_t *in) | ||
464 | { | ||
465 | int error; | ||
466 | if (!cpsema(&mp->m_growlock)) | ||
467 | return XFS_ERROR(EWOULDBLOCK); | ||
468 | error = xfs_growfs_log_private(mp, in); | ||
469 | vsema(&mp->m_growlock); | ||
470 | return error; | ||
471 | } | ||
472 | |||
473 | /* | ||
474 | * exported through ioctl XFS_IOC_FSCOUNTS | ||
475 | */ | ||
476 | |||
477 | int | ||
478 | xfs_fs_counts( | ||
479 | xfs_mount_t *mp, | ||
480 | xfs_fsop_counts_t *cnt) | ||
481 | { | ||
482 | unsigned long s; | ||
483 | |||
484 | s = XFS_SB_LOCK(mp); | ||
485 | cnt->freedata = mp->m_sb.sb_fdblocks; | ||
486 | cnt->freertx = mp->m_sb.sb_frextents; | ||
487 | cnt->freeino = mp->m_sb.sb_ifree; | ||
488 | cnt->allocino = mp->m_sb.sb_icount; | ||
489 | XFS_SB_UNLOCK(mp, s); | ||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | /* | ||
494 | * exported through ioctl XFS_IOC_SET_RESBLKS & XFS_IOC_GET_RESBLKS | ||
495 | * | ||
496 | * xfs_reserve_blocks is called to set m_resblks | ||
497 | * in the in-core mount table. The number of unused reserved blocks | ||
498 | * is kept in m_resbls_avail. | ||
499 | * | ||
500 | * Reserve the requested number of blocks if available. Otherwise return | ||
501 | * as many as possible to satisfy the request. The actual number | ||
502 | * reserved are returned in outval | ||
503 | * | ||
504 | * A null inval pointer indicates that only the current reserved blocks | ||
505 | * available should be returned no settings are changed. | ||
506 | */ | ||
507 | |||
508 | int | ||
509 | xfs_reserve_blocks( | ||
510 | xfs_mount_t *mp, | ||
511 | __uint64_t *inval, | ||
512 | xfs_fsop_resblks_t *outval) | ||
513 | { | ||
514 | __int64_t lcounter, delta; | ||
515 | __uint64_t request; | ||
516 | unsigned long s; | ||
517 | |||
518 | /* If inval is null, report current values and return */ | ||
519 | |||
520 | if (inval == (__uint64_t *)NULL) { | ||
521 | outval->resblks = mp->m_resblks; | ||
522 | outval->resblks_avail = mp->m_resblks_avail; | ||
523 | return(0); | ||
524 | } | ||
525 | |||
526 | request = *inval; | ||
527 | s = XFS_SB_LOCK(mp); | ||
528 | |||
529 | /* | ||
530 | * If our previous reservation was larger than the current value, | ||
531 | * then move any unused blocks back to the free pool. | ||
532 | */ | ||
533 | |||
534 | if (mp->m_resblks > request) { | ||
535 | lcounter = mp->m_resblks_avail - request; | ||
536 | if (lcounter > 0) { /* release unused blocks */ | ||
537 | mp->m_sb.sb_fdblocks += lcounter; | ||
538 | mp->m_resblks_avail -= lcounter; | ||
539 | } | ||
540 | mp->m_resblks = request; | ||
541 | } else { | ||
542 | delta = request - mp->m_resblks; | ||
543 | lcounter = mp->m_sb.sb_fdblocks - delta; | ||
544 | if (lcounter < 0) { | ||
545 | /* We can't satisfy the request, just get what we can */ | ||
546 | mp->m_resblks += mp->m_sb.sb_fdblocks; | ||
547 | mp->m_resblks_avail += mp->m_sb.sb_fdblocks; | ||
548 | mp->m_sb.sb_fdblocks = 0; | ||
549 | } else { | ||
550 | mp->m_sb.sb_fdblocks = lcounter; | ||
551 | mp->m_resblks = request; | ||
552 | mp->m_resblks_avail += delta; | ||
553 | } | ||
554 | } | ||
555 | |||
556 | outval->resblks = mp->m_resblks; | ||
557 | outval->resblks_avail = mp->m_resblks_avail; | ||
558 | XFS_SB_UNLOCK(mp, s); | ||
559 | return(0); | ||
560 | } | ||
561 | |||
562 | void | ||
563 | xfs_fs_log_dummy(xfs_mount_t *mp) | ||
564 | { | ||
565 | xfs_trans_t *tp; | ||
566 | xfs_inode_t *ip; | ||
567 | |||
568 | |||
569 | tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1); | ||
570 | atomic_inc(&mp->m_active_trans); | ||
571 | if (xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0)) { | ||
572 | xfs_trans_cancel(tp, 0); | ||
573 | return; | ||
574 | } | ||
575 | |||
576 | ip = mp->m_rootip; | ||
577 | xfs_ilock(ip, XFS_ILOCK_EXCL); | ||
578 | |||
579 | xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); | ||
580 | xfs_trans_ihold(tp, ip); | ||
581 | xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); | ||
582 | xfs_trans_set_sync(tp); | ||
583 | xfs_trans_commit(tp, 0, NULL); | ||
584 | |||
585 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
586 | } | ||
587 | |||
588 | int | ||
589 | xfs_fs_goingdown( | ||
590 | xfs_mount_t *mp, | ||
591 | __uint32_t inflags) | ||
592 | { | ||
593 | switch (inflags) { | ||
594 | case XFS_FSOP_GOING_FLAGS_DEFAULT: { | ||
595 | struct vfs *vfsp = XFS_MTOVFS(mp); | ||
596 | struct super_block *sb = freeze_bdev(vfsp->vfs_super->s_bdev); | ||
597 | |||
598 | if (sb) { | ||
599 | xfs_force_shutdown(mp, XFS_FORCE_UMOUNT); | ||
600 | thaw_bdev(sb->s_bdev, sb); | ||
601 | } | ||
602 | |||
603 | break; | ||
604 | } | ||
605 | case XFS_FSOP_GOING_FLAGS_LOGFLUSH: | ||
606 | xfs_force_shutdown(mp, XFS_FORCE_UMOUNT); | ||
607 | break; | ||
608 | case XFS_FSOP_GOING_FLAGS_NOLOGFLUSH: | ||
609 | xfs_force_shutdown(mp, XFS_FORCE_UMOUNT|XFS_LOG_IO_ERROR); | ||
610 | break; | ||
611 | default: | ||
612 | return XFS_ERROR(EINVAL); | ||
613 | } | ||
614 | |||
615 | return 0; | ||
616 | } | ||