1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(C) 2021 Marvell. 3 */ 4 #include "roc_api.h" 5 #include "roc_priv.h" 6 7 static void 8 npc_prep_mcam_ldata(uint8_t *ptr, const uint8_t *data, int len) 9 { 10 int idx; 11 12 for (idx = 0; idx < len; idx++) 13 ptr[idx] = data[len - 1 - idx]; 14 } 15 16 static int 17 npc_check_copysz(size_t size, size_t len) 18 { 19 if (len <= size) 20 return len; 21 return NPC_ERR_PARAM; 22 } 23 24 static inline int 25 npc_mem_is_zero(const void *mem, int len) 26 { 27 const char *m = mem; 28 int i; 29 30 for (i = 0; i < len; i++) { 31 if (m[i] != 0) 32 return 0; 33 } 34 return 1; 35 } 36 37 static void 38 npc_set_hw_mask(struct npc_parse_item_info *info, struct npc_xtract_info *xinfo, 39 char *hw_mask) 40 { 41 int max_off, offset; 42 int j; 43 44 if (xinfo->enable == 0) 45 return; 46 47 if (xinfo->hdr_off < info->hw_hdr_len) 48 return; 49 50 max_off = xinfo->hdr_off + xinfo->len - info->hw_hdr_len; 51 52 if (max_off > info->len) 53 max_off = info->len; 54 55 offset = xinfo->hdr_off - info->hw_hdr_len; 56 for (j = offset; j < max_off; j++) 57 hw_mask[j] = 0xff; 58 } 59 60 void 61 npc_get_hw_supp_mask(struct npc_parse_state *pst, 62 struct npc_parse_item_info *info, int lid, int lt) 63 { 64 struct npc_xtract_info *xinfo, *lfinfo; 65 char *hw_mask = info->hw_mask; 66 int lf_cfg = 0; 67 int i, j; 68 int intf; 69 70 intf = pst->nix_intf; 71 xinfo = pst->npc->prx_dxcfg[intf][lid][lt].xtract; 72 memset(hw_mask, 0, info->len); 73 74 for (i = 0; i < NPC_MAX_LD; i++) 75 npc_set_hw_mask(info, &xinfo[i], hw_mask); 76 77 for (i = 0; i < NPC_MAX_LD; i++) { 78 if (xinfo[i].flags_enable == 0) 79 continue; 80 81 lf_cfg = pst->npc->prx_lfcfg[i].i; 82 if (lf_cfg == lid) { 83 for (j = 0; j < NPC_MAX_LFL; j++) { 84 lfinfo = pst->npc->prx_fxcfg[intf][i][j].xtract; 85 npc_set_hw_mask(info, &lfinfo[0], hw_mask); 86 } 87 } 88 } 89 } 90 91 static inline int 92 npc_mask_is_supported(const char *mask, const char *hw_mask, int len) 93 { 94 /* 95 * If no hw_mask, assume nothing is supported. 96 * mask is never NULL 97 */ 98 if (hw_mask == NULL) 99 return npc_mem_is_zero(mask, len); 100 101 while (len--) { 102 if ((mask[len] | hw_mask[len]) != hw_mask[len]) 103 return 0; /* False */ 104 } 105 return 1; 106 } 107 108 int 109 npc_parse_item_basic(const struct roc_npc_item_info *item, 110 struct npc_parse_item_info *info) 111 { 112 /* Item must not be NULL */ 113 if (item == NULL) 114 return NPC_ERR_PARAM; 115 116 /* Don't support ranges */ 117 if (item->last != NULL) 118 return NPC_ERR_INVALID_RANGE; 119 120 /* If spec is NULL, both mask and last must be NULL, this 121 * makes it to match ANY value (eq to mask = 0). 122 * Setting either mask or last without spec is an error 123 */ 124 if (item->spec == NULL) { 125 if (item->last == NULL && item->mask == NULL) { 126 info->spec = NULL; 127 return 0; 128 } 129 return NPC_ERR_INVALID_SPEC; 130 } 131 132 /* We have valid spec */ 133 if (item->type != ROC_NPC_ITEM_TYPE_RAW) 134 info->spec = item->spec; 135 136 /* If mask is not set, use default mask, err if default mask is 137 * also NULL. 138 */ 139 if (item->mask == NULL) { 140 if (info->def_mask == NULL) 141 return NPC_ERR_PARAM; 142 info->mask = info->def_mask; 143 } else { 144 if (item->type != ROC_NPC_ITEM_TYPE_RAW) 145 info->mask = item->mask; 146 } 147 148 /* mask specified must be subset of hw supported mask 149 * mask | hw_mask == hw_mask 150 */ 151 if (!npc_mask_is_supported(info->mask, info->hw_mask, info->len)) 152 return NPC_ERR_INVALID_MASK; 153 154 return 0; 155 } 156 157 static int 158 npc_update_extraction_data(struct npc_parse_state *pst, 159 struct npc_parse_item_info *info, 160 struct npc_xtract_info *xinfo) 161 { 162 uint8_t int_info_mask[NPC_MAX_EXTRACT_DATA_LEN]; 163 uint8_t int_info[NPC_MAX_EXTRACT_DATA_LEN]; 164 struct npc_xtract_info *x; 165 int hdr_off; 166 int len = 0; 167 168 x = xinfo; 169 if (x->len > NPC_MAX_EXTRACT_DATA_LEN) 170 return NPC_ERR_INVALID_SIZE; 171 172 len = x->len; 173 hdr_off = x->hdr_off; 174 175 if (hdr_off < info->hw_hdr_len) 176 return 0; 177 178 if (x->enable == 0) 179 return 0; 180 181 hdr_off -= info->hw_hdr_len; 182 183 if (hdr_off >= info->len) 184 return 0; 185 186 if (hdr_off + len > info->len) 187 len = info->len - hdr_off; 188 189 len = npc_check_copysz((ROC_NPC_MAX_MCAM_WIDTH_DWORDS * 8) - x->key_off, 190 len); 191 if (len < 0) 192 return NPC_ERR_INVALID_SIZE; 193 194 /* Need to reverse complete structure so that dest addr is at 195 * MSB so as to program the MCAM using mcam_data & mcam_mask 196 * arrays 197 */ 198 npc_prep_mcam_ldata(int_info, (const uint8_t *)info->spec + hdr_off, 199 x->len); 200 npc_prep_mcam_ldata(int_info_mask, 201 (const uint8_t *)info->mask + hdr_off, x->len); 202 203 memcpy(pst->mcam_mask + x->key_off, int_info_mask, len); 204 memcpy(pst->mcam_data + x->key_off, int_info, len); 205 return 0; 206 } 207 208 int 209 npc_update_parse_state(struct npc_parse_state *pst, 210 struct npc_parse_item_info *info, int lid, int lt, 211 uint8_t flags) 212 { 213 struct npc_lid_lt_xtract_info *xinfo; 214 struct roc_npc_flow_dump_data *dump; 215 struct npc_xtract_info *lfinfo; 216 int intf, lf_cfg; 217 int i, j, rc = 0; 218 219 pst->layer_mask |= lid; 220 pst->lt[lid] = lt; 221 pst->flags[lid] = flags; 222 223 intf = pst->nix_intf; 224 xinfo = &pst->npc->prx_dxcfg[intf][lid][lt]; 225 if (xinfo->is_terminating) 226 pst->terminate = 1; 227 228 if (info->spec == NULL) 229 goto done; 230 231 for (i = 0; i < NPC_MAX_LD; i++) { 232 rc = npc_update_extraction_data(pst, info, &xinfo->xtract[i]); 233 if (rc != 0) 234 return rc; 235 } 236 237 for (i = 0; i < NPC_MAX_LD; i++) { 238 if (xinfo->xtract[i].flags_enable == 0) 239 continue; 240 241 lf_cfg = pst->npc->prx_lfcfg[i].i; 242 if (lf_cfg == lid) { 243 for (j = 0; j < NPC_MAX_LFL; j++) { 244 lfinfo = pst->npc->prx_fxcfg[intf][i][j].xtract; 245 rc = npc_update_extraction_data(pst, info, 246 &lfinfo[0]); 247 if (rc != 0) 248 return rc; 249 250 if (lfinfo[0].enable) 251 pst->flags[lid] = j; 252 } 253 } 254 } 255 256 done: 257 dump = &pst->flow->dump_data[pst->flow->num_patterns++]; 258 dump->lid = lid; 259 dump->ltype = lt; 260 pst->pattern++; 261 return 0; 262 } 263 264 static int 265 npc_initialise_mcam_entry(struct npc *npc, struct roc_npc_flow *flow, 266 int mcam_id) 267 { 268 struct npc_mcam_write_entry_req *req; 269 struct npc_mcam_write_entry_rsq *rsp; 270 int rc = 0, idx; 271 272 req = mbox_alloc_msg_npc_mcam_write_entry(npc->mbox); 273 if (req == NULL) 274 return -ENOSPC; 275 req->set_cntr = 0; 276 req->cntr = 0; 277 req->entry = mcam_id; 278 279 req->intf = (flow->nix_intf == NIX_INTF_RX) ? NPC_MCAM_RX : NPC_MCAM_TX; 280 req->enable_entry = 1; 281 req->entry_data.action = flow->npc_action; 282 req->entry_data.vtag_action = flow->vtag_action; 283 284 for (idx = 0; idx < ROC_NPC_MAX_MCAM_WIDTH_DWORDS; idx++) { 285 req->entry_data.kw[idx] = 0x0; 286 req->entry_data.kw_mask[idx] = 0x0; 287 } 288 289 if (flow->nix_intf == NIX_INTF_RX) { 290 req->entry_data.kw[0] |= (uint64_t)npc->channel; 291 req->entry_data.kw_mask[0] |= (BIT_ULL(12) - 1); 292 } else { 293 uint16_t pf_func = (flow->npc_action >> 4) & 0xffff; 294 295 pf_func = plt_cpu_to_be_16(pf_func); 296 req->entry_data.kw[0] |= ((uint64_t)pf_func << 32); 297 req->entry_data.kw_mask[0] |= ((uint64_t)0xffff << 32); 298 } 299 300 rc = mbox_process_msg(npc->mbox, (void *)&rsp); 301 if (rc != 0) { 302 plt_err("npc: mcam initialisation write failed"); 303 return rc; 304 } 305 return 0; 306 } 307 308 static int 309 npc_shift_mcam_entry(struct mbox *mbox, uint16_t old_ent, uint16_t new_ent) 310 { 311 struct npc_mcam_shift_entry_req *req; 312 struct npc_mcam_shift_entry_rsp *rsp; 313 int rc = -ENOSPC; 314 315 /* Old entry is disabled & it's contents are moved to new_entry, 316 * new entry is enabled finally. 317 */ 318 req = mbox_alloc_msg_npc_mcam_shift_entry(mbox); 319 if (req == NULL) 320 return rc; 321 req->curr_entry[0] = old_ent; 322 req->new_entry[0] = new_ent; 323 req->shift_count = 1; 324 325 rc = mbox_process_msg(mbox, (void *)&rsp); 326 if (rc) 327 return rc; 328 329 return 0; 330 } 331 332 enum SHIFT_DIR { 333 SLIDE_ENTRIES_TO_LOWER_INDEX, 334 SLIDE_ENTRIES_TO_HIGHER_INDEX, 335 }; 336 337 static int 338 npc_slide_mcam_entries(struct mbox *mbox, struct npc *npc, int prio, 339 uint16_t *free_mcam_id, int dir) 340 { 341 uint16_t to_mcam_id = 0, from_mcam_id = 0; 342 struct npc_prio_flow_list_head *list; 343 struct npc_prio_flow_entry *curr = 0; 344 int rc = 0; 345 346 list = &npc->prio_flow_list[prio]; 347 348 to_mcam_id = *free_mcam_id; 349 if (dir == SLIDE_ENTRIES_TO_HIGHER_INDEX) 350 curr = TAILQ_LAST(list, npc_prio_flow_list_head); 351 else if (dir == SLIDE_ENTRIES_TO_LOWER_INDEX) 352 curr = TAILQ_FIRST(list); 353 354 while (curr) { 355 from_mcam_id = curr->flow->mcam_id; 356 if ((dir == SLIDE_ENTRIES_TO_HIGHER_INDEX && 357 from_mcam_id < to_mcam_id) || 358 (dir == SLIDE_ENTRIES_TO_LOWER_INDEX && 359 from_mcam_id > to_mcam_id)) { 360 /* Newly allocated entry and the source entry given to 361 * npc_mcam_shift_entry_req will be in disabled state. 362 * Initialise and enable before moving an entry into 363 * this mcam. 364 */ 365 rc = npc_initialise_mcam_entry(npc, curr->flow, 366 to_mcam_id); 367 if (rc) 368 return rc; 369 rc = npc_shift_mcam_entry(mbox, from_mcam_id, 370 to_mcam_id); 371 if (rc) 372 return rc; 373 curr->flow->mcam_id = to_mcam_id; 374 to_mcam_id = from_mcam_id; 375 } 376 377 if (dir == SLIDE_ENTRIES_TO_HIGHER_INDEX) 378 curr = TAILQ_PREV(curr, npc_prio_flow_list_head, next); 379 else if (dir == SLIDE_ENTRIES_TO_LOWER_INDEX) 380 curr = TAILQ_NEXT(curr, next); 381 } 382 383 *free_mcam_id = from_mcam_id; 384 385 return 0; 386 } 387 388 /* 389 * The mcam_alloc request is first made with NPC_MCAM_LOWER_PRIO with the last 390 * entry in the requested priority level as the reference entry. If it fails, 391 * the alloc request is retried with NPC_MCAM_HIGHER_PRIO with the first entry 392 * in the next lower priority level as the reference entry. After obtaining 393 * the free MCAM from kernel, we check if it is at the right user requested 394 * priority level. If not, the flow rules are moved across MCAM entries till 395 * the user requested priority levels are met. 396 * The MCAM sorting algorithm works as below. 397 * For any given free MCAM obtained from the kernel, there are 3 possibilities. 398 * Case 1: 399 * There are entries belonging to higher user priority level (numerically 400 * lesser) in higher mcam indices. In this case, the entries with higher user 401 * priority are slided towards lower indices and a free entry is created in the 402 * higher indices. 403 * Example: 404 * Assume free entry = 1610, user requested priority = 2 and 405 * max user priority levels = 5 with below entries in respective priority 406 * levels. 407 * 0: 1630, 1635, 1641 408 * 1: 1646, 1650, 1651 409 * 2: 1652, 1655, 1660 410 * 3: 1661, 1662, 1663, 1664 411 * 4: 1665, 1667, 1670 412 * 413 * Entries (1630, 1635, 1641, 1646, 1650, 1651) have to be slided down towards 414 * lower indices. 415 * Shifting sequence will be as below: 416 * 1610 <- 1630 <- 1635 <- 1641 <- 1646 <- 1650 <- 1651 417 * Entry 1651 will be free-ed for writing the new flow. This entry will now 418 * become the head of priority level 2. 419 * 420 * Case 2: 421 * There are entries belonging to lower user priority level (numerically 422 * bigger) in lower mcam indices. In this case, the entries with lower user 423 * priority are slided towards higher indices and a free entry is created in the 424 * lower indices. 425 * 426 * Example: 427 * free entry = 1653, user requested priority = 0 428 * 0: 1630, 1635, 1641 429 * 1: 1646, 1650, 1651 430 * 2: 1652, 1655, 1660 431 * 3: 1661, 1662, 1663, 1664 432 * 4: 1665, 1667, 1670 433 * 434 * Entries (1646, 1650, 1651, 1652) have to be slided up towards higher 435 * indices. 436 * Shifting sequence will be as below: 437 * 1646 -> 1650 -> 1651 -> 1652 -> 1653 438 * Entry 1646 will be free-ed for writing the new flow. This entry will now 439 * become the last element in priority level 0. 440 * 441 * Case 3: 442 * Free mcam is at the right place, ie, all higher user priority level 443 * mcams lie in lower indices and all lower user priority level mcams lie in 444 * higher mcam indices. 445 * 446 * The priority level lists are scanned first for case (1) and if the 447 * condition is found true, case(2) is skipped because they are mutually 448 * exclusive. For example, consider below state. 449 * 0: 1630, 1635, 1641 450 * 1: 1646, 1650, 1651 451 * 2: 1652, 1655, 1660 452 * 3: 1661, 1662, 1663, 1664 453 * 4: 1665, 1667, 1670 454 * free entry = 1610, user requested priority = 2 455 * 456 * Case 1: Here the condition is; 457 * "if (requested_prio > prio_idx && free_mcam < tail->flow->mcam_id ){}" 458 * If this condition is true, it means at some higher priority level than 459 * requested priority level, there are entries at lower indices than the given 460 * free mcam. That is, we have found in levels 0,1 there is an mcam X which is 461 * greater than 1610. 462 * If, for any free entry and user req prio, the above condition is true, then 463 * the below case(2) condition will always be false since the lists are kept 464 * sorted. The case(2) condition is; 465 * "if (requested_prio < prio_idx && free_mcam > head->flow->mcam_id){}" 466 * There can't be entries at lower indices at priority level higher 467 * than the requested priority level. That is, here, at levels 3 & 4 there 468 * cannot be any entry greater than 1610. Because all entries in 3 & 4 must be 469 * greater than X which was found to be greater than 1610 earlier. 470 */ 471 472 static int 473 npc_sort_mcams_by_user_prio_level(struct mbox *mbox, 474 struct npc_prio_flow_entry *flow_list_entry, 475 struct npc *npc, 476 struct npc_mcam_alloc_entry_rsp *rsp) 477 { 478 int requested_prio = flow_list_entry->flow->priority; 479 struct npc_prio_flow_entry *head, *tail; 480 struct npc_prio_flow_list_head *list; 481 uint16_t free_mcam = rsp->entry; 482 bool do_reverse_scan = true; 483 int prio_idx = 0, rc = 0; 484 485 while (prio_idx <= npc->flow_max_priority - 1) { 486 list = &npc->prio_flow_list[prio_idx]; 487 tail = TAILQ_LAST(list, npc_prio_flow_list_head); 488 489 /* requested priority is lower than current level 490 * ie, numerically req prio is higher 491 */ 492 if ((requested_prio > prio_idx) && tail) { 493 /* but there are some mcams in current level 494 * at higher indices, ie, at priority lower 495 * than free_mcam. 496 */ 497 if (free_mcam < tail->flow->mcam_id) { 498 rc = npc_slide_mcam_entries( 499 mbox, npc, prio_idx, &free_mcam, 500 SLIDE_ENTRIES_TO_LOWER_INDEX); 501 if (rc) 502 return rc; 503 do_reverse_scan = false; 504 } 505 } 506 prio_idx++; 507 } 508 509 prio_idx = npc->flow_max_priority - 1; 510 while (prio_idx && do_reverse_scan) { 511 list = &npc->prio_flow_list[prio_idx]; 512 head = TAILQ_FIRST(list); 513 514 /* requested priority is higher than current level 515 * ie, numerically req prio is lower 516 */ 517 if (requested_prio < prio_idx && head) { 518 /* but free mcam is higher than lowest priority 519 * mcam in current level 520 */ 521 if (free_mcam > head->flow->mcam_id) { 522 rc = npc_slide_mcam_entries( 523 mbox, npc, prio_idx, &free_mcam, 524 SLIDE_ENTRIES_TO_HIGHER_INDEX); 525 if (rc) 526 return rc; 527 } 528 } 529 prio_idx--; 530 } 531 rsp->entry = free_mcam; 532 return rc; 533 } 534 535 static void 536 npc_insert_into_flow_list(struct npc *npc, struct npc_prio_flow_entry *entry) 537 { 538 struct npc_prio_flow_list_head *list; 539 struct npc_prio_flow_entry *curr; 540 541 list = &npc->prio_flow_list[entry->flow->priority]; 542 curr = TAILQ_FIRST(list); 543 544 if (curr) { 545 while (curr) { 546 if (entry->flow->mcam_id > curr->flow->mcam_id) 547 curr = TAILQ_NEXT(curr, next); 548 else 549 break; 550 } 551 if (curr) 552 TAILQ_INSERT_BEFORE(curr, entry, next); 553 else 554 TAILQ_INSERT_TAIL(list, entry, next); 555 } else { 556 TAILQ_INSERT_HEAD(list, entry, next); 557 } 558 } 559 560 static int 561 npc_allocate_mcam_entry(struct mbox *mbox, int prio, 562 struct npc_mcam_alloc_entry_rsp *rsp_local, 563 int ref_entry) 564 { 565 struct npc_mcam_alloc_entry_rsp *rsp_cmd; 566 struct npc_mcam_alloc_entry_req *req; 567 struct npc_mcam_alloc_entry_rsp *rsp; 568 int rc = -ENOSPC; 569 570 req = mbox_alloc_msg_npc_mcam_alloc_entry(mbox); 571 if (req == NULL) 572 return rc; 573 req->contig = 1; 574 req->count = 1; 575 req->priority = prio; 576 req->ref_entry = ref_entry; 577 578 rc = mbox_process_msg(mbox, (void *)&rsp_cmd); 579 if (rc) 580 return rc; 581 582 if (!rsp_cmd->count) 583 return -ENOSPC; 584 585 memcpy(rsp_local, rsp_cmd, sizeof(*rsp)); 586 587 return 0; 588 } 589 590 static void 591 npc_find_mcam_ref_entry(struct roc_npc_flow *flow, struct npc *npc, int *prio, 592 int *ref_entry, int dir) 593 { 594 struct npc_prio_flow_entry *head, *tail; 595 struct npc_prio_flow_list_head *list; 596 int prio_idx = flow->priority; 597 598 if (dir == NPC_MCAM_LOWER_PRIO) { 599 while (prio_idx >= 0) { 600 list = &npc->prio_flow_list[prio_idx]; 601 head = TAILQ_FIRST(list); 602 if (head) { 603 *prio = NPC_MCAM_LOWER_PRIO; 604 *ref_entry = head->flow->mcam_id; 605 return; 606 } 607 prio_idx--; 608 } 609 } else if (dir == NPC_MCAM_HIGHER_PRIO) { 610 prio_idx = flow->priority; 611 while (prio_idx <= npc->flow_max_priority - 1) { 612 list = &npc->prio_flow_list[prio_idx]; 613 tail = TAILQ_LAST(list, npc_prio_flow_list_head); 614 if (tail) { 615 *prio = NPC_MCAM_HIGHER_PRIO; 616 *ref_entry = tail->flow->mcam_id; 617 return; 618 } 619 prio_idx++; 620 } 621 } 622 *prio = NPC_MCAM_ANY_PRIO; 623 *ref_entry = 0; 624 } 625 626 static int 627 npc_alloc_mcam_by_ref_entry(struct mbox *mbox, struct roc_npc_flow *flow, 628 struct npc *npc, 629 struct npc_mcam_alloc_entry_rsp *rsp_local) 630 { 631 int prio, ref_entry = 0, rc = 0, dir = NPC_MCAM_LOWER_PRIO; 632 bool retry_done = false; 633 634 retry: 635 npc_find_mcam_ref_entry(flow, npc, &prio, &ref_entry, dir); 636 rc = npc_allocate_mcam_entry(mbox, prio, rsp_local, ref_entry); 637 if (rc && !retry_done) { 638 plt_info( 639 "npc: Failed to allocate lower priority entry. Retrying for higher priority"); 640 641 dir = NPC_MCAM_HIGHER_PRIO; 642 retry_done = true; 643 goto retry; 644 } else if (rc && retry_done) { 645 return rc; 646 } 647 648 return 0; 649 } 650 651 int 652 npc_get_free_mcam_entry(struct mbox *mbox, struct roc_npc_flow *flow, 653 struct npc *npc) 654 { 655 struct npc_mcam_alloc_entry_rsp rsp_local; 656 struct npc_prio_flow_entry *new_entry; 657 int rc = 0; 658 659 rc = npc_alloc_mcam_by_ref_entry(mbox, flow, npc, &rsp_local); 660 661 if (rc) 662 return rc; 663 664 new_entry = plt_zmalloc(sizeof(*new_entry), 0); 665 if (!new_entry) 666 return -ENOSPC; 667 668 new_entry->flow = flow; 669 670 plt_npc_dbg("kernel allocated MCAM entry %d", rsp_local.entry); 671 672 rc = npc_sort_mcams_by_user_prio_level(mbox, new_entry, npc, 673 &rsp_local); 674 if (rc) 675 goto err; 676 677 plt_npc_dbg("allocated MCAM entry after sorting %d", rsp_local.entry); 678 flow->mcam_id = rsp_local.entry; 679 npc_insert_into_flow_list(npc, new_entry); 680 681 return rsp_local.entry; 682 err: 683 plt_free(new_entry); 684 return rc; 685 } 686 687 void 688 npc_delete_prio_list_entry(struct npc *npc, struct roc_npc_flow *flow) 689 { 690 struct npc_prio_flow_list_head *list; 691 struct npc_prio_flow_entry *curr; 692 693 list = &npc->prio_flow_list[flow->priority]; 694 curr = TAILQ_FIRST(list); 695 696 if (!curr) 697 return; 698 699 while (curr) { 700 if (flow->mcam_id == curr->flow->mcam_id) { 701 TAILQ_REMOVE(list, curr, next); 702 plt_free(curr); 703 break; 704 } 705 curr = TAILQ_NEXT(curr, next); 706 } 707 } 708