2022-01-19 19:07:02

by Walt Drummond

[permalink] [raw]
Subject: [PATCH 1/3] vstatus: Allow the n_tty line dicipline to write to a user tty

Refactor the implementation of n_tty_write() into do_n_tty_write(), and
change n_tty_write() to call do_n_tty_write() after acquiring
tty.termios_rwsem.

This allows the n_tty line dicipline to write to a user tty via
do_n_tty_write() when already holding tty.termios_rwsem.

Signed-off-by: Walt Drummond <[email protected]>
---
drivers/tty/n_tty.c | 76 ++++++++++++++++++++++++++-------------------
1 file changed, 44 insertions(+), 32 deletions(-)

diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 5be6d02dc690..6a6e7da80095 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -2231,29 +2231,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
return retval;
}

-/**
- * n_tty_write - write function for tty
- * @tty: tty device
- * @file: file object
- * @buf: userspace buffer pointer
- * @nr: size of I/O
- *
- * Write function of the terminal device. This is serialized with
- * respect to other write callers but not to termios changes, reads
- * and other such events. Since the receive code will echo characters,
- * thus calling driver write methods, the output_lock is used in
- * the output processing functions called here as well as in the
- * echo processing function to protect the column state and space
- * left in the buffer.
- *
- * This code must be sure never to sleep through a hangup.
- *
- * Locking: output_lock to protect column state and space left
- * (note that the process_output*() functions take this
- * lock themselves)
- */
-
-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
+static ssize_t do_n_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
const unsigned char *b = buf;
@@ -2261,14 +2239,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
int c;
ssize_t retval = 0;

- /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
- if (L_TOSTOP(tty) && file->f_op->write_iter != redirected_tty_write) {
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- }
-
- down_read(&tty->termios_rwsem);
+ lockdep_assert_held_read(&tty->termios_rwsem);

/* Write out any echoed characters that are still pending */
process_echoes(tty);
@@ -2336,10 +2307,51 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
remove_wait_queue(&tty->write_wait, &wait);
if (nr && tty->fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
- up_read(&tty->termios_rwsem);
+
return (b - buf) ? b - buf : retval;
}

+/**
+ * n_tty_write - write function for tty
+ * @tty: tty device
+ * @file: file object
+ * @buf: userspace buffer pointer
+ * @nr: size of I/O
+ *
+ * Write function of the terminal device. This is serialized with
+ * respect to other write callers but not to termios changes, reads
+ * and other such events. Since the receive code will echo characters,
+ * thus calling driver write methods, the output_lock is used in
+ * the output processing functions called here as well as in the
+ * echo processing function to protect the column state and space
+ * left in the buffer.
+ *
+ * This code must be sure never to sleep through a hangup.
+ *
+ * Locking: output_lock to protect column state and space left
+ * (note that the process_output*() functions take this
+ * lock themselves)
+ */
+
+static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
+ const unsigned char *buf, size_t nr)
+{
+ ssize_t retval = 0;
+
+ /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
+ if (L_TOSTOP(tty) && file->f_op->write_iter != redirected_tty_write) {
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ }
+
+ down_read(&tty->termios_rwsem);
+ retval = do_n_tty_write(tty, file, buf, nr);
+ up_read(&tty->termios_rwsem);
+
+ return retval;
+}
+
/**
* n_tty_poll - poll method for N_TTY
* @tty: terminal device
--
2.30.2


2022-01-19 19:07:04

by Walt Drummond

[permalink] [raw]
Subject: [PATCH 3/3] status: Display an informational message when the VSTATUS character is pressed or TIOCSTAT ioctl is called.

When triggered by pressing the VSTATUS key or calling the TIOCSTAT
ioctl, the n_tty line discipline will display a message on the user's
tty that provides basic information about the system and an
'interesting' process in the current foreground process group, eg:

load: 0.58 cmd: sleep 744474 [sleeping] 0.36r 0.00u 0.00s 0% 772k

The status message provides:
- System load average
- Command name and process id (from the perspective of the session)
- Scheduler state
- Total wall-clock run time
- User space run time
- System space run time
- Percentage of on-cpu time
- Resident set size

The message is only displayed when the tty has the VSTATUS character
set and the local flag NOKERNINFO disabled; it is always displayed
when TIOCSTAT is called regardless of tty settings.

Signed-off-by: Walt Drummond <[email protected]>
---
drivers/tty/Makefile | 2 +-
drivers/tty/n_tty.c | 37 +++++++++
drivers/tty/n_tty_status.c | 162 +++++++++++++++++++++++++++++++++++++
drivers/tty/tty_io.c | 2 +-
include/linux/tty.h | 123 ++++++++++++++--------------
5 files changed, 265 insertions(+), 61 deletions(-)
create mode 100644 drivers/tty/n_tty_status.c

diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 07aca5184a55..84bc99aebcff 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -2,7 +2,7 @@
obj-$(CONFIG_TTY) += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
tty_buffer.o tty_port.o tty_mutex.o \
tty_ldsem.o tty_baudrate.o tty_jobctrl.o \
- n_null.o
+ n_null.o n_tty_status.o
obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
obj-$(CONFIG_AUDIT) += tty_audit.o
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 6a6e7da80095..2e9b038e84e0 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -80,6 +80,7 @@
#define ECHO_BLOCK 256
#define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32)

+#define STATUS_LINE_LEN 160 /* tty status line will truncate at this length */

#undef N_TTY_TRACE
#ifdef N_TTY_TRACE
@@ -127,6 +128,8 @@ struct n_tty_data {
struct mutex output_lock;
};

+static int n_tty_status(struct tty_struct *tty);
+
#define MASK(x) ((x) & (N_TTY_BUF_SIZE - 1))

static inline size_t read_cnt(struct n_tty_data *ldata)
@@ -1334,6 +1337,11 @@ static void n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
commit_echoes(tty);
return;
}
+ if (c == STATUS_CHAR(tty)) {
+ if (!L_NOKERNINFO(tty))
+ n_tty_status(tty);
+ return;
+ }
if (c == '\n') {
if (L_ECHO(tty) || L_ECHONL(tty)) {
echo_char_raw('\n', ldata);
@@ -1763,6 +1771,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
set_bit(EOF_CHAR(tty), ldata->char_map);
set_bit('\n', ldata->char_map);
set_bit(EOL_CHAR(tty), ldata->char_map);
+ set_bit(STATUS_CHAR(tty), ldata->char_map);
if (L_IEXTEN(tty)) {
set_bit(WERASE_CHAR(tty), ldata->char_map);
set_bit(LNEXT_CHAR(tty), ldata->char_map);
@@ -2412,6 +2421,29 @@ static unsigned long inq_canon(struct n_tty_data *ldata)
return nr;
}

+static int n_tty_status(struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ char *buf, msg[STATUS_LINE_LEN] = {0};
+ int ret = 0;
+ size_t len = STATUS_LINE_LEN - 1;
+
+ buf = (char *) &msg;
+ ret = n_tty_get_status(tty, buf + 1, &len);
+ if (ret)
+ return ret;
+
+ if (ldata->column != 0) {
+ msg[0] = '\n';
+ len++;
+ } else {
+ buf++;
+ }
+
+ do_n_tty_write(tty, NULL, buf, len);
+ return 0;
+}
+
static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
@@ -2429,6 +2461,11 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
retval = read_cnt(ldata);
up_write(&tty->termios_rwsem);
return put_user(retval, (unsigned int __user *) arg);
+ case TIOCSTAT:
+ down_read(&tty->termios_rwsem);
+ retval = n_tty_status(tty);
+ up_read(&tty->termios_rwsem);
+ return retval;
default:
return n_tty_ioctl_helper(tty, cmd, arg);
}
diff --git a/drivers/tty/n_tty_status.c b/drivers/tty/n_tty_status.c
new file mode 100644
index 000000000000..269776db0640
--- /dev/null
+++ b/drivers/tty/n_tty_status.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * n_tty_status.c --- implements VSTATUS and TIOCSTAT from BSD
+ *
+ * Display a basic status message containing information about the
+ * foreground process and system load on the users tty, triggered by
+ * the VSTATUS character or TIOCSTAT. Ex,
+ *
+ * load: 14.11 cmd: tcsh 19623 [running] 185756.62r 88.00u 17.50s 0% 4260k
+ *
+ */
+
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/sched/loadavg.h>
+
+static const char * const task_state_unknown = "unknown";
+static const char * const task_state_array[] = {
+ "running",
+ "sleeping",
+ "disk sleep",
+ "stopped",
+ "tracing stop",
+ "dead",
+ "zombie",
+ "parked",
+ "idle",
+};
+
+static inline unsigned long getRSSk(struct mm_struct *mm)
+{
+ if (mm == NULL)
+ return 0;
+ return get_mm_rss(mm) * PAGE_SIZE / 1024;
+}
+
+static inline long frac_sec(long l)
+{
+ l /= NSEC_PER_MSEC * 10;
+ if (l < 10)
+ l *= 10;
+ return l;
+}
+
+static inline struct task_struct *compare(struct task_struct *new,
+ struct task_struct *old)
+{
+ unsigned int ostate, nstate;
+
+ if (old == NULL)
+ return new;
+
+ ostate = task_state_index(old);
+ nstate = task_state_index(new);
+
+ if (ostate == nstate) {
+ if (old->start_time > new->start_time)
+ return old;
+ return new;
+ }
+
+ if (ostate < nstate)
+ return old;
+
+ return new;
+}
+
+static struct task_struct *pick_process(struct pid *pgrp)
+{
+ struct task_struct *new, *winner = NULL;
+
+ read_lock(&tasklist_lock);
+ do_each_pid_task(pgrp, PIDTYPE_PGID, new) {
+ winner = compare(new, winner);
+ } while_each_pid_task(pgrp, PIDTYPE_PGID, new);
+ read_unlock(&tasklist_lock);
+
+ return winner;
+}
+
+/* We want the pid from the context of session */
+static inline pid_t __get_pid(struct task_struct *tsk, struct tty_struct *tty)
+{
+ return __task_pid_nr_ns(tsk, PIDTYPE_PID, ns_of_pid(tty->ctrl.session));
+}
+
+static inline const char *get_task_state_name(struct task_struct *tsk)
+{
+
+ int index;
+
+ index = task_state_index(tsk);
+ if (index > ARRAY_SIZE(task_state_array))
+ return task_state_unknown;
+ return task_state_array[index];
+}
+
+int n_tty_get_status(struct tty_struct *tty, char *msg, size_t *msglen)
+{
+ unsigned long loadavg[3];
+ uint64_t pcpu, cputime, wallclock;
+ struct task_struct *p;
+ struct rusage rusage;
+ struct timespec64 utime, stime, rtime;
+ char tname[TASK_COMM_LEN];
+ size_t len;
+
+ if (tty == NULL)
+ return -ENOTTY;
+
+ get_avenrun(loadavg, FIXED_1/200, 0);
+ len = scnprintf(msg + len, *msglen - len, "load: %lu.%02lu ",
+ LOAD_INT(loadavg[0]), LOAD_FRAC(loadavg[0]));
+
+ if (tty->ctrl.session == NULL) {
+ len += scnprintf(msg + len, *msglen - len,
+ "not a controlling terminal\n");
+ goto out;
+ }
+
+ if (tty->ctrl.pgrp == NULL) {
+ len += scnprintf(msg + len, *msglen - len,
+ "no foreground process group\n");
+ goto out;
+ }
+
+ p = pick_process(tty->ctrl.pgrp);
+ if (p == NULL) {
+ len += scnprintf(msg + len, *msglen - len,
+ "empty foreground process group\n");
+ goto out;
+ }
+
+ get_task_comm(tname, p);
+ getrusage(p, RUSAGE_BOTH, &rusage);
+ wallclock = ktime_get_ns() - p->start_time;
+
+ utime.tv_sec = rusage.ru_utime.tv_sec;
+ utime.tv_nsec = rusage.ru_utime.tv_usec * NSEC_PER_USEC;
+ stime.tv_sec = rusage.ru_stime.tv_sec;
+ stime.tv_nsec = rusage.ru_stime.tv_usec * NSEC_PER_USEC;
+ rtime = ns_to_timespec64(wallclock);
+
+ cputime = timespec64_to_ns(&utime) + timespec64_to_ns(&stime);
+ pcpu = div64_u64(cputime * 100, wallclock);
+
+ len += scnprintf(msg + len, *msglen - len,
+ /* task, PID, task state */
+ "cmd: %s %d [%s] "
+ /* rtime, utime, stime, %cpu, rss */
+ "%llu.%02lur %llu.%02luu %llu.%02lus %llu%% %luk\n",
+ tname, __get_pid(p, tty),
+ (char *)get_task_state_name(p),
+ rtime.tv_sec, frac_sec(rtime.tv_nsec),
+ utime.tv_sec, frac_sec(utime.tv_nsec),
+ stime.tv_sec, frac_sec(stime.tv_nsec),
+ pcpu, getRSSk(p->mm));
+
+out:
+ *msglen = len;
+ return 0;
+}
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 6616d4a0d41d..f2f4f48ea502 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -125,7 +125,7 @@ struct ktermios tty_std_termios = { /* for the benefit of tty drivers */
.c_oflag = OPOST | ONLCR,
.c_cflag = B38400 | CS8 | CREAD | HUPCL,
.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
- ECHOCTL | ECHOKE | IEXTEN,
+ ECHOCTL | ECHOKE | IEXTEN | NOKERNINFO,
.c_cc = INIT_C_CC,
.c_ispeed = 38400,
.c_ospeed = 38400,
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 5dbd7c5afac7..e6ba6ce2efcb 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -49,71 +49,73 @@
#define WERASE_CHAR(tty) ((tty)->termios.c_cc[VWERASE])
#define LNEXT_CHAR(tty) ((tty)->termios.c_cc[VLNEXT])
#define EOL2_CHAR(tty) ((tty)->termios.c_cc[VEOL2])
+#define STATUS_CHAR(tty) ((tty)->termios.c_cc[VSTATUS])

#define _I_FLAG(tty, f) ((tty)->termios.c_iflag & (f))
#define _O_FLAG(tty, f) ((tty)->termios.c_oflag & (f))
#define _C_FLAG(tty, f) ((tty)->termios.c_cflag & (f))
#define _L_FLAG(tty, f) ((tty)->termios.c_lflag & (f))

-#define I_IGNBRK(tty) _I_FLAG((tty), IGNBRK)
-#define I_BRKINT(tty) _I_FLAG((tty), BRKINT)
-#define I_IGNPAR(tty) _I_FLAG((tty), IGNPAR)
-#define I_PARMRK(tty) _I_FLAG((tty), PARMRK)
-#define I_INPCK(tty) _I_FLAG((tty), INPCK)
-#define I_ISTRIP(tty) _I_FLAG((tty), ISTRIP)
-#define I_INLCR(tty) _I_FLAG((tty), INLCR)
-#define I_IGNCR(tty) _I_FLAG((tty), IGNCR)
-#define I_ICRNL(tty) _I_FLAG((tty), ICRNL)
-#define I_IUCLC(tty) _I_FLAG((tty), IUCLC)
-#define I_IXON(tty) _I_FLAG((tty), IXON)
-#define I_IXANY(tty) _I_FLAG((tty), IXANY)
-#define I_IXOFF(tty) _I_FLAG((tty), IXOFF)
-#define I_IMAXBEL(tty) _I_FLAG((tty), IMAXBEL)
-#define I_IUTF8(tty) _I_FLAG((tty), IUTF8)
-
-#define O_OPOST(tty) _O_FLAG((tty), OPOST)
-#define O_OLCUC(tty) _O_FLAG((tty), OLCUC)
-#define O_ONLCR(tty) _O_FLAG((tty), ONLCR)
-#define O_OCRNL(tty) _O_FLAG((tty), OCRNL)
-#define O_ONOCR(tty) _O_FLAG((tty), ONOCR)
-#define O_ONLRET(tty) _O_FLAG((tty), ONLRET)
-#define O_OFILL(tty) _O_FLAG((tty), OFILL)
-#define O_OFDEL(tty) _O_FLAG((tty), OFDEL)
-#define O_NLDLY(tty) _O_FLAG((tty), NLDLY)
-#define O_CRDLY(tty) _O_FLAG((tty), CRDLY)
-#define O_TABDLY(tty) _O_FLAG((tty), TABDLY)
-#define O_BSDLY(tty) _O_FLAG((tty), BSDLY)
-#define O_VTDLY(tty) _O_FLAG((tty), VTDLY)
-#define O_FFDLY(tty) _O_FLAG((tty), FFDLY)
-
-#define C_BAUD(tty) _C_FLAG((tty), CBAUD)
-#define C_CSIZE(tty) _C_FLAG((tty), CSIZE)
-#define C_CSTOPB(tty) _C_FLAG((tty), CSTOPB)
-#define C_CREAD(tty) _C_FLAG((tty), CREAD)
-#define C_PARENB(tty) _C_FLAG((tty), PARENB)
-#define C_PARODD(tty) _C_FLAG((tty), PARODD)
-#define C_HUPCL(tty) _C_FLAG((tty), HUPCL)
-#define C_CLOCAL(tty) _C_FLAG((tty), CLOCAL)
-#define C_CIBAUD(tty) _C_FLAG((tty), CIBAUD)
-#define C_CRTSCTS(tty) _C_FLAG((tty), CRTSCTS)
-#define C_CMSPAR(tty) _C_FLAG((tty), CMSPAR)
-
-#define L_ISIG(tty) _L_FLAG((tty), ISIG)
-#define L_ICANON(tty) _L_FLAG((tty), ICANON)
-#define L_XCASE(tty) _L_FLAG((tty), XCASE)
-#define L_ECHO(tty) _L_FLAG((tty), ECHO)
-#define L_ECHOE(tty) _L_FLAG((tty), ECHOE)
-#define L_ECHOK(tty) _L_FLAG((tty), ECHOK)
-#define L_ECHONL(tty) _L_FLAG((tty), ECHONL)
-#define L_NOFLSH(tty) _L_FLAG((tty), NOFLSH)
-#define L_TOSTOP(tty) _L_FLAG((tty), TOSTOP)
-#define L_ECHOCTL(tty) _L_FLAG((tty), ECHOCTL)
-#define L_ECHOPRT(tty) _L_FLAG((tty), ECHOPRT)
-#define L_ECHOKE(tty) _L_FLAG((tty), ECHOKE)
-#define L_FLUSHO(tty) _L_FLAG((tty), FLUSHO)
-#define L_PENDIN(tty) _L_FLAG((tty), PENDIN)
-#define L_IEXTEN(tty) _L_FLAG((tty), IEXTEN)
-#define L_EXTPROC(tty) _L_FLAG((tty), EXTPROC)
+#define I_IGNBRK(tty) _I_FLAG((tty), IGNBRK)
+#define I_BRKINT(tty) _I_FLAG((tty), BRKINT)
+#define I_IGNPAR(tty) _I_FLAG((tty), IGNPAR)
+#define I_PARMRK(tty) _I_FLAG((tty), PARMRK)
+#define I_INPCK(tty) _I_FLAG((tty), INPCK)
+#define I_ISTRIP(tty) _I_FLAG((tty), ISTRIP)
+#define I_INLCR(tty) _I_FLAG((tty), INLCR)
+#define I_IGNCR(tty) _I_FLAG((tty), IGNCR)
+#define I_ICRNL(tty) _I_FLAG((tty), ICRNL)
+#define I_IUCLC(tty) _I_FLAG((tty), IUCLC)
+#define I_IXON(tty) _I_FLAG((tty), IXON)
+#define I_IXANY(tty) _I_FLAG((tty), IXANY)
+#define I_IXOFF(tty) _I_FLAG((tty), IXOFF)
+#define I_IMAXBEL(tty) _I_FLAG((tty), IMAXBEL)
+#define I_IUTF8(tty) _I_FLAG((tty), IUTF8)
+
+#define O_OPOST(tty) _O_FLAG((tty), OPOST)
+#define O_OLCUC(tty) _O_FLAG((tty), OLCUC)
+#define O_ONLCR(tty) _O_FLAG((tty), ONLCR)
+#define O_OCRNL(tty) _O_FLAG((tty), OCRNL)
+#define O_ONOCR(tty) _O_FLAG((tty), ONOCR)
+#define O_ONLRET(tty) _O_FLAG((tty), ONLRET)
+#define O_OFILL(tty) _O_FLAG((tty), OFILL)
+#define O_OFDEL(tty) _O_FLAG((tty), OFDEL)
+#define O_NLDLY(tty) _O_FLAG((tty), NLDLY)
+#define O_CRDLY(tty) _O_FLAG((tty), CRDLY)
+#define O_TABDLY(tty) _O_FLAG((tty), TABDLY)
+#define O_BSDLY(tty) _O_FLAG((tty), BSDLY)
+#define O_VTDLY(tty) _O_FLAG((tty), VTDLY)
+#define O_FFDLY(tty) _O_FLAG((tty), FFDLY)
+
+#define C_BAUD(tty) _C_FLAG((tty), CBAUD)
+#define C_CSIZE(tty) _C_FLAG((tty), CSIZE)
+#define C_CSTOPB(tty) _C_FLAG((tty), CSTOPB)
+#define C_CREAD(tty) _C_FLAG((tty), CREAD)
+#define C_PARENB(tty) _C_FLAG((tty), PARENB)
+#define C_PARODD(tty) _C_FLAG((tty), PARODD)
+#define C_HUPCL(tty) _C_FLAG((tty), HUPCL)
+#define C_CLOCAL(tty) _C_FLAG((tty), CLOCAL)
+#define C_CIBAUD(tty) _C_FLAG((tty), CIBAUD)
+#define C_CRTSCTS(tty) _C_FLAG((tty), CRTSCTS)
+#define C_CMSPAR(tty) _C_FLAG((tty), CMSPAR)
+
+#define L_ISIG(tty) _L_FLAG((tty), ISIG)
+#define L_ICANON(tty) _L_FLAG((tty), ICANON)
+#define L_XCASE(tty) _L_FLAG((tty), XCASE)
+#define L_ECHO(tty) _L_FLAG((tty), ECHO)
+#define L_ECHOE(tty) _L_FLAG((tty), ECHOE)
+#define L_ECHOK(tty) _L_FLAG((tty), ECHOK)
+#define L_ECHONL(tty) _L_FLAG((tty), ECHONL)
+#define L_NOFLSH(tty) _L_FLAG((tty), NOFLSH)
+#define L_TOSTOP(tty) _L_FLAG((tty), TOSTOP)
+#define L_ECHOCTL(tty) _L_FLAG((tty), ECHOCTL)
+#define L_ECHOPRT(tty) _L_FLAG((tty), ECHOPRT)
+#define L_ECHOKE(tty) _L_FLAG((tty), ECHOKE)
+#define L_FLUSHO(tty) _L_FLAG((tty), FLUSHO)
+#define L_PENDIN(tty) _L_FLAG((tty), PENDIN)
+#define L_IEXTEN(tty) _L_FLAG((tty), IEXTEN)
+#define L_EXTPROC(tty) _L_FLAG((tty), EXTPROC)
+#define L_NOKERNINFO(tty) _L_FLAG((tty), NOKERNINFO)

struct device;
struct signal_struct;
@@ -388,6 +390,9 @@ void __init n_tty_init(void);
static inline void n_tty_init(void) { }
#endif

+/* n_tty_status.c */
+extern int n_tty_get_status(struct tty_struct *tty, char *msg, size_t *msglen);
+
/* tty_audit.c */
#ifdef CONFIG_AUDIT
void tty_audit_exit(void);
--
2.30.2

2022-01-19 19:07:27

by Walt Drummond

[permalink] [raw]
Subject: [PATCH 2/3] vstatus: Add user space API definitions for VSTATUS, NOKERNINFO and TIOCSTAT

Add definitions for the VSTATUS control character, and the NOKERNINFO
local control flag in the termios struct, and add an ioctl number for
the ioctl TIOCSTAT. Also add a default VSTATUS character (Ctrl-T)
default valuses in termios.c_cc. Do this for all architectures.

Signed-off-by: Walt Drummond <[email protected]>
---
arch/alpha/include/asm/termios.h | 4 +--
arch/alpha/include/uapi/asm/ioctls.h | 1 +
arch/alpha/include/uapi/asm/termbits.h | 34 +++++++++++----------
arch/ia64/include/asm/termios.h | 4 +--
arch/ia64/include/uapi/asm/termbits.h | 34 +++++++++++----------
arch/mips/include/asm/termios.h | 4 +--
arch/mips/include/uapi/asm/ioctls.h | 1 +
arch/mips/include/uapi/asm/termbits.h | 36 +++++++++++-----------
arch/parisc/include/asm/termios.h | 4 +--
arch/parisc/include/uapi/asm/ioctls.h | 1 +
arch/parisc/include/uapi/asm/termbits.h | 34 +++++++++++----------
arch/powerpc/include/asm/termios.h | 4 +--
arch/powerpc/include/uapi/asm/ioctls.h | 2 ++
arch/powerpc/include/uapi/asm/termbits.h | 34 +++++++++++----------
arch/s390/include/asm/termios.h | 4 +--
arch/sh/include/uapi/asm/ioctls.h | 1 +
arch/sparc/include/uapi/asm/ioctls.h | 1 +
arch/sparc/include/uapi/asm/termbits.h | 38 +++++++++++++-----------
arch/xtensa/include/uapi/asm/ioctls.h | 1 +
include/asm-generic/termios.h | 4 +--
include/uapi/asm-generic/ioctls.h | 1 +
include/uapi/asm-generic/termbits.h | 34 +++++++++++----------
22 files changed, 152 insertions(+), 129 deletions(-)

diff --git a/arch/alpha/include/asm/termios.h b/arch/alpha/include/asm/termios.h
index b7c77bb1bfd2..d28ddc649286 100644
--- a/arch/alpha/include/asm/termios.h
+++ b/arch/alpha/include/asm/termios.h
@@ -8,9 +8,9 @@
werase=^W kill=^U reprint=^R sxtc=\0
intr=^C quit=^\ susp=^Z <OSF/1 VDSUSP>
start=^Q stop=^S lnext=^V discard=^U
- vmin=\1 vtime=\0
+ vmin=\1 vtime=\0 status=^T
*/
-#define INIT_C_CC "\004\000\000\177\027\025\022\000\003\034\032\000\021\023\026\025\001\000"
+#define INIT_C_CC "\004\000\000\177\027\025\022\000\003\034\032\000\021\023\026\025\001\000\024"

/*
* Translate a "termio" structure into a "termios". Ugh.
diff --git a/arch/alpha/include/uapi/asm/ioctls.h b/arch/alpha/include/uapi/asm/ioctls.h
index 971311605288..4a092b917fc1 100644
--- a/arch/alpha/include/uapi/asm/ioctls.h
+++ b/arch/alpha/include/uapi/asm/ioctls.h
@@ -124,5 +124,6 @@

#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+#define TIOCSTAT 0x545E /* display process group stats on tty */

#endif /* _ASM_ALPHA_IOCTLS_H */
diff --git a/arch/alpha/include/uapi/asm/termbits.h b/arch/alpha/include/uapi/asm/termbits.h
index 4575ba34a0ea..c429e60e0b64 100644
--- a/arch/alpha/include/uapi/asm/termbits.h
+++ b/arch/alpha/include/uapi/asm/termbits.h
@@ -70,6 +70,7 @@ struct ktermios {
#define VDISCARD 15
#define VMIN 16
#define VTIME 17
+#define VSTATUS 18

/* c_iflag bits */
#define IGNBRK 0000001
@@ -187,22 +188,23 @@ struct ktermios {
#define IBSHIFT 16

/* c_lflag bits */
-#define ISIG 0x00000080
-#define ICANON 0x00000100
-#define XCASE 0x00004000
-#define ECHO 0x00000008
-#define ECHOE 0x00000002
-#define ECHOK 0x00000004
-#define ECHONL 0x00000010
-#define NOFLSH 0x80000000
-#define TOSTOP 0x00400000
-#define ECHOCTL 0x00000040
-#define ECHOPRT 0x00000020
-#define ECHOKE 0x00000001
-#define FLUSHO 0x00800000
-#define PENDIN 0x20000000
-#define IEXTEN 0x00000400
-#define EXTPROC 0x10000000
+#define ISIG 0x00000080
+#define ICANON 0x00000100
+#define XCASE 0x00004000
+#define ECHO 0x00000008
+#define ECHOE 0x00000002
+#define ECHOK 0x00000004
+#define ECHONL 0x00000010
+#define NOFLSH 0x80000000
+#define TOSTOP 0x00400000
+#define ECHOCTL 0x00000040
+#define ECHOPRT 0x00000020
+#define ECHOKE 0x00000001
+#define FLUSHO 0x00800000
+#define PENDIN 0x20000000
+#define IEXTEN 0x00000400
+#define EXTPROC 0x10000000
+#define NOKERNINFO 0x40000000

/* Values for the ACTION argument to `tcflow'. */
#define TCOOFF 0
diff --git a/arch/ia64/include/asm/termios.h b/arch/ia64/include/asm/termios.h
index 589c026444cc..40e83f9b6ead 100644
--- a/arch/ia64/include/asm/termios.h
+++ b/arch/ia64/include/asm/termios.h
@@ -15,9 +15,9 @@
eof=^D vtime=\0 vmin=\1 sxtc=\0
start=^Q stop=^S susp=^Z eol=\0
reprint=^R discard=^U werase=^W lnext=^V
- eol2=\0
+ eol2=\0 status=^T
*/
-#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
+#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0\024"

/*
* Translate a "termio" structure into a "termios". Ugh.
diff --git a/arch/ia64/include/uapi/asm/termbits.h b/arch/ia64/include/uapi/asm/termbits.h
index 000a1a297c75..2b6e943a89c6 100644
--- a/arch/ia64/include/uapi/asm/termbits.h
+++ b/arch/ia64/include/uapi/asm/termbits.h
@@ -67,6 +67,7 @@ struct ktermios {
#define VWERASE 14
#define VLNEXT 15
#define VEOL2 16
+#define VSTATUS 17

/* c_iflag bits */
#define IGNBRK 0000001
@@ -173,22 +174,23 @@ struct ktermios {
#define IBSHIFT 16 /* Shift from CBAUD to CIBAUD */

/* c_lflag bits */
-#define ISIG 0000001
-#define ICANON 0000002
-#define XCASE 0000004
-#define ECHO 0000010
-#define ECHOE 0000020
-#define ECHOK 0000040
-#define ECHONL 0000100
-#define NOFLSH 0000200
-#define TOSTOP 0000400
-#define ECHOCTL 0001000
-#define ECHOPRT 0002000
-#define ECHOKE 0004000
-#define FLUSHO 0010000
-#define PENDIN 0040000
-#define IEXTEN 0100000
-#define EXTPROC 0200000
+#define ISIG 0000001
+#define ICANON 0000002
+#define XCASE 0000004
+#define ECHO 0000010
+#define ECHOE 0000020
+#define ECHOK 0000040
+#define ECHONL 0000100
+#define NOFLSH 0000200
+#define TOSTOP 0000400
+#define ECHOCTL 0001000
+#define ECHOPRT 0002000
+#define ECHOKE 0004000
+#define FLUSHO 0010000
+#define PENDIN 0040000
+#define IEXTEN 0100000
+#define EXTPROC 0200000
+#define NOKERNINFO 0400000

/* tcflow() and TCXONC use these */
#define TCOOFF 0
diff --git a/arch/mips/include/asm/termios.h b/arch/mips/include/asm/termios.h
index bc29eeacc55a..04729018d882 100644
--- a/arch/mips/include/asm/termios.h
+++ b/arch/mips/include/asm/termios.h
@@ -17,9 +17,9 @@
* vmin=\1 vtime=\0 eol2=\0 swtc=\0
* start=^Q stop=^S susp=^Z vdsusp=
* reprint=^R discard=^U werase=^W lnext=^V
- * eof=^D eol=\0
+ * eof=^D eol=\0 status=^T
*/
-#define INIT_C_CC "\003\034\177\025\1\0\0\0\021\023\032\0\022\017\027\026\004\0"
+#define INIT_C_CC "\003\034\177\025\1\0\0\0\021\023\032\0\022\017\027\026\004\0\024"

#include <linux/string.h>

diff --git a/arch/mips/include/uapi/asm/ioctls.h b/arch/mips/include/uapi/asm/ioctls.h
index 16aa8a766aec..dff4e9a01336 100644
--- a/arch/mips/include/uapi/asm/ioctls.h
+++ b/arch/mips/include/uapi/asm/ioctls.h
@@ -115,5 +115,6 @@
#define TIOCSERSETMULTI 0x5490 /* Set multiport config */
#define TIOCMIWAIT 0x5491 /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x5492 /* read serial port inline interrupt counts */
+#define TIOCSTAT 0x5493 /* display process group stats on tty */

#endif /* __ASM_IOCTLS_H */
diff --git a/arch/mips/include/uapi/asm/termbits.h b/arch/mips/include/uapi/asm/termbits.h
index dfeffba729b7..402d44c2aa9c 100644
--- a/arch/mips/include/uapi/asm/termbits.h
+++ b/arch/mips/include/uapi/asm/termbits.h
@@ -78,6 +78,7 @@ struct ktermios {
#define VLNEXT 15 /* Literal-next character [IEXTEN]. */
#define VEOF 16 /* End-of-file character [ICANON]. */
#define VEOL 17 /* End-of-line character [ICANON]. */
+#define VSTATUS 18

/* c_iflag bits */
#define IGNBRK 0000001 /* Ignore break condition. */
@@ -188,23 +189,24 @@ struct ktermios {
#define IBSHIFT 16 /* Shift from CBAUD to CIBAUD */

/* c_lflag bits */
-#define ISIG 0000001 /* Enable signals. */
-#define ICANON 0000002 /* Do erase and kill processing. */
-#define XCASE 0000004
-#define ECHO 0000010 /* Enable echo. */
-#define ECHOE 0000020 /* Visual erase for ERASE. */
-#define ECHOK 0000040 /* Echo NL after KILL. */
-#define ECHONL 0000100 /* Echo NL even if ECHO is off. */
-#define NOFLSH 0000200 /* Disable flush after interrupt. */
-#define IEXTEN 0000400 /* Enable DISCARD and LNEXT. */
-#define ECHOCTL 0001000 /* Echo control characters as ^X. */
-#define ECHOPRT 0002000 /* Hardcopy visual erase. */
-#define ECHOKE 0004000 /* Visual erase for KILL. */
-#define FLUSHO 0020000
-#define PENDIN 0040000 /* Retype pending input (state). */
-#define TOSTOP 0100000 /* Send SIGTTOU for background output. */
-#define ITOSTOP TOSTOP
-#define EXTPROC 0200000 /* External processing on pty */
+#define ISIG 0000001 /* Enable signals. */
+#define ICANON 0000002 /* Do erase and kill processing. */
+#define XCASE 0000004
+#define ECHO 0000010 /* Enable echo. */
+#define ECHOE 0000020 /* Visual erase for ERASE. */
+#define ECHOK 0000040 /* Echo NL after KILL. */
+#define ECHONL 0000100 /* Echo NL even if ECHO is off. */
+#define NOFLSH 0000200 /* Disable flush after interrupt. */
+#define IEXTEN 0000400 /* Enable DISCARD and LNEXT. */
+#define ECHOCTL 0001000 /* Echo control characters as ^X. */
+#define ECHOPRT 0002000 /* Hardcopy visual erase. */
+#define ECHOKE 0004000 /* Visual erase for KILL. */
+#define FLUSHO 0020000
+#define PENDIN 0040000 /* Retype pending input (state). */
+#define TOSTOP 0100000 /* Send SIGTTOU for background output. */
+#define ITOSTOP TOSTOP
+#define EXTPROC 0200000 /* External processing on pty */
+#define NOKERNINFO 0400000 /* Disable kernel status message */

/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
diff --git a/arch/parisc/include/asm/termios.h b/arch/parisc/include/asm/termios.h
index cded9dc90c1b..63c6c7edb0ff 100644
--- a/arch/parisc/include/asm/termios.h
+++ b/arch/parisc/include/asm/termios.h
@@ -9,9 +9,9 @@
eof=^D vtime=\0 vmin=\1 sxtc=\0
start=^Q stop=^S susp=^Z eol=\0
reprint=^R discard=^U werase=^W lnext=^V
- eol2=\0
+ eol2=\0 status=^T
*/
-#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
+#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0\024"

/*
* Translate a "termio" structure into a "termios". Ugh.
diff --git a/arch/parisc/include/uapi/asm/ioctls.h b/arch/parisc/include/uapi/asm/ioctls.h
index 82d1148c6379..875db5ff080a 100644
--- a/arch/parisc/include/uapi/asm/ioctls.h
+++ b/arch/parisc/include/uapi/asm/ioctls.h
@@ -80,6 +80,7 @@

#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+#define TIOCSTAT 0x545E /* display process group stats on tty */
#define FIOQSIZE 0x5460 /* Get exact space used by quota */

#define TIOCSTART 0x5461
diff --git a/arch/parisc/include/uapi/asm/termbits.h b/arch/parisc/include/uapi/asm/termbits.h
index 40e920f8d683..384df7348b53 100644
--- a/arch/parisc/include/uapi/asm/termbits.h
+++ b/arch/parisc/include/uapi/asm/termbits.h
@@ -58,6 +58,7 @@ struct ktermios {
#define VWERASE 14
#define VLNEXT 15
#define VEOL2 16
+#define VSTATUS 17


/* c_iflag bits */
@@ -166,22 +167,23 @@ struct ktermios {


/* c_lflag bits */
-#define ISIG 0000001
-#define ICANON 0000002
-#define XCASE 0000004
-#define ECHO 0000010
-#define ECHOE 0000020
-#define ECHOK 0000040
-#define ECHONL 0000100
-#define NOFLSH 0000200
-#define TOSTOP 0000400
-#define ECHOCTL 0001000
-#define ECHOPRT 0002000
-#define ECHOKE 0004000
-#define FLUSHO 0010000
-#define PENDIN 0040000
-#define IEXTEN 0100000
-#define EXTPROC 0200000
+#define ISIG 0000001
+#define ICANON 0000002
+#define XCASE 0000004
+#define ECHO 0000010
+#define ECHOE 0000020
+#define ECHOK 0000040
+#define ECHONL 0000100
+#define NOFLSH 0000200
+#define TOSTOP 0000400
+#define ECHOCTL 0001000
+#define ECHOPRT 0002000
+#define ECHOKE 0004000
+#define FLUSHO 0010000
+#define PENDIN 0040000
+#define IEXTEN 0100000
+#define EXTPROC 0200000
+#define NOKERNINFO 0400000

/* tcflow() and TCXONC use these */
#define TCOOFF 0
diff --git a/arch/powerpc/include/asm/termios.h b/arch/powerpc/include/asm/termios.h
index 205de8f8a9d3..e5381c8f86f0 100644
--- a/arch/powerpc/include/asm/termios.h
+++ b/arch/powerpc/include/asm/termios.h
@@ -10,8 +10,8 @@

#include <uapi/asm/termios.h>

-/* ^C ^\ del ^U ^D 1 0 0 0 0 ^W ^R ^Z ^Q ^S ^V ^U */
-#define INIT_C_CC "\003\034\177\025\004\001\000\000\000\000\027\022\032\021\023\026\025"
+/* ^C ^\ del ^U ^D 1 0 0 0 0 ^W ^R ^Z ^Q ^S ^V ^U ^T */
+#define INIT_C_CC "\003\034\177\025\004\001\000\000\000\000\027\022\032\021\023\026\025\024"

#include <asm-generic/termios-base.h>

diff --git a/arch/powerpc/include/uapi/asm/ioctls.h b/arch/powerpc/include/uapi/asm/ioctls.h
index 2c145da3b774..5fc2449036a7 100644
--- a/arch/powerpc/include/uapi/asm/ioctls.h
+++ b/arch/powerpc/include/uapi/asm/ioctls.h
@@ -120,4 +120,6 @@
#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */

+#define TIOCSTAT 0x545E /* display process group stats on tty */
+
#endif /* _ASM_POWERPC_IOCTLS_H */
diff --git a/arch/powerpc/include/uapi/asm/termbits.h b/arch/powerpc/include/uapi/asm/termbits.h
index ed18bc61f63d..ffd0e50d680c 100644
--- a/arch/powerpc/include/uapi/asm/termbits.h
+++ b/arch/powerpc/include/uapi/asm/termbits.h
@@ -62,6 +62,7 @@ struct ktermios {
#define VSTOP 14
#define VLNEXT 15
#define VDISCARD 16
+#define VSTATUS 17

/* c_iflag bits */
#define IGNBRK 0000001
@@ -175,22 +176,23 @@ struct ktermios {
#define CRTSCTS 020000000000 /* flow control */

/* c_lflag bits */
-#define ISIG 0x00000080
-#define ICANON 0x00000100
-#define XCASE 0x00004000
-#define ECHO 0x00000008
-#define ECHOE 0x00000002
-#define ECHOK 0x00000004
-#define ECHONL 0x00000010
-#define NOFLSH 0x80000000
-#define TOSTOP 0x00400000
-#define ECHOCTL 0x00000040
-#define ECHOPRT 0x00000020
-#define ECHOKE 0x00000001
-#define FLUSHO 0x00800000
-#define PENDIN 0x20000000
-#define IEXTEN 0x00000400
-#define EXTPROC 0x10000000
+#define ISIG 0x00000080
+#define ICANON 0x00000100
+#define XCASE 0x00004000
+#define ECHO 0x00000008
+#define ECHOE 0x00000002
+#define ECHOK 0x00000004
+#define ECHONL 0x00000010
+#define NOFLSH 0x80000000
+#define TOSTOP 0x00400000
+#define ECHOCTL 0x00000040
+#define ECHOPRT 0x00000020
+#define ECHOKE 0x00000001
+#define FLUSHO 0x00800000
+#define PENDIN 0x20000000
+#define IEXTEN 0x00000400
+#define EXTPROC 0x10000000
+#define NOKERNINFO 0x40000000

/* Values for the ACTION argument to `tcflow'. */
#define TCOOFF 0
diff --git a/arch/s390/include/asm/termios.h b/arch/s390/include/asm/termios.h
index 46fa3020b41e..8d2017f4905d 100644
--- a/arch/s390/include/asm/termios.h
+++ b/arch/s390/include/asm/termios.h
@@ -14,9 +14,9 @@
eof=^D vtime=\0 vmin=\1 sxtc=\0
start=^Q stop=^S susp=^Z eol=\0
reprint=^R discard=^U werase=^W lnext=^V
- eol2=\0
+ eol2=\0 vstatus=^T
*/
-#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
+#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0\024"

#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios2))
#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios2))
diff --git a/arch/sh/include/uapi/asm/ioctls.h b/arch/sh/include/uapi/asm/ioctls.h
index 11866d4f60e1..a17d6eb802a2 100644
--- a/arch/sh/include/uapi/asm/ioctls.h
+++ b/arch/sh/include/uapi/asm/ioctls.h
@@ -112,5 +112,6 @@

#define TIOCMIWAIT _IO('T', 92) /* 0x545C */ /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+#define TIOCSTAT 0x545F /* display process group stats on tty */

#endif /* __ASM_SH_IOCTLS_H */
diff --git a/arch/sparc/include/uapi/asm/ioctls.h b/arch/sparc/include/uapi/asm/ioctls.h
index 7fd2f5873c9e..2207627ffcd2 100644
--- a/arch/sparc/include/uapi/asm/ioctls.h
+++ b/arch/sparc/include/uapi/asm/ioctls.h
@@ -124,6 +124,7 @@
#define TIOCSERSETMULTI 0x545B /* Set multiport config */
#define TIOCMIWAIT 0x545C /* Wait for change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* Read serial port inline interrupt counts */
+#define TIOCSTAT 0x545E /* Display process group stats on tty */

/* Kernel definitions */

diff --git a/arch/sparc/include/uapi/asm/termbits.h b/arch/sparc/include/uapi/asm/termbits.h
index ce5ad5d0f105..ec60e792a125 100644
--- a/arch/sparc/include/uapi/asm/termbits.h
+++ b/arch/sparc/include/uapi/asm/termbits.h
@@ -80,6 +80,7 @@ struct ktermios {
#define VDISCARD 13
#define VWERASE 14
#define VLNEXT 15
+#define VSTATUS 16

/* Kernel keeps vmin/vtime separated, user apps assume vmin/vtime is
* shared with eof/eol
@@ -206,24 +207,25 @@ struct ktermios {

#define IBSHIFT 16 /* Shift from CBAUD to CIBAUD */

-/* c_lflag bits */
-#define ISIG 0x00000001
-#define ICANON 0x00000002
-#define XCASE 0x00000004
-#define ECHO 0x00000008
-#define ECHOE 0x00000010
-#define ECHOK 0x00000020
-#define ECHONL 0x00000040
-#define NOFLSH 0x00000080
-#define TOSTOP 0x00000100
-#define ECHOCTL 0x00000200
-#define ECHOPRT 0x00000400
-#define ECHOKE 0x00000800
-#define DEFECHO 0x00001000 /* SUNOS thing, what is it? */
-#define FLUSHO 0x00002000
-#define PENDIN 0x00004000
-#define IEXTEN 0x00008000
-#define EXTPROC 0x00010000
+/* c_lflag bits */
+#define ISIG 0x00000001
+#define ICANON 0x00000002
+#define XCASE 0x00000004
+#define ECHO 0x00000008
+#define ECHOE 0x00000010
+#define ECHOK 0x00000020
+#define ECHONL 0x00000040
+#define NOFLSH 0x00000080
+#define TOSTOP 0x00000100
+#define ECHOCTL 0x00000200
+#define ECHOPRT 0x00000400
+#define ECHOKE 0x00000800
+#define DEFECHO 0x00001000 /* SUNOS thing, what is it? */
+#define FLUSHO 0x00002000
+#define PENDIN 0x00004000
+#define IEXTEN 0x00008000
+#define EXTPROC 0x00010000
+#define NOKERNINFO 0x00020000

/* modem lines */
#define TIOCM_LE 0x001
diff --git a/arch/xtensa/include/uapi/asm/ioctls.h b/arch/xtensa/include/uapi/asm/ioctls.h
index 6d4a87296c95..7c0198877bcf 100644
--- a/arch/xtensa/include/uapi/asm/ioctls.h
+++ b/arch/xtensa/include/uapi/asm/ioctls.h
@@ -126,5 +126,6 @@

#define TIOCMIWAIT _IO('T', 92) /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+#define TIOCSTAT 0x545E /* display process group stats on tty */

#endif /* _XTENSA_IOCTLS_H */
diff --git a/include/asm-generic/termios.h b/include/asm-generic/termios.h
index b1398d0d4a1d..9b080e1a82d4 100644
--- a/include/asm-generic/termios.h
+++ b/include/asm-generic/termios.h
@@ -10,9 +10,9 @@
eof=^D vtime=\0 vmin=\1 sxtc=\0
start=^Q stop=^S susp=^Z eol=\0
reprint=^R discard=^U werase=^W lnext=^V
- eol2=\0
+ eol2=\0 status=^T
*/
-#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
+#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0\024"

/*
* Translate a "termio" structure into a "termios". Ugh.
diff --git a/include/uapi/asm-generic/ioctls.h b/include/uapi/asm-generic/ioctls.h
index cdc9f4ca8c27..eafb662d6a0e 100644
--- a/include/uapi/asm-generic/ioctls.h
+++ b/include/uapi/asm-generic/ioctls.h
@@ -97,6 +97,7 @@

#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+#define TIOCSTAT 0x545E /* display process group stats on tty */

/*
* Some arches already define FIOQSIZE due to a historical
diff --git a/include/uapi/asm-generic/termbits.h b/include/uapi/asm-generic/termbits.h
index 2fbaf9ae89dd..cb4e9c6d629f 100644
--- a/include/uapi/asm-generic/termbits.h
+++ b/include/uapi/asm-generic/termbits.h
@@ -58,6 +58,7 @@ struct ktermios {
#define VWERASE 14
#define VLNEXT 15
#define VEOL2 16
+#define VSTATUS 17

/* c_iflag bits */
#define IGNBRK 0000001
@@ -164,22 +165,23 @@ struct ktermios {
#define IBSHIFT 16 /* Shift from CBAUD to CIBAUD */

/* c_lflag bits */
-#define ISIG 0000001
-#define ICANON 0000002
-#define XCASE 0000004
-#define ECHO 0000010
-#define ECHOE 0000020
-#define ECHOK 0000040
-#define ECHONL 0000100
-#define NOFLSH 0000200
-#define TOSTOP 0000400
-#define ECHOCTL 0001000
-#define ECHOPRT 0002000
-#define ECHOKE 0004000
-#define FLUSHO 0010000
-#define PENDIN 0040000
-#define IEXTEN 0100000
-#define EXTPROC 0200000
+#define ISIG 0000001
+#define ICANON 0000002
+#define XCASE 0000004
+#define ECHO 0000010
+#define ECHOE 0000020
+#define ECHOK 0000040
+#define ECHONL 0000100
+#define NOFLSH 0000200
+#define TOSTOP 0000400
+#define ECHOCTL 0001000
+#define ECHOPRT 0002000
+#define ECHOKE 0004000
+#define FLUSHO 0010000
+#define PENDIN 0040000
+#define IEXTEN 0100000
+#define EXTPROC 0200000
+#define NOKERNINFO 0400000

/* tcflow() and TCXONC use these */
#define TCOOFF 0
--
2.30.2

2022-01-19 22:27:13

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 2/3] vstatus: Add user space API definitions for VSTATUS, NOKERNINFO and TIOCSTAT

On Tue, Jan 18, 2022 at 5:43 AM Walt Drummond <[email protected]> wrote:

> diff --git a/include/uapi/asm-generic/ioctls.h b/include/uapi/asm-generic/ioctls.h
> index cdc9f4ca8c27..eafb662d6a0e 100644
> --- a/include/uapi/asm-generic/ioctls.h
> +++ b/include/uapi/asm-generic/ioctls.h
> @@ -97,6 +97,7 @@
>
> #define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
> #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
> +#define TIOCSTAT 0x545E /* display process group stats on tty */
>
> /*
> * Some arches already define FIOQSIZE due to a historical

I don't see any advantage in using the old-style ioctl command definitions
here, and looking through the history of this file, all recent additions used
the now normal _IOWR() style macros, so please use those as well.

Arnd

2022-01-20 06:16:10

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 3/3] status: Display an informational message when the VSTATUS character is pressed or TIOCSTAT ioctl is called.

Cc Oleg, scheduler and mm guys.

Hi,

The processes and their mm handling don't look right to me, but I don't
know that area that well.

Overall, is this really worth the hassle?

BTW I haven't received 2/3.

On 18. 01. 22, 5:43, Walt Drummond wrote:
> When triggered by pressing the VSTATUS key or calling the TIOCSTAT
> ioctl, the n_tty line discipline will display a message on the user's
> tty that provides basic information about the system and an
> 'interesting' process in the current foreground process group, eg:
>
> load: 0.58 cmd: sleep 744474 [sleeping] 0.36r 0.00u 0.00s 0% 772k
>
> The status message provides:
> - System load average
> - Command name and process id (from the perspective of the session)
> - Scheduler state
> - Total wall-clock run time
> - User space run time
> - System space run time
> - Percentage of on-cpu time
> - Resident set size
>
> The message is only displayed when the tty has the VSTATUS character
> set and the local flag NOKERNINFO disabled; it is always displayed
> when TIOCSTAT is called regardless of tty settings.
>
> Signed-off-by: Walt Drummond <[email protected]>
> ---
> drivers/tty/Makefile | 2 +-
> drivers/tty/n_tty.c | 37 +++++++++
> drivers/tty/n_tty_status.c | 162 +++++++++++++++++++++++++++++++++++++
> drivers/tty/tty_io.c | 2 +-
> include/linux/tty.h | 123 ++++++++++++++--------------
> 5 files changed, 265 insertions(+), 61 deletions(-)
> create mode 100644 drivers/tty/n_tty_status.c
>
> diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
> index 07aca5184a55..84bc99aebcff 100644
> --- a/drivers/tty/Makefile
> +++ b/drivers/tty/Makefile
> @@ -2,7 +2,7 @@
> obj-$(CONFIG_TTY) += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
> tty_buffer.o tty_port.o tty_mutex.o \
> tty_ldsem.o tty_baudrate.o tty_jobctrl.o \
> - n_null.o
> + n_null.o n_tty_status.o
> obj-$(CONFIG_LEGACY_PTYS) += pty.o
> obj-$(CONFIG_UNIX98_PTYS) += pty.o
> obj-$(CONFIG_AUDIT) += tty_audit.o
> diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
> index 6a6e7da80095..2e9b038e84e0 100644
> --- a/drivers/tty/n_tty.c
> +++ b/drivers/tty/n_tty.c
> @@ -80,6 +80,7 @@
> #define ECHO_BLOCK 256
> #define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32)
>
> +#define STATUS_LINE_LEN 160 /* tty status line will truncate at this length */
>
> #undef N_TTY_TRACE
> #ifdef N_TTY_TRACE
> @@ -127,6 +128,8 @@ struct n_tty_data {
> struct mutex output_lock;
> };
>
> +static int n_tty_status(struct tty_struct *tty);
> +
> #define MASK(x) ((x) & (N_TTY_BUF_SIZE - 1))
>
> static inline size_t read_cnt(struct n_tty_data *ldata)
> @@ -1334,6 +1337,11 @@ static void n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
> commit_echoes(tty);
> return;
> }
> + if (c == STATUS_CHAR(tty)) {
> + if (!L_NOKERNINFO(tty))
> + n_tty_status(tty);
> + return;
> + }
> if (c == '\n') {
> if (L_ECHO(tty) || L_ECHONL(tty)) {
> echo_char_raw('\n', ldata);
> @@ -1763,6 +1771,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
> set_bit(EOF_CHAR(tty), ldata->char_map);
> set_bit('\n', ldata->char_map);
> set_bit(EOL_CHAR(tty), ldata->char_map);
> + set_bit(STATUS_CHAR(tty), ldata->char_map);
> if (L_IEXTEN(tty)) {
> set_bit(WERASE_CHAR(tty), ldata->char_map);
> set_bit(LNEXT_CHAR(tty), ldata->char_map);
> @@ -2412,6 +2421,29 @@ static unsigned long inq_canon(struct n_tty_data *ldata)
> return nr;
> }
>
> +static int n_tty_status(struct tty_struct *tty)
> +{
> + struct n_tty_data *ldata = tty->disc_data;
> + char *buf, msg[STATUS_LINE_LEN] = {0};

160 B on stack?

> + int ret = 0;
> + size_t len = STATUS_LINE_LEN - 1;
> +
> + buf = (char *) &msg;
> + ret = n_tty_get_status(tty, buf + 1, &len);
> + if (ret)
> + return ret;
> +
> + if (ldata->column != 0) {
> + msg[0] = '\n';
> + len++;

It's not clear to me why this is after n_tty_get_status and therefore
you need buf? If you stored \n to msg[0], you could just pass msg (to
rewrite \n) or (msg + 1) to n_tty_get_status() -- depending on
ldata->column, right?

> + } else {
> + buf++;
> + }
> +
> + do_n_tty_write(tty, NULL, buf, len);
> + return 0;
> +}
> +
> static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
> unsigned int cmd, unsigned long arg)
> {
> @@ -2429,6 +2461,11 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
> retval = read_cnt(ldata);
> up_write(&tty->termios_rwsem);
> return put_user(retval, (unsigned int __user *) arg);
> + case TIOCSTAT:
> + down_read(&tty->termios_rwsem);
> + retval = n_tty_status(tty);
> + up_read(&tty->termios_rwsem);
> + return retval;
> default:
> return n_tty_ioctl_helper(tty, cmd, arg);
> }
> diff --git a/drivers/tty/n_tty_status.c b/drivers/tty/n_tty_status.c
> new file mode 100644
> index 000000000000..269776db0640
> --- /dev/null
> +++ b/drivers/tty/n_tty_status.c
> @@ -0,0 +1,162 @@
> +// SPDX-License-Identifier: GPL-1.0+
> +/*
> + * n_tty_status.c --- implements VSTATUS and TIOCSTAT from BSD
> + *
> + * Display a basic status message containing information about the
> + * foreground process and system load on the users tty, triggered by
> + * the VSTATUS character or TIOCSTAT. Ex,
> + *
> + * load: 14.11 cmd: tcsh 19623 [running] 185756.62r 88.00u 17.50s 0% 4260k
> + *
> + */
> +
> +#include <linux/tty.h>
> +#include <linux/mm.h>
> +#include <linux/sched/loadavg.h>
> +
> +static const char * const task_state_unknown = "unknown";

Why do you need a variable for this, actually? Plus why do you need also
a pointer (another 4/8B) to the storage?

> +static const char * const task_state_array[] = {
> + "running",
> + "sleeping",
> + "disk sleep",
> + "stopped",
> + "tracing stop",
> + "dead",
> + "zombie",
> + "parked",
> + "idle",
> +};
> +
> +static inline unsigned long getRSSk(struct mm_struct *mm)
> +{
> + if (mm == NULL)
> + return 0;
> + return get_mm_rss(mm) * PAGE_SIZE / 1024;
> +}
> +
> +static inline long frac_sec(long l)

I don't understand what this does. The name should be more descriptive.

> +{
> + l /= NSEC_PER_MSEC * 10;
> + if (l < 10)
> + l *= 10;
> + return l;
> +}
> +
> +static inline struct task_struct *compare(struct task_struct *new,
> + struct task_struct *old)
> +{
> + unsigned int ostate, nstate;
> +
> + if (old == NULL)
> + return new;
> +
> + ostate = task_state_index(old);
> + nstate = task_state_index(new);
> +
> + if (ostate == nstate) {
> + if (old->start_time > new->start_time)
> + return old;
> + return new;
> + }
> +
> + if (ostate < nstate)
> + return old;
> +
> + return new;
> +}
> +
> +static struct task_struct *pick_process(struct pid *pgrp)
> +{
> + struct task_struct *new, *winner = NULL;
> +
> + read_lock(&tasklist_lock);
> + do_each_pid_task(pgrp, PIDTYPE_PGID, new) {
> + winner = compare(new, winner);
> + } while_each_pid_task(pgrp, PIDTYPE_PGID, new);

Whys is get_task_struct() not needed?

> + read_unlock(&tasklist_lock);

IOW what happens if winner has just died?

> + return winner;
> +}
> +
> +/* We want the pid from the context of session */
> +static inline pid_t __get_pid(struct task_struct *tsk, struct tty_struct *tty)
> +{
> + return __task_pid_nr_ns(tsk, PIDTYPE_PID, ns_of_pid(tty->ctrl.session));

You're holding no locks protecting tty->ctrl.session.

> +}
> +
> +static inline const char *get_task_state_name(struct task_struct *tsk)

This definitely doesn't belong here. How do you ensure it matches the
returned index also in the future. Put it along with
task_index_to_char()? Or simply use task_state_to_char()?

> +{
> +
> + int index;
> +
> + index = task_state_index(tsk);
> + if (index > ARRAY_SIZE(task_state_array))

Should be >=, or?

> + return task_state_unknown;
> + return task_state_array[index];
> +}
> +
> +int n_tty_get_status(struct tty_struct *tty, char *msg, size_t *msglen)
> +{
> + unsigned long loadavg[3];
> + uint64_t pcpu, cputime, wallclock;
> + struct task_struct *p;
> + struct rusage rusage;
> + struct timespec64 utime, stime, rtime;
> + char tname[TASK_COMM_LEN];

How much stack did you consume in sum with its caller n_tty_status()?

> + size_t len;
> +
> + if (tty == NULL)
> + return -ENOTTY;

How can this happen?

> + get_avenrun(loadavg, FIXED_1/200, 0);
> + len = scnprintf(msg + len, *msglen - len, "load: %lu.%02lu ",
> + LOAD_INT(loadavg[0]), LOAD_FRAC(loadavg[0]));
> +
> + if (tty->ctrl.session == NULL) {
> + len += scnprintf(msg + len, *msglen - len,
> + "not a controlling terminal\n");
> + goto out;
> + }
> +
> + if (tty->ctrl.pgrp == NULL) {
> + len += scnprintf(msg + len, *msglen - len,
> + "no foreground process group\n");
> + goto out;
> + }
> +
> + p = pick_process(tty->ctrl.pgrp);

Why is no lock needed?

> + if (p == NULL) {
> + len += scnprintf(msg + len, *msglen - len,
> + "empty foreground process group\n");
> + goto out;
> + }
> +
> + get_task_comm(tname, p);
> + getrusage(p, RUSAGE_BOTH, &rusage);
> + wallclock = ktime_get_ns() - p->start_time;
> +
> + utime.tv_sec = rusage.ru_utime.tv_sec;
> + utime.tv_nsec = rusage.ru_utime.tv_usec * NSEC_PER_USEC;
> + stime.tv_sec = rusage.ru_stime.tv_sec;
> + stime.tv_nsec = rusage.ru_stime.tv_usec * NSEC_PER_USEC;
> + rtime = ns_to_timespec64(wallclock);
> +
> + cputime = timespec64_to_ns(&utime) + timespec64_to_ns(&stime);
> + pcpu = div64_u64(cputime * 100, wallclock);
> +
> + len += scnprintf(msg + len, *msglen - len,
> + /* task, PID, task state */
> + "cmd: %s %d [%s] "
> + /* rtime, utime, stime, %cpu, rss */
> + "%llu.%02lur %llu.%02luu %llu.%02lus %llu%% %luk\n",
> + tname, __get_pid(p, tty),
> + (char *)get_task_state_name(p),
> + rtime.tv_sec, frac_sec(rtime.tv_nsec),
> + utime.tv_sec, frac_sec(utime.tv_nsec),
> + stime.tv_sec, frac_sec(stime.tv_nsec),
> + pcpu, getRSSk(p->mm));

Why do you think p->mm is still alive (even after the getRSSk()'s
check)? So no get_task_mm() needed?

> +out:
> + *msglen = len;
> + return 0;
> +}
> diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
> index 6616d4a0d41d..f2f4f48ea502 100644
> --- a/drivers/tty/tty_io.c
> +++ b/drivers/tty/tty_io.c
> @@ -125,7 +125,7 @@ struct ktermios tty_std_termios = { /* for the benefit of tty drivers */
> .c_oflag = OPOST | ONLCR,
> .c_cflag = B38400 | CS8 | CREAD | HUPCL,
> .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
> - ECHOCTL | ECHOKE | IEXTEN,
> + ECHOCTL | ECHOKE | IEXTEN | NOKERNINFO,
> .c_cc = INIT_C_CC,
> .c_ispeed = 38400,
> .c_ospeed = 38400,
> diff --git a/include/linux/tty.h b/include/linux/tty.h
> index 5dbd7c5afac7..e6ba6ce2efcb 100644
> --- a/include/linux/tty.h
> +++ b/include/linux/tty.h
> @@ -49,71 +49,73 @@
> #define WERASE_CHAR(tty) ((tty)->termios.c_cc[VWERASE])
> #define LNEXT_CHAR(tty) ((tty)->termios.c_cc[VLNEXT])
> #define EOL2_CHAR(tty) ((tty)->termios.c_cc[VEOL2])
> +#define STATUS_CHAR(tty) ((tty)->termios.c_cc[VSTATUS])
>
> #define _I_FLAG(tty, f) ((tty)->termios.c_iflag & (f))
> #define _O_FLAG(tty, f) ((tty)->termios.c_oflag & (f))
> #define _C_FLAG(tty, f) ((tty)->termios.c_cflag & (f))
> #define _L_FLAG(tty, f) ((tty)->termios.c_lflag & (f))
>
> -#define I_IGNBRK(tty) _I_FLAG((tty), IGNBRK)
> -#define I_BRKINT(tty) _I_FLAG((tty), BRKINT)
> -#define I_IGNPAR(tty) _I_FLAG((tty), IGNPAR)
> -#define I_PARMRK(tty) _I_FLAG((tty), PARMRK)
> -#define I_INPCK(tty) _I_FLAG((tty), INPCK)
> -#define I_ISTRIP(tty) _I_FLAG((tty), ISTRIP)
> -#define I_INLCR(tty) _I_FLAG((tty), INLCR)
> -#define I_IGNCR(tty) _I_FLAG((tty), IGNCR)
> -#define I_ICRNL(tty) _I_FLAG((tty), ICRNL)
> -#define I_IUCLC(tty) _I_FLAG((tty), IUCLC)
> -#define I_IXON(tty) _I_FLAG((tty), IXON)
> -#define I_IXANY(tty) _I_FLAG((tty), IXANY)
> -#define I_IXOFF(tty) _I_FLAG((tty), IXOFF)
> -#define I_IMAXBEL(tty) _I_FLAG((tty), IMAXBEL)
> -#define I_IUTF8(tty) _I_FLAG((tty), IUTF8)
> -
> -#define O_OPOST(tty) _O_FLAG((tty), OPOST)
> -#define O_OLCUC(tty) _O_FLAG((tty), OLCUC)
> -#define O_ONLCR(tty) _O_FLAG((tty), ONLCR)
> -#define O_OCRNL(tty) _O_FLAG((tty), OCRNL)
> -#define O_ONOCR(tty) _O_FLAG((tty), ONOCR)
> -#define O_ONLRET(tty) _O_FLAG((tty), ONLRET)
> -#define O_OFILL(tty) _O_FLAG((tty), OFILL)
> -#define O_OFDEL(tty) _O_FLAG((tty), OFDEL)
> -#define O_NLDLY(tty) _O_FLAG((tty), NLDLY)
> -#define O_CRDLY(tty) _O_FLAG((tty), CRDLY)
> -#define O_TABDLY(tty) _O_FLAG((tty), TABDLY)
> -#define O_BSDLY(tty) _O_FLAG((tty), BSDLY)
> -#define O_VTDLY(tty) _O_FLAG((tty), VTDLY)
> -#define O_FFDLY(tty) _O_FLAG((tty), FFDLY)
> -
> -#define C_BAUD(tty) _C_FLAG((tty), CBAUD)
> -#define C_CSIZE(tty) _C_FLAG((tty), CSIZE)
> -#define C_CSTOPB(tty) _C_FLAG((tty), CSTOPB)
> -#define C_CREAD(tty) _C_FLAG((tty), CREAD)
> -#define C_PARENB(tty) _C_FLAG((tty), PARENB)
> -#define C_PARODD(tty) _C_FLAG((tty), PARODD)
> -#define C_HUPCL(tty) _C_FLAG((tty), HUPCL)
> -#define C_CLOCAL(tty) _C_FLAG((tty), CLOCAL)
> -#define C_CIBAUD(tty) _C_FLAG((tty), CIBAUD)
> -#define C_CRTSCTS(tty) _C_FLAG((tty), CRTSCTS)
> -#define C_CMSPAR(tty) _C_FLAG((tty), CMSPAR)
> -
> -#define L_ISIG(tty) _L_FLAG((tty), ISIG)
> -#define L_ICANON(tty) _L_FLAG((tty), ICANON)
> -#define L_XCASE(tty) _L_FLAG((tty), XCASE)
> -#define L_ECHO(tty) _L_FLAG((tty), ECHO)
> -#define L_ECHOE(tty) _L_FLAG((tty), ECHOE)
> -#define L_ECHOK(tty) _L_FLAG((tty), ECHOK)
> -#define L_ECHONL(tty) _L_FLAG((tty), ECHONL)
> -#define L_NOFLSH(tty) _L_FLAG((tty), NOFLSH)
> -#define L_TOSTOP(tty) _L_FLAG((tty), TOSTOP)
> -#define L_ECHOCTL(tty) _L_FLAG((tty), ECHOCTL)
> -#define L_ECHOPRT(tty) _L_FLAG((tty), ECHOPRT)
> -#define L_ECHOKE(tty) _L_FLAG((tty), ECHOKE)
> -#define L_FLUSHO(tty) _L_FLAG((tty), FLUSHO)
> -#define L_PENDIN(tty) _L_FLAG((tty), PENDIN)
> -#define L_IEXTEN(tty) _L_FLAG((tty), IEXTEN)
> -#define L_EXTPROC(tty) _L_FLAG((tty), EXTPROC)
> +#define I_IGNBRK(tty) _I_FLAG((tty), IGNBRK)
> +#define I_BRKINT(tty) _I_FLAG((tty), BRKINT)
> +#define I_IGNPAR(tty) _I_FLAG((tty), IGNPAR)
> +#define I_PARMRK(tty) _I_FLAG((tty), PARMRK)
> +#define I_INPCK(tty) _I_FLAG((tty), INPCK)
> +#define I_ISTRIP(tty) _I_FLAG((tty), ISTRIP)
> +#define I_INLCR(tty) _I_FLAG((tty), INLCR)
> +#define I_IGNCR(tty) _I_FLAG((tty), IGNCR)
> +#define I_ICRNL(tty) _I_FLAG((tty), ICRNL)
> +#define I_IUCLC(tty) _I_FLAG((tty), IUCLC)
> +#define I_IXON(tty) _I_FLAG((tty), IXON)
> +#define I_IXANY(tty) _I_FLAG((tty), IXANY)
> +#define I_IXOFF(tty) _I_FLAG((tty), IXOFF)
> +#define I_IMAXBEL(tty) _I_FLAG((tty), IMAXBEL)
> +#define I_IUTF8(tty) _I_FLAG((tty), IUTF8)
> +
> +#define O_OPOST(tty) _O_FLAG((tty), OPOST)
> +#define O_OLCUC(tty) _O_FLAG((tty), OLCUC)
> +#define O_ONLCR(tty) _O_FLAG((tty), ONLCR)
> +#define O_OCRNL(tty) _O_FLAG((tty), OCRNL)
> +#define O_ONOCR(tty) _O_FLAG((tty), ONOCR)
> +#define O_ONLRET(tty) _O_FLAG((tty), ONLRET)
> +#define O_OFILL(tty) _O_FLAG((tty), OFILL)
> +#define O_OFDEL(tty) _O_FLAG((tty), OFDEL)
> +#define O_NLDLY(tty) _O_FLAG((tty), NLDLY)
> +#define O_CRDLY(tty) _O_FLAG((tty), CRDLY)
> +#define O_TABDLY(tty) _O_FLAG((tty), TABDLY)
> +#define O_BSDLY(tty) _O_FLAG((tty), BSDLY)
> +#define O_VTDLY(tty) _O_FLAG((tty), VTDLY)
> +#define O_FFDLY(tty) _O_FLAG((tty), FFDLY)
> +
> +#define C_BAUD(tty) _C_FLAG((tty), CBAUD)
> +#define C_CSIZE(tty) _C_FLAG((tty), CSIZE)
> +#define C_CSTOPB(tty) _C_FLAG((tty), CSTOPB)
> +#define C_CREAD(tty) _C_FLAG((tty), CREAD)
> +#define C_PARENB(tty) _C_FLAG((tty), PARENB)
> +#define C_PARODD(tty) _C_FLAG((tty), PARODD)
> +#define C_HUPCL(tty) _C_FLAG((tty), HUPCL)
> +#define C_CLOCAL(tty) _C_FLAG((tty), CLOCAL)
> +#define C_CIBAUD(tty) _C_FLAG((tty), CIBAUD)
> +#define C_CRTSCTS(tty) _C_FLAG((tty), CRTSCTS)
> +#define C_CMSPAR(tty) _C_FLAG((tty), CMSPAR)
> +
> +#define L_ISIG(tty) _L_FLAG((tty), ISIG)
> +#define L_ICANON(tty) _L_FLAG((tty), ICANON)
> +#define L_XCASE(tty) _L_FLAG((tty), XCASE)
> +#define L_ECHO(tty) _L_FLAG((tty), ECHO)
> +#define L_ECHOE(tty) _L_FLAG((tty), ECHOE)
> +#define L_ECHOK(tty) _L_FLAG((tty), ECHOK)
> +#define L_ECHONL(tty) _L_FLAG((tty), ECHONL)
> +#define L_NOFLSH(tty) _L_FLAG((tty), NOFLSH)
> +#define L_TOSTOP(tty) _L_FLAG((tty), TOSTOP)
> +#define L_ECHOCTL(tty) _L_FLAG((tty), ECHOCTL)
> +#define L_ECHOPRT(tty) _L_FLAG((tty), ECHOPRT)
> +#define L_ECHOKE(tty) _L_FLAG((tty), ECHOKE)
> +#define L_FLUSHO(tty) _L_FLAG((tty), FLUSHO)
> +#define L_PENDIN(tty) _L_FLAG((tty), PENDIN)
> +#define L_IEXTEN(tty) _L_FLAG((tty), IEXTEN)
> +#define L_EXTPROC(tty) _L_FLAG((tty), EXTPROC)
> +#define L_NOKERNINFO(tty) _L_FLAG((tty), NOKERNINFO)

Huh, no. Don't do this in this patch. It's unclear what you are actually
doing here -- it's lost in all those whitespace or whatnot changes.

> struct device;
> struct signal_struct;
> @@ -388,6 +390,9 @@ void __init n_tty_init(void);
> static inline void n_tty_init(void) { }
> #endif
>
> +/* n_tty_status.c */
> +extern int n_tty_get_status(struct tty_struct *tty, char *msg, size_t *msglen);

Don't put any externs here to be consistent with the rest of the file.

> /* tty_audit.c */
> #ifdef CONFIG_AUDIT
> void tty_audit_exit(void);

thanks,
--
js
suse labs

2022-01-20 08:34:35

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH 3/3] status: Display an informational message when the VSTATUS character is pressed or TIOCSTAT ioctl is called.

On Tue, Jan 18, 2022 at 11:29:54AM +0100, Jiri Slaby wrote:

> > +static const char * const task_state_array[] = {
> > + "running",
> > + "sleeping",
> > + "disk sleep",
> > + "stopped",
> > + "tracing stop",
> > + "dead",
> > + "zombie",
> > + "parked",
> > + "idle",
> > +};

Please no; don't add yet another one of these things. Can't you use the
one in fs/proc/array.c ?


> > +static inline struct task_struct *compare(struct task_struct *new,
> > + struct task_struct *old)
> > +{
> > + unsigned int ostate, nstate;
> > +
> > + if (old == NULL)
> > + return new;
> > +
> > + ostate = task_state_index(old);
> > + nstate = task_state_index(new);
> > +
> > + if (ostate == nstate) {

That's not an ordered set, please don't do that.

> > + if (old->start_time > new->start_time)
> > + return old;
> > + return new;
> > + }
> > +
> > + if (ostate < nstate)
> > + return old;
> > +
> > + return new;
> > +}

> > +static inline const char *get_task_state_name(struct task_struct *tsk)
>
> This definitely doesn't belong here. How do you ensure it matches the
> returned index also in the future. Put it along with task_index_to_char()?
> Or simply use task_state_to_char()?
>
> > +{
> > +
> > + int index;
> > +
> > + index = task_state_index(tsk);
> > + if (index > ARRAY_SIZE(task_state_array))
>
> Should be >=, or?
>
> > + return task_state_unknown;
> > + return task_state_array[index];
> > +}

*groan*.. that's very bad copy paste from fs/proc/array.c. There at east
there's a BUILD_BUG_ON() to make sure things are good.

> > +
> > +int n_tty_get_status(struct tty_struct *tty, char *msg, size_t *msglen)
> > +{
> > + unsigned long loadavg[3];
> > + uint64_t pcpu, cputime, wallclock;
> > + struct task_struct *p;
> > + struct rusage rusage;
> > + struct timespec64 utime, stime, rtime;
> > + char tname[TASK_COMM_LEN];
>
> How much stack did you consume in sum with its caller n_tty_status()?
>
> > + size_t len;
> > +
> > + if (tty == NULL)
> > + return -ENOTTY;
>
> How can this happen?
>
> > + get_avenrun(loadavg, FIXED_1/200, 0);
> > + len = scnprintf(msg + len, *msglen - len, "load: %lu.%02lu ",
> > + LOAD_INT(loadavg[0]), LOAD_FRAC(loadavg[0]));
> > +
> > + if (tty->ctrl.session == NULL) {
> > + len += scnprintf(msg + len, *msglen - len,
> > + "not a controlling terminal\n");
> > + goto out;
> > + }
> > +
> > + if (tty->ctrl.pgrp == NULL) {
> > + len += scnprintf(msg + len, *msglen - len,
> > + "no foreground process group\n");
> > + goto out;
> > + }
> > +
> > + p = pick_process(tty->ctrl.pgrp);
>
> Why is no lock needed?
>
> > + if (p == NULL) {
> > + len += scnprintf(msg + len, *msglen - len,
> > + "empty foreground process group\n");
> > + goto out;
> > + }
> > +
> > + get_task_comm(tname, p);
> > + getrusage(p, RUSAGE_BOTH, &rusage);
> > + wallclock = ktime_get_ns() - p->start_time;
> > +
> > + utime.tv_sec = rusage.ru_utime.tv_sec;
> > + utime.tv_nsec = rusage.ru_utime.tv_usec * NSEC_PER_USEC;
> > + stime.tv_sec = rusage.ru_stime.tv_sec;
> > + stime.tv_nsec = rusage.ru_stime.tv_usec * NSEC_PER_USEC;
> > + rtime = ns_to_timespec64(wallclock);
> > +
> > + cputime = timespec64_to_ns(&utime) + timespec64_to_ns(&stime);
> > + pcpu = div64_u64(cputime * 100, wallclock);

How is this number useful?

> > +
> > + len += scnprintf(msg + len, *msglen - len,
> > + /* task, PID, task state */
> > + "cmd: %s %d [%s] "
> > + /* rtime, utime, stime, %cpu, rss */
> > + "%llu.%02lur %llu.%02luu %llu.%02lus %llu%% %luk\n",
> > + tname, __get_pid(p, tty),
> > + (char *)get_task_state_name(p),
> > + rtime.tv_sec, frac_sec(rtime.tv_nsec),
> > + utime.tv_sec, frac_sec(utime.tv_nsec),
> > + stime.tv_sec, frac_sec(stime.tv_nsec),
> > + pcpu, getRSSk(p->mm));
>
> Why do you think p->mm is still alive (even after the getRSSk()'s check)? So
> no get_task_mm() needed?
>
> > +out:
> > + *msglen = len;
> > + return 0;
> > +}

Re lack of refcounting and locking, perhaps he's attemting a root hole?

2022-01-20 12:46:45

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH 3/3] status: Display an informational message when the VSTATUS character is pressed or TIOCSTAT ioctl is called.

On Tue, Jan 18, 2022 at 12:37:03PM +0100, Peter Zijlstra wrote:
> > > +static inline struct task_struct *compare(struct task_struct *new,
> > > + struct task_struct *old)
> > > +{
> > > + unsigned int ostate, nstate;
> > > +
> > > + if (old == NULL)
> > > + return new;
> > > +
> > > + ostate = task_state_index(old);
> > > + nstate = task_state_index(new);
> > > +
> > > + if (ostate == nstate) {
>
> That's not an ordered set, please don't do that.

*sigh*.. sorry about that, I can't read, for some reason I thought you
did: ostate < nstate...

> > > + if (old->start_time > new->start_time)
> > > + return old;
> > > + return new;
> > > + }
> > > +
> > > + if (ostate < nstate)
> > > + return old;
> > > +
> > > + return new;
> > > +}