dls_soft_ring.c revision 8793b36b40d14ad0a0fecc97738dc118a928f46c
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* General Soft rings - Simulating Rx rings in S/W.
*
* This is a general purpose high-performance soft ring mechanism. It is
* similar to a taskq with a single worker thread. The dls creates a
* set of these rings to simulate the H/W Rx ring (DMA channels) some
* NICs have. The purpose is to present a common interface to IP
* so the individual squeues can control these rings and switch them
* between polling and interrupt mode.
*
* This code also serves as a fanout mechanism for fast NIC feeding slow
* CPU where incoming traffic can be separated into multiple soft rings
* based on capability negotiation with IP and IP also creates thread
* affinity to soft ring worker threads to CPU so that conenction to
*
* The soft rings can also be driven by a classifier which can direct
* traffic to individual soft rings based on the input from IP.
*/
#include <sys/condvar_impl.h>
#include <inet/ipsec_impl.h>
#include <sys/dls_impl.h>
#include <sys/dls_soft_ring.h>
static void soft_ring_fire(void *);
static void soft_ring_worker(soft_ring_t *);
static void soft_ring_stop_workers(soft_ring_t **, int);
static void dls_taskq_stop_soft_ring(void *);
typedef struct soft_ring_taskq {
int soft_ring_workerwait_ms = 10;
/* The values above converted to ticks */
static int soft_ring_workerwait_tick = 0;
#define SOFT_RING_WORKER_WAKEUP(ringp) { \
\
/* \
* Queue isn't being processed, so take \
* any post enqueue actions needed before leaving. \
*/ \
if (tid != 0) { \
/* \
* Waiting for an enter() to process mblk(s). \
*/ \
\
/* \
* Times up and have a worker thread \
* waiting for work, so schedule it. \
*/ \
(ringp)->s_ring_tid = 0; \
} else { \
} \
} else if ((ringp)->s_ring_wait != 0) { \
(ringp)->s_ring_wait); \
} else { \
/* \
* Schedule the worker thread. \
*/ \
} \
}
/* \
* Enqueue our mblk chain. \
*/ \
\
else \
}
void
soft_ring_init(void)
{
}
/* ARGSUSED */
{
return (NULL);
if (bind != S_RING_BIND_NONE)
return (ringp);
}
soft_ring_t **
{
int i;
if ((ringp_list =
KM_NOSLEEP)) != NULL) {
for (i = 0; i < ring_size; i++) {
if (ringp_list[i] == NULL)
break;
}
if (i != ring_size) {
ringp_list = NULL;
}
}
return (ringp_list);
}
static void
{
int i;
soft_ring_unbind((void *)ringp);
ringp->s_ring_tid = 0;
/* Wake the worker so it can exit */
}
/*
* Here comes the tricky part. IP and driver ensure
* that packet flow has stopped but worker thread
* might still be draining the soft ring. We have
* already set the S_RING_DESTROY flag. We wait till
* the worker thread takes notice and stops processing
* the soft_ring and exits. It sets S_RING_DEAD on
* exiting.
*/
if (t_did)
}
}
void
{
int i;
}
/*
* when we are destroying the soft rings otherwise bad
* things will happen.
*/
}
}
/* ARGSUSED */
void
{
return;
}
if (cpu_is_online(cp)) {
}
}
void
soft_ring_unbind(void *arg)
{
return;
}
}
/*
* soft_ring_enter() - enter soft_ring sqp with mblk mp (which can be
* a chain), while tail points to the end and cnt in number of
* mblks in the chain.
*
* For a chain of single packet (i.e. mp == tail), go through the
* fast path if no one is processing the soft_ring and nothing is queued.
*
* The proc and arg for each mblk is already stored in the mblk in
* appropriate places.
*/
/* ARGSUSED */
static void
{
/*
* See if anything is already queued. If we are the
* first packet, do inline processing else queue the
* packet and do the drain.
*/
/*
* Fast-path, ok to process and nothing queued.
*/
/*
* We are the chain of 1 packet so
* go through this fast path.
*/
/*
* We processed inline our packet and
* nothing new has arrived. We are done.
*/
return;
}
} else {
}
/*
* We are here because either we couldn't do inline
* processing (because something was already queued),
* or we had a chanin of more than one packet,
* or something else arrived after we were done with
* inline processing.
*/
return;
} else {
/*
* Queue is already being processed. Just enqueue
* the packet and go away.
*/
} else
} else {
}
return;
}
}
/*
* PRIVATE FUNCTIONS
*/
static void
soft_ring_fire(void *arg)
{
if (ringp->s_ring_tid == 0) {
return;
}
ringp->s_ring_tid = 0;
}
}
/* ARGSUSED */
static void
{
ringp->s_ring_tid = 0;
ringp->s_ring_count = 0;
if (tid != 0) {
tid = 0;
}
}
}
static void
{
for (;;) {
goto destroy;
thread_exit();
}
}
goto still_wait;
}
}
}
}
void
{
int ring_size;
}
if (ringp_list != NULL)
}
static void
dls_taskq_stop_soft_ring(void *arg)
{
int ring_size;
}
{
int i;
char name[64];
/*
* Both ds_lock and di_lock are held as writer.
* As soft_ring_stop_workers() blocks for the
* worker thread(s) to complete, there is a possibility
* that the worker thread(s) could be in the process
* of draining the queue and is blocked waiting for
* either ds_lock or di_lock. Moreover the NIC interrupt
* thread could be blocked in dls_accept().
* To avoid deadlock condition, taskq thread would be
* created to handle soft_ring_stop_workers() and
* blocking if required which would avoid holding
* both ds_lock and di_lock.
* NOTE: we cannot drop either locks here, due to
* weird race conditions seen.
*/
ring_taskq = (soft_ring_taskq_t *)
if (ring_taskq == NULL) {
return (B_FALSE);
}
return (B_FALSE);
}
}
dip->di_soft_ring_size = 0;
return (B_FALSE);
}
for (i = 0; i < soft_ringp->dls_ring_cnt; i++) {
softring = softring_set[i];
(void *)soft_ringp->dls_rx_handle;
(mac_resource_t *)&mrf);
}
/*
* Note that soft_ring is enabled. This prevents further DLIOCHDRINFO
* ioctls from overwriting the receive function pointer.
*/
return (B_TRUE);
}
int dls_bad_ip_pkt = 0;
static mblk_t *
{
return (NULL);
}
}
return (bp);
}
/*
* dls_soft_ring_fanout():
*/
/* ARGSUSED */
void
{
int indx, saved_indx;
int hash = 0;
int skip_len;
int count = 0;
continue;
}
}
switch (protocol) {
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_SCTP:
case IPPROTO_ESP:
/*
* Note that for ESP, we fanout on SPI and it is at the
* same offset as the 2x16-bit ports. So it is clumped
* along with TCP, UDP and SCTP.
*/
continue;
}
break;
case IPPROTO_AH: {
continue;
}
goto again;
}
default:
/*
*/
hash =
break;
}
saved_indx = indx;
count++;
} else if (indx == saved_indx) {
count++;
} else {
saved_indx = indx;
count = 1;
}
}
}