]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/gnu/fs/ext2fs/ext2_alloc.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / gnu / fs / ext2fs / ext2_alloc.c
1 /*-
2  *  modified for Lites 1.1
3  *
4  *  Aug 1995, Godmar Back (gback@cs.utah.edu)
5  *  University of Utah, Department of Computer Science
6  */
7 /*-
8  * Copyright (c) 1982, 1986, 1989, 1993
9  *      The Regents of the University of California.  All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *      @(#)ffs_alloc.c 8.8 (Berkeley) 2/21/94
36  * $FreeBSD$
37  */
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/conf.h>
42 #include <sys/vnode.h>
43 #include <sys/stat.h>
44 #include <sys/mount.h>
45 #include <sys/syslog.h>
46
47 #include <gnu/fs/ext2fs/inode.h>
48 #include <gnu/fs/ext2fs/ext2_mount.h>
49 #include <gnu/fs/ext2fs/ext2_fs.h>
50 #include <gnu/fs/ext2fs/ext2_fs_sb.h>
51 #include <gnu/fs/ext2fs/fs.h>
52 #include <gnu/fs/ext2fs/ext2_extern.h>
53
54 static void     ext2_fserr(struct ext2_sb_info *, u_int, char *);
55
56 /*
57  * Linux calls this functions at the following locations:
58  * (1) the inode is freed
59  * (2) a preallocation miss occurs
60  * (3) truncate is called
61  * (4) release_file is called and f_mode & 2
62  *
63  * I call it in ext2_inactive, ext2_truncate, ext2_vfree and in (2)
64  * the call in vfree might be redundant
65  */
66 void
67 ext2_discard_prealloc(ip)
68         struct inode * ip;
69 {
70 #ifdef EXT2_PREALLOCATE
71         if (ip->i_prealloc_count) {
72                 int i = ip->i_prealloc_count;
73                 ip->i_prealloc_count = 0;
74                 ext2_free_blocks (ITOV(ip)->v_mount,
75                                   ip->i_prealloc_block,
76                                   i);
77         }
78 #endif
79 }
80
81 /*
82  * Allocate a block in the file system.
83  * 
84  * this takes the framework from ffs_alloc. To implement the
85  * actual allocation, it calls ext2_new_block, the ported version
86  * of the same Linux routine.
87  *
88  * we note that this is always called in connection with ext2_blkpref
89  *
90  * preallocation is done as Linux does it
91  */
92 int
93 ext2_alloc(ip, lbn, bpref, size, cred, bnp)
94         struct inode *ip;
95         int32_t lbn, bpref;
96         int size;
97         struct ucred *cred;
98         int32_t *bnp;
99 {
100         struct ext2_sb_info *fs;
101         int32_t bno;
102         
103         *bnp = 0;
104         fs = ip->i_e2fs;
105 #ifdef DIAGNOSTIC
106         if ((u_int)size > fs->s_blocksize || blkoff(fs, size) != 0) {
107                 vn_printf(ip->i_devvp, "bsize = %lu, size = %d, fs = %s\n",
108                     fs->s_blocksize, size, fs->fs_fsmnt);
109                 panic("ext2_alloc: bad size");
110         }
111         if (cred == NOCRED)
112                 panic("ext2_alloc: missing credential");
113 #endif /* DIAGNOSTIC */
114         if (size == fs->s_blocksize && fs->s_es->s_free_blocks_count == 0)
115                 goto nospace;
116         if (cred->cr_uid != 0 && 
117                 fs->s_es->s_free_blocks_count < fs->s_es->s_r_blocks_count)
118                 goto nospace;
119         if (bpref >= fs->s_es->s_blocks_count)
120                 bpref = 0;
121         /* call the Linux code */
122 #ifdef EXT2_PREALLOCATE
123         /* To have a preallocation hit, we must
124          * - have at least one block preallocated
125          * - and our preferred block must have that block number or one below
126          */
127         if (ip->i_prealloc_count &&
128             (bpref == ip->i_prealloc_block ||
129              bpref + 1 == ip->i_prealloc_block))
130         {
131                 bno = ip->i_prealloc_block++;
132                 ip->i_prealloc_count--;
133                 /* ext2_debug ("preallocation hit (%lu/%lu).\n",
134                             ++alloc_hits, ++alloc_attempts); */
135
136                 /* Linux gets, clears, and releases the buffer at this
137                    point - we don't have to that; we leave it to the caller
138                  */
139         } else {
140                 ext2_discard_prealloc (ip);
141                 /* ext2_debug ("preallocation miss (%lu/%lu).\n",
142                             alloc_hits, ++alloc_attempts); */
143                 if (S_ISREG(ip->i_mode))
144                         bno = ext2_new_block
145                                 (ITOV(ip)->v_mount, bpref,
146                                  &ip->i_prealloc_count,
147                                  &ip->i_prealloc_block);
148                 else
149                         bno = (int32_t)ext2_new_block(ITOV(ip)->v_mount, 
150                                         bpref, 0, 0);
151         }
152 #else
153         bno = (int32_t)ext2_new_block(ITOV(ip)->v_mount, bpref, 0, 0);
154 #endif
155
156         if (bno > 0) {
157                 /* set next_alloc fields as done in block_getblk */
158                 ip->i_next_alloc_block = lbn;
159                 ip->i_next_alloc_goal = bno;
160
161                 ip->i_blocks += btodb(size);
162                 ip->i_flag |= IN_CHANGE | IN_UPDATE;
163                 *bnp = bno;
164                 return (0);
165         }
166 nospace:
167         ext2_fserr(fs, cred->cr_uid, "file system full");
168         uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt);
169         return (ENOSPC);
170 }
171
172 /*
173  * Reallocate a sequence of blocks into a contiguous sequence of blocks.
174  *
175  * The vnode and an array of buffer pointers for a range of sequential
176  * logical blocks to be made contiguous is given. The allocator attempts
177  * to find a range of sequential blocks starting as close as possible to
178  * an fs_rotdelay offset from the end of the allocation for the logical
179  * block immediately preceding the current range. If successful, the
180  * physical block numbers in the buffer pointers and in the inode are
181  * changed to reflect the new allocation. If unsuccessful, the allocation
182  * is left unchanged. The success in doing the reallocation is returned.
183  * Note that the error return is not reflected back to the user. Rather
184  * the previous block allocation will be used.
185  */
186
187 #ifdef FANCY_REALLOC
188 #include <sys/sysctl.h>
189 static int doasyncfree = 1;
190 #ifdef  OPT_DEBUG
191 SYSCTL_INT(_debug, 14, doasyncfree, CTLFLAG_RW, &doasyncfree, 0, "");
192 #endif  /* OPT_DEBUG */
193 #endif
194
195 int
196 ext2_reallocblks(ap)
197         struct vop_reallocblks_args /* {
198                 struct vnode *a_vp;
199                 struct cluster_save *a_buflist;
200         } */ *ap;
201 {
202 #ifndef FANCY_REALLOC
203 /* printf("ext2_reallocblks not implemented\n"); */
204 return ENOSPC;
205 #else
206
207         struct ext2_sb_info *fs;
208         struct inode *ip;
209         struct vnode *vp;
210         struct buf *sbp, *ebp;
211         int32_t *bap, *sbap, *ebap;
212         struct cluster_save *buflist;
213         int32_t start_lbn, end_lbn, soff, eoff, newblk, blkno;
214         struct indir start_ap[NIADDR + 1], end_ap[NIADDR + 1], *idp;
215         int i, len, start_lvl, end_lvl, pref, ssize;
216
217         vp = ap->a_vp;
218         ip = VTOI(vp);
219         fs = ip->i_e2fs;
220 #ifdef UNKLAR
221         if (fs->fs_contigsumsize <= 0)
222                 return (ENOSPC);
223 #endif
224         buflist = ap->a_buflist;
225         len = buflist->bs_nchildren;
226         start_lbn = buflist->bs_children[0]->b_lblkno;
227         end_lbn = start_lbn + len - 1;
228 #ifdef DIAGNOSTIC
229         for (i = 1; i < len; i++)
230                 if (buflist->bs_children[i]->b_lblkno != start_lbn + i)
231                         panic("ext2_reallocblks: non-cluster");
232 #endif
233         /*
234          * If the latest allocation is in a new cylinder group, assume that
235          * the filesystem has decided to move and do not force it back to
236          * the previous cylinder group.
237          */
238         if (dtog(fs, dbtofsb(fs, buflist->bs_children[0]->b_blkno)) !=
239             dtog(fs, dbtofsb(fs, buflist->bs_children[len - 1]->b_blkno)))
240                 return (ENOSPC);
241         if (ufs_getlbns(vp, start_lbn, start_ap, &start_lvl) ||
242             ufs_getlbns(vp, end_lbn, end_ap, &end_lvl))
243                 return (ENOSPC);
244         /*
245          * Get the starting offset and block map for the first block.
246          */
247         if (start_lvl == 0) {
248                 sbap = &ip->i_db[0];
249                 soff = start_lbn;
250         } else {
251                 idp = &start_ap[start_lvl - 1];
252                 if (bread(vp, idp->in_lbn, (int)fs->s_blocksize, NOCRED, &sbp)) {
253                         brelse(sbp);
254                         return (ENOSPC);
255                 }
256                 sbap = (int32_t *)sbp->b_data;
257                 soff = idp->in_off;
258         }
259         /*
260          * Find the preferred location for the cluster.
261          */
262         pref = ext2_blkpref(ip, start_lbn, soff, sbap);
263         /*
264          * If the block range spans two block maps, get the second map.
265          */
266         if (end_lvl == 0 || (idp = &end_ap[end_lvl - 1])->in_off + 1 >= len) {
267                 ssize = len;
268         } else {
269 #ifdef DIAGNOSTIC
270                 if (start_ap[start_lvl-1].in_lbn == idp->in_lbn)
271                         panic("ext2_reallocblk: start == end");
272 #endif
273                 ssize = len - (idp->in_off + 1);
274                 if (bread(vp, idp->in_lbn, (int)fs->s_blocksize, NOCRED, &ebp))
275                         goto fail;
276                 ebap = (int32_t *)ebp->b_data;
277         }
278         /*
279          * Search the block map looking for an allocation of the desired size.
280          */
281         if ((newblk = (int32_t)ext2_hashalloc(ip, dtog(fs, pref), (long)pref,
282             len, (u_long (*)())ext2_clusteralloc)) == 0)
283                 goto fail;
284         /*
285          * We have found a new contiguous block.
286          *
287          * First we have to replace the old block pointers with the new
288          * block pointers in the inode and indirect blocks associated
289          * with the file.
290          */
291         blkno = newblk;
292         for (bap = &sbap[soff], i = 0; i < len; i++, blkno += fs->s_frags_per_block) {
293                 if (i == ssize)
294                         bap = ebap;
295 #ifdef DIAGNOSTIC
296                 if (buflist->bs_children[i]->b_blkno != fsbtodb(fs, *bap))
297                         panic("ext2_reallocblks: alloc mismatch");
298 #endif
299                 *bap++ = blkno;
300         }
301         /*
302          * Next we must write out the modified inode and indirect blocks.
303          * For strict correctness, the writes should be synchronous since
304          * the old block values may have been written to disk. In practise
305          * they are almost never written, but if we are concerned about 
306          * strict correctness, the `doasyncfree' flag should be set to zero.
307          *
308          * The test on `doasyncfree' should be changed to test a flag
309          * that shows whether the associated buffers and inodes have
310          * been written. The flag should be set when the cluster is
311          * started and cleared whenever the buffer or inode is flushed.
312          * We can then check below to see if it is set, and do the
313          * synchronous write only when it has been cleared.
314          */
315         if (sbap != &ip->i_db[0]) {
316                 if (doasyncfree)
317                         bdwrite(sbp);
318                 else
319                         bwrite(sbp);
320         } else {
321                 ip->i_flag |= IN_CHANGE | IN_UPDATE;
322                 if (!doasyncfree)
323                         ext2_update(vp, 1);
324         }
325         if (ssize < len)
326                 if (doasyncfree)
327                         bdwrite(ebp);
328                 else
329                         bwrite(ebp);
330         /*
331          * Last, free the old blocks and assign the new blocks to the buffers.
332          */
333         for (blkno = newblk, i = 0; i < len; i++, blkno += fs->s_frags_per_block) {
334                 ext2_blkfree(ip, dbtofsb(fs, buflist->bs_children[i]->b_blkno),
335                     fs->s_blocksize);
336                 buflist->bs_children[i]->b_blkno = fsbtodb(fs, blkno);
337         }
338         return (0);
339
340 fail:
341         if (ssize < len)
342                 brelse(ebp);
343         if (sbap != &ip->i_db[0])
344                 brelse(sbp);
345         return (ENOSPC);
346
347 #endif /* FANCY_REALLOC */
348 }
349
350 /*
351  * Allocate an inode in the file system.
352  * 
353  * we leave the actual allocation strategy to the (modified)
354  * ext2_new_inode(), to make sure we get the policies right
355  */
356 int
357 ext2_valloc(pvp, mode, cred, vpp)
358         struct vnode *pvp;
359         int mode;
360         struct ucred *cred;
361         struct vnode **vpp;
362 {
363         struct inode *pip;
364         struct ext2_sb_info *fs;
365         struct inode *ip;
366         ino_t ino;
367         int i, error;
368         
369         *vpp = NULL;
370         pip = VTOI(pvp);
371         fs = pip->i_e2fs;
372         if (fs->s_es->s_free_inodes_count == 0)
373                 goto noinodes;
374
375         /* call the Linux routine - it returns the inode number only */
376         ino = ext2_new_inode(pip, mode);
377
378         if (ino == 0)
379                 goto noinodes;
380         error = VFS_VGET(pvp->v_mount, ino, LK_EXCLUSIVE, vpp);
381         if (error) {
382                 ext2_vfree(pvp, ino, mode);
383                 return (error);
384         }
385         ip = VTOI(*vpp);
386
387         /* 
388           the question is whether using VGET was such good idea at all -
389           Linux doesn't read the old inode in when it's allocating a
390           new one. I will set at least i_size & i_blocks the zero. 
391         */ 
392         ip->i_mode = 0;
393         ip->i_size = 0;
394         ip->i_blocks = 0;
395         ip->i_flags = 0;
396         /* now we want to make sure that the block pointers are zeroed out */
397         for (i = 0; i < NDADDR; i++)
398                 ip->i_db[i] = 0;
399         for (i = 0; i < NIADDR; i++)
400                 ip->i_ib[i] = 0;
401
402         /*
403          * Set up a new generation number for this inode.
404          * XXX check if this makes sense in ext2
405          */
406         if (ip->i_gen == 0 || ++ip->i_gen == 0)
407                 ip->i_gen = random() / 2 + 1;
408 /*
409 printf("ext2_valloc: allocated inode %d\n", ino);
410 */
411         return (0);
412 noinodes:
413         ext2_fserr(fs, cred->cr_uid, "out of inodes");
414         uprintf("\n%s: create/symlink failed, no inodes free\n", fs->fs_fsmnt);
415         return (ENOSPC);
416 }
417
418 /*
419  * Select the desired position for the next block in a file.  
420  *
421  * we try to mimic what Remy does in inode_getblk/block_getblk
422  *
423  * we note: blocknr == 0 means that we're about to allocate either
424  * a direct block or a pointer block at the first level of indirection
425  * (In other words, stuff that will go in i_db[] or i_ib[])
426  *
427  * blocknr != 0 means that we're allocating a block that is none
428  * of the above. Then, blocknr tells us the number of the block
429  * that will hold the pointer
430  */
431 int32_t
432 ext2_blkpref(ip, lbn, indx, bap, blocknr)
433         struct inode *ip;
434         int32_t lbn;
435         int indx;
436         int32_t *bap;
437         int32_t blocknr;
438 {
439         int     tmp;
440
441         /* if the next block is actually what we thought it is,
442            then set the goal to what we thought it should be
443         */
444         if(ip->i_next_alloc_block == lbn)
445                 return ip->i_next_alloc_goal;
446
447         /* now check whether we were provided with an array that basically
448            tells us previous blocks to which we want to stay closeby
449         */
450         if(bap) 
451                 for (tmp = indx - 1; tmp >= 0; tmp--) 
452                         if (bap[tmp]) 
453                                 return bap[tmp];
454
455         /* else let's fall back to the blocknr, or, if there is none,
456            follow the rule that a block should be allocated near its inode
457         */
458         return blocknr ? blocknr :
459                         (int32_t)(ip->i_block_group * 
460                         EXT2_BLOCKS_PER_GROUP(ip->i_e2fs)) + 
461                         ip->i_e2fs->s_es->s_first_data_block;
462 }
463
464 /*
465  * Free a block or fragment.
466  *
467  * pass on to the Linux code
468  */
469 void
470 ext2_blkfree(ip, bno, size)
471         struct inode *ip;
472         int32_t bno;
473         long size;
474 {
475         struct ext2_sb_info *fs;
476
477         fs = ip->i_e2fs;
478         /*
479          *      call Linux code with mount *, block number, count
480          */
481         ext2_free_blocks(ITOV(ip)->v_mount, bno, size / fs->s_frag_size);
482 }
483
484 /*
485  * Free an inode.
486  *
487  * the maintenance of the actual bitmaps is again up to the linux code
488  */
489 int
490 ext2_vfree(pvp, ino, mode)
491         struct vnode *pvp;
492         ino_t ino;
493         int mode;
494 {
495         struct ext2_sb_info *fs;
496         struct inode *pip;
497         mode_t save_i_mode;
498
499         pip = VTOI(pvp);
500         fs = pip->i_e2fs;
501         if ((u_int)ino > fs->s_inodes_per_group * fs->s_groups_count)
502                 panic("ext2_vfree: range: devvp = %p, ino = %d, fs = %s",
503                     pip->i_devvp, ino, fs->fs_fsmnt);
504
505 /* ext2_debug("ext2_vfree (%d, %d) called\n", pip->i_number, mode);
506  */
507         ext2_discard_prealloc(pip);
508
509         /* we need to make sure that ext2_free_inode can adjust the
510            used_dir_counts in the group summary information - I'd
511            really like to know what the rationale behind this
512            'set i_mode to zero to denote an unused inode' is
513          */
514         save_i_mode = pip->i_mode;
515         pip->i_mode = mode;
516         ext2_free_inode(pip);   
517         pip->i_mode = save_i_mode;
518         return (0);
519 }
520
521 /*
522  * Fserr prints the name of a file system with an error diagnostic.
523  * 
524  * The form of the error message is:
525  *      fs: error message
526  */
527 static void
528 ext2_fserr(fs, uid, cp)
529         struct ext2_sb_info *fs;
530         u_int uid;
531         char *cp;
532 {
533
534         log(LOG_ERR, "uid %d on %s: %s\n", uid, fs->fs_fsmnt, cp);
535 }