mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp
@ 2022-08-30  7:37 Enrico Scholz
  2022-08-30  7:37 ` [PATCH v4 01/21] tftp: add some 'const' annotations Enrico Scholz
                   ` (21 more replies)
  0 siblings, 22 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:37 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

The tftp "windowsize" greatly improves the performance of tftp
transfers.  This patchset adds support for it.

The first two patches are a little bit unrelated and enhance the 'cp
-v' output by giving information about the transfer speed.  They can
be dropped if they are unwanted.

I tested the function with an iMX8MP platform in three environments:

  - at home over OpenVPN on an ADSL 50 line  -->  27x speedup
  - 1 Gb/s connection --> 9x speedup
  - connection over 100 Mb/s switch  -->  4x speedup

In the test, I downloaded variable sized files which were filled from
/dev/urandom.  E.g.

| :/ global tftp.windowsize=128
| :/ cp -v /mnt/tftp/data-100MiB /tmp/data && sha1sum /tmp/data
|         [################################################################] 104857600 bytes, 98550375 bytes/s

For slow connection speeds, smaller files (1MiB, 4 MiB + 20 MiB) were
used.

The numbers (bytes/s) are

 | windowsize | VPN       | 1 Gb/s     | 100 Mb/s   |
 |------------|-----------|------------|------------|
 | 128        | 3.869.284 | 98.643.085 | 11.434.852 |
 |  64        | 3.863.581 | 98.550.375 | 11.434.852 |
 |  48        | 3.431.580 | 94.211.680 | 11.275.010 |
 |  32        | 2.835.129 | 85.250.081 | 10.985.605 |
 |  24        | 2.344.858 | 77.787.537 | 10.765.667 |
 |  16        | 1.734.186 | 67.519.381 | 10.210.087 |
 |  12        | 1.403.340 | 61.972.576 |  9.915.612 |
 |   8        | 1.002.462 | 50.852.376 |  9.016.130 |
 |   6        |   775.573 | 42.781.558 |  8.422.297 |
 |   4        |   547.845 | 32.066.544 |  6.835.567 |
 |   3        |   412.987 | 26.526.081 |  6.322.435 |
 |   2        |   280.987 | 19.120.641 |  5.494.241 |
 |   1        |   141.699 | 10.431.516 |  2.967.224 |
 |------------|-----------|------------|------------|
 | unpatched  |   140.587 | 10.553.301 |  2.978.063 |


Patchset has been tested with

| for i in data-0 data-100B data-1KiB data-1432B data-64KiB data-1MiB data-4MiB; do
|     tftp    "$i"
|     tftp -p "$i"
| done

against tftp servers with and without rfc 2747 support (OACK).


The window size related parts of the patchset (with deactivated
selftest) increase the barebox binary size by

| add/remove: 6/0 grow/shrink: 7/2 up/down: 1572/-32 (1540)
| Function                                     old     new   delta
| tftp_handler                                 756    1324    +568
| tftp_allocate_transfer                         -     196    +196
| tftp_put_data                                  -     184    +184
| tftp_window_cache_remove                       -     124    +124
| tftp_window_cache_get_pos                      -     120    +120
| tftp_send                                    296     412    +116
| tftp_do_open                                 428     512     +84
| tftp_states                                    -      72     +72
| tftp_do_close                                260     312     +52
| tftp_init                                     16      60     +44
| tftp_open                                     64      68      +4
| tftp_lookup                                  136     140      +4
| g_tftp_window_size                             -       4      +4
| tftp_read                                    180     164     -16
| tftp_poll                                    180     164     -16
| Total: Before=629556, After=631096, chg +0.24%

Turning of the datagram cache (CONFIG_FS_TFTP_REORDER_CACHE_SIZE=0)
reduces the overhead to

| add/remove: 3/0 grow/shrink: 6/2 up/down: 808/-32 (776)
| Function                                     old     new   delta
| tftp_handler                                 756    1092    +336
| tftp_allocate_transfer                         -     144    +144
| tftp_send                                    296     412    +116
| tftp_do_open                                 428     512     +84
| tftp_states                                    -      72     +72
| tftp_init                                     16      60     +44
| tftp_open                                     64      68      +4
| tftp_lookup                                  136     140      +4
| g_tftp_window_size                             -       4      +4
| tftp_read                                    180     164     -16
| tftp_poll                                    180     164     -16
| Total: Before=629556, After=630332, chg +0.12%

Restoring the old behaviour by CONFIG_FS_TFTP_MAX_WINDOW_SIZE=1 shows
an overhead of

| add/remove: 3/0 grow/shrink: 6/2 up/down: 720/-32 (688)
| Function                                     old     new   delta
| tftp_handler                                 756    1088    +332
| tftp_allocate_transfer                         -     144    +144
| tftp_do_open                                 428     512     +84
| tftp_states                                    -      72     +72
| tftp_init                                     16      60     +44
| tftp_send                                    296     328     +32
| tftp_open                                     64      68      +4
| tftp_lookup                                  136     140      +4
| g_tftp_window_size                             -       4      +4
| tftp_read                                    180     164     -16
| tftp_poll                                    180     164     -16
| Total: Before=629556, After=630244, chg +0.11%


Enrico Scholz (21):
  tftp: add some 'const' annotations
  tftp: allow to change tftp port
  cmd:tftp: add '-P' option to set tftp server port number
  tftp: do not set 'tsize' in WRQ requests
  tftp: assign 'priv->block' later in WRQ
  tftp: minor refactoring of RRQ/WRQ packet generation code
  tftp: replace hardcoded blksize by global constant
  tftp: remove sanity check of first block
  tftp: add debug_assert() macro
  tftp: allocate buffers and fifo dynamically
  tftp: add sanity check for OACK response
  tftp: record whether tftp file is opened for lookup operation only
  tftp: reduce block size on lookup requests
  tftp: refactor data processing
  tftp: detect out-of-memory situations
  tftp: implement 'windowsize' (RFC 7440) support
  tftp: do not use 'priv->block' for RRQ
  tftp: reorder tftp packets
  tftp: add selftest
  tftp: accept OACK + DATA datagrams only in certain states
  tftp: add some documentation about windowsize support

 Documentation/filesystems/tftp.rst |  38 ++
 commands/tftp.c                    |  22 +-
 fs/Kconfig                         |  36 ++
 fs/tftp-selftest.h                 |  56 +++
 fs/tftp.c                          | 763 +++++++++++++++++++++++++----
 test/self/Kconfig                  |   7 +
 6 files changed, 824 insertions(+), 98 deletions(-)
 create mode 100644 fs/tftp-selftest.h

---
v3 -> v4
  - fix operation with non rfc 2347 servers
  - do not send 'tsize' in WRQ requests
  - add more sanity checks
  - add some documentation

v2 -> v3
  - use "port=XX" mount options instead of global 'tftp.port' variable
  - allocate fifo and send buffer dynamically based on block- and
    window size of the transfer.  Do not use fixed constants anymore
  - rewritten cache code; use bitmap based functions with O(1)
    complexity instead of iterating over (small) arrays
  - unittest for cache functions
  - add information about binary sizes

v1 -> v2
  - fixes for non rfc7440 servers

-- 
2.37.2




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

* [PATCH v4 01/21] tftp: add some 'const' annotations
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
@ 2022-08-30  7:37 ` Enrico Scholz
  2022-08-30  7:37 ` [PATCH v4 02/21] tftp: allow to change tftp port Enrico Scholz
                   ` (20 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:37 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index d186e7983a6d..c26204ae76e6 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -94,7 +94,7 @@ static int tftp_truncate(struct device_d *dev, FILE *f, loff_t size)
 	return 0;
 }
 
-static char *tftp_states[] = {
+static char const * const tftp_states[] = {
 	[STATE_INVALID] = "INVALID",
 	[STATE_RRQ] = "RRQ",
 	[STATE_WRQ] = "WRQ",
-- 
2.37.2




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

* [PATCH v4 02/21] tftp: allow to change tftp port
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
  2022-08-30  7:37 ` [PATCH v4 01/21] tftp: add some 'const' annotations Enrico Scholz
@ 2022-08-30  7:37 ` Enrico Scholz
  2022-08-30  7:37 ` [PATCH v4 03/21] cmd:tftp: add '-P' option to set tftp server port number Enrico Scholz
                   ` (19 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:37 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

This adds a 'port=<port>' mount option for tftp filesystems.

Useful e.g. when working with a local, non-privileged tftp server

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index c26204ae76e6..913ca1df6e42 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -30,6 +30,7 @@
 #include <linux/stat.h>
 #include <linux/err.h>
 #include <kfifo.h>
+#include <parseopt.h>
 #include <linux/sizes.h>
 
 #define TFTP_PORT	69	/* Well known TFTP port number */
@@ -376,6 +377,7 @@ static struct file_priv *tftp_do_open(struct device_d *dev,
 	struct file_priv *priv;
 	struct tftp_priv *tpriv = dev->priv;
 	int ret;
+	unsigned short port = TFTP_PORT;
 
 	priv = xzalloc(sizeof(*priv));
 
@@ -405,8 +407,9 @@ static struct file_priv *tftp_do_open(struct device_d *dev,
 		goto out;
 	}
 
-	priv->tftp_con = net_udp_new(tpriv->server, TFTP_PORT, tftp_handler,
-			priv);
+	parseopt_hu(fsdev->options, "port", &port);
+
+	priv->tftp_con = net_udp_new(tpriv->server, port, tftp_handler, priv);
 	if (IS_ERR(priv->tftp_con)) {
 		ret = PTR_ERR(priv->tftp_con);
 		goto out1;
-- 
2.37.2




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

* [PATCH v4 03/21] cmd:tftp: add '-P' option to set tftp server port number
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
  2022-08-30  7:37 ` [PATCH v4 01/21] tftp: add some 'const' annotations Enrico Scholz
  2022-08-30  7:37 ` [PATCH v4 02/21] tftp: allow to change tftp port Enrico Scholz
@ 2022-08-30  7:37 ` Enrico Scholz
  2022-08-30  7:37 ` [PATCH v4 04/21] tftp: do not set 'tsize' in WRQ requests Enrico Scholz
                   ` (18 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:37 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 commands/tftp.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/commands/tftp.c b/commands/tftp.c
index 48ff00c6217c..6ac822c9e832 100644
--- a/commands/tftp.c
+++ b/commands/tftp.c
@@ -21,15 +21,24 @@ static int do_tftpb(int argc, char *argv[])
 	char *source, *dest, *freep;
 	int opt;
 	int tftp_push = 0;
+	int port = -1;
 	int ret;
 	IPaddr_t ip;
 	char ip4_str[sizeof("255.255.255.255")];
+	char mount_opts[sizeof("port=12345")];
 
-	while ((opt = getopt(argc, argv, "p")) > 0) {
+	while ((opt = getopt(argc, argv, "pP:")) > 0) {
 		switch(opt) {
 		case 'p':
 			tftp_push = 1;
 			break;
+		case 'P':
+			port = simple_strtoul(optarg, NULL, 0);
+			if (port <= 0 || port > 0xffff) {
+				pr_err("invalid port '%s'\n", optarg);
+				return COMMAND_ERROR_USAGE;
+			}
+			break;
 		default:
 			return COMMAND_ERROR_USAGE;
 		}
@@ -59,7 +68,13 @@ static int do_tftpb(int argc, char *argv[])
 
 	ip = net_get_serverip();
 	sprintf(ip4_str, "%pI4", &ip);
-	ret = mount(ip4_str, "tftp", TFTP_MOUNT_PATH, NULL);
+
+	if (port >= 0)
+		sprintf(mount_opts, "port=%u", port);
+	else
+		mount_opts[0] = '\0';
+
+	ret = mount(ip4_str, "tftp", TFTP_MOUNT_PATH, mount_opts);
 	if (ret)
 		goto err_rmdir;
 
@@ -84,12 +99,13 @@ BAREBOX_CMD_HELP_TEXT("server address is taken from the environment (ethX.server
 BAREBOX_CMD_HELP_TEXT("")
 BAREBOX_CMD_HELP_TEXT("Options:")
 BAREBOX_CMD_HELP_OPT ("-p", "push to TFTP server")
+BAREBOX_CMD_HELP_OPT ("-P PORT", "tftp server port number")
 BAREBOX_CMD_HELP_END
 
 BAREBOX_CMD_START(tftp)
 	.cmd		= do_tftpb,
 	BAREBOX_CMD_DESC("load (or save) a file using TFTP")
-	BAREBOX_CMD_OPTS("[-p] SOURCE [DEST]")
+	BAREBOX_CMD_OPTS("[-p] [-P <port>] SOURCE [DEST]")
 	BAREBOX_CMD_GROUP(CMD_GRP_NET)
 	BAREBOX_CMD_HELP(cmd_tftp_help)
 BAREBOX_CMD_END
-- 
2.37.2




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

* [PATCH v4 04/21] tftp: do not set 'tsize' in WRQ requests
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (2 preceding siblings ...)
  2022-08-30  7:37 ` [PATCH v4 03/21] cmd:tftp: add '-P' option to set tftp server port number Enrico Scholz
@ 2022-08-30  7:37 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 05/21] tftp: assign 'priv->block' later in WRQ Enrico Scholz
                   ` (17 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:37 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

The filesize is not known for push requests and barebox always sent
'0'.  Server might reject data because it will always exceed this
length.

Send this option only for RRQ requests.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index 913ca1df6e42..361661d2180e 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -132,18 +132,23 @@ static int tftp_send(struct file_priv *priv)
 				"octet%c"
 				"timeout%c"
 				"%d%c"
-				"tsize%c"
-				"%lld%c"
 				"blksize%c"
 				"1432",
 				priv->filename + 1, 0,
 				0,
 				0,
 				TIMEOUT, 0,
-				0,
-				priv->filesize, 0,
 				0);
 		pkt++;
+
+		if (!priv->push)
+			/* we do not know the filesize in WRQ requests and
+			   'priv->filesize' will always be zero */
+			pkt += sprintf((unsigned char *)pkt,
+				       "tsize%c%lld%c",
+				       '\0', priv->filesize,
+				       '\0');
+
 		len = pkt - xp;
 		break;
 
-- 
2.37.2




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

* [PATCH v4 05/21] tftp: assign 'priv->block' later in WRQ
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (3 preceding siblings ...)
  2022-08-30  7:37 ` [PATCH v4 04/21] tftp: do not set 'tsize' in WRQ requests Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 06/21] tftp: minor refactoring of RRQ/WRQ packet generation code Enrico Scholz
                   ` (16 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Some refactoring; makes next patches cleaner.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index 361661d2180e..c1a193711793 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -254,6 +254,7 @@ static void tftp_recv(struct file_priv *priv,
 			uint8_t *pkt, unsigned len, uint16_t uh_sport)
 {
 	uint16_t opcode;
+	uint16_t block;
 
 	/* according to RFC1350 minimal tftp packet length is 4 bytes */
 	if (len < 4)
@@ -276,14 +277,13 @@ static void tftp_recv(struct file_priv *priv,
 		if (!priv->push)
 			break;
 
-		priv->block = ntohs(*(uint16_t *)pkt);
-		if (priv->block != priv->last_block) {
-			pr_vdebug("ack %d != %d\n", priv->block, priv->last_block);
+		block = ntohs(*(uint16_t *)pkt);
+		if (block != priv->last_block) {
+			pr_vdebug("ack %d != %d\n", block, priv->last_block);
 			break;
 		}
 
-		priv->block++;
-
+		priv->block = block + 1;
 		tftp_timer_reset(priv);
 
 		if (priv->state == STATE_LAST) {
-- 
2.37.2




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

* [PATCH v4 06/21] tftp: minor refactoring of RRQ/WRQ packet generation code
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (4 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 05/21] tftp: assign 'priv->block' later in WRQ Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 07/21] tftp: replace hardcoded blksize by global constant Enrico Scholz
                   ` (15 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Having 11 printf arguments with lot of them being 0, makes it
difficulty to read and extend.

Add some comments and use '\0' for %c.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index c1a193711793..0b2a420b6639 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -134,11 +134,11 @@ static int tftp_send(struct file_priv *priv)
 				"%d%c"
 				"blksize%c"
 				"1432",
-				priv->filename + 1, 0,
-				0,
-				0,
-				TIMEOUT, 0,
-				0);
+				priv->filename + 1, '\0',
+				'\0',	/* "octet" */
+				'\0',	/* "timeout" */
+				TIMEOUT, '\0',
+				'\0');	/* "blksize" */
 		pkt++;
 
 		if (!priv->push)
-- 
2.37.2




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

* [PATCH v4 07/21] tftp: replace hardcoded blksize by global constant
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (5 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 06/21] tftp: minor refactoring of RRQ/WRQ packet generation code Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 08/21] tftp: remove sanity check of first block Enrico Scholz
                   ` (14 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index 0b2a420b6639..264841f24407 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -65,6 +65,7 @@
 #define STATE_DONE	8
 
 #define TFTP_BLOCK_SIZE		512	/* default TFTP block size */
+#define TFTP_MTU_SIZE		1432	/* MTU based block size */
 #define TFTP_FIFO_SIZE		4096
 
 #define TFTP_ERR_RESEND	1
@@ -133,12 +134,13 @@ static int tftp_send(struct file_priv *priv)
 				"timeout%c"
 				"%d%c"
 				"blksize%c"
-				"1432",
+				"%u",
 				priv->filename + 1, '\0',
 				'\0',	/* "octet" */
 				'\0',	/* "timeout" */
 				TIMEOUT, '\0',
-				'\0');	/* "blksize" */
+				'\0',	/* "blksize" */
+				TFTP_MTU_SIZE);
 		pkt++;
 
 		if (!priv->push)
-- 
2.37.2




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

* [PATCH v4 08/21] tftp: remove sanity check of first block
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (6 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 07/21] tftp: replace hardcoded blksize by global constant Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 09/21] tftp: add debug_assert() macro Enrico Scholz
                   ` (13 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

With tftp window size support in the next patches, the first received
block might be !=1 (e.g. when it was reordered or dropped).  There could
be checked whether it is in the first window, but the corresponding
sanity check can be dropped completely:

- OACK logic verifies that we speak with a tftp server (which always
  sends block #1 as the first one).  Diagnostic will help only with non
  rfc 2743 servers (which are probably very rare resp. non existing
  nowadays)

- the code some lines later handles this case already

Remove the check and simplify things.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index 264841f24407..51cb1109d2ff 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -321,14 +321,6 @@ static void tftp_recv(struct file_priv *priv,
 			priv->state = STATE_RDATA;
 			priv->tftp_con->udp->uh_dport = uh_sport;
 			priv->last_block = 0;
-
-			if (priv->block != 1) {	/* Assertion */
-				pr_err("error: First block is not block 1 (%d)\n",
-					priv->block);
-				priv->err = -EINVAL;
-				priv->state = STATE_DONE;
-				break;
-			}
 		}
 
 		if (priv->block == priv->last_block)
-- 
2.37.2




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

* [PATCH v4 09/21] tftp: add debug_assert() macro
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (7 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 08/21] tftp: remove sanity check of first block Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 10/21] tftp: allocate buffers and fifo dynamically Enrico Scholz
                   ` (12 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Is a noop in normal cases (when compiler sees that the condition can
be evaluated without sideeffects) but allows optimizations based on
the condition.

E.g. in

| void foo(int a)
| {
|     debug_assert(a == 23);
|
|     if (a == 23)
|         return;
|
|     bar();
| }

the call to 'bar()' will be optimized away.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/fs/tftp.c b/fs/tftp.c
index 51cb1109d2ff..07de8334f202 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -70,6 +70,15 @@
 
 #define TFTP_ERR_RESEND	1
 
+#ifdef DEBUG
+#  define debug_assert(_cond)	BUG_ON(!(_cond))
+#else
+#  define debug_assert(_cond) do {			\
+		if (!(_cond))				\
+			__builtin_unreachable();	\
+	} while (0)
+#endif
+
 struct file_priv {
 	struct net_connection *tftp_con;
 	int push;
-- 
2.37.2




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

* [PATCH v4 10/21] tftp: allocate buffers and fifo dynamically
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (8 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 09/21] tftp: add debug_assert() macro Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 11/21] tftp: add sanity check for OACK response Enrico Scholz
                   ` (11 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Use the actual blocksize for allocating buffers instead of assuming an
hardcoded value.

This requires to add an additional 'START' state which is entered
after receiving (RRQ) or sending (WRQ) the OACK.  Without it, the next
state would be entered and the (not allocated yet) fifo be used.

For non-rfc 2347 servers (which do not understand OACK and start with
data transfer immediately after RRQ/WRQ), additional transitions in
the state machine were implemented.

Code adds some sanity checks in the new code paths.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 194 ++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 144 insertions(+), 50 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index 07de8334f202..64a94797cda3 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -59,14 +59,15 @@
 #define STATE_WRQ	2
 #define STATE_RDATA	3
 #define STATE_WDATA	4
-#define STATE_OACK	5
+/* OACK from server has been received and we can begin to sent either the ACK
+   (for RRQ) or data (for WRQ) */
+#define STATE_START	5
 #define STATE_WAITACK	6
 #define STATE_LAST	7
 #define STATE_DONE	8
 
 #define TFTP_BLOCK_SIZE		512	/* default TFTP block size */
 #define TFTP_MTU_SIZE		1432	/* MTU based block size */
-#define TFTP_FIFO_SIZE		4096
 
 #define TFTP_ERR_RESEND	1
 
@@ -111,10 +112,10 @@ static char const * const tftp_states[] = {
 	[STATE_WRQ] = "WRQ",
 	[STATE_RDATA] = "RDATA",
 	[STATE_WDATA] = "WDATA",
-	[STATE_OACK] = "OACK",
 	[STATE_WAITACK] = "WAITACK",
 	[STATE_LAST] = "LAST",
 	[STATE_DONE] = "DONE",
+	[STATE_START] = "START",
 };
 
 static int tftp_send(struct file_priv *priv)
@@ -166,7 +167,6 @@ static int tftp_send(struct file_priv *priv)
 	case STATE_RDATA:
 		if (priv->block == priv->block_requested)
 			return 0;
-	case STATE_OACK:
 		xp = pkt;
 		s = (uint16_t *)pkt;
 		*s++ = htons(TFTP_ACK);
@@ -261,11 +261,39 @@ static void tftp_timer_reset(struct file_priv *priv)
 	priv->progress_timeout = priv->resend_timeout = get_time_ns();
 }
 
+static int tftp_allocate_transfer(struct file_priv *priv)
+{
+	debug_assert(!priv->fifo);
+	debug_assert(!priv->buf);
+
+	priv->fifo = kfifo_alloc(priv->blocksize);
+	if (!priv->fifo)
+		goto err;
+
+	if (priv->push) {
+		priv->buf = xmalloc(priv->blocksize);
+		if (!priv->buf) {
+			kfifo_free(priv->fifo);
+			priv->fifo = NULL;
+			goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	priv->err = -ENOMEM;
+	priv->state = STATE_DONE;
+
+	return priv->err;
+}
+
 static void tftp_recv(struct file_priv *priv,
 			uint8_t *pkt, unsigned len, uint16_t uh_sport)
 {
 	uint16_t opcode;
 	uint16_t block;
+	int rc;
 
 	/* according to RFC1350 minimal tftp packet length is 4 bytes */
 	if (len < 4)
@@ -294,48 +322,64 @@ static void tftp_recv(struct file_priv *priv,
 			break;
 		}
 
-		priv->block = block + 1;
-		tftp_timer_reset(priv);
+		switch (priv->state) {
+		case STATE_WRQ:
+			priv->tftp_con->udp->uh_dport = uh_sport;
+			priv->state = STATE_START;
+			break;
 
-		if (priv->state == STATE_LAST) {
+		case STATE_WAITACK:
+			priv->state = STATE_WDATA;
+			break;
+
+		case STATE_LAST:
 			priv->state = STATE_DONE;
 			break;
+
+		default:
+			pr_warn("ACK packet in %s state\n",
+				tftp_states[priv->state]);
+			goto ack_out;
 		}
-		priv->tftp_con->udp->uh_dport = uh_sport;
-		priv->state = STATE_WDATA;
+
+		priv->block = block + 1;
+		tftp_timer_reset(priv);
+
+	ack_out:
 		break;
 
 	case TFTP_OACK:
 		tftp_parse_oack(priv, pkt, len);
 		priv->tftp_con->udp->uh_dport = uh_sport;
-
-		if (priv->push) {
-			/* send first block */
-			priv->state = STATE_WDATA;
-			priv->block = 1;
-		} else {
-			/* send ACK */
-			priv->state = STATE_OACK;
-			priv->block = 0;
-			tftp_send(priv);
-		}
-
+		priv->state = STATE_START;
 		break;
+
 	case TFTP_DATA:
 		len -= 2;
 		priv->block = ntohs(*(uint16_t *)pkt);
 
-		if (priv->state == STATE_RRQ || priv->state == STATE_OACK) {
-			/* first block received */
-			priv->state = STATE_RDATA;
+		if (priv->state == STATE_RRQ) {
+			/* first block received; entered only with non rfc
+			   2347 (TFTP Option extension) compliant servers */
 			priv->tftp_con->udp->uh_dport = uh_sport;
+			priv->state = STATE_RDATA;
 			priv->last_block = 0;
+
+			rc = tftp_allocate_transfer(priv);
+			if (rc < 0)
+				break;
 		}
 
 		if (priv->block == priv->last_block)
 			/* Same block again; ignore it. */
 			break;
 
+		if (len > priv->blocksize) {
+			pr_warn("tftp: oversized packet (%u > %d) received\n",
+				len, priv->blocksize);
+			break;
+		}
+
 		priv->last_block = priv->block;
 
 		tftp_timer_reset(priv);
@@ -378,6 +422,30 @@ static void tftp_handler(void *ctx, char *packet, unsigned len)
 	tftp_recv(priv, pkt, net_eth_to_udplen(packet), udp->uh_sport);
 }
 
+static int tftp_start_transfer(struct file_priv *priv)
+{
+	int rc;
+
+	rc = tftp_allocate_transfer(priv);
+	if (rc < 0)
+		/* function sets 'priv->state = STATE_DONE' and 'priv->err' in
+		   error case */
+		return rc;
+
+	if (priv->push) {
+		/* send first block */
+		priv->state = STATE_WDATA;
+		priv->block = 1;
+	} else {
+		/* send ACK */
+		priv->state = STATE_RDATA;
+		priv->block = 0;
+		tftp_send(priv);
+	}
+
+	return 0;
+}
+
 static struct file_priv *tftp_do_open(struct device_d *dev,
 		int accmode, struct dentry *dentry)
 {
@@ -409,48 +477,74 @@ static struct file_priv *tftp_do_open(struct device_d *dev,
 	priv->blocksize = TFTP_BLOCK_SIZE;
 	priv->block_requested = -1;
 
-	priv->fifo = kfifo_alloc(TFTP_FIFO_SIZE);
-	if (!priv->fifo) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
 	parseopt_hu(fsdev->options, "port", &port);
 
 	priv->tftp_con = net_udp_new(tpriv->server, port, tftp_handler, priv);
 	if (IS_ERR(priv->tftp_con)) {
 		ret = PTR_ERR(priv->tftp_con);
-		goto out1;
+		goto out;
 	}
 
 	ret = tftp_send(priv);
 	if (ret)
-		goto out2;
+		goto out1;
 
 	tftp_timer_reset(priv);
-	while (priv->state != STATE_RDATA &&
-			priv->state != STATE_DONE &&
-			priv->state != STATE_WDATA) {
-		ret = tftp_poll(priv);
-		if (ret == TFTP_ERR_RESEND)
-			tftp_send(priv);
-		if (ret < 0)
-			goto out2;
-	}
 
-	if (priv->state == STATE_DONE && priv->err) {
-		ret = priv->err;
-		goto out2;
-	}
+	/* - 'ret < 0'  ... error
+	   - 'ret == 0' ... further tftp_poll() required
+	   - 'ret == 1' ... startup finished */
+	do {
+		switch (priv->state) {
+		case STATE_DONE:
+			/* branch is entered in two situations:
+			   - non rfc 2347 compliant servers finished the
+			     transfer by sending a small file
+			   - some error occurred */
+			if (priv->err < 0)
+				ret = priv->err;
+			else
+				ret = 1;
+			break;
 
-	priv->buf = xmalloc(priv->blocksize);
+		case STATE_START:
+			ret = tftp_start_transfer(priv);
+			if (!ret)
+				ret = 1;
+			break;
+
+		case STATE_RDATA:
+			/* first data block of non rfc 2347 servers */
+			ret = 1;
+			break;
+
+		case STATE_RRQ:
+		case STATE_WRQ:
+			ret = tftp_poll(priv);
+			if (ret == TFTP_ERR_RESEND) {
+				tftp_send(priv);
+				ret = 0;
+			}
+			break;
+
+		default:
+			debug_assert(false);
+			break;
+		}
+	} while (ret == 0);
+
+	if (ret < 0)
+		goto out1;
 
 	return priv;
-out2:
-	net_unregister(priv->tftp_con);
 out1:
-	kfifo_free(priv->fifo);
+	net_unregister(priv->tftp_con);
 out:
+	if (priv->fifo)
+		kfifo_free(priv->fifo);
+
+	free(priv->filename);
+	free(priv->buf);
 	free(priv);
 
 	return ERR_PTR(ret);
@@ -567,7 +661,7 @@ static int tftp_read(struct device_d *dev, FILE *f, void *buf, size_t insize)
 		if (priv->state == STATE_DONE)
 			return outsize;
 
-		if (TFTP_FIFO_SIZE - kfifo_len(priv->fifo) >= priv->blocksize)
+		if (kfifo_len(priv->fifo) == 0)
 			tftp_send(priv);
 
 		ret = tftp_poll(priv);
-- 
2.37.2




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

* [PATCH v4 11/21] tftp: add sanity check for OACK response
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (9 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 10/21] tftp: allocate buffers and fifo dynamically Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 12/21] tftp: record whether tftp file is opened for lookup operation only Enrico Scholz
                   ` (10 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Catch bad 'blocksize' or 'windowsize' responses from the server.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index 64a94797cda3..4e31adcd60ed 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -229,7 +229,7 @@ static int tftp_poll(struct file_priv *priv)
 	return 0;
 }
 
-static void tftp_parse_oack(struct file_priv *priv, unsigned char *pkt, int len)
+static int tftp_parse_oack(struct file_priv *priv, unsigned char *pkt, int len)
 {
 	unsigned char *opt, *val, *s;
 
@@ -246,7 +246,7 @@ static void tftp_parse_oack(struct file_priv *priv, unsigned char *pkt, int len)
 		opt = s;
 		val = s + strlen(s) + 1;
 		if (val > s + len)
-			return;
+			break;
 		if (!strcmp(opt, "tsize"))
 			priv->filesize = simple_strtoull(val, NULL, 10);
 		if (!strcmp(opt, "blksize"))
@@ -254,6 +254,13 @@ static void tftp_parse_oack(struct file_priv *priv, unsigned char *pkt, int len)
 		pr_debug("OACK opt: %s val: %s\n", opt, val);
 		s = val + strlen(val) + 1;
 	}
+
+	if (priv->blocksize > TFTP_MTU_SIZE) {
+		pr_warn("tftp: invalid oack response\n");
+		return -EINVAL;
+	}
+
+	return 0;
 }
 
 static void tftp_timer_reset(struct file_priv *priv)
@@ -349,8 +356,14 @@ static void tftp_recv(struct file_priv *priv,
 		break;
 
 	case TFTP_OACK:
-		tftp_parse_oack(priv, pkt, len);
 		priv->tftp_con->udp->uh_dport = uh_sport;
+
+		if (tftp_parse_oack(priv, pkt, len) < 0) {
+			priv->err = -EINVAL;
+			priv->state = STATE_DONE;
+			break;
+		}
+
 		priv->state = STATE_START;
 		break;
 
-- 
2.37.2




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

* [PATCH v4 12/21] tftp: record whether tftp file is opened for lookup operation only
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (10 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 11/21] tftp: add sanity check for OACK response Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 13/21] tftp: reduce block size on lookup requests Enrico Scholz
                   ` (9 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Opening a tftp is done in two steps: at first `tftp_lookup()` is
called to get the filesize and then it is opened again and data are
read.

The `tftp_lookup()` call sends only a RRQ/WRQ, reads then the "tsize"
from the response and closes the transfer by sending an error datagram.
The tftp server will send a full data window.

To prevent unneeded traffic, later patches set parameters to reduce
the size of the server response.

We need knowledge about type of operation which is recorded in an
"is_getattr" attribute.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index 4e31adcd60ed..7181c58d1083 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -95,6 +95,7 @@ struct file_priv {
 	void *buf;
 	int blocksize;
 	int block_requested;
+	bool is_getattr;
 };
 
 struct tftp_priv {
@@ -460,7 +461,7 @@ static int tftp_start_transfer(struct file_priv *priv)
 }
 
 static struct file_priv *tftp_do_open(struct device_d *dev,
-		int accmode, struct dentry *dentry)
+		int accmode, struct dentry *dentry, bool is_getattr)
 {
 	struct fs_device_d *fsdev = dev_to_fs_device(dev);
 	struct file_priv *priv;
@@ -489,6 +490,7 @@ static struct file_priv *tftp_do_open(struct device_d *dev,
 	priv->filename = dpath(dentry, fsdev->vfsmount.mnt_root);
 	priv->blocksize = TFTP_BLOCK_SIZE;
 	priv->block_requested = -1;
+	priv->is_getattr = is_getattr;
 
 	parseopt_hu(fsdev->options, "port", &port);
 
@@ -567,7 +569,7 @@ static int tftp_open(struct device_d *dev, FILE *file, const char *filename)
 {
 	struct file_priv *priv;
 
-	priv = tftp_do_open(dev, file->flags, file->dentry);
+	priv = tftp_do_open(dev, file->flags, file->dentry, false);
 	if (IS_ERR(priv))
 		return PTR_ERR(priv);
 
@@ -783,7 +785,7 @@ static struct dentry *tftp_lookup(struct inode *dir, struct dentry *dentry,
 	struct file_priv *priv;
 	loff_t filesize;
 
-	priv = tftp_do_open(&fsdev->dev, O_RDONLY, dentry);
+	priv = tftp_do_open(&fsdev->dev, O_RDONLY, dentry, true);
 	if (IS_ERR(priv))
 		return NULL;
 
-- 
2.37.2




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

* [PATCH v4 13/21] tftp: reduce block size on lookup requests
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (11 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 12/21] tftp: record whether tftp file is opened for lookup operation only Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 14/21] tftp: refactor data processing Enrico Scholz
                   ` (8 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Save some bytes on network traffic by reducing the server response for
lookup requests.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index 7181c58d1083..16e57daaef37 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -151,7 +151,9 @@ static int tftp_send(struct file_priv *priv)
 				'\0',	/* "timeout" */
 				TIMEOUT, '\0',
 				'\0',	/* "blksize" */
-				TFTP_MTU_SIZE);
+				/* use only a minimal blksize for getattr
+				   operations, */
+				priv->is_getattr ? TFTP_BLOCK_SIZE : TFTP_MTU_SIZE);
 		pkt++;
 
 		if (!priv->push)
-- 
2.37.2




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

* [PATCH v4 14/21] tftp: refactor data processing
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (12 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 13/21] tftp: reduce block size on lookup requests Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 15/21] tftp: detect out-of-memory situations Enrico Scholz
                   ` (7 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

move block handling into dedicated function

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 37 +++++++++++++++++++++----------------
 1 file changed, 21 insertions(+), 16 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index 16e57daaef37..5b0dcb2b75d6 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -298,6 +298,26 @@ err:
 	return priv->err;
 }
 
+static void tftp_put_data(struct file_priv *priv, uint16_t block,
+			  void const *pkt, size_t len)
+{
+	if (len > priv->blocksize) {
+		pr_warn("tftp: oversized packet (%zu > %d) received\n",
+			len, priv->blocksize);
+		return;
+	}
+
+	priv->last_block = block;
+
+	kfifo_put(priv->fifo, pkt, len);
+
+	if (len < priv->blocksize) {
+		tftp_send(priv);
+		priv->err = 0;
+		priv->state = STATE_DONE;
+	}
+}
+
 static void tftp_recv(struct file_priv *priv,
 			uint8_t *pkt, unsigned len, uint16_t uh_sport)
 {
@@ -390,23 +410,8 @@ static void tftp_recv(struct file_priv *priv,
 			/* Same block again; ignore it. */
 			break;
 
-		if (len > priv->blocksize) {
-			pr_warn("tftp: oversized packet (%u > %d) received\n",
-				len, priv->blocksize);
-			break;
-		}
-
-		priv->last_block = priv->block;
-
 		tftp_timer_reset(priv);
-
-		kfifo_put(priv->fifo, pkt + 2, len);
-
-		if (len < priv->blocksize) {
-			tftp_send(priv);
-			priv->err = 0;
-			priv->state = STATE_DONE;
-		}
+		tftp_put_data(priv, priv->block, pkt + 2, len);
 
 		break;
 
-- 
2.37.2




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

* [PATCH v4 15/21] tftp: detect out-of-memory situations
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (13 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 14/21] tftp: refactor data processing Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 16/21] tftp: implement 'windowsize' (RFC 7440) support Enrico Scholz
                   ` (6 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

it should never happen due to the program logic; but detect a failed
kfifo_put() just in case...

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index 5b0dcb2b75d6..e62ff2a80bb5 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -301,6 +301,8 @@ err:
 static void tftp_put_data(struct file_priv *priv, uint16_t block,
 			  void const *pkt, size_t len)
 {
+	unsigned int sz;
+
 	if (len > priv->blocksize) {
 		pr_warn("tftp: oversized packet (%zu > %d) received\n",
 			len, priv->blocksize);
@@ -309,9 +311,14 @@ static void tftp_put_data(struct file_priv *priv, uint16_t block,
 
 	priv->last_block = block;
 
-	kfifo_put(priv->fifo, pkt, len);
+	sz = kfifo_put(priv->fifo, pkt, len);
 
-	if (len < priv->blocksize) {
+	if (sz != len) {
+		pr_err("tftp: not enough room in kfifo (only %u out of %zu written\n",
+		       sz, len);
+		priv->err = -ENOMEM;
+		priv->state = STATE_DONE;
+	} else if (len < priv->blocksize) {
 		tftp_send(priv);
 		priv->err = 0;
 		priv->state = STATE_DONE;
-- 
2.37.2




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

* [PATCH v4 16/21] tftp: implement 'windowsize' (RFC 7440) support
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (14 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 15/21] tftp: detect out-of-memory situations Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 17/21] tftp: do not use 'priv->block' for RRQ Enrico Scholz
                   ` (5 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Results (with the reorder patch; numbers in bytes/s) on an iMX8MP are:

 | windowsize | VPN       | 1 Gb/s     | 100 Mb/s   |
 |------------|-----------|------------|------------|
 | 128        | 3.869.284 | 98.643.085 | 11.434.852 |
 |  64        | 3.863.581 | 98.550.375 | 11.434.852 |
 |  48        | 3.431.580 | 94.211.680 | 11.275.010 |
 |  32        | 2.835.129 | 85.250.081 | 10.985.605 |
 |  24        | 2.344.858 | 77.787.537 | 10.765.667 |
 |  16        | 1.734.186 | 67.519.381 | 10.210.087 |
 |  12        | 1.403.340 | 61.972.576 |  9.915.612 |
 |   8        | 1.002.462 | 50.852.376 |  9.016.130 |
 |   6        |   775.573 | 42.781.558 |  8.422.297 |
 |   4        |   547.845 | 32.066.544 |  6.835.567 |
 |   3        |   412.987 | 26.526.081 |  6.322.435 |
 |   2        |   280.987 | 19.120.641 |  5.494.241 |
 |   1        |   141.699 | 10.431.516 |  2.967.224 |

(VPN = OpenVPN on ADSL 50 Mb/s).

The window size can be configured at runtime.

This commit increases barebox size by

| add/remove: 1/0 grow/shrink: 4/1 up/down: 148/-16 (132)
| Function                                     old     new   delta
| tftp_send                                    336     384     +48
| tftp_handler                                 880     928     +48
| tftp_init                                     16      60     +44
| tftp_allocate_transfer                       100     104      +4
| g_tftp_window_size                             -       4      +4
| tftp_poll                                    180     164     -16
| Total: Before=629980, After=630112, chg +0.02%

Setting FS_TFTP_MAX_WINDOW_SIZE to zero reduces it to

| add/remove: 1/0 grow/shrink: 3/2 up/down: 96/-52 (44)
| Function                                     old     new   delta
| tftp_init                                     16      60     +44
| tftp_handler                                 880     924     +44
| tftp_allocate_transfer                       100     104      +4
| g_tftp_window_size                             -       4      +4
| tftp_poll                                    180     164     -16
| tftp_send                                    336     300     -36
| Total: Before=629980, After=630024, chg +0.01%

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/Kconfig | 14 ++++++++++++++
 fs/tftp.c  | 54 +++++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 55 insertions(+), 13 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index aeba00073eed..0c4934285942 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -43,6 +43,20 @@ config FS_TFTP
 	prompt "tftp support"
 	depends on NET
 
+config FS_TFTP_MAX_WINDOW_SIZE
+	int
+	prompt "maximum tftp window size (RFC 7440)"
+	depends on FS_TFTP
+	default 128
+	range 1 128
+	help
+	  The maximum allowed tftp "windowsize" (RFC 7440).  Higher
+	  value increase speed of the tftp download with the cost of
+	  memory (1432 bytes per slot).
+
+	  Requires tftp "windowsize" (RFC 7440) support on server side
+	  to have an effect.
+
 config FS_OMAP4_USBBOOT
 	bool
 	prompt "Filesystem over usb boot"
diff --git a/fs/tftp.c b/fs/tftp.c
index e62ff2a80bb5..88767223b052 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -26,9 +26,11 @@
 #include <libgen.h>
 #include <fcntl.h>
 #include <getopt.h>
+#include <globalvar.h>
 #include <init.h>
 #include <linux/stat.h>
 #include <linux/err.h>
+#include <linux/kernel.h>
 #include <kfifo.h>
 #include <parseopt.h>
 #include <linux/sizes.h>
@@ -68,6 +70,7 @@
 
 #define TFTP_BLOCK_SIZE		512	/* default TFTP block size */
 #define TFTP_MTU_SIZE		1432	/* MTU based block size */
+#define TFTP_MAX_WINDOW_SIZE	CONFIG_FS_TFTP_MAX_WINDOW_SIZE
 
 #define TFTP_ERR_RESEND	1
 
@@ -80,11 +83,14 @@
 	} while (0)
 #endif
 
+static int g_tftp_window_size = DIV_ROUND_UP(TFTP_MAX_WINDOW_SIZE, 2);
+
 struct file_priv {
 	struct net_connection *tftp_con;
 	int push;
 	uint16_t block;
 	uint16_t last_block;
+	uint16_t ack_block;
 	int state;
 	int err;
 	char *filename;
@@ -94,7 +100,7 @@ struct file_priv {
 	struct kfifo *fifo;
 	void *buf;
 	int blocksize;
-	int block_requested;
+	unsigned int windowsize;
 	bool is_getattr;
 };
 
@@ -125,6 +131,7 @@ static int tftp_send(struct file_priv *priv)
 	int len = 0;
 	uint16_t *s;
 	unsigned char *pkt = net_udp_get_payload(priv->tftp_con);
+	unsigned int window_size;
 	int ret;
 
 	pr_vdebug("%s: state %s\n", __func__, tftp_states[priv->state]);
@@ -132,6 +139,15 @@ static int tftp_send(struct file_priv *priv)
 	switch (priv->state) {
 	case STATE_RRQ:
 	case STATE_WRQ:
+		if (priv->push || priv->is_getattr)
+			/* atm, windowsize is supported only for RRQ and there
+			   is no need to request a full window when we are
+			   just looking up file attributes */
+			window_size = 1;
+		else
+			window_size = min_t(unsigned int, g_tftp_window_size,
+					    TFTP_MAX_WINDOW_SIZE);
+
 		xp = pkt;
 		s = (uint16_t *)pkt;
 		if (priv->state == STATE_RRQ)
@@ -164,17 +180,22 @@ static int tftp_send(struct file_priv *priv)
 				       '\0', priv->filesize,
 				       '\0');
 
+		if (window_size > 1)
+			pkt += sprintf((unsigned char *)pkt,
+				       "windowsize%c%u%c",
+				       '\0', window_size,
+				       '\0');
+
 		len = pkt - xp;
 		break;
 
 	case STATE_RDATA:
-		if (priv->block == priv->block_requested)
-			return 0;
 		xp = pkt;
 		s = (uint16_t *)pkt;
 		*s++ = htons(TFTP_ACK);
-		*s++ = htons(priv->block);
-		priv->block_requested = priv->block;
+		*s++ = htons(priv->last_block);
+		priv->ack_block  = priv->last_block;
+		priv->ack_block += priv->windowsize;
 		pkt = (unsigned char *)s;
 		len = pkt - xp;
 		break;
@@ -217,7 +238,6 @@ static int tftp_poll(struct file_priv *priv)
 	if (is_timeout(priv->resend_timeout, TFTP_RESEND_TIMEOUT)) {
 		printf("T ");
 		priv->resend_timeout = get_time_ns();
-		priv->block_requested = -1;
 		return TFTP_ERR_RESEND;
 	}
 
@@ -254,11 +274,15 @@ static int tftp_parse_oack(struct file_priv *priv, unsigned char *pkt, int len)
 			priv->filesize = simple_strtoull(val, NULL, 10);
 		if (!strcmp(opt, "blksize"))
 			priv->blocksize = simple_strtoul(val, NULL, 10);
+		if (!strcmp(opt, "windowsize"))
+			priv->windowsize = simple_strtoul(val, NULL, 10);
 		pr_debug("OACK opt: %s val: %s\n", opt, val);
 		s = val + strlen(val) + 1;
 	}
 
-	if (priv->blocksize > TFTP_MTU_SIZE) {
+	if (priv->blocksize > TFTP_MTU_SIZE ||
+	    priv->windowsize > TFTP_MAX_WINDOW_SIZE ||
+	    priv->windowsize == 0) {
 		pr_warn("tftp: invalid oack response\n");
 		return -EINVAL;
 	}
@@ -276,7 +300,9 @@ static int tftp_allocate_transfer(struct file_priv *priv)
 	debug_assert(!priv->fifo);
 	debug_assert(!priv->buf);
 
-	priv->fifo = kfifo_alloc(priv->blocksize);
+	/* multiplication is safe; both operands were checked in tftp_parse_oack()
+	   and are small integers */
+	priv->fifo = kfifo_alloc(priv->blocksize * priv->windowsize);
 	if (!priv->fifo)
 		goto err;
 
@@ -407,14 +433,14 @@ static void tftp_recv(struct file_priv *priv,
 			priv->tftp_con->udp->uh_dport = uh_sport;
 			priv->state = STATE_RDATA;
 			priv->last_block = 0;
+			priv->ack_block = priv->windowsize;
 
 			rc = tftp_allocate_transfer(priv);
 			if (rc < 0)
 				break;
 		}
 
-		if (priv->block == priv->last_block)
-			/* Same block again; ignore it. */
+		if (priv->block != (uint16_t)(priv->last_block + 1))
 			break;
 
 		tftp_timer_reset(priv);
@@ -467,7 +493,7 @@ static int tftp_start_transfer(struct file_priv *priv)
 	} else {
 		/* send ACK */
 		priv->state = STATE_RDATA;
-		priv->block = 0;
+		priv->last_block = 0;
 		tftp_send(priv);
 	}
 
@@ -503,7 +529,7 @@ static struct file_priv *tftp_do_open(struct device_d *dev,
 	priv->err = -EINVAL;
 	priv->filename = dpath(dentry, fsdev->vfsmount.mnt_root);
 	priv->blocksize = TFTP_BLOCK_SIZE;
-	priv->block_requested = -1;
+	priv->windowsize = 1;
 	priv->is_getattr = is_getattr;
 
 	parseopt_hu(fsdev->options, "port", &port);
@@ -690,7 +716,7 @@ static int tftp_read(struct device_d *dev, FILE *f, void *buf, size_t insize)
 		if (priv->state == STATE_DONE)
 			return outsize;
 
-		if (kfifo_len(priv->fifo) == 0)
+		if (priv->last_block == priv->ack_block)
 			tftp_send(priv);
 
 		ret = tftp_poll(priv);
@@ -882,6 +908,8 @@ static struct fs_driver_d tftp_driver = {
 
 static int tftp_init(void)
 {
+	globalvar_add_simple_int("tftp.windowsize", &g_tftp_window_size, "%u");
+
 	return register_fs_driver(&tftp_driver);
 }
 coredevice_initcall(tftp_init);
-- 
2.37.2




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

* [PATCH v4 17/21] tftp: do not use 'priv->block' for RRQ
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (15 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 16/21] tftp: implement 'windowsize' (RFC 7440) support Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 18/21] tftp: reorder tftp packets Enrico Scholz
                   ` (4 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

attribute is not used outside tftp_recv() for RRQ.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/tftp.c b/fs/tftp.c
index 88767223b052..c6c7a2e3c887 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -425,7 +425,7 @@ static void tftp_recv(struct file_priv *priv,
 
 	case TFTP_DATA:
 		len -= 2;
-		priv->block = ntohs(*(uint16_t *)pkt);
+		block = ntohs(*(uint16_t *)pkt);
 
 		if (priv->state == STATE_RRQ) {
 			/* first block received; entered only with non rfc
@@ -440,11 +440,11 @@ static void tftp_recv(struct file_priv *priv,
 				break;
 		}
 
-		if (priv->block != (uint16_t)(priv->last_block + 1))
+		if (block != (uint16_t)(priv->last_block + 1))
 			break;
 
 		tftp_timer_reset(priv);
-		tftp_put_data(priv, priv->block, pkt + 2, len);
+		tftp_put_data(priv, block, pkt + 2, len);
 
 		break;
 
-- 
2.37.2




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

* [PATCH v4 18/21] tftp: reorder tftp packets
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (16 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 17/21] tftp: do not use 'priv->block' for RRQ Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 19/21] tftp: add selftest Enrico Scholz
                   ` (3 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

With the tftp "windowsize" option, reordering of udp datagrams becomes
an issue.  Depending on the network topology, this reordering occurs
several times with large tftp transfers and will heavily reduce the
transfer speed.

This patch adds a packet cache so that datagrams can be reassembled in
the correct order.

Because it increases memory usage and barebox binary size, it is an
Kconfig option.

bloat-o-meter reports with a non-zero FS_TFTP_REORDER_CACHE_SIZE

| add/remove: 3/0 grow/shrink: 4/0 up/down: 916/0 (916)
| Function                                     old     new   delta
| tftp_handler                                 920    1244    +324
| tftp_put_data                                  -     184    +184
| tftp_window_cache_remove                       -     124    +124
| tftp_window_cache_get_pos                      -     120    +120
| tftp_allocate_transfer                       104     188     +84
| tftp_do_close                                260     312     +52
| tftp_send                                    384     412     +28
| Total: Before=630104, After=631020, chg +0.15%

After setting FS_TFTP_REORDER_CACHE_SIZE Kconfig option to 0, numbers
are going down to

| add/remove: 0/0 grow/shrink: 3/0 up/down: 152/0 (152)
| Function                                     old     new   delta
| tftp_handler                                 920    1012     +92
| tftp_allocate_transfer                       104     136     +32
| tftp_send                                    384     412     +28
| Total: Before=630104, After=630256, chg +0.02%

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/Kconfig |  22 ++++
 fs/tftp.c  | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 323 insertions(+), 6 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index 0c4934285942..cf884e0646a1 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -57,6 +57,28 @@ config FS_TFTP_MAX_WINDOW_SIZE
 	  Requires tftp "windowsize" (RFC 7440) support on server side
 	  to have an effect.
 
+config FS_TFTP_REORDER_CACHE_SIZE
+	int
+	prompt "number of out-of-order tftp packets to be cached"
+	depends on FS_TFTP
+	default 16 if FS_TFTP_MAX_WINDOW_SIZE > 16
+	default  0 if FS_TFTP_MAX_WINDOW_SIZE = 1
+        ## TODO: it should be 'FS_TFTP_MAX_WINDOW_SIZE - 1' but this
+        ## is not supported by Kconfig
+	default FS_TFTP_MAX_WINDOW_SIZE
+	range 0 FS_TFTP_MAX_WINDOW_SIZE
+	help
+	  UDP allows reordering of datagrams; with this option,
+	  unexpected tftp packets will be cached and later
+	  reassembled.  This increases stability of the tftp download
+	  with the cost of memory (around 1440 bytes per cache
+	  element) and barebox binary size (around 700 bytes).
+
+	  A value of 0 disables caching.
+
+	  Requires tftp "windowsize" (RFC 7440) support on server side
+	  to have an effect.
+
 config FS_OMAP4_USBBOOT
 	bool
 	prompt "Filesystem over usb boot"
diff --git a/fs/tftp.c b/fs/tftp.c
index c6c7a2e3c887..b010a6be6dbb 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -28,6 +28,7 @@
 #include <getopt.h>
 #include <globalvar.h>
 #include <init.h>
+#include <linux/bitmap.h>
 #include <linux/stat.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
@@ -72,6 +73,12 @@
 #define TFTP_MTU_SIZE		1432	/* MTU based block size */
 #define TFTP_MAX_WINDOW_SIZE	CONFIG_FS_TFTP_MAX_WINDOW_SIZE
 
+/* size of cache which deals with udp reordering */
+#define TFTP_WINDOW_CACHE_NUM	CONFIG_FS_TFTP_REORDER_CACHE_SIZE
+
+/* marker for an emtpy 'tftp_cache' */
+#define TFTP_CACHE_NO_ID	(-1)
+
 #define TFTP_ERR_RESEND	1
 
 #ifdef DEBUG
@@ -85,6 +92,30 @@
 
 static int g_tftp_window_size = DIV_ROUND_UP(TFTP_MAX_WINDOW_SIZE, 2);
 
+struct tftp_block {
+	uint16_t id;
+	uint16_t len;
+
+	struct list_head head;
+	uint8_t data[];
+};
+
+struct tftp_cache {
+	/* The id located at @pos or TFTP_CACHE_NO_ID when cache is empty.  It
+	   is possible that the corresponding bit in @used is NOT set. */
+	unsigned int		id;
+	unsigned int		pos;
+
+	/* bitmask */
+	unsigned long		used[BITS_TO_LONGS(TFTP_WINDOW_CACHE_NUM)];
+
+	/* size of the cache; is limited by TFTP_WINDOW_CACHE_NUM and the
+	   actual window size */
+	unsigned int		size;
+	unsigned int		block_len;
+	struct tftp_block	*blocks[TFTP_WINDOW_CACHE_NUM];
+};
+
 struct file_priv {
 	struct net_connection *tftp_con;
 	int push;
@@ -102,12 +133,222 @@ struct file_priv {
 	int blocksize;
 	unsigned int windowsize;
 	bool is_getattr;
+	struct tftp_cache cache;
 };
 
 struct tftp_priv {
 	IPaddr_t server;
 };
 
+static inline bool is_block_before(uint16_t a, uint16_t b)
+{
+	return (int16_t)(b - a) > 0;
+}
+
+static inline uint16_t get_block_delta(uint16_t a, uint16_t b)
+{
+	debug_assert(!is_block_before(b, a));
+
+	return b - a;
+}
+
+static bool in_window(uint16_t block, uint16_t start, uint16_t end)
+{
+	/* handle the three cases:
+	   - [ ......... | start | .. | BLOCK | .. | end | ......... ]
+	   - [ ..| BLOCK | .. | end | ................. | start | .. ]
+	   - [ ..| end | ................. | start | .. | BLOCK | .. ]
+	*/
+	return ((start <= block && block <= end) ||
+		(block <= end   && end   <= start) ||
+		(end   <= start && start <= block));
+}
+
+static inline size_t tftp_window_cache_size(struct tftp_cache const *cache)
+{
+	/* allows to optimize away the cache code when TFTP_WINDOW_CACHE_SIZE
+	   is 0 */
+	return TFTP_WINDOW_CACHE_NUM == 0 ? 0 : cache->size;
+}
+
+static void tftp_window_cache_init(struct tftp_cache *cache,
+				   uint16_t block_len, uint16_t window_size)
+{
+	debug_assert(window_size > 0);
+
+	*cache = (struct tftp_cache) {
+		.id		= TFTP_CACHE_NO_ID,
+		.block_len	= block_len,
+		.size		= min_t(uint16_t, window_size - 1,
+					ARRAY_SIZE(cache->blocks)),
+	};
+}
+
+static void tftp_window_cache_free(struct tftp_cache *cache)
+{
+	size_t	cache_size = tftp_window_cache_size(cache);
+	size_t	i;
+
+	for (i = 0; i < cache_size; ++i) {
+		free(cache->blocks[i]);
+		cache->blocks[i] = NULL;
+	}
+}
+
+static void tftp_window_cache_reset(struct tftp_cache *cache)
+{
+	cache->id = TFTP_CACHE_NO_ID;
+	memset(cache->used, 0, sizeof cache->used);
+}
+
+static int tftp_window_cache_get_pos(struct tftp_cache const *cache, uint16_t id)
+{
+	size_t		cache_size = tftp_window_cache_size(cache);
+	unsigned int	pos;
+
+	if (cache_size == 0)
+		return -1;
+
+	if (cache->id == TFTP_CACHE_NO_ID)
+		return -1;
+
+	if (!in_window(id, cache->id, cache->id + cache_size - 1))
+		return -1;
+
+	pos  = cache->pos + get_block_delta(cache->id, id);
+	pos %= cache_size;
+
+	return pos;
+}
+
+/* returns whether the first cached element has the given @id */
+static bool tftp_window_cache_starts_with(struct tftp_cache const *cache,
+					  uint16_t id)
+{
+	return (TFTP_WINDOW_CACHE_NUM > 0 &&
+		cache->id != TFTP_CACHE_NO_ID &&
+		cache->id == id &&
+		test_bit(cache->pos, cache->used));
+}
+
+static bool tftp_window_cache_is_empty(struct tftp_cache const *cache)
+{
+	/* use this indirection to avoid warnings about a '0 < 0' comparison
+	   in the loop condition when TFTP_WINDOW_CACHE_NUM is zero */
+	size_t	cache_size = ARRAY_SIZE(cache->used);
+	size_t	i;
+
+	for (i = 0; i < cache_size; ++i) {
+		if (cache->used[i] != 0)
+			return false;
+	}
+
+	return true;
+}
+
+static int tftp_window_cache_insert(struct tftp_cache *cache, uint16_t id,
+				    void const *data, size_t len)
+{
+	size_t const		cache_size = tftp_window_cache_size(cache);
+	int			pos;
+	struct tftp_block	*block;
+
+	if (cache_size == 0)
+		return -ENOSPC;
+
+	if (cache->id == TFTP_CACHE_NO_ID) {
+		/* initialize cache when it does not contain elements yet */
+		cache->id = id;
+		cache->pos = 0;
+
+		/* sanity check; cache is expected to be empty */
+		debug_assert(tftp_window_cache_is_empty(cache));
+	}
+
+	pos = tftp_window_cache_get_pos(cache, id);
+	if (pos < 0)
+		return -ENOSPC;
+
+	debug_assert(pos < cache_size);
+
+	if (test_bit(pos, cache->used))
+		/* block already cached */
+		return 0;
+
+	block = cache->blocks[pos];
+	if (!block) {
+		/* allocate space for the block; after being released, this
+		   memory can be reused for other blocks during the same tftp
+		   transfer */
+		block = malloc(sizeof *block + cache->block_len);
+		if (!block)
+			return -ENOMEM;
+
+		cache->blocks[pos] = block;
+	}
+
+	__set_bit(pos, cache->used);
+	memcpy(block->data, data, len);
+	block->id = id;
+	block->len = len;
+
+	return 0;
+}
+
+/* Marks the element at 'pos' as unused and updates internal cache information.
+   Returns true iff element at pos was valid. */
+static bool tftp_window_cache_remove(struct tftp_cache *cache, unsigned int pos)
+{
+	size_t const	cache_size = tftp_window_cache_size(cache);
+	bool		res;
+
+	if (cache_size == 0)
+		return 0;
+
+	res = __test_and_clear_bit(pos, cache->used);
+
+	if (tftp_window_cache_is_empty(cache)) {
+		/* cache has been cleared; reset pointers */
+		cache->id = TFTP_CACHE_NO_ID;
+	} else if (pos != cache->pos) {
+		/* noop; elements has been removed from the middle of cache */
+	} else {
+		/* first element of cache has been removed; proceed to the
+		   next one */
+		cache->id  += 1;
+		cache->pos += 1;
+		cache->pos %= cache_size;
+	}
+
+	return res;
+}
+
+/* Releases the first element from the cache and returns its content.
+ *
+ * Function can return NULL when the element is not cached
+ */
+static struct tftp_block *tftp_window_cache_pop(struct tftp_cache *cache)
+{
+	unsigned int	pos = cache->pos;
+
+	debug_assert(cache->id != TFTP_CACHE_NO_ID);
+
+	if (!tftp_window_cache_remove(cache, pos))
+		return NULL;
+
+	return cache->blocks[pos];
+}
+
+static bool tftp_window_cache_remove_id(struct tftp_cache *cache, uint16_t id)
+{
+	int		pos = tftp_window_cache_get_pos(cache, id);
+
+	if (pos < 0)
+		return false;
+
+	return tftp_window_cache_remove(cache, pos);
+}
+
 static int tftp_truncate(struct device_d *dev, FILE *f, loff_t size)
 {
 	return 0;
@@ -198,6 +439,7 @@ static int tftp_send(struct file_priv *priv)
 		priv->ack_block += priv->windowsize;
 		pkt = (unsigned char *)s;
 		len = pkt - xp;
+		tftp_window_cache_reset(&priv->cache);
 		break;
 	}
 
@@ -313,6 +555,9 @@ static int tftp_allocate_transfer(struct file_priv *priv)
 			priv->fifo = NULL;
 			goto err;
 		}
+	} else {
+		tftp_window_cache_init(&priv->cache,
+				       priv->blocksize, priv->windowsize);
 	}
 
 	return 0;
@@ -351,6 +596,60 @@ static void tftp_put_data(struct file_priv *priv, uint16_t block,
 	}
 }
 
+static void tftp_apply_window_cache(struct file_priv *priv)
+{
+	struct tftp_cache *cache = &priv->cache;
+
+	while (tftp_window_cache_starts_with(cache, priv->last_block + 1)) {
+		struct tftp_block *block;
+
+		/* can be changed by tftp_put_data() below and must be
+		   checked in each loop */
+		if (priv->state != STATE_RDATA)
+			return;
+
+		block = tftp_window_cache_pop(cache);
+
+		debug_assert(block);
+		debug_assert(block->id == (uint16_t)(priv->last_block + 1));
+
+		tftp_put_data(priv, block->id, block->data, block->len);
+	}
+}
+
+static void tftp_handle_data(struct file_priv *priv, uint16_t block,
+			     void const *data, size_t len)
+{
+	uint16_t exp_block;
+	int rc;
+
+	exp_block = priv->last_block + 1;
+
+	if (exp_block == block) {
+		/* datagram over network is the expected one; put it in the
+		   fifo directly and try to apply cached items then */
+		tftp_timer_reset(priv);
+		tftp_put_data(priv, block, data, len);
+		tftp_window_cache_remove_id(&priv->cache, block);
+		tftp_apply_window_cache(priv);
+	} else if (!in_window(block, exp_block, priv->ack_block)) {
+		/* completely unexpected and unrelated to actual window;
+		   ignore the packet. */
+		printf("B");
+	} else {
+		/* The 'rc < 0' below happens e.g. when datagrams in the first
+		   part of the transfer window are dropped.
+
+		   TODO: this will usually result in a timeout
+		   (TFTP_RESEND_TIMEOUT).  It should be possible to bypass
+		   this timeout by acknowledging the last packet (e.g. by
+		   doing 'priv->ack_block = priv->last_block' here). */
+		rc = tftp_window_cache_insert(&priv->cache, block, data, len);
+		if (rc < 0)
+			printf("M");
+	}
+}
+
 static void tftp_recv(struct file_priv *priv,
 			uint8_t *pkt, unsigned len, uint16_t uh_sport)
 {
@@ -440,12 +739,7 @@ static void tftp_recv(struct file_priv *priv,
 				break;
 		}
 
-		if (block != (uint16_t)(priv->last_block + 1))
-			break;
-
-		tftp_timer_reset(priv);
-		tftp_put_data(priv, block, pkt + 2, len);
-
+		tftp_handle_data(priv, block, pkt + 2, len);
 		break;
 
 	case TFTP_ERROR:
@@ -649,6 +943,7 @@ static int tftp_do_close(struct file_priv *priv)
 	}
 
 	net_unregister(priv->tftp_con);
+	tftp_window_cache_free(&priv->cache);
 	kfifo_free(priv->fifo);
 	free(priv->filename);
 	free(priv->buf);
-- 
2.37.2




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

* [PATCH v4 19/21] tftp: add selftest
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (17 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 18/21] tftp: reorder tftp packets Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 20/21] tftp: accept OACK + DATA datagrams only in certain states Enrico Scholz
                   ` (2 subsequent siblings)
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Unittest for window cache functions.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp-selftest.h |  56 ++++++++++++++++++++++++
 fs/tftp.c          | 106 ++++++++++++++++++++++++++++++++++++++++++++-
 test/self/Kconfig  |   7 +++
 3 files changed, 168 insertions(+), 1 deletion(-)
 create mode 100644 fs/tftp-selftest.h

diff --git a/fs/tftp-selftest.h b/fs/tftp-selftest.h
new file mode 100644
index 000000000000..2406ed329ecc
--- /dev/null
+++ b/fs/tftp-selftest.h
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-FileCopyrightText: 2022 Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
+
+#ifndef H_BAREBOX_FS_TFTP_SELFTEST_H
+#define H_BAREBOX_FS_TFTP_SELFTEST_H
+
+#include <bselftest.h>
+
+#define _expect_fmt(_v) _Generic(_v,				\
+				 void const *: "%p",		\
+				 void *: "%p",			\
+				 bool: "%d",			\
+				 long int: "%lld",		\
+				 long unsigned int: "%llu",	\
+				 unsigned short: "%h",		\
+				 signed int: "%d",		\
+				 unsigned int: "%u")
+
+#define _expect_op(_a, _b, _op)						\
+	({								\
+		__typeof__(_a)	__a = (_a);				\
+		__typeof__(_b)	__b = (_b);				\
+									\
+		total_tests++;						\
+									\
+		if (!(__a _op __b)) {					\
+			char fmt[sizeof "%s:%d: failed: %XXX  %XXX\n" # _op]; \
+			strcpy(fmt, "%s:%d: failed: ");			\
+			strcat(fmt, _expect_fmt(__a));			\
+			strcat(fmt, " " # _op " ");			\
+			strcat(fmt, _expect_fmt(__b));			\
+			strcat(fmt, "\n");				\
+									\
+			failed_tests++;					\
+			printf(fmt, __func__, __LINE__, __a, __b);	\
+		}							\
+	})
+
+#define _as_void(_p) ({					\
+			void const *__res = (_p);	\
+			__res;				\
+		})
+
+#define expect_eq(_a, _b)	_expect_op(_a, _b, ==);
+#define expect_ne(_a, _b)	_expect_op(_a, _b, !=);
+#define expect_it(_a)		_expect_op(_a, true, ==);
+
+#define expect_err(_a)		_expect_op(_a, 0, <);
+#define expect_ok(_a)		_expect_op(_a, 0, ==);
+
+/* _Generic() does not match 'void *' for typed pointers; cast them to raw
+   'void *' first */
+#define expect_NULL(_a)		_expect_op(_as_void(_a), NULL, ==);
+#define expect_PTR(_a)		_expect_op(_as_void(_a), NULL, !=);
+
+#endif	/* H_BAREBOX_FS_TFTP_SELFTEST_H */
diff --git a/fs/tftp.c b/fs/tftp.c
index b010a6be6dbb..a9cc0ff3b118 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -36,6 +36,8 @@
 #include <parseopt.h>
 #include <linux/sizes.h>
 
+#include "tftp-selftest.h"
+
 #define TFTP_PORT	69	/* Well known TFTP port number */
 
 /* Seconds to wait before remote server is allowed to resend a lost packet */
@@ -81,7 +83,7 @@
 
 #define TFTP_ERR_RESEND	1
 
-#ifdef DEBUG
+#if defined(DEBUG) || IS_ENABLED(CONFIG_SELFTEST_TFTP)
 #  define debug_assert(_cond)	BUG_ON(!(_cond))
 #else
 #  define debug_assert(_cond) do {			\
@@ -1208,3 +1210,105 @@ static int tftp_init(void)
 	return register_fs_driver(&tftp_driver);
 }
 coredevice_initcall(tftp_init);
+
+
+BSELFTEST_GLOBALS();
+
+static int __maybe_unused tftp_window_cache_selftest(void)
+{
+	struct tftp_cache	*cache = malloc(sizeof *cache);
+
+	if (!cache)
+		return -ENOMEM;
+
+	(void)skipped_tests;
+
+	expect_it( is_block_before(0, 1));
+	expect_it(!is_block_before(1, 0));
+	expect_it( is_block_before(65535, 0));
+	expect_it(!is_block_before(0, 65535));
+
+	expect_eq(get_block_delta(0, 1),     1);
+	expect_eq(get_block_delta(65535, 0), 1);
+	expect_eq(get_block_delta(65535, 1), 2);
+
+	expect_it(!in_window(0, 1, 3));
+	expect_it( in_window(1, 1, 3));
+	expect_it( in_window(2, 1, 3));
+	expect_it( in_window(3, 1, 3));
+	expect_it(!in_window(4, 1, 3));
+
+	expect_it(!in_window(65534, 65535, 1));
+	expect_it( in_window(65535, 65535, 1));
+	expect_it( in_window(    0, 65535, 1));
+	expect_it( in_window(    1, 65535, 1));
+	expect_it(!in_window(    2, 65535, 1));
+
+
+	tftp_window_cache_init(cache, 512, 5);
+
+	if (tftp_window_cache_size(cache) < 4)
+		goto out;
+
+	expect_eq(tftp_window_cache_size(cache), 4);
+
+	/* sequence 1 */
+	expect_ok (tftp_window_cache_insert(cache, 20, "20", 2));
+	expect_ok (tftp_window_cache_insert(cache, 22, "22", 2));
+	expect_ok (tftp_window_cache_insert(cache, 21, "21", 2));
+	expect_ok (tftp_window_cache_insert(cache, 23, "23", 2));
+	expect_err(tftp_window_cache_insert(cache, 24, "24", 2));
+	expect_err(tftp_window_cache_insert(cache, 19, "19", 2));
+	expect_ok (tftp_window_cache_insert(cache, 22, "22", 2));
+	expect_ok (tftp_window_cache_insert(cache, 20, "20", 2));
+
+	expect_eq(tftp_window_cache_pop(cache)->id, 20);
+	expect_eq(tftp_window_cache_pop(cache)->id, 21);
+	expect_eq(tftp_window_cache_pop(cache)->id, 22);
+	expect_eq(tftp_window_cache_pop(cache)->id, 23);
+	expect_eq(cache->id, TFTP_CACHE_NO_ID);
+
+	/* sequence 2 */
+	expect_ok (tftp_window_cache_insert(cache, 30, "30", 2));
+	expect_ok (tftp_window_cache_insert(cache, 32, "32", 2));
+	expect_err(tftp_window_cache_insert(cache, 34, "34", 2));
+
+	expect_it(tftp_window_cache_starts_with(cache, 30));
+	expect_eq(tftp_window_cache_pop(cache)->id, 30);
+
+	expect_ok (tftp_window_cache_insert(cache, 34, "34", 2));
+	expect_err(tftp_window_cache_insert(cache, 35, "35", 2));
+
+	expect_it(!tftp_window_cache_starts_with(cache, 30));
+	expect_it(!tftp_window_cache_starts_with(cache, 31));
+	expect_it(!tftp_window_cache_starts_with(cache, 32));
+	expect_NULL(tftp_window_cache_pop(cache));
+
+	expect_it(tftp_window_cache_starts_with(cache, 32));
+	expect_eq(tftp_window_cache_pop(cache)->id, 32);
+
+	expect_NULL(tftp_window_cache_pop(cache));
+	expect_eq(tftp_window_cache_pop(cache)->id, 34);
+
+	expect_eq(cache->id, TFTP_CACHE_NO_ID);
+
+	/* sequence 3 */
+	expect_ok(tftp_window_cache_insert(cache, 40, "40", 2));
+	expect_ok(tftp_window_cache_insert(cache, 42, "42", 2));
+	expect_ok(tftp_window_cache_insert(cache, 43, "43", 2));
+
+	expect_it(!tftp_window_cache_remove_id(cache, 30));
+	expect_it(!tftp_window_cache_remove_id(cache, 41));
+	expect_it(!tftp_window_cache_remove_id(cache, 44));
+
+	expect_it( tftp_window_cache_remove_id(cache, 42));
+	expect_it(!tftp_window_cache_remove_id(cache, 42));
+
+out:
+	tftp_window_cache_free(cache);
+
+	return 0;
+}
+#ifdef CONFIG_SELFTEST_TFTP
+bselftest(core, tftp_window_cache_selftest);
+#endif
diff --git a/test/self/Kconfig b/test/self/Kconfig
index cf11efe54486..9366f99300c5 100644
--- a/test/self/Kconfig
+++ b/test/self/Kconfig
@@ -29,6 +29,7 @@ config SELFTEST_ENABLE_ALL
 	bool "Enable all self-tests"
 	select SELFTEST_PRINTF
 	select SELFTEST_PROGRESS_NOTIFIER
+	imply SELFTEST_TFTP
 	help
 	  Selects all self-tests compatible with current configuration
 
@@ -51,4 +52,10 @@ config SELFTEST_OF_MANIPULATION
 config SELFTEST_PROGRESS_NOTIFIER
 	bool "progress notifier selftest"
 
+config SELFTEST_TFTP
+	bool "tftp selftest"
+	depends on FS_TFTP
+	help
+	  Tests tftp functionality
+
 endif
-- 
2.37.2




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

* [PATCH v4 20/21] tftp: accept OACK + DATA datagrams only in certain states
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (18 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 19/21] tftp: add selftest Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-30  7:38 ` [PATCH v4 21/21] tftp: add some documentation about windowsize support Enrico Scholz
  2022-08-31  6:40 ` [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Sascha Hauer
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

These packets are valid in certain points of the transfer only and
accepting them too early or too late can corrupt internal states.

Reject them when they are unexpected.

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 fs/tftp.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/fs/tftp.c b/fs/tftp.c
index a9cc0ff3b118..2bffae2bf36e 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -713,6 +713,12 @@ static void tftp_recv(struct file_priv *priv,
 		break;
 
 	case TFTP_OACK:
+		if (priv->state != STATE_RRQ && priv->state != STATE_WRQ) {
+			pr_warn("OACK packet in %s state\n",
+				tftp_states[priv->state]);
+			break;
+		}
+
 		priv->tftp_con->udp->uh_dport = uh_sport;
 
 		if (tftp_parse_oack(priv, pkt, len) < 0) {
@@ -741,6 +747,12 @@ static void tftp_recv(struct file_priv *priv,
 				break;
 		}
 
+		if (priv->state != STATE_RDATA) {
+			pr_warn("DATA packet in %s state\n",
+				tftp_states[priv->state]);
+			break;
+		}
+
 		tftp_handle_data(priv, block, pkt + 2, len);
 		break;
 
-- 
2.37.2




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

* [PATCH v4 21/21] tftp: add some documentation about windowsize support
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (19 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 20/21] tftp: accept OACK + DATA datagrams only in certain states Enrico Scholz
@ 2022-08-30  7:38 ` Enrico Scholz
  2022-08-31  6:40 ` [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Sascha Hauer
  21 siblings, 0 replies; 23+ messages in thread
From: Enrico Scholz @ 2022-08-30  7:38 UTC (permalink / raw)
  To: barebox; +Cc: Enrico Scholz

Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
---
 Documentation/filesystems/tftp.rst | 38 ++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/Documentation/filesystems/tftp.rst b/Documentation/filesystems/tftp.rst
index a292765e2511..8929213d3c4a 100644
--- a/Documentation/filesystems/tftp.rst
+++ b/Documentation/filesystems/tftp.rst
@@ -20,3 +20,41 @@ Example:
 
 In addition to the TFTP filesystem implementation, barebox does also have a
 :ref:`tftp command <command_tftp>`.
+
+RFC 7440 "windowsize" support
+=============================
+
+barebox supports the tftp windowsize option for downloading files.  It
+is not implemented for uploads.
+
+Generally, this option greatly improves the download speed (factors
+4-30 are not uncommon).  But choosing a too large windowsize can have
+the opposite effect.  Performance depends on:
+
+ - the network infrastructure: when the tftp server sends files with
+   1Gb/s but there are components in the network (switches or the
+   target nic) which support only 100 Mb/s, packets will be dropped.
+
+   The lower the internal buffers of the bottleneck components, the
+   lower the optimal window size.
+
+   In practice (iMX8MP on a Netgear GS108Ev3 with a port configured to
+   100 Mb/s) it had to be reduced to
+
+   .. code-block:: console
+     global tftp.windowsize=26
+
+   for example.
+
+ - the target network driver: datagrams from server will arive faster
+   than they can be processed and must be buffered internally.  For
+   example, the `fec-imx` driver reserves place for
+
+   .. code-block:: c
+     #define FEC_RBD_NUM		64
+
+   packets before they are dropped
+
+ - partially the workload: copying downloaded files to ram will be
+   faster than burning them into flash.  Latter can consume internal
+   buffers quicker so that windowsize might be reduced
-- 
2.37.2




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

* Re: [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp
  2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
                   ` (20 preceding siblings ...)
  2022-08-30  7:38 ` [PATCH v4 21/21] tftp: add some documentation about windowsize support Enrico Scholz
@ 2022-08-31  6:40 ` Sascha Hauer
  21 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2022-08-31  6:40 UTC (permalink / raw)
  To: Enrico Scholz; +Cc: barebox

On Tue, Aug 30, 2022 at 09:37:55AM +0200, Enrico Scholz wrote:
> The tftp "windowsize" greatly improves the performance of tftp
> transfers.  This patchset adds support for it.
> 
> The first two patches are a little bit unrelated and enhance the 'cp
> -v' output by giving information about the transfer speed.  They can
> be dropped if they are unwanted.
> 
> I tested the function with an iMX8MP platform in three environments:
> 
>   - at home over OpenVPN on an ADSL 50 line  -->  27x speedup
>   - 1 Gb/s connection --> 9x speedup
>   - connection over 100 Mb/s switch  -->  4x speedup
> 
> In the test, I downloaded variable sized files which were filled from
> /dev/urandom.  E.g.
> 
> | :/ global tftp.windowsize=128
> | :/ cp -v /mnt/tftp/data-100MiB /tmp/data && sha1sum /tmp/data
> |         [################################################################] 104857600 bytes, 98550375 bytes/s
> 
> For slow connection speeds, smaller files (1MiB, 4 MiB + 20 MiB) were
> used.

Applied, thanks

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



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

end of thread, other threads:[~2022-08-31  6:41 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-30  7:37 [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Enrico Scholz
2022-08-30  7:37 ` [PATCH v4 01/21] tftp: add some 'const' annotations Enrico Scholz
2022-08-30  7:37 ` [PATCH v4 02/21] tftp: allow to change tftp port Enrico Scholz
2022-08-30  7:37 ` [PATCH v4 03/21] cmd:tftp: add '-P' option to set tftp server port number Enrico Scholz
2022-08-30  7:37 ` [PATCH v4 04/21] tftp: do not set 'tsize' in WRQ requests Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 05/21] tftp: assign 'priv->block' later in WRQ Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 06/21] tftp: minor refactoring of RRQ/WRQ packet generation code Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 07/21] tftp: replace hardcoded blksize by global constant Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 08/21] tftp: remove sanity check of first block Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 09/21] tftp: add debug_assert() macro Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 10/21] tftp: allocate buffers and fifo dynamically Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 11/21] tftp: add sanity check for OACK response Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 12/21] tftp: record whether tftp file is opened for lookup operation only Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 13/21] tftp: reduce block size on lookup requests Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 14/21] tftp: refactor data processing Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 15/21] tftp: detect out-of-memory situations Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 16/21] tftp: implement 'windowsize' (RFC 7440) support Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 17/21] tftp: do not use 'priv->block' for RRQ Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 18/21] tftp: reorder tftp packets Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 19/21] tftp: add selftest Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 20/21] tftp: accept OACK + DATA datagrams only in certain states Enrico Scholz
2022-08-30  7:38 ` [PATCH v4 21/21] tftp: add some documentation about windowsize support Enrico Scholz
2022-08-31  6:40 ` [PATCH v4 00/21] add "windowsize" (RFC 7440) support for tftp Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox