diff mbox series

[PATCHv2,1/9] nl80211: New netlink command for TID specific configuration

Message ID 1550813554-11581-2-git-send-email-tamizhr@codeaurora.org (mailing list archive)
State Superseded
Delegated to: Johannes Berg
Headers show
Series cfg80211/mac80211: Add support for TID specific configuration | expand

Commit Message

Tamizh chelvam Feb. 22, 2019, 5:32 a.m. UTC
Add a new NL command, NL80211_CMD_SET_TID_CONFIG to support
data TID specific configuration. This per TID configurations
are passed in NL80211_ATTR_TID_CONFIG which is a nested
attribute. This patch adds support to configure per TID
noack policy through NL80211_ATTR_TID_CONFIG_NOACK attribute.
Data TID value for this configuration will be passed through
NL80211_ATTR_TID_CONFIG_TID attribute. When the user-space wants
this configuration peer specific rather than being applied for
all the connected stations, MAC address of the peer can be passed
in NL80211_ATTR_MAC attribute. This patch introduced
enum ieee80211_tid_conf_mask to notify the driver that which
configuration modified.
Driver supporting data TID specific noack policy configuration
should be advertise through NL80211_EXT_FEATURE_PER_TID_NOACK_CONFIG
and supporting per STA data TID noack policy configuration
should be advertise through NL80211_EXT_FEATURE_PER_STA_NOACK_CONFIG

Signed-off-by: Tamizh chelvam <tamizhr@codeaurora.org>
---
 include/net/cfg80211.h       |  35 +++++++++++++++
 include/uapi/linux/nl80211.h |  51 ++++++++++++++++++++++
 net/wireless/nl80211.c       | 102 +++++++++++++++++++++++++++++++++++++++++++
 net/wireless/rdev-ops.h      |  11 +++++
 net/wireless/trace.h         |  18 ++++++++
 5 files changed, 217 insertions(+)

Comments

Sergey Matyukevich Feb. 26, 2019, 12:29 p.m. UTC | #1
Hello Tamizh,

> Signed-off-by: Tamizh chelvam <tamizhr@codeaurora.org>
> ---
>  include/net/cfg80211.h       |  35 +++++++++++++++
>  include/uapi/linux/nl80211.h |  51 ++++++++++++++++++++++
>  net/wireless/nl80211.c       | 102 +++++++++++++++++++++++++++++++++++++++++++
>  net/wireless/rdev-ops.h      |  11 +++++
>  net/wireless/trace.h         |  18 ++++++++
>  5 files changed, 217 insertions(+)

...

> diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
> index 82d5e1e..352eb4a2 100644
> --- a/net/wireless/nl80211.c
> +++ b/net/wireless/nl80211.c
> @@ -280,6 +280,13 @@ static int validate_ie_attr(const struct nlattr *attr,
>                 NLA_POLICY_NESTED_ARRAY(nl80211_psmr_peer_attr_policy),
>  };
> 
> +static const struct nla_policy
> +nl80211_attr_tid_config_policy[NL80211_ATTR_TID_CONFIG_MAX + 1] = {
> +       [NL80211_ATTR_TID_CONFIG_TID] = NLA_POLICY_MAX(NLA_U8, 7),

Such a policy permits configuration of per-TID settings, either
for per-STA or for all the STAs. However it is not possible to
perform configuration for all TIDs at once, e.g. passing -1 value
to driver.

Maybe simplify policy and use .type = NLA_U8 ?

Sanity check, if needed, can be performed by driver or even by firmware.

> +       [NL80211_ATTR_TID_CONFIG_NOACK] =
> +                       NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE),
> +};
> +
>  const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
>         [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
>         [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
> @@ -541,6 +548,8 @@ static int validate_ie_attr(const struct nlattr *attr,
>         [NL80211_ATTR_PEER_MEASUREMENTS] =
>                 NLA_POLICY_NESTED(nl80211_pmsr_attr_policy),
>         [NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
> +       [NL80211_ATTR_TID_CONFIG] =
> +                       NLA_POLICY_NESTED(nl80211_attr_tid_config_policy),
>  };

...

> +static int nl80211_set_tid_config(struct sk_buff *skb,
> +                                 struct genl_info *info)
> +{
> +       struct cfg80211_registered_device *rdev = info->user_ptr[0];
> +       struct nlattr *attrs[NL80211_ATTR_TID_CONFIG_MAX + 1];
> +       struct net_device *dev = info->user_ptr[1];
> +       struct ieee80211_tid_config *tid_conf;
> +       struct nlattr *tid;
> +       int conf_idx = 0, rem_conf;
> +       u32 num_conf = 0, size_of_conf;
> +       int ret = -EINVAL;
> +
> +       if (!info->attrs[NL80211_ATTR_TID_CONFIG])
> +               return -EINVAL;
> +
> +       if (!rdev->ops->set_tid_config)
> +               return -EOPNOTSUPP;
> +
> +       nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG],
> +                           rem_conf)
> +               num_conf++;
> +
> +       size_of_conf = sizeof(struct ieee80211_tid_config) +
> +               num_conf * sizeof(struct ieee80211_tid_cfg);
> +
> +       tid_conf = kzalloc(size_of_conf, GFP_KERNEL);
> +       if (!tid_conf)
> +               return -ENOMEM;
> +
> +       tid_conf->n_tid_conf = num_conf;
> +
> +       if (info->attrs[NL80211_ATTR_MAC])
> +               tid_conf->peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
> +       else
> +               tid_conf->peer = NULL;
> +
> +       nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG],
> +                           rem_conf) {
> +               ret = nla_parse_nested(attrs, NL80211_ATTR_TID_CONFIG_MAX, tid,
> +                                      NULL, NULL);
> +
> +               if (ret)
> +                       return ret;
> +
> +               if (!attrs[NL80211_ATTR_TID_CONFIG_TID])
> +                       return -EINVAL;
> +
> +               ret = parse_tid_conf(rdev, attrs, &tid_conf->tid_conf[conf_idx],
> +                                    tid_conf->peer);
> +               if (ret)
> +                       goto bad_tid_conf;
> +
> +               conf_idx++;
> +       }
> +
> +       return rdev_set_tid_config(rdev, dev, tid_conf);

What is the ownership rule for tid_conf ? In other words, who is
responsible for freeing this structure ?

Unless I am missing something, in the suggested patches there is no
kfree for this structure and all the nested structures (see Tx bitrate
patch), neither in cfg80211/mac80211 nor in ath10k.

As far as I understand, we have two options here. One option is to
explicitly specify that ownership is passed to the caller, similar
to nl80211_set_reg. Another option is to release memory in this
function after driver makes copies of all necessary fields,
e.g. see nl80211_set_mac_acl.

> +
> +bad_tid_conf:
> +       kfree(tid_conf);
> +       return ret;
> +}

