#include <linux/notifier.h>
#include <linux/printk.h>
#include <linux/rculist.h>
+#include <linux/rhashtable-types.h>
+#include <linux/rhashtable.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "send.h"
#include "translation-table.h"
+static const struct rhashtable_params batadv_wifi_net_devices_params = {
+ .key_len = sizeof(struct net_device *),
+ .key_offset = offsetof(struct batadv_wifi_net_device_state, netdev),
+ .head_offset = offsetof(struct batadv_wifi_net_device_state, l),
+ .automatic_shrinking = true,
+};
+
+static struct rhashtable batadv_wifi_net_devices;
+
/**
* batadv_hardif_release() - release hard interface from lists and queue for
* free after rcu grace period
}
/**
- * batadv_is_cfg80211_hardif() - check if the given hardif is a cfg80211 wifi
- * interface
+ * batadv_hardif_get_wifi_flags() - retrieve wifi flags for hard_iface
* @hard_iface: the device to check
*
- * Return: true if the net device is a cfg80211 wireless device, false
- * otherwise.
+ * Return: batadv_hard_iface_wifi_flags flags of the device
*/
-bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface)
+u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface)
{
- u32 allowed_flags = 0;
+ struct batadv_wifi_net_device_state *device_state;
+ u32 wifi_flags = 0;
+
+ if (!hard_iface)
+ return 0;
- allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
- allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
+ rcu_read_lock();
+ device_state = rhashtable_lookup_fast(&batadv_wifi_net_devices,
+ &hard_iface->net_dev,
+ batadv_wifi_net_devices_params);
+ if (device_state)
+ wifi_flags = READ_ONCE(device_state->wifi_flags);
+ rcu_read_unlock();
- return !!(hard_iface->wifi_flags & allowed_flags);
+ return wifi_flags;
}
/**
*/
bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface)
{
- if (!hard_iface)
- return false;
+ u32 wifi_flags = batadv_hardif_get_wifi_flags(hard_iface);
- return hard_iface->wifi_flags != 0;
+ return batadv_is_wifi(wifi_flags);
}
/**
kref_init(&hard_iface->refcount);
hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
- hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev);
if (batadv_is_wifi_hardif(hard_iface))
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
return NOTIFY_DONE;
}
+/**
+ * batadv_wifi_net_device_insert() - save information about wifi net_device
+ * @net_dev: net_device to add to batadv_wifi_net_devices
+ * @wifi_flags: net_device which generated an event
+ *
+ * Return: 0 on result, negative value on error
+ */
+static int
+batadv_wifi_net_device_insert(struct net_device *net_dev, u32 wifi_flags)
+{
+ struct batadv_wifi_net_device_state *device_state;
+ int ret;
+
+ ASSERT_RTNL();
+
+ device_state = kzalloc_obj(*device_state, GFP_ATOMIC);
+ if (!device_state)
+ return -ENOMEM;
+
+ device_state->wifi_flags = wifi_flags;
+ netdev_hold(net_dev, &device_state->dev_tracker, GFP_ATOMIC);
+ device_state->netdev = net_dev;
+ WRITE_ONCE(device_state->wifi_flags, wifi_flags);
+
+ ret = rhashtable_insert_fast(&batadv_wifi_net_devices, &device_state->l,
+ batadv_wifi_net_devices_params);
+ if (ret < 0)
+ goto err_free;
+
+ return 0;
+
+err_free:
+ netdev_put(device_state->netdev, &device_state->dev_tracker);
+ kfree(device_state);
+ return ret;
+}
+
+/**
+ * batadv_wifi_net_device_remove() - remove information about wifi net_device
+ * @device_state: wifi net_device state to remove from batadv_wifi_net_device_state
+ */
+static void
+batadv_wifi_net_device_remove(struct batadv_wifi_net_device_state *device_state)
+{
+ ASSERT_RTNL();
+
+ rhashtable_remove_fast(&batadv_wifi_net_devices, &device_state->l,
+ batadv_wifi_net_devices_params);
+ netdev_put(device_state->netdev, &device_state->dev_tracker);
+ kfree_rcu(device_state, rcu);
+}
+
+/**
+ * batadv_wifi_net_device_update() - update wifi state of net_device
+ * @net_dev: net_device to update in batadv_wifi_net_devices
+ *
+ * The device will only be stored in batadv_wifi_net_devices when
+ * it could be identified as wifi device. If the net_device is no
+ * longer a wifi device, it is automatically removed from
+ * batadv_wifi_net_devices.
+ */
+static void
+batadv_wifi_net_device_update(struct net_device *net_dev)
+{
+ struct batadv_wifi_net_device_state *device_state;
+ u32 wifi_flags;
+
+ ASSERT_RTNL();
+
+ wifi_flags = batadv_wifi_flags_evaluate(net_dev);
+ device_state = rhashtable_lookup_fast(&batadv_wifi_net_devices,
+ &net_dev,
+ batadv_wifi_net_devices_params);
+
+ if (device_state) {
+ if (batadv_is_wifi(wifi_flags))
+ WRITE_ONCE(device_state->wifi_flags, wifi_flags);
+ else
+ batadv_wifi_net_device_remove(device_state);
+ } else if (batadv_is_wifi(wifi_flags)) {
+ batadv_wifi_net_device_insert(net_dev, wifi_flags);
+ }
+}
+
+/**
+ * batadv_wifi_net_device_unregister() - remove wifi state of net_device
+ * @net_dev: net_device to remove from batadv_wifi_net_devices
+ */
+static void
+batadv_wifi_net_device_unregister(struct net_device *net_dev)
+{
+ struct batadv_wifi_net_device_state *device_state;
+
+ ASSERT_RTNL();
+
+ device_state = rhashtable_lookup_fast(&batadv_wifi_net_devices,
+ &net_dev,
+ batadv_wifi_net_devices_params);
+ if (!device_state)
+ return;
+
+ batadv_wifi_net_device_remove(device_state);
+}
+
+/**
+ * batadv_wifi_net_device_event() - handle network events for batadv_wifi_net_devices
+ * @event: enum netdev_cmd event to handle
+ * @net_dev: net_device to update in batadv_wifi_net_devices
+ */
+static void batadv_wifi_net_device_event(unsigned long event,
+ struct net_device *net_dev)
+{
+ switch (event) {
+ case NETDEV_REGISTER:
+ case NETDEV_POST_TYPE_CHANGE:
+ case NETDEV_CHANGEUPPER:
+ batadv_wifi_net_device_update(net_dev);
+ break;
+ case NETDEV_UNREGISTER:
+ case NETDEV_PRE_TYPE_CHANGE:
+ batadv_wifi_net_device_unregister(net_dev);
+ break;
+ }
+}
+
static int batadv_hard_if_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
if (batadv_meshif_is_valid(net_dev))
return batadv_hard_if_event_meshif(event, net_dev);
+ batadv_wifi_net_device_event(event, net_dev);
+
hard_iface = batadv_hardif_get_by_netdev(net_dev);
if (!hard_iface && (event == NETDEV_REGISTER ||
event == NETDEV_POST_TYPE_CHANGE))
if (hard_iface == primary_if)
batadv_primary_if_update_addr(bat_priv, NULL);
break;
+ case NETDEV_REGISTER:
+ case NETDEV_POST_TYPE_CHANGE:
case NETDEV_CHANGEUPPER:
- hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev);
if (batadv_is_wifi_hardif(hard_iface))
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
break;
struct notifier_block batadv_hard_if_notifier = {
.notifier_call = batadv_hard_if_event,
};
+
+/**
+ * batadv_wifi_net_devices_init() - Initialize wifi devices cache
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int __init batadv_wifi_net_devices_init(void)
+{
+ return rhashtable_init(&batadv_wifi_net_devices,
+ &batadv_wifi_net_devices_params);
+}
+
+/**
+ * batadv_wifi_net_devices_deinit() - Deinitialize wifi devices cache
+ */
+void batadv_wifi_net_devices_deinit(void)
+{
+ rhashtable_destroy(&batadv_wifi_net_devices);
+}
#include "main.h"
#include <linux/compiler.h>
+#include <linux/init.h>
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/rcupdate.h>
struct net_device *__batadv_get_real_netdev(struct net_device *net_device);
struct net_device *batadv_get_real_netdev(struct net_device *net_device);
-bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface);
+u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface);
bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);
struct batadv_hard_iface*
batadv_hardif_get_by_netdev(const struct net_device *net_dev);
void batadv_hardif_release(struct kref *ref);
int batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing,
u8 *orig_addr, u8 *orig_neigh);
+int __init batadv_wifi_net_devices_init(void);
+void batadv_wifi_net_devices_deinit(void);
/**
* batadv_hardif_put() - decrement the hard interface refcounter and possibly
return hard_iface;
}
+/**
+ * batadv_is_cfg80211() - check if the given hardif is a cfg80211
+ * wifi interface
+ * @wifi_flags: extracted batadv_hard_iface_wifi_flagss of an net_device
+ *
+ * Return: true if the net device is a cfg80211 wireless device, false
+ * otherwise.
+ */
+static inline bool batadv_is_cfg80211(u32 wifi_flags)
+{
+ u32 allowed_flags = 0;
+
+ allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
+ allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
+
+ return !!(wifi_flags & allowed_flags);
+}
+
+/**
+ * batadv_is_wifi() - check if flags belong to wifi interface
+ * @wifi_flags: extracted batadv_hard_iface_wifi_flagss of an net_device
+ *
+ * Return: true if the net device is a 802.11 wireless device, false otherwise.
+ */
+static inline bool batadv_is_wifi(u32 wifi_flags)
+{
+ return wifi_flags != 0;
+}
+
#endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */