On Wed, Jun 23, 2021 at 09:37:10AM +0200, Martin Pieuchot wrote:
> On 16/06/21(Wed) 11:26, Martin Pieuchot wrote:
> > Diff below does two things:
> >
> > - Use atomic operations for incrementing/decrementing references of
> > anonymous objects. This allows us to manipulate them without holding
> > the KERNEL_LOCK().
> >
> > - Rewrite the loop from uao_swap_off() to only keep a reference to the
> > next item in the list. This is imported from NetBSD and is necessary
> > to introduce locking around uao_pagein().
> >
> > ok?
>
> Anyone?
uao_reference_locked() and uao_detach_locked() are prototyped in
uvm_extern.h, so they should be removed here too.
It doesn't look like uao_detach() is safe to call without the
kernel lock; it calls uao_dropswap() for each page, which calls
uao_set_swslot(), which includes a KERNEL_ASSERT_LOCKED().
Should we keep the KERNEL_ASSERT_LOCKED() in uao_detach()?
ok jmatthew@ otherwise
>
> >
> > Index: uvm/uvm_aobj.c
> > ===================================================================
> > RCS file: /cvs/src/sys/uvm/uvm_aobj.c,v
> > retrieving revision 1.98
> > diff -u -p -r1.98 uvm_aobj.c
> > --- uvm/uvm_aobj.c 16 Jun 2021 09:02:21 -0000 1.98
> > +++ uvm/uvm_aobj.c 16 Jun 2021 09:20:26 -0000
> > @@ -779,19 +779,11 @@ uao_init(void)
> > void
> > uao_reference(struct uvm_object *uobj)
> > {
> > - KERNEL_ASSERT_LOCKED();
> > - uao_reference_locked(uobj);
> > -}
> > -
> > -void
> > -uao_reference_locked(struct uvm_object *uobj)
> > -{
> > -
> > /* Kernel object is persistent. */
> > if (UVM_OBJ_IS_KERN_OBJECT(uobj))
> > return;
> >
> > - uobj->uo_refs++;
> > + atomic_inc_int(&uobj->uo_refs);
> > }
> >
> >
> > @@ -801,34 +793,19 @@ uao_reference_locked(struct uvm_object *
> > void
> > uao_detach(struct uvm_object *uobj)
> > {
> > - KERNEL_ASSERT_LOCKED();
> > - uao_detach_locked(uobj);
> > -}
> > -
> > -
> > -/*
> > - * uao_detach_locked: drop a reference to an aobj
> > - *
> > - * => aobj may freed upon return.
> > - */
> > -void
> > -uao_detach_locked(struct uvm_object *uobj)
> > -{
> > struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
> > struct vm_page *pg;
> >
> > /*
> > * Detaching from kernel_object is a NOP.
> > */
> > - if (UVM_OBJ_IS_KERN_OBJECT(uobj)) {
> > + if (UVM_OBJ_IS_KERN_OBJECT(uobj))
> > return;
> > - }
> >
> > /*
> > * Drop the reference. If it was the last one, destroy the object.
> > */
> > - uobj->uo_refs--;
> > - if (uobj->uo_refs) {
> > + if (atomic_dec_int_nv(&uobj->uo_refs) > 0) {
> > return;
> > }
> >
> > @@ -1265,68 +1242,54 @@ uao_dropswap(struct uvm_object *uobj, in
> > boolean_t
> > uao_swap_off(int startslot, int endslot)
> > {
> > - struct uvm_aobj *aobj, *nextaobj, *prevaobj = NULL;
> > + struct uvm_aobj *aobj;
> >
> > /*
> > - * Walk the list of all anonymous UVM objects.
> > + * Walk the list of all anonymous UVM objects. Grab the first.
> > */
> > mtx_enter(&uao_list_lock);
> > + if ((aobj = LIST_FIRST(&uao_list)) == NULL) {
> > + mtx_leave(&uao_list_lock);
> > + return FALSE;
> > + }
> > + uao_reference(&aobj->u_obj);
> >
> > - for (aobj = LIST_FIRST(&uao_list);
> > - aobj != NULL;
> > - aobj = nextaobj) {
> > + do {
> > + struct uvm_aobj *nextaobj;
> > boolean_t rv;
> >
> > /*
> > - * add a ref to the aobj so it doesn't disappear
> > - * while we're working.
> > - */
> > - uao_reference_locked(&aobj->u_obj);
> > -
> > - /*
> > - * now it's safe to unlock the uao list.
> > - * note that lock interleaving is alright with IPL_NONE mutexes.
> > + * Prefetch the next object and immediately hold a reference
> > + * on it, so neither the current nor the next entry could
> > + * disappear while we are iterating.
> > */
> > - mtx_leave(&uao_list_lock);
> > -
> > - if (prevaobj) {
> > - uao_detach_locked(&prevaobj->u_obj);
> > - prevaobj = NULL;
> > + if ((nextaobj = LIST_NEXT(aobj, u_list)) != NULL) {
> > + uao_reference(&nextaobj->u_obj);
> > }
> > + mtx_leave(&uao_list_lock);
> >
> > /*
> > - * page in any pages in the swslot range.
> > - * if there's an error, abort and return the error.
> > + * Page in all pages in the swap slot range.
> > */
> > rv = uao_pagein(aobj, startslot, endslot);
> > +
> > + /* Drop the reference of the current object. */
> > + uao_detach(&aobj->u_obj);
> > if (rv) {
> > - uao_detach_locked(&aobj->u_obj);
> > + if (nextaobj) {
> > + uao_detach(&nextaobj->u_obj);
> > + }
> > return rv;
> > }
> >
> > - /*
> > - * we're done with this aobj.
> > - * relock the list and drop our ref on the aobj.
> > - */
> > + aobj = nextaobj;
> > mtx_enter(&uao_list_lock);
> > - nextaobj = LIST_NEXT(aobj, u_list);
> > - /*
> > - * prevaobj means that we have an object that we need
> > - * to drop a reference for. We can't just drop it now with
> > - * the list locked since that could cause lock recursion in
> > - * the case where we reduce the refcount to 0. It will be
> > - * released the next time we drop the list lock.
> > - */
> > - prevaobj = aobj;
> > - }
> > + } while (aobj);
> >
> > /*
> > * done with traversal, unlock the list
> > */
> > mtx_leave(&uao_list_lock);
> > - if (prevaobj) {
> > - uao_detach_locked(&prevaobj->u_obj);
> > - }
> > return FALSE;
> > }
> >
> >
>