common/cnxk: support add/delete NIX TM node
[dpdk.git] / drivers / common / cnxk / roc_nix_tm.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2021 Marvell.
3  */
4
5 #include "roc_api.h"
6 #include "roc_priv.h"
7
8 int
9 nix_tm_node_add(struct roc_nix *roc_nix, struct nix_tm_node *node)
10 {
11         struct nix *nix = roc_nix_to_nix_priv(roc_nix);
12         struct nix_tm_shaper_profile *profile;
13         uint32_t node_id, parent_id, lvl;
14         struct nix_tm_node *parent_node;
15         uint32_t priority, profile_id;
16         uint8_t hw_lvl, exp_next_lvl;
17         enum roc_nix_tm_tree tree;
18         int rc;
19
20         node_id = node->id;
21         priority = node->priority;
22         parent_id = node->parent_id;
23         profile_id = node->shaper_profile_id;
24         lvl = node->lvl;
25         tree = node->tree;
26
27         plt_tm_dbg("Add node %s lvl %u id %u, prio 0x%x weight 0x%x "
28                    "parent %u profile 0x%x tree %u",
29                    nix_tm_hwlvl2str(nix_tm_lvl2nix(nix, lvl)), lvl, node_id,
30                    priority, node->weight, parent_id, profile_id, tree);
31
32         if (tree >= ROC_NIX_TM_TREE_MAX)
33                 return NIX_ERR_PARAM;
34
35         /* Translate sw level id's to nix hw level id's */
36         hw_lvl = nix_tm_lvl2nix(nix, lvl);
37         if (hw_lvl == NIX_TXSCH_LVL_CNT && !nix_tm_is_leaf(nix, lvl))
38                 return NIX_ERR_TM_INVALID_LVL;
39
40         /* Leaf nodes have to be same priority */
41         if (nix_tm_is_leaf(nix, lvl) && priority != 0)
42                 return NIX_ERR_TM_INVALID_PRIO;
43
44         parent_node = nix_tm_node_search(nix, parent_id, tree);
45
46         if (node_id < nix->nb_tx_queues)
47                 exp_next_lvl = NIX_TXSCH_LVL_SMQ;
48         else
49                 exp_next_lvl = hw_lvl + 1;
50
51         /* Check if there is no parent node yet */
52         if (hw_lvl != nix->tm_root_lvl &&
53             (!parent_node || parent_node->hw_lvl != exp_next_lvl))
54                 return NIX_ERR_TM_INVALID_PARENT;
55
56         /* Check if a node already exists */
57         if (nix_tm_node_search(nix, node_id, tree))
58                 return NIX_ERR_TM_NODE_EXISTS;
59
60         profile = nix_tm_shaper_profile_search(nix, profile_id);
61         if (!nix_tm_is_leaf(nix, lvl)) {
62                 /* Check if shaper profile exists for non leaf node */
63                 if (!profile && profile_id != ROC_NIX_TM_SHAPER_PROFILE_NONE)
64                         return NIX_ERR_TM_INVALID_SHAPER_PROFILE;
65
66                 /* Packet mode in profile should match with that of tm node */
67                 if (profile && profile->pkt_mode != node->pkt_mode)
68                         return NIX_ERR_TM_PKT_MODE_MISMATCH;
69         }
70
71         /* Check if there is second DWRR already in siblings or holes in prio */
72         rc = nix_tm_validate_prio(nix, lvl, parent_id, priority, tree);
73         if (rc)
74                 return rc;
75
76         if (node->weight > ROC_NIX_TM_MAX_SCHED_WT)
77                 return NIX_ERR_TM_WEIGHT_EXCEED;
78
79         /* Maintain minimum weight */
80         if (!node->weight)
81                 node->weight = 1;
82
83         node->hw_lvl = nix_tm_lvl2nix(nix, lvl);
84         node->rr_prio = 0xF;
85         node->max_prio = UINT32_MAX;
86         node->hw_id = NIX_TM_HW_ID_INVALID;
87         node->flags = 0;
88
89         if (profile)
90                 profile->ref_cnt++;
91
92         node->parent = parent_node;
93         if (parent_node)
94                 parent_node->child_realloc = true;
95         node->parent_hw_id = NIX_TM_HW_ID_INVALID;
96
97         TAILQ_INSERT_TAIL(&nix->trees[tree], node, node);
98         plt_tm_dbg("Added node %s lvl %u id %u (%p)",
99                    nix_tm_hwlvl2str(node->hw_lvl), lvl, node_id, node);
100         return 0;
101 }
102
103 int
104 nix_tm_clear_path_xoff(struct nix *nix, struct nix_tm_node *node)
105 {
106         struct mbox *mbox = (&nix->dev)->mbox;
107         struct nix_txschq_config *req;
108         struct nix_tm_node *p;
109         int rc;
110
111         /* Enable nodes in path for flush to succeed */
112         if (!nix_tm_is_leaf(nix, node->lvl))
113                 p = node;
114         else
115                 p = node->parent;
116         while (p) {
117                 if (!(p->flags & NIX_TM_NODE_ENABLED) &&
118                     (p->flags & NIX_TM_NODE_HWRES)) {
119                         req = mbox_alloc_msg_nix_txschq_cfg(mbox);
120                         req->lvl = p->hw_lvl;
121                         req->num_regs = nix_tm_sw_xoff_prep(p, false, req->reg,
122                                                             req->regval);
123                         rc = mbox_process(mbox);
124                         if (rc)
125                                 return rc;
126
127                         p->flags |= NIX_TM_NODE_ENABLED;
128                 }
129                 p = p->parent;
130         }
131
132         return 0;
133 }
134
135 int
136 nix_tm_smq_xoff(struct nix *nix, struct nix_tm_node *node, bool enable)
137 {
138         struct mbox *mbox = (&nix->dev)->mbox;
139         struct nix_txschq_config *req;
140         uint16_t smq;
141         int rc;
142
143         smq = node->hw_id;
144         plt_tm_dbg("Setting SMQ %u XOFF/FLUSH to %s", smq,
145                    enable ? "enable" : "disable");
146
147         rc = nix_tm_clear_path_xoff(nix, node);
148         if (rc)
149                 return rc;
150
151         req = mbox_alloc_msg_nix_txschq_cfg(mbox);
152         req->lvl = NIX_TXSCH_LVL_SMQ;
153         req->num_regs = 1;
154
155         req->reg[0] = NIX_AF_SMQX_CFG(smq);
156         req->regval[0] = enable ? (BIT_ULL(50) | BIT_ULL(49)) : 0;
157         req->regval_mask[0] =
158                 enable ? ~(BIT_ULL(50) | BIT_ULL(49)) : ~BIT_ULL(50);
159
160         return mbox_process(mbox);
161 }
162
163 int
164 nix_tm_leaf_data_get(struct nix *nix, uint16_t sq, uint32_t *rr_quantum,
165                      uint16_t *smq)
166 {
167         struct nix_tm_node *node;
168         int rc;
169
170         node = nix_tm_node_search(nix, sq, nix->tm_tree);
171
172         /* Check if we found a valid leaf node */
173         if (!node || !nix_tm_is_leaf(nix, node->lvl) || !node->parent ||
174             node->parent->hw_id == NIX_TM_HW_ID_INVALID) {
175                 return -EIO;
176         }
177
178         /* Get SMQ Id of leaf node's parent */
179         *smq = node->parent->hw_id;
180         *rr_quantum = nix_tm_weight_to_rr_quantum(node->weight);
181
182         rc = nix_tm_smq_xoff(nix, node->parent, false);
183         if (rc)
184                 return rc;
185         node->flags |= NIX_TM_NODE_ENABLED;
186         return 0;
187 }
188
189 int
190 roc_nix_tm_sq_flush_spin(struct roc_nix_sq *sq)
191 {
192         struct nix *nix = roc_nix_to_nix_priv(sq->roc_nix);
193         uint16_t sqb_cnt, head_off, tail_off;
194         uint64_t wdata, val, prev;
195         uint16_t qid = sq->qid;
196         int64_t *regaddr;
197         uint64_t timeout; /* 10's of usec */
198
199         /* Wait for enough time based on shaper min rate */
200         timeout = (sq->nb_desc * roc_nix_max_pkt_len(sq->roc_nix) * 8 * 1E5);
201         /* Wait for worst case scenario of this SQ being last priority
202          * and so have to wait for all other SQ's drain out by their own.
203          */
204         timeout = timeout * nix->nb_tx_queues;
205         timeout = timeout / nix->tm_rate_min;
206         if (!timeout)
207                 timeout = 10000;
208
209         wdata = ((uint64_t)qid << 32);
210         regaddr = (int64_t *)(nix->base + NIX_LF_SQ_OP_STATUS);
211         val = roc_atomic64_add_nosync(wdata, regaddr);
212
213         /* Spin multiple iterations as "sq->fc_cache_pkts" can still
214          * have space to send pkts even though fc_mem is disabled
215          */
216
217         while (true) {
218                 prev = val;
219                 plt_delay_us(10);
220                 val = roc_atomic64_add_nosync(wdata, regaddr);
221                 /* Continue on error */
222                 if (val & BIT_ULL(63))
223                         continue;
224
225                 if (prev != val)
226                         continue;
227
228                 sqb_cnt = val & 0xFFFF;
229                 head_off = (val >> 20) & 0x3F;
230                 tail_off = (val >> 28) & 0x3F;
231
232                 /* SQ reached quiescent state */
233                 if (sqb_cnt <= 1 && head_off == tail_off &&
234                     (*(volatile uint64_t *)sq->fc == sq->nb_sqb_bufs)) {
235                         break;
236                 }
237
238                 /* Timeout */
239                 if (!timeout)
240                         goto exit;
241                 timeout--;
242         }
243
244         return 0;
245 exit:
246         roc_nix_queues_ctx_dump(sq->roc_nix);
247         return -EFAULT;
248 }
249
250 /* Flush and disable tx queue and its parent SMQ */
251 int
252 nix_tm_sq_flush_pre(struct roc_nix_sq *sq)
253 {
254         struct roc_nix *roc_nix = sq->roc_nix;
255         struct nix_tm_node *node, *sibling;
256         struct nix_tm_node_list *list;
257         enum roc_nix_tm_tree tree;
258         struct mbox *mbox;
259         struct nix *nix;
260         uint16_t qid;
261         int rc;
262
263         nix = roc_nix_to_nix_priv(roc_nix);
264
265         /* Need not do anything if tree is in disabled state */
266         if (!(nix->tm_flags & NIX_TM_HIERARCHY_ENA))
267                 return 0;
268
269         mbox = (&nix->dev)->mbox;
270         qid = sq->qid;
271
272         tree = nix->tm_tree;
273         list = nix_tm_node_list(nix, tree);
274
275         /* Find the node for this SQ */
276         node = nix_tm_node_search(nix, qid, tree);
277         if (!node || !(node->flags & NIX_TM_NODE_ENABLED)) {
278                 plt_err("Invalid node/state for sq %u", qid);
279                 return -EFAULT;
280         }
281
282         /* Enable CGX RXTX to drain pkts */
283         if (!roc_nix->io_enabled) {
284                 /* Though it enables both RX MCAM Entries and CGX Link
285                  * we assume all the rx queues are stopped way back.
286                  */
287                 mbox_alloc_msg_nix_lf_start_rx(mbox);
288                 rc = mbox_process(mbox);
289                 if (rc) {
290                         plt_err("cgx start failed, rc=%d", rc);
291                         return rc;
292                 }
293         }
294
295         /* Disable smq xoff for case it was enabled earlier */
296         rc = nix_tm_smq_xoff(nix, node->parent, false);
297         if (rc) {
298                 plt_err("Failed to enable smq %u, rc=%d", node->parent->hw_id,
299                         rc);
300                 return rc;
301         }
302
303         /* As per HRM, to disable an SQ, all other SQ's
304          * that feed to same SMQ must be paused before SMQ flush.
305          */
306         TAILQ_FOREACH(sibling, list, node) {
307                 if (sibling->parent != node->parent)
308                         continue;
309                 if (!(sibling->flags & NIX_TM_NODE_ENABLED))
310                         continue;
311
312                 qid = sibling->id;
313                 sq = nix->sqs[qid];
314                 if (!sq)
315                         continue;
316
317                 rc = roc_nix_tm_sq_aura_fc(sq, false);
318                 if (rc) {
319                         plt_err("Failed to disable sqb aura fc, rc=%d", rc);
320                         goto cleanup;
321                 }
322
323                 /* Wait for sq entries to be flushed */
324                 rc = roc_nix_tm_sq_flush_spin(sq);
325                 if (rc) {
326                         plt_err("Failed to drain sq %u, rc=%d\n", sq->qid, rc);
327                         return rc;
328                 }
329         }
330
331         node->flags &= ~NIX_TM_NODE_ENABLED;
332
333         /* Disable and flush */
334         rc = nix_tm_smq_xoff(nix, node->parent, true);
335         if (rc) {
336                 plt_err("Failed to disable smq %u, rc=%d", node->parent->hw_id,
337                         rc);
338                 goto cleanup;
339         }
340 cleanup:
341         /* Restore cgx state */
342         if (!roc_nix->io_enabled) {
343                 mbox_alloc_msg_nix_lf_stop_rx(mbox);
344                 rc |= mbox_process(mbox);
345         }
346
347         return rc;
348 }
349
350 int
351 nix_tm_sq_flush_post(struct roc_nix_sq *sq)
352 {
353         struct roc_nix *roc_nix = sq->roc_nix;
354         struct nix_tm_node *node, *sibling;
355         struct nix_tm_node_list *list;
356         enum roc_nix_tm_tree tree;
357         struct roc_nix_sq *s_sq;
358         bool once = false;
359         uint16_t qid, s_qid;
360         struct nix *nix;
361         int rc;
362
363         nix = roc_nix_to_nix_priv(roc_nix);
364
365         /* Need not do anything if tree is in disabled state */
366         if (!(nix->tm_flags & NIX_TM_HIERARCHY_ENA))
367                 return 0;
368
369         qid = sq->qid;
370         tree = nix->tm_tree;
371         list = nix_tm_node_list(nix, tree);
372
373         /* Find the node for this SQ */
374         node = nix_tm_node_search(nix, qid, tree);
375         if (!node) {
376                 plt_err("Invalid node for sq %u", qid);
377                 return -EFAULT;
378         }
379
380         /* Enable all the siblings back */
381         TAILQ_FOREACH(sibling, list, node) {
382                 if (sibling->parent != node->parent)
383                         continue;
384
385                 if (sibling->id == qid)
386                         continue;
387
388                 if (!(sibling->flags & NIX_TM_NODE_ENABLED))
389                         continue;
390
391                 s_qid = sibling->id;
392                 s_sq = nix->sqs[s_qid];
393                 if (!s_sq)
394                         continue;
395
396                 if (!once) {
397                         /* Enable back if any SQ is still present */
398                         rc = nix_tm_smq_xoff(nix, node->parent, false);
399                         if (rc) {
400                                 plt_err("Failed to enable smq %u, rc=%d",
401                                         node->parent->hw_id, rc);
402                                 return rc;
403                         }
404                         once = true;
405                 }
406
407                 rc = roc_nix_tm_sq_aura_fc(s_sq, true);
408                 if (rc) {
409                         plt_err("Failed to enable sqb aura fc, rc=%d", rc);
410                         return rc;
411                 }
412         }
413
414         return 0;
415 }
416
417 int
418 nix_tm_free_node_resource(struct nix *nix, struct nix_tm_node *node)
419 {
420         struct mbox *mbox = (&nix->dev)->mbox;
421         struct nix_txsch_free_req *req;
422         struct plt_bitmap *bmp;
423         uint16_t avail, hw_id;
424         uint8_t hw_lvl;
425         int rc = -ENOSPC;
426
427         hw_lvl = node->hw_lvl;
428         hw_id = node->hw_id;
429         bmp = nix->schq_bmp[hw_lvl];
430         /* Free specific HW resource */
431         plt_tm_dbg("Free hwres %s(%u) lvl %u id %u (%p)",
432                    nix_tm_hwlvl2str(node->hw_lvl), hw_id, node->lvl, node->id,
433                    node);
434
435         avail = nix_tm_resource_avail(nix, hw_lvl, false);
436         /* Always for now free to discontiguous queue when avail
437          * is not sufficient.
438          */
439         if (nix->discontig_rsvd[hw_lvl] &&
440             avail < nix->discontig_rsvd[hw_lvl]) {
441                 PLT_ASSERT(hw_id < NIX_TM_MAX_HW_TXSCHQ);
442                 PLT_ASSERT(plt_bitmap_get(bmp, hw_id) == 0);
443                 plt_bitmap_set(bmp, hw_id);
444                 node->hw_id = NIX_TM_HW_ID_INVALID;
445                 node->flags &= ~NIX_TM_NODE_HWRES;
446                 return 0;
447         }
448
449         /* Free to AF */
450         req = mbox_alloc_msg_nix_txsch_free(mbox);
451         if (req == NULL)
452                 return rc;
453         req->flags = 0;
454         req->schq_lvl = node->hw_lvl;
455         req->schq = hw_id;
456         rc = mbox_process(mbox);
457         if (rc) {
458                 plt_err("failed to release hwres %s(%u) rc %d",
459                         nix_tm_hwlvl2str(node->hw_lvl), hw_id, rc);
460                 return rc;
461         }
462
463         /* Mark parent as dirty for reallocing it's children */
464         if (node->parent)
465                 node->parent->child_realloc = true;
466
467         node->hw_id = NIX_TM_HW_ID_INVALID;
468         node->flags &= ~NIX_TM_NODE_HWRES;
469         plt_tm_dbg("Released hwres %s(%u) to af",
470                    nix_tm_hwlvl2str(node->hw_lvl), hw_id);
471         return 0;
472 }
473
474 int
475 nix_tm_node_delete(struct roc_nix *roc_nix, uint32_t node_id,
476                    enum roc_nix_tm_tree tree, bool free)
477 {
478         struct nix *nix = roc_nix_to_nix_priv(roc_nix);
479         struct nix_tm_shaper_profile *profile;
480         struct nix_tm_node *node, *child;
481         struct nix_tm_node_list *list;
482         uint32_t profile_id;
483         int rc;
484
485         plt_tm_dbg("Delete node id %u tree %u", node_id, tree);
486
487         node = nix_tm_node_search(nix, node_id, tree);
488         if (!node)
489                 return NIX_ERR_TM_INVALID_NODE;
490
491         list = nix_tm_node_list(nix, tree);
492         /* Check for any existing children */
493         TAILQ_FOREACH(child, list, node) {
494                 if (child->parent == node)
495                         return NIX_ERR_TM_CHILD_EXISTS;
496         }
497
498         /* Remove shaper profile reference */
499         profile_id = node->shaper_profile_id;
500         profile = nix_tm_shaper_profile_search(nix, profile_id);
501
502         /* Free hw resource locally */
503         if (node->flags & NIX_TM_NODE_HWRES) {
504                 rc = nix_tm_free_node_resource(nix, node);
505                 if (rc)
506                         return rc;
507         }
508
509         if (profile)
510                 profile->ref_cnt--;
511
512         TAILQ_REMOVE(list, node, node);
513
514         plt_tm_dbg("Deleted node %s lvl %u id %u, prio 0x%x weight 0x%x "
515                    "parent %u profile 0x%x tree %u (%p)",
516                    nix_tm_hwlvl2str(node->hw_lvl), node->lvl, node->id,
517                    node->priority, node->weight,
518                    node->parent ? node->parent->id : UINT32_MAX,
519                    node->shaper_profile_id, tree, node);
520         /* Free only if requested */
521         if (free)
522                 nix_tm_node_free(node);
523         return 0;
524 }
525
526 int
527 nix_tm_conf_init(struct roc_nix *roc_nix)
528 {
529         struct nix *nix = roc_nix_to_nix_priv(roc_nix);
530         uint32_t bmp_sz, hw_lvl;
531         void *bmp_mem;
532         int rc, i;
533
534         PLT_STATIC_ASSERT(sizeof(struct nix_tm_node) <= ROC_NIX_TM_NODE_SZ);
535
536         nix->tm_flags = 0;
537         for (i = 0; i < ROC_NIX_TM_TREE_MAX; i++)
538                 TAILQ_INIT(&nix->trees[i]);
539
540         TAILQ_INIT(&nix->shaper_profile_list);
541         nix->tm_rate_min = 1E9; /* 1Gbps */
542
543         rc = -ENOMEM;
544         bmp_sz = plt_bitmap_get_memory_footprint(NIX_TM_MAX_HW_TXSCHQ);
545         bmp_mem = plt_zmalloc(bmp_sz * NIX_TXSCH_LVL_CNT * 2, 0);
546         if (!bmp_mem)
547                 return rc;
548         nix->schq_bmp_mem = bmp_mem;
549
550         /* Init contiguous and discontiguous bitmap per lvl */
551         rc = -EIO;
552         for (hw_lvl = 0; hw_lvl < NIX_TXSCH_LVL_CNT; hw_lvl++) {
553                 /* Bitmap for discontiguous resource */
554                 nix->schq_bmp[hw_lvl] =
555                         plt_bitmap_init(NIX_TM_MAX_HW_TXSCHQ, bmp_mem, bmp_sz);
556                 if (!nix->schq_bmp[hw_lvl])
557                         goto exit;
558
559                 bmp_mem = PLT_PTR_ADD(bmp_mem, bmp_sz);
560
561                 /* Bitmap for contiguous resource */
562                 nix->schq_contig_bmp[hw_lvl] =
563                         plt_bitmap_init(NIX_TM_MAX_HW_TXSCHQ, bmp_mem, bmp_sz);
564                 if (!nix->schq_contig_bmp[hw_lvl])
565                         goto exit;
566
567                 bmp_mem = PLT_PTR_ADD(bmp_mem, bmp_sz);
568         }
569
570         /* Disable TL1 Static Priority when VF's are enabled
571          * as otherwise VF's TL2 reallocation will be needed
572          * runtime to support a specific topology of PF.
573          */
574         if (nix->pci_dev->max_vfs)
575                 nix->tm_flags |= NIX_TM_TL1_NO_SP;
576
577         /* TL1 access is only for PF's */
578         if (roc_nix_is_pf(roc_nix)) {
579                 nix->tm_flags |= NIX_TM_TL1_ACCESS;
580                 nix->tm_root_lvl = NIX_TXSCH_LVL_TL1;
581         } else {
582                 nix->tm_root_lvl = NIX_TXSCH_LVL_TL2;
583         }
584
585         return 0;
586 exit:
587         nix_tm_conf_fini(roc_nix);
588         return rc;
589 }
590
591 void
592 nix_tm_conf_fini(struct roc_nix *roc_nix)
593 {
594         struct nix *nix = roc_nix_to_nix_priv(roc_nix);
595         uint16_t hw_lvl;
596
597         for (hw_lvl = 0; hw_lvl < NIX_TXSCH_LVL_CNT; hw_lvl++) {
598                 plt_bitmap_free(nix->schq_bmp[hw_lvl]);
599                 plt_bitmap_free(nix->schq_contig_bmp[hw_lvl]);
600         }
601         plt_free(nix->schq_bmp_mem);
602 }