xref: /illumos-gate/usr/src/uts/common/io/mac/mac_soft_ring.c (revision 27954b0d964ffcb749cf19296906e7fecdf3da1b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * General Soft rings - Simulating Rx rings in S/W.
28  *
29  * Soft ring is a data abstraction containing a queue and a worker
30  * thread and represents a hardware Rx ring in software. Each soft
31  * ring set can have a collection of soft rings for separating
32  * L3/L4 specific traffic (IPv4 from IPv6 or TCP from UDP) or for
33  * allowing a higher degree of parallelism by sending traffic to
34  * one of the soft rings for a SRS (using a hash on src IP or port).
35  * Each soft ring worker thread can be bound to a different CPU
36  * allowing the processing for each soft ring to happen in parallel
37  * and independent from each other.
38  *
39  * Protocol soft rings:
40  *
41  * Each SRS has at an minimum 3 softrings. One each for IPv4 TCP,
42  * IPv4 UDP and rest (OTH - for IPv6 and everything else). The
43  * SRS does dynamic polling and enforces link level bandwidth but
44  * it does so for all traffic (IPv4 and IPv6 and all protocols) on
45  * that link. However, each protocol layer wants a different
46  * behaviour. For instance IPv4 TCP has per CPU squeues which
47  * enforce their own polling and flow control so IPv4 TCP traffic
48  * needs to go to a separate soft ring which can be polled by the
49  * TCP squeue. It also allows TCP squeue to push back flow control
50  * all the way to NIC hardware (if it puts its corresponding soft
51  * ring in the poll mode and soft ring queue builds up, the
52  * shared srs_poll_pkt_cnt goes up and SRS automatically stops
53  * more packets from entering the system).
54  *
55  * Similarly, the UDP benefits from a DLS bypass and packet chaining
56  * so sending it to a separate soft ring is desired. All the rest of
57  * the traffic (including IPv6 is sent to OTH softring). The IPv6
58  * traffic current goes through OTH softring and via DLS because
59  * it need more processing to be done. Irrespective of the sap
60  * (IPv4 or IPv6) or the transport, the dynamic polling, B/W enforcement,
61  * cpu assignment, fanout, etc apply to all traffic since they
62  * are implement by the SRS which is agnostic to sap or transport.
63  *
64  * Fanout soft rings:
65  *
66  * On a multithreaded system, we can assign more CPU and multi thread
67  * the stack by creating a soft ring per CPU and spreading traffic
68  * based on a hash computed on src IP etc. Since we still need to
69  * keep the protocol separation, we create a set of 3 soft ring per
70  * CPU (specified by cpu list or degree of fanout).
71  *
72  * NOTE: See the block level comment on top of mac_sched.c
73  */
74 
75 #include <sys/types.h>
76 #include <sys/callb.h>
77 #include <sys/sdt.h>
78 #include <sys/strsubr.h>
79 #include <sys/strsun.h>
80 #include <sys/vlan.h>
81 #include <inet/ipsec_impl.h>
82 #include <inet/ip_impl.h>
83 #include <inet/sadb.h>
84 #include <inet/ipsecesp.h>
85 #include <inet/ipsecah.h>
86 
87 #include <sys/mac_impl.h>
88 #include <sys/mac_client_impl.h>
89 #include <sys/mac_soft_ring.h>
90 #include <sys/mac_flow_impl.h>
91 #include <sys/mac_stat.h>
92 
93 static void mac_rx_soft_ring_drain(mac_soft_ring_t *);
94 static void mac_soft_ring_fire(void *);
95 static void mac_soft_ring_worker(mac_soft_ring_t *);
96 static void mac_tx_soft_ring_drain(mac_soft_ring_t *);
97 
98 uint32_t mac_tx_soft_ring_max_q_cnt = 100000;
99 uint32_t mac_tx_soft_ring_hiwat = 1000;
100 
101 extern kmem_cache_t *mac_soft_ring_cache;
102 
103 #define	ADD_SOFTRING_TO_SET(mac_srs, softring) {			\
104 	if (mac_srs->srs_soft_ring_head == NULL) {			\
105 		mac_srs->srs_soft_ring_head = softring;			\
106 		mac_srs->srs_soft_ring_tail = softring;			\
107 	} else {							\
108 		/* ADD to the list */					\
109 		softring->s_ring_prev =					\
110 			mac_srs->srs_soft_ring_tail;			\
111 		mac_srs->srs_soft_ring_tail->s_ring_next = softring;	\
112 		mac_srs->srs_soft_ring_tail = softring;			\
113 	}								\
114 	mac_srs->srs_soft_ring_count++;					\
115 }
116 
117 /*
118  * mac_soft_ring_worker_wakeup
119  *
120  * Wake up the soft ring worker thread to process the queue as long
121  * as no one else is processing it and upper layer (client) is still
122  * ready to receive packets.
123  */
124 void
125 mac_soft_ring_worker_wakeup(mac_soft_ring_t *ringp)
126 {
127 	ASSERT(MUTEX_HELD(&ringp->s_ring_lock));
128 	if (!(ringp->s_ring_state & S_RING_PROC) &&
129 	    !(ringp->s_ring_state & S_RING_BLANK) &&
130 	    (ringp->s_ring_tid == NULL)) {
131 		if (ringp->s_ring_wait != 0) {
132 			ringp->s_ring_tid =
133 			    timeout(mac_soft_ring_fire, ringp,
134 			    ringp->s_ring_wait);
135 		} else {
136 			/* Schedule the worker thread. */
137 			cv_signal(&ringp->s_ring_async);
138 		}
139 	}
140 }
141 
142 /*
143  * mac_soft_ring_create
144  *
145  * Create a soft ring, do the necessary setup and bind the worker
146  * thread to the assigned CPU.
147  */
148 mac_soft_ring_t *
149 mac_soft_ring_create(int id, clock_t wait, uint16_t type,
150     pri_t pri, mac_client_impl_t *mcip, mac_soft_ring_set_t *mac_srs,
151     processorid_t cpuid, mac_direct_rx_t rx_func, void *x_arg1,
152     mac_resource_handle_t x_arg2)
153 {
154 	mac_soft_ring_t 	*ringp;
155 	char 			name[S_RING_NAMELEN];
156 
157 	bzero(name, 64);
158 	ringp = kmem_cache_alloc(mac_soft_ring_cache, KM_SLEEP);
159 
160 	if (type & ST_RING_TCP) {
161 		(void) snprintf(name, sizeof (name),
162 		    "mac_tcp_soft_ring_%d_%p", id, (void *)mac_srs);
163 	} else if (type & ST_RING_UDP) {
164 		(void) snprintf(name, sizeof (name),
165 		    "mac_udp_soft_ring_%d_%p", id, (void *)mac_srs);
166 	} else if (type & ST_RING_OTH) {
167 		(void) snprintf(name, sizeof (name),
168 		    "mac_oth_soft_ring_%d_%p", id, (void *)mac_srs);
169 	} else {
170 		ASSERT(type & ST_RING_TX);
171 		(void) snprintf(name, sizeof (name),
172 		    "mac_tx_soft_ring_%d_%p", id, (void *)mac_srs);
173 	}
174 
175 	bzero(ringp, sizeof (mac_soft_ring_t));
176 	(void) strncpy(ringp->s_ring_name, name, S_RING_NAMELEN + 1);
177 	ringp->s_ring_name[S_RING_NAMELEN] = '\0';
178 	mutex_init(&ringp->s_ring_lock, NULL, MUTEX_DEFAULT, NULL);
179 	ringp->s_ring_notify_cb_info.mcbi_lockp = &ringp->s_ring_lock;
180 
181 	ringp->s_ring_type = type;
182 	ringp->s_ring_wait = MSEC_TO_TICK(wait);
183 	ringp->s_ring_mcip = mcip;
184 	ringp->s_ring_set = mac_srs;
185 
186 	/*
187 	 * Protect against access from DR callbacks (mac_walk_srs_bind/unbind)
188 	 * which can't grab the mac perimeter
189 	 */
190 	mutex_enter(&mac_srs->srs_lock);
191 	ADD_SOFTRING_TO_SET(mac_srs, ringp);
192 	mutex_exit(&mac_srs->srs_lock);
193 
194 	/*
195 	 * set the bind CPU to -1 to indicate
196 	 * no thread affinity set
197 	 */
198 	ringp->s_ring_cpuid = ringp->s_ring_cpuid_save = -1;
199 	ringp->s_ring_worker = thread_create(NULL, 0,
200 	    mac_soft_ring_worker, ringp, 0, &p0, TS_RUN, pri);
201 	if (type & ST_RING_TX) {
202 		ringp->s_ring_drain_func = mac_tx_soft_ring_drain;
203 		ringp->s_ring_tx_arg1 = x_arg1;
204 		ringp->s_ring_tx_arg2 = x_arg2;
205 		ringp->s_ring_tx_max_q_cnt = mac_tx_soft_ring_max_q_cnt;
206 		ringp->s_ring_tx_hiwat =
207 		    (mac_tx_soft_ring_hiwat > mac_tx_soft_ring_max_q_cnt) ?
208 		    mac_tx_soft_ring_max_q_cnt : mac_tx_soft_ring_hiwat;
209 		if (mcip->mci_state_flags & MCIS_IS_AGGR) {
210 			mac_srs_tx_t *tx = &mac_srs->srs_tx;
211 
212 			ASSERT(tx->st_soft_rings[
213 			    ((mac_ring_t *)x_arg2)->mr_index] == NULL);
214 			tx->st_soft_rings[((mac_ring_t *)x_arg2)->mr_index] =
215 			    ringp;
216 		}
217 	} else {
218 		ringp->s_ring_drain_func = mac_rx_soft_ring_drain;
219 		ringp->s_ring_rx_func = rx_func;
220 		ringp->s_ring_rx_arg1 = x_arg1;
221 		ringp->s_ring_rx_arg2 = x_arg2;
222 		if (mac_srs->srs_state & SRS_SOFTRING_QUEUE)
223 			ringp->s_ring_type |= ST_RING_WORKER_ONLY;
224 	}
225 	if (cpuid != -1)
226 		(void) mac_soft_ring_bind(ringp, cpuid);
227 
228 	mac_soft_ring_stat_create(ringp);
229 
230 	return (ringp);
231 }
232 
233 /*
234  * mac_soft_ring_free
235  *
236  * Free the soft ring once we are done with it.
237  */
238 void
239 mac_soft_ring_free(mac_soft_ring_t *softring)
240 {
241 	ASSERT((softring->s_ring_state &
242 	    (S_RING_CONDEMNED | S_RING_CONDEMNED_DONE | S_RING_PROC)) ==
243 	    (S_RING_CONDEMNED | S_RING_CONDEMNED_DONE));
244 	mac_pkt_drop(NULL, NULL, softring->s_ring_first, B_FALSE);
245 	softring->s_ring_tx_arg2 = NULL;
246 	mac_soft_ring_stat_delete(softring);
247 	mac_callback_free(softring->s_ring_notify_cb_list);
248 	kmem_cache_free(mac_soft_ring_cache, softring);
249 }
250 
251 int mac_soft_ring_thread_bind = 1;
252 
253 /*
254  * mac_soft_ring_bind
255  *
256  * Bind a soft ring worker thread to supplied CPU.
257  */
258 cpu_t *
259 mac_soft_ring_bind(mac_soft_ring_t *ringp, processorid_t cpuid)
260 {
261 	cpu_t *cp;
262 	boolean_t clear = B_FALSE;
263 
264 	ASSERT(MUTEX_HELD(&cpu_lock));
265 
266 	if (mac_soft_ring_thread_bind == 0) {
267 		DTRACE_PROBE1(mac__soft__ring__no__cpu__bound,
268 		    mac_soft_ring_t *, ringp);
269 		return (NULL);
270 	}
271 
272 	cp = cpu_get(cpuid);
273 	if (cp == NULL || !cpu_is_online(cp))
274 		return (NULL);
275 
276 	mutex_enter(&ringp->s_ring_lock);
277 	ringp->s_ring_state |= S_RING_BOUND;
278 	if (ringp->s_ring_cpuid != -1)
279 		clear = B_TRUE;
280 	ringp->s_ring_cpuid = cpuid;
281 	mutex_exit(&ringp->s_ring_lock);
282 
283 	if (clear)
284 		thread_affinity_clear(ringp->s_ring_worker);
285 
286 	DTRACE_PROBE2(mac__soft__ring__cpu__bound, mac_soft_ring_t *,
287 	    ringp, processorid_t, cpuid);
288 
289 	thread_affinity_set(ringp->s_ring_worker, cpuid);
290 
291 	return (cp);
292 }
293 
294 /*
295  * mac_soft_ring_unbind
296  *
297  * Un Bind a soft ring worker thread.
298  */
299 void
300 mac_soft_ring_unbind(mac_soft_ring_t *ringp)
301 {
302 	ASSERT(MUTEX_HELD(&cpu_lock));
303 
304 	mutex_enter(&ringp->s_ring_lock);
305 	if (!(ringp->s_ring_state & S_RING_BOUND)) {
306 		ASSERT(ringp->s_ring_cpuid == -1);
307 		mutex_exit(&ringp->s_ring_lock);
308 		return;
309 	}
310 
311 	ringp->s_ring_cpuid = -1;
312 	ringp->s_ring_state &= ~S_RING_BOUND;
313 	thread_affinity_clear(ringp->s_ring_worker);
314 	mutex_exit(&ringp->s_ring_lock);
315 }
316 
317 /*
318  * PRIVATE FUNCTIONS
319  */
320 
321 static void
322 mac_soft_ring_fire(void *arg)
323 {
324 	mac_soft_ring_t	*ringp = arg;
325 
326 	mutex_enter(&ringp->s_ring_lock);
327 	if (ringp->s_ring_tid == 0) {
328 		mutex_exit(&ringp->s_ring_lock);
329 		return;
330 	}
331 
332 	ringp->s_ring_tid = 0;
333 
334 	if (!(ringp->s_ring_state & S_RING_PROC)) {
335 		cv_signal(&ringp->s_ring_async);
336 	}
337 	mutex_exit(&ringp->s_ring_lock);
338 }
339 
340 /*
341  * mac_rx_soft_ring_drain
342  *
343  * Called when worker thread model (ST_RING_WORKER_ONLY) of processing
344  * incoming packets is used. s_ring_first contain the queued packets.
345  * s_ring_rx_func contains the upper level (client) routine where the
346  * packets are destined and s_ring_rx_arg1/s_ring_rx_arg2 are the
347  * cookie meant for the client.
348  */
349 /* ARGSUSED */
350 static void
351 mac_rx_soft_ring_drain(mac_soft_ring_t *ringp)
352 {
353 	mblk_t		*mp;
354 	void		*arg1;
355 	mac_resource_handle_t arg2;
356 	timeout_id_t 	tid;
357 	mac_direct_rx_t	proc;
358 	size_t		sz;
359 	int		cnt;
360 	mac_soft_ring_set_t	*mac_srs = ringp->s_ring_set;
361 
362 	ringp->s_ring_run = curthread;
363 	ASSERT(mutex_owned(&ringp->s_ring_lock));
364 	ASSERT(!(ringp->s_ring_state & S_RING_PROC));
365 
366 	if ((tid = ringp->s_ring_tid) != 0)
367 		ringp->s_ring_tid = 0;
368 
369 	ringp->s_ring_state |= S_RING_PROC;
370 
371 	proc = ringp->s_ring_rx_func;
372 	arg1 = ringp->s_ring_rx_arg1;
373 	arg2 = ringp->s_ring_rx_arg2;
374 
375 	while ((ringp->s_ring_first != NULL) &&
376 	    !(ringp->s_ring_state & S_RING_PAUSE)) {
377 		mp = ringp->s_ring_first;
378 		ringp->s_ring_first = NULL;
379 		ringp->s_ring_last = NULL;
380 		cnt = ringp->s_ring_count;
381 		ringp->s_ring_count = 0;
382 		sz = ringp->s_ring_size;
383 		ringp->s_ring_size = 0;
384 		mutex_exit(&ringp->s_ring_lock);
385 
386 		if (tid != 0) {
387 			(void) untimeout(tid);
388 			tid = 0;
389 		}
390 
391 		(*proc)(arg1, arg2, mp, NULL);
392 
393 		/*
394 		 * If we have a soft ring set which is doing
395 		 * bandwidth control, we need to decrement its
396 		 * srs_size so it can have a accurate idea of
397 		 * what is the real data queued between SRS and
398 		 * its soft rings. We decrement the size for a
399 		 * packet only when it gets processed by both
400 		 * SRS and the soft ring.
401 		 */
402 		mutex_enter(&mac_srs->srs_lock);
403 		MAC_UPDATE_SRS_COUNT_LOCKED(mac_srs, cnt);
404 		MAC_UPDATE_SRS_SIZE_LOCKED(mac_srs, sz);
405 		mutex_exit(&mac_srs->srs_lock);
406 
407 		mutex_enter(&ringp->s_ring_lock);
408 	}
409 	ringp->s_ring_state &= ~S_RING_PROC;
410 	if (ringp->s_ring_state & S_RING_CLIENT_WAIT)
411 		cv_signal(&ringp->s_ring_client_cv);
412 	ringp->s_ring_run = NULL;
413 }
414 
415 /*
416  * mac_soft_ring_worker
417  *
418  * The soft ring worker routine to process any queued packets. In
419  * normal case, the worker thread is bound to a CPU. It the soft
420  * ring is dealing with TCP packets, then the worker thread will
421  * be bound to the same CPU as the TCP squeue.
422  */
423 static void
424 mac_soft_ring_worker(mac_soft_ring_t *ringp)
425 {
426 	kmutex_t *lock = &ringp->s_ring_lock;
427 	kcondvar_t *async = &ringp->s_ring_async;
428 	mac_soft_ring_set_t *srs = ringp->s_ring_set;
429 	callb_cpr_t cprinfo;
430 
431 	CALLB_CPR_INIT(&cprinfo, lock, callb_generic_cpr, "mac_soft_ring");
432 	mutex_enter(lock);
433 start:
434 	for (;;) {
435 		while (((ringp->s_ring_first == NULL ||
436 		    (ringp->s_ring_state & (S_RING_BLOCK|S_RING_BLANK))) &&
437 		    !(ringp->s_ring_state & S_RING_PAUSE)) ||
438 		    (ringp->s_ring_state & S_RING_PROC)) {
439 
440 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
441 			cv_wait(async, lock);
442 			CALLB_CPR_SAFE_END(&cprinfo, lock);
443 		}
444 
445 		/*
446 		 * Either we have work to do, or we have been asked to
447 		 * shutdown temporarily or permanently
448 		 */
449 		if (ringp->s_ring_state & S_RING_PAUSE)
450 			goto done;
451 
452 		ringp->s_ring_drain_func(ringp);
453 	}
454 done:
455 	mutex_exit(lock);
456 	mutex_enter(&srs->srs_lock);
457 	mutex_enter(lock);
458 
459 	ringp->s_ring_state |= S_RING_QUIESCE_DONE;
460 	if (!(ringp->s_ring_state & S_RING_CONDEMNED)) {
461 		srs->srs_soft_ring_quiesced_count++;
462 		cv_broadcast(&srs->srs_async);
463 		mutex_exit(&srs->srs_lock);
464 		while (!(ringp->s_ring_state &
465 		    (S_RING_RESTART | S_RING_CONDEMNED)))
466 			cv_wait(&ringp->s_ring_async, &ringp->s_ring_lock);
467 		mutex_exit(lock);
468 		mutex_enter(&srs->srs_lock);
469 		mutex_enter(lock);
470 		srs->srs_soft_ring_quiesced_count--;
471 		if (ringp->s_ring_state & S_RING_RESTART) {
472 			ASSERT(!(ringp->s_ring_state & S_RING_CONDEMNED));
473 			ringp->s_ring_state &= ~(S_RING_RESTART |
474 			    S_RING_QUIESCE | S_RING_QUIESCE_DONE);
475 			cv_broadcast(&srs->srs_async);
476 			mutex_exit(&srs->srs_lock);
477 			goto start;
478 		}
479 	}
480 	ASSERT(ringp->s_ring_state & S_RING_CONDEMNED);
481 	ringp->s_ring_state |= S_RING_CONDEMNED_DONE;
482 	CALLB_CPR_EXIT(&cprinfo);
483 	srs->srs_soft_ring_condemned_count++;
484 	cv_broadcast(&srs->srs_async);
485 	mutex_exit(&srs->srs_lock);
486 	thread_exit();
487 }
488 
489 /*
490  * mac_soft_ring_intr_enable and mac_soft_ring_intr_disable
491  *
492  * these functions are called to toggle the sending of packets to the
493  * client. They are called by the client. the client gets the name
494  * of these routine and corresponding cookie (pointing to softring)
495  * during capability negotiation at setup time.
496  *
497  * Enabling is allow the processing thread to send packets to the
498  * client while disabling does the opposite.
499  */
500 void
501 mac_soft_ring_intr_enable(void *arg)
502 {
503 	mac_soft_ring_t *ringp = (mac_soft_ring_t *)arg;
504 	mutex_enter(&ringp->s_ring_lock);
505 	ringp->s_ring_state &= ~S_RING_BLANK;
506 	if (ringp->s_ring_first != NULL)
507 		mac_soft_ring_worker_wakeup(ringp);
508 	mutex_exit(&ringp->s_ring_lock);
509 }
510 
511 boolean_t
512 mac_soft_ring_intr_disable(void *arg)
513 {
514 	mac_soft_ring_t *ringp = (mac_soft_ring_t *)arg;
515 	boolean_t sring_blanked = B_FALSE;
516 	/*
517 	 * Stop worker thread from sending packets above.
518 	 * Squeue will poll soft ring when it needs packets.
519 	 */
520 	mutex_enter(&ringp->s_ring_lock);
521 	if (!(ringp->s_ring_state & S_RING_PROC)) {
522 		ringp->s_ring_state |= S_RING_BLANK;
523 		sring_blanked = B_TRUE;
524 	}
525 	mutex_exit(&ringp->s_ring_lock);
526 	return (sring_blanked);
527 }
528 
529 /*
530  * mac_soft_ring_poll
531  *
532  * This routine is called by the client to poll for packets from
533  * the soft ring. The function name and cookie corresponding to
534  * the soft ring is exchanged during capability negotiation during
535  * setup.
536  */
537 mblk_t *
538 mac_soft_ring_poll(mac_soft_ring_t *ringp, int bytes_to_pickup)
539 {
540 	mblk_t	*head, *tail;
541 	mblk_t	*mp;
542 	size_t	sz = 0;
543 	int	cnt = 0;
544 	mac_soft_ring_set_t	*mac_srs = ringp->s_ring_set;
545 
546 	ASSERT(mac_srs != NULL);
547 
548 	mutex_enter(&ringp->s_ring_lock);
549 	head = tail = mp = ringp->s_ring_first;
550 	if (head == NULL) {
551 		mutex_exit(&ringp->s_ring_lock);
552 		return (NULL);
553 	}
554 
555 	if (ringp->s_ring_size <= bytes_to_pickup) {
556 		head = ringp->s_ring_first;
557 		ringp->s_ring_first = NULL;
558 		ringp->s_ring_last = NULL;
559 		cnt = ringp->s_ring_count;
560 		ringp->s_ring_count = 0;
561 		sz = ringp->s_ring_size;
562 		ringp->s_ring_size = 0;
563 	} else {
564 		while (mp && sz <= bytes_to_pickup) {
565 			sz += msgdsize(mp);
566 			cnt++;
567 			tail = mp;
568 			mp = mp->b_next;
569 		}
570 		ringp->s_ring_count -= cnt;
571 		ringp->s_ring_size -= sz;
572 		tail->b_next = NULL;
573 		if (mp == NULL) {
574 			ringp->s_ring_first = NULL;
575 			ringp->s_ring_last = NULL;
576 			ASSERT(ringp->s_ring_count == 0);
577 		} else {
578 			ringp->s_ring_first = mp;
579 		}
580 	}
581 
582 	mutex_exit(&ringp->s_ring_lock);
583 	/*
584 	 * Update the shared count and size counters so
585 	 * that SRS has a accurate idea of queued packets.
586 	 */
587 	mutex_enter(&mac_srs->srs_lock);
588 	MAC_UPDATE_SRS_COUNT_LOCKED(mac_srs, cnt);
589 	MAC_UPDATE_SRS_SIZE_LOCKED(mac_srs, sz);
590 	mutex_exit(&mac_srs->srs_lock);
591 	return (head);
592 }
593 
594 /*
595  * mac_soft_ring_dls_bypass
596  *
597  * Enable direct client (IP) callback function from the softrings.
598  * Callers need to make sure they don't need any DLS layer processing
599  */
600 void
601 mac_soft_ring_dls_bypass(void *arg, mac_direct_rx_t rx_func, void *rx_arg1)
602 {
603 	mac_soft_ring_t		*softring = arg;
604 	mac_soft_ring_set_t	*srs;
605 
606 	ASSERT(rx_func != NULL);
607 
608 	mutex_enter(&softring->s_ring_lock);
609 	softring->s_ring_rx_func = rx_func;
610 	softring->s_ring_rx_arg1 = rx_arg1;
611 	mutex_exit(&softring->s_ring_lock);
612 
613 	srs = softring->s_ring_set;
614 	mutex_enter(&srs->srs_lock);
615 	srs->srs_type |= SRST_DLS_BYPASS;
616 	mutex_exit(&srs->srs_lock);
617 }
618 
619 /*
620  * mac_soft_ring_signal
621  *
622  * Typically used to set the soft ring state to QUIESCE, CONDEMNED, or
623  * RESTART.
624  *
625  * In the Rx side, the quiescing is done bottom up. After the Rx upcalls
626  * from the driver are done, then the Rx SRS is quiesced and only then can
627  * we signal the soft rings. Thus this function can't be called arbitrarily
628  * without satisfying the prerequisites. On the Tx side, the threads from
629  * top need to quiesced, then the Tx SRS and only then can we signal the
630  * Tx soft rings.
631  */
632 void
633 mac_soft_ring_signal(mac_soft_ring_t *softring, uint_t sr_flag)
634 {
635 	mutex_enter(&softring->s_ring_lock);
636 	softring->s_ring_state |= sr_flag;
637 	cv_signal(&softring->s_ring_async);
638 	mutex_exit(&softring->s_ring_lock);
639 }
640 
641 /*
642  * mac_tx_soft_ring_drain
643  *
644  * The transmit side drain routine in case the soft ring was being
645  * used to transmit packets.
646  */
647 static void
648 mac_tx_soft_ring_drain(mac_soft_ring_t *ringp)
649 {
650 	mblk_t 			*mp;
651 	void 			*arg1;
652 	void 			*arg2;
653 	mblk_t 			*tail;
654 	uint_t			saved_pkt_count, saved_size;
655 	mac_tx_stats_t		stats;
656 	mac_soft_ring_set_t	*mac_srs = ringp->s_ring_set;
657 
658 	saved_pkt_count = saved_size = 0;
659 	ringp->s_ring_run = curthread;
660 	ASSERT(mutex_owned(&ringp->s_ring_lock));
661 	ASSERT(!(ringp->s_ring_state & S_RING_PROC));
662 
663 	ringp->s_ring_state |= S_RING_PROC;
664 	arg1 = ringp->s_ring_tx_arg1;
665 	arg2 = ringp->s_ring_tx_arg2;
666 
667 	while (ringp->s_ring_first != NULL) {
668 		mp = ringp->s_ring_first;
669 		tail = ringp->s_ring_last;
670 		saved_pkt_count = ringp->s_ring_count;
671 		saved_size = ringp->s_ring_size;
672 		ringp->s_ring_first = NULL;
673 		ringp->s_ring_last = NULL;
674 		ringp->s_ring_count = 0;
675 		ringp->s_ring_size = 0;
676 		mutex_exit(&ringp->s_ring_lock);
677 
678 		mp = mac_tx_send(arg1, arg2, mp, &stats);
679 
680 		mutex_enter(&ringp->s_ring_lock);
681 		if (mp != NULL) {
682 			/* Device out of tx desc, set block */
683 			tail->b_next = ringp->s_ring_first;
684 			ringp->s_ring_first = mp;
685 			ringp->s_ring_count +=
686 			    (saved_pkt_count - stats.mts_opackets);
687 			ringp->s_ring_size += (saved_size - stats.mts_obytes);
688 			if (ringp->s_ring_last == NULL)
689 				ringp->s_ring_last = tail;
690 
691 			if (ringp->s_ring_tx_woken_up) {
692 				ringp->s_ring_tx_woken_up = B_FALSE;
693 			} else {
694 				ringp->s_ring_state |= S_RING_BLOCK;
695 				ringp->s_st_stat.mts_blockcnt++;
696 			}
697 
698 			ringp->s_ring_state &= ~S_RING_PROC;
699 			ringp->s_ring_run = NULL;
700 			return;
701 		} else {
702 			ringp->s_ring_tx_woken_up = B_FALSE;
703 			SRS_TX_STATS_UPDATE(mac_srs, &stats);
704 			SOFTRING_TX_STATS_UPDATE(ringp, &stats);
705 		}
706 	}
707 
708 	if (ringp->s_ring_count == 0 && ringp->s_ring_state &
709 	    (S_RING_TX_HIWAT | S_RING_WAKEUP_CLIENT | S_RING_ENQUEUED)) {
710 		mac_client_impl_t *mcip =  ringp->s_ring_mcip;
711 		boolean_t wakeup_required = B_FALSE;
712 
713 		if (ringp->s_ring_state &
714 		    (S_RING_TX_HIWAT|S_RING_WAKEUP_CLIENT)) {
715 			wakeup_required = B_TRUE;
716 		}
717 		ringp->s_ring_state &=
718 		    ~(S_RING_TX_HIWAT | S_RING_WAKEUP_CLIENT | S_RING_ENQUEUED);
719 		mutex_exit(&ringp->s_ring_lock);
720 		if (wakeup_required) {
721 			mac_tx_invoke_callbacks(mcip, (mac_tx_cookie_t)ringp);
722 			/*
723 			 * If the client is not the primary MAC client, then we
724 			 * need to send the notification to the clients upper
725 			 * MAC, i.e. mci_upper_mip.
726 			 */
727 			mac_tx_notify(mcip->mci_upper_mip != NULL ?
728 			    mcip->mci_upper_mip : mcip->mci_mip);
729 		}
730 		mutex_enter(&ringp->s_ring_lock);
731 	}
732 	ringp->s_ring_state &= ~S_RING_PROC;
733 	ringp->s_ring_run = NULL;
734 }
735