#include <sys/types.h>
#include <sys/errno.h>
#include <sys/sysmacros.h>
#include <sys/param.h>
#include <sys/machsystm.h>
#include <sys/stream.h>
#include <sys/strsubr.h>
#include <sys/kmem.h>
#include <sys/strsun.h>
#include <sys/callb.h>
#include <sys/sdt.h>
#include <sys/mach_descrip.h>
#include <sys/mdeg.h>
#include <net/if.h>
#include <sys/vsw.h>
#include <sys/vio_mailbox.h>
#include <sys/vio_common.h>
#include <sys/vnet_common.h>
#include <sys/vnet_mailbox.h>
#include <sys/vio_util.h>
vio_dring_reg_msg_t *vsw_create_rx_dring_info(vsw_ldc_t *);
void vsw_destroy_rx_dring(vsw_ldc_t *ldcp);
dring_info_t *vsw_map_tx_dring(vsw_ldc_t *ldcp, void *pkt);
void vsw_unmap_tx_dring(vsw_ldc_t *ldcp);
int vsw_dringsend_shm(vsw_ldc_t *, mblk_t *);
void vsw_ldc_rcv_worker(void *arg);
void vsw_stop_rcv_thread(vsw_ldc_t *ldcp);
void vsw_process_dringdata_shm(void *, void *);
static dring_info_t *vsw_create_rx_dring(vsw_ldc_t *);
static int vsw_setup_rx_dring(vsw_ldc_t *ldcp, dring_info_t *dp);
static void vsw_process_dringdata_info_shm(vsw_ldc_t *ldcp,
vio_dring_msg_t *msg);
static void vsw_process_dringdata_ack_shm(vsw_ldc_t *ldcp,
vio_dring_msg_t *msg);
static void vsw_ldc_rcv_shm(vsw_ldc_t *ldcp);
static int vsw_receive_packet(vsw_ldc_t *ldcp, mblk_t **bp);
static int vsw_send_msg_shm(vsw_ldc_t *ldcp, void *msgp, int size,
boolean_t handle_reset);
extern void vsw_process_pkt(void *);
extern void vsw_destroy_rxpools(void *);
extern dring_info_t *vsw_map_dring_cmn(vsw_ldc_t *ldcp,
vio_dring_reg_msg_t *dring_pkt);
extern void vsw_process_conn_evt(vsw_ldc_t *, uint16_t);
extern mblk_t *vsw_vlan_frame_pretag(void *arg, int type, mblk_t *mp);
extern int vsw_wretries;
extern int vsw_recv_delay;
extern int vsw_recv_retries;
extern uint32_t vsw_chain_len;
extern uint32_t vsw_num_descriptors;
extern uint32_t vsw_nrbufs_factor;
#define VSW_SWITCH_FRAMES(vswp, ldcp, bp, bpt, count, total_count) \
{ \
DTRACE_PROBE2(vsw_rx_pkts, vsw_ldc_t *, (ldcp), int, (count)); \
(vswp)->vsw_switch_frame((vswp), (bp), VSW_VNETPORT, \
(ldcp)->ldc_port, NULL); \
(bp) = (bpt) = NULL; \
(count) = 0; \
}
vio_dring_reg_msg_t *
vsw_create_rx_dring_info(vsw_ldc_t *ldcp)
{
vio_dring_reg_msg_t *mp;
vio_dring_reg_ext_msg_t *emsg;
dring_info_t *dp;
uint8_t *buf;
vsw_t *vswp = ldcp->ldc_vswp;
D1(vswp, "%s enter\n", __func__);
if ((dp = vsw_create_rx_dring(ldcp)) == NULL)
return (NULL);
mp = kmem_zalloc(VNET_DRING_REG_EXT_MSG_SIZE(dp->data_ncookies),
KM_SLEEP);
mp->tag.vio_msgtype = VIO_TYPE_CTRL;
mp->tag.vio_subtype = VIO_SUBTYPE_INFO;
mp->tag.vio_subtype_env = VIO_DRING_REG;
mp->tag.vio_sid = ldcp->local_session;
mp->num_descriptors = dp->num_descriptors;
mp->descriptor_size = dp->descriptor_size;
mp->options = dp->options;
mp->ncookies = dp->dring_ncookies;
bcopy(&dp->dring_cookie[0], &mp->cookie[0],
sizeof (ldc_mem_cookie_t));
mp->dring_ident = 0;
buf = (uint8_t *)mp->cookie;
ASSERT(mp->ncookies == 1);
buf += (mp->ncookies * sizeof (ldc_mem_cookie_t));
emsg = (vio_dring_reg_ext_msg_t *)buf;
emsg->data_ncookies = dp->data_ncookies;
emsg->data_area_size = dp->data_sz;
bcopy(dp->data_cookie, (ldc_mem_cookie_t *)emsg->data_cookie,
sizeof (ldc_mem_cookie_t) * dp->data_ncookies);
D1(vswp, "%s exit\n", __func__);
return (mp);
}
static dring_info_t *
vsw_create_rx_dring(vsw_ldc_t *ldcp)
{
vsw_t *vswp = ldcp->ldc_vswp;
ldc_mem_info_t minfo;
dring_info_t *dp;
dp = (dring_info_t *)kmem_zalloc(sizeof (dring_info_t), KM_SLEEP);
mutex_init(&dp->dlock, NULL, MUTEX_DRIVER, NULL);
ldcp->lane_out.dringp = dp;
if ((ldc_mem_dring_create(vsw_num_descriptors,
sizeof (vnet_rx_dringdata_desc_t), &dp->dring_handle)) != 0) {
DERR(vswp, "vsw_create_rx_dring(%lld): ldc dring create "
"failed", ldcp->ldc_id);
goto fail;
}
ASSERT(dp->dring_handle != NULL);
if ((ldc_mem_dring_info(dp->dring_handle, &minfo)) != 0) {
DERR(vswp, "vsw_create_rx_dring(%lld): dring info failed\n",
ldcp->ldc_id);
goto fail;
} else {
ASSERT(minfo.vaddr != 0);
dp->pub_addr = minfo.vaddr;
}
dp->num_descriptors = vsw_num_descriptors;
dp->descriptor_size = sizeof (vnet_rx_dringdata_desc_t);
dp->options = VIO_RX_DRING_DATA;
dp->dring_ncookies = 1;
dp->num_bufs = VSW_RXDRING_NRBUFS;
dp->rxdp_to_vmp = kmem_zalloc(dp->num_descriptors * sizeof (uintptr_t),
KM_SLEEP);
if (vsw_setup_rx_dring(ldcp, dp)) {
DERR(vswp, "%s: unable to setup ring", __func__);
goto fail;
}
if ((ldc_mem_dring_bind(ldcp->ldc_handle, dp->dring_handle,
LDC_DIRECT_MAP | LDC_SHADOW_MAP, LDC_MEM_RW,
&dp->dring_cookie[0], &dp->dring_ncookies)) != 0) {
DERR(vswp, "vsw_create_rx_dring: unable to bind to channel "
"%lld", ldcp->ldc_id);
goto fail;
}
dp->end_idx = 0;
dp->last_ack_recv = -1;
dp->next_rxi = 0;
return (dp);
fail:
vsw_destroy_rx_dring(ldcp);
return (NULL);
}
static int
vsw_setup_rx_dring(vsw_ldc_t *ldcp, dring_info_t *dp)
{
int i, j;
int rv;
size_t data_sz;
vio_mblk_t *vmp;
vio_mblk_t **rxdp_to_vmp;
vnet_rx_dringdata_desc_t *rxdp;
vnet_rx_dringdata_desc_t *pub_addr;
vsw_t *vswp = ldcp->ldc_vswp;
uint32_t ncookies = 0;
static char *name = "vsw_setup_rx_dring";
void *data_addr = NULL;
data_sz = RXDRING_DBLK_SZ(vswp->max_frame_size);
dp->desc_data_sz = data_sz;
dp->data_sz = (dp->num_bufs * data_sz);
data_addr = kmem_zalloc(dp->data_sz, KM_SLEEP);
dp->data_addr = data_addr;
D2(vswp, "%s: allocated %lld bytes at 0x%llx\n", name,
dp->data_sz, dp->data_addr);
rv = ldc_mem_alloc_handle(ldcp->ldc_handle, &dp->data_handle);
if (rv != 0) {
DERR(vswp, "%s: alloc mem handle failed", name);
goto fail;
}
dp->data_cookie = kmem_zalloc(VNET_DATA_AREA_COOKIES *
sizeof (ldc_mem_cookie_t), KM_SLEEP);
rv = ldc_mem_bind_handle(dp->data_handle, (caddr_t)data_addr,
dp->data_sz, LDC_DIRECT_MAP, LDC_MEM_W,
dp->data_cookie, &ncookies);
if (rv != 0) {
DERR(vswp, "%s(%lld): ldc_mem_bind_handle failed "
"(rv %d)", name, ldcp->ldc_id, rv);
goto fail;
}
if ((ncookies == 0) || (ncookies > VNET_DATA_AREA_COOKIES)) {
goto fail;
}
dp->data_ncookies = ncookies;
for (j = 1; j < ncookies; j++) {
rv = ldc_mem_nextcookie(dp->data_handle,
&(dp->data_cookie[j]));
if (rv != 0) {
DERR(vswp, "%s: ldc_mem_nextcookie "
"failed rv (%d)", name, rv);
goto fail;
}
}
rv = vio_create_mblks(dp->num_bufs, data_sz, (uint8_t *)data_addr,
&dp->rx_vmp);
if (rv != 0) {
goto fail;
}
pub_addr = dp->pub_addr;
rxdp_to_vmp = dp->rxdp_to_vmp;
for (i = 0; i < dp->num_descriptors; i++) {
rxdp = &pub_addr[i];
vmp = vio_allocb(dp->rx_vmp);
ASSERT(vmp != NULL);
rxdp->data_buf_offset = VIO_MBLK_DATA_OFF(vmp) + VNET_IPALIGN;
rxdp->dstate = VIO_DESC_FREE;
rxdp_to_vmp[i] = vmp;
}
return (0);
fail:
return (1);
}
void
vsw_destroy_rx_dring(vsw_ldc_t *ldcp)
{
vsw_t *vswp = ldcp->ldc_vswp;
lane_t *lp = &ldcp->lane_out;
dring_info_t *dp;
dp = lp->dringp;
if (dp == NULL) {
return;
}
mutex_enter(&dp->dlock);
if (dp->rx_vmp != NULL) {
vio_clobber_pool(dp->rx_vmp);
if (vio_destroy_mblks(dp->rx_vmp) != 0) {
(void) ddi_taskq_dispatch(vswp->rxp_taskq,
vsw_destroy_rxpools, dp->rx_vmp, DDI_SLEEP);
}
}
if (dp->data_cookie != NULL) {
kmem_free(dp->data_cookie, VNET_DATA_AREA_COOKIES *
sizeof (ldc_mem_cookie_t));
dp->data_cookie = NULL;
}
if (dp->data_ncookies != 0) {
(void) ldc_mem_unbind_handle(dp->data_handle);
dp->data_ncookies = 0;
}
if (dp->data_handle) {
(void) ldc_mem_free_handle(dp->data_handle);
dp->data_handle = 0;
}
if (dp->data_addr != NULL) {
kmem_free(dp->data_addr, dp->data_sz);
}
if (dp->dring_handle != 0) {
(void) ldc_mem_dring_unbind(dp->dring_handle);
(void) ldc_mem_dring_destroy(dp->dring_handle);
}
if (dp->rxdp_to_vmp != NULL) {
kmem_free(dp->rxdp_to_vmp,
dp->num_descriptors * sizeof (uintptr_t));
dp->rxdp_to_vmp = NULL;
}
mutex_exit(&dp->dlock);
mutex_destroy(&dp->dlock);
mutex_destroy(&dp->restart_lock);
kmem_free(dp, sizeof (dring_info_t));
lp->dringp = NULL;
}
dring_info_t *
vsw_map_tx_dring(vsw_ldc_t *ldcp, void *pkt)
{
int i;
int rv;
dring_info_t *dp;
vnet_rx_dringdata_desc_t *txdp;
on_trap_data_t otd;
vio_dring_reg_msg_t *dring_pkt = pkt;
dp = vsw_map_dring_cmn(ldcp, dring_pkt);
if (dp == NULL) {
return (NULL);
}
mutex_init(&dp->txlock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&dp->restart_lock, NULL, MUTEX_DRIVER, NULL);
dp->next_txi = dp->restart_peer_txi = 0;
dp->restart_reqd = B_TRUE;
ldcp->dringdata_msgid = 0;
ldcp->lane_in.dringp = dp;
rv = LDC_ON_TRAP(&otd);
if (rv != 0) {
goto fail;
}
txdp = (vnet_rx_dringdata_desc_t *)dp->pub_addr;
for (i = 0; i < dp->num_descriptors; i++) {
txdp[i].dstate = VIO_DESC_DONE;
}
(void) LDC_NO_TRAP();
return (dp);
fail:
if (dp->dring_handle != 0) {
(void) ldc_mem_dring_unmap(dp->dring_handle);
}
kmem_free(dp, sizeof (*dp));
return (NULL);
}
void
vsw_unmap_tx_dring(vsw_ldc_t *ldcp)
{
lane_t *lp = &ldcp->lane_in;
dring_info_t *dp;
if ((dp = lp->dringp) == NULL) {
return;
}
if (dp->data_handle != 0) {
(void) ldc_mem_unmap(dp->data_handle);
(void) ldc_mem_free_handle(dp->data_handle);
dp->data_handle = 0;
}
if (dp->data_cookie != NULL) {
kmem_free(dp->data_cookie, dp->data_ncookies *
sizeof (ldc_mem_cookie_t));
dp->data_cookie = NULL;
dp->data_ncookies = 0;
}
if (dp->dring_handle != 0) {
(void) ldc_mem_dring_unmap(dp->dring_handle);
dp->dring_handle = 0;
}
mutex_destroy(&dp->txlock);
kmem_free(dp, sizeof (dring_info_t));
lp->dringp = NULL;
}
void
vsw_ldc_rcv_worker(void *arg)
{
callb_cpr_t cprinfo;
vsw_ldc_t *ldcp = (vsw_ldc_t *)arg;
vsw_t *vswp = ldcp->ldc_vswp;
D1(vswp, "%s(%lld):enter\n", __func__, ldcp->ldc_id);
CALLB_CPR_INIT(&cprinfo, &ldcp->rcv_thr_lock, callb_generic_cpr,
"vsw_rcv_thread");
mutex_enter(&ldcp->rcv_thr_lock);
while (!(ldcp->rcv_thr_flags & VSW_WTHR_STOP)) {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
while (!(ldcp->rcv_thr_flags &
(VSW_WTHR_DATARCVD | VSW_WTHR_STOP))) {
cv_wait(&ldcp->rcv_thr_cv, &ldcp->rcv_thr_lock);
}
CALLB_CPR_SAFE_END(&cprinfo, &ldcp->rcv_thr_lock)
if (ldcp->rcv_thr_flags & VSW_WTHR_STOP) {
D2(vswp, "%s(%lld):Rx thread stopped\n",
__func__, ldcp->ldc_id);
break;
}
ldcp->rcv_thr_flags &= ~VSW_WTHR_DATARCVD;
mutex_exit(&ldcp->rcv_thr_lock);
D1(vswp, "%s(%lld):calling vsw_process_pkt\n",
__func__, ldcp->ldc_id);
vsw_ldc_rcv_shm(ldcp);
mutex_enter(&ldcp->rcv_thr_lock);
}
ldcp->rcv_thr_flags &= ~VSW_WTHR_STOP;
ldcp->rcv_thread = NULL;
CALLB_CPR_EXIT(&cprinfo);
D1(vswp, "%s(%lld):exit\n", __func__, ldcp->ldc_id);
thread_exit();
}
static void
vsw_ldc_rcv_shm(vsw_ldc_t *ldcp)
{
int rv;
uint32_t end_ix;
vio_dring_msg_t msg;
vio_dring_msg_t *msgp = &msg;
int count = 0;
int total_count = 0;
uint32_t retries = 0;
mblk_t *bp = NULL;
mblk_t *bpt = NULL;
mblk_t *mp = NULL;
vsw_t *vswp = ldcp->ldc_vswp;
lane_t *lp = &ldcp->lane_out;
dring_info_t *dp = lp->dringp;
do {
again:
rv = vsw_receive_packet(ldcp, &mp);
if (rv != 0) {
if (rv == EINVAL) {
continue;
}
if (rv != EAGAIN) {
break;
}
if (retries == vsw_recv_retries) {
DTRACE_PROBE1(vsw_noready_rxds,
vsw_ldc_t *, ldcp);
break;
}
if (bp != NULL) {
VSW_SWITCH_FRAMES(vswp, ldcp, bp, bpt, count,
total_count);
}
retries++;
drv_usecwait(vsw_recv_delay);
goto again;
}
retries = 0;
if (bp == NULL) {
bp = mp;
bpt = bp;
bpt->b_next = NULL;
} else {
mp->b_next = NULL;
bpt->b_next = mp;
bpt = mp;
}
total_count++;
count++;
if (count == vsw_chain_len) {
VSW_SWITCH_FRAMES(vswp, ldcp, bp, bpt, count,
total_count);
}
} while (total_count < dp->num_bufs);
DTRACE_PROBE2(vsw_rx_total_count, vsw_ldc_t *, ldcp,
int, (total_count));
if (bp != NULL) {
VSW_SWITCH_FRAMES(vswp, ldcp, bp, bpt, count,
total_count);
}
end_ix = lp->dringp->next_rxi;
DECR_RXI(dp, end_ix);
msgp->tag.vio_msgtype = VIO_TYPE_DATA;
msgp->tag.vio_subtype = VIO_SUBTYPE_ACK;
msgp->tag.vio_subtype_env = VIO_DRING_DATA;
msgp->dring_ident = ldcp->lane_in.dringp->ident;
msgp->tag.vio_sid = ldcp->local_session;
msgp->dring_process_state = VIO_DP_STOPPED;
msgp->start_idx = VNET_START_IDX_UNSPEC;
msgp->end_idx = end_ix;
(void) vsw_send_msg_shm(ldcp, (void *)msgp,
sizeof (vio_dring_msg_t), B_TRUE);
ldcp->ldc_stats.dring_data_acks_sent++;
ldcp->ldc_stats.dring_stopped_acks_sent++;
}
static int
vsw_receive_packet(vsw_ldc_t *ldcp, mblk_t **bp)
{
uint32_t rxi;
vio_mblk_t *vmp;
vio_mblk_t *new_vmp;
struct ether_header *ehp;
vnet_rx_dringdata_desc_t *rxdp;
int err = 0;
uint_t nbytes = 0;
mblk_t *mp = NULL;
mblk_t *dmp = NULL;
vgen_stats_t *statsp = &ldcp->ldc_stats;
dring_info_t *dp = ldcp->lane_out.dringp;
vnet_rx_dringdata_desc_t *pub_addr = dp->pub_addr;
rxi = dp->next_rxi;
rxdp = &(pub_addr[rxi]);
vmp = dp->rxdp_to_vmp[rxi];
if (rxdp->dstate != VIO_DESC_READY) {
return (EAGAIN);
}
MEMBAR_CONSUMER();
if ((rxdp->nbytes < ETHERMIN) ||
(rxdp->nbytes > ldcp->lane_in.mtu) ||
(rxdp->data_buf_offset !=
(VIO_MBLK_DATA_OFF(vmp) + VNET_IPALIGN))) {
statsp->ierrors++;
rxdp->dstate = VIO_DESC_DONE;
err = EIO;
goto done;
}
new_vmp = vio_allocb(dp->rx_vmp);
nbytes = rxdp->nbytes;
mp = vmp->mp;
if (new_vmp == NULL) {
statsp->norcvbuf++;
dmp = allocb(nbytes + VNET_IPALIGN, BPRI_MED);
bcopy(mp->b_rptr + VNET_IPALIGN,
dmp->b_rptr + VNET_IPALIGN, nbytes);
mp = dmp;
} else {
vmp->state = VIO_MBLK_HAS_DATA;
rxdp->data_buf_offset =
VIO_MBLK_DATA_OFF(new_vmp) + VNET_IPALIGN;
dp->rxdp_to_vmp[rxi] = new_vmp;
}
mp->b_rptr += VNET_IPALIGN;
mp->b_wptr = mp->b_rptr + nbytes;
MEMBAR_PRODUCER();
rxdp->dstate = VIO_DESC_DONE;
statsp->ipackets++;
statsp->rbytes += rxdp->nbytes;
ehp = (struct ether_header *)mp->b_rptr;
if (IS_BROADCAST(ehp))
statsp->brdcstrcv++;
else if (IS_MULTICAST(ehp))
statsp->multircv++;
done:
INCR_RXI(dp, rxi);
dp->next_rxi = rxi;
*bp = mp;
return (err);
}
void
vsw_stop_rcv_thread(vsw_ldc_t *ldcp)
{
kt_did_t tid = 0;
vsw_t *vswp = ldcp->ldc_vswp;
D1(vswp, "%s(%lld):enter\n", __func__, ldcp->ldc_id);
mutex_enter(&ldcp->rcv_thr_lock);
if (ldcp->rcv_thread != NULL) {
tid = ldcp->rcv_thread->t_did;
ldcp->rcv_thr_flags |= VSW_WTHR_STOP;
cv_signal(&ldcp->rcv_thr_cv);
}
mutex_exit(&ldcp->rcv_thr_lock);
if (tid != 0) {
thread_join(tid);
}
D1(vswp, "%s(%lld):exit\n", __func__, ldcp->ldc_id);
}
int
vsw_dringsend_shm(vsw_ldc_t *ldcp, mblk_t *mp)
{
uint32_t next_txi;
uint32_t txi;
vnet_rx_dringdata_desc_t *txdp;
struct ether_header *ehp;
size_t mblksz;
caddr_t dst;
mblk_t *bp;
size_t size;
on_trap_data_t otd;
uint32_t buf_offset;
vnet_rx_dringdata_desc_t *pub_addr;
vio_dring_msg_t msg;
vio_dring_msg_t *msgp = &msg;
int rv = 0;
boolean_t resched_peer = B_FALSE;
boolean_t is_bcast = B_FALSE;
boolean_t is_mcast = B_FALSE;
vgen_stats_t *statsp = &ldcp->ldc_stats;
lane_t *lane_in = &ldcp->lane_in;
lane_t *lane_out = &ldcp->lane_out;
dring_info_t *dp = lane_in->dringp;
vsw_t *vswp = ldcp->ldc_vswp;
if ((!(lane_in->lstate & VSW_LANE_ACTIVE)) ||
(ldcp->ldc_status != LDC_UP) || (ldcp->ldc_handle == 0)) {
DWARN(vswp, "%s(%lld) status(%d) lstate(0x%llx), dropping "
"packet\n", __func__, ldcp->ldc_id, ldcp->ldc_status,
lane_in->lstate);
statsp->oerrors++;
return (LDC_TX_FAILURE);
}
if (dp == NULL) {
DERR(vswp, "%s(%lld): no dring for outbound lane on"
" channel %d", __func__, ldcp->ldc_id, ldcp->ldc_id);
statsp->oerrors++;
return (LDC_TX_FAILURE);
}
pub_addr = dp->pub_addr;
size = msgsize(mp);
if (size > (size_t)lane_out->mtu) {
DERR(vswp, "%s(%lld) invalid size (%ld)\n", __func__,
ldcp->ldc_id, size);
statsp->oerrors++;
return (LDC_TX_FAILURE);
}
if (size < ETHERMIN)
size = ETHERMIN;
ehp = (struct ether_header *)mp->b_rptr;
is_bcast = IS_BROADCAST(ehp);
is_mcast = IS_MULTICAST(ehp);
rv = LDC_ON_TRAP(&otd);
if (rv != 0) {
DERR(vswp, "%s(%lld) data access fault occured\n",
__func__, ldcp->ldc_id);
statsp->oerrors++;
if (mutex_owned(&dp->txlock)) {
mutex_exit(&dp->txlock);
}
if (mutex_owned(&dp->restart_lock)) {
mutex_exit(&dp->restart_lock);
}
goto dringsend_shm_exit;
}
mutex_enter(&dp->txlock);
txi = next_txi = dp->next_txi;
INCR_TXI(dp, next_txi);
txdp = &(pub_addr[txi]);
if (txdp->dstate != VIO_DESC_DONE) {
statsp->tx_no_desc++;
mutex_exit(&dp->txlock);
(void) LDC_NO_TRAP();
return (LDC_TX_NORESOURCES);
} else {
txdp->dstate = VIO_DESC_INITIALIZING;
}
dp->next_txi = next_txi;
mutex_exit(&dp->txlock);
MEMBAR_CONSUMER();
buf_offset = txdp->data_buf_offset;
dst = (caddr_t)dp->data_addr + buf_offset;
for (bp = mp; bp != NULL; bp = bp->b_cont) {
mblksz = MBLKL(bp);
bcopy(bp->b_rptr, dst, mblksz);
dst += mblksz;
}
txdp->nbytes = size;
MEMBAR_PRODUCER();
mutex_enter(&dp->restart_lock);
ASSERT(txdp->dstate == VIO_DESC_INITIALIZING);
txdp->dstate = VIO_DESC_READY;
if (dp->restart_reqd == B_TRUE && dp->restart_peer_txi == txi) {
dp->restart_reqd = B_FALSE;
resched_peer = B_TRUE;
}
statsp->opackets++;
statsp->obytes += size;
if (is_bcast)
statsp->brdcstxmt++;
else if (is_mcast)
statsp->multixmt++;
mutex_exit(&dp->restart_lock);
(void) LDC_NO_TRAP();
if (resched_peer == B_TRUE) {
msgp->tag.vio_msgtype = VIO_TYPE_DATA;
msgp->tag.vio_subtype = VIO_SUBTYPE_INFO;
msgp->tag.vio_subtype_env = VIO_DRING_DATA;
msgp->tag.vio_sid = ldcp->local_session;
msgp->dring_ident = lane_out->dringp->ident;
msgp->start_idx = txi;
msgp->end_idx = -1;
rv = vsw_send_msg_shm(ldcp, (void *)msgp, sizeof (*msgp),
B_FALSE);
if (rv != 0) {
DERR(vswp, "%s(%lld) failed sending dringdata msg\n",
__func__, ldcp->ldc_id);
mutex_enter(&dp->restart_lock);
statsp->oerrors++;
dp->restart_reqd = B_TRUE;
mutex_exit(&dp->restart_lock);
}
statsp->dring_data_msgs_sent++;
}
dringsend_shm_exit:
if (rv == ECONNRESET || rv == EACCES) {
vsw_process_conn_evt(ldcp, VSW_CONN_RESET);
}
return (LDC_TX_SUCCESS);
}
void
vsw_process_dringdata_shm(void *arg, void *dpkt)
{
vsw_ldc_t *ldcp = arg;
vsw_t *vswp = ldcp->ldc_vswp;
vio_dring_msg_t *dring_pkt = dpkt;
switch (dring_pkt->tag.vio_subtype) {
case VIO_SUBTYPE_INFO:
D2(vswp, "%s(%lld): VIO_SUBTYPE_INFO", __func__, ldcp->ldc_id);
vsw_process_dringdata_info_shm(ldcp, dring_pkt);
break;
case VIO_SUBTYPE_ACK:
D2(vswp, "%s(%lld): VIO_SUBTYPE_ACK", __func__, ldcp->ldc_id);
vsw_process_dringdata_ack_shm(ldcp, dring_pkt);
break;
case VIO_SUBTYPE_NACK:
DWARN(vswp, "%s(%lld): VIO_SUBTYPE_NACK",
__func__, ldcp->ldc_id);
vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
break;
default:
DERR(vswp, "%s(%lld): Unknown vio_subtype %x\n", __func__,
ldcp->ldc_id, dring_pkt->tag.vio_subtype);
}
}
static void
vsw_process_dringdata_info_shm(vsw_ldc_t *ldcp, vio_dring_msg_t *msg)
{
dring_info_t *dp = ldcp->lane_in.dringp;
vsw_t *vswp = ldcp->ldc_vswp;
vgen_stats_t *statsp = &ldcp->ldc_stats;
if (dp->ident != msg->dring_ident) {
DERR(vswp, "%s(%lld): Invalid dring ident 0x%llx",
__func__, ldcp->ldc_id, msg->dring_ident);
return;
}
statsp->dring_data_msgs_rcvd++;
ASSERT(MUTEX_HELD(&ldcp->ldc_cblock));
mutex_exit(&ldcp->ldc_cblock);
mutex_enter(&ldcp->rcv_thr_lock);
if (!(ldcp->rcv_thr_flags & VSW_WTHR_DATARCVD)) {
ldcp->rcv_thr_flags |= VSW_WTHR_DATARCVD;
cv_signal(&ldcp->rcv_thr_cv);
}
mutex_exit(&ldcp->rcv_thr_lock);
mutex_enter(&ldcp->ldc_cblock);
}
static void
vsw_process_dringdata_ack_shm(vsw_ldc_t *ldcp, vio_dring_msg_t *msg)
{
dring_info_t *dp;
uint32_t start;
int32_t end;
int rv;
on_trap_data_t otd;
uint32_t txi;
vnet_rx_dringdata_desc_t *txdp;
vnet_rx_dringdata_desc_t *pub_addr;
boolean_t ready_txd = B_FALSE;
vsw_t *vswp = ldcp->ldc_vswp;
vgen_stats_t *statsp = &ldcp->ldc_stats;
dp = ldcp->lane_in.dringp;
start = msg->start_idx;
end = msg->end_idx;
pub_addr = dp->pub_addr;
if (((start != VNET_START_IDX_UNSPEC) && !(CHECK_TXI(dp, start))) ||
!(CHECK_TXI(dp, end))) {
DWARN(vswp, "%s(%lld): Invalid Tx ack start(%d) or end(%d)\n",
__func__, ldcp->ldc_id, start, end);
return;
}
if (msg->dring_ident != ldcp->lane_out.dringp->ident) {
DWARN(vswp, "%s(%lld): Invalid dring ident 0x%x\n",
__func__, ldcp->ldc_id, msg->dring_ident);
return;
}
statsp->dring_data_acks_rcvd++;
if (msg->dring_process_state != VIO_DP_STOPPED) {
return;
}
statsp->dring_stopped_acks_rcvd++;
rv = LDC_ON_TRAP(&otd);
if (rv != 0) {
if (mutex_owned(&dp->restart_lock)) {
mutex_exit(&dp->restart_lock);
}
return;
}
mutex_enter(&dp->restart_lock);
ready_txd = B_FALSE;
txi = end;
INCR_TXI(dp, txi);
txdp = &pub_addr[txi];
if (txdp->dstate == VIO_DESC_READY) {
ready_txd = B_TRUE;
}
(void) LDC_NO_TRAP();
if (ready_txd == B_FALSE) {
dp->restart_reqd = B_TRUE;
dp->restart_peer_txi = txi;
mutex_exit(&dp->restart_lock);
return;
}
dp->restart_reqd = B_FALSE;
mutex_exit(&dp->restart_lock);
msg->tag.vio_msgtype = VIO_TYPE_DATA;
msg->tag.vio_subtype = VIO_SUBTYPE_INFO;
msg->tag.vio_subtype_env = VIO_DRING_DATA;
msg->tag.vio_sid = ldcp->local_session;
msg->dring_ident = ldcp->lane_out.dringp->ident;
msg->start_idx = txi;
msg->end_idx = -1;
rv = vsw_send_msg_shm(ldcp, (void *)msg,
sizeof (vio_dring_msg_t), B_FALSE);
statsp->dring_data_msgs_sent++;
if (rv != 0) {
mutex_enter(&dp->restart_lock);
dp->restart_reqd = B_TRUE;
mutex_exit(&dp->restart_lock);
}
if (rv == ECONNRESET) {
vsw_process_conn_evt(ldcp, VSW_CONN_RESET);
}
}
int
vsw_send_msg_shm(vsw_ldc_t *ldcp, void *msgp, int size, boolean_t handle_reset)
{
int rv;
int retries = vsw_wretries;
size_t msglen = size;
vsw_t *vswp = ldcp->ldc_vswp;
vio_dring_msg_t *dmsg = (vio_dring_msg_t *)msgp;
D1(vswp, "vsw_send_msg (%lld) enter : sending %d bytes",
ldcp->ldc_id, size);
dmsg->seq_num = atomic_inc_32_nv(&ldcp->dringdata_msgid);
do {
msglen = size;
rv = ldc_write(ldcp->ldc_handle, (caddr_t)msgp, &msglen);
} while (rv == EWOULDBLOCK && --retries > 0);
if ((rv != 0) || (msglen != size)) {
DERR(vswp, "vsw_send_msg_shm:ldc_write failed: "
"chan(%lld) rv(%d) size (%d) msglen(%d)\n",
ldcp->ldc_id, rv, size, msglen);
ldcp->ldc_stats.oerrors++;
}
if (rv == ECONNRESET) {
DWARN(vswp, "%s (%lld) channel reset", __func__, ldcp->ldc_id);
if (handle_reset) {
vsw_process_conn_evt(ldcp, VSW_CONN_RESET);
}
}
return (rv);
}