summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorguenther <guenther@openbsd.org>2014-05-25 18:57:07 +0000
committerguenther <guenther@openbsd.org>2014-05-25 18:57:07 +0000
commit494499d6f03ff30a15dfd0b8498eb92629bc23ee (patch)
tree99af3be01634769e8e0578e834cc925c86cd55f3
parentIn access(), use the real UID/GID for the path search too. (diff)
downloadwireguard-openbsd-494499d6f03ff30a15dfd0b8498eb92629bc23ee.tar.xz
wireguard-openbsd-494499d6f03ff30a15dfd0b8498eb92629bc23ee.zip
Handle threaded processes correctly when processing kerninfo status requests
(aka ^T), summing CPU and check runnable/running status of all threads. ok deraadt@
-rw-r--r--sys/kern/tty.c197
1 files changed, 107 insertions, 90 deletions
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
index 00874e8f3d6..9afcad6eb8b 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tty.c,v 1.105 2014/03/22 06:05:45 guenther Exp $ */
+/* $OpenBSD: tty.c,v 1.106 2014/05/25 18:57:07 guenther Exp $ */
/* $NetBSD: tty.c,v 1.68.4.2 1996/06/06 16:04:52 thorpej Exp $ */
/*-
@@ -70,7 +70,6 @@ static void ttyblock(struct tty *);
void ttyunblock(struct tty *);
static void ttyecho(int, struct tty *);
static void ttyrubo(struct tty *, int);
-static int proc_compare(struct proc *, struct proc *);
void ttkqflush(struct klist *klist);
int filt_ttyread(struct knote *kn, long hint);
void filt_ttyrdetach(struct knote *kn);
@@ -2106,13 +2105,36 @@ ttsetwater(struct tty *tp)
}
/*
+ * Get the total estcpu for a process, summing across threads.
+ * Returns true if at least one thread is runnable/running.
+ */
+static int
+process_sum(struct process *pr, fixpt_t *estcpup)
+{
+ struct proc *p;
+ fixpt_t estcpu;
+ int ret;
+
+ ret = 0;
+ estcpu = 0;
+ TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
+ if (p->p_stat == SRUN || p->p_stat == SONPROC)
+ ret = 1;
+ estcpu += p->p_pctcpu;
+ }
+
+ *estcpup = estcpu;
+ return (ret);
+}
+
+/*
* Report on state of foreground process group.
*/
void
ttyinfo(struct tty *tp)
{
- struct process *pr;
- struct proc *pick;
+ struct process *pr, *pickpr;
+ struct proc *p, *pick;
struct timespec utime, stime;
int tmp;
@@ -2130,20 +2152,52 @@ ttyinfo(struct tty *tp)
else if ((pr = LIST_FIRST(&tp->t_pgrp->pg_members)) == NULL)
ttyprintf(tp, "empty foreground process group\n");
else {
- int pctcpu;
+ const char *state;
+ fixpt_t pctcpu, pctcpu2;
+ int run, run2;
+ int calc_pctcpu;
long rss;
- /* Pick interesting process. */
- for (pick = NULL; pr != NULL; pr = LIST_NEXT(pr, ps_pglist))
- if (proc_compare(pick, pr->ps_mainproc))
- pick = pr->ps_mainproc;
+ /*
+ * Pick the most active process:
+ * - prefer at least one running/runnable thread
+ * - prefer higher total pctcpu
+ * - prefer non-zombie
+ * Otherwise take the most recently added to this process group
+ */
+ pickpr = pr;
+ run = process_sum(pickpr, &pctcpu);
+ while ((pr = LIST_NEXT(pr, ps_pglist)) != NULL) {
+ run2 = process_sum(pr, &pctcpu2);
+ if (run) {
+ /*
+ * pick is running; is p running w/same or
+ * more cpu?
+ */
+ if (run2 && pctcpu2 >= pctcpu)
+ goto update_pickpr;
+ continue;
+ }
+ /* pick isn't running; is p running *or* w/more cpu? */
+ if (run2 || pctcpu2 > pctcpu)
+ goto update_pickpr;
+
+ /* if p has less cpu or is zombie, then it's worse */
+ if (pctcpu2 < pctcpu || P_ZOMBIE(pr->ps_mainproc))
+ continue;
+update_pickpr:
+ pickpr = pr;
+ run = run2;
+ pctcpu = pctcpu2;
+ }
/* Calculate percentage cpu, resident set size. */
- pctcpu = (pick->p_pctcpu * 10000 + FSCALE / 2) >> FSHIFT;
- rss = pick->p_stat == SIDL || P_ZOMBIE(pick) ? 0 :
- vm_resident_count(pick->p_vmspace);
+ calc_pctcpu = (pctcpu * 10000 + FSCALE / 2) >> FSHIFT;
+ rss = (!P_ZOMBIE(pickpr->ps_mainproc) &&
+ pickpr->ps_mainproc->p_stat != SIDL) ? 0 :
+ vm_resident_count(pickpr->ps_vmspace);
- calctsru(&pick->p_p->ps_tu, &utime, &stime, NULL);
+ calctsru(&pickpr->ps_tu, &utime, &stime, NULL);
/* Round up and print user time. */
utime.tv_nsec += 5000000;
@@ -2159,92 +2213,55 @@ ttyinfo(struct tty *tp)
stime.tv_nsec -= 1000000000;
}
+ /*
+ * Find the most active thread:
+ * - prefer runnable
+ * - prefer higher pctcpu
+ * - prefer living
+ * Otherwise take the newest thread
+ */
+ pick = p = TAILQ_FIRST(&pickpr->ps_threads);
+ run = p->p_stat == SRUN || p->p_stat == SONPROC;
+ pctcpu = p->p_pctcpu;
+ while ((p = TAILQ_NEXT(p, p_thr_link)) != NULL) {
+ run2 = p->p_stat == SRUN || p->p_stat == SONPROC;
+ pctcpu2 = p->p_pctcpu;
+ if (run) {
+ /*
+ * pick is running; is p running w/same or
+ * more cpu?
+ */
+ if (run2 && pctcpu2 >= pctcpu)
+ goto update_pick;
+ continue;
+ }
+ /* pick isn't running; is p running *or* w/more cpu? */
+ if (run2 || pctcpu2 > pctcpu)
+ goto update_pick;
+
+ /* if p has less cpu or is exiting, then it's worse */
+ if (pctcpu2 < pctcpu || p->p_flag & P_WEXIT)
+ continue;
+update_pick:
+ pick = p;
+ run = run2;
+ pctcpu = p->p_pctcpu;
+ }
+ state = pick->p_stat == SONPROC ? "running" :
+ pick->p_stat == SRUN ? "runnable" :
+ pick->p_wmesg ? pick->p_wmesg : "iowait";
+
ttyprintf(tp,
" cmd: %s %d [%s] %lld.%02ldu %lld.%02lds %d%% %ldk\n",
- pick->p_comm, pick->p_pid,
- pick->p_stat == SONPROC ? "running" :
- pick->p_stat == SRUN ? "runnable" :
- pick->p_wmesg ? pick->p_wmesg : "iowait",
+ pick->p_comm, pickpr->ps_pid, state,
(long long)utime.tv_sec, utime.tv_nsec / 10000000,
(long long)stime.tv_sec, stime.tv_nsec / 10000000,
- pctcpu / 100, rss);
+ calc_pctcpu / 100, rss);
}
tp->t_rocount = 0; /* so pending input will be retyped if BS */
}
/*
- * Returns 1 if p2 is "better" than p1
- *
- * The algorithm for picking the "interesting" process is thus:
- *
- * 1) Only foreground processes are eligible - implied.
- * 2) Runnable processes are favored over anything else. The runner
- * with the highest cpu utilization is picked (p_estcpu). Ties are
- * broken by picking the highest pid.
- * 3) The sleeper with the shortest sleep time is next. With ties,
- * we pick out just "short-term" sleepers (P_SINTR == 0).
- * 4) Further ties are broken by picking the highest pid.
- */
-#define ISRUN(p) (((p)->p_stat == SRUN) || ((p)->p_stat == SIDL) || \
- ((p)->p_stat == SONPROC))
-#define TESTAB(a, b) ((a)<<1 | (b))
-#define ONLYA 2
-#define ONLYB 1
-#define BOTH 3
-
-static int
-proc_compare(struct proc *p1, struct proc *p2)
-{
-
- if (p1 == NULL)
- return (1);
- /*
- * see if at least one of them is runnable
- */
- switch (TESTAB(ISRUN(p1), ISRUN(p2))) {
- case ONLYA:
- return (0);
- case ONLYB:
- return (1);
- case BOTH:
- /*
- * tie - favor one with highest recent cpu utilization
- */
- if (p2->p_estcpu > p1->p_estcpu)
- return (1);
- if (p1->p_estcpu > p2->p_estcpu)
- return (0);
- return (p2->p_pid > p1->p_pid); /* tie - return highest pid */
- }
- /*
- * weed out zombies
- */
- switch (TESTAB(P_ZOMBIE(p1), P_ZOMBIE(p2))) {
- case ONLYA:
- return (1);
- case ONLYB:
- return (0);
- case BOTH:
- return (p2->p_pid > p1->p_pid); /* tie - return highest pid */
- }
- /*
- * pick the one with the smallest sleep time
- */
- if (p2->p_slptime > p1->p_slptime)
- return (0);
- if (p1->p_slptime > p2->p_slptime)
- return (1);
- /*
- * favor one sleeping in a non-interruptible sleep
- */
- if (p1->p_flag & P_SINTR && (p2->p_flag & P_SINTR) == 0)
- return (1);
- if (p2->p_flag & P_SINTR && (p1->p_flag & P_SINTR) == 0)
- return (0);
- return (p2->p_pid > p1->p_pid); /* tie - return highest pid */
-}
-
-/*
* Output char to tty; console putchar style.
*/
int