add null bmap routine; add printing routine; now flush dev vnode directly
[unix-history] / usr / src / sys / kern / vfs_subr.c
CommitLineData
3c4390e8
KM
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 *
32339c94 17 * @(#)vfs_subr.c 7.18 (Berkeley) %G%
3c4390e8
KM
18 */
19
20/*
21 * External virtual filesystem routines
22 */
23
24#include "param.h"
25#include "mount.h"
26#include "time.h"
27#include "vnode.h"
c60798ca
KM
28#include "namei.h"
29#include "ucred.h"
3c4390e8 30#include "errno.h"
ef24f6dd 31#include "malloc.h"
3c4390e8 32
3c4390e8
KM
33/*
34 * Remove a mount point from the list of mounted filesystems.
35 * Unmount of the root is illegal.
36 */
37void
38vfs_remove(mp)
39 register struct mount *mp;
40{
41
42 if (mp == rootfs)
43 panic("vfs_remove: unmounting root");
44 mp->m_prev->m_next = mp->m_next;
45 mp->m_next->m_prev = mp->m_prev;
46 mp->m_vnodecovered->v_mountedhere = (struct mount *)0;
47 vfs_unlock(mp);
48}
49
50/*
51 * Lock a filesystem.
52 * Used to prevent access to it while mounting and unmounting.
53 */
54vfs_lock(mp)
55 register struct mount *mp;
56{
57
594501df
KM
58 while(mp->m_flag & M_MLOCK) {
59 mp->m_flag |= M_MWAIT;
60 sleep((caddr_t)mp, PVFS);
61 }
3c4390e8
KM
62 mp->m_flag |= M_MLOCK;
63 return (0);
64}
65
66/*
67 * Unlock a locked filesystem.
68 * Panic if filesystem is not locked.
69 */
70void
71vfs_unlock(mp)
72 register struct mount *mp;
73{
74
75 if ((mp->m_flag & M_MLOCK) == 0)
76 panic("vfs_unlock: locked fs");
77 mp->m_flag &= ~M_MLOCK;
78 if (mp->m_flag & M_MWAIT) {
79 mp->m_flag &= ~M_MWAIT;
80 wakeup((caddr_t)mp);
81 }
82}
83
84/*
85 * Lookup a mount point by filesystem identifier.
86 */
87struct mount *
88getvfs(fsid)
89 fsid_t *fsid;
90{
91 register struct mount *mp;
92
d713f801
KM
93 mp = rootfs;
94 do {
3c4390e8
KM
95 if (mp->m_fsid.val[0] == fsid->val[0] &&
96 mp->m_fsid.val[1] == fsid->val[1]) {
d713f801 97 return (mp);
3c4390e8 98 }
d713f801
KM
99 mp = mp->m_next;
100 } while (mp != rootfs);
101 return ((struct mount *)0);
3c4390e8
KM
102}
103
104/*
105 * Set vnode attributes to VNOVAL
106 */
107void vattr_null(vap)
108 register struct vattr *vap;
109{
110
111 vap->va_type = VNON;
112 vap->va_mode = vap->va_nlink = vap->va_uid = vap->va_gid =
113 vap->va_fsid = vap->va_fileid = vap->va_size =
114 vap->va_size1 = vap->va_blocksize = vap->va_rdev =
115 vap->va_bytes = vap->va_bytes1 =
116 vap->va_atime.tv_sec = vap->va_atime.tv_usec =
117 vap->va_mtime.tv_sec = vap->va_mtime.tv_usec =
8cf4d4fb
KM
118 vap->va_ctime.tv_sec = vap->va_ctime.tv_usec =
119 vap->va_flags = vap->va_gen = VNOVAL;
3c4390e8 120}
c60798ca
KM
121
122/*
123 * Initialize a nameidata structure
124 */
125ndinit(ndp)
126 register struct nameidata *ndp;
127{
128
129 bzero((caddr_t)ndp, sizeof(struct nameidata));
130 ndp->ni_iov = &ndp->ni_nd.nd_iovec;
131 ndp->ni_iovcnt = 1;
132 ndp->ni_base = (caddr_t)&ndp->ni_dent;
133 ndp->ni_rw = UIO_WRITE;
134 ndp->ni_segflg = UIO_SYSSPACE;
135}
136
137/*
138 * Duplicate a nameidata structure
139 */
140nddup(ndp, newndp)
141 register struct nameidata *ndp, *newndp;
142{
143
144 ndinit(newndp);
145 newndp->ni_cdir = ndp->ni_cdir;
8fe1c702 146 VREF(newndp->ni_cdir);
c60798ca
KM
147 newndp->ni_rdir = ndp->ni_rdir;
148 if (newndp->ni_rdir)
8fe1c702 149 VREF(newndp->ni_rdir);
c60798ca
KM
150 newndp->ni_cred = ndp->ni_cred;
151 crhold(newndp->ni_cred);
152}
153
154/*
155 * Release a nameidata structure
156 */
157ndrele(ndp)
158 register struct nameidata *ndp;
159{
160
161 vrele(ndp->ni_cdir);
162 if (ndp->ni_rdir)
163 vrele(ndp->ni_rdir);
164 crfree(ndp->ni_cred);
165}
36d09cb1
KM
166
167/*
168 * Routines having to do with the management of the vnode table.
169 */
170struct vnode *vfreeh, **vfreet;
4ef5d036 171extern struct vnodeops dead_vnodeops, spec_vnodeops;
32339c94 172extern void vclean();
c0de8792
KM
173
174#define SPECHSZ 64
175#if ((SPECHSZ&(SPECHSZ-1)) == 0)
176#define SPECHASH(rdev) (((rdev>>5)+(rdev))&(SPECHSZ-1))
177#else
178#define SPECHASH(rdev) (((unsigned)((rdev>>5)+(rdev)))%SPECHSZ)
179#endif
180struct vnode *speclisth[SPECHSZ];
36d09cb1
KM
181
182/*
ef24f6dd 183 * Initialize the vnode structures and initialize each file system type.
36d09cb1 184 */
ef24f6dd 185vfsinit()
36d09cb1
KM
186{
187 register struct vnode *vp = vnode;
ef24f6dd 188 struct vfsops **vfsp;
36d09cb1 189
ef24f6dd
KM
190 /*
191 * Build vnode free list.
192 */
36d09cb1
KM
193 vfreeh = vp;
194 vfreet = &vp->v_freef;
195 vp->v_freeb = &vfreeh;
196 vp->v_op = &dead_vnodeops;
197 for (vp++; vp < vnodeNVNODE; vp++) {
198 *vfreet = vp;
199 vp->v_freeb = vfreet;
200 vfreet = &vp->v_freef;
201 vp->v_op = &dead_vnodeops;
202 }
203 vp--;
204 vp->v_freef = NULL;
ef24f6dd
KM
205 /*
206 * Initialize the vnode name cache
207 */
208 nchinit();
209 /*
210 * Initialize each file system type.
211 */
212 for (vfsp = &vfssw[0]; vfsp <= &vfssw[MOUNT_MAXTYPE]; vfsp++) {
213 if (*vfsp == NULL)
214 continue;
215 (*(*vfsp)->vfs_init)();
216 }
36d09cb1
KM
217}
218
219/*
220 * Return the next vnode from the free list.
221 */
222getnewvnode(tag, mp, vops, vpp)
223 enum vtagtype tag;
224 struct mount *mp;
225 struct vnodeops *vops;
226 struct vnode **vpp;
227{
228 register struct vnode *vp, *vq;
229
230 if ((vp = vfreeh) == NULL) {
231 tablefull("vnode");
232 *vpp = 0;
233 return (ENFILE);
234 }
ef24f6dd 235 if (vp->v_count)
36d09cb1
KM
236 panic("free vnode isn't");
237 if (vq = vp->v_freef)
238 vq->v_freeb = &vfreeh;
239 vfreeh = vq;
240 vp->v_freef = NULL;
241 vp->v_freeb = NULL;
b027498b 242 if (vp->v_type != VNON && vp->v_type != VBAD)
ef24f6dd 243 vgone(vp);
b027498b 244 vp->v_type = VNON;
36d09cb1
KM
245 vp->v_flag = 0;
246 vp->v_shlockc = 0;
247 vp->v_exlockc = 0;
248 vp->v_socket = 0;
36d09cb1
KM
249 cache_purge(vp);
250 vp->v_tag = tag;
ef24f6dd 251 vp->v_op = vops;
36d09cb1
KM
252 insmntque(vp, mp);
253 VREF(vp);
254 *vpp = vp;
255 return (0);
256}
257
258/*
259 * Move a vnode from one mount queue to another.
260 */
261insmntque(vp, mp)
262 register struct vnode *vp;
263 register struct mount *mp;
264{
265 struct vnode *vq;
266
267 /*
268 * Delete from old mount point vnode list, if on one.
269 */
270 if (vp->v_mountb) {
271 if (vq = vp->v_mountf)
272 vq->v_mountb = vp->v_mountb;
273 *vp->v_mountb = vq;
274 }
275 /*
276 * Insert into list of vnodes for the new mount point, if available.
277 */
a45ff315 278 vp->v_mount = mp;
36d09cb1
KM
279 if (mp == NULL) {
280 vp->v_mountf = NULL;
281 vp->v_mountb = NULL;
282 return;
283 }
284 if (mp->m_mounth) {
285 vp->v_mountf = mp->m_mounth;
286 vp->v_mountb = &mp->m_mounth;
287 mp->m_mounth->v_mountb = &vp->v_mountf;
288 mp->m_mounth = vp;
289 } else {
290 mp->m_mounth = vp;
291 vp->v_mountb = &mp->m_mounth;
292 vp->v_mountf = NULL;
293 }
294}
295
296/*
ef24f6dd
KM
297 * Create a vnode for a block device.
298 * Used for root filesystem, argdev, and swap areas.
299 * Also used for memory file system special devices.
300 */
301bdevvp(dev, vpp)
302 dev_t dev;
303 struct vnode **vpp;
304{
ef24f6dd
KM
305 register struct vnode *vp;
306 struct vnode *nvp;
307 int error;
308
4ef5d036 309 error = getnewvnode(VT_NON, (struct mount *)0, &spec_vnodeops, &nvp);
ef24f6dd
KM
310 if (error) {
311 *vpp = 0;
312 return (error);
313 }
314 vp = nvp;
315 vp->v_type = VBLK;
c0de8792 316 if (nvp = checkalias(vp, dev, (struct mount *)0)) {
ef24f6dd
KM
317 vput(vp);
318 vp = nvp;
319 }
320 *vpp = vp;
321 return (0);
322}
323
324/*
325 * Check to see if the new vnode represents a special device
326 * for which we already have a vnode (either because of
327 * bdevvp() or because of a different vnode representing
328 * the same block device). If such an alias exists, deallocate
f0556f86 329 * the existing contents and return the aliased vnode. The
ef24f6dd
KM
330 * caller is responsible for filling it with its new contents.
331 */
332struct vnode *
c0de8792 333checkalias(nvp, nvp_rdev, mp)
ef24f6dd 334 register struct vnode *nvp;
c0de8792 335 dev_t nvp_rdev;
ef24f6dd
KM
336 struct mount *mp;
337{
338 register struct vnode *vp;
c0de8792 339 struct vnode **vpp;
ef24f6dd
KM
340
341 if (nvp->v_type != VBLK && nvp->v_type != VCHR)
342 return ((struct vnode *)0);
c0de8792
KM
343
344 vpp = &speclisth[SPECHASH(nvp_rdev)];
ef24f6dd 345loop:
c0de8792
KM
346 for (vp = *vpp; vp; vp = vp->v_specnext) {
347 if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type)
ef24f6dd 348 continue;
c0de8792
KM
349 /*
350 * Alias, but not in use, so flush it out.
351 */
ef62830d 352 if (vp->v_count == 0) {
c0de8792
KM
353 vgone(vp);
354 goto loop;
355 }
ef62830d
KM
356 if (vget(vp))
357 goto loop;
ef24f6dd
KM
358 break;
359 }
c0de8792
KM
360 if (vp == NULL || vp->v_tag != VT_NON) {
361 if (vp != NULL) {
c0de8792 362 nvp->v_flag |= VALIASED;
a45ff315
KM
363 vp->v_flag |= VALIASED;
364 vput(vp);
c0de8792
KM
365 }
366 MALLOC(nvp->v_specinfo, struct specinfo *,
367 sizeof(struct specinfo), M_VNODE, M_WAITOK);
368 nvp->v_rdev = nvp_rdev;
369 nvp->v_mounton = NULL;
370 nvp->v_lastr = 0;
371 nvp->v_specnext = *vpp;
372 *vpp = nvp;
ef24f6dd
KM
373 return ((struct vnode *)0);
374 }
2bae1875
KM
375 VOP_UNLOCK(vp);
376 vclean(vp, 0);
ef24f6dd
KM
377 vp->v_op = nvp->v_op;
378 vp->v_tag = nvp->v_tag;
379 nvp->v_type = VNON;
380 insmntque(vp, mp);
381 return (vp);
382}
383
384/*
385 * Grab a particular vnode from the free list, increment its
386 * reference count and lock it. The vnode lock bit is set the
387 * vnode is being eliminated in vgone. The process is awakened
388 * when the transition is completed, and an error returned to
389 * indicate that the vnode is no longer usable (possibly having
390 * been changed to a new file system type).
36d09cb1
KM
391 */
392vget(vp)
393 register struct vnode *vp;
394{
395 register struct vnode *vq;
396
ef24f6dd
KM
397 if (vp->v_flag & VXLOCK) {
398 vp->v_flag |= VXWANT;
399 sleep((caddr_t)vp, PINOD);
400 return (1);
401 }
402 if (vp->v_count == 0) {
403 if (vq = vp->v_freef)
404 vq->v_freeb = vp->v_freeb;
405 else
406 vfreet = vp->v_freeb;
407 *vp->v_freeb = vq;
408 vp->v_freef = NULL;
409 vp->v_freeb = NULL;
410 }
36d09cb1 411 VREF(vp);
ef24f6dd
KM
412 VOP_LOCK(vp);
413 return (0);
36d09cb1
KM
414}
415
416/*
417 * Vnode reference, just increment the count
418 */
419void vref(vp)
420 struct vnode *vp;
421{
422
423 vp->v_count++;
424}
425
426/*
427 * vput(), just unlock and vrele()
428 */
429void vput(vp)
430 register struct vnode *vp;
431{
432 VOP_UNLOCK(vp);
433 vrele(vp);
434}
435
436/*
437 * Vnode release.
438 * If count drops to zero, call inactive routine and return to freelist.
439 */
440void vrele(vp)
441 register struct vnode *vp;
442{
443
444 if (vp == NULL)
ef24f6dd 445 panic("vrele: null vp");
36d09cb1
KM
446 vp->v_count--;
447 if (vp->v_count < 0)
448 printf("vnode bad ref count %d, type %d, tag %d\n",
449 vp->v_count, vp->v_type, vp->v_tag);
450 if (vp->v_count > 0)
451 return;
36d09cb1
KM
452 if (vfreeh == (struct vnode *)0) {
453 /*
454 * insert into empty list
455 */
456 vfreeh = vp;
457 vp->v_freeb = &vfreeh;
36d09cb1
KM
458 } else {
459 /*
460 * insert at tail of list
461 */
462 *vfreet = vp;
463 vp->v_freeb = vfreet;
36d09cb1 464 }
ef24f6dd
KM
465 vp->v_freef = NULL;
466 vfreet = &vp->v_freef;
467 VOP_INACTIVE(vp);
468}
469
f0556f86
KM
470/*
471 * Remove any vnodes in the vnode table belonging to mount point mp.
472 *
473 * If MNT_NOFORCE is specified, there should not be any active ones,
474 * return error if any are found (nb: this is a user error, not a
475 * system error). If MNT_FORCE is specified, detach any active vnodes
476 * that are found.
477 */
478int busyprt = 0; /* patch to print out busy vnodes */
479
480vflush(mp, skipvp, flags)
481 struct mount *mp;
482 struct vnode *skipvp;
483 int flags;
484{
485 register struct vnode *vp, *nvp;
486 int busy = 0;
487
488 for (vp = mp->m_mounth; vp; vp = nvp) {
489 nvp = vp->v_mountf;
490 /*
491 * Skip over a selected vnode.
492 * Used by ufs to skip over the quota structure inode.
493 */
494 if (vp == skipvp)
495 continue;
496 /*
497 * With v_count == 0, all we need to do is clear
498 * out the vnode data structures and we are done.
499 */
500 if (vp->v_count == 0) {
501 vgone(vp);
502 continue;
503 }
504 /*
505 * For block or character devices, revert to an
506 * anonymous device. For all other files, just kill them.
507 */
508 if (flags & MNT_FORCE) {
509 if (vp->v_type != VBLK && vp->v_type != VCHR) {
510 vgone(vp);
511 } else {
512 vclean(vp, 0);
513 vp->v_op = &spec_vnodeops;
514 insmntque(vp, (struct mount *)0);
515 }
516 continue;
517 }
518 if (busyprt)
519 printf("vflush: busy vnode count %d type %d tag %d\n",
520 vp->v_count, vp->v_type, vp->v_tag);
521 busy++;
522 }
523 if (busy)
524 return (EBUSY);
525 return (0);
526}
527
ef24f6dd
KM
528/*
529 * Disassociate the underlying file system from a vnode.
ef24f6dd 530 */
2bae1875 531void vclean(vp, doclose)
ef24f6dd 532 register struct vnode *vp;
2bae1875 533 long doclose;
ef24f6dd
KM
534{
535 struct vnodeops *origops;
2bae1875 536 int active;
ef24f6dd 537
2bae1875
KM
538 /*
539 * Check to see if the vnode is in use.
540 * If so we have to lock it before we clean it out.
541 */
542 if (active = vp->v_count) {
543 VREF(vp);
544 VOP_LOCK(vp);
545 }
546 /*
547 * Prevent the vnode from being recycled or
548 * brought into use while we clean it out.
549 */
ef24f6dd
KM
550 while (vp->v_flag & VXLOCK) {
551 vp->v_flag |= VXWANT;
552 sleep((caddr_t)vp, PINOD);
553 }
554 vp->v_flag |= VXLOCK;
555 /*
556 * Prevent any further operations on the vnode from
557 * being passed through to the old file system.
558 */
559 origops = vp->v_op;
560 vp->v_op = &dead_vnodeops;
561 vp->v_tag = VT_NON;
562 /*
2bae1875
KM
563 * If purging an active vnode, it must be unlocked, closed,
564 * and deactivated before being reclaimed.
ef24f6dd 565 */
2bae1875 566 if (active) {
ef24f6dd 567 (*(origops->vn_unlock))(vp);
2bae1875
KM
568 if (doclose)
569 (*(origops->vn_close))(vp, 0, NOCRED);
ef24f6dd
KM
570 (*(origops->vn_inactive))(vp);
571 }
572 /*
573 * Reclaim the vnode.
574 */
575 if ((*(origops->vn_reclaim))(vp))
576 panic("vclean: cannot reclaim");
2bae1875
KM
577 if (active)
578 vrele(vp);
ef24f6dd
KM
579 /*
580 * Done with purge, notify sleepers in vget of the grim news.
581 */
582 vp->v_flag &= ~VXLOCK;
583 if (vp->v_flag & VXWANT) {
584 vp->v_flag &= ~VXWANT;
585 wakeup((caddr_t)vp);
586 }
587}
588
ef62830d
KM
589/*
590 * Eliminate all activity associated with the requested vnode
591 * and with all vnodes aliased to the requested vnode.
592 */
593void vgoneall(vp)
594 register struct vnode *vp;
595{
596 register struct vnode *vq, **vpp;
597
598 if (vp->v_flag & VALIASED) {
599 vpp = &speclisth[SPECHASH(vp->v_rdev)];
600 loop:
601 for (vq = *vpp; vq; vq = vq->v_specnext) {
602 if (vq->v_rdev != vp->v_rdev || vp == vq)
603 continue;
604 vgone(vq);
605 goto loop;
606 }
607 }
608 vgone(vp);
609}
610
ef24f6dd
KM
611/*
612 * Eliminate all activity associated with a vnode
613 * in preparation for reuse.
614 */
615void vgone(vp)
616 register struct vnode *vp;
617{
c0de8792
KM
618 register struct vnode *vq, **vpp;
619 struct vnode *vx;
620 long count;
ef24f6dd 621
ef24f6dd
KM
622 /*
623 * Clean out the filesystem specific data.
624 */
2bae1875 625 vclean(vp, 1);
ef24f6dd
KM
626 /*
627 * Delete from old mount point vnode list, if on one.
628 */
629 if (vp->v_mountb) {
630 if (vq = vp->v_mountf)
631 vq->v_mountb = vp->v_mountb;
632 *vp->v_mountb = vq;
633 vp->v_mountf = NULL;
634 vp->v_mountb = NULL;
635 }
636 /*
637 * If special device, remove it from special device alias list.
638 */
639 if (vp->v_type == VBLK || vp->v_type == VCHR) {
c0de8792
KM
640 vpp = &speclisth[SPECHASH(vp->v_rdev)];
641 if (*vpp == vp) {
642 *vpp = vp->v_specnext;
ef24f6dd 643 } else {
c0de8792
KM
644 for (vq = *vpp; vq; vq = vq->v_specnext) {
645 if (vq->v_specnext != vp)
ef24f6dd 646 continue;
c0de8792 647 vq->v_specnext = vp->v_specnext;
ef24f6dd
KM
648 break;
649 }
c0de8792 650 if (vq == NULL)
ef24f6dd
KM
651 panic("missing bdev");
652 }
c0de8792
KM
653 if (vp->v_flag & VALIASED) {
654 for (count = 0, vq = *vpp; vq; vq = vq->v_specnext) {
655 if (vq->v_rdev != vp->v_rdev)
656 continue;
657 count++;
658 vx = vq;
659 }
660 if (count == 0)
661 panic("missing alias");
662 if (count == 1)
663 vx->v_flag &= ~VALIASED;
664 vp->v_flag &= ~VALIASED;
665 }
666 FREE(vp->v_specinfo, M_VNODE);
667 vp->v_specinfo = NULL;
ef24f6dd
KM
668 }
669 /*
670 * If it is on the freelist, move it to the head of the list.
671 */
672 if (vp->v_freeb) {
673 if (vq = vp->v_freef)
674 vq->v_freeb = vp->v_freeb;
675 else
676 vfreet = vp->v_freeb;
677 *vp->v_freeb = vq;
678 vp->v_freef = vfreeh;
679 vp->v_freeb = &vfreeh;
680 vfreeh->v_freeb = &vp->v_freef;
681 vfreeh = vp;
682 }
2bae1875 683 vp->v_type = VBAD;
36d09cb1 684}
ef62830d
KM
685
686/*
687 * Calculate the total number of references to a special device.
688 */
689vcount(vp)
690 register struct vnode *vp;
691{
692 register struct vnode *vq, **vpp;
693 int count;
694
695 if ((vp->v_flag & VALIASED) == 0)
696 return (vp->v_count);
697 vpp = &speclisth[SPECHASH(vp->v_rdev)];
698loop:
699 for (count = 0, vq = *vpp; vq; vq = vq->v_specnext) {
700 if (vq->v_rdev != vp->v_rdev)
701 continue;
702 /*
703 * Alias, but not in use, so flush it out.
704 */
705 if (vq->v_count == 0) {
706 vgone(vq);
707 goto loop;
708 }
709 count += vq->v_count;
710 }
711 return (count);
712}