aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSara Sharon <sara.sharon@intel.com>2015-11-19 04:53:49 -0500
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2015-12-16 03:19:53 -0500
commitc97dab40796c59a4b03c532603e837077718fb81 (patch)
tree2276f722f30d18fc1048c66a5f646d3f68a7e3d2
parente6eb8ca9e486a8f73ae237630e89406683b2be75 (diff)
iwlwifi: mvm: change protocol offload flows
RFC4862 states that "In all cases, a node MUST NOT respond to a Neighbor Solicitation for a tentative address". Currently the driver configures the NS offload and does not wait for address to become permanent, thus violating the RFC. Just removing the address from the address list is not good enough for all cases, since the NS messages are needed for the duplicate address detection and should not be discarded. For d0i3 disable NS offload. Put tentative address in the address list so the NS packet will not be filtered out by ucode. For D3 the platform will not wake from NS packets - so enable NS offload while removing the tentative address from the list. Given that now NS offload might be disabled, and that the ucode uses the IP data for other puroposes (L3 filtering) add two independent flags indicating if IPv4\IPv6 data is valid. Signed-off-by: Sara Sharon <sara.sharon@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/d3.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/offloading.c74
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ops.c3
5 files changed, 69 insertions, 20 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index b1c99921c376..8824a894aab0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -104,9 +104,13 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
104 struct inet6_ifaddr *ifa; 104 struct inet6_ifaddr *ifa;
105 int idx = 0; 105 int idx = 0;
106 106
107 memset(mvmvif->tentative_addrs, 0, sizeof(mvmvif->tentative_addrs));
108
107 read_lock_bh(&idev->lock); 109 read_lock_bh(&idev->lock);
108 list_for_each_entry(ifa, &idev->addr_list, if_list) { 110 list_for_each_entry(ifa, &idev->addr_list, if_list) {
109 mvmvif->target_ipv6_addrs[idx] = ifa->addr; 111 mvmvif->target_ipv6_addrs[idx] = ifa->addr;
112 if (ifa->flags & IFA_F_TENTATIVE)
113 __set_bit(idx, mvmvif->tentative_addrs);
110 idx++; 114 idx++;
111 if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX) 115 if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)
112 break; 116 break;
@@ -964,7 +968,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
964 if (ret) 968 if (ret)
965 return ret; 969 return ret;
966 970
967 ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0); 971 ret = iwl_mvm_send_proto_offload(mvm, vif, false, true, 0);
968 if (ret) 972 if (ret)
969 return ret; 973 return ret;
970 974
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h
index 228684cfdb7e..c36c9563f744 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h
@@ -94,10 +94,14 @@ struct iwl_d3_manager_config {
94 * enum iwl_d3_proto_offloads - enabled protocol offloads 94 * enum iwl_d3_proto_offloads - enabled protocol offloads
95 * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled 95 * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled
96 * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled 96 * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled
97 * @IWL_D3_PROTO_IPV4_VALID: IPv4 data is valid
98 * @IWL_D3_PROTO_IPV6_VALID: IPv6 data is valid
97 */ 99 */
98enum iwl_proto_offloads { 100enum iwl_proto_offloads {
99 IWL_D3_PROTO_OFFLOAD_ARP = BIT(0), 101 IWL_D3_PROTO_OFFLOAD_ARP = BIT(0),
100 IWL_D3_PROTO_OFFLOAD_NS = BIT(1), 102 IWL_D3_PROTO_OFFLOAD_NS = BIT(1),
103 IWL_D3_PROTO_IPV4_VALID = BIT(2),
104 IWL_D3_PROTO_IPV6_VALID = BIT(3),
101}; 105};
102 106
103#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 107#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 3fc7199abc94..d8760fa9e322 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -422,6 +422,7 @@ struct iwl_mvm_vif {
422#if IS_ENABLED(CONFIG_IPV6) 422#if IS_ENABLED(CONFIG_IPV6)
423 /* IPv6 addresses for WoWLAN */ 423 /* IPv6 addresses for WoWLAN */
424 struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; 424 struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
425 unsigned long tentative_addrs[BITS_TO_LONGS(IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)];
425 int num_target_ipv6_addrs; 426 int num_target_ipv6_addrs;
426#endif 427#endif
427 428
@@ -1299,6 +1300,7 @@ void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
1299int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, 1300int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
1300 struct ieee80211_vif *vif, 1301 struct ieee80211_vif *vif,
1301 bool disable_offloading, 1302 bool disable_offloading,
1303 bool offload_ns,
1302 u32 cmd_flags); 1304 u32 cmd_flags);
1303 1305
1304/* D0i3 */ 1306/* D0i3 */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c
index f83e2bc6d0d5..6338d9cf7070 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c
@@ -7,6 +7,7 @@
7 * 7 *
8 * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 8 * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
9 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH 9 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
10 * Copyright(c) 2015 Intel Deutschland GmbH
10 * 11 *
11 * This program is free software; you can redistribute it and/or modify 12 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of version 2 of the GNU General Public License as 13 * it under the terms of version 2 of the GNU General Public License as
@@ -33,6 +34,7 @@
33 * 34 *
34 * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 35 * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
35 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH 36 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
37 * Copyright(c) 2015 Intel Deutschland GmbH
36 * All rights reserved. 38 * All rights reserved.
37 * 39 *
38 * Redistribution and use in source and binary forms, with or without 40 * Redistribution and use in source and binary forms, with or without
@@ -64,6 +66,7 @@
64 *****************************************************************************/ 66 *****************************************************************************/
65#include <net/ipv6.h> 67#include <net/ipv6.h>
66#include <net/addrconf.h> 68#include <net/addrconf.h>
69#include <linux/bitops.h>
67#include "mvm.h" 70#include "mvm.h"
68 71
69void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, 72void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
@@ -86,6 +89,7 @@ void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
86int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, 89int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
87 struct ieee80211_vif *vif, 90 struct ieee80211_vif *vif,
88 bool disable_offloading, 91 bool disable_offloading,
92 bool offload_ns,
89 u32 cmd_flags) 93 u32 cmd_flags)
90{ 94{
91 union { 95 union {
@@ -106,6 +110,13 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
106#if IS_ENABLED(CONFIG_IPV6) 110#if IS_ENABLED(CONFIG_IPV6)
107 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 111 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
108 int i; 112 int i;
113 /*
114 * Skip tentative address when ns offload is enabled to avoid
115 * violating RFC4862.
116 * Keep tentative address when ns offload is disabled so the NS packets
117 * will not be filtered out and will wake up the host.
118 */
119 bool skip_tentative = offload_ns;
109 120
110 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || 121 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
111 capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { 122 capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
@@ -113,6 +124,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
113 struct iwl_targ_addr *addrs; 124 struct iwl_targ_addr *addrs;
114 int n_nsc, n_addrs; 125 int n_nsc, n_addrs;
115 int c; 126 int c;
127 int num_skipped = 0;
116 128
117 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { 129 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
118 nsc = cmd.v3s.ns_config; 130 nsc = cmd.v3s.ns_config;
@@ -126,9 +138,6 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
126 n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; 138 n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
127 } 139 }
128 140
129 if (mvmvif->num_target_ipv6_addrs)
130 enabled |= IWL_D3_PROTO_OFFLOAD_NS;
131
132 /* 141 /*
133 * For each address we have (and that will fit) fill a target 142 * For each address we have (and that will fit) fill a target
134 * address struct and combine for NS offload structs with the 143 * address struct and combine for NS offload structs with the
@@ -140,6 +149,12 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
140 struct in6_addr solicited_addr; 149 struct in6_addr solicited_addr;
141 int j; 150 int j;
142 151
152 if (skip_tentative &&
153 test_bit(i, mvmvif->tentative_addrs)) {
154 num_skipped++;
155 continue;
156 }
157
143 addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], 158 addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
144 &solicited_addr); 159 &solicited_addr);
145 for (j = 0; j < c; j++) 160 for (j = 0; j < c; j++)
@@ -154,41 +169,64 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
154 memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); 169 memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
155 } 170 }
156 171
172 if (mvmvif->num_target_ipv6_addrs - num_skipped)
173 enabled |= IWL_D3_PROTO_IPV6_VALID;
174
157 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) 175 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
158 cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i); 176 cmd.v3s.num_valid_ipv6_addrs =
177 cpu_to_le32(i - num_skipped);
159 else 178 else
160 cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i); 179 cmd.v3l.num_valid_ipv6_addrs =
180 cpu_to_le32(i - num_skipped);
161 } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { 181 } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
162 if (mvmvif->num_target_ipv6_addrs) { 182 bool found = false;
163 enabled |= IWL_D3_PROTO_OFFLOAD_NS;
164 memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
165 }
166 183
167 BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != 184 BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=
168 sizeof(mvmvif->target_ipv6_addrs[0])); 185 sizeof(mvmvif->target_ipv6_addrs[0]));
169 186
170 for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, 187 for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
171 IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) 188 IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) {
189 if (skip_tentative &&
190 test_bit(i, mvmvif->tentative_addrs))
191 continue;
192
172 memcpy(cmd.v2.target_ipv6_addr[i], 193 memcpy(cmd.v2.target_ipv6_addr[i],
173 &mvmvif->target_ipv6_addrs[i], 194 &mvmvif->target_ipv6_addrs[i],
174 sizeof(cmd.v2.target_ipv6_addr[i])); 195 sizeof(cmd.v2.target_ipv6_addr[i]));
175 } else {
176 if (mvmvif->num_target_ipv6_addrs) {
177 enabled |= IWL_D3_PROTO_OFFLOAD_NS;
178 memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
179 }
180 196
197 found = true;
198 }
199 if (found) {
200 enabled |= IWL_D3_PROTO_IPV6_VALID;
201 memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
202 }
203 } else {
204 bool found = false;
181 BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != 205 BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=
182 sizeof(mvmvif->target_ipv6_addrs[0])); 206 sizeof(mvmvif->target_ipv6_addrs[0]));
183 207
184 for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, 208 for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
185 IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) 209 IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) {
210 if (skip_tentative &&
211 test_bit(i, mvmvif->tentative_addrs))
212 continue;
213
186 memcpy(cmd.v1.target_ipv6_addr[i], 214 memcpy(cmd.v1.target_ipv6_addr[i],
187 &mvmvif->target_ipv6_addrs[i], 215 &mvmvif->target_ipv6_addrs[i],
188 sizeof(cmd.v1.target_ipv6_addr[i])); 216 sizeof(cmd.v1.target_ipv6_addr[i]));
217
218 found = true;
219 }
220
221 if (found) {
222 enabled |= IWL_D3_PROTO_IPV6_VALID;
223 memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
224 }
189 } 225 }
190#endif
191 226
227 if (offload_ns && (enabled & IWL_D3_PROTO_IPV6_VALID))
228 enabled |= IWL_D3_PROTO_OFFLOAD_NS;
229#endif
192 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { 230 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
193 common = &cmd.v3s.common; 231 common = &cmd.v3s.common;
194 size = sizeof(cmd.v3s); 232 size = sizeof(cmd.v3s);
@@ -204,7 +242,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
204 } 242 }
205 243
206 if (vif->bss_conf.arp_addr_cnt) { 244 if (vif->bss_conf.arp_addr_cnt) {
207 enabled |= IWL_D3_PROTO_OFFLOAD_ARP; 245 enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID;
208 common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; 246 common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
209 memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); 247 memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
210 } 248 }
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index 31c16a185001..4753ecd1abda 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -1153,7 +1153,8 @@ static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac,
1153 data->disable_offloading = true; 1153 data->disable_offloading = true;
1154 1154
1155 iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags); 1155 iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags);
1156 iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading, flags); 1156 iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading,
1157 false, flags);
1157 1158
1158 /* 1159 /*
1159 * on init/association, mvm already configures POWER_TABLE_CMD 1160 * on init/association, mvm already configures POWER_TABLE_CMD