Regards,
Sergey
Tamizh chelvam Feb. 27, 2019, 6:03 a.m. UTC | #2
Hi Sergey,
> 
>> Signed-off-by: Tamizh chelvam <tamizhr@codeaurora.org>
>> ---
>>  include/net/cfg80211.h       |  35 +++++++++++++++
>>  include/uapi/linux/nl80211.h |  51 ++++++++++++++++++++++
>>  net/wireless/nl80211.c       | 102 
>> +++++++++++++++++++++++++++++++++++++++++++
>>  net/wireless/rdev-ops.h      |  11 +++++
>>  net/wireless/trace.h         |  18 ++++++++
>>  5 files changed, 217 insertions(+)
> 
> ...
> 
>> diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
>> index 82d5e1e..352eb4a2 100644
>> --- a/net/wireless/nl80211.c
>> +++ b/net/wireless/nl80211.c
>> @@ -280,6 +280,13 @@ static int validate_ie_attr(const struct nlattr 
>> *attr,
>>                 
>> NLA_POLICY_NESTED_ARRAY(nl80211_psmr_peer_attr_policy),
>>  };
>> 
>> +static const struct nla_policy
>> +nl80211_attr_tid_config_policy[NL80211_ATTR_TID_CONFIG_MAX + 1] = {
>> +       [NL80211_ATTR_TID_CONFIG_TID] = NLA_POLICY_MAX(NLA_U8, 7),
> 
> Such a policy permits configuration of per-TID settings, either
> for per-STA or for all the STAs. However it is not possible to
> perform configuration for all TIDs at once, e.g. passing -1 value
> to driver.
> 
> Maybe simplify policy and use .type = NLA_U8 ?
> 
> Sanity check, if needed, can be performed by driver or even by 
> firmware.
> 
Sure, we can have like that. And do you feel driver should advertise 
support to perform configuration for all TIDs(like accepting tid -1) ?

>> +       [NL80211_ATTR_TID_CONFIG_NOACK] =
>> +                       NLA_POLICY_MAX(NLA_U8, 
>> NL80211_TID_CONFIG_DISABLE),
>> +};
>> +
>>  const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
>>         [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
>>         [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
>> @@ -541,6 +548,8 @@ static int validate_ie_attr(const struct nlattr 
>> *attr,
>>         [NL80211_ATTR_PEER_MEASUREMENTS] =
>>                 NLA_POLICY_NESTED(nl80211_pmsr_attr_policy),
>>         [NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
>> +       [NL80211_ATTR_TID_CONFIG] =
>> +                       
>> NLA_POLICY_NESTED(nl80211_attr_tid_config_policy),
>>  };
> 
> ...
> 
>> +static int nl80211_set_tid_config(struct sk_buff *skb,
>> +                                 struct genl_info *info)
>> +{
>> +       struct cfg80211_registered_device *rdev = info->user_ptr[0];
>> +       struct nlattr *attrs[NL80211_ATTR_TID_CONFIG_MAX + 1];
>> +       struct net_device *dev = info->user_ptr[1];
>> +       struct ieee80211_tid_config *tid_conf;
>> +       struct nlattr *tid;
>> +       int conf_idx = 0, rem_conf;
>> +       u32 num_conf = 0, size_of_conf;
>> +       int ret = -EINVAL;
>> +
>> +       if (!info->attrs[NL80211_ATTR_TID_CONFIG])
>> +               return -EINVAL;
>> +
>> +       if (!rdev->ops->set_tid_config)
>> +               return -EOPNOTSUPP;
>> +
>> +       nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG],
>> +                           rem_conf)
>> +               num_conf++;
>> +
>> +       size_of_conf = sizeof(struct ieee80211_tid_config) +
>> +               num_conf * sizeof(struct ieee80211_tid_cfg);
>> +
>> +       tid_conf = kzalloc(size_of_conf, GFP_KERNEL);
>> +       if (!tid_conf)
>> +               return -ENOMEM;
>> +
>> +       tid_conf->n_tid_conf = num_conf;
>> +
>> +       if (info->attrs[NL80211_ATTR_MAC])
>> +               tid_conf->peer = 
>> nla_data(info->attrs[NL80211_ATTR_MAC]);
>> +       else
>> +               tid_conf->peer = NULL;
>> +
>> +       nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG],
>> +                           rem_conf) {
>> +               ret = nla_parse_nested(attrs, 
>> NL80211_ATTR_TID_CONFIG_MAX, tid,
>> +                                      NULL, NULL);
>> +
>> +               if (ret)
>> +                       return ret;
>> +
>> +               if (!attrs[NL80211_ATTR_TID_CONFIG_TID])
>> +                       return -EINVAL;
>> +
>> +               ret = parse_tid_conf(rdev, attrs, 
>> &tid_conf->tid_conf[conf_idx],
>> +                                    tid_conf->peer);
>> +               if (ret)
>> +                       goto bad_tid_conf;
>> +
>> +               conf_idx++;
>> +       }
>> +
>> +       return rdev_set_tid_config(rdev, dev, tid_conf);
> 
> What is the ownership rule for tid_conf ? In other words, who is
> responsible for freeing this structure ?
> 
> Unless I am missing something, in the suggested patches there is no
> kfree for this structure and all the nested structures (see Tx bitrate
> patch), neither in cfg80211/mac80211 nor in ath10k.
> 
> As far as I understand, we have two options here. One option is to
> explicitly specify that ownership is passed to the caller, similar
> to nl80211_set_reg. Another option is to release memory in this
> function after driver makes copies of all necessary fields,
> e.g. see nl80211_set_mac_acl.
> 
Thanks for pointing out, will free it in this function(cfg80211) itself. 
I will make the change and send next patchset.
>> +
>> +bad_tid_conf:
>> +       kfree(tid_conf);
>> +       return ret;
>> +}

