move read/writeblkvp from old ufs_io.c to spec_read/write
[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 *
b027498b 17 * @(#)vfs_subr.c 7.14 (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;
ef24f6dd
KM
172struct speclist *speclisth;
173struct speclist {
174 struct speclist *sl_next;
175 struct vnode *sl_vp;
176};
36d09cb1
KM
177
178/*
ef24f6dd 179 * Initialize the vnode structures and initialize each file system type.
36d09cb1 180 */
ef24f6dd 181vfsinit()
36d09cb1
KM
182{
183 register struct vnode *vp = vnode;
ef24f6dd 184 struct vfsops **vfsp;
36d09cb1 185
ef24f6dd
KM
186 /*
187 * Build vnode free list.
188 */
36d09cb1
KM
189 vfreeh = vp;
190 vfreet = &vp->v_freef;
191 vp->v_freeb = &vfreeh;
192 vp->v_op = &dead_vnodeops;
193 for (vp++; vp < vnodeNVNODE; vp++) {
194 *vfreet = vp;
195 vp->v_freeb = vfreet;
196 vfreet = &vp->v_freef;
197 vp->v_op = &dead_vnodeops;
198 }
199 vp--;
200 vp->v_freef = NULL;
ef24f6dd
KM
201 /*
202 * Initialize the vnode name cache
203 */
204 nchinit();
205 /*
206 * Initialize each file system type.
207 */
208 for (vfsp = &vfssw[0]; vfsp <= &vfssw[MOUNT_MAXTYPE]; vfsp++) {
209 if (*vfsp == NULL)
210 continue;
211 (*(*vfsp)->vfs_init)();
212 }
36d09cb1
KM
213}
214
215/*
216 * Return the next vnode from the free list.
217 */
218getnewvnode(tag, mp, vops, vpp)
219 enum vtagtype tag;
220 struct mount *mp;
221 struct vnodeops *vops;
222 struct vnode **vpp;
223{
224 register struct vnode *vp, *vq;
225
226 if ((vp = vfreeh) == NULL) {
227 tablefull("vnode");
228 *vpp = 0;
229 return (ENFILE);
230 }
ef24f6dd 231 if (vp->v_count)
36d09cb1
KM
232 panic("free vnode isn't");
233 if (vq = vp->v_freef)
234 vq->v_freeb = &vfreeh;
235 vfreeh = vq;
236 vp->v_freef = NULL;
237 vp->v_freeb = NULL;
b027498b 238 if (vp->v_type != VNON && vp->v_type != VBAD)
ef24f6dd 239 vgone(vp);
b027498b 240 vp->v_type = VNON;
36d09cb1
KM
241 vp->v_flag = 0;
242 vp->v_shlockc = 0;
243 vp->v_exlockc = 0;
244 vp->v_socket = 0;
36d09cb1
KM
245 cache_purge(vp);
246 vp->v_tag = tag;
ef24f6dd 247 vp->v_op = vops;
36d09cb1
KM
248 vp->v_mount = mp;
249 insmntque(vp, mp);
250 VREF(vp);
251 *vpp = vp;
252 return (0);
253}
254
255/*
256 * Move a vnode from one mount queue to another.
257 */
258insmntque(vp, mp)
259 register struct vnode *vp;
260 register struct mount *mp;
261{
262 struct vnode *vq;
263
264 /*
265 * Delete from old mount point vnode list, if on one.
266 */
267 if (vp->v_mountb) {
268 if (vq = vp->v_mountf)
269 vq->v_mountb = vp->v_mountb;
270 *vp->v_mountb = vq;
271 }
272 /*
273 * Insert into list of vnodes for the new mount point, if available.
274 */
275 if (mp == NULL) {
276 vp->v_mountf = NULL;
277 vp->v_mountb = NULL;
278 return;
279 }
280 if (mp->m_mounth) {
281 vp->v_mountf = mp->m_mounth;
282 vp->v_mountb = &mp->m_mounth;
283 mp->m_mounth->v_mountb = &vp->v_mountf;
284 mp->m_mounth = vp;
285 } else {
286 mp->m_mounth = vp;
287 vp->v_mountb = &mp->m_mounth;
288 vp->v_mountf = NULL;
289 }
290}
291
292/*
ef24f6dd
KM
293 * Create a vnode for a block device.
294 * Used for root filesystem, argdev, and swap areas.
295 * Also used for memory file system special devices.
296 */
297bdevvp(dev, vpp)
298 dev_t dev;
299 struct vnode **vpp;
300{
ef24f6dd
KM
301 register struct vnode *vp;
302 struct vnode *nvp;
303 int error;
304
4ef5d036 305 error = getnewvnode(VT_NON, (struct mount *)0, &spec_vnodeops, &nvp);
ef24f6dd
KM
306 if (error) {
307 *vpp = 0;
308 return (error);
309 }
310 vp = nvp;
311 vp->v_type = VBLK;
312 vp->v_rdev = dev;
313 if (nvp = checkalias(vp, (struct mount *)0)) {
314 vput(vp);
315 vp = nvp;
316 }
317 *vpp = vp;
318 return (0);
319}
320
321/*
322 * Check to see if the new vnode represents a special device
323 * for which we already have a vnode (either because of
324 * bdevvp() or because of a different vnode representing
325 * the same block device). If such an alias exists, deallocate
f0556f86 326 * the existing contents and return the aliased vnode. The
ef24f6dd
KM
327 * caller is responsible for filling it with its new contents.
328 */
329struct vnode *
330checkalias(nvp, mp)
331 register struct vnode *nvp;
332 struct mount *mp;
333{
334 register struct vnode *vp;
335 register struct speclist *slp;
336
337 if (nvp->v_type != VBLK && nvp->v_type != VCHR)
338 return ((struct vnode *)0);
339loop:
340 for (slp = speclisth; slp; slp = slp->sl_next) {
341 vp = slp->sl_vp;
342 if (nvp->v_rdev != vp->v_rdev ||
343 nvp->v_type != vp->v_type)
344 continue;
345 if (vget(vp))
346 goto loop;
347 break;
348 }
349 if (slp == NULL) {
350 MALLOC(slp, struct speclist *, sizeof(*slp), M_VNODE, M_WAITOK);
351 slp->sl_vp = nvp;
352 slp->sl_next = speclisth;
353 speclisth = slp;
354 return ((struct vnode *)0);
355 }
2bae1875
KM
356 VOP_UNLOCK(vp);
357 vclean(vp, 0);
ef24f6dd
KM
358 vp->v_op = nvp->v_op;
359 vp->v_tag = nvp->v_tag;
360 nvp->v_type = VNON;
361 insmntque(vp, mp);
362 return (vp);
363}
364
365/*
366 * Grab a particular vnode from the free list, increment its
367 * reference count and lock it. The vnode lock bit is set the
368 * vnode is being eliminated in vgone. The process is awakened
369 * when the transition is completed, and an error returned to
370 * indicate that the vnode is no longer usable (possibly having
371 * been changed to a new file system type).
36d09cb1
KM
372 */
373vget(vp)
374 register struct vnode *vp;
375{
376 register struct vnode *vq;
377
ef24f6dd
KM
378 if (vp->v_flag & VXLOCK) {
379 vp->v_flag |= VXWANT;
380 sleep((caddr_t)vp, PINOD);
381 return (1);
382 }
383 if (vp->v_count == 0) {
384 if (vq = vp->v_freef)
385 vq->v_freeb = vp->v_freeb;
386 else
387 vfreet = vp->v_freeb;
388 *vp->v_freeb = vq;
389 vp->v_freef = NULL;
390 vp->v_freeb = NULL;
391 }
36d09cb1 392 VREF(vp);
ef24f6dd
KM
393 VOP_LOCK(vp);
394 return (0);
36d09cb1
KM
395}
396
397/*
398 * Vnode reference, just increment the count
399 */
400void vref(vp)
401 struct vnode *vp;
402{
403
404 vp->v_count++;
405}
406
407/*
408 * vput(), just unlock and vrele()
409 */
410void vput(vp)
411 register struct vnode *vp;
412{
413 VOP_UNLOCK(vp);
414 vrele(vp);
415}
416
417/*
418 * Vnode release.
419 * If count drops to zero, call inactive routine and return to freelist.
420 */
421void vrele(vp)
422 register struct vnode *vp;
423{
424
425 if (vp == NULL)
ef24f6dd 426 panic("vrele: null vp");
36d09cb1
KM
427 vp->v_count--;
428 if (vp->v_count < 0)
429 printf("vnode bad ref count %d, type %d, tag %d\n",
430 vp->v_count, vp->v_type, vp->v_tag);
431 if (vp->v_count > 0)
432 return;
36d09cb1
KM
433 if (vfreeh == (struct vnode *)0) {
434 /*
435 * insert into empty list
436 */
437 vfreeh = vp;
438 vp->v_freeb = &vfreeh;
36d09cb1
KM
439 } else {
440 /*
441 * insert at tail of list
442 */
443 *vfreet = vp;
444 vp->v_freeb = vfreet;
36d09cb1 445 }
ef24f6dd
KM
446 vp->v_freef = NULL;
447 vfreet = &vp->v_freef;
448 VOP_INACTIVE(vp);
449}
450
f0556f86
KM
451/*
452 * Remove any vnodes in the vnode table belonging to mount point mp.
453 *
454 * If MNT_NOFORCE is specified, there should not be any active ones,
455 * return error if any are found (nb: this is a user error, not a
456 * system error). If MNT_FORCE is specified, detach any active vnodes
457 * that are found.
458 */
459int busyprt = 0; /* patch to print out busy vnodes */
460
461vflush(mp, skipvp, flags)
462 struct mount *mp;
463 struct vnode *skipvp;
464 int flags;
465{
466 register struct vnode *vp, *nvp;
467 int busy = 0;
468
469 for (vp = mp->m_mounth; vp; vp = nvp) {
470 nvp = vp->v_mountf;
471 /*
472 * Skip over a selected vnode.
473 * Used by ufs to skip over the quota structure inode.
474 */
475 if (vp == skipvp)
476 continue;
477 /*
478 * With v_count == 0, all we need to do is clear
479 * out the vnode data structures and we are done.
480 */
481 if (vp->v_count == 0) {
482 vgone(vp);
483 continue;
484 }
485 /*
486 * For block or character devices, revert to an
487 * anonymous device. For all other files, just kill them.
488 */
489 if (flags & MNT_FORCE) {
490 if (vp->v_type != VBLK && vp->v_type != VCHR) {
491 vgone(vp);
492 } else {
493 vclean(vp, 0);
494 vp->v_op = &spec_vnodeops;
495 insmntque(vp, (struct mount *)0);
496 }
497 continue;
498 }
499 if (busyprt)
500 printf("vflush: busy vnode count %d type %d tag %d\n",
501 vp->v_count, vp->v_type, vp->v_tag);
502 busy++;
503 }
504 if (busy)
505 return (EBUSY);
506 return (0);
507}
508
ef24f6dd
KM
509/*
510 * Disassociate the underlying file system from a vnode.
ef24f6dd 511 */
2bae1875 512void vclean(vp, doclose)
ef24f6dd 513 register struct vnode *vp;
2bae1875 514 long doclose;
ef24f6dd
KM
515{
516 struct vnodeops *origops;
2bae1875 517 int active;
ef24f6dd 518
2bae1875
KM
519 /*
520 * Check to see if the vnode is in use.
521 * If so we have to lock it before we clean it out.
522 */
523 if (active = vp->v_count) {
524 VREF(vp);
525 VOP_LOCK(vp);
526 }
527 /*
528 * Prevent the vnode from being recycled or
529 * brought into use while we clean it out.
530 */
ef24f6dd
KM
531 while (vp->v_flag & VXLOCK) {
532 vp->v_flag |= VXWANT;
533 sleep((caddr_t)vp, PINOD);
534 }
535 vp->v_flag |= VXLOCK;
536 /*
537 * Prevent any further operations on the vnode from
538 * being passed through to the old file system.
539 */
540 origops = vp->v_op;
541 vp->v_op = &dead_vnodeops;
542 vp->v_tag = VT_NON;
543 /*
2bae1875
KM
544 * If purging an active vnode, it must be unlocked, closed,
545 * and deactivated before being reclaimed.
ef24f6dd 546 */
2bae1875 547 if (active) {
ef24f6dd 548 (*(origops->vn_unlock))(vp);
2bae1875
KM
549 if (doclose)
550 (*(origops->vn_close))(vp, 0, NOCRED);
ef24f6dd
KM
551 (*(origops->vn_inactive))(vp);
552 }
553 /*
554 * Reclaim the vnode.
555 */
556 if ((*(origops->vn_reclaim))(vp))
557 panic("vclean: cannot reclaim");
2bae1875
KM
558 if (active)
559 vrele(vp);
ef24f6dd
KM
560 /*
561 * Done with purge, notify sleepers in vget of the grim news.
562 */
563 vp->v_flag &= ~VXLOCK;
564 if (vp->v_flag & VXWANT) {
565 vp->v_flag &= ~VXWANT;
566 wakeup((caddr_t)vp);
567 }
568}
569
570/*
571 * Eliminate all activity associated with a vnode
572 * in preparation for reuse.
573 */
574void vgone(vp)
575 register struct vnode *vp;
576{
577 register struct speclist *slp;
578 struct speclist *pslp;
579 register struct vnode *vq;
580
ef24f6dd
KM
581 /*
582 * Clean out the filesystem specific data.
583 */
2bae1875 584 vclean(vp, 1);
ef24f6dd
KM
585 /*
586 * Delete from old mount point vnode list, if on one.
587 */
588 if (vp->v_mountb) {
589 if (vq = vp->v_mountf)
590 vq->v_mountb = vp->v_mountb;
591 *vp->v_mountb = vq;
592 vp->v_mountf = NULL;
593 vp->v_mountb = NULL;
594 }
595 /*
596 * If special device, remove it from special device alias list.
597 */
598 if (vp->v_type == VBLK || vp->v_type == VCHR) {
599 if (speclisth->sl_vp == vp) {
600 slp = speclisth;
601 speclisth = slp->sl_next;
602 } else {
ff4fb102 603 for (pslp = speclisth, slp = pslp->sl_next; slp;
ef24f6dd
KM
604 pslp = slp, slp = slp->sl_next) {
605 if (slp->sl_vp != vp)
606 continue;
607 pslp->sl_next = slp->sl_next;
608 break;
609 }
610 if (slp == NULL)
611 panic("missing bdev");
612 }
613 FREE(slp, M_VNODE);
614 }
615 /*
616 * If it is on the freelist, move it to the head of the list.
617 */
618 if (vp->v_freeb) {
619 if (vq = vp->v_freef)
620 vq->v_freeb = vp->v_freeb;
621 else
622 vfreet = vp->v_freeb;
623 *vp->v_freeb = vq;
624 vp->v_freef = vfreeh;
625 vp->v_freeb = &vfreeh;
626 vfreeh->v_freeb = &vp->v_freef;
627 vfreeh = vp;
628 }
2bae1875 629 vp->v_type = VBAD;
36d09cb1 630}