[v2,6/7] batman-adv: ELP - use tp meter to estimate the throughput if otherwise not available

Message ID 20180518014754.23644-7-mareklindner@neomailbox.ch (mailing list archive)
State Superseded, archived
Delegated to: Simon Wunderlich
Headers
Series B.A.T.M.A.N. V - fallback to tp meter estimation if throughput otherwise not available |

Commit Message

Marek Lindner May 18, 2018, 1:47 a.m. UTC
  Signed-off-by: Marek Lindner <mareklindner@neomailbox.ch>
---
 net/batman-adv/bat_v_elp.c | 63 ++++++++++++++++++++++++++++++++++++--
 net/batman-adv/bat_v_elp.h | 21 +++++++++++++
 net/batman-adv/main.h      |  1 +
 net/batman-adv/tp_meter.c  | 37 +++++++++++++++++-----
 net/batman-adv/types.h     | 14 +++++++++
 5 files changed, 126 insertions(+), 10 deletions(-)
  

Comments

Linus Lüssing May 21, 2018, 1:17 p.m. UTC | #1
On Fri, May 18, 2018 at 09:47:53AM +0800, Marek Lindner wrote:
> @@ -251,6 +253,21 @@ static void batadv_tp_caller_notify(struct batadv_priv *bat_priv,
>  
>  		break;
>  	case BATADV_TP_ELP:
> +		if (reason_is_error) {
> +			batadv_v_elp_tp_fail(tp_vars->hardif_neigh);
> +			return;
> +		}
> +
> +		test_time = jiffies_to_msecs(jiffies - tp_vars->start_time);
> +		total_bytes = atomic64_read(&tp_vars->tot_sent);
> +
> +		/* The following calculation includes these steps:
> +		 * - convert bytes to bits
> +		 * - divide bits by the test length (msecs)
> +		 * - convert result from bits/ms to 0.1Mb/s (* 1024 * 10 / 1000)
> +		 */
> +		throughput = total_bytes * 8 >> ilog2(test_time) / 10;
> +		batadv_v_elp_tp_finish(tp_vars->hardif_neigh, throughput);

I find the throughput calculation quite hard to read here, would
it be possible to put this into an extra (inline?) function?

Also the comment for the "convert result..." seems wrong,
"[bits/ms]*1024*10/1000" would be 0.01Mb/s, not 0.1Mb/s?

What is the advantage of using the ilog2 and shift operator here
compared to plain multiplications and divisions?

Also, when trying this in a small C program I get weird results:

-----
#include <stdio.h>
#include <math.h>

int main()
{
        unsigned long test_time = 10000;        // 10s
        unsigned long total_bytes = 20000000;   // 16MBit/s
        unsigned long throughput, throughput2;
        unsigned long log_test_time = log(test_time) / log(2);

        throughput = total_bytes * 8 >> log_test_time / 10;

	// Straightforward approach?
        throughput2 = total_bytes * 8 / test_time * 1000 / 1024 / 100;

        printf("Result: %lu (log_test_time: %lu)\n", throughput, log_test_time);
        printf("Result2: %lu\n", throughput2);

        return 0;
}
-----
$ ./test
Result: 80000000 (log_test_time: 13)
Result2: 156
$ file ./test
./test: ELF 32-bit LSB pie executable ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=d18f32829cdd2bc42cf744cdcafde7cdbd315cb0, not stripped
-----


Regards, Linus
  
Linus Lüssing May 21, 2018, 2:43 p.m. UTC | #2
On Fri, May 18, 2018 at 09:47:53AM +0800, Marek Lindner wrote:
> +fallback_throughput:
> +	last_tp_run_jiffies = jiffies - neigh->bat_v.last_tp_meter_run;
> +	last_tp_run_msecs = jiffies_to_msecs(last_tp_run_jiffies);
> +
> +	/* check the tp_meter_running flag before checking the timestamp to
> +	 * avoid a race condition where a new tp meter session is scheduled
> +	 * right after the previous tp meter session has completed
> +	 */
> +	if (!neigh->bat_v.tp_meter_running &&
> +	    last_tp_run_msecs > BATADV_ELP_TP_RUN_INTERVAL)
> +		batadv_v_elp_tp_start(neigh);
> +
> +	/* discard too old tp test results */
> +	if (last_tp_run_msecs > 2 * BATADV_ELP_TP_RUN_INTERVAL)
> +		neigh->bat_v.tp_meter_throughput = 0;
> +

So far we were either using time_before(), time_after_eq() or our
own wrapper batadv_has_timed_out(). Would it make sense to use
some of these here, too?
  
Linus Lüssing May 21, 2018, 2:48 p.m. UTC | #3
On Fri, May 18, 2018 at 09:47:53AM +0800, Marek Lindner wrote:
> +/**
> + * batadv_v_elp_tp_start() - start a tp meter session for a neighbor
> + * @neigh: neighbor to run tp meter on
> + */
> +static void batadv_v_elp_tp_start(struct batadv_hardif_neigh_node *neigh)
> +{
> +	struct batadv_hard_iface *hard_iface = neigh->if_incoming;
> +	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
> +
> +	neigh->bat_v.tp_meter_running = true;
> +	batadv_tp_start(bat_priv, neigh->addr, neigh, 10, NULL, BATADV_TP_ELP);
> +}

This seems racy here? We have no ordering constraints between
the assigment and batadv_tp_start() which might lead to...

> @@ -152,6 +192,25 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
>  			return throughput * 10;
>  	}
>  
> +fallback_throughput:
> +	last_tp_run_jiffies = jiffies - neigh->bat_v.last_tp_meter_run;
> +	last_tp_run_msecs = jiffies_to_msecs(last_tp_run_jiffies);
> +
> +	/* check the tp_meter_running flag before checking the timestamp to
> +	 * avoid a race condition where a new tp meter session is scheduled
> +	 * right after the previous tp meter session has completed
> +	 */
> +	if (!neigh->bat_v.tp_meter_running &&
> +	    last_tp_run_msecs > BATADV_ELP_TP_RUN_INTERVAL)
> +		batadv_v_elp_tp_start(neigh);