Thanks,
Tamizh.
Sergey Matyukevich Feb. 27, 2019, 10:01 a.m. UTC | #3
Hi Tamizh,

> Hi Sergey,
> > 
> > > Signed-off-by: Tamizh chelvam <tamizhr@codeaurora.org>
> > > ---
> > >  include/net/cfg80211.h       |  35 +++++++++++++++
> > >  include/uapi/linux/nl80211.h |  51 ++++++++++++++++++++++
> > >  net/wireless/nl80211.c       | 102
> > > +++++++++++++++++++++++++++++++++++++++++++
> > >  net/wireless/rdev-ops.h      |  11 +++++
> > >  net/wireless/trace.h         |  18 ++++++++
> > >  5 files changed, 217 insertions(+)
> > 
> > ...
> > 
> > > diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
> > > index 82d5e1e..352eb4a2 100644
> > > --- a/net/wireless/nl80211.c
> > > +++ b/net/wireless/nl80211.c
> > > @@ -280,6 +280,13 @@ static int validate_ie_attr(const struct nlattr
> > > *attr,
> > > 
> > > NLA_POLICY_NESTED_ARRAY(nl80211_psmr_peer_attr_policy),
> > >  };
> > > 
> > > +static const struct nla_policy
> > > +nl80211_attr_tid_config_policy[NL80211_ATTR_TID_CONFIG_MAX + 1] = {
> > > +       [NL80211_ATTR_TID_CONFIG_TID] = NLA_POLICY_MAX(NLA_U8, 7),
> > 
> > Such a policy permits configuration of per-TID settings, either
> > for per-STA or for all the STAs. However it is not possible to
> > perform configuration for all TIDs at once, e.g. passing -1 value
> > to driver.
> > 
> > Maybe simplify policy and use .type = NLA_U8 ?
> > 
> > Sanity check, if needed, can be performed by driver or even by
> > firmware.
> > 
> Sure, we can have like that. And do you feel driver should advertise
> support to perform configuration for all TIDs(like accepting tid -1) ?

Do you think this additional level of granularity is needed ?
IIUC if driver/firmware supports per TID feature configuration,
then can handle all-TIDs configuration as well. Anyway, attempt
for global feature configuration can be rejected on driver
or firmware level if needed.

> > > +       [NL80211_ATTR_TID_CONFIG_NOACK] =
> > > +                       NLA_POLICY_MAX(NLA_U8,
> > > NL80211_TID_CONFIG_DISABLE),
> > > +};
> > > +
> > >  const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
> > >         [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
> > >         [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
> > > @@ -541,6 +548,8 @@ static int validate_ie_attr(const struct nlattr
> > > *attr,
> > >         [NL80211_ATTR_PEER_MEASUREMENTS] =
> > >                 NLA_POLICY_NESTED(nl80211_pmsr_attr_policy),
> > >         [NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
> > > +       [NL80211_ATTR_TID_CONFIG] =
> > > +
> > > NLA_POLICY_NESTED(nl80211_attr_tid_config_policy),
> > >  };
> > 
> > ...
> > 
> > > +static int nl80211_set_tid_config(struct sk_buff *skb,
> > > +                                 struct genl_info *info)
> > > +{
> > > +       struct cfg80211_registered_device *rdev = info->user_ptr[0];
> > > +       struct nlattr *attrs[NL80211_ATTR_TID_CONFIG_MAX + 1];
> > > +       struct net_device *dev = info->user_ptr[1];
> > > +       struct ieee80211_tid_config *tid_conf;
> > > +       struct nlattr *tid;
> > > +       int conf_idx = 0, rem_conf;
> > > +       u32 num_conf = 0, size_of_conf;
> > > +       int ret = -EINVAL;
> > > +
> > > +       if (!info->attrs[NL80211_ATTR_TID_CONFIG])
> > > +               return -EINVAL;
> > > +
> > > +       if (!rdev->ops->set_tid_config)
> > > +               return -EOPNOTSUPP;
> > > +
> > > +       nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG],
> > > +                           rem_conf)
> > > +               num_conf++;
> > > +
> > > +       size_of_conf = sizeof(struct ieee80211_tid_config) +
> > > +               num_conf * sizeof(struct ieee80211_tid_cfg);
> > > +
> > > +       tid_conf = kzalloc(size_of_conf, GFP_KERNEL);
> > > +       if (!tid_conf)
> > > +               return -ENOMEM;
> > > +
> > > +       tid_conf->n_tid_conf = num_conf;
> > > +
> > > +       if (info->attrs[NL80211_ATTR_MAC])
> > > +               tid_conf->peer =
> > > nla_data(info->attrs[NL80211_ATTR_MAC]);
> > > +       else
> > > +               tid_conf->peer = NULL;
> > > +
> > > +       nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG],
> > > +                           rem_conf) {
> > > +               ret = nla_parse_nested(attrs,
> > > NL80211_ATTR_TID_CONFIG_MAX, tid,
> > > +                                      NULL, NULL);
> > > +
> > > +               if (ret)
> > > +                       return ret;
> > > +
> > > +               if (!attrs[NL80211_ATTR_TID_CONFIG_TID])
> > > +                       return -EINVAL;
> > > +
> > > +               ret = parse_tid_conf(rdev, attrs,
> > > &tid_conf->tid_conf[conf_idx],
> > > +                                    tid_conf->peer);
> > > +               if (ret)
> > > +                       goto bad_tid_conf;
> > > +
> > > +               conf_idx++;
> > > +       }
> > > +
> > > +       return rdev_set_tid_config(rdev, dev, tid_conf);
> > 
> > What is the ownership rule for tid_conf ? In other words, who is
> > responsible for freeing this structure ?
> > 
> > Unless I am missing something, in the suggested patches there is no
> > kfree for this structure and all the nested structures (see Tx bitrate
> > patch), neither in cfg80211/mac80211 nor in ath10k.
> > 
> > As far as I understand, we have two options here. One option is to
> > explicitly specify that ownership is passed to the caller, similar
> > to nl80211_set_reg. Another option is to release memory in this
> > function after driver makes copies of all necessary fields,
> > e.g. see nl80211_set_mac_acl.
> > 
> Thanks for pointing out, will free it in this function(cfg80211) itself.
> I will make the change and send next patchset.

