Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 04 Oct 2001 02:31:06 -0700 (PDT)
From:      John Baldwin <jhb@FreeBSD.org>
To:        Brian Fundakowski Feldman <green@FreeBSD.org>
Cc:        arch@FreeBSD.org
Subject:   RE: sx-ifying src/sys/vm
Message-ID:  <XFMail.011004023106.jhb@FreeBSD.org>
In-Reply-To: <200110040105.f9415av15297@green.bikeshed.org>

next in thread | previous in thread | raw e-mail | index | archive | help

On 04-Oct-01 Brian Fundakowski Feldman wrote:
> I've taken a shot at changing the vm_map locking from using lockmgr() locks 
> (EAAARGHH...) to sx locks.  Already I can tell that it 1. seems to work and 
> 2. seems to not give me bogus lock ordering messages like lockmgr() used to.
> I'd appreciate if people took a look at it and told me of any concerns, 
> whether it also looks fine to more than just myself, etc.
> 
> I added a few more vm_map locking operations than were there already for the 
> purpose of removing the hard-coded lockmgr() ops that were in a few places 
> previously.  Instead of having a sx_locked() operation I used 
> sx_try_xlock(), and instead of recursion I saved the state of the one case 
> where a lock was attempted to be "recursed" upon, and removed the actual 
> recursion on the lock.

Note that recursion of shared locks is allowed.  Also, the name
vm_map_lookup2() is really gross.  However, if you only recurse on a read lock,
then you can get rid of lockheld it seems and remove the vm_map_lookup()
ugliness.

