Date: Tue, 29 Oct 2019 20:58:46 +0000 (UTC) From: Jeff Roberson <jeff@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r354157 - in head/sys: sys vm Message-ID: <201910292058.x9TKwkvE036657@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jeff Date: Tue Oct 29 20:58:46 2019 New Revision: 354157 URL: https://svnweb.freebsd.org/changeset/base/354157 Log: Use atomics and a shared object lock to protect the object reference count. Certain consumers still need to guarantee a stable reference so we can not switch entirely to atomics yet. Exclusive lock holders can still modify and examine the refcount without using the ref api. Reviewed by: kib Tested by: pho Sponsored by: Netflix, Intel Differential Revision: https://reviews.freebsd.org/D21598 Modified: head/sys/sys/refcount.h head/sys/vm/vm_object.c head/sys/vm/vm_object.h head/sys/vm/vnode_pager.c Modified: head/sys/sys/refcount.h ============================================================================== --- head/sys/sys/refcount.h Tue Oct 29 20:46:25 2019 (r354156) +++ head/sys/sys/refcount.h Tue Oct 29 20:58:46 2019 (r354157) @@ -175,4 +175,22 @@ refcount_release_if_not_last(volatile u_int *count) } } +static __inline __result_use_check bool +refcount_release_if_gt(volatile u_int *count, u_int n) +{ + u_int old; + + KASSERT(n > 0, + ("refcount_release_if_gt: Use refcount_release for final ref")); + old = *count; + for (;;) { + if (REFCOUNT_COUNT(old) <= n) + return (false); + if (__predict_false(REFCOUNT_SATURATED(old))) + return (true); + if (atomic_fcmpset_int(count, &old, old - 1)) + return (true); + } +} + #endif /* ! __SYS_REFCOUNT_H__ */ Modified: head/sys/vm/vm_object.c ============================================================================== --- head/sys/vm/vm_object.c Tue Oct 29 20:46:25 2019 (r354156) +++ head/sys/vm/vm_object.c Tue Oct 29 20:58:46 2019 (r354157) @@ -224,8 +224,8 @@ vm_object_zinit(void *mem, int size, int flags) /* These are true for any object that has been freed */ object->type = OBJT_DEAD; - object->ref_count = 0; vm_radix_init(&object->rtree); + refcount_init(&object->ref_count, 0); refcount_init(&object->paging_in_progress, 0); refcount_init(&object->busy, 0); object->resident_page_count = 0; @@ -282,7 +282,7 @@ _vm_object_allocate(objtype_t type, vm_pindex_t size, object->size = size; object->domain.dr_policy = NULL; object->generation = 1; - object->ref_count = 1; + refcount_init(&object->ref_count, 1); object->memattr = VM_MEMATTR_DEFAULT; object->cred = NULL; object->charge = 0; @@ -444,9 +444,9 @@ vm_object_reference(vm_object_t object) { if (object == NULL) return; - VM_OBJECT_WLOCK(object); + VM_OBJECT_RLOCK(object); vm_object_reference_locked(object); - VM_OBJECT_WUNLOCK(object); + VM_OBJECT_RUNLOCK(object); } /* @@ -461,8 +461,8 @@ vm_object_reference_locked(vm_object_t object) { struct vnode *vp; - VM_OBJECT_ASSERT_WLOCKED(object); - object->ref_count++; + VM_OBJECT_ASSERT_LOCKED(object); + refcount_acquire(&object->ref_count); if (object->type == OBJT_VNODE) { vp = object->handle; vref(vp); @@ -477,24 +477,16 @@ vm_object_vndeallocate(vm_object_t object) { struct vnode *vp = (struct vnode *) object->handle; - VM_OBJECT_ASSERT_WLOCKED(object); KASSERT(object->type == OBJT_VNODE, ("vm_object_vndeallocate: not a vnode object")); KASSERT(vp != NULL, ("vm_object_vndeallocate: missing vp")); -#ifdef INVARIANTS - if (object->ref_count == 0) { - vn_printf(vp, "vm_object_vndeallocate "); - panic("vm_object_vndeallocate: bad object reference count"); - } -#endif - if (!umtx_shm_vnobj_persistent && object->ref_count == 1) + if (refcount_release(&object->ref_count) && + !umtx_shm_vnobj_persistent) umtx_shm_object_terminated(object); - object->ref_count--; - + VM_OBJECT_RUNLOCK(object); /* vrele may need the vnode lock. */ - VM_OBJECT_WUNLOCK(object); vrele(vp); } @@ -513,24 +505,32 @@ void vm_object_deallocate(vm_object_t object) { vm_object_t temp; + bool released; while (object != NULL) { - VM_OBJECT_WLOCK(object); + VM_OBJECT_RLOCK(object); if (object->type == OBJT_VNODE) { vm_object_vndeallocate(object); return; } - KASSERT(object->ref_count != 0, - ("vm_object_deallocate: object deallocated too many times: %d", object->type)); - /* * If the reference count goes to 0 we start calling - * vm_object_terminate() on the object chain. - * A ref count of 1 may be a special case depending on the - * shadow count being 0 or 1. + * vm_object_terminate() on the object chain. A ref count + * of 1 may be a special case depending on the shadow count + * being 0 or 1. These cases require a write lock on the + * object. */ - object->ref_count--; + released = refcount_release_if_gt(&object->ref_count, 2); + VM_OBJECT_RUNLOCK(object); + if (released) + return; + + VM_OBJECT_WLOCK(object); + KASSERT(object->ref_count != 0, + ("vm_object_deallocate: object deallocated too many times: %d", object->type)); + + refcount_release(&object->ref_count); if (object->ref_count > 1) { VM_OBJECT_WUNLOCK(object); return; @@ -558,7 +558,7 @@ vm_object_deallocate(vm_object_t object) /* * Avoid a potential deadlock. */ - object->ref_count++; + refcount_acquire(&object->ref_count); VM_OBJECT_WUNLOCK(object); /* * More likely than not the thread @@ -580,7 +580,7 @@ vm_object_deallocate(vm_object_t object) (robject->type == OBJT_DEFAULT || robject->type == OBJT_SWAP)) { - robject->ref_count++; + refcount_acquire(&robject->ref_count); retry: if (REFCOUNT_COUNT(robject->paging_in_progress) > 0) { VM_OBJECT_WUNLOCK(object); @@ -1223,15 +1223,15 @@ vm_object_shadow( * Don't create the new object if the old object isn't shared. */ if (source != NULL) { - VM_OBJECT_WLOCK(source); + VM_OBJECT_RLOCK(source); if (source->ref_count == 1 && source->handle == NULL && (source->type == OBJT_DEFAULT || source->type == OBJT_SWAP)) { - VM_OBJECT_WUNLOCK(source); + VM_OBJECT_RUNLOCK(source); return; } - VM_OBJECT_WUNLOCK(source); + VM_OBJECT_RUNLOCK(source); } /* @@ -1822,7 +1822,7 @@ vm_object_collapse(vm_object_t object) * Drop the reference count on backing_object. Since * its ref_count was at least 2, it will not vanish. */ - backing_object->ref_count--; + refcount_release(&backing_object->ref_count); VM_OBJECT_WUNLOCK(backing_object); counter_u64_add(object_bypasses, 1); } Modified: head/sys/vm/vm_object.h ============================================================================== --- head/sys/vm/vm_object.h Tue Oct 29 20:46:25 2019 (r354156) +++ head/sys/vm/vm_object.h Tue Oct 29 20:58:46 2019 (r354157) @@ -106,7 +106,7 @@ struct vm_object { vm_pindex_t size; /* Object size */ struct domainset_ref domain; /* NUMA policy. */ int generation; /* generation ID */ - int ref_count; /* How many refs?? */ + volatile u_int ref_count; /* How many refs?? */ int shadow_count; /* how many objects that this is a shadow for */ vm_memattr_t memattr; /* default memory attribute for pages */ objtype_t type; /* type of pager */ Modified: head/sys/vm/vnode_pager.c ============================================================================== --- head/sys/vm/vnode_pager.c Tue Oct 29 20:46:25 2019 (r354156) +++ head/sys/vm/vnode_pager.c Tue Oct 29 20:58:46 2019 (r354157) @@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$"); #include <sys/ktr.h> #include <sys/limits.h> #include <sys/conf.h> +#include <sys/refcount.h> #include <sys/rwlock.h> #include <sys/sf_buf.h> #include <sys/domainset.h> @@ -172,9 +173,9 @@ vnode_create_vobject(struct vnode *vp, off_t isize, st * Dereference the reference we just created. This assumes * that the object is associated with the vp. */ - VM_OBJECT_WLOCK(object); - object->ref_count--; - VM_OBJECT_WUNLOCK(object); + VM_OBJECT_RLOCK(object); + refcount_release(&object->ref_count); + VM_OBJECT_RUNLOCK(object); vrele(vp); KASSERT(vp->v_object != NULL, ("vnode_create_vobject: NULL object")); @@ -285,7 +286,7 @@ retry: KASSERT(object->ref_count == 1, ("leaked ref %p %d", object, object->ref_count)); object->type = OBJT_DEAD; - object->ref_count = 0; + refcount_init(&object->ref_count, 0); VM_OBJECT_WUNLOCK(object); vm_object_destroy(object); goto retry; @@ -294,7 +295,7 @@ retry: VI_UNLOCK(vp); } else { VM_OBJECT_WLOCK(object); - object->ref_count++; + refcount_acquire(&object->ref_count); #if VM_NRESERVLEVEL > 0 vm_object_color(object, 0); #endif
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201910292058.x9TKwkvE036657>