Date: Tue, 14 Nov 1995 11:06:20 -0700 (MST) From: Terry Lambert <terry@lambert.org> To: davidg@root.com Cc: terry@lambert.org, rashid@rk.ios.com, jgreco@brasil.moneng.mei.com, hackers@freebsd.org Subject: Re: Odd crash after inode depletion.. Message-ID: <199511141806.LAA20459@phaeton.artisoft.com> In-Reply-To: <199511141756.JAA00503@corbin.Root.COM> from "David Greenman" at Nov 14, 95 09:56:07 am
next in thread | previous in thread | raw e-mail | index | archive | help
> > >> Do you know the difference of appearance of a cicular queue in our sources? > >> Based on your comments, I must conclude that you do not. tailq's are NOT > >> circular queues. The vnode free list is NOT a circular queue and it never has > >> been in FreeBSD. > > > >First of all, I correctly identified the code that was screwing up from > >a written description of the problem. I wrote the part about the circular > >queues before going to the code (I was going to not attempt a cursor fix > >precisely because it doesn't matter what I post, I get attacked). > > No you did not identify the problem. vnodes on the free list are not > supposed to *ever* have non-zero usecounts. Something gained a reference to > the vnode without pulling it off the freelist. THAT is the problem. int vget(vp, lockflag) register struct vnode *vp; int lockflag; { /* * If the vnode is in the process of being cleaned out for another * use, we wait for the cleaning to finish and then return failure. * Cleaning is determined either by checking that the VXLOCK flag is * set, or that the use count is zero with the back pointer set to * show that it has been removed from the free list by getnewvnode. * The VXLOCK flag may not have been set yet because vclean is blocked * in the VOP_LOCK call waiting for the VOP_INACTIVE to complete. */ if ((vp->v_flag & VXLOCK) || (vp->v_usecount == 0 && vp->v_freelist.tqe_prev == (struct vnode **) 0xdeadb)) { vp->v_flag |= VXWANT; (void) tsleep((caddr_t) vp, PINOD, "vget", 0); return (1); } ****** ****** Next compare assumes node from freelist can have non-0 usecount ****** if (vp->v_usecount == 0) { TAILQ_REMOVE(&vnode_free_list, vp, v_freelist); freevnodes--; } ****** ****** This ordering is evil. To make it less evil would require ****** abstracting the vnode locking from the underlying file systems ****** into a file system independent layer using an owner and a counting ****** semaphore. Like SunOS or SVR4 or Dynix. ****** vp->v_usecount++; if (lockflag) VOP_LOCK(vp); return (0); } > > >Second, it was a change to the queue management that cause the screwup, > >since even you must admit that it didn't happen in the past, and the > >change was to add more vnodes, and the failure mode was in the queue > >head check that guards the allocation code. > > We did !!!NOT!!! change the queue management for vnodes! How many times do > I have to say this? Until you change the comments. /* * Return the next vnode from the free list. */ int getnewvnode(tag, mp, vops, vpp) enum vtagtype tag; struct mount *mp; vop_t **vops; struct vnode **vpp; { register struct vnode *vp; vp = vnode_free_list.tqh_first; /* * we allocate a new vnode if ******** * 1. we don't have any free ******** * Pretty obvious, we actually used to panic, but that ******** * is a silly thing to do. * 2. we havn't filled our pool yet * We don't want to trash the incore (VM-)vnodecache. * 3. if less that 1/4th of our vnodes are free. * We don't want to trash the namei cache either. */ if (freevnodes < (numvnodes >> 2) || numvnodes < desiredvnodes || vp == NULL) { vp = (struct vnode *) malloc((u_long) sizeof *vp, M_VNODE, M_WAITOK); bzero((char *) vp, sizeof *vp); numvnodes++; } else { TAILQ_REMOVE(&vnode_free_list, vp, v_freelist); freevnodes--; "used to panic" == "no longer panic" == "changed code". > >Third, I identified two potential quick hack fixes. > > Which are wrong. You don't understand the problem or the code. Which is why I used that little adjective "potential" and didn't claim them as fixes in my post. > >> The change to a circular queue for the mountlist was NOT made for aesthetic > >> reasons. It was made because it is required to traverse the mount queue in the > >> reverse direction in order to properly dismount filesystems (because of mount > >> point dependencies). > > > >This is why god invented the stack and function recursion. It's called a > >depth first traversal. > > I had considered doing the mount insertion at the head. "df" then comes out > backwards. circleq's are the correct solution. It would have been equally valid to do a recursive descent, unmounting on ascent instead of descent to ensure proper ordering. Terry Lambert terry@lambert.org --- Any opinions in this posting are my own and not those of my present or previous employers.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199511141806.LAA20459>