1 /* $NetBSD: ffs_wapbl.c,v 1.30 2015/03/28 19:24:04 maxv Exp $ */
4 * Copyright (c) 2003,2006,2008 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Wasabi Systems, Inc.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: ffs_wapbl.c,v 1.30 2015/03/28 19:24:04 maxv Exp $");
35 #define WAPBL_INTERNAL
37 #if defined(_KERNEL_OPT)
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/vnode.h>
45 #include <sys/mount.h>
48 #include <sys/ioctl.h>
49 #include <sys/errno.h>
50 #include <sys/kauth.h>
51 #include <sys/wapbl.h>
53 #include <ufs/ufs/inode.h>
54 #include <ufs/ufs/quota.h>
55 #include <ufs/ufs/ufsmount.h>
56 #include <ufs/ufs/ufs_bswap.h>
57 #include <ufs/ufs/ufs_extern.h>
58 #include <ufs/ufs/ufs_wapbl.h>
60 #include <ufs/ffs/fs.h>
61 #include <ufs/ffs/ffs_extern.h>
65 int ffs_wapbl_debug
= 1;
66 #define DPRINTF(fmt, args...) \
68 if (ffs_wapbl_debug) \
69 printf("%s:%d "fmt, __func__ , __LINE__, ##args); \
70 } while (/* CONSTCOND */0)
72 #define DPRINTF(fmt, args...) \
75 } while (/* CONSTCOND */0)
78 static int ffs_superblock_layout(struct fs
*);
79 static int wapbl_log_position(struct mount
*, struct fs
*, struct vnode
*,
80 daddr_t
*, size_t *, size_t *, uint64_t *);
81 static int wapbl_create_infs_log(struct mount
*, struct fs
*, struct vnode
*,
82 daddr_t
*, size_t *, uint64_t *);
83 static void wapbl_find_log_start(struct mount
*, struct vnode
*, off_t
,
84 daddr_t
*, daddr_t
*, size_t *);
85 static int wapbl_remove_log(struct mount
*);
86 static int wapbl_allocate_log_file(struct mount
*, struct vnode
*,
87 daddr_t
*, size_t *, uint64_t *);
90 * Return the super block layout format - UFS1 or UFS2.
91 * WAPBL only works with UFS2 layout (which is still available
94 * XXX Should this be in ufs/ffs/fs.h? Same style of check is
95 * also used in ffs_alloc.c in a few places.
98 ffs_superblock_layout(struct fs
*fs
)
100 if ((fs
->fs_magic
== FS_UFS1_MAGIC
) &&
101 ((fs
->fs_old_flags
& FS_FLAGS_UPDATED
) == 0))
108 * This function is invoked after a log is replayed to
109 * disk to perform logical cleanup actions as described by
113 ffs_wapbl_replay_finish(struct mount
*mp
)
115 struct wapbl_replay
*wr
= mp
->mnt_wapbl_replay
;
122 KDASSERT((mp
->mnt_flag
& MNT_RDONLY
) == 0);
124 for (i
= 0; i
< wr
->wr_inodescnt
; i
++) {
127 error
= VFS_VGET(mp
, wr
->wr_inodes
[i
].wr_inumber
, &vp
);
129 printf("ffs_wapbl_replay_finish: "
130 "unable to cleanup inode %" PRIu32
"\n",
131 wr
->wr_inodes
[i
].wr_inumber
);
135 KDASSERT(wr
->wr_inodes
[i
].wr_inumber
== ip
->i_number
);
137 printf("ffs_wapbl_replay_finish: "
138 "cleaning inode %" PRIu64
" size=%" PRIu64
" mode=%o nlink=%d\n",
139 ip
->i_number
, ip
->i_size
, ip
->i_mode
, ip
->i_nlink
);
141 KASSERT(ip
->i_nlink
== 0);
144 * The journal may have left partially allocated inodes in mode
145 * zero. This may occur if a crash occurs betweeen the node
146 * allocation in ffs_nodeallocg and when the node is properly
147 * initialized in ufs_makeinode. If so, just dallocate them.
149 if (ip
->i_mode
== 0) {
150 error
= UFS_WAPBL_BEGIN(mp
);
152 printf("ffs_wapbl_replay_finish: "
153 "unable to cleanup inode %" PRIu32
"\n",
154 wr
->wr_inodes
[i
].wr_inumber
);
156 ffs_vfree(vp
, ip
->i_number
,
157 wr
->wr_inodes
[i
].wr_imode
);
163 wapbl_replay_stop(wr
);
164 wapbl_replay_free(wr
);
165 mp
->mnt_wapbl_replay
= NULL
;
168 /* Callback for wapbl */
170 ffs_wapbl_sync_metadata(struct mount
*mp
, daddr_t
*deallocblks
,
171 int *dealloclens
, int dealloccnt
)
173 struct ufsmount
*ump
= VFSTOUFS(mp
);
174 struct fs
*fs
= ump
->um_fs
;
175 int i
, error __diagused
;
177 #ifdef WAPBL_DEBUG_INODES
178 ufs_wapbl_verify_inodes(mp
, "ffs_wapbl_sync_metadata");
181 for (i
= 0; i
< dealloccnt
; i
++) {
183 * blkfree errors are unreported, might silently fail
184 * if it cannot read the cylinder group block
186 ffs_blkfree(fs
, ump
->um_devvp
,
187 FFS_DBTOFSB(fs
, deallocblks
[i
]), dealloclens
[i
], -1);
191 fs
->fs_time
= time_second
;
192 error
= ffs_cgupdate(ump
, 0);
197 ffs_wapbl_abort_sync_metadata(struct mount
*mp
, daddr_t
*deallocblks
,
198 int *dealloclens
, int dealloccnt
)
200 struct ufsmount
*ump
= VFSTOUFS(mp
);
201 struct fs
*fs
= ump
->um_fs
;
204 for (i
= 0; i
< dealloccnt
; i
++) {
206 * Since the above blkfree may have failed, this blkalloc might
207 * fail as well, so don't check its error. Note that if the
208 * blkfree succeeded above, then this shouldn't fail because
209 * the buffer will be locked in the current transaction.
211 ffs_blkalloc_ump(ump
, FFS_DBTOFSB(fs
, deallocblks
[i
]),
217 wapbl_remove_log(struct mount
*mp
)
219 struct ufsmount
*ump
= VFSTOUFS(mp
);
220 struct fs
*fs
= ump
->um_fs
;
226 /* If super block layout is too old to support WAPBL, return */
227 if (ffs_superblock_layout(fs
) < 2)
230 /* If all the log locators are 0, just clean up */
231 if (fs
->fs_journallocs
[0] == 0 &&
232 fs
->fs_journallocs
[1] == 0 &&
233 fs
->fs_journallocs
[2] == 0 &&
234 fs
->fs_journallocs
[3] == 0) {
235 DPRINTF("empty locators, just clear\n");
239 switch (fs
->fs_journal_location
) {
240 case UFS_WAPBL_JOURNALLOC_NONE
:
245 case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM
:
246 log_ino
= fs
->fs_journallocs
[UFS_WAPBL_INFS_INO
];
247 DPRINTF("in-fs log, ino = %" PRId64
"\n",log_ino
);
249 /* if no existing log inode, just clear all fields and bail */
252 error
= VFS_VGET(mp
, log_ino
, &vp
);
254 printf("ffs_wapbl: vget failed %d\n",
256 /* clear out log info on error */
260 KASSERT(log_ino
== ip
->i_number
);
261 if ((ip
->i_flags
& SF_LOG
) == 0) {
262 printf("ffs_wapbl: try to clear non-log inode "
263 "%" PRId64
"\n", log_ino
);
265 /* clear out log info on error */
270 * remove the log inode by setting its link count back
274 DIP_ASSIGN(ip
, nlink
, 0);
277 case UFS_WAPBL_JOURNALLOC_END_PARTITION
:
278 DPRINTF("end-of-partition log\n");
279 /* no extra work required */
283 printf("ffs_wapbl: unknown journal type %d\n",
284 fs
->fs_journal_location
);
290 /* Clear out all previous knowledge of journal */
291 fs
->fs_journal_version
= 0;
292 fs
->fs_journal_location
= 0;
293 fs
->fs_journal_flags
= 0;
294 fs
->fs_journallocs
[0] = 0;
295 fs
->fs_journallocs
[1] = 0;
296 fs
->fs_journallocs
[2] = 0;
297 fs
->fs_journallocs
[3] = 0;
298 (void) ffs_sbupdate(ump
, MNT_WAIT
);
304 ffs_wapbl_start(struct mount
*mp
)
306 struct ufsmount
*ump
= VFSTOUFS(mp
);
307 struct fs
*fs
= ump
->um_fs
;
308 struct vnode
*devvp
= ump
->um_devvp
;
315 if (mp
->mnt_wapbl
== NULL
) {
316 if (fs
->fs_journal_flags
& UFS_WAPBL_FLAGS_CLEAR_LOG
) {
317 /* Clear out any existing journal file */
318 error
= wapbl_remove_log(mp
);
323 if (mp
->mnt_flag
& MNT_LOG
) {
324 KDASSERT(fs
->fs_ronly
== 0);
326 /* WAPBL needs UFS2 format super block */
327 if (ffs_superblock_layout(fs
) < 2) {
328 printf("%s fs superblock in old format, "
330 VFSTOUFS(mp
)->um_fs
->fs_fsmnt
);
331 mp
->mnt_flag
&= ~MNT_LOG
;
335 error
= wapbl_log_position(mp
, fs
, devvp
, &off
,
336 &count
, &blksize
, &extradata
);
340 error
= wapbl_start(&mp
->mnt_wapbl
, mp
, devvp
, off
,
341 count
, blksize
, mp
->mnt_wapbl_replay
,
342 ffs_wapbl_sync_metadata
,
343 ffs_wapbl_abort_sync_metadata
);
347 mp
->mnt_wapbl_op
= &wapbl_ops
;
350 printf("%s: enabling logging\n", fs
->fs_fsmnt
);
353 if ((fs
->fs_flags
& FS_DOWAPBL
) == 0) {
354 fs
->fs_flags
|= FS_DOWAPBL
;
355 if ((error
= UFS_WAPBL_BEGIN(mp
)) != 0)
357 error
= ffs_sbupdate(ump
, MNT_WAIT
);
363 error
= wapbl_flush(mp
->mnt_wapbl
, 1);
367 } else if (fs
->fs_flags
& FS_DOWAPBL
) {
369 fs
->fs_flags
&= ~FS_DOWAPBL
;
374 * It is recommended that you finish replay with logging enabled.
375 * However, even if logging is not enabled, the remaining log
376 * replay should be safely recoverable with an fsck, so perform
379 if ((fs
->fs_ronly
== 0) && mp
->mnt_wapbl_replay
) {
380 int saveflag
= mp
->mnt_flag
& MNT_RDONLY
;
382 * Make sure MNT_RDONLY is not set so that the inode
383 * cleanup in ufs_inactive will actually do its work.
385 mp
->mnt_flag
&= ~MNT_RDONLY
;
386 ffs_wapbl_replay_finish(mp
);
387 mp
->mnt_flag
|= saveflag
;
388 KASSERT(fs
->fs_ronly
== 0);
393 ffs_wapbl_stop(mp
, MNT_FORCE
);
398 ffs_wapbl_stop(struct mount
*mp
, int force
)
400 struct ufsmount
*ump
= VFSTOUFS(mp
);
401 struct fs
*fs
= ump
->um_fs
;
405 KDASSERT(fs
->fs_ronly
== 0);
408 * Make sure turning off FS_DOWAPBL is only removed
409 * as the only change in the final flush since otherwise
410 * a transaction may reorder writes.
412 error
= wapbl_flush(mp
->mnt_wapbl
, 1);
417 error
= UFS_WAPBL_BEGIN(mp
);
422 KASSERT(fs
->fs_flags
& FS_DOWAPBL
);
424 fs
->fs_flags
&= ~FS_DOWAPBL
;
425 error
= ffs_sbupdate(ump
, MNT_WAIT
);
426 KASSERT(error
== 0); /* XXX a bit drastic! */
429 error
= wapbl_stop(mp
->mnt_wapbl
, force
);
432 fs
->fs_flags
|= FS_DOWAPBL
;
435 fs
->fs_flags
&= ~FS_DOWAPBL
; /* Repeat in case of forced error */
436 mp
->mnt_wapbl
= NULL
;
439 printf("%s: disabled logging\n", fs
->fs_fsmnt
);
447 ffs_wapbl_replay_start(struct mount
*mp
, struct fs
*fs
, struct vnode
*devvp
)
456 * WAPBL needs UFS2 format super block, if we got here with a
457 * UFS1 format super block something is amiss...
459 if (ffs_superblock_layout(fs
) < 2)
462 error
= wapbl_log_position(mp
, fs
, devvp
, &off
, &count
, &blksize
,
468 error
= wapbl_replay_start(&mp
->mnt_wapbl_replay
, devvp
, off
,
473 mp
->mnt_wapbl_op
= &wapbl_ops
;
479 * If the superblock doesn't already have a recorded journal location
480 * then we allocate the journal in one of two positions:
482 * - At the end of the partition after the filesystem if there's
483 * enough space. "Enough space" is defined as >= 1MB of journal
484 * per 1GB of filesystem or 64MB, whichever is smaller.
486 * - Inside the filesystem. We try to allocate a contiguous journal
487 * based on the total filesystem size - the target is 1MB of journal
488 * per 1GB of filesystem, up to a maximum journal size of 64MB. As
489 * a worst case allowing for fragmentation, we'll allocate a journal
490 * 1/4 of the desired size but never smaller than 1MB.
492 * XXX In the future if we allow for non-contiguous journal files we
493 * can tighten the above restrictions.
496 * These seems like a lot of duplication both here and in some of
497 * the userland tools (fsck_ffs, dumpfs, tunefs) with similar
498 * "switch (fs_journal_location)" constructs. Can we centralise
499 * this sort of code somehow/somewhere?
502 wapbl_log_position(struct mount
*mp
, struct fs
*fs
, struct vnode
*devvp
,
503 daddr_t
*startp
, size_t *countp
, size_t *blksizep
, uint64_t *extradatap
)
505 struct ufsmount
*ump
= VFSTOUFS(mp
);
506 daddr_t logstart
, logend
, desired_logsize
;
511 if (fs
->fs_journal_version
== UFS_WAPBL_VERSION
) {
512 switch (fs
->fs_journal_location
) {
513 case UFS_WAPBL_JOURNALLOC_END_PARTITION
:
514 DPRINTF("found existing end-of-partition log\n");
515 *startp
= fs
->fs_journallocs
[UFS_WAPBL_EPART_ADDR
];
516 *countp
= fs
->fs_journallocs
[UFS_WAPBL_EPART_COUNT
];
517 *blksizep
= fs
->fs_journallocs
[UFS_WAPBL_EPART_BLKSZ
];
518 DPRINTF(" start = %" PRId64
", size = %zu, "
519 "blksize = %zu\n", *startp
, *countp
, *blksizep
);
522 case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM
:
523 DPRINTF("found existing in-filesystem log\n");
524 *startp
= fs
->fs_journallocs
[UFS_WAPBL_INFS_ADDR
];
525 *countp
= fs
->fs_journallocs
[UFS_WAPBL_INFS_COUNT
];
526 *blksizep
= fs
->fs_journallocs
[UFS_WAPBL_INFS_BLKSZ
];
527 DPRINTF(" start = %" PRId64
", size = %zu, "
528 "blksize = %zu\n", *startp
, *countp
, *blksizep
);
532 printf("ffs_wapbl: unknown journal type %d\n",
533 fs
->fs_journal_location
);
539 ffs_lfragtosize(fs
, fs
->fs_size
) / UFS_WAPBL_JOURNAL_SCALE
;
540 DPRINTF("desired log size = %" PRId64
" kB\n", desired_logsize
/ 1024);
541 desired_logsize
= max(desired_logsize
, UFS_WAPBL_MIN_JOURNAL_SIZE
);
542 desired_logsize
= min(desired_logsize
, UFS_WAPBL_MAX_JOURNAL_SIZE
);
543 DPRINTF("adjusted desired log size = %" PRId64
" kB\n",
544 desired_logsize
/ 1024);
546 /* Is there space after after filesystem on partition for log? */
547 logstart
= FFS_FSBTODB(fs
, fs
->fs_size
);
548 error
= getdisksize(devvp
, &numsecs
, &secsize
);
551 KDASSERT(secsize
!= 0);
552 logend
= btodb(numsecs
* secsize
);
554 if (dbtob(logend
- logstart
) >= desired_logsize
) {
555 DPRINTF("enough space, use end-of-partition log\n");
557 location
= UFS_WAPBL_JOURNALLOC_END_PARTITION
;
561 *countp
= (logend
- logstart
);
564 /* convert to physical block numbers */
565 *startp
= dbtob(*startp
) / secsize
;
566 *countp
= dbtob(*countp
) / secsize
;
568 fs
->fs_journallocs
[UFS_WAPBL_EPART_ADDR
] = *startp
;
569 fs
->fs_journallocs
[UFS_WAPBL_EPART_COUNT
] = *countp
;
570 fs
->fs_journallocs
[UFS_WAPBL_EPART_BLKSZ
] = *blksizep
;
571 fs
->fs_journallocs
[UFS_WAPBL_EPART_UNUSED
] = *extradatap
;
573 DPRINTF("end-of-partition has only %" PRId64
" free\n",
576 location
= UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM
;
579 error
= wapbl_create_infs_log(mp
, fs
, devvp
,
580 startp
, countp
, extradatap
);
581 ffs_sync(mp
, MNT_WAIT
, FSCRED
);
583 /* convert to physical block numbers */
584 *startp
= dbtob(*startp
) / secsize
;
585 *countp
= dbtob(*countp
) / secsize
;
587 fs
->fs_journallocs
[UFS_WAPBL_INFS_ADDR
] = *startp
;
588 fs
->fs_journallocs
[UFS_WAPBL_INFS_COUNT
] = *countp
;
589 fs
->fs_journallocs
[UFS_WAPBL_INFS_BLKSZ
] = *blksizep
;
590 fs
->fs_journallocs
[UFS_WAPBL_INFS_INO
] = *extradatap
;
594 /* update superblock with log location */
595 fs
->fs_journal_version
= UFS_WAPBL_VERSION
;
596 fs
->fs_journal_location
= location
;
597 fs
->fs_journal_flags
= 0;
599 error
= ffs_sbupdate(ump
, MNT_WAIT
);
606 * Try to create a journal log inside the filesystem.
609 wapbl_create_infs_log(struct mount
*mp
, struct fs
*fs
, struct vnode
*devvp
,
610 daddr_t
*startp
, size_t *countp
, uint64_t *extradatap
)
612 struct vnode
*vp
, *rvp
;
617 if ((error
= VFS_ROOT(mp
, &rvp
)) != 0)
624 error
= vcache_new(mp
, rvp
, &va
, NOCRED
, &vp
);
629 error
= vn_lock(vp
, LK_EXCLUSIVE
);
636 ip
->i_flags
= SF_LOG
;
637 DIP_ASSIGN(ip
, flags
, ip
->i_flags
);
639 DIP_ASSIGN(ip
, nlink
, 1);
640 ip
->i_flag
|= IN_ACCESS
| IN_CHANGE
| IN_UPDATE
;
641 ffs_update(vp
, NULL
, NULL
, UPDATE_WAIT
);
643 if ((error
= wapbl_allocate_log_file(mp
, vp
,
644 startp
, countp
, extradatap
)) != 0) {
646 * If we couldn't allocate the space for the log file,
647 * remove the inode by setting its link count back to
651 DIP_ASSIGN(ip
, nlink
, 0);
659 * Now that we have the place-holder inode for the journal,
660 * we don't need the vnode ever again.
669 wapbl_allocate_log_file(struct mount
*mp
, struct vnode
*vp
,
670 daddr_t
*startp
, size_t *countp
, uint64_t *extradatap
)
672 struct ufsmount
*ump
= VFSTOUFS(mp
);
673 struct fs
*fs
= ump
->um_fs
;
674 daddr_t addr
, indir_addr
;
680 /* check if there's a suggested log size */
681 if (fs
->fs_journal_flags
& UFS_WAPBL_FLAGS_CREATE_LOG
&&
682 fs
->fs_journal_location
== UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM
)
683 logsize
= fs
->fs_journallocs
[UFS_WAPBL_INFS_COUNT
];
685 if (vp
->v_size
> 0) {
686 printf("%s: file size (%" PRId64
") non zero\n", __func__
,
690 wapbl_find_log_start(mp
, vp
, logsize
, &addr
, &indir_addr
, &size
);
692 printf("%s: log not allocated, largest extent is "
693 "%" PRId64
"MB\n", __func__
,
694 ffs_lblktosize(fs
, size
) / (1024 * 1024));
698 logsize
= ffs_lblktosize(fs
, size
); /* final log size */
700 VTOI(vp
)->i_ffs_first_data_blk
= addr
;
701 VTOI(vp
)->i_ffs_first_indir_blk
= indir_addr
;
703 error
= GOP_ALLOC(vp
, 0, logsize
, B_CONTIG
, FSCRED
);
705 printf("%s: GOP_ALLOC error %d\n", __func__
, error
);
709 *startp
= FFS_FSBTODB(fs
, addr
);
710 *countp
= btodb(logsize
);
711 *extradatap
= VTOI(vp
)->i_number
;
717 * Find a suitable location for the journal in the filesystem.
719 * Our strategy here is to look for a contiguous block of free space
720 * at least "logfile" MB in size (plus room for any indirect blocks).
721 * We start at the middle of the filesystem and check each cylinder
722 * group working outwards. If "logfile" MB is not available as a
723 * single contigous chunk, then return the address and size of the
724 * largest chunk found.
727 * At what stage does the search fail? Is if the largest space we could
728 * find is less than a quarter the requested space reasonable? If the
729 * search fails entirely, return a block address if "0" it indicate this.
732 wapbl_find_log_start(struct mount
*mp
, struct vnode
*vp
, off_t logsize
,
733 daddr_t
*addr
, daddr_t
*indir_addr
, size_t *size
)
735 struct ufsmount
*ump
= VFSTOUFS(mp
);
736 struct fs
*fs
= ump
->um_fs
;
737 struct vnode
*devvp
= ump
->um_devvp
;
741 daddr_t blkno
, best_addr
, start_addr
;
742 daddr_t desired_blks
, min_desired_blks
;
743 daddr_t freeblks
, best_blks
;
744 int bpcg
, cg
, error
, fixedsize
, indir_blks
, n
, s
;
745 const int needswap
= UFS_FSNEEDSWAP(fs
);
748 fixedsize
= 0; /* We can adjust the size if tight */
749 logsize
= ffs_lfragtosize(fs
, fs
->fs_dsize
) /
750 UFS_WAPBL_JOURNAL_SCALE
;
751 DPRINTF("suggested log size = %" PRId64
"\n", logsize
);
752 logsize
= max(logsize
, UFS_WAPBL_MIN_JOURNAL_SIZE
);
753 logsize
= min(logsize
, UFS_WAPBL_MAX_JOURNAL_SIZE
);
754 DPRINTF("adjusted log size = %" PRId64
"\n", logsize
);
757 DPRINTF("fixed log size = %" PRId64
"\n", logsize
);
760 desired_blks
= logsize
/ fs
->fs_bsize
;
761 DPRINTF("desired blocks = %" PRId64
"\n", desired_blks
);
763 /* add in number of indirect blocks needed */
765 if (desired_blks
>= UFS_NDADDR
) {
766 struct indir indirs
[UFS_NIADDR
+ 2];
769 error
= ufs_getlbns(vp
, desired_blks
, indirs
, &num
);
771 printf("%s: ufs_getlbns failed, error %d!\n",
778 indir_blks
= 1; /* 1st level indirect */
781 indir_blks
= 1 + /* 1st level indirect */
782 1 + /* 2nd level indirect */
783 indirs
[1].in_off
+ 1; /* extra 1st level indirect */
786 printf("%s: unexpected numlevels %d from ufs_getlbns\n",
791 desired_blks
+= indir_blks
;
793 DPRINTF("desired blocks = %" PRId64
" (including indirect)\n",
797 * If a specific size wasn't requested, allow for a smaller log
798 * if we're really tight for space...
800 min_desired_blks
= desired_blks
;
802 min_desired_blks
= desired_blks
/ 4;
804 /* Look at number of blocks per CG. If it's too small, bail early. */
805 bpcg
= ffs_fragstoblks(fs
, fs
->fs_fpg
);
806 if (min_desired_blks
> bpcg
) {
807 printf("ffs_wapbl: cylinder group size of %" PRId64
" MB "
808 " is not big enough for journal\n",
809 ffs_lblktosize(fs
, bpcg
) / (1024 * 1024));
814 * Start with the middle cylinder group, and search outwards in
815 * both directions until we either find the requested log size
816 * or reach the start/end of the file system. If we reach the
817 * start/end without finding enough space for the full requested
818 * log size, use the largest extent found if it is large enough
819 * to satisfy the our minimum size.
822 * Can we just use the cluster contigsum stuff (esp on UFS2)
823 * here to simplify this search code?
827 for (cg
= fs
->fs_ncg
/ 2, s
= 0, n
= 1;
828 best_blks
< desired_blks
&& cg
>= 0 && cg
< fs
->fs_ncg
;
829 s
++, n
= -n
, cg
+= n
* s
) {
830 DPRINTF("check cg %d of %d\n", cg
, fs
->fs_ncg
);
831 error
= bread(devvp
, FFS_FSBTODB(fs
, cgtod(fs
, cg
)),
832 fs
->fs_cgsize
, 0, &bp
);
836 cgp
= (struct cg
*)bp
->b_data
;
837 if (!cg_chkmagic(cgp
, UFS_FSNEEDSWAP(fs
))) {
842 blksfree
= cg_blksfree(cgp
, needswap
);
844 for (blkno
= 0; blkno
< bpcg
;) {
845 /* look for next free block */
846 /* XXX use scanc() and fragtbl[] here? */
847 for (; blkno
< bpcg
- min_desired_blks
; blkno
++)
848 if (ffs_isblock(fs
, blksfree
, blkno
))
851 /* past end of search space in this CG? */
852 if (blkno
>= bpcg
- min_desired_blks
)
855 /* count how many free blocks in this extent */
857 for (freeblks
= 0; blkno
< bpcg
; blkno
++, freeblks
++)
858 if (!ffs_isblock(fs
, blksfree
, blkno
))
861 if (freeblks
> best_blks
) {
862 best_blks
= freeblks
;
863 best_addr
= ffs_blkstofrags(fs
, start_addr
) +
866 if (freeblks
>= desired_blks
) {
867 DPRINTF("found len %" PRId64
868 " at offset %" PRId64
" in gc\n",
869 freeblks
, start_addr
);
876 DPRINTF("best found len = %" PRId64
", wanted %" PRId64
877 " at addr %" PRId64
"\n", best_blks
, desired_blks
, best_addr
);
879 if (best_blks
< min_desired_blks
) {
883 /* put indirect blocks at start, and data blocks after */
884 *addr
= best_addr
+ ffs_blkstofrags(fs
, indir_blks
);
885 *indir_addr
= best_addr
;
887 *size
= min(desired_blks
, best_blks
) - indir_blks
;