For vm_zone_stats() sysctl handler, do not drain sbuf calling
copyout(9) while owning zone lock. Despite old value sysctl buffer is wired, spurious faults might still occur. Note that we still own the uma_rwlock there, but this lock does not participate in sensitive lock orders. Reported and tested by: pho (as part of the larger patch) Sponsored by: The FreeBSD Foundation MFC after: 1 week
This commit is contained in:
parent
5ccb8e69d6
commit
a5910f9167
@ -3692,7 +3692,7 @@ sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct uma_stream_header ush;
|
||||
struct uma_type_header uth;
|
||||
struct uma_percpu_stat ups;
|
||||
struct uma_percpu_stat *ups;
|
||||
uma_bucket_t bucket;
|
||||
uma_zone_domain_t zdom;
|
||||
struct sbuf sbuf;
|
||||
@ -3708,6 +3708,7 @@ sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS)
|
||||
return (error);
|
||||
sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
|
||||
sbuf_clear_flags(&sbuf, SBUF_INCLUDENUL);
|
||||
ups = malloc((mp_maxid + 1) * sizeof(*ups), M_TEMP, M_WAITOK);
|
||||
|
||||
count = 0;
|
||||
rw_rlock(&uma_rwlock);
|
||||
@ -3760,7 +3761,6 @@ sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS)
|
||||
uth.uth_frees = z->uz_frees;
|
||||
uth.uth_fails = z->uz_fails;
|
||||
uth.uth_sleeps = z->uz_sleeps;
|
||||
(void)sbuf_bcat(&sbuf, &uth, sizeof(uth));
|
||||
/*
|
||||
* While it is not normally safe to access the cache
|
||||
* bucket pointers while not on the CPU that owns the
|
||||
@ -3769,30 +3769,31 @@ sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS)
|
||||
* accept the possible race associated with bucket
|
||||
* exchange during monitoring.
|
||||
*/
|
||||
for (i = 0; i < (mp_maxid + 1); i++) {
|
||||
bzero(&ups, sizeof(ups));
|
||||
if (kz->uk_flags & UMA_ZFLAG_INTERNAL)
|
||||
goto skip;
|
||||
if (CPU_ABSENT(i))
|
||||
goto skip;
|
||||
for (i = 0; i < mp_maxid + 1; i++) {
|
||||
bzero(&ups[i], sizeof(*ups));
|
||||
if (kz->uk_flags & UMA_ZFLAG_INTERNAL ||
|
||||
CPU_ABSENT(i))
|
||||
continue;
|
||||
cache = &z->uz_cpu[i];
|
||||
if (cache->uc_allocbucket != NULL)
|
||||
ups.ups_cache_free +=
|
||||
ups[i].ups_cache_free +=
|
||||
cache->uc_allocbucket->ub_cnt;
|
||||
if (cache->uc_freebucket != NULL)
|
||||
ups.ups_cache_free +=
|
||||
ups[i].ups_cache_free +=
|
||||
cache->uc_freebucket->ub_cnt;
|
||||
ups.ups_allocs = cache->uc_allocs;
|
||||
ups.ups_frees = cache->uc_frees;
|
||||
skip:
|
||||
(void)sbuf_bcat(&sbuf, &ups, sizeof(ups));
|
||||
ups[i].ups_allocs = cache->uc_allocs;
|
||||
ups[i].ups_frees = cache->uc_frees;
|
||||
}
|
||||
ZONE_UNLOCK(z);
|
||||
(void)sbuf_bcat(&sbuf, &uth, sizeof(uth));
|
||||
for (i = 0; i < mp_maxid + 1; i++)
|
||||
(void)sbuf_bcat(&sbuf, &ups[i], sizeof(ups[i]));
|
||||
}
|
||||
}
|
||||
rw_runlock(&uma_rwlock);
|
||||
error = sbuf_finish(&sbuf);
|
||||
sbuf_delete(&sbuf);
|
||||
free(ups, M_TEMP);
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user