The vmtotal sysctl handler marks active vm objects to calculate
statistics. Marking is done by setting the OBJ_ACTIVE flag. The flags change is locked, but the problem is that many parts of system assume that vm object initialization ensures that no other code could change the object, and thus performed lockless. The end result is corrupted flags in vm objects, most visible is spurious OBJ_DEAD flag, causing random hangs. Avoid the active object marking, instead provide equally inexact but immutable is_object_alive() definition for the object mapped state. Avoid iterating over the processes mappings altogether by using arguably improved definition of the paging thread as one which sleeps on the v_free_count. PR: 204764 Diagnosed by: pho Tested by: pho (previous version) Reviewed by: alc Sponsored by: The FreeBSD Foundation MFC after: 1 week Approved by: re (gjb)
This commit is contained in:
parent
1f3d721c13
commit
907f8ff806
@ -89,29 +89,31 @@ SYSCTL_PROC(_vm, VM_LOADAVG, loadavg, CTLTYPE_STRUCT | CTLFLAG_RD |
|
||||
CTLFLAG_MPSAFE, NULL, 0, sysctl_vm_loadavg, "S,loadavg",
|
||||
"Machine loadaverage history");
|
||||
|
||||
/*
|
||||
* This function aims to determine if the object is mapped,
|
||||
* specifically, if it is referenced by a vm_map_entry. Because
|
||||
* objects occasionally acquire transient references that do not
|
||||
* represent a mapping, the method used here is inexact. However, it
|
||||
* has very low overhead and is good enough for the advisory
|
||||
* vm.vmtotal sysctl.
|
||||
*/
|
||||
static bool
|
||||
is_object_active(vm_object_t obj)
|
||||
{
|
||||
|
||||
return (obj->ref_count > obj->shadow_count);
|
||||
}
|
||||
|
||||
static int
|
||||
vmtotal(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct proc *p;
|
||||
struct vmtotal total;
|
||||
vm_map_entry_t entry;
|
||||
vm_object_t object;
|
||||
vm_map_t map;
|
||||
int paging;
|
||||
struct proc *p;
|
||||
struct thread *td;
|
||||
struct vmspace *vm;
|
||||
|
||||
bzero(&total, sizeof(total));
|
||||
/*
|
||||
* Mark all objects as inactive.
|
||||
*/
|
||||
mtx_lock(&vm_object_list_mtx);
|
||||
TAILQ_FOREACH(object, &vm_object_list, object_list) {
|
||||
VM_OBJECT_WLOCK(object);
|
||||
vm_object_clear_flag(object, OBJ_ACTIVE);
|
||||
VM_OBJECT_WUNLOCK(object);
|
||||
}
|
||||
mtx_unlock(&vm_object_list_mtx);
|
||||
|
||||
/*
|
||||
* Calculate process statistics.
|
||||
*/
|
||||
@ -132,11 +134,15 @@ vmtotal(SYSCTL_HANDLER_ARGS)
|
||||
case TDS_INHIBITED:
|
||||
if (TD_IS_SWAPPED(td))
|
||||
total.t_sw++;
|
||||
else if (TD_IS_SLEEPING(td) &&
|
||||
td->td_priority <= PZERO)
|
||||
else if (TD_IS_SLEEPING(td)) {
|
||||
if (td->td_priority <= PZERO)
|
||||
total.t_dw++;
|
||||
else
|
||||
total.t_sl++;
|
||||
if (td->td_wchan ==
|
||||
&vm_cnt.v_free_count)
|
||||
total.t_pw++;
|
||||
}
|
||||
break;
|
||||
|
||||
case TDS_CAN_RUN:
|
||||
@ -154,29 +160,6 @@ vmtotal(SYSCTL_HANDLER_ARGS)
|
||||
}
|
||||
}
|
||||
PROC_UNLOCK(p);
|
||||
/*
|
||||
* Note active objects.
|
||||
*/
|
||||
paging = 0;
|
||||
vm = vmspace_acquire_ref(p);
|
||||
if (vm == NULL)
|
||||
continue;
|
||||
map = &vm->vm_map;
|
||||
vm_map_lock_read(map);
|
||||
for (entry = map->header.next;
|
||||
entry != &map->header; entry = entry->next) {
|
||||
if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) ||
|
||||
(object = entry->object.vm_object) == NULL)
|
||||
continue;
|
||||
VM_OBJECT_WLOCK(object);
|
||||
vm_object_set_flag(object, OBJ_ACTIVE);
|
||||
paging |= object->paging_in_progress;
|
||||
VM_OBJECT_WUNLOCK(object);
|
||||
}
|
||||
vm_map_unlock_read(map);
|
||||
vmspace_free(vm);
|
||||
if (paging)
|
||||
total.t_pw++;
|
||||
}
|
||||
sx_sunlock(&allproc_lock);
|
||||
/*
|
||||
@ -202,9 +185,18 @@ vmtotal(SYSCTL_HANDLER_ARGS)
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
if (object->ref_count == 1 &&
|
||||
(object->flags & OBJ_NOSPLIT) != 0) {
|
||||
/*
|
||||
* Also skip otherwise unreferenced swap
|
||||
* objects backing tmpfs vnodes, and POSIX or
|
||||
* SysV shared memory.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
total.t_vm += object->size;
|
||||
total.t_rm += object->resident_page_count;
|
||||
if (object->flags & OBJ_ACTIVE) {
|
||||
if (is_object_active(object)) {
|
||||
total.t_avm += object->size;
|
||||
total.t_arm += object->resident_page_count;
|
||||
}
|
||||
@ -212,7 +204,7 @@ vmtotal(SYSCTL_HANDLER_ARGS)
|
||||
/* shared object */
|
||||
total.t_vmshr += object->size;
|
||||
total.t_rmshr += object->resident_page_count;
|
||||
if (object->flags & OBJ_ACTIVE) {
|
||||
if (is_object_active(object)) {
|
||||
total.t_avmshr += object->size;
|
||||
total.t_armshr += object->resident_page_count;
|
||||
}
|
||||
|
@ -182,7 +182,6 @@ struct vm_object {
|
||||
*/
|
||||
#define OBJ_FICTITIOUS 0x0001 /* (c) contains fictitious pages */
|
||||
#define OBJ_UNMANAGED 0x0002 /* (c) contains unmanaged pages */
|
||||
#define OBJ_ACTIVE 0x0004 /* active objects */
|
||||
#define OBJ_DEAD 0x0008 /* dead objects (during rundown) */
|
||||
#define OBJ_NOSPLIT 0x0010 /* dont split this object */
|
||||
#define OBJ_UMTXDEAD 0x0020 /* umtx pshared was terminated */
|
||||
|
Loading…
Reference in New Issue
Block a user