wil6210: support set_multicast_to_unicast cfg80211 operation

Wil6210 AP has a separate ring for transmitting multicast packets,
multicast packets are transmitted without an ack from the receiver side.
Therefore, 802.11 spec defines some low MCS rates for multicat packets.
However, there is no guarantee that these packets were really received
and handled on the client side.

Some applications that rely on multicast packets, may prefer to
transmit these packets as a unicast to ensure reliability, and also
to ensure better performance with high MCS rates.
multicast to unicast is done by duplicating multicast packets to all
clients and changing the DA (multicast) to the MAC address of the
client.
see NL80211_CMD_SET_MULTICAST_TO_UNICAST for more info.

Signed-off-by: Ahmad Masri <amasri@codeaurora.org>
Signed-off-by: Maya Erez <merez@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
Ahmad Masri 2019-12-18 20:10:21 +02:00 committed by Kalle Valo
parent 18beb61d84
commit 5e5f069c30
3 changed files with 48 additions and 1 deletions

View File

@ -2579,6 +2579,21 @@ wil_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev,
return rc;
}
static int wil_cfg80211_set_multicast_to_unicast(struct wiphy *wiphy,
struct net_device *dev,
const bool enabled)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
if (wil->multicast_to_unicast == enabled)
return 0;
wil_info(wil, "set multicast to unicast, enabled=%d\n", enabled);
wil->multicast_to_unicast = enabled;
return 0;
}
static const struct cfg80211_ops wil_cfg80211_ops = {
.add_virtual_intf = wil_cfg80211_add_iface,
.del_virtual_intf = wil_cfg80211_del_iface,
@ -2615,6 +2630,7 @@ static const struct cfg80211_ops wil_cfg80211_ops = {
.sched_scan_start = wil_cfg80211_sched_scan_start,
.sched_scan_stop = wil_cfg80211_sched_scan_stop,
.update_ft_ies = wil_cfg80211_update_ft_ies,
.set_multicast_to_unicast = wil_cfg80211_set_multicast_to_unicast,
};
static void wil_wiphy_init(struct wiphy *wiphy)

View File

@ -10,6 +10,7 @@
#include <linux/moduleparam.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/if_vlan.h>
#include <net/ipv6.h>
#include <linux/prefetch.h>
@ -1529,6 +1530,35 @@ static struct wil_ring *wil_find_tx_bcast_1(struct wil6210_priv *wil,
return v;
}
/* apply multicast to unicast only for ARP and IP packets
* (see NL80211_CMD_SET_MULTICAST_TO_UNICAST for more info)
*/
static bool wil_check_multicast_to_unicast(struct wil6210_priv *wil,
struct sk_buff *skb)
{
const struct ethhdr *eth = (void *)skb->data;
const struct vlan_ethhdr *ethvlan = (void *)skb->data;
__be16 ethertype;
if (!wil->multicast_to_unicast)
return false;
/* multicast to unicast conversion only for some payload */
ethertype = eth->h_proto;
if (ethertype == htons(ETH_P_8021Q) && skb->len >= VLAN_ETH_HLEN)
ethertype = ethvlan->h_vlan_encapsulated_proto;
switch (ethertype) {
case htons(ETH_P_ARP):
case htons(ETH_P_IP):
case htons(ETH_P_IPV6):
break;
default:
return false;
}
return true;
}
static void wil_set_da_for_vring(struct wil6210_priv *wil,
struct sk_buff *skb, int vring_index)
{
@ -2336,7 +2366,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/* in STA mode (ESS), all to same VRING (to AP) */
ring = wil_find_tx_ring_sta(wil, vif, skb);
} else if (bcast) {
if (vif->pbss)
if (vif->pbss || wil_check_multicast_to_unicast(wil, skb))
/* in pbss, no bcast VRING - duplicate skb in
* all stations VRINGs
*/

View File

@ -1059,6 +1059,7 @@ struct wil6210_priv {
u32 max_agg_wsize;
u32 max_ampdu_size;
u8 multicast_to_unicast;
};
#define wil_to_wiphy(i) (i->wiphy)