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.34 (Berkeley) 07/23/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/trace.h> 17 18 #include <miscfs/specfs/specdev.h> 19 20 #include <ufs/ufs/quota.h> 21 #include <ufs/ufs/inode.h> 22 #include <ufs/ufs/ufsmount.h> 23 24 #include <ufs/lfs/lfs.h> 25 #include <ufs/lfs/lfs_extern.h> 26 27 int lfs_getlbns __P((struct vnode *, daddr_t, INDIR *, int *)); 28 29 /* 30 * Bmap converts a the logical block number of a file to its physical block 31 * number on the disk. The conversion is done by using the logical block 32 * number to index into the array of block pointers described by the dinode. 33 */ 34 int 35 lfs_bmap(ap) 36 struct vop_bmap_args /* { 37 struct vnode *a_vp; 38 daddr_t a_bn; 39 struct vnode **a_vpp; 40 daddr_t *a_bnp; 41 } */ *ap; 42 { 43 #ifdef VERBOSE 44 printf("lfs_bmap\n"); 45 #endif 46 /* 47 * Check for underlying vnode requests and ensure that logical 48 * to physical mapping is requested. 49 */ 50 if (ap->a_vpp != NULL) 51 *ap->a_vpp = VTOI(ap->a_vp)->i_devvp; 52 if (ap->a_bnp == NULL) 53 return (0); 54 55 return (lfs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL)); 56 } 57 58 /* 59 * LFS has a different version of bmap from FFS because of a naming conflict. 60 * In FFS, meta blocks are given real disk addresses at allocation time, and 61 * are linked into the device vnode, using a logical block number which is 62 * the same as the physical block number. This can't be done by LFS because 63 * blocks aren't given disk addresses until they're written, so there's no 64 * way to distinguish the meta-data blocks for one file from any other file. 65 * This means that meta-data blocks have to be on the vnode for the file so 66 * they can be found, and have to have "names" different from the standard 67 * data blocks. To do this, we divide the name space into positive and 68 * negative block numbers, and give the meta-data blocks negative logical 69 * numbers. Indirect blocks are addressed by the negative address of the 70 * first data block to which they point. Double indirect blocks are addressed 71 * by one less than the address of the first indirect block to which they 72 * point. Triple indirect blocks are addressed by one less than the address 73 * of the first double indirect block to which they point. 74 */ 75 int 76 lfs_bmaparray(vp, bn, bnp, ap, nump) 77 struct vnode *vp; 78 register daddr_t bn; 79 daddr_t *bnp; 80 INDIR *ap; 81 int *nump; 82 { 83 register struct inode *ip; 84 struct buf *bp; 85 struct lfs *fs; 86 struct vnode *devvp; 87 INDIR a[NIADDR], *xap; 88 daddr_t *bap, daddr; 89 long metalbn; 90 int error, num, off; 91 struct vop_strategy_args vop_strategy_a; 92 93 ip = VTOI(vp); 94 #ifdef VERBOSE 95 printf("lfs_bmap: block number %d, inode %d\n", bn, ip->i_number); 96 #endif 97 #ifdef DIAGNOSTIC 98 if (ap != NULL && nump == NULL || ap == NULL && nump != NULL) 99 panic("lfs_bmaparray: invalid arguments"); 100 #endif 101 102 xap = ap == NULL ? a : ap; 103 if (!nump) 104 nump = # 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 /* 154 * Call a strategy VOP by hand. 155 */ 156 vop_strategy_a.a_desc = VDESC(vop_strategy); 157 vop_strategy_a.a_bp=bp; 158 VOCALL(devvp->v_op, VOFFSET(vop_strategy), \ 159 &vop_strategy_a); 160 curproc->p_stats->p_ru.ru_inblock++; /* XXX */ 161 if (error = biowait(bp)) { 162 brelse(bp); 163 return (error); 164 } 165 } 166 daddr = bp->b_un.b_daddr[xap->in_off]; 167 } 168 if (bp) 169 brelse(bp); 170 171 *bnp = daddr == 0 ? UNASSIGNED : daddr; 172 return (0); 173 } 174 175 /* 176 * Create an array of logical block number/offset pairs which represent the 177 * path of indirect blocks required to access a data block. The first "pair" 178 * contains the logical block number of the appropriate single, double or 179 * triple indirect block and the offset into the inode indirect block array. 180 * Note, the logical block number of the inode single/double/triple indirect 181 * block appears twice in the array, once with the offset into the i_ib and 182 * once with the offset into the page itself. 183 */ 184 int 185 lfs_getlbns(vp, bn, ap, nump) 186 struct vnode *vp; 187 register daddr_t bn; 188 INDIR *ap; 189 int *nump; 190 { 191 struct lfs *fs; 192 long metalbn, realbn; 193 int j, numlevels, off, sh; 194 195 #ifdef VERBOSE 196 printf("lfs_getlbns: bn %d, inode %d\n", bn, VTOI(vp)->i_number); 197 #endif 198 if (nump) 199 *nump = 0; 200 numlevels = 0; 201 realbn = bn; 202 if ((long)bn < 0) 203 bn = -(long)bn; 204 205 /* The first NDADDR blocks are direct blocks. */ 206 if (bn < NDADDR) 207 return (0); 208 209 /* 210 * Determine the number of levels of indirection. After this loop 211 * is done, sh indicates the number of data blocks possible at the 212 * given level of indirection, and NIADDR - j is the number of levels 213 * of indirection needed to locate the requested block. 214 */ 215 bn -= NDADDR; 216 fs = VTOI(vp)->i_lfs; 217 sh = 1; 218 for (j = NIADDR; j > 0; j--) { 219 sh *= NINDIR(fs); 220 if (bn < sh) 221 break; 222 bn -= sh; 223 } 224 if (j == 0) 225 return (EFBIG); 226 227 /* Calculate the address of the first meta-block. */ 228 if (realbn >= 0) 229 metalbn = -(realbn - bn + NIADDR - j); 230 else 231 metalbn = -(-realbn - bn + NIADDR - j); 232 233 /* 234 * At each iteration, off is the offset into the bap array which is 235 * an array of disk addresses at the current level of indirection. 236 * The logical block number and the offset in that block are stored 237 * into the argument array. 238 */ 239 ++numlevels; 240 ap->in_lbn = metalbn; 241 ap->in_off = off = NIADDR - j; 242 ap++; 243 for (; j <= NIADDR; j++) { 244 /* If searching for a meta-data block, quit when found. */ 245 if (metalbn == realbn) 246 break; 247 248 sh /= NINDIR(fs); 249 off = (bn / sh) % NINDIR(fs); 250 251 ++numlevels; 252 ap->in_lbn = metalbn; 253 ap->in_off = off; 254 ++ap; 255 256 metalbn -= -1 + off * sh; 257 } 258 if (nump) 259 *nump = numlevels; 260 return (0); 261 } 262 263 int 264 lfs_balloc(vp, iosize, lbn, bpp) 265 struct vnode *vp; 266 u_long iosize; 267 daddr_t lbn; 268 struct buf **bpp; 269 { 270 struct buf *bp; 271 struct inode *ip; 272 struct lfs *fs; 273 daddr_t daddr; 274 int error, newblock; 275 276 ip = VTOI(vp); 277 fs = ip->i_lfs; 278 279 /* 280 * Three cases: it's a block beyond the end of file, it's a block in 281 * the file that may or may not have been assigned a disk address or 282 * we're writing an entire block. Note, if the daddr is unassigned, 283 * the block might still have existed in the cache. If it did, make 284 * sure we don't count it as a new block or zero out its contents. 285 */ 286 newblock = ip->i_size <= lbn << fs->lfs_bshift; 287 if (!newblock && (error = VOP_BMAP(vp, lbn, NULL, &daddr))) 288 return (error); 289 290 if (newblock || daddr == UNASSIGNED || iosize == fs->lfs_bsize) { 291 *bpp = bp = getblk(vp, lbn, fs->lfs_bsize); 292 if (newblock || 293 daddr == UNASSIGNED && !(bp->b_flags & B_CACHE)) { 294 ip->i_blocks += btodb(fs->lfs_bsize); 295 fs->lfs_bfree -= btodb(fs->lfs_bsize); 296 if (iosize != fs->lfs_bsize) 297 clrbuf(bp); 298 } 299 return (0); 300 } 301 return (bread(vp, lbn, fs->lfs_bsize, NOCRED, bpp)); 302 303 } 304