[PATCH 2/3] tftp: implement UDP reorder cache using lists

Enrico Scholz enrico.scholz at sigma-chemnitz.de
Fri Sep 16 07:12:07 PDT 2022


Sascha Hauer <s.hauer at pengutronix.de> writes:

> The UDP reorder cache can be much easier implemented using lists.
> As a bonus the cache grows and shrinks on demand and no fixed size
> has to be configured at compile time.

There are three variants of the cache

- small, fixed sized array with linear search (very first
  implementation)  -->  O(n)
- list --> O(n) on insert, O(1) on lookup
- bitmap --> O(1)


Performance wise, I think the list implementation is the slowest one
(although the fixed sized array is O(n) on every operation, this should
be still faster for small n than the list operations and the related
memory management).

>From code size, the list implementation is in the middle.

I am not sure whether dynamic shrinking/growing of the cache is so
important or whether a small, fixed sized cache suffices.  In my
experience, only the first couple of packets really matter regarding
reordering.

Of course, the 'kfifo' could be replaced completely by a custom buffer
implementation where packets are inserted at the correct position.  O(1)
for every operation + zero additional memory.


> -static inline bool is_block_before(uint16_t a, uint16_t b)
> -{
> -	return (int16_t)(b - a) > 0;
> -}
> -
> ....
>  static int tftp_window_cache_insert(struct tftp_cache *cache, uint16_t id,
>  				    void const *data, size_t len)
>  {
> ...
> +	list_for_each_entry(block, &cache->blocks, list) {

fwiw, iterating in the reverse direction will find the position probably
faster


> +		if (block->id == id)
> +			return 0;
> +		if (block->id < id)

This will break when wrapping at 65535; the removed 'is_block_before()'
function was written for this case.


> @@ -614,12 +431,26 @@ static void tftp_apply_window_cache(struct file_priv *priv)
>  		if (priv->state != STATE_RDATA)
>  			return;
>  
> -		block = tftp_window_cache_pop(cache);
> +		if (list_empty(&cache->blocks))
> +			return;
>  
> -		debug_assert(block);
> -		debug_assert(block->id == (uint16_t)(priv->last_block + 1));
> +		block = list_first_entry(&cache->blocks, struct tftp_block, list);
> +
> +		if (block->id < priv->last_block + 1) {

ditto; wrapping at 65535


> +			/* shouldn't happen, but be sure */
> +			list_del(&block->list);
> +			free(block);
> +			continue;
> +		}
> +
> +		if (block->id != priv->last_block + 1)

ditto; wrapping at 65535.  Resp. should be written as

|		if (block->id != (uint16_t)(priv->last_block + 1))



Enrico



More information about the barebox mailing list