All of lore.kernel.org
 help / color / mirror / Atom feed
* [LTP] [PATCH v3] pty: Test data transmission with SLIP line discipline
@ 2020-03-06 12:00 Richard Palethorpe
  2020-03-06 13:20 ` Petr Vorel
  0 siblings, 1 reply; 3+ messages in thread
From: Richard Palethorpe @ 2020-03-06 12:00 UTC (permalink / raw)
  To: ltp

Basic transmission test and try to trigger a possible race between receiving
and hangup (it appears there is none from my testing).

This only includes SLIP for now, but more disciplines can be added. Probably
at least CAN. However the packet generation will be different for each.

Signed-off-by: Richard Palethorpe <rpalethorpe@suse.com>
---

V3:
* Use linux/tty.h

 runtest/pty                     |   1 +
 testcases/kernel/pty/.gitignore |   1 +
 testcases/kernel/pty/pty04.c    | 279 ++++++++++++++++++++++++++++++++
 3 files changed, 281 insertions(+)
 create mode 100644 testcases/kernel/pty/pty04.c

diff --git a/runtest/pty b/runtest/pty
index e484da0ff..5587312d3 100644
--- a/runtest/pty
+++ b/runtest/pty
@@ -2,6 +2,7 @@
 pty01 pty01
 pty02 pty02
 pty03 pty03
+pty04 pty04
 ptem01 ptem01
 hangup01 hangup01
 
diff --git a/testcases/kernel/pty/.gitignore b/testcases/kernel/pty/.gitignore
index 5b4f6a8c9..c67d723d2 100644
--- a/testcases/kernel/pty/.gitignore
+++ b/testcases/kernel/pty/.gitignore
@@ -3,3 +3,4 @@
 /pty01
 /pty02
 /pty03
+/pty04
diff --git a/testcases/kernel/pty/pty04.c b/testcases/kernel/pty/pty04.c
new file mode 100644
index 000000000..8db2be229
--- /dev/null
+++ b/testcases/kernel/pty/pty04.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020 SUSE
+ *
+ * Test transmitting data over a PTY/TTY line discipline and reading from the
+ * virtual netdev created by the line discipline. Also hangup the PTY while
+ * data is in flight to try to cause a race between the netdev being deleted
+ * and the discipline receive function writing to the netdev.
+ *
+ * Test flow:
+ * 1. Create PTY with ldisc X which creates netdev Y
+ * 2. Open raw packet socket and bind to netdev Y
+ * 3. Send data on ptmx and read packets from socket
+ * 4. Hangup while transmission in progress
+ *
+ * Note that not all line disciplines call unthrottle when they are ready to
+ * read more bytes. So it is possible to fill all the write buffers causing
+ * write to block forever (because once write sleeps it needs unthrottle to
+ * wake it). So we write with O_NONBLOCK.
+ *
+ * Also the max buffer size for PTYs is 8192, so even if the protocol MTU is
+ * greater everything may still be processed in 8129 byte chunks. At least
+ * until we are in the netdev code which can have a bigger buffer. Of course
+ * the MTU still decides exactly where the packet delimiter goes, this just
+ * concerns choosing the best packet size to cause a race.
+ */
+
+#define _GNU_SOURCE
+#include "tst_test.h"
+#include "config.h"
+
+#if defined(HAVE_LINUX_IF_PACKET_H) && defined(HAVE_LINUX_IF_ETHER_H)
+
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#include <linux/tty.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include "lapi/ioctl.h"
+
+#include "tst_safe_stdio.h"
+
+struct ldisc_info {
+	int n;
+	char *name;
+	int max_mtu;
+};
+
+static struct ldisc_info ldiscs[] = {
+	{N_SLIP, "N_SLIP", 8192},
+};
+
+static volatile int ptmx, pts, sk, mtu, no_check;
+
+static int set_ldisc(int tty, struct ldisc_info *ldisc)
+{
+	TEST(ioctl(tty, TIOCSETD, &ldisc->n));
+
+	if (!TST_RET)
+		return 0;
+
+	if (TST_ERR == EINVAL) {
+		tst_res(TCONF | TTERRNO,
+			"You don't appear to have the %s TTY line discipline",
+			ldisc->name);
+	} else {
+		tst_res(TBROK | TTERRNO,
+			"Failed to set the %s line discipline", ldisc->name);
+	}
+
+	return 1;
+}
+
+static void open_pty(struct ldisc_info *ldisc)
+{
+	char pts_path[PATH_MAX];
+
+	ptmx = SAFE_OPEN("/dev/ptmx", O_RDWR);
+	if (grantpt(ptmx))
+		tst_brk(TBROK | TERRNO, "grantpt(ptmx)");
+	if (unlockpt(ptmx))
+		tst_brk(TBROK | TERRNO, "unlockpt(ptmx)");
+	if (ptsname_r(ptmx, pts_path, sizeof(pts_path)))
+		tst_brk(TBROK | TERRNO, "ptsname_r(ptmx, ...)");
+
+	SAFE_FCNTL(ptmx, F_SETFL, O_NONBLOCK);
+
+	tst_res(TINFO, "PTS path is %s", pts_path);
+	pts = SAFE_OPEN(pts_path, O_RDWR);
+
+	set_ldisc(pts, ldisc);
+}
+
+static ssize_t try_write(int fd, char *data, ssize_t size, ssize_t *written)
+{
+	ssize_t ret = write(fd, data, size);
+
+	if (ret < 0)
+		return -(errno != EAGAIN);
+
+	return !written || (*written += ret) >= size;
+}
+
+static void write_pty(void)
+{
+	char *data = SAFE_MALLOC(mtu);
+	ssize_t written, ret;
+
+	memset(data, '_', mtu - 1);
+	data[mtu - 1] = 0300;
+
+	written = 0;
+	ret = TST_RETRY_FUNC(try_write(ptmx, data, mtu, &written), TST_RETVAL_NOTNULL);
+	if (ret < 0)
+		tst_brk(TBROK | TERRNO, "Failed 1st write to PTY");
+	tst_res(TPASS, "Wrote PTY 1");
+
+	written = 0;
+	ret = TST_RETRY_FUNC(try_write(ptmx, data, mtu, &written), TST_RETVAL_NOTNULL);
+	if (ret < 0)
+		tst_brk(TBROK | TERRNO, "Failed 2nd write to PTY");
+
+	if (tcflush(ptmx, TCIFLUSH))
+		tst_brk(TBROK | TERRNO, "tcflush(ptmx, TCIFLUSH)");
+
+	tst_res(TPASS, "Wrote PTY 2");
+
+	while (try_write(ptmx, data, mtu, NULL) >= 0)
+		;
+
+	tst_res(TPASS, "Writing to PTY interrupted by hangup");
+
+	free(data);
+}
+
+static void open_netdev(struct ldisc_info *ldisc)
+{
+	struct ifreq ifreq = { 0 };
+	struct sockaddr_ll lla = { 0 };
+
+	SAFE_IOCTL(pts, SIOCGIFNAME, ifreq.ifr_name);
+	tst_res(TINFO, "Netdev is %s", ifreq.ifr_name);
+
+	sk = SAFE_SOCKET(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+
+	ifreq.ifr_mtu = ldisc->max_mtu;
+	if (ioctl(sk, SIOCSIFMTU, &ifreq))
+		tst_res(TWARN | TERRNO, "Failed to set netdev MTU to maximum");
+	SAFE_IOCTL(sk, SIOCGIFMTU, &ifreq);
+	mtu = ifreq.ifr_mtu;
+	tst_res(TINFO, "Netdev MTU is %d (we set %d)", mtu, ldisc->max_mtu);
+
+	SAFE_IOCTL(sk, SIOCGIFFLAGS, &ifreq);
+	ifreq.ifr_flags |= IFF_UP | IFF_RUNNING;
+	SAFE_IOCTL(sk, SIOCSIFFLAGS, &ifreq);
+	SAFE_IOCTL(sk, SIOCGIFFLAGS, &ifreq);
+
+	if (!(ifreq.ifr_flags & IFF_UP))
+		tst_brk(TBROK, "Netdev did not come up");
+
+	SAFE_IOCTL(sk, SIOCGIFINDEX, &ifreq);
+
+	lla.sll_family = PF_PACKET;
+	lla.sll_protocol = htons(ETH_P_ALL);
+	lla.sll_ifindex = ifreq.ifr_ifindex;
+	SAFE_BIND(sk, (struct sockaddr *)&lla, sizeof(struct sockaddr_ll));
+
+	tst_res(TINFO, "Bound netdev %d to socket %d", ifreq.ifr_ifindex, sk);
+}
+
+static void check_data(const char *data, ssize_t len)
+{
+	ssize_t i = 0, j;
+
+	if (no_check)
+		return;
+
+	do {
+		if (i >= len)
+			return;
+	} while (data[i++] == '_');
+
+	j = i--;
+
+	while (j < len && j - i < 65 && data[j++] != '_')
+		;
+	j--;
+
+	tst_res_hexd(TFAIL, data + i, j - i,
+		     "Corrupt data (max 64 bytes shown): data[%ld..%ld] = ",
+		     i, j);
+	tst_res(TINFO, "Will continue test without data checking");
+	no_check = 1;
+}
+
+static void read_netdev(void)
+{
+	int rlen, plen = mtu - 1;
+	char *data = SAFE_MALLOC(plen);
+
+	tst_res(TINFO, "Reading from socket %d", sk);
+
+	SAFE_READ(1, sk, data, plen);
+	check_data(data, plen);
+
+	tst_res(TPASS, "Read netdev 1");
+	SAFE_READ(1, sk, data, plen);
+	check_data(data, plen);
+
+	tst_res(TPASS, "Read netdev 2");
+
+	TST_CHECKPOINT_WAKE(0);
+	while((rlen = read(sk, data, plen)) > 0)
+		check_data(data, rlen);
+
+	tst_res(TPASS, "Reading data from netdev interrupted by hangup");
+
+	free(data);
+}
+
+static void do_test(unsigned int n)
+{
+	struct ldisc_info *ldisc = &ldiscs[n];
+
+	open_pty(ldisc);
+	open_netdev(ldisc);
+
+	if (!SAFE_FORK()) {
+		read_netdev();
+		return;
+	}
+
+	if (!SAFE_FORK()) {
+		write_pty();
+		return;
+	}
+
+	if (!SAFE_FORK()) {
+		TST_CHECKPOINT_WAIT(0);
+		SAFE_IOCTL(pts, TIOCVHANGUP);
+		tst_res(TINFO, "Sent hangup ioctl to PTS");
+		SAFE_IOCTL(ptmx, TIOCVHANGUP);
+		tst_res(TINFO, "Sent hangup ioctl to PTM");
+		return;
+	}
+
+	tst_reap_children();
+}
+
+static void cleanup(void)
+{
+	ioctl(pts, TIOCVHANGUP);
+	ioctl(ptmx, TIOCVHANGUP);
+
+	tst_reap_children();
+}
+
+static struct tst_test test = {
+	.test = do_test,
+	.cleanup = cleanup,
+	.tcnt = 1,
+	.forks_child = 1,
+	.needs_checkpoints = 1,
+	.needs_root = 1,
+	.min_kver = "4.10"
+};
+
+#else
+
+TST_TEST_TCONF("Need <linux/if_packet.h> and <linux/if_ether.h>");
+
+#endif
-- 
2.24.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [LTP] [PATCH v3] pty: Test data transmission with SLIP line discipline
  2020-03-06 12:00 [LTP] [PATCH v3] pty: Test data transmission with SLIP line discipline Richard Palethorpe
@ 2020-03-06 13:20 ` Petr Vorel
  2020-03-06 13:45   ` Richard Palethorpe
  0 siblings, 1 reply; 3+ messages in thread
From: Petr Vorel @ 2020-03-06 13:20 UTC (permalink / raw)
  To: ltp

Hi Richard,

...
> +static int set_ldisc(int tty, struct ldisc_info *ldisc)
> +{
> +	TEST(ioctl(tty, TIOCSETD, &ldisc->n));
> +
> +	if (!TST_RET)
> +		return 0;
> +
> +	if (TST_ERR == EINVAL) {
> +		tst_res(TCONF | TTERRNO,
> +			"You don't appear to have the %s TTY line discipline",
> +			ldisc->name);
> +	} else {
> +		tst_res(TBROK | TTERRNO,
This must be TFAIL now.
This can be done by person who merges this patch.

BTW as the same function (with corrected TBROK) is
already in pty03.c (although struct ldisc_info is different).

Kind regards,
Petr

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [LTP] [PATCH v3] pty: Test data transmission with SLIP line discipline
  2020-03-06 13:20 ` Petr Vorel
