fix infinite loop when rtshare=0; fix a crash in Queue/CPU + best2; Added stat counter for sched and priority
This commit is contained in:
parent
638e096379
commit
7f31542099
@ -451,6 +451,7 @@ kevq_avail(struct kevq *kevq)
|
|||||||
static inline struct kevq *
|
static inline struct kevq *
|
||||||
kevq_lock_check_avail(struct kevq *next_kevq)
|
kevq_lock_check_avail(struct kevq *next_kevq)
|
||||||
{
|
{
|
||||||
|
CTR1(KTR_KQ, "kevq_lock_check_avail: kevq %p", next_kevq);
|
||||||
if (next_kevq != NULL) {
|
if (next_kevq != NULL) {
|
||||||
KEVQ_NOTOWNED(next_kevq);
|
KEVQ_NOTOWNED(next_kevq);
|
||||||
KEVQ_LOCK(next_kevq);
|
KEVQ_LOCK(next_kevq);
|
||||||
@ -2420,7 +2421,9 @@ kevq_dump(struct sbuf *buf, struct kevq *kevq, int level)
|
|||||||
"avg_events=\"%ld\" "
|
"avg_events=\"%ld\" "
|
||||||
"total_fallbacks=\"%ld\" "
|
"total_fallbacks=\"%ld\" "
|
||||||
"total_mismatches=\"%ld\" "
|
"total_mismatches=\"%ld\" "
|
||||||
"total_worksteal=\"%ld\" />\n",
|
"total_worksteal=\"%ld\" "
|
||||||
|
"total_realtime=\"%ld\" "
|
||||||
|
"total_sched=\"%ld\" />\n",
|
||||||
level * DUMP_INDENT, ' ', kevq, kevq->kn_count, kevq->kn_rt_count,
|
level * DUMP_INDENT, ' ', kevq, kevq->kn_count, kevq->kn_rt_count,
|
||||||
kevq->kevq_tot_time,
|
kevq->kevq_tot_time,
|
||||||
kevq->kevq_tot_syscall,
|
kevq->kevq_tot_syscall,
|
||||||
@ -2429,7 +2432,9 @@ kevq_dump(struct sbuf *buf, struct kevq *kevq, int level)
|
|||||||
kevq->kevq_avg_ev,
|
kevq->kevq_avg_ev,
|
||||||
kevq->kevq_tot_fallback,
|
kevq->kevq_tot_fallback,
|
||||||
kevq->kevq_tot_kqd_mismatch,
|
kevq->kevq_tot_kqd_mismatch,
|
||||||
kevq->kevq_tot_ws);
|
kevq->kevq_tot_ws,
|
||||||
|
kevq->kevq_tot_realtime,
|
||||||
|
kevq->kevq_tot_sched);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -2739,7 +2744,7 @@ kevq_worksteal(struct kevq *kevq)
|
|||||||
tgt_count = KQSCHED_GET_FARGS(kq);
|
tgt_count = KQSCHED_GET_FARGS(kq);
|
||||||
|
|
||||||
/* XXX: hack */
|
/* XXX: hack */
|
||||||
KASSERT(tgt_count < 8, ("too many kevq ws knotes"));
|
KASSERT(tgt_count <= 8, ("too many kevq ws knotes"));
|
||||||
|
|
||||||
KVLST_RLOCK(kq);
|
KVLST_RLOCK(kq);
|
||||||
other_kevq = kevq_vec_select_kevq(&kq->kevq_vlist, 1);
|
other_kevq = kevq_vec_select_kevq(&kq->kevq_vlist, 1);
|
||||||
@ -2765,7 +2770,6 @@ kevq_worksteal(struct kevq *kevq)
|
|||||||
/* steal from the first because it arrived late */
|
/* steal from the first because it arrived late */
|
||||||
ws_kn = kevq_peek_knote(other_kevq);
|
ws_kn = kevq_peek_knote(other_kevq);
|
||||||
|
|
||||||
/* TODO: maybe limit the number of knotes to go through */
|
|
||||||
while((ws_count < tgt_count) && (ws_kn != NULL)) {
|
while((ws_count < tgt_count) && (ws_kn != NULL)) {
|
||||||
|
|
||||||
/* fast fail */
|
/* fast fail */
|
||||||
@ -2918,8 +2922,16 @@ kqueue_scan(struct kevq *kevq, int maxevents, struct kevent_copyops *k_ops,
|
|||||||
/* adjust rtlimit according to the target share
|
/* adjust rtlimit according to the target share
|
||||||
* = ceil(maxevents * kq->kq_rtshare%)
|
* = ceil(maxevents * kq->kq_rtshare%)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* XXX: actually rtlimit can be 0 but we don't allow it yet/forever?
|
||||||
|
* the current implementation has an issue when only runtime events are present and rtlimit = 0
|
||||||
|
* since kevq_total_knotes returns > 0, but rtlimit = 0 so we don't dequeue any runtime event
|
||||||
|
* the function will be trapped infinitely in (wakeup because tot_ev > 0 -> dequeue normal marker -> count = 0 -> retry -> wakeup because tot ev > 0)
|
||||||
|
* We simply don't allow users to set rlimit to 0 so we at least hand back one rt event, otherwise the solution might be very complicated
|
||||||
|
* because it involves sleep waiting on different queues as rtshare changes, AND in RUNTIME too? Not worth it really.
|
||||||
|
*/
|
||||||
rtlimit = (maxevents * kq->kq_rtshare + 99) / 100;
|
rtlimit = (maxevents * kq->kq_rtshare + 99) / 100;
|
||||||
KASSERT(rtlimit >= 0, ("the math above is fundamentally broken"));
|
KASSERT(rtlimit > 0, ("the math above is fundamentally broken"));
|
||||||
|
|
||||||
rsbt = 0;
|
rsbt = 0;
|
||||||
if (tsp != NULL) {
|
if (tsp != NULL) {
|
||||||
@ -2976,7 +2988,7 @@ retry:
|
|||||||
KEVQ_OWNED(kevq);
|
KEVQ_OWNED(kevq);
|
||||||
|
|
||||||
kevp = keva;
|
kevp = keva;
|
||||||
CTR3(KTR_KQ, "kqueue_scan: td %d on kevq %p has %d events", td->td_tid, kevq, kevq_total_knote(kevq));
|
CTR4(KTR_KQ, "kqueue_scan: td %d on kevq %p has %d events, max_ev %d", td->td_tid, kevq, kevq_total_knote(kevq), maxevents);
|
||||||
|
|
||||||
if (kevq_total_knote(kevq) == 0) {
|
if (kevq_total_knote(kevq) == 0) {
|
||||||
if (fsbt == -1) {
|
if (fsbt == -1) {
|
||||||
@ -3048,7 +3060,9 @@ retry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
KEVQ_OWNED(kevq);
|
KEVQ_OWNED(kevq);
|
||||||
|
// if (kevq_total_knote(kevq) > 0) {
|
||||||
|
// KASSERT(!(TAILQ_FIRST(&kevq->kn_rt_head) == NULL && TAILQ_FIRST(&kevq->kn_head) == NULL), ("NULL > 0?"));
|
||||||
|
// }
|
||||||
/* quick check */
|
/* quick check */
|
||||||
if (curr < rtlimit) {
|
if (curr < rtlimit) {
|
||||||
rdrained = 0;
|
rdrained = 0;
|
||||||
@ -3105,7 +3119,7 @@ retry:
|
|||||||
/* Now we have exclusive access to kn */
|
/* Now we have exclusive access to kn */
|
||||||
TAILQ_REMOVE(kntq, kn, kn_tqe);
|
TAILQ_REMOVE(kntq, kn, kn_tqe);
|
||||||
|
|
||||||
CTR3(KTR_KQ, "kqueue_scan: td %d on kevq %p dequeued knote %p", td->td_tid, kevq, kn);
|
CTR4(KTR_KQ, "kqueue_scan: td %d on kevq %p dequeued knote %p, curr %d", td->td_tid, kevq, kn, curr);
|
||||||
if ((kn->kn_status & KN_DISABLED) == KN_DISABLED) {
|
if ((kn->kn_status & KN_DISABLED) == KN_DISABLED) {
|
||||||
kn->kn_status &= ~(KN_QUEUED | KN_PROCESSING | KN_WS);
|
kn->kn_status &= ~(KN_QUEUED | KN_PROCESSING | KN_WS);
|
||||||
*kncnt -= 1;
|
*kncnt -= 1;
|
||||||
@ -3117,7 +3131,7 @@ retry:
|
|||||||
/* We are dequeuing our marker, wakeup threads waiting on it */
|
/* We are dequeuing our marker, wakeup threads waiting on it */
|
||||||
knote_flux_wakeup(kn);
|
knote_flux_wakeup(kn);
|
||||||
KN_FLUX_UNLOCK(kn);
|
KN_FLUX_UNLOCK(kn);
|
||||||
CTR2(KTR_KQ, "kqueue_scan: td %d MARKER WAKEUP %p", td->td_tid, kn);
|
CTR3(KTR_KQ, "kqueue_scan: td %d MARKER WAKEUP %p PRI %d", td->td_tid, kn, pri);
|
||||||
|
|
||||||
if (kn == rtmarker) {
|
if (kn == rtmarker) {
|
||||||
rdrained = 1;
|
rdrained = 1;
|
||||||
@ -3247,6 +3261,7 @@ retry:
|
|||||||
|
|
||||||
if (pri) {
|
if (pri) {
|
||||||
curr++;
|
curr++;
|
||||||
|
kevq->kevq_tot_realtime++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nkev == KQ_NEVENTS) {
|
if (nkev == KQ_NEVENTS) {
|
||||||
@ -3395,7 +3410,7 @@ kqueue_ioctl(struct file *fp, u_long cmd, void *data,
|
|||||||
switch KQTUNE_PARSE_OBJ(tune) {
|
switch KQTUNE_PARSE_OBJ(tune) {
|
||||||
case KQTUNE_RTSHARE:
|
case KQTUNE_RTSHARE:
|
||||||
tune = KQTUNE_PARSE_ARGS(tune);
|
tune = KQTUNE_PARSE_ARGS(tune);
|
||||||
if (tune >= 0 && tune <= 100)
|
if (tune > 0 && tune <= 100)
|
||||||
kq->kq_rtshare = tune;
|
kq->kq_rtshare = tune;
|
||||||
else
|
else
|
||||||
error = (EINVAL);
|
error = (EINVAL);
|
||||||
@ -4530,14 +4545,18 @@ done_cq:
|
|||||||
if (sargs > 0) {
|
if (sargs > 0) {
|
||||||
KVLST_RLOCK(kq);
|
KVLST_RLOCK(kq);
|
||||||
other_kevq = kevq_vec_select_kevq(&kq->kevq_vlist, sargs);
|
other_kevq = kevq_vec_select_kevq(&kq->kevq_vlist, sargs);
|
||||||
KVLST_RUNLOCK(kq);
|
|
||||||
|
|
||||||
if (next_kevq == NULL || (other_kevq != NULL && kevq_lat_wcmp(next_kevq, other_kevq, 90) > 0)) {
|
if (next_kevq == NULL || (other_kevq != NULL && kevq_lat_wcmp(next_kevq, other_kevq, 90) > 0)) {
|
||||||
next_kevq = other_kevq;
|
next_kevq = other_kevq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next_kevq = kevq_lock_check_avail(next_kevq);
|
next_kevq = kevq_lock_check_avail(next_kevq);
|
||||||
|
|
||||||
|
/* need to unlock after kevq lock acquire because other_kevq might be drained too */
|
||||||
|
if (sargs > 0) {
|
||||||
|
KVLST_RUNLOCK(kq);
|
||||||
|
}
|
||||||
KQD_RUNLOCK(kqd);
|
KQD_RUNLOCK(kqd);
|
||||||
|
|
||||||
if (kqd_mismatch && next_kevq != NULL) {
|
if (kqd_mismatch && next_kevq != NULL) {
|
||||||
@ -4583,8 +4602,10 @@ done_cq:
|
|||||||
CTR2(KTR_KQ, "knote_next_kevq: [RAND] next kevq %p for kn %p", next_kevq, kn);
|
CTR2(KTR_KQ, "knote_next_kevq: [RAND] next kevq %p for kn %p", next_kevq, kn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (next_kevq != NULL)
|
if (next_kevq != NULL) {
|
||||||
KEVQ_OWNED(next_kevq);
|
KEVQ_OWNED(next_kevq);
|
||||||
|
next_kevq->kevq_tot_sched++;
|
||||||
|
}
|
||||||
|
|
||||||
return next_kevq;
|
return next_kevq;
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,31 @@
|
|||||||
#define KQDIR_INACTIVE (1)
|
#define KQDIR_INACTIVE (1)
|
||||||
|
|
||||||
struct kevq {
|
struct kevq {
|
||||||
|
/* 1st cacheline */
|
||||||
|
/* 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;
|
||||||
|
uint64_t kevq_last_kev;
|
||||||
|
uint32_t kevq_last_nkev;
|
||||||
|
#define KEVQ_SLEEP 0x01
|
||||||
|
#define KEVQ_CLOSING 0x02
|
||||||
|
#define KEVQ_ACTIVE 0x04
|
||||||
|
#define KEVQ_WS 0x08 /* the kevq is work stealing */
|
||||||
|
int kevq_state;
|
||||||
|
int kn_count; /* number of pending knotes */
|
||||||
|
int kn_rt_count; /* number of runtime knotes */
|
||||||
|
|
||||||
|
/* 2nd cacheline */
|
||||||
|
uint64_t kevq_tot_ws;
|
||||||
|
/* TODO: maybe these should be in kqdomain or global */
|
||||||
|
uint64_t kevq_tot_fallback;
|
||||||
|
uint64_t kevq_tot_kqd_mismatch;
|
||||||
|
uint64_t kevq_tot_sched;
|
||||||
|
uint64_t kevq_tot_realtime;
|
||||||
|
|
||||||
LIST_ENTRY(kevq) kevq_th_e; /* entry into kevq_thred's hashtable */
|
LIST_ENTRY(kevq) kevq_th_e; /* entry into kevq_thred's hashtable */
|
||||||
LIST_ENTRY(kevq) kq_e; /* entry into kq */
|
LIST_ENTRY(kevq) kq_e; /* entry into kq */
|
||||||
LIST_ENTRY(kevq) kevq_th_tqe; /* entry into kevq_thred's kevq_list */
|
LIST_ENTRY(kevq) kevq_th_tqe; /* entry into kevq_thred's kevq_list */
|
||||||
@ -61,29 +86,7 @@ struct kevq {
|
|||||||
struct knote kn_marker;
|
struct knote kn_marker;
|
||||||
struct ktailq kn_rt_head; /* list of pending knotes with runtime priority */
|
struct ktailq kn_rt_head; /* list of pending knotes with runtime priority */
|
||||||
struct knote kn_marker_rt;
|
struct knote kn_marker_rt;
|
||||||
int kn_count; /* number of pending knotes */
|
|
||||||
int kn_rt_count; /* number of runtime knotes */
|
|
||||||
|
|
||||||
#define KEVQ_SLEEP 0x01
|
|
||||||
#define KEVQ_CLOSING 0x02
|
|
||||||
#define KEVQ_ACTIVE 0x04
|
|
||||||
#define KEVQ_WS 0x08 /* the kevq is work stealing */
|
|
||||||
int kevq_state;
|
|
||||||
int kevq_refcnt;
|
int kevq_refcnt;
|
||||||
|
|
||||||
uint64_t kevq_last_kev;
|
|
||||||
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;
|
|
||||||
uint64_t kevq_tot_ws;
|
|
||||||
|
|
||||||
/* 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 */
|
||||||
|
@ -325,12 +325,14 @@ test_socket_queue(void)
|
|||||||
tid++;
|
tid++;
|
||||||
group[i][j].evcnt = 0;
|
group[i][j].evcnt = 0;
|
||||||
group[i][j].group_id = i;
|
group[i][j].group_id = i;
|
||||||
pthread_create(&group[i][j].thrd, NULL, test_socket_queue_thrd, &group[i][j]);
|
pthread_attr_t attr;
|
||||||
|
pthread_attr_init(&attr);
|
||||||
CPU_ZERO(&cpuset);
|
CPU_ZERO(&cpuset);
|
||||||
CPU_SET(i, &cpuset);
|
CPU_SET(i, &cpuset);
|
||||||
if (pthread_setaffinity_np(group[i][j].thrd, sizeof(cpuset_t), &cpuset) < 0) {
|
if (pthread_attr_setaffinity_np(&attr, sizeof(cpuset_t), &cpuset) < 0) {
|
||||||
err(1, "thread_affinity");
|
err(1, "thread_affinity");
|
||||||
}
|
}
|
||||||
|
pthread_create(&group[i][j].thrd, &attr, test_socket_queue_thrd, &group[i][j]);
|
||||||
#ifdef TEST_DEBUG
|
#ifdef TEST_DEBUG
|
||||||
printf("READ_M: created and affinitized thread %d to core group %d\n", group[i][j].tid, i);
|
printf("READ_M: created and affinitized thread %d to core group %d\n", group[i][j].tid, i);
|
||||||
#endif
|
#endif
|
||||||
@ -636,10 +638,10 @@ test_socket_ws_timeout()
|
|||||||
/***************************
|
/***************************
|
||||||
* Brutal test
|
* Brutal test
|
||||||
***************************/
|
***************************/
|
||||||
#define THREAD_BRUTE_CNT (16)
|
#define THREAD_BRUTE_CNT (8)
|
||||||
#define SOCK_BRUTE_CNT (128)
|
#define SOCK_BRUTE_CNT (512)
|
||||||
#define PACKET_BRUTE_CNT (50 * (SOCK_BRUTE_CNT))
|
#define PACKET_BRUTE_CNT (100 * (SOCK_BRUTE_CNT))
|
||||||
#define THREAD_EXIT_PROB (67)
|
#define THREAD_EXIT_PROB (50)
|
||||||
#define BRUTE_REALTIME_PROB (50)
|
#define BRUTE_REALTIME_PROB (50)
|
||||||
#define BRUTE_MAX_FREQ (10000)
|
#define BRUTE_MAX_FREQ (10000)
|
||||||
#define BRUTE_MIN_FREQ (1)
|
#define BRUTE_MIN_FREQ (1)
|
||||||
@ -776,7 +778,7 @@ test_socket_brutal(char* name)
|
|||||||
int error;
|
int error;
|
||||||
int val;
|
int val;
|
||||||
|
|
||||||
val = KQTUNE_MAKE(KQTUNE_RTSHARE, rand() % 100);
|
val = KQTUNE_MAKE(KQTUNE_RTSHARE, (rand() % 100) + 1);
|
||||||
error = ioctl(g_kqfd, FKQTUNE, &val);
|
error = ioctl(g_kqfd, FKQTUNE, &val);
|
||||||
if (error == -1) {
|
if (error == -1) {
|
||||||
err(1, "ioctl TUNE");
|
err(1, "ioctl TUNE");
|
||||||
@ -894,7 +896,7 @@ test_socket_realtime()
|
|||||||
socket_push(socks[i][1], '.');
|
socket_push(socks[i][1], '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i <= 100; i++) {
|
for (int i = 1; i <= 100; i++) {
|
||||||
test_socket_rt_share(kqfd, 4, i);
|
test_socket_rt_share(kqfd, 4, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -933,10 +935,32 @@ test_evfilt_read_m()
|
|||||||
if (error == -1) {
|
if (error == -1) {
|
||||||
err(1, "ioctl");
|
err(1, "ioctl");
|
||||||
}
|
}
|
||||||
test_socket_queue();
|
//test_socket_queue();
|
||||||
test_socket_brutal("queue0");
|
test_socket_brutal("queue0");
|
||||||
close(g_kqfd);
|
close(g_kqfd);
|
||||||
|
|
||||||
|
/* Queue + bo2*/
|
||||||
|
flags = KQSCHED_MAKE(KQ_SCHED_QUEUE,2, 0, 0);
|
||||||
|
g_kqfd = kqueue();
|
||||||
|
error = ioctl(g_kqfd, FKQMULTI, &flags);
|
||||||
|
if (error == -1) {
|
||||||
|
err(1, "ioctl");
|
||||||
|
}
|
||||||
|
//test_socket_queue();
|
||||||
|
test_socket_brutal("queue2");
|
||||||
|
close(g_kqfd);
|
||||||
|
|
||||||
|
/* Queue + bo2 + ws */
|
||||||
|
flags = KQSCHED_MAKE(KQ_SCHED_QUEUE,2, KQ_SCHED_FEAT_WS, 1);
|
||||||
|
g_kqfd = kqueue();
|
||||||
|
error = ioctl(g_kqfd, FKQMULTI, &flags);
|
||||||
|
if (error == -1) {
|
||||||
|
err(1, "ioctl");
|
||||||
|
}
|
||||||
|
//test_socket_queue();
|
||||||
|
test_socket_brutal("queue2_ws");
|
||||||
|
close(g_kqfd);
|
||||||
|
|
||||||
/* CPU + Bo0 */
|
/* CPU + Bo0 */
|
||||||
flags = KQSCHED_MAKE(KQ_SCHED_CPU,0,0,0);;
|
flags = KQSCHED_MAKE(KQ_SCHED_CPU,0,0,0);;
|
||||||
g_kqfd = kqueue();
|
g_kqfd = kqueue();
|
||||||
@ -947,16 +971,6 @@ test_evfilt_read_m()
|
|||||||
test_socket_brutal("cpu0");
|
test_socket_brutal("cpu0");
|
||||||
close(g_kqfd);
|
close(g_kqfd);
|
||||||
|
|
||||||
/* CPU + Bo1 */
|
|
||||||
flags = KQSCHED_MAKE(KQ_SCHED_CPU,1,0,0);;
|
|
||||||
g_kqfd = kqueue();
|
|
||||||
error = ioctl(g_kqfd, FKQMULTI, &flags);
|
|
||||||
if (error == -1) {
|
|
||||||
err(1, "ioctl");
|
|
||||||
}
|
|
||||||
test_socket_brutal("cpu1");
|
|
||||||
close(g_kqfd);
|
|
||||||
|
|
||||||
/* CPU + Bo2 */
|
/* CPU + Bo2 */
|
||||||
flags = KQSCHED_MAKE(KQ_SCHED_CPU,2,0,0);
|
flags = KQSCHED_MAKE(KQ_SCHED_CPU,2,0,0);
|
||||||
g_kqfd = kqueue();
|
g_kqfd = kqueue();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user