vserver 2.0 rc7
[linux-2.6.git] / fs / xfs / xfs_fsops.c
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 }