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