/** * struct batadv_v_metric_queue_entry - list of hardif neighbors which require * and metric update
*/ struct batadv_v_metric_queue_entry { /** @hardif_neigh: hardif neighbor scheduled for metric update */ struct batadv_hardif_neigh_node *hardif_neigh;
/** @list: list node for metric_queue */ struct list_head list;
};
/** * batadv_v_elp_start_timer() - restart timer for ELP periodic work * @hard_iface: the interface for which the timer has to be reset
*/ staticvoid batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface)
{ unsignedint msecs;
/** * batadv_v_elp_get_throughput() - get the throughput towards a neighbour * @neigh: the neighbour for which the throughput has to be obtained * @pthroughput: calculated throughput towards the given neighbour in multiples * of 100kpbs (a value of '1' equals 0.1Mbps, '10' equals 1Mbps, etc). * * Return: true when value behind @pthroughput was set
*/ staticbool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,
u32 *pthroughput)
{ struct batadv_hard_iface *hard_iface = neigh->if_incoming; struct net_device *mesh_iface = hard_iface->mesh_iface; struct ethtool_link_ksettings link_settings; struct net_device *real_netdev; struct station_info sinfo;
u32 throughput; int ret;
/* don't query throughput when no longer associated with any * batman-adv interface
*/ if (!mesh_iface) returnfalse;
/* if the user specified a customised value for this interface, then * return it directly
*/
throughput = atomic_read(&hard_iface->bat_v.throughput_override); if (throughput != 0) {
*pthroughput = throughput; returntrue;
}
/* if this is a wireless device, then ask its throughput through * cfg80211 API
*/ if (batadv_is_wifi_hardif(hard_iface)) { if (!batadv_is_cfg80211_hardif(hard_iface)) /* unsupported WiFi driver version */ goto default_throughput;
real_netdev = batadv_get_real_netdev(hard_iface->net_dev); if (!real_netdev) goto default_throughput;
ret = cfg80211_get_station(real_netdev, neigh->addr, &sinfo);
if (!ret) { /* free the TID stats immediately */
cfg80211_sinfo_release_content(&sinfo);
}
dev_put(real_netdev); if (ret == -ENOENT) { /* Node is not associated anymore! It would be * possible to delete this neighbor. For now set * the throughput metric to 0.
*/
*pthroughput = 0; returntrue;
} if (ret) goto default_throughput;
/* try to estimate the expected throughput based on reported tx * rates
*/ if (sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE)) {
*pthroughput = cfg80211_calculate_bitrate(&sinfo.txrate) / 3; returntrue;
}
goto default_throughput;
}
/* only use rtnl_trylock because the elp worker will be cancelled while * the rntl_lock is held. the cancel_delayed_work_sync() would otherwise * wait forever when the elp work_item was started and it is then also * trying to rtnl_lock
*/ if (!rtnl_trylock()) returnfalse;
/* if not a wifi interface, check if this device provides data via * ethtool (e.g. an Ethernet adapter)
*/
ret = __ethtool_get_link_ksettings(hard_iface->net_dev, &link_settings);
rtnl_unlock(); if (ret == 0) { /* link characteristics might change over time */ if (link_settings.base.duplex == DUPLEX_FULL)
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX; else
hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
default_throughput: if (!(hard_iface->bat_v.flags & BATADV_WARNING_DEFAULT)) {
batadv_info(mesh_iface, "WiFi driver or ethtool info does not provide information about link speeds on interface %s, therefore defaulting to hardcoded throughput values of %u.%1u Mbps. Consider overriding the throughput manually or checking your driver.\n",
hard_iface->net_dev->name,
BATADV_THROUGHPUT_DEFAULT_VALUE / 10,
BATADV_THROUGHPUT_DEFAULT_VALUE % 10);
hard_iface->bat_v.flags |= BATADV_WARNING_DEFAULT;
}
/* if none of the above cases apply, return the base_throughput */
*pthroughput = BATADV_THROUGHPUT_DEFAULT_VALUE; returntrue;
}
/** * batadv_v_elp_throughput_metric_update() - worker updating the throughput * metric of a single hop neighbour * @neigh: the neighbour to probe
*/ staticvoid
batadv_v_elp_throughput_metric_update(struct batadv_hardif_neigh_node *neigh)
{
u32 throughput; bool valid;
valid = batadv_v_elp_get_throughput(neigh, &throughput); if (!valid) return;
/** * batadv_v_elp_wifi_neigh_probe() - send link probing packets to a neighbour * @neigh: the neighbour to probe * * Sends a predefined number of unicast wifi packets to a given neighbour in * order to trigger the throughput estimation on this link by the RC algorithm. * Packets are sent only if there is not enough payload unicast traffic towards * this neighbour.. * * Return: True on success and false in case of error during skb preparation.
*/ staticbool
batadv_v_elp_wifi_neigh_probe(struct batadv_hardif_neigh_node *neigh)
{ struct batadv_hard_iface *hard_iface = neigh->if_incoming; struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); unsignedlong last_tx_diff; struct sk_buff *skb; int probe_len, i; int elp_skb_len;
/* this probing routine is for Wifi neighbours only */ if (!batadv_is_wifi_hardif(hard_iface)) returntrue;
/* probe the neighbor only if no unicast packets have been sent * to it in the last 100 milliseconds: this is the rate control * algorithm sampling interval (minstrel). In this way, if not * enough traffic has been sent to the neighbor, batman-adv can * generate 2 probe packets and push the RC algorithm to perform * the sampling
*/
last_tx_diff = jiffies_to_msecs(jiffies - neigh->bat_v.last_unicast_tx); if (last_tx_diff <= BATADV_ELP_PROBE_MAX_TX_DIFF) returntrue;
for (i = 0; i < BATADV_ELP_PROBES_PER_NODE; i++) {
elp_skb_len = hard_iface->bat_v.elp_skb->len;
skb = skb_copy_expand(hard_iface->bat_v.elp_skb, 0,
probe_len - elp_skb_len,
GFP_ATOMIC); if (!skb) returnfalse;
/* Tell the skb to get as big as the allocated space (we want * the packet to be exactly of that size to make the link * throughput estimation effective.
*/
skb_put_zero(skb, probe_len - hard_iface->bat_v.elp_skb->len);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Sending unicast (probe) ELP packet on interface %s to %pM\n",
hard_iface->net_dev->name, neigh->addr);
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) goto out;
/* we are in the process of shutting this interface down */ if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
hard_iface->if_status == BATADV_IF_TO_BE_REMOVED) goto out;
/* the interface was enabled but may not be ready yet */ if (hard_iface->if_status != BATADV_IF_ACTIVE) goto restart_timer;
skb = skb_copy(hard_iface->bat_v.elp_skb, GFP_ATOMIC); if (!skb) goto restart_timer;
/* The throughput metric is updated on each sent packet. This way, if a * node is dead and no longer sends packets, batman-adv is still able to * react timely to its death. * * The throughput metric is updated by following these steps: * 1) if the hard_iface is wifi => send a number of unicast ELPs for * probing/sampling to each neighbor * 2) update the throughput metric value of each neighbor (note that the * value retrieved in this step might be 100ms old because the * probing packets at point 1) could still be in the HW queue)
*/
rcu_read_lock();
hlist_for_each_entry_rcu(hardif_neigh, &hard_iface->neigh_list, list) { if (!batadv_v_elp_wifi_neigh_probe(hardif_neigh)) /* if something goes wrong while probing, better to stop * sending packets immediately and reschedule the task
*/ break;
if (!kref_get_unless_zero(&hardif_neigh->refcount)) continue;
/* Reading the estimated throughput from cfg80211 is a task that * may sleep and that is not allowed in an rcu protected * context. Therefore add it to metric_queue and process it * outside rcu protected context.
*/
metric_entry = kzalloc(sizeof(*metric_entry), GFP_ATOMIC); if (!metric_entry) {
batadv_hardif_neigh_put(hardif_neigh); continue;
}
/** * batadv_v_elp_iface_enable() - setup the ELP interface private resources * @hard_iface: interface for which the data has to be prepared * * Return: 0 on success or a -ENOMEM in case of failure.
*/ int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface)
{ staticconst size_t tvlv_padding = sizeof(__be32); struct batadv_elp_packet *elp_packet; unsignedchar *elp_buff;
u32 random_seqno;
size_t size; int res = -ENOMEM;
/* assume full-duplex by default */
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
/* warn the user (again) if there is no throughput data is available */
hard_iface->bat_v.flags &= ~BATADV_WARNING_DEFAULT;
if (batadv_is_wifi_hardif(hard_iface))
hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
INIT_DELAYED_WORK(&hard_iface->bat_v.elp_wq,
batadv_v_elp_periodic_work);
batadv_v_elp_start_timer(hard_iface);
res = 0;
out: return res;
}
/** * batadv_v_elp_iface_disable() - release ELP interface private resources * @hard_iface: interface for which the resources have to be released
*/ void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface)
{
cancel_delayed_work_sync(&hard_iface->bat_v.elp_wq);
/** * batadv_v_elp_primary_iface_set() - change internal data to reflect the new * primary interface * @primary_iface: the new primary interface
*/ void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface)
{ struct batadv_hard_iface *hard_iface; struct list_head *iter;
/* update orig field of every elp iface belonging to this mesh */
rcu_read_lock();
netdev_for_each_lower_private_rcu(primary_iface->mesh_iface, hard_iface, iter)
batadv_v_elp_iface_activate(primary_iface, hard_iface);
rcu_read_unlock();
}
/** * batadv_v_elp_neigh_update() - update an ELP neighbour node * @bat_priv: the bat priv with all the mesh interface information * @neigh_addr: the neighbour interface address * @if_incoming: the interface the packet was received through * @elp_packet: the received ELP packet * * Updates the ELP neighbour node state with the data received within the new * ELP packet.
*/ staticvoid batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,
u8 *neigh_addr, struct batadv_hard_iface *if_incoming, struct batadv_elp_packet *elp_packet)
/* known or older sequence numbers are ignored. However always adopt * if the router seems to have been restarted.
*/ if (seqno_diff < 1 && seqno_diff > -BATADV_ELP_MAX_AGE) goto hardif_free;
/** * batadv_v_elp_packet_recv() - main ELP packet handler * @skb: the received packet * @if_incoming: the interface this packet was received through * * Return: NET_RX_SUCCESS and consumes the skb if the packet was properly * processed or NET_RX_DROP in case of failure.
*/ int batadv_v_elp_packet_recv(struct sk_buff *skb, struct batadv_hard_iface *if_incoming)
{ struct batadv_priv *bat_priv = netdev_priv(if_incoming->mesh_iface); struct batadv_elp_packet *elp_packet; struct batadv_hard_iface *primary_if; struct ethhdr *ethhdr; bool res; int ret = NET_RX_DROP;
res = batadv_check_management_packet(skb, if_incoming, BATADV_ELP_HLEN); if (!res) goto free_skb;
ethhdr = eth_hdr(skb); if (batadv_is_my_mac(bat_priv, ethhdr->h_source)) goto free_skb;
/* did we receive a B.A.T.M.A.N. V ELP packet on an interface * that does not have B.A.T.M.A.N. V ELP enabled ?
*/ if (strcmp(bat_priv->algo_ops->name, "BATMAN_V") != 0) goto free_skb;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.