|
@@ -427,16 +427,127 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
|
|
|
struct batadv_neigh_node *
|
|
|
batadv_find_router(struct batadv_priv *bat_priv,
|
|
|
struct batadv_orig_node *orig_node,
|
|
|
- const struct batadv_hard_iface *recv_if)
|
|
|
+ struct batadv_hard_iface *recv_if)
|
|
|
{
|
|
|
- struct batadv_neigh_node *router;
|
|
|
+ struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
|
|
|
+ struct batadv_neigh_node *first_candidate_router = NULL;
|
|
|
+ struct batadv_neigh_node *next_candidate_router = NULL;
|
|
|
+ struct batadv_neigh_node *router, *cand_router = NULL;
|
|
|
+ struct batadv_neigh_node *last_cand_router = NULL;
|
|
|
+ struct batadv_orig_ifinfo *cand, *first_candidate = NULL;
|
|
|
+ struct batadv_orig_ifinfo *next_candidate = NULL;
|
|
|
+ struct batadv_orig_ifinfo *last_candidate;
|
|
|
+ bool last_candidate_found = false;
|
|
|
|
|
|
if (!orig_node)
|
|
|
return NULL;
|
|
|
|
|
|
router = batadv_orig_router_get(orig_node, recv_if);
|
|
|
|
|
|
- /* TODO: fill this later with new bonding mechanism */
|
|
|
+ /* only consider bonding for recv_if == BATADV_IF_DEFAULT (first hop)
|
|
|
+ * and if activated.
|
|
|
+ */
|
|
|
+ if (recv_if == BATADV_IF_DEFAULT || !atomic_read(&bat_priv->bonding) ||
|
|
|
+ !router)
|
|
|
+ return router;
|
|
|
+
|
|
|
+ /* bonding: loop through the list of possible routers found
|
|
|
+ * for the various outgoing interfaces and find a candidate after
|
|
|
+ * the last chosen bonding candidate (next_candidate). If no such
|
|
|
+ * router is found, use the first candidate found (the previously
|
|
|
+ * chosen bonding candidate might have been the last one in the list).
|
|
|
+ * If this can't be found either, return the previously choosen
|
|
|
+ * router - obviously there are no other candidates.
|
|
|
+ */
|
|
|
+ rcu_read_lock();
|
|
|
+ last_candidate = orig_node->last_bonding_candidate;
|
|
|
+ if (last_candidate)
|
|
|
+ last_cand_router = rcu_dereference(last_candidate->router);
|
|
|
+
|
|
|
+ hlist_for_each_entry_rcu(cand, &orig_node->ifinfo_list, list) {
|
|
|
+ /* acquire some structures and references ... */
|
|
|
+ if (!atomic_inc_not_zero(&cand->refcount))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ cand_router = rcu_dereference(cand->router);
|
|
|
+ if (!cand_router)
|
|
|
+ goto next;
|
|
|
+
|
|
|
+ if (!atomic_inc_not_zero(&cand_router->refcount)) {
|
|
|
+ cand_router = NULL;
|
|
|
+ goto next;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* alternative candidate should be good enough to be
|
|
|
+ * considered
|
|
|
+ */
|
|
|
+ if (!bao->bat_neigh_is_equiv_or_better(cand_router,
|
|
|
+ cand->if_outgoing,
|
|
|
+ router, recv_if))
|
|
|
+ goto next;
|
|
|
+
|
|
|
+ /* don't use the same router twice */
|
|
|
+ if (last_cand_router == cand_router)
|
|
|
+ goto next;
|
|
|
+
|
|
|
+ /* mark the first possible candidate */
|
|
|
+ if (!first_candidate) {
|
|
|
+ atomic_inc(&cand_router->refcount);
|
|
|
+ atomic_inc(&cand->refcount);
|
|
|
+ first_candidate = cand;
|
|
|
+ first_candidate_router = cand_router;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* check if the loop has already passed the previously selected
|
|
|
+ * candidate ... this function should select the next candidate
|
|
|
+ * AFTER the previously used bonding candidate.
|
|
|
+ */
|
|
|
+ if (!last_candidate || last_candidate_found) {
|
|
|
+ next_candidate = cand;
|
|
|
+ next_candidate_router = cand_router;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (last_candidate == cand)
|
|
|
+ last_candidate_found = true;
|
|
|
+next:
|
|
|
+ /* free references */
|
|
|
+ if (cand_router) {
|
|
|
+ batadv_neigh_node_free_ref(cand_router);
|
|
|
+ cand_router = NULL;
|
|
|
+ }
|
|
|
+ batadv_orig_ifinfo_free_ref(cand);
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ /* last_bonding_candidate is reset below, remove the old reference. */
|
|
|
+ if (orig_node->last_bonding_candidate)
|
|
|
+ batadv_orig_ifinfo_free_ref(orig_node->last_bonding_candidate);
|
|
|
+
|
|
|
+ /* After finding candidates, handle the three cases:
|
|
|
+ * 1) there is a next candidate, use that
|
|
|
+ * 2) there is no next candidate, use the first of the list
|
|
|
+ * 3) there is no candidate at all, return the default router
|
|
|
+ */
|
|
|
+ if (next_candidate) {
|
|
|
+ batadv_neigh_node_free_ref(router);
|
|
|
+
|
|
|
+ /* remove references to first candidate, we don't need it. */
|
|
|
+ if (first_candidate) {
|
|
|
+ batadv_neigh_node_free_ref(first_candidate_router);
|
|
|
+ batadv_orig_ifinfo_free_ref(first_candidate);
|
|
|
+ }
|
|
|
+ router = next_candidate_router;
|
|
|
+ orig_node->last_bonding_candidate = next_candidate;
|
|
|
+ } else if (first_candidate) {
|
|
|
+ batadv_neigh_node_free_ref(router);
|
|
|
+
|
|
|
+ /* refcounting has already been done in the loop above. */
|
|
|
+ router = first_candidate_router;
|
|
|
+ orig_node->last_bonding_candidate = first_candidate;
|
|
|
+ } else {
|
|
|
+ orig_node->last_bonding_candidate = NULL;
|
|
|
+ }
|
|
|
|
|
|
return router;
|
|
|
}
|