1 /*
2 * Copyright (c) 2016-2021 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <skywalk/os_skywalk_private.h>
30 #include <skywalk/nexus/flowswitch/fsw_var.h>
31 #include <skywalk/nexus/flowswitch/flow/flow_var.h>
32
33 static uint32_t flow_owner_bucket_purge_common(struct flow_owner_bucket *,
34 nexus_port_t, boolean_t);
35 static int fo_cmp(const struct flow_owner *, const struct flow_owner *);
36 static struct flow_owner *fo_alloc(boolean_t);
37 static void fo_free(struct flow_owner *);
38
39 static LCK_GRP_DECLARE(flow_owner_lock_group, "sk_flow_owner_lock");
40 static LCK_ATTR_DECLARE(flow_owner_lock_attr, 0, 0);
41
42 RB_GENERATE_PREV(flow_owner_tree, flow_owner, fo_link, fo_cmp);
43
44 struct flow_owner_bucket *
flow_owner_buckets_alloc(size_t fob_cnt,size_t * fob_sz,size_t * tot_sz)45 flow_owner_buckets_alloc(size_t fob_cnt, size_t *fob_sz, size_t *tot_sz)
46 {
47 size_t cache_sz = skmem_cpu_cache_line_size();
48 struct flow_owner_bucket *fob;
49 void *fob_buf, **fob_pbuf;
50 size_t fob_tot_sz;
51
52 /* each bucket is CPU cache-aligned */
53 *fob_sz = P2ROUNDUP(sizeof(*fob), cache_sz);
54
55 /* total size includes extra for alignment requirements */
56 *tot_sz = fob_tot_sz = (sizeof(void *) + (fob_cnt * (*fob_sz)) + cache_sz);
57 // rdar://88962126
58 /* BEGIN IGNORE CODESTYLE */
59 __typed_allocators_ignore_push
60 fob_buf = sk_alloc(fob_tot_sz, Z_WAITOK, skmem_tag_fsw_fob_hash);
61 __typed_allocators_ignore_pop
62 /* END IGNORE CODESTYLE */
63 if (__improbable(fob_buf == NULL)) {
64 return NULL;
65 }
66
67 /*
68 * In case we didn't get a cache-aligned memory, round it up
69 * accordingly. This is needed in order to get the rest of
70 * the structure members aligned properly. It also means that
71 * the memory span gets shifted due to the round up, but it
72 * is okay since we've allocated extra space for this.
73 */
74 fob = (struct flow_owner_bucket *)
75 P2ROUNDUP((intptr_t)fob_buf + sizeof(void *), cache_sz);
76 fob_pbuf = (void **)((intptr_t)fob - sizeof(void *));
77 ASSERT((intptr_t)fob_pbuf >= (intptr_t)fob_buf);
78 ASSERT(((intptr_t)fob + (fob_cnt * (*fob_sz))) <=
79 ((intptr_t)fob_buf + fob_tot_sz));
80 *fob_pbuf = fob_buf;
81
82 SK_DF(SK_VERB_MEM, "fob 0x%llx fob_cnt %zu fob_sz %zu "
83 "(total %zu bytes, fob_buf 0x%llx) ALLOC", SK_KVA(fob), fob_cnt,
84 *fob_sz, fob_tot_sz, SK_KVA(fob_buf));
85
86 return fob;
87 }
88
89 void
flow_owner_buckets_free(struct flow_owner_bucket * fob,size_t tot_sz)90 flow_owner_buckets_free(struct flow_owner_bucket *fob, size_t tot_sz)
91 {
92 void *fob_buf, **fob_pbuf;
93
94 /* get the original address that we stuffed in earlier and free it */
95 fob_pbuf = (void **)((intptr_t)fob - sizeof(void *));
96 fob_buf = *fob_pbuf;
97 SK_DF(SK_VERB_MEM, "fob 0x%llx (fob_buf 0x%llx) FREE", SK_KVA(fob),
98 SK_KVA(fob_buf));
99 // rdar://88962126
100 __typed_allocators_ignore_push
101 sk_free(fob_buf, tot_sz);
102 __typed_allocators_ignore_pop
103 }
104
105 void
flow_owner_bucket_init(struct flow_owner_bucket * fob)106 flow_owner_bucket_init(struct flow_owner_bucket *fob)
107 {
108 ASSERT(IS_P2ALIGNED(fob, skmem_cpu_cache_line_size()));
109 lck_mtx_init(&fob->fob_lock, &flow_owner_lock_group,
110 &flow_owner_lock_attr);
111 RB_INIT(&fob->fob_owner_head);
112 }
113
114 void
flow_owner_bucket_destroy(struct flow_owner_bucket * fob)115 flow_owner_bucket_destroy(struct flow_owner_bucket *fob)
116 {
117 /*
118 * In the event we are called as part of the nexus destructor,
119 * we need to wait until all threads have exited the flow close
120 * critical section, and that the flow_owner_bucket is empty.
121 * By the time we get here, the module initiating the request
122 * (e.g. NECP) has been quiesced, so any flow open requests would
123 * have been rejected.
124 */
125 FOB_LOCK(fob);
126 while (!RB_EMPTY(&fob->fob_owner_head)) {
127 SK_ERR("waiting for fob 0x%llx to go idle", SK_KVA(fob));
128 if (++(fob->fob_dtor_waiters) == 0) { /* wraparound */
129 fob->fob_dtor_waiters++;
130 }
131 (void) msleep(&fob->fob_dtor_waiters, &fob->fob_lock,
132 (PZERO - 1), __FUNCTION__, NULL);
133 }
134 while (fob->fob_busy_flags & FOBF_CLOSE_BUSY) {
135 if (++(fob->fob_close_waiters) == 0) { /* wraparound */
136 fob->fob_close_waiters++;
137 }
138 (void) msleep(&fob->fob_close_waiters, &fob->fob_lock,
139 (PZERO - 1), __FUNCTION__, NULL);
140 }
141 ASSERT(RB_EMPTY(&fob->fob_owner_head));
142 ASSERT(!(fob->fob_busy_flags & FOBF_OPEN_BUSY));
143 ASSERT(!(fob->fob_busy_flags & FOBF_CLOSE_BUSY));
144 FOB_UNLOCK(fob);
145 lck_mtx_destroy(&fob->fob_lock, &flow_owner_lock_group);
146 }
147
148 static uint32_t
flow_owner_bucket_purge_common(struct flow_owner_bucket * fob,nexus_port_t nx_port,boolean_t if_idle)149 flow_owner_bucket_purge_common(struct flow_owner_bucket *fob,
150 nexus_port_t nx_port, boolean_t if_idle)
151 {
152 /* called by flow_owner_bucket_purge_all()? */
153 boolean_t locked = (nx_port == NEXUS_PORT_ANY);
154 struct flow_owner *fo, *tfo;
155 struct flow_entry *fe, *tfe;
156 uint32_t cnt = 0;
157
158 if (!locked) {
159 FOB_LOCK(fob);
160 }
161 FOB_LOCK_ASSERT_HELD(fob);
162
163 RB_FOREACH_SAFE(fo, flow_owner_tree, &fob->fob_owner_head, tfo) {
164 if (fo->fo_nx_port != nx_port && nx_port != NEXUS_PORT_ANY) {
165 continue;
166 }
167
168 if (!if_idle || nx_port == NEXUS_PORT_ANY) {
169 RB_FOREACH_SAFE(fe, flow_entry_id_tree,
170 &fo->fo_flow_entry_id_head, tfe) {
171 ASSERT(fe->fe_nx_port == fo->fo_nx_port);
172 flow_entry_retain(fe);
173 flow_entry_destroy(fo, fe, FALSE, NULL);
174 }
175 }
176
177 ASSERT(nx_port != NEXUS_PORT_ANY ||
178 RB_EMPTY(&fo->fo_flow_entry_id_head));
179
180 if (RB_EMPTY(&fo->fo_flow_entry_id_head)) {
181 flow_owner_free(fob, fo);
182 ++cnt;
183 } else if (nx_port != NEXUS_PORT_ANY) {
184 /* let ms_flow_unbind() know this port is gone */
185 fo->fo_nx_port_destroyed = TRUE;
186 VERIFY(fo->fo_nx_port_na == NULL);
187 }
188 }
189
190 if (!locked) {
191 FOB_UNLOCK(fob);
192 }
193
194 return cnt;
195 }
196
197 void
flow_owner_bucket_purge_all(struct flow_owner_bucket * fob)198 flow_owner_bucket_purge_all(struct flow_owner_bucket *fob)
199 {
200 (void) flow_owner_bucket_purge_common(fob, NEXUS_PORT_ANY, TRUE);
201 }
202
203 static uint32_t
flow_owner_bucket_activate_nx_port_common(struct flow_owner_bucket * fob,nexus_port_t nx_port,struct nexus_adapter * nx_port_na,na_activate_mode_t mode)204 flow_owner_bucket_activate_nx_port_common(struct flow_owner_bucket *fob,
205 nexus_port_t nx_port, struct nexus_adapter *nx_port_na,
206 na_activate_mode_t mode)
207 {
208 struct flow_owner *fo;
209 struct flow_entry *fe;
210 uint32_t cnt = 0;
211
212 VERIFY(nx_port != NEXUS_PORT_ANY);
213 FOB_LOCK(fob);
214
215 RB_FOREACH(fo, flow_owner_tree, &fob->fob_owner_head) {
216 if (fo->fo_nx_port_destroyed || (fo->fo_nx_port != nx_port)) {
217 continue;
218 }
219
220 if (mode == NA_ACTIVATE_MODE_ON) {
221 VERIFY(fo->fo_nx_port_na == NULL);
222 *(struct nexus_adapter **)(uintptr_t)&fo->fo_nx_port_na = nx_port_na;
223 }
224
225 RB_FOREACH(fe, flow_entry_id_tree,
226 &fo->fo_flow_entry_id_head) {
227 if (fe->fe_flags & FLOWENTF_TORN_DOWN) {
228 continue;
229 }
230 VERIFY(fe->fe_nx_port == fo->fo_nx_port);
231 if (fe->fe_adv_idx != FLOWADV_IDX_NONE) {
232 if (mode == NA_ACTIVATE_MODE_ON) {
233 na_flowadv_entry_alloc(
234 fo->fo_nx_port_na, fe->fe_uuid,
235 fe->fe_adv_idx, fe->fe_flowid);
236 } else if (fo->fo_nx_port_na != NULL) {
237 na_flowadv_entry_free(fo->fo_nx_port_na,
238 fe->fe_uuid, fe->fe_adv_idx,
239 fe->fe_flowid);
240 }
241 }
242 }
243
244 if (mode != NA_ACTIVATE_MODE_ON && fo->fo_nx_port_na != NULL) {
245 *(struct nexus_adapter **)(uintptr_t)&fo->fo_nx_port_na = NULL;
246 }
247
248 ++cnt;
249 }
250
251 FOB_UNLOCK(fob);
252 return cnt;
253 }
254
255 uint32_t
flow_owner_activate_nexus_port(struct flow_mgr * fm,boolean_t pid_bound,pid_t pid,nexus_port_t nx_port,struct nexus_adapter * nx_port_na,na_activate_mode_t mode)256 flow_owner_activate_nexus_port(struct flow_mgr *fm,
257 boolean_t pid_bound, pid_t pid, nexus_port_t nx_port,
258 struct nexus_adapter *nx_port_na, na_activate_mode_t mode)
259 {
260 struct flow_owner_bucket *fob;
261 uint32_t fo_cnt = 0;
262
263 VERIFY(nx_port != NEXUS_PORT_ANY);
264 VERIFY(nx_port_na != NULL);
265
266 if (pid_bound) {
267 fob = flow_mgr_get_fob_by_pid(fm, pid);
268 fo_cnt = flow_owner_bucket_activate_nx_port_common(fob, nx_port,
269 nx_port_na, mode);
270 } else {
271 uint32_t i;
272 /*
273 * Otherwise, this can get expensive since we need to search
274 * thru all proc-mapping buckets to find the flows that are
275 * related to this nexus port.
276 */
277 for (i = 0; i < fm->fm_owner_buckets_cnt; i++) {
278 fob = flow_mgr_get_fob_at_idx(fm, i);
279 fo_cnt += flow_owner_bucket_activate_nx_port_common(fob,
280 nx_port, nx_port_na, mode);
281 }
282 }
283 /* There shouldn't be more than one flow owners on a nexus port */
284 VERIFY(fo_cnt <= 1);
285 return fo_cnt;
286 }
287
288 static void
flow_owner_bucket_attach_common(struct flow_owner_bucket * fob,nexus_port_t nx_port)289 flow_owner_bucket_attach_common(struct flow_owner_bucket *fob,
290 nexus_port_t nx_port)
291 {
292 struct flow_owner *fo;
293
294 VERIFY(nx_port != NEXUS_PORT_ANY);
295 FOB_LOCK(fob);
296
297 RB_FOREACH(fo, flow_owner_tree, &fob->fob_owner_head) {
298 if (fo->fo_nx_port_destroyed && (fo->fo_nx_port == nx_port)) {
299 fo->fo_nx_port_destroyed = FALSE;
300 }
301 }
302
303 FOB_UNLOCK(fob);
304 }
305
306 void
flow_owner_attach_nexus_port(struct flow_mgr * fm,boolean_t pid_bound,pid_t pid,nexus_port_t nx_port)307 flow_owner_attach_nexus_port(struct flow_mgr *fm, boolean_t pid_bound,
308 pid_t pid, nexus_port_t nx_port)
309 {
310 struct flow_owner_bucket *fob;
311 ASSERT(nx_port != NEXUS_PORT_ANY);
312
313 if (pid_bound) {
314 fob = flow_mgr_get_fob_by_pid(fm, pid);
315 flow_owner_bucket_attach_common(fob, nx_port);
316 } else {
317 uint32_t i;
318 /*
319 * Otherwise, this can get expensive since we need to search
320 * thru all proc-mapping buckets to find the flows that are
321 * related to this nexus port.
322 */
323 for (i = 0; i < fm->fm_owner_buckets_cnt; i++) {
324 fob = flow_mgr_get_fob_at_idx(fm, i);
325 flow_owner_bucket_attach_common(fob, nx_port);
326 }
327 }
328 }
329
330 uint32_t
flow_owner_detach_nexus_port(struct flow_mgr * fm,boolean_t pid_bound,pid_t pid,nexus_port_t nx_port,boolean_t if_idle)331 flow_owner_detach_nexus_port(struct flow_mgr *fm, boolean_t pid_bound,
332 pid_t pid, nexus_port_t nx_port, boolean_t if_idle)
333 {
334 struct flow_owner_bucket *fob;
335 uint32_t purged = 0;
336 ASSERT(nx_port != NEXUS_PORT_ANY);
337
338 if (pid_bound) {
339 fob = flow_mgr_get_fob_by_pid(fm, pid);
340 purged = flow_owner_bucket_purge_common(fob, nx_port, if_idle);
341 } else {
342 uint32_t i;
343 /*
344 * Otherwise, this can get expensive since we need to search
345 * thru all proc-mapping buckets to find the flows that are
346 * related to this nexus port.
347 */
348 for (i = 0; i < fm->fm_owner_buckets_cnt; i++) {
349 fob = flow_mgr_get_fob_at_idx(fm, i);
350 purged += flow_owner_bucket_purge_common(fob,
351 nx_port, if_idle);
352 }
353 }
354 return purged;
355 }
356
357 /* 64-bit mask with range */
358 #define FO_BMASK64(_beg, _end) \
359 ((((uint64_t)0xffffffffffffffff) >> \
360 (63 - (_end))) & ~((1ULL << (_beg)) - 1))
361
362 struct flow_owner *
flow_owner_alloc(struct flow_owner_bucket * fob,struct proc * p,nexus_port_t nx_port,bool nx_port_pid_bound,bool flowadv,struct nx_flowswitch * fsw,struct nexus_adapter * nx_port_na,void * context,bool low_latency)363 flow_owner_alloc(struct flow_owner_bucket *fob, struct proc *p,
364 nexus_port_t nx_port, bool nx_port_pid_bound, bool flowadv,
365 struct nx_flowswitch *fsw, struct nexus_adapter *nx_port_na,
366 void *context, bool low_latency)
367 {
368 struct flow_owner *fo;
369 const pid_t pid = proc_pid(p);
370
371 _CASSERT(true == 1);
372 _CASSERT(false == 0);
373 ASSERT(low_latency == true || low_latency == false);
374 ASSERT(nx_port != NEXUS_PORT_ANY);
375 FOB_LOCK_ASSERT_HELD(fob);
376
377 #if DEBUG
378 ASSERT(flow_owner_find_by_pid(fob, pid, context, low_latency) == NULL);
379 RB_FOREACH(fo, flow_owner_tree, &fob->fob_owner_head) {
380 if (!fo->fo_nx_port_destroyed && (fo->fo_nx_port == nx_port)) {
381 VERIFY(0);
382 /* NOTREACHED */
383 __builtin_unreachable();
384 }
385 }
386 #endif /* DEBUG */
387
388 fo = fo_alloc(TRUE);
389 if (fo != NULL) {
390 if (flowadv) {
391 uint32_t i;
392
393 if ((fo->fo_flowadv_bmap =
394 skmem_cache_alloc(sk_fab_cache, SKMEM_SLEEP)) == NULL) {
395 SK_ERR("failed to alloc flow advisory bitmap");
396 fo_free(fo);
397 return NULL;
398 }
399 bzero(fo->fo_flowadv_bmap, sk_fab_size);
400 fo->fo_flowadv_max = sk_max_flows;
401
402 /* set the bits for free indices */
403 for (i = 0; i < sk_fadv_nchunks; i++) {
404 uint32_t end = 63;
405
406 if (i == (sk_fadv_nchunks - 1)) {
407 end = ((sk_max_flows - 1) %
408 FO_FLOWADV_CHUNK);
409 }
410
411 fo->fo_flowadv_bmap[i] = FO_BMASK64(0, end);
412 }
413 }
414 RB_INIT(&fo->fo_flow_entry_id_head);
415 /* const override */
416 *(struct flow_owner_bucket **)(uintptr_t)&fo->fo_bucket = fob;
417 fo->fo_context = context;
418 fo->fo_pid = pid;
419 (void) snprintf(fo->fo_name, sizeof(fo->fo_name), "%s",
420 proc_name_address(p));
421 fo->fo_nx_port_pid_bound = nx_port_pid_bound;
422 fo->fo_low_latency = low_latency;
423 fo->fo_nx_port = nx_port;
424 *(struct nexus_adapter **)(uintptr_t)&fo->fo_nx_port_na = nx_port_na;
425 *(struct nx_flowswitch **)(uintptr_t)&fo->fo_fsw = fsw;
426 RB_INSERT(flow_owner_tree, &fob->fob_owner_head, fo);
427
428 SK_DF(SK_VERB_FLOW, "%s(%d) fob 0x%llx added fo 0x%llx "
429 "nx_port %d nx_port_pid_bound %d ll %d nx_port_na 0x%llx",
430 fo->fo_name, fo->fo_pid, SK_KVA(fob), SK_KVA(fo),
431 (int)nx_port, nx_port_pid_bound, fo->fo_low_latency,
432 SK_KVA(nx_port_na));
433 }
434
435 return fo;
436 }
437
438 void
flow_owner_free(struct flow_owner_bucket * fob,struct flow_owner * fo)439 flow_owner_free(struct flow_owner_bucket *fob, struct flow_owner *fo)
440 {
441 FOB_LOCK_ASSERT_HELD(fob);
442
443 ASSERT(fo->fo_bucket == fob);
444 *(struct flow_owner_bucket **)(uintptr_t)&fo->fo_bucket = NULL;
445 RB_REMOVE(flow_owner_tree, &fob->fob_owner_head, fo);
446
447 ASSERT(fo->fo_num_flowadv == 0);
448 skmem_cache_free(sk_fab_cache, fo->fo_flowadv_bmap);
449 fo->fo_flowadv_bmap = NULL;
450
451 /* wake up any thread blocked in flow_owner_bucket_destroy() */
452 if (RB_EMPTY(&fob->fob_owner_head) && fob->fob_dtor_waiters > 0) {
453 fob->fob_dtor_waiters = 0;
454 wakeup(&fob->fob_dtor_waiters);
455 }
456
457 SK_DF(SK_VERB_FLOW, "%s(%d) fob 0x%llx removed fo 0x%llx nx_port %d",
458 fo->fo_name, fo->fo_pid, SK_KVA(fob), SK_KVA(fo),
459 (int)fo->fo_nx_port);
460
461 fo_free(fo);
462 }
463
464 int
flow_owner_flowadv_index_alloc(struct flow_owner * fo,flowadv_idx_t * fadv_idx)465 flow_owner_flowadv_index_alloc(struct flow_owner *fo, flowadv_idx_t *fadv_idx)
466 {
467 bitmap_t *bmap = fo->fo_flowadv_bmap;
468 size_t nchunks, i, j, idx = FLOWADV_IDX_NONE;
469
470 FOB_LOCK_ASSERT_HELD(FO_BUCKET(fo));
471 ASSERT(fo->fo_flowadv_max != 0);
472
473 nchunks = P2ROUNDUP(fo->fo_flowadv_max, FO_FLOWADV_CHUNK) /
474 FO_FLOWADV_CHUNK;
475
476 for (i = 0; i < nchunks; i++) {
477 j = ffsll(bmap[i]);
478 if (j == 0) {
479 /* All indices in this chunk are in use */
480 continue;
481 }
482 --j;
483 /* mark the index as in use */
484 bit_clear(bmap[i], j);
485 idx = (i * FO_FLOWADV_CHUNK) + j;
486 break;
487 }
488
489 if (idx == FLOWADV_IDX_NONE) {
490 SK_ERR("%s(%d) flow advisory table full: num %u max %u",
491 fo->fo_name, fo->fo_pid, fo->fo_num_flowadv,
492 fo->fo_flowadv_max);
493 VERIFY(fo->fo_num_flowadv == fo->fo_flowadv_max);
494 *fadv_idx = FLOWADV_IDX_NONE;
495 return ENOSPC;
496 }
497
498 fo->fo_num_flowadv++;
499 ASSERT(idx < ((flowadv_idx_t) -1));
500 *fadv_idx = (flowadv_idx_t)idx;
501 ASSERT(*fadv_idx < fo->fo_flowadv_max);
502 return 0;
503 }
504
505 void
flow_owner_flowadv_index_free(struct flow_owner * fo,flowadv_idx_t fadv_idx)506 flow_owner_flowadv_index_free(struct flow_owner *fo, flowadv_idx_t fadv_idx)
507 {
508 uint32_t chunk_idx, bit_pos;
509 bitmap_t *bmap = fo->fo_flowadv_bmap;
510
511 FOB_LOCK_ASSERT_HELD(FO_BUCKET(fo));
512 ASSERT(fo->fo_num_flowadv != 0);
513 ASSERT((fo->fo_flowadv_max != 0) && (fadv_idx < fo->fo_flowadv_max));
514
515 chunk_idx = fadv_idx / FO_FLOWADV_CHUNK;
516 bit_pos = fadv_idx % FO_FLOWADV_CHUNK;
517 ASSERT(!bit_test(bmap[chunk_idx], bit_pos));
518 /* mark the index as free */
519 bit_set(bmap[chunk_idx], bit_pos);
520 fo->fo_num_flowadv--;
521 }
522
523 int
flow_owner_destroy_entry(struct flow_owner * fo,uuid_t uuid,bool nolinger,void * close_params)524 flow_owner_destroy_entry(struct flow_owner *fo, uuid_t uuid,
525 bool nolinger, void *close_params)
526 {
527 struct flow_entry *fe = NULL;
528 int err = 0;
529
530 FOB_LOCK_ASSERT_HELD(FO_BUCKET(fo));
531
532 /* lookup such flow for this process */
533 fe = flow_entry_find_by_uuid(fo, uuid);
534 if (fe == NULL) {
535 err = ENOENT;
536 } else {
537 /* free flow entry (OK to linger if caller asked) */
538 flow_entry_destroy(fo, fe, nolinger, close_params);
539 }
540
541 return err;
542 }
543
544 static inline int
fo_cmp(const struct flow_owner * a,const struct flow_owner * b)545 fo_cmp(const struct flow_owner *a, const struct flow_owner *b)
546 {
547 if (a->fo_pid > b->fo_pid) {
548 return 1;
549 }
550 if (a->fo_pid < b->fo_pid) {
551 return -1;
552 }
553 if ((intptr_t)a->fo_context > (intptr_t)b->fo_context) {
554 return 1;
555 } else if ((intptr_t)a->fo_context < (intptr_t)b->fo_context) {
556 return -1;
557 }
558 if (a->fo_low_latency != b->fo_low_latency) {
559 if (a->fo_low_latency) {
560 return 1;
561 } else {
562 return -1;
563 }
564 }
565 return 0;
566 }
567
568 static struct flow_owner *
fo_alloc(boolean_t can_block)569 fo_alloc(boolean_t can_block)
570 {
571 struct flow_owner *fo;
572
573 fo = skmem_cache_alloc(sk_fo_cache,
574 can_block ? SKMEM_SLEEP : SKMEM_NOSLEEP);
575 if (fo == NULL) {
576 return NULL;
577 }
578
579 bzero(fo, sk_fo_size);
580
581 SK_DF(SK_VERB_MEM, "fo 0x%llx ALLOC", SK_KVA(fo));
582
583 return fo;
584 }
585
586 static void
fo_free(struct flow_owner * fo)587 fo_free(struct flow_owner *fo)
588 {
589 ASSERT(fo->fo_bucket == NULL);
590 ASSERT(RB_EMPTY(&fo->fo_flow_entry_id_head));
591 ASSERT(fo->fo_flowadv_bmap == NULL);
592
593 SK_DF(SK_VERB_MEM, "fo 0x%llx FREE", SK_KVA(fo));
594
595 skmem_cache_free(sk_fo_cache, fo);
596 }
597