@@ -183,11 +183,17 @@ int wilc_get_vif_idx(struct wilc_vif *vif)
static struct wilc_vif *wilc_get_vif_from_idx(struct wilc *wilc, int idx)
{
int index = idx - 1;
+ struct wilc_vif *vif;
if (index < 0 || index >= WILC_NUM_CONCURRENT_IFC)
return NULL;
- return wilc->vif[index];
+ list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
+ if (vif->idx == index)
+ return vif;
+ }
+
+ return NULL;
}
static int handle_scan_done(struct wilc_vif *vif, enum scan_event evt)
@@ -97,29 +97,25 @@ void wilc_mac_indicate(struct wilc *wilc)
static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header)
{
u8 *bssid, *bssid1;
- int i = 0;
struct net_device *ndev = NULL;
+ struct wilc_vif *vif;
bssid = mac_header + 10;
bssid1 = mac_header + 4;
- mutex_lock(&wilc->vif_mutex);
- for (i = 0; i < wilc->vif_num; i++) {
- if (wilc->vif[i]->mode == WILC_STATION_MODE)
- if (ether_addr_equal_unaligned(bssid,
- wilc->vif[i]->bssid)) {
- ndev = wilc->vif[i]->ndev;
+ list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
+ if (vif->mode == WILC_STATION_MODE)
+ if (ether_addr_equal_unaligned(bssid, vif->bssid)) {
+ ndev = vif->ndev;
goto out;
}
- if (wilc->vif[i]->mode == WILC_AP_MODE)
- if (ether_addr_equal_unaligned(bssid1,
- wilc->vif[i]->bssid)) {
- ndev = wilc->vif[i]->ndev;
+ if (vif->mode == WILC_AP_MODE)
+ if (ether_addr_equal_unaligned(bssid1, vif->bssid)) {
+ ndev = vif->ndev;
goto out;
}
}
out:
- mutex_unlock(&wilc->vif_mutex);
return ndev;
}
@@ -137,13 +133,16 @@ void wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid, u8 mode)
int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc)
{
- u8 i = 0;
+ int srcu_idx;
u8 ret_val = 0;
+ struct wilc_vif *vif;
- for (i = 0; i < wilc->vif_num; i++)
- if (!is_zero_ether_addr(wilc->vif[i]->bssid))
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
+ if (!is_zero_ether_addr(vif->bssid))
ret_val++;
-
+ }
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
return ret_val;
}
@@ -167,16 +166,16 @@ static int wilc_txq_task(void *vp)
do {
ret = wilc_wlan_handle_txq(wl, &txq_count);
if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD) {
- int i;
+ int srcu_idx;
struct wilc_vif *ifc;
- mutex_lock(&wl->vif_mutex);
- for (i = 0; i < wl->vif_num; i++) {
- ifc = wl->vif[i];
+ srcu_idx = srcu_read_lock(&wl->srcu);
+ list_for_each_entry_rcu(ifc, &wl->vif_list,
+ list) {
if (ifc->mac_opened && ifc->ndev)
netif_wake_queue(ifc->ndev);
}
- mutex_unlock(&wl->vif_mutex);
+ srcu_read_unlock(&wl->srcu, srcu_idx);
}
} while (ret == -ENOBUFS && !wl->close);
}
@@ -725,14 +724,15 @@ netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev)
wilc_tx_complete);
if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) {
- int i;
+ int srcu_idx;
+ struct wilc_vif *vif;
- mutex_lock(&wilc->vif_mutex);
- for (i = 0; i < wilc->vif_num; i++) {
- if (wilc->vif[i]->mac_opened)
- netif_stop_queue(wilc->vif[i]->ndev);
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
+ if (vif->mac_opened)
+ netif_stop_queue(vif->ndev);
}
- mutex_unlock(&wilc->vif_mutex);
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
}
return 0;
@@ -810,14 +810,13 @@ void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size,
void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size)
{
- int i = 0;
+ int srcu_idx;
struct wilc_vif *vif;
- mutex_lock(&wilc->vif_mutex);
- for (i = 0; i < wilc->vif_num; i++) {
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
u16 type = le16_to_cpup((__le16 *)buff);
- vif = netdev_priv(wilc->vif[i]->ndev);
if ((type == vif->frame_reg[0].type && vif->frame_reg[0].reg) ||
(type == vif->frame_reg[1].type && vif->frame_reg[1].reg)) {
wilc_wfi_p2p_rx(vif, buff, size);
@@ -829,7 +828,7 @@ void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size)
break;
}
}
- mutex_unlock(&wilc->vif_mutex);
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
}
static const struct net_device_ops wilc_netdev_ops = {
@@ -843,7 +842,8 @@ static const struct net_device_ops wilc_netdev_ops = {
void wilc_netdev_cleanup(struct wilc *wilc)
{
- int i;
+ struct wilc_vif *vif;
+ int srcu_idx;
if (!wilc)
return;
@@ -853,14 +853,32 @@ void wilc_netdev_cleanup(struct wilc *wilc)
wilc->firmware = NULL;
}
- for (i = 0; i < wilc->vif_num; i++) {
- if (wilc->vif[i] && wilc->vif[i]->ndev)
- unregister_netdev(wilc->vif[i]->ndev);
+ srcu_idx = srcu_read_lock(&wilc->srcu);
+ list_for_each_entry_rcu(vif, &wilc->vif_list, list) {
+ if (vif->ndev)
+ unregister_netdev(vif->ndev);
}
+ srcu_read_unlock(&wilc->srcu, srcu_idx);
wilc_wfi_deinit_mon_interface(wilc, false);
flush_workqueue(wilc->hif_workqueue);
destroy_workqueue(wilc->hif_workqueue);
+
+ do {
+ mutex_lock(&wilc->vif_mutex);
+ if (wilc->vif_num <= 0) {
+ mutex_unlock(&wilc->vif_mutex);
+ break;
+ }
+ vif = wilc_get_wl_to_vif(wilc);
+ if (!IS_ERR(vif))
+ list_del_rcu(&vif->list);
+
+ wilc->vif_num--;
+ mutex_unlock(&wilc->vif_mutex);
+ synchronize_srcu(&wilc->srcu);
+ } while (1);
+
wilc_wlan_cfg_deinit(wilc);
wlan_deinit_locks(wilc);
kfree(wilc->bus_data);
@@ -869,6 +887,23 @@ void wilc_netdev_cleanup(struct wilc *wilc)
}
EXPORT_SYMBOL_GPL(wilc_netdev_cleanup);
+static u8 wilc_get_available_idx(struct wilc *wl)
+{
+ int idx = 0;
+ struct wilc_vif *vif;
+ int srcu_idx;
+
+ srcu_idx = srcu_read_lock(&wl->srcu);
+ list_for_each_entry_rcu(vif, &wl->vif_list, list) {
+ if (vif->idx == 0)
+ idx = 1;
+ else
+ idx = 0;
+ }
+ srcu_read_unlock(&wl->srcu, srcu_idx);
+ return idx;
+}
+
struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name,
int vif_type, enum nl80211_iftype type,
bool rtnl_locked)
@@ -909,10 +944,14 @@ struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name,
ndev->needs_free_netdev = true;
vif->iftype = vif_type;
- vif->wilc->vif[wl->vif_num] = vif;
- vif->idx = wl->vif_num;
- wl->vif_num += 1;
+ vif->idx = wilc_get_available_idx(wl);
vif->mac_opened = 0;
+ mutex_lock(&wl->vif_mutex);
+ list_add_tail_rcu(&vif->list, &wl->vif_list);
+ wl->vif_num += 1;
+ mutex_unlock(&wl->vif_mutex);
+ synchronize_srcu(&wl->srcu);
+
return vif;
}
@@ -186,15 +186,15 @@ static void cfg_connect_result(enum conn_event conn_disconn_evt, u8 mac_status,
}
}
-static struct wilc_vif *wilc_get_wl_to_vif(struct wilc *wl)
+struct wilc_vif *wilc_get_wl_to_vif(struct wilc *wl)
{
- int i;
+ struct wilc_vif *vif;
- for (i = 0; i < wl->vif_num; i++)
- if (wl->vif[i])
- return wl->vif[i];
+ vif = list_first_or_null_rcu(&wl->vif_list, typeof(*vif), list);
+ if (!vif)
+ return ERR_PTR(-EINVAL);
- return ERR_PTR(-EINVAL);
+ return vif;
}
static int set_channel(struct wiphy *wiphy,
@@ -204,11 +204,12 @@ static int set_channel(struct wiphy *wiphy,
struct wilc_vif *vif;
u32 channelnum;
int result;
+ int srcu_idx;
- mutex_lock(&wl->vif_mutex);
+ srcu_idx = srcu_read_lock(&wl->srcu);
vif = wilc_get_wl_to_vif(wl);
if (IS_ERR(vif)) {
- mutex_unlock(&wl->vif_mutex);
+ srcu_read_unlock(&wl->srcu, srcu_idx);
return PTR_ERR(vif);
}
@@ -219,7 +220,7 @@ static int set_channel(struct wiphy *wiphy,
if (result)
netdev_err(vif->ndev, "Error in setting channel\n");
- mutex_unlock(&wl->vif_mutex);
+ srcu_read_unlock(&wl->srcu, srcu_idx);
return result;
}
@@ -749,33 +750,19 @@ static int change_bss(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
-struct wilc_vif *wilc_get_interface(struct wilc *wl)
-{
- int i;
- struct wilc_vif *vif = NULL;
-
- mutex_lock(&wl->vif_mutex);
- for (i = 0; i < wl->vif_num; i++) {
- if (wl->vif[i]) {
- vif = wl->vif[i];
- break;
- }
- }
- mutex_unlock(&wl->vif_mutex);
- return vif;
-}
-
static int set_wiphy_params(struct wiphy *wiphy, u32 changed)
{
- int ret;
+ int ret = -EINVAL;
struct cfg_param_attr cfg_param_val;
struct wilc *wl = wiphy_priv(wiphy);
struct wilc_vif *vif;
struct wilc_priv *priv;
+ int srcu_idx;
- vif = wilc_get_interface(wl);
- if (!vif)
- return -EINVAL;
+ srcu_idx = srcu_read_lock(&wl->srcu);
+ vif = wilc_get_wl_to_vif(wl);
+ if (IS_ERR(vif))
+ goto out;
priv = &vif->priv;
cfg_param_val.flag = 0;
@@ -805,7 +792,7 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed)
} else {
netdev_err(vif->ndev,
"Fragmentation threshold out of range\n");
- return -EINVAL;
+ goto out;
}
}
@@ -818,7 +805,7 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed)
cfg_param_val.rts_threshold = wiphy->rts_threshold;
} else {
netdev_err(vif->ndev, "RTS threshold out of range\n");
- return -EINVAL;
+ goto out;
}
}
@@ -826,6 +813,8 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed)
if (ret)
netdev_err(priv->dev, "Error in setting WIPHY PARAMS\n");
+out:
+ srcu_read_unlock(&wl->srcu, srcu_idx);
return ret;
}
@@ -1554,20 +1543,16 @@ static int change_station(struct wiphy *wiphy, struct net_device *dev,
return ret;
}
-static int wilc_get_vif_from_type(struct wilc *wl, int type)
+static struct wilc_vif *wilc_get_vif_from_type(struct wilc *wl, int type)
{
- int i;
+ struct wilc_vif *vif;
- mutex_lock(&wl->vif_mutex);
- for (i = 0; i < wl->vif_num; i++) {
- if (wl->vif[i]->iftype == type) {
- mutex_unlock(&wl->vif_mutex);
- return i;
- }
+ list_for_each_entry_rcu(vif, &wl->vif_list, list) {
+ if (vif->iftype == type)
+ return vif;
}
- mutex_unlock(&wl->vif_mutex);
- return -EINVAL;
+ return NULL;
}
static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
@@ -1580,29 +1565,36 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
struct wilc_vif *vif;
struct wireless_dev *wdev;
int iftype;
- int ret;
if (type == NL80211_IFTYPE_MONITOR) {
struct net_device *ndev;
- int ap_index = wilc_get_vif_from_type(wl, WILC_AP_MODE);
-
- if (ap_index < 0) {
- ap_index = wilc_get_vif_from_type(wl, WILC_GO_MODE);
- if (ap_index < 0)
+ int srcu_idx;
+
+ srcu_idx = srcu_read_lock(&wl->srcu);
+ vif = wilc_get_vif_from_type(wl, WILC_AP_MODE);
+ if (!vif) {
+ vif = wilc_get_vif_from_type(wl, WILC_GO_MODE);
+ if (!vif) {
+ srcu_read_unlock(&wl->srcu, srcu_idx);
goto validate_interface;
+ }
}
- vif = wl->vif[ap_index];
- if (vif->monitor_flag)
+ if (vif->monitor_flag) {
+ srcu_read_unlock(&wl->srcu, srcu_idx);
goto validate_interface;
+ }
ndev = wilc_wfi_init_mon_interface(wl, name, vif->ndev);
- if (ndev)
+ if (ndev) {
vif->monitor_flag = 1;
- else
+ } else {
+ srcu_read_unlock(&wl->srcu, srcu_idx);
return ERR_PTR(-EINVAL);
+ }
wdev = &vif->priv.wdev;
+ srcu_read_unlock(&wl->srcu, srcu_idx);
return wdev;
}
@@ -1610,9 +1602,10 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
mutex_lock(&wl->vif_mutex);
if (wl->vif_num == WILC_NUM_CONCURRENT_IFC) {
pr_err("Reached maximum number of interface\n");
- ret = -EINVAL;
- goto out_err;
+ mutex_unlock(&wl->vif_mutex);
+ return ERR_PTR(-EINVAL);
}
+ mutex_unlock(&wl->vif_mutex);
switch (type) {
case NL80211_IFTYPE_STATION:
@@ -1622,30 +1615,20 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
iftype = WILC_AP_MODE;
break;
default:
- ret = -EOPNOTSUPP;
- goto out_err;
+ return ERR_PTR(-EOPNOTSUPP);
}
vif = wilc_netdev_ifc_init(wl, name, iftype, type, true);
- if (IS_ERR(vif)) {
- ret = PTR_ERR(vif);
- goto out_err;
- }
-
- mutex_unlock(&wl->vif_mutex);
+ if (IS_ERR(vif))
+ return ERR_CAST(vif);
return &vif->priv.wdev;
-
-out_err:
- mutex_unlock(&wl->vif_mutex);
- return ERR_PTR(ret);
}
static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
{
struct wilc *wl = wiphy_priv(wiphy);
struct wilc_vif *vif;
- int i;
if (wdev->iftype == NL80211_IFTYPE_AP ||
wdev->iftype == NL80211_IFTYPE_P2P_GO)
@@ -1655,22 +1638,12 @@ static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
unregister_netdevice(vif->ndev);
vif->monitor_flag = 0;
- mutex_lock(&wl->vif_mutex);
wilc_set_operation_mode(vif, 0, 0, 0);
- for (i = vif->idx; i < wl->vif_num; i++) {
- if ((i + 1) >= wl->vif_num) {
- wl->vif[i] = NULL;
- } else {
- vif = wl->vif[i + 1];
- vif->idx = i;
- wl->vif[i] = vif;
- wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
- vif->iftype, vif->idx);
- }
- }
+ mutex_lock(&wl->vif_mutex);
+ list_del_rcu(&vif->list);
wl->vif_num--;
mutex_unlock(&wl->vif_mutex);
-
+ synchronize_srcu(&wl->srcu);
return 0;
}
@@ -1695,16 +1668,17 @@ static void wilc_set_wakeup(struct wiphy *wiphy, bool enabled)
{
struct wilc *wl = wiphy_priv(wiphy);
struct wilc_vif *vif;
+ int srcu_idx;
- mutex_lock(&wl->vif_mutex);
+ srcu_idx = srcu_read_lock(&wl->srcu);
vif = wilc_get_wl_to_vif(wl);
if (IS_ERR(vif)) {
- mutex_unlock(&wl->vif_mutex);
+ srcu_read_unlock(&wl->srcu, srcu_idx);
return;
}
netdev_info(vif->ndev, "cfg set wake up = %d\n", enabled);
- mutex_unlock(&wl->vif_mutex);
+ srcu_read_unlock(&wl->srcu, srcu_idx);
}
static int set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
@@ -1800,6 +1774,7 @@ static void wlan_init_locks(struct wilc *wl)
init_completion(&wl->cfg_event);
init_completion(&wl->sync_event);
init_completion(&wl->txq_thread_started);
+ init_srcu_struct(&wl->srcu);
}
void wlan_deinit_locks(struct wilc *wilc)
@@ -1809,6 +1784,7 @@ void wlan_deinit_locks(struct wilc *wilc)
mutex_destroy(&wilc->cfg_cmd_lock);
mutex_destroy(&wilc->txq_add_to_head_cs);
mutex_destroy(&wilc->vif_mutex);
+ cleanup_srcu_struct(&wilc->srcu);
}
int wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type,
@@ -1834,6 +1810,7 @@ int wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type,
wl->chip_ps_state = WILC_CHIP_WAKEDUP;
INIT_LIST_HEAD(&wl->txq_head.list);
INIT_LIST_HEAD(&wl->rxq_head.list);
+ INIT_LIST_HEAD(&wl->vif_list);
wl->hif_workqueue = create_singlethread_workqueue("WILC_wq");
if (!wl->hif_workqueue) {
@@ -24,5 +24,6 @@ struct net_device *wilc_wfi_init_mon_interface(struct wilc *wl,
void wilc_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
u16 frame_type, bool reg);
struct wilc_vif *wilc_get_interface(struct wilc *wl);
+struct wilc_vif *wilc_get_wl_to_vif(struct wilc *wl);
void wlan_deinit_locks(struct wilc *wilc);
#endif
@@ -208,6 +208,7 @@ struct wilc_vif {
struct tcp_ack_filter ack_filter;
bool connecting;
struct wilc_priv priv;
+ struct list_head list;
};
struct wilc {
@@ -221,9 +222,10 @@ struct wilc {
int dev_irq_num;
int close;
u8 vif_num;
- struct wilc_vif *vif[WILC_NUM_CONCURRENT_IFC];
+ struct list_head vif_list;
/*protect vif list*/
struct mutex vif_mutex;
+ struct srcu_struct srcu;
u8 open_ifcs;
/*protect head of transmit queue*/
struct mutex txq_add_to_head_cs;