Great!

Thanks,
Sergey
diff mbox series

Patch

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index f81677f..07eb2de 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -509,6 +509,35 @@  struct cfg80211_chan_def {
 	u32 center_freq2;
 };
 
+enum ieee80211_tid_conf_mask {
+	IEEE80211_TID_CONF_NOACK	= BIT(0),
+};
+
+/**
+ * struct ieee80211_tid_cfg - TID specific configuration
+ * @tid: TID number
+ * @tid_conf_mask: bitmap indicating which parameter changed
+ *	see %enum ieee80211_tid_conf_mask
+ * @noack: noack configuration value for the TID
+ */
+struct ieee80211_tid_cfg {
+	u8 tid;
+	enum ieee80211_tid_conf_mask tid_conf_mask;
+	u8 noack;
+};
+
+/**
+ * struct ieee80211_tid_config - TID configuration
+ * @peer: Station's MAC address
+ * @n_tid_conf: Number of TID specific configurations to be applied
+ * @tid_conf: Configuration change info
+ */
+struct ieee80211_tid_config {
+	const u8 *peer;
+	u32 n_tid_conf;
+	struct ieee80211_tid_cfg tid_conf[];
+};
+
 /**
  * cfg80211_get_chandef_type - return old channel type from chandef
  * @chandef: the channel definition
@@ -3436,6 +3465,10 @@  struct cfg80211_pmsr_request {
  *	Statistics should be cumulative, currently no way to reset is provided.
  * @start_pmsr: start peer measurement (e.g. FTM)
  * @abort_pmsr: abort peer measurement
+ * @set_tid_config: TID specific configuration. Apply this configuration for
+ *	all the connected stations in the BSS if peer is NULL. Otherwise
+ *	apply this configuration to the specific station.
+ *	This callback may sleep.
  */
 struct cfg80211_ops {
 	int	(*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -3750,6 +3783,8 @@  struct cfg80211_ops {
 			      struct cfg80211_pmsr_request *request);
 	void	(*abort_pmsr)(struct wiphy *wiphy, struct wireless_dev *wdev,
 			      struct cfg80211_pmsr_request *request);
+	int     (*set_tid_config)(struct wiphy *wiphy, struct net_device *dev,
+				  struct ieee80211_tid_config *tid_conf);
 };
 
 /*
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index dd4f86e..c901a48 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1065,6 +1065,10 @@ 
  *	indicated by %NL80211_ATTR_WIPHY_FREQ and other attributes
  *	determining the width and type.
  *
+ * @NL80211_CMD_SET_TID_CONFIG: Data frame TID specific configuration
+ *	is passed through this command using %NL80211_ATTR_TID_CONFIG
+ *	nested attributes.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1285,6 +1289,8 @@  enum nl80211_commands {
 
 	NL80211_CMD_NOTIFY_RADAR,
 
+	NL80211_CMD_SET_TID_CONFIG,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -2308,6 +2314,9 @@  enum nl80211_commands {
  * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime
  *	scheduler.
  *
+ * @NL80211_ATTR_TID_CONFIG: TID specific configuration in a
+ *	nested attribute with %NL80211_ATTR_TID_* sub-attributes.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2759,6 +2768,8 @@  enum nl80211_attrs {
 
 	NL80211_ATTR_AIRTIME_WEIGHT,
 
+	NL80211_ATTR_TID_CONFIG,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -4543,6 +4554,39 @@  enum nl80211_tx_power_setting {
 	NL80211_TX_POWER_FIXED,
 };
 
+enum nl80211_tid_config {
+	NL80211_TID_CONFIG_DEFAULT,
+	NL80211_TID_CONFIG_ENABLE,
+	NL80211_TID_CONFIG_DISABLE,
+};
+
+/* enum nl80211_attr_tid_config - TID specific configuration.
+ * @NL80211_ATTR_TID_CONFIG_TID: a TID value (u8 attribute).
+ * @NL80211_ATTR_TID_CONFIG_NOACK: Configure ack policy for the TID.
+ *	specified in %NL80211_ATTR_TID_CONFIG_TID. see %enum nl80211_tid_config.
+ *	Its type is u8, if the peer MAC address is passed in %NL80211_ATTR_MAC,
+ *	then the noack configuration is applied to the data frame for the tid
+ *	to that connected station. This configuration is valid only for STA's
+ *      current connection. i.e. the configuration will be reset to default when
+ *      the station connects back after disconnection/roaming.
+ *      when user-space does not include %NL80211_ATTR_MAC, then this
+ *	configuration should be treated as per-netdev configuration.
+ *	This configuration will be cleared when the interface goes down and on
+ *	the disconnection from a BSS. Driver supporting this feature should
+ *	advertise NL80211_EXT_FEATURE_PER_TID_NOACK_CONFIG and
+ *	NL80211_EXT_FEATURE_PER_STA_NOACK_CONFIG for supporting  per sta
+ *	configuration.
+ */
+enum nl80211_attr_tid_config {
+	__NL80211_ATTR_TID_INVALID,
+	NL80211_ATTR_TID_CONFIG_TID,
+	NL80211_ATTR_TID_CONFIG_NOACK,
+
+	/* keep last */
+	__NL80211_ATTR_TID_CONFIG_AFTER_LAST,
+	NL80211_ATTR_TID_CONFIG_MAX = __NL80211_ATTR_TID_CONFIG_AFTER_LAST - 1
+};
+
 /**
  * enum nl80211_packet_pattern_attr - packet pattern attribute
  * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
@@ -5343,6 +5387,11 @@  enum nl80211_feature_flags {
  * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
  *	(set/del PMKSA operations) in AP mode.
  *
+ * @NL80211_EXT_FEATURE_PER_TID_NOACK_CONFIG: Driver supports per TID NoAck
+ *	policy functionality.
+ * @NL80211_EXT_FEATURE_PER_STA_NOACK_CONFIG: Driver supports STA specific NoAck
+ *	policy functionality.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -5384,6 +5433,8 @@  enum nl80211_ext_feature_index {
 	NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
 	NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
 	NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
+	NL80211_EXT_FEATURE_PER_TID_NOACK_CONFIG,
+	NL80211_EXT_FEATURE_PER_STA_NOACK_CONFIG,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 82d5e1e..352eb4a2 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -280,6 +280,13 @@  static int validate_ie_attr(const struct nlattr *attr,
 		NLA_POLICY_NESTED_ARRAY(nl80211_psmr_peer_attr_policy),
 };
 
+static const struct nla_policy
+nl80211_attr_tid_config_policy[NL80211_ATTR_TID_CONFIG_MAX + 1] = {
+	[NL80211_ATTR_TID_CONFIG_TID] = NLA_POLICY_MAX(NLA_U8, 7),
+	[NL80211_ATTR_TID_CONFIG_NOACK] =
+			NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE),
+};
+
 const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
 	[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
 	[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
@@ -541,6 +548,8 @@  static int validate_ie_attr(const struct nlattr *attr,
 	[NL80211_ATTR_PEER_MEASUREMENTS] =
 		NLA_POLICY_NESTED(nl80211_pmsr_attr_policy),
 	[NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
+	[NL80211_ATTR_TID_CONFIG] =
+			NLA_POLICY_NESTED(nl80211_attr_tid_config_policy),
 };
 
 /* policy for the key attributes */
@@ -13241,6 +13250,91 @@  static int nl80211_get_ftm_responder_stats(struct sk_buff *skb,
 	return -ENOBUFS;
 }
 
+static int parse_tid_conf(struct cfg80211_registered_device *rdev,
+			  struct nlattr *attrs[],
+			  struct ieee80211_tid_cfg *tid_conf,
+			  const u8 *peer)
+{
+	tid_conf->tid = nla_get_u8(attrs[NL80211_ATTR_TID_CONFIG_TID]);
+	if (attrs[NL80211_ATTR_TID_CONFIG_NOACK]) {
+		if (!wiphy_ext_feature_isset(&rdev->wiphy,
+				NL80211_EXT_FEATURE_PER_TID_NOACK_CONFIG))
+			return -ENOTSUPP;
+
+		if (peer && !wiphy_ext_feature_isset(&rdev->wiphy,
+				NL80211_EXT_FEATURE_PER_STA_NOACK_CONFIG))
+			return -ENOTSUPP;
+
+		tid_conf->tid_conf_mask |= IEEE80211_TID_CONF_NOACK;
+		tid_conf->noack =
+			nla_get_u8(attrs[NL80211_ATTR_TID_CONFIG_NOACK]);
+	}
+
+	return 0;
+}
+
+static int nl80211_set_tid_config(struct sk_buff *skb,
+				  struct genl_info *info)
+{
+	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+	struct nlattr *attrs[NL80211_ATTR_TID_CONFIG_MAX + 1];
+	struct net_device *dev = info->user_ptr[1];
+	struct ieee80211_tid_config *tid_conf;
+	struct nlattr *tid;
+	int conf_idx = 0, rem_conf;
+	u32 num_conf = 0, size_of_conf;
+	int ret = -EINVAL;
+
+	if (!info->attrs[NL80211_ATTR_TID_CONFIG])
+		return -EINVAL;
+
+	if (!rdev->ops->set_tid_config)
+		return -EOPNOTSUPP;
+
+	nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG],
+			    rem_conf)
+		num_conf++;
+
+	size_of_conf = sizeof(struct ieee80211_tid_config) +
+		num_conf * sizeof(struct ieee80211_tid_cfg);
+
+	tid_conf = kzalloc(size_of_conf, GFP_KERNEL);
+	if (!tid_conf)
+		return -ENOMEM;
+
+	tid_conf->n_tid_conf = num_conf;
+
+	if (info->attrs[NL80211_ATTR_MAC])
+		tid_conf->peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
+	else
+		tid_conf->peer = NULL;
+
+	nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG],
+			    rem_conf) {
+		ret = nla_parse_nested(attrs, NL80211_ATTR_TID_CONFIG_MAX, tid,
+				       NULL, NULL);
+
+		if (ret)
+			return ret;
+
+		if (!attrs[NL80211_ATTR_TID_CONFIG_TID])
+			return -EINVAL;
+
+		ret = parse_tid_conf(rdev, attrs, &tid_conf->tid_conf[conf_idx],
+				     tid_conf->peer);
+		if (ret)
+			goto bad_tid_conf;
+
+		conf_idx++;
+	}
+
+	return rdev_set_tid_config(rdev, dev, tid_conf);
+
+bad_tid_conf:
+	kfree(tid_conf);
+	return ret;
+}
+
 #define NL80211_FLAG_NEED_WIPHY		0x01
 #define NL80211_FLAG_NEED_NETDEV	0x02
 #define NL80211_FLAG_NEED_RTNL		0x04
