HTB offload programs NIX_AF_TLxX_TOPOLOGY on QoS-allocated scheduler queues via otx2_qos_txschq_set_parent_topology(), but teardown freed those queues without clearing TOPOLOGY. The AF only restores PARENT and SCHEDULE on free, so PRIO_ANCHOR/RR_PRIO settings can survive in the shared scheduler pool and affect later allocations. Add otx2_qos_reset_schq_topology() and otx2_qos_free_hw_schq() to zero TL4 through TL2 TOPOLOGY before each schq is returned to the AF during hierarchy teardown and cfg rollback. Skip the aggregation level (TL1): it is a per-tx-link queue shared by the PF, default Tx hierarchy and VFs, and is not freed back to the AF by nix_txschq_free_one().
Unified · Split
| @@ -235,13 +235,63 @@ static int otx2_qos_txschq_set_parent_topology(struct otx2_nic *pfvf, · 234 unmodified lines | |||
| 235 | return rc; | 235 | return rc; |
| 236 | } | 236 | } |
| 237 | 237 | ||
| 238 | static int otx2_qos_reset_schq_topology(struct otx2_nic *pfvf, u16 lvl, | ||
| 239 | u16 schq) | ||
| 240 | { | ||
| 241 | struct mbox *mbox = &pfvf->mbox; | ||
| 242 | struct nix_txschq_config *cfg; | ||
| 243 | int rc; | ||
| 244 | |||
| 245 | if (lvl < NIX_TXSCH_LVL_TL4 || lvl >= NIX_TXSCH_LVL_TL1) | ||
| 246 | return 0; | ||
| 247 | |||
| 248 | mutex_lock(&mbox->lock); | ||
| 249 | |||
| 250 | cfg = otx2_mbox_alloc_msg_nix_txschq_cfg(mbox); | ||
| 251 | if (!cfg) { | ||
| 252 | mutex_unlock(&mbox->lock); | ||
| 253 | return -ENOMEM; | ||
| 254 | } | ||
| 255 | |||
| 256 | cfg->lvl = lvl; | ||
| 257 | cfg->num_regs = 1; | ||
| 258 | |||
| 259 | if (lvl == NIX_TXSCH_LVL_TL4) | ||
| 260 | cfg->reg[0] = NIX_AF_TL4X_TOPOLOGY(schq); | ||
| 261 | else if (lvl == NIX_TXSCH_LVL_TL3) | ||
| 262 | cfg->reg[0] = NIX_AF_TL3X_TOPOLOGY(schq); | ||
| 263 | else if (lvl == NIX_TXSCH_LVL_TL2) | ||
| 264 | cfg->reg[0] = NIX_AF_TL2X_TOPOLOGY(schq); | ||
| 265 | |||
| 266 | cfg->regval[0] = 0; | ||
| 267 | |||
| 268 | rc = otx2_sync_mbox_msg(mbox); | ||
| 269 | |||
| 270 | mutex_unlock(&mbox->lock); | ||
| 271 | |||
| 272 | return rc; | ||
| 273 | } | ||
| 274 | |||
| 275 | static void otx2_qos_free_hw_schq(struct otx2_nic *pfvf, u16 lvl, u16 schq) | ||
| 276 | { | ||
| 277 | int err; | ||
| 278 | |||
| 279 | err = otx2_qos_reset_schq_topology(pfvf, lvl, schq); | ||
| 280 | if (err) | ||
| 281 | netdev_warn(pfvf->netdev, | ||
| 282 | "QoS: failed to reset topology for schq %u at level %u: %d\n", | ||
| 283 | schq, lvl, err); | ||
| 284 | |||
| 285 | otx2_txschq_free_one(pfvf, lvl, schq); | ||
| 286 | } | ||
| 287 | |||
| 238 | static void otx2_qos_free_hw_node_schq(struct otx2_nic *pfvf, | 288 | static void otx2_qos_free_hw_node_schq(struct otx2_nic *pfvf, |
| 239 | struct otx2_qos_node *parent) | 289 | struct otx2_qos_node *parent) |
| 240 | { | 290 | { |
| 241 | struct otx2_qos_node *node; | 291 | struct otx2_qos_node *node; |
| 242 | 292 | ||
| 243 | list_for_each_entry_reverse(node, &parent->child_schq_list, list) | 293 | list_for_each_entry_reverse(node, &parent->child_schq_list, list) |
| 244 | otx2_txschq_free_one(pfvf, node->level, node->schq); | 294 | otx2_qos_free_hw_schq(pfvf, node->level, node->schq); |
| 245 | } | 295 | } |
| 246 | 296 | ||
| 247 | static void otx2_qos_free_hw_node(struct otx2_nic *pfvf, | 297 | static void otx2_qos_free_hw_node(struct otx2_nic *pfvf, |
| @@ -252,7 +302,7 @@ static void otx2_qos_free_hw_node(struct otx2_nic *pfvf, · 4 unmodified lines | |||
| 252 | list_for_each_entry_safe(node, tmp, &parent->child_list, list) { | 302 | list_for_each_entry_safe(node, tmp, &parent->child_list, list) { |
| 253 | otx2_qos_free_hw_node(pfvf, node); | 303 | otx2_qos_free_hw_node(pfvf, node); |
| 254 | otx2_qos_free_hw_node_schq(pfvf, node); | 304 | otx2_qos_free_hw_node_schq(pfvf, node); |
| 255 | otx2_txschq_free_one(pfvf, node->level, node->schq); | 305 | otx2_qos_free_hw_schq(pfvf, node->level, node->schq); |
| 256 | } | 306 | } |
| 257 | } | 307 | } |
| 258 | 308 | ||
| @@ -266,7 +316,7 @@ static void otx2_qos_free_hw_cfg(struct otx2_nic *pfvf, · 7 unmodified lines | |||
| 266 | otx2_qos_free_hw_node_schq(pfvf, node); | 316 | otx2_qos_free_hw_node_schq(pfvf, node); |
| 267 | 317 | ||
| 268 | /* free node hw mappings */ | 318 | /* free node hw mappings */ |
| 269 | otx2_txschq_free_one(pfvf, node->level, node->schq); | 319 | otx2_qos_free_hw_schq(pfvf, node->level, node->schq); |
| 270 | 320 | ||
| 271 | mutex_unlock(&pfvf->qos.qos_lock); | 321 | mutex_unlock(&pfvf->qos.qos_lock); |
| 272 | } | 322 | } |
| @@ -913,7 +963,7 @@ static void otx2_qos_free_cfg(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg) · 640 unmodified lines | |||
| 913 | for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { | 963 | for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { |
| 914 | for (idx = 0; idx < cfg->schq[lvl]; idx++) { | 964 | for (idx = 0; idx < cfg->schq[lvl]; idx++) { |
| 915 | schq = cfg->schq_list[lvl][idx]; | 965 | schq = cfg->schq_list[lvl][idx]; |
| 916 | otx2_txschq_free_one(pfvf, lvl, schq); | 966 | otx2_qos_free_hw_schq(pfvf, lvl, schq); |
| 917 | } | 967 | } |
| 918 | } | 968 | } |
| 919 | 969 | ||
| @@ -921,7 +971,7 @@ static void otx2_qos_free_cfg(struct otx2_nic *pfvf, struct otx2_qos_cfg *cfg) · 1 unmodified line | |||
| 921 | for (idx = 0; idx < cfg->schq_contig[lvl]; idx++) { | 971 | for (idx = 0; idx < cfg->schq_contig[lvl]; idx++) { |
| 922 | if (cfg->schq_index_used[lvl][idx]) { | 972 | if (cfg->schq_index_used[lvl][idx]) { |
| 923 | schq = cfg->schq_contig_list[lvl][idx]; | 973 | schq = cfg->schq_contig_list[lvl][idx]; |
| 924 | otx2_txschq_free_one(pfvf, lvl, schq); | 974 | otx2_qos_free_hw_schq(pfvf, lvl, schq); |
| 925 | } | 975 | } |
| 926 | } | 976 | } |
| 927 | } | 977 | } |