Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cfg80211/nl80211: Add packet coalesce support

In most cases, host that receives IPv4 and IPv6 multicast/broadcast
packets does not do anything with these packets. Therefore the
reception of these unwanted packets causes unnecessary processing
and power consumption.

Packet coalesce feature helps to reduce number of received
interrupts to host by buffering these packets in firmware/hardware
for some predefined time. Received interrupt will be generated when
one of the following events occur.
a) Expiration of hardware timer whose expiration time is set to
maximum coalescing delay of matching coalesce rule.
b) Coalescing buffer in hardware reaches it's limit.
c) Packet doesn't match any of the configured coalesce rules.

This patch adds set/get configuration support for packet coalesce.
User needs to configure following parameters for creating a coalesce
rule.
a) Maximum coalescing delay
b) List of packet patterns which needs to be matched
c) Condition for coalescence. pattern 'match' or 'no match'
Multiple such rules can be created.

This feature needs to be advertised during driver initialization.
Drivers are supposed to do required firmware/hardware settings based
on user configuration.

Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
[fix kernel-doc, change free function, fix copy/paste error]
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>

authored by

Amitkumar Karwar and committed by
Johannes Berg
be29b99a a144f378

+463 -2
+54
include/net/cfg80211.h
··· 1781 1781 }; 1782 1782 1783 1783 /** 1784 + * struct cfg80211_coalesce_rules - Coalesce rule parameters 1785 + * 1786 + * This structure defines coalesce rule for the device. 1787 + * @delay: maximum coalescing delay in msecs. 1788 + * @condition: condition for packet coalescence. 1789 + * see &enum nl80211_coalesce_condition. 1790 + * @patterns: array of packet patterns 1791 + * @n_patterns: number of patterns 1792 + */ 1793 + struct cfg80211_coalesce_rules { 1794 + int delay; 1795 + enum nl80211_coalesce_condition condition; 1796 + struct cfg80211_pkt_pattern *patterns; 1797 + int n_patterns; 1798 + }; 1799 + 1800 + /** 1801 + * struct cfg80211_coalesce - Packet coalescing settings 1802 + * 1803 + * This structure defines coalescing settings. 1804 + * @rules: array of coalesce rules 1805 + * @n_rules: number of rules 1806 + */ 1807 + struct cfg80211_coalesce { 1808 + struct cfg80211_coalesce_rules *rules; 1809 + int n_rules; 1810 + }; 1811 + 1812 + /** 1784 1813 * struct cfg80211_wowlan_wakeup - wakeup report 1785 1814 * @disconnect: woke up by getting disconnected 1786 1815 * @magic_pkt: woke up by receiving magic packet ··· 2105 2076 * driver can take the most appropriate actions. 2106 2077 * @crit_proto_stop: Indicates critical protocol no longer needs increased link 2107 2078 * reliability. This operation can not fail. 2079 + * @set_coalesce: Set coalesce parameters. 2108 2080 */ 2109 2081 struct cfg80211_ops { 2110 2082 int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); ··· 2341 2311 u16 duration); 2342 2312 void (*crit_proto_stop)(struct wiphy *wiphy, 2343 2313 struct wireless_dev *wdev); 2314 + int (*set_coalesce)(struct wiphy *wiphy, 2315 + struct cfg80211_coalesce *coalesce); 2344 2316 }; 2345 2317 2346 2318 /* ··· 2569 2537 }; 2570 2538 2571 2539 /** 2540 + * struct wiphy_coalesce_support - coalesce support data 2541 + * @n_rules: maximum number of coalesce rules 2542 + * @max_delay: maximum supported coalescing delay in msecs 2543 + * @n_patterns: number of supported patterns in a rule 2544 + * (see nl80211.h for the pattern definition) 2545 + * @pattern_max_len: maximum length of each pattern 2546 + * @pattern_min_len: minimum length of each pattern 2547 + * @max_pkt_offset: maximum Rx packet offset 2548 + */ 2549 + struct wiphy_coalesce_support { 2550 + int n_rules; 2551 + int max_delay; 2552 + int n_patterns; 2553 + int pattern_max_len; 2554 + int pattern_min_len; 2555 + int max_pkt_offset; 2556 + }; 2557 + 2558 + /** 2572 2559 * struct wiphy - wireless hardware description 2573 2560 * @reg_notifier: the driver's regulatory notification callback, 2574 2561 * note that if your driver uses wiphy_apply_custom_regulatory() ··· 2697 2646 * 802.11-2012 8.4.2.29 for the defined fields. 2698 2647 * @extended_capabilities_mask: mask of the valid values 2699 2648 * @extended_capabilities_len: length of the extended capabilities 2649 + * @coalesce: packet coalescing support information 2700 2650 */ 2701 2651 struct wiphy { 2702 2652 /* assign these fields before you register the wiphy */ ··· 2806 2754 #ifdef CONFIG_CFG80211_WEXT 2807 2755 const struct iw_handler_def *wext; 2808 2756 #endif 2757 + 2758 + const struct wiphy_coalesce_support *coalesce; 2809 2759 2810 2760 char priv[0] __aligned(NETDEV_ALIGN); 2811 2761 };
+88 -2
include/uapi/linux/nl80211.h
··· 126 126 */ 127 127 128 128 /** 129 + * DOC: packet coalesce support 130 + * 131 + * In most cases, host that receives IPv4 and IPv6 multicast/broadcast 132 + * packets does not do anything with these packets. Therefore the 133 + * reception of these unwanted packets causes unnecessary processing 134 + * and power consumption. 135 + * 136 + * Packet coalesce feature helps to reduce number of received interrupts 137 + * to host by buffering these packets in firmware/hardware for some 138 + * predefined time. Received interrupt will be generated when one of the 139 + * following events occur. 140 + * a) Expiration of hardware timer whose expiration time is set to maximum 141 + * coalescing delay of matching coalesce rule. 142 + * b) Coalescing buffer in hardware reaches it's limit. 143 + * c) Packet doesn't match any of the configured coalesce rules. 144 + * 145 + * User needs to configure following parameters for creating a coalesce 146 + * rule. 147 + * a) Maximum coalescing delay 148 + * b) List of packet patterns which needs to be matched 149 + * c) Condition for coalescence. pattern 'match' or 'no match' 150 + * Multiple such rules can be created. 151 + */ 152 + 153 + /** 129 154 * enum nl80211_commands - supported nl80211 commands 130 155 * 131 156 * @NL80211_CMD_UNSPEC: unspecified command to catch errors ··· 673 648 * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can 674 649 * return back to normal. 675 650 * 651 + * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules. 652 + * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules. 653 + * 676 654 * @NL80211_CMD_MAX: highest used command number 677 655 * @__NL80211_CMD_AFTER_LAST: internal use 678 656 */ ··· 837 809 838 810 NL80211_CMD_CRIT_PROTOCOL_START, 839 811 NL80211_CMD_CRIT_PROTOCOL_STOP, 812 + 813 + NL80211_CMD_GET_COALESCE, 814 + NL80211_CMD_SET_COALESCE, 840 815 841 816 /* add new commands above here */ 842 817 ··· 1467 1436 * allowed to be used with the first @NL80211_CMD_SET_STATION command to 1468 1437 * update a TDLS peer STA entry. 1469 1438 * 1439 + * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. 1440 + * 1470 1441 * @NL80211_ATTR_MAX: highest attribute number currently defined 1471 1442 * @__NL80211_ATTR_AFTER_LAST: internal use 1472 1443 */ ··· 1768 1735 NL80211_ATTR_MAX_CRIT_PROT_DURATION, 1769 1736 1770 1737 NL80211_ATTR_PEER_AID, 1738 + 1739 + NL80211_ATTR_COALESCE_RULE, 1771 1740 1772 1741 /* add attributes here, update the policy in nl80211.c */ 1773 1742 ··· 3133 3098 * @max_pkt_offset: maximum Rx packet offset 3134 3099 * 3135 3100 * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when 3136 - * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the 3137 - * capability information given by the kernel to userspace. 3101 + * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in 3102 + * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of 3103 + * %NL80211_ATTR_COALESCE_RULE in the capability information given 3104 + * by the kernel to userspace. 3138 3105 */ 3139 3106 struct nl80211_pattern_support { 3140 3107 __u32 max_patterns; ··· 3352 3315 /* keep last */ 3353 3316 NUM_NL80211_WOWLAN_TCP, 3354 3317 MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1 3318 + }; 3319 + 3320 + /** 3321 + * struct nl80211_coalesce_rule_support - coalesce rule support information 3322 + * @max_rules: maximum number of rules supported 3323 + * @pat: packet pattern support information 3324 + * @max_delay: maximum supported coalescing delay in msecs 3325 + * 3326 + * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the 3327 + * capability information given by the kernel to userspace. 3328 + */ 3329 + struct nl80211_coalesce_rule_support { 3330 + __u32 max_rules; 3331 + struct nl80211_pattern_support pat; 3332 + __u32 max_delay; 3333 + } __attribute__((packed)); 3334 + 3335 + /** 3336 + * enum nl80211_attr_coalesce_rule - coalesce rule attribute 3337 + * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute 3338 + * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing 3339 + * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence, 3340 + * see &enum nl80211_coalesce_condition. 3341 + * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched 3342 + * after these fixed number of bytes of received packet 3343 + * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes 3344 + * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number 3345 + */ 3346 + enum nl80211_attr_coalesce_rule { 3347 + __NL80211_COALESCE_RULE_INVALID, 3348 + NL80211_ATTR_COALESCE_RULE_DELAY, 3349 + NL80211_ATTR_COALESCE_RULE_CONDITION, 3350 + NL80211_ATTR_COALESCE_RULE_PKT_PATTERN, 3351 + 3352 + /* keep last */ 3353 + NUM_NL80211_ATTR_COALESCE_RULE, 3354 + NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1 3355 + }; 3356 + 3357 + /** 3358 + * enum nl80211_coalesce_condition - coalesce rule conditions 3359 + * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns 3360 + * in a rule are matched. 3361 + * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns 3362 + * in a rule are not matched. 3363 + */ 3364 + enum nl80211_coalesce_condition { 3365 + NL80211_COALESCE_CONDITION_MATCH, 3366 + NL80211_COALESCE_CONDITION_NO_MATCH 3355 3367 }; 3356 3368 3357 3369 /**
+9
net/wireless/core.c
··· 462 462 return -EINVAL; 463 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 473 if (WARN_ON(wiphy->ap_sme_capa && 466 474 !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) 467 475 return -EINVAL; ··· 676 668 rdev_set_wakeup(rdev, false); 677 669 #endif 678 670 cfg80211_rdev_free_wowlan(rdev); 671 + cfg80211_rdev_free_coalesce(rdev); 679 672 } 680 673 EXPORT_SYMBOL(wiphy_unregister); 681 674
+2
net/wireless/core.h
··· 79 79 /* netlink port which started critical protocol (0 means not started) */ 80 80 u32 crit_proto_nlportid; 81 81 82 + struct cfg80211_coalesce *coalesce; 83 + 82 84 /* must be last because of the way we do wiphy_priv(), 83 85 * and it should at least be aligned to NETDEV_ALIGN */ 84 86 struct wiphy wiphy __aligned(NETDEV_ALIGN);
+308
net/wireless/nl80211.c
··· 403 403 [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 }, 404 404 }; 405 405 406 + /* policy for coalesce rule attributes */ 407 + static const struct nla_policy 408 + nl80211_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 414 /* policy for GTK rekey offload attributes */ 407 415 static const struct nla_policy 408 416 nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { ··· 1003 995 } 1004 996 #endif 1005 997 998 + static int nl80211_send_coalesce(struct sk_buff *msg, 999 + struct cfg80211_registered_device *dev) 1000 + { 1001 + struct nl80211_coalesce_rule_support rule; 1002 + 1003 + if (!dev->wiphy.coalesce) 1004 + return 0; 1005 + 1006 + rule.max_rules = dev->wiphy.coalesce->n_rules; 1007 + rule.max_delay = dev->wiphy.coalesce->max_delay; 1008 + rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns; 1009 + rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len; 1010 + rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len; 1011 + rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset; 1012 + 1013 + if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule)) 1014 + return -ENOBUFS; 1015 + 1016 + return 0; 1017 + } 1018 + 1006 1019 static int nl80211_send_band_rateinfo(struct sk_buff *msg, 1007 1020 struct ieee80211_supported_band *sband) 1008 1021 { ··· 1540 1511 nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, 1541 1512 sizeof(*dev->wiphy.vht_capa_mod_mask), 1542 1513 dev->wiphy.vht_capa_mod_mask)) 1514 + goto nla_put_failure; 1515 + 1516 + state->split_start++; 1517 + break; 1518 + case 10: 1519 + if (nl80211_send_coalesce(msg, dev)) 1543 1520 goto nla_put_failure; 1544 1521 1545 1522 /* done */ ··· 8078 8043 } 8079 8044 #endif 8080 8045 8046 + static int nl80211_send_coalesce_rules(struct sk_buff *msg, 8047 + struct cfg80211_registered_device *rdev) 8048 + { 8049 + struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules; 8050 + int i, j, pat_len; 8051 + struct cfg80211_coalesce_rules *rule; 8052 + 8053 + if (!rdev->coalesce->n_rules) 8054 + return 0; 8055 + 8056 + nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE); 8057 + if (!nl_rules) 8058 + return -ENOBUFS; 8059 + 8060 + for (i = 0; i < rdev->coalesce->n_rules; i++) { 8061 + nl_rule = nla_nest_start(msg, i + 1); 8062 + if (!nl_rule) 8063 + return -ENOBUFS; 8064 + 8065 + rule = &rdev->coalesce->rules[i]; 8066 + if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY, 8067 + rule->delay)) 8068 + return -ENOBUFS; 8069 + 8070 + if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION, 8071 + rule->condition)) 8072 + return -ENOBUFS; 8073 + 8074 + nl_pats = nla_nest_start(msg, 8075 + NL80211_ATTR_COALESCE_RULE_PKT_PATTERN); 8076 + if (!nl_pats) 8077 + return -ENOBUFS; 8078 + 8079 + for (j = 0; j < rule->n_patterns; j++) { 8080 + nl_pat = nla_nest_start(msg, j + 1); 8081 + if (!nl_pat) 8082 + return -ENOBUFS; 8083 + pat_len = rule->patterns[j].pattern_len; 8084 + if (nla_put(msg, NL80211_PKTPAT_MASK, 8085 + DIV_ROUND_UP(pat_len, 8), 8086 + rule->patterns[j].mask) || 8087 + nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len, 8088 + rule->patterns[j].pattern) || 8089 + nla_put_u32(msg, NL80211_PKTPAT_OFFSET, 8090 + rule->patterns[j].pkt_offset)) 8091 + return -ENOBUFS; 8092 + nla_nest_end(msg, nl_pat); 8093 + } 8094 + nla_nest_end(msg, nl_pats); 8095 + nla_nest_end(msg, nl_rule); 8096 + } 8097 + nla_nest_end(msg, nl_rules); 8098 + 8099 + return 0; 8100 + } 8101 + 8102 + static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info) 8103 + { 8104 + struct cfg80211_registered_device *rdev = info->user_ptr[0]; 8105 + struct sk_buff *msg; 8106 + void *hdr; 8107 + 8108 + if (!rdev->wiphy.coalesce) 8109 + return -EOPNOTSUPP; 8110 + 8111 + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 8112 + if (!msg) 8113 + return -ENOMEM; 8114 + 8115 + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, 8116 + NL80211_CMD_GET_COALESCE); 8117 + if (!hdr) 8118 + goto nla_put_failure; 8119 + 8120 + if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev)) 8121 + goto nla_put_failure; 8122 + 8123 + genlmsg_end(msg, hdr); 8124 + return genlmsg_reply(msg, info); 8125 + 8126 + nla_put_failure: 8127 + nlmsg_free(msg); 8128 + return -ENOBUFS; 8129 + } 8130 + 8131 + void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev) 8132 + { 8133 + struct cfg80211_coalesce *coalesce = rdev->coalesce; 8134 + int i, j; 8135 + struct cfg80211_coalesce_rules *rule; 8136 + 8137 + if (!coalesce) 8138 + return; 8139 + 8140 + for (i = 0; i < coalesce->n_rules; i++) { 8141 + rule = &coalesce->rules[i]; 8142 + for (j = 0; j < rule->n_patterns; j++) 8143 + kfree(rule->patterns[j].mask); 8144 + kfree(rule->patterns); 8145 + } 8146 + kfree(coalesce->rules); 8147 + kfree(coalesce); 8148 + rdev->coalesce = NULL; 8149 + } 8150 + 8151 + static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, 8152 + struct nlattr *rule, 8153 + struct cfg80211_coalesce_rules *new_rule) 8154 + { 8155 + int err, i; 8156 + const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; 8157 + struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat; 8158 + int rem, pat_len, mask_len, pkt_offset, n_patterns = 0; 8159 + struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; 8160 + 8161 + err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule), 8162 + nla_len(rule), nl80211_coalesce_policy); 8163 + if (err) 8164 + return err; 8165 + 8166 + if (tb[NL80211_ATTR_COALESCE_RULE_DELAY]) 8167 + new_rule->delay = 8168 + nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]); 8169 + if (new_rule->delay > coalesce->max_delay) 8170 + return -EINVAL; 8171 + 8172 + if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION]) 8173 + new_rule->condition = 8174 + nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]); 8175 + if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH && 8176 + new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH) 8177 + return -EINVAL; 8178 + 8179 + if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN]) 8180 + return -EINVAL; 8181 + 8182 + nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], 8183 + rem) 8184 + n_patterns++; 8185 + if (n_patterns > coalesce->n_patterns) 8186 + return -EINVAL; 8187 + 8188 + new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]), 8189 + GFP_KERNEL); 8190 + if (!new_rule->patterns) 8191 + return -ENOMEM; 8192 + 8193 + new_rule->n_patterns = n_patterns; 8194 + i = 0; 8195 + 8196 + nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], 8197 + rem) { 8198 + nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), 8199 + nla_len(pat), NULL); 8200 + if (!pat_tb[NL80211_PKTPAT_MASK] || 8201 + !pat_tb[NL80211_PKTPAT_PATTERN]) 8202 + return -EINVAL; 8203 + pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]); 8204 + mask_len = DIV_ROUND_UP(pat_len, 8); 8205 + if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len) 8206 + return -EINVAL; 8207 + if (pat_len > coalesce->pattern_max_len || 8208 + pat_len < coalesce->pattern_min_len) 8209 + return -EINVAL; 8210 + 8211 + if (!pat_tb[NL80211_PKTPAT_OFFSET]) 8212 + pkt_offset = 0; 8213 + else 8214 + pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]); 8215 + if (pkt_offset > coalesce->max_pkt_offset) 8216 + return -EINVAL; 8217 + new_rule->patterns[i].pkt_offset = pkt_offset; 8218 + 8219 + new_rule->patterns[i].mask = 8220 + kmalloc(mask_len + pat_len, GFP_KERNEL); 8221 + if (!new_rule->patterns[i].mask) 8222 + return -ENOMEM; 8223 + new_rule->patterns[i].pattern = 8224 + new_rule->patterns[i].mask + mask_len; 8225 + memcpy(new_rule->patterns[i].mask, 8226 + nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len); 8227 + new_rule->patterns[i].pattern_len = pat_len; 8228 + memcpy(new_rule->patterns[i].pattern, 8229 + nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len); 8230 + i++; 8231 + } 8232 + 8233 + return 0; 8234 + } 8235 + 8236 + static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info) 8237 + { 8238 + struct cfg80211_registered_device *rdev = info->user_ptr[0]; 8239 + const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; 8240 + struct cfg80211_coalesce new_coalesce = {}; 8241 + struct cfg80211_coalesce *n_coalesce; 8242 + int err, rem_rule, n_rules = 0, i, j; 8243 + struct nlattr *rule; 8244 + struct cfg80211_coalesce_rules *tmp_rule; 8245 + 8246 + if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce) 8247 + return -EOPNOTSUPP; 8248 + 8249 + if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) { 8250 + cfg80211_rdev_free_coalesce(rdev); 8251 + rdev->ops->set_coalesce(&rdev->wiphy, NULL); 8252 + return 0; 8253 + } 8254 + 8255 + nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], 8256 + rem_rule) 8257 + n_rules++; 8258 + if (n_rules > coalesce->n_rules) 8259 + return -EINVAL; 8260 + 8261 + new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]), 8262 + GFP_KERNEL); 8263 + if (!new_coalesce.rules) 8264 + return -ENOMEM; 8265 + 8266 + new_coalesce.n_rules = n_rules; 8267 + i = 0; 8268 + 8269 + nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], 8270 + rem_rule) { 8271 + err = nl80211_parse_coalesce_rule(rdev, rule, 8272 + &new_coalesce.rules[i]); 8273 + if (err) 8274 + goto error; 8275 + 8276 + i++; 8277 + } 8278 + 8279 + err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce); 8280 + if (err) 8281 + goto error; 8282 + 8283 + n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL); 8284 + if (!n_coalesce) { 8285 + err = -ENOMEM; 8286 + goto error; 8287 + } 8288 + cfg80211_rdev_free_coalesce(rdev); 8289 + rdev->coalesce = n_coalesce; 8290 + 8291 + return 0; 8292 + error: 8293 + for (i = 0; i < new_coalesce.n_rules; i++) { 8294 + tmp_rule = &new_coalesce.rules[i]; 8295 + for (j = 0; j < tmp_rule->n_patterns; j++) 8296 + kfree(tmp_rule->patterns[j].mask); 8297 + kfree(tmp_rule->patterns); 8298 + } 8299 + kfree(new_coalesce.rules); 8300 + 8301 + return err; 8302 + } 8303 + 8081 8304 static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) 8082 8305 { 8083 8306 struct cfg80211_registered_device *rdev = info->user_ptr[0]; ··· 9342 9049 .policy = nl80211_policy, 9343 9050 .flags = GENL_ADMIN_PERM, 9344 9051 .internal_flags = NL80211_FLAG_NEED_WDEV_UP | 9052 + NL80211_FLAG_NEED_RTNL, 9053 + }, 9054 + { 9055 + .cmd = NL80211_CMD_GET_COALESCE, 9056 + .doit = nl80211_get_coalesce, 9057 + .policy = nl80211_policy, 9058 + .internal_flags = NL80211_FLAG_NEED_WIPHY | 9059 + NL80211_FLAG_NEED_RTNL, 9060 + }, 9061 + { 9062 + .cmd = NL80211_CMD_SET_COALESCE, 9063 + .doit = nl80211_set_coalesce, 9064 + .policy = nl80211_policy, 9065 + .flags = GENL_ADMIN_PERM, 9066 + .internal_flags = NL80211_FLAG_NEED_WIPHY | 9345 9067 NL80211_FLAG_NEED_RTNL, 9346 9068 } 9347 9069 };
+2
net/wireless/nl80211.h
··· 74 74 enum nl80211_radar_event event, 75 75 struct net_device *netdev, gfp_t gfp); 76 76 77 + void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev); 78 + 77 79 #endif /* __NET_WIRELESS_NL80211_H */