add mismatch and fallback counters
This commit is contained in:
parent
caba92060d
commit
21aa3325fc
@ -89,8 +89,8 @@ __FBSDID("$FreeBSD$");
|
|||||||
static MALLOC_DEFINE(M_KQUEUE, "kqueue", "memory for kqueue system");
|
static MALLOC_DEFINE(M_KQUEUE, "kqueue", "memory for kqueue system");
|
||||||
|
|
||||||
#define KQDOM_FLAGS (KQ_SCHED_CPU | KQ_SCHED_QUEUE)
|
#define KQDOM_FLAGS (KQ_SCHED_CPU | KQ_SCHED_QUEUE)
|
||||||
#define KEVQ_LAT_FLAGS (KQ_SCHED_CPU | KQ_SCHED_QUEUE | KQ_SCHED_BEST)
|
#define KEVQ_LAT_FLAGS ((uint64_t)-1) //(KQ_SCHED_CPU | KQ_SCHED_QUEUE | KQ_SCHED_BEST)
|
||||||
|
#define DUMP_INDENT (4)
|
||||||
/*
|
/*
|
||||||
* This lock is used if multiple kq locks are required. This possibly
|
* This lock is used if multiple kq locks are required. This possibly
|
||||||
* should be made into a per proc lock.
|
* should be made into a per proc lock.
|
||||||
@ -112,11 +112,13 @@ TASKQUEUE_DEFINE_THREAD(kqueue_ctx);
|
|||||||
|
|
||||||
// TODO: only use it in SMP
|
// TODO: only use it in SMP
|
||||||
extern struct cpu_group *cpu_top;
|
extern struct cpu_group *cpu_top;
|
||||||
#define AVG_WEIGHT_FACTOR_OLD (4)
|
|
||||||
#define AVG_WEIGHT_FACTOR_NEW (1)
|
|
||||||
|
|
||||||
/* no need to handle overflow as long as the existing org/cur doesn't overflow */
|
static inline uint64_t
|
||||||
#define CALC_OVERTIME_AVG(prev, cur) (((prev) * AVG_WEIGHT_FACTOR_OLD + (cur) * AVG_WEIGHT_FACTOR_NEW) / (AVG_WEIGHT_FACTOR_OLD + AVG_WEIGHT_FACTOR_NEW))
|
calc_overtime_avg(uint64_t prev, uint64_t cur, uint32_t prev_pct)
|
||||||
|
{
|
||||||
|
KASSERT(prev_pct < 100, ("overtime average prev_pct >= 100"));
|
||||||
|
return (prev * prev_pct + cur * (100 - prev_pct)) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
static struct kevq * kevqlist_find(struct kevqlist *kevq_list, struct kqueue *kq);
|
static struct kevq * kevqlist_find(struct kevqlist *kevq_list, struct kqueue *kq);
|
||||||
static void kevq_thred_init(struct kevq_thred *kevq_th);
|
static void kevq_thred_init(struct kevq_thred *kevq_th);
|
||||||
@ -127,7 +129,7 @@ static void kevq_release(struct kevq* kevq, int locked);
|
|||||||
static void kevq_destroy(struct kevq *kevq);
|
static void kevq_destroy(struct kevq *kevq);
|
||||||
static int kevq_acquire(struct kevq *kevq, int locked);
|
static int kevq_acquire(struct kevq *kevq, int locked);
|
||||||
static void kevq_worksteal(struct kevq *kevq);
|
static void kevq_worksteal(struct kevq *kevq);
|
||||||
void kevq_drain(struct kevq *kevq, struct thread *td);
|
static void kevq_drain(struct kevq *kevq, struct thread *td);
|
||||||
static void kevq_activate(struct kevq *kevq, struct thread *td);
|
static void kevq_activate(struct kevq *kevq, struct thread *td);
|
||||||
|
|
||||||
|
|
||||||
@ -1474,13 +1476,14 @@ kqueue_kevent(struct kqueue *kq, struct kevq *kevq, struct thread *td, int nchan
|
|||||||
|
|
||||||
CTR3(KTR_KQ, "kevent: td %d nkev %d kevent (delta) %ld ns", td->td_tid, kevq->kevq_last_nkev, cur_ts);
|
CTR3(KTR_KQ, "kevent: td %d nkev %d kevent (delta) %ld ns", td->td_tid, kevq->kevq_last_nkev, cur_ts);
|
||||||
|
|
||||||
/* divide by the number of events processed */
|
/* update total time */
|
||||||
avg = cur_ts / kevq->kevq_last_nkev;
|
kevq->kevq_tot_time += cur_ts;
|
||||||
|
|
||||||
|
/* update average latency */
|
||||||
|
avg = cur_ts / kevq->kevq_last_nkev;
|
||||||
CTR3(KTR_KQ, "kevent: td %d nkev %d kevent (avg) %ld ns", td->td_tid, kevq->kevq_last_nkev, avg);
|
CTR3(KTR_KQ, "kevent: td %d nkev %d kevent (avg) %ld ns", td->td_tid, kevq->kevq_last_nkev, avg);
|
||||||
|
|
||||||
if (kevq->kevq_avg_lat != 0) {
|
if (kevq->kevq_avg_lat != 0) {
|
||||||
kevq->kevq_avg_lat = CALC_OVERTIME_AVG(kevq->kevq_avg_lat, avg);
|
kevq->kevq_avg_lat = calc_overtime_avg(kevq->kevq_avg_lat, avg, 80);
|
||||||
} else {
|
} else {
|
||||||
kevq->kevq_avg_lat = avg;
|
kevq->kevq_avg_lat = avg;
|
||||||
}
|
}
|
||||||
@ -2339,7 +2342,26 @@ kqdom_destroy(struct kqdom *root)
|
|||||||
static void
|
static void
|
||||||
kevq_dump(struct kevq *kevq, int level, struct sbuf *buf)
|
kevq_dump(struct kevq *kevq, int level, struct sbuf *buf)
|
||||||
{
|
{
|
||||||
sbuf_printf(buf, "%*c<kevq: %p #knotes: %d avg_lat: %ld/>\n", level * 2, ' ', kevq, kevq->kn_count, kevq->kevq_avg_lat);
|
int error;
|
||||||
|
|
||||||
|
error = sbuf_printf(buf, "%*c<kevq: %p knotes: %d "
|
||||||
|
"total_time: %ld "
|
||||||
|
"total_syscall: %ld "
|
||||||
|
"total_events: %ld "
|
||||||
|
"avg_latency: %ld "
|
||||||
|
"avg_events: %ld "
|
||||||
|
"total_fallbacks: %ld "
|
||||||
|
"total_mismatches: %ld/>\n",
|
||||||
|
level * DUMP_INDENT, ' ', kevq, kevq->kn_count,
|
||||||
|
kevq->kevq_tot_time,
|
||||||
|
kevq->kevq_tot_syscall,
|
||||||
|
kevq->kevq_tot_ev,
|
||||||
|
kevq->kevq_avg_lat,
|
||||||
|
kevq->kevq_avg_ev,
|
||||||
|
kevq->kevq_tot_fallback,
|
||||||
|
kevq->kevq_tot_kqd_mismatch);
|
||||||
|
|
||||||
|
KASSERT(error == 0, ("error writing sbuf"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -2347,10 +2369,8 @@ kqdom_dump(struct kqdom *kqd, int level, struct sbuf *buf)
|
|||||||
{
|
{
|
||||||
/* XXX: No potential race between this and kqdom_build() for now.
|
/* XXX: No potential race between this and kqdom_build() for now.
|
||||||
* If we move kqdom_build() out of kqueue() syscall then there is a potential race */
|
* If we move kqdom_build() out of kqueue() syscall then there is a potential race */
|
||||||
|
int error;
|
||||||
|
error = sbuf_printf(buf, "%*c<kqdom id: %d level: %d cpu_mask:0x%lx #children: %d #active: %d leaf: %d #kevq: %d>\n", level * DUMP_INDENT, ' ',
|
||||||
/* XXX2: check return code */
|
|
||||||
sbuf_printf(buf, "%*c<kqdom id: %d level: %d cpu_mask:0x%lx #children: %d #active: %d leaf: %d #kevq: %d>\n", level * 2, ' ',
|
|
||||||
kqd->id,
|
kqd->id,
|
||||||
level,
|
level,
|
||||||
kqd->cpu_mask.__bits[0],
|
kqd->cpu_mask.__bits[0],
|
||||||
@ -2358,11 +2378,12 @@ kqdom_dump(struct kqdom *kqd, int level, struct sbuf *buf)
|
|||||||
veclist_size(&kqd->kqd_activelist),
|
veclist_size(&kqd->kqd_activelist),
|
||||||
kqdom_is_leaf(kqd),
|
kqdom_is_leaf(kqd),
|
||||||
veclist_size(&kqd->kqd_kevqs));
|
veclist_size(&kqd->kqd_kevqs));
|
||||||
|
KASSERT(error == 0, ("error writing sbuf"));
|
||||||
|
|
||||||
if (kqdom_is_leaf(kqd)) {
|
if (kqdom_is_leaf(kqd)) {
|
||||||
KQD_RLOCK(kqd);
|
KQD_RLOCK(kqd);
|
||||||
/* print all kevqs */
|
/* print all kevqs */
|
||||||
for (int i = 0; i < veclist_size(&kqd->kqd_kevqs); i++) {
|
for (int i = 0; i < veclist_size(&kqd->kqd_kevqs); i++) {
|
||||||
/* XXX: check return code */
|
|
||||||
kevq_dump(veclist_at(&kqd->kqd_kevqs, i), level + 1, buf);
|
kevq_dump(veclist_at(&kqd->kqd_kevqs, i), level + 1, buf);
|
||||||
}
|
}
|
||||||
KQD_RUNLOCK(kqd);
|
KQD_RUNLOCK(kqd);
|
||||||
@ -2371,9 +2392,8 @@ kqdom_dump(struct kqdom *kqd, int level, struct sbuf *buf)
|
|||||||
kqdom_dump(veclist_at(&kqd->children, i), level + 1, buf);
|
kqdom_dump(veclist_at(&kqd->children, i), level + 1, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
error = sbuf_printf(buf, "%*c</kqdom>\n", level * DUMP_INDENT, ' ');
|
||||||
/* XXX: check return code */
|
KASSERT(error == 0, ("error writing sbuf"));
|
||||||
sbuf_printf(buf, "%*c</kqdom>\n", level * 2, ' ');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2440,7 +2460,7 @@ kqdom_update_lat(struct kqdom *leaf, uint64_t avg)
|
|||||||
while(leaf != NULL) {
|
while(leaf != NULL) {
|
||||||
if (leaf->avg_lat != 0) {
|
if (leaf->avg_lat != 0) {
|
||||||
// bit rot race here?
|
// bit rot race here?
|
||||||
leaf->avg_lat = CALC_OVERTIME_AVG(leaf->avg_lat, avg);
|
leaf->avg_lat = calc_overtime_avg(leaf->avg_lat, avg, 80);
|
||||||
} else {
|
} else {
|
||||||
leaf->avg_lat = avg;
|
leaf->avg_lat = avg;
|
||||||
}
|
}
|
||||||
@ -3005,11 +3025,20 @@ kqueue_scan(struct kevq *kevq, int maxevents, struct kevent_copyops *k_ops,
|
|||||||
|
|
||||||
KEVQ_UNLOCK(kevq);
|
KEVQ_UNLOCK(kevq);
|
||||||
|
|
||||||
if (nkev != 0 && (KQSCHED_GET_SCHED(kq) & KEVQ_LAT_FLAGS)) {
|
if (nkev > 0 && (KQSCHED_GET_SCHED(kq) & KEVQ_LAT_FLAGS)) {
|
||||||
/* book keep the statistics */
|
/* book keep the statistics */
|
||||||
kevq->kevq_last_kev = get_cyclecount();
|
kevq->kevq_last_kev = get_cyclecount();
|
||||||
kevq->kevq_last_nkev = nkev;
|
kevq->kevq_last_nkev = nkev;
|
||||||
CTR3(KTR_KQ, "kevent: td %d nkev %d kevent (exit) %ld ns", td->td_tid, kevq->kevq_last_nkev, kevq->kevq_last_kev);
|
CTR3(KTR_KQ, "kevent: td %d nkev %d kevent (exit) %ld ns", td->td_tid, kevq->kevq_last_nkev, kevq->kevq_last_kev);
|
||||||
|
|
||||||
|
/* update total ev */
|
||||||
|
kevq->kevq_tot_ev += nkev;
|
||||||
|
kevq->kevq_tot_syscall++;
|
||||||
|
if (kevq->kevq_avg_ev == 0) {
|
||||||
|
kevq->kevq_avg_ev = nkev;
|
||||||
|
} else {
|
||||||
|
kevq->kevq_avg_ev = calc_overtime_avg(kevq->kevq_avg_ev, nkev, 80);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CTR2(KTR_KQ, "kqueue_scan: knote_free marker %p td %d", marker, td->td_tid);
|
CTR2(KTR_KQ, "kqueue_scan: knote_free marker %p td %d", marker, td->td_tid);
|
||||||
@ -3099,11 +3128,11 @@ kqueue_ioctl(struct file *fp, u_long cmd, void *data,
|
|||||||
if (kq->kq_flags & KQ_FLAG_MULTI) {
|
if (kq->kq_flags & KQ_FLAG_MULTI) {
|
||||||
/* 4MB buffer */
|
/* 4MB buffer */
|
||||||
dat = malloc(sizeof(char) * 4096 * 1024, M_KQUEUE, M_WAITOK | M_ZERO);
|
dat = malloc(sizeof(char) * 4096 * 1024, M_KQUEUE, M_WAITOK | M_ZERO);
|
||||||
sbuf_new(&buf, dat, 4096 * 1024 * sizeof(char), SBUF_AUTOEXTEND | SBUF_INCLUDENUL);
|
sbuf_new(&buf, dat, 4096 * 1024 * sizeof(char), SBUF_INCLUDENUL);
|
||||||
/* dump kvlst */
|
/* dump kvlst */
|
||||||
/* XXX: check return status of sbuf_printf */
|
|
||||||
|
|
||||||
sbuf_printf(&buf, "KQ 0x%p dump:\n\nActive KEVQ:\n", kq);
|
error = sbuf_printf(&buf, "KQ 0x%p dump:\n\nActive KEVQ:\n", kq);
|
||||||
|
KASSERT(error == 0, ("error writing sbuf"));
|
||||||
KVLST_RLOCK(kq);
|
KVLST_RLOCK(kq);
|
||||||
for(int i = 0; i < veclist_size(&kq->kevq_vlist); i++) {
|
for(int i = 0; i < veclist_size(&kq->kevq_vlist); i++) {
|
||||||
kevq_dump(veclist_at(&kq->kevq_vlist, i), 0, &buf);
|
kevq_dump(veclist_at(&kq->kevq_vlist, i), 0, &buf);
|
||||||
@ -3112,11 +3141,13 @@ kqueue_ioctl(struct file *fp, u_long cmd, void *data,
|
|||||||
|
|
||||||
/* dump kqdom if used */
|
/* dump kqdom if used */
|
||||||
if (KQSCHED_GET_SCHED(kq) & KQDOM_FLAGS) {
|
if (KQSCHED_GET_SCHED(kq) & KQDOM_FLAGS) {
|
||||||
sbuf_printf(&buf, "\nKQDOM:\n");
|
error = sbuf_printf(&buf, "\nKQDOM:\n");
|
||||||
|
KASSERT(error == 0, ("error writing sbuf"));
|
||||||
kqdom_dump(kq->kq_kqd, 0, &buf);
|
kqdom_dump(kq->kq_kqd, 0, &buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
sbuf_finish(&buf);
|
error = sbuf_finish(&buf);
|
||||||
|
KASSERT(error == 0, ("error sbuf_finish"));
|
||||||
uprintf("%s", sbuf_data(&buf));
|
uprintf("%s", sbuf_data(&buf));
|
||||||
sbuf_delete(&buf);
|
sbuf_delete(&buf);
|
||||||
free(dat, M_KQUEUE);
|
free(dat, M_KQUEUE);
|
||||||
@ -4141,7 +4172,9 @@ knote_next_kevq(struct knote *kn)
|
|||||||
int sargs;
|
int sargs;
|
||||||
int sched;
|
int sched;
|
||||||
int rand, sz;
|
int rand, sz;
|
||||||
|
int kqd_mismatch;
|
||||||
|
|
||||||
|
kqd_mismatch = 0;
|
||||||
next_kevq = NULL;
|
next_kevq = NULL;
|
||||||
kq = kn->kn_kq;
|
kq = kn->kn_kq;
|
||||||
sargs = KQSCHED_GET_SARGS(kq);
|
sargs = KQSCHED_GET_SARGS(kq);
|
||||||
@ -4180,10 +4213,14 @@ knote_next_kevq(struct knote *kn)
|
|||||||
kn->kn_kqd = kqdom_find(kq->kq_kqd, PCPU_GET(cpuid));
|
kn->kn_kqd = kqdom_find(kq->kq_kqd, PCPU_GET(cpuid));
|
||||||
|
|
||||||
KASSERT(kn->kn_kqd != NULL, ("knote scheduled on an unidentified CPU"));
|
KASSERT(kn->kn_kqd != NULL, ("knote scheduled on an unidentified CPU"));
|
||||||
CTR3(KTR_KQ, "knote_next_kevq: [QUEUE%d] knote %p attached to kqdom id %d", sargs, kn, kn->kn_kqd->id);
|
CTR4(KTR_KQ, "knote_next_kevq: [QUEUE%d] knote %p attached to kqdom id %d cpuset 0x%lx", sargs, kn, kn->kn_kqd->id, kn->kn_kqd->cpu_mask.__bits[0]);
|
||||||
}
|
}
|
||||||
kqd = kn->kn_kqd;
|
kqd = kn->kn_kqd;
|
||||||
|
|
||||||
|
/* Check if the knote interrupt is triggered on a cpu that's different from the memorized one */
|
||||||
|
if (!CPU_ISSET(PCPU_GET(cpuid), &kqd->cpu_mask)) {
|
||||||
|
kqd_mismatch = 1;
|
||||||
|
}
|
||||||
done_cq:
|
done_cq:
|
||||||
KASSERT(kqdom_is_leaf(kqd), ("found kqdom not leaf"));
|
KASSERT(kqdom_is_leaf(kqd), ("found kqdom not leaf"));
|
||||||
|
|
||||||
@ -4203,6 +4240,10 @@ knote_next_kevq(struct knote *kn)
|
|||||||
next_kevq = kevq_lock_check_avail(next_kevq);
|
next_kevq = kevq_lock_check_avail(next_kevq);
|
||||||
KQD_RUNLOCK(kqd);
|
KQD_RUNLOCK(kqd);
|
||||||
|
|
||||||
|
if (kqd_mismatch && next_kevq != NULL) {
|
||||||
|
next_kevq->kevq_tot_kqd_mismatch++;
|
||||||
|
}
|
||||||
|
|
||||||
CTR3(KTR_KQ, "knote_next_kevq: [QUEUE/CPU%d] next kevq %p for kn %p", sargs, next_kevq, kn);
|
CTR3(KTR_KQ, "knote_next_kevq: [QUEUE/CPU%d] next kevq %p for kn %p", sargs, next_kevq, kn);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -4232,6 +4273,7 @@ knote_next_kevq(struct knote *kn)
|
|||||||
next_kevq = kevq_lock_check_avail(next_kevq);
|
next_kevq = kevq_lock_check_avail(next_kevq);
|
||||||
|
|
||||||
if (next_kevq != NULL) {
|
if (next_kevq != NULL) {
|
||||||
|
next_kevq->kevq_tot_fallback++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,10 +65,18 @@ struct kevq {
|
|||||||
int kevq_state;
|
int kevq_state;
|
||||||
int kevq_refcnt;
|
int kevq_refcnt;
|
||||||
|
|
||||||
/* Used by the scheduler */
|
|
||||||
uint64_t kevq_avg_lat;
|
|
||||||
uint64_t kevq_last_kev;
|
uint64_t kevq_last_kev;
|
||||||
uint64_t kevq_last_nkev;
|
uint64_t kevq_last_nkev;
|
||||||
|
/* Sched stats */
|
||||||
|
uint64_t kevq_avg_lat;
|
||||||
|
uint64_t kevq_avg_ev;
|
||||||
|
uint64_t kevq_tot_ev;
|
||||||
|
uint64_t kevq_tot_time;
|
||||||
|
uint64_t kevq_tot_syscall;
|
||||||
|
|
||||||
|
/* TODO: maybe these should be in kqdomain or global */
|
||||||
|
uint64_t kevq_tot_fallback;
|
||||||
|
uint64_t kevq_tot_kqd_mismatch;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* TODO: assumed that threads don't get rescheduled across cores */
|
/* TODO: assumed that threads don't get rescheduled across cores */
|
||||||
|
@ -274,7 +274,7 @@ test_socket_queue(void)
|
|||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
const char *test_id = "[Multi][Queue]kevent(EVFILT_READ)";
|
const char *test_id = "[Multi][Queue]kevent(EVFILT_READ)";
|
||||||
|
|
||||||
test_begin(test_id);
|
test_begin(test_id);
|
||||||
|
|
||||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, &g_sockfd[0]) < 0)
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, &g_sockfd[0]) < 0)
|
||||||
|
Loading…
Reference in New Issue
Block a user