get rid of all users of a particular device
[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 *
a45ff315 17 * @(#)vfs_subr.c 7.16 (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;
c0de8792
KM
172
173#define SPECHSZ 64
174#if ((SPECHSZ&(SPECHSZ-1)) == 0)
175#define SPECHASH(rdev) (((rdev>>5)+(rdev))&(SPECHSZ-1))
176#else
177#define SPECHASH(rdev) (((unsigned)((rdev>>5)+(rdev)))%SPECHSZ)
178#endif
179struct vnode *speclisth[SPECHSZ];
36d09cb1
KM
180
181/*
ef24f6dd 182 * Initialize the vnode structures and initialize each file system type.
36d09cb1 183 */
ef24f6dd 184vfsinit()
36d09cb1
KM
185{
186 register struct vnode *vp = vnode;
ef24f6dd 187 struct vfsops **vfsp;
36d09cb1 188
ef24f6dd
KM
189 /*
190 * Build vnode free list.
191 */
36d09cb1
KM
192 vfreeh = vp;
193 vfreet = &vp->v_freef;
194 vp->v_freeb = &vfreeh;
195 vp->v_op = &dead_vnodeops;
196 for (vp++; vp < vnodeNVNODE; vp++) {
197 *vfreet = vp;
198 vp->v_freeb = vfreet;
199 vfreet = &vp->v_freef;
200 vp->v_op = &dead_vnodeops;
201 }
202 vp--;
203 vp->v_freef = NULL;
ef24f6dd
KM
204 /*
205 * Initialize the vnode name cache
206 */
207 nchinit();
208 /*
209 * Initialize each file system type.
210 */
211 for (vfsp = &vfssw[0]; vfsp <= &vfssw[MOUNT_MAXTYPE]; vfsp++) {
212 if (*vfsp == NULL)
213 continue;
214 (*(*vfsp)->vfs_init)();
215 }
36d09cb1
KM
216}
217
218/*
219 * Return the next vnode from the free list.
220 */
221getnewvnode(tag, mp, vops, vpp)
222 enum vtagtype tag;
223 struct mount *mp;
224 struct vnodeops *vops;
225 struct vnode **vpp;
226{
227 register struct vnode *vp, *vq;
228
229 if ((vp = vfreeh) == NULL) {
230 tablefull("vnode");
231 *vpp = 0;
232 return (ENFILE);
233 }
ef24f6dd 234 if (vp->v_count)
36d09cb1
KM
235 panic("free vnode isn't");
236 if (vq = vp->v_freef)
237 vq->v_freeb = &vfreeh;
238 vfreeh = vq;
239 vp->v_freef = NULL;
240 vp->v_freeb = NULL;
b027498b 241 if (vp->v_type != VNON && vp->v_type != VBAD)
ef24f6dd 242 vgone(vp);
b027498b 243 vp->v_type = VNON;
36d09cb1
KM
244 vp->v_flag = 0;
245 vp->v_shlockc = 0;
246 vp->v_exlockc = 0;
247 vp->v_socket = 0;
36d09cb1
KM
248 cache_purge(vp);
249 vp->v_tag = tag;
ef24f6dd 250 vp->v_op = vops;
36d09cb1
KM
251 insmntque(vp, mp);
252 VREF(vp);
253 *vpp = vp;
254 return (0);
255}
256
257/*
258 * Move a vnode from one mount queue to another.
259 */
260insmntque(vp, mp)
261 register struct vnode *vp;
262 register struct mount *mp;
263{
264 struct vnode *vq;
265
266 /*
267 * Delete from old mount point vnode list, if on one.
268 */
269 if (vp->v_mountb) {
270 if (vq = vp->v_mountf)
271 vq->v_mountb = vp->v_mountb;
272 *vp->v_mountb = vq;
273 }
274 /*
275 * Insert into list of vnodes for the new mount point, if available.
276 */
a45ff315 277 vp->v_mount = mp;
36d09cb1
KM
278 if (mp == NULL) {
279 vp->v_mountf = NULL;
280 vp->v_mountb = NULL;
281 return;
282 }
283 if (mp->m_mounth) {
284 vp->v_mountf = mp->m_mounth;
285 vp->v_mountb = &mp->m_mounth;
286 mp->m_mounth->v_mountb = &vp->v_mountf;
287 mp->m_mounth = vp;
288 } else {
289 mp->m_mounth = vp;
290 vp->v_mountb = &mp->m_mounth;
291 vp->v_mountf = NULL;
292 }
293}
294
295/*
ef24f6dd
KM
296 * Create a vnode for a block device.
297 * Used for root filesystem, argdev, and swap areas.
298 * Also used for memory file system special devices.
299 */
300bdevvp(dev, vpp)
301 dev_t dev;
302 struct vnode **vpp;
303{
ef24f6dd
KM
304 register struct vnode *vp;
305 struct vnode *nvp;
306 int error;
307
4ef5d036 308 error = getnewvnode(VT_NON, (struct mount *)0, &spec_vnodeops, &nvp);
ef24f6dd
KM
309 if (error) {
310 *vpp = 0;
311 return (error);
312 }
313 vp = nvp;
314 vp->v_type = VBLK;
c0de8792 315 if (nvp = checkalias(vp, dev, (struct mount *)0)) {
ef24f6dd
KM
316 vput(vp);
317 vp = nvp;
318 }
319 *vpp = vp;
320 return (0);
321}
322
323/*
324 * Check to see if the new vnode represents a special device
325 * for which we already have a vnode (either because of
326 * bdevvp() or because of a different vnode representing
327 * the same block device). If such an alias exists, deallocate
f0556f86 328 * the existing contents and return the aliased vnode. The
ef24f6dd
KM
329 * caller is responsible for filling it with its new contents.
330 */
331struct vnode *
c0de8792 332checkalias(nvp, nvp_rdev, mp)
ef24f6dd 333 register struct vnode *nvp;
c0de8792 334 dev_t nvp_rdev;
ef24f6dd
KM
335 struct mount *mp;
336{
337 register struct vnode *vp;
c0de8792 338 struct vnode **vpp;
ef24f6dd
KM
339
340 if (nvp->v_type != VBLK && nvp->v_type != VCHR)
341 return ((struct vnode *)0);
c0de8792
KM
342
343 vpp = &speclisth[SPECHASH(nvp_rdev)];
ef24f6dd 344loop:
c0de8792
KM
345 for (vp = *vpp; vp; vp = vp->v_specnext) {
346 if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type)
ef24f6dd
KM
347 continue;
348 if (vget(vp))
349 goto loop;
c0de8792
KM
350 /*
351 * Alias, but not in use, so flush it out.
352 */
353 if (vp->v_count == 1) {
354 vput(vp);
355 vgone(vp);
356 goto loop;
357 }
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
589/*
590 * Eliminate all activity associated with a vnode
591 * in preparation for reuse.
592 */
593void vgone(vp)
594 register struct vnode *vp;
595{
c0de8792
KM
596 register struct vnode *vq, **vpp;
597 struct vnode *vx;
598 long count;
ef24f6dd 599
ef24f6dd
KM
600 /*
601 * Clean out the filesystem specific data.
602 */
2bae1875 603 vclean(vp, 1);
ef24f6dd
KM
604 /*
605 * Delete from old mount point vnode list, if on one.
606 */
607 if (vp->v_mountb) {
608 if (vq = vp->v_mountf)
609 vq->v_mountb = vp->v_mountb;
610 *vp->v_mountb = vq;
611 vp->v_mountf = NULL;
612 vp->v_mountb = NULL;
613 }
614 /*
615 * If special device, remove it from special device alias list.
616 */
617 if (vp->v_type == VBLK || vp->v_type == VCHR) {
c0de8792
KM
618 vpp = &speclisth[SPECHASH(vp->v_rdev)];
619 if (*vpp == vp) {
620 *vpp = vp->v_specnext;
ef24f6dd 621 } else {
c0de8792
KM
622 for (vq = *vpp; vq; vq = vq->v_specnext) {
623 if (vq->v_specnext != vp)
ef24f6dd 624 continue;
c0de8792 625 vq->v_specnext = vp->v_specnext;
ef24f6dd
KM
626 break;
627 }
c0de8792 628 if (vq == NULL)
ef24f6dd
KM
629 panic("missing bdev");
630 }
c0de8792
KM
631 if (vp->v_flag & VALIASED) {
632 for (count = 0, vq = *vpp; vq; vq = vq->v_specnext) {
633 if (vq->v_rdev != vp->v_rdev)
634 continue;
635 count++;
636 vx = vq;
637 }
638 if (count == 0)
639 panic("missing alias");
640 if (count == 1)
641 vx->v_flag &= ~VALIASED;
642 vp->v_flag &= ~VALIASED;
643 }
644 FREE(vp->v_specinfo, M_VNODE);
645 vp->v_specinfo = NULL;
ef24f6dd
KM
646 }
647 /*
648 * If it is on the freelist, move it to the head of the list.
649 */
650 if (vp->v_freeb) {
651 if (vq = vp->v_freef)
652 vq->v_freeb = vp->v_freeb;
653 else
654 vfreet = vp->v_freeb;
655 *vp->v_freeb = vq;
656 vp->v_freef = vfreeh;
657 vp->v_freeb = &vfreeh;
658 vfreeh->v_freeb = &vp->v_freef;
659 vfreeh = vp;
660 }
2bae1875 661 vp->v_type = VBAD;
36d09cb1 662}