// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2008, 2009 open80211s Ltd.
* Copyright (C) 2023 Intel Corporation
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*/
#include <linux/etherdevice.h>
#include <linux/list.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <net/mac80211.h>
#include "wme.h"
#include "ieee80211_i.h"
#include "mesh.h"
#include <linux/rhashtable.h>
static void mesh_path_free_rcu(struct mesh_table *tbl, struct mesh_path *mpath);
static u32 mesh_table_hash(const void *addr, u32 len, u32 seed)
{
/* Use last four bytes of hw addr as hash index */
return jhash_1word(__get_unaligned_cpu32((u8 *)addr + 2), seed);
}
static const struct rhashtable_params mesh_rht_params = {
.nelem_hint = 2,
.automatic_shrinking = true,
.key_len = ETH_ALEN,
.key_offset = offsetof(struct mesh_path, dst),
.head_offset = offsetof(struct mesh_path, rhash),
.hashfn = mesh_table_hash,
};
static const struct rhashtable_params fast_tx_rht_params = {
.nelem_hint = 10,
.automatic_shrinking = true,
.key_len = sizeof_field(struct ieee80211_mesh_fast_tx, key),
.key_offset = offsetof(struct ieee80211_mesh_fast_tx, key),
.head_offset = offsetof(struct ieee80211_mesh_fast_tx, rhash),
.hashfn = mesh_table_hash,
};
static void __mesh_fast_tx_entry_free(void *ptr, void *tblptr)
{
struct ieee80211_mesh_fast_tx *entry = ptr;
kfree_rcu(entry, fast_tx.rcu_head);
}
static void mesh_fast_tx_deinit(struct ieee80211_sub_if_data *sdata)
{
struct mesh_tx_cache *cache;
cache = &sdata->u.mesh.tx_cache;
rhashtable_free_and_destroy(&cache->rht,
__mesh_fast_tx_entry_free, NULL);
}
static void mesh_fast_tx_init(struct ieee80211_sub_if_data *sdata)
{
struct mesh_tx_cache *cache;
cache = &sdata->u.mesh.tx_cache;
rhashtable_init(&cache->rht, &fast_tx_rht_params);
INIT_HLIST_HEAD(&cache->walk_head);
spin_lock_init(&cache->walk_lock);
}
static inline bool mpath_expired(struct mesh_path *mpath)
{
return (mpath->flags & MESH_PATH_ACTIVE) &&
time_after(jiffies, mpath->exp_time) &&
!(mpath->flags & MESH_PATH_FIXED);
}
static void mesh_path_rht_free(void *ptr, void *tblptr)
{
struct mesh_path *mpath = ptr;
struct mesh_table *tbl = tblptr;
mesh_path_free_rcu(tbl, mpath);
}
static void mesh_table_init(struct mesh_table *tbl)
{
INIT_HLIST_HEAD(&tbl->known_gates);
INIT_HLIST_HEAD(&tbl->walk_head);
atomic_set(&tbl->entries, 0);
spin_lock_init(&tbl->gates_lock);
spin_lock_init(&tbl->walk_lock);
/* rhashtable_init() may fail only in case of wrong
* mesh_rht_params
*/
WARN_ON(rhashtable_init(&tbl->rhead, &mesh_rht_params));
}
static void mesh_table_free(struct mesh_table *tbl)
{
rhashtable_free_and_destroy(&tbl->rhead,
mesh_path_rht_free, tbl);
}
/**
* mesh_path_assign_nexthop - update mesh path next hop
*
* @mpath: mesh path to update
* @sta: next hop to assign
*
* Locking: mpath->state_lock must be held when calling this function
*/
void mesh_path_assign_nexthop(struct mesh_path