/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* IP interface to squeues.
*
* IP uses squeues to force serialization of packets, both incoming and
* outgoing. Each squeue is associated with a connection instance (conn_t)
* above, and a soft ring (if enabled) below. Each CPU will have a default
* squeue for outbound connections, and each soft ring of an interface will
* have an squeue to which it sends incoming packets. squeues are never
* destroyed, and if they become unused they are kept around against future
* needs.
*
* IP organizes its squeues using squeue sets (squeue_set_t). For each CPU
* in the system there will be one squeue set, all of whose squeues will be
* bound to that CPU, plus one additional set known as the unbound set. Sets
* associated with CPUs will have one default squeue, for outbound
* connections, and a linked list of squeues used by various NICs for inbound
* packets. The unbound set also has a linked list of squeues, but no default
* squeue.
*
* When a CPU goes offline its squeue set is destroyed, and all its squeues
* are moved to the unbound set. When a CPU comes online, a new squeue set is
* created and the default set is searched for a default squeue formerly bound
* to this CPU. If no default squeue is found, a new one is created.
*
* Two fields of the squeue_t, namely sq_next and sq_set, are owned by IP
* and not the squeue code. squeue.c will not touch them, and we can modify
* them without holding the squeue lock because of the guarantee that squeues
* are never destroyed. ip_squeue locks must be held, however.
*
* All the squeue sets are protected by a single lock, the sqset_lock. This
* is also used to protect the sq_next and sq_set fields of an squeue_t.
*
* The lock order is: cpu_lock --> ill_lock --> sqset_lock --> sq_lock
*
* There are two modes of associating connection with squeues. The first mode
* associates each connection with the CPU that creates the connection (either
* during open time or during accept time). The second mode associates each
* connection with a random CPU, effectively distributing load over all CPUs
* and all squeues in the system. The mode is controlled by the
* ip_squeue_fanout variable.
*
* NOTE: The fact that there is an association between each connection and
* squeue and squeue and CPU does not mean that each connection is always
* processed on this CPU and on this CPU only. Any thread calling squeue_enter()
* may process the connection on whatever CPU it is scheduled. The squeue to CPU
* binding is only relevant for the worker thread.
*
* INTERFACE:
*
* squeue_t *ip_squeue_get(ill_rx_ring_t)
*
* Returns the squeue associated with an ill receive ring. If the ring is
* not bound to a CPU, and we're currently servicing the interrupt which
* generated the packet, then bind the squeue to CPU.
*
*
* DR Notes
* ========
*
* The ip_squeue_init() registers a call-back function with the CPU DR
* subsystem using register_cpu_setup_func(). The call-back function does two
* things:
*
* o When the CPU is going off-line or unconfigured, the worker thread is
* unbound from the CPU. This allows the CPU unconfig code to move it to
* another CPU.
*
* o When the CPU is going online, it creates a new squeue for this CPU if
* necessary and binds the squeue worker thread to this CPU.
*
* TUNABLES:
*
* ip_squeue_fanout: used when TCP calls IP_SQUEUE_GET(). If 1, then
* pick the default squeue from a random CPU, otherwise use our CPU's default
* squeue.
*
*
* ip_squeue_worker_wait: global value for the sq_wait field for all squeues *
* created. This is the time squeue code waits before waking up the worker
* thread after queuing a request.
*/
#include <inet/ipclassifier.h>
#include <sys/squeue_impl.h>
#include <inet/udp_impl.h>
/*
* List of all created squeue sets. The list and its size are protected by
* sqset_lock.
*/
/*
* ip_squeue_worker_wait: global value for the sq_wait field for all squeues
* created. This is the time squeue code waits before waking up the worker
* thread after queuing a request.
*/
static int ip_squeue_cpu_setup(cpu_setup_t, int, void *);
static void ip_squeue_set_destroy(cpu_t *);
static void ip_squeue_clean(void *, mblk_t *, void *);
static squeue_t *
{
if (ip_squeue_create_callback != NULL)
return (sqp);
}
/*
* Create a new squeue_set. If id == -1, then we're creating the unbound set,
* which should only happen once when we are first initialized. Otherwise id
* is the id of the CPU that needs a set, either because we are initializing
* or because the CPU has come online.
*
* If id != -1, then we need at a minimum to provide a default squeue for the
* new set. We search the unbound set for candidates, and if none are found we
* create a new one.
*/
static squeue_set_t *
{
if (id == -1) {
ASSERT(sqset_global_size == 0);
sqset_global_list[0] = sqs;
sqset_global_size = 1;
return (sqs);
}
/*
* When we create an squeue set id != -1, we need to give it a
* default squeue, in order to support fanout of conns across
* CPUs. Try to find a former default squeue that matches this
* cpu id on the unbound squeue set. If no such squeue is found,
* find some non-default TCP squeue that is free. If still no such
* candidate is found, create a new squeue.
*/
while (*lastsqp) {
/*
* Exact match. Former default squeue of cpu 'id'
*/
break;
}
if (defaultq_lastp == NULL &&
/*
* A free non-default TCP squeue
*/
}
}
if (defaultq_lastp != NULL) {
/* Remove from src set and set SQS_DEFAULT */
sq = *defaultq_lastp;
}
} else {
}
return (sqs);
}
/*
* Called by ill_ring_add() to find an squeue to associate with a new ring.
*/
squeue_t *
{
/*
* Select a non-default TCP squeue that is free i.e. not
* bound to any ill.
*/
break;
}
}
}
return (sq);
}
/*
* Initialize IP squeues.
*/
void
{
int i;
squeue_init();
sqset_global_size = 0;
/*
* We are called at system boot time and we don't
* expect memory allocation failure.
*/
/* Create squeue for each active CPU available */
for (i = 0; i < NCPU; i++) {
/*
* We are called at system boot time and we don't
* expect memory allocation failure then
*/
}
}
}
/*
* Get a default squeue, either from the current CPU or a CPU derived by hash
* from the index argument, depending upon the setting of ip_squeue_fanout.
*/
squeue_t *
{
/*
* The minimum value of sqset_global_size is 2, one for the unbound
* squeue set and another for the squeue set of the zeroth CPU.
* Even though the value could be changing, it can never go below 2,
* so the assert does not need the lock protection.
*/
/* Protect against changes to sqset_global_list */
if (!ip_squeue_fanout)
/*
* sqset_global_list[0] corresponds to the unbound squeue set.
* The computation below picks a set other than the unbound set.
*/
return (sq);
}
/*
* Move squeue from its current set to newset. Not used for default squeues.
* Bind or unbind the worker thread as appropriate.
*/
static void
{
return;
if (cpuid == -1)
else
}
/*
* Move squeue from its current set to cpuid's set and bind to cpuid.
*/
int
{
return (-1);
return (-1);
}
/*
* The mac layer is calling, asking us to move an squeue to a
* new CPU. This routine is called with cpu_lock held.
*/
void
{
return;
}
}
void *
{
int ip_rx_index;
break;
}
if (ip_rx_index == ILL_MAX_RINGS) {
/*
* We ran out of ILL_MAX_RINGS worth rx_ring structures. If
* we have devices which can overwhelm this limit,
* ILL_MAX_RING should be made configurable. Meanwhile it
* cause no panic because driver will pass ip_input a NULL
* handle which will make IP allocate the default squeue and
* Polling mode will not be used for this ring.
*/
"Reached maximum number of receiving rings (%d) for %s\n",
return (NULL);
}
/* XXX: Hard code it to tcp accept for now */
/* Assign the squeue to the specified CPU as well */
return (rx_ring);
}
/*
* sanitize the squeue etc. Some of the processing
* needs to be done from inside the perimeter.
*/
void
{
/* Just clean one squeue */
return;
}
/*
* Move the squeue to sqset_global_list[0] which holds the set of
* squeues not bound to any cpu. Note that the squeue is still
* considered bound to an ill as long as SQS_ILL_BOUND is set.
*/
/*
* CPU going offline can also trigger a move of the squeue to the
* unbound set sqset_global_list[0]. However the squeue won't be
* recycled for the next use as long as the SQS_ILL_BOUND flag
* is set. Hence we clear the SQS_ILL_BOUND flag only towards the
* end after the move.
*/
}
/*
* Stop the squeue from polling. This needs to be done
* from inside the perimeter.
*/
void
{
}
/*
* Restart polling etc. Needs to be inside the perimeter to
* prevent races.
*/
void
{
/*
* Handle change in number of rings between the quiesce and
* restart operations by checking for a previous quiesce before
* attempting a restart.
*/
return;
}
}
/*
* sanitize all squeues associated with the ill.
*/
void
{
int idx;
}
}
/*
* Used by IP to get the squeue associated with a ring. If the squeue isn't
* yet bound to a CPU, and we're being called directly from the NIC's
* interrupt, then we know what CPU we want to assign the squeue to, so
* dispatch that task to a taskq.
*/
squeue_t *
{
return (IP_SQUEUE_GET(CPU_PSEUDO_RANDOM()));
return (sqp);
}
/*
* Called when a CPU goes offline. It's squeue_set_t is destroyed, and all
* squeues are unboudn and moved to the unbound set.
*/
static void
{
int i;
return;
}
/* Move all squeues to unbound set */
}
}
/* Also move default squeue to unbound set */
for (i = 1; i < sqset_global_size; i++)
if (sqset_global_list[i] == sqs)
break;
ASSERT(i < sqset_global_size);
}
/*
* Reconfiguration callback
*/
/* ARGSUSED */
static int
{
switch (what) {
case CPU_CONFIG:
case CPU_ON:
case CPU_INIT:
case CPU_CPUPART_IN:
break;
case CPU_UNCONFIG:
case CPU_OFF:
case CPU_CPUPART_OUT:
}
break;
default:
break;
}
return (0);
}