Re: git: c35ec1efdcb2 - main - vfs: [1/2] fix stalls in vnode reclaim by not requeieing from vnlru

From: Mateusz Guzik <mjguzik_at_gmail.com>
Date: Sun, 27 Mar 2022 14:40:52 UTC
Oops. I did make sure the comment is there. :) Interestingly this
still would have worked fine enough as most of the problem is worked
around by 2/2. Anyhow, fixed.

thanks

On 3/26/22, Mitchell Horne <mhorne@freebsd.org> wrote:
>
>
> On 3/10/22 05:42, Mateusz Guzik wrote:
>> The branch main has been updated by mjg:
>>
>> URL:
>> https://cgit.FreeBSD.org/src/commit/?id=c35ec1efdcb2978bc3b6a0098c2b412be8d33e39
>>
>> commit c35ec1efdcb2978bc3b6a0098c2b412be8d33e39
>> Author:     Mateusz Guzik <mjg@FreeBSD.org>
>> AuthorDate: 2022-03-07 10:33:59 +0000
>> Commit:     Mateusz Guzik <mjg@FreeBSD.org>
>> CommitDate: 2022-03-10 09:41:50 +0000
>>
>>      vfs: [1/2] fix stalls in vnode reclaim by not requeieing from vnlru
>>
>>      Reported by:    pho
>> ---
>>   sys/kern/vfs_subr.c | 55
>> +++++++++++++++++++++++++++++++++++++++++++----------
>>   1 file changed, 45 insertions(+), 10 deletions(-)
>>
>> diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
>> index afafd02d92b9..436323873f7f 100644
>> --- a/sys/kern/vfs_subr.c
>> +++ b/sys/kern/vfs_subr.c
>> @@ -113,6 +113,8 @@ static void	vn_seqc_init(struct vnode *);
>>   static void	vn_seqc_write_end_free(struct vnode *vp);
>>   static void	vgonel(struct vnode *);
>>   static bool	vhold_recycle_free(struct vnode *);
>> +static void	vdropl_recycle(struct vnode *vp);
>> +static void	vdrop_recycle(struct vnode *vp);
>>   static void	vfs_knllock(void *arg);
>>   static void	vfs_knlunlock(void *arg);
>>   static void	vfs_knl_assert_lock(void *arg, int what);
>> @@ -1207,11 +1209,11 @@ restart:
>>   		mtx_unlock(&vnode_list_mtx);
>>
>>   		if (vn_start_write(vp, &mp, V_NOWAIT) != 0) {
>> -			vdrop(vp);
>> +			vdrop_recycle(vp);
>>   			goto next_iter_unlocked;
>>   		}
>>   		if (VOP_LOCK(vp, LK_EXCLUSIVE|LK_NOWAIT) != 0) {
>> -			vdrop(vp);
>> +			vdrop_recycle(vp);
>>   			vn_finished_write(mp);
>>   			goto next_iter_unlocked;
>>   		}
>> @@ -1222,14 +1224,14 @@ restart:
>>   		    (vp->v_object != NULL && vp->v_object->handle == vp &&
>>   		    vp->v_object->resident_page_count > trigger)) {
>>   			VOP_UNLOCK(vp);
>> -			vdropl(vp);
>> +			vdropl_recycle(vp);
>>   			vn_finished_write(mp);
>>   			goto next_iter_unlocked;
>>   		}
>>   		counter_u64_add(recycles_count, 1);
>>   		vgonel(vp);
>>   		VOP_UNLOCK(vp);
>> -		vdropl(vp);
>> +		vdropl_recycle(vp);
>>   		vn_finished_write(mp);
>>   		done++;
>>   next_iter_unlocked:
>> @@ -1613,7 +1615,7 @@ vtryrecycle(struct vnode *vp)
>>   		CTR2(KTR_VFS,
>>   		    "%s: impossible to recycle, vp %p lock is already held",
>>   		    __func__, vp);
>> -		vdrop(vp);
>> +		vdrop_recycle(vp);
>>   		return (EWOULDBLOCK);
>>   	}
>>   	/*
>> @@ -1624,7 +1626,7 @@ vtryrecycle(struct vnode *vp)
>>   		CTR2(KTR_VFS,
>>   		    "%s: impossible to recycle, cannot start the write for %p",
>>   		    __func__, vp);
>> -		vdrop(vp);
>> +		vdrop_recycle(vp);
>>   		return (EBUSY);
>>   	}
>>   	/*
>> @@ -1636,7 +1638,7 @@ vtryrecycle(struct vnode *vp)
>>   	VI_LOCK(vp);
>>   	if (vp->v_usecount) {
>>   		VOP_UNLOCK(vp);
>> -		vdropl(vp);
>> +		vdropl_recycle(vp);
>>   		vn_finished_write(vnmp);
>>   		CTR2(KTR_VFS,
>>   		    "%s: impossible to recycle, %p is already referenced",
>> @@ -1648,7 +1650,7 @@ vtryrecycle(struct vnode *vp)
>>   		vgonel(vp);
>>   	}
>>   	VOP_UNLOCK(vp);
>> -	vdropl(vp);
>> +	vdropl_recycle(vp);
>>   	vn_finished_write(vnmp);
>>   	return (0);
>>   }
>> @@ -3598,8 +3600,8 @@ vdrop(struct vnode *vp)
>>   	vdropl(vp);
>>   }
>>
>> -void
>> -vdropl(struct vnode *vp)
>> +static void __always_inline
>> +vdropl_impl(struct vnode *vp, bool enqueue)
>>   {
>
> Hi,
>
> It seems like enqueue is completely unused within the function. Was
> there a hunk missing from the final commit?
>
> Cheers,
> Mitchell
>
>>
>>   	ASSERT_VI_LOCKED(vp, __func__);
>> @@ -3627,6 +3629,39 @@ vdropl(struct vnode *vp)
>>   	vdbatch_enqueue(vp);
>>   }
>>
>> +void
>> +vdropl(struct vnode *vp)
>> +{
>> +
>> +	vdropl_impl(vp, true);
>> +}
>> +
>> +/*
>> + * vdrop a vnode when recycling
>> + *
>> + * This is a special case routine only to be used when recycling, differs
>> from
>> + * regular vdrop by not requeieing the vnode on LRU.
>> + *
>> + * Consider a case where vtryrecycle continuously fails with all vnodes
>> (due to
>> + * e.g., frozen writes on the filesystem), filling the batch and causing
>> it to
>> + * be requeued. Then vnlru will end up revisiting the same vnodes. This
>> is a
>> + * loop which can last for as long as writes are frozen.
>> + */
>> +static void
>> +vdropl_recycle(struct vnode *vp)
>> +{
>> +
>> +	vdropl_impl(vp, false);
>> +}
>> +
>> +static void
>> +vdrop_recycle(struct vnode *vp)
>> +{
>> +
>> +	VI_LOCK(vp);
>> +	vdropl_recycle(vp);
>> +}
>> +
>>   /*
>>    * Call VOP_INACTIVE on the vnode and manage the DOINGINACT and
>> OWEINACT
>>    * flags.  DOINGINACT prevents us from recursing in calls to vinactive.
>


-- 
Mateusz Guzik <mjguzik gmail.com>