Index: sys/vm/vm_fault.c =================================================================== --- sys/vm/vm_fault.c +++ sys/vm/vm_fault.c @@ -81,6 +81,7 @@ #include #include +#include #include #include #include @@ -162,12 +163,30 @@ "Number of page allocation attempts in page fault handler before it " "triggers OOM handling"); +static SYSCTL_NODE(_vm_stats, OID_AUTO, fault, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, + "VM fault statistics"); + static int vm_pfault_oom_wait = 10; SYSCTL_INT(_vm, OID_AUTO, pfault_oom_wait, CTLFLAG_RWTUN, &vm_pfault_oom_wait, 0, "Number of seconds to wait for free pages before retrying " "the page fault handler"); +static COUNTER_U64_DEFINE_EARLY(vmpfw_shared_read); +SYSCTL_COUNTER_U64(_vm_stats_fault, OID_AUTO, vmpfw_shared_read, + CTLFLAG_RD, &vmpfw_shared_read, + "Number of waits on valid+sbusy pages caused by read faults"); + +static COUNTER_U64_DEFINE_EARLY(vmpfw_shared_cow); +SYSCTL_COUNTER_U64(_vm_stats_fault, OID_AUTO, vmpfw_shared_cow, + CTLFLAG_RD, &vmpfw_shared_cow, + "Number of waits on valid+sbusy pages caused by CoW faults"); + +static COUNTER_U64_DEFINE_EARLY(vmpfw_exclusive); +SYSCTL_COUNTER_U64(_vm_stats_fault, OID_AUTO, vmpfw_exclusive, + CTLFLAG_RD, &vmpfw_exclusive, + "Number of waits on invalid/xbusy pages"); + static inline void fault_page_release(vm_page_t *mp) { @@ -1223,11 +1242,11 @@ * and we could end up trying to pagein and pageout the same page * simultaneously. * - * We can theoretically allow the busy case on a read fault if the page - * is marked valid, but since such pages are typically already pmap'd, - * putting that special case in might be more effort then it is worth. - * We cannot under any circumstances mess around with a shared busied - * page except, perhaps, to pmap it. + * We can theoretically allow the busy case on a read fault or a CoW + * fault if the page is marked valid, but since such pages are typically + * already pmap'd, putting that special case in might be more effort than + * it is worth. We cannot under any circumstances mess around with a + * shared busied page except, perhaps, to pmap it. */ static void vm_fault_busy_sleep(struct faultstate *fs) @@ -1244,9 +1263,25 @@ } vm_object_pip_wakeup(fs->object); unlock_map(fs); - if (fs->m == vm_page_lookup(fs->object, fs->pindex)) + if (fs->m == vm_page_lookup(fs->object, fs->pindex)) { + /* + * Provide statistics on waits that we issue for shared + * and valid pages that could potentially be avoided, + * in comparison to the expected common case of waits + * that cannot be avoided. + */ + if (vm_page_all_valid(fs->m) && vm_page_sbusied(fs->m)) { + if ((fs->fault_type & + (VM_PROT_COPY | VM_PROT_WRITE)) == 0) + counter_u64_add(vmpfw_shared_read, 1); + else if (fs->object != fs->first_object) + counter_u64_add(vmpfw_shared_cow, 1); + else + counter_u64_add(vmpfw_exclusive, 1); + } else + counter_u64_add(vmpfw_exclusive, 1); vm_page_busy_sleep(fs->m, "vmpfw", false); - else + } else VM_OBJECT_WUNLOCK(fs->object); VM_CNT_INC(v_intrans); vm_object_deallocate(fs->first_object);