@ 2020-03-06 13:45   ` Richard Palethorpe
  0 siblings, 0 replies; 3+ messages in thread
From: Richard Palethorpe @ 2020-03-06 13:45 UTC (permalink / raw)
  To: ltp

Hi,

Petr Vorel <pvorel@suse.cz> writes:

> Hi Richard,
>
> ...
>> +static int set_ldisc(int tty, struct ldisc_info *ldisc)
>> +{
>> +	TEST(ioctl(tty, TIOCSETD, &ldisc->n));
>> +
>> +	if (!TST_RET)
>> +		return 0;
>> +
>> +	if (TST_ERR == EINVAL) {
>> +		tst_res(TCONF | TTERRNO,
>> +			"You don't appear to have the %s TTY line discipline",
>> +			ldisc->name);
>> +	} else {
>> +		tst_res(TBROK | TTERRNO,
> This must be TFAIL now.
> This can be done by person who merges this patch.
>
> BTW as the same function (with corrected TBROK) is
> already in pty03.c (although struct ldisc_info is different).

HHmm, I suppose this also means I should also either check the return
value in open_pty or use tst_brk.


>
> Kind regards,
> Petr


-- 
Thank you,
Richard.

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-03-06 13:45 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-06 12:00 [LTP] [PATCH v3] pty: Test data transmission with SLIP line discipline Richard Palethorpe
2020-03-06 13:20 ` Petr Vorel
2020-03-06 13:45   ` Richard Palethorpe

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.