#include <sys/types.h>
#include <sys/callb.h>
#include <sys/cpupart.h>
#include <sys/pool.h>
#include <sys/pool_pset.h>
#include <sys/sdt.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#include <sys/vlan.h>
#include <inet/ipsec_impl.h>
#include <inet/ip_impl.h>
#include <inet/sadb.h>
#include <inet/ipsecesp.h>
#include <inet/ipsecah.h>
#include <sys/mac_impl.h>
#include <sys/mac_client_impl.h>
#include <sys/mac_client_priv.h>
#include <sys/mac_soft_ring.h>
#include <sys/mac_flow_impl.h>
#include <sys/mac_stat.h>
static void mac_srs_soft_rings_signal(mac_soft_ring_set_t *,
const mac_soft_ring_state_t);
static void mac_srs_update_fanout_list(mac_soft_ring_set_t *);
static void mac_srs_poll_unbind(mac_soft_ring_set_t *);
static void mac_srs_worker_unbind(mac_soft_ring_set_t *);
static void mac_srs_soft_rings_quiesce(mac_soft_ring_set_t *,
const mac_soft_ring_state_t);
static int mac_srs_cpu_setup(cpu_setup_t, int, void *);
static void mac_srs_worker_bind(mac_soft_ring_set_t *, processorid_t);
static void mac_srs_poll_bind(mac_soft_ring_set_t *, processorid_t);
static void mac_srs_threads_unbind(mac_soft_ring_set_t *);
static void mac_srs_add_glist(mac_soft_ring_set_t *);
static void mac_srs_remove_glist(mac_soft_ring_set_t *);
static void mac_srs_fanout_list_free(mac_soft_ring_set_t *);
static void mac_soft_ring_remove(mac_soft_ring_set_t *, mac_soft_ring_t *);
static int mac_compute_soft_ring_count(flow_entry_t *, int, int);
static void mac_walk_srs_and_bind(int);
static void mac_walk_srs_and_unbind(int);
extern boolean_t mac_latency_optimize;
static kmem_cache_t *mac_srs_cache;
kmem_cache_t *mac_soft_ring_cache;
uint32_t mac_soft_ring_worker_wait = 0;
int mac_poll_enable = B_TRUE;
int mac_soft_ring_max_q_cnt = 1024;
int mac_soft_ring_min_q_cnt = 256;
int mac_soft_ring_poll_thres = 16;
boolean_t mac_tx_serialize = B_FALSE;
uint32_t mac_tx_srs_max_q_cnt = 100000;
uint32_t mac_tx_srs_hiwat = 1000;
uint_t mac_rx_soft_ring_count = 8;
uint_t mac_rx_soft_ring_10gig_count = 8;
static mac_soft_ring_set_t *mac_srs_g_list = NULL;
static krwlock_t mac_srs_g_lock;
boolean_t mac_srs_thread_bind = B_TRUE;
boolean_t mac_tx_intr_retarget = B_FALSE;
boolean_t mac_rx_intr_retarget = B_FALSE;
#define BIND_TX_SRS_AND_SOFT_RINGS(mac_tx_srs, mrp) { \
processorid_t cpuid; \
int i; \
mac_soft_ring_t *softring; \
mac_cpus_t *srs_cpu; \
\
srs_cpu = &mac_tx_srs->srs_cpu; \
cpuid = srs_cpu->mc_tx_fanout_cpus[0]; \
mac_srs_worker_bind(mac_tx_srs, cpuid); \
if (MAC_TX_SOFT_RINGS(mac_tx_srs)) { \
for (i = 0; i < mac_tx_srs->srs_tx_ring_count; i++) { \
cpuid = srs_cpu->mc_tx_fanout_cpus[i]; \
softring = mac_tx_srs->srs_tx_soft_rings[i]; \
if (cpuid != -1) { \
(void) mac_soft_ring_bind(softring, \
cpuid); \
} \
} \
} \
}
#define RETARGETABLE_CLIENT(group, mcip) \
((((group) != NULL) && \
((group)->mrg_state == MAC_GROUP_STATE_RESERVED)) || \
mac_is_primary_client(mcip))
#define MAC_RING_RETARGETABLE(ring) \
(((ring) != NULL) && \
((ring)->mr_info.mri_intr.mi_ddi_handle != NULL) && \
!((ring)->mr_info.mri_intr.mi_ddi_shared))
void
mac_soft_ring_init(void)
{
mac_soft_ring_cache = kmem_cache_create("mac_soft_ring_cache",
sizeof (mac_soft_ring_t), 64, NULL, NULL, NULL, NULL, NULL, 0);
mac_srs_cache = kmem_cache_create("mac_srs_cache",
sizeof (mac_soft_ring_set_t),
64, NULL, NULL, NULL, NULL, NULL, 0);
rw_init(&mac_srs_g_lock, NULL, RW_DEFAULT, NULL);
mutex_enter(&cpu_lock);
register_cpu_setup_func(mac_srs_cpu_setup, NULL);
mutex_exit(&cpu_lock);
}
void
mac_soft_ring_finish(void)
{
mutex_enter(&cpu_lock);
unregister_cpu_setup_func(mac_srs_cpu_setup, NULL);
mutex_exit(&cpu_lock);
rw_destroy(&mac_srs_g_lock);
kmem_cache_destroy(mac_soft_ring_cache);
kmem_cache_destroy(mac_srs_cache);
}
static void
mac_srs_soft_rings_free(mac_soft_ring_set_t *mac_srs)
{
mac_soft_ring_t *softring, *next, *head;
mutex_enter(&mac_srs->srs_lock);
head = mac_srs->srs_soft_ring_head;
mac_srs->srs_soft_ring_head = NULL;
mac_srs->srs_soft_ring_tail = NULL;
mac_srs->srs_soft_ring_count = 0;
mutex_exit(&mac_srs->srs_lock);
for (softring = head; softring != NULL; softring = next) {
next = softring->s_ring_next;
mac_soft_ring_free(softring);
}
}
static void
mac_srs_add_glist(mac_soft_ring_set_t *mac_srs)
{
ASSERT(mac_srs->srs_next == NULL && mac_srs->srs_prev == NULL);
ASSERT(MAC_PERIM_HELD((mac_handle_t)mac_srs->srs_mcip->mci_mip));
rw_enter(&mac_srs_g_lock, RW_WRITER);
mutex_enter(&mac_srs->srs_lock);
ASSERT((mac_srs->srs_state & SRS_IN_GLIST) == 0);
if (mac_srs_g_list == NULL) {
mac_srs_g_list = mac_srs;
} else {
mac_srs->srs_next = mac_srs_g_list;
mac_srs_g_list->srs_prev = mac_srs;
mac_srs->srs_prev = NULL;
mac_srs_g_list = mac_srs;
}
mac_srs->srs_state |= SRS_IN_GLIST;
mutex_exit(&mac_srs->srs_lock);
rw_exit(&mac_srs_g_lock);
}
static void
mac_srs_remove_glist(mac_soft_ring_set_t *mac_srs)
{
ASSERT(MAC_PERIM_HELD((mac_handle_t)mac_srs->srs_mcip->mci_mip));
rw_enter(&mac_srs_g_lock, RW_WRITER);
mutex_enter(&mac_srs->srs_lock);
ASSERT((mac_srs->srs_state & SRS_IN_GLIST) != 0);
if (mac_srs == mac_srs_g_list) {
mac_srs_g_list = mac_srs->srs_next;
if (mac_srs_g_list != NULL)
mac_srs_g_list->srs_prev = NULL;
} else {
mac_srs->srs_prev->srs_next = mac_srs->srs_next;
if (mac_srs->srs_next != NULL)
mac_srs->srs_next->srs_prev = mac_srs->srs_prev;
}
mac_srs->srs_state &= ~SRS_IN_GLIST;
mutex_exit(&mac_srs->srs_lock);
rw_exit(&mac_srs_g_lock);
}
void
mac_srs_client_poll_quiesce(mac_client_impl_t *mcip, mac_soft_ring_set_t *srs)
{
VERIFY(mac_perim_held((mac_handle_t)mcip->mci_mip));
if (srs->srs_type & SRST_CLIENT_POLL_V4) {
for (uint_t i = 0; i < srs->srs_tcp_ring_count; i++) {
mac_soft_ring_t *sr = srs->srs_tcp_soft_rings[i];
if (sr->s_ring_rx_arg2 != NULL) {
mcip->mci_rcb4.mrc_quiesce(
mcip->mci_rcb4.mrc_arg, sr->s_ring_rx_arg2);
}
}
}
if (srs->srs_type & SRST_CLIENT_POLL_V6) {
for (uint_t i = 0; i < srs->srs_tcp6_ring_count; i++) {
mac_soft_ring_t *sr = srs->srs_tcp6_soft_rings[i];
if (sr->s_ring_rx_arg2 != NULL) {
mcip->mci_rcb6.mrc_quiesce(
mcip->mci_rcb6.mrc_arg, sr->s_ring_rx_arg2);
}
}
}
}
void
mac_srs_client_poll_restart(mac_client_impl_t *mcip, mac_soft_ring_set_t *srs)
{
VERIFY(mac_perim_held((mac_handle_t)mcip->mci_mip));
if (srs->srs_type & SRST_CLIENT_POLL_V4) {
for (uint_t i = 0; i < srs->srs_tcp_ring_count; i++) {
mac_soft_ring_t *sr = srs->srs_tcp_soft_rings[i];
if (sr->s_ring_rx_arg2 != NULL) {
mcip->mci_rcb4.mrc_restart(
mcip->mci_rcb4.mrc_arg, sr->s_ring_rx_arg2);
}
}
}
if (srs->srs_type & SRST_CLIENT_POLL_V6) {
for (uint_t i = 0; i < srs->srs_tcp6_ring_count; i++) {
mac_soft_ring_t *sr = srs->srs_tcp6_soft_rings[i];
if (sr->s_ring_rx_arg2 != NULL) {
mcip->mci_rcb6.mrc_restart(
mcip->mci_rcb6.mrc_arg, sr->s_ring_rx_arg2);
}
}
}
}
static void
mac_srs_client_poll_enable_i(mac_soft_ring_set_t *srs, uint_t sr_cnt,
mac_soft_ring_t **udp_rings, mac_soft_ring_t **tcp_rings,
mac_direct_rx_t drx, void *drx_arg, mac_resource_cb_t *rcb)
{
for (uint_t i = 0; i < sr_cnt; i++) {
mac_soft_ring_dls_bypass_enable(udp_rings[i], drx, drx_arg);
}
for (uint_t i = 0; i < sr_cnt; i++) {
mac_soft_ring_poll_enable(tcp_rings[i], drx, drx_arg, rcb,
srs->srs_pri);
}
}
void
mac_srs_client_poll_enable(mac_client_impl_t *mcip, mac_soft_ring_set_t *srs,
boolean_t is_v6)
{
VERIFY3P(srs->srs_mcip, ==, mcip);
VERIFY(mac_perim_held((mac_handle_t)mcip->mci_mip));
if (!(mcip->mci_state_flags & MCIS_CLIENT_POLL_CAPABLE))
return;
if (srs->srs_type & SRST_NO_SOFT_RINGS)
return;
if (is_v6 && mcip->mci_direct_rx.mdrx_v6 != NULL &&
mcip->mci_rcb6.mrc_arg != NULL) {
mac_srs_client_poll_enable_i(srs, srs->srs_tcp_ring_count,
srs->srs_udp6_soft_rings, srs->srs_tcp6_soft_rings,
mcip->mci_direct_rx.mdrx_v6,
mcip->mci_direct_rx.mdrx_arg_v6, &mcip->mci_rcb6);
mutex_enter(&srs->srs_lock);
srs->srs_type |= (SRST_CLIENT_POLL_V6 | SRST_DLS_BYPASS_V6);
mutex_exit(&srs->srs_lock);
} else if (!is_v6 && mcip->mci_direct_rx.mdrx_v4 != NULL &&
mcip->mci_rcb4.mrc_arg != NULL) {
mac_srs_client_poll_enable_i(srs, srs->srs_tcp_ring_count,
srs->srs_udp_soft_rings, srs->srs_tcp_soft_rings,
mcip->mci_direct_rx.mdrx_v4,
mcip->mci_direct_rx.mdrx_arg_v4, &mcip->mci_rcb4);
mutex_enter(&srs->srs_lock);
srs->srs_type |= (SRST_CLIENT_POLL_V4 | SRST_DLS_BYPASS_V4);
mutex_exit(&srs->srs_lock);
}
}
static void
mac_srs_client_poll_disable_i(mac_client_impl_t *mcip, uint_t sr_cnt,
mac_soft_ring_t **udp_rings, mac_soft_ring_t **tcp_rings,
mac_resource_cb_t *rcb)
{
for (uint_t i = 0; i < sr_cnt; i++) {
mac_soft_ring_poll_disable(tcp_rings[i], rcb, mcip);
}
for (uint_t i = 0; i < sr_cnt; i++) {
mac_soft_ring_t *udp_sr = udp_rings[i];
VERIFY3P(udp_sr->s_ring_rx_arg2, ==, NULL);
mac_soft_ring_dls_bypass_disable(udp_sr, mcip);
}
}
void
mac_srs_client_poll_disable(mac_client_impl_t *mcip, mac_soft_ring_set_t *srs,
boolean_t is_v6)
{
VERIFY(mac_perim_held((mac_handle_t)mcip->mci_mip));
if (srs->srs_type & SRST_NO_SOFT_RINGS)
return;
mutex_enter(&srs->srs_lock);
if (!is_v6 && !(srs->srs_type & SRST_CLIENT_POLL_V4)) {
VERIFY(!(srs->srs_type & SRST_DLS_BYPASS_V4));
mutex_exit(&srs->srs_lock);
return;
}
if (is_v6 && !(srs->srs_type & SRST_CLIENT_POLL_V6)) {
VERIFY(!(srs->srs_type & SRST_DLS_BYPASS_V6));
mutex_exit(&srs->srs_lock);
return;
}
if (is_v6) {
srs->srs_type &= ~(SRST_CLIENT_POLL_V6 |
SRST_DLS_BYPASS_V6);
} else {
srs->srs_type &= ~(SRST_CLIENT_POLL_V4 |
SRST_DLS_BYPASS_V4);
}
mutex_exit(&srs->srs_lock);
if (is_v6) {
mac_srs_client_poll_disable_i(mcip, srs->srs_tcp_ring_count,
srs->srs_udp6_soft_rings, srs->srs_tcp6_soft_rings,
&mcip->mci_rcb6);
} else {
mac_srs_client_poll_disable_i(mcip, srs->srs_tcp_ring_count,
srs->srs_udp_soft_rings, srs->srs_tcp_soft_rings,
&mcip->mci_rcb4);
}
}
static void
mac_srs_poll_state_change(mac_soft_ring_set_t *mac_srs,
boolean_t turn_off_poll_capab, mac_rx_func_t rx_func)
{
boolean_t need_restart = B_FALSE;
mac_srs_rx_t *srs_rx = &mac_srs->srs_rx;
mac_ring_t *ring;
if (!SRS_QUIESCED(mac_srs)) {
mac_rx_srs_quiesce(mac_srs, SRS_QUIESCE);
need_restart = B_TRUE;
}
ring = mac_srs->srs_ring;
if ((ring != NULL) &&
(ring->mr_classify_type == MAC_HW_CLASSIFIER)) {
if (turn_off_poll_capab)
mac_srs->srs_state &= ~SRS_POLLING_CAPAB;
else if (mac_poll_enable)
mac_srs->srs_state |= SRS_POLLING_CAPAB;
}
srs_rx->sr_lower_proc = rx_func;
if (need_restart)
mac_rx_srs_restart(mac_srs);
}
static processorid_t
mac_next_bind_cpu(cpupart_t *cpupart)
{
static cpu_t *cp = NULL;
cpu_t *cp_start;
ASSERT(MUTEX_HELD(&cpu_lock));
if (cp == NULL)
cp = cpu_list;
cp = cp->cpu_next_onln;
cp_start = cp;
do {
if ((cpupart == NULL) || (cp->cpu_part == cpupart))
return (cp->cpu_id);
} while ((cp = cp->cpu_next_onln) != cp_start);
return (-1);
}
static int
mac_srs_cpu_setup(cpu_setup_t what, int id, void *arg)
{
ASSERT(MUTEX_HELD(&cpu_lock));
switch (what) {
case CPU_CONFIG:
case CPU_ON:
case CPU_CPUPART_IN:
mac_walk_srs_and_bind(id);
break;
case CPU_UNCONFIG:
case CPU_OFF:
case CPU_CPUPART_OUT:
mac_walk_srs_and_unbind(id);
break;
default:
break;
}
return (0);
}
boolean_t mac_check_overlay = B_TRUE;
boolean_t mac_use_bw_heuristic = B_TRUE;
static int
mac_compute_soft_ring_count(flow_entry_t *flent, int rx_srs_cnt, int maxcpus)
{
uint64_t cpu_speed, bw = 0;
int srings = 0;
boolean_t bw_enabled = B_FALSE;
mac_client_impl_t *mcip = flent->fe_mcip;
ASSERT(!(flent->fe_type & FLOW_USER));
if (flent->fe_resource_props.mrp_mask & MRP_MAXBW &&
mac_use_bw_heuristic) {
bw_enabled = B_TRUE;
bw = flent->fe_resource_props.mrp_maxbw;
}
if (!bw_enabled) {
if (mac_soft_ring_enable)
srings = mac_rx_soft_ring_count;
flent->fe_nic_speed = mac_client_stat_get(
(mac_client_handle_t)flent->fe_mcip, MAC_STAT_IFSPEED);
if (((flent->fe_nic_speed)/1000000) > 1000 &&
mac_rx_soft_ring_10gig_count > 0) {
srings = mac_rx_soft_ring_10gig_count;
if (mac_soft_ring_enable)
srings = srings * 2;
} else if (mac_check_overlay == B_TRUE &&
(mcip->mci_state_flags & MCIS_IS_VNIC) != 0) {
mac_handle_t mh = (mac_handle_t)mcip->mci_mip;
if (mac_is_overlay(mh) == B_TRUE) {
srings = mac_rx_soft_ring_10gig_count;
}
}
} else {
cpu_speed = (uint64_t)CPU->cpu_type_info.pi_clock;
bw = bw/1000000;
if (bw >= 1000) {
srings = bw/cpu_speed;
if (srings <= 1 && mac_soft_ring_enable) {
srings = 2;
}
}
}
if (rx_srs_cnt > 1) {
int remainder;
remainder = srings%rx_srs_cnt;
srings = srings/rx_srs_cnt;
if (remainder != 0)
srings++;
if (srings == 1)
srings = 0;
}
srings = min(srings, maxcpus);
srings = min(srings, MAX_SR_FANOUT);
return (srings);
}
static void
mac_tx_cpu_init(flow_entry_t *flent, mac_resource_props_t *mrp,
cpupart_t *cpupart)
{
mac_soft_ring_set_t *tx_srs = flent->fe_tx_srs;
mac_srs_tx_t *srs_tx = &tx_srs->srs_tx;
mac_cpus_t *srs_cpu = &tx_srs->srs_cpu;
mac_soft_ring_t *sringp;
mac_ring_t *ring;
processorid_t worker_cpuid;
boolean_t retargetable_client = B_FALSE;
int i, j;
if (RETARGETABLE_CLIENT((mac_group_t *)flent->fe_tx_ring_group,
flent->fe_mcip)) {
retargetable_client = B_TRUE;
}
if (MAC_TX_SOFT_RINGS(tx_srs)) {
if (mrp != NULL)
j = mrp->mrp_ncpus - 1;
for (i = 0; i < tx_srs->srs_tx_ring_count; i++) {
if (mrp != NULL) {
if (j < 0)
j = mrp->mrp_ncpus - 1;
worker_cpuid = mrp->mrp_cpu[j];
} else {
worker_cpuid = -1;
}
sringp = tx_srs->srs_tx_soft_rings[i];
ring = (mac_ring_t *)sringp->s_ring_tx_arg2;
srs_cpu->mc_tx_fanout_cpus[i] = worker_cpuid;
if (MAC_RING_RETARGETABLE(ring) &&
retargetable_client) {
mutex_enter(&cpu_lock);
srs_cpu->mc_tx_intr_cpu[i] =
(mrp != NULL) ? mrp->mrp_cpu[j] :
(mac_tx_intr_retarget ?
mac_next_bind_cpu(cpupart) : -1);
mutex_exit(&cpu_lock);
} else {
srs_cpu->mc_tx_intr_cpu[i] = -1;
}
if (mrp != NULL)
j--;
}
} else {
srs_cpu->mc_tx_fanout_cpus[0] =
(mrp != NULL) ? mrp->mrp_cpu[mrp->mrp_ncpus - 1] : -1;
ring = (mac_ring_t *)srs_tx->st_arg2;
if (MAC_RING_RETARGETABLE(ring) && retargetable_client) {
mutex_enter(&cpu_lock);
srs_cpu->mc_tx_intr_cpu[0] = (mrp != NULL) ?
mrp->mrp_cpu[mrp->mrp_ncpus - 1] :
(mac_tx_intr_retarget ?
mac_next_bind_cpu(cpupart) : -1);
mutex_exit(&cpu_lock);
} else {
srs_cpu->mc_tx_intr_cpu[0] = -1;
}
}
}
static void
mac_flow_user_cpu_init(flow_entry_t *flent, mac_resource_props_t *mrp)
{
mac_soft_ring_set_t *rx_srs, *tx_srs;
int i, srs_cnt;
mac_cpus_t *srs_cpu;
int no_of_cpus, cpu_cnt;
int rx_srs_cnt, reqd_rx_cpu_cnt;
int fanout_cpu_cnt, reqd_tx_cpu_cnt;
int reqd_poll_worker_cnt, fanout_cnt_per_srs;
mac_resource_props_t *emrp = &flent->fe_effective_props;
ASSERT(mrp->mrp_fanout_mode == MCM_CPUS);
ASSERT(mrp->mrp_ncpus > 0);
no_of_cpus = mrp->mrp_ncpus;
if (mrp->mrp_rx_intr_cpu != -1) {
for (i = 0; i < no_of_cpus; i++) {
if (mrp->mrp_cpu[i] == mrp->mrp_rx_intr_cpu)
break;
}
mrp->mrp_cpu[i] = mrp->mrp_cpu[0];
mrp->mrp_cpu[0] = mrp->mrp_rx_intr_cpu;
}
rx_srs_cnt = (flent->fe_rx_srs_cnt > 1) ?
(flent->fe_rx_srs_cnt - 1) : flent->fe_rx_srs_cnt;
reqd_rx_cpu_cnt = mac_latency_optimize ?
(rx_srs_cnt * 2) : (rx_srs_cnt * 3);
tx_srs = flent->fe_tx_srs;
reqd_tx_cpu_cnt = MAC_TX_SOFT_RINGS(tx_srs) ?
tx_srs->srs_tx_ring_count : 1;
reqd_poll_worker_cnt = mac_latency_optimize ?
rx_srs_cnt : rx_srs_cnt * 2;
if (no_of_cpus >= (reqd_rx_cpu_cnt + reqd_tx_cpu_cnt)) {
fanout_cpu_cnt = no_of_cpus -
reqd_poll_worker_cnt - reqd_tx_cpu_cnt;
fanout_cnt_per_srs = fanout_cpu_cnt/rx_srs_cnt;
fanout_cnt_per_srs = min(fanout_cnt_per_srs, MAX_SR_FANOUT);
cpu_cnt = 0;
rx_srs = flent->fe_rx_srs[0];
ASSERT(rx_srs->srs_ring == NULL);
if (rx_srs->srs_fanout_state == SRS_FANOUT_INIT)
rx_srs->srs_fanout_state = SRS_FANOUT_REINIT;
srs_cpu = &rx_srs->srs_cpu;
srs_cpu->mc_ncpus = no_of_cpus;
bcopy(mrp->mrp_cpu,
srs_cpu->mc_cpus, sizeof (srs_cpu->mc_cpus));
srs_cpu->mc_rx_fanout_cnt = fanout_cnt_per_srs;
srs_cpu->mc_rx_pollid = mrp->mrp_cpu[cpu_cnt++];
srs_cpu->mc_rx_intr_cpu = srs_cpu->mc_rx_pollid;
srs_cpu->mc_rx_workerid = (mac_latency_optimize ?
srs_cpu->mc_rx_pollid : mrp->mrp_cpu[cpu_cnt++]);
for (i = 0; i < fanout_cnt_per_srs; i++)
srs_cpu->mc_rx_fanout_cpus[i] = mrp->mrp_cpu[cpu_cnt++];
if (flent->fe_rx_srs_cnt > 1) {
cpu_cnt = 0;
for (srs_cnt = 1;
srs_cnt < flent->fe_rx_srs_cnt; srs_cnt++) {
rx_srs = flent->fe_rx_srs[srs_cnt];
ASSERT(rx_srs->srs_ring != NULL);
if (rx_srs->srs_fanout_state ==
SRS_FANOUT_INIT) {
rx_srs->srs_fanout_state =
SRS_FANOUT_REINIT;
}
srs_cpu = &rx_srs->srs_cpu;
srs_cpu->mc_ncpus = no_of_cpus;
bcopy(mrp->mrp_cpu, srs_cpu->mc_cpus,
sizeof (srs_cpu->mc_cpus));
srs_cpu->mc_rx_fanout_cnt = fanout_cnt_per_srs;
srs_cpu->mc_rx_pollid = mrp->mrp_cpu[cpu_cnt++];
srs_cpu->mc_rx_intr_cpu = srs_cpu->mc_rx_pollid;
srs_cpu->mc_rx_workerid =
(mac_latency_optimize ?
srs_cpu->mc_rx_pollid :
mrp->mrp_cpu[cpu_cnt++]);
for (i = 0; i < fanout_cnt_per_srs; i++) {
srs_cpu->mc_rx_fanout_cpus[i] =
mrp->mrp_cpu[cpu_cnt++];
}
ASSERT(cpu_cnt <= no_of_cpus);
}
}
goto tx_cpu_init;
}
if (no_of_cpus >= (rx_srs_cnt + reqd_tx_cpu_cnt)) {
boolean_t worker_assign = B_FALSE;
if (no_of_cpus >= (reqd_poll_worker_cnt + reqd_tx_cpu_cnt))
worker_assign = B_TRUE;
rx_srs = flent->fe_rx_srs[0];
ASSERT(rx_srs->srs_ring == NULL);
if (rx_srs->srs_fanout_state == SRS_FANOUT_INIT)
rx_srs->srs_fanout_state = SRS_FANOUT_REINIT;
cpu_cnt = 0;
srs_cpu = &rx_srs->srs_cpu;
srs_cpu->mc_ncpus = no_of_cpus;
bcopy(mrp->mrp_cpu,
srs_cpu->mc_cpus, sizeof (srs_cpu->mc_cpus));
srs_cpu->mc_rx_fanout_cnt = 1;
srs_cpu->mc_rx_pollid = mrp->mrp_cpu[cpu_cnt++];
srs_cpu->mc_rx_intr_cpu = srs_cpu->mc_rx_pollid;
srs_cpu->mc_rx_workerid =
((!mac_latency_optimize && worker_assign) ?
mrp->mrp_cpu[cpu_cnt++] : srs_cpu->mc_rx_pollid);
srs_cpu->mc_rx_fanout_cpus[0] = mrp->mrp_cpu[cpu_cnt];
if (flent->fe_rx_srs_cnt > 1) {
cpu_cnt = 0;
for (srs_cnt = 1;
srs_cnt < flent->fe_rx_srs_cnt; srs_cnt++) {
rx_srs = flent->fe_rx_srs[srs_cnt];
ASSERT(rx_srs->srs_ring != NULL);
if (rx_srs->srs_fanout_state ==
SRS_FANOUT_INIT) {
rx_srs->srs_fanout_state =
SRS_FANOUT_REINIT;
}
srs_cpu = &rx_srs->srs_cpu;
srs_cpu->mc_ncpus = no_of_cpus;
bcopy(mrp->mrp_cpu, srs_cpu->mc_cpus,
sizeof (srs_cpu->mc_cpus));
srs_cpu->mc_rx_pollid =
mrp->mrp_cpu[cpu_cnt];
srs_cpu->mc_rx_intr_cpu = srs_cpu->mc_rx_pollid;
srs_cpu->mc_rx_workerid =
((!mac_latency_optimize && worker_assign) ?
mrp->mrp_cpu[++cpu_cnt] :
srs_cpu->mc_rx_pollid);
srs_cpu->mc_rx_fanout_cnt = 1;
srs_cpu->mc_rx_fanout_cpus[0] =
mrp->mrp_cpu[cpu_cnt];
cpu_cnt++;
ASSERT(cpu_cnt <= no_of_cpus);
}
}
goto tx_cpu_init;
}
cpu_cnt = 0;
for (srs_cnt = 0; srs_cnt < flent->fe_rx_srs_cnt; srs_cnt++) {
rx_srs = flent->fe_rx_srs[srs_cnt];
srs_cpu = &rx_srs->srs_cpu;
if (rx_srs->srs_fanout_state == SRS_FANOUT_INIT)
rx_srs->srs_fanout_state = SRS_FANOUT_REINIT;
srs_cpu->mc_ncpus = no_of_cpus;
bcopy(mrp->mrp_cpu,
srs_cpu->mc_cpus, sizeof (srs_cpu->mc_cpus));
srs_cpu->mc_rx_fanout_cnt = 1;
srs_cpu->mc_rx_pollid = mrp->mrp_cpu[cpu_cnt];
srs_cpu->mc_rx_intr_cpu = srs_cpu->mc_rx_pollid;
srs_cpu->mc_rx_workerid = mrp->mrp_cpu[cpu_cnt];
srs_cpu->mc_rx_fanout_cpus[0] = mrp->mrp_cpu[cpu_cnt];
if (++cpu_cnt >= no_of_cpus)
cpu_cnt = 0;
}
tx_cpu_init:
mac_tx_cpu_init(flent, mrp, NULL);
for (i = 0; i < mrp->mrp_ncpus; i++) {
emrp->mrp_cpu[i] = mrp->mrp_cpu[i];
}
emrp->mrp_ncpus = mrp->mrp_ncpus;
emrp->mrp_mask = mrp->mrp_mask;
bzero(emrp->mrp_pool, MAXPATHLEN);
}
static void
mac_flow_cpu_init(flow_entry_t *flent, cpupart_t *cpupart)
{
mac_soft_ring_set_t *rx_srs;
processorid_t cpuid;
int i, j, k, srs_cnt, maxcpus, soft_ring_cnt = 0;
mac_cpus_t *srs_cpu;
mac_resource_props_t *emrp = &flent->fe_effective_props;
maxcpus = (cpupart != NULL) ? cpupart->cp_ncpus : ncpus;
maxcpus = MIN(maxcpus, MRP_NCPUS - 2);
soft_ring_cnt = mac_compute_soft_ring_count(flent,
flent->fe_rx_srs_cnt - 1, maxcpus);
if (soft_ring_cnt == 0) {
soft_ring_cnt = 1;
}
emrp->mrp_ncpus = 0;
for (srs_cnt = 0; srs_cnt < flent->fe_rx_srs_cnt &&
emrp->mrp_ncpus < MRP_NCPUS; srs_cnt++) {
rx_srs = flent->fe_rx_srs[srs_cnt];
srs_cpu = &rx_srs->srs_cpu;
if (rx_srs->srs_fanout_state == SRS_FANOUT_INIT)
rx_srs->srs_fanout_state = SRS_FANOUT_REINIT;
srs_cpu->mc_ncpus = soft_ring_cnt;
srs_cpu->mc_rx_fanout_cnt = soft_ring_cnt;
mutex_enter(&cpu_lock);
for (j = 0; j < soft_ring_cnt; j++) {
cpuid = mac_next_bind_cpu(cpupart);
srs_cpu->mc_cpus[j] = cpuid;
srs_cpu->mc_rx_fanout_cpus[j] = cpuid;
}
cpuid = mac_next_bind_cpu(cpupart);
srs_cpu->mc_rx_pollid = cpuid;
srs_cpu->mc_rx_intr_cpu = (mac_rx_intr_retarget ?
srs_cpu->mc_rx_pollid : -1);
srs_cpu->mc_ncpus++;
srs_cpu->mc_cpus[j++] = cpuid;
if (!mac_latency_optimize) {
cpuid = mac_next_bind_cpu(cpupart);
srs_cpu->mc_ncpus++;
srs_cpu->mc_cpus[j++] = cpuid;
}
srs_cpu->mc_rx_workerid = cpuid;
mutex_exit(&cpu_lock);
for (i = 0; i < srs_cpu->mc_ncpus &&
emrp->mrp_ncpus < MRP_NCPUS; i++) {
for (j = 0; j < emrp->mrp_ncpus; j++) {
if (emrp->mrp_cpu[j] == srs_cpu->mc_cpus[i])
break;
}
if (j == emrp->mrp_ncpus) {
emrp->mrp_cpu[emrp->mrp_ncpus++] =
srs_cpu->mc_cpus[i];
}
}
}
mac_tx_cpu_init(flent, NULL, cpupart);
}
static void
mac_srs_fanout_list_alloc(mac_soft_ring_set_t *mac_srs)
{
mac_client_impl_t *mcip = mac_srs->srs_mcip;
if (mac_srs->srs_type & SRST_TX) {
mac_srs->srs_tx_soft_rings = (mac_soft_ring_t **)
kmem_zalloc(sizeof (mac_soft_ring_t *) *
MAX_RINGS_PER_GROUP, KM_SLEEP);
if (mcip->mci_state_flags & MCIS_IS_AGGR_CLIENT) {
mac_srs_tx_t *tx = &mac_srs->srs_tx;
tx->st_soft_rings = (mac_soft_ring_t **)
kmem_zalloc(sizeof (mac_soft_ring_t *) *
MAX_RINGS_PER_GROUP, KM_SLEEP);
}
} else {
mac_srs->srs_tcp_soft_rings = (mac_soft_ring_t **)
kmem_zalloc(sizeof (mac_soft_ring_t *) * MAX_SR_FANOUT,
KM_SLEEP);
mac_srs->srs_tcp6_soft_rings = (mac_soft_ring_t **)
kmem_zalloc(sizeof (mac_soft_ring_t *) * MAX_SR_FANOUT,
KM_SLEEP);
mac_srs->srs_udp_soft_rings = (mac_soft_ring_t **)
kmem_zalloc(sizeof (mac_soft_ring_t *) * MAX_SR_FANOUT,
KM_SLEEP);
mac_srs->srs_udp6_soft_rings = (mac_soft_ring_t **)
kmem_zalloc(sizeof (mac_soft_ring_t *) * MAX_SR_FANOUT,
KM_SLEEP);
mac_srs->srs_oth_soft_rings = (mac_soft_ring_t **)
kmem_zalloc(sizeof (mac_soft_ring_t *) * MAX_SR_FANOUT,
KM_SLEEP);
}
}
static void
mac_srs_worker_bind(mac_soft_ring_set_t *mac_srs, processorid_t cpuid)
{
cpu_t *cp;
boolean_t clear = B_FALSE;
ASSERT(MUTEX_HELD(&cpu_lock));
if (!mac_srs_thread_bind)
return;
cp = cpu_get(cpuid);
if (cp == NULL || !cpu_is_online(cp))
return;
mutex_enter(&mac_srs->srs_lock);
mac_srs->srs_state |= SRS_WORKER_BOUND;
if (mac_srs->srs_worker_cpuid != -1)
clear = B_TRUE;
mac_srs->srs_worker_cpuid = cpuid;
mutex_exit(&mac_srs->srs_lock);
if (clear)
thread_affinity_clear(mac_srs->srs_worker);
thread_affinity_set(mac_srs->srs_worker, cpuid);
DTRACE_PROBE1(worker__CPU, processorid_t, cpuid);
}
static void
mac_srs_poll_bind(mac_soft_ring_set_t *mac_srs, processorid_t cpuid)
{
cpu_t *cp;
boolean_t clear = B_FALSE;
ASSERT(MUTEX_HELD(&cpu_lock));
if (!mac_srs_thread_bind || mac_srs->srs_poll_thr == NULL)
return;
cp = cpu_get(cpuid);
if (cp == NULL || !cpu_is_online(cp))
return;
mutex_enter(&mac_srs->srs_lock);
mac_srs->srs_state |= SRS_POLL_BOUND;
if (mac_srs->srs_poll_cpuid != -1)
clear = B_TRUE;
mac_srs->srs_poll_cpuid = cpuid;
mutex_exit(&mac_srs->srs_lock);
if (clear)
thread_affinity_clear(mac_srs->srs_poll_thr);
thread_affinity_set(mac_srs->srs_poll_thr, cpuid);
DTRACE_PROBE1(poll__CPU, processorid_t, cpuid);
}
void
mac_rx_srs_retarget_intr(mac_soft_ring_set_t *mac_srs, processorid_t cpuid)
{
cpu_t *cp;
mac_ring_t *ring = mac_srs->srs_ring;
mac_intr_t *mintr = &ring->mr_info.mri_intr;
flow_entry_t *flent = mac_srs->srs_flent;
boolean_t primary = mac_is_primary_client(mac_srs->srs_mcip);
ASSERT(MUTEX_HELD(&cpu_lock));
if (ring == NULL || mintr->mi_ddi_shared || cpuid == -1 ||
(mintr->mi_ddi_handle == NULL && !primary) || (primary &&
mintr->mi_ddi_handle == NULL && flent->fe_rx_srs_cnt > 2)) {
mac_srs->srs_cpu.mc_rx_intr_cpu = -1;
return;
}
if (mintr->mi_ddi_handle == NULL)
return;
cp = cpu_get(cpuid);
if (cp == NULL || !cpu_is_online(cp))
return;
mutex_exit(&cpu_lock);
if (set_intr_affinity(mintr->mi_ddi_handle, cpuid) == DDI_SUCCESS)
mac_srs->srs_cpu.mc_rx_intr_cpu = cpuid;
else
mac_srs->srs_cpu.mc_rx_intr_cpu = -1;
mutex_enter(&cpu_lock);
}
void
mac_tx_srs_retarget_intr(mac_soft_ring_set_t *mac_srs)
{
cpu_t *cp;
mac_ring_t *ring;
mac_intr_t *mintr;
mac_soft_ring_t *sringp;
mac_srs_tx_t *srs_tx;
mac_cpus_t *srs_cpu;
processorid_t cpuid;
int i;
ASSERT(MUTEX_HELD(&cpu_lock));
srs_cpu = &mac_srs->srs_cpu;
if (MAC_TX_SOFT_RINGS(mac_srs)) {
for (i = 0; i < mac_srs->srs_tx_ring_count; i++) {
sringp = mac_srs->srs_tx_soft_rings[i];
ring = (mac_ring_t *)sringp->s_ring_tx_arg2;
cpuid = srs_cpu->mc_tx_intr_cpu[i];
cp = cpu_get(cpuid);
if (cp == NULL || !cpu_is_online(cp) ||
!MAC_RING_RETARGETABLE(ring)) {
srs_cpu->mc_tx_retargeted_cpu[i] = -1;
continue;
}
mintr = &ring->mr_info.mri_intr;
mutex_exit(&cpu_lock);
if (set_intr_affinity(mintr->mi_ddi_handle,
cpuid) == DDI_SUCCESS) {
srs_cpu->mc_tx_retargeted_cpu[i] = cpuid;
} else {
srs_cpu->mc_tx_retargeted_cpu[i] = -1;
}
mutex_enter(&cpu_lock);
}
} else {
cpuid = srs_cpu->mc_tx_intr_cpu[0];
cp = cpu_get(cpuid);
if (cp == NULL || !cpu_is_online(cp)) {
srs_cpu->mc_tx_retargeted_cpu[0] = -1;
return;
}
srs_tx = &mac_srs->srs_tx;
ring = (mac_ring_t *)srs_tx->st_arg2;
if (MAC_RING_RETARGETABLE(ring)) {
mintr = &ring->mr_info.mri_intr;
mutex_exit(&cpu_lock);
if ((set_intr_affinity(mintr->mi_ddi_handle,
cpuid) == DDI_SUCCESS)) {
srs_cpu->mc_tx_retargeted_cpu[0] = cpuid;
} else {
srs_cpu->mc_tx_retargeted_cpu[0] = -1;
}
mutex_enter(&cpu_lock);
}
}
}
static void
mac_walk_srs_and_bind(int cpuid)
{
mac_soft_ring_set_t *mac_srs;
mac_soft_ring_t *soft_ring;
rw_enter(&mac_srs_g_lock, RW_READER);
if ((mac_srs = mac_srs_g_list) == NULL)
goto done;
for (; mac_srs != NULL; mac_srs = mac_srs->srs_next) {
if (mac_srs->srs_worker_cpuid == -1 &&
mac_srs->srs_worker_cpuid_save == cpuid) {
mac_srs->srs_worker_cpuid_save = -1;
mac_srs_worker_bind(mac_srs, cpuid);
}
if (!(mac_srs->srs_type & SRST_TX)) {
if (mac_srs->srs_poll_cpuid == -1 &&
mac_srs->srs_poll_cpuid_save == cpuid) {
mac_srs->srs_poll_cpuid_save = -1;
mac_srs_poll_bind(mac_srs, cpuid);
}
}
mutex_enter(&mac_srs->srs_lock);
for (soft_ring = mac_srs->srs_soft_ring_head; soft_ring != NULL;
soft_ring = soft_ring->s_ring_next) {
if (soft_ring->s_ring_cpuid == -1 &&
soft_ring->s_ring_cpuid_save == cpuid) {
soft_ring->s_ring_cpuid_save = -1;
(void) mac_soft_ring_bind(soft_ring, cpuid);
}
}
mutex_exit(&mac_srs->srs_lock);
}
done:
rw_exit(&mac_srs_g_lock);
}
void
mac_update_srs_priority(mac_soft_ring_set_t *mac_srs, pri_t prival)
{
mac_soft_ring_t *ringp;
mac_srs->srs_pri = prival;
thread_lock(mac_srs->srs_worker);
(void) thread_change_pri(mac_srs->srs_worker, mac_srs->srs_pri, 0);
thread_unlock(mac_srs->srs_worker);
if (mac_srs->srs_poll_thr != NULL) {
thread_lock(mac_srs->srs_poll_thr);
(void) thread_change_pri(mac_srs->srs_poll_thr,
mac_srs->srs_pri, 0);
thread_unlock(mac_srs->srs_poll_thr);
}
if ((ringp = mac_srs->srs_soft_ring_head) == NULL)
return;
while (ringp != mac_srs->srs_soft_ring_tail) {
thread_lock(ringp->s_ring_worker);
(void) thread_change_pri(ringp->s_ring_worker,
mac_srs->srs_pri, 0);
thread_unlock(ringp->s_ring_worker);
ringp = ringp->s_ring_next;
}
ASSERT(ringp == mac_srs->srs_soft_ring_tail);
thread_lock(ringp->s_ring_worker);
(void) thread_change_pri(ringp->s_ring_worker, mac_srs->srs_pri, 0);
thread_unlock(ringp->s_ring_worker);
}
static void
mac_bw_ctl_set_state(mac_bw_ctl_t *bw, const boolean_t do_enable,
const mac_resource_props_t *mrp)
{
VERIFY(MUTEX_HELD(&bw->mac_bw_lock));
if (do_enable) {
const boolean_t was_disabled = !mac_bw_ctl_is_enabled(bw);
bw->mac_bw_state |= BW_ENABLED;
bw->mac_bw_limit = FLOW_BYTES_PER_TICK(mrp->mrp_maxbw);
bw->mac_bw_drop_threshold = bw->mac_bw_limit << 1;
if (was_disabled) {
bw->mac_bw_state &= ~BW_ENFORCED;
bw->mac_bw_curr_time = gethrtime();
bw->mac_bw_used = 0;
bw->mac_bw_sz = 0;
} else if (bw->mac_bw_used < bw->mac_bw_limit) {
bw->mac_bw_state &= ~BW_ENFORCED;
}
} else {
bw->mac_bw_state &= ~(BW_ENABLED | BW_ENFORCED);
}
}
static void
mac_srs_update_drain_proc(mac_soft_ring_set_t *srs)
{
mac_srs_drain_proc_t drain_fn = NULL;
if ((srs->srs_type & SRST_TX) != 0) {
drain_fn = mac_tx_srs_drain;
} else if (mac_srs_is_bw_controlled(srs)) {
drain_fn = mac_rx_srs_drain_bw;
} else {
drain_fn = mac_rx_srs_drain;
}
VERIFY3P(drain_fn, !=, NULL);
srs->srs_drain_func = drain_fn;
}
static void
mac_tx_srs_update_bwlimit_state(mac_soft_ring_set_t *srs,
const boolean_t is_enabled)
{
uint32_t ring_info = 0;
mac_srs_tx_t *srs_tx = &srs->srs_tx;
mac_client_impl_t *mcip = srs->srs_mcip;
VERIFY3U(srs->srs_type & SRST_TX, !=, 0);
mac_tx_client_quiesce((mac_client_handle_t)mcip);
mutex_enter(&srs->srs_lock);
mac_tx_srs_mode_t tx_mode = srs_tx->st_mode;
if (is_enabled) {
if (tx_mode != SRS_TX_BW && tx_mode != SRS_TX_BW_FANOUT &&
tx_mode != SRS_TX_BW_AGGR) {
if (tx_mode == SRS_TX_SERIALIZE ||
tx_mode == SRS_TX_DEFAULT) {
srs_tx->st_mode = SRS_TX_BW;
} else if (tx_mode == SRS_TX_FANOUT) {
srs_tx->st_mode = SRS_TX_BW_FANOUT;
} else if (tx_mode == SRS_TX_AGGR) {
srs_tx->st_mode = SRS_TX_BW_AGGR;
} else {
panic("Unhandled BW->non-BW mode change: %d",
tx_mode);
}
}
srs->srs_type |= SRST_BW_CONTROL;
} else {
if (tx_mode == SRS_TX_BW) {
if (srs_tx->st_arg2 != NULL) {
mac_ring_handle_t mrh =
(mac_ring_handle_t)srs_tx->st_arg2;
ring_info = mac_hwring_getinfo(mrh);
}
if (mac_tx_serialize ||
(ring_info & MAC_RING_TX_SERIALIZE)) {
srs_tx->st_mode = SRS_TX_SERIALIZE;
} else {
srs_tx->st_mode = SRS_TX_DEFAULT;
}
} else if (tx_mode == SRS_TX_BW_FANOUT) {
srs_tx->st_mode = SRS_TX_FANOUT;
} else if (tx_mode == SRS_TX_BW_AGGR) {
srs_tx->st_mode = SRS_TX_AGGR;
}
srs->srs_type &= ~SRST_BW_CONTROL;
}
srs_tx->st_func = mac_tx_get_func(srs_tx->st_mode);
mutex_exit(&srs->srs_lock);
mac_tx_client_restart((mac_client_handle_t)mcip);
}
static void
mac_srs_update_rx_bwlimit_state(mac_soft_ring_set_t *srs,
const boolean_t is_enabled)
{
VERIFY3U(srs->srs_type & SRST_TX, ==, 0);
mutex_enter(&srs->srs_lock);
if (is_enabled) {
srs->srs_type |= SRST_BW_CONTROL;
} else {
srs->srs_type &= ~SRST_BW_CONTROL;
}
mac_srs_update_drain_proc(srs);
mutex_exit(&srs->srs_lock);
}
void
mac_srs_update_bwlimit(flow_entry_t *flent, mac_resource_props_t *mrp)
{
const boolean_t enable = mrp->mrp_maxbw != MRP_MAXBW_RESETVAL;
mutex_enter(&flent->fe_rx_bw.mac_bw_lock);
mac_bw_ctl_set_state(&flent->fe_rx_bw, enable, mrp);
mutex_exit(&flent->fe_rx_bw.mac_bw_lock);
for (uint32_t i = 0; i < flent->fe_rx_srs_cnt; i++) {
mac_srs_update_rx_bwlimit_state(flent->fe_rx_srs[i], enable);
}
mutex_enter(&flent->fe_tx_bw.mac_bw_lock);
mac_bw_ctl_set_state(&flent->fe_tx_bw, enable, mrp);
mutex_exit(&flent->fe_tx_bw.mac_bw_lock);
mac_tx_srs_update_bwlimit_state(flent->fe_tx_srs, enable);
}
void
mac_client_update_classifier(mac_client_impl_t *mcip, boolean_t enable)
{
flow_entry_t *flent = mcip->mci_flent;
int i;
mac_impl_t *mip = mcip->mci_mip;
mac_rx_func_t rx_func;
uint_t rx_srs_cnt;
boolean_t enable_classifier;
ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
enable_classifier = !FLOW_TAB_EMPTY(mcip->mci_subflow_tab) && enable;
rx_func = enable_classifier ? mac_rx_srs_subflow_process :
mac_rx_srs_process;
if (mip->mi_state_flags & MIS_POLL_DISABLE)
enable_classifier = B_TRUE;
if (flent->fe_cb_fn == (flow_fn_t)rx_func)
return;
rx_srs_cnt = flent->fe_rx_srs_cnt;
for (i = 0; i < rx_srs_cnt; i++) {
ASSERT(flent->fe_rx_srs[i] != NULL);
mac_srs_poll_state_change(flent->fe_rx_srs[i],
enable_classifier, rx_func);
}
mutex_enter(&flent->fe_lock);
flent->fe_cb_fn = (flow_fn_t)rx_func;
flent->fe_cb_arg1 = (void *)mip;
flent->fe_cb_arg2 = flent->fe_rx_srs[0];
mutex_exit(&flent->fe_lock);
}
static void
mac_srs_update_fanout_list(mac_soft_ring_set_t *mac_srs)
{
int tcp_count = 0, tcp6_count = 0, udp_count = 0, udp6_count = 0,
oth_count = 0, tx_count = 0;
mac_soft_ring_t *softring;
softring = mac_srs->srs_soft_ring_head;
if (softring == NULL) {
ASSERT(mac_srs->srs_soft_ring_count == 0);
mac_srs->srs_tcp_ring_count = 0;
mac_srs->srs_udp_ring_count = 0;
mac_srs->srs_tcp6_ring_count = 0;
mac_srs->srs_udp6_ring_count = 0;
mac_srs->srs_oth_ring_count = 0;
mac_srs->srs_tx_ring_count = 0;
if ((mac_srs->srs_type & SRST_TX) != 0) {
mac_srs->srs_type |= SRST_NO_SOFT_RINGS;
}
return;
}
if ((mac_srs->srs_type & SRST_TX) != 0) {
mac_srs->srs_type &= ~SRST_NO_SOFT_RINGS;
}
while (softring != NULL) {
if (softring->s_ring_state & ST_RING_TCP) {
mac_srs->srs_tcp_soft_rings[tcp_count++] = softring;
} else if (softring->s_ring_state & ST_RING_TCP6) {
mac_srs->srs_tcp6_soft_rings[tcp6_count++] = softring;
} else if (softring->s_ring_state & ST_RING_UDP) {
mac_srs->srs_udp_soft_rings[udp_count++] = softring;
} else if (softring->s_ring_state & ST_RING_UDP6) {
mac_srs->srs_udp6_soft_rings[udp6_count++] = softring;
} else if (softring->s_ring_state & ST_RING_OTH) {
mac_srs->srs_oth_soft_rings[oth_count++] = softring;
} else {
ASSERT(softring->s_ring_state & ST_RING_TX);
mac_srs->srs_tx_soft_rings[tx_count++] = softring;
}
softring = softring->s_ring_next;
}
ASSERT(mac_srs->srs_soft_ring_count == (tcp_count + tcp6_count +
udp_count + udp6_count + oth_count + tx_count));
mac_srs->srs_tcp_ring_count = tcp_count;
mac_srs->srs_tcp6_ring_count = tcp6_count;
mac_srs->srs_udp_ring_count = udp_count;
mac_srs->srs_udp6_ring_count = udp6_count;
mac_srs->srs_oth_ring_count = oth_count;
mac_srs->srs_tx_ring_count = tx_count;
}
static void
mac_srs_create_proto_softrings(int id, pri_t pri, mac_client_impl_t *mcip,
mac_soft_ring_set_t *mac_srs, processorid_t cpuid, mac_direct_rx_t rx_func,
void *x_arg1, boolean_t set_bypass)
{
mac_soft_ring_t *softring;
softring = mac_soft_ring_create_rx(id, mac_soft_ring_worker_wait,
ST_RING_TCP, pri, mcip, mac_srs, cpuid, rx_func, x_arg1);
if (set_bypass && mcip->mci_direct_rx.mdrx_v4 != NULL &&
(mcip->mci_rcb4.mrc_arg != NULL)) {
mac_soft_ring_poll_enable(softring, mcip->mci_direct_rx.mdrx_v4,
mcip->mci_direct_rx.mdrx_arg_v4, &mcip->mci_rcb4, pri);
}
softring = mac_soft_ring_create_rx(id, mac_soft_ring_worker_wait,
ST_RING_UDP, pri, mcip, mac_srs, cpuid, rx_func, x_arg1);
if (set_bypass && mcip->mci_direct_rx.mdrx_v4 != NULL) {
mac_soft_ring_dls_bypass_enable(softring,
mcip->mci_direct_rx.mdrx_v4,
mcip->mci_direct_rx.mdrx_arg_v4);
}
softring = mac_soft_ring_create_rx(id, mac_soft_ring_worker_wait,
ST_RING_TCP6, pri, mcip, mac_srs, cpuid, rx_func, x_arg1);
if (set_bypass && mcip->mci_direct_rx.mdrx_v6 != NULL &&
(mcip->mci_rcb6.mrc_arg != NULL)) {
mac_soft_ring_poll_enable(softring, mcip->mci_direct_rx.mdrx_v6,
mcip->mci_direct_rx.mdrx_arg_v6, &mcip->mci_rcb6, pri);
}
softring = mac_soft_ring_create_rx(id, mac_soft_ring_worker_wait,
ST_RING_UDP6, pri, mcip, mac_srs, cpuid, rx_func, x_arg1);
softring->s_ring_rx_arg2 = NULL;
if (set_bypass && mcip->mci_direct_rx.mdrx_v6 != NULL) {
mac_soft_ring_dls_bypass_enable(softring,
mcip->mci_direct_rx.mdrx_v6,
mcip->mci_direct_rx.mdrx_arg_v6);
}
softring = mac_soft_ring_create_rx(id, mac_soft_ring_worker_wait,
ST_RING_OTH, pri, mcip, mac_srs, cpuid, rx_func, x_arg1);
}
static void
mac_srs_fanout_modify(mac_client_impl_t *mcip, mac_direct_rx_t rx_func,
void *x_arg1, mac_soft_ring_set_t *mac_rx_srs,
mac_soft_ring_set_t *mac_tx_srs)
{
mac_soft_ring_t *softring;
processorid_t cpuid = -1;
int i, srings_present, new_fanout_cnt;
mac_cpus_t *srs_cpu;
ASSERT(mac_rx_srs->srs_fanout_state == SRS_FANOUT_REINIT);
mac_rx_srs->srs_fanout_state = SRS_FANOUT_INIT;
srings_present = mac_rx_srs->srs_tcp_ring_count;
srs_cpu = &mac_rx_srs->srs_cpu;
new_fanout_cnt = srs_cpu->mc_rx_fanout_cnt;
if (new_fanout_cnt > srings_present) {
mutex_enter(&mac_rx_srs->srs_lock);
mac_rx_srs->srs_type |= SRST_FANOUT_SRC_IP;
mutex_exit(&mac_rx_srs->srs_lock);
for (i = mac_rx_srs->srs_tcp_ring_count;
i < new_fanout_cnt; i++) {
mac_srs_create_proto_softrings(i, mac_rx_srs->srs_pri,
mcip, mac_rx_srs, cpuid, rx_func, x_arg1, B_TRUE);
}
mac_srs_update_fanout_list(mac_rx_srs);
} else if (new_fanout_cnt < srings_present) {
if (new_fanout_cnt == 1) {
mutex_enter(&mac_rx_srs->srs_lock);
mac_rx_srs->srs_type &= ~SRST_FANOUT_SRC_IP;
ASSERT(mac_rx_srs->srs_type & SRST_FANOUT_PROTO);
mutex_exit(&mac_rx_srs->srs_lock);
}
for (i = new_fanout_cnt;
i < mac_rx_srs->srs_tcp_ring_count; i++) {
softring = mac_rx_srs->srs_tcp_soft_rings[i];
if (softring->s_ring_rx_arg2 != NULL) {
mcip->mci_rcb4.mrc_remove(
mcip->mci_rcb4.mrc_arg,
softring->s_ring_rx_arg2);
}
softring = mac_rx_srs->srs_tcp6_soft_rings[i];
if (softring->s_ring_rx_arg2 != NULL) {
mcip->mci_rcb6.mrc_remove(
mcip->mci_rcb6.mrc_arg,
softring->s_ring_rx_arg2);
}
mac_soft_ring_remove(mac_rx_srs,
mac_rx_srs->srs_tcp_soft_rings[i]);
mac_soft_ring_remove(mac_rx_srs,
mac_rx_srs->srs_tcp6_soft_rings[i]);
mac_soft_ring_remove(mac_rx_srs,
mac_rx_srs->srs_udp_soft_rings[i]);
mac_soft_ring_remove(mac_rx_srs,
mac_rx_srs->srs_udp6_soft_rings[i]);
mac_soft_ring_remove(mac_rx_srs,
mac_rx_srs->srs_oth_soft_rings[i]);
}
mac_srs_update_fanout_list(mac_rx_srs);
}
ASSERT(new_fanout_cnt == mac_rx_srs->srs_tcp_ring_count);
mutex_enter(&cpu_lock);
for (i = 0; i < mac_rx_srs->srs_tcp_ring_count; i++) {
cpuid = srs_cpu->mc_rx_fanout_cpus[i];
(void) mac_soft_ring_bind(mac_rx_srs->srs_udp_soft_rings[i],
cpuid);
(void) mac_soft_ring_bind(mac_rx_srs->srs_udp6_soft_rings[i],
cpuid);
(void) mac_soft_ring_bind(mac_rx_srs->srs_oth_soft_rings[i],
cpuid);
(void) mac_soft_ring_bind(mac_rx_srs->srs_tcp_soft_rings[i],
cpuid);
(void) mac_soft_ring_bind(mac_rx_srs->srs_tcp6_soft_rings[i],
cpuid);
softring = mac_rx_srs->srs_tcp_soft_rings[i];
if (softring->s_ring_rx_arg2 != NULL) {
mcip->mci_rcb4.mrc_bind(mcip->mci_rcb4.mrc_arg,
softring->s_ring_rx_arg2, cpuid);
}
softring = mac_rx_srs->srs_tcp6_soft_rings[i];
if (softring->s_ring_rx_arg2 != NULL) {
mcip->mci_rcb6.mrc_bind(mcip->mci_rcb6.mrc_arg,
softring->s_ring_rx_arg2, cpuid);
}
}
mac_srs_worker_bind(mac_rx_srs, srs_cpu->mc_rx_workerid);
mac_srs_poll_bind(mac_rx_srs, srs_cpu->mc_rx_pollid);
mac_rx_srs_retarget_intr(mac_rx_srs, srs_cpu->mc_rx_intr_cpu);
if (mac_tx_srs != NULL) {
BIND_TX_SRS_AND_SOFT_RINGS(mac_tx_srs, mrp);
mac_tx_srs_retarget_intr(mac_tx_srs);
}
mutex_exit(&cpu_lock);
}
void
mac_srs_fanout_init(mac_client_impl_t *mcip, mac_resource_props_t *mrp,
mac_direct_rx_t rx_func, void *x_arg1, mac_soft_ring_set_t *mac_rx_srs,
mac_soft_ring_set_t *mac_tx_srs, cpupart_t *cpupart)
{
int i;
processorid_t cpuid;
int soft_ring_cnt;
mac_cpus_t *srs_cpu = &mac_rx_srs->srs_cpu;
mutex_enter(&mac_rx_srs->srs_lock);
mac_rx_srs->srs_type &= ~SRST_NO_SOFT_RINGS;
mutex_exit(&mac_rx_srs->srs_lock);
ASSERT(mac_rx_srs->srs_soft_ring_head == NULL);
ASSERT(mac_rx_srs->srs_fanout_state == SRS_FANOUT_UNINIT);
mac_rx_srs->srs_fanout_state = SRS_FANOUT_INIT;
ASSERT(mrp != NULL);
soft_ring_cnt = srs_cpu->mc_rx_fanout_cnt;
if (soft_ring_cnt > 0) {
mutex_enter(&cpu_lock);
for (i = 0; i < soft_ring_cnt; i++) {
cpuid = srs_cpu->mc_rx_fanout_cpus[i];
mac_srs_create_proto_softrings(i, mac_rx_srs->srs_pri,
mcip, mac_rx_srs, cpuid, rx_func, x_arg1, B_FALSE);
}
mac_srs_worker_bind(mac_rx_srs, srs_cpu->mc_rx_workerid);
mac_srs_poll_bind(mac_rx_srs, srs_cpu->mc_rx_pollid);
mac_rx_srs_retarget_intr(mac_rx_srs, srs_cpu->mc_rx_intr_cpu);
if (mac_tx_srs == NULL) {
mutex_exit(&cpu_lock);
goto alldone;
}
BIND_TX_SRS_AND_SOFT_RINGS(mac_tx_srs, mrp);
mac_tx_srs_retarget_intr(mac_tx_srs);
mutex_exit(&cpu_lock);
} else {
mutex_enter(&cpu_lock);
mac_srs_worker_bind(mac_rx_srs, mrp->mrp_rx_workerid);
mac_srs_poll_bind(mac_rx_srs, mrp->mrp_rx_pollid);
mutex_exit(&cpu_lock);
goto no_softrings;
}
alldone:
if (soft_ring_cnt > 1)
mac_rx_srs->srs_type |= SRST_FANOUT_SRC_IP;
mac_srs_update_fanout_list(mac_rx_srs);
mac_srs_client_poll_enable(mcip, mac_rx_srs, B_FALSE);
mac_srs_client_poll_enable(mcip, mac_rx_srs, B_TRUE);
return;
no_softrings:
if (mac_rx_srs->srs_type & SRST_FANOUT_PROTO) {
mutex_enter(&cpu_lock);
cpuid = mac_next_bind_cpu(cpupart);
mac_srs_create_proto_softrings(0, mac_rx_srs->srs_pri, mcip,
mac_rx_srs, cpuid, rx_func, x_arg1, B_FALSE);
mutex_exit(&cpu_lock);
} else {
mac_rx_srs->srs_type |= SRST_NO_SOFT_RINGS;
}
mac_srs_update_fanout_list(mac_rx_srs);
mac_srs_client_poll_enable(mcip, mac_rx_srs, B_FALSE);
mac_srs_client_poll_enable(mcip, mac_rx_srs, B_TRUE);
}
void
mac_fanout_setup(mac_client_impl_t *mcip, flow_entry_t *flent,
mac_resource_props_t *mrp, mac_direct_rx_t rx_func, void *x_arg1,
cpupart_t *cpupart)
{
mac_soft_ring_set_t *mac_rx_srs, *mac_tx_srs;
int i, rx_srs_cnt;
ASSERT(MAC_PERIM_HELD((mac_handle_t)mcip->mci_mip));
ASSERT3U((mcip->mci_state_flags & MCIS_IS_AGGR_PORT), ==, 0);
mac_rx_srs = flent->fe_rx_srs[0];
mac_tx_srs = flent->fe_tx_srs;
rx_srs_cnt = flent->fe_rx_srs_cnt;
if (flent->fe_type & FLOW_USER) {
mac_srs_fanout_init(mcip, mrp, rx_func,
x_arg1, mac_rx_srs, mac_tx_srs, cpupart);
return;
}
if (mrp->mrp_mask & MRP_CPUS_USERSPEC)
mac_flow_user_cpu_init(flent, mrp);
else
mac_flow_cpu_init(flent, cpupart);
mrp->mrp_rx_fanout_cnt = mac_rx_srs->srs_cpu.mc_rx_fanout_cnt;
for (i = 0; i < rx_srs_cnt; i++) {
mac_rx_srs = flent->fe_rx_srs[i];
if (i != 0)
mac_tx_srs = NULL;
switch (mac_rx_srs->srs_fanout_state) {
case SRS_FANOUT_UNINIT:
mac_srs_fanout_init(mcip, mrp, rx_func, x_arg1,
mac_rx_srs, mac_tx_srs, cpupart);
break;
case SRS_FANOUT_INIT:
break;
case SRS_FANOUT_REINIT:
mac_rx_srs_quiesce(mac_rx_srs, SRS_QUIESCE);
mac_srs_fanout_modify(mcip, rx_func, x_arg1, mac_rx_srs,
mac_tx_srs);
mac_rx_srs_restart(mac_rx_srs);
break;
default:
VERIFY(mac_rx_srs->srs_fanout_state <=
SRS_FANOUT_REINIT);
break;
}
}
}
static mac_soft_ring_set_t *
mac_srs_create(mac_client_impl_t *mcip, flow_entry_t *flent,
const mac_soft_ring_set_type_t srs_type, mac_direct_rx_t rx_func,
mac_ring_t *ring)
{
mac_soft_ring_set_t *mac_srs;
mac_srs_rx_t *srs_rx;
mac_srs_tx_t *srs_tx;
mac_resource_props_t *mrp;
const boolean_t is_tx_srs = ((srs_type & SRST_TX) != 0);
mac_srs = kmem_cache_alloc(mac_srs_cache, KM_SLEEP);
bzero(mac_srs, sizeof (mac_soft_ring_set_t));
srs_rx = &mac_srs->srs_rx;
srs_tx = &mac_srs->srs_tx;
mutex_enter(&flent->fe_lock);
mac_bw_ctl_t *my_bw = is_tx_srs ? &flent->fe_tx_bw : &flent->fe_rx_bw;
mac_srs->srs_bw = my_bw;
if (is_tx_srs) {
bzero(my_bw, sizeof (*my_bw));
flent->fe_tx_srs = mac_srs;
} else {
if (flent->fe_rx_srs_cnt == 0)
bzero(my_bw, sizeof (*my_bw));
if (flent->fe_rx_srs_cnt >= MAX_RINGS_PER_GROUP) {
panic("Array Overrun detected due to MAC client %p "
" having more rings than %d", (void *)mcip,
MAX_RINGS_PER_GROUP);
}
flent->fe_rx_srs[flent->fe_rx_srs_cnt] = mac_srs;
flent->fe_rx_srs_cnt++;
}
mac_srs->srs_flent = flent;
mutex_exit(&flent->fe_lock);
mac_srs->srs_state = 0;
mac_srs->srs_type = (srs_type | SRST_NO_SOFT_RINGS);
mac_srs->srs_worker_cpuid = mac_srs->srs_worker_cpuid_save = -1;
mac_srs->srs_poll_cpuid = mac_srs->srs_poll_cpuid_save = -1;
mac_srs->srs_mcip = mcip;
mac_srs_fanout_list_alloc(mac_srs);
mrp = &flent->fe_effective_props;
if ((mac_srs->srs_type & SRST_FLOW) != 0) {
mac_srs->srs_pri = FLOW_PRIORITY(mcip->mci_min_pri,
mcip->mci_max_pri, mrp->mrp_priority);
} else {
mac_srs->srs_pri = mcip->mci_max_pri;
}
mac_srs_add_glist(mac_srs);
if ((mrp->mrp_mask & MRP_MAXBW) != 0) {
mutex_enter(&my_bw->mac_bw_lock);
mac_bw_ctl_set_state(my_bw, B_TRUE, mrp);
mutex_exit(&my_bw->mac_bw_lock);
mac_srs->srs_type |= SRST_BW_CONTROL;
}
mac_srs_update_drain_proc(mac_srs);
if (!is_tx_srs) {
srs_rx->sr_hiwat = mac_soft_ring_max_q_cnt;
srs_rx->sr_lowat = mac_soft_ring_min_q_cnt <=
mac_soft_ring_max_q_cnt ? mac_soft_ring_min_q_cnt :
(mac_soft_ring_max_q_cnt >> 2);
srs_rx->sr_poll_thres = mac_soft_ring_poll_thres <=
(srs_rx->sr_lowat >> 1) ? mac_soft_ring_poll_thres :
(srs_rx->sr_lowat >> 1);
mac_srs->srs_type |= mac_latency_optimize ?
SRST_LATENCY_OPT : SRST_ENQUEUE;
}
mac_srs->srs_worker = thread_create(NULL, default_stksize << 1,
mac_srs_worker, mac_srs, 0, &p0, TS_RUN, mac_srs->srs_pri);
if (is_tx_srs) {
srs_tx->st_max_q_cnt = mac_tx_srs_max_q_cnt;
srs_tx->st_hiwat =
(mac_tx_srs_hiwat > mac_tx_srs_max_q_cnt) ?
mac_tx_srs_max_q_cnt : mac_tx_srs_hiwat;
srs_tx->st_arg1 = mcip;
srs_tx->st_arg2 = NULL;
goto done;
}
if ((srs_type & SRST_FLOW) != 0 ||
FLOW_TAB_EMPTY(mcip->mci_subflow_tab))
srs_rx->sr_lower_proc = mac_rx_srs_process;
else
srs_rx->sr_lower_proc = mac_rx_srs_subflow_process;
srs_rx->sr_func = rx_func;
srs_rx->sr_arg1 = mcip;
if (ring != NULL) {
uint_t ring_info;
if (ring->mr_gh == (mac_group_handle_t)
MAC_DEFAULT_RX_GROUP(mcip->mci_mip)) {
mac_srs->srs_type |= SRST_DEFAULT_GRP;
}
mac_srs->srs_ring = ring;
ring->mr_srs = mac_srs;
ring->mr_classify_type = MAC_HW_CLASSIFIER;
ring->mr_flag |= MR_INCIPIENT;
if (!(mcip->mci_mip->mi_state_flags & MIS_POLL_DISABLE) &&
FLOW_TAB_EMPTY(mcip->mci_subflow_tab) && mac_poll_enable)
mac_srs->srs_state |= SRS_POLLING_CAPAB;
mac_srs->srs_poll_thr = thread_create(NULL, 0,
mac_rx_srs_poll_ring, mac_srs, 0, &p0, TS_RUN,
mac_srs->srs_pri);
ring_info = mac_hwring_getinfo((mac_ring_handle_t)ring);
if (ring_info & MAC_RING_RX_ENQUEUE) {
mac_srs->srs_type |= SRST_ENQUEUE;
}
}
done:
mac_srs_stat_create(mac_srs);
return (mac_srs);
}
static mac_soft_ring_set_type_t
mac_find_fanout(flow_entry_t *flent, const mac_soft_ring_set_type_t link_type)
{
uint32_t fanout_type;
mac_resource_props_t *mrp = &flent->fe_effective_props;
switch (link_type) {
case SRST_FLOW:
fanout_type = SRST_NO_SOFT_RINGS;
break;
case SRST_LINK:
fanout_type = SRST_FANOUT_PROTO;
break;
}
if (flent->fe_type & FLOW_PRIMARY_MAC) {
if (mac_soft_ring_enable && mac_rx_soft_ring_count > 1) {
fanout_type |= SRST_FANOUT_SRC_IP;
}
} else if (flent->fe_type & FLOW_VNIC) {
if (mrp != NULL && mrp->mrp_ncpus > 0) {
fanout_type |= SRST_FANOUT_SRC_IP;
}
}
return (fanout_type);
}
void
mac_rx_switch_grp_to_sw(mac_group_t *group)
{
mac_ring_t *ring;
mac_soft_ring_set_t *mac_srs;
for (ring = group->mrg_rings; ring != NULL; ring = ring->mr_next) {
if (ring->mr_classify_type == MAC_HW_CLASSIFIER) {
mac_srs = ring->mr_srs;
ASSERT(mac_srs != NULL);
mac_rx_srs_remove(mac_srs);
ring->mr_srs = NULL;
}
if (ring->mr_state != MR_INUSE)
(void) mac_start_ring(ring);
ring->mr_flag = 0;
ring->mr_classify_type = MAC_SW_CLASSIFIER;
}
}
void
mac_srs_group_setup(mac_client_impl_t *mcip, flow_entry_t *flent,
uint32_t link_type)
{
cpupart_t *cpupart;
mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip);
mac_resource_props_t *emrp = MCIP_EFFECTIVE_PROPS(mcip);
boolean_t use_default = B_FALSE;
mac_rx_srs_group_setup(mcip, flent, link_type);
mac_tx_srs_group_setup(mcip, flent, link_type);
if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0)
return;
pool_lock();
cpupart = mac_pset_find(mrp, &use_default);
mac_fanout_setup(mcip, flent, MCIP_RESOURCE_PROPS(mcip),
mac_rx_deliver, mcip, cpupart);
mac_set_pool_effective(use_default, cpupart, mrp, emrp);
pool_unlock();
}
void
mac_rx_srs_group_setup(mac_client_impl_t *mcip, flow_entry_t *flent,
const mac_soft_ring_set_type_t link_type)
{
mac_impl_t *mip = mcip->mci_mip;
mac_soft_ring_set_t *mac_srs;
mac_ring_t *ring;
mac_group_t *rx_group = flent->fe_rx_ring_group;
boolean_t no_unicast;
if (mcip->mci_state_flags & MCIS_IS_AGGR_PORT) {
if (rx_group == NULL)
return;
for (ring = rx_group->mrg_rings; ring != NULL;
ring = ring->mr_next) {
if (ring->mr_state != MR_INUSE)
(void) mac_start_ring(ring);
}
return;
}
ASSERT3U((mcip->mci_state_flags & MCIS_IS_AGGR_PORT), ==, 0);
const mac_soft_ring_set_type_t fanout_type =
mac_find_fanout(flent, link_type);
no_unicast = (mcip->mci_state_flags & MCIS_NO_UNICAST_ADDR) != 0;
if (flent->fe_rx_srs[0] == NULL) {
ASSERT(flent->fe_rx_srs_cnt == 0);
mac_srs = mac_srs_create(mcip, flent, fanout_type | link_type,
mac_rx_deliver, NULL);
mutex_enter(&flent->fe_lock);
flent->fe_cb_fn = (flow_fn_t)mac_srs->srs_rx.sr_lower_proc;
flent->fe_cb_arg1 = (void *)mip;
flent->fe_cb_arg2 = (void *)mac_srs;
mutex_exit(&flent->fe_lock);
}
if (rx_group == NULL)
return;
switch (rx_group->mrg_state) {
case MAC_GROUP_STATE_RESERVED:
for (ring = rx_group->mrg_rings; ring != NULL;
ring = ring->mr_next) {
uint16_t vid = i_mac_flow_vid(mcip->mci_flent);
switch (ring->mr_state) {
case MR_INUSE:
case MR_FREE:
if (ring->mr_srs != NULL)
break;
if (ring->mr_state != MR_INUSE)
(void) mac_start_ring(ring);
if ((!MAC_GROUP_HW_VLAN(rx_group) &&
vid != VLAN_ID_NONE) || no_unicast)
break;
mac_srs = mac_srs_create(mcip, flent,
fanout_type | link_type,
mac_rx_deliver, ring);
break;
default:
cmn_err(CE_PANIC,
"srs_setup: mcip = %p "
"trying to add UNKNOWN ring = %p\n",
(void *)mcip, (void *)ring);
break;
}
}
break;
case MAC_GROUP_STATE_SHARED:
mac_rx_switch_grp_to_sw(rx_group);
break;
default:
ASSERT(B_FALSE);
break;
}
}
void
mac_tx_srs_group_setup(mac_client_impl_t *mcip, flow_entry_t *flent,
const mac_soft_ring_set_type_t link_type)
{
if (mcip->mci_state_flags & MCIS_EXCLUSIVE) {
mac_ring_t *ring;
mac_group_t *grp;
grp = (mac_group_t *)flent->fe_tx_ring_group;
if (grp == NULL)
return;
for (ring = grp->mrg_rings; ring != NULL;
ring = ring->mr_next) {
if (ring->mr_state != MR_INUSE)
(void) mac_start_ring(ring);
}
return;
}
ASSERT3U((mcip->mci_state_flags & MCIS_IS_AGGR_PORT), ==, 0);
if (flent->fe_tx_srs == NULL) {
(void) mac_srs_create(mcip, flent, SRST_TX | link_type,
NULL, NULL);
}
mac_tx_srs_setup(mcip, flent);
}
void
mac_rx_srs_group_teardown(flow_entry_t *flent, boolean_t hwonly)
{
mac_soft_ring_set_t *mac_srs;
int i;
int count = flent->fe_rx_srs_cnt;
for (i = 0; i < count; i++) {
if (i == 0 && hwonly)
continue;
mac_srs = flent->fe_rx_srs[i];
mac_rx_srs_quiesce(mac_srs, SRS_CONDEMNED);
mac_srs_free(mac_srs);
flent->fe_rx_srs[i] = NULL;
flent->fe_rx_srs_cnt--;
}
if (hwonly)
VERIFY3S(flent->fe_rx_srs_cnt, ==, 1);
else
VERIFY3S(flent->fe_rx_srs_cnt, ==, 0);
}
void
mac_tx_srs_group_teardown(mac_client_impl_t *mcip, flow_entry_t *flent,
const mac_soft_ring_set_type_t link_type)
{
mac_soft_ring_set_t *tx_srs;
mac_srs_tx_t *tx;
if ((tx_srs = flent->fe_tx_srs) == NULL)
return;
tx = &tx_srs->srs_tx;
switch (link_type) {
case SRST_FLOW:
mac_tx_srs_quiesce(tx_srs, SRS_CONDEMNED);
break;
case SRST_LINK:
mac_tx_client_condemn((mac_client_handle_t)mcip);
if (tx->st_arg2 != NULL) {
ASSERT(tx_srs->srs_type & SRST_TX);
tx->st_arg2 = NULL;
}
break;
default:
ASSERT(B_FALSE);
break;
}
mac_srs_free(tx_srs);
flent->fe_tx_srs = NULL;
}
mac_group_state_t
mac_group_next_state(mac_group_t *grp, mac_client_impl_t **group_only_mcip,
mac_group_t *defgrp, boolean_t rx_group)
{
mac_impl_t *mip = (mac_impl_t *)grp->mrg_mh;
*group_only_mcip = NULL;
if (grp != defgrp) {
if (MAC_GROUP_NO_CLIENT(grp))
return (MAC_GROUP_STATE_REGISTERED);
*group_only_mcip = MAC_GROUP_ONLY_CLIENT(grp);
if (*group_only_mcip != NULL)
return (MAC_GROUP_STATE_RESERVED);
return (MAC_GROUP_STATE_SHARED);
}
if (MAC_GROUP_NO_CLIENT(grp)) {
if (rx_group)
return (MAC_GROUP_STATE_SHARED);
else
return (MAC_GROUP_STATE_REGISTERED);
}
*group_only_mcip = MAC_GROUP_ONLY_CLIENT(grp);
if (*group_only_mcip == NULL)
return (MAC_GROUP_STATE_SHARED);
if (rx_group && mip->mi_nactiveclients != 1)
return (MAC_GROUP_STATE_SHARED);
ASSERT(*group_only_mcip != NULL);
return (MAC_GROUP_STATE_RESERVED);
}
int
mac_datapath_setup(mac_client_impl_t *mcip, flow_entry_t *flent,
const mac_soft_ring_set_type_t link_type)
{
mac_impl_t *mip = mcip->mci_mip;
mac_group_t *rgroup = NULL;
mac_group_t *tgroup = NULL;
mac_group_t *default_rgroup;
mac_group_t *default_tgroup;
int err;
uint16_t vid;
uint8_t *mac_addr;
mac_group_state_t next_state;
mac_client_impl_t *group_only_mcip;
mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip);
mac_resource_props_t *emrp = MCIP_EFFECTIVE_PROPS(mcip);
boolean_t rxhw;
boolean_t txhw;
boolean_t use_default = B_FALSE;
cpupart_t *cpupart;
boolean_t no_unicast;
boolean_t isprimary = flent->fe_type & FLOW_PRIMARY_MAC;
mac_client_impl_t *reloc_pmcip = NULL;
boolean_t use_hw;
ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
switch (link_type) {
case SRST_FLOW:
mac_srs_group_setup(mcip, flent, link_type);
return (0);
case SRST_LINK:
no_unicast = mcip->mci_state_flags & MCIS_NO_UNICAST_ADDR;
mac_addr = flent->fe_flow_desc.fd_dst_mac;
default_rgroup = MAC_DEFAULT_RX_GROUP(mip);
default_tgroup = MAC_DEFAULT_TX_GROUP(mip);
if (no_unicast) {
rgroup = default_rgroup;
tgroup = default_tgroup;
goto grp_found;
}
rxhw = (mrp->mrp_mask & MRP_RX_RINGS) &&
(mrp->mrp_nrxrings > 0 ||
(mrp->mrp_mask & MRP_RXRINGS_UNSPEC));
txhw = (mrp->mrp_mask & MRP_TX_RINGS) &&
(mrp->mrp_ntxrings > 0 ||
(mrp->mrp_mask & MRP_TXRINGS_UNSPEC));
if (!isprimary && mip->mi_nactiveclients == 2 &&
(group_only_mcip = mac_primary_client_handle(mip)) !=
NULL && mip->mi_rx_group_type == MAC_GROUP_TYPE_DYNAMIC) {
reloc_pmcip = mac_check_primary_relocation(
group_only_mcip, rxhw);
}
rgroup = mac_reserve_rx_group(mcip, mac_addr, B_FALSE);
if (rgroup == NULL && rxhw) {
err = ENOSPC;
goto setup_failed;
} else if (rgroup == NULL) {
rgroup = default_rgroup;
}
if (rgroup != default_rgroup &&
rgroup->mrg_state == MAC_GROUP_STATE_RESERVED) {
group_only_mcip = MAC_GROUP_ONLY_CLIENT(rgroup);
err = mac_rx_switch_group(group_only_mcip, rgroup,
default_rgroup);
if (err != 0)
goto setup_failed;
rgroup = default_rgroup;
}
tgroup = mac_reserve_tx_group(mcip, B_FALSE);
if (tgroup == NULL && txhw) {
if (rgroup != NULL && rgroup != default_rgroup)
mac_release_rx_group(mcip, rgroup);
err = ENOSPC;
goto setup_failed;
} else if (tgroup == NULL) {
tgroup = default_tgroup;
}
grp_found:
if (rgroup != NULL) {
if (rgroup != default_rgroup &&
MAC_GROUP_NO_CLIENT(rgroup) &&
(rxhw || mcip->mci_share != 0)) {
MAC_RX_GRP_RESERVED(mip);
if (mip->mi_rx_group_type ==
MAC_GROUP_TYPE_DYNAMIC) {
MAC_RX_RING_RESERVED(mip,
rgroup->mrg_cur_count);
}
}
flent->fe_rx_ring_group = rgroup;
mac_group_add_client(rgroup, mcip);
next_state = mac_group_next_state(rgroup,
&group_only_mcip, default_rgroup, B_TRUE);
mac_set_group_state(rgroup, next_state);
}
if (tgroup != NULL) {
if (tgroup != default_tgroup &&
MAC_GROUP_NO_CLIENT(tgroup) &&
(txhw || mcip->mci_share != 0)) {
MAC_TX_GRP_RESERVED(mip);
if (mip->mi_tx_group_type ==
MAC_GROUP_TYPE_DYNAMIC) {
MAC_TX_RING_RESERVED(mip,
tgroup->mrg_cur_count);
}
}
flent->fe_tx_ring_group = tgroup;
mac_group_add_client(tgroup, mcip);
next_state = mac_group_next_state(tgroup,
&group_only_mcip, default_tgroup, B_FALSE);
tgroup->mrg_state = next_state;
}
if (no_unicast) {
mac_srs_group_setup(mcip, flent, link_type);
break;
}
if ((err = mac_flow_add(mip->mi_flow_tab, flent)) != 0)
goto setup_failed;
vid = i_mac_flow_vid(flent);
use_hw = (mcip->mci_state_flags & MCIS_UNICAST_HW) != 0;
err = mac_add_macaddr_vlan(mip, rgroup, mac_addr, vid, use_hw);
if (err != 0)
goto setup_failed;
mcip->mci_unicast = mac_find_macaddr(mip, mac_addr);
VERIFY(mcip->mci_unicast != NULL);
mac_srs_group_setup(mcip, flent, link_type);
mac_protect_update_mac_token(mcip);
break;
default:
ASSERT(B_FALSE);
break;
}
if (rgroup != NULL) {
if (rgroup != default_rgroup) {
if (default_rgroup->mrg_state ==
MAC_GROUP_STATE_RESERVED) {
group_only_mcip = MAC_GROUP_ONLY_CLIENT(
default_rgroup);
ASSERT(group_only_mcip != NULL &&
mip->mi_nactiveclients > 1);
mac_set_group_state(default_rgroup,
MAC_GROUP_STATE_SHARED);
mac_rx_srs_group_setup(group_only_mcip,
group_only_mcip->mci_flent, SRST_LINK);
pool_lock();
cpupart = mac_pset_find(mrp, &use_default);
mac_fanout_setup(group_only_mcip,
group_only_mcip->mci_flent,
MCIP_RESOURCE_PROPS(group_only_mcip),
mac_rx_deliver, group_only_mcip, cpupart);
mac_set_pool_effective(use_default, cpupart,
mrp, emrp);
pool_unlock();
}
ASSERT(default_rgroup->mrg_state ==
MAC_GROUP_STATE_SHARED);
}
if (rgroup->mrg_state == MAC_GROUP_STATE_RESERVED &&
((!MAC_GROUP_HW_VLAN(rgroup) && vid != VLAN_ID_NONE) ||
no_unicast)) {
mac_rx_switch_grp_to_sw(rgroup);
}
}
mac_set_rings_effective(mcip);
return (0);
setup_failed:
if (reloc_pmcip != NULL) {
(void) mac_rx_switch_group(reloc_pmcip,
reloc_pmcip->mci_flent->fe_rx_ring_group, default_rgroup);
}
mac_datapath_teardown(mcip, flent, link_type);
return (err);
}
void
mac_datapath_teardown(mac_client_impl_t *mcip, flow_entry_t *flent,
const mac_soft_ring_set_type_t link_type)
{
mac_impl_t *mip = mcip->mci_mip;
mac_group_t *group = NULL;
mac_client_impl_t *grp_only_mcip;
flow_entry_t *group_only_flent;
mac_group_t *default_group;
boolean_t check_default_group = B_FALSE;
mac_group_state_t next_state;
mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip);
uint16_t vid;
ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
switch (link_type) {
case SRST_FLOW:
mac_rx_srs_group_teardown(flent, B_FALSE);
mac_tx_srs_group_teardown(mcip, flent, SRST_FLOW);
return;
case SRST_LINK:
mac_tx_client_block(mcip);
group = flent->fe_rx_ring_group;
vid = i_mac_flow_vid(flent);
if (mcip->mci_unicast != NULL) {
int err;
err = mac_remove_macaddr_vlan(mcip->mci_unicast, vid);
if (err != 0) {
cmn_err(CE_WARN, "%s: failed to remove a MAC HW"
" filters because of error 0x%x",
mip->mi_name, err);
}
mcip->mci_unicast = NULL;
}
mac_flow_remove(mip->mi_flow_tab, flent, B_FALSE);
mac_flow_wait(flent, FLOW_DRIVER_UPCALL);
mac_rx_srs_group_teardown(flent, B_FALSE);
mac_tx_srs_group_teardown(mcip, flent, SRST_LINK);
ASSERT3P(mcip->mci_flent, ==, flent);
ASSERT3P(flent->fe_next, ==, NULL);
default_group = MAC_DEFAULT_RX_GROUP(mip);
if (group != NULL) {
mac_group_remove_client(group, mcip);
next_state = mac_group_next_state(group,
&grp_only_mcip, default_group, B_TRUE);
if (next_state == MAC_GROUP_STATE_RESERVED) {
VERIFY(grp_only_mcip != NULL);
mac_set_group_state(group,
MAC_GROUP_STATE_RESERVED);
group_only_flent = grp_only_mcip->mci_flent;
mac_rx_srs_group_setup(grp_only_mcip,
group_only_flent, SRST_LINK);
mac_fanout_setup(grp_only_mcip,
group_only_flent,
MCIP_RESOURCE_PROPS(grp_only_mcip),
mac_rx_deliver, grp_only_mcip, NULL);
mac_rx_group_unmark(group, MR_INCIPIENT);
mac_set_rings_effective(grp_only_mcip);
} else if (next_state == MAC_GROUP_STATE_REGISTERED) {
VERIFY3P(group, !=, MAC_DEFAULT_RX_GROUP(mip));
if (mrp->mrp_mask & MRP_RX_RINGS) {
MAC_RX_GRP_RELEASED(mip);
if (mip->mi_rx_group_type ==
MAC_GROUP_TYPE_DYNAMIC) {
MAC_RX_RING_RELEASED(mip,
group->mrg_cur_count);
}
}
mac_release_rx_group(mcip, group);
mac_set_group_state(group,
MAC_GROUP_STATE_REGISTERED);
check_default_group = B_TRUE;
} else {
VERIFY3S(next_state, ==,
MAC_GROUP_STATE_SHARED);
mac_set_group_state(group,
MAC_GROUP_STATE_SHARED);
mac_rx_group_unmark(group, MR_CONDEMNED);
}
flent->fe_rx_ring_group = NULL;
}
group = flent->fe_tx_ring_group;
default_group = MAC_DEFAULT_TX_GROUP(mip);
if (group != NULL) {
mac_group_remove_client(group, mcip);
next_state = mac_group_next_state(group,
&grp_only_mcip, default_group, B_FALSE);
if (next_state == MAC_GROUP_STATE_REGISTERED) {
if (group != default_group) {
if (mrp->mrp_mask & MRP_TX_RINGS) {
MAC_TX_GRP_RELEASED(mip);
if (mip->mi_tx_group_type ==
MAC_GROUP_TYPE_DYNAMIC) {
MAC_TX_RING_RELEASED(
mip, group->
mrg_cur_count);
}
}
mac_release_tx_group(mcip, group);
if (mip->mi_tx_group_type ==
MAC_GROUP_TYPE_DYNAMIC &&
default_group->mrg_state ==
MAC_GROUP_STATE_RESERVED) {
grp_only_mcip =
MAC_GROUP_ONLY_CLIENT
(default_group);
mac_set_rings_effective(
grp_only_mcip);
}
} else {
mac_ring_t *ring;
int cnt;
int ringcnt;
ringcnt = group->mrg_cur_count;
ring = group->mrg_rings;
for (cnt = 0; cnt < ringcnt; cnt++) {
if (ring->mr_state ==
MR_INUSE && ring !=
(mac_ring_t *)
mip->mi_default_tx_ring) {
mac_stop_ring(ring);
ring->mr_flag = 0;
}
ring = ring->mr_next;
}
}
} else if (next_state == MAC_GROUP_STATE_RESERVED) {
mac_set_rings_effective(grp_only_mcip);
}
flent->fe_tx_ring_group = NULL;
group->mrg_state = next_state;
}
break;
default:
ASSERT(B_FALSE);
break;
}
if (check_default_group) {
default_group = MAC_DEFAULT_RX_GROUP(mip);
VERIFY3S(default_group->mrg_state, ==, MAC_GROUP_STATE_SHARED);
next_state = mac_group_next_state(default_group,
&grp_only_mcip, default_group, B_TRUE);
if (next_state == MAC_GROUP_STATE_RESERVED) {
VERIFY(grp_only_mcip != NULL);
VERIFY3U(mip->mi_nactiveclients, ==, 1);
mac_set_group_state(default_group,
MAC_GROUP_STATE_RESERVED);
mac_rx_srs_group_setup(grp_only_mcip,
grp_only_mcip->mci_flent, SRST_LINK);
mac_fanout_setup(grp_only_mcip,
grp_only_mcip->mci_flent,
MCIP_RESOURCE_PROPS(grp_only_mcip), mac_rx_deliver,
grp_only_mcip, NULL);
mac_rx_group_unmark(default_group, MR_INCIPIENT);
mac_set_rings_effective(grp_only_mcip);
}
}
if (!(flent->fe_type & FLOW_PRIMARY_MAC) &&
mip->mi_nactiveclients == 1 &&
mip->mi_rx_group_type == MAC_GROUP_TYPE_DYNAMIC) {
default_group = MAC_DEFAULT_RX_GROUP(mip);
grp_only_mcip = mac_primary_client_handle(mip);
if (grp_only_mcip == NULL)
return;
group_only_flent = grp_only_mcip->mci_flent;
mrp = MCIP_RESOURCE_PROPS(grp_only_mcip);
if (mrp->mrp_mask & MRP_RX_RINGS)
return;
(void) mac_rx_switch_group(grp_only_mcip,
group_only_flent->fe_rx_ring_group, default_group);
}
}
static void
mac_srs_fanout_list_free(mac_soft_ring_set_t *mac_srs)
{
if (mac_srs->srs_type & SRST_TX) {
mac_srs_tx_t *tx;
ASSERT(mac_srs->srs_tcp_soft_rings == NULL);
ASSERT(mac_srs->srs_udp_soft_rings == NULL);
ASSERT(mac_srs->srs_tcp6_soft_rings == NULL);
ASSERT(mac_srs->srs_udp6_soft_rings == NULL);
ASSERT(mac_srs->srs_oth_soft_rings == NULL);
ASSERT(mac_srs->srs_tx_soft_rings != NULL);
kmem_free(mac_srs->srs_tx_soft_rings,
sizeof (mac_soft_ring_t *) * MAX_RINGS_PER_GROUP);
mac_srs->srs_tx_soft_rings = NULL;
tx = &mac_srs->srs_tx;
if (tx->st_soft_rings != NULL) {
kmem_free(tx->st_soft_rings,
sizeof (mac_soft_ring_t *) * MAX_RINGS_PER_GROUP);
}
} else {
ASSERT(mac_srs->srs_tx_soft_rings == NULL);
ASSERT(mac_srs->srs_tcp_soft_rings != NULL);
kmem_free(mac_srs->srs_tcp_soft_rings,
sizeof (mac_soft_ring_t *) * MAX_SR_FANOUT);
mac_srs->srs_tcp_soft_rings = NULL;
ASSERT(mac_srs->srs_udp_soft_rings != NULL);
kmem_free(mac_srs->srs_udp_soft_rings,
sizeof (mac_soft_ring_t *) * MAX_SR_FANOUT);
mac_srs->srs_udp_soft_rings = NULL;
ASSERT(mac_srs->srs_tcp6_soft_rings != NULL);
kmem_free(mac_srs->srs_tcp6_soft_rings,
sizeof (mac_soft_ring_t *) * MAX_SR_FANOUT);
mac_srs->srs_tcp6_soft_rings = NULL;
ASSERT(mac_srs->srs_udp6_soft_rings != NULL);
kmem_free(mac_srs->srs_udp6_soft_rings,
sizeof (mac_soft_ring_t *) * MAX_SR_FANOUT);
mac_srs->srs_udp6_soft_rings = NULL;
ASSERT(mac_srs->srs_oth_soft_rings != NULL);
kmem_free(mac_srs->srs_oth_soft_rings,
sizeof (mac_soft_ring_t *) * MAX_SR_FANOUT);
mac_srs->srs_oth_soft_rings = NULL;
}
}
static void
mac_srs_ring_free(mac_soft_ring_set_t *mac_srs)
{
mac_client_impl_t *mcip;
mac_ring_t *ring;
flow_entry_t *flent;
ring = mac_srs->srs_ring;
if (mac_srs->srs_type & SRST_TX) {
ASSERT(ring == NULL);
return;
}
if (ring == NULL)
return;
flent = mac_srs->srs_flent;
mcip = flent->fe_mcip;
ASSERT(mcip != NULL);
ring->mr_classify_type = MAC_NO_CLASSIFIER;
ring->mr_srs = NULL;
}
void
mac_srs_free(mac_soft_ring_set_t *mac_srs)
{
ASSERT(mac_srs->srs_mcip == NULL ||
MAC_PERIM_HELD((mac_handle_t)mac_srs->srs_mcip->mci_mip));
ASSERT((mac_srs->srs_state & (SRS_CONDEMNED | SRS_CONDEMNED_DONE |
SRS_PROC | SRS_PROC_FAST)) == (SRS_CONDEMNED | SRS_CONDEMNED_DONE));
mac_drop_chain(mac_srs->srs_first, "SRS free");
mac_srs_ring_free(mac_srs);
mac_srs_soft_rings_free(mac_srs);
mac_srs_fanout_list_free(mac_srs);
mac_srs->srs_bw = NULL;
mac_srs_stat_delete(mac_srs);
kmem_cache_free(mac_srs_cache, mac_srs);
}
static void
mac_srs_soft_rings_quiesce(mac_soft_ring_set_t *mac_srs,
const mac_soft_ring_state_t s_ring_flag)
{
mac_soft_ring_t *softring;
ASSERT(MUTEX_HELD(&mac_srs->srs_lock));
mac_srs_soft_rings_signal(mac_srs, s_ring_flag);
if (s_ring_flag == S_RING_CONDEMNED) {
while (mac_srs->srs_soft_ring_condemned_count !=
mac_srs->srs_soft_ring_count)
cv_wait(&mac_srs->srs_async, &mac_srs->srs_lock);
} else {
while (mac_srs->srs_soft_ring_quiesced_count !=
mac_srs->srs_soft_ring_count)
cv_wait(&mac_srs->srs_async, &mac_srs->srs_lock);
}
mutex_exit(&mac_srs->srs_lock);
for (softring = mac_srs->srs_soft_ring_head; softring != NULL;
softring = softring->s_ring_next) {
(void) untimeout(softring->s_ring_tid);
softring->s_ring_tid = NULL;
}
(void) untimeout(mac_srs->srs_tid);
mac_srs->srs_tid = NULL;
mutex_enter(&mac_srs->srs_lock);
}
void
mac_srs_worker_quiesce(mac_soft_ring_set_t *mac_srs)
{
VERIFY(MUTEX_HELD(&mac_srs->srs_lock));
VERIFY((mac_srs->srs_state & (SRS_CONDEMNED | SRS_QUIESCE)) != 0);
const boolean_t condemn = (mac_srs->srs_state & SRS_CONDEMNED) != 0;
const mac_soft_ring_state_t s_ring_flag = condemn ?
S_RING_CONDEMNED : S_RING_QUIESCE;
const mac_soft_ring_set_state_t srs_poll_wait_flag = condemn ?
SRS_POLL_THR_EXITED : SRS_POLL_THR_QUIESCED;
if ((mac_srs->srs_type & SRST_TX) == 0 &&
mac_srs->srs_poll_thr != NULL) {
while (!(mac_srs->srs_state & srs_poll_wait_flag))
cv_wait(&mac_srs->srs_async, &mac_srs->srs_lock);
MAC_SRS_POLLING_OFF(mac_srs);
mac_srs->srs_state &= ~(SRS_POLLING | SRS_GET_PKTS);
}
mac_srs_soft_rings_quiesce(mac_srs, s_ring_flag);
if (condemn)
mac_srs->srs_state |= (SRS_QUIESCE_DONE | SRS_CONDEMNED_DONE);
else
mac_srs->srs_state |= SRS_QUIESCE_DONE;
cv_signal(&mac_srs->srs_quiesce_done_cv);
}
void
mac_srs_signal(mac_soft_ring_set_t *mac_srs,
const mac_soft_ring_set_state_t srs_flag)
{
mac_ring_t *ring;
ring = mac_srs->srs_ring;
ASSERT(ring == NULL || ring->mr_refcnt == 0);
if (srs_flag == SRS_CONDEMNED) {
mac_srs_threads_unbind(mac_srs);
mac_srs_remove_glist(mac_srs);
}
mutex_enter(&mac_srs->srs_lock);
mac_srs->srs_state |= srs_flag;
cv_signal(&mac_srs->srs_async);
cv_signal(&mac_srs->srs_cv);
mutex_exit(&mac_srs->srs_lock);
}
static void
mac_srs_soft_rings_signal(mac_soft_ring_set_t *mac_srs,
const mac_soft_ring_state_t sr_flag)
{
mac_soft_ring_t *softring;
for (softring = mac_srs->srs_soft_ring_head; softring != NULL;
softring = softring->s_ring_next)
mac_soft_ring_signal(softring, sr_flag);
}
void
mac_srs_worker_restart(mac_soft_ring_set_t *mac_srs)
{
boolean_t iam_rx_srs;
mac_soft_ring_t *softring;
ASSERT(MUTEX_HELD(&mac_srs->srs_lock));
if ((mac_srs->srs_type & SRST_TX) != 0) {
iam_rx_srs = B_FALSE;
ASSERT((mac_srs->srs_state &
(SRS_POLL_THR_QUIESCED | SRS_QUIESCE_DONE | SRS_QUIESCE)) ==
(SRS_QUIESCE_DONE | SRS_QUIESCE));
} else {
iam_rx_srs = B_TRUE;
ASSERT((mac_srs->srs_state &
(SRS_QUIESCE_DONE | SRS_QUIESCE)) ==
(SRS_QUIESCE_DONE | SRS_QUIESCE));
if (mac_srs->srs_poll_thr != NULL) {
ASSERT((mac_srs->srs_state & SRS_POLL_THR_QUIESCED) ==
SRS_POLL_THR_QUIESCED);
}
}
if (mac_srs->srs_soft_ring_quiesced_count != 0) {
for (softring = mac_srs->srs_soft_ring_head; softring != NULL;
softring = softring->s_ring_next) {
if (!(softring->s_ring_state & S_RING_QUIESCE))
continue;
mac_soft_ring_signal(softring, S_RING_RESTART);
}
while (mac_srs->srs_soft_ring_quiesced_count != 0)
cv_wait(&mac_srs->srs_async, &mac_srs->srs_lock);
}
mac_srs->srs_state &= ~(SRS_QUIESCE_DONE | SRS_QUIESCE | SRS_RESTART);
if (iam_rx_srs && mac_srs->srs_poll_thr != NULL) {
mac_srs->srs_state |= SRS_POLL_THR_RESTART;
cv_signal(&mac_srs->srs_cv);
while (mac_srs->srs_state & SRS_POLL_THR_QUIESCED)
cv_wait(&mac_srs->srs_async, &mac_srs->srs_lock);
ASSERT(!(mac_srs->srs_state & SRS_POLL_THR_RESTART));
}
mac_srs->srs_state |= SRS_RESTART_DONE;
cv_signal(&mac_srs->srs_quiesce_done_cv);
}
static void
mac_srs_worker_unbind(mac_soft_ring_set_t *mac_srs)
{
mutex_enter(&mac_srs->srs_lock);
if (!(mac_srs->srs_state & SRS_WORKER_BOUND)) {
ASSERT(mac_srs->srs_worker_cpuid == -1);
mutex_exit(&mac_srs->srs_lock);
return;
}
mac_srs->srs_worker_cpuid = -1;
mac_srs->srs_state &= ~SRS_WORKER_BOUND;
thread_affinity_clear(mac_srs->srs_worker);
mutex_exit(&mac_srs->srs_lock);
}
static void
mac_srs_poll_unbind(mac_soft_ring_set_t *mac_srs)
{
mutex_enter(&mac_srs->srs_lock);
if (mac_srs->srs_poll_thr == NULL ||
(mac_srs->srs_state & SRS_POLL_BOUND) == 0) {
ASSERT(mac_srs->srs_poll_cpuid == -1);
mutex_exit(&mac_srs->srs_lock);
return;
}
mac_srs->srs_poll_cpuid = -1;
mac_srs->srs_state &= ~SRS_POLL_BOUND;
thread_affinity_clear(mac_srs->srs_poll_thr);
mutex_exit(&mac_srs->srs_lock);
}
static void
mac_srs_threads_unbind(mac_soft_ring_set_t *mac_srs)
{
mac_soft_ring_t *soft_ring;
ASSERT(MAC_PERIM_HELD((mac_handle_t)mac_srs->srs_mcip->mci_mip));
mutex_enter(&cpu_lock);
mac_srs_worker_unbind(mac_srs);
if (!(mac_srs->srs_type & SRST_TX))
mac_srs_poll_unbind(mac_srs);
for (soft_ring = mac_srs->srs_soft_ring_head; soft_ring != NULL;
soft_ring = soft_ring->s_ring_next) {
mac_soft_ring_unbind(soft_ring);
}
mutex_exit(&cpu_lock);
}
static void
mac_walk_srs_and_unbind(int cpuid)
{
mac_soft_ring_set_t *mac_srs;
mac_soft_ring_t *soft_ring;
rw_enter(&mac_srs_g_lock, RW_READER);
if ((mac_srs = mac_srs_g_list) == NULL)
goto done;
for (; mac_srs != NULL; mac_srs = mac_srs->srs_next) {
if (mac_srs->srs_worker_cpuid == cpuid) {
mac_srs->srs_worker_cpuid_save = cpuid;
mac_srs_worker_unbind(mac_srs);
}
if (!(mac_srs->srs_type & SRST_TX)) {
if (mac_srs->srs_poll_cpuid == cpuid) {
mac_srs->srs_poll_cpuid_save = cpuid;
mac_srs_poll_unbind(mac_srs);
}
}
mutex_enter(&mac_srs->srs_lock);
for (soft_ring = mac_srs->srs_soft_ring_head; soft_ring != NULL;
soft_ring = soft_ring->s_ring_next) {
if (soft_ring->s_ring_cpuid == cpuid) {
soft_ring->s_ring_cpuid_save = cpuid;
mac_soft_ring_unbind(soft_ring);
}
}
mutex_exit(&mac_srs->srs_lock);
}
done:
rw_exit(&mac_srs_g_lock);
}
void
mac_tx_srs_add_ring(mac_soft_ring_set_t *mac_srs, mac_ring_t *tx_ring)
{
mac_client_impl_t *mcip = mac_srs->srs_mcip;
mac_soft_ring_t *soft_ring;
int count = mac_srs->srs_tx_ring_count;
mac_soft_ring_state_t soft_ring_type = 0;
uint_t ring_info;
ASSERT(mac_srs->srs_state & SRS_QUIESCE);
ring_info = mac_hwring_getinfo((mac_ring_handle_t)tx_ring);
if (mac_tx_serialize || (ring_info & MAC_RING_TX_SERIALIZE))
soft_ring_type |= ST_RING_WORKER_ONLY;
soft_ring = mac_soft_ring_create_tx(count, 0, soft_ring_type,
maxclsyspri, mcip, mac_srs, -1, tx_ring);
mac_srs->srs_tx_ring_count++;
mac_srs_update_fanout_list(mac_srs);
mac_soft_ring_signal(soft_ring, S_RING_QUIESCE);
}
static void
mac_soft_ring_remove(mac_soft_ring_set_t *mac_srs, mac_soft_ring_t *softring)
{
int sringcnt;
mutex_enter(&mac_srs->srs_lock);
sringcnt = mac_srs->srs_soft_ring_count;
ASSERT(sringcnt > 0);
mac_soft_ring_signal(softring, S_RING_CONDEMNED);
ASSERT(mac_srs->srs_soft_ring_condemned_count == 0);
while (mac_srs->srs_soft_ring_condemned_count != 1)
cv_wait(&mac_srs->srs_async, &mac_srs->srs_lock);
if (softring == mac_srs->srs_soft_ring_head) {
mac_srs->srs_soft_ring_head = softring->s_ring_next;
if (mac_srs->srs_soft_ring_head != NULL) {
mac_srs->srs_soft_ring_head->s_ring_prev = NULL;
} else {
mac_srs->srs_soft_ring_tail = NULL;
}
} else {
softring->s_ring_prev->s_ring_next =
softring->s_ring_next;
if (softring->s_ring_next != NULL) {
softring->s_ring_next->s_ring_prev =
softring->s_ring_prev;
} else {
mac_srs->srs_soft_ring_tail =
softring->s_ring_prev;
}
}
mac_srs->srs_soft_ring_count--;
mac_srs->srs_soft_ring_condemned_count--;
mutex_exit(&mac_srs->srs_lock);
mac_soft_ring_free(softring);
}
void
mac_tx_srs_del_ring(mac_soft_ring_set_t *mac_srs, mac_ring_t *tx_ring)
{
int i;
mac_soft_ring_t *soft_ring, *remove_sring;
mac_client_impl_t *mcip = mac_srs->srs_mcip;
mutex_enter(&mac_srs->srs_lock);
for (i = 0; i < mac_srs->srs_tx_ring_count; i++) {
soft_ring = mac_srs->srs_tx_soft_rings[i];
if (soft_ring->s_ring_tx_arg2 == tx_ring)
break;
}
mutex_exit(&mac_srs->srs_lock);
ASSERT(i < mac_srs->srs_tx_ring_count);
remove_sring = soft_ring;
if (mcip->mci_state_flags & MCIS_IS_AGGR_CLIENT) {
mac_srs_tx_t *tx = &mac_srs->srs_tx;
ASSERT(tx->st_soft_rings[tx_ring->mr_index] == remove_sring);
tx->st_soft_rings[tx_ring->mr_index] = NULL;
}
mac_soft_ring_remove(mac_srs, remove_sring);
mac_srs_update_fanout_list(mac_srs);
}
void
mac_tx_srs_setup(mac_client_impl_t *mcip, flow_entry_t *flent)
{
mac_impl_t *mip = mcip->mci_mip;
mac_soft_ring_set_t *tx_srs = flent->fe_tx_srs;
int i;
int tx_ring_count = 0;
mac_group_t *grp = NULL;
mac_ring_t *ring;
mac_srs_tx_t *tx = &tx_srs->srs_tx;
boolean_t is_aggr;
uint_t ring_info = 0;
is_aggr = (mcip->mci_state_flags & MCIS_IS_AGGR_CLIENT) != 0;
grp = flent->fe_tx_ring_group;
if (grp == NULL) {
ring = (mac_ring_t *)mip->mi_default_tx_ring;
goto no_group;
}
tx_ring_count = grp->mrg_cur_count;
ring = grp->mrg_rings;
switch (grp->mrg_state) {
case MAC_GROUP_STATE_SHARED:
case MAC_GROUP_STATE_RESERVED:
if (tx_ring_count <= 1 && !is_aggr) {
no_group:
if (ring != NULL &&
ring->mr_state != MR_INUSE) {
(void) mac_start_ring(ring);
ring_info = mac_hwring_getinfo(
(mac_ring_handle_t)ring);
}
tx->st_arg2 = (void *)ring;
mac_tx_srs_stat_recreate(tx_srs, B_FALSE);
if (mac_srs_is_bw_controlled(tx_srs)) {
tx->st_mode = SRS_TX_BW;
} else if (mac_tx_serialize ||
(ring_info & MAC_RING_TX_SERIALIZE)) {
tx->st_mode = SRS_TX_SERIALIZE;
} else {
tx->st_mode = SRS_TX_DEFAULT;
}
break;
}
if (mac_srs_is_bw_controlled(tx_srs)) {
tx->st_mode = is_aggr ?
SRS_TX_BW_AGGR : SRS_TX_BW_FANOUT;
} else {
tx->st_mode = is_aggr ? SRS_TX_AGGR :
SRS_TX_FANOUT;
}
for (i = 0; i < tx_ring_count; i++) {
VERIFY(ring != NULL);
mac_soft_ring_state_t soft_ring_type = 0;
switch (ring->mr_state) {
case MR_INUSE:
case MR_FREE:
VERIFY3P(ring->mr_srs, ==, NULL);
if (ring->mr_state != MR_INUSE)
(void) mac_start_ring(ring);
ring_info = mac_hwring_getinfo(
(mac_ring_handle_t)ring);
if (mac_tx_serialize || (ring_info &
MAC_RING_TX_SERIALIZE)) {
soft_ring_type |=
ST_RING_WORKER_ONLY;
}
(void) mac_soft_ring_create_tx(i, 0,
soft_ring_type, maxclsyspri,
mcip, tx_srs, -1, ring);
break;
default:
cmn_err(CE_PANIC,
"srs_setup: mcip = %p "
"trying to add UNKNOWN ring = %p\n",
(void *)mcip, (void *)ring);
break;
}
ring = ring->mr_next;
}
mac_srs_update_fanout_list(tx_srs);
break;
default:
ASSERT(B_FALSE);
break;
}
tx->st_func = mac_tx_get_func(tx->st_mode);
if (is_aggr) {
VERIFY(i_mac_capab_get((mac_handle_t)mip,
MAC_CAPAB_AGGR, &tx->st_capab_aggr));
}
DTRACE_PROBE3(tx__srs___setup__return, mac_soft_ring_set_t *, tx_srs,
mac_tx_srs_mode_t, tx->st_mode, int, tx_srs->srs_tx_ring_count);
}
void
mac_fanout_recompute_client(mac_client_impl_t *mcip, cpupart_t *cpupart)
{
uint64_t link_speed;
mac_resource_props_t *mcip_mrp;
flow_entry_t *flent = mcip->mci_flent;
mac_soft_ring_set_t *rx_srs;
mac_cpus_t *srs_cpu;
int soft_ring_count, maxcpus;
ASSERT(MAC_PERIM_HELD((mac_handle_t)mcip->mci_mip));
link_speed = mac_client_stat_get(
(mac_client_handle_t)mcip->mci_flent->fe_mcip, MAC_STAT_IFSPEED);
if ((link_speed != 0) &&
(link_speed != mcip->mci_flent->fe_nic_speed)) {
mcip_mrp = MCIP_RESOURCE_PROPS(mcip);
maxcpus = (cpupart != NULL) ? cpupart->cp_ncpus : ncpus;
soft_ring_count = mac_compute_soft_ring_count(flent,
flent->fe_rx_srs_cnt - 1, maxcpus);
soft_ring_count = (soft_ring_count == 0) ?
1 : soft_ring_count;
rx_srs = flent->fe_rx_srs[0];
srs_cpu = &rx_srs->srs_cpu;
if (soft_ring_count != srs_cpu->mc_rx_fanout_cnt) {
mac_fanout_setup(mcip, flent, mcip_mrp,
mac_rx_deliver, mcip, cpupart);
}
}
}
void
mac_fanout_recompute(mac_impl_t *mip)
{
mac_client_impl_t *mcip;
cpupart_t *cpupart;
boolean_t use_default;
mac_resource_props_t *mrp, *emrp;
i_mac_perim_enter(mip);
if ((mip->mi_state_flags & MIS_IS_VNIC) != 0 ||
mip->mi_linkstate != LINK_STATE_UP) {
i_mac_perim_exit(mip);
return;
}
for (mcip = mip->mi_clients_list; mcip != NULL;
mcip = mcip->mci_client_next) {
if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0)
continue;
if ((mcip->mci_state_flags & MCIS_SHARE_BOUND) != 0 ||
!MCIP_DATAPATH_SETUP(mcip))
continue;
mrp = MCIP_RESOURCE_PROPS(mcip);
emrp = MCIP_EFFECTIVE_PROPS(mcip);
use_default = B_FALSE;
pool_lock();
cpupart = mac_pset_find(mrp, &use_default);
mac_fanout_recompute_client(mcip, cpupart);
mac_set_pool_effective(use_default, cpupart, mrp, emrp);
pool_unlock();
}
i_mac_perim_exit(mip);
}
void
mac_poll_state_change(mac_handle_t mh, boolean_t enable)
{
mac_impl_t *mip = (mac_impl_t *)mh;
mac_client_impl_t *mcip;
i_mac_perim_enter(mip);
if (enable)
mip->mi_state_flags &= ~MIS_POLL_DISABLE;
else
mip->mi_state_flags |= MIS_POLL_DISABLE;
for (mcip = mip->mi_clients_list; mcip != NULL;
mcip = mcip->mci_client_next)
mac_client_update_classifier(mcip, B_TRUE);
i_mac_perim_exit(mip);
}