xref: /original-bsd/sys/ufs/lfs/lfs_balloc.c (revision d1f811c7)
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 = &num;
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