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 len = x->len; 170 hdr_off = x->hdr_off; 171 172 if (hdr_off < info->hw_hdr_len) 173 return 0; 174 175 if (x->enable == 0) 176 return 0; 177 178 hdr_off -= info->hw_hdr_len; 179 180 if (hdr_off >= info->len) 181 return 0; 182 183 if (hdr_off + len > info->len) 184 len = info->len - hdr_off; 185 186 len = npc_check_copysz((ROC_NPC_MAX_MCAM_WIDTH_DWORDS * 8) - x->key_off, 187 len); 188 if (len < 0) 189 return NPC_ERR_INVALID_SIZE; 190 191 /* Need to reverse complete structure so that dest addr is at 192 * MSB so as to program the MCAM using mcam_data & mcam_mask 193 * arrays 194 */ 195 npc_prep_mcam_ldata(int_info, (const uint8_t *)info->spec + hdr_off, 196 x->len); 197 npc_prep_mcam_ldata(int_info_mask, 198 (const uint8_t *)info->mask + hdr_off, x->len); 199 200 memcpy(pst->mcam_mask + x->key_off, int_info_mask, len); 201 memcpy(pst->mcam_data + x->key_off, int_info, len); 202 return 0; 203 } 204 205 int 206 npc_update_parse_state(struct npc_parse_state *pst, 207 struct npc_parse_item_info *info, int lid, int lt, 208 uint8_t flags) 209 { 210 struct npc_lid_lt_xtract_info *xinfo; 211 struct roc_npc_flow_dump_data *dump; 212 struct npc_xtract_info *lfinfo; 213 int intf, lf_cfg; 214 int i, j, rc = 0; 215 216 pst->layer_mask |= lid; 217 pst->lt[lid] = lt; 218 pst->flags[lid] = flags; 219 220 intf = pst->nix_intf; 221 xinfo = &pst->npc->prx_dxcfg[intf][lid][lt]; 222 if (xinfo->is_terminating) 223 pst->terminate = 1; 224 225 if (info->spec == NULL) 226 goto done; 227 228 for (i = 0; i < NPC_MAX_LD; i++) { 229 rc = npc_update_extraction_data(pst, info, &xinfo->xtract[i]); 230 if (rc != 0) 231 return rc; 232 } 233 234 for (i = 0; i < NPC_MAX_LD; i++) { 235 if (xinfo->xtract[i].flags_enable == 0) 236 continue; 237 238 lf_cfg = pst->npc->prx_lfcfg[i].i; 239 if (lf_cfg == lid) { 240 for (j = 0; j < NPC_MAX_LFL; j++) { 241 lfinfo = pst->npc->prx_fxcfg[intf][i][j].xtract; 242 rc = npc_update_extraction_data(pst, info, 243 &lfinfo[0]); 244 if (rc != 0) 245 return rc; 246 247 if (lfinfo[0].enable) 248 pst->flags[lid] = j; 249 } 250 } 251 } 252 253 done: 254 dump = &pst->flow->dump_data[pst->flow->num_patterns++]; 255 dump->lid = lid; 256 dump->ltype = lt; 257 pst->pattern++; 258 return 0; 259 } 260 261 static int 262 npc_initialise_mcam_entry(struct npc *npc, struct roc_npc_flow *flow, 263 int mcam_id) 264 { 265 struct npc_mcam_write_entry_req *req; 266 struct npc_mcam_write_entry_rsq *rsp; 267 int rc = 0, idx; 268 269 req = mbox_alloc_msg_npc_mcam_write_entry(npc->mbox); 270 if (req == NULL) 271 return -ENOSPC; 272 req->set_cntr = 0; 273 req->cntr = 0; 274 req->entry = mcam_id; 275 276 req->intf = (flow->nix_intf == NIX_INTF_RX) ? NPC_MCAM_RX : NPC_MCAM_TX; 277 req->enable_entry = 1; 278 req->entry_data.action = flow->npc_action; 279 req->entry_data.vtag_action = flow->vtag_action; 280 281 for (idx = 0; idx < ROC_NPC_MAX_MCAM_WIDTH_DWORDS; idx++) { 282 req->entry_data.kw[idx] = 0x0; 283 req->entry_data.kw_mask[idx] = 0x0; 284 } 285 286 if (flow->nix_intf == NIX_INTF_RX) { 287 req->entry_data.kw[0] |= (uint64_t)npc->channel; 288 req->entry_data.kw_mask[0] |= (BIT_ULL(12) - 1); 289 } else { 290 uint16_t pf_func = (flow->npc_action >> 4) & 0xffff; 291 292 pf_func = plt_cpu_to_be_16(pf_func); 293 req->entry_data.kw[0] |= ((uint64_t)pf_func << 32); 294 req->entry_data.kw_mask[0] |= ((uint64_t)0xffff << 32); 295 } 296 297 rc = mbox_process_msg(npc->mbox, (void *)&rsp); 298 if (rc != 0) { 299 plt_err("npc: mcam initialisation write failed"); 300 return rc; 301 } 302 return 0; 303 } 304 305 static int 306 npc_shift_mcam_entry(struct mbox *mbox, uint16_t old_ent, uint16_t new_ent) 307 { 308 struct npc_mcam_shift_entry_req *req; 309 struct npc_mcam_shift_entry_rsp *rsp; 310 int rc = -ENOSPC; 311 312 /* Old entry is disabled & it's contents are moved to new_entry, 313 * new entry is enabled finally. 314 */ 315 req = mbox_alloc_msg_npc_mcam_shift_entry(mbox); 316 if (req == NULL) 317 return rc; 318 req->curr_entry[0] = old_ent; 319 req->new_entry[0] = new_ent; 320 req->shift_count = 1; 321 322 rc = mbox_process_msg(mbox, (void *)&rsp); 323 if (rc) 324 return rc; 325 326 return 0; 327 } 328 329 enum SHIFT_DIR { 330 SLIDE_ENTRIES_TO_LOWER_INDEX, 331 SLIDE_ENTRIES_TO_HIGHER_INDEX, 332 }; 333 334 static int 335 npc_slide_mcam_entries(struct mbox *mbox, struct npc *npc, int prio, 336 uint16_t *free_mcam_id, int dir) 337 { 338 uint16_t to_mcam_id = 0, from_mcam_id = 0; 339 struct npc_prio_flow_list_head *list; 340 struct npc_prio_flow_entry *curr = 0; 341 int rc = 0; 342 343 list = &npc->prio_flow_list[prio]; 344 345 to_mcam_id = *free_mcam_id; 346 if (dir == SLIDE_ENTRIES_TO_HIGHER_INDEX) 347 curr = TAILQ_LAST(list, npc_prio_flow_list_head); 348 else if (dir == SLIDE_ENTRIES_TO_LOWER_INDEX) 349 curr = TAILQ_FIRST(list); 350 351 while (curr) { 352 from_mcam_id = curr->flow->mcam_id; 353 if ((dir == SLIDE_ENTRIES_TO_HIGHER_INDEX && 354 from_mcam_id < to_mcam_id) || 355 (dir == SLIDE_ENTRIES_TO_LOWER_INDEX && 356 from_mcam_id > to_mcam_id)) { 357 /* Newly allocated entry and the source entry given to 358 * npc_mcam_shift_entry_req will be in disabled state. 359 * Initialise and enable before moving an entry into 360 * this mcam. 361 */ 362 rc = npc_initialise_mcam_entry(npc, curr->flow, 363 to_mcam_id); 364 if (rc) 365 return rc; 366 rc = npc_shift_mcam_entry(mbox, from_mcam_id, 367 to_mcam_id); 368 if (rc) 369 return rc; 370 curr->flow->mcam_id = to_mcam_id; 371 to_mcam_id = from_mcam_id; 372 } 373 374 if (dir == SLIDE_ENTRIES_TO_HIGHER_INDEX) 375 curr = TAILQ_PREV(curr, npc_prio_flow_list_head, next); 376 else if (dir == SLIDE_ENTRIES_TO_LOWER_INDEX) 377 curr = TAILQ_NEXT(curr, next); 378 } 379 380 *free_mcam_id = from_mcam_id; 381 382 return 0; 383 } 384 385 /* 386 * The mcam_alloc request is first made with NPC_MCAM_LOWER_PRIO with the last 387 * entry in the requested priority level as the reference entry. If it fails, 388 * the alloc request is retried with NPC_MCAM_HIGHER_PRIO with the first entry 389 * in the next lower priority level as the reference entry. After obtaining 390 * the free MCAM from kernel, we check if it is at the right user requested 391 * priority level. If not, the flow rules are moved across MCAM entries till 392 * the user requested priority levels are met. 393 * The MCAM sorting algorithm works as below. 394 * For any given free MCAM obtained from the kernel, there are 3 possibilities. 395 * Case 1: 396 * There are entries belonging to higher user priority level (numerically 397 * lesser) in higher mcam indices. In this case, the entries with higher user 398 * priority are slided towards lower indices and a free entry is created in the 399 * higher indices. 400 * Example: 401 * Assume free entry = 1610, user requested priority = 2 and 402 * max user priority levels = 5 with below entries in respective priority 403 * levels. 404 * 0: 1630, 1635, 1641 405 * 1: 1646, 1650, 1651 406 * 2: 1652, 1655, 1660 407 * 3: 1661, 1662, 1663, 1664 408 * 4: 1665, 1667, 1670 409 * 410 * Entries (1630, 1635, 1641, 1646, 1650, 1651) have to be slided down towards 411 * lower indices. 412 * Shifting sequence will be as below: 413 * 1610 <- 1630 <- 1635 <- 1641 <- 1646 <- 1650 <- 1651 414 * Entry 1651 will be free-ed for writing the new flow. This entry will now 415 * become the head of priority level 2. 416 * 417 * Case 2: 418 * There are entries belonging to lower user priority level (numerically 419 * bigger) in lower mcam indices. In this case, the entries with lower user 420 * priority are slided towards higher indices and a free entry is created in the 421 * lower indices. 422 * 423 * Example: 424 * free entry = 1653, user requested priority = 0 425 * 0: 1630, 1635, 1641 426 * 1: 1646, 1650, 1651 427 * 2: 1652, 1655, 1660 428 * 3: 1661, 1662, 1663, 1664 429 * 4: 1665, 1667, 1670 430 * 431 * Entries (1646, 1650, 1651, 1652) have to be slided up towards higher 432 * indices. 433 * Shifting sequence will be as below: 434 * 1646 -> 1650 -> 1651 -> 1652 -> 1653 435 * Entry 1646 will be free-ed for writing the new flow. This entry will now 436 * become the last element in priority level 0. 437 * 438 * Case 3: 439 * Free mcam is at the right place, ie, all higher user priority level 440 * mcams lie in lower indices and all lower user priority level mcams lie in 441 * higher mcam indices. 442 * 443 * The priority level lists are scanned first for case (1) and if the 444 * condition is found true, case(2) is skipped because they are mutually 445 * exclusive. For example, consider below state. 446 * 0: 1630, 1635, 1641 447 * 1: 1646, 1650, 1651 448 * 2: 1652, 1655, 1660 449 * 3: 1661, 1662, 1663, 1664 450 * 4: 1665, 1667, 1670 451 * free entry = 1610, user requested priority = 2 452 * 453 * Case 1: Here the condition is; 454 * "if (requested_prio > prio_idx && free_mcam < tail->flow->mcam_id ){}" 455 * If this condition is true, it means at some higher priority level than 456 * requested priority level, there are entries at lower indices than the given 457 * free mcam. That is, we have found in levels 0,1 there is an mcam X which is 458 * greater than 1610. 459 * If, for any free entry and user req prio, the above condition is true, then 460 * the below case(2) condition will always be false since the lists are kept 461 * sorted. The case(2) condition is; 462 * "if (requested_prio < prio_idx && free_mcam > head->flow->mcam_id){}" 463 * There can't be entries at lower indices at priority level higher 464 * than the requested priority level. That is, here, at levels 3 & 4 there 465 * cannot be any entry greater than 1610. Because all entries in 3 & 4 must be 466 * greater than X which was found to be greater than 1610 earlier. 467 */ 468 469 static int 470 npc_sort_mcams_by_user_prio_level(struct mbox *mbox, 471 struct npc_prio_flow_entry *flow_list_entry, 472 struct npc *npc, 473 struct npc_mcam_alloc_entry_rsp *rsp) 474 { 475 int requested_prio = flow_list_entry->flow->priority; 476 struct npc_prio_flow_entry *head, *tail; 477 struct npc_prio_flow_list_head *list; 478 uint16_t free_mcam = rsp->entry; 479 bool do_reverse_scan = true; 480 int prio_idx = 0, rc = 0; 481 482 while (prio_idx <= npc->flow_max_priority - 1) { 483 list = &npc->prio_flow_list[prio_idx]; 484 tail = TAILQ_LAST(list, npc_prio_flow_list_head); 485 486 /* requested priority is lower than current level 487 * ie, numerically req prio is higher 488 */ 489 if ((requested_prio > prio_idx) && tail) { 490 /* but there are some mcams in current level 491 * at higher indices, ie, at priority lower 492 * than free_mcam. 493 */ 494 if (free_mcam < tail->flow->mcam_id) { 495 rc = npc_slide_mcam_entries( 496 mbox, npc, prio_idx, &free_mcam, 497 SLIDE_ENTRIES_TO_LOWER_INDEX); 498 if (rc) 499 return rc; 500 do_reverse_scan = false; 501 } 502 } 503 prio_idx++; 504 } 505 506 prio_idx = npc->flow_max_priority - 1; 507 while (prio_idx && do_reverse_scan) { 508 list = &npc->prio_flow_list[prio_idx]; 509 head = TAILQ_FIRST(list); 510 511 /* requested priority is higher than current level 512 * ie, numerically req prio is lower 513 */ 514 if (requested_prio < prio_idx && head) { 515 /* but free mcam is higher than lowest priority 516 * mcam in current level 517 */ 518 if (free_mcam > head->flow->mcam_id) { 519 rc = npc_slide_mcam_entries( 520 mbox, npc, prio_idx, &free_mcam, 521 SLIDE_ENTRIES_TO_HIGHER_INDEX); 522 if (rc) 523 return rc; 524 } 525 } 526 prio_idx--; 527 } 528 rsp->entry = free_mcam; 529 return rc; 530 } 531 532 static void 533 npc_insert_into_flow_list(struct npc *npc, struct npc_prio_flow_entry *entry) 534 { 535 struct npc_prio_flow_list_head *list; 536 struct npc_prio_flow_entry *curr; 537 538 list = &npc->prio_flow_list[entry->flow->priority]; 539 curr = TAILQ_FIRST(list); 540 541 if (curr) { 542 while (curr) { 543 if (entry->flow->mcam_id > curr->flow->mcam_id) 544 curr = TAILQ_NEXT(curr, next); 545 else 546 break; 547 } 548 if (curr) 549 TAILQ_INSERT_BEFORE(curr, entry, next); 550 else 551 TAILQ_INSERT_TAIL(list, entry, next); 552 } else { 553 TAILQ_INSERT_HEAD(list, entry, next); 554 } 555 } 556 557 static int 558 npc_allocate_mcam_entry(struct mbox *mbox, int prio, 559 struct npc_mcam_alloc_entry_rsp *rsp_local, 560 int ref_entry) 561 { 562 struct npc_mcam_alloc_entry_rsp *rsp_cmd; 563 struct npc_mcam_alloc_entry_req *req; 564 struct npc_mcam_alloc_entry_rsp *rsp; 565 int rc = -ENOSPC; 566 567 req = mbox_alloc_msg_npc_mcam_alloc_entry(mbox); 568 if (req == NULL) 569 return rc; 570 req->contig = 1; 571 req->count = 1; 572 req->priority = prio; 573 req->ref_entry = ref_entry; 574 575 rc = mbox_process_msg(mbox, (void *)&rsp_cmd); 576 if (rc) 577 return rc; 578 579 if (!rsp_cmd->count) 580 return -ENOSPC; 581 582 memcpy(rsp_local, rsp_cmd, sizeof(*rsp)); 583 584 return 0; 585 } 586 587 static void 588 npc_find_mcam_ref_entry(struct roc_npc_flow *flow, struct npc *npc, int *prio, 589 int *ref_entry, int dir) 590 { 591 struct npc_prio_flow_entry *head, *tail; 592 struct npc_prio_flow_list_head *list; 593 int prio_idx = flow->priority; 594 595 if (dir == NPC_MCAM_LOWER_PRIO) { 596 while (prio_idx >= 0) { 597 list = &npc->prio_flow_list[prio_idx]; 598 head = TAILQ_FIRST(list); 599 if (head) { 600 *prio = NPC_MCAM_LOWER_PRIO; 601 *ref_entry = head->flow->mcam_id; 602 return; 603 } 604 prio_idx--; 605 } 606 } else if (dir == NPC_MCAM_HIGHER_PRIO) { 607 prio_idx = flow->priority; 608 while (prio_idx <= npc->flow_max_priority - 1) { 609 list = &npc->prio_flow_list[prio_idx]; 610 tail = TAILQ_LAST(list, npc_prio_flow_list_head); 611 if (tail) { 612 *prio = NPC_MCAM_HIGHER_PRIO; 613 *ref_entry = tail->flow->mcam_id; 614 return; 615 } 616 prio_idx++; 617 } 618 } 619 *prio = NPC_MCAM_ANY_PRIO; 620 *ref_entry = 0; 621 } 622 623 static int 624 npc_alloc_mcam_by_ref_entry(struct mbox *mbox, struct roc_npc_flow *flow, 625 struct npc *npc, 626 struct npc_mcam_alloc_entry_rsp *rsp_local) 627 { 628 int prio, ref_entry = 0, rc = 0, dir = NPC_MCAM_LOWER_PRIO; 629 bool retry_done = false; 630 631 retry: 632 npc_find_mcam_ref_entry(flow, npc, &prio, &ref_entry, dir); 633 rc = npc_allocate_mcam_entry(mbox, prio, rsp_local, ref_entry); 634 if (rc && !retry_done) { 635 plt_info( 636 "npc: Failed to allocate lower priority entry. Retrying for higher priority"); 637 638 dir = NPC_MCAM_HIGHER_PRIO; 639 retry_done = true; 640 goto retry; 641 } else if (rc && retry_done) { 642 return rc; 643 } 644 645 return 0; 646 } 647 648 int 649 npc_get_free_mcam_entry(struct mbox *mbox, struct roc_npc_flow *flow, 650 struct npc *npc) 651 { 652 struct npc_mcam_alloc_entry_rsp rsp_local; 653 struct npc_prio_flow_entry *new_entry; 654 int rc = 0; 655 656 rc = npc_alloc_mcam_by_ref_entry(mbox, flow, npc, &rsp_local); 657 658 if (rc) 659 return rc; 660 661 new_entry = plt_zmalloc(sizeof(*new_entry), 0); 662 if (!new_entry) 663 return -ENOSPC; 664 665 new_entry->flow = flow; 666 667 plt_info("npc: kernel allocated MCAM entry %d", rsp_local.entry); 668 669 rc = npc_sort_mcams_by_user_prio_level(mbox, new_entry, npc, 670 &rsp_local); 671 if (rc) 672 goto err; 673 674 plt_info("npc: allocated MCAM entry after sorting %d", rsp_local.entry); 675 flow->mcam_id = rsp_local.entry; 676 npc_insert_into_flow_list(npc, new_entry); 677 678 return rsp_local.entry; 679 err: 680 plt_free(new_entry); 681 return rc; 682 } 683 684 void 685 npc_delete_prio_list_entry(struct npc *npc, struct roc_npc_flow *flow) 686 { 687 struct npc_prio_flow_list_head *list; 688 struct npc_prio_flow_entry *curr; 689 690 list = &npc->prio_flow_list[flow->priority]; 691 curr = TAILQ_FIRST(list); 692 693 if (!curr) 694 return; 695 696 while (curr) { 697 if (flow->mcam_id == curr->flow->mcam_id) { 698 TAILQ_REMOVE(list, curr, next); 699 plt_free(curr); 700 break; 701 } 702 curr = TAILQ_NEXT(curr, next); 703 } 704 } 705