From: Matthias Brugger <matthias.bgg@gmail.com>
To: u-boot@lists.denx.de
Subject: [PATCH v4] net: tftp: Add client support for RFC 7440
Date: Sat, 23 May 2020 19:39:58 +0200 [thread overview]
Message-ID: <b561df84-4557-3f2f-b732-fe595ecdc0ad@gmail.com> (raw)
In-Reply-To: <20200519192557.18075-1-rfried.dev@gmail.com>
On 19/05/2020 21:25, Ramon Fried wrote:
> Add support for RFC 7440: "TFTP Windowsize Option".
>
> This optional feature allows the client and server
> to negotiate a window size of consecutive blocks to send as an
> alternative for replacing the single-block lockstep schema.
>
> windowsize can be defined statically during compilation by
> setting CONFIG_TFTP_WINDOWSIZE, or defined in runtime by
> setting an environment variable: "tftpwindowsize"
> If not defined, the windowsize is set to 1, meaning that it
> behaves as it was never defined.
>
> Choosing the appropriate windowsize depends on the specific
> network topology, underlying NIC.
> You should test various windowsize scenarios and see which
> best work for you.
>
> Setting a windowsize too big can actually decreases performance.
>
> Signed-off-by: Ramon Fried <rfried.dev@gmail.com>
> Reviewed-by: Marek Vasut <marex@denx.de>
> ---
> v2:
> * Don't send windowsize option on tftpput, as it's not implemented yet.
> * Don't send NACK for every out of order block that arrives, one nack
> is enough.
> v3:
> * Add option CONFIG_TFTP_WINDOWSIZE to kconfig with default 1.
> * Fixed some spelling errors.
> * Took assignment out of a loop.
> * simplified variable increment.
> v4:
> * send ack for last packet, so the server can finish
> the tranfer gracefully and not in timeout.
>
> README | 5 ++++
> net/Kconfig | 9 ++++++
> net/tftp.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++------
> 3 files changed, 87 insertions(+), 8 deletions(-)
>
> diff --git a/README b/README
> index be9e6391d6..686474a2f1 100644
> --- a/README
> +++ b/README
> @@ -3522,6 +3522,11 @@ List of environment variables (most likely not complete):
> downloads succeed with high packet loss rates, or with
> unreliable TFTP servers or client hardware.
>
> + tftpwindowsize - if this is set, the value is used for TFTP's
> + window size as described by RFC 7440.
> + This means the count of blocks we can receive before
> + sending ack to server.
> +
> vlan - When set to a value < 4095 the traffic over
> Ethernet is encapsulated/received over 802.1q
> VLAN tagged frames.
> diff --git a/net/Kconfig b/net/Kconfig
> index ac6d0cf8a6..7916ae305f 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -49,4 +49,13 @@ config TFTP_BLOCKSIZE
> almost-MTU block sizes.
> You can also activate CONFIG_IP_DEFRAG to set a larger block.
>
> +config TFTP_WINDOWSIZE
> + int "TFTP window size"
> + default 1
> + help
> + Default TFTP window size.
> + RFC7440 defines an optional window size of transmits,
> + before an ack response is required.
> + The default TFTP implementation implies a window size of 1.
> +
> endif # if NET
> diff --git a/net/tftp.c b/net/tftp.c
> index be24e63075..72d23e1574 100644
> --- a/net/tftp.c
> +++ b/net/tftp.c
> @@ -5,7 +5,6 @@
> * Copyright 2011 Comelit Group SpA,
> * Luca Ceresoli <luca.ceresoli@comelit.it>
> */
> -
> #include <common.h>
> #include <command.h>
> #include <efi_loader.h>
> @@ -95,6 +94,12 @@ static int tftp_tsize;
> /* The number of hashes we printed */
> static short tftp_tsize_num_hash;
> #endif
> +/* The window size negotiated */
> +static ushort tftp_windowsize;
> +/* Next block to send ack to */
> +static ushort tftp_next_ack;
> +/* Last nack block we send */
> +static ushort tftp_last_nack;
> #ifdef CONFIG_CMD_TFTPPUT
> /* 1 if writing, else 0 */
> static int tftp_put_active;
> @@ -134,8 +139,19 @@ static char tftp_filename[MAX_LEN];
> * (but those using CONFIG_IP_DEFRAG may want to set a larger block in cfg file)
> */
>
> +/* When windowsize is defined to 1,
> + * tftp behaves the same way as it was
> + * never declared
> + */
> +#ifdef CONFIG_TFTP_WINDOWSIZE
> +#define TFTP_WINDOWSIZE CONFIG_TFTP_WINDOWSIZE
> +#else
> +#define TFTP_WINDOWSIZE 1
> +#endif
> +
> static unsigned short tftp_block_size = TFTP_BLOCK_SIZE;
> static unsigned short tftp_block_size_option = CONFIG_TFTP_BLOCKSIZE;
> +static unsigned short tftp_window_size_option = TFTP_WINDOWSIZE;
>
> static inline int store_block(int block, uchar *src, unsigned int len)
> {
> @@ -348,6 +364,14 @@ static void tftp_send(void)
> /* try for more effic. blk size */
> pkt += sprintf((char *)pkt, "blksize%c%d%c",
> 0, tftp_block_size_option, 0);
> +
> + /* try for more effic. window size.
> + * Implemented only for tftp get.
> + * Don't bother sending if it's 1
> + */
> + if (tftp_state == STATE_SEND_RRQ && tftp_window_size_option > 1)
I think it makes more sense to check:
if (tftp_window_size_option > 1 && tftp_state == STATE_SEND_RRQ)
Because I understand that the tftp_state will change while the
tftp_window_size_option is set or at compile time or through the environment. So
we can save the check of the tftp_state if we have the default value.
Regards,
Matthias
> + pkt += sprintf((char *)pkt, "windowsize%c%d%c",
> + 0, tftp_window_size_option, 0);
> len = pkt - xp;
> break;
>
> @@ -500,7 +524,18 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
> (char *)pkt + i + 6, tftp_tsize);
> }
> #endif
> + if (strcmp((char *)pkt + i, "windowsize") == 0) {
> + tftp_windowsize =
> + simple_strtoul((char *)pkt + i + 11,
> + NULL, 10);
> + debug("windowsize = %s, %d\n",
> + (char *)pkt + i + 11, tftp_windowsize);
> + }
> +
> }
> +
> + tftp_next_ack = tftp_windowsize;
> +
> #ifdef CONFIG_CMD_TFTPPUT
> if (tftp_put_active) {
> /* Get ready to send the first block */
> @@ -514,12 +549,32 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
> if (len < 2)
> return;
> len -= 2;
> - tftp_cur_block = ntohs(*(__be16 *)pkt);
> +
> + if (ntohs(*(__be16 *)pkt) != (ushort)(tftp_cur_block + 1)) {
> + debug("Received unexpected block: %d, expected: %d\n",
> + ntohs(*(__be16 *)pkt),
> + (ushort)(tftp_cur_block + 1));
> + /*
> + * If one packet is dropped most likely
> + * all other buffers in the window
> + * that will arrive will cause a sending NACK.
> + * This just overwellms the server, let's just send one.
> + */
> + if (tftp_last_nack != tftp_cur_block) {
> + tftp_send();
> + tftp_last_nack = tftp_cur_block;
> + tftp_next_ack = (ushort)(tftp_cur_block +
> + tftp_windowsize);
> + }
> + break;
> + }
> +
> + tftp_cur_block++;
>
> update_block_number();
>
> if (tftp_state == STATE_SEND_RRQ)
> - debug("Server did not acknowledge timeout option!\n");
> + debug("Server did not acknowledge the options!\n");
>
> if (tftp_state == STATE_SEND_RRQ || tftp_state == STATE_OACK ||
> tftp_state == STATE_RECV_WRQ) {
> @@ -557,10 +612,15 @@ static void tftp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
> * Acknowledge the block just received, which will prompt
> * the remote for the next one.
> */
> - tftp_send();
> + if (tftp_cur_block == tftp_next_ack) {
> + tftp_send();
> + tftp_next_ack += tftp_windowsize;
> + }
>
> - if (len < tftp_block_size)
> + if (len < tftp_block_size) {
> + tftp_send();
> tftp_complete();
> + }
> break;
>
> case TFTP_ERROR:
> @@ -634,6 +694,10 @@ void tftp_start(enum proto_t protocol)
> if (ep != NULL)
> tftp_block_size_option = simple_strtol(ep, NULL, 10);
>
> + ep = env_get("tftpwindowsize");
> + if (ep != NULL)
> + tftp_window_size_option = simple_strtol(ep, NULL, 10);
> +
> ep = env_get("tftptimeout");
> if (ep != NULL)
> timeout_ms = simple_strtol(ep, NULL, 10);
> @@ -655,8 +719,8 @@ void tftp_start(enum proto_t protocol)
> }
> #endif
>
> - debug("TFTP blocksize = %i, timeout = %ld ms\n",
> - tftp_block_size_option, timeout_ms);
> + debug("TFTP blocksize = %i, TFTP windowsize = %d timeout = %ld ms\n",
> + tftp_block_size_option, tftp_window_size_option, timeout_ms);
>
> tftp_remote_ip = net_server_ip;
> if (!net_parse_bootfile(&tftp_remote_ip, tftp_filename, MAX_LEN)) {
> @@ -752,7 +816,8 @@ void tftp_start(enum proto_t protocol)
> tftp_our_port = simple_strtol(ep, NULL, 10);
> #endif
> tftp_cur_block = 0;
> -
> + tftp_windowsize = 1;
> + tftp_last_nack = 0;
> /* zero out server ether in case the server ip has changed */
> memset(net_server_ethaddr, 0, 6);
> /* Revert tftp_block_size to dflt */
>
next prev parent reply other threads:[~2020-05-23 17:39 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-05-19 19:25 [PATCH v4] net: tftp: Add client support for RFC 7440 Ramon Fried
2020-05-22 0:29 ` rahasij
2020-05-22 19:19 ` Ramon Fried
2020-05-23 17:39 ` Matthias Brugger [this message]
2020-05-23 17:59 ` Ramon Fried
2020-06-03 2:54 ` Ravik Hasija
2020-06-04 16:17 ` Ramon Fried
2020-06-04 17:51 ` Ravik Hasija
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=b561df84-4557-3f2f-b732-fe595ecdc0ad@gmail.com \
--to=matthias.bgg@gmail.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox