#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/ethernet.h>
#include <sys/mach_descrip.h>
#include <sys/mdeg.h>
#include <sys/vnet.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>
#include <sys/vnet_gen.h>
int vgen_create_rx_dring(vgen_ldc_t *ldcp);
void vgen_destroy_rx_dring(vgen_ldc_t *ldcp);
int vgen_map_tx_dring(vgen_ldc_t *ldcp, void *pkt);
void vgen_unmap_tx_dring(vgen_ldc_t *ldcp);
int vgen_map_data(vgen_ldc_t *ldcp, void *pkt);
int vgen_dringsend_shm(void *arg, mblk_t *mp);
int vgen_handle_dringdata_shm(void *arg1, void *arg2);
mblk_t *vgen_poll_rcv_shm(vgen_ldc_t *ldcp, int bytes_to_pickup);
int vgen_send_dringack_shm(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp,
uint32_t start, int32_t end, uint8_t pstate);
static int vgen_handle_dringdata_info_shm(vgen_ldc_t *ldcp, vio_msg_tag_t *tp);
static int vgen_handle_dringdata_ack_shm(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
static int vgen_handle_dringdata_nack_shm(vgen_ldc_t *ldcp, vio_msg_tag_t *tp);
static int vgen_intr_rcv_shm(vgen_ldc_t *ldcp);
static int vgen_receive_packet(vgen_ldc_t *ldcp, mblk_t **bp, uint_t *size);
static int vgen_send_dringdata_shm(vgen_ldc_t *ldcp, uint32_t start,
int32_t end);
static int vgen_sendmsg_shm(vgen_ldc_t *ldcp, caddr_t msg, size_t msglen);
extern int vgen_handle_evt_read(vgen_ldc_t *ldcp, vgen_caller_t caller);
extern int vgen_handle_evt_reset(vgen_ldc_t *ldcp, vgen_caller_t caller);
extern void vgen_handle_pkt_data(void *arg1, void *arg2, uint32_t msglen);
extern void vgen_destroy_rxpools(void *arg);
extern uint32_t vnet_num_descriptors;
extern uint32_t vgen_chain_len;
extern uint32_t vgen_ldcwr_retries;
extern uint32_t vgen_recv_delay;
extern uint32_t vgen_recv_retries;
extern uint32_t vgen_nrbufs_factor;
#ifdef DEBUG
#define DEBUG_PRINTF vgen_debug_printf
extern int vnet_dbglevel;
extern int vgen_inject_err_flag;
extern void vgen_debug_printf(const char *fname, vgen_t *vgenp,
vgen_ldc_t *ldcp, const char *fmt, ...);
extern boolean_t vgen_inject_error(vgen_ldc_t *ldcp, int error);
#endif
int
vgen_create_rx_dring(vgen_ldc_t *ldcp)
{
int i, j;
int rv;
uint32_t ncookies;
ldc_mem_info_t minfo;
vnet_rx_dringdata_desc_t *rxdp;
size_t data_sz;
vio_mblk_t *vmp;
vio_mblk_t **rxdp_to_vmp;
uint32_t rxdsize;
caddr_t datap = NULL;
vgen_t *vgenp = LDC_TO_VGEN(ldcp);
rxdsize = sizeof (vnet_rx_dringdata_desc_t);
ldcp->num_rxds = vnet_num_descriptors;
ldcp->num_rbufs = VGEN_RXDRING_NRBUFS;
rv = ldc_mem_dring_create(ldcp->num_rxds, rxdsize,
&ldcp->rx_dring_handle);
if (rv != 0) {
DWARN(vgenp, ldcp, "ldc_mem_dring_create() failed\n");
goto fail;
}
rv = ldc_mem_dring_info(ldcp->rx_dring_handle, &minfo);
if (rv != 0) {
DWARN(vgenp, ldcp, "ldc_mem_dring_info() failed\n");
goto fail;
}
ldcp->rxdp = (vnet_rx_dringdata_desc_t *)(minfo.vaddr);
bzero(ldcp->rxdp, sizeof (*rxdp) * (ldcp->num_rxds));
rxdp_to_vmp = kmem_zalloc(ldcp->num_rxds * sizeof (uintptr_t),
KM_SLEEP);
ldcp->rxdp_to_vmp = rxdp_to_vmp;
data_sz = RXDRING_DBLK_SZ(vgenp->max_frame_size);
ldcp->rx_data_sz = data_sz * ldcp->num_rbufs;
ldcp->rx_dblk_sz = data_sz;
datap = kmem_zalloc(ldcp->rx_data_sz, KM_SLEEP);
ldcp->rx_datap = datap;
rv = ldc_mem_alloc_handle(ldcp->ldc_handle, &ldcp->rx_data_handle);
if (rv) {
ldcp->rx_data_handle = 0;
goto fail;
}
ldcp->rx_data_cookie = kmem_zalloc(VNET_DATA_AREA_COOKIES *
sizeof (ldc_mem_cookie_t), KM_SLEEP);
ncookies = 0;
rv = ldc_mem_bind_handle(ldcp->rx_data_handle, (caddr_t)datap,
ldcp->rx_data_sz, LDC_DIRECT_MAP, LDC_MEM_W,
ldcp->rx_data_cookie, &ncookies);
if (rv != 0) {
goto fail;
}
if ((ncookies == 0) || (ncookies > VNET_DATA_AREA_COOKIES)) {
goto fail;
}
ldcp->rx_data_ncookies = ncookies;
for (j = 1; j < ncookies; j++) {
rv = ldc_mem_nextcookie(ldcp->rx_data_handle,
&(ldcp->rx_data_cookie[j]));
if (rv != 0) {
DERR(vgenp, ldcp, "ldc_mem_nextcookie "
"failed rv (%d)", rv);
goto fail;
}
}
rv = vio_create_mblks(ldcp->num_rbufs, data_sz, (uint8_t *)datap,
&ldcp->rx_vmp);
if (rv != 0) {
goto fail;
}
for (i = 0; i < ldcp->num_rxds; i++) {
rxdp = &(ldcp->rxdp[i]);
vmp = vio_allocb(ldcp->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;
}
rv = ldc_mem_dring_bind(ldcp->ldc_handle, ldcp->rx_dring_handle,
LDC_DIRECT_MAP | LDC_SHADOW_MAP, LDC_MEM_RW,
&ldcp->rx_dring_cookie, &ncookies);
if (rv != 0) {
DWARN(vgenp, ldcp, "ldc_mem_dring_bind failed "
"rv(%x)\n", rv);
goto fail;
}
ASSERT(ncookies == 1);
ldcp->rx_dring_ncookies = ncookies;
ldcp->next_rxseq = VNET_ISS;
ldcp->next_rxi = 0;
return (VGEN_SUCCESS);
fail:
vgen_destroy_rx_dring(ldcp);
return (VGEN_FAILURE);
}
void
vgen_destroy_rx_dring(vgen_ldc_t *ldcp)
{
vgen_t *vgenp = LDC_TO_VGEN(ldcp);
if (ldcp->rx_dring_ncookies != 0) {
(void) ldc_mem_dring_unbind(ldcp->rx_dring_handle);
ldcp->rx_dring_ncookies = 0;
}
if (ldcp->rx_vmp != NULL) {
vio_clobber_pool(ldcp->rx_vmp);
if (vio_destroy_mblks(ldcp->rx_vmp) != 0) {
(void) ddi_taskq_dispatch(vgenp->rxp_taskq,
vgen_destroy_rxpools, ldcp->rx_vmp, DDI_SLEEP);
}
ldcp->rx_vmp = NULL;
}
if (ldcp->rx_data_cookie != NULL) {
kmem_free(ldcp->rx_data_cookie, VNET_DATA_AREA_COOKIES *
sizeof (ldc_mem_cookie_t));
ldcp->rx_data_cookie = NULL;
}
if (ldcp->rx_data_ncookies != 0) {
(void) ldc_mem_unbind_handle(ldcp->rx_data_handle);
ldcp->rx_data_ncookies = 0;
}
if (ldcp->rx_data_handle != 0) {
(void) ldc_mem_free_handle(ldcp->rx_data_handle);
ldcp->rx_data_handle = 0;
}
if (ldcp->rx_datap != NULL) {
kmem_free(ldcp->rx_datap, ldcp->rx_data_sz);
ldcp->rx_datap = NULL;
ldcp->rx_data_sz = 0;
}
if (ldcp->rx_dring_handle != 0) {
(void) ldc_mem_dring_destroy(ldcp->rx_dring_handle);
ldcp->rx_dring_handle = 0;
ldcp->rxdp = NULL;
}
if (ldcp->rxdp_to_vmp != NULL) {
kmem_free(ldcp->rxdp_to_vmp,
ldcp->num_rxds * sizeof (uintptr_t));
ldcp->rxdp_to_vmp = NULL;
}
ldcp->next_rxi = 0;
ldcp->next_rxseq = VNET_ISS;
}
int
vgen_map_tx_dring(vgen_ldc_t *ldcp, void *pkt)
{
int i;
int rv;
ldc_mem_info_t minfo;
ldc_mem_cookie_t dcookie;
uint32_t ncookies;
uint32_t num_desc;
uint32_t desc_size;
vnet_rx_dringdata_desc_t *txdp;
on_trap_data_t otd;
vio_dring_reg_msg_t *msg = pkt;
ncookies = msg->ncookies;
num_desc = msg->num_descriptors;
desc_size = msg->descriptor_size;
if (num_desc < VGEN_NUM_DESCRIPTORS_MIN ||
desc_size < sizeof (vnet_rx_dringdata_desc_t) ||
ncookies > 1) {
goto fail;
}
bcopy(&msg->cookie[0], &dcookie, sizeof (ldc_mem_cookie_t));
rv = ldc_mem_dring_map(ldcp->ldc_handle, &dcookie, ncookies, num_desc,
desc_size, LDC_DIRECT_MAP, &(ldcp->tx_dring_handle));
if (rv != 0) {
goto fail;
}
rv = ldc_mem_dring_info(ldcp->tx_dring_handle, &minfo);
if (rv != 0) {
goto fail;
}
ldcp->mtxdp = (vnet_rx_dringdata_desc_t *)(minfo.vaddr);
bcopy(&dcookie, &(ldcp->tx_dring_cookie), sizeof (dcookie));
ldcp->tx_dring_ncookies = ncookies;
ldcp->num_txds = num_desc;
ldcp->next_txi = ldcp->cur_txi = ldcp->resched_peer_txi = 0;
ldcp->next_txseq = VNET_ISS - 1;
ldcp->resched_peer = B_TRUE;
ldcp->dring_mtype = minfo.mtype;
ldcp->dringdata_msgid = 0;
bcopy(&dcookie, &(ldcp->peer_hparams.dring_cookie),
sizeof (ldc_mem_cookie_t));
ldcp->peer_hparams.num_desc = num_desc;
ldcp->peer_hparams.desc_size = desc_size;
ldcp->peer_hparams.dring_ncookies = ncookies;
ldcp->peer_hparams.dring_ident = (uint64_t)ldcp->mtxdp;
msg->dring_ident = (uint64_t)ldcp->mtxdp;
rv = LDC_ON_TRAP(&otd);
if (rv != 0) {
goto fail;
}
for (i = 0; i < num_desc; i++) {
txdp = &ldcp->mtxdp[i];
txdp->dstate = VIO_DESC_DONE;
}
(void) LDC_NO_TRAP();
return (VGEN_SUCCESS);
fail:
if (ldcp->tx_dring_handle != 0) {
(void) ldc_mem_dring_unmap(ldcp->tx_dring_handle);
ldcp->tx_dring_handle = 0;
}
return (VGEN_FAILURE);
}
void
vgen_unmap_tx_dring(vgen_ldc_t *ldcp)
{
if (ldcp->tx_datap != NULL) {
(void) ldc_mem_unmap(ldcp->tx_data_handle);
ldcp->tx_datap = NULL;
}
if (ldcp->tx_data_handle != 0) {
(void) ldc_mem_free_handle(ldcp->tx_data_handle);
ldcp->tx_data_handle = 0;
}
if (ldcp->tx_data_cookie != NULL) {
kmem_free(ldcp->tx_data_cookie, ldcp->tx_data_ncookies *
sizeof (ldc_mem_cookie_t));
ldcp->tx_data_cookie = NULL;
ldcp->tx_data_ncookies = 0;
}
if (ldcp->tx_dring_handle != 0) {
(void) ldc_mem_dring_unmap(ldcp->tx_dring_handle);
ldcp->tx_dring_handle = 0;
}
bzero(&ldcp->tx_dring_cookie, sizeof (ldcp->tx_dring_cookie));
ldcp->mtxdp = NULL;
ldcp->next_txi = ldcp->cur_txi = ldcp->resched_peer_txi = 0;
ldcp->num_txds = 0;
ldcp->next_txseq = VNET_ISS - 1;
ldcp->resched_peer = B_TRUE;
}
int
vgen_map_data(vgen_ldc_t *ldcp, void *pkt)
{
int rv;
vio_dring_reg_ext_msg_t *emsg;
vio_dring_reg_msg_t *msg = (vio_dring_reg_msg_t *)pkt;
uint8_t *buf = (uint8_t *)msg->cookie;
vgen_t *vgenp = LDC_TO_VGEN(ldcp);
ldc_mem_info_t minfo;
ASSERT(msg->ncookies == 1);
buf += (msg->ncookies * sizeof (ldc_mem_cookie_t));
emsg = (vio_dring_reg_ext_msg_t *)buf;
if (emsg->data_ncookies > VNET_DATA_AREA_COOKIES) {
return (VGEN_FAILURE);
}
ldcp->tx_data_ncookies = emsg->data_ncookies;
ldcp->tx_data_sz = emsg->data_area_size;
rv = ldc_mem_alloc_handle(ldcp->ldc_handle, &ldcp->tx_data_handle);
if (rv != 0) {
DWARN(vgenp, ldcp, "ldc_mem_alloc_handle() failed: %d\n", rv);
return (VGEN_FAILURE);
}
rv = ldc_mem_map(ldcp->tx_data_handle, emsg->data_cookie,
emsg->data_ncookies, LDC_DIRECT_MAP, LDC_MEM_W,
(caddr_t *)&ldcp->tx_datap, NULL);
if (rv != 0) {
DWARN(vgenp, ldcp, "ldc_mem_map() failed: %d\n", rv);
return (VGEN_FAILURE);
}
rv = ldc_mem_info(ldcp->tx_data_handle, &minfo);
if (rv != 0) {
DWARN(vgenp, ldcp, "ldc_mem_info() failed: %d\n", rv);
return (VGEN_FAILURE);
}
if (minfo.mtype != LDC_DIRECT_MAP) {
DWARN(vgenp, ldcp, "mtype(%d) is not direct map\n",
minfo.mtype);
return (VGEN_FAILURE);
}
ldcp->tx_data_cookie = kmem_zalloc(emsg->data_ncookies *
sizeof (ldc_mem_cookie_t), KM_SLEEP);
bcopy(emsg->data_cookie, ldcp->tx_data_cookie,
emsg->data_ncookies * sizeof (ldc_mem_cookie_t));
return (VGEN_SUCCESS);
}
int
vgen_dringsend_shm(void *arg, 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;
uint32_t buf_offset;
on_trap_data_t otd;
int rv = 0;
boolean_t is_bcast = B_FALSE;
boolean_t is_mcast = B_FALSE;
vgen_ldc_t *ldcp = (vgen_ldc_t *)arg;
vgen_t *vgenp = LDC_TO_VGEN(ldcp);
vgen_stats_t *statsp = &ldcp->stats;
vgen_hparams_t *lp = &ldcp->local_hparams;
boolean_t resched_peer = B_FALSE;
boolean_t tx_update = B_FALSE;
if (ldcp->ldc_status != LDC_UP) {
DBG2(vgenp, ldcp, "status(%d), dropping packet\n",
ldcp->ldc_status);
goto dringsend_shm_exit;
}
if (ldcp->hphase != VH_DONE) {
DWARN(vgenp, ldcp, "hphase(%x), dropping packet\n",
ldcp->hphase);
goto dringsend_shm_exit;
}
size = msgsize(mp);
if (size > (size_t)lp->mtu) {
DWARN(vgenp, ldcp, "invalid size(%d)\n", size);
goto dringsend_shm_exit;
}
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(vgenp, ldcp, "data access fault occured\n");
statsp->oerrors++;
if (mutex_owned(&ldcp->txlock)) {
mutex_exit(&ldcp->txlock);
}
if (mutex_owned(&ldcp->wrlock)) {
mutex_exit(&ldcp->wrlock);
}
goto dringsend_shm_exit;
}
mutex_enter(&ldcp->txlock);
txi = next_txi = ldcp->next_txi;
INCR_TXI(next_txi, ldcp);
txdp = &(ldcp->mtxdp[txi]);
if (txdp->dstate != VIO_DESC_DONE) {
if (ldcp->tx_blocked == B_FALSE) {
ldcp->tx_blocked_lbolt = ddi_get_lbolt();
ldcp->tx_blocked = B_TRUE;
}
statsp->tx_no_desc++;
mutex_exit(&ldcp->txlock);
(void) LDC_NO_TRAP();
return (VGEN_TX_NORESOURCES);
} else {
txdp->dstate = VIO_DESC_INITIALIZING;
}
if (ldcp->tx_blocked == B_TRUE) {
ldcp->tx_blocked = B_FALSE;
tx_update = B_TRUE;
}
ldcp->next_txi = next_txi;
mutex_exit(&ldcp->txlock);
if (tx_update == B_TRUE) {
vio_net_tx_update_t vtx_update =
ldcp->portp->vcb.vio_net_tx_update;
vtx_update(ldcp->portp->vhp);
}
MEMBAR_CONSUMER();
buf_offset = txdp->data_buf_offset;
dst = (caddr_t)ldcp->tx_datap + 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(&ldcp->wrlock);
ASSERT(txdp->dstate == VIO_DESC_INITIALIZING);
txdp->dstate = VIO_DESC_READY;
if (ldcp->resched_peer == B_TRUE && ldcp->resched_peer_txi == txi) {
resched_peer = B_TRUE;
ldcp->resched_peer = B_FALSE;
}
statsp->opackets++;
statsp->obytes += size;
if (is_bcast)
statsp->brdcstxmt++;
else if (is_mcast)
statsp->multixmt++;
mutex_exit(&ldcp->wrlock);
(void) LDC_NO_TRAP();
if (resched_peer == B_TRUE) {
rv = vgen_send_dringdata_shm(ldcp, (uint32_t)txi, -1);
if (rv != 0) {
DWARN(vgenp, ldcp, "failed sending dringdata msg "
"rv(%d) len(%d)\n", rv, size);
mutex_enter(&ldcp->wrlock);
statsp->oerrors++;
ldcp->resched_peer = B_TRUE;
mutex_exit(&ldcp->wrlock);
}
}
dringsend_shm_exit:
if (rv == ECONNRESET || rv == EACCES) {
(void) vgen_handle_evt_reset(ldcp, VGEN_OTHER);
}
freemsg(mp);
return (VGEN_TX_SUCCESS);
}
int
vgen_handle_dringdata_shm(void *arg1, void *arg2)
{
vgen_ldc_t *ldcp = (vgen_ldc_t *)arg1;
vio_msg_tag_t *tagp = (vio_msg_tag_t *)arg2;
vgen_t *vgenp = LDC_TO_VGEN(ldcp);
int rv = 0;
switch (tagp->vio_subtype) {
case VIO_SUBTYPE_INFO:
mutex_exit(&ldcp->cblock);
mutex_enter(&ldcp->rxlock);
rv = vgen_handle_dringdata_info_shm(ldcp, tagp);
mutex_exit(&ldcp->rxlock);
mutex_enter(&ldcp->cblock);
if (rv != 0) {
DWARN(vgenp, ldcp, "handle_data_info failed(%d)\n", rv);
}
break;
case VIO_SUBTYPE_ACK:
rv = vgen_handle_dringdata_ack_shm(ldcp, tagp);
if (rv != 0) {
DWARN(vgenp, ldcp, "handle_data_ack failed(%d)\n", rv);
}
break;
case VIO_SUBTYPE_NACK:
rv = vgen_handle_dringdata_nack_shm(ldcp, tagp);
if (rv != 0) {
DWARN(vgenp, ldcp, "handle_data_nack failed(%d)\n", rv);
}
break;
}
return (rv);
}
static int
vgen_handle_dringdata_info_shm(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
{
uint32_t start;
int32_t end;
int rv = 0;
vio_dring_msg_t *dringmsg = (vio_dring_msg_t *)tagp;
vgen_t *vgenp = LDC_TO_VGEN(ldcp);
vgen_stats_t *statsp = &ldcp->stats;
start = dringmsg->start_idx;
end = dringmsg->end_idx;
DBG1(vgenp, ldcp, "INFO: start(%d), end(%d)\n",
start, end);
if (!(CHECK_RXI(start, ldcp)) ||
((end != -1) && !(CHECK_RXI(end, ldcp)))) {
DWARN(vgenp, ldcp, "Invalid Rx start(%d) or end(%d)\n",
start, end);
return (0);
}
if (dringmsg->dring_ident != ldcp->peer_hparams.dring_ident) {
DWARN(vgenp, ldcp, "Invalid dring ident 0x%x\n",
dringmsg->dring_ident);
return (0);
}
statsp->dring_data_msgs_rcvd++;
if (ldcp->polling_on == B_TRUE) {
return (0);
}
rv = vgen_intr_rcv_shm(ldcp);
if (rv != 0) {
DWARN(vgenp, ldcp, "vgen_intr_rcv_shm() failed\n");
}
return (rv);
}
static int
vgen_intr_rcv_shm(vgen_ldc_t *ldcp)
{
int rv;
uint32_t end_ix;
vio_dring_msg_t msg;
uint_t mblk_sz;
int count = 0;
int total_count = 0;
mblk_t *bp = NULL;
mblk_t *bpt = NULL;
mblk_t *mp = NULL;
vio_net_rx_cb_t vrx_cb = ldcp->portp->vcb.vio_net_rx_cb;
ASSERT(MUTEX_HELD(&ldcp->rxlock));
do {
rv = vgen_receive_packet(ldcp, &mp, &mblk_sz);
if (rv != 0) {
if (rv == EINVAL) {
continue;
}
DTRACE_PROBE1(vgen_intr_nopkts, vgen_ldc_t *, ldcp);
break;
}
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 == vgen_chain_len) {
DTRACE_PROBE2(vgen_intr_pkts, vgen_ldc_t *, ldcp,
int, count);
mutex_exit(&ldcp->rxlock);
vrx_cb(ldcp->portp->vhp, bp);
mutex_enter(&ldcp->rxlock);
bp = bpt = NULL;
count = 0;
}
} while (total_count < ldcp->num_rxds);
if (bp != NULL) {
DTRACE_PROBE2(vgen_intr_pkts, vgen_ldc_t *, ldcp, int, count);
mutex_exit(&ldcp->rxlock);
vrx_cb(ldcp->portp->vhp, bp);
mutex_enter(&ldcp->rxlock);
}
if (ldcp->polling_on == B_FALSE) {
end_ix = ldcp->next_rxi;
DECR_RXI(end_ix, ldcp);
msg.dring_ident = ldcp->peer_hparams.dring_ident;
rv = vgen_send_dringack_shm(ldcp, (vio_msg_tag_t *)&msg,
VNET_START_IDX_UNSPEC, end_ix, VIO_DP_STOPPED);
return (rv);
}
return (0);
}
mblk_t *
vgen_poll_rcv_shm(vgen_ldc_t *ldcp, int bytes_to_pickup)
{
uint_t mblk_sz = 0;
uint_t sz = 0;
mblk_t *bp = NULL;
mblk_t *bpt = NULL;
mblk_t *mp = NULL;
int count = 0;
int rv;
mutex_enter(&ldcp->rxlock);
if (ldcp->hphase != VH_DONE) {
mutex_exit(&ldcp->rxlock);
return (NULL);
}
do {
rv = vgen_receive_packet(ldcp, &mp, &mblk_sz);
if (rv != 0) {
if (rv == EINVAL) {
continue;
}
DTRACE_PROBE1(vgen_poll_nopkts, vgen_ldc_t *, ldcp);
break;
}
if (bp == NULL) {
bp = mp;
bpt = bp;
bpt->b_next = NULL;
} else {
mp->b_next = NULL;
bpt->b_next = mp;
bpt = mp;
}
sz += mblk_sz;
count++;
if (sz >= bytes_to_pickup) {
break;
}
_NOTE(CONSTCOND)
} while (1);
if (ldcp->rx_pktdata == vgen_handle_pkt_data &&
ldcp->rx_pri_head != NULL) {
ldcp->rx_pri_tail->b_next = bp;
bp = ldcp->rx_pri_head;
ldcp->rx_pri_head = ldcp->rx_pri_tail = NULL;
}
mutex_exit(&ldcp->rxlock);
DTRACE_PROBE2(vgen_poll_pkts, vgen_ldc_t *, ldcp, int, count);
DTRACE_PROBE2(vgen_poll_bytes, vgen_ldc_t *, ldcp, uint_t, sz);
return (bp);
}
static int
vgen_receive_packet(vgen_ldc_t *ldcp, mblk_t **bp, uint_t *size)
{
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;
uint32_t nbytes = 0;
mblk_t *mp = NULL;
mblk_t *dmp = NULL;
vgen_stats_t *statsp = &ldcp->stats;
vgen_hparams_t *lp = &ldcp->local_hparams;
rxi = ldcp->next_rxi;
rxdp = &(ldcp->rxdp[rxi]);
vmp = ldcp->rxdp_to_vmp[rxi];
if (rxdp->dstate != VIO_DESC_READY) {
DTRACE_PROBE1(vgen_noready_rxds, vgen_ldc_t *, ldcp);
return (EAGAIN);
}
MEMBAR_CONSUMER();
nbytes = rxdp->nbytes;
if ((nbytes < ETHERMIN) ||
(nbytes > lp->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(ldcp->rx_vmp);
mp = vmp->mp;
if (new_vmp == NULL) {
statsp->norcvbuf++;
dmp = allocb(nbytes + VNET_IPALIGN, BPRI_MED);
if (dmp == NULL) {
statsp->ierrors++;
return (ENOMEM);
}
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;
ldcp->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(rxi, ldcp);
ldcp->next_rxi = rxi;
*size = nbytes;
*bp = mp;
return (err);
}
static int
vgen_handle_dringdata_ack_shm(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
{
uint32_t start;
int32_t end;
uint32_t txi;
vgen_stats_t *statsp;
vnet_rx_dringdata_desc_t *txdp;
on_trap_data_t otd;
int rv = 0;
boolean_t ready_txd = B_FALSE;
vgen_t *vgenp = LDC_TO_VGEN(ldcp);
vio_dring_msg_t *dringmsg = (vio_dring_msg_t *)tagp;
start = dringmsg->start_idx;
end = dringmsg->end_idx;
statsp = &ldcp->stats;
DBG2(vgenp, ldcp, "ACK: start(%d), end(%d)\n", start, end);
if (((start != VNET_START_IDX_UNSPEC) && !(CHECK_TXI(start, ldcp))) ||
!(CHECK_TXI(end, ldcp))) {
DWARN(vgenp, ldcp, "Invalid Tx ack start(%d) or end(%d)\n",
start, end);
return (rv);
}
if (dringmsg->dring_ident != ldcp->local_hparams.dring_ident) {
DWARN(vgenp, ldcp, "Invalid dring ident 0x%x\n",
dringmsg->dring_ident);
return (rv);
}
statsp->dring_data_acks_rcvd++;
mutex_enter(&ldcp->txlock);
if (ldcp->tx_blocked == B_TRUE) {
vio_net_tx_update_t vtx_update =
ldcp->portp->vcb.vio_net_tx_update;
ldcp->tx_blocked = B_FALSE;
vtx_update(ldcp->portp->vhp);
}
mutex_exit(&ldcp->txlock);
if (dringmsg->dring_process_state != VIO_DP_STOPPED) {
return (rv);
}
statsp->dring_stopped_acks_rcvd++;
rv = LDC_ON_TRAP(&otd);
if (rv != 0) {
if (mutex_owned(&ldcp->wrlock)) {
mutex_exit(&ldcp->wrlock);
}
return (ECONNRESET);
}
mutex_enter(&ldcp->wrlock);
ready_txd = B_FALSE;
txi = end;
INCR_TXI(txi, ldcp);
txdp = &ldcp->mtxdp[txi];
if (txdp->dstate == VIO_DESC_READY) {
ready_txd = B_TRUE;
}
(void) LDC_NO_TRAP();
if (ready_txd == B_FALSE) {
ldcp->resched_peer = B_TRUE;
ldcp->resched_peer_txi = txi;
mutex_exit(&ldcp->wrlock);
return (rv);
}
ldcp->resched_peer = B_FALSE;
mutex_exit(&ldcp->wrlock);
rv = vgen_send_dringdata_shm(ldcp, txi, -1);
if (rv != VGEN_SUCCESS) {
mutex_enter(&ldcp->wrlock);
ldcp->resched_peer = B_TRUE;
mutex_exit(&ldcp->wrlock);
}
return (rv);
}
static int
vgen_handle_dringdata_nack_shm(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
{
uint32_t start;
int32_t end;
uint32_t txi;
vnet_rx_dringdata_desc_t *txdp;
on_trap_data_t otd;
int rv = 0;
vgen_t *vgenp = LDC_TO_VGEN(ldcp);
vio_dring_msg_t *dringmsg = (vio_dring_msg_t *)tagp;
DBG1(vgenp, ldcp, "enter\n");
start = dringmsg->start_idx;
end = dringmsg->end_idx;
DWARN(vgenp, ldcp, "NACK: start(%d), end(%d)\n", start, end);
if (((start != VNET_START_IDX_UNSPEC) && !(CHECK_TXI(start, ldcp))) ||
!(CHECK_TXI(end, ldcp))) {
DWARN(vgenp, ldcp, "Invalid Tx nack start(%d) or end(%d)\n",
start, end);
return (rv);
}
if (dringmsg->dring_ident != ldcp->local_hparams.dring_ident) {
DWARN(vgenp, ldcp, "Invalid dring ident 0x%x\n",
dringmsg->dring_ident);
return (rv);
}
rv = LDC_ON_TRAP(&otd);
if (rv != 0) {
mutex_exit(&ldcp->txlock);
return (ECONNRESET);
}
mutex_enter(&ldcp->txlock);
for (txi = start; txi <= end; ) {
txdp = &(ldcp->mtxdp[txi]);
if (txdp->dstate == VIO_DESC_READY)
txdp->dstate = VIO_DESC_DONE;
INCR_TXI(txi, ldcp);
}
(void) LDC_NO_TRAP();
mutex_exit(&ldcp->txlock);
return (rv);
}
static int
vgen_send_dringdata_shm(vgen_ldc_t *ldcp, uint32_t start, int32_t end)
{
vgen_t *vgenp = LDC_TO_VGEN(ldcp);
vio_dring_msg_t dringmsg, *msgp = &dringmsg;
vio_msg_tag_t *tagp = &msgp->tag;
vgen_stats_t *statsp = &ldcp->stats;
int rv;
#ifdef DEBUG
if (vgen_inject_error(ldcp, VGEN_ERR_TXTIMEOUT)) {
return (VGEN_SUCCESS);
}
#endif
bzero(msgp, sizeof (*msgp));
tagp->vio_msgtype = VIO_TYPE_DATA;
tagp->vio_subtype = VIO_SUBTYPE_INFO;
tagp->vio_subtype_env = VIO_DRING_DATA;
tagp->vio_sid = ldcp->local_sid;
msgp->dring_ident = ldcp->local_hparams.dring_ident;
msgp->start_idx = start;
msgp->end_idx = end;
msgp->seq_num = atomic_inc_32_nv(&ldcp->dringdata_msgid);
rv = vgen_sendmsg_shm(ldcp, (caddr_t)tagp, sizeof (dringmsg));
if (rv != VGEN_SUCCESS) {
DWARN(vgenp, ldcp, "vgen_sendmsg_shm() failed\n");
return (rv);
}
statsp->dring_data_msgs_sent++;
DBG2(vgenp, ldcp, "DRING_DATA_SENT \n");
return (VGEN_SUCCESS);
}
int
vgen_send_dringack_shm(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp, uint32_t start,
int32_t end, uint8_t pstate)
{
int rv = 0;
vgen_t *vgenp = LDC_TO_VGEN(ldcp);
vio_dring_msg_t *msgp = (vio_dring_msg_t *)tagp;
vgen_stats_t *statsp = &ldcp->stats;
tagp->vio_msgtype = VIO_TYPE_DATA;
tagp->vio_subtype = VIO_SUBTYPE_ACK;
tagp->vio_subtype_env = VIO_DRING_DATA;
tagp->vio_sid = ldcp->local_sid;
msgp->start_idx = start;
msgp->end_idx = end;
msgp->dring_process_state = pstate;
msgp->seq_num = atomic_inc_32_nv(&ldcp->dringdata_msgid);
rv = vgen_sendmsg_shm(ldcp, (caddr_t)tagp, sizeof (*msgp));
if (rv != VGEN_SUCCESS) {
DWARN(vgenp, ldcp, "vgen_sendmsg_shm() failed\n");
}
statsp->dring_data_acks_sent++;
if (pstate == VIO_DP_STOPPED) {
statsp->dring_stopped_acks_sent++;
}
return (rv);
}
static int
vgen_sendmsg_shm(vgen_ldc_t *ldcp, caddr_t msg, size_t msglen)
{
int rv;
size_t len;
uint32_t retries = 0;
vgen_t *vgenp = LDC_TO_VGEN(ldcp);
len = msglen;
if ((len == 0) || (msg == NULL))
return (VGEN_FAILURE);
do {
len = msglen;
rv = ldc_write(ldcp->ldc_handle, (caddr_t)msg, &len);
if (retries++ >= vgen_ldcwr_retries)
break;
} while (rv == EWOULDBLOCK);
if (rv != 0) {
DWARN(vgenp, ldcp, "ldc_write failed: rv(%d) msglen(%d)\n",
rv, msglen);
return (rv);
}
if (len != msglen) {
DWARN(vgenp, ldcp, "ldc_write failed: rv(%d) msglen (%d)\n",
rv, msglen);
return (VGEN_FAILURE);
}
return (VGEN_SUCCESS);
}