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