@@ -14175,6 +14269,14 @@  static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
 		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
 				  NL80211_FLAG_NEED_RTNL,
 	},
+	{
+		.cmd = NL80211_CMD_SET_TID_CONFIG,
+		.doit = nl80211_set_tid_config,
+		.policy = nl80211_policy,
+		.flags = GENL_UNS_ADMIN_PERM,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV |
+				  NL80211_FLAG_NEED_RTNL,
+	},
 };
 
 static struct genl_family nl80211_fam __ro_after_init = {
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 5cb48d1..59e1905 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -1272,4 +1272,15 @@  static inline int rdev_del_pmk(struct cfg80211_registered_device *rdev,
 	trace_rdev_return_void(&rdev->wiphy);
 }
 
+static inline int rdev_set_tid_config(struct cfg80211_registered_device *rdev,
+				      struct net_device *dev,
+				      struct ieee80211_tid_config *tid_conf)
+{
+	int ret;
+
+	trace_rdev_set_tid_config(&rdev->wiphy, dev, tid_conf);
+	ret = rdev->ops->set_tid_config(&rdev->wiphy, dev, tid_conf);
+	trace_rdev_return_int(&rdev->wiphy, ret);
+	return ret;
+}
 #endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 44b2ce1..186de1f 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -3362,6 +3362,24 @@ 
 		  WIPHY_PR_ARG, WDEV_PR_ARG,
 		  (unsigned long long)__entry->cookie)
 );
+
+TRACE_EVENT(rdev_set_tid_config,
+	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+		 struct ieee80211_tid_config *tid_conf),
+	TP_ARGS(wiphy, netdev, tid_conf),
+	TP_STRUCT__entry(
+		WIPHY_ENTRY
+		NETDEV_ENTRY
+		MAC_ENTRY(peer)
+	),
+	TP_fast_assign(
+		WIPHY_ASSIGN;
+		NETDEV_ASSIGN;
+		MAC_ASSIGN(peer, tid_conf->peer);
+	),
+	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT,
+		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer))
+);
 #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH