diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 53fbdbbe8f4f..3fe09b4c3046 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -2424,25 +2424,26 @@ static int batadv_iv_ogm_neigh_cmp(struct batadv_neigh_node *neigh1, /** * batadv_iv_ogm_neigh_is_sob() - check if neigh1 is similarly good or better * than neigh2 from the metric prospective - * @neigh1: the first neighbor object of the comparison - * @if_outgoing1: outgoing interface for the first neighbor - * @neigh2: the second neighbor object of the comparison - * @if_outgoing2: outgoing interface for the second neighbor + * @candidate: the first neighbor object of the comparison + * @if_outgoing_cand: outgoing interface for the @candidate neighbor + * @reference: the second neighbor object of the comparison + * @if_outgoing_ref: outgoing interface for the @reference neighbor * - * Return: true if the metric via neigh1 is equally good or better than - * the metric via neigh2, false otherwise. + * Return: true if the metric via @candidate is equally good or better than + * the metric via @reference, false otherwise. */ static bool -batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1, - struct batadv_hard_iface *if_outgoing1, - struct batadv_neigh_node *neigh2, - struct batadv_hard_iface *if_outgoing2) +batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *candidate, + struct batadv_hard_iface *if_outgoing_cand, + struct batadv_neigh_node *reference, + struct batadv_hard_iface *if_outgoing_ref) { bool ret; int diff; - ret = batadv_iv_ogm_neigh_diff(neigh1, if_outgoing1, neigh2, - if_outgoing2, &diff); + ret = batadv_iv_ogm_neigh_diff(candidate, if_outgoing_cand, + reference, if_outgoing_ref, + &diff); if (!ret) return false; diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index 0c27447cf688..596889b3522d 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -514,40 +514,40 @@ static int batadv_v_neigh_cmp(struct batadv_neigh_node *neigh1, /** * batadv_v_neigh_is_sob() - check whether two B.A.T.M.A.N. V neighbours have * a similar or better throughput - * @neigh1: first neighbour to compare - * @if_outgoing1: outgoing interface to use for @neigh1 - * @neigh2: second neighbour to compare - * @if_outgoing2: outgoing interface to use for @neigh2 + * @candidate: the first neighbor object of the comparison + * @if_outgoing_cand: outgoing interface for the @candidate neighbor + * @reference: the second neighbor object of the comparison + * @if_outgoing_ref: outgoing interface for the @reference neighbor * - * Return: true if the throughput of @neigh2 is at least 3/4 of the - * @neigh1 throughput + * Return: true if the throughput of @candidate is more than 3/4 of the + * @reference throughput */ -static bool batadv_v_neigh_is_sob(struct batadv_neigh_node *neigh1, - struct batadv_hard_iface *if_outgoing1, - struct batadv_neigh_node *neigh2, - struct batadv_hard_iface *if_outgoing2) +static bool batadv_v_neigh_is_sob(struct batadv_neigh_node *candidate, + struct batadv_hard_iface *if_outgoing_cand, + struct batadv_neigh_node *reference, + struct batadv_hard_iface *if_outgoing_ref) { - struct batadv_neigh_ifinfo *ifinfo1; - struct batadv_neigh_ifinfo *ifinfo2; + struct batadv_neigh_ifinfo *ifinfo_cand; + struct batadv_neigh_ifinfo *ifinfo_ref; bool ret = false; u32 threshold; - ifinfo1 = batadv_neigh_ifinfo_get(neigh1, if_outgoing1); - if (!ifinfo1) + ifinfo_cand = batadv_neigh_ifinfo_get(candidate, if_outgoing_cand); + if (!ifinfo_cand) goto err_ifinfo1; - ifinfo2 = batadv_neigh_ifinfo_get(neigh2, if_outgoing2); - if (!ifinfo2) + ifinfo_ref = batadv_neigh_ifinfo_get(reference, if_outgoing_ref); + if (!ifinfo_ref) goto err_ifinfo2; - threshold = ifinfo1->bat_v.throughput / 4; - threshold = ifinfo1->bat_v.throughput - threshold; + threshold = ifinfo_ref->bat_v.throughput / 4; + threshold = ifinfo_ref->bat_v.throughput - threshold; - ret = ifinfo2->bat_v.throughput > threshold; + ret = ifinfo_cand->bat_v.throughput > threshold; - batadv_neigh_ifinfo_put(ifinfo2); + batadv_neigh_ifinfo_put(ifinfo_ref); err_ifinfo2: - batadv_neigh_ifinfo_put(ifinfo1); + batadv_neigh_ifinfo_put(ifinfo_cand); err_ifinfo1: return ret; } diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index f635da4b8ca6..ad6ab4a50658 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -663,7 +663,7 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw) static void batadv_bla_send_announce(struct batadv_priv *bat_priv, struct batadv_bla_backbone_gw *backbone_gw) { - u8 mac[ETH_ALEN]; + u8 mac[ETH_ALEN] __aligned(2); __be16 crc; memcpy(mac, batadv_announce_mac, 4); @@ -1582,7 +1582,9 @@ static struct lock_class_key batadv_backbone_hash_lock_class_key; */ int batadv_bla_init(struct batadv_priv *bat_priv) { - u8 claim_dest[ETH_ALEN] = {0xff, 0x43, 0x05, 0x00, 0x00, 0x00}; + static const u8 claim_dest[ETH_ALEN] __aligned(2) = { + 0xff, 0x43, 0x05, 0x00, 0x00, 0x00 + }; struct batadv_hard_iface *primary_if; unsigned long entrytime; u16 crc; diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 011cfdc47fab..a4be5b2a87d7 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -548,7 +548,7 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb, break; default: batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: Unknown (%u)!\n", - unicast_4addr_packet->u.packet_type); + unicast_4addr_packet->subtype); } break; case BATADV_BCAST: @@ -940,7 +940,7 @@ batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid, struct netlink_callback *cb, struct batadv_dat_entry *dat_entry) { - u8 mac[ETH_ALEN]; + u8 mac[ETH_ALEN] __aligned(2); u64 u64_mac; int msecs; void *hdr; @@ -1236,10 +1236,10 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, { struct net_device *mesh_iface = bat_priv->mesh_iface; struct batadv_dat_entry *dat_entry = NULL; + u8 mac[ETH_ALEN] __aligned(2); struct sk_buff *skb_new; unsigned short vid; bool ret = false; - u8 mac[ETH_ALEN]; int hdr_size = 0; __be32 ip_dst; __be32 ip_src; @@ -1344,10 +1344,10 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, struct sk_buff *skb, int hdr_size) { struct batadv_dat_entry *dat_entry = NULL; + u8 mac[ETH_ALEN] __aligned(2); struct sk_buff *skb_new; unsigned short vid; bool ret = false; - u8 mac[ETH_ALEN]; __be32 ip_src; __be32 ip_dst; u64 u64_mac; @@ -1474,9 +1474,9 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, struct sk_buff *skb, int hdr_size) { struct batadv_dat_entry *dat_entry = NULL; + u8 mac[ETH_ALEN] __aligned(2); bool dropped = false; unsigned short vid; - u8 mac[ETH_ALEN]; __be32 ip_src; __be32 ip_dst; u64 u64_mac; diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index e202088cf1fd..63af21954cf9 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -141,9 +141,9 @@ static struct net_device_stats *batadv_interface_stats(struct net_device *dev) static int batadv_interface_set_mac_addr(struct net_device *dev, void *p) { struct batadv_priv *bat_priv = netdev_priv(dev); + u8 old_addr[ETH_ALEN] __aligned(2); struct batadv_meshif_vlan *vlan; struct sockaddr *addr = p; - u8 old_addr[ETH_ALEN]; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; @@ -213,13 +213,18 @@ static void batadv_interface_set_rx_mode(struct net_device *dev) static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, struct net_device *mesh_iface) { - static const u8 ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00, 0x00, 0x00}; - static const u8 stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x00}; struct batadv_priv *bat_priv = netdev_priv(mesh_iface); enum batadv_dhcp_recipient dhcp_rcp = BATADV_DHCP_NO; enum batadv_forw_mode forw_mode = BATADV_FORW_BCAST; + static const u8 ectp_addr[ETH_ALEN] __aligned(2) = { + 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + static const u8 stp_addr[ETH_ALEN] __aligned(2) = { + 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 + }; struct batadv_hard_iface *primary_if = NULL; struct batadv_bcast_packet *bcast_packet; + u8 chaddr[ETH_ALEN] __aligned(2); int network_offset = ETH_HLEN; unsigned int header_len = 0; unsigned long brd_delay = 0; @@ -229,7 +234,6 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb, struct ethhdr *ethhdr; bool do_bcast = false; u8 *dst_hint = NULL; - u8 chaddr[ETH_ALEN]; unsigned short vid; bool client_added; __be16 proto; diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index 82fd527b61c0..22dd28d5e1ab 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -368,9 +368,9 @@ batadv_mcast_mla_meshif_get_ipv4(struct net_device *dev, struct hlist_head *mcast_list, struct batadv_mcast_mla_flags *flags) { + u8 mcast_addr[ETH_ALEN] __aligned(2); struct batadv_hw_addr *new; struct in_device *in_dev; - u8 mcast_addr[ETH_ALEN]; struct ip_mc_list *pmc; int ret = 0; @@ -435,9 +435,9 @@ batadv_mcast_mla_meshif_get_ipv6(struct net_device *dev, struct hlist_head *mcast_list, struct batadv_mcast_mla_flags *flags) { + u8 mcast_addr[ETH_ALEN] __aligned(2); struct batadv_hw_addr *new; struct inet6_dev *in6_dev; - u8 mcast_addr[ETH_ALEN]; struct ifmcaddr6 *pmc6; int ret = 0; @@ -587,10 +587,10 @@ static int batadv_mcast_mla_bridge_get(struct net_device *dev, struct batadv_mcast_mla_flags *flags) { struct list_head bridge_mcast_list = LIST_HEAD_INIT(bridge_mcast_list); + u8 mcast_addr[ETH_ALEN] __aligned(2); u8 tvlv_flags = flags->tvlv_flags; struct br_ip_list *br_ip_entry; struct batadv_hw_addr *new; - u8 mcast_addr[ETH_ALEN]; struct br_ip_list *tmp; int ret; diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 68f79853032e..44a8cf89382c 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,26 @@ static bool batadv_compare_tt(const struct hlist_node *node, const void *data2) return (tt1->vid == tt2->vid) && batadv_compare_eth(data1, data2); } +/** + * batadv_compare_tt_entry() - check if a hash node is a specific TT entry + * @node: the list element pointer of the TT entry stored in the bucket + * @data2: pointer to the tt_common_entry which is looked for + * + * Unlike batadv_compare_tt(), this only matches the very object which is + * passed as @data2 and not just any entry for the same TT client. It is meant + * for batadv_hash_remove() callers which must not unlink an entry they did not + * look up themselves. + * + * Return: true if @node belongs to @data2, false otherwise + */ +static bool batadv_compare_tt_entry(const struct hlist_node *node, + const void *data2) +{ + const struct batadv_tt_common_entry *tt = data2; + + return node == &tt->hash_entry; +} + /** * batadv_choose_tt() - return the index of the tt entry in the hash table * @data: pointer to the tt_common_entry object to map @@ -312,6 +333,10 @@ static void batadv_tt_local_size_mod(struct batadv_priv *bat_priv, * given vid * @bat_priv: the bat priv with all the mesh interface information * @vid: the VLAN identifier + * + * It must only be called when removing the NEW flag of a + * batadv_tt_local_entry while it is still part of the bat_priv->tt.local_hash. + * It must therefore be checked under the specific list_locks[i]. */ static void batadv_tt_local_size_inc(struct batadv_priv *bat_priv, unsigned short vid) @@ -324,6 +349,11 @@ static void batadv_tt_local_size_inc(struct batadv_priv *bat_priv, * given vid * @bat_priv: the bat priv with all the mesh interface information * @vid: the VLAN identifier + * + * It must only be called after a batadv_tt_local_entry without NEW flag + * was removed from bat_priv->tt.local_hash (under the specific + * list_locks[i]) or when it is ensured that it is not accessible + * in this list. */ static void batadv_tt_local_size_dec(struct batadv_priv *bat_priv, unsigned short vid) @@ -413,6 +443,22 @@ batadv_tt_orig_list_entry_put(struct batadv_tt_orig_list_entry *orig_entry) kref_put(&orig_entry->refcount, batadv_tt_orig_list_entry_release); } +/** + * batadv_tt_flags_get() - get a snapshot of the flags of a TT entry + * @common: tt local & tt global common data + * + * Return: the flags of the TT entry as observed under its flags_lock. + */ +static u16 batadv_tt_flags_get(struct batadv_tt_common_entry *common) +{ + u16 flags; + + scoped_guard(spinlock_bh, &common->flags_lock) + flags = common->flags; + + return flags; +} + /** * batadv_tt_local_event() - store a local TT event (ADD/DEL) * @bat_priv: the bat priv with all the mesh interface information @@ -425,23 +471,25 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, { struct batadv_tt_common_entry *common = &tt_local_entry->common; struct batadv_tt_change_node *tt_change_node; - u8 flags = common->flags | event_flags; struct batadv_tt_change_node *entry; struct batadv_tt_change_node *safe; bool del_op_requested; bool del_op_entry; size_t changes; + u8 flags; tt_change_node = kmem_cache_alloc(batadv_tt_change_cache, GFP_ATOMIC); if (!tt_change_node) return; - tt_change_node->change.flags = flags; memset(tt_change_node->change.reserved, 0, sizeof(tt_change_node->change.reserved)); ether_addr_copy(tt_change_node->change.addr, common->addr); tt_change_node->change.vid = htons(common->vid); + flags = batadv_tt_flags_get(common) | event_flags; + + tt_change_node->change.flags = flags; del_op_requested = flags & BATADV_TT_CLIENT_DEL; /* check for ADD+DEL, DEL+ADD, ADD+ADD or DEL+DEL events */ @@ -575,7 +623,6 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv, struct batadv_tt_global_entry *tt_global, const char *message) { - struct batadv_tt_global_entry *tt_removed_entry; struct hlist_node *tt_removed_node; batadv_dbg(BATADV_DBG_TT, bat_priv, @@ -583,18 +630,67 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv, tt_global->common.addr, batadv_print_vid(tt_global->common.vid), message); + /* remove exactly this object when still present in hash */ tt_removed_node = batadv_hash_remove(bat_priv->tt.global_hash, - batadv_compare_tt, + batadv_compare_tt_entry, batadv_choose_tt, &tt_global->common); if (!tt_removed_node) return; /* drop reference of remove hash entry */ - tt_removed_entry = hlist_entry(tt_removed_node, - struct batadv_tt_global_entry, - common.hash_entry); - batadv_tt_global_entry_put(tt_removed_entry); + batadv_tt_global_entry_put(tt_global); +} + +/** + * batadv_tt_local_add_roam() - handle roamed clients during batadv_tt_local_add() + * @bat_priv: the bat priv with all the mesh interface information + * @tt_global: the global TT entry + * @roamed_back: whether @tt_global roamed back + */ +static void batadv_tt_local_add_roam(struct batadv_priv *bat_priv, + struct batadv_tt_global_entry *tt_global, + bool roamed_back) +{ + struct batadv_tt_orig_list_entry *orig_entry; + struct hlist_head *head; + + if (!tt_global) + return; + + /* Check whether it is a roaming, but don't do anything if the roaming + * process has already been handled + */ + scoped_guard(spinlock_bh, &tt_global->common.flags_lock) { + if (tt_global->common.flags & BATADV_TT_CLIENT_ROAM) + return; + + if (!roamed_back) { + /* The global entry has to be marked as ROAMING and has to be + * kept for consistency purpose. + * + * batadv_tt_global_to_purge() evaluates roam_at as soon as it + * observes BATADV_TT_CLIENT_ROAM, so the timeout has to be + * stamped before the flag is published. Otherwise the entry can + * be deleted right away as "Roaming timeout". + */ + tt_global->roam_at = jiffies; + tt_global->common.flags |= BATADV_TT_CLIENT_ROAM; + } + } + + /* These node are probably going to update their tt table */ + head = &tt_global->orig_list; + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_entry, head, list) { + batadv_send_roam_adv(bat_priv, tt_global->common.addr, + tt_global->common.vid, + orig_entry->orig_node); + } + rcu_read_unlock(); + + if (roamed_back) + batadv_tt_global_free(bat_priv, tt_global, "Roaming canceled"); } /** @@ -615,20 +711,19 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, { struct batadv_priv *bat_priv = netdev_priv(mesh_iface); struct batadv_tt_global_entry *tt_global = NULL; - struct batadv_tt_orig_list_entry *orig_entry; struct batadv_tt_local_entry *tt_local; struct net *net = dev_net(mesh_iface); struct net_device *in_dev = NULL; struct batadv_meshif_vlan *vlan; bool roamed_back = false; bool iif_is_wifi = false; - struct hlist_head *head; int packet_size_max; bool ret = false; u8 remote_flags; int hash_added; int table_size; u32 match_mark; + bool modified; if (ifindex != BATADV_NULL_IFINDEX) in_dev = dev_get_by_index(net, ifindex); @@ -646,6 +741,8 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, if (tt_local) { tt_local->last_seen = jiffies; + + spin_lock_bh(&tt_local->common.flags_lock); if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) { batadv_dbg(BATADV_DBG_TT, bat_priv, "Re-adding pending client %pM (vid: %d)\n", @@ -656,6 +753,8 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, * flag can be reset like it was never enqueued */ tt_local->common.flags &= ~BATADV_TT_CLIENT_PENDING; + spin_unlock_bh(&tt_local->common.flags_lock); + goto add_event; } @@ -671,6 +770,8 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, tt_local->common.flags &= ~BATADV_TT_CLIENT_ROAM; roamed_back = true; } + spin_unlock_bh(&tt_local->common.flags_lock); + goto check_roaming; } @@ -706,18 +807,21 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, (u8)atomic_read(&bat_priv->tt.vn)); ether_addr_copy(tt_local->common.addr, addr); + tt_local->common.vid = vid; + kref_init(&tt_local->common.refcount); + tt_local->last_seen = jiffies; + tt_local->common.added_at = tt_local->last_seen; + tt_local->vlan = vlan; + spin_lock_init(&tt_local->common.flags_lock); + + spin_lock_bh(&tt_local->common.flags_lock); /* The local entry has to be marked as NEW to avoid to send it in * a full table response going out before the next ttvn increment * (consistency check) */ tt_local->common.flags = BATADV_TT_CLIENT_NEW; - tt_local->common.vid = vid; if (iif_is_wifi) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; - kref_init(&tt_local->common.refcount); - tt_local->last_seen = jiffies; - tt_local->common.added_at = tt_local->last_seen; - tt_local->vlan = vlan; /* the batman interface mac and multicast addresses should never be * purged @@ -725,6 +829,7 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, if (batadv_compare_eth(addr, mesh_iface->dev_addr) || is_multicast_ether_addr(addr)) tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE; + spin_unlock_bh(&tt_local->common.flags_lock); kref_get(&tt_local->common.refcount); hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt, @@ -741,31 +846,9 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS); check_roaming: - /* Check whether it is a roaming, but don't do anything if the roaming - * process has already been handled - */ - if (tt_global && !(tt_global->common.flags & BATADV_TT_CLIENT_ROAM)) { - /* These node are probably going to update their tt table */ - head = &tt_global->orig_list; - rcu_read_lock(); - hlist_for_each_entry_rcu(orig_entry, head, list) { - batadv_send_roam_adv(bat_priv, tt_global->common.addr, - tt_global->common.vid, - orig_entry->orig_node); - } - rcu_read_unlock(); - if (roamed_back) { - batadv_tt_global_free(bat_priv, tt_global, - "Roaming canceled"); - } else { - /* The global entry has to be marked as ROAMING and - * has to be kept for consistency purpose - */ - tt_global->common.flags |= BATADV_TT_CLIENT_ROAM; - tt_global->roam_at = jiffies; - } - } + batadv_tt_local_add_roam(bat_priv, tt_global, roamed_back); + spin_lock_bh(&tt_local->common.flags_lock); /* store the current remote flags before altering them. This helps * understanding is flags are changing or not */ @@ -787,10 +870,13 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, else tt_local->common.flags &= ~BATADV_TT_CLIENT_ISOLA; + modified = remote_flags ^ (tt_local->common.flags & BATADV_TT_REMOTE_MASK); + spin_unlock_bh(&tt_local->common.flags_lock); + /* if any "dynamic" flag has been modified, resend an ADD event for this * entry so that all the nodes can get the new flags */ - if (remote_flags ^ (tt_local->common.flags & BATADV_TT_REMOTE_MASK)) + if (modified) batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS); ret = true; @@ -1115,6 +1201,7 @@ batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid, struct batadv_meshif_vlan *vlan; unsigned int last_seen_msecs; void *hdr; + u16 flags; u32 crc; local = container_of(common, struct batadv_tt_local_entry, common); @@ -1136,13 +1223,15 @@ batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid, genl_dump_check_consistent(cb, hdr); + flags = batadv_tt_flags_get(common); + if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) || nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) || nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) || - nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, common->flags)) + nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, flags)) goto nla_put_failure; - if (!(common->flags & BATADV_TT_CLIENT_NOPURGE) && + if (!(flags & BATADV_TT_CLIENT_NOPURGE) && nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, last_seen_msecs)) goto nla_put_failure; @@ -1249,29 +1338,23 @@ int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb) } /** - * batadv_tt_local_set_pending() - mark a local TT entry as pending removal + * batadv_tt_local_set_pending_event() - trigger events for TT pending removal * @bat_priv: the bat priv with all the mesh interface information * @tt_local_entry: local TT entry to mark * @flags: TT change flags to announce together with the pending removal * @message: debug message describing the reason for the change * - * Schedule the TT change announcement and set BATADV_TT_CLIENT_PENDING on the - * entry. The entry is kept in the local table until the next TTVN increment - * so that a consistency-check response can still be answered. + * Schedule the TT change announcement for the entry. The entry is kept in the + * local table until the next TTVN increment so that a consistency-check + * response can still be answered. */ static void -batadv_tt_local_set_pending(struct batadv_priv *bat_priv, - struct batadv_tt_local_entry *tt_local_entry, - u16 flags, const char *message) +batadv_tt_local_set_pending_event(struct batadv_priv *bat_priv, + struct batadv_tt_local_entry *tt_local_entry, + u16 flags, const char *message) { batadv_tt_local_event(bat_priv, tt_local_entry, flags); - /* The local client has to be marked as "pending to be removed" but has - * to be kept in the table in order to send it in a full table - * response issued before the net ttvn increment (consistency check) - */ - tt_local_entry->common.flags |= BATADV_TT_CLIENT_PENDING; - batadv_dbg(BATADV_DBG_TT, bat_priv, "Local tt entry (%pM, vid: %d) pending to be removed: %s\n", tt_local_entry->common.addr, @@ -1292,16 +1375,17 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr, unsigned short vid, const char *message, bool roaming) { - struct batadv_tt_local_entry *tt_removed_entry; struct batadv_tt_local_entry *tt_local_entry; struct hlist_node *tt_removed_node; u16 curr_flags = BATADV_NO_FLAGS; + bool pending = false; u16 flags; tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid); if (!tt_local_entry) goto out; + spin_lock_bh(&tt_local_entry->common.flags_lock); curr_flags = tt_local_entry->common.flags; flags = BATADV_TT_CLIENT_DEL; @@ -1316,27 +1400,39 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr, } if (!(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW)) { - batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags, - message); + tt_local_entry->common.flags |= BATADV_TT_CLIENT_PENDING; + pending = true; + } + spin_unlock_bh(&tt_local_entry->common.flags_lock); + + if (pending) { + batadv_tt_local_set_pending_event(bat_priv, tt_local_entry, flags, + message); goto out; } + /* if this client has been added right now, it is possible to * immediately purge it */ batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL); + /* remove exactly this object when still present in hash */ tt_removed_node = batadv_hash_remove(bat_priv->tt.local_hash, - batadv_compare_tt, + batadv_compare_tt_entry, batadv_choose_tt, &tt_local_entry->common); if (!tt_removed_node) goto out; + /* batadv_tt_local_transition_new() may have committed the entry and + * thus counted it in the local table size since the BATADV_TT_CLIENT_NEW + * check above. + */ + if (!(batadv_tt_flags_get(&tt_local_entry->common) & BATADV_TT_CLIENT_NEW)) + batadv_tt_local_size_dec(bat_priv, tt_local_entry->common.vid); + /* drop reference of remove hash entry */ - tt_removed_entry = hlist_entry(tt_removed_node, - struct batadv_tt_local_entry, - common.hash_entry); - batadv_tt_local_entry_put(tt_removed_entry); + batadv_tt_local_entry_put(tt_local_entry); out: batadv_tt_local_entry_put(tt_local_entry); @@ -1361,21 +1457,37 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv, hlist_for_each_entry_safe(tt_common_entry, node_tmp, head, hash_entry) { + bool cont = false; + tt_local_entry = container_of(tt_common_entry, struct batadv_tt_local_entry, common); - if (tt_local_entry->common.flags & BATADV_TT_CLIENT_NOPURGE) + + scoped_guard(spinlock_bh, &tt_local_entry->common.flags_lock) { + if (tt_local_entry->common.flags & BATADV_TT_CLIENT_NOPURGE) { + cont = true; + break; + } + + /* entry already marked for deletion */ + if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) { + cont = true; + break; + } + + if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout)) { + cont = true; + break; + } + + tt_local_entry->common.flags |= BATADV_TT_CLIENT_PENDING; + } + + if (cont) continue; - /* entry already marked for deletion */ - if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) - continue; - - if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout)) - continue; - - batadv_tt_local_set_pending(bat_priv, tt_local_entry, - BATADV_TT_CLIENT_DEL, "timed out"); + batadv_tt_local_set_pending_event(bat_priv, tt_local_entry, + BATADV_TT_CLIENT_DEL, "timed out"); } } @@ -1581,8 +1693,10 @@ batadv_tt_global_sync_flags(struct batadv_tt_global_entry *tt_global) flags |= orig_entry->flags; rcu_read_unlock(); - flags |= tt_global->common.flags & (~BATADV_TT_SYNC_MASK); - tt_global->common.flags = flags; + scoped_guard(spinlock_bh, &tt_global->common.flags_lock) { + flags |= tt_global->common.flags & (~BATADV_TT_SYNC_MASK); + tt_global->common.flags = flags; + } } /** @@ -1664,8 +1778,10 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv, struct batadv_tt_local_entry *tt_local_entry; struct batadv_tt_common_entry *common; bool ret = false; + u16 global_flags; u16 local_flags; int hash_added; + bool delete; /* ignore global entries from backbone nodes */ if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig, vid)) @@ -1678,9 +1794,11 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv, * for a roaming advertisement instead of manually messing up the global * table */ - if ((flags & BATADV_TT_CLIENT_TEMP) && tt_local_entry && - !(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW)) - goto out; + if ((flags & BATADV_TT_CLIENT_TEMP) && tt_local_entry) { + local_flags = batadv_tt_flags_get(&tt_local_entry->common); + if (!(local_flags & BATADV_TT_CLIENT_NEW)) + goto out; + } if (!tt_global_entry) { tt_global_entry = kmem_cache_zalloc(batadv_tg_cache, @@ -1691,9 +1809,13 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv, common = &tt_global_entry->common; ether_addr_copy(common->addr, tt_addr); common->vid = vid; + spin_lock_init(&common->flags_lock); - if (!is_multicast_ether_addr(common->addr)) + if (!is_multicast_ether_addr(common->addr)) { + spin_lock_bh(&common->flags_lock); common->flags = flags & (~BATADV_TT_SYNC_MASK); + spin_unlock_bh(&common->flags_lock); + } tt_global_entry->roam_at = 0; /* node must store current time in case of roaming. This is @@ -1733,8 +1855,10 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv, * originator list and add the new one orig_entry */ if (flags & BATADV_TT_CLIENT_TEMP) { - if (!(common->flags & BATADV_TT_CLIENT_TEMP)) + global_flags = batadv_tt_flags_get(common); + if (!(global_flags & BATADV_TT_CLIENT_TEMP)) goto out; + if (batadv_tt_global_entry_has_orig(tt_global_entry, orig_node, NULL)) goto out_remove; @@ -1742,6 +1866,9 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv, goto add_orig_entry; } + delete = false; + + spin_lock_bh(&common->flags_lock); /* if the client was temporary added before receiving the first * OGM announcing it, we have to clear the TEMP flag. Also, * remove the previous temporary orig node and re-add it @@ -1749,7 +1876,7 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv, * is a non-temporary entry is preferred. */ if (common->flags & BATADV_TT_CLIENT_TEMP) { - batadv_tt_global_del_orig_list(tt_global_entry); + delete = true; common->flags &= ~BATADV_TT_CLIENT_TEMP; } @@ -1768,10 +1895,14 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv, * new one. */ if (common->flags & BATADV_TT_CLIENT_ROAM) { - batadv_tt_global_del_orig_list(tt_global_entry); - common->flags &= ~BATADV_TT_CLIENT_ROAM; + delete = true; tt_global_entry->roam_at = 0; + common->flags &= ~BATADV_TT_CLIENT_ROAM; } + spin_unlock_bh(&common->flags_lock); + + if (delete) + batadv_tt_global_del_orig_list(tt_global_entry); } add_orig_entry: /* add the new orig_entry (if needed) or update it */ @@ -1795,6 +1926,8 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv, local_flags = batadv_tt_local_remove(bat_priv, tt_addr, vid, "global tt received", flags & BATADV_TT_CLIENT_ROAM); + + spin_lock_bh(&tt_global_entry->common.flags_lock); tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI; if (!(flags & BATADV_TT_CLIENT_ROAM)) @@ -1802,6 +1935,7 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv, * roaming state anymore. */ tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM; + spin_unlock_bh(&tt_global_entry->common.flags_lock); out: batadv_tt_global_entry_put(tt_global_entry); @@ -1871,9 +2005,9 @@ batadv_tt_global_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq, struct batadv_tt_orig_list_entry *orig, bool best) { - u16 flags = (common->flags & (~BATADV_TT_SYNC_MASK)) | orig->flags; struct batadv_orig_node_vlan *vlan; u8 last_ttvn; + u16 flags; void *hdr; u32 crc; @@ -1892,6 +2026,7 @@ batadv_tt_global_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq, if (!hdr) return -ENOBUFS; + flags = (batadv_tt_flags_get(common) & (~BATADV_TT_SYNC_MASK)) | orig->flags; last_ttvn = READ_ONCE(orig->orig_node->last_ttvn); if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) || @@ -2179,9 +2314,12 @@ batadv_tt_global_del_roaming(struct batadv_priv *bat_priv, rcu_read_unlock(); if (last_entry) { - /* its the last one, mark for roaming. */ - tt_global_entry->common.flags |= BATADV_TT_CLIENT_ROAM; - tt_global_entry->roam_at = jiffies; + scoped_guard(spinlock_bh, &tt_global_entry->common.flags_lock) { + /* its the last one, mark for roaming. */ + tt_global_entry->roam_at = jiffies; + + tt_global_entry->common.flags |= BATADV_TT_CLIENT_ROAM; + } } else { /* there is another entry, we can simply delete this * one and can still use the other one. @@ -2330,16 +2468,18 @@ static bool batadv_tt_global_to_purge(struct batadv_tt_global_entry *tt_global, unsigned long temp_timeout = BATADV_TT_CLIENT_TEMP_TIMEOUT; bool purge = false; - if ((tt_global->common.flags & BATADV_TT_CLIENT_ROAM) && - batadv_has_timed_out(tt_global->roam_at, roam_timeout)) { - purge = true; - *msg = "Roaming timeout\n"; - } + scoped_guard(spinlock_bh, &tt_global->common.flags_lock) { + if ((tt_global->common.flags & BATADV_TT_CLIENT_ROAM) && + batadv_has_timed_out(tt_global->roam_at, roam_timeout)) { + purge = true; + *msg = "Roaming timeout\n"; + } - if ((tt_global->common.flags & BATADV_TT_CLIENT_TEMP) && - batadv_has_timed_out(tt_global->common.added_at, temp_timeout)) { - purge = true; - *msg = "Temporary client timeout\n"; + if ((tt_global->common.flags & BATADV_TT_CLIENT_TEMP) && + batadv_has_timed_out(tt_global->common.added_at, temp_timeout)) { + purge = true; + *msg = "Temporary client timeout\n"; + } } return purge; @@ -2437,8 +2577,8 @@ static void batadv_tt_global_table_free(struct batadv_priv *bat_priv) /** * _batadv_is_ap_isolated() - check whether two clients are AP-isolated from * each other - * @tt_local_entry: local TT entry of the sending client - * @tt_global_entry: global TT entry of the destination client + * @tt_local_entry: local TT entry of one local client + * @tt_global_entry: global TT entry of the remote client * * Return: true if traffic between the two clients should be dropped because * either both are WiFi clients or both carry the ISOLATION flag; false @@ -2448,13 +2588,19 @@ static bool _batadv_is_ap_isolated(struct batadv_tt_local_entry *tt_local_entry, struct batadv_tt_global_entry *tt_global_entry) { - if (tt_local_entry->common.flags & BATADV_TT_CLIENT_WIFI && - tt_global_entry->common.flags & BATADV_TT_CLIENT_WIFI) + u16 global_flags; + u16 local_flags; + + global_flags = batadv_tt_flags_get(&tt_global_entry->common); + local_flags = batadv_tt_flags_get(&tt_local_entry->common); + + if (local_flags & BATADV_TT_CLIENT_WIFI && + global_flags & BATADV_TT_CLIENT_WIFI) return true; /* check if the two clients are marked as isolated */ - if (tt_local_entry->common.flags & BATADV_TT_CLIENT_ISOLA && - tt_global_entry->common.flags & BATADV_TT_CLIENT_ISOLA) + if (local_flags & BATADV_TT_CLIENT_ISOLA && + global_flags & BATADV_TT_CLIENT_ISOLA) return true; return false; @@ -2483,11 +2629,15 @@ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv, struct batadv_tt_local_entry *tt_local_entry = NULL; struct batadv_tt_orig_list_entry *best_entry; struct batadv_orig_node *orig_node = NULL; + u16 flags; if (src && batadv_vlan_ap_isola_get(bat_priv, vid)) { tt_local_entry = batadv_tt_local_hash_find(bat_priv, src, vid); - if (!tt_local_entry || - (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)) + if (!tt_local_entry) + goto out; + + flags = batadv_tt_flags_get(&tt_local_entry->common); + if (flags & BATADV_TT_CLIENT_PENDING) goto out; } @@ -2562,6 +2712,8 @@ static u32 batadv_tt_global_crc(struct batadv_priv *bat_priv, rcu_read_lock(); hlist_for_each_entry_rcu(tt_common, head, hash_entry) { + u16 tt_flags; + tt_global = container_of(tt_common, struct batadv_tt_global_entry, common); @@ -2571,18 +2723,21 @@ static u32 batadv_tt_global_crc(struct batadv_priv *bat_priv, if (tt_common->vid != vid) continue; + tt_flags = batadv_tt_flags_get(tt_common); + /* Roaming clients are in the global table for * consistency only. They don't have to be * taken into account while computing the * global crc */ - if (tt_common->flags & BATADV_TT_CLIENT_ROAM) + if (tt_flags & BATADV_TT_CLIENT_ROAM) continue; + /* Temporary clients have not been announced yet, so * they have to be skipped while computing the global * crc */ - if (tt_common->flags & BATADV_TT_CLIENT_TEMP) + if (tt_flags & BATADV_TT_CLIENT_TEMP) continue; /* find out if this global entry is announced by this @@ -2642,18 +2797,24 @@ static u32 batadv_tt_local_crc(struct batadv_priv *bat_priv, rcu_read_lock(); hlist_for_each_entry_rcu(tt_common, head, hash_entry) { + u16 tt_flags; + /* compute the CRC only for entries belonging to the * VLAN identified by vid */ if (tt_common->vid != vid) continue; + tt_flags = batadv_tt_flags_get(tt_common); + /* not yet committed clients have not to be taken into * account while computing the CRC */ - if (tt_common->flags & BATADV_TT_CLIENT_NEW) + if (tt_flags & BATADV_TT_CLIENT_NEW) continue; + flags = tt_flags & BATADV_TT_SYNC_MASK; + /* use network order to read the VID: this ensures that * every node reads the bytes in the same order. */ @@ -2663,7 +2824,6 @@ static u32 batadv_tt_local_crc(struct batadv_priv *bat_priv, /* compute the CRC on flags that have to be kept in sync * among nodes */ - flags = tt_common->flags & BATADV_TT_SYNC_MASK; crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags)); crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN); @@ -2821,17 +2981,19 @@ batadv_tt_req_node_new(struct batadv_priv *bat_priv, * * Return: true if the entry is a valid, false otherwise. */ -static bool batadv_tt_local_valid(const void *entry_ptr, +static bool batadv_tt_local_valid(void *entry_ptr, const void *data_ptr, u8 *flags) { - const struct batadv_tt_common_entry *tt_common_entry = entry_ptr; + struct batadv_tt_common_entry *tt_common_entry = entry_ptr; + u16 tt_flags; - if (tt_common_entry->flags & BATADV_TT_CLIENT_NEW) + tt_flags = batadv_tt_flags_get(tt_common_entry); + if (tt_flags & BATADV_TT_CLIENT_NEW) return false; if (flags) - *flags = tt_common_entry->flags; + *flags = tt_flags; return true; } @@ -2848,16 +3010,18 @@ static bool batadv_tt_local_valid(const void *entry_ptr, * * Return: true if the entry is a valid, false otherwise. */ -static bool batadv_tt_global_valid(const void *entry_ptr, +static bool batadv_tt_global_valid(void *entry_ptr, const void *data_ptr, u8 *flags) { - const struct batadv_tt_common_entry *tt_common_entry = entry_ptr; - const struct batadv_tt_global_entry *tt_global_entry; + struct batadv_tt_common_entry *tt_common_entry = entry_ptr; const struct batadv_orig_node *orig_node = data_ptr; + struct batadv_tt_global_entry *tt_global_entry; + u16 tt_flags; - if (tt_common_entry->flags & BATADV_TT_CLIENT_ROAM || - tt_common_entry->flags & BATADV_TT_CLIENT_TEMP) + tt_flags = batadv_tt_flags_get(tt_common_entry); + if (tt_flags & BATADV_TT_CLIENT_ROAM || + tt_flags & BATADV_TT_CLIENT_TEMP) return false; tt_global_entry = container_of(tt_common_entry, @@ -2886,7 +3050,7 @@ static bool batadv_tt_global_valid(const void *entry_ptr, static u16 batadv_tt_tvlv_generate(struct batadv_priv *bat_priv, struct batadv_hashtable *hash, void *tvlv_buff, u16 tt_len, - bool (*valid_cb)(const void *, + bool (*valid_cb)(void *, const void *, u8 *flags), void *cb_data) @@ -3511,19 +3675,21 @@ bool batadv_is_my_client(struct batadv_priv *bat_priv, const u8 *addr, unsigned short vid) { struct batadv_tt_local_entry *tt_local_entry; - bool ret = false; + u16 tt_flags; + bool ret; tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid); if (!tt_local_entry) - goto out; + return false; + + tt_flags = batadv_tt_flags_get(&tt_local_entry->common); + /* Check if the client has been logically deleted (but is kept for * consistency purpose) */ - if ((tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) || - (tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM)) - goto out; - ret = true; -out: + ret = !((tt_flags & BATADV_TT_CLIENT_PENDING) || + (tt_flags & BATADV_TT_CLIENT_ROAM)); + batadv_tt_local_entry_put(tt_local_entry); return ret; } @@ -3782,16 +3948,13 @@ void batadv_tt_free(struct batadv_priv *bat_priv) } /** - * batadv_tt_local_set_flags() - set or unset the specified flags on the local - * table and possibly count them in the TT size + * batadv_tt_local_transition_new() - unset the NEW flag on the local + * table and count them in the TT size * @bat_priv: the bat priv with all the mesh interface information - * @flags: the flag to switch - * @enable: whether to set or unset the flag - * @count: whether to increase the TT size by the number of changed entries */ -static void batadv_tt_local_set_flags(struct batadv_priv *bat_priv, u16 flags, - bool enable, bool count) +static void batadv_tt_local_transition_new(struct batadv_priv *bat_priv) { + spinlock_t *list_lock; /* protects write access to the hash lists */ struct batadv_hashtable *hash = bat_priv->tt.local_hash; struct batadv_tt_common_entry *tt_common_entry; struct hlist_head *head; @@ -3802,27 +3965,28 @@ static void batadv_tt_local_set_flags(struct batadv_priv *bat_priv, u16 flags, for (i = 0; i < hash->size; i++) { head = &hash->table[i]; + list_lock = &hash->list_locks[i]; - rcu_read_lock(); - hlist_for_each_entry_rcu(tt_common_entry, - head, hash_entry) { - if (enable) { - if ((tt_common_entry->flags & flags) == flags) - continue; - tt_common_entry->flags |= flags; - } else { - if (!(tt_common_entry->flags & flags)) - continue; - tt_common_entry->flags &= ~flags; + spin_lock_bh(list_lock); + hlist_for_each_entry(tt_common_entry, head, hash_entry) { + bool cont = false; + + scoped_guard(spinlock_bh, &tt_common_entry->flags_lock) { + if (!(tt_common_entry->flags & BATADV_TT_CLIENT_NEW)) { + cont = true; + break; + } + + tt_common_entry->flags &= ~BATADV_TT_CLIENT_NEW; } - if (!count) + if (cont) continue; batadv_tt_local_size_inc(bat_priv, tt_common_entry->vid); } - rcu_read_unlock(); + spin_unlock_bh(list_lock); } } @@ -3854,16 +4018,31 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) spin_lock_bh(list_lock); hlist_for_each_entry_safe(tt_common, node_tmp, head, hash_entry) { - if (!(tt_common->flags & BATADV_TT_CLIENT_PENDING)) + bool cont = false; + + scoped_guard(spinlock_bh, &tt_common->flags_lock) { + if (!(tt_common->flags & BATADV_TT_CLIENT_PENDING)) { + cont = true; + break; + } + + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Deleting local tt entry (%pM, vid: %d): pending\n", + tt_common->addr, + batadv_print_vid(tt_common->vid)); + + hlist_del_rcu(&tt_common->hash_entry); + + /* An entry which still carries BATADV_TT_CLIENT_NEW was + * never counted and must not be uncounted here. + */ + if (!(tt_common->flags & BATADV_TT_CLIENT_NEW)) + batadv_tt_local_size_dec(bat_priv, tt_common->vid); + } + + if (cont) continue; - batadv_dbg(BATADV_DBG_TT, bat_priv, - "Deleting local tt entry (%pM, vid: %d): pending\n", - tt_common->addr, - batadv_print_vid(tt_common->vid)); - - batadv_tt_local_size_dec(bat_priv, tt_common->vid); - hlist_del_rcu(&tt_common->hash_entry); tt_local = container_of(tt_common, struct batadv_tt_local_entry, common); @@ -3891,7 +4070,7 @@ static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv) return; } - batadv_tt_local_set_flags(bat_priv, BATADV_TT_CLIENT_NEW, false, true); + batadv_tt_local_transition_new(bat_priv); batadv_tt_local_purge_pending_clients(bat_priv); batadv_tt_local_update_crc(bat_priv); @@ -4061,15 +4240,16 @@ bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv, u8 *addr, unsigned short vid) { struct batadv_tt_global_entry *tt_global_entry; - bool ret = false; + bool ret; tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid); if (!tt_global_entry) - goto out; + return false; + + ret = batadv_tt_flags_get(&tt_global_entry->common) & BATADV_TT_CLIENT_ROAM; - ret = tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM; batadv_tt_global_entry_put(tt_global_entry); -out: + return ret; } @@ -4087,15 +4267,16 @@ bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv, u8 *addr, unsigned short vid) { struct batadv_tt_local_entry *tt_local_entry; - bool ret = false; + bool ret; tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid); if (!tt_local_entry) - goto out; + return false; + + ret = batadv_tt_flags_get(&tt_local_entry->common) & BATADV_TT_CLIENT_ROAM; - ret = tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM; batadv_tt_local_entry_put(tt_local_entry); -out: + return ret; } @@ -4404,7 +4585,7 @@ bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv, if (!tt) return false; - ret = tt->common.flags & BATADV_TT_CLIENT_ISOLA; + ret = batadv_tt_flags_get(&tt->common) & BATADV_TT_CLIENT_ISOLA; batadv_tt_global_entry_put(tt); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 9bdc5a3e799e..67872927cfb5 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -410,7 +410,7 @@ struct batadv_orig_bat_iv { */ struct batadv_orig_node { /** @orig: originator ethernet address */ - u8 orig[ETH_ALEN]; + u8 orig[ETH_ALEN] __aligned(2); /** @ifinfo_list: list for routers per outgoing interface */ struct hlist_head ifinfo_list; @@ -634,12 +634,12 @@ struct batadv_hardif_neigh_node { struct hlist_node list; /** @addr: the MAC address of the neighboring interface */ - u8 addr[ETH_ALEN]; + u8 addr[ETH_ALEN] __aligned(2); /** * @orig: the address of the originator this neighbor node belongs to */ - u8 orig[ETH_ALEN]; + u8 orig[ETH_ALEN] __aligned(2); /** @if_incoming: pointer to incoming hard-interface */ struct batadv_hard_iface *if_incoming; @@ -677,7 +677,7 @@ struct batadv_neigh_node { #endif /** @addr: the MAC address of the neighboring interface */ - u8 addr[ETH_ALEN]; + u8 addr[ETH_ALEN] __aligned(2); /** @ifinfo_list: list for routing metrics per outgoing interface */ struct hlist_head ifinfo_list; @@ -777,7 +777,7 @@ struct batadv_neigh_ifinfo { */ struct batadv_bcast_duplist_entry { /** @orig: mac address of orig node originating the broadcast */ - u8 orig[ETH_ALEN]; + u8 orig[ETH_ALEN] __aligned(2); /** @crc: crc32 checksum of broadcast payload */ u32 crc; @@ -1085,7 +1085,7 @@ struct batadv_priv_bla { struct batadv_hashtable *backbone_hash; /** @loopdetect_addr: MAC address used for own loopdetection frames */ - u8 loopdetect_addr[ETH_ALEN]; + u8 loopdetect_addr[ETH_ALEN] __aligned(2); /** * @loopdetect_lasttime: time when the loopdetection frames were sent @@ -1352,7 +1352,7 @@ struct batadv_tp_vars_common { struct batadv_priv *bat_priv; /** @other_end: mac address of remote */ - u8 other_end[ETH_ALEN]; + u8 other_end[ETH_ALEN] __aligned(2); /** @session: TP session identifier */ u8 session[2]; @@ -1765,7 +1765,7 @@ struct batadv_bla_backbone_gw { * @orig: originator address of backbone node (mac address of primary * iface) */ - u8 orig[ETH_ALEN]; + u8 orig[ETH_ALEN] __aligned(2); /** @vid: vlan id this gateway was detected on */ unsigned short vid; @@ -1810,7 +1810,7 @@ struct batadv_bla_backbone_gw { */ struct batadv_bla_claim { /** @addr: mac address of claimed non-mesh client */ - u8 addr[ETH_ALEN]; + u8 addr[ETH_ALEN] __aligned(2); /** @vid: vlan id this client was detected on */ unsigned short vid; @@ -1840,7 +1840,7 @@ struct batadv_bla_claim { */ struct batadv_tt_common_entry { /** @addr: mac address of non-mesh client */ - u8 addr[ETH_ALEN]; + u8 addr[ETH_ALEN] __aligned(2); /** @vid: VLAN identifier */ unsigned short vid; @@ -1851,12 +1851,21 @@ struct batadv_tt_common_entry { */ struct hlist_node hash_entry; - /** @flags: various state handling flags (see batadv_tt_client_flags) */ + /** + * @flags: various state handling flags (see batadv_tt_client_flags), + * protected by @flags_lock + */ u16 flags; /** @added_at: timestamp used for purging stale tt common entries */ unsigned long added_at; + /** + * @flags_lock: protect modifications of @flags and + * @batadv_tt_global_entry.roam_at + */ + spinlock_t flags_lock; + /** @refcount: number of contexts the object is used */ struct kref refcount; @@ -1942,7 +1951,7 @@ struct batadv_tt_req_node { /** * @addr: mac address of the originator this request was sent to */ - u8 addr[ETH_ALEN]; + u8 addr[ETH_ALEN] __aligned(2); /** @issued_at: timestamp used for purging stale tt requests */ unsigned long issued_at; @@ -1959,7 +1968,7 @@ struct batadv_tt_req_node { */ struct batadv_tt_roam_node { /** @addr: mac address of the client in the roaming phase */ - u8 addr[ETH_ALEN]; + u8 addr[ETH_ALEN] __aligned(2); /** @vid: VLAN identifier */ u16 vid; @@ -2087,14 +2096,14 @@ struct batadv_algo_neigh_ops { struct batadv_hard_iface *if_outgoing2); /** - * @is_similar_or_better: check if neigh1 is equally similar or better - * than neigh2 for their respective outgoing interface from the metric + * @is_similar_or_better: check if @candidate is equally similar or better + * than @reference for their respective outgoing interface from the metric * prospective */ - bool (*is_similar_or_better)(struct batadv_neigh_node *neigh1, - struct batadv_hard_iface *if_outgoing1, - struct batadv_neigh_node *neigh2, - struct batadv_hard_iface *if_outgoing2); + bool (*is_similar_or_better)(struct batadv_neigh_node *candidate, + struct batadv_hard_iface *if_outgoing_cand, + struct batadv_neigh_node *reference, + struct batadv_hard_iface *if_outgoing_ref); /** @dump: dump neighbors to a netlink socket (optional) */ void (*dump)(struct sk_buff *msg, struct netlink_callback *cb, @@ -2204,7 +2213,7 @@ struct batadv_hw_addr { struct hlist_node list; /** @addr: the MAC address of this list entry */ - unsigned char addr[ETH_ALEN]; + unsigned char addr[ETH_ALEN] __aligned(2); }; /**