On 28/11/22(Mon) 15:04, Mark Kettenis wrote:
> > Date: Wed, 23 Nov 2022 17:33:26 +0100
> > From: Martin Pieuchot <m...@openbsd.org>
> > 
> > On 23/11/22(Wed) 16:34, Mark Kettenis wrote:
> > > > Date: Wed, 23 Nov 2022 10:52:32 +0100
> > > > From: Martin Pieuchot <m...@openbsd.org>
> > > > 
> > > > On 22/11/22(Tue) 23:40, Mark Kettenis wrote:
> > > > > > Date: Tue, 22 Nov 2022 17:47:44 +0000
> > > > > > From: Miod Vallat <m...@online.fr>
> > > > > > 
> > > > > > > Here is a diff.  Maybe bluhm@ can try this on the macppc machine 
> > > > > > > that
> > > > > > > triggered the original "vref used where vget required" problem?
> > > > > > 
> > > > > > On a similar machine it panics after a few hours with:
> > > > > > 
> > > > > > panic: uvn_flush: PGO_SYNCIO return 'try again' error (impossible)
> > > > > > 
> > > > > > The trace (transcribed by hand) is
> > > > > > uvn_flush+0x820
> > > > > > uvm_vnp_terminate+0x79
> > > > > > vclean+0xdc
> > > > > > vgonel+0x70
> > > > > > getnewvnode+0x240
> > > > > > ffs_vget+0xcc
> > > > > > ffs_inode_alloc+0x13c
> > > > > > ufs_makeinode+0x94
> > > > > > ufs_create+0x58
> > > > > > VOP_CREATE+0x48
> > > > > > vn_open+0x188
> > > > > > doopenat+0x1b4
> > > > > 
> > > > > Ah right, there is another path where we end up with a refcount of
> > > > > zero.  Should be fixable, but I need to think about this for a bit.
> > > > 
> > > > Not sure to understand what you mean with refcount of 0.  Could you
> > > > elaborate?
> > > 
> > > Sorry, I was thinking ahead a bit.  I'm pretty much convinced that the
> > > issue we're dealing with is a race between a vnode being
> > > recycled/cleaned and the pagedaemon paging out pages associated with
> > > that same vnode.
> > > 
> > > The crashes we've seen before were all in the pagedaemon path where we
> > > end up calling into the VFS layer with a vnode that has v_usecount ==
> > > 0.  My "fix" avoids that, but hits the issue that when we are in the
> > > codepath that is recycling/cleaning the vnode, we can't use vget() to
> > > get a reference to the vnode since it checks that the vnode isn't in
> > > the process of being cleaned.
> > > 
> > > But if we avoid that issue (by for example) skipping the vget() call
> > > if the UVM_VNODE_DYING flag is set, we run into the same scenario
> > > where we call into the VFS layer with v_usecount == 0.  Now that may
> > > not actually be a problem, but I need to investigate this a bit more.
> > 
> > When the UVM_VNODE_DYING flag is set the caller always own a valid
> > reference to the vnode.  Either because it is in the process of cleaning
> > it via  uvm_vnp_terminate() or because it uvn_detach() has been called
> > which means the reference to the vnode hasn't been dropped yet.  So I
> > believe `v_usecount' for such vnode is positive.
> 
> I don't think so.  The vnode that can be recycled is sitting on the
> freelist with v_usecount == 0.  When getnewvnode() decides to recycle
> a vnode it takes it off the freelist and calls vgonel(), which i turn
> calls vclean(), which only increases v_usecount if it is non-zero.  So
> at the point where uvn_vnp_terminate() gets called v_usecount
> definitely is 0.
> 
> That said, the vnode is no longer on the freelist at that point and
> since UVM_VNODE_DYING is set, uvn_vnp_uncache() will return
> immediately without calling vref() to get another reference.  So that
> is fine.
> 
> > > Or maybe calling into the VFS layer with a vnode that has v_usecount
> > > == 0 is perfectly fine and we should do the vget() dance I propose in
> > > uvm_vnp_unache() instead of in uvn_put().
> > 
> > I'm not following.  uvm_vnp_uncache() is always called with a valid
> > vnode, no?
> 
> Not sure what you mean with a "valid vnode"; uvm_vnp_uncache() checks
> the UVM_VNODE_VALID flag at the start, which suggests that it can be
> called in cases where that flag is not set.  But it will unlock and
> return immediately in that case, so it should be safe.
> 
> Anyway, I think I have convinced myself that in the case where the
> pagedaemon ends up calling uvn_put() for a persisting vnode vm object,
> we do need to get a refence before calling uvn_io().  Otherwise we run
> the risk of the vnode being recycled under our feet while we're doing
> I/O.
> 
> So here is an updated diff that checks the UVM_VNODE_DYING flag and
> skips the refcount manipulation if it is set.
> 
> What do you think?

If this works, I'm more than happy and definitively ok with it.

> Index: uvm/uvm_vnode.c
> ===================================================================
> RCS file: /cvs/src/sys/uvm/uvm_vnode.c,v
> retrieving revision 1.130
> diff -u -p -r1.130 uvm_vnode.c
> --- uvm/uvm_vnode.c   20 Oct 2022 13:31:52 -0000      1.130
> +++ uvm/uvm_vnode.c   28 Nov 2022 14:01:20 -0000
> @@ -899,11 +899,21 @@ uvn_cluster(struct uvm_object *uobj, vof
>  int
>  uvn_put(struct uvm_object *uobj, struct vm_page **pps, int npages, int flags)
>  {
> -     int retval;
> +     struct uvm_vnode *uvn = (struct uvm_vnode *)uobj;
> +     int dying, retval;
>  
>       KASSERT(rw_write_held(uobj->vmobjlock));
>  
> +     dying = (uvn->u_flags & UVM_VNODE_DYING);
> +     if (!dying) {
> +             if (vget(uvn->u_vnode, LK_NOWAIT))
> +                     return VM_PAGER_AGAIN;
> +     }
> +
>       retval = uvn_io((struct uvm_vnode*)uobj, pps, npages, flags, UIO_WRITE);
> +
> +     if (!dying)
> +             vrele(uvn->u_vnode);
>  
>       return retval;
>  }
> 
> 
> 

Reply via email to