aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/wireless/core.c9
-rw-r--r--net/wireless/core.h2
-rw-r--r--net/wireless/nl80211.c308
-rw-r--r--net/wireless/nl80211.h2
4 files changed, 321 insertions, 0 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 4f9f216665e9..389a3f2ee464 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -462,6 +462,14 @@ int wiphy_register(struct wiphy *wiphy)
462 return -EINVAL; 462 return -EINVAL;
463#endif 463#endif
464 464
465 if (WARN_ON(wiphy->coalesce &&
466 (!wiphy->coalesce->n_rules ||
467 !wiphy->coalesce->n_patterns) &&
468 (!wiphy->coalesce->pattern_min_len ||
469 wiphy->coalesce->pattern_min_len >
470 wiphy->coalesce->pattern_max_len)))
471 return -EINVAL;
472
465 if (WARN_ON(wiphy->ap_sme_capa && 473 if (WARN_ON(wiphy->ap_sme_capa &&
466 !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) 474 !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME)))
467 return -EINVAL; 475 return -EINVAL;
@@ -668,6 +676,7 @@ void wiphy_unregister(struct wiphy *wiphy)
668 rdev_set_wakeup(rdev, false); 676 rdev_set_wakeup(rdev, false);
669#endif 677#endif
670 cfg80211_rdev_free_wowlan(rdev); 678 cfg80211_rdev_free_wowlan(rdev);
679 cfg80211_rdev_free_coalesce(rdev);
671} 680}
672EXPORT_SYMBOL(wiphy_unregister); 681EXPORT_SYMBOL(wiphy_unregister);
673 682
diff --git a/net/wireless/core.h b/net/wireless/core.h
index a6b45bf00f33..9ad43c619c54 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -79,6 +79,8 @@ struct cfg80211_registered_device {
79 /* netlink port which started critical protocol (0 means not started) */ 79 /* netlink port which started critical protocol (0 means not started) */
80 u32 crit_proto_nlportid; 80 u32 crit_proto_nlportid;
81 81
82 struct cfg80211_coalesce *coalesce;
83
82 /* must be last because of the way we do wiphy_priv(), 84 /* must be last because of the way we do wiphy_priv(),
83 * and it should at least be aligned to NETDEV_ALIGN */ 85 * and it should at least be aligned to NETDEV_ALIGN */
84 struct wiphy wiphy __aligned(NETDEV_ALIGN); 86 struct wiphy wiphy __aligned(NETDEV_ALIGN);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 0492478ab74e..6dca5a700174 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -403,6 +403,14 @@ nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
403 [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 }, 403 [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
404}; 404};
405 405
406/* policy for coalesce rule attributes */
407static const struct nla_policy
408nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
409 [NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 },
410 [NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 },
411 [NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED },
412};
413
406/* policy for GTK rekey offload attributes */ 414/* policy for GTK rekey offload attributes */
407static const struct nla_policy 415static const struct nla_policy
408nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { 416nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
@@ -995,6 +1003,27 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
995} 1003}
996#endif 1004#endif
997 1005
1006static int nl80211_send_coalesce(struct sk_buff *msg,
1007 struct cfg80211_registered_device *dev)
1008{
1009 struct nl80211_coalesce_rule_support rule;
1010
1011 if (!dev->wiphy.coalesce)
1012 return 0;
1013
1014 rule.max_rules = dev->wiphy.coalesce->n_rules;
1015 rule.max_delay = dev->wiphy.coalesce->max_delay;
1016 rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns;
1017 rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len;
1018 rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len;
1019 rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset;
1020
1021 if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
1022 return -ENOBUFS;
1023
1024 return 0;
1025}
1026
998static int nl80211_send_band_rateinfo(struct sk_buff *msg, 1027static int nl80211_send_band_rateinfo(struct sk_buff *msg,
999 struct ieee80211_supported_band *sband) 1028 struct ieee80211_supported_band *sband)
1000{ 1029{
@@ -1513,6 +1542,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
1513 dev->wiphy.vht_capa_mod_mask)) 1542 dev->wiphy.vht_capa_mod_mask))
1514 goto nla_put_failure; 1543 goto nla_put_failure;
1515 1544
1545 state->split_start++;
1546 break;
1547 case 10:
1548 if (nl80211_send_coalesce(msg, dev))
1549 goto nla_put_failure;
1550
1516 /* done */ 1551 /* done */
1517 state->split_start = 0; 1552 state->split_start = 0;
1518 break; 1553 break;
@@ -8043,6 +8078,264 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
8043} 8078}
8044#endif 8079#endif
8045 8080
8081static int nl80211_send_coalesce_rules(struct sk_buff *msg,
8082 struct cfg80211_registered_device *rdev)
8083{
8084 struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules;
8085 int i, j, pat_len;
8086 struct cfg80211_coalesce_rules *rule;
8087
8088 if (!rdev->coalesce->n_rules)
8089 return 0;
8090
8091 nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
8092 if (!nl_rules)
8093 return -ENOBUFS;
8094
8095 for (i = 0; i < rdev->coalesce->n_rules; i++) {
8096 nl_rule = nla_nest_start(msg, i + 1);
8097 if (!nl_rule)
8098 return -ENOBUFS;
8099
8100 rule = &rdev->coalesce->rules[i];
8101 if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
8102 rule->delay))
8103 return -ENOBUFS;
8104
8105 if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
8106 rule->condition))
8107 return -ENOBUFS;
8108
8109 nl_pats = nla_nest_start(msg,
8110 NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
8111 if (!nl_pats)
8112 return -ENOBUFS;
8113
8114 for (j = 0; j < rule->n_patterns; j++) {
8115 nl_pat = nla_nest_start(msg, j + 1);
8116 if (!nl_pat)
8117 return -ENOBUFS;
8118 pat_len = rule->patterns[j].pattern_len;
8119 if (nla_put(msg, NL80211_PKTPAT_MASK,
8120 DIV_ROUND_UP(pat_len, 8),
8121 rule->patterns[j].mask) ||
8122 nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
8123 rule->patterns[j].pattern) ||
8124 nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
8125 rule->patterns[j].pkt_offset))
8126 return -ENOBUFS;
8127 nla_nest_end(msg, nl_pat);
8128 }
8129 nla_nest_end(msg, nl_pats);
8130 nla_nest_end(msg, nl_rule);
8131 }
8132 nla_nest_end(msg, nl_rules);
8133
8134 return 0;
8135}
8136
8137static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info)
8138{
8139 struct cfg80211_registered_device *rdev = info->user_ptr[0];
8140 struct sk_buff *msg;
8141 void *hdr;
8142
8143 if (!rdev->wiphy.coalesce)
8144 return -EOPNOTSUPP;
8145
8146 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
8147 if (!msg)
8148 return -ENOMEM;
8149
8150 hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
8151 NL80211_CMD_GET_COALESCE);
8152 if (!hdr)
8153 goto nla_put_failure;
8154
8155 if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev))
8156 goto nla_put_failure;
8157
8158 genlmsg_end(msg, hdr);
8159 return genlmsg_reply(msg, info);
8160
8161nla_put_failure:
8162 nlmsg_free(msg);
8163 return -ENOBUFS;
8164}
8165
8166void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev)
8167{
8168 struct cfg80211_coalesce *coalesce = rdev->coalesce;
8169 int i, j;
8170 struct cfg80211_coalesce_rules *rule;
8171
8172 if (!coalesce)
8173 return;
8174
8175 for (i = 0; i < coalesce->n_rules; i++) {
8176 rule = &coalesce->rules[i];
8177 for (j = 0; j < rule->n_patterns; j++)
8178 kfree(rule->patterns[j].mask);
8179 kfree(rule->patterns);
8180 }
8181 kfree(coalesce->rules);
8182 kfree(coalesce);
8183 rdev->coalesce = NULL;
8184}
8185
8186static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
8187 struct nlattr *rule,
8188 struct cfg80211_coalesce_rules *new_rule)
8189{
8190 int err, i;
8191 const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
8192 struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat;
8193 int rem, pat_len, mask_len, pkt_offset, n_patterns = 0;
8194 struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
8195
8196 err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule),
8197 nla_len(rule), nl80211_coalesce_policy);
8198 if (err)
8199 return err;
8200
8201 if (tb[NL80211_ATTR_COALESCE_RULE_DELAY])
8202 new_rule->delay =
8203 nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]);
8204 if (new_rule->delay > coalesce->max_delay)
8205 return -EINVAL;
8206
8207 if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION])
8208 new_rule->condition =
8209 nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]);
8210 if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH &&
8211 new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH)
8212 return -EINVAL;
8213
8214 if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN])
8215 return -EINVAL;
8216
8217 nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
8218 rem)
8219 n_patterns++;
8220 if (n_patterns > coalesce->n_patterns)
8221 return -EINVAL;
8222
8223 new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]),
8224 GFP_KERNEL);
8225 if (!new_rule->patterns)
8226 return -ENOMEM;
8227
8228 new_rule->n_patterns = n_patterns;
8229 i = 0;
8230
8231 nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
8232 rem) {
8233 nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
8234 nla_len(pat), NULL);
8235 if (!pat_tb[NL80211_PKTPAT_MASK] ||
8236 !pat_tb[NL80211_PKTPAT_PATTERN])
8237 return -EINVAL;
8238 pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
8239 mask_len = DIV_ROUND_UP(pat_len, 8);
8240 if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
8241 return -EINVAL;
8242 if (pat_len > coalesce->pattern_max_len ||
8243 pat_len < coalesce->pattern_min_len)
8244 return -EINVAL;
8245
8246 if (!pat_tb[NL80211_PKTPAT_OFFSET])
8247 pkt_offset = 0;
8248 else
8249 pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]);
8250 if (pkt_offset > coalesce->max_pkt_offset)
8251 return -EINVAL;
8252 new_rule->patterns[i].pkt_offset = pkt_offset;
8253
8254 new_rule->patterns[i].mask =
8255 kmalloc(mask_len + pat_len, GFP_KERNEL);
8256 if (!new_rule->patterns[i].mask)
8257 return -ENOMEM;
8258 new_rule->patterns[i].pattern =
8259 new_rule->patterns[i].mask + mask_len;
8260 memcpy(new_rule->patterns[i].mask,
8261 nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len);
8262 new_rule->patterns[i].pattern_len = pat_len;
8263 memcpy(new_rule->patterns[i].pattern,
8264 nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len);
8265 i++;
8266 }
8267
8268 return 0;
8269}
8270
8271static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
8272{
8273 struct cfg80211_registered_device *rdev = info->user_ptr[0];
8274 const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
8275 struct cfg80211_coalesce new_coalesce = {};
8276 struct cfg80211_coalesce *n_coalesce;
8277 int err, rem_rule, n_rules = 0, i, j;
8278 struct nlattr *rule;
8279 struct cfg80211_coalesce_rules *tmp_rule;
8280
8281 if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce)
8282 return -EOPNOTSUPP;
8283
8284 if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
8285 cfg80211_rdev_free_coalesce(rdev);
8286 rdev->ops->set_coalesce(&rdev->wiphy, NULL);
8287 return 0;
8288 }
8289
8290 nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
8291 rem_rule)
8292 n_rules++;
8293 if (n_rules > coalesce->n_rules)
8294 return -EINVAL;
8295
8296 new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]),
8297 GFP_KERNEL);
8298 if (!new_coalesce.rules)
8299 return -ENOMEM;
8300
8301 new_coalesce.n_rules = n_rules;
8302 i = 0;
8303
8304 nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
8305 rem_rule) {
8306 err = nl80211_parse_coalesce_rule(rdev, rule,
8307 &new_coalesce.rules[i]);
8308 if (err)
8309 goto error;
8310
8311 i++;
8312 }
8313
8314 err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
8315 if (err)
8316 goto error;
8317
8318 n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL);
8319 if (!n_coalesce) {
8320 err = -ENOMEM;
8321 goto error;
8322 }
8323 cfg80211_rdev_free_coalesce(rdev);
8324 rdev->coalesce = n_coalesce;
8325
8326 return 0;
8327error:
8328 for (i = 0; i < new_coalesce.n_rules; i++) {
8329 tmp_rule = &new_coalesce.rules[i];
8330 for (j = 0; j < tmp_rule->n_patterns; j++)
8331 kfree(tmp_rule->patterns[j].mask);
8332 kfree(tmp_rule->patterns);
8333 }
8334 kfree(new_coalesce.rules);
8335
8336 return err;
8337}
8338
8046static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) 8339static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
8047{ 8340{
8048 struct cfg80211_registered_device *rdev = info->user_ptr[0]; 8341 struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -9050,6 +9343,21 @@ static struct genl_ops nl80211_ops[] = {
9050 .flags = GENL_ADMIN_PERM, 9343 .flags = GENL_ADMIN_PERM,
9051 .internal_flags = NL80211_FLAG_NEED_WDEV_UP | 9344 .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
9052 NL80211_FLAG_NEED_RTNL, 9345 NL80211_FLAG_NEED_RTNL,
9346 },
9347 {
9348 .cmd = NL80211_CMD_GET_COALESCE,
9349 .doit = nl80211_get_coalesce,
9350 .policy = nl80211_policy,
9351 .internal_flags = NL80211_FLAG_NEED_WIPHY |
9352 NL80211_FLAG_NEED_RTNL,
9353 },
9354 {
9355 .cmd = NL80211_CMD_SET_COALESCE,
9356 .doit = nl80211_set_coalesce,
9357 .policy = nl80211_policy,
9358 .flags = GENL_ADMIN_PERM,
9359 .internal_flags = NL80211_FLAG_NEED_WIPHY |
9360 NL80211_FLAG_NEED_RTNL,
9053 } 9361 }
9054}; 9362};
9055 9363
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index a4073e808c13..44341bf53cfc 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -74,4 +74,6 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
74 enum nl80211_radar_event event, 74 enum nl80211_radar_event event,
75 struct net_device *netdev, gfp_t gfp); 75 struct net_device *netdev, gfp_t gfp);
76 76
77void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
78
77#endif /* __NET_WIRELESS_NL80211_H */ 79#endif /* __NET_WIRELESS_NL80211_H */