1 /* 2 * Copyright (c) 1989, 1991 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 * 7 * @(#)lfs_balloc.c 7.28 (Berkeley) 05/14/92 8 */ 9 10 #include <sys/param.h> 11 #include <sys/buf.h> 12 #include <sys/proc.h> 13 #include <sys/vnode.h> 14 #include <sys/mount.h> 15 #include <sys/resourcevar.h> 16 #include <sys/specdev.h> 17 #include <sys/trace.h> 18 19 #include <ufs/ufs/quota.h> 20 #include <ufs/ufs/inode.h> 21 #include <ufs/ufs/ufsmount.h> 22 23 #include <ufs/lfs/lfs.h> 24 #include <ufs/lfs/lfs_extern.h> 25 26 int lfs_getlbns __P((struct vnode *, daddr_t, INDIR *, int *)); 27 28 /* 29 * Bmap converts a the logical block number of a file to its physical block 30 * number on the disk. The conversion is done by using the logical block 31 * number to index into the array of block pointers described by the dinode. 32 */ 33 int 34 lfs_bmap (ap) 35 struct vop_bmap_args *ap; 36 #define vp (ap->a_vp) 37 #define bn (ap->a_bn) 38 #define vpp (ap->a_vpp) 39 #define bnp (ap->a_bnp) 40 { 41 #ifdef VERBOSE 42 printf("lfs_bmap\n"); 43 #endif 44 /* 45 * Check for underlying vnode requests and ensure that logical 46 * to physical mapping is requested. 47 */ 48 if (vpp != NULL) 49 *vpp = VTOI(vp)->i_devvp; 50 if (bnp == NULL) 51 return (0); 52 53 return (lfs_bmaparray(vp, bn, bnp, NULL, NULL)); 54 } 55 #undef vp 56 #undef bn 57 #undef vpp 58 #undef bnp 59 60 /* 61 * LFS has a different version of bmap from FFS because of a naming conflict. 62 * In FFS, meta blocks are given real disk addresses at allocation time, and 63 * are linked into the device vnode, using a logical block number which is 64 * the same as the physical block number. This can't be done by LFS because 65 * blocks aren't given disk addresses until they're written, so there's no 66 * way to distinguish the meta-data blocks for one file from any other file. 67 * This means that meta-data blocks have to be on the vnode for the file so 68 * they can be found, and have to have "names" different from the standard 69 * data blocks. To do this, we divide the name space into positive and 70 * negative block numbers, and give the meta-data blocks negative logical 71 * numbers. Indirect blocks are addressed by the negative address of the 72 * first data block to which they point. Double indirect blocks are addressed 73 * by one less than the address of the first indirect block to which they 74 * point. Triple indirect blocks are addressed by one less than the address 75 * of the first double indirect block to which they point. 76 */ 77 int 78 lfs_bmaparray(vp, bn, bnp, ap, nump) 79 struct vnode *vp; 80 register daddr_t bn; 81 daddr_t *bnp; 82 INDIR *ap; 83 int *nump; 84 { 85 register struct inode *ip; 86 struct buf *bp; 87 struct lfs *fs; 88 struct vnode *devvp; 89 INDIR a[NIADDR], *xap; 90 daddr_t *bap, daddr; 91 long metalbn; 92 int error, num, off; 93 94 95 ip = VTOI(vp); 96 #ifdef VERBOSE 97 printf("lfs_bmap: block number %d, inode %d\n", bn, ip->i_number); 98 #endif 99 #ifdef DIAGNOSTIC 100 if (ap != NULL && nump == NULL || ap == NULL && nump != NULL) 101 panic("lfs_bmaparray: invalid arguments"); 102 #endif 103 104 xap = ap == NULL ? a : ap; 105 if (error = lfs_getlbns(vp, bn, xap, nump)) 106 return (error); 107 108 num = *nump; 109 110 if (num == 0) { 111 *bnp = ip->i_db[bn]; 112 if (*bnp == 0) 113 *bnp = UNASSIGNED; 114 return (0); 115 } 116 117 118 /* Get disk address out of indirect block array */ 119 daddr = ip->i_ib[xap->in_off]; 120 121 /* Fetch through the indirect blocks. */ 122 fs = ip->i_lfs; 123 devvp = VFSTOUFS(vp->v_mount)->um_devvp; 124 125 for (bp = NULL, ++xap; daddr && --num; ++xap) { 126 /* If looking for a meta-block, break out when we find it. */ 127 metalbn = xap->in_lbn; 128 if (metalbn == bn) 129 break; 130 131 /* 132 * Read in the appropriate indirect block. LFS can't do a 133 * bread because bread knows that FFS will hand it the device 134 * vnode, not the file vnode, so the b_dev and b_blkno would 135 * be wrong. 136 * 137 * XXX 138 * This REALLY needs to be fixed, at the very least it needs 139 * to be rethought when the buffer cache goes away. When it's 140 * fixed, change lfs_bmaparray and lfs_getlbns to take an ip, 141 * not a vp. 142 */ 143 if (bp) 144 brelse(bp); 145 bp = getblk(vp, metalbn, fs->lfs_bsize); 146 if (bp->b_flags & (B_DONE | B_DELWRI)) { 147 trace(TR_BREADHIT, pack(vp, size), metalbn); 148 } else { 149 trace(TR_BREADMISS, pack(vp, size), metalbn); 150 bp->b_blkno = daddr; 151 bp->b_flags |= B_READ; 152 bp->b_dev = devvp->v_rdev; 153 (devvp->v_op->vop_strategy)(bp); 154 curproc->p_stats->p_ru.ru_inblock++; /* XXX */ 155 if (error = biowait(bp)) { 156 brelse(bp); 157 return (error); 158 } 159 } 160 daddr = bp->b_un.b_daddr[xap->in_off]; 161 } 162 if (bp) 163 brelse(bp); 164 165 *bnp = daddr == 0 ? UNASSIGNED : daddr; 166 return (0); 167 } 168 169 /* 170 * Create an array of logical block number/offset pairs which represent the 171 * path of indirect blocks required to access a data block. The first "pair" 172 * contains the logical block number of the appropriate single, double or 173 * triple indirect block and the offset into the inode indirect block array. 174 * Note, the logical block number of the inode single/double/triple indirect 175 * block appears twice in the array, once with the offset into the i_ib and 176 * once with the offset into the page itself. 177 */ 178 int 179 lfs_getlbns(vp, bn, ap, nump) 180 struct vnode *vp; 181 register daddr_t bn; 182 INDIR *ap; 183 int *nump; 184 { 185 struct lfs *fs; 186 long metalbn, realbn; 187 int j, off, sh; 188 189 #ifdef VERBOSE 190 printf("lfs_getlbns: bn %d, inode %d\n", bn, VTOI(vp)->i_number); 191 #endif 192 *nump = 0; 193 realbn = bn; 194 if ((long)bn < 0) 195 bn = -(long)bn; 196 197 /* The first NDADDR blocks are direct blocks. */ 198 if (bn < NDADDR) 199 return (0); 200 201 /* 202 * Determine the number of levels of indirection. After this loop 203 * is done, sh indicates the number of data blocks possible at the 204 * given level of indirection, and NIADDR - j is the number of levels 205 * of indirection needed to locate the requested block. 206 */ 207 bn -= NDADDR; 208 fs = VTOI(vp)->i_lfs; 209 sh = 1; 210 for (j = NIADDR; j > 0; j--) { 211 sh *= NINDIR(fs); 212 if (bn < sh) 213 break; 214 bn -= sh; 215 } 216 if (j == 0) 217 return (EFBIG); 218 219 /* Calculate the address of the first meta-block. */ 220 if (realbn >= 0) 221 metalbn = -(realbn - bn + NIADDR - j); 222 else 223 metalbn = -(-realbn - bn + NIADDR - j); 224 225 /* 226 * At each iteration, off is the offset into the bap array which is 227 * an array of disk addresses at the current level of indirection. 228 * The logical block number and the offset in that block are stored 229 * into the argument array. 230 */ 231 ++*nump; 232 ap->in_lbn = metalbn; 233 ap->in_off = off = NIADDR - j; 234 ap++; 235 for (; j <= NIADDR; j++) { 236 /* If searching for a meta-data block, quit when found. */ 237 if (metalbn == realbn) 238 break; 239 240 sh /= NINDIR(fs); 241 off = (bn / sh) % NINDIR(fs); 242 243 ++*nump; 244 ap->in_lbn = metalbn; 245 ap->in_off = off; 246 ++ap; 247 248 metalbn -= -1 + off * sh; 249 } 250 return (0); 251 } 252 253 int 254 lfs_balloc(vp, iosize, lbn, bpp) 255 struct vnode *vp; 256 u_long iosize; 257 daddr_t lbn; 258 struct buf **bpp; 259 { 260 USES_VOP_BMAP; 261 struct buf *bp; 262 struct inode *ip; 263 struct lfs *fs; 264 daddr_t daddr; 265 int error, newblock; 266 267 ip = VTOI(vp); 268 fs = ip->i_lfs; 269 270 /* 271 * Three cases: it's a block beyond the end of file, it's a block in 272 * the file that may or may not have been assigned a disk address or 273 * we're writing an entire block. Note, if the daddr is unassigned, 274 * the block might still have existed in the cache. If it did, make 275 * sure we don't count it as a new block or zero out its contents. 276 */ 277 newblock = ip->i_size <= lbn << fs->lfs_bshift; 278 if (!newblock && (error = VOP_BMAP(vp, lbn, NULL, &daddr))) 279 return (error); 280 281 if (newblock || daddr == UNASSIGNED || iosize == fs->lfs_bsize) { 282 *bpp = bp = getblk(vp, lbn, fs->lfs_bsize); 283 if (newblock || 284 daddr == UNASSIGNED && !(bp->b_flags & B_CACHE)) { 285 ++ip->i_blocks; 286 if (iosize != fs->lfs_bsize) 287 clrbuf(bp); 288 } 289 return (0); 290 } 291 return (bread(vp, lbn, fs->lfs_bsize, NOCRED, bpp)); 292 293 } 294