...multiple instances potentially being started at the same time here?
  
Linus Lüssing May 21, 2018, 3:01 p.m. UTC | #4
On Fri, May 18, 2018 at 09:47:53AM +0800, Marek Lindner wrote:
> diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
> index 0a9bee88..148f23ad 100644
> --- a/net/batman-adv/types.h
> +++ b/net/batman-adv/types.h
> @@ -583,6 +583,20 @@ struct batadv_hardif_neigh_node_bat_v {
>  
>  	/** @metric_work: work queue callback item for metric update */
>  	struct work_struct metric_work;
> +
> +	/**
> +	 * @tp_meter_running: tp meter measurements towards this neighbor in
> +	 * progress
> +	 */
> +	unsigned char tp_meter_running:1;

I'm currently wondering: Would it make sense to prevent parallel
tp meter execution not only to a specific neighbor but parallel
executions on a hard-interface in general?
  
Sven Eckelmann May 21, 2018, 4:36 p.m. UTC | #5
On Freitag, 18. Mai 2018 03:47:53 CEST Marek Lindner wrote:
> Signed-off-by: Marek Lindner <mareklindner@neomailbox.ch>
> ---
>  net/batman-adv/bat_v_elp.c | 63 ++++++++++++++++++++++++++++++++++++--
>  net/batman-adv/bat_v_elp.h | 21 +++++++++++++
>  net/batman-adv/main.h      |  1 +
>  net/batman-adv/tp_meter.c  | 37 +++++++++++++++++-----
>  net/batman-adv/types.h     | 14 +++++++++
>  5 files changed, 126 insertions(+), 10 deletions(-)

This seems to create weird (as in incorrect) looking results. Here the ones 
from Matthias Fritzsche:

[2018-05-21 18:19:41] <txt-file> ordexm, marec, ecsv_: Now I have one router and the its neighbour with batman_V with tp-meter. https://paste.debian.net/1025691/
[2018-05-21 18:24:28] <txt-file> ah … now "batctl n" shows a tp of 16064.1
[2018-05-21 18:29:07] <ecsv_> uhm, isn't this rather... high
[2018-05-21 18:29:53] <txt-file> ecsv_: I think so. This node has an upload of 4 MBit/s
[2018-05-21 18:30:34] <txt-file> now (6 minutes later) tq=2.2
[2018-05-21 18:30:48] <txt-file> which sounds much more realistic.

The setup is using an gluon based router (TP-Link TL-WDR3600 v1) [1] which 
uses a vpn connection (fastd) to a server 
(descartes.routers.chemnitz.freifunk.net) in the internet. The automatic 
throughput test runs between these two devices.

Kind regards,
	Sven

[1] https://meshviewer.chemnitz.freifunk.net/#!v:m;n:c04a00e44be0
  
Sven Eckelmann May 21, 2018, 5:51 p.m. UTC | #6
On Montag, 21. Mai 2018 15:17:11 CEST Linus Lüssing wrote:
> What is the advantage of using the ilog2 and shift operator here
> compared to plain multiplications and divisions?

They most likely ran into the problem that they must do a 64 bit division 
(which cannot be done on 32 bit machines in the kernel directly with C's 
native "x / y" statement ). But in this case, it is most likely a lot better 
to use do_div (or similar function) to avoid a lot of rounding problems which 
this shift approach has.

Kind regards,
	Sven
  
Sven Eckelmann May 21, 2018, 7:06 p.m. UTC | #7
On Montag, 21. Mai 2018 15:17:11 CEST Linus Lüssing wrote:
> > +               throughput = total_bytes * 8 >> ilog2(test_time) / 10;
[...]
> -----
[...]
> 
>         throughput = total_bytes * 8 >> log_test_time / 10;
> 
>         // Straightforward approach?
>         throughput2 = total_bytes * 8 / test_time * 1000 / 1024 / 100;
[...]
> -----
> $ ./test
> Result: 80000000 (log_test_time: 13)
> Result2: 156
> $ file ./test
> ./test: ELF 32-bit LSB pie executable ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=d18f32829cdd2bc42cf744cdcafde7cdbd315cb0, not stripped
> -----

Thanks for this small example program. Yes, there are parenthesis missing in 
the calculation. Right now, following is calculated:

    (total_bytes * 8) >> (ilog2(test_time) / 10);

But the author most likely wanted following precedence:

    ((total_bytes * 8) >> ilog2(test_time)) / 10;

And together with the fixed unit, you would get:

    (total_bytes * 8 >> ilog2(test_time) / 100;

Your example program would then show following result because the shifting 
stuff is still the wrong approach:

    Result: 195 (log_test_time: 13)
    Result2: 156

The calculation still has to be changed to something like this to get 

    // when 0.1 Mbit/s == 100 kbit/s
    throughput = total_bytes * 5;
	 do_div(throughput, test_time * 64);

    // when 0.1 Mbit/s == 102.4 kbit/s
    throughput = total_bytes * 625;
	 do_div(throughput, test_time * 8192);

    // when 0.1 Mbit/s == 100 kbit/s, and 1kbit/s == 1000 bit (instead of 1024 bit):
    throughput = total_bytes;
    do_div(throughput, test_time * 125);

Please keep in mind that we must do a check of the divisor (for 0) before 
doing this do_div.

Kind regards,
	Sven
  
Antonio Quartulli Aug. 4, 2018, 8:59 a.m. UTC | #8
Hi,

On 21/05/18 23:01, Linus Lüssing wrote:
> On Fri, May 18, 2018 at 09:47:53AM +0800, Marek Lindner wrote:
>> diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
>> index 0a9bee88..148f23ad 100644
>> --- a/net/batman-adv/types.h
>> +++ b/net/batman-adv/types.h
>> @@ -583,6 +583,20 @@ struct batadv_hardif_neigh_node_bat_v {
>>  
>>  	/** @metric_work: work queue callback item for metric update */
>>  	struct work_struct metric_work;
>> +
>> +	/**
>> +	 * @tp_meter_running: tp meter measurements towards this neighbor in
>> +	 * progress
>> +	 */
>> +	unsigned char tp_meter_running:1;
> 
> I'm currently wondering: Would it make sense to prevent parallel
> tp meter execution not only to a specific neighbor but parallel
> executions on a hard-interface in general?
> 

This flag prevents scheduling new measurements to this neighbour if one
has already been scheduled and has not returned any result yet.

What you are looking for is implemented in patch 1/7: a system-wide
queue prevents concurrent measurements at all. All the measurements are
serialized and performed one after the other.


Cheers,
  
Antonio Quartulli Aug. 4, 2018, 9:31 a.m. UTC | #9
Hi,

On 22/05/18 03:06, Sven Eckelmann wrote:
> On Montag, 21. Mai 2018 15:17:11 CEST Linus Lüssing wrote:
>>> +               throughput = total_bytes * 8 >> ilog2(test_time) / 10;
> [...]
>> -----
> [...]
>>
>>         throughput = total_bytes * 8 >> log_test_time / 10;
>>
>>         // Straightforward approach?
>>         throughput2 = total_bytes * 8 / test_time * 1000 / 1024 / 100;
> [...]
>> -----
>> $ ./test
>> Result: 80000000 (log_test_time: 13)
>> Result2: 156
>> $ file ./test
>> ./test: ELF 32-bit LSB pie executable ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=d18f32829cdd2bc42cf744cdcafde7cdbd315cb0, not stripped
>> -----
> 
> Thanks for this small example program. Yes, there are parenthesis missing in 
> the calculation. Right now, following is calculated:
> 
>     (total_bytes * 8) >> (ilog2(test_time) / 10);
> 
> But the author most likely wanted following precedence:
> 
>     ((total_bytes * 8) >> ilog2(test_time)) / 10;
> 
> And together with the fixed unit, you would get:
> 
>     (total_bytes * 8 >> ilog2(test_time) / 100;
> 
> Your example program would then show following result because the shifting 
> stuff is still the wrong approach:
> 
>     Result: 195 (log_test_time: 13)
>     Result2: 156
> 
> The calculation still has to be changed to something like this to get 
> 
>     // when 0.1 Mbit/s == 100 kbit/s
>     throughput = total_bytes * 5;
> 	 do_div(throughput, test_time * 64);
> 
>     // when 0.1 Mbit/s == 102.4 kbit/s
>     throughput = total_bytes * 625;
> 	 do_div(throughput, test_time * 8192);
> 
>     // when 0.1 Mbit/s == 100 kbit/s, and 1kbit/s == 1000 bit (instead of 1024 bit):
>     throughput = total_bytes;
>     do_div(throughput, test_time * 125);
> 
> Please keep in mind that we must do a check of the divisor (for 0) before 
> doing this do_div.

Thanks for the code and the correction :-)

Yes, we wanted to avoid 64 bits explicit divisions and we assumed
test_time would have been a power of 2 (otherwise we'd accept a small
error).

Anyway, the last case brought by Sven depicts what we want to implement.
The patch will be changed accordingly.

Cheers,
  
Marek Lindner Aug. 4, 2018, 9:35 a.m. UTC | #10
On Monday, 21 May 2018 22:43:04 HKT Linus Lüssing wrote:
> On Fri, May 18, 2018 at 09:47:53AM +0800, Marek Lindner wrote:
> > +fallback_throughput:
> > +       last_tp_run_jiffies = jiffies - neigh->bat_v.last_tp_meter_run;
> > +       last_tp_run_msecs = jiffies_to_msecs(last_tp_run_jiffies);
> > +
> > +       /* check the tp_meter_running flag before checking the timestamp
> > to
> > +        * avoid a race condition where a new tp meter session is
> > scheduled
> > +        * right after the previous tp meter session has completed
> > +        */
> > +       if (!neigh->bat_v.tp_meter_running &&
> > +           last_tp_run_msecs > BATADV_ELP_TP_RUN_INTERVAL)
> > +               batadv_v_elp_tp_start(neigh);
> > +
> > +       /* discard too old tp test results */
> > +       if (last_tp_run_msecs > 2 * BATADV_ELP_TP_RUN_INTERVAL)
> > +               neigh->bat_v.tp_meter_throughput = 0;
> > +
> 
> So far we were either using time_before(), time_after_eq() or our
> own wrapper batadv_has_timed_out(). Would it make sense to use
> some of these here, too?

Good point. That changes has been staged for v3.

Cheers,
Marek
  
Antonio Quartulli Aug. 4, 2018, 9:39 a.m. UTC | #11
Hi Lunus,

On 21/05/18 22:48, Linus Lüssing wrote:
> On Fri, May 18, 2018 at 09:47:53AM +0800, Marek Lindner wrote:
>> +/**
>> + * batadv_v_elp_tp_start() - start a tp meter session for a neighbor
>> + * @neigh: neighbor to run tp meter on
>> + */
>> +static void batadv_v_elp_tp_start(struct batadv_hardif_neigh_node *neigh)
>> +{
>> +	struct batadv_hard_iface *hard_iface = neigh->if_incoming;
>> +	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
>> +
>> +	neigh->bat_v.tp_meter_running = true;
>> +	batadv_tp_start(bat_priv, neigh->addr, neigh, 10, NULL, BATADV_TP_ELP);
>> +}
> 
> This seems racy here? We have no ordering constraints between
> the assigment and batadv_tp_start() which might lead to...
> 
>> @@ -152,6 +192,25 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
>>  			return throughput * 10;
>>  	}
>>  
>> +fallback_throughput:
>> +	last_tp_run_jiffies = jiffies - neigh->bat_v.last_tp_meter_run;
>> +	last_tp_run_msecs = jiffies_to_msecs(last_tp_run_jiffies);
>> +
>> +	/* check the tp_meter_running flag before checking the timestamp to
>> +	 * avoid a race condition where a new tp meter session is scheduled
>> +	 * right after the previous tp meter session has completed
>> +	 */
>> +	if (!neigh->bat_v.tp_meter_running &&
>> +	    last_tp_run_msecs > BATADV_ELP_TP_RUN_INTERVAL)
>> +		batadv_v_elp_tp_start(neigh);
> 
> ...multiple instances potentially being started at the same time here?
> 

- batadv_tp_start() is only invoked by batadv_v_elp_get_throughput();
- batadv_v_elp_get_throughput() is only invoked by
batadv_v_elp_throughput_metric_update();
- batadv_v_elp_throughput_metric_update() is a periodic function
executed by a timer and we can't have concurrent executions (unless we
use crazy interval values).

Maybe we should add a comment to make this more clear?
  

Patch

diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c
index 71c20c1d..3f0d7816 100644
--- a/net/batman-adv/bat_v_elp.c
+++ b/net/batman-adv/bat_v_elp.c
@@ -51,6 +51,7 @@ 
 #include "originator.h"
 #include "routing.h"
 #include "send.h"
+#include "tp_meter.h"
 
 /**
  * batadv_v_elp_start_timer() - restart timer for ELP periodic work
@@ -67,6 +68,41 @@  static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface)
 			   msecs_to_jiffies(msecs));
 }
 
+/**
+ * batadv_v_elp_tp_start() - start a tp meter session for a neighbor
+ * @neigh: neighbor to run tp meter on
+ */
+static void batadv_v_elp_tp_start(struct batadv_hardif_neigh_node *neigh)
+{
+	struct batadv_hard_iface *hard_iface = neigh->if_incoming;
+	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+
+	neigh->bat_v.tp_meter_running = true;
+	batadv_tp_start(bat_priv, neigh->addr, neigh, 10, NULL, BATADV_TP_ELP);
+}
+
+/**
+ * batadv_v_elp_tp_fail() - handle tp meter session failure
+ * @neigh: neighbor to run tp meter on
+ */
+void batadv_v_elp_tp_fail(struct batadv_hardif_neigh_node *neigh)
+{
+	neigh->bat_v.tp_meter_running = false;
+}
+
+/**
+ * batadv_v_elp_tp_finish() - post-process tp meter results
+ * @neigh: neighbor tp meter on
+ * @throughput: tp meter throughput result
+ */
+void batadv_v_elp_tp_finish(struct batadv_hardif_neigh_node *neigh,
+			    u32 throughput)
+{
+	neigh->bat_v.tp_meter_throughput = throughput;
+	neigh->bat_v.last_tp_meter_run = jiffies;
+	neigh->bat_v.tp_meter_running = false;
+}
+
 /**
  * batadv_v_elp_get_throughput() - get the throughput towards a neighbour
  * @neigh: the neighbour for which the throughput has to be obtained
@@ -78,6 +114,7 @@  static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
 {
 	struct batadv_hard_iface *hard_iface = neigh->if_incoming;
 	struct ethtool_link_ksettings link_settings;
+	u32 last_tp_run_msecs, last_tp_run_jiffies;
 	struct net_device *real_netdev;
 	struct station_info sinfo;
 	u32 throughput;
@@ -112,10 +149,13 @@  static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
 			 */
 			return 0;
 		}
