Under normal circumstances B.A.T.M.A.N. V retrieves the neighbor throughput values to populate its metric tables from the various drivers such as WiFi throughput tables and Ethernet throughput.. Whenever the interface drivers do not export link throughput information manual overrides become necessary. To further automate and thus better support these setups, ELP may call the batman-adv throughput meter to schedule a throughput estimation to be used to populate the metric table.
v5: * fix tp_vars refcount on queue_work() failure * squash batadv_tp_start_work() into batadv_tp_start()
v4: * read tp measurement result only once
v3: * fix ELP tp meter result computation * use batadv_has_timed_out() instead of custom implementation * set ELP tp meter test duration to 1000ms in patch #6 * add comment explaining periodic scheduling
v2: * added sysfs attribute to configure tp meter test duration * fixed null pointer dereference in TP meter packet sending routine * fixed storing the measured throughput in the correct variable * checkpatch/kerneldoc/sparse/smatch cleanup
Antonio Quartulli (3): batman-adv: tp_meter - prevent concurrent tp_meter sessions by using workqueue batman-adv: tp_meter - don't check for existing session batman-adv: tp_meter - add option to perform one-hop test
Marek Lindner (4): batman-adv: tp_meter - allow up to 10 queued sessions batman-adv: tp_meter - add caller distinction batman-adv: ELP - use tp meter to estimate the throughput if otherwise not available batman-adv: ELP - add throughput meter test duration attribute
.../ABI/testing/sysfs-class-net-batman-adv | 7 + include/uapi/linux/batadv_packet.h | 2 + net/batman-adv/bat_v.c | 1 + net/batman-adv/bat_v_elp.c | 69 ++- net/batman-adv/bat_v_elp.h | 21 + net/batman-adv/main.c | 10 +- net/batman-adv/main.h | 7 +- net/batman-adv/netlink.c | 3 +- net/batman-adv/routing.c | 6 +- net/batman-adv/sysfs.c | 3 + net/batman-adv/tp_meter.c | 484 +++++++++++------- net/batman-adv/tp_meter.h | 11 +- net/batman-adv/types.h | 36 ++ 13 files changed, 453 insertions(+), 207 deletions(-)
From: Antonio Quartulli a@unstable.cc
To ensure that no more than one tp_meter session runs at the same time, use an ordered workqueue instead of spawning one kthread per session.
Signed-off-by: Antonio Quartulli a@unstable.cc --- net/batman-adv/main.c | 10 +++- net/batman-adv/tp_meter.c | 96 +++++++++++++++++++-------------------- net/batman-adv/tp_meter.h | 3 +- net/batman-adv/types.h | 3 ++ 4 files changed, 60 insertions(+), 52 deletions(-)
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 69c0d85b..cd3f1924 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -91,6 +91,10 @@ static int __init batadv_init(void) if (ret < 0) return ret;
+ ret = batadv_tp_meter_init(); + if (ret < 0) + goto err_tp_meter; + INIT_LIST_HEAD(&batadv_hardif_list); batadv_algo_init();
@@ -99,7 +103,6 @@ static int __init batadv_init(void) batadv_v_init(); batadv_iv_init(); batadv_nc_init(); - batadv_tp_meter_init();
batadv_event_workqueue = create_singlethread_workqueue("bat_events"); if (!batadv_event_workqueue) @@ -118,9 +121,11 @@ static int __init batadv_init(void) return 0;
err_create_wq: + batadv_tp_meter_destroy(); +err_tp_meter: batadv_tt_cache_destroy();
- return -ENOMEM; + return ret; }
static void __exit batadv_exit(void) @@ -138,6 +143,7 @@ static void __exit batadv_exit(void) rcu_barrier();
batadv_tt_cache_destroy(); + batadv_tp_meter_destroy(); }
/** diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 11520de9..9a29ab62 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -32,7 +32,6 @@ #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/kref.h> -#include <linux/kthread.h> #include <linux/list.h> #include <linux/netdevice.h> #include <linux/param.h> @@ -97,6 +96,9 @@
static u8 batadv_tp_prerandom[4096] __read_mostly;
+/* ordered work queue */ +static struct workqueue_struct *batadv_tp_meter_queue; + /** * batadv_tp_session_cookie() - generate session cookie based on session ids * @session: TP session identifier @@ -808,19 +810,22 @@ static int batadv_tp_wait_available(struct batadv_tp_vars *tp_vars, size_t plen)
/** * batadv_tp_send() - main sending thread of a tp meter session - * @arg: address of the related tp_vars + * @work: delayed work reference of the related tp_vars * * Return: nothing, this function never returns */ -static int batadv_tp_send(void *arg) +static void batadv_tp_send(struct work_struct *work) { - struct batadv_tp_vars *tp_vars = arg; - struct batadv_priv *bat_priv = tp_vars->bat_priv; struct batadv_hard_iface *primary_if = NULL; struct batadv_orig_node *orig_node = NULL; + struct batadv_tp_vars *tp_vars; size_t payload_len, packet_len; + struct batadv_priv *bat_priv; int err = 0;
+ tp_vars = container_of(work, struct batadv_tp_vars, test_work); + bat_priv = tp_vars->bat_priv; + if (unlikely(tp_vars->role != BATADV_TP_SENDER)) { err = BATADV_TP_REASON_DST_UNREACHABLE; tp_vars->reason = err; @@ -901,40 +906,6 @@ static int batadv_tp_send(void *arg) batadv_tp_sender_cleanup(bat_priv, tp_vars);
batadv_tp_vars_put(tp_vars); - - do_exit(0); -} - -/** - * batadv_tp_start_kthread() - start new thread which manages the tp meter - * sender - * @tp_vars: the private data of the current TP meter session - */ -static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars) -{ - struct task_struct *kthread; - struct batadv_priv *bat_priv = tp_vars->bat_priv; - u32 session_cookie; - - kref_get(&tp_vars->refcount); - kthread = kthread_create(batadv_tp_send, tp_vars, "kbatadv_tp_meter"); - if (IS_ERR(kthread)) { - session_cookie = batadv_tp_session_cookie(tp_vars->session, - tp_vars->icmp_uid); - pr_err("batadv: cannot create tp meter kthread\n"); - batadv_tp_batctl_error_notify(BATADV_TP_REASON_MEMORY_ERROR, - tp_vars->other_end, - bat_priv, session_cookie); - - /* drop reserved reference for kthread */ - batadv_tp_vars_put(tp_vars); - - /* cleanup of failed tp meter variables */ - batadv_tp_sender_cleanup(bat_priv, tp_vars); - return; - } - - wake_up_process(kthread); }
/** @@ -951,6 +922,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, u8 session_id[2]; u8 icmp_uid; u32 session_cookie; + bool ret;
get_random_bytes(session_id, sizeof(session_id)); get_random_bytes(&icmp_uid, 1); @@ -1046,20 +1018,28 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, if (!tp_vars->test_length) tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH;
- batadv_dbg(BATADV_DBG_TP_METER, bat_priv, - "Meter: starting throughput meter towards %pM (length=%ums)\n", - dst, test_length); - /* init work item for finished tp tests */ INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish);
- /* start tp kthread. This way the write() call issued from userspace can - * happily return and avoid to block + /* schedule the tp worker. This way the write() call issued from + * userspace can happily return and avoid to block */ - batadv_tp_start_kthread(tp_vars); + INIT_WORK(&tp_vars->test_work, batadv_tp_send); + ret = queue_work(batadv_tp_meter_queue, &tp_vars->test_work);
- /* don't return reference to new tp_vars */ - batadv_tp_vars_put(tp_vars); + if (!ret) { + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, + "Meter: too many queued workers, aborting (SEND)\n"); + batadv_tp_caller_init_error(bat_priv, caller, + BATADV_TP_REASON_ALREADY_ONGOING, + dst, session_cookie, neigh); + batadv_tp_vars_put(tp_vars); + return; + } + + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, + "Meter: starting throughput meter towards %pM (length=%ums)\n", + dst, test_length); }
/** @@ -1498,8 +1478,26 @@ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb)
/** * batadv_tp_meter_init() - initialize global tp_meter structures + * + * Return: 0 on success, -ENOMEM if the tp_meter queue allocation fails */ -void __init batadv_tp_meter_init(void) +int __init batadv_tp_meter_init(void) { get_random_bytes(batadv_tp_prerandom, sizeof(batadv_tp_prerandom)); + + batadv_tp_meter_queue = alloc_ordered_workqueue("bat_tp_meter", 0); + if (!batadv_tp_meter_queue) + return -ENOMEM; + + return 0; +} + +/** + * batadv_tp_meter_destroy() - destroy tp meter memory allocations + */ +void batadv_tp_meter_destroy(void) +{ + flush_workqueue(batadv_tp_meter_queue); + destroy_workqueue(batadv_tp_meter_queue); + batadv_tp_meter_queue = NULL; } diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h index 68e60097..ab0bde26 100644 --- a/net/batman-adv/tp_meter.h +++ b/net/batman-adv/tp_meter.h @@ -25,7 +25,8 @@
struct sk_buff;
-void batadv_tp_meter_init(void); +int batadv_tp_meter_init(void); +void batadv_tp_meter_destroy(void); void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, u32 test_length, u32 *cookie); void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 343d3048..2696b093 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1355,6 +1355,9 @@ struct batadv_tp_vars { /** @finish_work: work item for the finishing procedure */ struct delayed_work finish_work;
+ /** @test_work: work item for the test process */ + struct work_struct test_work; + /** @test_length: test length in milliseconds */ u32 test_length;
From: Antonio Quartulli a@unstable.cc
Since the conversion from kthread to queue worker it is not possible to run more than one "sender" session at a time. For this reason, checking if another session to the same destination is already scheduled is not useful anymore.
Remove such check and allow the user to enqueue a new session to a previously targeted node.
Signed-off-by: Antonio Quartulli a@unstable.cc --- net/batman-adv/tp_meter.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-)
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 9a29ab62..e54aa1b8 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -929,21 +929,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, session_cookie = batadv_tp_session_cookie(session_id, icmp_uid); *cookie = session_cookie;
- /* look for an already existing test towards this node */ - spin_lock_bh(&bat_priv->tp_list_lock); - tp_vars = batadv_tp_list_find(bat_priv, dst); - if (tp_vars) { - spin_unlock_bh(&bat_priv->tp_list_lock); - batadv_tp_vars_put(tp_vars); - batadv_dbg(BATADV_DBG_TP_METER, bat_priv, - "Meter: test to or from the same node already ongoing, aborting\n"); - batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING, - dst, bat_priv, session_cookie); - return; - } - if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) { - spin_unlock_bh(&bat_priv->tp_list_lock); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: too many ongoing sessions, aborting (SEND)\n"); batadv_tp_batctl_error_notify(BATADV_TP_REASON_TOO_MANY, dst, @@ -953,10 +939,6 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
tp_vars = kmalloc(sizeof(*tp_vars), GFP_ATOMIC); if (!tp_vars) { - spin_unlock_bh(&bat_priv->tp_list_lock); - batadv_dbg(BATADV_DBG_TP_METER, bat_priv, - "Meter: %s cannot allocate list elements\n", - __func__); batadv_tp_batctl_error_notify(BATADV_TP_REASON_MEMORY_ERROR, dst, bat_priv, session_cookie); return; @@ -1011,6 +993,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, spin_lock_init(&tp_vars->prerandom_lock);
kref_get(&tp_vars->refcount); + spin_lock_bh(&bat_priv->tp_list_lock); hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); spin_unlock_bh(&bat_priv->tp_list_lock);
Signed-off-by: Marek Lindner mareklindner@neomailbox.ch --- net/batman-adv/main.h | 6 ++++-- net/batman-adv/tp_meter.c | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 8da3c933..89dfaf87 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -137,9 +137,11 @@ #define BATADV_NC_NODE_TIMEOUT 10000 /* Milliseconds */
/** - * BATADV_TP_MAX_NUM - maximum number of simultaneously active tp sessions + * BATADV_TP_MAX_NUM_QUEUE - maximum number of queued (outgoing) tp sessions + * BATADV_TP_MAX_NUM_RECV - maximum number of simultaneous receiving streams */ -#define BATADV_TP_MAX_NUM 5 +#define BATADV_TP_MAX_NUM_QUEUE 10 +#define BATADV_TP_MAX_NUM_RECV 1
/** * enum batadv_mesh_state - State of a soft interface diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index e54aa1b8..73f7f168 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -929,7 +929,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, session_cookie = batadv_tp_session_cookie(session_id, icmp_uid); *cookie = session_cookie;
- if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) { + if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM_QUEUE)) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: too many ongoing sessions, aborting (SEND)\n"); batadv_tp_batctl_error_notify(BATADV_TP_REASON_TOO_MANY, dst, @@ -1314,7 +1314,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, if (tp_vars) goto out_unlock;
- if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM)) { + if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM_RECV)) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: too many ongoing sessions, aborting (RECV)\n"); goto out_unlock;
The throughput meter can be called from user space as well as from the batman-adv kernel module itself. Add infrastructure to handle the different callers.
Signed-off-by: Marek Lindner mareklindner@neomailbox.ch --- net/batman-adv/netlink.c | 3 +- net/batman-adv/tp_meter.c | 108 ++++++++++++++++++++++---------------- net/batman-adv/tp_meter.h | 3 +- net/batman-adv/types.h | 13 +++++ 4 files changed, 79 insertions(+), 48 deletions(-)
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index 0d9459b6..b0e1b73c 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -378,7 +378,8 @@ batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info) }
bat_priv = netdev_priv(soft_iface); - batadv_tp_start(bat_priv, dst, test_length, &cookie); + batadv_tp_start(bat_priv, dst, test_length, &cookie, + BATADV_TP_USERSPACE);
ret = batadv_netlink_tp_meter_put(msg, cookie);
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 73f7f168..1f201ec7 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -215,50 +215,71 @@ static void batadv_tp_update_rto(struct batadv_tp_vars *tp_vars, }
/** - * batadv_tp_batctl_notify() - send client status result to client - * @reason: reason for tp meter session stop - * @dst: destination of tp_meter session + * batadv_tp_caller_notify() - send tp meter status result to caller * @bat_priv: the bat priv with all the soft interface information - * @start_time: start of transmission in jiffies - * @total_sent: bytes acked to the receiver - * @cookie: cookie of tp_meter session + * @tp_vars: the private data of the current TP meter session + * @reason: reason for tp meter session stop */ -static void batadv_tp_batctl_notify(enum batadv_tp_meter_reason reason, - const u8 *dst, struct batadv_priv *bat_priv, - unsigned long start_time, u64 total_sent, - u32 cookie) +static void batadv_tp_caller_notify(struct batadv_priv *bat_priv, + struct batadv_tp_vars *tp_vars, + enum batadv_tp_meter_reason reason) { - u32 test_time; - u8 result; u32 total_bytes; + u32 test_time; + u32 cookie; + bool reason_is_error;
- if (!batadv_tp_is_error(reason)) { - result = BATADV_TP_REASON_COMPLETE; - test_time = jiffies_to_msecs(jiffies - start_time); - total_bytes = total_sent; - } else { - result = reason; - test_time = 0; - total_bytes = 0; - } + reason_is_error = batadv_tp_is_error(reason); + + switch (tp_vars->caller) { + case BATADV_TP_USERSPACE: + cookie = batadv_tp_session_cookie(tp_vars->session, + tp_vars->icmp_uid); + + if (reason_is_error) { + batadv_netlink_tpmeter_notify(bat_priv, + tp_vars->other_end, + reason, 0, 0, cookie); + return; + } + + test_time = jiffies_to_msecs(jiffies - tp_vars->start_time); + total_bytes = atomic64_read(&tp_vars->tot_sent); + batadv_netlink_tpmeter_notify(bat_priv, tp_vars->other_end, + BATADV_TP_REASON_COMPLETE, + test_time, total_bytes, cookie);
- batadv_netlink_tpmeter_notify(bat_priv, dst, result, test_time, - total_bytes, cookie); + break; + case BATADV_TP_ELP: + break; + default: + break; + } }
/** - * batadv_tp_batctl_error_notify() - send client error result to client + * batadv_tp_caller_init_error() - report early tp meter errors to caller + * @bat_priv: the bat priv with all the soft interface information + * @caller: caller of tp meter session (user space or ELP) * @reason: reason for tp meter session stop * @dst: destination of tp_meter session - * @bat_priv: the bat priv with all the soft interface information * @cookie: cookie of tp_meter session */ -static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason, - const u8 *dst, - struct batadv_priv *bat_priv, - 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) { - batadv_tp_batctl_notify(reason, dst, bat_priv, 0, 0, cookie); + switch (caller) { + case BATADV_TP_USERSPACE: + batadv_netlink_tpmeter_notify(bat_priv, dst, reason, 0, 0, + cookie); + break; + case BATADV_TP_ELP: + break; + default: + break; + } }
/** @@ -411,8 +432,6 @@ static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv, static void batadv_tp_sender_end(struct batadv_priv *bat_priv, struct batadv_tp_vars *tp_vars) { - u32 session_cookie; - batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Test towards %pM finished..shutting down (reason=%d)\n", tp_vars->other_end, tp_vars->reason); @@ -425,15 +444,7 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv, "Final values: cwnd=%u ss_threshold=%u\n", tp_vars->cwnd, tp_vars->ss_threshold);
- session_cookie = batadv_tp_session_cookie(tp_vars->session, - tp_vars->icmp_uid); - - batadv_tp_batctl_notify(tp_vars->reason, - tp_vars->other_end, - bat_priv, - tp_vars->start_time, - atomic64_read(&tp_vars->tot_sent), - session_cookie); + batadv_tp_caller_notify(bat_priv, tp_vars, tp_vars->reason); }
/** @@ -914,9 +925,11 @@ static void batadv_tp_send(struct work_struct *work) * @dst: the receiver MAC address * @test_length: test length in milliseconds * @cookie: session cookie + * @caller: caller of tp meter session (user space or ELP) */ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, - u32 test_length, u32 *cookie) + u32 test_length, u32 *cookie, + enum batadv_tp_meter_caller caller) { struct batadv_tp_vars *tp_vars; u8 session_id[2]; @@ -932,15 +945,17 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM_QUEUE)) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: too many ongoing sessions, aborting (SEND)\n"); - batadv_tp_batctl_error_notify(BATADV_TP_REASON_TOO_MANY, dst, - bat_priv, session_cookie); + batadv_tp_caller_init_error(bat_priv, caller, + BATADV_TP_REASON_TOO_MANY, dst, + session_cookie); return; }
tp_vars = kmalloc(sizeof(*tp_vars), GFP_ATOMIC); if (!tp_vars) { - batadv_tp_batctl_error_notify(BATADV_TP_REASON_MEMORY_ERROR, - dst, bat_priv, session_cookie); + batadv_tp_caller_init_error(bat_priv, caller, + BATADV_TP_REASON_MEMORY_ERROR, dst, + session_cookie); return; }
@@ -948,6 +963,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, ether_addr_copy(tp_vars->other_end, dst); kref_init(&tp_vars->refcount); tp_vars->role = BATADV_TP_SENDER; + tp_vars->caller = caller; atomic_set(&tp_vars->sending, 1); memcpy(tp_vars->session, session_id, sizeof(session_id)); tp_vars->icmp_uid = icmp_uid; diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h index ab0bde26..3b11a3e9 100644 --- a/net/batman-adv/tp_meter.h +++ b/net/batman-adv/tp_meter.h @@ -28,7 +28,8 @@ struct sk_buff; int batadv_tp_meter_init(void); void batadv_tp_meter_destroy(void); void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, - u32 test_length, u32 *cookie); + u32 test_length, u32 *cookie, + enum batadv_tp_meter_caller caller); void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, u8 return_value); void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 2696b093..56034b54 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1324,6 +1324,16 @@ enum batadv_tp_meter_role { BATADV_TP_SENDER };
+/** + * enum batadv_tp_meter_caller - initiator of the tp meter session + * @BATADV_TP_USERSPACE: initiated by user space + * @BATADV_TP_ELP: initiated by ELP + */ +enum batadv_tp_meter_caller { + BATADV_TP_USERSPACE, + BATADV_TP_ELP +}; + /** * struct batadv_tp_vars - tp meter private variables per session */ @@ -1346,6 +1356,9 @@ struct batadv_tp_vars { /** @role: receiver/sender modi */ enum batadv_tp_meter_role role;
+ /** @caller: caller of tp meter session (user space or ELP) */ + enum batadv_tp_meter_caller caller; + /** @sending: sending binary semaphore: 1 if sending, 0 is not */ atomic_t sending;
From: Antonio Quartulli a@unstable.cc
A link test is a TP session ran over a specific one-hop link, rather than towards an originator in the mesh.
Signed-off-by: Antonio Quartulli a@unstable.cc --- include/uapi/linux/batadv_packet.h | 2 + net/batman-adv/netlink.c | 2 +- net/batman-adv/routing.c | 6 +- net/batman-adv/tp_meter.c | 232 +++++++++++++++++++---------- net/batman-adv/tp_meter.h | 5 +- net/batman-adv/types.h | 3 + 6 files changed, 167 insertions(+), 83 deletions(-)
diff --git a/include/uapi/linux/batadv_packet.h b/include/uapi/linux/batadv_packet.h index 894d8d2f..4e6e075b 100644 --- a/include/uapi/linux/batadv_packet.h +++ b/include/uapi/linux/batadv_packet.h @@ -351,10 +351,12 @@ struct batadv_icmp_tp_packet { * enum batadv_icmp_tp_subtype - ICMP TP Meter packet subtypes * @BATADV_TP_MSG: Msg from sender to receiver * @BATADV_TP_ACK: acknowledgment from receiver to sender + * @BATADV_TP_MSG_LINK: Msg from sender to receiver used for link test (one-hop) */ enum batadv_icmp_tp_subtype { BATADV_TP_MSG = 0, BATADV_TP_ACK, + BATADV_TP_MSG_LINK, };
#define BATADV_RR_LEN 16 diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index b0e1b73c..064020cc 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -378,7 +378,7 @@ batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info) }
bat_priv = netdev_priv(soft_iface); - batadv_tp_start(bat_priv, dst, test_length, &cookie, + batadv_tp_start(bat_priv, dst, NULL, test_length, &cookie, BATADV_TP_USERSPACE);
ret = batadv_netlink_tp_meter_put(msg, cookie); diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index cc3ed93a..dbf2d556 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -223,12 +223,14 @@ bool batadv_check_management_packet(struct sk_buff *skb, /** * batadv_recv_my_icmp_packet() - receive an icmp packet locally * @bat_priv: the bat priv with all the soft interface information + * @recv_if: interface that the skb is received on * @skb: icmp packet to process * * Return: NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP * otherwise. */ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, + struct batadv_hard_iface *recv_if, struct sk_buff *skb) { struct batadv_hard_iface *primary_if = NULL; @@ -281,7 +283,7 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, if (!pskb_may_pull(skb, sizeof(struct batadv_icmp_tp_packet))) goto out;
- batadv_tp_meter_recv(bat_priv, skb); + batadv_tp_meter_recv(bat_priv, recv_if, skb); ret = NET_RX_SUCCESS; /* skb was consumed */ skb = NULL; @@ -418,7 +420,7 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
/* packet for me */ if (batadv_is_my_mac(bat_priv, icmph->dst)) - return batadv_recv_my_icmp_packet(bat_priv, skb); + return batadv_recv_my_icmp_packet(bat_priv, recv_if, skb);
/* TTL exceeded */ if (icmph->ttl < 2) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 1f201ec7..f23bb847 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -381,6 +381,9 @@ static void batadv_tp_vars_release(struct kref *ref) } spin_unlock_bh(&tp_vars->unacked_lock);
+ if (tp_vars->hardif_neigh) + batadv_hardif_neigh_put(tp_vars->hardif_neigh); + kfree_rcu(tp_vars, rcu); }
@@ -579,9 +582,8 @@ static void batadv_tp_fill_prerandom(struct batadv_tp_vars *tp_vars,
/** * batadv_tp_send_msg() - send a single message + * @bat_priv: the bat priv with all the soft interface information * @tp_vars: the private TP meter data for this session - * @src: source mac address - * @orig_node: the originator of the destination * @seqno: sequence number of this packet * @len: length of the entire packet * @session: session identifier @@ -594,26 +596,56 @@ static void batadv_tp_fill_prerandom(struct batadv_tp_vars *tp_vars, * not reachable, BATADV_TP_REASON_MEMORY_ERROR if the packet couldn't be * allocated */ -static int batadv_tp_send_msg(struct batadv_tp_vars *tp_vars, const u8 *src, - struct batadv_orig_node *orig_node, - u32 seqno, size_t len, const u8 *session, - int uid, u32 timestamp) +static int batadv_tp_send_msg(struct batadv_priv *bat_priv, + struct batadv_tp_vars *tp_vars, u32 seqno, + size_t len, const u8 *session, int uid, + u32 timestamp) { + struct batadv_hard_iface *primary_if = NULL; + struct batadv_orig_node *orig_node = NULL; struct batadv_icmp_tp_packet *icmp; struct sk_buff *skb; - int r; - u8 *data; + int r, ret = 0; + u8 *data, *src, *dst, subtype; + struct net_device *netdev; size_t data_len;
- skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN); - if (unlikely(!skb)) - return BATADV_TP_REASON_MEMORY_ERROR; + /* link test */ + if (tp_vars->hardif_neigh) { + dst = tp_vars->hardif_neigh->addr; + src = tp_vars->hardif_neigh->if_incoming->net_dev->dev_addr; + subtype = BATADV_TP_MSG_LINK; + netdev = tp_vars->hardif_neigh->if_incoming->net_dev; + } else { + orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end); + if (unlikely(!orig_node)) { + ret = BATADV_TP_REASON_DST_UNREACHABLE; + goto out; + } + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (unlikely(!primary_if)) { + ret = BATADV_TP_REASON_DST_UNREACHABLE; + goto out; + } + + dst = orig_node->orig; + src = primary_if->net_dev->dev_addr; + subtype = BATADV_TP_MSG; + netdev = NULL; + } + + skb = netdev_alloc_skb_ip_align(netdev, len + ETH_HLEN); + if (unlikely(!skb)) { + ret = BATADV_TP_REASON_MEMORY_ERROR; + goto out; + }
skb_reserve(skb, ETH_HLEN); icmp = skb_put(skb, sizeof(*icmp));
/* fill the icmp header */ - ether_addr_copy(icmp->dst, orig_node->orig); + ether_addr_copy(icmp->dst, dst); ether_addr_copy(icmp->orig, src); icmp->version = BATADV_COMPAT_VERSION; icmp->packet_type = BATADV_ICMP; @@ -621,7 +653,7 @@ static int batadv_tp_send_msg(struct batadv_tp_vars *tp_vars, const u8 *src, icmp->msg_type = BATADV_TP; icmp->uid = uid;
- icmp->subtype = BATADV_TP_MSG; + icmp->subtype = subtype; memcpy(icmp->session, session, sizeof(icmp->session)); icmp->seqno = htonl(seqno); icmp->timestamp = htonl(timestamp); @@ -630,11 +662,23 @@ static int batadv_tp_send_msg(struct batadv_tp_vars *tp_vars, const u8 *src, data = skb_put(skb, data_len); batadv_tp_fill_prerandom(tp_vars, data, data_len);
- r = batadv_send_skb_to_orig(skb, orig_node, NULL); - if (r == NET_XMIT_SUCCESS) - return 0; + if (tp_vars->hardif_neigh) + r = batadv_send_skb_packet(skb, + tp_vars->hardif_neigh->if_incoming, + dst); + else + r = batadv_send_skb_to_orig(skb, orig_node, NULL);
- return BATADV_TP_REASON_CANT_SEND; + if (unlikely(r != NET_XMIT_SUCCESS)) + ret = BATADV_TP_REASON_CANT_SEND; + +out: + if (likely(primary_if)) + batadv_hardif_put(primary_if); + if (likely(orig_node)) + batadv_orig_node_put(orig_node); + + return ret; }
/** @@ -653,7 +697,6 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, struct batadv_tp_vars *tp_vars; size_t packet_len, mss; u32 rtt, recv_ack, cwnd; - unsigned char *dev_addr;
packet_len = BATADV_TP_PLEN; mss = BATADV_TP_PLEN; @@ -675,13 +718,11 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, (u32)atomic_read(&tp_vars->last_acked))) goto out;
- primary_if = batadv_primary_if_get_selected(bat_priv); - if (unlikely(!primary_if)) - goto out; - - orig_node = batadv_orig_hash_find(bat_priv, icmp->orig); - if (unlikely(!orig_node)) - goto out; + if (!tp_vars->hardif_neigh) { + primary_if = batadv_primary_if_get_selected(bat_priv); + if (unlikely(!primary_if)) + goto out; + }
/* update RTO with the new sampled RTT, if any */ rtt = jiffies_to_msecs(jiffies) - ntohl(icmp->timestamp); @@ -703,8 +744,11 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, goto out;
/* if this is the third duplicate ACK do Fast Retransmit */ - batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr, - orig_node, recv_ack, packet_len, + + /* if we have a hardif_neigh, it means that this is a LINK test, + * therefore use the according function + */ + batadv_tp_send_msg(bat_priv, tp_vars, recv_ack, packet_len, icmp->session, icmp->uid, jiffies_to_msecs(jiffies));
@@ -741,9 +785,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, * immediately as specified by NewReno (see * Section 3.2 of RFC6582 for details) */ - dev_addr = primary_if->net_dev->dev_addr; - batadv_tp_send_msg(tp_vars, dev_addr, - orig_node, recv_ack, + batadv_tp_send_msg(bat_priv, tp_vars, recv_ack, packet_len, icmp->session, icmp->uid, jiffies_to_msecs(jiffies)); @@ -827,8 +869,6 @@ static int batadv_tp_wait_available(struct batadv_tp_vars *tp_vars, size_t plen) */ static void batadv_tp_send(struct work_struct *work) { - struct batadv_hard_iface *primary_if = NULL; - struct batadv_orig_node *orig_node = NULL; struct batadv_tp_vars *tp_vars; size_t payload_len, packet_len; struct batadv_priv *bat_priv; @@ -843,20 +883,6 @@ static void batadv_tp_send(struct work_struct *work) goto out; }
- orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end); - if (unlikely(!orig_node)) { - err = BATADV_TP_REASON_DST_UNREACHABLE; - tp_vars->reason = err; - goto out; - } - - primary_if = batadv_primary_if_get_selected(bat_priv); - if (unlikely(!primary_if)) { - err = BATADV_TP_REASON_DST_UNREACHABLE; - tp_vars->reason = err; - goto out; - } - /* assume that all the hard_interfaces have a correctly * configured MTU, so use the soft_iface MTU as MSS. * This might not be true and in that case the fragmentation @@ -883,10 +909,9 @@ static void batadv_tp_send(struct work_struct *work) */ packet_len = payload_len + sizeof(struct batadv_unicast_packet);
- err = batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr, - orig_node, tp_vars->last_sent, - packet_len, - tp_vars->session, tp_vars->icmp_uid, + err = batadv_tp_send_msg(bat_priv, tp_vars, tp_vars->last_sent, + packet_len, tp_vars->session, + tp_vars->icmp_uid, jiffies_to_msecs(jiffies));
/* something went wrong during the preparation/transmission */ @@ -908,11 +933,6 @@ static void batadv_tp_send(struct work_struct *work) }
out: - if (likely(primary_if)) - batadv_hardif_put(primary_if); - if (likely(orig_node)) - batadv_orig_node_put(orig_node); - batadv_tp_sender_end(bat_priv, tp_vars); batadv_tp_sender_cleanup(bat_priv, tp_vars);
@@ -923,24 +943,28 @@ static void batadv_tp_send(struct work_struct *work) * batadv_tp_start() - start a new tp meter session * @bat_priv: the bat priv with all the soft interface information * @dst: the receiver MAC address + * @neigh: neighbour towars which we have to run the test (one-hop test) * @test_length: test length in milliseconds * @cookie: session cookie * @caller: caller of tp meter session (user space or ELP) */ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, + struct batadv_hardif_neigh_node *neigh, u32 test_length, u32 *cookie, enum batadv_tp_meter_caller caller) { struct batadv_tp_vars *tp_vars; u8 session_id[2]; u8 icmp_uid; - u32 session_cookie; + u32 session_cookie = 0; bool ret;
get_random_bytes(session_id, sizeof(session_id)); get_random_bytes(&icmp_uid, 1); - session_cookie = batadv_tp_session_cookie(session_id, icmp_uid); - *cookie = session_cookie; + if (cookie) { + session_cookie = batadv_tp_session_cookie(session_id, icmp_uid); + *cookie = session_cookie; + }
if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM_QUEUE)) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, @@ -961,6 +985,10 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
/* initialize tp_vars */ ether_addr_copy(tp_vars->other_end, dst); + if (neigh) { + kref_get(&neigh->refcount); + tp_vars->hardif_neigh = neigh; + } kref_init(&tp_vars->refcount); tp_vars->role = BATADV_TP_SENDER; tp_vars->caller = caller; @@ -1133,7 +1161,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) /** * batadv_tp_send_ack() - send an ACK packet * @bat_priv: the bat priv with all the soft interface information - * @dst: the mac address of the destination originator + * @tp_vars: the private data of the current TP meter session * @seq: the sequence number to ACK * @timestamp: the timestamp to echo back in the ACK * @session: session identifier @@ -1142,29 +1170,42 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) * Return: 0 on success, a positive integer representing the reason of the * failure otherwise */ -static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, +static int batadv_tp_send_ack(struct batadv_priv *bat_priv, + struct batadv_tp_vars *tp_vars, u32 seq, __be32 timestamp, const u8 *session, int socket_index) { struct batadv_hard_iface *primary_if = NULL; - struct batadv_orig_node *orig_node; + struct batadv_orig_node *orig_node = NULL; struct batadv_icmp_tp_packet *icmp; + struct net_device *netdev = NULL; struct sk_buff *skb; + u8 *src, *dst; int r, ret;
- orig_node = batadv_orig_hash_find(bat_priv, dst); - if (unlikely(!orig_node)) { - ret = BATADV_TP_REASON_DST_UNREACHABLE; - goto out; - } + if (tp_vars->hardif_neigh) { + dst = tp_vars->hardif_neigh->addr; + src = tp_vars->hardif_neigh->if_incoming->net_dev->dev_addr; + netdev = tp_vars->hardif_neigh->if_incoming->net_dev; + } else { + orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end); + if (unlikely(!orig_node)) { + ret = BATADV_TP_REASON_DST_UNREACHABLE; + goto out; + }
- primary_if = batadv_primary_if_get_selected(bat_priv); - if (unlikely(!primary_if)) { - ret = BATADV_TP_REASON_DST_UNREACHABLE; - goto out; + primary_if = batadv_primary_if_get_selected(bat_priv); + if (unlikely(!primary_if)) { + ret = BATADV_TP_REASON_DST_UNREACHABLE; + goto out; + } + + dst = orig_node->orig; + src = primary_if->net_dev->dev_addr; + netdev = NULL; }
- skb = netdev_alloc_skb_ip_align(NULL, sizeof(*icmp) + ETH_HLEN); + skb = netdev_alloc_skb_ip_align(netdev, sizeof(*icmp) + ETH_HLEN); if (unlikely(!skb)) { ret = BATADV_TP_REASON_MEMORY_ERROR; goto out; @@ -1176,8 +1217,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, icmp->version = BATADV_COMPAT_VERSION; icmp->ttl = BATADV_TTL; icmp->msg_type = BATADV_TP; - ether_addr_copy(icmp->dst, orig_node->orig); - ether_addr_copy(icmp->orig, primary_if->net_dev->dev_addr); + ether_addr_copy(icmp->dst, dst); + ether_addr_copy(icmp->orig, src); icmp->uid = socket_index;
icmp->subtype = BATADV_TP_ACK; @@ -1186,7 +1227,13 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst, icmp->timestamp = timestamp;
/* send the ack */ - r = batadv_send_skb_to_orig(skb, orig_node, NULL); + if (tp_vars->hardif_neigh) + r = batadv_send_skb_packet(skb, + tp_vars->hardif_neigh->if_incoming, + dst); + else + r = batadv_send_skb_to_orig(skb, orig_node, NULL); + if (unlikely(r < 0) || r == NET_XMIT_DROP) { ret = BATADV_TP_REASON_DST_UNREACHABLE; goto out; @@ -1314,14 +1361,17 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars) /** * batadv_tp_init_recv() - return matching or create new receiver tp_vars * @bat_priv: the bat priv with all the soft interface information + * @recv_if: interface that the skb is received on * @icmp: received icmp tp msg * * Return: corresponding tp_vars or NULL on errors */ static struct batadv_tp_vars * batadv_tp_init_recv(struct batadv_priv *bat_priv, + struct batadv_hard_iface *recv_if, const struct batadv_icmp_tp_packet *icmp) { + struct batadv_hardif_neigh_node *neigh = NULL; struct batadv_tp_vars *tp_vars;
spin_lock_bh(&bat_priv->tp_list_lock); @@ -1336,15 +1386,30 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, goto out_unlock; }
+ /* the sender is starting a LINK test, therefore we have retrieve its + * corresponding hardif_neigh_node that we'll use later to send ACKs + * back + */ + if (icmp->subtype == BATADV_TP_MSG_LINK) { + neigh = batadv_hardif_neigh_get(recv_if, icmp->orig); + if (!neigh) { + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, + "Meter: %s() can't retrieve sender neigh object for %pM\n", + __func__, icmp->orig); + goto out_unlock; + } + } + tp_vars = kmalloc(sizeof(*tp_vars), GFP_ATOMIC); if (!tp_vars) - goto out_unlock; + goto err_neigh_put;
ether_addr_copy(tp_vars->other_end, icmp->orig); tp_vars->role = BATADV_TP_RECEIVER; memcpy(tp_vars->session, icmp->session, sizeof(tp_vars->session)); tp_vars->last_recv = BATADV_TP_FIRST_SEQ; tp_vars->bat_priv = bat_priv; + tp_vars->hardif_neigh = neigh; kref_init(&tp_vars->refcount);
spin_lock_init(&tp_vars->unacked_lock); @@ -1357,7 +1422,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
batadv_tp_reset_receiver_timer(tp_vars); + goto out_unlock;
+err_neigh_put: + batadv_hardif_neigh_put(neigh); out_unlock: spin_unlock_bh(&bat_priv->tp_list_lock);
@@ -1367,11 +1435,13 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, /** * batadv_tp_recv_msg() - process a single data message * @bat_priv: the bat priv with all the soft interface information + * @recv_if: interface that the skb is received on * @skb: the buffer containing the received packet * * Process a received TP MSG packet */ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, + struct batadv_hard_iface *recv_if, const struct sk_buff *skb) { const struct batadv_icmp_tp_packet *icmp; @@ -1386,7 +1456,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, * first packet is lost, the tp meter does not work anymore! */ if (seqno == BATADV_TP_FIRST_SEQ) { - tp_vars = batadv_tp_init_recv(bat_priv, icmp); + tp_vars = batadv_tp_init_recv(bat_priv, recv_if, icmp); if (!tp_vars) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: seqno != BATADV_TP_FIRST_SEQ cannot initiate connection\n"); @@ -1442,7 +1512,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, * is going to be sent is a duplicate (the sender will count them and * possibly enter Fast Retransmit as soon as it has reached 3) */ - batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv, + batadv_tp_send_ack(bat_priv, tp_vars, tp_vars->last_recv, icmp->timestamp, icmp->session, icmp->uid); out: if (likely(tp_vars)) @@ -1452,9 +1522,12 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, /** * batadv_tp_meter_recv() - main TP Meter receiving function * @bat_priv: the bat priv with all the soft interface information + * @recv_if: interface that the skb is received on * @skb: the buffer containing the received packet */ -void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb) +void batadv_tp_meter_recv(struct batadv_priv *bat_priv, + struct batadv_hard_iface *recv_if, + struct sk_buff *skb) { struct batadv_icmp_tp_packet *icmp;
@@ -1462,7 +1535,8 @@ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb)
switch (icmp->subtype) { case BATADV_TP_MSG: - batadv_tp_recv_msg(bat_priv, skb); + case BATADV_TP_MSG_LINK: + batadv_tp_recv_msg(bat_priv, recv_if, skb); break; case BATADV_TP_ACK: batadv_tp_recv_ack(bat_priv, skb); diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h index 3b11a3e9..3a1be483 100644 --- a/net/batman-adv/tp_meter.h +++ b/net/batman-adv/tp_meter.h @@ -28,10 +28,13 @@ struct sk_buff; int batadv_tp_meter_init(void); void batadv_tp_meter_destroy(void); void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, + struct batadv_hardif_neigh_node *neigh, u32 test_length, u32 *cookie, enum batadv_tp_meter_caller caller); void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, u8 return_value); -void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb); +void batadv_tp_meter_recv(struct batadv_priv *bat_priv, + struct batadv_hard_iface *recv_if, + struct sk_buff *skb);
#endif /* _NET_BATMAN_ADV_TP_METER_H_ */ diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 56034b54..f72db6cf 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1455,6 +1455,9 @@ struct batadv_tp_vars {
/** @rcu: struct used for freeing in an RCU-safe manner */ struct rcu_head rcu; + + /** @hardif_neigh: in case of LINK test, represents the other-end */ + struct batadv_hardif_neigh_node *hardif_neigh; };
/**
Signed-off-by: Marek Lindner mareklindner@neomailbox.ch --- net/batman-adv/bat_v_elp.c | 67 ++++++++++++++++++++++++++++++++++++-- net/batman-adv/bat_v_elp.h | 21 ++++++++++++ net/batman-adv/main.h | 1 + net/batman-adv/tp_meter.c | 41 ++++++++++++++++++----- net/batman-adv/types.h | 14 ++++++++ 5 files changed, 133 insertions(+), 11 deletions(-)
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index e103c759..6e700fcf 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,42 @@ 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, + 1000, 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 @@ -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,29 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) return throughput * 10; }
+fallback_throughput: + /* 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. + * + * No lock is required because this is the only point where + * batadv_v_elp_tp_start() is invoked and we get here only through a + * periodic timer. This means we will never run this function + * concurrently with itself. + */ + if (!neigh->bat_v.tp_meter_running && + batadv_has_timed_out(neigh->bat_v.last_tp_meter_run, + BATADV_ELP_TP_RUN_INTERVAL)) + batadv_v_elp_tp_start(neigh); + + /* discard too old tp test results */ + if (batadv_has_timed_out(neigh->bat_v.last_tp_meter_run, + 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 f23bb847..96e03da0 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" @@ -224,7 +225,7 @@ static void batadv_tp_caller_notify(struct batadv_priv *bat_priv, struct batadv_tp_vars *tp_vars, enum batadv_tp_meter_reason reason) { - u32 total_bytes; + u64 total_bytes; u32 test_time; u32 cookie; bool reason_is_error; @@ -251,6 +252,24 @@ 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); + if (!test_time) { + batadv_v_elp_tp_fail(tp_vars->hardif_neigh); + return; + } + + /* The following calculation includes these steps: + * - divide bytes by the test length (msecs) + * - convert result from bits/ms to 0.1Mb/s (1Mb/s==1000000b/s) + */ + total_bytes = atomic64_read(&tp_vars->tot_sent); + do_div(total_bytes, test_time * 125); + batadv_v_elp_tp_finish(tp_vars->hardif_neigh, total_bytes); break; default: break; @@ -264,11 +283,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 +298,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; @@ -971,7 +994,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; }
@@ -979,7 +1002,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 f72db6cf..fe763410 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -584,6 +584,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; };
/**
On Freitag, 7. September 2018 11:59:57 CEST Marek Lindner wrote:
Signed-off-by: Marek Lindner mareklindner@neomailbox.ch
Most patches in the patchset have quite long patch summary phrase [1]. And this one here (and the patch 3) doesn't have a commit message at all.
Wouldn't it be better to have some actual message and a more brief+precise subject
batman-adv: ELP - Fall back to tpmeter for throughput estimation
... when nothing works ... blablabla .. better than nothing ... blublub under following conditions ... done every ... more text here ...
(actual info has to be added and the subject can be better then the example one here).
Kind regards, Sven
[1] https://www.kernel.org/doc/html/v4.17/process/submitting-patches.html#the-ca...
When the ELP throughput meter fallback kicks in to trigger a throughput meter measurement the test duration can be configured via this attribute.
Default tp test duration: 1000ms
Signed-off-by: Marek Lindner mareklindner@neomailbox.ch --- Documentation/ABI/testing/sysfs-class-net-batman-adv | 7 +++++++ net/batman-adv/bat_v.c | 1 + net/batman-adv/bat_v_elp.c | 6 ++++-- net/batman-adv/sysfs.c | 3 +++ net/batman-adv/types.h | 3 +++ 5 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-class-net-batman-adv b/Documentation/ABI/testing/sysfs-class-net-batman-adv index 89810684..7b3974a5 100644 --- a/Documentation/ABI/testing/sysfs-class-net-batman-adv +++ b/Documentation/ABI/testing/sysfs-class-net-batman-adv @@ -6,6 +6,13 @@ Description: Defines the interval in milliseconds in which batman emits probing packets for neighbor sensing (ELP).
+What: /sys/class/net/<iface>/batman-adv/elp_tp_duration +Date: May 2018 +Contact: Marek Lindner mareklindner@neomailbox.ch +Description: + Defines measurement duration in milliseconds upon + ELP fallback throughput meter measurements. + What: /sys/class/net/<iface>/batman-adv/iface_status Date: May 2010 Contact: Marek Lindner mareklindner@neomailbox.ch diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index 6baec4e6..1b3a250f 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -1086,6 +1086,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) */ atomic_set(&hard_iface->bat_v.throughput_override, 0); atomic_set(&hard_iface->bat_v.elp_interval, 500); + atomic_set(&hard_iface->bat_v.elp_tp_duration, 1000); }
/** diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index 6e700fcf..5ade4a66 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -76,10 +76,12 @@ 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); + u32 elp_tp_duration;
neigh->bat_v.tp_meter_running = true; - batadv_tp_start(bat_priv, neigh->addr, neigh, - 1000, NULL, BATADV_TP_ELP); + elp_tp_duration = atomic_read(&hard_iface->bat_v.elp_tp_duration); + batadv_tp_start(bat_priv, neigh->addr, neigh, elp_tp_duration, + NULL, BATADV_TP_ELP); }
/** diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index 09427fc6..56d765be 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -1138,6 +1138,8 @@ static BATADV_ATTR(iface_status, 0444, batadv_show_iface_status, NULL); #ifdef CONFIG_BATMAN_ADV_BATMAN_V BATADV_ATTR_HIF_UINT(elp_interval, bat_v.elp_interval, 0644, 2 * BATADV_JITTER, INT_MAX, NULL); +BATADV_ATTR_HIF_UINT(elp_tp_duration, bat_v.elp_tp_duration, 0644, + 1, INT_MAX, NULL); static BATADV_ATTR(throughput_override, 0644, batadv_show_throughput_override, batadv_store_throughput_override); #endif @@ -1147,6 +1149,7 @@ static struct batadv_attribute *batadv_batman_attrs[] = { &batadv_attr_iface_status, #ifdef CONFIG_BATMAN_ADV_BATMAN_V &batadv_attr_elp_interval, + &batadv_attr_elp_tp_duration, &batadv_attr_throughput_override, #endif NULL, diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index fe763410..62c9b49d 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -118,6 +118,9 @@ struct batadv_hard_iface_bat_v { /** @elp_interval: time interval between two ELP transmissions */ atomic_t elp_interval;
+ /** @elp_tp_duration: throughput meter fallback test duration */ + atomic_t elp_tp_duration; + /** @elp_seqno: current ELP sequence number */ atomic_t elp_seqno;
On Freitag, 7. September 2018 11:59:51 CEST Marek Lindner wrote:
Under normal circumstances B.A.T.M.A.N. V retrieves the neighbor throughput values to populate its metric tables from the various drivers such as WiFi throughput tables and Ethernet throughput.. Whenever the interface drivers do not export link throughput information manual overrides become necessary. To further automate and thus better support these setups, ELP may call the batman-adv throughput meter to schedule a throughput estimation to be used to populate the metric table.
I know that this patchset is required to have some throughput estimates for non-wifi connections (or for wifi drivers without expected throughput). But since we have the other discussion with Ligang LIU, I still have to bring this up:
We already know that something (maybe ELP unicast probes and the related messages) are "breaking" the connectivity between 2 neighbors in Ligang LIU's test setup (with 6 not so well connected nodes). It also seems to be the case that the driver doesn't return the expected throughput for each neighbor all the time and this missing data would then trigger the single-link tpmeter test.
The test seem to be done every 60 seconds (or longer when there is another source for the throughput) for each neighbor and by default 1s long. My assumption would be now that this test could be even more harmful for wifi connections than unicast ELP probes (or whatever is causing Ligang LUI's problems). Let us just assume that we have these 6 nodes with 5 neighbors (I see a lot more in real world setups). If the tests on the different nodes don't overlap (which would also be bad for the test results) than all tests would take at least 30 seconds of the 60 seconds. So even without the problem in the "Performance Evaluation of BATMAN-adv Wireless Mesh Network Routing Algorithms" mailing list thread, the tpmeter fallback has a significant cost.
Is there anything which I missed that could ease my mind?
Kind regards, Sven
On Saturday, 8 September 2018 19:38:01 CET Sven Eckelmann wrote: [... explanation why this test could create problems for other wifi connections...]
Is there anything which I missed that could ease my mind?
Beside this potential problem, there is also the problem of the interaction with the ethtool throughput gathering (brought up by Matthias Schiffer [1] and Matthias Fritzsche [2]). The ethtool code basically prevents the correct measurement in many situations. I will try to summarize it now:
* ethtool's link_ksettings is not about speed towards a neighbor behind the interface but about either
- PHY layer speed class from local ethernet port to next peer (for direct connections) - connection towards next (internal) switch - propagated value from a lower layer device (e.g for vxlan) - "random" value (e.g. tun/tap) - ...
* often connections indirect (switches, VPN, WiFi bridges, L2 firewalls, ...) * B.A.T.M.A.N. V requires a measurement for the throughput towards a neighbor and not the maximum(?) PHY layer speed of the first hop over the current medium
The proposed solution is to drop the ethtool link_ksettings usage when the tpmeter (or similar) approach for neighbor speed measurements is integrated.
Kind regards, Sven
[1] https://github.com/freifunk-gluon/gluon/pull/1872#issuecomment-55767698 [2] https://patchwork.open-mesh.org/patch/17371/#31059
b.a.t.m.a.n@lists.open-mesh.org