> Index: vm_fault.c
> ===================================================================
> RCS file: /home/freebsd/ncvs/src/sys/vm/vm_fault.c,v
> retrieving revision 1.125
> diff -u -r1.125 vm_fault.c
> --- vm_fault.c        2001/09/12 08:38:11     1.125
> +++ vm_fault.c        2001/10/04 00:41:36
> @@ -213,12 +214,16 @@
>       int hardfault;
>       int faultcount;
>       struct faultstate fs;
> +     boolean_t user_wire;
> 
>       GIANT_REQUIRED;
> 
>       cnt.v_vm_faults++;
>       hardfault = 0;
> +     user_wire = (fault_flags & VM_FAULT_WIRE_MASK) == VM_FAULT_USER_WIRE;
> +     fs.lockheld = user_wire;
>  
> +
>  RetryFault:;
>  
>       /*

Extra blank line?

> Index: vm_glue.c
> ===================================================================
> RCS file: /home/freebsd/ncvs/src/sys/vm/vm_glue.c,v
> retrieving revision 1.119
> diff -u -r1.119 vm_glue.c
> --- vm_glue.c 2001/09/12 08:38:11     1.119
> +++ vm_glue.c 2001/10/04 00:41:37
> @@ -577,9 +577,7 @@
>                        * data structures there is a
>                        * possible deadlock.
>                        */
> -                     if (lockmgr(&vm->vm_map.lock,
> -                                     LK_EXCLUSIVE | LK_NOWAIT,
> -                                     NULL, curthread)) {
> +                     if (vm_map_trylock(&vm->vm_map)) {
>                               vmspace_free(vm);
>                               PROC_UNLOCK(p);
>                               goto nextproc;

Hrm, this reads weird.  It should be 'if (!vm_map_trylock()) {'.  I would make
vm_map_trylock() return true on success and false on failure.  Same for
vm_map_lock_upgrade().

> Index: vm_map.c
> ===================================================================
> RCS file: /home/freebsd/ncvs/src/sys/vm/vm_map.c,v
> retrieving revision 1.207
> diff -u -r1.207 vm_map.c
> --- vm_map.c  2001/09/12 08:38:11     1.207
> +++ vm_map.c  2001/10/04 
> @@ -403,7 +392,7 @@
>       map->first_free = &map->header;
>       map->hint = &map->header;
>       map->timestamp = 0;
> -     lockinit(&map->lock, PVM, "thrd_sleep", 0, LK_NOPAUSE);
> +     sx_init(&map->lock, "thrd_sleep");
>  }
>  
>  void

Why not a more recognizable name such as "vm_map"?

> @@ -1490,7 +1479,6 @@
>                       estart = entry->start;
>  
>                       /* First we need to allow map modifications */
> -                     vm_map_set_recursive(map);
>                       vm_map_lock_downgrade(map);
>                       map->timestamp++;
>  

Old bug: why is the timestamp updated while not holding an exclusive lock?  If
anything, it should be bumped before the downgrade.  Otherwise there isn't a
memory barrier there to ensure other slock holders will see the right timestamp
on other CPU's.

> @@ -1500,14 +1488,12 @@
>                               entry->wired_count--;
>                               entry->eflags &= ~MAP_ENTRY_USER_WIRED;
>  
> -                             vm_map_clear_recursive(map);
> -                             vm_map_unlock(map);
> +                             vm_map_unlock_read(map);
>                               
>                               (void) vm_map_user_pageable(map, start,
entry->start, TRUE);
>                               return rv;
>                       }
>  
> -                     vm_map_clear_recursive(map);
>                       if (vm_map_lock_upgrade(map)) {
>                               vm_map_lock(map);
>                               if (vm_map_lookup_entry(map, estart, &entry) 

Another old bug: why is the lock being modified w/o holding an exclusive lock? 
Looks like this one is now moot, however.

> @@ -2678,6 +2670,22 @@
>             vm_prot_t *out_prot,              /* OUT */
>             boolean_t *wired)                 /* OUT */
>  {
> +     
> +     return (vm_map_lookup2(var_map, vaddr, fault_typea, out_entry,
> +         object, pindex, out_prot, wired, 0));
> +}
> +
> +int
> +vm_map_lookup2(vm_map_t *var_map,            /* IN/OUT */
> +           vm_offset_t vaddr,
> +           vm_prot_t fault_typea,
> +           vm_map_entry_t *out_entry,        /* OUT */
> +           vm_object_t *object,              /* OUT */
> +           vm_pindex_t *pindex,              /* OUT */
> +           vm_prot_t *out_prot,              /* OUT */
> +           boolean_t *wired,                 /* OUT */
> +           boolean_t lockheld)
> +{
>       vm_map_entry_t entry;
>       vm_map_t map = *var_map;
>       vm_prot_t prot;
> @@ -2690,11 +2698,13 @@
>        * Lookup the faulting address.
>        */
>  
> -     vm_map_lock_read(map);
> +     if (!lockheld)
> +             vm_map_lock_read(map);
>  
>  #define      RETURN(why) \
>               { \
> -             vm_map_unlock_read(map); \
> +             if (!lockheld) \
> +                     vm_map_unlock_read(map); \
>               return(why); \
>               }
>  
> @@ -2730,7 +2740,8 @@
>               vm_map_t old_map = map;
>  
>               *var_map = map = entry->object.sub_map;
> -             vm_map_unlock_read(old_map);
> +             if (!lockheld)
> +                     vm_map_unlock_read(old_map);
>               goto RetryLookup;
>       }
>  

Ok, I don't like the lockheld parameter here. :)  It looks like the recursive
locks are read locks.  Is the problem that you can call this function with an
exclusive lock held?  If so, I would rather that you add an assertion
SX_ASSERT_LOCKED(&map->lock) that the map is locked (either read or write) by
the caller.  Then vm_map_lookup() can simply assume the map is already locked.

> Index: vm_zone.c
> ===================================================================
> RCS file: /home/freebsd/ncvs/src/sys/vm/vm_zone.c,v
> retrieving revision 1.48
> diff -u -r1.48 vm_zone.c
> --- vm_zone.c 2001/08/05 03:55:02     1.48
> +++ vm_zone.c 2001/10/04 00:41:48
> @@ -409,11 +409,12 @@
>                * map.
>                */
>               mtx_unlock(&z->zmtx);
> -             if (lockstatus(&kernel_map->lock, NULL)) {
> +             if (vm_map_trylock(kernel_map)) {
>                       item = (void *) kmem_malloc(kmem_map, nbytes, M_WAITOK);
>                       if (item != NULL)
>                               atomic_add_int(&zone_kmem_pages, z->zalloc);
>               } else {
> +                     vm_map_unlock(kernel_map);
>                       item = (void *) kmem_alloc(kernel_map, nbytes);
>                       if (item != NULL)
>                               atomic_add_int(&zone_kern_pages, z->zalloc);

Question: why does the zone allocator care if the map is locked or not?  Also,
this has a race: some other thread could easily come in and lock the map in
between the vm_map_unlock() and the kmem_alloc(), thus rendering this test
useless.  This entire test here is not thread safe and needs to be rethought.

-- 

John Baldwin <jhb@FreeBSD.org> -- http://www.FreeBSD.org/~jhb/
PGP Key: http://www.baldwin.cx/~john/pgpkey.asc
"Power Users Use the Power to Serve!"  -  http://www.FreeBSD.org/

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?XFMail.011004023106.jhb>