+
+		/* unsupported WiFi driver */
 		if (ret)
-			goto default_throughput;
+			goto fallback_throughput;
+
 		if (!(sinfo.filled & BIT(NL80211_STA_INFO_EXPECTED_THROUGHPUT)))
-			goto default_throughput;
+			goto fallback_throughput;
 
 		return sinfo.expected_throughput / 100;
 	}
@@ -152,6 +192,25 @@  static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
 			return throughput * 10;
 	}
 
+fallback_throughput:
+	last_tp_run_jiffies = jiffies - neigh->bat_v.last_tp_meter_run;
+	last_tp_run_msecs = jiffies_to_msecs(last_tp_run_jiffies);
+
+	/* check the tp_meter_running flag before checking the timestamp to
+	 * avoid a race condition where a new tp meter session is scheduled
+	 * right after the previous tp meter session has completed
+	 */
+	if (!neigh->bat_v.tp_meter_running &&
+	    last_tp_run_msecs > BATADV_ELP_TP_RUN_INTERVAL)
+		batadv_v_elp_tp_start(neigh);
+
+	/* discard too old tp test results */
+	if (last_tp_run_msecs > 2 * BATADV_ELP_TP_RUN_INTERVAL)
+		neigh->bat_v.tp_meter_throughput = 0;
+
+	if (neigh->bat_v.tp_meter_throughput)
+		return neigh->bat_v.tp_meter_throughput;
+
 default_throughput:
 	if (!(hard_iface->bat_v.flags & BATADV_WARNING_DEFAULT)) {
 		batadv_info(hard_iface->soft_iface,
diff --git a/net/batman-adv/bat_v_elp.h b/net/batman-adv/bat_v_elp.h
index e8c7b7fd..4ffb1fde 100644
--- a/net/batman-adv/bat_v_elp.h
+++ b/net/batman-adv/bat_v_elp.h
@@ -21,6 +21,8 @@ 
 
 #include "main.h"
 
+#include <linux/types.h>
+
 struct sk_buff;
 struct work_struct;
 
@@ -33,4 +35,23 @@  int batadv_v_elp_packet_recv(struct sk_buff *skb,
 			     struct batadv_hard_iface *if_incoming);
 void batadv_v_elp_throughput_metric_update(struct work_struct *work);
 
+#ifdef CONFIG_BATMAN_ADV_BATMAN_V
+
+void batadv_v_elp_tp_fail(struct batadv_hardif_neigh_node *neigh);
+void batadv_v_elp_tp_finish(struct batadv_hardif_neigh_node *neigh,
+			    u32 throughput);
+
+#else
+
+static inline void batadv_v_elp_tp_fail(struct batadv_hardif_neigh_node *neigh)
+{
+}
+
+static inline void
+batadv_v_elp_tp_finish(struct batadv_hardif_neigh_node *neigh, u32 throughput)
+{
+}
+
+#endif /* CONFIG_BATMAN_ADV_BATMAN_V */
+
 #endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index 89dfaf87..ed4ae913 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -69,6 +69,7 @@ 
 #define BATADV_ELP_MIN_PROBE_SIZE 200 /* bytes */
 #define BATADV_ELP_PROBE_MAX_TX_DIFF 100 /* milliseconds */
 #define BATADV_ELP_MAX_AGE 64
+#define BATADV_ELP_TP_RUN_INTERVAL 60000 /* milliseconds */
 #define BATADV_OGM_MAX_ORIGDIFF 5
 #define BATADV_OGM_MAX_AGE 64
 
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index 87aaeb1d..ec47ca79 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -24,7 +24,7 @@ 
 #include <linux/byteorder/generic.h>
 #include <linux/cache.h>
 #include <linux/compiler.h>
-#include <linux/err.h>
+#include <linux/errno.h>
 #include <linux/etherdevice.h>
 #include <linux/gfp.h>
 #include <linux/if_ether.h>
@@ -33,9 +33,9 @@ 
 #include <linux/kernel.h>
 #include <linux/kref.h>
 #include <linux/list.h>
+#include <linux/log2.h>
 #include <linux/netdevice.h>
 #include <linux/param.h>
-#include <linux/printk.h>
 #include <linux/random.h>
 #include <linux/rculist.h>
 #include <linux/rcupdate.h>
@@ -51,6 +51,7 @@ 
 #include <uapi/linux/batadv_packet.h>
 #include <uapi/linux/batman_adv.h>
 
+#include "bat_v_elp.h"
 #include "hard-interface.h"
 #include "log.h"
 #include "netlink.h"
@@ -225,6 +226,7 @@  static void batadv_tp_caller_notify(struct batadv_priv *bat_priv,
 				    enum batadv_tp_meter_reason reason)
 {
 	u32 total_bytes;
+	u64 throughput;
 	u32 test_time;
 	u32 cookie;
 	bool reason_is_error;
@@ -251,6 +253,21 @@  static void batadv_tp_caller_notify(struct batadv_priv *bat_priv,
 
 		break;
 	case BATADV_TP_ELP:
+		if (reason_is_error) {
+			batadv_v_elp_tp_fail(tp_vars->hardif_neigh);
+			return;
+		}
+
+		test_time = jiffies_to_msecs(jiffies - tp_vars->start_time);
+		total_bytes = atomic64_read(&tp_vars->tot_sent);
+
+		/* The following calculation includes these steps:
+		 * - convert bytes to bits
+		 * - divide bits by the test length (msecs)
+		 * - convert result from bits/ms to 0.1Mb/s (* 1024 * 10 / 1000)
+		 */
+		throughput = total_bytes * 8 >> ilog2(test_time) / 10;
+		batadv_v_elp_tp_finish(tp_vars->hardif_neigh, throughput);
 		break;
 	default:
 		break;
@@ -264,11 +281,14 @@  static void batadv_tp_caller_notify(struct batadv_priv *bat_priv,
  * @reason: reason for tp meter session stop
  * @dst: destination of tp_meter session
  * @cookie: cookie of tp_meter session
+ * @hardif_neigh: neighbor towards which the test was ran (for one-hop test)
  */
-static void batadv_tp_caller_init_error(struct batadv_priv *bat_priv,
-					enum batadv_tp_meter_caller caller,
-					enum batadv_tp_meter_reason reason,
-					const u8 *dst, u32 cookie)
+static void
+batadv_tp_caller_init_error(struct batadv_priv *bat_priv,
+			    enum batadv_tp_meter_caller caller,
+			    enum batadv_tp_meter_reason reason, const u8 *dst,
+			    u32 cookie,
+			    struct batadv_hardif_neigh_node *hardif_neigh)
 {
 	switch (caller) {
 	case BATADV_TP_USERSPACE:
@@ -276,6 +296,7 @@  static void batadv_tp_caller_init_error(struct batadv_priv *bat_priv,
 					      cookie);
 		break;
 	case BATADV_TP_ELP:
+		batadv_v_elp_tp_fail(hardif_neigh);
 		break;
 	default:
 		break;
@@ -981,7 +1002,7 @@  void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 			   "Meter: too many ongoing sessions, aborting (SEND)\n");
 		batadv_tp_caller_init_error(bat_priv, caller,
 					    BATADV_TP_REASON_TOO_MANY, dst,
-					    session_cookie);
+					    session_cookie, neigh);
 		return;
 	}
 
@@ -989,7 +1010,7 @@  void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 	if (!tp_vars) {
 		batadv_tp_caller_init_error(bat_priv, caller,
 					    BATADV_TP_REASON_MEMORY_ERROR, dst,
-					    session_cookie);
+					    session_cookie, neigh);
 		return;
 	}
 
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 0a9bee88..148f23ad 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -583,6 +583,20 @@  struct batadv_hardif_neigh_node_bat_v {
 
 	/** @metric_work: work queue callback item for metric update */
 	struct work_struct metric_work;
+
+	/**
+	 * @tp_meter_running: tp meter measurements towards this neighbor in
+	 * progress
+	 */
+	unsigned char tp_meter_running:1;
+
+	/**
+	 * @last_tp_meter_run: timestamp of last tp meter measurement completion
+	 */
+	unsigned long last_tp_meter_run;
+
+	/** @tp_meter_throughput: throughput information measured by tp meter */
+	unsigned long tp_meter_throughput;
 };
 
 /**