xref: /original-bsd/sys/ufs/lfs/lfs_balloc.c (revision 99ddf719)
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