static void clear_blocks(ufs2_daddr_t start, ufs2_daddr_t end) { if (debug) printf("Zero frags %jd to %jd\n", start, end); if (Zflag) blzero(fswritefd, fsbtodb(&sblock, start), lfragtosize(&sblock, end - start + 1)); if (Eflag) blerase(fswritefd, fsbtodb(&sblock, start), lfragtosize(&sblock, end - start + 1)); }
/* * Find a suitable location for the journal in the filesystem. * * Our strategy here is to look for a contiguous block of free space * at least "logfile" MB in size (plus room for any indirect blocks). * We start at the middle of the filesystem and check each cylinder * group working outwards. If "logfile" MB is not available as a * single contigous chunk, then return the address and size of the * largest chunk found. * * XXX * At what stage does the search fail? Is if the largest space we could * find is less than a quarter the requested space reasonable? If the * search fails entirely, return a block address if "0" it indicate this. */ void wapbl_find_log_start(struct mount *mp, struct vnode *vp, off_t logsize, daddr_t *addr, daddr_t *indir_addr, size_t *size) { struct ufsmount *ump = VFSTOUFS(mp); struct fs *fs = ump->um_fs; struct vnode *devvp = ump->um_devvp; struct cg *cgp; struct buf *bp; uint8_t *blksfree; daddr_t blkno, best_addr, start_addr; daddr_t desired_blks, min_desired_blks; daddr_t freeblks, best_blks; int bpcg, cg, error, fixedsize, indir_blks, n, s; #ifdef FFS_EI const int needswap = UFS_FSNEEDSWAP(fs); #endif if (logsize == 0) { fixedsize = 0; /* We can adjust the size if tight */ logsize = lfragtosize(fs, fs->fs_dsize) / UFS_WAPBL_JOURNAL_SCALE; DPRINTF("suggested log size = %lld\n", logsize); logsize = max(logsize, UFS_WAPBL_MIN_JOURNAL_SIZE); logsize = min(logsize, UFS_WAPBL_MAX_JOURNAL_SIZE); DPRINTF("adjusted log size = %lld\n", logsize); } else { fixedsize = 1; DPRINTF("fixed log size = %lld\n", logsize); } desired_blks = logsize / fs->fs_bsize; DPRINTF("desired blocks = %lld\n", desired_blks); /* add in number of indirect blocks needed */ indir_blks = 0; if (desired_blks >= NDADDR) { struct indir indirs[NIADDR + 2]; int num; error = ufs_getlbns(vp, desired_blks, indirs, &num); if (error) { printf("%s: ufs_getlbns failed, error %d!\n", __func__, error); goto bad; } switch (num) { case 2: indir_blks = 1; /* 1st level indirect */ break; case 3: indir_blks = 1 + /* 1st level indirect */ 1 + /* 2nd level indirect */ indirs[1].in_off + 1; /* extra 1st level indirect */ break; default: printf("%s: unexpected numlevels %d from ufs_getlbns\n", __func__, num); *size = 0; goto bad; } desired_blks += indir_blks; } DPRINTF("desired blocks = %lld (including indirect)\n", desired_blks); /* * If a specific size wasn't requested, allow for a smaller log * if we're really tight for space... */ min_desired_blks = desired_blks; if (!fixedsize) min_desired_blks = desired_blks / 4; /* Look at number of blocks per CG. If it's too small, bail early. */ bpcg = fragstoblks(fs, fs->fs_fpg); if (min_desired_blks > bpcg) { printf("ffs_wapbl: cylinder group size of %lld MB " " is not big enough for journal\n", lblktosize(fs, bpcg) / (1024 * 1024)); goto bad; } /* * Start with the middle cylinder group, and search outwards in * both directions until we either find the requested log size * or reach the start/end of the file system. If we reach the * start/end without finding enough space for the full requested * log size, use the largest extent found if it is large enough * to satisfy the our minimum size. * * XXX * Can we just use the cluster contigsum stuff (esp on UFS2) * here to simplify this search code? */ best_addr = 0; best_blks = 0; for (cg = fs->fs_ncg / 2, s = 0, n = 1; best_blks < desired_blks && cg >= 0 && cg < fs->fs_ncg; s++, n = -n, cg += n * s) { DPRINTF("check cg %d of %d\n", cg, fs->fs_ncg); error = bread(devvp, fsbtodb(fs, cgtod(fs, cg)), fs->fs_cgsize, &bp); if (error) { continue; } cgp = (struct cg *)bp->b_data; if (!cg_chkmagic(cgp)) { brelse(bp); continue; } blksfree = cg_blksfree(cgp); for (blkno = 0; blkno < bpcg;) { /* look for next free block */ /* XXX use scanc() and fragtbl[] here? */ for (; blkno < bpcg - min_desired_blks; blkno++) if (ffs_isblock(fs, blksfree, blkno)) break; /* past end of search space in this CG? */ if (blkno >= bpcg - min_desired_blks) break; /* count how many free blocks in this extent */ start_addr = blkno; for (freeblks = 0; blkno < bpcg; blkno++, freeblks++) if (!ffs_isblock(fs, blksfree, blkno)) break; if (freeblks > best_blks) { best_blks = freeblks; best_addr = blkstofrags(fs, start_addr) + cgbase(fs, cg); if (freeblks >= desired_blks) { DPRINTF("found len %lld" " at offset %lld in gc\n", freeblks, start_addr); break; } } } brelse(bp); } DPRINTF("best found len = %lld, wanted %lld" " at addr %lld\n", best_blks, desired_blks, best_addr); if (best_blks < min_desired_blks) { *addr = 0; *indir_addr = 0; } else { /* put indirect blocks at start, and data blocks after */ *addr = best_addr + blkstofrags(fs, indir_blks); *indir_addr = best_addr; } *size = min(desired_blks, best_blks) - indir_blks; return; bad: *addr = 0; *indir_addr = 0; *size = 0; return; }
/* * If the superblock doesn't already have a recorded journal location * then we allocate the journal in one of two positions: * * - At the end of the partition after the filesystem if there's * enough space. "Enough space" is defined as >= 1MB of journal * per 1GB of filesystem or 64MB, whichever is smaller. * * - Inside the filesystem. We try to allocate a contiguous journal * based on the total filesystem size - the target is 1MB of journal * per 1GB of filesystem, up to a maximum journal size of 64MB. As * a worst case allowing for fragmentation, we'll allocate a journal * 1/4 of the desired size but never smaller than 1MB. * * XXX In the future if we allow for non-contiguous journal files we * can tighten the above restrictions. * * XXX * These seems like a lot of duplication both here and in some of * the userland tools (fsck_ffs, dumpfs, tunefs) with similar * "switch (fs_journal_location)" constructs. Can we centralise * this sort of code somehow/somewhere? */ int wapbl_log_position(struct mount *mp, struct fs *fs, struct vnode *devvp, daddr_t *startp, size_t *countp, size_t *blksizep, uint64_t *extradatap) { struct ufsmount *ump = VFSTOUFS(mp); daddr_t logstart, logend, desired_logsize; uint64_t numsecs; unsigned secsize; int error, location; if (fs->fs_journal_version == UFS_WAPBL_VERSION) { switch (fs->fs_journal_location) { case UFS_WAPBL_JOURNALLOC_END_PARTITION: DPRINTF("found existing end-of-partition log\n"); *startp = fs->fs_journallocs[UFS_WAPBL_EPART_ADDR]; *countp = fs->fs_journallocs[UFS_WAPBL_EPART_COUNT]; *blksizep = fs->fs_journallocs[UFS_WAPBL_EPART_BLKSZ]; DPRINTF(" start = %lld, size = %zu, " "blksize = %zu\n", *startp, *countp, *blksizep); return 0; case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM: DPRINTF("found existing in-filesystem log\n"); *startp = fs->fs_journallocs[UFS_WAPBL_INFS_ADDR]; *countp = fs->fs_journallocs[UFS_WAPBL_INFS_COUNT]; *blksizep = fs->fs_journallocs[UFS_WAPBL_INFS_BLKSZ]; DPRINTF(" start = %lld, size = %zu, " "blksize = %zu\n", *startp, *countp, *blksizep); return 0; default: printf("ffs_wapbl: unknown journal type %d\n", fs->fs_journal_location); return EINVAL; } } desired_logsize = lfragtosize(fs, fs->fs_size) / UFS_WAPBL_JOURNAL_SCALE; DPRINTF("desired log size = %lld kB\n", desired_logsize / 1024); desired_logsize = max(desired_logsize, UFS_WAPBL_MIN_JOURNAL_SIZE); desired_logsize = min(desired_logsize, UFS_WAPBL_MAX_JOURNAL_SIZE); DPRINTF("adjusted desired log size = %lld kB\n", desired_logsize / 1024); /* Is there space after after filesystem on partition for log? */ logstart = fsbtodb(fs, fs->fs_size); error = wapbl_getdisksize(devvp, &numsecs, &secsize); if (error) return error; KDASSERT(secsize != 0); logend = btodb(numsecs * secsize); if (dbtob(logend - logstart) >= desired_logsize) { DPRINTF("enough space, use end-of-partition log\n"); location = UFS_WAPBL_JOURNALLOC_END_PARTITION; *blksizep = secsize; *startp = logstart; *countp = (logend - logstart); *extradatap = 0; /* convert to physical block numbers */ *startp = dbtob(*startp) / secsize; *countp = dbtob(*countp) / secsize; fs->fs_journallocs[UFS_WAPBL_EPART_ADDR] = *startp; fs->fs_journallocs[UFS_WAPBL_EPART_COUNT] = *countp; fs->fs_journallocs[UFS_WAPBL_EPART_BLKSZ] = *blksizep; fs->fs_journallocs[UFS_WAPBL_EPART_UNUSED] = *extradatap; } else { DPRINTF("end-of-partition has only %lld free\n", logend - logstart); location = UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM; *blksizep = secsize; error = wapbl_create_infs_log(mp, fs, devvp, startp, countp, extradatap); ffs_sync(mp, MNT_WAIT, FSCRED, curproc); /* convert to physical block numbers */ *startp = dbtob(*startp) / secsize; *countp = dbtob(*countp) / secsize; fs->fs_journallocs[UFS_WAPBL_INFS_ADDR] = *startp; fs->fs_journallocs[UFS_WAPBL_INFS_COUNT] = *countp; fs->fs_journallocs[UFS_WAPBL_INFS_BLKSZ] = *blksizep; fs->fs_journallocs[UFS_WAPBL_INFS_INO] = *extradatap; } if (error == 0) { /* update superblock with log location */ fs->fs_journal_version = UFS_WAPBL_VERSION; fs->fs_journal_location = location; fs->fs_journal_flags = 0; error = ffs_sbupdate(ump, MNT_WAIT); } return error; }